Metadata-Version: 2.5
Name: agent-peer
Version: 0.11.1
Summary: A local IPC mesh so any AI coding agent harness (Claude Code, Codex CLI, Antigravity, pi, opencode, ...) can find, message, and reactively wake up any other.
Project-URL: Homepage, https://github.com/mkhuda/agent-peer
Project-URL: Changelog, https://github.com/mkhuda/agent-peer/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/mkhuda/agent-peer/issues
Author-email: "M. Khoirul Huda" <mhmmd.huda@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agents,antigravity,claude-code,cli,codex,developer-tools,ipc,opencode
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.10
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://raw.githubusercontent.com/mkhuda/agent-peer/main/assets/agent-peer-overview.webp" alt="agent-peer is a local IPC mesh for coding agents with four capabilities: direct messages, shared threads, live views, and a Telegram bridge. It connects Claude Code, Codex CLI, Antigravity, pi, opencode, and muse on one machine." width="880" />
</p>

<p align="center">
  <a href="#install"><strong>Install</strong></a> &middot;
  <a href="#usage"><strong>Usage</strong></a> &middot;
  <a href="./skills"><strong>Skills</strong></a> &middot;
  <a href="./docs"><strong>Docs</strong></a> &middot;
  <a href="./docs/status.md"><strong>Status</strong></a> &middot;
  <a href="#license"><strong>License</strong></a>
</p>

<p align="center">
  <a href="https://pypi.org/project/agent-peer/"><img src="https://img.shields.io/pypi/v/agent-peer?color=3178c6" alt="PyPI version" /></a>
  <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-3178c6" alt="MIT license" /></a>
  <img src="https://img.shields.io/badge/python-3.10%2B-3776AB" alt="Python 3.10+" />
  <img src="https://img.shields.io/badge/dependencies-zero-10b981" alt="Zero dependencies" />
  <img src="https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-6b7280" alt="macOS, Linux, Windows" />
</p>

# agent-peer

A local IPC mesh so any agent harness on your machine — Claude Code,
Antigravity, `pi`/oh-my-pi, `opencode`, muse, Codex CLI, or your own script —
can find, message, and reactively wake up any other. No polling, no
per-harness glue code.

<p align="center">
  <img src="https://raw.githubusercontent.com/mkhuda/agent-peer/main/assets/agent-peer-flow.webp" alt="agent-peer send finds the target in the registry, then delivers natively to Claude Code, Codex CLI, and Antigravity, or through a Unix socket, an inbox file, and a wait loop to pi, opencode, and muse. Every delivery is recorded in the shared registry and state directory." width="880" />
</p>

No harness is the hub here. Claude Code and Codex CLI each already ship their
own native inter-session delivery (`/peer` over Unix Domain Sockets, and
`codex queue` respectively) — `agent-peer send` uses whichever one applies
directly, so those two receive messages with no `listen`/`wait` step at all.
Antigravity (agy) has a door of its own: its language server accepts a user turn,
which `agent-peer send` uses once the session runs `listen` from agent-peer 0.11.0 or
later, so it too is woken without `wait`. `pi`, opencode, and muse have no native
equivalent, so `agent-peer` gives them a shared socket transport (Unix Domain
Sockets on macOS/Linux, Named Pipes on Windows) plus a blocking `wait` that plays
the same role. An Antigravity peer on an older agent-peer keeps using that path. Every delivery gets logged to the same registry either way, so
`agent-peer list`/`watch`/`logs` see the whole mesh regardless of which
transport actually carried a given message.

## Highlights

- **Sub-200ms delivery**, no polling anywhere in the loop.
- **Reactive wakeup**: `wait` blocks and returns the instant a message
  arrives — the tool call returning is what wakes the agent's own loop back up.
- **Never misses a backlog**: messages that pile up while an agent is busy get
  merged and returned in one shot, in order, the next time it calls `wait`.
- **Zero-config identity**: auto-detects a stable session name and engine
  from whichever harness is actually running it — no `--name` required.
- **Quota/usage dashboard**: `agent-peer status` (agy, Claude Code, Codex CLI)
  and a refreshing `--live` view, so the same command tells you who's
  reachable and who's about to run out of budget.
- **Zero heavy dependencies** — pure Python 3.10+, standard library only
  (the `setup` picker and `status --live` dashboard are hand-rolled on
  `curses`/`msvcrt`/ANSI, not a TUI framework).
- **Native Windows, not just WSL** — every OS-specific call (transport, locking,
  process detection, permissions) is isolated behind `agent_peer/compat.py`
  and verified live on a real Windows machine, not assumed from docs.
- **Shared threads**: a multi-party room several sessions post into and read
  from freely, not just 1-to-1. An active member reads it via its own poll
  loop with **zero socket push while that loop runs** — the socket is strictly a
  doorbell for members who are gated (`--leave`/peek), whose loop has stopped, or
  who haven't joined at all, ringing only on an explicit `@mention`/`@all`/`[stop]`. Join/leave are
  logged as plain, ambient lines in the room stream itself.

## Install

```bash
curl -fsSL https://raw.githubusercontent.com/mkhuda/agent-peer/main/install.sh | sh
```

**On native Windows** (PowerShell, no WSL needed):
```powershell
irm https://raw.githubusercontent.com/mkhuda/agent-peer/main/install.ps1 | iex
```

One door: detects your platform and Python, picks whichever of `uv`/`pipx`/
`pip` is available, installs the CLI, then hands off to `agent-peer setup` —
an interactive picker that detects which supported harnesses (agy, Codex,
muse, pi/oh-my-pi, opencode, Claude Code) are actually on this machine and
installs each one's `SKILL.md` at its known path. Re-run `agent-peer setup`
any time to add or remove a harness; `--all`/`--harness <id>`/`--remove`/
`--list` cover non-interactive/scripted use.

Pass `--rules` (or toggle the rules checkbox in the interactive picker) to also
inject managed mesh discipline blocks into your harness-global instruction files
(`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, `~/.gemini/GEMINI.md`,
`~/.agents/AGENTS.md`, `~/.pi/agent/AGENTS.md`, `~/.config/opencode/AGENTS.md`).
This ensures agents know core mesh invariants from Turn 1 across every workspace
and freshly cloned repo without waiting for on-demand skill triggers. Use
`--no-rules` to remove injected rules cleanly without touching installed skills.

**Prefer to install the CLI yourself?**
```bash
uv tool install agent-peer
```
No `uv`? `pipx install agent-peer` or `python3 -m pip install --user agent-peer`
work the same way, then run `agent-peer setup` for the skill picker.

**Contributing or tracking `main` instead of a release?**
```bash
git clone https://github.com/mkhuda/agent-peer.git
cd agent-peer
uv tool install --editable . --force
```
An editable install means source changes take effect immediately, no reinstall.

## Usage

**Discover who's reachable:**
```bash
agent-peer list
```
```text
PID      SESSION NAME    ENGINE   STATUS   ALIVE  SOCKET        CWD
------------------------------------------------------------------------
41213    my-app-fe       Claude   idle     yes    41213.sock    ~/projects/my-app
52901    agy-33402       AGY      idle     yes    52901.sock    ~/projects/my-app
```
Filter to one project with `--cwd <substring>`, e.g. `agent-peer list --cwd my-app`.

**Send a message**, by name or PID:
```bash
agent-peer send my-app-fe "review the auth middleware diff when you're free"
agent-peer send agy-33402 "[stop] hold off on that migration, see docs/" --priority now
```
Add `--await-reply [seconds]` to block the same call for the target's reply
instead of a separate `wait` — closes the race where a fast reply arrives
before the target re-arms its own `wait`. Bare flag waits indefinitely, a
timeout exits 1, and it never touches the target's own read cursor.

**Become reachable**, from any harness:
```bash
agent-peer listen
```
`--name` is optional everywhere (`listen`, `send --sender`, and the session
filter on `wait`). Leave it out and `agent-peer` walks up the parent-process
chain to find the first non-generic-shell ancestor and uses it as a stable
identity (e.g. `pi-<pid>`, `opencode-<pid>`) — explicit `--name` /
`$AGENT_PEER_NAME` always wins when given.

**React without polling:**
```bash
agent-peer wait --timeout 30
```
The call blocks and returns the moment there's something to read. If
messages already queued up while the harness was busy, it returns **all of
them at once**, instantly — no separate "mark as read" step, and nothing gets
replayed twice. Only one `wait` may run per session at a time; a second one
fails fast (exit 1) instead of silently racing.

**Inspect the inbox:**
```bash
agent-peer inbox            # recent messages
agent-peer inbox --clear    # wipe it (also resets the read cursor)
```

**Watch the mesh live:**
```bash
agent-peer watch             # tail everything, formatted
agent-peer watch -s my-app-fe   # just one session
```

**Clean up dead registrations:**
```bash
agent-peer prune
```
A listener killed with `SIGKILL` (not a graceful `Ctrl+C`) never gets the
chance to clean up after itself, leaving a registration behind that shows
`ALIVE: no` in `list` forever. `prune` removes only sessions confirmed dead
(`kill -0` fails) — it never touches a session that's still alive.

**Check quota/usage across every provider you're bridging:**
```bash
agent-peer status          # agy, Claude Code, Codex - one-shot printout
agent-peer status --live   # same data, refreshing ANSI dashboard, Ctrl-C to exit
agent-peer status --json   # machine-readable
```

**Shared threads — a room, not a DM:**
```bash
agent-peer thread dev-sync                          # backlog + block for the next new message, exit 0
agent-peer send --thread dev-sync "found the bug"   # post - every participant sees it, not just one
agent-peer thread dev-sync --leave                  # step out: gated from banter, still reachable by @mention
agent-peer join dev-sync                            # human-only: live two-way view with an invite picker
```
Every `thread` call is one-shot (block for the next message, print, exit) - what you pass
decides your presence and whether the socket ever reaches you:
- **`--timeout N` (peek):** a quick backlog check. Gated - safe mid-task, never arms
  banter push. An explicit `@name`/`@all`/`[stop]` still knocks through your socket.
- **No `--timeout` (active room member):** you're in the meeting. **Zero socket push
  while your poll runs** - the room stream, via your own poll, is the only speaker inside
  the room (a stopped poll is knocked on a mention, `@all` or `[stop]` after about 30 s). Stay in it by looping the call (Claude Code: the `Monitor` tool with
  `while true; do agent-peer thread <id> || sleep 5; done`; other harnesses: their own
  background-task/re-arm pattern - see [`skills/`](./skills) for the idiom per harness).
  Add `--mention-only` to be woken only by `@you`, `@all` or `[stop]`: it prints just those posts
  and a line saying how many others were left out and the `agent-peer logs` command that shows
  them (`--context` prints everything). For a worker on a long task, not for an agent that must
  see all traffic.

You must always be either polling or `--leave`d - a room membership with nothing actually
reading it is unreachable by anything, mention included, until it polls again. Join and
leave are recorded as plain lines in the room's own stream (`— <name> joined/left the
thread —`), so ambient awareness of who's around never requires a separate command.

### Telegram bridge

`agent-peer telegram --thread <id>` mirrors one shared thread into one Telegram chat and
relays what you type there back into the thread, so you can follow and steer agents from
your phone. It runs on your machine (long-polling, no server) and needs a bot you create
yourself - there is no shared bot.

1. In Telegram, message `@BotFather`, send `/newbot`, and copy the token.
2. Message your bot once (or add it to a group; for a group, turn off privacy mode in
   BotFather so it sees ordinary messages).
3. `agent-peer telegram --print-chat-id` lists the chat id and your user id (stop any running
   bridge first; it would consume the pending message).
4. Put the values in `~/.agent-peer/telegram.env` (mode `600`):

   ```
   TELEGRAM_BOT_KEY=<token>
   TELEGRAM_CHAT_ID=<chat id>
   TELEGRAM_ALLOWED_USER_IDS=<your user id>
   ```

5. `agent-peer telegram --thread <id>` (Ctrl+C stops it; `--name` sets its name in the
   thread, default `telegram-bridge`; `--env-file` reads another file).

Anyone in the chat can otherwise instruct your agents, so `TELEGRAM_ALLOWED_USER_IDS`
(comma-separated) is required for group chats; the bridge refuses to start without it.
Only one bridge can poll a given bot token at a time. In the chat, `/list` and `/status`
show your sessions; anything else is posted to the thread. If a token leaks, revoke it
with `/revoke` in BotFather and update the file.

## Teaching a harness about `agent-peer`

`agent-peer setup` (see [Install](#install)) detects and installs this
automatically — the section below is what it does under the hood, useful if
you're installing a skill by hand or adding a new harness.

[`skills/`](./skills) ships a ready `SKILL.md` per harness (agy, pi/oh-my-pi,
opencode, muse, Codex CLI, Claude Code) plus a README explaining exactly
where and how to install it — each harness turned out to have a genuinely
different convention for skill location, frontmatter, and trigger mechanism,
verified against its own source/docs rather than assumed.

## Architecture

**`pi`/oh-my-pi, opencode, and muse** (and Antigravity when a peer is on an older
agent-peer) go through agent-peer's own socket transport (Unix Domain Sockets on macOS/Linux, Named Pipes on
Windows via `agent_peer/compat.py`), which mirrors the handshake Claude Code
enforces for its native sessions:

1. **PID validation** — the target process must actually be running.
2. **Start-time verification** — matches the registered process's start
   time (`ps -o lstart=` on macOS/Linux, `GetProcessTimes` on Windows), so a
   reused PID can't impersonate an old session.
3. **Auth handshake** — first frame must be `{"type":"auth","token":"<peerToken>"}`.
4. **Message frame** — `{"type":"user","priority":"now","from":"...","message":{"content":"..."}}`.

`agent-peer` handles this handshake, socket binding, token generation, and
session cleanup automatically.

**Claude Code and Codex CLI** skip all of that — `agent-peer send` detects
the target's real protocol and uses it directly (Claude's own `/peer` socket,
or `codex queue --thread <uuid>` for a Codex session). A Codex session just
needs `agent-peer listen`, no flags: Codex sets `CODEX_THREAD_ID` in its own
process environment (since 0.154.0) and `listen` picks it up automatically —
`--codex-thread <uuid>` / `$CODEX_THREAD_ID` remain as an explicit override
for an older Codex without it. Either way the delivery is still recorded to
`~/.agent-peer/inbox.jsonl` so `watch`/`logs`/`inbox` show it — see the
diagram at the top for the full picture.

**Antigravity (agy)** exports its language server's address, CSRF token and conversation id
into the shell it runs tools in. `agent-peer listen` reads them from its own environment,
keeps the token only in a private file under `~/.agent-peer/agy-ls/`, and registers just the
conversation id. `agent-peer send` then calls the server's `SendUserCascadeMessage`, which
lands as an ordinary user turn - at once if agy is idle, at its next step if it is working.
The conversation id is stable across a resume, so the session also gets its name back without
`--name`. Anything that goes wrong falls back to the socket path above; set
`AGENT_PEER_AGY_NATIVE=0` to force that path. The server's interface is undocumented and may
change between agy releases.

## Docs

- [`docs/status.md`](./docs/status.md) — how `agent-peer status` sources agy
  and Claude Code quota/context numbers (Codex CLI is newer - see
  `agent_peer/codex_status.py` for how that one works until this doc covers it too).
- [`docs/`](./docs) — design notes, known limitations, and past investigation
  reports written while building this.
- [`skills/`](./skills) — per-harness `SKILL.md` templates and install guides.

## License

[MIT](./LICENSE).
