--- summary: "Multi-agent routing: isolated agents, channel accounts, and bindings" title: Multi-Agent Routing read_when: "You want multiple isolated agents (workspaces + auth) in one gateway process." status: active --- # Multi-Agent Routing Goal: multiple *isolated* agents (separate workspace + `agentDir` + sessions), plus multiple channel accounts (e.g. two WhatsApps) in one running Gateway. Inbound is routed to an agent via bindings. ## What is “one agent”? An **agent** is a fully scoped brain with its own: - **Workspace** (files, AGENTS.md/SOUL.md/USER.md, local notes, persona rules). - **State directory** (`agentDir`) for auth profiles, model registry, and per-agent config. - **Session store** (chat history + routing state) under `~/.clawdbot/agents//sessions`. Auth profiles are **per-agent**. Each agent reads from its own: ``` ~/.clawdbot/agents//agent/auth-profiles.json ``` Main agent credentials are **not** shared automatically. Never reuse `agentDir` across agents (it causes auth/session collisions). If you want to share creds, copy `auth-profiles.json` into the other agent's `agentDir`. Skills are per-agent via each workspace’s `skills/` folder, with shared skills available from `~/.clawdbot/skills`. See [Skills: per-agent vs shared](/tools/skills#per-agent-vs-shared-skills). The Gateway can host **one agent** (default) or **many agents** side-by-side. **Workspace note:** each agent’s workspace is the **default cwd**, not a hard sandbox. Relative paths resolve inside the workspace, but absolute paths can reach other host locations unless sandboxing is enabled. See [Sandboxing](/gateway/sandboxing). ## Paths (quick map) - Config: `~/.clawdbot/clawdbot.json` (or `CLAWDBOT_CONFIG_PATH`) - State dir: `~/.clawdbot` (or `CLAWDBOT_STATE_DIR`) - Workspace: `~/clawd` (or `~/clawd-`) - Agent dir: `~/.clawdbot/agents//agent` (or `agents.list[].agentDir`) - Sessions: `~/.clawdbot/agents//sessions` ### Single-agent mode (default) If you do nothing, Clawdbot runs a single agent: - `agentId` defaults to **`main`**. - Sessions are keyed as `agent:main:`. - Workspace defaults to `~/clawd` (or `~/clawd-` when `CLAWDBOT_PROFILE` is set). - State defaults to `~/.clawdbot/agents/main/agent`. ## Agent helper Use the agent wizard to add a new isolated agent: ```bash clawdbot agents add work ``` Then add `bindings` (or let the wizard do it) to route inbound messages. Verify with: ```bash clawdbot agents list --bindings ``` ## Multiple agents = multiple people, multiple personalities With **multiple agents**, each `agentId` becomes a **fully isolated persona**: - **Different phone numbers/accounts** (per channel `accountId`). - **Different personalities** (per-agent workspace files like `AGENTS.md` and `SOUL.md`). - **Separate auth + sessions** (no cross-talk unless explicitly enabled). This lets **multiple people** share one Gateway server while keeping their AI “brains” and data isolated. ## One WhatsApp number, multiple people (DM split) You can route **different WhatsApp DMs** to different agents while staying on **one WhatsApp account**. Match on sender E.164 (like `+15551234567`) with `peer.kind: "dm"`. Replies still come from the same WhatsApp number (no per‑agent sender identity). Important detail: direct chats collapse to the agent’s **main session key**, so true isolation requires **one agent per person**. Example: ```json5 { agents: { list: [ { id: "alex", workspace: "~/clawd-alex" }, { id: "mia", workspace: "~/clawd-mia" } ] }, bindings: [ { agentId: "alex", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230001" } } }, { agentId: "mia", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551230002" } } } ], channels: { whatsapp: { dmPolicy: "allowlist", allowFrom: ["+15551230001", "+15551230002"] } } } ``` Notes: - DM access control is **global per WhatsApp account** (pairing/allowlist), not per agent. - For shared groups, bind the group to one agent or use [Broadcast groups](/broadcast-groups). ## Routing rules (how messages pick an agent) Bindings are **deterministic** and **most-specific wins**: 1. `peer` match (exact DM/group/channel id) 2. `guildId` (Discord) 3. `teamId` (Slack) 4. `accountId` match for a channel 5. channel-level match (`accountId: "*"`) 6. fallback to default agent (`agents.list[].default`, else first list entry, default: `main`) ## Multiple accounts / phone numbers Channels that support **multiple accounts** (e.g. WhatsApp) use `accountId` to identify each login. Each `accountId` can be routed to a different agent, so one server can host multiple phone numbers without mixing sessions. ## Concepts - `agentId`: one “brain” (workspace, per-agent auth, per-agent session store). - `accountId`: one channel account instance (e.g. WhatsApp account `"personal"` vs `"biz"`). - `binding`: routes inbound messages to an `agentId` by `(channel, accountId, peer)` and optionally guild/team ids. - Direct chats collapse to `agent::` (per-agent “main”; `session.mainKey`). ## Example: two WhatsApps → two agents `~/.clawdbot/clawdbot.json` (JSON5): ```js { agents: { list: [ { id: "home", default: true, name: "Home", workspace: "~/clawd-home", agentDir: "~/.clawdbot/agents/home/agent", }, { id: "work", name: "Work", workspace: "~/clawd-work", agentDir: "~/.clawdbot/agents/work/agent", }, ], }, // Deterministic routing: first match wins (most-specific first). bindings: [ { agentId: "home", match: { channel: "whatsapp", accountId: "personal" } }, { agentId: "work", match: { channel: "whatsapp", accountId: "biz" } }, // Optional per-peer override (example: send a specific group to work agent). { agentId: "work", match: { channel: "whatsapp", accountId: "personal", peer: { kind: "group", id: "1203630...@g.us" }, }, }, ], // Off by default: agent-to-agent messaging must be explicitly enabled + allowlisted. tools: { agentToAgent: { enabled: false, allow: ["home", "work"], }, }, channels: { whatsapp: { accounts: { personal: { // Optional override. Default: ~/.clawdbot/credentials/whatsapp/personal // authDir: "~/.clawdbot/credentials/whatsapp/personal", }, biz: { // Optional override. Default: ~/.clawdbot/credentials/whatsapp/biz // authDir: "~/.clawdbot/credentials/whatsapp/biz", }, }, }, }, } ``` ## Example: WhatsApp daily chat + Telegram deep work Split by channel: route WhatsApp to a fast everyday agent and Telegram to an Opus agent. ```json5 { agents: { list: [ { id: "chat", name: "Everyday", workspace: "~/clawd-chat", model: "anthropic/claude-sonnet-4-5" }, { id: "opus", name: "Deep Work", workspace: "~/clawd-opus", model: "anthropic/claude-opus-4-5" } ] }, bindings: [ { agentId: "chat", match: { channel: "whatsapp" } }, { agentId: "opus", match: { channel: "telegram" } } ] } ``` Notes: - If you have multiple accounts for a channel, add `accountId` to the binding (for example `{ channel: "whatsapp", accountId: "personal" }`). - To route a single DM/group to Opus while keeping the rest on chat, add a `match.peer` binding for that peer; peer matches always win over channel-wide rules. ## Example: same channel, one peer to Opus Keep WhatsApp on the fast agent, but route one DM to Opus: ```json5 { agents: { list: [ { id: "chat", name: "Everyday", workspace: "~/clawd-chat", model: "anthropic/claude-sonnet-4-5" }, { id: "opus", name: "Deep Work", workspace: "~/clawd-opus", model: "anthropic/claude-opus-4-5" } ] }, bindings: [ { agentId: "opus", match: { channel: "whatsapp", peer: { kind: "dm", id: "+15551234567" } } }, { agentId: "chat", match: { channel: "whatsapp" } } ] } ``` Peer bindings always win, so keep them above the channel-wide rule. ## Per-Agent Sandbox and Tool Configuration Starting with v2026.1.6, each agent can have its own sandbox and tool restrictions: ```js { agents: { list: [ { id: "personal", workspace: "~/clawd-personal", sandbox: { mode: "off", // No sandbox for personal agent }, // No tool restrictions - all tools available }, { id: "family", workspace: "~/clawd-family", sandbox: { mode: "all", // Always sandboxed scope: "agent", // One container per agent docker: { // Optional one-time setup after container creation setupCommand: "apt-get update && apt-get install -y git curl", }, }, tools: { allow: ["read"], // Only read tool deny: ["exec", "write", "edit", "apply_patch"], // Deny others }, }, ], }, } ``` Note: `setupCommand` lives under `sandbox.docker` and runs once on container creation. Per-agent `sandbox.docker.*` overrides are ignored when the resolved scope is `"shared"`. **Benefits:** - **Security isolation**: Restrict tools for untrusted agents - **Resource control**: Sandbox specific agents while keeping others on host - **Flexible policies**: Different permissions per agent Note: `tools.elevated` is **global** and sender-based; it is not configurable per agent. If you need per-agent boundaries, use `agents.list[].tools` to deny `exec`. For group targeting, use `agents.list[].groupChat.mentionPatterns` so @mentions map cleanly to the intended agent. See [Multi-Agent Sandbox & Tools](/multi-agent-sandbox-tools) for detailed examples.