@ctliz/agent-intercom-pi 0.11.0-connect.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.
@@ -0,0 +1,537 @@
1
+ ---
2
+ name: pi-intercom
3
+ description: |
4
+ Streamline session-to-session coordination with pi-intercom. Send messages,
5
+ delegate tasks, and coordinate work across multiple pi sessions on the same
6
+ machine. Use for planner-worker workflows, cross-session context sharing,
7
+ and real-time collaboration between sessions.
8
+ ---
9
+
10
+ # Pi Intercom Skill
11
+
12
+ Use this skill when you need to coordinate work across multiple pi sessions
13
+ running on the same machine. Pi-intercom enables direct 1:1 messaging between
14
+ sessions for delegation, context sharing, and collaborative workflows.
15
+
16
+ When you are supervising `pi-subagents`, delegated child agents can escalate to
17
+ you via `contact_supervisor` if `pi-subagents` supplied child bridge metadata.
18
+ This skill covers how to handle those orchestrator-side escalations.
19
+
20
+ ## When to Use
21
+
22
+ - **Task delegation**: Split work between a planner session and worker sessions
23
+ - **Context handoffs**: Send findings from a research session to an execution session
24
+ - **Clarification loops**: Worker asks questions, planner answers, work continues
25
+ - **Multi-session workflows**: Coordinate between specialized sessions (frontend/backend, research/implementation)
26
+
27
+ ## Core Patterns
28
+
29
+ ### Pattern 1: Planner-Worker Delegation
30
+
31
+ The most common pattern. One session holds the big picture, others do hands-on work.
32
+
33
+ **Setup** (in each session):
34
+ ```
35
+ /name planner # Terminal 1
36
+ /name worker # Terminal 2
37
+ ```
38
+
39
+ **Planner delegates a task** (fire-and-forget):
40
+ ```typescript
41
+ intercom_send({
42
+ to: "worker",
43
+ message: "Task-3: Add retry logic to API client. Key files: src/api/client.ts. Ask if anything's unclear."
44
+ })
45
+ ```
46
+
47
+ **Worker asks for clarification** (waits briefly, then continues asynchronously if needed):
48
+ ```typescript
49
+ intercom_ask({
50
+ to: "planner",
51
+ message: "Should I use exponential backoff or fixed intervals?"
52
+ })
53
+ // → Returns a prompt reply as the result
54
+ // → After 30 seconds, returns control; a late reply arrives as a new intercom message
55
+ ```
56
+
57
+ **Worker reports completion**:
58
+ ```typescript
59
+ intercom_send({
60
+ to: "planner",
61
+ message: "Task-3 complete. Added exponential backoff (100ms → 1600ms, max 5 retries)."
62
+ })
63
+ ```
64
+
65
+ Use `ask` only when the next step genuinely depends on an answer. Keep at most one unresolved ask to the same recipient; the broker rejects a second one. Different recipients may be asked concurrently when their answers are genuinely blocking. Assignments, completion notices, follow-ups, progress requests, and status checkpoints should use `send` so they do not create unnecessary wait edges.
66
+
67
+ ### Pattern 2: Send Related Updates as a Burst
68
+
69
+ The receiver durably queues every message and combines messages that arrive within a 300 ms quiet window into one model turn (never waiting more than 1 second). This means several independently produced updates can be sent promptly without waking the recipient once per message:
70
+
71
+ ```typescript
72
+ intercom_send({ to: "planner", message: "Tests pass." })
73
+ intercom_send({ to: "planner", message: "One API compatibility concern remains." })
74
+ intercom_send({ to: "planner", message: "See src/client.ts:88." })
75
+ ```
76
+
77
+ Do not add artificial sleeps to form a batch. Each original message keeps its sender, ID, attachments, timestamp, and reply context.
78
+
79
+ ### Pattern 3: Find Your Manager or Team
80
+
81
+ Orchestrator-owned coworkers should use the no-argument team tool instead of searching the global peer list:
82
+
83
+ ```typescript
84
+ intercom_team({})
85
+ // → Manager target plus live coworkers owned by that manager
86
+ ```
87
+
88
+ Use the returned manager target directly with `intercom_send` or `intercom_ask`. The manager follows adoption dynamically.
89
+
90
+ ### Pattern 4: Global Status Check
91
+
92
+ Use the global list only when you need independently launched peers:
93
+
94
+ ```typescript
95
+ intercom_list({})
96
+ // → Shows all connected sessions with names, cwd, models, and live status (`idle`, `thinking`, `tool:<name>`)
97
+ ```
98
+
99
+ ### Pattern 5: Reply Naturally
100
+
101
+ When responding to an inbound ask, prefer `reply` instead of reconstructing raw IDs:
102
+
103
+ ```typescript
104
+ // In the turn triggered by the ask:
105
+ intercom_reply({
106
+ message: "Use exponential backoff starting at 100ms."
107
+ })
108
+
109
+ // If replying later and there might be more than one pending ask:
110
+ intercom_pending({})
111
+ intercom_pending({ askId: "ask-..." }) // Retrieve the full untruncated body when needed
112
+ intercom_reply({ askId: "ask-...", message: "Use exponential backoff starting at 100ms." })
113
+ ```
114
+
115
+ `intercom_reply` preserves exact threading internally. `intercom_pending` returns stable receiver-local ask IDs and short previews; models never see or construct the protocol message ID. Managers can pass `session` with an owned coworker target from `intercom_team` to inspect that coworker's pending inbox.
116
+
117
+ ### Pattern 5: Broadcast to Multiple Workers
118
+
119
+ Send to multiple sessions in parallel:
120
+
121
+ ```typescript
122
+ const workers = ["worker-1", "worker-2", "worker-3"];
123
+ const task = "Check for null pointer exceptions in your assigned files";
124
+
125
+ // Fire-and-forget to all workers
126
+ workers.forEach(w =>
127
+ intercom_send({ to: w, message: task })
128
+ );
129
+ ```
130
+
131
+ ### Pattern 6: Send with Attachments
132
+
133
+ Share code snippets, files, or context:
134
+
135
+ ```typescript
136
+ intercom_send({
137
+ to: "worker",
138
+ message: "Here's the fix for the auth issue:",
139
+ attachments: [{
140
+ type: "snippet",
141
+ name: "auth.ts",
142
+ language: "typescript",
143
+ content: `function validateUser(user: User | null) {
144
+ if (!user) throw new Error("User required");
145
+ return user.email?.includes("@");
146
+ }`
147
+ }]
148
+ })
149
+ ```
150
+
151
+ ### Pattern 7: Handle Subagent Escalations (Orchestrator Side)
152
+
153
+ When `pi-subagents` spawns a delegated child and supplies child bridge metadata,
154
+ that child can reach you through `contact_supervisor`. You receive a formatted
155
+ message that includes run metadata:
156
+
157
+ ```
158
+ **From subagent-worker-78f659a3-1**
159
+
160
+ Subagent needs a supervisor decision.
161
+ Run: 78f659a3
162
+ Agent: worker
163
+ Child index: 0
164
+
165
+ Which API should I use?
166
+ ```
167
+
168
+ **Reply using `reply`:**
169
+
170
+ ```typescript
171
+ // The reply hint in the incoming message will show the exact call:
172
+ intercom_reply({ message: "Use the stable v2 API." })
173
+ ```
174
+
175
+ This works because `reply` resolves the correct sender and message ID automatically.
176
+
177
+ **Three types of escalations to expect:**
178
+
179
+ | Type | What it means | How to respond |
180
+ |------|---------------|----------------|
181
+ | `need_decision` | Subagent waits up to 30 seconds, then continues asynchronously while the request remains open. | Reply promptly with a clear decision. If you need more context, ask follow-up questions via `reply`. |
182
+ | `interview_request` | Subagent waits up to 30 seconds for structured answers, then continues asynchronously. | Reply with plain JSON or a fenced `json` block using the provided `{ "responses": [...] }` shape. |
183
+ | `progress_update` | Subagent is sharing meaningful progress or a plan-changing discovery. Not blocking. | Read and acknowledge. No reply required unless you want to redirect. |
184
+
185
+ **When a subagent asks:**
186
+
187
+ ```typescript
188
+ // In the turn triggered by the incoming ask:
189
+ intercom_reply({ message: "Use exponential backoff, max 3 retries." })
190
+ ```
191
+
192
+ **When a subagent sends an interview request:**
193
+
194
+ Read the rendered questions in the incoming message and reply with the exact ids in JSON. `info` questions are context-only and do not need response entries:
195
+
196
+ ```typescript
197
+ intercom_reply({
198
+ message: "```json\n{\n \"responses\": [\n { \"id\": \"api\", \"value\": \"Stable API\" },\n { \"id\": \"constraints\", \"value\": \"Keep the public error shape unchanged.\" }\n ]\n}\n```"
199
+ })
200
+ ```
201
+
202
+ **If you receive multiple pending asks from different subagents:**
203
+
204
+ ```typescript
205
+ intercom_pending({})
206
+ // → Shows all unresolved inbound asks with stable ask ID, sender, elapsed time, and preview
207
+
208
+ intercom_reply({ askId: "ask-...", message: "Use the v2 API." })
209
+ ```
210
+
211
+ **Important:** Only sessions where `pi-subagents` supplied child bridge metadata
212
+ get the `contact_supervisor` tool. Normal sessions use the split `intercom_send`,
213
+ `intercom_ask`, `intercom_reply`, `intercom_team`, `intercom_list`, `intercom_pending`, and
214
+ `intercom_status` tools. If you see the formatted supervisor decision/progress update message, treat
215
+ it as a `contact_supervisor` escalation.
216
+
217
+ ## Key Differences
218
+
219
+ | Action | Behavior | Use When |
220
+ |--------|----------|----------|
221
+ | `send` | Fire-and-forget | You don't need a response |
222
+ | `ask` | Waits up to 30 seconds, then continues asynchronously | You need an answer but should not hold the agent indefinitely |
223
+ | `reply` | Responds to the active or pending inbound ask | You were asked something and need to answer naturally |
224
+ | `pending` | Lists unresolved inbound asks | You need to see who is waiting before replying |
225
+ | `list` | Returns all sessions with live status | You need to discover targets or choose an idle peer |
226
+ | `status` | Returns your connection state | Troubleshooting |
227
+
228
+ ## Optional: Visible Peer Sessions via cmux or tmux
229
+
230
+ If no suitable intercom-connected peer session already exists and the task benefits from a long-lived visible conversation, you may spawn a new `pi` session.
231
+
232
+ Prefer `cmux new-split right` over new surfaces or workspaces so both sessions are visible side by side.
233
+
234
+ If `cmux` is unavailable, `tmux` is an optional fallback when it is installed and relevant. Use it with a private socket so the session is isolated and observable.
235
+
236
+ Use spawned peer sessions only for:
237
+ - same-codebase worker/planner splits
238
+ - reference-codebase scouting
239
+ - long-lived visible conversations where the user benefits from watching both sides
240
+
241
+ Do not use this for unrelated repos, trivial questions, or work you can finish cleanly in the current session.
242
+
243
+ ### Preferred: cmux Worker or Scout Session
244
+
245
+ Same codebase:
246
+
247
+ ```bash
248
+ cmux new-split right
249
+ sleep 0.5
250
+ cmux send --surface right 'cd /path/to/current/repo && pi\n'
251
+ ```
252
+
253
+ Reference codebase:
254
+
255
+ ```bash
256
+ cmux new-split right
257
+ sleep 0.5
258
+ cmux send --surface right 'cd /path/to/reference/repo && pi\n'
259
+ ```
260
+
261
+ ### Optional Fallback: tmux Worker or Scout Session
262
+
263
+ Same codebase:
264
+
265
+ ```bash
266
+ SOCKET_DIR=${TMPDIR:-/tmp}/pi-tmux-sockets
267
+ mkdir -p "$SOCKET_DIR"
268
+ SOCKET="$SOCKET_DIR/pi.sock"
269
+ SESSION=pi-worker
270
+ tmux -S "$SOCKET" new -d -s "$SESSION" -c "/path/to/current/repo" 'pi'
271
+ ```
272
+
273
+ Reference codebase:
274
+
275
+ ```bash
276
+ SOCKET_DIR=${TMPDIR:-/tmp}/pi-tmux-sockets
277
+ mkdir -p "$SOCKET_DIR"
278
+ SOCKET="$SOCKET_DIR/pi.sock"
279
+ SESSION=pi-reference-auth
280
+ tmux -S "$SOCKET" new -d -s "$SESSION" -c "/path/to/reference/repo" 'pi'
281
+ ```
282
+
283
+ When you use `tmux`, tell the user how to watch it:
284
+
285
+ ```bash
286
+ tmux -S "$SOCKET" attach -t "$SESSION"
287
+ ```
288
+
289
+ After launch, name the new session clearly so it is easy to target:
290
+
291
+ ```text
292
+ /name worker
293
+ /name reference-auth
294
+ ```
295
+
296
+ Then coordinate from the current session:
297
+
298
+ ```typescript
299
+ intercom_send({
300
+ to: "worker",
301
+ message: "Take task X. Ask if blocked."
302
+ })
303
+
304
+ intercom_ask({
305
+ to: "reference-auth",
306
+ message: "How does this repo structure token refresh retries?"
307
+ })
308
+ ```
309
+
310
+ ### Spawn Decision Rule
311
+
312
+ Spawn a visible peer session only when all of these are true:
313
+ - no existing intercom-connected session already fits the need
314
+ - the work benefits from a long-lived visible peer session
315
+ - the peer session is either in the same codebase or in an intentional reference codebase
316
+ - `cmux` is available, or `tmux` is available as an intentional fallback
317
+
318
+ If neither `cmux` nor `tmux` is available, skip this path and use normal `intercom` workflows.
319
+
320
+ ## Important Constraints
321
+
322
+ ### `ask` Limitations
323
+
324
+ - **30-second soft wait**: If no reply arrives, the tool returns a successful pending result and the agent continues
325
+ - **10-minute reply window**: The recipient can still answer later; the reply arrives as a new intercom message
326
+ - **One synchronous wait per recipient**: Independent recipients may be asked concurrently, but a second unresolved ask to the same recipient is rejected
327
+ - **Deferred is not cancelled**: After the soft wait, reverse asks are allowed and the original request stays late-replyable, including across broker restart
328
+ - **Cannot self-target**: A session cannot ask itself
329
+
330
+ ```typescript
331
+ // Check if already waiting before asking
332
+ const result = await intercom_ask({ to: "planner", message: "..." });
333
+ if (result.isError && result.content[0].text.includes("Already waiting")) {
334
+ // Use send instead, or wait for current ask to complete
335
+ }
336
+ ```
337
+
338
+ ### `send` Behavior
339
+
340
+ - **No reply wait**: `send` waits only until the receiver durably queues and acknowledges the message; it never waits for a conversational reply
341
+ - **Structured delivery**: `accepted` means broker-accepted, while `delivered` means receiver-acknowledged
342
+ - **Crash replay**: Unfinished sends are durably replayed with the same message ID after broker reconnect, so do not invent a new ID for a manual retry
343
+ - **Burst batching**: Nearby inbound messages become one recipient turn after 300 ms quiet, capped at 1 second
344
+ - **Confirmation dialogs**: If `confirmSend: true` in config, interactive sessions show a confirmation dialog
345
+ - **Replies skip confirmation**: `intercom_reply` never shows a send confirmation dialog
346
+
347
+ ## Best Practices
348
+
349
+ ### Use `ask` for decision workflows
350
+
351
+ When the worker needs information to proceed:
352
+
353
+ ```typescript
354
+ // GOOD: Worker waits briefly for a prompt decision
355
+ const reply = await intercom_ask({
356
+ to: "planner",
357
+ message: "API rate limit is 100/min. Should I implement client-side throttling or batching?"
358
+ });
359
+ // If reply.details?.pending is true, continue only with work that does not depend on the answer.
360
+ // A late answer will arrive as a new intercom message.
361
+ ```
362
+
363
+ ### Use `send` for notifications
364
+
365
+ When you just want to inform:
366
+
367
+ ```typescript
368
+ // GOOD: Fire-and-forget notification
369
+ intercom_send({
370
+ to: "reviewer",
371
+ message: "PR #123 is ready for review. Key changes in auth.ts."
372
+ });
373
+ // Continue immediately, don't wait
374
+ ```
375
+
376
+ ### Include reply hints in messages
377
+
378
+ Make it easy for recipients to respond:
379
+
380
+ ```typescript
381
+ // GOOD: Recipient sees exact command to reply
382
+ intercom_send({
383
+ to: "worker",
384
+ message: `Found the issue in auth.ts:142. Use getUserById() instead of getUser().
385
+
386
+ Reply with: intercom_reply({ message: "..." })`
387
+ });
388
+ ```
389
+
390
+ ### Name sessions meaningfully
391
+
392
+ Use `/name` so others can target you easily:
393
+
394
+ ```
395
+ /name api-worker
396
+ /name frontend-dev
397
+ /name planner
398
+ ```
399
+
400
+ ## Error Handling
401
+
402
+ ### Common Errors and Solutions
403
+
404
+ **"Already waiting for a reply"**
405
+ ```typescript
406
+ // You already have an unresolved ask to this recipient.
407
+ // Use send for a non-blocking follow-up or progress/status request.
408
+ intercom_send({ to: "planner", message: "Additional context for the open question." });
409
+
410
+ // Or wait for the current ask to resolve before asking another question.
411
+ // Asks to a different recipient may proceed concurrently.
412
+ ```
413
+
414
+ **"Cannot message the current session"**
415
+ ```typescript
416
+ // You cannot target yourself
417
+ // This usually means you confused session names - double-check the target
418
+ ```
419
+
420
+ **"Session not found"**
421
+ ```typescript
422
+ const result = await intercom_send({ to: "worker", message: "..." });
423
+ if (!result.delivered) {
424
+ console.log("Failed:", result.reason);
425
+ // → "Session not found" - check the name and list available sessions
426
+ await intercom_list({});
427
+ }
428
+ ```
429
+
430
+ **Ask continues asynchronously after 30 seconds**
431
+ ```typescript
432
+ const result = await intercom_ask({ to: "planner", message: "..." });
433
+ if (result.details?.pending) {
434
+ // The message was delivered, but no prompt reply arrived.
435
+ // Continue independent work; a late reply will trigger a new intercom message.
436
+ }
437
+ ```
438
+
439
+ ## Troubleshooting
440
+
441
+ ### Session not appearing in list
442
+
443
+ 1. Check intercom is enabled: `intercom_status({})`
444
+ 2. Verify the target session has loaded pi-intercom
445
+ 3. Ensure both sessions are on the same machine (intercom is same-machine only)
446
+
447
+ ### Session name flips or registration reports `SESSION_ID_IN_USE`
448
+
449
+ The same Pi session is open in more than one live runtime, such as a desktop terminal and a mobile/RPC host. Intercom keeps the first runtime authoritative instead of allowing the two clients to evict each other. Close or switch away from the duplicate runtime; one transcript/session ID must have only one live owner.
450
+
451
+ ### Message not delivered
452
+
453
+ ```typescript
454
+ const result = await intercom_send({ to: "worker", message: "..." });
455
+ if (!result.delivered) {
456
+ console.log("Failed:", result.reason);
457
+ // → "Session not found" or delivery failure reason
458
+ }
459
+ ```
460
+
461
+ ### Connection lost
462
+
463
+ Sessions automatically reconnect if the broker restarts. If persistently disconnected:
464
+
465
+ ```typescript
466
+ intercom_status({})
467
+ // Check if broker is running and restart if needed
468
+ ```
469
+
470
+ ## Common Workflows
471
+
472
+ ### Research → Implementation Handoff
473
+
474
+ ```typescript
475
+ // Research session finds relevant code
476
+ intercom_send({
477
+ to: "impl-session",
478
+ message: "Found the bug. The issue is in validateUser() - it doesn't check for null.",
479
+ attachments: [{
480
+ type: "snippet",
481
+ name: "validate.ts",
482
+ language: "typescript",
483
+ content: `// Line 45-52 - missing null check
484
+ function validateUser(user: User) {
485
+ return user.email?.includes("@"); // crashes if user is null
486
+ }`
487
+ }]
488
+ });
489
+ ```
490
+
491
+ ### Pair Debugging
492
+
493
+ ```typescript
494
+ // Session A encounters error
495
+ intercom_ask({
496
+ to: "session-b",
497
+ message: "Getting 'Cannot read property of undefined' at line 78. Can you check if data.users is populated before this call?"
498
+ });
499
+
500
+ // Session B investigates and replies
501
+ intercom_reply({
502
+ message: "data.users is null. The fetch failed silently. Add error handling in loadUsers()."
503
+ });
504
+ ```
505
+
506
+ ### Progress Reporting
507
+
508
+ ```typescript
509
+ // Worker sends periodic updates
510
+ intercom_send({ to: "planner", message: "Task-1 complete (15min). Starting Task-2." });
511
+ // ... work ...
512
+ intercom_send({ to: "planner", message: "Task-2 complete (30min). Task-3 blocked - need API key." });
513
+ // ... get unblocked ...
514
+ intercom_send({ to: "planner", message: "Task-3 complete. All done." });
515
+ ```
516
+
517
+ ### Long-Running Task with Checkpoints
518
+
519
+ ```typescript
520
+ // For long tasks, use send for progress and ask only for concrete decisions
521
+
522
+ // 1. Initial send with full context
523
+ intercom_send({
524
+ to: "worker",
525
+ message: "Implement user authentication. This will take 30+ minutes. I'll check in at milestones."
526
+ });
527
+
528
+ // 2. Worker sends progress via send (no timeout)
529
+ intercom_send({ to: "planner", message: "Milestone 1: Login form complete (10min)" });
530
+
531
+ // 3. Worker asks for specific decision when needed
532
+ const decision = await intercom_ask({
533
+ to: "planner",
534
+ message: "Should we use JWT or session cookies? Need decision to continue."
535
+ });
536
+ // Continue with the decision when prompt; otherwise continue only independent work.
537
+ ```