Sub-agents: Spawn Isolated Background Agent Runs
Learn how to spawn isolated background agent runs from within an existing run. This page covers parallel execution, sub-agent isolation, tool restrictions, and nesting depth for orchestrator patterns.
Read this when
- You want background or parallel work via the agent
- You are changing sessions_spawn or sub-agent tool policy
- You are implementing or troubleshooting thread-bound subagent sessions
Sub-agents are background agent runs initiated from within an existing agent run.
Each operates in its own session (agent:<agentId>:subagent:<uuid>) and, upon completion, announces its result back to the requester's chat channel.
Every sub-agent run is logged as a background task.
Objectives:
- Run research, lengthy processes, and slow tool operations in parallel without stalling the main run.
- Maintain sub-agent isolation by default (separate sessions, optional sandboxing).
- Reduce misuse of the tool surface: sub-agents are not given session or message tools by default.
- Allow adjustable nesting depth for orchestrator patterns.
Note
Cost note: each sub-agent uses its own context and tokens by default. For heavy or repetitive work, assign a cheaper model to sub-agents and keep your main agent on a higher-quality model using
agents.defaults.subagents.modelor per-agent overrides. When a child genuinely requires the requester's current transcript, start it withcontext: "fork". Thread-bound subagent sessions default tocontext: "fork"because they fork the current conversation into a follow-up thread.
Slash command
/subagents examines sub-agent runs for the current session:
/subagents list
/subagents log <id|#> [limit] [tools]
/subagents info <id|#>
/subagents info displays run metadata (status, timestamps, session id,
transcript path, cleanup). /subagents log shows recent chat turns for a
run; add the tools token to include tool-call/result messages (excluded
by default). Use sessions_history for a limited, safety-filtered recall
view from within an agent turn, or inspect the transcript path on disk for
the complete raw transcript.
In the Control UI, parent sessions with recent child runs show an expandable sidebar row. The nested rows display child status and runtime, and selecting one opens that child's chat while keeping the parent hierarchy intact.
Thread binding controls
These commands work on channels with persistent thread bindings. See Thread supporting channels below.
/focus <subagent-label|session-key|session-id|session-label>
/unfocus
/agents
/session idle <duration|off>
/session max-age <duration|off>
Spawn behavior
Agents launch background sub-agents using the sessions_spawn tool.
Completions arrive as internal parent-session events; the parent/requester
agent decides whether a user-facing update is necessary.
Non-blocking, push-based completion
sessions_spawnis non-blocking; it returns a run id immediately.- When finished, the sub-agent reports back to the parent/requester session.
- Agent turns that need child results should call
sessions_yieldafter spawning required work. That ends the current turn and lets the completion event arrive as the next model-visible message. - 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.
- On completion, OpenClaw best-effort closes tracked browser tabs/processes opened by that sub-agent session before the announce cleanup flow continues.
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 be woken, OpenClaw falls back to a requester-agent handoff with the same completion context instead of dropping the announce.
- A successful parent handoff completes sub-agent delivery even when the parent decides no visible user update is needed.
- 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.
- If direct handoff cannot be used, delivery falls back to queue routing, then to a short exponential-backoff retry of the announce before final give-up.
- 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 resolved route (
lastChannel/lastTo/lastAccountId) so direct delivery still works.
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.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
--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
Native sub-agents start isolated unless the caller explicitly asks to fork the current transcript.
| Mode | When to use it | Behavior |
|---|---|---|
isolated | Fresh research, independent implementation, slow tool work, or anything that can be briefed in the task text | Creates a clean child transcript. This is the default and keeps token use lower. |
fork | Work that depends on the current conversation, prior tool results, or nuanced instructions already present in the requester transcript | Branches the requester transcript into the child session before the child starts. |
Use fork sparingly. It is for context-sensitive delegation, not a
replacement for writing a clear task prompt.
Tool: sessions_spawn
A sub-agent run begins by placing deliver: false on the global subagent lane, then executes an announce step and delivers the announce reply to the requester's chat channel.
Whether this tool is available depends on the caller's effective tool policy. The built-in coding and messaging profiles grant access to sessions_spawn, sessions_yield, and subagents, while minimal does not include them. full permits every tool. To add these tools, use tools.alsoAllow or apply one of the profiles mentioned above for an agent on a custom narrower profile that still needs to delegate tasks.
Channel/group, provider, sandbox, and per-agent allow/deny policies can still block the tool after the profile stage. Use /tools from the same session to verify the effective tool list.
Defaults:
- Model: native sub-agents inherit the caller's model unless you specify
agents.defaults.subagents.modelor per-agentagents.entries.*.subagents.model. ACP runtime spawns use the same configured subagent model when available; otherwise the ACP harness keeps its own default. An explicitsessions_spawn.modelalways takes precedence. - Thinking: native sub-agents inherit the caller's thinking level unless you set
agents.defaults.subagents.thinkingor 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: OpenClaw uses
agents.defaults.subagents.runTimeoutSecondswhen it is set; otherwise it falls back to0meaning no timeout.sessions_spawndoes not accept per-call timeout overrides. - Process lifetime: a detached OpenClaw sub-agent has its own run lifecycle. A background task created inside an external CLI backend differs: it shares the parent CLI subprocess and stops when that parent reaches
agents.defaults.timeoutSeconds. - Task delivery: native sub-agents receive the delegated task in their first visible
[Subagent Task]message. The sub-agent system prompt contains runtime rules and routing context, not a hidden duplicate of the task.
Accepted native sub-agent spawns include the resolved child model metadata in the tool result: 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.
suggest(default): keep the standard prompt nudge to use sub-agents for larger or slower work.prefer: tell the main agent to stay responsive and delegate anything more involved than a direct reply throughsessions_spawn.
Per-agent override: agents.entries.*.subagents.delegationMode.
{
agents: {
defaults: {
subagents: {
delegationMode: "prefer",
maxConcurrent: 4,
},
},
list: [
{
id: "coordinator",
subagents: { delegationMode: "prefer" },
},
],
},
}
Tool parameters
-
task(string, required), The task description for the sub-agent. -
taskName(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 aslastorall. -
label(string), Optional human-readable label. -
agentId(string), Spawn under another configured agent id when allowed bysubagents.allowAgents. -
cwd(string), Optional task working directory for the child run. Native sub-agents still load bootstrap files from the target agent workspace;cwdonly changes where runtime tools and CLI harnesses do the delegated work. -
runtime(subagent" | "acp, default: subagent),acpis only for external ACP harnesses (claude,droid,gemini,opencode, or explicitly requested Codex ACP/acpx) and foragents.entries.*entries whoseruntime.typeisacp. -
resumeSessionId(string), ACP-only. Resumes an existing ACP harness session whenruntime: "acp"; ignored for native sub-agent spawns. -
streamTo(parent), ACP-only. Streams ACP run output to the parent session whenruntime: "acp"; omit for native sub-agent spawns. -
model(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. -
thinking(string), Override thinking level for the sub-agent run. Not available withvisible: true. -
thread(boolean, default: false), When set totrue, thread binding is requested for this sub-agent session's channel. -
mode(run" | "session, default: run), Ifthread: trueis true andmodeis not provided, the default switches tosession. Usingmode: "session"requiresthread: true. When thread binding is not available for the requester's channel, fall back tomode: "run". Withvisible: true, leave outmode; visible sessions are persistent and do not allowmode: "run". -
cleanup(delete" | "keep, default: keep), Setting"delete"archives the session right after announce (the transcript is still preserved through a rename). -
sandbox(inherit" | "require, default: inherit), Whenrequireis used, the spawn is refused unless the target child runtime runs inside a sandbox. -
context(isolated" | "fork, default: isolated),forkcopies the requester's current transcript into the child session. This works only for native sub-agents. Thread-bound spawns default tofork, while non-thread spawns default toisolated. A visible fork must point to the same agent as the requester. -
visible(boolean, default: false), Produces a persistent dashboard session the user can open in the Control UI. Visible spawns only supportruntime: "subagent"and always retain the created session. -
worktree(boolean, default: false), Sets up a managed git worktree for the new dashboard session. This requiresvisible: true. -
worktreeName(string), An optional name for the managed worktree. Needsvisible: trueandworktree: true. -
worktreeBaseRef(string), An optional git base reference for the managed worktree. Needsvisible: trueandworktree: true.
Warning
sessions_spawndoes not accept channel delivery parameters (target,channel,to,threadId,replyTo,transport). Native sub-agents send their most recent assistant turn back to the requester; external delivery remains with the parent or requester agent.
With visible: true, model, cwd, and a same-agent context: "fork" are supported. A sandboxed target restricts cwd to that agent's workspace. Thread binding, mode, thinking overrides, lightContext, attachments, and attachAs are not available on this path because visible sessions are persistent dashboard sessions created through sessions.create. Visible spawning is rejected if the requester was itself spawned with an inherited tool allowlist or denylist; that restriction is fixed at spawn time and cannot be overridden by configuration. Session listing and addressing follow tools.sessions.visibility; the default tree scope covers the current session and its own spawn subtree. See Managed worktrees for checkout naming, setup, cleanup, and restore behavior.
Task names and targeting
taskName is a model-facing handle for orchestration, not a session identifier.
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 or 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 values last and all are not acceptable as taskName targets because each one already carries a control function.
Tool: sessions_yield
Terminates the current model turn and pauses for runtime events, mainly sub-agent completion notifications, which arrive as the next message. Call this after launching necessary child work when the caller cannot deliver a final response until those completions are received.
sessions_yield serves as the waiting mechanism. Avoid replacing it with polling routines that check subagents, sessions_list, sessions_history, shell sleep, or process status just to find out when a child finishes.
Only invoke sessions_yield when the active tool set for the session includes it. Certain minimal or custom tool configurations might provide sessions_spawn and subagents but omit sessions_yield; in that situation, do not create a polling loop just to wait for completion.
When child sessions are active, OpenClaw inserts a compact runtime-generated Active Subagents prompt block into normal turns, letting the requester view the current child sessions, run ids, statuses, labels, tasks, and taskName aliases without polling. The task and label fields in that block appear as quoted data rather than instructions, because they may come from user or model-provided spawn arguments.
Tool: subagents
Displays 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 or media work, and cron executions. The scope is limited to the current requester; a child can only see its own controlled children.
Use subagents for on-demand status checks and debugging. Use sessions_yield to wait for completion events.
Use action: "cancel" together with a taskId returned by action: "list" to halt a task. Cancellation is limited to the controlled session tree; a leaf sub-agent cannot cancel work owned by another session.
Thread-bound sessions
When thread bindings are active for a channel, a sub-agent can remain attached to a thread so that subsequent user messages in that thread continue 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 has a conversation binding adapter registered. Bundled channels with this support include 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 configuration keys for enablement, timeouts, and spawnSessions.
Quick flow
Spawn
sessions_spawn with thread: true (and optionally mode: "session").
Bind
OpenClaw creates or binds a thread to that session target in the active channel.
Route follow-ups
Replies and follow-up messages in that thread route to the bound session.
Inspect timeouts
Use /session idle to inspect or update inactivity auto-unfocus and /session max-age to control the hard cap.
Detach
Use /unfocus to detach manually.
Manual controls
| Command | Effect |
|---|---|
/focus <target> | Bind the current thread (or create one) to a sub-agent or session target |
/unfocus | Remove the binding for the current bound thread |
/agents | List active runs and binding state (binding:<id>, unbound, or bindings unavailable) |
/session idle | Inspect or update idle auto-unfocus (focused bound threads only) |
/session max-age | Inspect or update hard cap (focused bound threads only) |
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.
See Configuration reference and Slash commands for current adapter details.
Allowlist
-
agents.entries.*.subagents.allowAgents(string[]), List of configured agent ids that can be targeted via explicitagentId(["*"]allows any configured target). Default: only the requester agent. If you set a list and still want the requester to spawn itself withagentId, include the requester id in the list. -
agents.defaults.subagents.allowAgents(string[]), The default allowlist for configured target agents, applied when the requesting agent does not specify its ownsubagents.allowAgents. -
agents.defaults.subagents.requireAgentId(boolean, default: false), Preventssessions_spawncalls that lackagentId(requiring explicit profile selection). Can be overridden per agent withagents.entries.*.subagents.requireAgentId. -
agents.defaults.subagents.announceTimeoutMs(number, default: 120000), The timeout in milliseconds for each gatewayagentannounce delivery attempt. Values must be positive integers and are capped at the platform's safe timer limit. Because transient retries may occur, the total time for announce delivery can exceed a single configured timeout.
When the requester session runs in a sandbox, sessions_spawn refuses targets that would execute without one.
Discovery
Run agents_list to list the agent IDs currently permitted for sessions_spawn. The response includes the effective model and embedded runtime metadata for each listed agent, allowing callers to differentiate OpenClaw, Codex app-server, and other configured native runtimes.
Every allowAgents entry must reference a configured agent ID from agents.entries.*. The value ["*"] covers all configured target agents plus the requester itself. If an agent configuration is removed but its ID remains in allowAgents, sessions_spawn rejects that ID and agents_list omits it. Use openclaw doctor --fix to remove stale allowlist entries, or add a minimal agents.entries.* entry if the target should remain spawnable with inherited defaults.
Auto-archive
- Sub-agent sessions are archived automatically after
agents.defaults.subagents.archiveAfterMinutes(default60). - Archiving uses
sessions.deleteand renames the transcript to*.deleted.<timestamp>(keeping it in the same folder). cleanup: "delete"archives immediately after announce (the transcript is still preserved through the rename).- Auto-archive runs on a best-effort basis; pending timers are lost if the gateway restarts.
- Configured run timeouts do not trigger auto-archive; they only stop the run. The session persists until auto-archive occurs.
- Auto-archive applies to both depth-1 and depth-2 sessions.
- Browser cleanup operates independently of archive cleanup: tracked browser tabs and processes are closed on a best-effort basis when the run ends, even if the transcript or session record is retained.
Nested sub-agents
By default, sub-agents cannot create their own sub-agents (maxSpawnDepth: 1). To allow one level of nesting, set maxSpawnDepth: 2, this enables the orchestrator pattern: main → orchestrator sub-agent → worker sub-sub-agents.
{
agents: {
defaults: {
subagents: {
maxSpawnDepth: 2, // allow sub-agents to spawn children (default: 1, range 1-5)
maxChildrenPerAgent: 5, // max active children per agent session (default: 5, range 1-20)
maxConcurrent: 8, // global concurrency lane cap (default: 8)
runTimeoutSeconds: 900, // default timeout for sessions_spawn (0 = no timeout)
announceTimeoutMs: 120000, // per-call gateway announce timeout
},
},
},
}
Depth levels
| Depth | Session key shape | Role | Can spawn? |
|---|---|---|---|
| 0 | agent:<id>:main | Main agent | Always |
| 1 | agent:<id>:subagent:<uuid> | Sub-agent (orchestrator when depth 2 allowed) | Only if maxSpawnDepth >= 2 |
| 2 | agent:<id>:subagent:<uuid>:subagent:<uuid> | Sub-sub-agent (leaf worker) | Never |
Announce chain
Results propagate upward through the chain:
- A depth-2 worker completes and announces to its parent (the depth-1 orchestrator).
- The depth-1 orchestrator receives the announce, processes the results, finishes, and announces to the main agent.
- The main agent receives the announce and presents it to the user.
Each level only sees announces coming from its immediate children.
Note
Operational guidance: initiate child work once and listen for completion events rather than building polling loops around
sessions_list,sessions_history,/subagents list, orexecsleep commands.sessions_listand/subagents listkeep child-session relationships focused on active work, live children remain attached, ended children stay visible for a short recent period, and stale store-only child links are ignored once their freshness window expires. This prevents oldspawnedBy/parentSessionKeymetadata from reviving ghost children after a restart. If a child completion event arrives after you have already sent the final answer, the correct response is the exact silent tokenNO_REPLY/no_reply.
Tool policy by depth
- When a child is spawned, it captures the requester's effective sender policy. 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 the current sender policy instead. - Role and control scope are recorded in session metadata at spawn time. This prevents flat or restored session keys from accidentally regaining orchestrator privileges.
- Depth 1 (orchestrator, when
maxSpawnDepth >= 2): receivessessions_spawn,subagents,sessions_list,sessions_historyso it can spawn children and check their status. Other session and system tools remain unavailable. - Depth 1 (leaf, when
maxSpawnDepth == 1): no session tools (this is the 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 no more than maxChildrenPerAgent (default 5) active children at once. This prevents excessive fan-out from a single orchestrator.
Cascade stop
Stopping a depth-1 orchestrator also stops all its depth-2 children:
/stopin the main chat stops all depth-1 agents and cascades the stop to their depth-2 children.
Authentication
Sub-agent authentication is determined by agent id, not by the session type:
- The sub-agent's session key is
agent:<agentId>:subagent:<uuid>. - The authentication store is loaded from that agent's
agentDir. - The primary agent's authentication profiles are included as a fallback; when conflicts arise, agent profiles take precedence over main profiles.
The merge process is additive, meaning main profiles are always accessible as fallbacks. Fully separate authentication for each agent is not currently supported.
Announce
Sub-agents communicate results through an announcement step:
- The announcement step executes within the sub-agent's session, not the requester's session.
- If the sub-agent responds with exactly
ANNOUNCE_SKIP, no post is made. - When the latest assistant text matches the silent token
NO_REPLY/no_reply, announcement output is suppressed even if earlier visible progress was made.
Delivery behavior depends on the requester's depth:
- Top-level requester sessions use a subsequent
agentcall with external delivery (deliver=true). - Nested requester sub-agent sessions receive an internal follow-up injection (
deliver=false), allowing the orchestrator to combine child results within the session. - If a nested requester sub-agent session no longer exists, OpenClaw falls back to that session's requester when one is available.
For top-level requester sessions, completion-mode direct delivery first resolves any bound conversation or thread route and hook override, then fills in missing channel-target fields from the requester session's stored route. This ensures completions land on the correct chat or topic even when the completion origin only identifies the channel.
When building nested completion findings, child completion aggregation is scoped to the current requester run, preventing stale child outputs from previous runs leaking into the current announcement. Announce replies maintain thread or topic routing when available on channel adapters.
Announce context
Announce context is normalized into a stable internal event block:
| Field | Source |
|---|---|
| Source | subagent or cron |
| Session ids | Child session key/id |
| Type | Announce type + task label |
| Status | Derived from runtime outcome (ok, error, timeout, or unknown), not inferred from model text |
| Result content | Latest visible assistant text from the child |
| Follow-up | Instruction describing when to reply vs stay silent |
Terminal failed runs report a failure status without replaying captured reply text. Tool and toolResult output is not promoted into child result text.
Stats line
Announce payloads include a stats line at the end, even when wrapped:
- Runtime (for example,
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.
Internal metadata is intended only for orchestration; user-facing replies should be rewritten in a normal assistant voice.
Why prefer sessions_history
sessions_history is the safer orchestration path for reading a child's
transcript during an agent turn:
- Redacts credential or token-like text even when general-purpose log redaction is disabled.
- Truncates long text blocks (4000 characters per block) and removes thinking signatures, reasoning replay payloads, and inline image data.
- Enforces an 80 KB response cap; oversized rows are replaced with
[sessions_history omitted: message too large]. - Use
nextOffsetwhen present to page backward through older transcript windows. sessions_historydoes not strip reasoning tags,<relevant-memories>scaffolding, or tool-call XML from message text, it returns structured content blocks close to the raw transcript shape, just redacted and size-bounded./subagents logapplies the heavier prose sanitizer (strips reasoning tags, memory scaffolding, and tool-call XML) because it renders plain chat lines instead of structured blocks.- Raw on-disk transcript inspection is the fallback when you need the full byte-for-byte transcript.
Tool policy
Sub-agents first use the same profile and tool-policy pipeline as the parent or target agent. After that, OpenClaw applies the sub-agent restriction layer.
Sub-agents always lose gateway, agents_list, session_status, and
cron regardless of depth or role (system-level or interactive tools, or
tools the main agent should coordinate). Leaf sub-agents (default depth-1
behavior, and always at depth 2) additionally lose subagents,
sessions_list, sessions_history, and sessions_spawn. Sub-agents never
get the message tool, it is disabled at spawn time, not filtered by
this deny list, and sessions_send stays denied so sub-agents
communicate only through the announce chain.
sessions_history remains a bounded, sanitized recall view here too, it
is not a raw transcript dump.
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
{
agents: {
defaults: {
subagents: {
maxConcurrent: 1,
},
},
},
tools: {
subagents: {
tools: {
// deny wins
deny: ["gateway", "cron"],
// if allow is set, it becomes allow-only (deny still wins)
// allow: ["read", "exec", "process"]
},
},
},
}
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:
{
tools: {
profile: "coding",
alsoAllow: ["browser"],
},
}
Use per-agent 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 treat the absence of endedAt as definitive evidence that a sub-agent remains active. Runs that never finished and exceed the stale-run window (2 hours, or the configured run timeout plus a short grace period, whichever is larger) no longer count as active or pending in /subagents list, status summaries, descendant completion gating, or per-session concurrency checks.
When a gateway restarts, stale unended restored runs are removed unless their child session carries the abortedLastRun: true marker. Runs that were aborted by the restart remain in the sub-agent orphan recovery flow: stale runs are terminated without a resume, while fresh child sessions receive a synthetic resume message before the aborted marker is cleared.
Automatic restart recovery has a limit per child session. If the same sub-agent child is accepted for orphan recovery repeatedly within the rapid re-wedge window, OpenClaw writes a recovery tombstone on that session and stops automatically resuming it on later restarts. Use openclaw tasks maintenance --apply to reconcile the task record, or openclaw doctor --fix to clear stale aborted recovery flags on tombstoned sessions.
Note
When a sub-agent spawn fails with Gateway
PAIRING_REQUIRED/scope-upgrade, inspect the RPC caller before modifying pairing state. Internalsessions_spawncoordination dispatches in process if 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 asclient.id: "gateway-client"withclient.mode: "backend"over direct loopback shared-token/password auth. Remote callers, explicitdeviceIdentity, explicit device-token paths, and browser/node clients still need normal device approval for scope upgrades.
Stopping
- Sending
/stopin the requester chat ends the requester session and halts any active sub-agent runs spawned from it, cascading to nested children.
Limitations
- Sub-agent announce is best-effort. When the gateway restarts, pending "announce back" work is lost.
- Sub-agents still share the same gateway process resources; treat
maxConcurrentas a safety valve. sessions_spawnis always non-blocking: it returns{ status: "accepted", runId, childSessionKey }immediately.- Sub-agent context only injects
AGENTS.mdandTOOLS.md(noSOUL.md,IDENTITY.md,USER.md,MEMORY.md,HEARTBEAT.md, orBOOTSTRAP.md). Codex-native subagents follow the same boundary:TOOLS.mdstays in inherited Codex thread instructions, 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).