# Configuration — cross-agent, session, and subagent tools

> tools.agentToAgent, tools.sessions visibility, sessionsspawn attachments, and subagent defaults

- 网址：https://funcoding.ai/agents/openclaw/gateway/config-tools/sessions-and-subagents/
- 来源：OpenClaw 官方文档原文（英文），MIT 许可，同步于 2026-10-11
- 官方原文：https://docs.openclaw.ai/zh-CN/gateway/config-tools/sessions-and-subagents

---
How far the session and subagent tools reach: which agents may call each other, which sessions they can target, and the defaults applied to spawned sub-agents.

## `tools.agentToAgent`

```json5
{
  tools: {
    agentToAgent: {
      allow: ["home", "work"],
    },
  },
}
```

Cross-agent access is on by default. `enabled` (default `true`) gates cross-agent session tool calls: `sessions_send` to another agent, and cross-agent `sessions_list`, `sessions_history`, `sessions_search`, and status reads under the default `tools.sessions.visibility: "all"`. Set `enabled: false` to turn cross-agent access off. Same-agent access never consults this policy. Requester-owned native subagent and ACP child sessions are the one exception: under `tree` or `all` visibility they stay reachable across agent boundaries before this policy is consulted, including with `enabled: false`.

`allow` lists the agent ids or `*` patterns that may take part in a cross-agent call. Both the requesting agent and the target agent must match an entry. Exact ids are case-sensitive; wildcard patterns are case-insensitive.

<div class="callout callout-note">

An omitted or empty `allow` counts as unset: with agent-to-agent access enabled by default, every agent can reach every other agent. List every participating agent, requester and target alike, to restrict cross-agent access, as in the example above. A list containing only blank entries denies all cross-agent calls. Deleting an agent (`openclaw agents delete`) prunes its id from `allow`; if that empties the list, the policy falls back to allow-all, so re-check `allow` after removing agents.

</div>

### Per-agent send-only access

Use `agents.entries.<agentId>.tools.agentToAgent.send` to let one agent send
requests to selected agents without widening session visibility. It does not
remove existing read access: keep `tools.sessions.visibility` below `all` when
you want send-only access. For example, let `specialist` ask `ops` for help
without granting direct cross-agent history access:

```json5
{
  tools: {
    agentToAgent: { enabled: true, allow: ["specialist", "ops"] },
    sessions: { visibility: "agent" },
  },
  agents: {
    ownership: "explicit",
    entries: {
      specialist: {
        tools: { agentToAgent: { send: ["ops"] } },
      },
      ops: {
        tools: { agentToAgent: { send: [] } },
      },
    },
  },
}
```

`specialist` can use `sessions_send` to reach `ops` and receive the reply to
that sent turn, even though `ops` has `send: []`. `ops` cannot initiate ordinary
cross-agent sends. Neither agent gains direct access to the other agent's history.
The `sessions_send` tool must still be enabled by the caller's effective tool policy.
The specialist can address the configured agent directly; it does not need to
list the ops agent's sessions first. Call `sessions_send` with:

```json
{ "agentId": "ops", "message": "Please check the service status." }
```

`send` is an optional string array:

- **Omitted `send`:** preserves the global `tools.agentToAgent` policy and
  `tools.sessions.visibility` checks for sends.
- **`send: []`:** denies ordinary cross-agent sends from that agent, even under
  `visibility: "all"`. Unlike the global `allow`, an empty `send` is not allow-all.
- **Target IDs or `*` patterns:** only matching target agents can receive ordinary
  cross-agent sends from that requester. A match permits `sessions_send` even
  under `self`, `tree`, or `agent` visibility; it does not widen other tools.
  Use `["*"]` for any target agent. Deleting an agent prunes its exact ID from
  these lists; an emptied list remains `[]` (deny), and wildcard rules remain.
- **Global policy still applies:** `enabled: false` or an `allow` list excluding
  either requester or target denies the send. This is an outgoing policy, not a
  per-target receive policy.
- **Existing boundaries remain:** same-agent sends still obey visibility.
  Requester-owned native/ACP child access is unchanged. Incognito restrictions
  and the sandbox spawned-session clamp still apply, including to sandboxed
  subagents; `send` cannot reach outside that clamp.
- **Changes before delivery:** the Gateway rechecks the current send policy after
  asynchronous preparation and immediately before accepting the target input.
  Withdrawing a destination blocks sends that have not been accepted; it does
  not retract already accepted input or its owed reply.
- **Replies, not history:** the reply belongs to the authorized sent turn.
  `send` does not grant list, history, search, status, or session-control access.
  `watch: true` additionally requires normal status visibility; send-only access
  cannot subscribe to otherwise hidden session state.

<div class="callout callout-warning">

Send-only access is permission to ask the target agent to act. It does not prevent
a privileged target from disclosing data in its reply or executing an untrusted
request. Keep target tools and instructions appropriate for incoming requests;
use separate Gateways for strong isolation. See [Security trust model](https://funcoding.ai/agents/openclaw/gateway/security/trust-model/).

</div>

## `tools.sessions`

Controls which sessions can be targeted by the session tools (`sessions_list`, `sessions_history`, `sessions_search`, `sessions_send`, `session_status`).

Default: `all` (every session on the Gateway, including other agents' and other
users' transcripts). Cross-agent access is governed by `tools.agentToAgent` and
is on by default. Use `agent`, `tree`, or `self` to narrow visibility.

```json5
{
  tools: {
    sessions: {
      // "self" | "tree" | "agent" | "all"
      visibility: "all",
    },
  },
}
```

<details>
<summary>Visibility scopes</summary>

- `self`: only the current session key.
- `tree`: current session + sessions spawned by the current session (subagents). When the caller is the canonical main session, it includes every same-agent session for list, history, search, send, and status.
- `agent`: any session belonging to the current agent id (can include other users if you run per-sender sessions under the same agent id).
- `all`: any session. Cross-agent targeting is governed by `tools.agentToAgent`, which is on by default.
- `self` has no main-session visibility exception. Incognito denial remains absolute. Narrowing visibility to `agent`, `tree`, or `self` blocks ordinary cross-agent access unless a per-agent `tools.agentToAgent.send` rule permits a send-only exception. `tree` also permits owned native/ACP children across agent boundaries. `agent` does not include that child exception, so keep explicit `tree` if your workflow relies on it.
- Sandbox clamp: when the current session is sandboxed and `agents.defaults.sandbox.sessionToolsVisibility="spawned"` (the default), access stays limited to spawned sessions even if the caller is main or `tools.sessions.visibility="all"`.
- When not `all`, `sessions_list` includes a compact `visibility` field
  describing the effective mode and a warning that some sessions may be
  omitted outside the current scope.

</details>

Ambient group watches still queue activity notices and tell the main session
where something happened. They do not grant access. The default `all` scope
already covers sessions across agents, including conversations with other users.
A per-peer `session.dmScope` separates DM context but does not restrict session
tools. For narrower access, explicitly choose `agent`, `tree`, or `self`, or
restrict agent pairs with `tools.agentToAgent.allow`. Set
`tools.agentToAgent.enabled: false` to block ordinary cross-agent access; requester-owned native subagent and ACP child sessions stay reachable under `tree` or `all`. `tree` retains the
canonical main-session exception; `self` restricts even main to its current session.

## `tools.sessions_spawn`

Controls inline attachment support for `sessions_spawn`.

```json5
{
  tools: {
    sessions_spawn: {
      attachments: {
        enabled: false, // opt-in: set true to allow inline file attachments
        maxTotalBytes: 5242880, // 5 MB total across all files
        maxFiles: 50,
        maxFileBytes: 1048576, // 1 MB per file
        retainOnSessionKeep: false, // keep attachments when cleanup="keep"
      },
    },
  },
}
```

<details>
<summary>Attachment notes</summary>

- Attachments require `enabled: true`.
- Subagent attachments are staged in Gateway-owned state with a `.manifest.json`; they are never written through the child workspace.
- Sandboxed children receive only their session-owned attachments read-only at `/openclaw/attachments/<uuid>/`. Attachment-bearing agent-scoped sessions use a dedicated runtime so sibling guests cannot inherit the projection. Shared-scope sandboxes and backends without read-only resource projection reject attachment-bearing spawns before staging.
- Unsandboxed children receive the absolute Gateway-owned path and can read it through workspace-scoped file/media tools.
- ACP attachments are image-only and forwarded inline to the ACP runtime after the same file count, per-file byte, and total byte limits pass.
- Attachment content is automatically redacted from transcript persistence.
- Base64 inputs are validated with strict alphabet/padding checks and a pre-decode size guard.
- Subagent attachment file permissions are `0700` for directories and `0600` for files.
- Subagent cleanup follows the `cleanup` policy: `delete` always removes attachments; `keep` retains them only when `retainOnSessionKeep: true`.
- Upgrading from a pre-`readOnlyResourceMounts` release: previously staged attachments remain in their child workspaces at `.openclaw/attachments/<uuid>/`. Their registry records retire without deleting or traversing those files, so remove leftovers with your normal workspace cleanup. New spawns stage in Gateway-owned state; the child prompt path is the only usable filesystem location. The attachment receipt's `relDir` is a retained identifier, not a usable location, and must not be resolved.

</details>

## `agents.defaults.subagents`

```json5
{
  agents: {
    defaults: {
      subagents: {
        allowAgents: ["research"],
        model: "minimax/MiniMax-M2.7",
        maxConcurrent: 8,
        runTimeoutSeconds: 900,
        announceTimeoutMs: 120000,
        archiveAfterMinutes: 60,
      },
    },
  },
}
```

- `model`: default model for spawned sub-agents. If omitted, sub-agents inherit the caller's model.
- `allowAgents`: default allowlist of configured target agent ids for `sessions_spawn` when the requester agent does not set its own `subagents.allowAgents` (`["*"]` = any configured target; default: same agent only). Stale entries whose agent config was deleted are rejected by `sessions_spawn` and omitted from `agents_list`; run `openclaw doctor --fix` to clean them up.
- `maxConcurrent`: max concurrent ordinary sub-agent runs per immediate spawning/controller session. Default: `8`; independent sessions do not share this budget. Swarm collector children use `tools.swarm.maxConcurrent` instead. Codex-native subagents use Codex's separate scheduler and limits.
- `maxChildrenPerAgent`: separate admission limit on active children per session. Default: `5`; raising `maxConcurrent` does not raise this limit.
- `runTimeoutSeconds`: timeout (seconds) for `sessions_spawn` when the caller does not pass its own override. Default: `0` (no timeout); the `900` shown above is a common opt-in value, not the built-in default.
- `announceTimeoutMs`: per-call timeout (milliseconds) for gateway `agent` announce delivery attempts. Default: `120000`. Transient retries can make the total announce wait longer than one configured timeout.
- `archiveAfterMinutes`: minutes after a sub-agent session completes before it is auto-archived. Default: `60`; `0` disables auto-archive.
- Per-subagent tool policy: `tools.subagents.tools.allow` / `tools.subagents.tools.deny`.

## `tools.swarm`

Swarm is enabled by default. Collector children (`collect: true`) run in a
dedicated `subagent:swarm:<schedulerGroupKey>` lane, with the group's resolved
`maxConcurrent` cap. They leave the parent's ordinary sub-agent lane available.
Ordinary children spawned by a collector use that collector's own session lane.

```json5
{
  tools: {
    swarm: {
      maxConcurrent: 32,
      maxChildrenPerGroup: 50,
      maxTotalPerGroup: 200,
    },
  },
}
```

`maxConcurrent` defaults to `32` and accepts integers from `1` to `1000`.
Each running child costs one model stream and one Code Mode worker isolate.
The separate `maxChildrenPerGroup` and `maxTotalPerGroup` admission limits
remain `50` live children and `200` lifetime spawns per group by default.
See [Swarm configuration](https://funcoding.ai/agents/openclaw/tools/swarm/#enable-swarm) for all settings and
per-agent overrides.
