UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

118 lines (95 loc) • 6.69 kB
--- summary: "Bind a sub-agent to a channel thread, and the allowlist, discovery, and auto-archive rules" title: "Thread-bound sub-agent sessions" read_when: - You are implementing or troubleshooting thread-bound subagent sessions - You need the per-agent spawn allowlist or agents_list discovery rules - You need to know when a sub-agent session is archived --- ## Thread-bound sessions When thread bindings are enabled for a channel, a spawned sub-agent can get its own new thread. Follow-up user messages in that thread keep routing to the same sub-agent session, while the conversation you spawned it from stays with your agent. ### Thread supporting channels `sessions_spawn` with `thread: true` always opens a new child thread; it never hands the current conversation to the worker. Bundled channels that can open one: **Discord** and **Matrix**. On channels that would bind the current conversation instead (for example Telegram, iMessage, Feishu, and LINE), `thread: true` is rejected; spawn with `mode: "run"` and the result is announced back to the conversation. Use the per-channel `threadBindings` config keys for enablement, timeouts, and `spawnSessions`. Worker bindings created by older versions on the current conversation are ignored: messages there route to your agent again, and the stale binding expires through its normal idle timeout. To hand a conversation to an ACP session deliberately, use `/acp spawn --bind here`. ### Quick flow <Steps> <Step title="Spawn"> `sessions_spawn` with `thread: true` (and optionally `mode: "session"`). </Step> <Step title="Bind"> OpenClaw opens a new child thread in the active channel and binds it to that session. </Step> <Step title="Route follow-ups"> Replies and follow-up messages in that thread route to the bound session. </Step> <Step title="Inspect timeouts"> Use `/session idle` to inspect/update inactivity expiry and `/session max-age` to control the hard cap. </Step> <Step title="Detach"> Use `/session unbind` to detach without closing the agent session. </Step> </Steps> ### Manual controls | Command | Effect | | ------------------ | ----------------------------------------------------------------------------------------- | | `/session unbind` | Remove the current conversation binding without closing the agent session | | `/agents` | List active runs and binding state (`binding:<id>`, `unbound`, or `bindings unavailable`) | | `/session idle` | Inspect/update inactivity expiry for the current binding | | `/session max-age` | Inspect/update the maximum age of the current binding | ### 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](#thread-supporting-channels) above. See [Configuration reference](/gateway/configuration-reference) and [Slash commands](/tools/slash-commands) for current adapter details. ### Allowlist <ParamField path="agents.entries.*.subagents.allowAgents" type="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. </ParamField> <ParamField path="agents.defaults.subagents.allowAgents" type="string[]"> Default configured target-agent allowlist used when the requester agent does not set its own `subagents.allowAgents`. </ParamField> <ParamField path="agents.defaults.subagents.requireAgentId" type="boolean" default="false"> Block `sessions_spawn` calls that omit `agentId` (forces explicit profile selection). Per-agent override: `agents.entries.*.subagents.requireAgentId`. </ParamField> <ParamField path="agents.defaults.subagents.announceTimeoutMs" type="number" default="120000"> Timeout for gateway `agent` announcement handoff attempts. Once a handoff is accepted, waiting for the parent session's turn does not consume this budget. After execution starts, the requester's normal [runtime timeout and cancellation controls](/concepts/agent-loop#timeouts) apply; the announcement timer does not restart. Values are positive integer milliseconds and are clamped to the platform-safe timer maximum. Queue waits, requester execution, and transient retries can make total delivery time longer than one configured timeout. </ParamField> If the requester session is sandboxed, `sessions_spawn` rejects targets that would run unsandboxed. ### Discovery Use `agents_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` (default `60`). - Archive uses `sessions.delete` and 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 at every sub-agent depth. - 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. If a newer run takes over the same session, the older run stops claiming tabs for cleanup. Cleanup already admitted for a tab still settles against that tab's captured ownership; it does not remove a later registration. The `subagent_ended` plugin hook is best-effort. Hook execution or plugin runtime loading failures are logged and do not abort sub-agent cleanup.