Skip to content

moogento/agent-chat

v0.9.0MIT

Coordinate local coding agents through a shared, file-backed mailbox.

agent-chat

Let Codex, Claude Code, and OpenCode agents coordinate across chats, worktrees, and Docker containers. Send focused requests to named peers and receive 💬 Agent Chat inbox hints through optional client hooks.

The messaging server and optional broker use only Node built-ins. The installer includes a bundled TOML parser, so local release packages install without downloading dependencies.

What it does

  • Connect Codex, Claude Code, and OpenCode through MCP tools for messaging, discovery, and invitations.
  • Keep conversations scoped to task rooms, with named recipients and independent unread-message cursors.
  • Limit message pages and wait budgets, with quiet unchanged status checks to reduce context and polling overhead.
  • Show optional 💬 Agent Chat inbox hints through client-specific notification hooks.
  • Await a specific peer's reply for up to two hours with an opt-in, session-bound watch.
  • Install, diagnose, update, and remove project integrations while preserving unrelated settings.
  • Connect agents in separate Docker containers through an optional authenticated broker.

Quick start

Requires Node.js 22 or newer. Install from this repository:

git clone https://github.com/moogento/agent-chat.git
cd agent-chat
npm ci --ignore-scripts
node agent-chat.mjs install \
  --project /absolute/path/to/your-project \
  --clients codex,claude,opencode \
  --room checkout-refactor \
  --hooks
node agent-chat.mjs doctor --project /absolute/path/to/your-project

Choose only the clients you use. Omit --hooks to start with messaging and skills alone. Restart the selected clients in your project. In Codex, review and trust the project hooks in /hooks; other clients may also require project trust or hook approval.

With --hooks, the installer also adds a short managed "Agent Chat" block to the project's CLAUDE.md for Claude Code and AGENTS.md for Codex and OpenCode, creating the file if needed. The block tells a woken agent it may call chat_read to read messages addressed to it without asking you first. Hooks can wake an idle session when a message arrives, but a wake notice is not a user message. Automatic approval reviewers, such as Codex's approval review and Claude's auto mode, trust only your messages and these instruction files, so without the block they can refuse to read the waiting messages. Message content stays untrusted and does not authorize other actions. Add --no-wake-permission to skip the block; update keeps that choice. If you set up Agent Chat globally or by hand instead of with the project installer, add the same instruction yourself to ~/.codex/AGENTS.md or ~/.claude/CLAUDE.md; see approval reviewers for the text.

--room configures one named local room for the selected clients. A plain name such as checkout-refactor and a directory path are different rooms. If you omit it, each client defaults to its current Git worktree or directory. doctor reports the configured source and warns when a same-named path room exists.

The MCP connection joins its configured repository room on startup with a unique provisional handle. For a shared task room, give sessions clear names and join that room:

First agent:  Use agent-chat. Join room checkout-refactor as builder.
Second agent: Use agent-chat. Join room checkout-refactor as reviewer.
First agent:  Ask reviewer to check the checkout changes and report concrete bugs.

Hooks need a conversation binding before they can show inbox hints. For managed Codex and Claude installs with --hooks, restart the client after installation or upgrade and call chat_who once in each conversation to bind it. If the client omits structured tool results, use manual notification binding. On the next supported event, the hook prompts the agent to call chat_read itself when a message is waiting. OpenCode and manually registered plugins also need manual binding. Peer messages carry context, not permission to take additional actions.

The installer copies a stable runtime into your project, so moving the original checkout does not break the connection. It changes project settings only, preserves unrelated entries, and backs up changes. Preview installation with --dry-run.

There is no published npm package assumed by these instructions. You can build offline npm, plugin, and marketplace archives with npm run release:local; see installation and distribution.

Choose your setup

Where your agents runSetup
On one machine, including agents that run tests in DockerDefault local mailbox; test containers need no Agent Chat setup
Together in one containerLocal mailbox with shared writable storage
Across separate containers, or on the host and in containersOptional broker; only the broker mounts the mailbox volume

For container-based agents, follow the Docker setup guide. It includes a Compose example, token generation, host and container connections, saved-session recovery, and cleanup. Connections use an explicit room name so different checkout paths still reach the same room. Notification hooks run alongside their client and use the same broker connection.

The example uses a private Docker network and a loopback host port. It is not a public internet deployment recipe.

Install, update, and remove

Use the CLI from the checkout, or install a generated local tarball to get the agent-chat command:

node agent-chat.mjs install --project /path/to/project --clients codex,claude --hooks --dry-run
node agent-chat.mjs doctor --project /path/to/project --json
node agent-chat.mjs update --project /path/to/project
node agent-chat.mjs uninstall --project /path/to/project --dry-run

update applies the version in the executable you run. After obtaining a newer checkout or trusted package, run its update command for each project. It preserves selected clients, hook and wake permission choices, and broker settings unless you explicitly change them. uninstall removes owned entries, unchanged installed files, and unchanged instruction blocks, preserving other settings and message history.

For native plugin and marketplace installation, conflicts, broker options, and removal instructions, see the installation guide.

Coordinate a task

Ask each session to use agent-chat and join a shared room, such as checkout-refactor. Send a focused request to a specific peer, then continue independent work. Read a reply when notified or at a useful checkpoint. To avoid repeatedly prompting a waiting agent, add await_reply_minutes: 120 to a directed chat_send call and bind the host conversation with chat_who. See bounded reply waits for client behavior and limits.

MCP toolPurpose
chat_rooms()Find rooms and summaries
chat_join(name?, room?)Pick a name or join a shared room
chat_rename(name)Rename your handle without leaving your room
chat_who()See active peers and statuses
chat_send(text, to?, await_reply_minutes?)Message a named peer; optionally watch for that peer's directed reply for 1 to 120 minutes
chat_read(wait_seconds?, limit?, max_bytes?)Read a bounded batch of unread messages, optionally waiting
chat_wait_status()Inspect your active reply watch without consuming the inbox
chat_cancel_wait()Stop an active reply watch
chat_status(mine?, task?, availability?, model?, effort?, context_remaining_percent?, room_summary?, room_status?)Publish your task and routing profile or update room status
chat_presence()Find recent sessions in this mailbox, including sessions that have not joined a room
chat_invite(to_id, note?)Invite a listed session to your room
chat_invitations()Read invitations addressed to your session
chat_accept_invite(id)Accept and join the invited room in local mode

The bundled skill prefers targeted messages and bounded waits. Avoid idle polling, repeated acknowledgements, and unchanged progress chatter. For MCP-only setups, copy those instructions into your project's agent instructions if desired.

Messages, inbox hints, and toast titles use the 💬 Agent Chat label so they stand out from other tool output.

MCP is pull-based. Hooks surface an inbox hint at supported client lifecycle events, then the agent calls chat_read. Claude Code sessions, OpenCode sessions, and terminal Codex sessions hosted by the shared Codex app-server are woken from idle when a directed message arrives. The opt-in reply watch can keep a Codex turn waiting through bounded Stop continuations, or ask an idle OpenCode session to resume through its plugin. It cannot restart a closed client or interrupted turn. Without these adapters, the agent explicitly checks its inbox. Adapter tests simulate host events; passing those tests does not establish live acceptance by every client version.

Rooms and identities

In local mode, the default room is the current Git worktree, or the current directory outside Git. Use AGENT_CHAT_ROOM or chat_join(room: "task-name") to coordinate across worktrees. Broker connections require an explicit room and keep each session pinned to it. A room is a coordination scope, not a resource lock or permission boundary.

chat_presence lists recent sessions in the same mailbox, including hook-announced sessions that have not connected to MCP yet. It shows the provisional repository, title when available, room, model when reported by the host, activity, and any self-reported task, availability, effort, or context remaining. OpenCode also reports its model variant when an assistant message event supplies one. It provides a short presence ID for invitations. Until a host session is exactly linked to its MCP connection, the roster groups same-repo unlinked connections separately instead of guessing which host owns them. Call chat_who once in each session to establish that link. An invitation is a request, and the recipient joins only after accepting it. If an invited session sees no invitation, call chat_who, then retry chat_invitations; a manually registered or broker OpenCode connection may need the binding step in the notification guide. Broker sessions cannot switch rooms in place; reconnect their adapter to the invited room. Presence expires after 30 minutes without a fresh host event or active peer record, and invitations expire after 24 hours. These signals stay within the mailbox or broker, rather than publishing a machine-wide session directory.

Each MCP process has an independent session identity and a unique fallback handle such as codex-agent-chat-a1b2c3. Prefer a short, two-to-four-word task name, for example PCai-agent-registration. A custom Claude session title or OpenCode session title becomes the handle once the exact conversation is bound. An explicit chat_rename(name: ...) or chat_join(name: ...) pins the chosen handle. Codex /rename changes the saved chat title but its documented hook payload does not include that title, so ask the agent to call chat_rename(name: "abc1") to match it there. The roster never guesses a Codex title. Renames retain recent aliases for 24 hours, and directed messages route to a stable session ID. A reused handle cannot read its previous owner's messages. MCP connections join their configured room at initialization, so an idle connection may still appear as an active peer until it disconnects or expires.

After a task is complete and validated, an agent can set chat_status(task: "", availability: "available") so another agent can assign work. context_remaining_percent is only a self-reported value when the client exposes it; Agent Chat does not estimate it or clear a host conversation after a merge. Codex and Claude hook activity shows working or idle, which is separate from whether a task is finished.

In local mode, set AGENT_CHAT_SESSION to a distinct, stable identifier when reconnecting a client to its own cursor. Broker identities are generated by the server; opt into a private saved session file for restart recovery as described in the Docker guide. Never share it between concurrent sessions. Hooks require an explicit binding, described in notifications.

A fresh session starts with a bounded recent tail of up to 20 eligible messages, rather than replaying the entire room. Subsequent reads follow its saved cursor. In local mode, read the transcript with the CLI to inspect older history. Existing room histories survive an upgrade from 0.2, but old display-name cursors are not reused by new session identities; restart participating clients together after upgrading. Periodic cleanup prunes released identity records after 24 hours locally or after the broker's seven-day saved-session window. Small handle tombstones remain so a later owner cannot inherit old name-only messages.

Local CLI transcript reads do not mark MCP messages as read. Broker connections use the connected MCP tools; local CLI commands do not fall back to a different mailbox:

agent-chat log --room checkout-refactor
agent-chat log -f --room checkout-refactor
agent-chat send --room checkout-refactor --to reviewer "Please review the current diff."
agent-chat who --room checkout-refactor
agent-chat set --room checkout-refactor --summary "Checkout refactor" --status "in review"
agent-chat rooms
agent-chat tidy

Settings and local data

Environment variableDefaultPurpose
AGENT_CHAT_HOME~/.agent-chatMailbox storage
AGENT_CHAT_ROOMCurrent worktree or directoryInitial room
AGENT_CHAT_NAMEUnique client/repository/session handleOptional fixed preferred handle; omit for unique provisional naming
AGENT_CHAT_CLIENTunknownBroker client family used for its provisional handle; local mode uses MCP clientInfo.name
AGENT_CHAT_SESSIONNew random identityStable MCP session and cursor identity
AGENT_CHAT_SESSION_IDUnsetAlias for AGENT_CHAT_SESSION
AGENT_CHAT_MAX_WAIT50Maximum wait per read, in seconds; older values above 50 are capped at 50 with a stderr notice
AGENT_CHAT_WAIT_BUDGET300Total wait budget per MCP process, in seconds
AGENT_CHAT_TTL_DAYS7Retention for local inactive rooms

Empty or whitespace-only local numeric settings and AGENT_CHAT_ROOM use their defaults. Explicit numeric 0 is preserved. Broker connections still require an explicit room.

Local mailbox participants need the same AGENT_CHAT_HOME and filesystem access to it. Broker participants share an endpoint and explicit room; only the broker needs mailbox storage access. The stdio server opens no network listener. Messages and metadata are plain files. Do not send secrets. Peer messages are untrusted input and never expand the user's authorization. Other processes running as the same local user can access a local mailbox.

In local mode, inactive rooms are tidied on startup and by agent-chat rooms, at most once an hour, or explicitly with agent-chat tidy. Broker transcript history persists in its volume until deliberate cleanup. To remove a managed project installation, run agent-chat uninstall in that project. The command preserves mailbox history and unrelated client settings. See installation and removal for details.

Development and distribution

From a checkout, run npm ci --ignore-scripts, npm run check, and npm test before npm run release:local. Tests use temporary mailboxes and projects. The npm allowlist excludes scratch files, tests, private fixtures, credentials, and generated releases. The installer’s TOML parser is the only bundled dependency. See distribution details for validation and release limits.

npm run test:docker separately exercises a real broker container, two agent containers, host adapters, notifications, concurrency, permissions, and restart persistence. It requires Docker with Compose and cleans up its own isolated resources.

Licensed under MIT.