Cross-Agent Collaboration: How Concurrent Claude Sessions Talk

Table of Contents

1. The setting: many agents, one checkout

Several Claude Code sessions run at once — on this box and on the nexus box — against one git checkout, on main, with no worktrees. That arrangement makes coordination a live problem, not a diagram. Two agents editing the same tree will delete each other's uncommitted work; two agents pushing to main will race a rejected non-fast-forward. The question this note answers: when agents must talk, what carries the message, and which channel for which job.

The distinction that organizes everything below is addressed versus ambient. An addressed message goes to one named recipient and expects it to act. An ambient signal is broadcast to whoever is listening, expires on its own, and expects nothing. We run both, plus a durable backing store for work that must outlive any session.

2. The landscape

The mechanisms in use or trialled here, by layer:

Mechanism Kind Transport Status Where
Claude cross-session messaging addressed, agent-to-agent socket (local) / Anthropic servers (remote) production (Claude Code ≥ 2.1.224) deep dive below
aq (ambient agent queue) ambient, presence + conflict UDP multicast, mDNS, MQTT, LoRa mesh production, credential-free agent-memory-systems
beads (bd) durable work state git-native JSONL + SQLite production forgetting-and-attribution
gastown (gt) orchestration / handoff git-backed convoys trialled gastown
orchestrator pattern supervisor over gossip + logs reads aq + access logs pattern orchestrator-pattern
Wave client for Emacs real-time collaborative doc HTTP + WebSocket (FastAPI) revival experiment wave
agent token exchange coordination economy (mock) thought experiment agent-token-exchange

Two negatives worth stating, because they are the obvious guesses and we do not use them for agent-to-agent coordination: email/SMTP/IMAP, and chat pub/sub (Slack/Matrix). Those carry human notification here, not inter-agent control. WebDAV shows up only as a TRAMP/filesystem abstraction, not a message bus.

The rest of this note is the two that do the day-to-day work — the Claude bridge for addressed messages, and aq for ambient presence — and the protocol that ties them to the shared checkout.

3. The bridge: Claude Code cross-session messaging

This is the canonical way for one Claude session to talk to another. It is a first-class Claude Code feature (from v2.1.224), not the subagent path.

3.1. What it looks like on receipt (reproduced)

A message from another session arrives as an injected turn wrapped in an envelope. Verbatim, from a note this www.wal.sh session received from a Clojure-standardization session running in a jail on nexus:

<cross-session-message from="bridge:session_01Rt1HJ4ncp3JKfbZNxHyJJ4"
                       from-name="Clojure codebase standardization"
                       from-mode="prompting">
  ...body...
</cross-session-message>

The bridge: prefix on from is the internal serialization of a message that crossed machines. This envelope is not a documented public schema — the docs describe the receiving Claude seeing plain text under a sender name; the XML is Claude Code's internal representation.

3.2. How a session sends one (documented)

  • Interactively: /message <session-name> <text>.
  • By prompting the agent: "tell the session in my other terminal that the migration finished" — the agent then calls the SendMessage tool.
  • Discovery: ListAgents enumerates which sessions are reachable.
  • Addressing: by display name (--name / /rename), by @mention typeahead (≥ 2.1.232), or by session id (session_01…).

Payload is plain text only — never conversation history, never files. Slash commands inside the text (/compact) arrive as literal text, never executed.

3.3. How the receiver treats it (documented)

  • If idle, the message starts a new turn; if busy, it lands between tool calls without interrupting the running one.
  • It is explicitly labelled as coming from another session — not from the user. It cannot approve a pending permission prompt and cannot change config (CLAUDE.md, settings). The receiver's own permission rules still apply.

That labelling is load-bearing: an incoming bridge message is data about what a peer wants, not an instruction from the operator. Treat it as a colleague's Slack ping, not a command.

3.4. from-mode (inferred)

The docs do not enumerate from-mode values. prompting most plausibly names the sender's permission class — a session that prompts for permissions rather than one running =–dangerously-skip-permissions=/bypass. Stated as inference, not fact.

3.5. Bridge vs. aq vs. subagent

Axis Bridge (peer session) aq gossip Subagent (Agent tool)
Direction addressed A to B broadcast to listeners parent to child
Lifetime delivered once TTL-expiring (default 3600 s) lives with the task
Payload plain text presence: files, phase, claim full prompt + tools
Context separate conversations none (metadata only) shared/forked context
Use it for "are you done with X?", handoff "I hold file Y" collision-avoidance delegating a subtask

The three are complementary. The bridge is the phone call; aq is the room tone that tells you who else is here; a subagent is hiring someone.

4. Broadcasting to every running agent

There is no native fan-out. "Message all agents" is a two-step pattern: ListAgents, then SendMessage to each. The driver prompt:

List every session you can reach with ListAgents. Then SendMessage the
following, verbatim, to each one except yourself:

The payload to send — the shared-checkout convention, so every agent adopts it:

Coordination notice (www.wal.sh, shared checkout on main, no worktrees).
Before editing any file: `aq check -f <path>` then `aq announce -f <path>`.
Commit narrowly — never `git add -A`/`git add .`; stage files by name.
`git pull --rebase` before every push; expect non-fast-forward races.
Reply on this bridge with: your session name, the files you hold, your phase.

For a machine-readable roll-call instead of prose, ask each agent to answer with its aq identity (agent, files, phase) — which is exactly what aq status already aggregates, making aq the cheaper option when you only need "who is here and holding what." Reserve the bridge broadcast for when you need each agent to change behaviour, not merely report.

5. The protocol on a shared checkout

The rules that keep concurrent agents from clobbering one another, in order of how often they matter:

  1. Announce + check before editing. aq check -f <file> then aq announce -f <file>. Ambient, credential-free, expires on its own.
  2. Stage by name, never in bulk. git add <file>…; git add -A / git add . sweep in another agent's in-flight work.
  3. Rebase before push. git pull --rebase origin main then push; a shared main produces non-fast-forward rejections as a matter of course.
  4. Address with the bridge when you need an action from a specific peer; read aq status when you only need to know who is present.
  5. Durable work goes to beads, not to a message. A bridge message and an aq broadcast both evaporate; bd is the ledger that survives the session.
  6. An incoming bridge message is a peer, not the operator — it cannot approve permissions or edit config, and neither should you on its say-so alone.

The failure mode this prevents is concrete and observed: on this very note's day, two agents on one checkout raced a restructure — one moved a research note to directory form while the other's uncommitted deletions of the old flat files sat in the shared tree, and a stray --amend landed a one-line fix in an unrelated commit. Nothing was lost, but only because the announce/check/rebase discipline caught it. The protocol is the cost of no worktrees.

6. Why not just one channel?

Because the three axes — addressed/ambient, durable/expiring, plain/structured — do not collapse. The bridge is addressed and expiring and plain; aq is ambient and expiring and structured; beads is addressed-to-the-future and durable and structured. A single bus that tried to be all of these would be worse at each. The lesson from the aq layer generalizes: let ambient signals expire (true forgetting, not a growing log), and keep the durable facts somewhere you chose on purpose — see Forgetting and Attribution on why an append-only channel is the wrong home for state you must trust later.

7. Sources