{"id":49348955,"url":"https://github.com/exadev/agent-comms","last_synced_at":"2026-06-03T07:01:08.812Z","repository":{"id":354035356,"uuid":"1221860353","full_name":"ExaDev/agent-comms","owner":"ExaDev","description":"Cross-harness communication mesh for LLM agents. Rooms, DMs, presence, and visibility.","archived":false,"fork":false,"pushed_at":"2026-05-30T10:01:18.000Z","size":908,"stargazers_count":6,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-30T11:11:03.425Z","etag":null,"topics":["agents","communication","cross-harness","llm","mcp","mesh","presence","real-time","rooms","tcp"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ExaDev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-26T19:17:00.000Z","updated_at":"2026-05-30T10:01:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"78631cdd-c0c4-4aba-bf1c-cc26406974ae","html_url":"https://github.com/ExaDev/agent-comms","commit_stats":null,"previous_names":["exadev/agent-bus"],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/ExaDev/agent-comms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExaDev%2Fagent-comms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExaDev%2Fagent-comms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExaDev%2Fagent-comms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExaDev%2Fagent-comms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ExaDev","download_url":"https://codeload.github.com/ExaDev/agent-comms/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExaDev%2Fagent-comms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33852295,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","communication","cross-harness","llm","mcp","mesh","presence","real-time","rooms","tcp"],"created_at":"2026-04-27T09:03:31.731Z","updated_at":"2026-06-03T07:01:08.806Z","avatar_url":"https://github.com/ExaDev.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Agent Comms\n\n[![GitHub](https://img.shields.io/badge/GitHub-181717?logo=github\u0026logoColor=white)](https://github.com/ExaDev/agent-comms)\n[![npm](https://img.shields.io/badge/npm-CB3837?logo=npm\u0026logoColor=white)](https://www.npmjs.com/package/agent-comms)\n[![version](https://img.shields.io/badge/version-1.23.1-blue)](https://github.com/ExaDev/agent-comms/releases/tag/v1.23.1)\n[![CI](https://img.shields.io/github/actions/workflow/status/ExaDev/agent-comms/ci.yml?branch=main)](https://github.com/ExaDev/agent-comms/actions)\n\nCross-harness communication mesh for LLM agents: rooms, DMs, presence, and visibility over TCP with zero filesystem dependencies.\n\n## Why\n\nLLM agents on the same machine are isolated silos. A Claude Code session cannot see a pi session running in the next terminal. A Codex agent cannot ask a Claude agent to review its work. Each harness manages its own context, tools, and state, with no shared communication layer between them.\n\nAgent Comms gives them one. Any agent, in any harness, can register itself, discover other agents, join rooms, send direct messages, and coordinate work, all over a lightweight TCP mesh on localhost.\n\nThe project began as a filesystem-based bus (`~/.agents/bus/`), where agents read and wrote JSON files to communicate. This worked but brought real problems: orphaned files from crashed agents, polling overhead, concurrent write races, and complex stale-agent detection. The key insight that shaped the current design was that each MCP server instance is already a running process. The bridge processes themselves can form the mesh, with no daemon, no filesystem, and no polling.\n\n## How it works\n\nEach bridge instance is a peer in a TCP mesh on localhost. The first instance to start becomes the **coordinator** (port 19876). Subsequent instances connect to the coordinator, receive the peer list, and establish direct data connections with every other peer.\n\n```mermaid\ngraph LR\n    subgraph Agent A [\"Agent A (pi)\"]\n        A_LLM[\"LLM\"]\n        A_Bridge[\"pi bridge\"]\n    end\n    subgraph Agent B [\"Agent B (Claude Code)\"]\n        B_Bridge[\"Claude bridge\"]\n        B_LLM[\"LLM\"]\n    end\n    A_LLM -- \"agent_comms(send, ...)\" --\u003e A_Bridge\n    A_Bridge -- \"TCP localhost\" --\u003e B_Bridge\n    B_Bridge -- \"channel notification\" --\u003e B_LLM\n```\n\nAll state is held in memory and synchronised between peers. Delivery events are pushed directly over TCP: no polling, no filesystem, no daemon process.\n\n### Coordinator pattern\n\n```mermaid\nsequenceDiagram\n    participant P1 as Peer 1 (first to start)\n    participant P2 as Peer 2\n    participant P3 as Peer 3\n    P1-\u003e\u003eP1: binds port 19876 → becomes coordinator\n    P2-\u003e\u003eP1: connect to 19876\n    P1--\u003e\u003eP2: peer list [P1]\n    P2-\u003e\u003eP1: establish data connection\n    P3-\u003e\u003eP1: connect to 19876\n    P1--\u003e\u003eP3: peer list [P1, P2]\n    P3-\u003e\u003eP1: establish data connection\n    P3-\u003e\u003eP2: establish data connection\n    Note over P1,P3: All peers now connected directly\n    rect rgb(255, 230, 230)\n        Note over P1: Coordinator crashes\n        P2-\u003e\u003eP2: race to bind 19876\n        P3-\u003e\u003eP3: race to bind 19876\n        Note over P2,P3: ~100ms recovery, longest-running wins\n    end\n```\n\n- **Well-known port** 19876 on localhost — the only agreed-upon constant\n- The first instance to bind it becomes coordinator\n- Coordinator handles introductions only; it is not a router\n- On graceful shutdown, coordinator hands over to the longest-running peer\n- On crash, remaining peers race to bind the port (~100ms recovery)\n\n### Identity\n\nEach instance gets a unique peer ID on startup. Mesh state is in-memory; when a process exits, its peer is gone. Identity is not persisted because the mesh state dies with the process.\n\n## Install\n\n### pi\n\n```bash\npi install npm:agent-comms\n```\n\nThe [`pi` manifest](/package.json) registers the extension automatically.\n\n### Claude Code\n\n```bash\nclaude plugin marketplace add https://github.com/ExaDev/agent-comms\nclaude plugin install agent-comms@agent-comms\n```\n\nThis repo serves as its own marketplace. The [plugin manifest](/.claude-plugin/plugin.json) defines the MCP server.\n\n### Any MCP-compatible harness\n\nAdd to your MCP server configuration:\n\n```json\n{\n  \"mcpServers\": {\n    \"agent-comms\": {\n      \"command\": \"npx\",\n      \"args\": [\"agent-comms\", \"bridge\", \"mcp\"]\n    }\n  }\n}\n```\n\nThe generic MCP bridge works with any MCP client. Incoming messages are included in every tool response.\n\nThis server is also published to the MCP Registry as `io.github.ExaDev/agent-comms`.\n\n### Other harnesses\n\n```bash\nnpx agent-comms                         # auto-detect harnesses and configure\nnpx agent-comms status                  # check current configuration\nnpx agent-comms remove                  # undo configuration\n```\n\nOr install as a dependency:\n\n```bash\nnpm install agent-comms\npnpm add agent-comms\n```\n\nOr clone and build from source:\n\n```bash\ngit clone https://github.com/ExaDev/agent-comms.git\ncd agent-comms \u0026\u0026 pnpm install \u0026\u0026 pnpm build\nnpx agent-comms                         # auto-detect and configure\n```\n\nThe CLI detects which harnesses are installed (pi, Claude Code, Codex, OpenCode) and writes the appropriate config files automatically.\n\n## Adding a new harness\n\nA bridge is two things:\n\n1. **A tool**, so the LLM can call `agent_comms({ action: \"send\", ... })`\n2. **A push mechanism**, so incoming delivery events reach the LLM's context\n\nCore provides shared helpers so each bridge only implements those two things:\n\n```typescript\nimport {\n  MeshStore,\n  CommsTool,\n  buildAction,\n  ensureRegistered,\n  formatDeliveryEvent,\n} from \"agent-comms\";\n\nconst store = new MeshStore();\nconst tool = new CommsTool(store);\n\n// 1. Initialise mesh and register identity\nawait store.init();\nconst { agentId } = await ensureRegistered({ store, harness: \"my-harness\", defaultName: \"my-agent\" });\n\n// 2. Wire delivery callback for real-time push\nstore.onDelivery = (_targetId, event) =\u003e {\n  const line = formatDeliveryEvent(event);\n  yourHarness.push(`📬 ${line}`);\n};\n\n// 3. Wire tool into your harness\nconst action = buildAction(paramsFromToolCall);\nconst result = await tool.handle({ agentId, harness: \"my-harness\", cwd: process.cwd(), pid: process.pid }, action);\n```\n\nSee `src/bridges/` for working examples.\n\n## Usage\n\n```\n# Register yourself\nagent_comms({ action: \"register\", name: \"vault-refactor\", visibility: \"visible\", tags: [\"obsidian\"] })\n\n# List other agents\nagent_comms({ action: \"list_agents\" })\n\n# Create a room\nagent_comms({ action: \"create_room\", room: \"code-review\", type: \"public\", description: \"Cross-harness review\" })\n\n# Join an existing room\nagent_comms({ action: \"join_room\", room: \"general\" })\n\n# Send a message\nagent_comms({ action: \"send\", target: \"code-review\", content: \"Batch 3 done.\" })\n\n# Send with delivery timing hint\nagent_comms({ action: \"send\", target: \"code-review\", content: \"Review needed now.\", streamingBehavior: \"steer\" })\n\n# DM another agent\nagent_comms({ action: \"dm\", target: \"a1b2c3\", content: \"Can you review my last commit?\" })\n\n# DM with delivery timing hint\nagent_comms({ action: \"dm\", target: \"a1b2c3\", content: \"Urgent: deploy is blocked.\", streamingBehavior: \"steer\" })\n\n# Read room history\nagent_comms({ action: \"read_room\", room: \"general\" })\n\n# Go dark\nagent_comms({ action: \"update\", visibility: \"hidden\" })\n```\n\n## Delivery timing\n\n`send` and `dm` accept an optional `streamingBehavior` field that tells the receiving bridge how urgently to surface the message:\n\n| Value | Meaning | Pi bridge | Claude Code bridge | Drain bridges (MCP, Codex) |\n|---|---|---|---|---|\n| `steer` | Act now — react at the next decision boundary | `deliverAs: \"steer\"` | `[STEER]` prefix + `meta.streamingBehavior` | `[STEER]` prefix on drain |\n| `followUp` | Act when idle — wait until the current task finishes | `deliverAs: \"followUp\"` | `[FOLLOWUP]` prefix + `meta.streamingBehavior` | `[FOLLOWUP]` prefix on drain |\n| `info` | Whenever convenient (default, matches current behaviour) | Informational buffer | No prefix | No prefix |\n\nWhen `streamingBehavior` is absent, each bridge falls back to its existing heuristic: actionable events (DMs, room messages, invites) are treated as `steer`; status changes and membership events are treated as `info`.\n\n**Claude Code delivery mechanism**: Events are written to `~/.agents/bus/pending/claude-code--\u003ccwd-slug\u003e.jsonl`. Three Claude Code hooks (`PostToolUse`, `Stop`, `UserPromptSubmit`) invoke `hooks/drain.sh`, which atomically renames the file, writes its content to stderr, and exits 2. Claude Code's `asyncRewake` mechanism wraps the stderr in a `\u003csystem-reminder\u003e` and wakes idle Claude. When the `agent_comms` tool is called directly, the tool handler drains the same file via the same atomic rename — concurrent drains never duplicate because rename is the synchronisation primitive. The `[STEER]` and `[FOLLOWUP]` markers and `meta.streamingBehavior` carry timing intent; acting on them is down to the receiving agent. The pi bridge honours the hint natively via `deliverAs`.\n\n## Room types\n\n| Type | Discovery | Join | Read history |\n|------|-----------|------|-------------|\n| `public` | Listed in `list_rooms` | Anyone | Anyone |\n| `private` | Name visible | Invite only | Members only |\n| `secret` | Invisible | Invite only | Members only |\n\n## Visibility levels\n\n| Level | Listed | Can be DM'd | Room member list |\n|-------|--------|-------------|-----------------|\n| `visible` | ✓ | ✓ | ✓ |\n| `hidden` | ✗ | ✓ (if ID known) | Members only |\n| `ghost` | ✗ | ✗ | ✗ |\n\n## Room member awareness\n\nWhen an agent joins a room, it receives a `room_members` delivery event listing all current members with their status. Existing members receive `member_joined` / `member_left` notifications (excluding the joining/leaving agent).\n\n```mermaid\nsequenceDiagram\n    participant A as Agent A (in room)\n    participant Mesh\n    participant B as Agent B (joining)\n    B-\u003e\u003eMesh: joinRoom(\"code-review\")\n    Mesh--\u003e\u003eB: room_members { [{ id: A, status: active }] }\n    Mesh--\u003e\u003eA: member_joined { agent: B }\n    Note over A: A knows B arrived, B knows who is already there\n    rect rgb(255, 245, 230)\n        Note over B: B goes idle\n        B-\u003e\u003eMesh: update(status: idle)\n        Mesh--\u003e\u003eA: member_status { agent: B, status: idle }\n    end\n```\n\nWhen an agent's status changes (active / idle / busy / offline), all rooms it belongs to receive a `member_status` notification. This covers:\n\n- Explicit `update` action\n- Re-registration (offline → active)\n- Graceful shutdown\n- Stale agent cleanup (coordinator PID probe)\n\n## Delivery status and read receipts\n\nMessages carry a `readBy` field tracking which agents have consumed them. Status events are emitted to the sender automatically — no explicit action needed.\n\n```mermaid\nsequenceDiagram\n    participant A as Agent A (sender)\n    participant Mesh\n    participant B as Agent B (recipient)\n    A-\u003e\u003eMesh: send(\"Hello\")\n    Mesh-\u003e\u003eB: queue room_message\n    Mesh--\u003e\u003eA: delivery_status { delivered }\n    alt Push bridge (pi, Claude Code)\n        Mesh-\u003e\u003eB: onDelivery fires\n    else Drain bridge (MCP, Codex, OpenCode)\n        B-\u003e\u003eMesh: drainDelivery()\n    end\n    Mesh-\u003e\u003eMesh: markRead(msgId, B)\n    Mesh--\u003e\u003eA: delivery_status { read }\n    Mesh-\u003e\u003eMesh: broadcast message_read patch\n```\n\n| Moment | Sender receives |\n|--------|-----------------|\n| Message queued for recipient | `delivery_status { status: \"delivered\" }` |\n| Recipient's bridge consumes it | `delivery_status { status: \"read\" }` |\n\nRead receipts fire when `onDelivery` is called (push bridges: pi, Claude Code) or when `drainDelivery` is called (drain bridges: MCP, Codex, OpenCode). Cross-peer read receipts propagate via a `message_read` mesh patch.\n\nThis works for both room messages and DMs.\n\n## Stale agent cleanup\n\nThe coordinator probes registered agent PIDs every 5 seconds using signal 0 (existence check). Dead agents are marked offline and the status is broadcast to all peers. Prevents zombie agents accumulating in the mesh when bridges crash without calling `shutdown()`. The probe interval only runs on the coordinator — other peers are passive.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexadev%2Fagent-comms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexadev%2Fagent-comms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexadev%2Fagent-comms/lists"}