Messages between AI coding agents of different vendors. A Claude Code session and a Codex session talk to each other while both are running.
JavaScript
0
31 commits
updated Sep 20, 2026
Messages between AI coding agents of different vendors, on one machine or between two people.
A Claude Code session can send a message to a Codex session. An OpenCode session can answer a Claude Code session. The message goes into the session that already runs, with its context, and not into a new process.
Two people who work on one project can do the same across their machines. Those messages are sealed end to end, and the server that carries them cannot read one.
Status: version 0.2 is on npm. Version 0.1, between the agents of one person, works and is tested with live sessions. Version 0.2, between two people, passes the fourteen acceptance cases of its specification, and the live tests on one machine. No team has run it across the internet yet.
Each vendor has its own way to accept a message while it runs. openmsg uses that native way, and gives all of them one command:
openmsg list # the agents that run now, all vendors
openmsg send <agent> "<text>" # deliver a message now
openmsg inbox # the messages for this agent
openmsg whoami # how other agents address this one
An address is <vendor>:<name>, for example claude:api-worker.
| Vendor | How openmsg delivers the message | State |
|---|---|---|
| Claude Code | The inbox socket of the session | Works, tested live |
| Codex | codex queue on the shared app-server daemon | Works, tested live |
| OpenCode | POST /session/{id}/prompt_async on its local server | Delivery tested live. A reply needs a model account |
| Cursor CLI | A hook reads the mailbox at the end of each turn | Written, tested with fixtures. A live test needs CURSOR_API_KEY |
| Gemini CLI | The same hook, as an AfterAgent deny | Written, tested with fixtures. Gemini CLI is not installed here |
| Other agents | tmux send-keys | Not started |
A reply is a new message. The receiving agent answers with openmsg send. No
program reads the screen of another program.
Version 0.2 adds one more step: an address with an owner, such as
claude:api-worker@alice.
openmsg id create --label alice # a signing key and a sealing key
openmsg invite create --project web # a token for the other person
openmsg invite accept <token> --fingerprint "A1B2 ..."
openmsg publish claude:api-worker # let the project reach this session
openmsg gateway start --relay <url> # the one process that the network reaches
openmsg send claude:reviewer@bob "the migration drops a column"
What holds:
openmsg publish, and only in the project that the owner names.openmsg accept. Membership of a project is not
permission to write into a session.The states of a message are queued, held, adapter-accepted,
agent-acknowledged, replied, refused, and expired. A write to a socket
is not a read by a model: only an event from the agent gives
agent-acknowledged.
The envelope uses the field names of the A2A standard (messageId, contextId,
parts, role). The same message can travel over A2A on HTTP later, between
two machines.
Each delivered message carries a header that names the sender. The text tells the receiving model that the message is from another agent, and that it approves nothing. Each message keeps a list of the agents that it passed through, so a loop between two agents stops.
npx openmsg list
Or from the source:
git clone https://github.com/marciob/openmsg.git && cd openmsg
node src/cli.mjs list
Node 22 or later. No dependencies, and none for the relay either: the
WebSocket of src/wsframe.mjs is both sides of RFC 6455 in one small file.
Run the tests with node --test. Seventy-eight of them, and they need no
account: they run two gateways and a relay on this machine, as two people.
An agent with no push entry point needs its hook:
openmsg install --hooks
research/2026-09-19-transport-research.md
is the work that produced this design. It tests every way one program can put a message into a running
agent: typing into the terminal of another program, the native entry point of
each vendor, subprocess calls, mailboxes, A2A, and ACP. Each claim carries a
label: [local] for a fact that a test on one Mac verified, [docs] for a
fact from the vendor, and [not verified] for a fact from a source without a
test.
The short answer: typing into a terminal is possible and bad, and each of the four main agents has an official way to take a message while it runs.
spec/openmsg-0.1.md:
the protocol for the agents of one person on one machine.spec/openmsg-0.2-draft.md:
the agents of different people on one project. The identity of an owner,
the sealed envelope, the gateway, the relay, the seven states of a message,
and the trust rules. Every rule of 0.1 still holds.The code implements both, and the fourteen acceptance cases of section 11 of 0.2 pass as tests.
MIT
31 commits
JavaScript
100.0%
Messages between AI coding agents of different vendors. A Claude Code session and a Codex session talk to each other while both are running.
JavaScript
0
31 commits
updated Sep 20, 2026
Messages between AI coding agents of different vendors, on one machine or between two people.
A Claude Code session can send a message to a Codex session. An OpenCode session can answer a Claude Code session. The message goes into the session that already runs, with its context, and not into a new process.
Two people who work on one project can do the same across their machines. Those messages are sealed end to end, and the server that carries them cannot read one.
Status: version 0.2 is on npm. Version 0.1, between the agents of one person, works and is tested with live sessions. Version 0.2, between two people, passes the fourteen acceptance cases of its specification, and the live tests on one machine. No team has run it across the internet yet.
Each vendor has its own way to accept a message while it runs. openmsg uses that native way, and gives all of them one command:
openmsg list # the agents that run now, all vendors
openmsg send <agent> "<text>" # deliver a message now
openmsg inbox # the messages for this agent
openmsg whoami # how other agents address this one
An address is <vendor>:<name>, for example claude:api-worker.
| Vendor | How openmsg delivers the message | State |
|---|---|---|
| Claude Code | The inbox socket of the session | Works, tested live |
| Codex | codex queue on the shared app-server daemon | Works, tested live |
| OpenCode | POST /session/{id}/prompt_async on its local server | Delivery tested live. A reply needs a model account |
| Cursor CLI | A hook reads the mailbox at the end of each turn | Written, tested with fixtures. A live test needs CURSOR_API_KEY |
| Gemini CLI | The same hook, as an AfterAgent deny | Written, tested with fixtures. Gemini CLI is not installed here |
| Other agents | tmux send-keys | Not started |
A reply is a new message. The receiving agent answers with openmsg send. No
program reads the screen of another program.
Version 0.2 adds one more step: an address with an owner, such as
claude:api-worker@alice.
openmsg id create --label alice # a signing key and a sealing key
openmsg invite create --project web # a token for the other person
openmsg invite accept <token> --fingerprint "A1B2 ..."
openmsg publish claude:api-worker # let the project reach this session
openmsg gateway start --relay <url> # the one process that the network reaches
openmsg send claude:reviewer@bob "the migration drops a column"
What holds:
openmsg publish, and only in the project that the owner names.openmsg accept. Membership of a project is not
permission to write into a session.The states of a message are queued, held, adapter-accepted,
agent-acknowledged, replied, refused, and expired. A write to a socket
is not a read by a model: only an event from the agent gives
agent-acknowledged.
The envelope uses the field names of the A2A standard (messageId, contextId,
parts, role). The same message can travel over A2A on HTTP later, between
two machines.
Each delivered message carries a header that names the sender. The text tells the receiving model that the message is from another agent, and that it approves nothing. Each message keeps a list of the agents that it passed through, so a loop between two agents stops.
npx openmsg list
Or from the source:
git clone https://github.com/marciob/openmsg.git && cd openmsg
node src/cli.mjs list
Node 22 or later. No dependencies, and none for the relay either: the
WebSocket of src/wsframe.mjs is both sides of RFC 6455 in one small file.
Run the tests with node --test. Seventy-eight of them, and they need no
account: they run two gateways and a relay on this machine, as two people.
An agent with no push entry point needs its hook:
openmsg install --hooks
research/2026-09-19-transport-research.md
is the work that produced this design. It tests every way one program can put a message into a running
agent: typing into the terminal of another program, the native entry point of
each vendor, subprocess calls, mailboxes, A2A, and ACP. Each claim carries a
label: [local] for a fact that a test on one Mac verified, [docs] for a
fact from the vendor, and [not verified] for a fact from a source without a
test.
The short answer: typing into a terminal is possible and bad, and each of the four main agents has an official way to take a message while it runs.
spec/openmsg-0.1.md:
the protocol for the agents of one person on one machine.spec/openmsg-0.2-draft.md:
the agents of different people on one project. The identity of an owner,
the sealed envelope, the gateway, the relay, the seven states of a message,
and the trust rules. Every rule of 0.1 still holds.The code implements both, and the fourteen acceptance cases of section 11 of 0.2 pass as tests.
MIT
31 commits
JavaScript
100.0%