remote-pi 0.4.1 → 0.4.2
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.
- package/dist/index.js +60 -58
- package/dist/index.js.map +1 -1
- package/dist/mcp/mesh_server.js +170 -44
- package/dist/mcp/mesh_server.js.map +1 -1
- package/dist/session/broker.d.ts +18 -22
- package/dist/session/broker.js +10 -51
- package/dist/session/broker.js.map +1 -1
- package/dist/session/broker_remote.js +5 -5
- package/dist/session/broker_remote.js.map +1 -1
- package/dist/session/cwd_lock.js +11 -4
- package/dist/session/cwd_lock.js.map +1 -1
- package/dist/session/mesh_node.d.ts +11 -0
- package/dist/session/mesh_node.js +81 -2
- package/dist/session/mesh_node.js.map +1 -1
- package/dist/session/tools.js +17 -11
- package/dist/session/tools.js.map +1 -1
- package/package.json +1 -1
- package/skills/agent-network/SKILL.md +163 -280
- package/skills/claude-agent-network/SKILL.md +0 -239
|
@@ -1,52 +1,80 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-network
|
|
3
|
-
description: Use when
|
|
3
|
+
description: Use when the remote-pi mesh tools (`list_peers`, `agent_send`, and — on Claude — `get_messages`) are available. You are an agent (a Claude session or a Pi coding agent) connected to the remote-pi agent mesh over a local broker. This skill teaches how to discover who's online (`list_peers`), how to send messages with a delivery ACK (`agent_send`), how incoming messages reach you (via `get_messages` on Claude, or delivered into your turn on Pi), how to reply (echo `re`), and how cross-PC addressing works (`<pc_label>:<peer>`).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Agent Network (
|
|
6
|
+
# Agent Network (remote-pi mesh)
|
|
7
7
|
|
|
8
|
-
You are connected to
|
|
9
|
-
|
|
10
|
-
messages and you can send messages
|
|
8
|
+
You are connected to the **remote-pi agent mesh**. Other agents — other Claude
|
|
9
|
+
sessions, Pi coding agents on this machine, and agents on the Owner's other PCs
|
|
10
|
+
(reached through the relay) — can send you messages, and you can send messages
|
|
11
|
+
to them.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Read this to the end before acting. The protocol is **event-driven**, not
|
|
14
|
+
request/reply. Getting the receive model wrong leaves coordination broken.
|
|
15
|
+
|
|
16
|
+
**Your tools:** `list_peers` and `agent_send` always. On Claude you also have
|
|
17
|
+
`get_messages` (a Pi agent receives messages directly into its turn instead —
|
|
18
|
+
see below).
|
|
15
19
|
|
|
16
20
|
---
|
|
17
21
|
|
|
18
|
-
## The most important rule
|
|
22
|
+
## The most important rule: read your inbox every turn
|
|
23
|
+
|
|
24
|
+
You only ever receive messages addressed to you — the broker filters before
|
|
25
|
+
delivery. **If a message arrived, someone wanted your attention. Don't ignore
|
|
26
|
+
it.** How a message reaches you depends on your runtime:
|
|
27
|
+
|
|
28
|
+
- **Claude (MCP):** incoming messages are buffered. **At the start of every
|
|
29
|
+
turn, call `get_messages`** to drain and read them:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
get_messages()
|
|
33
|
+
→ "[2026-05-30T12:00:01Z] from=backend re=<your-id>
|
|
34
|
+
id=<msg-id>
|
|
35
|
+
{ "shape": { "sub": "string", "exp": "number" } }"
|
|
36
|
+
```
|
|
19
37
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
38
|
+
It returns all pending messages and clears the buffer (call once per turn),
|
|
39
|
+
or `(no messages)` when nothing is waiting — that's normal, keep working. A
|
|
40
|
+
channel push (`📨 Message from …`) may nudge you mid-session; still call
|
|
41
|
+
`get_messages` for the full structured payload.
|
|
23
42
|
|
|
24
|
-
**
|
|
25
|
-
|
|
43
|
+
- **Pi:** the runtime delivers each incoming message directly as a new turn
|
|
44
|
+
input the moment it arrives — no polling, no `get_messages`. You'll see it
|
|
45
|
+
prefixed `[agent-network] message from "<peer>" (id=…, re=…)`.
|
|
46
|
+
|
|
47
|
+
Either way: no wait/sleep/poll-loop. Replies to your own sends arrive on a
|
|
48
|
+
**later turn**, never inline.
|
|
26
49
|
|
|
27
50
|
---
|
|
28
51
|
|
|
29
|
-
## First thing
|
|
52
|
+
## First thing in a new session: `list_peers`
|
|
30
53
|
|
|
31
|
-
Before
|
|
32
|
-
a cheap metadata tool that returns the current inventory:
|
|
54
|
+
Before sending anything, find out who's actually online:
|
|
33
55
|
|
|
34
56
|
```
|
|
35
57
|
list_peers()
|
|
36
|
-
→
|
|
58
|
+
→ backend
|
|
59
|
+
frontend
|
|
60
|
+
casa:agent-1
|
|
61
|
+
trab:worker
|
|
37
62
|
```
|
|
38
63
|
|
|
39
|
-
|
|
40
|
-
not a turn of another agent). Use it freely:
|
|
64
|
+
Synchronous (resolves in milliseconds — not another agent's turn). Use it:
|
|
41
65
|
|
|
42
66
|
- At the start of a session, to see what mesh you're in
|
|
43
|
-
- After receiving `peer_joined` / `peer_left` to refresh
|
|
44
67
|
- Before any `agent_send` whose target name is uncertain
|
|
68
|
+
- To refresh — peers join and leave over time
|
|
69
|
+
|
|
70
|
+
**Presence is passive (pull, not push).** A peer joining or leaving does **not**
|
|
71
|
+
wake your turn. When your view feels stale, just call `list_peers` again — it's
|
|
72
|
+
the authoritative snapshot. Don't expect `peer_joined`/`peer_left` events.
|
|
45
73
|
|
|
46
74
|
**Entry shape:**
|
|
47
|
-
- `backend` → local peer (this machine, same
|
|
48
|
-
- `casa:agent-1` → cross-PC peer on the
|
|
49
|
-
|
|
75
|
+
- `backend` → local peer (this machine, same broker)
|
|
76
|
+
- `casa:agent-1` → cross-PC peer on the PC labeled `casa` (the Owner's other
|
|
77
|
+
machine, reached through the relay)
|
|
50
78
|
|
|
51
79
|
You are excluded from the result — no need to filter yourself out.
|
|
52
80
|
|
|
@@ -54,112 +82,79 @@ You are excluded from the result — no need to filter yourself out.
|
|
|
54
82
|
|
|
55
83
|
## Anatomy of a message (envelope)
|
|
56
84
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
```json
|
|
60
|
-
{
|
|
61
|
-
"from": "orchestrator",
|
|
62
|
-
"to": "backend",
|
|
63
|
-
"id": "uuid-v7",
|
|
64
|
-
"re": null,
|
|
65
|
-
"body": <message contents>
|
|
66
|
-
}
|
|
67
|
-
```
|
|
85
|
+
Each message carries: `from`, `to`, `id`, `re`, and `body`.
|
|
68
86
|
|
|
69
87
|
| Field | Meaning |
|
|
70
88
|
|---|---|
|
|
71
|
-
| `from` | Who sent it. Use this
|
|
72
|
-
| `to` | You (or
|
|
73
|
-
| `id` | Unique
|
|
74
|
-
| `re` | If this message is a REPLY to
|
|
75
|
-
| `body` | Free-form content
|
|
89
|
+
| `from` | Who sent it. Use this verbatim as your `to` when replying. |
|
|
90
|
+
| `to` | You (or `broadcast`, or a list of names including yours). |
|
|
91
|
+
| `id` | Unique id of this message. Echo it as `re` when you reply. |
|
|
92
|
+
| `re` | If set, this message is itself a REPLY to an earlier `id` of yours. Otherwise `null`. |
|
|
93
|
+
| `body` | Free-form content — string or JSON, sender's choice. |
|
|
76
94
|
|
|
77
95
|
---
|
|
78
96
|
|
|
79
|
-
##
|
|
97
|
+
## Sending: `agent_send` returns an ACK status
|
|
80
98
|
|
|
81
|
-
`agent_send
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
`agent_send({ to, body, re? })` is how you talk to peers. Every **unicast**
|
|
100
|
+
call returns a status telling you what happened at the recipient. **Always
|
|
101
|
+
inspect the status — it dictates what to do next.**
|
|
84
102
|
|
|
85
|
-
| Status |
|
|
103
|
+
| Status | Means | What you do |
|
|
86
104
|
|---|---|---|
|
|
87
|
-
| `received` |
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `timeout` | No ACK in 5s. Transport error — broker may be down, peer disappeared mid-handshake. | Treat as transient. Retry once after a longer delay (10s+), then escalate. |
|
|
91
|
-
| `sent` | You used `to: "broadcast"` or an array. There is no single ACK target. | Move on. Broadcasts are fire-and-forget. |
|
|
92
|
-
| `refused` | The tool refused your call locally (e.g., you tried to message yourself, or you're not in a session). | Fix the call. Don't retry the same arguments. |
|
|
93
|
-
|
|
94
|
-
`busy` is the most common non-trivial answer. Two **new** messages aimed
|
|
95
|
-
at the same peer in quick succession will see the second one as `busy`
|
|
96
|
-
— the peer can only be processing one turn at a time.
|
|
97
|
-
|
|
98
|
-
**Replies are exempt from busy gating.** A message with `re=<some-id>`
|
|
99
|
-
(an answer to something the recipient asked) is always delivered,
|
|
100
|
-
because it resolves pending state at the recipient rather than starting
|
|
101
|
-
a new turn for them. So if you fan out questions to several peers in
|
|
102
|
-
the same turn, every peer's reply will reach you even when you're still
|
|
103
|
-
processing — they all flow into your inbox for the next turn.
|
|
105
|
+
| `received` | Broker delivered the envelope. Delivery is reliable — even if the peer is mid-turn, its harness enqueues the message for the next turn. | Move on. Any reply arrives later. |
|
|
106
|
+
| `denied` | Peer explicitly refused (or no such peer). | Do NOT retry. Report to the user. |
|
|
107
|
+
| `timeout` | No ACK (~5s). Transport error — broker down, or peer vanished. | Treat as transient. Retry once after ~10s, then escalate. |
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
For `to: "broadcast"` (or a name array), there's no single ACK — it's
|
|
110
|
+
fire-and-forget (`status: "sent"`).
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
**Delivery is reliable — no retry-on-busy.** A message sent to a peer that's
|
|
113
|
+
mid-turn is still delivered: the peer's harness queues it and processes it on
|
|
114
|
+
its upcoming turn. You never need to retry because a peer was busy. `re=<id>`
|
|
115
|
+
is purely **correlation** — set it so the recipient (and you) can thread an
|
|
116
|
+
answer to a question; it carries no special delivery semantics.
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
push-based:
|
|
118
|
+
---
|
|
111
119
|
|
|
112
|
-
|
|
113
|
-
2. Your turn continues. You might do other work, or finish.
|
|
114
|
-
3. **Later** — possibly several turns later — the peer finishes its
|
|
115
|
-
own turn, processes your message, and sends a reply.
|
|
116
|
-
4. The reply lands in your inbox as a normal envelope. You see it on
|
|
117
|
-
your next turn input, with `re` set to the `id` you sent earlier.
|
|
120
|
+
## Receiving: replies arrive on a later turn
|
|
118
121
|
|
|
119
|
-
You do not
|
|
120
|
-
a new turn input the moment it arrives.
|
|
122
|
+
You **do not block** waiting for a reply. The model is event-driven:
|
|
121
123
|
|
|
122
|
-
|
|
124
|
+
1. You call `agent_send` → status `received`.
|
|
125
|
+
2. Your turn continues / ends.
|
|
126
|
+
3. **Later** the peer finishes its own work and sends a reply.
|
|
127
|
+
4. The reply reaches your inbox (via `get_messages` on Claude, or as a new turn
|
|
128
|
+
input on Pi), with `re` set to the `id` you originally sent.
|
|
123
129
|
|
|
124
|
-
|
|
130
|
+
### Walk-through
|
|
125
131
|
|
|
126
132
|
```
|
|
127
133
|
agent_send({ to: "backend", body: { q: "what's the JWT shape?" } })
|
|
128
|
-
→
|
|
134
|
+
→ Delivered to backend # status received; remember the message id
|
|
129
135
|
```
|
|
130
136
|
|
|
131
|
-
|
|
132
|
-
sends). Turn ends.
|
|
133
|
-
|
|
134
|
-
A few seconds later, the runtime hands you a new turn with this input:
|
|
137
|
+
Your turn continues. A turn or two later you receive:
|
|
135
138
|
|
|
136
139
|
```
|
|
137
|
-
|
|
140
|
+
from=backend re=<your-id> id=<new-id>
|
|
138
141
|
{ "shape": { "sub": "string", "exp": "number", "roles": ["string"] } }
|
|
139
|
-
(This is a reply to a previous message of yours.)
|
|
140
142
|
```
|
|
141
143
|
|
|
142
|
-
You correlate by
|
|
143
|
-
when you originally sent. Now you have your answer. Use it.
|
|
144
|
+
You correlate by `re` — it matches the send you made. Now you have your answer.
|
|
144
145
|
|
|
145
146
|
---
|
|
146
147
|
|
|
147
|
-
##
|
|
148
|
+
## Replying to a message
|
|
148
149
|
|
|
149
|
-
|
|
150
|
+
When you receive:
|
|
150
151
|
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
"to": "backend",
|
|
155
|
-
"id": "abc-uuid",
|
|
156
|
-
"re": null,
|
|
157
|
-
"body": { "task": "Implement POST /auth/login" }
|
|
158
|
-
}
|
|
152
|
+
```
|
|
153
|
+
from=orchestrator id=abc-uuid re=(none)
|
|
154
|
+
{ "task": "Implement POST /auth/login" }
|
|
159
155
|
```
|
|
160
156
|
|
|
161
|
-
|
|
162
|
-
you send back another envelope **with `re` set to the original `id`**:
|
|
157
|
+
Reply with `re` set to that `id`, and `to` set to the sender's `from`:
|
|
163
158
|
|
|
164
159
|
```
|
|
165
160
|
agent_send({
|
|
@@ -169,240 +164,128 @@ agent_send({
|
|
|
169
164
|
})
|
|
170
165
|
```
|
|
171
166
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
question — and the coordination drifts. **Always echo `re` on a reply.**
|
|
167
|
+
Without `re`, the sender gets your message but can't match it to the
|
|
168
|
+
question — coordination drifts. **Always echo `re` on a reply.**
|
|
175
169
|
|
|
176
170
|
---
|
|
177
171
|
|
|
178
172
|
## Asking multiple peers at once
|
|
179
173
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
own ACK status. Then your turn ends, and the replies arrive in future
|
|
183
|
-
turns as they come in.
|
|
174
|
+
Fire multiple `agent_send` in one turn — each returns its own ACK. Replies
|
|
175
|
+
arrive on future turns as peers finish.
|
|
184
176
|
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
agent_send({ to: "
|
|
188
|
-
agent_send({ to: "
|
|
189
|
-
agent_send({ to: "infra", body: { q: "ETA for Y?" } }); // -> busy — retry next turn
|
|
177
|
+
```
|
|
178
|
+
agent_send({ to: "backend", body: { q: "JWT shape?" } }) // received
|
|
179
|
+
agent_send({ to: "frontend", body: { q: "theme tokens?" } }) // received
|
|
180
|
+
agent_send({ to: "infra", body: { q: "ETA for Y?" } }) // received (queued if mid-turn)
|
|
190
181
|
```
|
|
191
182
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
question (the ACK return shape includes `id`, store it).
|
|
195
|
-
|
|
196
|
-
Don't assume replies arrive in send order. Use `re` to identify what
|
|
197
|
-
each reply is for.
|
|
198
|
-
|
|
199
|
-
### When to retry
|
|
200
|
-
|
|
201
|
-
A `busy` peer might be free in a few seconds. The skill recommends:
|
|
202
|
-
|
|
203
|
-
- Try once → `busy` → wait ~2s, try again
|
|
204
|
-
- Still `busy` → wait ~5s, try again
|
|
205
|
-
- Still `busy` → abandon (report to human) or escalate to orchestrator
|
|
206
|
-
|
|
207
|
-
Retries are **your** responsibility as the sender. The broker does not
|
|
208
|
-
queue messages.
|
|
183
|
+
Track which `id` maps to which question. Don't assume replies arrive in send
|
|
184
|
+
order — use `re` to identify what each reply answers.
|
|
209
185
|
|
|
210
186
|
---
|
|
211
187
|
|
|
212
188
|
## Cross-PC addressing (`<pc_label>:<peer>`)
|
|
213
189
|
|
|
214
|
-
When the Owner has paired multiple
|
|
215
|
-
on the other machine appear in `list_peers` with a prefix:
|
|
190
|
+
When the Owner has paired multiple PCs, remote peers appear with a prefix:
|
|
216
191
|
|
|
217
192
|
```
|
|
218
|
-
|
|
193
|
+
list_peers() → backend frontend casa:agent-1 trab:worker
|
|
219
194
|
```
|
|
220
195
|
|
|
221
|
-
|
|
196
|
+
Send to a remote peer with the prefixed name verbatim:
|
|
222
197
|
|
|
223
198
|
```
|
|
224
199
|
agent_send({ to: "casa:agent-1", body: { ... } })
|
|
225
200
|
```
|
|
226
201
|
|
|
227
|
-
The
|
|
228
|
-
|
|
229
|
-
`
|
|
230
|
-
|
|
231
|
-
When you **reply** to a cross-PC message, use the original sender's
|
|
232
|
-
`from` verbatim — it already carries the prefix:
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
Incoming: { from: "casa:sess-3", to: "agent-1", id: "abc", re: null, ... }
|
|
236
|
-
Reply: agent_send({ to: "casa:sess-3", body: {...}, re: "abc" })
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
You do NOT prefix your own outgoing `from` — that rewrite happens at the
|
|
240
|
-
broker layer.
|
|
202
|
+
The relay routes it across the mesh; `received | denied | timeout` semantics
|
|
203
|
+
are identical to local. When you **reply** to a cross-PC message, use the
|
|
204
|
+
sender's `from` verbatim (it already carries the prefix) as your `to`. You
|
|
205
|
+
never prefix your own name — the broker handles that.
|
|
241
206
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"_relay"`) for offline/not_authorized/bad_envelope — you'll see it in
|
|
249
|
-
the inbox as a reply with `re=<your-send-id>` and `body.type:
|
|
250
|
-
"transport_error"`. Treat exactly like timeout.
|
|
207
|
+
Cross-PC failure notes:
|
|
208
|
+
- `denied` → the remote broker has no peer by that name (left, or stale cache
|
|
209
|
+
→ call `list_peers` again).
|
|
210
|
+
- `timeout` → the other PC is offline or the relay is unreachable. The relay
|
|
211
|
+
may also synthesise a `transport_error` reply (`from: "_relay"`,
|
|
212
|
+
`body.type: "transport_error"`) — treat exactly like timeout.
|
|
251
213
|
|
|
252
214
|
---
|
|
253
215
|
|
|
254
216
|
## Broadcast and multicast
|
|
255
217
|
|
|
256
|
-
`to: "broadcast"`
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
-
|
|
260
|
-
|
|
261
|
-
uncorrelated
|
|
262
|
-
|
|
263
|
-
Broadcast/multicast skip the ACK protocol entirely — the tool returns
|
|
264
|
-
`status: "sent"` immediately. You don't know who received it. If you
|
|
265
|
-
need delivery confirmation, use multiple unicast sends.
|
|
218
|
+
- `to: "broadcast"` → every other peer. `to: ["a", "b"]` → the listed names.
|
|
219
|
+
- Use for announcements ("wave 2 started", "I'm taking the lock on /contracts"),
|
|
220
|
+
never for questions (replies would be uncorrelated).
|
|
221
|
+
- Broadcast/multicast skip the ACK — status is `sent`, you don't know who
|
|
222
|
+
received it. For delivery confirmation, use individual unicast sends.
|
|
266
223
|
|
|
267
224
|
---
|
|
268
225
|
|
|
269
|
-
##
|
|
226
|
+
## When in doubt
|
|
270
227
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
"body": { "type": "peer_left", "name": "frontend" } }
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Use these to track who's online. Don't ask peers you know are offline.
|
|
284
|
-
|
|
285
|
-
If you missed events (just woke up, or your view feels stale), call
|
|
286
|
-
`list_peers` — it returns the authoritative snapshot in milliseconds.
|
|
287
|
-
|
|
288
|
-
Do **not** send a `list_peers` envelope to the broker via `agent_send`.
|
|
289
|
-
That's the old pre-tool pattern: it worked, but the reply arrived in a
|
|
290
|
-
future turn and the ACK status didn't carry the peer list. The dedicated
|
|
291
|
-
`list_peers` tool is strictly better — synchronous, typed return.
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Situations where you're in doubt
|
|
296
|
-
|
|
297
|
-
### "I received a task I don't understand"
|
|
298
|
-
|
|
299
|
-
Reply with `status: "error"` in the body, echoing the original `id` in
|
|
300
|
-
`re`. Don't go silent.
|
|
301
|
-
|
|
302
|
-
### "I received a message with `re` set, but I never sent that question"
|
|
303
|
-
|
|
304
|
-
Late reply to something that already wrapped up. Ignore. Don't reply
|
|
305
|
-
to a reply.
|
|
306
|
-
|
|
307
|
-
### "I'm in a session but no message ever arrives"
|
|
308
|
-
|
|
309
|
-
Normal. You only receive when someone addresses you. Keep working on
|
|
310
|
-
the current task. Don't poll the broker.
|
|
311
|
-
|
|
312
|
-
### "I sent something but got `timeout`"
|
|
313
|
-
|
|
314
|
-
The broker didn't ACK in 5s. Either the broker is restarting (failover)
|
|
315
|
-
or the peer disappeared between registration and delivery. Retry once
|
|
316
|
-
after ~10s; if still timeout, treat as transport failure and escalate.
|
|
317
|
-
|
|
318
|
-
### "The leader died (peer_left from `broker` for the leader)"
|
|
319
|
-
|
|
320
|
-
The transport layer automatically promotes another peer to leader. Your
|
|
321
|
-
client reconnects transparently in ~500ms. During that window,
|
|
322
|
-
`agent_send` may return `timeout` — retry once after a beat before
|
|
323
|
-
giving up.
|
|
228
|
+
- **Received a task you don't understand** → reply with `body.status:"error"`,
|
|
229
|
+
echoing the original `id` in `re`. Don't go silent.
|
|
230
|
+
- **Received a `re` you never sent** → late reply to something already wrapped
|
|
231
|
+
up. Ignore. Don't reply to a reply.
|
|
232
|
+
- **No messages ever arrive** → normal. You only receive when addressed. Keep
|
|
233
|
+
working; don't poll the broker.
|
|
234
|
+
- **`timeout` on send** → broker restarting (failover) or peer vanished. The
|
|
235
|
+
client reconnects transparently in ~500ms; retry once after a beat, then
|
|
236
|
+
escalate.
|
|
324
237
|
|
|
325
238
|
---
|
|
326
239
|
|
|
327
|
-
## Legacy: `agent_request`
|
|
328
|
-
|
|
329
|
-
You may see references to a tool called `agent_request` that takes a
|
|
330
|
-
target + body and **blocks the entire turn** waiting for the peer's
|
|
331
|
-
content reply. It still works, but emits a deprecation warning on use
|
|
332
|
-
and will be removed.
|
|
240
|
+
## Legacy: `agent_request` (Pi only, deprecated)
|
|
333
241
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
**Migration:** every `agent_request` call becomes an `agent_send`. The
|
|
341
|
-
reply arrives in a future turn (see the walk-through above). Treat the
|
|
342
|
-
inbox as your event loop, not your call stack.
|
|
242
|
+
On Pi you may see a tool called `agent_request` that takes a target + body and
|
|
243
|
+
**blocks the entire turn** waiting for the peer's content reply. It still
|
|
244
|
+
works but emits a deprecation warning. It blocks your turn (costs tokens and
|
|
245
|
+
wall time), gives no ACK signal, and pairs badly with parallel multi-peer
|
|
246
|
+
questions. **Migrate every `agent_request` to `agent_send`** + reading your
|
|
247
|
+
inbox on a later turn. (Claude has no `agent_request` — use `agent_send`.)
|
|
343
248
|
|
|
344
249
|
---
|
|
345
250
|
|
|
346
251
|
## Single-page summary
|
|
347
252
|
|
|
348
|
-
1. **
|
|
349
|
-
|
|
350
|
-
2. **
|
|
351
|
-
—
|
|
352
|
-
3. **
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
6.
|
|
358
|
-
|
|
359
|
-
7. You never receive your own messages.
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
9. `agent_request` is deprecated. Migrate to `agent_send` + inbox.
|
|
363
|
-
|
|
364
|
-
That's the whole protocol. Re-read it when in doubt.
|
|
253
|
+
1. **Every turn**: read your inbox first — `get_messages()` on Claude; on Pi
|
|
254
|
+
messages arrive as turn input automatically.
|
|
255
|
+
2. **Discover**: `list_peers()` → locals + `<pc>:<peer>` cross-PC. Synchronous,
|
|
256
|
+
self-excluded. Presence is pull-based — join/leave don't wake you.
|
|
257
|
+
3. **Send**: `agent_send({to, body, re?})` → inspect the status.
|
|
258
|
+
4. **Unicast status**: `received | denied | timeout`. Delivery is reliable —
|
|
259
|
+
`received` even if the peer is mid-turn (its harness queues it); abandon on
|
|
260
|
+
`denied`; investigate on `timeout`. No retry-on-busy.
|
|
261
|
+
5. **Broadcast/multicast**: status `sent`. Fire-and-forget.
|
|
262
|
+
6. **Reply**: set `re` to their `id`, `to` to their `from` (prefix and all).
|
|
263
|
+
`re` is correlation only.
|
|
264
|
+
7. You never receive your own messages.
|
|
265
|
+
|
|
266
|
+
Re-read when in doubt.
|
|
365
267
|
|
|
366
268
|
---
|
|
367
269
|
|
|
368
270
|
## Mini-FAQ
|
|
369
271
|
|
|
370
272
|
**Q: Can I send a message to myself?**
|
|
371
|
-
A: No. `agent_send` refuses early
|
|
372
|
-
|
|
373
|
-
as a second line of defense.
|
|
273
|
+
A: No. `agent_send` refuses early (`status: "refused"`) when `to` matches your
|
|
274
|
+
own name, and the broker drops unicast self-loops as a second line of defense.
|
|
374
275
|
|
|
375
|
-
**Q: What if the peer never replies
|
|
376
|
-
A: Then you never see a reply.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
skill in the peer's process should make them reply. If they're a
|
|
380
|
-
non-Pi process that just listens, you live with the silence.
|
|
276
|
+
**Q: What if the peer never replies?**
|
|
277
|
+
A: Then you never see a reply. Your send returned `received` (the broker handed
|
|
278
|
+
it over); the peer just chose not to answer. There's no implicit timeout on
|
|
279
|
+
replies.
|
|
381
280
|
|
|
382
281
|
**Q: How many sends can I fire in one turn?**
|
|
383
|
-
A: No hard limit. But if you fire 10+ unicasts, question whether
|
|
384
|
-
|
|
385
|
-
narrow; orchestrators dispatch wide.
|
|
282
|
+
A: No hard limit. But if you fire 10+ unicasts, question whether you should be a
|
|
283
|
+
worker (answer narrow) rather than an orchestrator (dispatch wide).
|
|
386
284
|
|
|
387
285
|
**Q: Is order preserved?**
|
|
388
|
-
A: Per-pair, yes — the broker is FIFO. Across pairs, replies arrive
|
|
389
|
-
|
|
390
|
-
order.
|
|
286
|
+
A: Per-pair, yes — the broker is FIFO. Across pairs, replies arrive whenever the
|
|
287
|
+
senders finish. Don't assume reply order matches send order.
|
|
391
288
|
|
|
392
289
|
**Q: Can `body` be binary?**
|
|
393
|
-
A: Not directly.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
**Q: Can I disconnect any time?**
|
|
397
|
-
A: Yes. The transport sends `peer_left` automatically when you close.
|
|
398
|
-
Other agents see you go.
|
|
399
|
-
|
|
400
|
-
---
|
|
401
|
-
|
|
402
|
-
## See also
|
|
403
|
-
|
|
404
|
-
- [`plan/19-agent-network.md`](../plan/19-agent-network.md) — original protocol design
|
|
405
|
-
- [`plan/25-pc-mesh-bootstrap.md`](../plan/25-pc-mesh-bootstrap.md) — ACK protocol motivation + Wave 0 + cross-PC plans
|
|
406
|
-
- `~/.pi/remote/sessions/<name>/audit.jsonl` — append-only log of every
|
|
407
|
-
envelope that passed through the broker, with `ack_status` per entry
|
|
408
|
-
for cross-checking what really happened.
|
|
290
|
+
A: Not directly. Base64 inside a string if you must. JSON is the intended
|
|
291
|
+
payload.
|