Skip to content

Repository files navigation

Open Cross-session

Claude Code, Codex, Pi, and terminal agents message and wake each other — on one machine, and across the computers on your LAN (macOS, Linux, Windows). No server, no account.

ci release license

中文文档

ocs-en-Landscape-VO.mp4

ocs gives every AI coding session a shared message channel, and wakes the target session for real instead of only writing a file. Claude Code sessions, ChatGPT Desktop tasks, Pi TUIs, and terminal agents all speak through the same append-only local log — and since 0.6, a Claude on your Mac can hand work to a Claude or Codex on your Windows box down the hall.

New in 0.6: agents across your computers

# machine A                         # machine B
ocs lan up                          ocs lan up
ocs lan pair   # prints a code  →   ocs lan pair 7K2M-9QXD-…
                                    ocs who --lan
                                    ocs dm claude-1a2b3c4d@mini "can you run the Windows build?"

The session on A wakes with the message and a Reply: line that routes straight back. Verified Mac ↔ Windows (Claude Code 2.1 and ChatGPT Desktop Codex on both sides).

  • Finds each other on the LAN (multicast + subnet broadcast); --addr when the network blocks both.
  • Pair once, no trust-on-first-use: the one-time code carries the issuer's key fingerprint.
  • Mutually authenticated and encrypted: Ed25519 identities, signed X25519 handshake, AES-256-GCM, forward secrecy. Unpaired machines can only redeem a live code — nothing else.
  • Off by default. ocs lan up starts it; ocs lan autostart on keeps it across logins.

Protocol and threat model: docs/lan.md. Setup details: Cross-machine.

Native cross-session messaging stops at the product boundary. ocs adds the pieces needed when agents from different products must work together:

  • Cross-vendor direct wake: Claude Code ↔ ChatGPT Desktop ↔ Pi, plus terminal Claude/Codex TUIs when they run in cmux.
  • Real multi-party channels: any number of agents and human observers, with @ mentions, --reply-to, cursors, and replayable sequence numbers.
  • Conversation continuity: messages remain in local JSONL logs; stable workspace identities preserve Claude DMs across restarts and Git worktrees, with an explicit migration path for older DM history.
  • Memorable addresses: every session has a fixed short id, and ocs rename <name> adds a name; both reach it from any other agent.
  • One roster and one workflow: ocs who, ocs dm, automatic sender detection, bundled skills, and ocs doctor work across all supported harnesses.
  • Safer delivery behavior: Pi queues messages behind a busy turn, cmux never types into a busy TUI, self-wakes are suppressed, and unknown IPC outcomes are reported without retrying and risking duplicates.
  • Local by default: no daemon, account, API key, or server; one static binary and files under ~/.ocs.
  • Across your LAN: paired computers reach each other's agents as <address>@<peer>; see above.

When your LAN stops being enough — different networks, a team, other organizations — the same habits carry over to Agent Party, a team integration and coordination solution for cross-machine, cross-org channels. Use the hosted service, or self-host it within Cloudflare's Free plan quotas.

Name your sessions

Every session already has a fixed short id, such as claude-7043ea85, codex-01a06a98, or pi-01a09109; ocs who lists them. Add a name that people and agents can remember:

ocs rename reviewer                             # run inside the session (or just ask its agent)
ocs dm reviewer "take a look at this diff"      # reach it by name
ocs dm claude-7043ea85 "same session, by id"    # the id keeps working
ocs send dev "ready? @reviewer"                 # @name wakes it: Claude, Codex, or Pi
ocs rename --clear                              # drop the name
  • Each session has at most one name; renaming releases the old one. Names are case-insensitive, use A-Z a-z 0-9 . _ -, and are at most 64 characters.
  • If another session already holds the name, ocs refuses it; --force takes it over once you know the old owner is gone. A name equal to another live Claude session's exact name is rejected.
  • In Claude, the name stays with the window across /clear, and replies to your DMs come back as ocs dm <your-name>.
  • Tools can read ocs whoami --json [--session <claude-session-id>], which prints {host, id, name, session, addresses}. Every entry in addresses works with ocs dm.

Pair it with Claude Status Bar

Claude Status Bar (cs) shows each session's ocs address on its own status-line row, for example ocs reviewer · claude-7043ea85, so you can see who to message without running ocs who. From v3.43.1 the row appears automatically when ocs is installed; hide it with cs config set show_ocs false.

curl -fsSL https://raw.githubusercontent.com/leeguooooo/claude-code-usage-bar/main/install.sh | bash

Install

curl -fsSL https://raw.githubusercontent.com/leeguooooo/open-cross-session/main/install.sh | sh

Windows (PowerShell):

irm https://raw.githubusercontent.com/leeguooooo/open-cross-session/main/install.ps1 | iex

Single static binary, zero runtime dependencies. macOS (arm64/x64), Linux (x64), and Windows (x64). The installer also registers the version-matched ocs skill for Claude Code, Codex, and Pi. It uses the pinned skills CLI when npx is available, with telemetry disabled, then runs the binary's embedded fallback and Pi-extension setup. To install only the binary:

curl -fsSL https://raw.githubusercontent.com/leeguooooo/open-cross-session/main/install.sh | OCS_INSTALL_SKILLS=0 sh

From source: bun install && bun link && ocs skill install.

Quick start

The curl installer prepares the skill automatically. After restarting any open Pi session, tell Claude Code, Codex, or Pi things like "find another agent to review this" — it discovers peers and talks to them on its own. Under the hood:

ocs doctor --fix              # one-time: safely repair setup, then re-check every wake path
ocs skill install             # explicit skill/Pi-extension reinstall (normally unnecessary)

ocs who                       # same-project peers first; you are marked
ocs dm codex-01a06a98 "can you review this diff?"   # short, copyable target
                              # channel auto-derived, your identity auto-detected
ocs inbox                     # resume unread threads after a restart

# one-time migration for DM history created before v0.3.4
ocs dm agentparty "continuing in the old thread" --inherit dm-<old-channel>

# multi-party rooms when you want them (channels are just files, nothing to manage)
ocs send dev "status? @agentparty-d8 @piggo-67"
ocs watch dev                 # tail a channel as a human observer

A conversation sustains itself: each wake note carries the message body and a copy-paste Reply: command, and ending a message with the peer's @name wakes them for the next turn. To be told when a peer finishes, subscribe once with ocs notify-when-idle <name> (or --notify-when-idle on send/dm).

How it works

ocs send ──▶ append to channel log ──▶ wake carrier per target
             (~/.ocs, monotonic seq)     ├─ Claude session   → per-session Unix socket inbox
                                         ├─ Desktop task     → ChatGPT's native cross-task IPC
                                         ├─ Pi TUI           → ocs Pi extension Unix socket
                                         ├─ cmux terminal    → surface-addressed input (when idle)
                                         └─ (any session)    → reads with `ocs read`, replies

The wake payload is the message itself, delivered the way Claude Code's built-in cross-session does it — as data inside a <cross-session-message> wrapper:

[ocs wake] alice mentioned you in #dev (seq 7, reply to seq 3)

<the message body, verbatim up to 4096 bytes; longer bodies show the first 512
bytes plus "… (N bytes total; full text: ocs read dev)">

Reply: ocs dm alice "<your reply>"          # for a Claude-to-Claude DM
Thread: ocs read dm-<derived-channel>

For a Claude-to-Claude DM, the Reply: line uses the sender's ocs name when it has one, otherwise its unique workspace alias; the derived channel stays in Thread: only. With neither, the note falls back to ocs send <channel> ... --reply-to .... Live Claude, Codex, and Pi targets infer their own identity, so only unverifiable headless or cmux targets need an explicit --as. The whole note is capped at 5120 bytes. The protocol is shared with Agent Party: docs/wake-protocol.md.

Who can be woken

Target How Requirement
Interactive Claude Code session @<name>, @claude-<8hex>, or @<session name> Receiver sets "crossSessionInbound": "accept" in ~/.claude/settings.json. The default is hold: the message waits for manual approval and is dropped after 5 minutes. Since 0.7 the sender is told: wake: HELD (exit 2) at send time, and one [ocs delivery notice] if it is never approved. ocs doctor checks the setting.
ChatGPT Desktop task / cmux Codex TUI ocs dm codex-<8hex> …, @<thread-id>, or --codex <thread-id|codex-8hex> Desktop delivery needs the task open plus a second open task under the same renderer. If that path is definitely unavailable, ocs safely falls back to a uniquely matched, idle cmux surface that still has a live Codex process.
Pi TUI ocs dm pi-<8hex> … or @pi-<8hex> Run ocs skill install, then restart Pi. The installed extension registers the live TUI and queues inbound messages as follow-ups, so a busy turn is not interrupted.
Claude/Codex terminal TUI in cmux ocs dm surface:<n> … Optional: when cmux is detected, ocs who lists terminal surfaces and can submit the wake note to an idle surface. A busy surface is left untouched.
Other terminal or headless agent ocs read / ocs send Full channel participation, persistence, and replies, but no unsolicited direct wake unless its harness exposes a supported carrier.
Human at a shell ocs send / ocs read / ocs watch Can post, read once, or tail the same channels without running an agent.

Delivery honesty: the first line says stored #<channel> seq <n> once the append-only log commit succeeds; it does not claim wake delivery. Each requested wake then reports accepted, stored-only, or unknown separately. Exit 2 means the message is stored but at least one wake failed; exit 3 means the message is stored and a wake outcome is unknown. In either case, do not resend: use the printed channel and seq to inspect the existing message. A send that wakes nobody (no @mention, no --reply-to) prints stored-only instead of staying silent, and exits 2 in a dm-* channel. Mentions count after any non-address character, so 。@claude-9e6c0ae7 works. Pi acceptance means its extension queued the message.

Claude targets report back through Claude Code's own delivery receipts (macOS and Linux; wire format and process model in docs/delivery-receipts.md):

Output Exit What it means
wake: accepted by inbox → X 0 The frame is in X's inbox and no hold/refuse receipt arrived. With accept that means it entered the conversation. It is not a read receipt.
wake: HELD, not delivered yet → X 2 X's crossSessionInbound gate parked it for manual approval; it is dropped if nobody approves within 5 minutes. ocs keeps watching and sends your session one [ocs delivery notice] if it ends up not delivered.
wake: NOT delivered → X: refused (or dropped, denied, expired) 2 X's side rejected it.
wake: delivered to inbox → X 0 Receipts unavailable (Windows, or OCS_NO_RECEIPTS=1): the frame reached the inbox socket and nothing more is known, as in 0.6.

In every case the message is already in the channel log and shows up in the peer's ocs inbox, so do not resend. ocs read shows [wake → X: <status>] under your own messages (delivery in --json). The fix for a held message is on the receiving side: ocs doctor --fix; a repo-level Claude setting can still force hold.

For Codex, ocs who includes only tasks currently claimed by an open Desktop renderer. ocs codex-sessions is rollout history, not presence. When Desktop definitely reports unavailable, not-open, or no-source, ocs may reuse the same stored channel/seq to wake a uniquely matched idle cmux Codex surface. The fallback requires both an exact task suffix in the surface title and a live foreground Codex process; stale shells and ambiguous matches fail closed. It is never attempted after an unknown IPC outcome. Without a safe carrier match, the message stays in the append-only log for recovery with ocs inbox.

Commands

Command Purpose
ocs who Roster of every reachable agent, with same-project peers first and yourself marked; --verbose shows raw IDs/paths, --json is machine-readable
ocs whoami Print the auto-detected sender identity; --json [--session <id>] describes the host session ({host, id, name, session, addresses})
ocs rename <name> Give this session a memorable address; its short id keeps working. --force takes over a name held by another session; --clear removes it
ocs dm <name-or-id> <text> Message + wake one agent; unique Claude workspaces keep one channel across restarts. --inherit <old-dm-channel> binds pre-v0.3.4 history once; --notify-when-idle
ocs inbox List unread threads that can be safely attributed to the current identity; --json for automation, --session <claude-session-id> to resolve one Claude session by id (for status bars outside Claude's process tree)
ocs send <ch> <body> Append to a channel; @ mentions wake, --reply-to <seq> also wakes that seq's author. --as is only an override. --codex and --codex-source accept a full thread ID or the unambiguous codex-<8hex> printed by ocs who. Also supports --no-wake and --notify-when-idle
ocs read <ch> Read new messages since your cursor, then advance it. Your own messages fold to one line (--include-self shows them; --json adds self). --as overrides identity; also supports --since, --peek
ocs notify-when-idle <name> One-shot: a [Cross-session idle notice] lands in your session when that Claude session next goes idle or exits (immediately if already idle; expires after 6h)
ocs sessions List live Claude Code sessions
ocs codex-sessions List local Codex rollout history (--limit <n>); unlike ocs who, this does not imply the task is open or wakeable
ocs watch <ch> Tail a channel (--interval-ms <n>)
ocs doctor Health check for Claude, Codex, Pi, skills, and the data directory; --fix repairs safe local setup and re-checks it
ocs skill install Repair/update the bundled skill for Claude Code, Codex, and Pi, plus Pi's direct-wake extension
ocs upgrade Fetch and install the latest GitHub Release binary (--check only reports; --party prints the hosted Agent Party migration path)
ocs lan up | pair | who | status | peers | scan | unpair | down Opt-in LAN mode: pair machines, then ocs dm <address>@<peer> and ocs who --lan (see Cross-machine)
ocs version Print the version

Stuck below 0.4.3? ocs upgrade only started upgrading the binary in 0.4.3 — before that it just printed a migration blurb and exited, so an older install can never reach a newer release on its own and will keep looking current. Re-run the installer once:

curl -fsSL https://raw.githubusercontent.com/leeguooooo/open-cross-session/main/install.sh | sh

After that ocs upgrade works, and ocs doctor warns when the binary falls behind.

Data lives in ~/.ocs (override with OCS_HOME). Channels are plain JSONL logs. Back up the whole directory, including workspace-key: that local secret keeps workspace identities stable without exposing repository paths or remotes in channel names.

vs native cross-session

Claude Code and Codex each shipped their own cross-session capability. They are good — inside their own islands. ocs is not a replacement for either; it is the bridge between them, plus what neither provides:

Claude native cross-session Codex native cross-task ocs Agent Party
Reach claude ↔ claude (local + cross-machine) codex ↔ codex (inside ChatGPT Desktop) any ↔ any on one machine and across paired LAN machines (Claude, Codex, Pi, terminal TUIs) any ↔ any across networks and organizations
Best fit direct Claude session handoff direct ChatGPT task handoff personal cross-vendor coordination, on one machine or across your LAN team integration across machines and organizations
Cross-vendor — — ✅ local + LAN bridge ✅ cross-vendor channels
Multi-party agent teams (same harness) task @ mentions ✅ local agents + humans ✅ hosted agents + humans
Offline delivery live sessions only open tasks only ◐ messages persist in the local channel* ✅ persistent channel history + directed delivery
Shared history / audit per-session transcripts per-task ✅ append-only log, seq-referenced receipts, replayable ✅ server-backed history, receipts, task and decision ledgers
Unified roster Claude sessions only Codex tasks only ✅ ocs who lists Claude, Codex, Pi, and cmux surfaces ✅ party agents lists channel-wide addresses
Pi support — — ✅ direct wake extension, busy-turn queue connector-dependent
Terminal TUI support Claude Code sessions — (Desktop tasks only) ✅ channel access everywhere; optional cmux wake connector-dependent
Thread references harness-native harness-native ✅ portable seq + --reply-to across harnesses ✅ channel receipts and ledgers
Setup built into Claude Code built into ChatGPT Desktop one static binary; no daemon, account, or API key hosted or self-hosted service

* Persistence has no auto-nudge: nothing watches for sessions coming online, so the peer sees backlog on its next ocs inbox, ocs read, wake, or human prompt. Claude's generated session name still changes after restart, but a unique workspace alias maps to a salted local identity. Git repositories use their normalized origin so worktrees converge; non-Git workspaces use their launch directory. That identity keeps the same DM channel and can be recovered from the local index while the peer is offline. Same-repository multi-session cases deliberately fall back to exact session names rather than sharing private history. Use OCS_NAME / --as when you need an explicit role identity. History created before v0.3.4 can be attached once with --inherit; ocs refuses ambiguous workspaces, one-sided histories, and third participants. If both the old and stable channels already have messages, ocs builds a deterministic merged channel (old first, stable second) and retains both source logs unchanged. The sender cursor advances to the merged tail; the peer's first read can inspect the full inherited history. New DMs append an opaque namespaced route sidecar in the same log so ocs inbox can attribute unread messages without reversing private channel hashes. Old clients ignore the sidecar and still read the unchanged message frame. Legacy DM records without that metadata appear only when an existing cursor already proves participation; ocs does not guess and expose unrelated private threads.

Honest guidance: for a quick claude↔claude direct message, native is smoother — ocs's Claude carrier literally rides on the native inbox socket. Use ocs when the conversation crosses vendors, needs more than two participants, needs messages to survive one side being offline, or should leave an auditable trail.

Cross-machine

Same LAN: ocs lan (opt-in)

Pair two machines once, then address a remote agent as <address>@<peer>:

# machine A ("mini")
ocs lan up                  # start the LAN daemon (off until you do this)
ocs lan pair                # prints a one-time code, waits up to 10 minutes

# machine B
ocs lan up
ocs lan pair 7K2M-9QXD-…    # finds A on the LAN; if multicast is blocked add --addr <A-ip>:47890
ocs who --lan               # agents on A: claude-1a2b3c4d@mini  claude  idle  …
ocs dm claude-1a2b3c4d@mini "can you look at the CI failure?"

The woken session on A sees the sender as claude-9f8e7d6c@<label> and a Reply: line that routes straight back. ocs lan status | peers | scan | who | unpair <peer> | down manage it; ocs lan autostart on starts the daemon at login. For agents to answer each other without a human clicking "deliver" on every message, the receiving Claude needs crossSessionInbound: accept (ocs doctor --fix) — otherwise held messages drop after 5 minutes. Windows specifics (named-pipe inbox, firewall rule): docs/lan.md.

Security, in short: every machine has an Ed25519 key; pairing binds the issuer's key fingerprint into the code, so there is no trust-on-first-use; each connection runs a signed X25519 handshake with forward secrecy and AES-256-GCM; unpaired machines can only redeem a live code. Pairing means "this machine may prompt my agents" — the same power a local session has. Discovery replies reveal only an instance name, port, and key fingerprint. Full protocol and threat model: docs/lan.md.

Anywhere else: SSH

Without the LAN daemon OCS has no listener at all. When two personal machines already have passwordless SSH, keep authentication and host-key checking in the user's SSH config and invoke the target machine's local tools directly:

ssh workbox ocs who --verbose
ssh workbox ocs dm codex-<8hex> "review the current failure"

# Remote agent/runtime control remains Herdr's job, not OCS's.
ssh workbox herdr agent list
ssh workbox herdr agent prompt reviewer "run tests and summarize failures" --wait --timeout 120000

The SSH direction determines the roles. If only machine B can connect to machine A, then B is the controller and A is workbox; no reverse login or OCS adapter is needed. Prefix remote targets with the SSH host in human-facing instructions (for example workbox/reviewer) so they cannot be confused with same-named local agents.

For cross-network or cross-org coordination and shared multi-party channels, use Agent Party.

Local vs hosted

Open Cross-session Agent Party
Best for personal use: your agents on one machine and your computers on one LAN team integration and shared channels
Deployment none — a single binary hosted service, or self-hosted on Cloudflare
Scope one machine, many agents; paired machines on one LAN across networks and organizations
Transport local sockets + JSONL log Cloudflare Workers + Durable Objects
Included coordination local channels, unified roster, direct wake, idle notifications directed delivery, leases, presence, tasks, web UI

Same command habits on both. ocs upgrade --party prints the migration path. A self-hosted Agent Party can run within the Cloudflare Free plan quotas for Workers, D1, and SQLite-backed Durable Objects.

FAQ

The story behind ocs, and the "it said OK but the message was gone" bugs it is built around: Making Claude Code and Codex talk to each other.

Can Claude Code and Codex talk to each other?

Yes. Install ocs on the machine and run ocs dm codex-<id> "review this diff" from Claude Code (or ask Claude to "find another agent to review this"). The Codex task is woken with the message and a ready-to-run reply command, so the two agents can go back and forth without you copying text between windows. It works the other way round too, and with Pi and terminal agents.

How do I get two Claude Code sessions to talk to each other?

Claude Code's built-in cross-session messaging already covers claude ↔ claude, and ocs rides on the same inbox. Use ocs when the conversation also involves Codex or Pi, needs more than two participants, has to survive a session restart, or runs across two computers. Set "crossSessionInbound": "accept" in ~/.claude/settings.json on the receiving side (ocs doctor --fix does it). With the default hold, a message waits for manual approval and is dropped after 5 minutes; ocs tells the sender so — wake: HELD with exit 2 when it is sent, and a delivery notice if nobody approves it — instead of reporting it as delivered.

Can AI agents on different computers message each other?

Yes, on the same LAN. Run ocs lan up on both machines, pair them once with ocs lan pair, then address a remote agent as <name>@<peer>. Traffic is mutually authenticated and encrypted (Ed25519 identities, signed X25519 handshake, AES-256-GCM); tested between macOS and Windows. Across the internet, use SSH (see Cross-machine) or the hosted Agent Party.

Does it work on Windows?

Yes. ocs ships a Windows x64 binary (irm https://raw.githubusercontent.com/leeguooooo/open-cross-session/main/install.ps1 | iex), wakes Claude Code through its named-pipe inbox and ChatGPT Desktop's Codex through its local IPC pipe, and joins the LAN mode.

The Codex pipe name (\\.\pipe\codex-ipc) is fixed and any local process could create it first, so since 0.7.1 ocs checks who is serving the pipe on the very connection it is about to use: the pipe must be owned by your user, and its server process must run as you and be ChatGPT Desktop (Store package OpenAI.Codex, image inside the package's install directory). Otherwise nothing is sent, the message stays in the inbox, and ocs doctor prints the reason. A Desktop that is not the Store package, or that runs elevated, is refused too.

Do I need a server, an account, or an API key?

No. ocs is one static binary; messages are JSONL files in ~/.ocs. Nothing leaves your machine unless you turn on LAN mode, and then only to computers you paired.

How is this different from subagents, agent teams, or calling Codex through MCP?

Subagents and agent teams are spawned and owned by one Claude session; an MCP bridge makes Codex a tool that Claude calls. ocs connects independent, long-lived sessions — each keeps its own context, tools, and human — and lets any of them start the conversation.

Development

bun install
bun test            # Claude/Pi Unix-socket E2E + a fake Desktop-IPC router
bunx tsc --noEmit

Architecture decisions and component provenance: DESIGN.md and docs/agentparty-extraction-map.md. Engineering invariants for contributors: CLAUDE.md.

License

MIT. Three source files are vendored from AgentParty by the same copyright holder and relicensed under MIT; their headers mark the upstream origin.

Author

Built by 郭立 (Guo Li / leeguoo) — leeguoo.com · GitHub · X · more tools in the *-use family.

About

Let Claude Code, Codex and Pi talk to each other: cross-session messaging that wakes AI agents on one machine or across your LAN (macOS/Linux/Windows). No server. 让 Claude Code、Codex 互相唤醒、互发消息。

Topics

Resources

Stars

20 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages