4.8 KiB
subagent_generate & subagent_spawn
Spawn independent subagent sessions with JIT-generated identities via echo JSON | subagent_generate | xargs subagent_spawn.
Trigger condition: a task decomposes into independent subtasks each requiring multi-step reasoning or multiple tool calls; you are processing N items with the same operation; or the current agent's identity would work against the quality of a subtask — delegate to an agent whose identity is aligned with it.
Calling convention:
pipe: stages=[
{tool: "subagent_generate", args: [{"role": "ROLE", "shared_task": "TASK_DESC"}]},
{tool: "subagent_spawn", args: ["-name", "NAME", "TASK"]}
]
Parallel — issue separate pipe calls for each subagent (subagent_spawn returns immediately, so sequential dispatch is fine):
pipe: stages=[
{tool: "subagent_generate", args: [{"role": "ROLE_A", "roster": "a (you), b", "shared_task": "X"}]},
{tool: "subagent_spawn", args: ["-name", "a", "TASK_A"]}
]
pipe: stages=[
{tool: "subagent_generate", args: [{"role": "ROLE_B", "roster": "a, b (you)", "shared_task": "X"}]},
{tool: "subagent_spawn", args: ["-name", "b", "TASK_B"]}
]
Returns: subagent_generate returns the agent config name. subagent_spawn returns the session path (e.g. s/my-session).
subagent_generate
No stdin prints the template. With JSON on stdin, creates a JIT agent config inheriting hooks and tooling from default.json with a clean prompt (SYSTEM_PROMPT + identity only).
| Field | Required | Description |
|---|---|---|
name |
no | Config name (default: jit-<hex>) |
role |
yes* | Who this agent is and what it does |
roster |
no | Complete name → task mapping of all peers, (you) marking this agent |
shared_task |
no | Beads issue ID or tracking entry |
protocol |
no | Coordination mechanics: lead, decision path, triggers, prompt file targets |
constraints |
no | Array of constraint strings |
prompt |
yes* | Legacy: complete identity as a single string |
*One of role or prompt is required. Prefer role with structured fields.
When role is provided, the fields are assembled into a markdown identity envelope:
## Role
<role>
## Roster
<roster>
## Shared Task
<shared_task>
## Coordination Protocol
<protocol>
## Constraints
- <constraint 1>
- <constraint 2>
The prompt field is a legacy fallback — use it only when the structured fields don't fit.
subagent_spawn
| Flag | Default | Description |
|---|---|---|
-name NAME |
auto | Session name |
-agent A |
server default | Agent config name (from subagent_generate) |
-backend B |
server default | Backend override |
-model M |
server default | Model override |
-cwd DIR |
pwd |
Working directory |
Remaining args: the task prompt (immediate task only — coordination lives in the identity).
subagent_spawn auto-injects context headers into the prompt:
[parent_session_id=...]— the parent's session ID[session_id=...]— the child's own session ID[parent_plan_start]...[parent_plan_end]— the parent's current plan file contents (if non-empty)
Constraints:
- Prefer the pipeline. Use
subagent_generate→subagent_spawnwhen the subagent needs a custom identity. For simple delegation with no identity requirements,subagent_spawnalone with-agentor server defaults is fine. - Identity carries coordination, prompt carries task. Roster, shared task, and protocol go in the identity. The seed prompt is purely the task.
- Subagents do not share memory. Pass context explicitly in the seed prompt.
- Peers are known at spawn time. Assign
-nameto each and include the full roster in each identity. - Names must be globally unique under
s/. Duplicate names fail silently.
Returning results to the parent
Subagents signal completion by writing directly to s/{parent_session_id}/prompt. This delivers the result as the parent's next queued turn — no polling, no blocking, no active waiting.
Critical: The parent must NEVER actively wait for subagent results. No statewait, no polling loops, no blocking reads. After spawning, the parent either continues its own work or ends its turn. When a child writes to the parent's prompt, the message queues and drains automatically when the parent next idles. This avoids wasting expensive coordinator tokens on idle waiting and eliminates coordination deadlocks.
The result written to the parent's prompt should be compact and unambiguous. Use this format:
[from={session_id}]
STATUS: done|blocked|partial
SUMMARY: <what was accomplished, one short paragraph>
ARTIFACTS: <newline-separated filesystem paths written, omit if none>
OPEN: <unresolved items, omit if none>
The [from=...] header is essential when multiple subagents report to the same parent — it lets the parent correlate results to tasks without having to infer identity from content.