delegate and agent_wait¶
delegate hands self-contained sub-tasks to isolated agents. agent_wait
collects their reports. Together they are how one conversation fans work out to
several without drowning in the output.
Delegate when the work branches — several investigation paths at once — or when a sub-task would flood the main conversation with noise that only its conclusion matters. A wide directory brute-force belongs in an agent; its one useful line comes back, the ten thousand 404s do not.
Both are registered by the CLI, always. The internals — supervision, detachment, focus, review, the shared workspace — are in the agent subsystem concept page; the operator controls are in the agents guide.
delegate¶
Spawns a batch of agents and returns immediately with their ids. The agents run detached in the background while the master keeps its turn.
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
tasks |
array of task specs | required | One or more tasks, launched concurrently. |
Each task spec:
| Field | Type | Default | Description |
|---|---|---|---|
agent_type |
string | required | Profile: a built-in (recon, analysis, exploit, general, container-operator) or a user-defined AGENT.md. |
instruction |
string | required | A clear, self-contained task. The agent does not share the master's conversation. |
name |
string | derived | A short kebab-case name (2–4 words) — what you see while it runs, e.g. tomcat-enum. Falls back to a slug of the instruction, then the profile. |
success_criteria |
string | — | How the agent knows it is done. Optional but recommended. |
allowed_tools |
array[string] | profile's set | A subset of the profile's tools. Can only narrow, never widen. report is always available and need not be listed. |
If a task requests a tool outside its profile's whitelist, the whole batch fails
validation. Listing report is allowed, but an explicit subset must retain at
least one tool from the profile.
Batch size is capped at
AGENT_MAX_BATCH (5), with further limits on depth,
total, and concurrency.
Self-contained is not optional¶
An agent starts cold. It does not see the master's conversation, only its
instruction, success_criteria, and the shared ledger. An
instruction that says "continue the scan from before" gives the agent nothing to
continue from. Write it as if to someone who just walked in.
Approval¶
The master's delegate call follows the current approval mode and
rules. With the packaged rules, it prompts in strict
and normal, and runs automatically in permissive and yolo. A matching deny
rule rejects it in every mode except yolo.
The prompt offers this exact call as a saved rule, with no broader option.
Saving it can auto-approve a later call with identical arguments in normal mode;
strict still prompts. Broader rules can be written in approvals.yaml.
Spawned agents auto-approve their own tool calls within their profile's whitelist.
Allowing the batch, by prompt, rule, or mode, grants that autonomy for those
tasks. It is also why sast_scan is in no built-in profile — a whitelisted call
bypasses the prompt, and that scan is worth seeing.
That autonomy reaches further than the batch you approved. Below
AGENT_MAX_DEPTH every spawned agent receives a
nested delegate of its own — outside its whitelist, since the runner appends it
after allowed_tools is applied — and its nested tasks may name any profile,
wider ones included. Approving a delegate therefore approves fan-out to the depth
cap, not one level of it; see the ceiling is per agent, not per
subtree.
Returns¶
A confirmation naming each spawned agent and its task, with the agent_wait call
to collect them. Metadata carries agent_ids, detached: true, and an agents
list of {id, name, profile, task}.
Ids are internal (a1, a2, monotonic, never reused — what agent_wait and logs
key on). Names are what you chose and what the CLI shows. Model-facing text carries
both, as name (id, profile).
Example¶
{
"tasks": [
{
"agent_type": "recon",
"name": "smb-enum",
"instruction": "Enumerate SMB on 10.10.10.5: shares, null sessions, signing, users. Do not exploit.",
"success_criteria": "A list of shares and whether null/guest access works."
},
{
"agent_type": "recon",
"name": "web-enum",
"instruction": "Enumerate the web app on 10.10.10.5:8080: tech stack, directories, obvious version strings.",
"success_criteria": "Tech stack identified and a directory listing."
}
]
}
agent_wait¶
Blocks on delegated agents and returns their reports.
Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
ids |
array[string] | — | Agent ids to wait for, e.g. ["a1", "a2"]. Omit to collect every report not yet seen. |
timeout_s |
number | — | Give up after this many seconds and return what is ready. Omit to wait for all. |
You do not have to call it¶
If the master never calls agent_wait, finished reports are still delivered — each
front end drains completed reports at the next turn boundary and appends them as
messages, exactly once. agent_wait is the way to block now; letting reports
arrive on their own is the way to keep working meanwhile.
A timeout_s that expires with agents still running is a success, not an
error: it returns the ready reports plus a Still running: … line. Losing finished
work to a timeout would be the worse outcome.
Approval¶
Auto-approved (allow: "agent_wait({})"). Approval widens to the whole tool — it
only blocks on agents already spawned and approved.
Returns¶
The agents' reports, concatenated in one consistent shape whether they arrive
through agent_wait or at the turn boundary. Each report is headed with the
agent's name, profile, status, id, and token cost, followed by FINDINGS: and, if
present, NEXT STEPS:.
Agent status is one of:
| Status | Meaning |
|---|---|
completed |
It called report with status="completed", or it stopped emitting tool calls and its closing text was taken as the findings. |
partial |
It reported partial progress itself, or the operator stopped it with /agents stop — whatever it had is still reported. Read NEXT STEPS. |
stuck |
It never reported: it exhausted AGENT_MAX_ITERATIONS or its profile's max_iterations, or it ended with no usable final response. Its last assistant text is reported in place of findings. |
A stuck report is the one status that makes the collection fail: agent_wait
returns success=false with error: "One or more agents returned status=stuck" and
any_stuck: true in metadata, with every report — stuck or not — still in the
output. Running out of iterations is therefore an error to the master, not a
partial result.
The report also prints the agent's engagement id. Pass that to
recall's engagement filter to read the raw tool output behind a
summary that was too thin.
Example¶
{ "ids": ["a1", "a2"] }
Collect whatever has finished, without blocking on the rest:
{ "timeout_s": 0.1 }
Configuration¶
| Variable | Default | Effect |
|---|---|---|
AGENT_MAX_DEPTH |
2 |
Deepest a delegation chain may nest. |
AGENT_MAX_TOTAL |
20 |
Total agents per session. |
AGENT_MAX_BATCH |
5 |
Agents one delegate may spawn at once. |
AGENT_MAX_CONCURRENT |
5 |
Live agents at the same time. |
AGENT_MAX_ITERATIONS |
30 |
Tool-call iterations an agent may run before stopping. |
AGENT_TEMPERATURE |
(unset) | Sampling temperature for agents. |
Related¶
- Agents guide —
/agents, attaching, review, stop. - Agent subsystem — how detachment, supervision, and delivery actually work.
- Agent profiles — the built-in profiles and the
AGENT.mdschema. recall— reading the raw output behind a delegated report.ledger— the shared working memory agents read and write.