{ "title": "Session Tools", "content": "Goal: small, hard-to-misuse tool set so agents can list sessions, fetch history, and send to another session.\n\n* `sessions_list`\n* `sessions_history`\n* `sessions_send`\n* `sessions_spawn`\n\n* Main direct chat bucket is always the literal key `\"main\"` (resolved to the current agent’s main key).\n* Group chats use `agent:::group:` or `agent:::channel:` (pass the full key).\n* Cron jobs use `cron:`.\n* Hooks use `hook:` unless explicitly set.\n* Node sessions use `node-` unless explicitly set.\n\n`global` and `unknown` are reserved values and are never listed. If `session.scope = \"global\"`, we alias it to `main` for all tools so callers never see `global`.\n\nList sessions as an array of rows.\n\n* `kinds?: string[]` filter: any of `\"main\" | \"group\" | \"cron\" | \"hook\" | \"node\" | \"other\"`\n* `limit?: number` max rows (default: server default, clamp e.g. 200)\n* `activeMinutes?: number` only sessions updated within N minutes\n* `messageLimit?: number` 0 = no messages (default 0); >0 = include last N messages\n\n* `messageLimit > 0` fetches `chat.history` per session and includes the last N messages.\n* Tool results are filtered out in list output; use `sessions_history` for tool messages.\n* When running in a **sandboxed** agent session, session tools default to **spawned-only visibility** (see below).\n\n* `key`: session key (string)\n* `kind`: `main | group | cron | hook | node | other`\n* `channel`: `whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown`\n* `displayName` (group display label if available)\n* `updatedAt` (ms)\n* `sessionId`\n* `model`, `contextTokens`, `totalTokens`\n* `thinkingLevel`, `verboseLevel`, `systemSent`, `abortedLastRun`\n* `sendPolicy` (session override if set)\n* `lastChannel`, `lastTo`\n* `deliveryContext` (normalized `{ channel, to, accountId }` when available)\n* `transcriptPath` (best-effort path derived from store dir + sessionId)\n* `messages?` (only when `messageLimit > 0`)\n\nFetch transcript for one session.\n\n* `sessionKey` (required; accepts session key or `sessionId` from `sessions_list`)\n* `limit?: number` max messages (server clamps)\n* `includeTools?: boolean` (default false)\n\n* `includeTools=false` filters `role: \"toolResult\"` messages.\n* Returns messages array in the raw transcript format.\n* When given a `sessionId`, OpenClaw resolves it to the corresponding session key (missing ids error).\n\nSend a message into another session.\n\n* `sessionKey` (required; accepts session key or `sessionId` from `sessions_list`)\n* `message` (required)\n* `timeoutSeconds?: number` (default >0; 0 = fire-and-forget)\n\n* `timeoutSeconds = 0`: enqueue and return `{ runId, status: \"accepted\" }`.\n* `timeoutSeconds > 0`: wait up to N seconds for completion, then return `{ runId, status: \"ok\", reply }`.\n* If wait times out: `{ runId, status: \"timeout\", error }`. Run continues; call `sessions_history` later.\n* If the run fails: `{ runId, status: \"error\", error }`.\n* Announce delivery runs after the primary run completes and is best-effort; `status: \"ok\"` does not guarantee the announce was delivered.\n* Waits via gateway `agent.wait` (server-side) so reconnects don't drop the wait.\n* Agent-to-agent message context is injected for the primary run.\n* After the primary run completes, OpenClaw runs a **reply-back loop**:\n * Round 2+ alternates between requester and target agents.\n * Reply exactly `REPLY_SKIP` to stop the ping‑pong.\n * Max turns is `session.agentToAgent.maxPingPongTurns` (0–5, default 5).\n* Once the loop ends, OpenClaw runs the **agent‑to‑agent announce step** (target agent only):\n * Reply exactly `ANNOUNCE_SKIP` to stay silent.\n * Any other reply is sent to the target channel.\n * Announce step includes the original request + round‑1 reply + latest ping‑pong reply.\n\n* For groups, `channel` is the channel recorded on the session entry.\n* For direct chats, `channel` maps from `lastChannel`.\n* For cron/hook/node, `channel` is `internal`.\n* If missing, `channel` is `unknown`.\n\n## Security / Send Policy\n\nPolicy-based blocking by channel/chat type (not per session id).\n\nRuntime override (per session entry):\n\n* `sendPolicy: \"allow\" | \"deny\"` (unset = inherit config)\n* Settable via `sessions.patch` or owner-only `/send on|off|inherit` (standalone message).\n\n* `chat.send` / `agent` (gateway)\n* auto-reply delivery logic\n\nSpawn a sub-agent run in an isolated session and announce the result back to the requester chat channel.\n\n* `task` (required)\n* `label?` (optional; used for logs/UI)\n* `agentId?` (optional; spawn under another agent id if allowed)\n* `model?` (optional; overrides the sub-agent model; invalid values error)\n* `runTimeoutSeconds?` (default 0; when set, aborts the sub-agent run after N seconds)\n* `cleanup?` (`delete|keep`, default `keep`)\n\n* `agents.list[].subagents.allowAgents`: list of agent ids allowed via `agentId` (`[\"*\"]` to allow any). Default: only the requester agent.\n\n* Use `agents_list` to discover which agent ids are allowed for `sessions_spawn`.\n\n* Starts a new `agent::subagent:` session with `deliver: false`.\n* Sub-agents default to the full tool set **minus session tools** (configurable via `tools.subagents.tools`).\n* Sub-agents are not allowed to call `sessions_spawn` (no sub-agent → sub-agent spawning).\n* Always non-blocking: returns `{ status: \"accepted\", runId, childSessionKey }` immediately.\n* After completion, OpenClaw runs a sub-agent **announce step** and posts the result to the requester chat channel.\n* Reply exactly `ANNOUNCE_SKIP` during the announce step to stay silent.\n* Announce replies are normalized to `Status`/`Result`/`Notes`; `Status` comes from runtime outcome (not model text).\n* Sub-agent sessions are auto-archived after `agents.defaults.subagents.archiveAfterMinutes` (default: 60).\n* Announce replies include a stats line (runtime, tokens, sessionKey/sessionId, transcript path, and optional cost).\n\n## Sandbox Session Visibility\n\nSandboxed sessions can use session tools, but by default they only see sessions they spawned via `sessions_spawn`.", "code_samples": [ { "code": "Runtime override (per session entry):\n\n* `sendPolicy: \"allow\" | \"deny\"` (unset = inherit config)\n* Settable via `sessions.patch` or owner-only `/send on|off|inherit` (standalone message).\n\nEnforcement points:\n\n* `chat.send` / `agent` (gateway)\n* auto-reply delivery logic\n\n## sessions\\_spawn\n\nSpawn a sub-agent run in an isolated session and announce the result back to the requester chat channel.\n\nParameters:\n\n* `task` (required)\n* `label?` (optional; used for logs/UI)\n* `agentId?` (optional; spawn under another agent id if allowed)\n* `model?` (optional; overrides the sub-agent model; invalid values error)\n* `runTimeoutSeconds?` (default 0; when set, aborts the sub-agent run after N seconds)\n* `cleanup?` (`delete|keep`, default `keep`)\n\nAllowlist:\n\n* `agents.list[].subagents.allowAgents`: list of agent ids allowed via `agentId` (`[\"*\"]` to allow any). Default: only the requester agent.\n\nDiscovery:\n\n* Use `agents_list` to discover which agent ids are allowed for `sessions_spawn`.\n\nBehavior:\n\n* Starts a new `agent::subagent:` session with `deliver: false`.\n* Sub-agents default to the full tool set **minus session tools** (configurable via `tools.subagents.tools`).\n* Sub-agents are not allowed to call `sessions_spawn` (no sub-agent → sub-agent spawning).\n* Always non-blocking: returns `{ status: \"accepted\", runId, childSessionKey }` immediately.\n* After completion, OpenClaw runs a sub-agent **announce step** and posts the result to the requester chat channel.\n* Reply exactly `ANNOUNCE_SKIP` during the announce step to stay silent.\n* Announce replies are normalized to `Status`/`Result`/`Notes`; `Status` comes from runtime outcome (not model text).\n* Sub-agent sessions are auto-archived after `agents.defaults.subagents.archiveAfterMinutes` (default: 60).\n* Announce replies include a stats line (runtime, tokens, sessionKey/sessionId, transcript path, and optional cost).\n\n## Sandbox Session Visibility\n\nSandboxed sessions can use session tools, but by default they only see sessions they spawned via `sessions_spawn`.\n\nConfig:", "language": "unknown" } ], "headings": [ { "level": "h2", "text": "Tool Names", "id": "tool-names" }, { "level": "h2", "text": "Key Model", "id": "key-model" }, { "level": "h2", "text": "sessions\\_list", "id": "sessions\\_list" }, { "level": "h2", "text": "sessions\\_history", "id": "sessions\\_history" }, { "level": "h2", "text": "sessions\\_send", "id": "sessions\\_send" }, { "level": "h2", "text": "Channel Field", "id": "channel-field" }, { "level": "h2", "text": "Security / Send Policy", "id": "security-/-send-policy" }, { "level": "h2", "text": "sessions\\_spawn", "id": "sessions\\_spawn" }, { "level": "h2", "text": "Sandbox Session Visibility", "id": "sandbox-session-visibility" } ], "url": "llms-txt#session-tools", "links": [] }