Your agents. One conversation.
Relay is a local, cross-platform terminal chatroom for AI coding agents. It discovers the agent
CLIs you already have installed (Claude Code and OpenAI Codex), puts them in a shared room with
you, and routes your messages to the agent you address with an @mention.
Relay drives each agent through its own official CLI. It needs no API keys of its own, runs no backend server, and never changes an agent's permission or sandbox settings.
╭──────────────────────────────────────╮
│ RELAY │
│ Your agents. One conversation. │
╰──────────────────────────────────────╯
● Claude connected
● Codex connected
# general /help for commands
You: @claude Say hello to Relay.
Claude: Hello, Relay!
You: @codex What is 41 + 1? Reply with only the number.
Codex: 42
- .NET 10 SDK
- Windows, macOS, or Linux
- At least one agent CLI, installed on your
PATHand already signed in:- Claude Code (
claude) - OpenAI Codex (
codex)
- Claude Code (
Relay still runs without them; missing agents are shown as unavailable.
dotnet build
dotnet test
dotnet run --project src/Relay.Cli -- <command>
| Command | Description |
|---|---|
relay |
Home screen: banner, agent status, and available commands |
relay agents |
List agent providers, install status, version, and path |
relay start |
Start an interactive chatroom |
relay start --trust-workspace |
Start a chatroom, trusting the current folder for that session only |
relay start --workspace <path> |
Start a chatroom whose agents work in that folder instead of the current one |
relay start --agents claude,codex |
Start a chatroom with only the agents named; each must be installed |
relay start --turn-timeout 30m |
Start a chatroom in which an agent whose turn runs longer than 30 minutes is stopped (90s, 30m, 2h; at most 24h) |
relay start --max-turns 10 |
Start a chatroom in which one exchange between agents may run 10 agent turns (1 to 20; 6 without it) |
relay start --room <name> |
Start a chatroom that is saved for this folder, or reopen it where you left it (see below) |
relay trust |
Trust the current folder and remember it (--remove to undo, --list to show all) |
relay rooms |
List saved rooms (--delete <name> deletes one for the current folder, --workspace <path> for another folder) |
relay --version |
Print the Relay version |
relay --help |
Show help |
Inside the chatroom:
| Input | Effect |
|---|---|
@claude <message> |
Send the message to Claude and show its reply |
@codex <message> |
Send the message to Codex and show its reply |
@claude @codex <message> |
Send the message to Claude, and let the agents named pass messages to each other |
| any other text | Record the message in the room; agents see it the next time you address them. A line whose first word is /word is always treated as a command, never recorded |
| a paste of several lines | One message, with its line breaks (see below) |
/agents |
Show the agents in this room |
/history |
Replay the room's message history |
/clear |
Clear the display (history is kept) |
/restart |
Start a new conversation with every agent, showing each the room again |
/restart <agent> |
The same for one agent; use it when an agent's conversation is broken |
/reset |
Start a new conversation with every agent from a clean slate |
/reset <agent> |
The same for one agent: it is shown nothing said before the reset |
/pause on, /pause off |
Pause mode: ask before one agent's message is passed to another. /pause alone shows which it is |
/approvals on, /approvals off |
Be asked before a step an agent's own settings do not allow. /approvals alone shows which it is |
/auto on, /auto off |
Run Claude in its own auto mode from the next turn, so it asks you far less (see below). /auto alone shows which it is |
/role |
Show each agent's role and the roles available |
/role <agent> <role> |
Give an agent a role: implementer, reviewer, architect or coordinator |
/role <agent> <name>: <description> |
Give an agent a role of your own |
/role <agent> none |
Remove an agent's role |
/help |
Show the command list |
/exit |
Leave the chatroom |
Ctrl+C |
Cancel the request or exchange in flight; exit when idle |
Lines that arrive together are one message. Pasting several lines sends them as a single message with its line breaks, and Relay says how many lines it is sending before it sends them. Typing a message over several lines is not possible yet: Enter always sends (#67).
- Only the first line is read as a
/commandor for the@agentsthe message is addressed to. A/commandpasted on a later line is text, and a mention there addresses nobody. - If the first line of a paste is a command, it runs with only that line, and Relay says how many lines it did not use.
- The answer to an approval question, and the choice at a pause, are never taken from input that arrived as several lines. The whole block is dropped and the question asked again.
- With input from a pipe or a file, every line is its own message, as before.
Relay tells a paste from typing by how fast the lines arrive, because a terminal cannot tell
Console.ReadLine that a paste is happening. A paste that arrives slowly — over a slow connection,
or on a loaded machine — can still split, and the lines after the split are read as if you had
typed them, so a /command among them runs. The line count Relay prints before it sends is how you
see that this has happened.
Without --room, a room lives in memory and is gone when you leave. relay start --room work
saves the room as it goes, and the next relay start --room work in the same folder opens it again:
/historyshows what was said before, and each agent continues its own conversation where its CLI still has it. An agent whose conversation cannot be continued starts a new one and is shown the room again, except what/resettold it to forget. If an agent's CLI has dropped the conversation, its next turn fails;/restart <agent>starts afresh.- Roles are saved with the room.
/pause,/approvalsand/autoalways start off, and--max-turnsis whatever you start Relay with. - A room belongs to the folder it was started in. The same name in another folder is another room.
- Only one Relay can have a room open at a time.
- Saved rooms are in your local data folder, readable only by you, but not encrypted. They hold
everything said in the room, including anything you paste.
relay rooms --delete <name>deletes one; nothing is deleted automatically. See docs/threat-model.md. - If a saved room cannot be read, Relay moves it aside, says where, and starts the room empty.
Everything said in a room, by you or by an agent, is shown to each agent the next time you address it. If you tell Codex something and then ask Claude about it, Claude has seen both your message and Codex's reply, each labelled with who said it.
- Nothing in a room is private from its agents, including what you said to a different agent.
- An agent only speaks when you address it, or when another agent addresses it in an exchange you opened to both (see below).
- Relay replays at most the 20 most recent messages an agent has missed, and tells it how many older ones were left out.
/restart <agent>starts a new conversation with that agent and shows it the room again./reset <agent>also makes it forget everything said before the reset.
See docs/adr/0001-shared-room-context.md for the design.
Name more than one agent at the start of a message to open an exchange:
You: @claude @codex agree on a queue design
Claude to Codex: @codex
I propose a channel. Objections?
Codex: None. A channel it is.
- The first agent named answers. The others speak only if an agent hands over to them, by putting
their
@namealone on the first line of its reply. Naming two agents does not ask both for an answer. - Any other reply comes back to you and ends the exchange, including one that merely begins by
mentioning an agent (
@claude and I agree...). - An exchange is at most 6 agent turns, or the number you chose with
relay start --max-turns <N>(1 to 20). Relay says why it stopped whenever the reason is anything other than a reply to you, and/helpstates the limit for the session. - Only the mentions your message begins with count, and only while they name agents in the room.
@claude ask @codex to reviewand@claude @Override is missingboth go to Claude alone, as typed. Outside an exchange nothing an agent writes is delivered to another agent. - Agents cannot invite other agents, raise the limit, or start an exchange of their own. The limit
is settled by your
relay startand holds for the whole session: no command changes it, and nothing an agent writes or anything in the working folder can raise it. Ctrl+Cstops the whole exchange.- After
/pause on, Relay shows each message one agent addresses to another and waits:/gopasses it on,/stopends the exchange, and anything else you type goes back to its author as your message, so it can write a better one. You never edit an agent's message yourself. A line that starts with/or with another agent's name is never sent; Relay asks again.
Inside an exchange with pause mode off, you do not read a reply before the next agent acts on it. See docs/adr/0002-agent-to-agent-exchanges.md for the design and docs/threat-model.md for what that means.
You can tell an agent what to be in the room, so you do not have to repeat it in every message:
You: /role claude implementer
You: /role codex reviewer
You: @claude @codex add retry to the uploader
- The built-in roles are
implementer,reviewer,architectandcoordinator. Any agent can hold any of them; none is tied to Claude or Codex. /role codex tester: you write a failing test before any fixgives a role of your own, with a description of up to 500 characters.- Relay tells the agent its role on every turn, and in an exchange tells each agent the others' roles. An agent with no role is sent your message exactly as before.
- A role is an instruction, not a restriction. A reviewer can still edit files if its own configuration lets it. Relay never changes what an agent is allowed to do.
- Relay does not decide who speaks next. Roles inform the agents; the one speaking still chooses whether to hand over.
- Roles last for the session, including across
/restartand/reset, and are gone when Relay exits, unless the room is saved with--room.
See docs/adr/0003-agent-roles.md for the design.
A turn can take minutes. While Relay waits it shows what the agent is doing, as it happens:
You: @claude add retry to the uploader
Waiting for Claude... (Ctrl+C to cancel)
- Looking at how uploads are sent today.
- Read: src/Uploader.cs
- Write: src/Retry.cs
! Write was refused without asking you: approvals are off. /approvals on turns them on.
- PowerShell: dotnet test
Claude: I could not add src/Retry.cs: writing files needs your approval.
- A line starting with
-is something the agent said along the way, or a step it took: a file it read or changed, a command it ran. - A line starting with
!is a step that was refused or that failed. With approvals on, in a terminal, Claude asks you first instead (see "Approving a step"). - These lines are only for you. They are not part of the room:
/historydoes not show them and no other agent sees them. - Claude's own reasoning is not shown, because Claude Code does not make it available. Codex reports what it says and the commands it runs.
- The tool a step used is in bold, and a skill is shown with the skill it ran, as in
Skill: code-review.
See docs/adr/0004-agent-activity.md for the design.
Agents write Markdown, and Relay shows the common parts of it: headings in bold, • for bullet
points, numbered lists, bold, italic, code in its own colour, code blocks behind a │
with every line exactly as written, and quotes dimmed. Tables, links and anything else are shown
as written. An @mention is highlighted only where Relay acted on it: the agents your message
was sent to, and an agent's handover to another inside an exchange. /history shows
messages the same way.
The styles are Relay's own. Whatever a reply contains, it cannot choose colours, make links, or send anything to your terminal but text; see docs/threat-model.md.
Each agent is limited by its own settings. When Claude wants to take a step those settings do not
allow, Relay asks you, and the step happens only if you type yes:
You: @claude save the date to today.txt
Waiting for Claude... (Ctrl+C to cancel)
- PowerShell: Get-Date | Out-File today.txt
? Claude wants to use PowerShell:
command: Get-Date | Out-File today.txt
description: Save the date to today.txt
Allow it this once? Type yes or no. Ctrl+C cancels the turn.
You: yes
Claude: Saved the date to today.txt.
- A yes is for that one step. Relay does not remember it and never changes an agent's settings. To stop being asked about something, allow it in the agent's own settings, or switch Claude into its auto mode (below).
- Only
yesoryallows.norefuses, and Claude is told. An empty line, or anything else, is asked again and is not sent to anyone. - Read the command, not the description. The description is the agent's own. A command is always shown in full; a longer value, such as a file's content, is cut short and says so.
- Claude can also ask you a question, with options to pick by number or an answer of your own.
/skipleaves it unanswered. - Your answers are for that agent only. They are not part of the room.
- A question waits until you answer it. For when you are not watching,
/approvals offrefuses every such step without asking, andrelay start --turn-timeoutstops a turn that has waited too long, which withdraws its question. - Only in a terminal, and only Claude. With input or output redirected nobody is asked and the step is refused, as before. Codex cannot ask: a step its own settings do not allow fails.
See docs/adr/0005-approvals.md for the design.
Being asked about every step soon gets in the way. /auto on runs Claude in Claude Code's own
auto mode from its next turn: a classifier model decides the steps Claude's settings leave open,
and only what it will not decide is put to you. /auto off goes back, for example to approve one
thing yourself; you can switch as often as you like, between turns.
- Your rules still hold. A deny rule still denies, and an ask rule, such as one for
gh pr merge, still asks you. - It is a trade. The classifier is meant to block steps that go beyond your request or that
something hostile put Claude up to, but it can be wrong. With agents passing work to each other,
/pause onlets you read each handover first. See docs/threat-model.md. - Claude only. Codex has no auto mode Relay can switch on.
- Not always available. Auto mode depends on your plan, model and settings, and on a Claude
Code version that has it. When Claude cannot use it, or does not say which mode it ran in, Relay
says so once for that message, and steps may be put to you as before. A Claude Code from before
auto mode fails every turn instead;
/auto offputs it back. - Each step the classifier reviews is a model call on your account.
See docs/adr/0006-claude-auto-mode.md for the design.
Agents run in the folder you start Relay from, with whatever access your own agent configuration grants them there.
By default Relay treats that folder as untrusted: Claude loads only your user-level settings
and ignores the folder's own hooks, MCP servers, settings and CLAUDE.md. This matters because a
folder's hooks and MCP servers run commands on your machine, and Claude Code does not show its
usual trust prompt when Relay starts it.
The first time you run relay start in a folder, or name it with --workspace, it asks whether
you trust the files there. A yes is remembered, so you are asked once per folder; relay trust --remove undoes it. Trust only folders you would run code from. The chatroom header shows which
mode is in effect.
This setting governs Claude only. Codex applies its own trust checks: in a folder you have never
trusted in Codex it runs none of the folder's hooks or MCP servers, but it does read the folder's
AGENTS.md and skills. A folder you have trusted in Codex itself stays trusted there, whatever
you told Relay.
See docs/threat-model.md for the full security model and remaining risks.
| Agent | Discovery | Chat | How Relay talks to it |
|---|---|---|---|
| Claude Code | yes | yes | claude -p --output-format stream-json --verbose, continued with --resume <id>. In a terminal, also --mcp-config <file> --permission-prompt-tool <tool>, so it can ask you. With /auto on, also --permission-mode auto |
| OpenAI Codex | yes | yes | codex exec --json -, continued with codex exec resume <id> --json - |
Both integrations were verified against Claude Code 2.1.273 and codex-cli 0.162.0 on Windows.
- One room per run:
general, held in memory, or the room you name with--room, which is saved. - There are no private messages: every agent in a room sees everything said in it.
- Because agents now read each other's replies, one agent can influence another through the room. See the threat model.
- A reply arrives whole when the agent finishes. While you wait, Relay shows what the agent is doing (see "Watching an agent work"), but not the reply as it is written.
- Only Claude can ask you before a step its own settings do not allow, and only when Relay runs in a terminal (see "Approving a step"). Codex cannot: such a step fails, and Codex does not report it as a refusal. Codex also refuses to run outside a Git repository.
- An agent's turn has no time limit unless you start Relay with
--turn-timeout. Without one, pressCtrl+Cto cancel a request that hangs. With one, an agent that runs past it is stopped, Relay says so, and the agent keeps its conversation. Time spent waiting for your answer to an approval question counts towards the limit. - You cannot type while an agent is working, so an exchange can only be interrupted with
Ctrl+Cor, in pause mode, between turns. - One agent speaks at a time, and an exchange between agents is limited to 6 turns, or to the
--max-turnsyou chose at start-up, up to 20. The limit cannot be changed without restarting Relay, and a higher one means a longer run with nobody watching when pause mode is off. - Not yet packaged; run it with
dotnet run.
- Streaming the reply itself as it is written
- Several rooms open in one run
- Packaging as a .NET global tool and native executables
See CONTRIBUTING.md for how to report a bug or propose a change, and AGENTS.md for architecture, conventions, and the rules for process execution. Everyone taking part is expected to follow the Code of Conduct.
To have Git refuse a commit that contains a secret, install gitleaks and enable the hook once per clone:
git config core.hooksPath .githooks
CI runs the same scan on the commits of every pull request.
To report a vulnerability, follow SECURITY.md; please do not open a public issue. docs/threat-model.md describes what Relay protects against and what it does not.
Relay is released under the MIT License.