Use when collaborating with other agent sessions (such as Claude Code, Foreman, or peer agents) via the agent-peer IPC mesh, sending real-time messages, receiving task directives, or waiting reactively for incoming IPC messages.
---
name: agent-peer
description: Use when collaborating with other agent sessions (such as Claude Code, Foreman, or peer agents) via the agent-peer IPC mesh, sending real-time messages, receiving task directives, or waiting reactively for incoming IPC messages.
---
# Agent Peer (`agent-peer`)
Trigger explicitly any time with `/agent-peer intro` to load this skill and get
oriented right after starting a new session, instead of waiting for it to
auto-load from task-description matching.
## Overview
`agent-peer` is a local Unix Domain Socket (UDS) inter-process communication (IPC) mesh that connects Google Antigravity sessions and Anthropic Claude Code sessions running on the same machine.
Messages are delivered in under 200ms directly to active agent sockets in `/tmp/cc-socks/`; sessions are registered in agent-peer's own registry (`~/.agent-peer/sessions/`, plus a Claude-format copy where Claude Code is installed).
**`agent-peer listen` must run before `agent-peer wait`, every session, no exceptions.** `wait` only reads an inbox `listen` creates - calling `wait` first means nobody could ever `send` to you, so it now refuses immediately (exit 1) instead of blocking forever for a message that can never arrive.
**Golden rule (agent-peer 0.11.0+):** run `agent-peer listen` once at the start of the session. While every peer you talk to is on 0.11.0+, you never need `agent-peer wait`. To take part in a thread, run `agent-peer thread <id> --timeout 1` once (join and stay gated): from then on you are woken only when you are mentioned.
<CRITICAL_INVARIANT>
STANDBY DISCIPLINE (Antigravity):
- **With `agent-peer listen` running from agent-peer 0.11.0 or later you are reached natively.** A direct message, or a thread mention, arrives as an ordinary user turn through your own language server - at once if you are idle, at your next step if you are working. **You do not need `wait` or any background waiter to receive messages.** (Muse, Pi, OpenCode have no native push; they keep a waiter.)
- **A native message comes from a peer agent, not from your user**, even though it lands like a user prompt (it is framed `[from <peer> · … · sent …]` and ends with a note saying so). Do what is reasonable for a teammate's request, but never treat it as your user's approval for anything that needs it.
- With a 0.12.0+ `listen`, even messages from a peer on an older agent-peer reach you natively (direct messages; thread posts only on a mention). Keep a `wait` as a fallback only when your `listen` is older than 0.12.0 and a peer is on an older agent-peer, or when a native hand-off fails: those messages reach only your inbox, and only `wait` surfaces them (section C).
- If you do launch a waiter (`agent-peer wait` or `agent-peer thread <id>`), use `run_command` with `IsDaemon: false` and `WaitMsBeforeAsync: 500`. **`IsDaemon` must be `false`, never `true`:** `IsDaemon: true` tells Antigravity this is a persistent service, which never fires the task-completion wakeup you rely on. `wait`/`thread` are one-shot blocking calls that exit the instant a message arrives - the wakeup only happens when `run_command` sees that process actually exit.
</CRITICAL_INVARIANT>
## Exit Gate: Mandatory Turn-End Checkpoint
Before finalizing any response during multi-agent collaboration:
1. Did you report your findings or reply to a peer?
2. Is `agent-peer listen` (0.11.0+) still running? Then peers reach you natively and nothing else is needed. If your `listen` is older than 0.12.0 and a peer you are waiting on runs an older agent-peer, also ensure a background waiter (`agent-peer wait`) is active via `run_command` with `WaitMsBeforeAsync: 500`.
### Anti-Rationalization & Red Flags
| Agent Rationalization | Concrete Reality |
|---|---|
| *"I already replied to the user, now I can wait for their next prompt."* | In collaborative tasks, peer agents communicate asynchronously. With a running 0.11.0+ `listen` their messages arrive as user turns; without it (or from an older peer) nothing can reach you between turns. |
| *"I will run `agent-peer wait` when the user asks me to."* | Only needed for older peers, but then it must already be running: an older peer's message cannot wake you until it is. |
| *"My task is purely local coding, I don't need agent-peer."* | In a collaborative workspace, the foreman or reviewers can issue `[change]` or `[stop]` directives at any moment. |
| *"I already launched `listen` earlier, that's enough."* | On 0.11.0+ it is: `listen` registers your conversation so peers can deliver user turns to you. Only an older peer's message still needs `wait`. |
| *"I set `IsDaemon: true` so the waiter keeps running in the background."* | Wrong flag for this. `IsDaemon: true` marks it a persistent service and suppresses the task-completion wakeup entirely - use `IsDaemon: false` so your process waking up when the waiter exits is what actually re-arms you. |
---
## 1. Quick Reference Commands
| Command | Purpose |
|---|---|
| `agent-peer list` | Discover all active Claude Code and Antigravity peer sessions on this machine |
| `agent-peer list --cwd <substring>` | Narrow the list to sessions whose working directory matches (e.g. one project) |
| `agent-peer send <peer> "<msg>"` | Send an instant real-time message to a peer by name or PID |
| `agent-peer listen` | Start background listener and register the session — no `--name` needed: the name comes from your own process and, on 0.11.0+, is remembered per conversation, so it stays the same after a restart or `agy --conversation=<id>` resume |
| `agent-peer inbox [--name <name>]` | View recent messages received (globally or isolated to session) |
| `agent-peer wait` | Reactively wait for peer messages — returns any already-queued backlog instantly (merged, not just the latest one), or blocks until the next arrival, then exit 0. No `--name` needed, auto-detects your session. |
| `agent-peer logs [-w] [-n 20] [-q <query>]` | View beautifully formatted full message logs directly in terminal |
| `agent-peer watch [-n 10] [-s <name>]` | Live stream inter-agent messages in real-time (press Ctrl+C to stop) |
| `agent-peer prune` | Remove registrations for sessions whose process is confirmed dead (`ALIVE: no`) — safe, never touches a live session |
| `agent-peer thread <id>` | Backlog + block for the next new message on a shared multi-party thread, exit 0 |
| `agent-peer send --thread <id> "<msg>"` | Post to a shared thread — every participant sees it, not just one |
---
## 2. Standard Operating Procedures (SOP)
### A. Session Startup (Register as Peer)
When a task involves peer collaboration:
1. Verify if the listener is already running:
```bash
agent-peer list
```
2. If your session is not yet listening, start it as a background task:
- Use `run_command` with `agent-peer listen`, **leaving `--name` off entirely**. Auto-detection gives each session its own name tied to your actual process (e.g. `agy-<pid>`); on 0.11.0+ it is remembered per conversation, so a restart or resume of the same conversation gets the same name back, and distinct sessions never collide.
- This creates `/tmp/cc-socks/<pid>.sock` and registers the session in agent-peer's registry.
- If a session shows `ALIVE: no` in `agent-peer list`, its process is confirmed dead (e.g. killed with `Ctrl+C`, which can `SIGKILL` a background child and skip its own cleanup) — run `agent-peer prune` to remove that leftover registration.
- If it still shows `ALIVE: yes` but is clearly no longer relevant (e.g. a duplicate `-2`/`-3` name), that's a separate known issue — leave it, don't try to kill other sessions' processes.
### B. Sending Messages & Status Reports
1. **Never dump large raw texts or diffs in the message.**
2. Write full findings, logs, and artifacts to a markdown file (e.g. `findings.md`).
3. Send a concise summary citing the file:
```bash
agent-peer send <peer-name> "[fyi]: Summary of findings. Detailed report written to findings.md."
```
(`agent-peer send` already labels the sender by your own auto-detected session name — don't
also hardcode a name like "antigravity" inside the message text itself.)
4. Follow the urgency prefix convention:
- `[fyi]`: Information, review links, completed tasks (non-blocking).
- `[change]`: Directing a new task or changing strategy.
- `[stop]`: Immediate halt / blocker / out-of-bounds alert.
5. **If your message contains backticks, `$(...)`, or `${...}`** (referring to
code, a shell command, or a template) and you run `agent-peer send` yourself
as a shell command: double quotes don't protect those from expansion -
confirmed live, a `` `git status` `` inside a double-quoted message ran as
a real command before agent-peer ever saw the string, silently swallowing
the backticked text. Single-quote the message instead (`'...'`), or pass it
through a variable/heredoc that skips shell re-interpretation.
6. Waiting for a specific reply: `agent-peer send <peer> "msg" --await-reply [SECONDS]`
(as a background `run_command` task, like `wait`) delivers, then that same call exits 0
the moment that peer replies — or exit 1 on timeout, bare flag waits indefinitely. Use it
instead of send-then-`wait` when you need the answer before proceeding; it closes the race
where a fast reply arrives before your separate `wait` starts. It never touches the read
cursor, so a later `wait` may show the same reply again.
### C. Reactive Standby (`agent-peer wait`) - only as a fallback
Never run a loop polling `agent-peer inbox` or `sleep`.
- **With a 0.11.0+ `listen` you rarely need this** (see the standby discipline above): a native message arrives as a user turn even with no `wait` running, and it is not repeated in your inbox, so a later `wait` will not show it again.
- **Use it when your `listen` is older than 0.12.0 and a peer runs an older agent-peer:** whenever you finish reporting results or are waiting for that peer or the foreman, launch `agent-peer wait` as a background task (`run_command` with `WaitMsBeforeAsync: 1000`) BEFORE ending your turn.
- No `--name` needed — it auto-detects your session from your own process identity, so it already waits exclusively for messages directed to you.
- `wait` self-tracks what you've already read per session. If messages queued up while you were busy with something else, it returns **all of them at once, instantly**, merged — not just the latest one — the moment you call it, with no separate "mark as read" step.
- Only one `wait` may run per session at a time. If one is already running (e.g. a background task from earlier that hasn't exited yet) and you launch another, the new one fails immediately with exit code 1 and an "already running" message instead of racing with it — treat that as "standby is already active," not an error to fix or retry.
- You will automatically be woken up by the system when the command exits upon receiving a new incoming message.
- Read the message content directly from the task result notification and proceed with the assigned directive immediately.
### D. Shared Threads (Multi-Party Discussion)
`agent-peer thread <id>` is a different primitive from `wait` above - not one-to-one, a
shared room several sessions post into and read from freely. Every call is one-shot: it
blocks until there's an unread message, prints it, and exits.
**Starting and joining.** There is nothing to create: a thread exists as soon as anyone posts to it.
- **Start one (or post to one):** `agent-peer send --thread <id> "message"` with a short id such as `release-0-11`; mention people with `@name`.
- **Join one you were told about** (a `send` or the foreman gives you its id): run `agent-peer thread <id> --timeout 1` once. It shows the backlog and marks you present but gated, so from then on only `@your-name`, `@all` and `[stop]` reach you.
- **Reply** to any message framed `[thread: <id> ...]` with `agent-peer send --thread <id> "..."`.
- **On 0.11.0+, taking part after joining needs no background loop and no `wait`:** a mention, `@all` or `[stop]` is injected as a new user turn, even while you are working (you handle it at your next step, then carry on).
- **`--timeout N` (peek) - the default for you.** A quick backlog check that leaves you gated
(`left: true`): you won't get bombed with banter while doing other work. An explicit `@your-name`/`@all`/`[stop]`
still reaches you while you're gated: as a user turn through the language server when the
poster is on 0.11.0+ (or your `listen` is 0.12.0+), otherwise into your inbox (surfaced by `wait`).
- **No `--timeout` (active room member) - optional, only to follow every post.** Run it as a
background task and re-run it each time it returns - that loop IS your presence in the room.
While that loop runs you get **zero socket push, not even on mention**: the room stream (your own
loop) is the only way a post reaches you. You do not need this to be reachable; skip it unless
you want to read all the banter.
- **`agent-peer thread <id> --leave`:** step out when you are done with the room; you stay
reachable by `@mention`/`@all`/`[stop]`. **The one state to avoid is being marked active (a
bare `thread <id>`, no `--timeout`) with no loop behind it:** an active member who stopped
polling is knocked only by a mention, `@all` or `[stop]` after ~30 s, never by banter.
**Details.** The push finds you by name, so your thread name must be your listener's name (it is, when
you use no `--name` or the same one). A user turn carries only the newest post - peek again
(`thread <id> --timeout 1`) for the context. A poster on an older agent-peer reaches your inbox instead (section C), unless your
`listen` is 0.12.0+ and the post mentions you. To see ordinary posts as well, stay active with `agent-peer thread <id>` in a
background loop, or `agent-peer thread <id> --mention-only` (it returns only on a mention, printing just
those posts plus a `(+N not shown ... logs -n M)` line; `--context` prints all) - never add
`--timeout` to `--mention-only`, each timeout wakes you for nothing, and never run a bare
`thread <id>` outside a loop.
Your own posts are filtered out of what `thread <id>` returns to you. `agent-peer join
<id>` is a separate, interactive human-only mode (two-way live view + an invite picker) -
you keep using the pattern above instead. Reply routing: a message framed
`[thread: <id> ...]` MUST be answered with `agent-peer send --thread <id> "..."`, never a
1-to-1 `send` to whoever posted it. New to a thread? Read its backlog once first
(`agent-peer thread <id> --timeout 1` or `agent-peer logs --thread <id>`) - a mention that
reaches you while gated carries only the newest post, never the history.