Skip to main content

Session Tools

Goal: small, hard-to-misuse tool set so agents can list sessions, fetch history, and send to another session.

Tool Names

  • sessions_list
  • sessions_history
  • sessions_send
  • sessions_spawn

Key Model

  • Main direct chat bucket is always the literal key "main" (resolved to the current agent’s main key).
  • Group chats use agent:<agentId>:<channel>:group:<id> or agent:<agentId>:<channel>:channel:<id> (pass the full key).
  • Cron jobs use cron:<job.id>.
  • Hooks use hook:<uuid> unless explicitly set.
  • Node sessions use node-<nodeId> unless explicitly set.
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.

sessions_list

List sessions as an array of rows. Parameters:
  • kinds?: string[] filter: any of "main" | "group" | "cron" | "hook" | "node" | "other"
  • limit?: number max rows (default: server default, clamp e.g. 200)
  • activeMinutes?: number only sessions updated within N minutes
  • messageLimit?: number 0 = no messages (default 0); >0 = include last N messages
Behavior:
  • messageLimit > 0 fetches chat.history per session and includes the last N messages.
  • Tool results are filtered out in list output; use sessions_history for tool messages.
  • When running in a sandboxed agent session, session tools default to spawned-only visibility (see below).
Row shape (JSON):
  • key: session key (string)
  • kind: main | group | cron | hook | node | other
  • channel: whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown
  • displayName (group display label if available)
  • updatedAt (ms)
  • sessionId
  • model, contextTokens, totalTokens
  • thinkingLevel, verboseLevel, systemSent, abortedLastRun
  • sendPolicy (session override if set)
  • lastChannel, lastTo
  • deliveryContext (normalized { channel, to, accountId } when available)
  • transcriptPath (best-effort path derived from store dir + sessionId)
  • messages? (only when messageLimit > 0)

sessions_history

Fetch transcript for one session. Parameters:
  • sessionKey (required; accepts session key or sessionId from sessions_list)
  • limit?: number max messages (server clamps)
  • includeTools?: boolean (default false)
Behavior:
  • includeTools=false filters role: "toolResult" messages.
  • Returns messages array in the raw transcript format.
  • When given a sessionId, OpenClaw resolves it to the corresponding session key (missing ids error).

Gateway session history and live transcript APIs

Control UI and gateway clients can use the lower level history and live transcript surfaces directly. HTTP:
  • GET /sessions/{sessionKey}/history
  • Query params: limit, cursor, includeTools=1, follow=1
  • Unknown sessions return HTTP 404 with error.type = "not_found"
  • follow=1 upgrades the response to an SSE stream of transcript updates for that session
WebSocket:
  • sessions.subscribe subscribes to all session lifecycle and transcript events visible to the client
  • sessions.messages.subscribe { key } subscribes only to session.message events for one session
  • sessions.messages.unsubscribe { key } removes that targeted transcript subscription
  • session.message carries appended transcript messages plus live usage metadata when available
  • sessions.changed emits phase: "message" for transcript appends so session lists can refresh counters and previews

sessions_send

Send a message into another session. Parameters:
  • sessionKey (required; accepts session key or sessionId from sessions_list)
  • message (required)
  • timeoutSeconds?: number (default >0; 0 = fire-and-forget)
Behavior:
  • timeoutSeconds = 0: enqueue and return { runId, status: "accepted" }.
  • timeoutSeconds > 0: wait up to N seconds for completion, then return { runId, status: "ok", reply }.
  • If wait times out: { runId, status: "timeout", error }. Run continues; call sessions_history later.
  • If the run fails: { runId, status: "error", error }.
  • Announce delivery runs after the primary run completes and is best-effort; status: "ok" does not guarantee the announce was delivered.
  • Waits via gateway agent.wait (server-side) so reconnects don’t drop the wait.
  • Agent-to-agent message context is injected for the primary run.
  • Inter-session messages are persisted with message.provenance.kind = "inter_session" so transcript readers can distinguish routed agent instructions from external user input.
  • After the primary run completes, OpenClaw runs a reply-back loop:
    • Round 2+ alternates between requester and target agents.
    • Reply exactly REPLY_SKIP to stop the ping‑pong.
    • Max turns is session.agentToAgent.maxPingPongTurns (0–5, default 5).
  • Once the loop ends, OpenClaw runs the agent‑to‑agent announce step (target agent only):
    • Reply exactly ANNOUNCE_SKIP to stay silent.
    • Any other reply is sent to the target channel.
    • Announce step includes the original request + round‑1 reply + latest ping‑pong reply.

Channel Field

  • For groups, channel is the channel recorded on the session entry.
  • For direct chats, channel maps from lastChannel.
  • For cron/hook/node, channel is internal.
  • If missing, channel is unknown.

Security / Send Policy

Policy-based blocking by channel/chat type (not per session id).
{
  "session": {
    "sendPolicy": {
      "rules": [
        {
          "match": { "channel": "discord", "chatType": "group" },
          "action": "deny"
        }
      ],
      "default": "allow"
    }
  }
}
Runtime override (per session entry):
  • sendPolicy: "allow" | "deny" (unset = inherit config)
  • Settable via sessions.patch or owner-only /send on|off|inherit (standalone message).
Enforcement points:
  • chat.send / agent (gateway)
  • auto-reply delivery logic

sessions_spawn

Spawn a sub-agent run in an isolated session and announce the result back to the requester chat channel. Parameters:
  • task (required)
  • label? (optional; used for logs/UI)
  • agentId? (optional; spawn under another agent id if allowed)
  • model? (optional; overrides the sub-agent model; invalid values error)
  • thinking? (optional; overrides thinking level for the sub-agent run)
  • runTimeoutSeconds? (defaults to agents.defaults.subagents.runTimeoutSeconds when set, otherwise 0; when set, aborts the sub-agent run after N seconds)
  • thread? (default false; request thread-bound routing for this spawn when supported by the channel/plugin)
  • mode? (run|session; defaults to run, but defaults to session when thread=true; mode="session" requires thread=true)
  • cleanup? (delete|keep, default keep)
  • sandbox? (inherit|require, default inherit; require rejects spawn unless the target child runtime is sandboxed)
  • attachments? (optional array of inline files; subagent runtime only, ACP rejects). Each entry: { name, content, encoding?: "utf8" | "base64", mimeType? }. Files are materialized into the child workspace at .openclaw/attachments/<uuid>/. Returns a receipt with sha256 per file.
  • attachAs? (optional; { mountPath? } hint reserved for future mount implementations)
Allowlist:
  • agents.list[].subagents.allowAgents: list of agent ids allowed via agentId (["*"] to allow any). Default: only the requester agent.
  • Sandbox inheritance guard: if the requester session is sandboxed, sessions_spawn rejects targets that would run unsandboxed.
Discovery:
  • Use agents_list to discover which agent ids are allowed for sessions_spawn.
Behavior:
  • Starts a new agent:<agentId>:subagent:<uuid> session with deliver: false.
  • Sub-agents default to the full tool set minus session tools (configurable via tools.subagents.tools).
  • Sub-agents are not allowed to call sessions_spawn (no sub-agent → sub-agent spawning).
  • Always non-blocking: returns { status: "accepted", runId, childSessionKey } immediately.
  • With thread=true, channel plugins can bind delivery/routing to a thread target (Discord support is controlled by session.threadBindings.* and channels.discord.threadBindings.*).
  • After completion, OpenClaw runs a sub-agent announce step and posts the result to the requester chat channel.
    • If the assistant final reply is empty, the latest toolResult from sub-agent history is included as Result.
  • Reply exactly ANNOUNCE_SKIP during the announce step to stay silent.
  • Announce replies are normalized to Status/Result/Notes; Status comes from runtime outcome (not model text).
  • Sub-agent sessions are auto-archived after agents.defaults.subagents.archiveAfterMinutes (default: 60).
  • Announce replies include a stats line (runtime, tokens, sessionKey/sessionId, transcript path, and optional cost).

Sandbox Session Visibility

Session tools can be scoped to reduce cross-session access. Default behavior:
  • tools.sessions.visibility defaults to tree (current session + spawned subagent sessions).
  • For sandboxed sessions, agents.defaults.sandbox.sessionToolsVisibility can hard-clamp visibility.
Config:
{
  tools: {
    sessions: {
      // "self" | "tree" | "agent" | "all"
      // default: "tree"
      visibility: "tree",
    },
  },
  agents: {
    defaults: {
      sandbox: {
        // default: "spawned"
        sessionToolsVisibility: "spawned", // or "all"
      },
    },
  },
}
Notes:
  • self: only the current session key.
  • tree: current session + sessions spawned by the current session.
  • agent: any session belonging to the current agent id.
  • all: any session (cross-agent access still requires tools.agentToAgent).
  • When a session is sandboxed and sessionToolsVisibility="spawned", OpenClaw clamps visibility to tree even if you set tools.sessions.visibility="all".