A small OpenClaw plugin for worker-first delegation.
Headcrab helps a main OpenClaw session hand work to subagents cleanly. It does three narrow things for scoped direct-message parent sessions:
- adds one transient orchestrator reminder during
before_prompt_build; - wraps only
sessions_spawn.params.taskduringbefore_tool_callwith a main↔worker handoff contract; - normalizes
sessions_spawn.params.runTimeoutSecondsto a 30-minute default and 30-minute maximum.
It does not block direct tools, persist delegation state, rewrite user messages, or add broad logging. The plugin is intentionally small and fail-closed.
Use Headcrab when you want the main assistant to act as an orchestrator for action-heavy work while preserving the original task exactly for the worker.
It is not a scheduler, policy engine, permission system, or OpenClaw core patch. It only affects the plugin hooks it registers.
- ARCHITECTURE.md is the canonical product architecture contract.
- src/CONTEXT.md describes the source container boundary.
- src/core/CONTEXT.md describes the pure core boundary.
Historical proposal/plan documents are intentionally not kept in the active repo; useful decisions are distilled into the architecture contract above.
This repository is meant to be used as an OpenClaw plugin source tree. Headcrab is a native OpenClaw plugin and expects the OpenClaw plugin runtime/SDK to load index.js.
A raw standalone check such as node -e "import('./index.js')" outside OpenClaw can fail to resolve openclaw/plugin-sdk/plugin-entry; that is an expected OpenClaw runtime dependency for native plugins, not the correct standalone test path. Use OpenClaw plugin loading, or the development checks below, instead.
Typical local plugin path intent:
If you keep OpenClaw plugins inside another repository, add Headcrab as a git subtree or copy this tree into your plugin directory. Keep the repository root intact so package.json, openclaw.plugin.json, index.js, src/, scripts/, and test/ stay together.
Headcrab has no dependency on Delegate Mode Skill or any other OpenClaw skill. Its only OpenClaw-specific dependency is the expected OpenClaw runtime/SDK contract for native plugins.
Global on/off is controlled by the top-level OpenClaw plugin enabled flag. There is no plugin-local config.enabled.
Default config:
{
"scope": ["dm:*"],
"features": {
"promptReminder": true,
"taskWrapping": true,
"forthrightCommunication": true
}
}dm:*selects any main/root direct-message session matching the knownagent:<agent>:<channel>:direct:<id>shape.dm:<id>selects one direct-message session by direct/chat id.
Docs and tests use synthetic ids only, for example dm:synthetic-direct-a. Do not publish real chat ids, direct ids, raw session keys, or user identifiers in configs, docs, or test fixtures.
promptReminder: inject the transient reminder into scoped parent sessions.taskWrapping: wrapsessions_spawn.params.taskfrom scoped parent sessions and normalizesessions_spawn.params.runTimeoutSeconds.forthrightCommunication: include the compact worker communication prefix in wrapped tasks.
Missing config, {}, missing features, and missing individual feature booleans all default to enabled. Explicit false disables only that feature.
Invalid config fails closed/inactive instead of widening activation.
Unsupported keys include targetDirectIds, targetDirectSessionKeys, plugin-local enabled, directToolBlocking, logging config, unknown top-level keys, unknown selectors, empty scope, non-string scope items, and non-boolean feature values.
- Parent scoped direct-message sessions can receive the orchestrator reminder and task wrapping.
- Child/subagent sessions do not inherit scope.
- Nested child spawns are not wrapped unless that child independently matches scope.
- Non-
sessions_spawntools are unchanged. sessions_spawn.params.runTimeoutSecondsdefaults to1800when absent, invalid, or unlimited (0).sessions_spawn.params.runTimeoutSecondsis capped at1800when a higher value is requested.- Explicit shorter positive
runTimeoutSecondsvalues are preserved. - Already wrapped tasks are not wrapped a second time, but their
runTimeoutSecondsis still normalized to the same 30-minute default/maximum rules. - Worker completion sanitation is a parent-session responsibility. Headcrab reminds the parent to merge worker output into a clean user-facing result and requests compact worker packets, but the current hooks do not let Headcrab intercept or rewrite worker completion text after return.
Run:
npm run healthcheckThe healthcheck uses only synthetic cases. It prints bounded PASS/FAIL check names and counts. It must not print raw prompts, raw tasks, session ids, config payloads, paths, secrets, or hook payloads.
npm test
npm run check
npm run healthcheckThere are no runtime npm dependencies in this package. Do not add openclaw as a package dependency or devDependency just to make a standalone Node import work; OpenClaw supplies the plugin SDK import when it loads native plugins. Run npm install only if a future change adds a lockfile or dependencies that require it.
Headcrab emits low-noise best-effort logs through OpenClaw api.logger at the hook boundary:
debugfor expected skips and non-applicable hook outcomes;infowhen a reminder is injected or a task is wrapped;errorfor sanitized hook exceptions while preserving throw behavior.
Log payloads are intentionally coarse: plugin code, monotonic adapter counter, hook name, stage, outcome, bounded reason code, and optional wrapping variant.
The plugin must not log prompts, task text, user messages, rewritten text, raw hook/tool params, session keys, direct ids, chat/user/message ids, raw config, secrets, local paths, Error.message, stacks, causes, or serialized exceptions.
Headcrab is designed to minimize data movement:
- no plugin-local persistent state;
- no plugin-local files or background jobs;
- no network calls;
- no raw prompt/task/session logging;
- synthetic-only tests and healthcheck fixtures.
Limits:
- OpenClaw core and other plugins may still log or persist data outside Headcrab.
- Headcrab cannot enforce delegation or final user-facing sanitation by itself; it only adds reminders and wraps spawn tasks.
- Misconfiguration can disable the plugin or narrow scope. Invalid config fails closed rather than guessing.
See LICENSE.
{ "plugins": { "headcrab": { "enabled": true, "path": "/path/to/Headcrab", "config": {} } } }