ghul0/open-agent-bridge-native

Multi-agent communication hub. Connects Claude Code, OpenAI Codex and Google Gemini CLI sessions through a shared local registry so they can message each other, share inboxes and inject prompts across processes — backed by an MCP adapter, ACP/app-server

★ 0Forks 0GitHub ↗Compare

README

open-agent-bridge

version node license TypeScript

Let Claude Code, OpenCode, Codex, and Antigravity (agy) talk to each other — over MCP, locally, bidirectionally.

open-agent-bridge is a local communication hub for AI agents. It provides service discovery, bidirectional messaging, and MCP tool exposure so that Claude Code, OpenCode, Codex, and Google Antigravity CLI (agy) running in separate projects can delegate tasks, exchange context, and coordinate work without leaving the local machine.

Sessions can be scoped by --identity: every launcher accepts an identity namespace, and agents only see channel messages from peers sharing the same namespace (default global). This gives each ticket/agent its own private inbox.


Table of Contents

TL;DR — 60-second quickstart

# 1. Install the CLI globally (from a cloned repo) — builds and puts `oab` on your PATH
bash bin/install.sh

# 2. Set up your project — writes .mcp.json and starts the registry in the background
cd /path/to/your/project
oab init                      # interactive wizard (identity + clients)

# 3. Launch Claude Code wired to the bridge
oab claude --identity dev

That's it — no dedicated terminal for the registry, no manual .mcp.json editing. Claude Code now has six MCP tools: agent_bridge_guide, list_agents, channel_inbox, channel_clear, message_client_session, reply.

Prefer explicit commands? The wizard is optional:

oab up                                    # registry in the background (idempotent)
oab mcp config --write --identity dev     # write/merge .mcp.json with an identity
oab claude --identity dev                 # launch Claude with the channel enabled
oab status                                # registry health + agents + .mcp.json
oab doctor                                # diagnose your environment
oab down                                  # stop the background registry

For OpenCode, install the push plugin (the wizard does this for you when you select it):

oab opencode install-plugin --project .

Why open-agent-bridge?

  • Local-first, zero infrastructure. Everything runs on localhost. No cloud relay, no auth tokens, no subscriptions. The registry binds to :4999; agents bind to :5001+.
  • Native MCP integration. Exposes the agent network as first-class MCP tools via stdio transport, so Claude Code, OpenCode, Codex, Antigravity, and other MCP clients can use the same bridge tools.
  • Bidirectional channel, not fire-and-forget. Messages carry a conversationId, delivery ACKs are tracked in SQLite, and the reply tool closes the loop back to the sender. Claude Code can ask Codex a question and receive the answer in the same conversation thread.
  • Per-identity inboxes. Every launcher accepts --identity <ns>. Sessions only see channel messages from peers in the same namespace — a hard wall, even for directed messages. list_agents is filtered to the namespace too. Default namespace is global.
  • Bounded inbox, no saturation. channel_inbox is capped (limit=25, previews only by default) so a flood of pending messages never buries new ones, and channel_clear plus an automatic terminal sweep keep each inbox light.
  • OpenCode plugin bridge. The opencode install-plugin command installs a local OpenCode plugin that registers an OpenCode bridge client, opens a registry WebSocket, and injects incoming channel messages into the active OpenCode session with session.prompt_async.
  • Codex app-server bridge. CodexAppServerBridge injects incoming channel messages directly into the Codex app-server via turn/start JSON-RPC, so Codex actually processes requests rather than just receiving raw text.
  • Antigravity native push. AntigravityLsBridgeService (antigravity ls-push) delivers channel messages straight into a live agy TUI through its Cascade Language Server (SendUserCascadeMessage) — native push, no tmux, no manual "check inbox".

How it works

Claude Code / OpenCode / Codex / Antigravity (agy) / Dashboard
              |
              v
    ┌─────────────────────────────────────┐
    │       RegistryServer :4999          │
    │  HTTP  /agents /health /channel/*   │
    │  WS    /ws  (relay + broadcast)     │
    │  SQLite ~/.open-agent-bridge/registry.sqlite│
    └──────┬─────────────┬───────────────┘
           │             │
           v             v
  ┌────────────────┐  ┌──────────────────────────┐
  │  AgentServer   │  │  McpAgentBridge (stdio)   │
  │  :5001+        │  │  list_agents              │
  │  A2A JSON-RPC  │  │  channel_inbox            │
  │  Skills        │  │  channel_clear            │
  │  AG-UI SSE     │  │  message_client_session   │
  │                │  │  reply                    │
  └────────────────┘  └──────────────────────────┘

  ┌──────────────────────────────────────────────┐
  │  CodexAppServerBridge  (separate daemon)      │
  │  Spawns: codex app-server (:4500)             │
  │  Connects: WS to registry + WS to app-server  │
  │  Injects channel messages via turn/start       │
  └──────────────────────────────────────────────┘

  ┌──────────────────────────────────────────────┐
  │  OpenCode plugin bridge                       │
  │  Auto-loads from .opencode/plugins/           │
  │  Connects: WS to registry                     │
  │  Injects via session.prompt_async             │
  └──────────────────────────────────────────────┘

  ┌──────────────────────────────────────────────┐
  │  AntigravityLsBridgeService (ls-push daemon)  │
  │  Discovers agy's Cascade Language Server port │
  │  Connects: WS to registry                     │
  │  Injects via SendUserCascadeMessage (live TUI)│
  └──────────────────────────────────────────────┘

Runtime components — AgentServer, McpAgentBridge, CodexAppServerBridge, AntigravityLsBridgeService, and the OpenCode plugin bridge — connect independently to the registry. None depends on the others being present.

Message flow — Claude Code delegates a task to Codex:

# sequence
Claude Code
  calls message_client_session MCP tool
  McpAgentBridge posts ChannelMessage to RegistryServer /channel/messages
  RegistryServer broadcasts via WS to all subscribers
  CodexAppServerBridge receives channel.message event
  CodexAppServerBridge calls turn/start on codex app-server
  Codex processes the turn and produces a reply
  CodexAppServerBridge sends reply ChannelMessage back to registry
  McpAgentBridge receives channel.message with the reply
  McpAgentBridge surfaces reply in channel_inbox
  Claude Code calls reply tool to close the conversation thread

Getting started

Requirements

  • Node.js >=22
  • pnpm >=9 and git (to build and install)
  • (Optional) claude, codex, opencode, or agy in $PATH for the clients you want to bridge

Install — Path A: from scratch (nothing cloned yet)

# 1. Prerequisites
node -v                  # must be >= 22
npm install -g pnpm      # if you don't have pnpm

# 2. Clone
git clone <repo-url>
cd open-agent-bridge

# 3. Build + put `oab` / `open-agent-bridge` on your PATH (one step)
bash bin/install.sh
#    equivalent to: pnpm install && pnpm run build:all && npm install -g .

# 4. Verify
oab --version
oab doctor

# 5. Use it in any project
cd /path/to/your/project
oab init                 # wizard: identity + clients → writes .mcp.json + starts the registry
oab claude --identity dev

Install — Path B: you already cloned the repo

cd open-agent-bridge
git pull                 # optional: get the latest
pnpm install             # (re)install dependencies
pnpm run build:all       # compile CLI + dashboard
npm install -g .         # expose oab / open-agent-bridge on PATH
#    (these four steps = bin/install.sh)

oab doctor               # validate environment + PATH

Updating later: cd open-agent-bridge && git pull && pnpm install && pnpm run build:all && npm install -g .

oab not found after install? bin/install.sh and oab doctor print the exact export PATH=... line and which rc file to add it to. With npm, the global bin dir ($(npm prefix -g)/bin) is usually already on your PATH.

Contributor mode (no global install): pnpm run dev -- <command> still works from the repo (e.g. pnpm run dev -- doctor).

What oab init does

In one step it: detects your project, asks for a channel identity and which clients to wire up, writes/merges .mcp.json (with AGENT_BRIDGE_IDENTITY baked in), starts the registry as a background daemon (no terminal to keep open), configures plugin-based clients (OpenCode/Antigravity), registers the MCP server with Codex via codex mcp add (Codex ignores .mcp.json — it only loads servers from ~/.codex/config.toml), and prints the exact launch command per client.

The registry exposes:

Endpoint Description
http://localhost:4999 HTTP REST (/agents, /health, /channel/*)
ws://localhost:4999/ws WebSocket relay and channel broadcast
http://localhost:4999/dashboard Dashboard SPA (requires pnpm run build:all)

Manage its lifecycle with oab up / oab down / oab status. To run an agent server for richer project skills, use oab start /path/to/project (add --claude for the Claude Code AI backend).


Launching each client

Once the registry is running and .mcp.json is in place, each AI client connects to open-agent-bridge through its own startup command.

Claude Code

The easy way — ensures the registry is up, writes .mcp.json if missing, sets the identity, and launches Claude:

oab claude --identity dev

Equivalent manual command:

claude --dangerously-load-development-channels server:open-agent-bridge

The --dangerously-load-development-channels flag tells the Claude CLI to activate the MCP server named server:open-agent-bridge as a development notification channel. The name open-agent-bridge matches the entry in .mcp.json. This enables:

  • The six MCP tools (agent_bridge_guide, list_agents, channel_inbox, channel_clear, message_client_session, reply).
  • Push notifications via notifications/claude/channel — incoming channel messages appear as <channel> blocks inline in the terminal.

The MCP adapter registers the Claude Code session automatically on the first initialize handshake.

OpenCode

OpenCode needs two pieces:

  1. An MCP server entry so the OpenCode model can call list_agents, message_client_session, channel_inbox, and reply.
  2. The local OpenCode plugin so incoming channel messages can be pushed into the active OpenCode session automatically.

Install the plugin into a project:

open-agent-bridge opencode install-plugin --project "/absolute/path/to/your/project"

Or install it globally:

open-agent-bridge opencode install-plugin --global

The command copies agent-bridge.ts into .opencode/plugins/ (or ~/.config/opencode/plugins/) and merges the plugin dependencies into the matching .opencode/package.json / global package file. Local plugins are auto-loaded by OpenCode, so no opencode.json plugin entry is required. Restart OpenCode after installing.

The plugin registers as an OpenCode bridge client (clientVersion: "opencode-plugin-bridge"), opens a WebSocket to the registry, sends heartbeats, and injects matching channel.message events into the active session through ctx.client.session.prompt_async. It also re-syncs missed pending messages periodically, deduplicates message IDs, and sends delivered_to_bridge / displayed_to_client ACKs back to the registry.

When OpenCode is connected through MCP, list_agents(includeClients=true) shows rows like [OpenCode] and [OpenCode bridge]. Routing is automatic: sending to the OpenCode MCP client is redirected to the sibling plugin bridge when present, while the MCP client can still use channel_inbox and reply.

Codex

open-agent-bridge codex start --project "/absolute/path/to/your/project"

This single command:

  1. Starts the registry (if not already running).
  2. Launches the CodexAppServerBridge daemon, which spawns codex app-server on :4500.
  3. Registers the Codex client session in the registry.
  4. Opens the Codex TUI.

From that point, message_client_session routes to the Codex bridge automatically (it has the highest delivery priority).

If you prefer to start Codex separately:

# Terminal A — bridge only
open-agent-bridge codex app-bridge --project "/absolute/path/to/your/project"

# Terminal B — Codex TUI connecting to the app-server
codex --remote ws://127.0.0.1:4500

A plain codex command starts an isolated session. The registry may still see an inner MCP client, but the bridge cannot inject automatic turns into that TUI. If no remote TUI attaches to the bridge app-server, queued messages fail with a detail that tells you to run codex --remote ws://127.0.0.1:<port>.

Antigravity (agy)

Google's Antigravity CLI (agy) connects through native MCP plus a Cascade Language Server push daemon. There is no ACP and no tmux scraping — agy loads the bridge as an MCP server, and ls-push injects channel messages straight into the live TUI.

1. Install the MCP config into the workspace:

open-agent-bridge antigravity install-plugin --project "/absolute/path/to/your/project"
# add --identity ticket-123 for a private namespace
# add --global to install into ~/.gemini/antigravity-cli instead of the workspace .agents/

This writes .agents/mcp_config.json pointing agy at open-agent-bridge mcp start. Restart agy in the workspace so it loads the server. (No hooks.json is written — agy's hook schema differs and the Stop-hook path is superseded by ls-push.)

2. Start the native push daemon so messages arrive in the live TUI automatically:

open-agent-bridge antigravity ls-push --project "/absolute/path/to/your/project"
# match the identity used at install: --identity ticket-123

ls-push discovers agy's Cascade Language Server port (from ~/.gemini/antigravity-cli/log/cli-*.log, with an ss fallback), resolves the active cascadeId, and on each pending channel message calls SendUserCascadeMessage so agy processes it as a real user turn — no manual "check inbox".

Requires an open conversation. agy must be running with an active trajectory in the workspace for the language server to accept an injected turn. Occasionally agy's model executor errors mid-turn (an agy-internal limitation) and the user nudges it with a plain ok.

There are also antigravity hook-stop / antigravity hook-session-start subcommands available for hook-driven setups, but ls-push is the recommended delivery path.

Identity scoping (--identity)

By default every session lives in the global namespace and sees all global channel traffic. Passing --identity <name> puts a session in a private namespace: it only sees messages from peers that share the exact same identity, and only those peers appear in its list_agents. This is a hard wall — even a message addressed directly to an agentId is dropped if the namespaces differ.

# MCP launcher (Claude Code, generic clients) — also honours AGENT_BRIDGE_IDENTITY env var
open-agent-bridge mcp start --project /path --identity ticket-123

# Antigravity (install + push must share the identity)
open-agent-bridge antigravity install-plugin --project /path --identity ticket-123
open-agent-bridge antigravity ls-push        --project /path --identity ticket-123

# Codex
open-agent-bridge codex start --project /path --identity ticket-123

How it works:

  • identity is folded into the stable agentId hash (name + project + identity), so each (client, project, identity) is a distinct registry entry — two sessions of the same client/project no longer collide.
  • The hard wall is enforced in every client profile's acceptsChannelMessage; the inbox reads the local store, which is only populated with accepted (same-namespace) messages.
  • Outbound messages are stamped with the sender's identity, so a ticket-123 reply stays inside ticket-123.

Implication: to delegate to an agy running in ticket-123, the sender must also run in ticket-123. A multi-ticket coordinator would have to send per namespace.

Dashboard

# One-time: build the Vue SPA
pnpm run build:all

# Open in browser
open-agent-bridge dashboard
# → http://localhost:4999/dashboard

For hot-reload development: pnpm run dev:dashboard (Vite on :5173, proxies API calls to :4999).


MCP integration

.mcp.json

open-agent-bridge mcp config generates the .mcp.json entry. The AGENT_BRIDGE_PROJECT environment variable tells the MCP adapter which project to associate the Claude Code session with:

{
  "mcpServers": {
    "open-agent-bridge": {
      "command": "node",
      "args": ["/absolute/path/to/dist/cli/index.js", "mcp", "start"],
      "env": {
        "AGENT_BRIDGE_PROJECT": "/absolute/path/to/your/project"
      }
    }
  }
}

For a globally installed version, pass --global:

node dist/cli/index.js mcp config --global --write

Which produces:

{
  "mcpServers": {
    "open-agent-bridge": {
      "command": "pnpm",
      "args": ["dlx", "open-agent-bridge", "mcp", "start"],
      "env": {
        "AGENT_BRIDGE_PROJECT": "/absolute/path/to/your/project"
      }
    }
  }
}

The mcp config command writes absolute paths for the current machine. Commit the result to the project or adapt the same open-agent-bridge mcp start command for your MCP client settings.

Available tools

Tool Description
agent_bridge_guide Built-in MCP usage guide. Returns setup, send/reply workflow, ACK semantics, and troubleshooting by topic.
list_agents Discover connected agents and client sessions in the current identity namespace. Client rows include peer labels like [Claude Code], [OpenCode], [OpenCode bridge], [Codex inner], [Codex bridge], and [Antigravity inner], plus the unique 8-char suffix of the agentId (e.g. [3fdb0c6a]) so two same-project peers stay visually distinct. Bridge routing is automatic.
channel_inbox Inspect channel conversations. Bounded by default (limit=25, previews only) so a flood of pending messages never buries new ones; the response carries a summary block and a replyWith hint per pending entry.
channel_clear Clear handled conversations from the inbox so new ones keep surfacing. Scope answered / failed / all (never bulk-clears unanswered work) or a specific conversationId.
message_client_session Send a message to a named client session. Automatically resolves OpenCode/Codex inner clients to their bridge daemon when available and infers expectsResponse when omitted.
reply Respond to an incoming channel message, correlating by conversationId.

Check live tool and agent status at any time:

pnpm run dev -- mcp status

Agent-bridge skill (Claude Code only)

The MCP instructions block intentionally stays compact. The deeper guide — peer-type semantics, send/reply patterns, troubleshooting delivered_to_bridge stalls, OpenCode plugin setup, and Codex --remote setup — lives in skill files loaded on demand by each client:

  • OpenCode: .opencode/skills/open-agent-bridge/SKILL.md (project-local, auto-scanned by OpenCode)
  • Codex: ~/.codex/skills/open-agent-bridge/SKILL.md (global Codex skill)

Each skill file includes a references/ folder with peer-type taxonomy, send/reply patterns, delivery states, routing rules, troubleshooting ladder, and the injection prompt contract.

The same content can also be queried at runtime from any client via the agent_bridge_guide MCP tool — useful for agents that don't load skill files.


Channels

Channels are the bidirectional messaging layer of open-agent-bridge. They let any agent or client session — Claude Code, OpenCode, Codex, Antigravity (agy), the dashboard — send and receive structured conversational messages through the registry, with full delivery tracking and SQLite persistence.

This is the core feature of the project. Everything else (MCP tools, bridge daemons, dashboard chat) is built on top of it.

Conversation model

Every message belongs to a conversation identified by a conversationId. A conversation is a thread of related turns between two parties — like a back-and-forth between Claude Code and Codex.

Conversation abc-123
  ├── Message m1  from: client-dashboard-ui  to: client-codex-bridge-xyz  "Review routes.ts"
  ├── Ack     a1  state: delivered_to_bridge
  ├── Message m2  from: client-codex-bridge-xyz  to: client-dashboard-ui  "Found 2 issues…"
  └── Ack     a2  state: answered

Key properties:

  • conversationId is stable for the entire thread — use it to continue an existing conversation.
  • replyTo links a message to the specific messageId it responds to.
  • expectsResponse: true marks a message as pending until a reply arrives. In MCP sends, the adapter defaults to true when you omit the field (agent-to-agent messages are a conversation, not a log stream). Only explicit fire-and-forget markers in the text — FYI, no reply, sin respuesta, just letting you know, etc. — downgrade it to false. Pass the flag explicitly for deterministic behavior.
  • requiresAck: true requests an explicit delivery acknowledgement from the recipient.
  • All messages and ACKs are persisted in SQLite and survive registry restarts.

Message payload

interface ChannelMessage {
  conversationId:   string;    // Thread identifier — stable per conversation
  messageId:        string;    // Unique per message (UUID)
  fromAgentId:      string;    // Sender's registry ID (required)
  fromAgentName?:   string;    // Human-readable sender name
  toAgentId?:       string;    // Recipient's registry ID (omit for broadcast)
  replyTo?:         string;    // messageId this turn responds to
  taskId?:          string;    // Optional task association
  kind:             "chat" | "task_request" | "task_result" | "ack" | "error" | "presence";
  content:          string;    // Message body
  meta?:            Record<string, unknown>;  // Arbitrary metadata
  createdAt:        number;    // Unix ms timestamp (set by registry)
  expiresAt?:       number;    // Expiry timestamp in ms
  requiresAck?:     boolean;   // Request delivery acknowledgement
  expectsResponse?: boolean;   // Sender awaits a reply (default true; FYI markers downgrade to false)
  attemptCount?:    number;    // Delivery attempt counter (for retries)
}

Delivery states

Each message transitions through delivery states tracked via ChannelAck records:

queued
  └─→ delivered_to_bridge       (bridge daemon received it)
        └─→ displayed_to_client (client session received it)
              ├─→ answered       (recipient replied)
              └─→ failed         (delivery or reply failed)

ACK records carry: conversationId, messageId, state, actorId, actorType (registry | bridge | client | agent), and timestamp.

MCP tool reference

These are the six tools Claude Code gets after configuring open-agent-bridge as an MCP server.

agent_bridge_guide

Read the usage guide exposed by the MCP server itself. This is the quickest way for an agent to refresh the protocol contract without relying on external docs.

Parameters:
  topic?  "overview" | "setup" | "send" | "reply" | "acks" | "troubleshooting" | "all"
          Section to return (default: "all")

list_agents

Discover what agents and client sessions are currently connected.

Parameters:
  skill?        string   — Filter to agents that expose this skill
  project?      string   — Filter by project name or path substring
  healthyOnly?  boolean  — Only show healthy agents (default: true)
  includeClients? boolean — Include passive client sessions (default: false)

message_client_session

Send a channel message to a client session. The tool resolves the best target automatically.

Parameters:
  message       string   — Message body (required)
  project?      string   — Project name or path to identify the target session
  clientId?     string   — Exact agentId of the target (skips all resolution)
  clientType?   string   — Disambiguate when a project has multiple sessions
                           ("claude-code" | "opencode" | "codex" | "antigravity")
  conversationId? string — Continue an existing conversation thread
  replyTo?      string   — messageId this message responds to
  taskId?       string   — Associate with a task
  expectsResponse? boolean — Whether you expect a reply. Default: true. Only explicit
                           FYI/no-reply markers in the text downgrade it to false.
  timeoutMs?    number   — Ms before the message expires without a reply

expectsResponse controls whether the receiver should answer:

  • Default is true. Agent-to-agent channel messages are a conversation contract — the receiver should reply unless the sender opts out. A bare "hola" or "build done" will wake up OpenCode/Codex's bridge (or push into a live agy TUI) with a "reply required" prompt, and Claude Code will surface it as a pending conversation.
  • Pass false (or include explicit FYI markers — FYI, no reply, sin respuesta, just letting you know, for your information, no need to reply) for fire-and-forget. The bridge daemon will inject the message as informational context and suppress the receiver's outbound reply.
  • For deterministic workflows pass the flag explicitly — text inference is a convenience for proactive sends, not a contract.

The mapping from message text to the boolean lives in inferExpectsResponse and is unit-tested under src/__tests__/peer-type-label.test.ts.

Target resolution order:

  1. clientId provided → direct lookup, no further resolution
  2. No clientId and no project, but conversationId provided → resolves from conversation history
  3. project provided → filter by project path/name match
  4. Multiple matches → sorted by client type priority: bridge daemons first (app-server-bridge, opencode-plugin-bridge), then claude-code > claude > opencode > codex-cli > codex > antigravity

Returns: toAgentId, conversationId, messageId, deliveryState.

channel_inbox

Inspect channel conversations. By default it shows only pending (awaiting-reply) conversations and is bounded so a backlog of messages can never saturate the agent's context and bury new arrivals.

Parameters:
  pendingOnly?    boolean — Only show conversations awaiting reply (default: true).
                            Set false for the GLOBAL view of all tracked conversations.
  expiredOnly?    boolean — Only show conversations past their expiry
  limit?          number  — Max conversations returned (default: 25). Applies to ALL views.
  includeMessages? boolean — Include each conversation's full message history
                            (default: false — only previews + replyWith are returned, to keep
                            the inbox light). Set true, ideally with a narrow view, to read threads.

The response is wrapped with a summary block plus the conversations array. When the result is truncated, the summary hints at channel_clear and the global view:

{
  "summary": {
    "view": "pending",
    "total": 42,
    "shown": 25,
    "truncated": true,
    "hint": "Use channel_clear({scope:'answered'|'failed'|'all'}) to clear handled threads so new ones surface, or raise `limit`. Use channel_inbox(pendingOnly=false) for the global view."
  },
  "conversations": [
    {
      "conversationId": "abc-123",
      "status": "pending",
      "lastMessagePreview": "Review src/api/routes.ts for N+1 queries",
      "replyWith": {
        "agentId": "client-codex-bridge-xyz",
        "conversationId": "abc-123",
        "replyTo": "msg-456"
      }
    }
  ]
}

channel_clear

Clear handled conversations from this agent's inbox so saturation never buries new messages. Suppresses them from channel_inbox (reversible at the registry) and removes them from the local store. Only ever touches the calling agent's own tracked conversations.

Parameters:
  scope  string — "answered"  → clear only answered threads
                  "failed"    → clear failed + locally-expired threads
                  "all"       → clear every NON-pending thread (never bulk-clears unanswered work)
                  <conversationId> → clear exactly that one, even if pending (explicit intent)

In addition to manual clearing, the registry runs an automatic terminal sweep: answered/failed conversations older than the retention window are pruned periodically so every inbox stays light without intervention.

reply

Respond to a pending channel message. Use the replyWith values from channel_inbox.

Parameters:
  agentId        string  — fromAgentId of the message you're replying to (required)
  conversationId string  — conversationId from channel_inbox replyWith (required)
  replyTo        string  — messageId from channel_inbox replyWith (required)
  message        string  — Your reply content (required)
  taskId?        string  — Task association (optional)
  skillId?       string  — Fallback skill for task invocation (optional)

HTTP API

The registry exposes a REST API at http://localhost:4999.

Agent management:

Method Path Description
POST /agents Register a new agent
DELETE /agents/:id Deregister an agent
POST /agents/:id/heartbeat Update agent heartbeat
GET /agents List all registered agents
GET /agents/:id Get a single agent by ID
POST /agents/:id/message Relay a task message to a specific agent
POST /agents/:id/ag-ui Proxy AG-UI SSE stream for a specific agent
GET /health Registry liveness check

Channel operations:

Method Path Description
POST /channel/messages Create and broadcast a new channel message
POST /channel/acks Record a delivery acknowledgement
GET /channel/conversations List conversations (?pending=true for pending only)
GET /channel/conversations/:id Full conversation snapshot with messages and ACKs
POST /channel/conversations/:id/suppress Hide a conversation from inbox listings
DELETE /channel/conversations/:id/suppress Restore a suppressed conversation
POST /channel/messages/:convId/:msgId/retry Re-deliver a message (increments attemptCount)
POST /notify-claude Shorthand: send a channel notification to a Claude Code session

Create a message (POST /channel/messages):

{
  "fromAgentId": "my-agent-id",
  "toAgentId": "client-dashboard-ui",
  "kind": "chat",
  "content": "Task complete. Found 3 endpoints.",
  "conversationId": "abc-123",
  "replyTo": "msg-456",
  "expectsResponse": false,
  "requiresAck": true
}

WebSocket events

Connect to ws://localhost:4999/ws to receive real-time channel events. Send { "type": "identify", "agentId": "<your-id>" } immediately after connecting to enable targeted delivery.

Event type Payload When
channel.message ChannelMessage A new message was posted to the registry
channel.ack ChannelAck A delivery state changed
channel.conversation.suppressed { conversationId } A conversation was hidden
channel.conversation.revived { conversationId } A suppressed conversation was restored

Targeted delivery: If a message has toAgentId, the registry delivers it directly to that agent's WS connection before broadcasting to all subscribers.

End-to-end example

Claude Code asks Codex to review a file, waits for the answer, and closes the thread:

# Step 1 — discover what's connected
list_agents
→ "codex" session active on project /projects/my-app

# Step 2 — send the request
message_client_session(
  project: "my-app",
  message: "Review src/api/routes.ts for N+1 queries",
  expectsResponse: true,
  timeoutMs: 120000
)
→ conversationId: "abc-123", messageId: "msg-001", deliveryState: "queued"

# Step 3 — Codex processes the turn via app-server bridge, sends reply
# (happens automatically via CodexAppServerBridge → turn/start JSON-RPC)

# Step 4 — check the inbox
channel_inbox
→ conversationId: "abc-123", status: "pending"
  lastMessagePreview: "Found 2 N+1 issues in getUserPosts() and..."
  replyWith: { agentId: "client-codex-bridge-xyz", conversationId: "abc-123", replyTo: "msg-002" }

# Step 5 — acknowledge and close the thread
reply(
  agentId: "client-codex-bridge-xyz",
  conversationId: "abc-123",
  replyTo: "msg-002",
  message: "Thanks, applying the fix now."
)
→ Reply sent. Conversation answered.

Client bridges

Each AI client has a different bridge mechanism depending on how it receives channel messages. All clients ultimately use the same channel protocol — what differs is how the message surfaces to the human.

Claude Code bridge (push)

Claude Code's bridge is built into the MCP adapter itself — no separate daemon required.

When Claude Code connects to open-agent-bridge via MCP, the McpAgentBridge intercepts the MCP initialize handshake. It reads the client name (Claude Code, version, workspace roots), builds a stable agentId (client-claude-code-{hash}), and registers it as a client session in the registry automatically.

From that point on, any channel message addressed to that agentId is pushed to the Claude terminal as an MCP notification:

Incoming channel message
  → ChannelClientRuntime receives channel.message event via WS
  → ClaudeClientProfile.mapChannelMessage() formats it
  → server.notification({ method: "notifications/claude/channel", params: { content, meta } })
  → Claude terminal renders a <channel> block inline

What Claude sees in its terminal:

<channel source="open-agent-bridge" from_agent="my-agent-id" conversation_id="abc-123" message_id="msg-001">
  Task complete. Found 3 N+1 queries in getUserPosts().
</channel>

Claude then uses the reply MCP tool to respond, closing the conversation thread.

Delivery ACKs sent automatically:

  1. delivered_to_bridge — MCP adapter received the message
  2. displayed_to_client — notification push to Claude succeeded

If Claude Code is not yet connected when a message arrives, the adapter buffers up to 100 messages and replays them on initialize.

Sending to Claude Code from another agent — use the notify-claude skill or POST /notify-claude:

# From any AgentServer via notify-claude skill
{
  "targetProject": "/path/to/my-project",
  "content": "Build finished. 3 tests failed in auth module.",
  "expectsResponse": true
}

# Or directly via HTTP
POST http://localhost:4999/notify-claude
{
  "agentId": "my-agent",
  "toAgentId": "client-claude-code-abc123",
  "content": "Build finished. 3 tests failed in auth module.",
  "conversationId": "conv-xyz",
  "expectsResponse": true
}

OpenCode bridge (plugin push)

OpenCode's bridge is a local plugin installed by:

pnpm run dev -- opencode install-plugin --project /path/to/opencode-project
# or, after build/install:
open-agent-bridge opencode install-plugin --project /path/to/opencode-project

The installer copies the bridge plugin into .opencode/plugins/agent-bridge.ts. For global use it writes to ~/.config/opencode/plugins/agent-bridge.ts. OpenCode auto-scans local plugins, so the installer does not need to mutate opencode.json for plugin registration.

At runtime the plugin:

  1. Registers a client row named like <project> (opencode bridge) with clientVersion: "opencode-plugin-bridge".
  2. Identifies its WebSocket as client-opencode-bridge-{hash} so the registry can target it directly.
  3. Tracks the active OpenCode session from session.created, session.updated, and session.idle events.
  4. Injects inbound channel messages with ctx.client.session.prompt_async.
  5. Sends delivery ACKs and periodically re-syncs missed pending messages from /channel/conversations.

The injected prompt mirrors the Codex format. If expectsResponse !== false, OpenCode receives a "reply required" turn with the exact agent-bridge.reply fields to copy. Informational messages are marked "no reply" and tell the receiving agent not to call reply.

OpenCode can also connect to the MCP adapter as a normal MCP client. In that case OpenCodeClientProfile accepts direct messages for the OpenCode MCP session and sibling bridge messages for the same project, so channel_inbox(pendingOnly=true) still works even when auto-routing chooses the plugin bridge.


Codex bridge (app-server)

The Codex bridge runs as a separate daemon (CodexAppServerBridge) that connects the channel layer to the Codex app-server JSON-RPC protocol:

  1. Connects to the Codex app-server WebSocket (ws://127.0.0.1:4500)
  2. Performs the initialize handshake
  3. Registers as a client session in the registry (clientName: "codex", clientVersion: "app-server-bridge")
  4. On incoming channel.message: calls turn/start on the app-server — Codex processes it as a real prompt turn
  5. Codex's reply is sent back through the channel

This is the preferred path when Codex is active: message_client_session automatically routes to the bridge (priority -1) over the Codex TUI MCP client.

One-command startup (registry + bridge + Codex TUI):

pnpm run dev -- codex start --project /path/to/codex-project

Bridge daemon only:

pnpm run dev -- codex app-bridge --project /path/to/codex-project
# Start Codex separately with: codex --remote ws://127.0.0.1:4500

tmux fallback — when the app-server is not available, inject follow-up prompts into the active Codex pane:

pnpm run dev -- codex tmux-bind      # bind current tmux pane to this session
pnpm run dev -- codex tmux-sidecar   # poll and inject pending channel messages

Troubleshooting — delivered_to_bridge stuck without a Codex TUI

If Claude Code (or any sender) sees status delivered_to_bridge for a Codex peer but Codex never picks the turn up, the most common cause is that the Codex TUI is running in isolated mode (plain codex) instead of attached to the bridge's app-server. The bridge daemon's app-server has nothing to inject into. Fix by either:

# Option A — let the bridge launch and attach Codex for you
pnpm run dev -- codex start --project /path/to/codex-project

# Option B — start the bridge separately, then attach Codex manually
pnpm run dev -- codex app-bridge --project /path/to/codex-project
codex --remote ws://127.0.0.1:4500

The inner MCP client of an isolated codex session still registers with the registry, so channel_inbox(pendingOnly=true) will surface the message — but the agent has to call it manually instead of receiving an automatic turn injection. The full diagnosis ladder lives in the agent-bridge skill.


Antigravity bridge (Cascade Language Server push)

Antigravity (agy) has no ACP and no app-server. It does, however, spawn a Cascade Language Server (Codeium/Windsurf lineage) on a random localhost port whenever it runs. AntigravityLsBridgeService uses that to push channel messages into the live TUI:

  1. Discovers the language server's HTTP port — parses the newest ~/.gemini/antigravity-cli/log/cli-*.log (listening on random port at <N> for HTTP), with an ss-based fallback, and verifies it via GetWorkspaceInfos.
  2. Resolves the active cascadeId for the workspace (via GetAllCascadeTrajectories).
  3. On each pending channel message, calls SendUserCascadeMessage (POST …/exa.language_server_pb.LanguageServerService/SendUserCascadeMessage) so agy processes it as a real user turn — not a passive notification.
  4. Deduplicates by message ID and applies a per-message re-injection blackout (state file .open-agent-bridge/antigravity-ls-state.json).

The bridge runs as a poller registered as a client (clientName: "antigravity"). It is identity-aware: it resolves the agy session by project + identity and only injects pending messages for that namespace.

Setup:

# 1. Install the MCP config (and restart agy so it loads the server)
open-agent-bridge antigravity install-plugin --project "/abs/path" [--identity ticket-123]

# 2. Run the push daemon (match the identity)
open-agent-bridge antigravity ls-push --project "/abs/path" [--identity ticket-123]

Options for ls-push: --registry-url <url>, --identity <id>, --client-id <id>, --poll-interval-ms <n> (default 2000), --retry-interval-ms <n> (default 30000), --once, --verbose.

Limitations (both agy-internal): the language server only accepts an injected turn when agy has an open/active conversation in the workspace; and occasionally agy's model executor errors mid-turn (neither PlanModel nor RequestedModel specified), which the user clears with a plain ok. SendAgentMessage returns 200 but does not trigger a turn — SendUserCascadeMessage is the one that works. Because this is an undocumented internal RPC, it is fully isolated in antigravity-ls-client.ts to bound the blast radius across agy versions.


Injection prompt format (OpenCode / Codex)

The Codex bridge daemon uses a prompt template (src/client/injection-prompt.ts) that wraps every inbound channel message before injecting it as a turn. The OpenCode plugin carries the same contract in its local plugin file. (Antigravity uses the language-server push instead, with its own concise wrapper.) The wrapper exists so the receiving LLM can identify the sender, see the full content delimited from instructions, and copy a pre-filled reply call without having to derive any IDs.

A reply-required injection looks like this:

[open-agent-bridge] Channel message — reply required
===============================================
From:          rcm-worker (3fdb0c6a)
Conversation:  7734035a-2c10-4626-5761-13f6d02f6e45
Message ID:    1d147308-a831-4525-aaf6-3c41614d5a20

----- BEGIN MESSAGE -----
<sender's content>
----- END MESSAGE -----

▶ Respond to the sender NOW with the agent-bridge MCP. The sender is
  waiting on this turn — silence will block them.

Tool call (copy each field verbatim — the adapter handles routing):

  agent-bridge.reply
    agentId:        "client-claude-code-2d1c3fdb0c6a"
    conversationId: "7734035a-2c10-4626-5761-13f6d02f6e45"
    replyTo:        "1d147308-a831-4525-aaf6-3c41614d5a20"
    message:        "<your answer here>"

How to compose the reply:
  1. Do any local work the sender's message implies …
  2. Put your answer or finding in the `message` field.
  ...

When expectsResponse resolves to false (sender opted into FYI), the header switches to Channel message — informational (no reply) and the prompt instructs the receiver to NOT call reply. Claude Code peers do not receive this wrapper — they get a raw <channel> push event and decide what to do based on conversation context.

Test contract: src/__tests__/injection-prompt.test.ts locks the structure (BEGIN/END markers, literal IDs in the call template, header wording) so future edits don't silently regress it.


Delivery mode comparison

Client Bridge type How messages arrive Requires daemon
Claude Code Built-in MCP push <channel> block in terminal via notifications/claude/channel No — part of MCP adapter
OpenCode Local plugin bridge session.prompt_async → OpenCode processes as a prompt turn No separate process — plugin runs inside OpenCode
Codex App-server daemon turn/start JSON-RPC → Codex processes as a prompt turn Yes — codex app-bridge
Antigravity (agy) Cascade Language Server push SendUserCascadeMessage → agy processes as a user turn in the live TUI Yes — antigravity ls-push
Dashboard WebSocket Chat panel updates via channel.message WS event No — built into registry WS

All paths share the same channel protocol (ChannelMessage, ChannelAck, conversationId). The bridge layer is just the last-mile delivery mechanism.


Dashboard

The dashboard is a Vue 3 SPA served by the registry at http://localhost:4999/dashboard. It shows live agent status, channel conversations, and task events. Agents display an identity badge and are grouped by their identity namespace, so multi-ticket setups stay legible at a glance.

Build the dashboard:

pnpm run build:dashboard
# or build everything at once
pnpm run build:all

Open in the browser:

pnpm run dev -- dashboard
# Dashboard: http://localhost:4999/dashboard

The dashboard connects to the registry WebSocket for live updates. Pass --no-open to print the URL without launching a browser. For hot-reload development use pnpm run dev:dashboard (Vite on :5173).


Skills

Built-in skills

Always available on every AgentServer, regardless of project type.

Skill Description
file-search Find files by glob pattern within the project directory.
endpoint-find Detect HTTP endpoints in backends or API calls in frontends.
code-query Search source code by text or regex.
prompt-execute Render a prompt template with variables and return the result.
notify-claude Send a notification to a Claude Code terminal via the channel.
shell-execute Run an arbitrary shell command in the project directory.

Auto-detected skills

Activated based on files found in the project root at startup.

Skill Activation condition Requires --claude
run-script package.json present No
run-tests Jest, Vitest, pytest, or pom.xml detected No
docker-build Dockerfile present No
code-review src/ directory present (uses Claude Code AI backend) Yes
claude-execute Always registered when --claude is active Yes

CLI reference

📖 Full command reference: docs/cli-reference.md — every command, option, default, and recipe. The table below is a summary.

Once installed globally, run oab <command> (or open-agent-bridge <command>). From a source checkout, pnpm run dev -- <command> also works.

Command Description
start [path] Start an agent server for the given directory (defaults to .).
registry start Start the registry on :4999.
registry status Query registry health and agent count.
list List all active agents.
health [agent-id] Check reachability of one or all agents.
ask <agent> <message> Send a one-shot task to a specific agent.
find <query> Search agents by skill or project type.
delegate <skill-id> <message> Send a task to the healthiest agent exposing a given skill.
broadcast <message> Send a message to all healthy agents.
mcp start Start the MCP adapter in stdio mode (used by Claude Code, OpenCode, Codex, Antigravity, and other MCP clients). Accepts --identity <ns> (or AGENT_BRIDGE_IDENTITY).
mcp config [--write] [--global] Print or write .mcp.json configuration.
mcp status Show live agents and registered MCP tools.
mcp server Start the MCP adapter in HTTP/SSE mode (port 6000).
opencode install-plugin Install the local OpenCode plugin bridge into a project or globally.
codex start One-command: registry + bridge + Codex TUI.
codex app-bridge Start the Codex app-server bridge daemon only.
codex tmux-bind Bind the current tmux pane to the active Codex session.
codex tmux-sidecar Poll and inject pending channel messages into a tmux pane.
antigravity install-plugin Write the agy MCP config (.agents/mcp_config.json) into a workspace or globally. Accepts --identity.
antigravity ls-push Push pending channel messages into a live agy TUI via its Cascade Language Server. Accepts --identity, --poll-interval-ms, --once.
antigravity hook-stop Stop-hook handler: deliver pending messages (hook-driven setups).
antigravity hook-session-start SessionStart-hook handler: surface pending messages as context.
dashboard Print or open the dashboard URL in the browser.

Scripts

Script Command Description
build tsc + plugin copy Compile TypeScript to dist/ and copy the OpenCode plugin files into dist/client/opencode-plugin/.
dev tsx src/cli/index.ts Run CLI from source without building.
start node dist/cli/index.js Run the compiled CLI.
clean rm -rf dist Delete build output.
build:dashboard cd dashboard && pnpm run build Build the Vue dashboard SPA.
build:all build + build:dashboard + copy Full production build including dashboard.
dev:dashboard cd dashboard && pnpm run dev Vite hot-reload dev server for the dashboard.
test vitest run Run all unit tests once.
lint biome check src/ Lint and check code style with Biome.
lint:fix biome check src/ --write Auto-fix lint issues.

Architecture

src/
├── agent/
│   ├── server.ts              AgentServer: HTTP + WS + A2A JSON-RPC + AG-UI SSE
│   ├── handlers.ts            TaskStore, RequestRouter, skill inference
│   ├── card.ts                A2A AgentCard builder
│   ├── project-detector.ts    Project type detection from filesystem
│   └── ag-ui-events.ts        AG-UI SSE event helpers
├── cli/
│   ├── index.ts               CLI entrypoint (Commander)
│   └── commands/              One file per CLI sub-command
├── client/
│   ├── a2a-client.ts                A2AClient: HTTP + WS client for agent-to-agent calls
│   ├── registry-client.ts           RegistryClient: HTTP client for the registry REST API
│   ├── client-profile-resolver.ts   Resolves delivery profile by client type (Claude/OpenCode/Codex/Antigravity)
│   ├── conversation-session-store.ts In-memory conversation state with ACK tracking
│   ├── codex-app-server-bridge.ts   CodexAppServerBridge daemon
│   ├── codex-app-server-client.ts   WS client for the Codex app-server protocol
│   ├── codex-runtime-discovery.ts   Detect running Codex process
│   ├── codex-session-files.ts       Read/write active Codex session marker
│   ├── codex-tmux.ts                Low-level tmux pane helpers for Codex
│   ├── codex-tmux-bridge-service.ts tmux-based Codex injection sidecar
│   ├── antigravity-ls-client.ts     Cascade Language Server client (port discovery + SendUserCascadeMessage)
│   ├── antigravity-ls-bridge-service.ts  ls-push poller — inject pending messages into live agy TUI
│   ├── antigravity-pending.ts       collectPendingForClient helper (identity-scoped)
│   ├── antigravity-runtime-discovery.ts  Detect running agy process
│   ├── antigravity-history.ts       Read agy conversation/history storage
│   ├── antigravity-agents-file.ts   Append original-request context for agy hooks
│   ├── antigravity-hooks.ts         Build Stop / SessionStart hook outputs
│   ├── opencode-plugin/             OpenCode local plugin source copied by `opencode install-plugin`
│   ├── channel-transport.ts         WebSocket transport to registry (stamps identity)
│   ├── channel-client-runtime.ts    WS runtime with reconnect + event bus
│   ├── conversation-service.ts      High-level send/reply/inbox helpers
│   └── profiles/                    Client behavior profiles (Claude, OpenCode, Codex, Antigravity) — identity hard wall
├── mcp/
│   ├── adapter.ts             McpAgentBridge — 6 MCP tools + session resolution + identity + shutdown
│   └── clear-scope.ts         Pure scope selector for channel_clear
├── registry/
│   ├── server.ts              RegistryServer: HTTP + WebSocket hub (:4999) + terminal-conversation sweep
│   ├── store.ts               AgentStore: in-memory only (dedup keyed by client+project+identity)
│   ├── channel-store.ts       SQLite persistence for channel messages and ACKs (+ terminal cleanup)
│   └── events.ts              RegistryEventBus
├── skills/
│   ├── framework.ts           BaseSkill, SkillRegistry
│   ├── state-graph.ts         StateGraph for multi-step skill workflows
│   └── builtins/              Built-in and auto-detected skill implementations
└── types/
    ├── a2a.ts                 A2A spec types (AgentCard, Task, etc.)
    ├── messages.ts            Registry wire types (AgentMessage, ChannelMessage, etc.)
    └── skills.ts              Skill context and I/O types

Persistence: channel messages and ACKs are stored in SQLite at ~/.open-agent-bridge/registry.sqlite (per-user, independent of the registry's cwd) across three tables: channel_messages, channel_acks, and channel_suppressed_conversations. SQLite access uses the node:sqlite built-in module (Node 22+) — there is no external SQLite dependency. The AgentStore (registered agents and heartbeats) is in-memory only and resets on registry restart; agents re-register automatically on reconnect.

AgentServer — A2A JSON-RPC methods:

Method Description
message/send Submit a task to the agent (A2A 0.3.0; honors message.taskId/contextId, resumes input-required tasks)
tasks/send Legacy alias of message/send (pre-0.3 draft param shape)
tasks/get Poll the status of an in-flight task
tasks/cancel Cancel a running task
agent.health Liveness check (returns uptime, version, skill count)
agent.hello Handshake — returns agent name, project, and capabilities
project.info Project metadata: type, framework, category
project.files List files matching a glob pattern
project.search Full-text / regex search across project files

Feature status

Feature Status
Registry HTTP + WS + SQLite stable
MCP adapter — 6 tools stable
Claude Code push bridge stable
OpenCode plugin push bridge stable
Codex app-server bridge stable
Antigravity native push (Cascade Language Server, ls-push) stable — requires an open agy conversation
Identity scoping (--identity, per-namespace inbox + list_agents) stable
Inbox bounding + channel_clear + terminal sweep stable
Dashboard Vue SPA (identity badges + grouping) stable
6 built-in skills stable
HTTP SSE MCP mode (mcp server, port 6000) stable
Dynamic skills (run-script, run-tests, docker-build, code-review) stable — require --claude
A2A 0.3.0 conformance layer (message/send, agent-card.json, protocolVersion, dual kind/type Parts, Task.kind/contextId) stable — legacy draft methods kept as aliases
message/stream / tasks/sendSubscribe — A2A SSE streaming stub — not implemented (card advertises streaming: false)
StateGraph skill composition implemented, unused in production
ask --stream CLI flag declared, not implemented

Testing

pnpm run test   # vitest — runs the full suite (60 test files, 420 tests) once
pnpm run lint   # biome — lint and style check

Test coverage includes: injection prompt contract (injection-prompt.test.ts), peer-type label generation (peer-type-label.test.ts), inferExpectsResponse text inference, conversation ID determinism, ACK state transitions, MCP adapter routing, identity scoping (identity-scoping.test.ts, stable-agent-id.test.ts), channel_clear scope selection (channel-clear-select.test.ts), the Antigravity language-server client (antigravity-ls-client.test.ts), and the Antigravity history/hooks/profile/runtime modules.


Limitations

  • Local only. The registry, agents, and bridges all run on localhost. No remote or cloud deployment is supported in v0.1.
  • Single registry. All agents must connect to the same registry instance. Multi-registry federation is not implemented.
  • No authentication. All local connections are unauthenticated. Do not expose registry or agent ports beyond localhost.
  • Volatile agent registry. The AgentStore is in-memory only. Restarting the registry clears all registered agents and heartbeats — agents re-register automatically on reconnect, but any in-flight state is lost. Only channel messages and ACKs (in channel_messages, channel_acks, channel_suppressed_conversations) are persisted to SQLite.
  • Codex bridge requires app-server remote TUI or tmux fallback. The preferred path is open-agent-bridge codex start or codex --remote ws://127.0.0.1:<port> against the bridge app-server. A plain codex session is isolated and cannot receive automatic turn injection. The tmux sidecar fallback polls at a fixed interval and injects follow-ups as synthetic keypresses, which is inherently racy under heavy TUI use.
  • OpenCode push requires the local plugin. OpenCode can call the MCP tools without the plugin, but automatic turn injection depends on open-agent-bridge opencode install-plugin and an OpenCode restart. Without the plugin bridge, inbound work must be discovered through channel_inbox.
  • Dashboard handleChannelMessage depends on toAgentId in broadcast. When a channel message is broadcast without a toAgentId, the dashboard may not correctly attribute it to the right conversation in the UI — this is a known issue with the current broadcast routing in the registry WebSocket relay.
  • tasks/sendSubscribe not implemented. End-to-end A2A streaming (Server-Sent Events per task) is not yet supported. The method is a stub — it is not announced in the Agent Card.
  • ask --stream flag is a no-op. The --stream option is declared in the CLI but the handler never reads it. Streaming task output is not implemented.
  • Antigravity push needs a live conversation + occasional nudge. antigravity ls-push injects via agy's Cascade Language Server, which only accepts a turn when agy has an open/active conversation in the workspace. The underlying RPC (SendUserCascadeMessage) is undocumented and may change between agy versions (isolated in antigravity-ls-client.ts). agy's model executor also errors mid-turn intermittently (neither PlanModel nor RequestedModel specified), which the user clears with a plain ok.
  • Identity is a hard wall. Cross-namespace delegation is intentionally impossible — the sender must run in the same --identity as the recipient. A multi-ticket coordinator has to send per namespace. The registry storage remains a global pool; isolation is enforced by inbox/list_agents filtering, not at the registry API.

Contributing

  1. Fork the repository and create a feature branch.
  2. Run pnpm install and pnpm run build to verify the build.
  3. Run pnpm run test (Vitest) and pnpm run lint (Biome) before committing.
  4. Open a pull request describing the change and its motivation.

License

MIT

Contributors

kevinArgueta96pmazariegos

Issues