agent-peer 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_peer-0.2.1/.dev/CHARTER.md +66 -0
- agent_peer-0.2.1/.dev/HANDOFF.md +464 -0
- agent_peer-0.2.1/.dev/log.md +5 -0
- agent_peer-0.2.1/.dev/reviews/agy-review.md +74 -0
- agent_peer-0.2.1/.dev/reviews/claude-review.md +111 -0
- agent_peer-0.2.1/.dev/reviews/opencode-report.md +124 -0
- agent_peer-0.2.1/.dev/reviews/opencode-review.md +226 -0
- agent_peer-0.2.1/.dev/reviews/pi-review.md +106 -0
- agent_peer-0.2.1/.github/workflows/release.yml +93 -0
- agent_peer-0.2.1/.gitignore +25 -0
- agent_peer-0.2.1/AGENTS.md +95 -0
- agent_peer-0.2.1/CHANGELOG.md +56 -0
- agent_peer-0.2.1/CLAUDE.md +1 -0
- agent_peer-0.2.1/LICENSE +21 -0
- agent_peer-0.2.1/PKG-INFO +170 -0
- agent_peer-0.2.1/README.md +142 -0
- agent_peer-0.2.1/agent_peer/__init__.py +3 -0
- agent_peer-0.2.1/agent_peer/__main__.py +4 -0
- agent_peer-0.2.1/agent_peer/agy_live.py +102 -0
- agent_peer-0.2.1/agent_peer/agy_status.py +148 -0
- agent_peer-0.2.1/agent_peer/claude_status.py +195 -0
- agent_peer-0.2.1/agent_peer/cli.py +273 -0
- agent_peer-0.2.1/agent_peer/inbox.py +124 -0
- agent_peer-0.2.1/agent_peer/listener.py +306 -0
- agent_peer-0.2.1/agent_peer/logs.py +354 -0
- agent_peer-0.2.1/agent_peer/protocol.py +130 -0
- agent_peer-0.2.1/agent_peer/registry.py +132 -0
- agent_peer-0.2.1/agent_peer/sender.py +63 -0
- agent_peer-0.2.1/assets/agent-peer-architecture.svg +97 -0
- agent_peer-0.2.1/docs/agent-peer-weaknesses-report.md +66 -0
- agent_peer-0.2.1/docs/notification-click-target.md +42 -0
- agent_peer-0.2.1/docs/stale-listener-detection.md +72 -0
- agent_peer-0.2.1/docs/status.md +123 -0
- agent_peer-0.2.1/docs/tasks/0001-inbox-files-are-not-world-readable.md +27 -0
- agent_peer-0.2.1/docs/tasks/0002-cursor-never-silently-eats-a-backlog.md +33 -0
- agent_peer-0.2.1/docs/tasks/0003-session-list-shows-idle-after-messages-are-read.md +29 -0
- agent_peer-0.2.1/docs/wait-unread-cursor.md +101 -0
- agent_peer-0.2.1/pyproject.toml +55 -0
- agent_peer-0.2.1/skills/README.md +20 -0
- agent_peer-0.2.1/skills/agy/README.md +31 -0
- agent_peer-0.2.1/skills/agy/SKILL.md +66 -0
- agent_peer-0.2.1/skills/opencode/README.md +32 -0
- agent_peer-0.2.1/skills/opencode/SKILL.md +63 -0
- agent_peer-0.2.1/skills/pi/README.md +33 -0
- agent_peer-0.2.1/skills/pi/SKILL.md +72 -0
- agent_peer-0.2.1/tests/__init__.py +0 -0
- agent_peer-0.2.1/tests/helpers.py +72 -0
- agent_peer-0.2.1/tests/test_auth.py +103 -0
- agent_peer-0.2.1/tests/test_cli_smoke.py +36 -0
- agent_peer-0.2.1/tests/test_cursor.py +125 -0
- agent_peer-0.2.1/tests/test_permissions.py +71 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Charter
|
|
2
|
+
|
|
3
|
+
Written by the owner. Read by every session at start. Changes rarely.
|
|
4
|
+
|
|
5
|
+
## What we are building, and for whom
|
|
6
|
+
|
|
7
|
+
`agent-peer` is a local IPC mesh so any agent harness (Claude Code, Antigravity/agy,
|
|
8
|
+
pi, opencode, others later) can discover, message, and reactively wake up any other
|
|
9
|
+
harness session on the same machine — no polling, no per-harness configuration. For
|
|
10
|
+
the owner (rg): use agent-peer daily, across harnesses, without worrying about
|
|
11
|
+
hidden bugs that only surface once it's actually relied on.
|
|
12
|
+
|
|
13
|
+
## Current milestone
|
|
14
|
+
|
|
15
|
+
**M1: Use agent-peer without worrying about hidden bugs.**
|
|
16
|
+
|
|
17
|
+
Close out the highest-priority findings from the 4 review documents
|
|
18
|
+
(`.dev/reviews/*.md`) written 17 Sep 2026 by agy, pi, opencode, and Claude —
|
|
19
|
+
especially the ones independently confirmed by 2+ reviewers, and the ones about
|
|
20
|
+
security/data correctness (not just documentation convenience).
|
|
21
|
+
|
|
22
|
+
**Gate:** Owner runs `agent-peer list` and `agent-peer wait` once more after every
|
|
23
|
+
M1 task is closed, and nothing looks off.
|
|
24
|
+
|
|
25
|
+
## Who owns what
|
|
26
|
+
|
|
27
|
+
Paths, never responsibilities. A path with no owner is nobody's, and changing it is a
|
|
28
|
+
request to the owner rather than an edit.
|
|
29
|
+
|
|
30
|
+
| Path | Owner |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `agent_peer/*.py` (core package) | `agent-peer-e4` |
|
|
33
|
+
| `~/.gemini/antigravity/skills/agent-peer/SKILL.md`, `~/.gemini/GEMINI.md` (bagian agent-peer) | `antigravity-test` |
|
|
34
|
+
| `~/.pi/agent/skills/agent-peer/SKILL.md`, `~/.pi/agent/AGENTS.md` (bagian agent-peer) | `pi-98661` |
|
|
35
|
+
| `~/.config/opencode/skills/agent-peer/SKILL.md`, `~/.config/opencode/AGENTS.md` (bagian agent-peer) | `opencode-15297` |
|
|
36
|
+
| `.dev/reviews/<name>-review.md` | the session with the matching name, each owns their own |
|
|
37
|
+
| `docs/`, `README.md`, `.dev/CHARTER.md`, `pyproject.toml` | owner only (rg) |
|
|
38
|
+
|
|
39
|
+
**Not owned by anyone working here:** git operations (commit/push/branch) — owner
|
|
40
|
+
only, per rg's global instructions. Killing/terminating other agents' processes —
|
|
41
|
+
owner only.
|
|
42
|
+
|
|
43
|
+
## Decisions that are already made
|
|
44
|
+
|
|
45
|
+
- **Test policy:** all verification during the 17 Sep 2026 session was done live and
|
|
46
|
+
manually (ad-hoc scripts + real cross-harness tests), there's no automated test
|
|
47
|
+
suite in the repo yet — that's itself one of the M1 findings (see reviews), but
|
|
48
|
+
there's no mandatory-test rule for other tasks until the base suite exists.
|
|
49
|
+
- **Don't auto-spawn a new process as a side effect of another command.** Tried once
|
|
50
|
+
(`wait` auto-starting `listen`), the owner rejected it — risk of listeners silently
|
|
51
|
+
piling up. If a new process is needed, it must be explicit from the user/agent, not
|
|
52
|
+
automatic.
|
|
53
|
+
- **`wait` doesn't hard-refuse when unreachable** — just a warning (stderr), still
|
|
54
|
+
proceeds (reading an already-piled-up backlog is a valid use case even without a
|
|
55
|
+
live listener).
|
|
56
|
+
- **Auto session-name (`detect_harness_identity`) deliberately doesn't guess a
|
|
57
|
+
per-tool env var** — walking the parent-process chain was chosen because it's more
|
|
58
|
+
reliable and needs no cooperation from each harness. Its limitation (subagents
|
|
59
|
+
sharing a parent collide) is known — that's an M1 task.
|
|
60
|
+
- **`--timeout` is not a default that gets forced onto anyone** — briefly
|
|
61
|
+
recommended then rejected by the owner for the mandatory-standby pattern
|
|
62
|
+
(background task); leave it per-context, don't hardcode a specific timeout
|
|
63
|
+
recommendation into any skill.
|
|
64
|
+
- Editable install (`uv tool install --editable .`) — source changes are
|
|
65
|
+
automatically live on the installed binary with no manual reinstall. A process
|
|
66
|
+
already running before an edit keeps using the old code until it's restarted.
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
# HANDOFF
|
|
2
|
+
|
|
3
|
+
## 2026-09-17 — `wait`/`watch` mechanism analysis, unread cursor plan
|
|
4
|
+
|
|
5
|
+
**Context:** user actively uses `agent-peer` for Claude Code <-> Antigravity
|
|
6
|
+
(agy) communication. This discussion session was pure research + planning,
|
|
7
|
+
no code implementation yet.
|
|
8
|
+
|
|
9
|
+
**`wait` vs `watch` mechanism findings:**
|
|
10
|
+
- `wait` (`inbox.py:wait_for_message`) = blocking tool call, the only thing
|
|
11
|
+
that genuinely triggers agy's "auto wakeup" (process exit = control back
|
|
12
|
+
to the LLM loop). But fragile: this process dies every time agy moves to
|
|
13
|
+
another tool call/task, so it has to be manually restarted every time work
|
|
14
|
+
finishes.
|
|
15
|
+
- `watch` (`logs.py:show_logs` follow mode) = a background process that
|
|
16
|
+
never exits on its own, stays alive across tasks — good for passive
|
|
17
|
+
visibility, but doesn't prove it triggers wakeup on its own (depends on
|
|
18
|
+
whether the agy harness monitors its stdout as an async notification —
|
|
19
|
+
unconfirmed).
|
|
20
|
+
- `notify_idle` in `peerFeatures` (listener.py:103) checked: cosmetic field,
|
|
21
|
+
no implementation anywhere in this codebase reads it.
|
|
22
|
+
|
|
23
|
+
**Concrete bugs found in `wait_for_message`:**
|
|
24
|
+
1. Baseline computed from `len(inbox)` at the moment `wait` is called →
|
|
25
|
+
backlog messages that arrive while `wait` is dead (agy busy working)
|
|
26
|
+
never get automatically "claimed" when `wait` restarts.
|
|
27
|
+
2. `return msgs[-1]` only grabs the last message → if several messages pile
|
|
28
|
+
up within one poll cycle (100ms), the rest are permanently lost (the next
|
|
29
|
+
baseline already considers them "old").
|
|
30
|
+
|
|
31
|
+
**Decision & status:** design in
|
|
32
|
+
[`docs/wait-unread-cursor.md`](../docs/wait-unread-cursor.md) — per-session
|
|
33
|
+
timestamp-based cursor (`~/.agent-peer/cursors/<name>.json`), auto-advances
|
|
34
|
+
when `wait` is called (no explicit "mark as read" needed from the agent),
|
|
35
|
+
`wait` returns a list instead of 1 message. **Implemented**
|
|
36
|
+
(`protocol.py`, `inbox.py`, `cli.py`) and verified via isolated tests (`HOME`
|
|
37
|
+
override, plus a simulation using a read-only copy of the real production
|
|
38
|
+
antigravity inbox with 42 messages — first-call-after-restart proven not to
|
|
39
|
+
replay old history). Local install is editable, so source changes are
|
|
40
|
+
automatically live for the next `agent-peer` invocation; `listen`/`wait`
|
|
41
|
+
processes already running before an edit only pick up the new code after
|
|
42
|
+
being restarted. A settle-window/delay before returning was considered then
|
|
43
|
+
decided against (user's usage pattern isn't sub-second bursts, the existing
|
|
44
|
+
backlog-merge is already enough).
|
|
45
|
+
|
|
46
|
+
**Separate finding (operational, not a code bug):** `agent-peer list` shows
|
|
47
|
+
`antigravity-2` (PID 71277) as ALIVE=yes even though the user thought they'd
|
|
48
|
+
closed that session. Verified not PID-reuse (procStart matches `ps lstart`)
|
|
49
|
+
— the `agent-peer listen` process is genuinely still alive, it just never
|
|
50
|
+
received SIGTERM/SIGINT when the UI session was closed, so `cleanup()` in
|
|
51
|
+
`listener.py` never got called. **Correction:** initially suspected this
|
|
52
|
+
could be detected via detached TTY (`??`) — turned out wrong, the actively-
|
|
53
|
+
used `antigravity` process (72769) has an identical TTY/PPID/STAT (`??`,
|
|
54
|
+
ppid=1/launchd, `S`). There's no OS-level signal that distinguishes stale
|
|
55
|
+
from active. Fix plan (based on duplicate-name-group detection + recency,
|
|
56
|
+
plus a `stop` command using SIGTERM) is in
|
|
57
|
+
[`docs/stale-listener-detection.md`](../docs/stale-listener-detection.md).
|
|
58
|
+
Temporary manual fix: `kill <pid>`.
|
|
59
|
+
|
|
60
|
+
**Dead end:** briefly suspected there was an active push/interrupt mechanism
|
|
61
|
+
from `agent-peer` to the agy process for wakeup — investigated via a fork
|
|
62
|
+
agent, found nothing. No signal/callback of any kind exists; everything
|
|
63
|
+
relies on the "blocking tool call" pattern that agy itself initiates, not
|
|
64
|
+
any wiring on the `agent-peer` side.
|
|
65
|
+
|
|
66
|
+
**Live end-to-end verification (real agy session, `antigravity-test` PID
|
|
67
|
+
52285):** sent 2 messages (5 second gap) while agy was busy with another
|
|
68
|
+
task. Proven by comparing cursor timestamp vs message `received_at` (exact
|
|
69
|
+
match in both cases): message 1 triggered an instant wakeup while `wait` was
|
|
70
|
+
blocking; message 2 piled up while busy then got auto-consumed when `wait`
|
|
71
|
+
was called again after the task finished. The cursor/backlog mechanism
|
|
72
|
+
proven to work under real conditions, not just isolated unit tests.
|
|
73
|
+
|
|
74
|
+
**Follow-up found directly from the live session (`antigravity-test`):**
|
|
75
|
+
`ps` confirmed 2 `agent-peer wait --name antigravity-test` processes alive
|
|
76
|
+
at the same time (same PPID, different TTY) — the agy harness spawns a new
|
|
77
|
+
`wait` without closing the old one. Real race: both processes could
|
|
78
|
+
potentially catch the same message (duplicate, not lost). **Fix
|
|
79
|
+
implemented:** `cmd_wait` takes a non-blocking exclusive per-session lock
|
|
80
|
+
(`fcntl.flock` on `~/.agent-peer/locks/<session>.lock`) — a second
|
|
81
|
+
invocation for the same session fails immediately (`exit 1` + a clear
|
|
82
|
+
message) instead of silently racing. The lock is released automatically by
|
|
83
|
+
the OS when the process dies/crashes. Verified in sandbox: the second
|
|
84
|
+
process is rejected instantly, the first process is undisturbed, the lock
|
|
85
|
+
releases after the first process dies (including via `kill`), sessions with
|
|
86
|
+
different names don't block each other. Details in
|
|
87
|
+
[`docs/wait-unread-cursor.md`](../docs/wait-unread-cursor.md).
|
|
88
|
+
|
|
89
|
+
**Additional fixes done this session (outside the original plan, found/
|
|
90
|
+
requested as the session went on):**
|
|
91
|
+
- **Auth-bypass in `listener.py`** — found while dissecting agy's report
|
|
92
|
+
(worse than their finding 1.2): the `authenticated` variable was computed
|
|
93
|
+
but never checked before `process_incoming_frame` was called, so the auth
|
|
94
|
+
token was never enforced at all. Fix: add `if not authenticated: continue`
|
|
95
|
+
before `user`/`control` frames get processed. Tested (auth-gate test):
|
|
96
|
+
frame without auth rejected, wrong token rejected, correct token accepted
|
|
97
|
+
— all PASS.
|
|
98
|
+
- **Notification click opens Script Editor instead of the relevant app** —
|
|
99
|
+
root cause is macOS behavior (notifications via `osascript` always get
|
|
100
|
+
attributed to Script Editor). Fix: switch to
|
|
101
|
+
`terminal-notifier -activate com.googlecode.iterm2` (falls back to
|
|
102
|
+
`osascript` if `terminal-notifier` isn't available). User confirmed
|
|
103
|
+
clicking the notification now opens iTerm. Details in
|
|
104
|
+
[`docs/notification-click-target.md`](../docs/notification-click-target.md).
|
|
105
|
+
|
|
106
|
+
**Multi-harness generalization (`agy`/`pi`/`opencode`/etc.) — auto-detected
|
|
107
|
+
session names:** the user wants `agent-peer` to stop being Antigravity-only.
|
|
108
|
+
Briefly tried detecting harness identity via env vars (grepping strings in
|
|
109
|
+
the `agy`/`pi`/`opencode` binaries) — inconclusive, lots of noise, no clear
|
|
110
|
+
identity env var. **Approach that proved to work:** walk up the parent
|
|
111
|
+
process chain (`os.getppid()` → `ps -o comm=`), skip generic shell/
|
|
112
|
+
interpreter names (`zsh`, `bash`, `python3`, etc.), use the first
|
|
113
|
+
distinctive process name found + that process's PID (not `agent-peer`'s own
|
|
114
|
+
PID, since `wait` is called repeatedly with a different PID each time — a
|
|
115
|
+
stable identity is needed, and the harness process itself is stable for the
|
|
116
|
+
life of the session). Validated directly against real data from this
|
|
117
|
+
session: `zsh` (PID 79203) → `claude` (PID 23386), `agy` (PID 33402) was
|
|
118
|
+
also already confirmed from earlier data. Implemented in
|
|
119
|
+
`protocol.py:detect_harness_identity`/`auto_session_name`, used lazily
|
|
120
|
+
(only computed when `--name`/`--sender` and `$AGENT_PEER_NAME` are both
|
|
121
|
+
empty) in `cmd_send`/`cmd_listen`/`cmd_wait` — other commands (`list`,
|
|
122
|
+
`status`, etc.) incur zero overhead from this (tested, `list` stays
|
|
123
|
+
~0.05s). Agreed scope: every command (`wait`, `listen`, `send`), and `wait`
|
|
124
|
+
with no name now reads the per-harness inbox (auto-named) instead of the
|
|
125
|
+
merged global inbox — a deliberate decision, not a regression.
|
|
126
|
+
|
|
127
|
+
**Additional bug found & fixed while testing auto-name:** the
|
|
128
|
+
listen→send→wait-for-the-first-time sequence (before that session had ever
|
|
129
|
+
called `wait`) caused an already-arrived message to be **missed** — because
|
|
130
|
+
the cursor got initialized to "now" at the moment `wait` was FIRST called
|
|
131
|
+
(not when the session actually started), so a message that arrived before
|
|
132
|
+
that first `wait` call was treated as "already old". Fix:
|
|
133
|
+
`inbox.py:mark_session_start()` is called at the end of
|
|
134
|
+
`PeerListener.setup()` — initializes the cursor at the earliest possible
|
|
135
|
+
point (before `accept()` could ever process anything), instead of waiting
|
|
136
|
+
for the first `wait` call. Sessions already running before this fix remain
|
|
137
|
+
safe (still use the old fallback in `_read_cursor`, unchanged — the
|
|
138
|
+
`test_wait_cursor.py` regression suite still PASSES entirely).
|
|
139
|
+
|
|
140
|
+
`README.md` updated: the listen section generalized (no longer
|
|
141
|
+
Antigravity-only), a new section added about auto-detected names and an
|
|
142
|
+
explanation of `wait` (backlog/cursor/lock) that previously wasn't
|
|
143
|
+
documented in the README at all.
|
|
144
|
+
|
|
145
|
+
**Another bug found by the user:** `listener.py` hardcoded
|
|
146
|
+
`self.cwd = cwd or os.path.expanduser("~/projects")` — so the CWD column in
|
|
147
|
+
`agent-peer list` always showed `~/projects` regardless of the actual
|
|
148
|
+
directory `agent-peer listen` was run from. Fix: switched to `os.getcwd()`.
|
|
149
|
+
This is in core code (shared by every harness), so it automatically applies
|
|
150
|
+
to agy/pi/opencode at once, no per-harness work needed. Tested in sandbox
|
|
151
|
+
(cwd now correctly points to the real working directory). Live sessions
|
|
152
|
+
that were already registered wrong (`antigravity-test` PID 52285, `pi-98661`
|
|
153
|
+
PID 512) were manually patched to `/Users/rg/projects/agent-peer` to match
|
|
154
|
+
their actual current working context.
|
|
155
|
+
|
|
156
|
+
**Bug found by the user during a live `pi` test:** `listener.py` hardcoded
|
|
157
|
+
`"agentType": "AGY"` in the session json — a leftover from when this project
|
|
158
|
+
was still Antigravity-only. The `pi` session (`pi-98661`, PID 512) was
|
|
159
|
+
registered with the ENGINE column wrongly showing "AGY". Fix:
|
|
160
|
+
`PeerListener.__init__` accepts an optional `agent_type` (default fallback
|
|
161
|
+
`"AGENT"` if unknown), `cli.py:cmd_listen` detects the engine via
|
|
162
|
+
`detect_harness_identity()` (independent of the session name — even if
|
|
163
|
+
`--name` is given manually, the engine is still detected from the process
|
|
164
|
+
tree) and passes it to `PeerListener`. Tested in sandbox: new sessions now
|
|
165
|
+
correctly show `CLAUDE`/`PI`/etc. matching their parent process. The
|
|
166
|
+
`pi-98661` session json that was already wrong (started before the fix) was
|
|
167
|
+
manually patched (`agentType: "PI"`) — the listener process itself didn't
|
|
168
|
+
need restarting since the file is only read passively by `agent-peer list`,
|
|
169
|
+
not held under a continuous lock by the listener.
|
|
170
|
+
|
|
171
|
+
**Root cause of opencode only running `wait` without `listen`:** asked
|
|
172
|
+
`pi-98661` to audit this — the answer was sharp. Confirmed: `pi` itself
|
|
173
|
+
deliberately runs `listen` first (per its SKILL.md), BUT it identified a
|
|
174
|
+
real gap in the `SKILL.md`: there was never an explicit sentence saying
|
|
175
|
+
"wait alone isn't enough to be reachable — without a live listen, `send` to
|
|
176
|
+
your name will fail, and `wait` itself won't error even if you're
|
|
177
|
+
unreachable" — a silent failure mode that's easy to miss because `wait`
|
|
178
|
+
still "succeeds" (blocks normally) even though that session actually can't
|
|
179
|
+
be reached from outside.
|
|
180
|
+
|
|
181
|
+
**Briefly tried a radical fix (auto-spawn `listen` from inside `wait`), then
|
|
182
|
+
the user rejected it** — reason: a subprocess spawned automatically and
|
|
183
|
+
fully detached (`start_new_session=True`) risks silently piling up listeners
|
|
184
|
+
unnoticed, EXACTLY the "stale listener" problem that was already hard-won
|
|
185
|
+
diagnosed in [[stale-listener-detection]]. **Replaced with a non-invasive
|
|
186
|
+
guard:** `cmd_wait` now checks `resolve_session(session)` first — if it
|
|
187
|
+
fails, print a clear warning to stderr ("no listener running... run
|
|
188
|
+
agent-peer listen if you want to be reachable") then still proceed with the
|
|
189
|
+
wait as usual (not a hard refusal, since reading an already-piled-up backlog
|
|
190
|
+
without a live listener is still a valid use case). No new process is ever
|
|
191
|
+
created — it just informs, the decision stays with the agent/user. Tested:
|
|
192
|
+
warning appears exactly when unreachable, silent when already reachable, no
|
|
193
|
+
new listener appears in either case.
|
|
194
|
+
|
|
195
|
+
**Final decision on documentation:** considered then decided NOT to
|
|
196
|
+
propagate this further into SKILL.md/AGENTS.md — the runtime warning in
|
|
197
|
+
`wait` already closes the most dangerous gap (pi's point 2) more reliably
|
|
198
|
+
than static documentation (self-documenting at exactly the right moment,
|
|
199
|
+
doesn't depend on the agent reading the skill carefully).
|
|
200
|
+
|
|
201
|
+
**Four-way review collaboration (agy, pi, opencode, Claude) + handwalk task
|
|
202
|
+
system:** initialized `.dev/CHARTER.md` (M1 goal: "use agent-peer without
|
|
203
|
+
worrying about hidden bugs", gate: run `agent-peer list`+`wait` once more
|
|
204
|
+
and nothing looks off), a per-path ownership map across the 4 sessions. Out
|
|
205
|
+
of ~20 findings in `.dev/reviews/*.md`, triaged into 10 candidate tasks; the
|
|
206
|
+
3 highest-priority ones (tasks 0001-0003) were done directly (not via
|
|
207
|
+
worktree, per the user's instruction — worktree deferred):
|
|
208
|
+
|
|
209
|
+
- **0001** chmod 0600 on inbox/cursor/lock files + 0700 on
|
|
210
|
+
`~/.agent-peer/*` directories (not `SOCKET_DIR`/`SESSIONS_DIR`, which
|
|
211
|
+
belong to Claude Code — deliberately left untouched).
|
|
212
|
+
- **0002** cursor safety: (a) `_read_cursor` on corruption now falls back
|
|
213
|
+
to `0` (replay everything) instead of `time.time()` (silently swallowing)
|
|
214
|
+
+ a stderr warning; (b) `clear_inbox()` resets the cursor. **A regression
|
|
215
|
+
was found while testing this myself**: the initial fix DELETED the cursor
|
|
216
|
+
file on clear — that reopened the exact gap `mark_session_start` had
|
|
217
|
+
already closed (a message arriving between clear and the next wait would
|
|
218
|
+
get swallowed again). Fixed: the cursor is now **rewritten to the
|
|
219
|
+
moment-of-clear**, not deleted — exactly the same pattern as
|
|
220
|
+
`mark_session_start`.
|
|
221
|
+
- **0003** the `new-msg` status in the session json now gets reset to
|
|
222
|
+
`idle` (via `_reset_status_idle` in `cmd_wait`, best-effort through
|
|
223
|
+
`resolve_session`) every time `wait` successfully gets a message — a bug
|
|
224
|
+
`pi` found (status stuck forever after the first message, no code ever
|
|
225
|
+
wrote `idle` back).
|
|
226
|
+
|
|
227
|
+
All tested in sandbox (`test_wait_cursor.py`/`test_auth_gate.py` regressions
|
|
228
|
+
still PASS + new scenarios), **not yet hand-walked by the user** (handwalk
|
|
229
|
+
skill rule: a task can only be closed once the owner themself runs its
|
|
230
|
+
acceptance sentence on a real machine and writes down what happened). Task
|
|
231
|
+
files: `docs/tasks/0001-*.md` through `0003-*.md`. The remaining 7 candidate
|
|
232
|
+
tasks (sender-label resolution, "Delivered" wording, `_GENERIC_PROC_NAMES`
|
|
233
|
+
expansion, stdout warning, subagent `--name` documentation, test suite,
|
|
234
|
+
skills-into-repo) haven't been worked on yet — listed in chat, not yet
|
|
235
|
+
turned into formal task files.
|
|
236
|
+
|
|
237
|
+
**Live `opencode` validation complete, self-diagnosed by that session
|
|
238
|
+
itself** (full report in `.dev/reviews/opencode-report.md`, cross-checked
|
|
239
|
+
against the real cursor/inbox/lock files — all accurate). Root cause
|
|
240
|
+
exactly as predicted: opencode's bash tool is synchronous, default TOOL
|
|
241
|
+
timeout of 5 seconds (stricter than the 2 minutes guessed from initial web
|
|
242
|
+
research), plus the first session hadn't run `listen` first. Once the order
|
|
243
|
+
was right (`listen` detached → other work → `wait` as the last tool call
|
|
244
|
+
with no timeout), every feature (auto-name `opencode-15297`, ENGINE
|
|
245
|
+
`OPENCODE`, cwd-fix, backlog-merge, lock) validated identically to agy/pi.
|
|
246
|
+
**All three target harnesses (agy, pi, opencode) are now validated
|
|
247
|
+
end-to-end**, each with its own independent diagnosis from that agent
|
|
248
|
+
itself, not just a claim from this session.
|
|
249
|
+
|
|
250
|
+
**Important architecture correction from a live `pi` test:** the user asked
|
|
251
|
+
about the pattern `agent-peer wait; echo "WAIT-EXIT:$?" (timeout 600s)`
|
|
252
|
+
that showed up in a `pi-98661` tool call. Asked that session directly, the
|
|
253
|
+
answer was detailed and accurate (checked against `dist/core/tools/bash.js`
|
|
254
|
+
source by the pi agent itself): **`pi` has no background-bash at all**
|
|
255
|
+
(unlike Antigravity, which has `run_command` + a wakeup notification) — its
|
|
256
|
+
bash tool is fully synchronous. The 600s wasn't a pi infra limit, it was a
|
|
257
|
+
`timeout` parameter the agent itself passed to the tool call (SIGKILLs the
|
|
258
|
+
process tree if exceeded). Without a `timeout` on the tool call = genuinely
|
|
259
|
+
indefinite blocking.
|
|
260
|
+
|
|
261
|
+
Implication: the `SKILL.md` I originally wrote for `pi` had the wrong
|
|
262
|
+
"background task" framing (copied from the Antigravity version). **Already
|
|
263
|
+
fixed:**
|
|
264
|
+
- `wait` → stays a synchronous tool call, made the LAST tool call of the
|
|
265
|
+
turn (not "background"), with no `--timeout` on agent-peer NOR the pi
|
|
266
|
+
bash tool's own `timeout` (two different things that got mixed up) — the
|
|
267
|
+
turn stays "open" until a message arrives.
|
|
268
|
+
- `listen` → the opposite — it actually MUST be detached manually at the
|
|
269
|
+
shell level (`&`), because calling it synchronously would freeze the turn
|
|
270
|
+
forever (the `listen` process is designed to never exit on its own).
|
|
271
|
+
`AGENTS.md`/`README.md` weren't affected by this issue since they already
|
|
272
|
+
used the phrase "blocking shell call" from the start, never explicitly
|
|
273
|
+
said "background task" like `SKILL.md` did.
|
|
274
|
+
|
|
275
|
+
**`agent-peer` skill for `opencode`:** researched its convention, turned out
|
|
276
|
+
different again from agy/pi — confirmed via WebFetch to
|
|
277
|
+
`opencode.ai/docs/skills/`: frontmatter requires `name`+`description`
|
|
278
|
+
(optional `license`/`compatibility`/`metadata`), **no auto-load or slash
|
|
279
|
+
command** — the skill is invoked explicitly via the tool call
|
|
280
|
+
`skill({ name: "agent-peer" })`. Global location:
|
|
281
|
+
`~/.config/opencode/skills/<name>/SKILL.md` (there's also a fallback to
|
|
282
|
+
`~/.claude/skills/`/`~/.agents/skills/`, deliberately not used so it doesn't
|
|
283
|
+
bleed into the real Claude Code install). Created at
|
|
284
|
+
`~/.config/opencode/skills/agent-peer/SKILL.md`.
|
|
285
|
+
|
|
286
|
+
On opencode's bash tool: web research found a default of **synchronous,
|
|
287
|
+
2-minute timeout** (env var `OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS`
|
|
288
|
+
to change it), plus a `run_in_background` feature (similar to the
|
|
289
|
+
Antigravity pattern, auto re-invoke on command exit) — but **not confirmed
|
|
290
|
+
whether this feature exists in the installed opencode version (1.18.28)**,
|
|
291
|
+
the source was a PR that might be recent. Learning from the earlier wrong
|
|
292
|
+
assumption about `pi`, this SKILL.md was DELIBERATELY written without
|
|
293
|
+
assuming either way (gives 2 paths: background-capable vs
|
|
294
|
+
synchronous-only, asks the agent to check first) — **not yet validated
|
|
295
|
+
live**, waiting for the user to open a real `opencode` session to test it
|
|
296
|
+
exactly like `agy`/`pi`.
|
|
297
|
+
|
|
298
|
+
**`agent-peer` skill for `pi`:** injected at
|
|
299
|
+
`~/.pi/agent/skills/agent-peer/SKILL.md` (the native location read directly
|
|
300
|
+
by pi's core, confirmed from the `skill-store.ts` source — sits alongside
|
|
301
|
+
the bundled `web-search` skill). Same frontmatter format
|
|
302
|
+
(`name`+`description`), content adapted from the Antigravity SKILL.md but
|
|
303
|
+
more concise, matching the `web-search` style. **Not yet validated live**
|
|
304
|
+
whether `/agent-peer` in `pi` actually resolves to this file — `pi -p`
|
|
305
|
+
failed due to an auth error (`UnrecognizedClientException`) unrelated to
|
|
306
|
+
our change. User is testing an interactive `pi` session manually to
|
|
307
|
+
confirm.
|
|
308
|
+
|
|
309
|
+
**Other harnesses' global AGENTS.md:** checked 6 files (`pi`, `opencode`,
|
|
310
|
+
Codex, Hermes, Gemini x2) — all empty regarding `agent-peer` (turns out the
|
|
311
|
+
instruction for `agy` to use `agent-peer wait --name antigravity` has always
|
|
312
|
+
been purely manual, typed by the user every session, not from any permanent
|
|
313
|
+
config in `~/.gemini/AGENTS.md`). With the user's approval, a concise
|
|
314
|
+
"Cross-agent messaging (agent-peer)" section was added to
|
|
315
|
+
`~/.pi/agent/AGENTS.md` and `~/.config/opencode/AGENTS.md` (identical
|
|
316
|
+
content, just different auto-detect name examples, `pi-<pid>` vs
|
|
317
|
+
`opencode-<pid>`) — covering: `listen`/`wait` without `--name`, `send`,
|
|
318
|
+
`list`, pointing to the README for details (no content duplication).
|
|
319
|
+
`~/.gemini/GEMINI.md` (agy's actual global config — not
|
|
320
|
+
`~/.gemini/AGENTS.md`/`~/.gemini/config/AGENTS.md`, which turned out unused,
|
|
321
|
+
containing the "SuperAntigravity Skills" framework instead) also got the
|
|
322
|
+
same section added (example name adjusted to `agy-<pid>`). Codex/Hermes
|
|
323
|
+
AGENTS.md **not** touched yet — not requested. Whether `pi`/`opencode`
|
|
324
|
+
themselves actually have the blocking-tool-call capability needed for
|
|
325
|
+
`wait` to be a genuine reactive trigger for them, same as `agy`, **hasn't
|
|
326
|
+
been verified** — only assumed to work similarly, no end-to-end test done
|
|
327
|
+
yet like the one run against `agy`.
|
|
328
|
+
|
|
329
|
+
**Found a pre-existing `agent-peer` skill already installed for agy**
|
|
330
|
+
(`~/.gemini/antigravity/skills/agent-peer/SKILL.md`) — this is what had been
|
|
331
|
+
letting `agy` "know" how to use `agent-peer` on its own without ever being
|
|
332
|
+
taught manually in this session (GEMINI.md auto-loads skills based on
|
|
333
|
+
description). This skill turned out to be the **original source** of 2 bugs
|
|
334
|
+
fixed this session: the old SOP explicitly told it to always use
|
|
335
|
+
`--name antigravity` (causing `antigravity-2`/`-3` pileup), and the old
|
|
336
|
+
mandatory rule told it to always launch a new `wait` before ending a turn
|
|
337
|
+
without ever checking whether the old one was still alive (exactly the
|
|
338
|
+
2-wait race scenario found earlier). **Already updated:** removed all
|
|
339
|
+
`--name antigravity` hardcoding (replaced with auto-detect), fixed the
|
|
340
|
+
`wait` description (now instant backlog-merge, not just "next message"),
|
|
341
|
+
added guidance about the new lock-error ("already running" = normal, not
|
|
342
|
+
something to retry). Added a "Trigger explicitly `/agent-peer intro`" line
|
|
343
|
+
at the top — user confirmed `/agent-peer` in agy really does auto-list the
|
|
344
|
+
skill by name.
|
|
345
|
+
|
|
346
|
+
**Correction:** briefly recommended `--timeout 60` in the mandatory-standby
|
|
347
|
+
rule — the user rejected it, correctly. Since `wait` runs as a background
|
|
348
|
+
task that triggers wakeup the moment the process exits, a bounded timeout
|
|
349
|
+
would make it exit every 60 seconds with nothing to report and need
|
|
350
|
+
relaunching — that's polling in disguise, contradicting `wait`'s own
|
|
351
|
+
purpose ("never poll"). My lock-safety argument was also wrong — `flock`
|
|
352
|
+
releases automatically at the OS level no matter why the process died, it
|
|
353
|
+
doesn't need a timeout for that. Sequential sessions waiting on a peer for
|
|
354
|
+
minutes/hours are a valid case, let `wait` block without a limit. Already
|
|
355
|
+
revised: the mandatory rule is now `agent-peer wait` with no `--timeout`;
|
|
356
|
+
the flag stays documented as an option available for other, non-background
|
|
357
|
+
uses.
|
|
358
|
+
|
|
359
|
+
**Additional findings from agy's exploration**
|
|
360
|
+
(`docs/agent-peer-weaknesses-report.md`, cross-checked against source —
|
|
361
|
+
valid): inbox/cursor files not `chmod 0600` (only the socket & key file are
|
|
362
|
+
protected); no `SO_PEERCRED` verification on the socket auth (only string
|
|
363
|
+
token matching); race condition from non-atomic writes in `append_inbox`
|
|
364
|
+
(no `flock`); cursor doesn't get reset on `agent-peer inbox --clear`; stale
|
|
365
|
+
sessions also happen if a listener gets `SIGKILL`/crashes (not just when a
|
|
366
|
+
UI session is closed — overlaps with [[stale-listener-detection]] but a
|
|
367
|
+
different trigger). None of this list has been worked on yet — pure
|
|
368
|
+
findings, waiting on the user's prioritization.
|
|
369
|
+
|
|
370
|
+
**Bug found by the user while testing `pi` live:** `listener.py` hardcodes
|
|
371
|
+
`self.cwd = cwd or os.path.expanduser("~/projects")` — so the CWD column in
|
|
372
|
+
`agent-peer list` always shows `~/projects` no matter which directory
|
|
373
|
+
`agent-peer listen` was actually run from. Fix: switched to `os.getcwd()`.
|
|
374
|
+
This is in core code (shared across every harness), so it automatically
|
|
375
|
+
applies to agy/pi/opencode at once, no separate per-harness work needed.
|
|
376
|
+
Tested in sandbox (cwd now correctly points to the real working directory).
|
|
377
|
+
Live sessions that were already registered wrong (`antigravity-test` PID
|
|
378
|
+
52285, `pi-98661` PID 512) were manually patched to
|
|
379
|
+
`/Users/rg/projects/agent-peer` to match their current working context.
|
|
380
|
+
|
|
381
|
+
## 2026-09-17 — Watch accuracy test, git history bootstrap
|
|
382
|
+
|
|
383
|
+
**`agent-peer watch` accuracy validated live.** User relies on `watch` to
|
|
384
|
+
monitor the 3 test harnesses and was worried it might be inaccurate or drop
|
|
385
|
+
something. Reviewed `logs.py` and found it had never been specifically
|
|
386
|
+
audited by any of the 4 reviewers. Two real caveats found (not bugs in
|
|
387
|
+
current normal operation, but worth knowing): (1) a sender/recipient
|
|
388
|
+
already dead by the time you view the log shows as `pid-<N>` instead of a
|
|
389
|
+
name; (2) `extract_recipient_info`'s "legacy inference" fallback
|
|
390
|
+
(`logs.py:144-154`) hardcodes THIS session's own dev PIDs (72769/29258/
|
|
391
|
+
71277) as magic numbers — dead weight for anything logged going forward,
|
|
392
|
+
but a landmine if those PIDs ever get reused by an unrelated process later
|
|
393
|
+
(confirmed PID reuse is a real possibility on macOS). Recommended removing
|
|
394
|
+
it; not yet done.
|
|
395
|
+
|
|
396
|
+
Ran a live 3-way triangle test (agy → opencode, pi → antigravity-test,
|
|
397
|
+
opencode → pi, all reporting back to Claude) while capturing
|
|
398
|
+
`agent-peer watch --raw` in the background. Cross-checked every reported
|
|
399
|
+
message verbatim against the captured JSONL — **100% match, byte-for-byte,
|
|
400
|
+
correct sender/recipient attribution, zero loss.** Side finding: `watch`
|
|
401
|
+
without a `-s <name>` filter is a genuinely global feed across every
|
|
402
|
+
session on the machine — it also picked up unrelated real production
|
|
403
|
+
traffic from a different project (`claude-81449`/ottoshare-factory) running
|
|
404
|
+
in parallel. Worth using `-s <name>` if the goal is only to watch specific
|
|
405
|
+
sessions.
|
|
406
|
+
|
|
407
|
+
**Gotcha hit while starting the watch process:** mixed `run_in_background`
|
|
408
|
+
(the tool's own backgrounding) with a manual `&` plus trailing foreground
|
|
409
|
+
commands in the same script — the tool considered the whole invocation
|
|
410
|
+
"completed" once the trailing commands finished, even though the actual
|
|
411
|
+
`agent-peer watch` child process (backgrounded via `&`) kept running,
|
|
412
|
+
reparented to launchd. Not a crash, just confusing: don't mix the two
|
|
413
|
+
backgrounding mechanisms in one call — launch the persistent command alone
|
|
414
|
+
via `run_in_background`, nothing else in the same invocation.
|
|
415
|
+
|
|
416
|
+
**Kicked off a 4-way collaborative review** (agy, pi, opencode, Claude), each
|
|
417
|
+
independently exploring `agent-peer` from their own harness's perspective
|
|
418
|
+
and writing findings to `.dev/reviews/<name>-review.md`. All 4 completed;
|
|
419
|
+
cross-confirmed findings across 2+ reviewers were treated as highest
|
|
420
|
+
confidence. One direct contradiction surfaced and got resolved: agy's
|
|
421
|
+
review initially claimed `/agent-peer` wasn't a real slash command in
|
|
422
|
+
Antigravity, then agy itself retracted that after re-verifying live — the
|
|
423
|
+
original user claim ("it works") was correct. Corrected in
|
|
424
|
+
`agy-review.md` with a strikethrough + note (kept, not deleted, so the
|
|
425
|
+
investigation trail survives).
|
|
426
|
+
|
|
427
|
+
Triaged the ~20 findings from the 4 reviews into 10 candidate tasks (see the
|
|
428
|
+
M1 entry above); tasks 0001-0003 were implemented and tested directly.
|
|
429
|
+
|
|
430
|
+
**Skill templates shipped into the repo** (`skills/<harness>/`) — previously
|
|
431
|
+
only lived in each user's global config directory
|
|
432
|
+
(`~/.gemini/...`/`~/.pi/...`/`~/.config/opencode/...`), so cloning this repo
|
|
433
|
+
gave no way to reconstruct them. Each harness folder ships its current
|
|
434
|
+
`SKILL.md` plus a `README.md` covering that harness's own install location,
|
|
435
|
+
frontmatter convention, and trigger mechanism — verified against each
|
|
436
|
+
harness's own source/docs during this session, not assumed (the three
|
|
437
|
+
turned out to genuinely differ: auto-load + `/agent-peer` slash for agy/pi,
|
|
438
|
+
explicit `skill()` tool call only for opencode, no slash command at all).
|
|
439
|
+
|
|
440
|
+
**Git history bootstrapped.** All of this session's work had been sitting
|
|
441
|
+
uncommitted the entire time. Committed in 4 logical commits (code / docs+
|
|
442
|
+
reviews / handwalk scaffolding / opencode's report), renamed `master` to
|
|
443
|
+
`main`, created a private GitHub repo (`mkhuda/agent-peer`) via `gh`, and
|
|
444
|
+
pushed. No `Co-Authored-By` line on any commit per the user's standing
|
|
445
|
+
instruction.
|
|
446
|
+
|
|
447
|
+
## Dead ends and rejected approaches (kept so they aren't retried)
|
|
448
|
+
|
|
449
|
+
- **Auto-spawning `listen` from inside `wait`** when no listener is found —
|
|
450
|
+
rejected by the user: risks silently piling up listeners, the exact
|
|
451
|
+
"stale listener" failure mode already diagnosed separately. Replaced with
|
|
452
|
+
a stderr warning that doesn't create any new process.
|
|
453
|
+
- **Detecting a "detached"/orphaned listener via TTY** (`??`) — disproven:
|
|
454
|
+
a currently-in-use listener has an identical TTY/PPID/STAT to a genuinely
|
|
455
|
+
stale one. No OS-level signal distinguishes them.
|
|
456
|
+
- **Checking the listener's own parent process for liveness** as a
|
|
457
|
+
detached-session signal — disproven: both listeners were already
|
|
458
|
+
reparented to launchd (PPID=1) immediately on spawn, and there was no
|
|
459
|
+
Antigravity app process visible locally at all to check against.
|
|
460
|
+
- **Recommending a bounded `--timeout` for the mandatory background-standby
|
|
461
|
+
pattern** — rejected by the user: turns `wait` into disguised polling for
|
|
462
|
+
any harness that re-invokes on background-task exit, and the lock-safety
|
|
463
|
+
argument for it was wrong (`flock` already releases on process death
|
|
464
|
+
regardless of cause).
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Review & Audit of `agent-peer` + `SKILL.md` from Google Antigravity Harness (AGY) Perspective
|
|
2
|
+
|
|
3
|
+
**Author:** Google Antigravity Agent (AGY)
|
|
4
|
+
**Date:** September 17, 2026
|
|
5
|
+
**Session Context:** Real-world evaluation of message delivery, backlog cursor, lock concurrency, auth fixes, multi-harness relay test (AGY -> PI -> OPENCODE -> AGY), and auto-session name detection.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Executive Summary
|
|
10
|
+
|
|
11
|
+
In a series of live multi-harness tests, `agent-peer` demonstrated excellent core IPC (inter-process communication) functionality between agents:
|
|
12
|
+
- Message delivery latency via Unix Domain Sockets was consistently **< 200ms** (~165-174ms).
|
|
13
|
+
- The **unread cursor & backlog merging** feature successfully captured messages arriving while the agent was busy executing other tasks without any message loss.
|
|
14
|
+
- The exclusive `fcntl.flock` lock successfully prevented duplicate *race conditions* for the same session name.
|
|
15
|
+
|
|
16
|
+
However, deep evaluation from the **Google Antigravity** harness perspective revealed several **ambiguities in `SKILL.md`**, **edge cases in the auto-detection process**, and **potential concurrency issues with subagents/multi-session setups** that are critical to address.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Ambiguities & Issues in `SKILL.md`
|
|
21
|
+
|
|
22
|
+
### 2.1 ~~The `/agent-peer intro` Command is Incompatible with Antigravity CLI~~ — WITHDRAWN
|
|
23
|
+
* **Initial Claim:** Line 8 stated `Trigger explicitly any time with /agent-peer intro to load this skill`, which was initially deemed incompatible with Antigravity's built-in slash commands.
|
|
24
|
+
* **Correction (re-verified in this session):** `/agent-peer` was proven to be fully supported & officially registered once the skill is installed — Antigravity UI integration for custom skill slash commands works seamlessly. Claim 2.1 above is **invalid**, left struck-through (not deleted) as an investigation trail.
|
|
25
|
+
|
|
26
|
+
### 2.2 Confusion Between Role Alias vs Registered Session Name
|
|
27
|
+
* **Issue in SKILL.md:** Section B instructs `agent-peer send <peer-name> "[fyi from antigravity]: ..."` and permits `--sender`.
|
|
28
|
+
* **Harness Reality:** When a sender executes `agent-peer send claude-test "..."`, the command fails (`ValueError: Session 'claude-test' not found`) if the receiving listener is registered in `~/.claude/sessions/` with a physical name like `agent-peer-e4`. The `--sender` parameter in the sender frame only alters the `from` string inside the payload, without registering a name alias in the registry.
|
|
29
|
+
* **Recommendation:** Clarify in `SKILL.md` that `<peer-name>` **MUST** match the `SESSION NAME` column output by `agent-peer list`, not a functional/role alias of the agent (unless that session was explicitly started with `--name <role>`).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 3. Auto-Detection Edge Cases (`detect_harness_identity`)
|
|
34
|
+
|
|
35
|
+
### 3.1 Subagent & Parallel Task Lock Collision
|
|
36
|
+
* **Auto-Detect Mechanism:** `detect_harness_identity()` traverses up the process tree (`os.getppid()`) looking for the first non-generic process name. For Antigravity, it finds `agy` with the main PID (e.g. `PID 33402`), creating the name `agy-33402`.
|
|
37
|
+
* **Issue with Subagent/Child Tasks:**
|
|
38
|
+
If an Antigravity agent spawns a subagent (e.g. via `invoke_subagent` or background worker processes within the same workspace), all subagents inherit the **same** parent `agy` process (`PID 33402`).
|
|
39
|
+
As a result:
|
|
40
|
+
1. All subagents are auto-detected with the **exact same** name (`agy-33402`).
|
|
41
|
+
2. When subagent A and subagent B both call `agent-peer wait`, subagent B fails immediately due to the `fcntl.flock` lock on `~/.agent-peer/locks/agy-33402.lock` ("already running").
|
|
42
|
+
3. Messages intended for a specific subagent get mixed up in `agy-33402`'s inbox.
|
|
43
|
+
* **Recommendation:** Add a rule in `SKILL.md` stating that if an agent spawns separate subagents or parallel tasks requiring independent communication, subagents **MUST** specify an explicit name (e.g., `agent-peer wait --name agy-subagent-1`).
|
|
44
|
+
|
|
45
|
+
### 3.2 Accumulated File Locks & Multi-Name Standby Leak
|
|
46
|
+
* **Antigravity Harness Reality:**
|
|
47
|
+
Antigravity manages background processes via `run_command` with `WaitMsBeforeAsync: 1000`. Over a long session, an agent might switch standby names from `--name antigravity-test` to auto-detected `agy-33402`.
|
|
48
|
+
* **Effect:** `task-58` continues to hold `antigravity-test.lock`, while `task-84` holds `agy-33402.lock`. Two separate listeners/waiters run concurrently in the background without releasing locks because their session names differ.
|
|
49
|
+
* **Recommendation:** `agent-peer` should include an `agent-peer stop` command or automatic lock cleanup if a previous listener under the same parent PID is detected with an old name alias.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 4. Codebase Security & Reliability Evaluation (Technical Findings Summary)
|
|
54
|
+
|
|
55
|
+
1. **Inbox File Permissions (`~/.agent-peer/inbox.jsonl`):**
|
|
56
|
+
The `~/.agent-peer/` directory and inbox files are created with default umask permissions (`0644`). In a multi-user Unix environment, other local users can read inter-agent message contents. *Fix: Enforce `chmod 0700` on the directory and `0600` on inbox/cursor files.*
|
|
57
|
+
2. **Missing `SO_PEERCRED` Socket Verification:**
|
|
58
|
+
`listener.py` verifies the string authentication token, but does not verify the sender's OS socket credentials (`SO_PEERCRED`).
|
|
59
|
+
3. **Non-Atomic File Append (`append_inbox`):**
|
|
60
|
+
`append_inbox()` performs message appending without `fcntl.flock`. Under high load from multiple socket threads, JSONL writes can suffer line interleaving.
|
|
61
|
+
4. **Notification Sanitization (AppleScript / Terminal Notifier):**
|
|
62
|
+
The regex `clean_snippet = re.sub(r'<[^>]+>', '', content)` in `listener.py:216` strips HTML/XML tags. If an agent sends JSX/HTML code snippets (like `<div>`), characters inside those tags get accidentally deleted from the notification display.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 5. Conclusion & SKILL.md Improvement Checklist
|
|
67
|
+
|
|
68
|
+
| Item | Status | Recommended Action |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| Slash Command `/agent-peer` | Withdrawn | Retain custom slash command support documentation in Antigravity UI |
|
|
71
|
+
| Peer Name Resolution | Error-prone | Enforce that `<peer-name>` must strictly match `agent-peer list` output |
|
|
72
|
+
| Subagent Isolation | High Risk | Document mandatory `--name <subagent-id>` requirement for subagents |
|
|
73
|
+
| File & Socket Security | Medium Risk | Enforce `chmod 0600` on inbox/cursors & lock files |
|
|
74
|
+
| Atomic File Write | Medium Risk | Implement `fcntl.flock` on `append_inbox()` |
|