agent:<agentId>:subagent:<uuid>) and,
by default, announces its result back to the requester for review.
Every sub-agent run is tracked as a background task.
Goals:
- Parallelize research, long tasks, and slow tool work without blocking the main run.
- Keep sub-agents isolated by default (session separation, optional sandboxing).
- Keep the tool surface hard to misuse: sub-agents do not get session or message tools by default.
- Support configurable nesting depth for orchestrator patterns.
Cost note: each sub-agent has its own context and token usage by
default. For heavy or repetitive tasks, set a cheaper model for sub-agents
and keep your main agent on a higher-quality model via
agents.defaults.subagents.model or per-agent overrides. When a child
genuinely needs the requester’s current transcript, spawn it with
context: "fork". Thread-bound subagent sessions default to
context: "fork" because they branch the current conversation into a
follow-up thread.Slash command
/subagents inspects sub-agent runs for the current session:
/subagents info shows run metadata (status, timestamps, session id,
transcript path, cleanup). /subagents log prints recent chat turns for a
run; add the tools token to include tool-call/result messages (omitted
by default). Use sessions_history for a bounded, safety-filtered recall
view from within an agent turn, or inspect the transcript path on disk for
the raw full transcript.
In the Control UI, parent sessions with recent child runs have an expandable
sidebar row. The nested rows show child status and runtime, and selecting one
opens that child’s chat while preserving the parent hierarchy.
Thread binding controls
These commands work on channels with persistent thread bindings. See Thread supporting channels below.Spawn behavior
Agents start background sub-agents with thesessions_spawn tool. Follow the
completion path described in the accepted receipt:
- Ordinary announcing runs return an internal completion event to the requester, which reviews the result and decides whether a user-facing update is needed.
- Swarm collectors return results through explicit collection, not completion notifications.
- Thread-bound session runs with a deliverable bound route reply directly to that thread, without a separate parent announcement.
- Caller-managed quiet runs send no completion notification.
Non-blocking, push-based completion
Non-blocking, push-based completion
sessions_spawnreturns a run id after startup is accepted, without waiting for the child task to finish. Spawns from an OpenClaw cloud worker can first wait for child provisioning and node enrollment.- Announcing sub-agents report back to the parent/requester session on completion.
- Agent turns that need those announced results should call
sessions_yieldwhen available. That ends the current turn and lets the completion event arrive as the next model-visible message. Collectors instead require explicit result collection. - Announced completion is push-based. Once spawned, do not poll
/subagents list,sessions_list, orsessions_historyin a loop just to wait for it to finish; check status on-demand only when debugging. - Child output is a report/evidence for the requester agent to synthesize. It is not user-authored instruction text and cannot override system, developer, or user policy.
- A child run ending does not by itself complete the requester’s user-facing goal. The requester compares the result with the requested outcome and continues in-scope work, including review findings and failed checks, before replying. Persistent child sessions can be continued with
sessions_send. - Report the overall goal as blocked only when continuation requires new user authority or an unavailable external decision. Ordinary fixable findings are continuation work, not a terminal blocker.
- On completion, OpenClaw best-effort closes tracked browser tabs/processes opened by that sub-agent session before the announce cleanup flow continues.
Completion delivery
Completion delivery
- OpenClaw hands completions back to the requester session through an
agentturn with a stable idempotency key. - If the requester run is still active, OpenClaw first tries to wake/steer that run instead of starting a second visible reply path.
- If an active requester cannot accept steering, including a busy CLI run, the handoff waits in the same session lane and starts after the current turn releases its claim. A failed wake does not start a competing turn or discard the completion.
- A successful in-session parent handoff completes sub-agent delivery even when the parent decides no visible user update is needed. External completion delivery requires a confirmed send, not merely an answer saved in the requester transcript.
- Native sub-agents do not get the message tool. They return plain assistant text to the parent/requester agent; human-visible replies stay owned by the parent/requester agent’s normal delivery policy.
- Queue acceptance is not delivery. If direct handoff cannot be used, delivery falls back to queue routing; the completion remains
session_queued, rather than delivered, until the durable queue settles. - Automatic completion delivery retries for up to 30 minutes, starting around 15 seconds and capping the backoff at 5 minutes. Permanent failure or deadline expiry leaves the successful child task visibly blocked instead of discarding its result.
- Missing or empty external delivery receipts remain unconfirmed and follow that bounded retry policy. An adapter-reported unconfirmed send remains ambiguous, never intentional suppression. Empty requester output still uses the existing completion fallback; it is not an outbound-hook cancellation. A confirmed message-tool send to the requester still counts as delivery.
- If an outbound hook intentionally suppresses a completion, the child can remain completed while its task delivery is marked
failedwith the suppression reason. OpenClaw does not retry or start another requester turn to bypass that decision. Inspect the task error and hook policy before manually retrying. - Blocked canonical results are retained for 7 days. Operators can retry or intentionally dismiss them from the Tasks page or with
openclaw tasks retry/openclaw tasks dismiss; retry can duplicate a visible result after an ambiguous provider acknowledgement. - Delivery keeps the resolved requester route: thread-bound or conversation-bound completion routes win when available. If the completion origin only provides a channel, OpenClaw fills the missing target/account from the requester session’s recorded delivery context so direct delivery still works.
Completion handoff metadata
Completion handoff metadata
The completion handoff to the requester session is runtime-generated
internal context (not user-authored text) and includes:
Result— the latest visibleassistantreply text from the child. Tool/toolResult output is not promoted into child results. Terminal failed runs do not reuse captured reply text.Model route change— when the terminal producer proves that fallback changed the requested model, one bounded and redacted route fact is carried separately fromResult. Local and nested parents preserve it in their update. External channel parents keep it as private orchestration context, and raw direct-delivery fallback sends onlyResult.Status—completed; ready for parent review/failed/timed out/unknown.- Compact runtime/token stats.
- A review instruction telling the requester agent to verify the result before deciding whether the original task is done.
- Follow-up guidance telling the requester agent to continue the task or record a follow-up when the child result leaves more action.
- A final-update instruction for the no-more-action path, written in normal assistant voice without forwarding raw internal metadata.
Modes and ACP runtime
Modes and ACP runtime
--modeland--thinkingoverride defaults for that specific run.- Use
info/logto inspect details and output after completion. - For persistent thread-bound sessions, use
sessions_spawnwiththread: trueandmode: "session". - If the requester channel does not support thread bindings, use
mode: "run"instead of retrying an impossible thread-bound combination. - For ACP harness sessions (Claude Code, Gemini CLI, OpenCode, or explicit Codex ACP/acpx), use
sessions_spawnwithruntime: "acp"when the tool advertises that runtime. See ACP delivery model when debugging completions or agent-to-agent loops. When thecodexplugin is enabled, Codex chat/thread control should prefer/codex ...over ACP unless the user explicitly asks for ACP/acpx. - OpenClaw hides
runtime: "acp"until ACP is enabled, the requester is not sandboxed, and a backend plugin such asacpxis loaded.runtime: "acp"expects an external ACP harness id, or anagents.entries.*entry withruntime.type="acp"; use the default sub-agent runtime for normal OpenClaw config agents fromagents_list.
Context modes
Non-thread native sub-agents start isolated unless the caller explicitly asks to fork the current transcript. Thread-bound spawns followthreadBindings.defaultSpawnContext, which defaults to fork. Pass
context: "isolated" explicitly when the child must start with clean context.
Use
fork sparingly. It is for context-sensitive delegation, not a
replacement for writing a clear task prompt.
Tool: sessions_spawn
Starts a sub-agent run on the global subagent lane. Ordinary one-shot runs
use deliver: false and return through an announce step; collectors, quiet
runs, and direct thread replies use the completion paths above.
Availability depends on the caller’s effective tool policy. The built-in
coding and messaging profiles include sessions_spawn,
sessions_yield, and subagents; minimal does not. full allows every
tool. Add those tools with tools.alsoAllow, or use one of the profiles
above, for an agent on a custom narrower profile that should still
delegate work.
Channel/group, provider, sandbox, and per-agent allow/deny policies can
still remove the tool after the profile stage. Use /tools from the same
session to confirm the effective tool list.
Defaults:
- Model: native sub-agents inherit the caller unless you set
agents.defaults.subagents.model(or per-agentagents.entries.*.subagents.model). ACP runtime spawns use the same configured subagent model when present; otherwise the ACP harness keeps its own default. An explicitsessions_spawn.modelstill wins. - Thinking: native sub-agents inherit the caller’s active turn, including one-shot thinking overrides, unless you set
agents.defaults.subagents.thinking(or per-agentagents.entries.*.subagents.thinking). ACP runtime spawns also applyagents.defaults.models["provider/model"].params.thinkingfor the selected model. An explicitsessions_spawn.thinkingstill wins. - Run timeout: pass
runTimeoutSecondsto set a timeout for a specific native, ACP, or visible sub-agent run. When omitted, OpenClaw usesagents.defaults.subagents.runTimeoutSecondsif configured; otherwise it falls back to0(no timeout). An explicit0disables the timeout for that run. - Process lifetime: a detached OpenClaw sub-agent has its own run lifecycle. A background task created inside an external CLI backend is different: it shares the parent CLI subprocess and stops if that parent reaches
agents.defaults.timeoutSeconds. - Task delivery: native sub-agents receive their delegated task in a
[Subagent Task]message appended after any forked history. Inherited task envelopes are context, not the current child’s assignment. The sub-agent system prompt carries runtime rules and routing context, not a hidden duplicate of the task.
context
(fork or isolated), including isolated when a requested fork exceeds the
parent-context size cap. They also include resolved child model metadata:
resolvedModel contains the applied model ref and resolvedProvider contains
the provider prefix when the ref has one.
Delegation prompt mode
agents.defaults.subagents.delegationMode controls prompt guidance only; it does not change tool policy or enforce delegation. With no explicit setting, OpenClaw uses prefer in each agent’s main session and suggest in every other session.
suggest: keep the standard prompt nudge to use sub-agents for larger or slower work.prefer: tell the agent to stay responsive and delegate anything more involved than a direct reply throughsessions_spawn.
suggest in a main session and prefer elsewhere. Per-agent overrides use agents.entries.*.subagents.delegationMode.
In prefer mode, hidden sub-agents are for internal legwork that the user does not need to follow. Work the user will watch or return to, or work with its own deliverable such as a URL, PR, or report, should use sessions_spawn with visible: true so it remains in the sidebar.
Tool parameters
string
required
The task description for the sub-agent.
string
Optional stable handle for identifying a specific child in later status output. Must match
[a-z][a-z0-9_-]{0,63} and cannot be a reserved target such as last or all.string
Optional short task title shown in UI lists (task ledger, session sidebar). Name the work being done, not the agent; it is set on the child session at run start.
string
Spawn under another configured agent id when allowed by
subagents.allowAgents.string
Optional task working directory for the child run. Native sub-agents still load bootstrap files from the target agent workspace;
cwd only changes where runtime tools and CLI harnesses do the delegated work. For visible sessions, paths outside configured agent workspaces require operator.admin. With worktree: true, omitting cwd inherits the same-agent parent’s managed repository when available; otherwise the target agent workspace is used."subagent" | "acp"
default:"subagent"
acp is only for external ACP harnesses (claude, droid, gemini, opencode, or explicitly requested Codex ACP/acpx) and for agents.entries.* entries whose runtime.type is acp.string
ACP-only. Resumes an existing ACP harness session when
runtime: "acp"; ignored for native sub-agent spawns."parent"
ACP-only. Streams ACP run output to the parent session when
runtime: "acp"; omit for native sub-agent spawns.string
Override the sub-agent model. Invalid values are skipped and the sub-agent runs on the default model with a warning in the tool result.
integer
Override the configured run timeout for this child. Must be a non-negative integer;
0 disables the timeout. Applies to native, ACP, and visible sessions.string
Override thinking level for the sub-agent run. Not available with
visible: true.boolean
default:"false"
When
true, requests channel thread binding for this sub-agent session."run" | "session"
default:"run"
If
thread: true and mode is omitted, default becomes session. mode: "session" requires thread: true.
If thread binding is unavailable for the requester channel, use mode: "run" instead.
With visible: true, omit mode or use the default "run"; the visible session remains persistent. mode: "session" is unavailable on this path."delete" | "keep"
default:"keep"
"delete" archives the session immediately after announce (still keeps the transcript via rename).boolean
default:"true"
Set
false for fire-and-forget children. When the child finishes, OpenClaw skips the completion handoff to the requester (no announce or steer turn), records the delivery as not required, and still runs child cleanup. Inspect such children with subagents or sessions_history. collect: true always uses false."inherit" | "require"
default:"inherit"
require rejects the spawn unless the target child runtime is sandboxed."isolated" | "fork"
fork branches the requester’s current transcript into the child session. Native sub-agents only. Non-thread spawns default to isolated; thread-bound spawns follow threadBindings.defaultSpawnContext, which defaults to fork. Pass isolated explicitly to guarantee clean context. All native forks, hidden or visible, must target the same agent as the requester.boolean
default:"false"
Create a persistent dashboard session for work the user will watch or return to, or when they ask for a thread. Visible spawns support only
runtime: "subagent" and always keep the created session.string
Optional custom sidebar group for a visible session; a new name creates the group. Omitted, empty, and whitespace-only values mean ungrouped and are also accepted for hidden or ACP runs. A nonempty group requires
visible: true.boolean
default:"false"
Provision a managed git worktree for the new dashboard session. Requires
visible: true.string
Optional managed-worktree name. Requires
visible: true and worktree: true.string
Optional git base ref for the managed worktree. Requires
visible: true and worktree: true.visible: true, group, model, cwd, and a same-agent context: "fork" are supported. Use this durable mode for coding, multi-step work, or results the user may revisit, steer, or keep; it appears in the sidebar when the web UI is available and still works without it. Pass group to place the new session in that sidebar group atomically; omitted or blank values leave it ungrouped. A sandboxed target restricts cwd to that agent’s workspace. Non-admin callers may use cwd only inside a configured agent workspace. With worktree: true, omitting cwd inherits the same-agent parent’s live managed repository and creates a separate worktree. Other spawns use the target agent workspace; for another repository, ask the operator to start the session from a registered project. Do not replace a rejected persistent spawn with the synchronous openclaw agent CLI, whose command deadline defaults to 600 seconds. Thread binding, mode: "session", thinking overrides, lightContext, and attachment staging are unavailable on this path because visible sessions are persistent dashboard sessions created through sessions.create. The default mode: "run", empty attachments, and an empty attachAs.mountPath are accepted without changing that behavior. The new dashboard child inherits the requester’s effective tool-policy ceiling before its first turn. Session listing and addressing obey tools.sessions.visibility; the default all scope covers sessions across agents on the Gateway for unsandboxed callers. Cross-agent access is on by default and governed by tools.agentToAgent; use allow to restrict agent pairs or set enabled: false to block ordinary cross-agent access (requester-owned native subagent and ACP child sessions stay reachable under tree or all). Set agent for same-agent-only access, tree for current plus spawned scope (main retains its same-agent exception), or self for current-session-only access. Sandbox spawned-only clamps still apply. Cross-agent owned children are included by tree, not agent; preserve explicit tree for that workflow. See Session tools and Managed worktrees.
If a call fails with Parameters require visible=true, omit the named group or worktree options to keep the hidden or ACP runtime. To create a visible session instead, use visible: true with runtime: "subagent" and omit mode, thread, thinking, lightContext, attachments, attachAs, swarm options, and the ACP-only streamTo and resumeSessionId. Worktree names and base refs also require worktree: true. Adding visible: true alone does not make an ACP call compatible.
A visible spawn is attributed to the requesting agent: the new session’s creator and initial owner is that agent, shown with its configured identity name and avatar in the sidebar. The accepted result doubles as a receipt with childSessionKey, runId, a Control UI sessionUrl (omitted when the Control UI is disabled), and an owner record. When acknowledging the spawn in a channel, put the session URL on the first line and Owner: <label> on the second so the user can open the session and see who is responsible. Owners can be reassigned later; see Multi-user mode.
Task names and targeting
taskName is a model-facing handle for orchestration, not a session key.
Use it for stable child names such as review_subagents,
linux_validation, or docs_update when a coordinator may need to inspect
that child later.
Target resolution accepts exact taskName matches and unambiguous
prefixes. Matching is scoped to the same active/recent target window used
by numbered /subagents targets, so a stale completed child does not make
a reused handle ambiguous. If two active or recent children share the same
taskName, the target is ambiguous; use the list index, session key, or
run id instead.
The reserved targets last and all are not valid taskName values
because they already have control meanings.
Tool: sessions_yield
Ends the current model turn and waits for announced child completion events
to arrive as the next message. Use it when the requester needs results from
announcing children before answering. It does not collect Swarm results:
collectors require agents_wait, or an awaited agents.run() in OpenClaw
Code Mode, and do not send completion notifications.
sessions_yield is the waiting primitive for announced completions. Do not replace it with polling
loops over subagents, sessions_list, sessions_history, shell
sleep, or process polling just to detect child completion.
Use the optional message field for private context that the resumed turn
should receive. Use acknowledgment for a waiting reply when an interactive
parent turn would otherwise end silently. The acknowledgment is not sent from
sub-agent, heartbeat, or silent turns, and it does not replace a reply or
message already delivered during the turn. This host-owned waiting status
bypasses message-tool-only source suppression; ordinary model replies remain
private unless the model sends them through the message tool.
On native Codex harness turns, wait_agent keeps the current turn active and
is reserved for an intentional same-turn wait when the immediate next step is
blocked on the child. Use sessions_yield instead when a native child’s result
should resume the parent in a later turn.
Only use sessions_yield when the session’s effective tool list includes
it. Some minimal or custom tool profiles may expose sessions_spawn and
subagents without exposing sessions_yield; in that case, do not invent
a polling loop just to wait for completion.
A sub-agent can also yield on its own behalf to wait for external work, such
as a remote job or a long-running task it does not drive itself. That pauses
the child run instead of completing it, so the requester receives no
completion event yet and keeps waiting. A plugin can then continue that same run
by calling api.runtime.subagent.run with the paused sessionKey, instead of
starting a sibling. The requester is announced once such a follow-up finishes
normally; a follow-up that yields again leaves the run paused and the requester
waiting.
Automatic continuation is specific to the plugin runtime API above. Ordinary
follow-ups through routes not tracked as sub-agent runs neither continue the
paused run nor announce its requester.
Among plugin runtime follow-ups, continuation applies to those that use default
delivery. A follow-up that supplies its own requester or completion-delivery
context is asking for its own audience, so it runs as a separate sibling and
delivers there instead. The paused run stays resumable, and a later default
follow-up still continues it.
When active children exist, OpenClaw injects a compact runtime-generated
Active Subagents prompt block into normal turns so the requester can see
the current child sessions, run ids, statuses, labels, tasks, and
taskName aliases without polling. The task and label fields in that
block are quoted as data, not instructions, because they can originate
from user/model-provided spawn arguments.
Tool: subagents
Lists spawned sub-agent runs and background-task records owned by the
requester session tree. The task rows cover native sub-agents, ACP runs,
Gateway CLI/media work, and cron executions. It is scoped to the current
requester; a child can only see its own controlled children.
Use subagents for on-demand status and debugging. Use sessions_yield to
wait for completion events.
Use action: "cancel" with a taskId returned by action: "list" to stop
a task. Cancellation is confined to the controlled session tree; a leaf
sub-agent cannot cancel work owned by another session.
Thread-bound sessions
When thread bindings are enabled for a channel, a sub-agent can stay bound to a thread so follow-up user messages in that thread keep routing to the same sub-agent session.Thread supporting channels
A channel supports persistent thread-bound subagent sessions (sessions_spawn with thread: true) when it registers a conversation
binding adapter. Bundled channels with that support: Discord,
iMessage, Matrix, and Telegram. Discord and Matrix default to
creating a child thread; Telegram and iMessage default to binding the
current conversation. Use the per-channel threadBindings config keys for
enablement, timeouts, and spawnSessions.
Quick flow
1
Spawn
sessions_spawn with thread: true (and optionally mode: "session").2
Bind
OpenClaw creates or binds a thread to that session target in the active channel.
3
Route follow-ups
Replies and follow-up messages in that thread route to the bound session.
4
Inspect timeouts
Use
/session idle to inspect/update inactivity expiry and
/session max-age to control the hard cap.5
Detach
Use
/session unbind to detach without closing the agent session.Manual controls
Config switches
- Global default:
session.threadBindings.enabled,session.threadBindings.idleHours,session.threadBindings.maxAgeHours. - Channel override and spawn auto-bind keys are adapter-specific. See Thread supporting channels above.
Allowlist
string[]
List of configured agent ids that can be targeted via explicit
agentId (["*"] allows any configured target). Default: only the requester agent. If you set a list and still want the requester to spawn itself with agentId, include the requester id in the list.string[]
Default configured target-agent allowlist used when the requester agent does not set its own
subagents.allowAgents.boolean
default:"false"
Block
sessions_spawn calls that omit agentId (forces explicit profile selection). Per-agent override: agents.entries.*.subagents.requireAgentId.number
default:"120000"
Timeout for gateway
agent announce delivery attempts. Once a handoff is accepted, waiting for the parent session’s turn does not consume this budget; the timer starts again when execution begins. Values are positive integer milliseconds and are clamped to the platform-safe timer maximum. Queue waits and transient retries can make total delivery time longer than one configured timeout.sessions_spawn rejects targets
that would run unsandboxed.
Discovery
Useagents_list to see which agent ids are currently allowed for
sessions_spawn. The response includes each listed agent’s effective
model and embedded runtime metadata so callers can distinguish OpenClaw, Codex
app-server, and other configured native runtimes.
allowAgents entries must point at configured agent ids in agents.entries.*.
["*"] means any configured target agent plus the requester. If an agent config
is deleted but its id remains in allowAgents, sessions_spawn rejects that id
and agents_list omits it. Run openclaw doctor --fix to clean stale
allowlist entries, or add a minimal agents.entries.* entry when the target should
remain spawnable while inheriting defaults.
Auto-archive
- Sub-agent sessions are automatically archived after
agents.defaults.subagents.archiveAfterMinutes(default60). - Archive uses
sessions.deleteand renames the transcript to*.deleted.<timestamp>(same folder). cleanup: "delete"archives immediately after announce (still keeps the transcript via rename).- Auto-archive is best-effort; pending timers are lost if the gateway restarts.
- Configured run timeouts do not auto-archive; they only stop the run. The session remains until auto-archive.
- Auto-archive applies equally to depth-1 and depth-2 sessions.
- Browser cleanup is separate from archive cleanup: tracked browser tabs/processes are best-effort closed when the run finishes, even if the transcript/session record is kept.
subagent_ended plugin hook is best-effort. Hook execution or plugin runtime
loading failures are logged and do not abort sub-agent cleanup.
Nested sub-agents
By default, sub-agents cannot spawn their own sub-agents (maxSpawnDepth: 1). Set maxSpawnDepth: 2 to enable one level of
nesting — the orchestrator pattern: main → orchestrator sub-agent →
worker sub-sub-agents.
Depth levels
Announce chain
Results flow back up the chain:- Depth-2 worker finishes → announces to its parent (depth-1 orchestrator).
- Depth-1 orchestrator receives the announce, synthesizes results, finishes → announces to main.
- Main agent receives the announce and delivers to the user.
Operational guidance: start child work once and wait for completion
events instead of building poll loops around
sessions_list,
sessions_history, /subagents list, or exec sleep commands.
sessions_list and /subagents list keep child-session relationships
focused on live work — live children remain attached, ended children stay
visible for a short recent window, and stale store-only child links are
ignored after their freshness window. This prevents old spawnedBy /
parentSessionKey metadata from resurrecting ghost children after
restart. If a child completion event arrives after you already sent the
final answer, the correct follow-up is the exact silent token
NO_REPLY / no_reply.Tool policy by depth
- A child captures the requester’s effective sender policy when it is spawned. Senderless child runs and authenticated operator resumes keep that snapshot even if
toolsBySenderchanges later; current global, agent, provider, sandbox, and sub-agent restrictions still apply. A new external channel turn targeting the child re-resolves current sender policy instead. - Role and control scope are written into session metadata at spawn time. That keeps flat or restored session keys from accidentally regaining orchestrator privileges.
- Depth 1 (orchestrator, when
maxSpawnDepth >= 2): getssessions_spawn,subagents,sessions_list,sessions_historyso it can spawn children and inspect their status. Other session/system tools remain denied. - Depth 1 (leaf, when
maxSpawnDepth == 1): no session tools (current default behavior). - Depth 2 (leaf worker): no session tools —
sessions_spawnis always denied at depth 2. Cannot spawn further children.
Per-agent spawn limit
Each agent session (at any depth) can have at mostmaxChildrenPerAgent
(default 5) active children at a time. This prevents runaway fan-out
from a single orchestrator.
Cascade stop
Explicit cancellation of a depth-1 orchestrator cascades to its depth-2 children./stop in the main chat applies to that requester’s child tree.
See Stopping for scope and incomplete-cancellation behavior.
Authentication
Sub-agent auth is resolved by agent id, not by session type:- The sub-agent session key is
agent:<agentId>:subagent:<uuid>. - The local auth overlay is loaded from that agent’s
agentDir. - The shared auth profiles are merged in as a fallback; agent profiles override shared profiles on conflicts.
Announce
Sub-agents report back via an announce step:- The announce step runs inside the sub-agent session (not the requester session).
- Runs spawned with
expectsCompletionMessage: falseskip the announce step entirely; the run registry records their delivery as not required. - An exact
ANNOUNCE_SKIPresponse suppresses announce output. - For completion-required runs, an exact child
NO_REPLYresponse or no output is a missing deliverable handed to the requester/parent for visible representation or retry; it is not credited as silent delivery. - Optional, duplicate, already-visible, or otherwise non-required paths may use exact
NO_REPLYfor intentional silence.
- Top-level requester sessions use a follow-up
agentcall with external delivery (deliver=true). - Nested requester subagent sessions receive an internal follow-up injection (
deliver=false) so the orchestrator can synthesize child results in-session. - If a nested requester subagent session is gone, OpenClaw falls back to that session’s requester when available.
Announce context
Announce context is normalized to a stable internal event block:
Terminal failed runs report failure status without replaying captured
reply text. Tool/toolResult output is not promoted into child result text.
Stats line
Announce payloads include a stats line at the end (even when wrapped):- Runtime (e.g.
runtime 5m12s). - Token usage (input/output/total).
- Estimated cost when model pricing is configured (
models.providers.*.models[].cost). sessionKey,sessionId, and transcript path so the main agent can fetch history viasessions_historyor inspect the file on disk.
Why prefer sessions_history
sessions_history is the safer orchestration path for reading a child’s
transcript from within an agent turn:
- Redacts credential/token-like text even when general-purpose log redaction is disabled.
- Truncates long text blocks (4000 chars per block) and drops thinking signatures, reasoning replay payloads, and inline image data.
- Caps returned messages at 80 KB; older rows can be dropped or an oversized row replaced with
[sessions_history omitted: message too large]. - Use
nextOffsetwhen present to page backward through older transcript windows. - Returns structured history rather than
/subagents log’s plain chat lines. Reasoning tags,<relevant-memories>/<relevant_memories>scaffolding, and tool-call XML can remain in message text:sessions_historydoes not apply the log command’s assistant prose sanitizer. See Session tools for the recall guarantees. - Raw on-disk transcript inspection is the fallback when you need the full byte-for-byte transcript.
Tool policy
Sub-agents use the same profile and tool-policy pipeline as the parent or target agent first. After that, OpenClaw applies the sub-agent restriction layer. Sub-agents always losegateway, agents_list, session_status, cron,
message, sessions_send, and the conversations_* tools regardless of
depth or role (system-level/interactive tools, direct delivery surfaces, or
tools the main agent should coordinate). This hard-deny layer is derived from
the persisted sub-agent session envelope on every turn, including resumed and
visible dashboard sessions; ordinary allow/alsoAllow entries cannot override
it. Hidden launches also disable message before tool construction as defense in
depth. Leaf sub-agents (default depth-1 behavior, and always at depth 2)
additionally lose subagents, sessions_list, sessions_history, and
sessions_spawn, so sub-agent communication stays on the announce chain.
sessions_history remains a bounded, redacted recall view here too — it
is neither a raw transcript dump nor a prose-only rendering.
When maxSpawnDepth >= 2, depth-1 orchestrator sub-agents additionally
receive sessions_spawn, subagents, sessions_list, and
sessions_history so they can manage their children.
Override via config
tools.subagents.tools.allow is a final allow-only filter. It can narrow
the already-resolved tool set, but it cannot add back a tool removed
by tools.profile. For example, tools.profile: "coding" includes
web_search/web_fetch but not the browser tool. To let
coding-profile sub-agents use browser automation, add browser at the
profile stage:
agents.entries.*.tools.alsoAllow: ["browser"] when only one
agent should get browser automation.
Concurrency
Sub-agents use a dedicated in-process queue lane:- Lane name:
subagent - Concurrency:
agents.defaults.subagents.maxConcurrent(default8)
Liveness and recovery
OpenClaw does not treatendedAt absence as permanent proof that a
sub-agent is still alive. Unended runs older than the stale-run window
(2 hours, or the configured run timeout plus a short grace period,
whichever is longer) stop counting as active/pending in /subagents list,
status summaries, descendant completion gating, and per-session
concurrency checks.
After a Gateway restart, fresh interrupted sub-agents resume automatically
from their existing child transcript. Recovery handles both sessions marked
abortedLastRun: true and hard kills that prevented the shutdown marker from
being written. For a hard kill, the child session must still identify the exact
running sub-agent from the retired Gateway process, with no newer run or admitted
work owning that session. Stale interrupted runs are finalized without a resume;
other stale unended restored runs are pruned.
An accepted recovery keeps the original task, Task Flow, requester, and child
session identities. The task returns to running as the replacement execution
continues, and the aborted marker is cleared after acceptance. You do not need
to send another prompt to restart the work.
If saving an accepted recovery temporarily fails, the Gateway retries adopting
that same execution into its original task. Cancellation, replacement by a newer
run, or another Gateway restart prevents that adoption.
For sub-agents that announce completion, OpenClaw also attempts a notice to the
original requester: “Resumed your interrupted task after the Gateway restart.”
Failed or suppressed notices are retried without launching another recovery
turn; completion continues through the normal delivery path.
Automatic restart recovery is bounded per child session. If the same
sub-agent child is accepted for orphan recovery repeatedly inside the
rapid re-wedge window, OpenClaw persists a recovery tombstone on that
session and stops auto-resuming it on later restarts. Run
openclaw tasks maintenance --apply to reconcile the task record, or
openclaw doctor --fix to clear stale aborted recovery flags on
tombstoned sessions.
If a sub-agent spawn fails with Gateway
PAIRING_REQUIRED /
scope-upgrade, check the RPC caller before editing pairing state.
Internal sessions_spawn coordination dispatches in process when the
caller is already running inside the gateway request context, so it does
not open a loopback WebSocket or depend on the CLI’s paired-device scope
baseline. Callers outside the gateway process still use the WebSocket
fallback as client.id: "gateway-client" with client.mode: "backend"
over direct loopback shared-token/password auth. Remote callers, explicit
deviceIdentity, explicit device-token paths, and browser/node clients
still need normal device approval for scope upgrades.Stopping
An explicit Stop targeting a parent run cancels the children associated with that run and their descendants, including ordinary sub-agents and Swarm collectors. Successful cancellation keeps selected queued collectors from starting while running children stop. Exact-run cancellation does not cancel unrelated turns or clear unrelated session-wide queues. For Gateway callers,chat.abort with a runId uses this exact-parent scope.
sessions.abort with a runId also targets that run. When it resolves a recovered
native run without a chat controller, it cancels children only if the captured
active parent accepts Stop; a declined or no-active-run result, including an
already-finalizing parent, leaves those children alone.
Sending /stop in the requester chat has broader scope: it aborts requester
session work, clears its queues, and cancels its active child tree. Session-wide
sessions.abort also requests descendant cancellation; clearing queued follow-ups
requires clearQueued: true. Ordinary chat.abort without a runId does not
cascade to children. These operations retain their normal authorization checks.
Incomplete cancellation is reported as an error, not a clean success. /stop
reports actual stopped and failed child counts. Inspect the remaining
background tasks and retry their cancellation;
request acknowledgment does not mean all runtime cleanup is instantaneous.
Accepted children remain independent after ordinary parent completion, yield, or
timeout. Those events do not automatically cancel them.
Limitations
- Direct announce attempts are best-effort, but admitted session-queued completion handoffs and their owner/task projections survive gateway restarts in the shared SQLite state database.
- Sub-agents still share the same gateway process resources; treat
maxConcurrentas a safety valve. sessions_spawnreturns{ status: "accepted", runId, childSessionKey }when startup is accepted, without waiting for the child task to finish. Cloud-worker spawns can wait for provisioning before returning this receipt.- Sub-agent context only injects
AGENTS.md(noSOUL.md,IDENTITY.md,USER.md,MEMORY.md, orBOOTSTRAP.md). Its## Toolssection carries environment-specific notes. Codex-native subagents follow the same boundary through nativeAGENTS.mddiscovery, while parent-only persona, identity, and user files are injected as turn-scoped collaboration instructions so children do not clone them. - Maximum nesting depth is 5 (
maxSpawnDepthrange: 1-5). Depth 2 is recommended for most use cases. maxChildrenPerAgentcaps active children per session (default5, range1-20).