amc-session-bridge
v1.2.0
Published
Local-machine bridge that lets two AI coding-agent CLI sessions message each other and share artifacts.
Maintainers
Readme
session-bridge
A vendor-agnostic local bridge that lets sessions across different coding-agent CLIs on the same developer machine message each other and share artifacts mid-conversation — without manual copy-paste, without a network listener, and without a third-party service.
Run Claude Code in one window planning a feature, Codex in another implementing it, and opencode in a third reviewing the PR — they all see the same inbox, the same artifact store, and the same broadcast channel.
Supported CLIs
| Provider | Tier | Install |
| ------------------------------------------ | ---- | -------------------------------------------------------------------------- |
| Claude Code | 1 | Marketplace plugin OR init --provider=claude-code |
| Cline CLI | 1 | session-bridge init --provider=cline |
| Codex CLI | 1 | session-bridge init --provider=codex |
| Grok CLI | 1 | session-bridge init --provider=grok (macOS / Linux; Windows best-effort) |
| opencode | 1 | session-bridge init --provider=opencode |
| Gemini CLI | 2 | session-bridge init --provider=gemini-cli |
| Cursor | 2 | session-bridge init --provider=cursor |
Tier 1 providers ship at v1.0.0 with full release-gate manual smokes. Tier 2 ships best-effort — the adapter works but the host CLI's hook surface is private and may require a bridge release to catch up after upstream changes. See docs/providers.md for the full contract and docs/limitations.md for the per-provider caveat matrix.
Verification status (v1.2). Claude Code, Codex, and Grok are the actively tested trio — they pass the full local and cross-provider suites on this release and are what the getting-started guide walks through. The remaining adapters (Cline, opencode, Gemini CLI, Cursor) are implemented but still need a fresh verification pass for v1.2 — treat them as best-effort until then.
Quickstart
npm i -g amc-session-bridge
# Auto-detect from env: picks the right provider for your current CLI.
session-bridge init
# Or be explicit (single provider):
session-bridge init --provider claude-code --label sessionA
# Or wire up multiple providers in one go:
session-bridge init --provider claude-code --provider codex --provider opencode
# Or install for every supported provider:
session-bridge init --allRestart any open coding-agent windows. Once two or more sessions are
live, each one exposes the bridge_* MCP tools to its model. Inbound
messages surface as a <session-bridge-inbox> block at the start of the
next user prompt — never mid-turn.
Run session-bridge doctor to confirm the install, and
session-bridge uninstall --provider <name> to revert.
New here? docs/getting-started.md walks a first-time user from install to two CLIs messaging locally, and then — optionally — to messaging a teammate's sessions across machines.
How it works
┌───────────────────────┐ ┌───────────────────────┐
│ Claude Code session A │ │ Codex CLI session B │
│ ┌─────────────────┐ │ │ ┌─────────────────┐ │
│ │ MCP server │──┼─reads───┼──│ ~/.session- │ │
│ │ session-bridge │ │ │ │ bridge/inbox/ │ │
│ └─────────────────┘ │ │ └─────────────────┘ │
│ ┌─────────────────┐ │ │ ┌─────────────────┐ │
│ │ Hook │──┼─writes──┼──│ inbox/<B>/ │ │
│ │ UserPromptSubmit│ │ │ │ <msg>.json │ │
│ └─────────────────┘ │ │ └─────────────────┘ │
└───────────────────────┘ └───────────────────────┘
▲ ▲
└─── POSIX mode 0700 ────────────┘
(same OS user; no network)- One MCP server per session. Spawned by the host CLI, exposes the
bridge_*tools (whoami, send, receive, broadcast, post/get artifact, channels, search, wait-for-message). - One hook (or plugin, for opencode) per session. Drains the inbox
and surfaces unread messages as
additionalContextbefore the next turn. - All state lives on disk under
~/.session-bridge/. Mode 0700; every file mode 0600. No port. No daemon. No telemetry.
For the full architecture, see ADR-0001 and ADR-0003.
Across machines — opt-in cloud contacts
Everything above is same-machine, no network. If you also want your sessions to message a coworker's sessions on their machine, the bridge ships an opt-in cloud layer — nothing here runs unless you set it up, and local messaging is unchanged whether you do or not.
YOUR MACHINE RELAY COWORKER
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ agent CLIs │ │ accounts, │ │ agent CLIs │
│ ~/.session- │ │ contact graph│ │ ~/.session- │
│ bridge/ │ │ message spool│ │ bridge/ │
│ daemon ────┼──HTTPS───┼──→ ←─────────┼──HTTPS──┼─── daemon │
└──────────────┘ └──────────────┘ └──────────────┘How it works
- A relay — a small
node:httpserver, self-hosted by one person — stores accounts, the contact graph, and a per-user message spool. It routes; it never runs agents. - A daemon on each machine is the only networked component. It reports your live sessions' presence and long-polls the relay for inbound messages, dropping each into the same local inbox a same-machine peer would write to — so your agent and the MCP server are unchanged.
- Contacts are mutual and explicit. You connect by invite →
add → accept; a message flows only across an
activeconnection. There is no stranger discovery — you reach only people who handed you an invite code. Either side can revoke instantly.
<<<<<<< HEAD
Requirements
Everyone (host and each joiner):
- Node.js ≥ 20.19 and npm ≥ 10, with
amc-session-bridgeinstalled (npm i -g amc-session-bridge) and Layer 1 already working.
The one person hosting the relay also needs either:
- Docker — Docker Desktop on macOS / Windows, Docker Engine on Linux (recommended), or
- nothing extra — run the relay straight from Node:
npm run build && node dist/relay/bin.js(needs the repo checked out).
…and the relay host must make the relay reachable by the joiners:
- same LAN → the host's LAN IP is enough;
- over the internet → forward the relay port on the router, or use a tunnel (Tailscale / Cloudflare Tunnel / ngrok), or run the relay on a small cloud VM. Distance is irrelevant once it is reachable.
- For anything past
localhost, terminate HTTPS in front of it (a one-line Caddy reverse proxy — see the quickstart).
Platform notes
- Daemon must stay running. It is a foreground process.
- macOS / Linux: a
tmux/screentab,nohup session-bridge daemon &, or alaunchd/systemduser service. - Windows: leave it in its own PowerShell window, or register it with Task Scheduler / NSSM to run as a service.
- macOS / Linux: a
~/.session-bridgeis%USERPROFILE%\.session-bridgeon Windows; credentials there are protected by per-user ACLs instead of0600.- Docker Desktop on Windows needs the WSL2 backend enabled. The
-v sb-relay:/datanamed volume works the same on all three OSes. - Grok on Windows is best-effort (path quoting / stdout buffering) — see docs/limitations.md.
feat/cloud-contacts
Get started
One person hosts the relay (Docker; needs an https:// URL for any
non-localhost use — see the quickstart):
docker build -f Dockerfile.relay -t session-bridge-relay .
docker run -d -p 8788:8788 -v sb-relay:/data session-bridge-relayThen each person, on their own machine:
# 1 — register an account and go online
session-bridge cloud register --relay https://relay.example.com --handle you
session-bridge daemon # keep running
# 2 — connect (mutual consent)
session-bridge contact invite # you: prints a code
session-bridge contact add <code> # them: redeem it
session-bridge contact accept <their-handle> # you: accept
# 3 — see their live sessions, then message one
session-bridge contact sessions
session-bridge contact send <handle> "subject" --body "..." --label <name>Security at a glance
- A remote contact can do exactly two things — send you a message or an artifact — plus see the presence of sessions you run. No tool calls, no channel access, no enumeration.
- Remote messages are framed in the prompt as external, untrusted data — treat them like a Slack DM from that person.
- v1.2 trusts the relay operator (TLS in transit, no end-to-end encryption) — so self-host your relay, or only join one you trust.
Full detail: docs/cloud.md (how it works + security) · docs/cloud-quickstart.md (setup) · docs/getting-started.md (first-time walkthrough) · ADR-0006 (design rationale).
Tool reference
Each session's MCP server exposes seventeen bridge_* tools. The full
parameter, return, and error reference is in docs/tools.md;
the one-line summary of the most commonly used ones:
| Tool | Purpose |
| ------------------------- | ------------------------------------------------------------------------ |
| bridge_whoami | Identify the current session (sessionId, label, cwd). |
| bridge_list_sessions | List peer sessions seen recently. |
| bridge_send_message | Send a direct message to another session by id/label or to a #channel. |
| bridge_broadcast | Post a TTL'd announcement visible to all sessions. |
| bridge_list_inbox | List unread (or all) inbox entries for the current session. |
| bridge_read_message | Fetch a full message envelope + artifact metas. |
| bridge_mark_read | Idempotently mark inbox / broadcast entries as read. |
| bridge_post_artifact | Upload bytes (base64) to the content-addressed store. |
| bridge_get_artifact | Fetch artifact bytes by sha256 (raw text or base64). |
| bridge_wait_for_message | Long-poll for one new inbox or broadcast entry (use sparingly). |
Plus the channel / thread / set-summary / search tools — see docs/tools.md for the complete list.
The bridge also ships a CLI (session-bridge) with init, uninstall,
doctor, list-providers, broadcast, send-message, list-sessions,
select, tail-inbox, ui, channel, search, inspect, gc,
purge, version, and plugin emit-manifest subcommands. Run
session-bridge --help for the grouped output. session-bridge ui
starts a local read-only dashboard (live view of sessions, messages, and
channels) bound to 127.0.0.1.
Addressing peers
bridge_send_message accepts four to shapes; pick whichever is
unambiguous for your workflow:
to.label(recommended). Set at install time withsession-bridge init --label=<name>. Unique-per-machine is your discipline — the bridge does not enforce uniqueness; it warns at session start when it detects a collision.to.cwd— absolute path. One session per cwd is the default; ideal when "the session in this repo" is unambiguous regardless of what it was named.to.sessionId— full 26-char ULID, or a 6-25 char prefix if unique against the live-session roster. The bridge uppercases the prefix before matching.to.channel— a multi-recipient#channelname; the caller mustbridge_join_channelfirst.
When to.label (or to.cwd) matches more than one live session, the
bridge picks the newest lastSeen deterministically and logs
ambiguous_label_resolved (or ambiguous_cwd_resolved). Run
session-bridge list-sessions to enumerate the active roster when
this happens.
# Send by label (most common):
session-bridge send-message --as=alice bob "subject" --body "ping"Trust model
The bridge stores everything under $SESSION_BRIDGE_HOME (default
~/.session-bridge). The directory is created mode 0700, every file
mode 0600, and on POSIX the bridge tightens permissions it finds
looser. There is no network listener and no encryption on disk:
POSIX permissions are the trust boundary.
Concretely:
- Only the same OS user can read or write the bridge's files.
- A session's
sessionIdis resolved deterministically — from the cwd-pointer written by that session's own hook (claude-code, codex, cline, grok, cursor), from the host CLI's session-id env var (opencode'sOPENCODE_SESSION_ID, gemini-cli'sGEMINI_SESSION_ID), or via an explicitSESSION_BRIDGE_SESSION_IDenv override. Within a single OS user the env override and the cwd-pointer file are both unattested; same-user impersonation is documented and bounded, not silently prevented. See SECURITY.md for the full same-user threat model. - The bridge never reads or writes outside
$SESSION_BRIDGE_HOMEand the provider config files thatinitmutates (each provider's install guide lists them). - Multi-user machines: each user gets a separate
$HOME/.session-bridge; cross-user messaging is not supported and not attempted.
On Windows the bridge relies on default per-user ACLs for
%USERPROFILE% instead of mode bits — see
docs/limitations.md.
For the full threat model — what's protected, what's deliberately not, and how to report a vulnerability — see SECURITY.md. The most recent audit summary is SECURITY-AUDIT-v1.1.1.md.
Limitations
A short list of caveats lives in docs/limitations.md. Highlights:
- One session per
cwdfor the five cwd-pointer providers (claude-code, cline, codex, grok, cursor). opencode and gemini-cli use a per-subprocess session-id env var and do not flap. - Cursor and Cline have no first-class
SessionStart/SessionEnd— session records are registered lazily on the first prompt. - Grok on Windows is best-effort (path quoting, stdout buffering).
- Artifacts cap at 10 MiB by default (
SESSION_BRIDGE_MAX_ARTIFACT_BYTES). - Direct/broadcast bodies cap at 64 KiB (use artifacts for larger payloads).
- Broadcasts expire after
ttlSec(default 1 hour). - Sessions whose
lastSeenis older than 10 minutes are treated as offline.
When something goes wrong, docs/troubleshooting.md
maps every code returned by the tools to a remediation. Designers and
contributors should also read the ADRs under
docs/adr/.
Verifying a release
CI runs npm test, which includes the cross-process e2e scenarios in
src/e2e/scenarios.test.ts (peer message,
artifact share, broadcast fanout, crash recovery, duplicate-id race,
backlog cap, hook timeout budget) and the cross-provider matrix in
src/e2e/cross-provider.test.ts.
Before tagging a release, also run the hand-driven smoke tests:
- docs/manual-smoke/manual-smoke.md —
two real
claudewindows. - docs/manual-smoke/manual-smoke-multi.md — multi-provider release gate: pair Claude Code with at least one other real CLI (codex / grok / opencode / gemini / cursor).
- Per-provider smokes: codex, grok, opencode, gemini, cursor, cline.
Migrating from amc-bridge
session-bridge is the renamed successor to amc-bridge v0.x. On
first run, the new build copies any existing ~/.amc-bridge/ directory
to ~/.session-bridge/ (preserving 0700) and writes a .migrated-to
pointer file in the legacy directory. The legacy amc-bridge-* binaries
stay as deprecation shims (one-line stderr banner + re-exec) through
v2.0.0. Set SESSION_BRIDGE_MIGRATE=0 to opt out and keep using the
legacy directory directly. See the CHANGELOG for the
full migration matrix.
Constraints
- Works today with stock coding-agent CLIs on macOS, Linux, and Windows (the per-provider install guides note any platform caveats).
- No third-party service dependency — everything stays on disk in
$HOME. - Privacy: no network egress; messages live on disk under the OS user's home directory.
Scaffolded by Agent Mission Control. Licensed MIT.
