@melihmucuk/pi-crew 1.0.6 → 1.0.8

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.
Files changed (39) hide show
  1. package/dist/agent-discovery.d.ts +0 -5
  2. package/dist/agent-discovery.js +1 -1
  3. package/dist/bootstrap-session.d.ts +13 -4
  4. package/dist/bootstrap-session.js +25 -16
  5. package/dist/index.js +37 -9
  6. package/dist/integration/register-command.d.ts +2 -2
  7. package/dist/integration/register-command.js +5 -5
  8. package/dist/integration/register-renderers.js +3 -0
  9. package/dist/integration/register-tools.d.ts +2 -2
  10. package/dist/integration/register-tools.js +2 -2
  11. package/dist/integration/tool-presentation.d.ts +2 -3
  12. package/dist/integration/tool-presentation.js +7 -8
  13. package/dist/integration/tools/crew-abort.d.ts +1 -1
  14. package/dist/integration/tools/crew-abort.js +3 -3
  15. package/dist/integration/tools/crew-done.d.ts +1 -1
  16. package/dist/integration/tools/crew-done.js +2 -2
  17. package/dist/integration/tools/crew-list.d.ts +1 -1
  18. package/dist/integration/tools/crew-list.js +3 -3
  19. package/dist/integration/tools/crew-respond.d.ts +1 -1
  20. package/dist/integration/tools/crew-respond.js +6 -7
  21. package/dist/integration/tools/crew-spawn.d.ts +1 -1
  22. package/dist/integration/tools/crew-spawn.js +17 -14
  23. package/dist/integration/tools/tool-deps.d.ts +3 -2
  24. package/dist/integration.d.ts +2 -2
  25. package/dist/integration.js +3 -3
  26. package/dist/runtime/crew-runtime.d.ts +61 -0
  27. package/dist/{crew-manager.js → runtime/crew-runtime.js} +84 -58
  28. package/dist/runtime/delivery-coordinator.d.ts +16 -7
  29. package/dist/runtime/delivery-coordinator.js +47 -21
  30. package/dist/runtime/subagent-registry.d.ts +1 -0
  31. package/dist/runtime/subagent-registry.js +3 -0
  32. package/dist/runtime/subagent-state.d.ts +2 -0
  33. package/dist/status-widget.d.ts +2 -2
  34. package/dist/status-widget.js +3 -3
  35. package/dist/subagent-messages.d.ts +5 -2
  36. package/dist/subagent-messages.js +5 -4
  37. package/docs/architecture.md +106 -847
  38. package/package.json +7 -7
  39. package/dist/crew-manager.d.ts +0 -44
@@ -1,908 +1,167 @@
1
1
  # pi-crew Architecture
2
2
 
3
- This document explains the technical architecture of `@melihmucuk/pi-crew` for both human users and coding agents.
3
+ This document explains the technical architecture of `@melihmucuk/pi-crew`, focusing on what makes this extension unique.
4
4
 
5
- It describes runtime behavior, integration points, ownership rules, delivery semantics, and implementation boundaries. It intentionally avoids code snippets. Source files are referenced instead, because exact code can change while the behavioral contract should remain stable.
5
+ For pi fundamentals, see pi docs: `extensions.md`, `sdk.md`, `session.md`. Project-level guardrails are in `AGENTS.md`.
6
6
 
7
- All paths in this document are relative to the repository root.
7
+ ## 1. What pi-crew adds
8
8
 
9
- ## 1. What the project is
9
+ `pi-crew` is a non-blocking subagent orchestration extension. It lets one pi session delegate work to isolated subagent sessions without blocking the caller. Results are delivered back as `crew-result` custom messages.
10
10
 
11
- `pi-crew` is a non-blocking subagent orchestration extension for pi.
11
+ Primary components:
12
12
 
13
- Its job is to let one pi session delegate work to one or more isolated subagent sessions without blocking the caller session. Each spawned subagent runs independently, and its result is delivered back to the session that spawned it as a `crew-result` custom message. When the owner session is already streaming, that message is delivered as steering content.
13
+ - `extension/runtime/crew-runtime.ts` - Process-level singleton owning all subagent state
14
+ - `extension/runtime/subagent-registry.ts` - In-memory subagent registry
15
+ - `extension/runtime/delivery-coordinator.ts` - Owner-based result routing
16
+ - `extension/bootstrap-session.ts` - Subagent session construction with extension filtering
17
+ - `extension/agent-discovery.ts` - Subagent definition discovery and validation
14
18
 
15
- At a high level, the system adds:
19
+ ## 2. Core runtime components
16
20
 
17
- - tool-based orchestration for spawning, listing, aborting, responding to, and closing subagents
18
- - a slash-command emergency abort path
19
- - TUI renderers for subagent custom messages
20
- - a live status widget for active subagents owned by the current session
21
- - bundled subagent definitions and a bundled review prompt template
21
+ ### 2.1 CrewRuntime singleton
22
22
 
23
- Primary entry points:
24
-
25
- - `package.json`
26
- - `extension/index.ts`
27
- - `README.md`
28
-
29
- ## 2. Architectural goals
30
-
31
- The extension is built around a few explicit goals:
32
-
33
- 1. **Keep the caller session interactive**
34
- The main session must not block while background work is happening.
35
-
36
- 2. **Preserve session isolation**
37
- Each subagent gets its own pi SDK session, context window, and resolved tool/skill set.
38
-
39
- 3. **Route results to the owning session**
40
- Results are not sent to whichever session happens to be active. They are sent to the session that created the subagent.
41
-
42
- 4. **Make multi-agent work understandable in the TUI**
43
- Users need visible state, clear completion messages, and an emergency stop path.
44
-
45
- 5. **Stay behavior-first**
46
- Agent definitions, tool restrictions, and delivery semantics are designed as explicit contracts, not incidental implementation details.
47
-
48
- Project-level guardrails that define these contracts live in:
49
-
50
- - `AGENTS.md`
51
-
52
- ## 3. Package layout and extension surface
53
-
54
- ### 3.1 Package registration in pi
55
-
56
- `pi-crew` is packaged as a pi extension package.
57
-
58
- Registration metadata lives in:
59
-
60
- - `package.json`
61
-
62
- Important package-level behaviors:
63
-
64
- - `pi.extensions` points pi to the built extension entry at `./dist/index.js`
65
- - `pi.prompts` exposes the bundled prompt template directory at `./prompts`
66
- - `agents/` is shipped as package data and auto-discovered as the lowest-priority source during agent discovery
67
-
68
- ### 3.2 Repository structure
69
-
70
- Main implementation areas:
71
-
72
- - `extension/` - runtime extension logic
73
- - `extension/runtime/` - in-memory state and delivery coordination
74
- - `extension/integration/` - pi tool, command, and renderer registration
75
- - `agents/` - bundled subagent definitions
76
- - `prompts/` - bundled prompt templates
77
-
78
- ## 4. Core runtime components
79
-
80
- ### 4.1 Extension bootstrap
81
-
82
- File:
83
-
84
- - `extension/index.ts`
85
-
86
- Responsibilities:
87
-
88
- - instantiate one `CrewManager` for the extension runtime
89
- - observe pi session lifecycle events
90
- - activate delivery routing when the active pi session changes
91
- - abort owned abortable subagents when an owner session shuts down
92
- - register tools, command, and message renderers
93
- - refresh the status widget when state changes
94
-
95
- Session lifecycle events used by the extension:
96
-
97
- - `session_start`
98
- - `session_switch`
99
- - `session_fork`
100
- - `session_shutdown`
101
-
102
- This is the bridge between pi core events and `pi-crew` runtime behavior.
103
-
104
- ### 4.2 Crew manager
105
-
106
- File:
107
-
108
- - `extension/crew-manager.ts`
109
-
110
- `CrewManager` is the main orchestration service.
111
-
112
- It owns the extension’s operational workflow:
113
-
114
- - create subagent state records
115
- - bootstrap isolated subagent sessions
116
- - send prompts into subagent sessions
117
- - interpret prompt outcomes
118
- - transition subagents between `running`, `waiting`, `done`, `error`, and `aborted`
119
- - deliver results back to the owner session
120
- - enforce ownership checks for `respond`, `done`, and owned abort paths
121
- - dispose finished subagents
122
-
123
- In practice, `CrewManager` is the coordination layer above the registry and delivery subsystems.
124
-
125
- ### 4.3 Subagent registry
126
-
127
- Files:
128
-
129
- - `extension/runtime/subagent-registry.ts`
130
- - `extension/runtime/subagent-state.ts`
131
-
132
- Responsibilities:
133
-
134
- - store live in-memory subagent state in a `Map`
135
- - generate unique runtime IDs such as `<name>-<hex>`
136
- - filter subagents by owner session
137
- - provide summaries for the TUI widget and abort command
138
- - count other still-running subagents for ordered delivery notes
139
-
140
- Important architectural property:
141
-
142
- - the registry is **in-memory only**
143
- - active subagents are therefore runtime-scoped, not globally persistent across process restarts
144
-
145
- However, per project rules, subagent session files themselves are intentionally not cleaned up, so post-hoc inspection through pi session history remains possible.
146
-
147
- ### 4.4 Delivery coordinator
148
-
149
- File:
150
-
151
- - `extension/runtime/delivery-coordinator.ts`
152
-
153
- This component solves one of the most important architecture problems in the extension: **how to deliver background results to the correct session at the correct time**.
23
+ `CrewRuntime` is a process-level singleton that survives pi runtime replacement (`/resume`, `/new`, `/fork`). When pi discards an old extension instance and creates a new one, the new instance reconnects to the same `crewRuntime` and picks up existing subagent state.
154
24
 
155
25
  Responsibilities:
156
26
 
157
- - track which pi session is currently active
158
- - know whether the active owner session is idle or already streaming
159
- - queue results for inactive owner sessions
160
- - flush queued results when the owner session becomes active again
161
- - preserve ordering between the main result message and the optional “remaining subagents” note
162
-
163
- This separation keeps delivery logic independent from spawning logic.
164
-
165
- ## 5. Integration with pi
166
-
167
- ### 5.1 Registered tools
168
-
169
- Files:
170
-
171
- - `extension/integration/register-tools.ts`
172
- - `extension/integration/tools/crew-list.ts`
173
- - `extension/integration/tools/crew-spawn.ts`
174
- - `extension/integration/tools/crew-abort.ts`
175
- - `extension/integration/tools/crew-respond.ts`
176
- - `extension/integration/tools/crew-done.ts`
177
-
178
- The extension registers five tools:
179
-
180
- - `crew_list`
181
- - `crew_spawn`
182
- - `crew_abort`
183
- - `crew_respond`
184
- - `crew_done`
185
-
186
- These are the primary public API for LLM-driven orchestration.
187
-
188
- Behavioral contracts:
189
-
190
- - `crew_list` shows discovered subagent definitions and active subagents owned by the current session
191
- - `crew_spawn` starts a non-blocking subagent owned by the current session
192
- - `crew_abort` can abort one, many, or all active subagents owned by the current session
193
- - `crew_respond` continues a waiting interactive subagent without blocking the caller session
194
- - `crew_done` closes a waiting interactive subagent without emitting a duplicate completion message
195
-
196
- ### 5.2 Registered command
197
-
198
- File:
199
-
200
- - `extension/integration/register-command.ts`
201
-
202
- The extension also registers the `/pi-crew-abort` command.
203
-
204
- This command differs from `crew_abort` in one important way:
205
-
206
- - it is intentionally unrestricted across sessions
207
-
208
- It exists as an emergency escape hatch. This is a deliberate operational tool, not an ownership-safe automation surface.
209
-
210
- ### 5.3 Custom message renderers
211
-
212
- Files:
213
-
214
- - `extension/integration/register-renderers.ts`
215
- - `extension/subagent-messages.ts`
216
-
217
- The extension defines two custom message types:
218
-
219
- - `crew-result`
220
- - `crew-remaining`
221
-
222
- These render in the TUI with custom formatting so asynchronous subagent output is readable and visually distinct from normal assistant text.
223
-
224
- ### 5.4 Status widget
225
-
226
- File:
227
-
228
- - `extension/status-widget.ts`
229
-
230
- When the current session owns active subagents, a live widget shows:
231
-
232
- - subagent ID
233
- - the latest assistant-reported model name, or `…` before the first assistant turn ends
234
- - turn count
235
- - the latest `assistant.usage.totalTokens` value, displayed as `ctx`
236
- - running vs waiting state
237
-
238
- This widget is session-scoped. It only shows subagents owned by the currently active session.
239
-
240
- ## 6. Subagent definition model
241
-
242
- ### 6.1 Discovery sources and priority
243
-
244
- File:
245
-
246
- - `extension/agent-discovery.ts`
247
-
248
- Subagent definitions are discovered from three directories in priority order:
249
-
250
- 1. **Project**: `<cwd>/.pi/agents/*.md`
251
- 2. **User global**: `~/.pi/agent/agents/*.md`
252
- 3. **Bundled**: `agents/` in the package
253
-
254
- When the same subagent name exists in multiple sources, the higher-priority source wins silently. Duplicate names within the same directory produce a warning.
255
-
256
- If a directory does not exist, it is skipped without warnings.
257
-
258
- ### 6.2 Definition format
259
-
260
- Each subagent is a Markdown file with YAML frontmatter plus a Markdown body.
261
-
262
- Frontmatter fields currently recognized by the runtime:
263
-
264
- - `name`
265
- - `description`
266
- - `model`
267
- - `thinking`
268
- - `tools`
269
- - `skills`
270
- - `compaction`
271
- - `interactive`
272
-
273
- The Markdown body becomes appended system prompt content for the spawned subagent session.
274
-
275
- ### 6.3 JSON override sources and precedence
276
-
277
- File:
278
-
279
- - `extension/agent-discovery.ts`
280
-
281
- After `.md` discovery, the runtime loads optional per-agent override config from two JSON files:
282
-
283
- - global: `~/.pi/agent/pi-crew.json`
284
- - project: `<cwd>/.pi/pi-crew.json`
285
-
286
- Only these fields are overridable:
287
-
288
- - `model`
289
- - `thinking`
290
- - `tools`
291
- - `skills`
292
- - `compaction`
293
- - `interactive`
294
-
295
- `name` and `description` are never overridable.
296
-
297
- Merge behavior:
298
-
299
- - global config is loaded first
300
- - project config is loaded second
301
- - project overrides global for the same subagent and field
302
- - if both files define the same subagent, unspecified fields from global remain unless project overrides them
303
-
304
- ### 6.4 Validation and fallback behavior
305
-
306
- Key behaviors implemented in discovery and bootstrap:
307
-
308
- - missing or empty `name` or `description` makes the definition invalid and it is ignored
309
- - names cannot contain whitespace
310
- - later duplicate names are skipped once an earlier valid definition with the same name has been loaded
311
- - `model` must use `provider/model-id` format to be considered valid
312
- - invalid model format is ignored for model resolution with a warning
313
- - invalid `thinking` values are ignored with a warning
314
- - invalid `tools` or `skills` field formats produce a warning and are treated as empty lists in frontmatter
315
- - unknown tools are filtered out with a warning
316
- - unknown skills are not part of discovery validation; when `skills` is present, bootstrap filters against the loaded skill set, omits unknown names, and writes a `console.warn`
317
- - non-boolean `compaction` or `interactive` values in frontmatter are ignored without a warning
318
-
319
- Override-specific validation behavior:
320
-
321
- - override entry value must be a JSON object
322
- - unknown override fields produce warnings
323
- - non-overridable fields (`name`, `description`) produce warnings
324
- - invalid field types in overrides produce warnings and are ignored
325
- - invalid `model`/`thinking` values in overrides produce warnings and are ignored
326
- - invalid `tools`/`skills` formats in overrides produce warnings and are ignored
327
- - unknown tool names in override `tools` produce warnings; valid names in the same list are still applied
328
- - override entries for undiscovered subagent names produce warnings and are ignored
329
-
330
- Important semantic distinction:
331
-
332
- - omitted `tools` means “use the full supported tool allowlist”
333
- - omitted `skills` means “do not install a `skillsOverride`, so all skills from the base resource loader remain available”
334
- - explicit empty `tools` or `skills` means “grant none”
335
-
336
- That distinction matters for both end users and coding agents authoring subagent definitions.
337
-
338
- ### 6.5 Supported tool set for spawned subagents
339
-
340
- File:
341
-
342
- - `extension/tool-registry.ts`
343
-
344
- The current built-in tool allowlist for spawned subagents is:
345
-
346
- - `read`
347
- - `bash`
348
- - `edit`
349
- - `write`
350
- - `grep`
351
- - `find`
352
- - `ls`
353
-
354
- Important limitation:
355
-
356
- - tool restriction here is a pi tool-resolution choice, not an OS sandbox boundary
357
- - for example, a “read-only” subagent remains read-only because of instruction and selected tool capabilities, not because of a kernel-level confinement model
358
-
359
- ## 7. Session bootstrapping and isolation
360
-
361
- ### 7.1 How a subagent session is created
362
-
363
- File:
364
-
365
- - `extension/bootstrap-session.ts`
366
-
367
- When `crew_spawn` is executed, the runtime does not clone the current conversation directly. It creates a new agent session with explicit configuration.
368
-
369
- Bootstrapping responsibilities:
370
-
371
- - resolve the target model
372
- - resolve the selected tool set
373
- - construct a `DefaultResourceLoader`
374
- - optionally filter skills
375
- - append the subagent system prompt body
376
- - create an in-memory settings manager for compaction settings
377
- - create a new `SessionManager`
378
- - call `sessionManager.newSession({ parentSession: parentSessionFile })` before creating the agent session
379
- - create the actual `AgentSession`
380
-
381
- ### 7.2 Parent-child session linkage
382
-
383
- A critical implementation rule is that the subagent session must be created through `SessionManager.newSession({ parentSession })`.
384
-
385
- The current caller session file is passed when one exists. This is the supported way to preserve parent-child linkage for resumable sessions. Project rules explicitly forbid creating subagent sessions through the wrong session factory because it breaks lifecycle expectations.
386
-
387
- Architectural rationale is documented in:
388
-
389
- - `AGENTS.md`
390
-
391
- ### 7.3 Preventing recursive orchestration
392
-
393
- One of the most important safety guards in the system is this:
394
-
395
- - spawned subagent sessions must not load the `pi-crew` extension again
396
-
397
- This is enforced by filtering the extension out through `extensionsOverride` inside the resource loader.
398
-
399
- Why it matters:
400
-
401
- - if a subagent can access `crew_spawn`, it can create more subagents
402
- - that creates recursive or runaway orchestration loops
403
- - preventing that at session bootstrap time is far safer than relying on prompt instructions alone
404
-
405
- ### 7.4 Model resolution
406
-
407
- The runtime resolves models in two stages:
408
-
409
- 1. if the subagent definition does not specify a valid parsed model, the caller session model is reused
410
- 2. if a model is specified but not found in the active pi model registry, the runtime falls back to the caller session model and writes a `console.warn`
411
-
412
- This keeps execution resilient to local model availability differences.
413
-
414
- ### 7.5 Compaction behavior
415
-
416
- Each subagent session gets its own in-memory settings manager.
417
-
418
- Behavior:
419
-
420
- - `compaction` defaults to `true` if omitted
421
- - a definition can explicitly disable it
422
-
423
- This setting is applied per spawned session and does not reuse the caller session's compaction setting.
424
-
425
- ## 8. Spawn lifecycle
426
-
427
- ### 8.1 High-level flow
428
-
429
- Relevant files:
430
-
431
- - `extension/integration/tools/crew-spawn.ts`
432
- - `extension/crew-manager.ts`
433
- - `extension/bootstrap-session.ts`
434
-
435
- Behavior sequence:
436
-
437
- 1. The `crew_spawn` tool handler calls `discoverAgents()` to load the current subagent definitions.
438
- 2. The requested subagent name is resolved against that discovery result.
439
- 3. The current session ID becomes the subagent owner ID.
440
- 4. `CrewManager.spawn()` creates a registry entry with status `running` and returns a runtime subagent ID immediately.
441
- 5. `CrewManager` starts asynchronous session bootstrap in the background.
442
- 6. Once bootstrapped, the new subagent session receives the task string as its prompt.
443
- 7. Completion or failure is delivered later as a `crew-result` custom message. If the owner session is already streaming, that message is delivered with `deliverAs: "steer"`.
444
-
445
- This immediate-return plus background-execution split is the key non-blocking contract of the extension.
446
-
447
- ### 8.2 Runtime metrics during execution
448
-
449
- `CrewManager` subscribes to subagent `turn_end` events.
450
-
451
- Tracked metrics include:
452
-
453
- - turn count, incremented on each `turn_end`
454
- - the latest `assistant.usage.totalTokens` value seen on a `turn_end`
455
- - the latest assistant-reported model name
456
-
457
- These metrics power the status widget and active subagent summaries.
458
-
459
- ### 8.3 Prompt outcome interpretation
460
-
461
- After a prompt cycle completes, the runtime inspects the last assistant message in the subagent session.
462
-
463
- Outcome mapping:
464
-
465
- - assistant stop reason `error` -> subagent status `error`
466
- - assistant stop reason `aborted` -> subagent status `aborted`
467
- - normal completion with `interactive: true` -> subagent status `waiting`
468
- - normal completion with non-interactive definition -> subagent status `done`
469
-
470
- This means interactive subagents and non-interactive subagents share the same prompt execution flow up to the final state transition.
471
-
472
- ## 9. Delivery model and message ordering
473
-
474
- ### 9.1 Owner-based routing
475
-
476
- The most important delivery rule is:
477
-
478
- - results belong to the session that spawned the subagent
479
-
480
- They do not belong to:
481
-
482
- - the currently visible session
483
- - the most recent session
484
- - the session that called a command later
485
-
486
- Ownership identity is based on the current session ID, not the session file path. This matters because unsaved or in-memory sessions may not have stable file paths.
487
-
488
- ### 9.2 Immediate vs deferred delivery
489
-
490
- Relevant files:
491
-
492
- - `extension/runtime/delivery-coordinator.ts`
493
- - `extension/index.ts`
494
-
495
- Delivery branches:
496
-
497
- - if the owner session is currently active, the result is sent immediately
498
- - if the owner session is not active, the result is queued in memory until that session becomes active again
499
-
500
- Queued results are flushed when the extension sees that owner session through:
501
-
502
- - `session_start`
503
- - `session_switch`
504
- - `session_fork`
505
-
506
- ### 9.3 Idle vs streaming session behavior
507
-
508
- pi requires different delivery semantics depending on whether the target session is idle or already processing output.
509
-
510
- Behavioral rule for a `crew-result` when no separate remaining-note ordering is needed:
511
-
512
- - idle owner session -> send with `triggerTurn: true`
513
- - streaming owner session -> send as steering content with `deliverAs: "steer"` and `triggerTurn: true`
514
-
515
- When an idle owner also needs a `crew-remaining` note, section 9.5 describes the deliberate `triggerTurn: false` / `true` split used to preserve message order.
516
-
517
- Why this distinction exists:
518
-
519
- - sending `deliverAs: "steer"` to an idle session can leave the message unprocessed because there is no active turn loop
520
- - when the owner session is already streaming, the extension injects the result into that active turn as steering content instead of using the idle-session delivery path
521
-
522
- This is one of the most subtle but important integration details in the extension.
523
-
524
- ### 9.4 Deferred flush timing
525
-
526
- Pending message flush after session activation is intentionally deferred to the next macrotask.
527
-
528
- Reason:
529
-
530
- - pi-core can emit `session_switch` before reconnecting the agent listener during resume flows
531
- - synchronous message delivery at that moment can lose custom message persistence
532
-
533
- This is why queued results are flushed asynchronously instead of synchronously during session activation.
534
-
535
- ### 9.5 Result and remaining-note ordering
536
-
537
- Files:
538
-
539
- - `extension/subagent-messages.ts`
540
- - `extension/runtime/delivery-coordinator.ts`
541
-
542
- When a result is delivered and other subagents from the same owner are still in `running` state, the extension may send two messages:
543
-
544
- 1. the `crew-result`
545
- 2. a separate `crew-remaining` note
546
-
547
- They are intentionally separate messages.
548
-
549
- Why:
550
-
551
- - the result message should stay focused on one subagent outcome
552
- - the remaining count is transient orchestration state, not part of that result payload
553
-
554
- Ordering guarantee:
555
-
556
- - the result must appear before the remaining-note message
557
-
558
- When the owner is idle and other subagents are still running, the extension sends the `crew-result` with `triggerTurn: false`, then sends the remaining note with `triggerTurn: true` so the next turn processes both in order.
559
-
560
- When the owner is already streaming, the extension sends the `crew-result` first with `deliverAs: "steer"` and `triggerTurn: true`, then sends the `crew-remaining` note after it with `deliverAs: "steer"` and `triggerTurn: false`.
561
-
562
- The remaining count is computed at send time from the current registry state, not stored in the `crew-result` payload.
563
-
564
- ## 10. Interactive subagents
565
-
566
- ### 10.1 Waiting state
567
-
568
- Interactive subagents are defined with `interactive: true` in frontmatter.
569
-
570
- After a successful prompt cycle, they transition to `waiting` instead of `done`.
571
-
572
- Meaning of `waiting`:
573
-
574
- - the session remains alive
575
- - the subagent can receive follow-up messages
576
- - the status widget shows a waiting icon rather than a spinner
577
-
578
- ### 10.2 `crew_respond`
579
-
580
- Relevant files:
581
-
582
- - `extension/integration/tools/crew-respond.ts`
583
- - `extension/crew-manager.ts`
584
-
585
- Behavior:
586
-
587
- - validate that the subagent exists
588
- - validate that it belongs to the caller session
589
- - validate that it is in `waiting` state
590
- - mark it `running`
591
- - asynchronously send the follow-up prompt
592
- - return immediately to the caller
593
-
594
- This is deliberately fire-and-forget. The response comes later as another `crew-result` message.
595
-
596
- ### 10.3 `crew_done`
597
-
598
- Relevant files:
599
-
600
- - `extension/integration/tools/crew-done.ts`
601
- - `extension/crew-manager.ts`
602
-
603
- Behavior:
604
-
605
- - validate existence, ownership, and `waiting` state
606
- - dispose the session
607
- - remove the subagent from the registry
608
- - do **not** send another `crew-result` message
609
-
610
- That last point is essential. The latest subagent response was already delivered when it entered `waiting`. Sending another completion custom message on `crew_done` would duplicate signal and create an unnecessary turn.
611
-
612
- ## 11. Abort semantics
613
-
614
- ### 11.1 Supported abort paths
615
-
616
- There are three conceptually different abort sources:
617
-
618
- 1. tool-triggered aborts through `crew_abort`
619
- 2. unrestricted manual aborts through `/pi-crew-abort`
620
- 3. cleanup aborts when an owner session shuts down
621
-
622
- Each path should report the real reason.
623
-
624
- ### 11.2 Owned abort behavior
625
-
626
- Relevant files:
627
-
628
- - `extension/integration/tools/crew-abort.ts`
629
- - `extension/crew-manager.ts`
630
-
631
- `crew_abort` supports exactly one mode per call:
632
-
633
- - `subagent_id`
634
- - `subagent_ids`
635
- - `all: true`
636
-
637
- It only operates on subagents owned by the current session.
638
-
639
- The result separates:
640
-
641
- - actually aborted IDs
642
- - missing or already finished IDs
643
- - IDs that belong to another session
644
-
645
- ### 11.3 Unrestricted command abort
646
-
647
- Relevant file:
648
-
649
- - `extension/integration/register-command.ts`
650
-
651
- `/pi-crew-abort` can target any active abortable subagent, regardless of owner. This is not a bug. It is an explicit operational decision.
652
-
653
- ### 11.4 Session shutdown cleanup
654
-
655
- Relevant file:
656
-
657
- - `extension/index.ts`
658
-
659
- On `session_shutdown`, the extension:
660
-
661
- - aborts all abortable subagents currently owned by that session
662
- - clears pending queued messages for that owner
663
-
664
- This avoids memory leaks and prevents stale queued results from surviving after their owner session is gone.
665
-
666
- ## 12. Ownership and isolation rules
667
-
668
- These are core architecture invariants, not optional conventions.
669
-
670
- ### 12.1 Owner identity
671
-
672
- Owner identity uses the pi session ID.
673
-
674
- Why:
675
-
676
- - session file paths may be undefined for in-memory sessions
677
- - file-path-based ownership can collapse multiple live sessions into the same logical owner
678
-
679
- ### 12.2 Session-scoped visibility
680
-
681
- The following surfaces are ownership-restricted:
682
-
683
- - `crew_list` active subagent section
684
- - `crew_abort`
685
- - `crew_respond`
686
- - `crew_done`
687
- - status widget
688
- - owner cleanup on session shutdown
689
-
690
- This prevents cross-session interference in normal tool-driven workflows.
691
-
692
- ### 12.3 What is intentionally not isolated
693
-
694
- The emergency command `/pi-crew-abort` is intentionally cross-session.
695
-
696
- This is the only major exception to normal ownership isolation.
697
-
698
- ## 13. User-facing message model
699
-
700
- Relevant file:
701
-
702
- - `extension/subagent-messages.ts`
703
-
704
- Subagent outcome messaging is normalized into one common format.
705
-
706
- Every `crew-result` payload includes:
707
-
708
- - runtime subagent ID
709
- - logical subagent name
710
- - final status
711
- - an optional body derived from the subagent result or error
712
-
713
- In current completion and error paths, the runtime usually populates that body with assistant text, an error message, or `"(no output)"`.
714
-
715
- `crew-remaining` is simpler. It carries display text only and does not attach structured `details`.
716
-
717
- The `SubagentStatus` union currently includes:
718
-
719
- - `running`
720
- - `waiting`
721
- - `done`
722
- - `error`
723
- - `aborted`
724
-
725
- Current `crew-result` deliveries use `waiting`, `done`, `error`, or `aborted`. `running` is a live registry/widget state, not a completion message state.
726
-
727
- ## 14. Bundled subagents and prompt template
728
-
729
- ### 14.1 Bundled subagents
730
-
731
- Files:
732
-
733
- - `agents/scout.md`
734
- - `agents/planner.md`
735
- - `agents/oracle.md`
736
- - `agents/worker.md`
737
- - `agents/code-reviewer.md`
738
- - `agents/quality-reviewer.md`
739
-
740
- These are opinionated subagent definitions for common workflows.
741
-
742
- They demonstrate how the extension is intended to be used:
743
-
744
- - `scout` for quick investigation
745
- - `planner` for interactive planning
746
- - `oracle` for decision evaluation and blind spot detection
747
- - `worker` for implementation
748
- - `code-reviewer` for correctness review
749
- - `quality-reviewer` for maintainability review
750
-
751
- Architecturally, these files are not special-cased by the runtime. They are auto-discovered as the lowest-priority source (priority 3) and go through the same discovery and bootstrap pipeline as project-level or user-level definitions. Users can override any bundled subagent by placing a same-named `.md` file in `<cwd>/.pi/agents/` or `~/.pi/agent/agents/`.
752
-
753
- ### 14.2 Bundled plan orchestration prompt
754
-
755
- File:
756
-
757
- - `prompts/pi-crew-plan.md`
758
-
759
- This prompt template orchestrates discovery and planning workflow.
760
-
761
- It tells the orchestrating agent to:
762
-
763
- - gather minimal orientation context
764
- - spawn scout subagents to investigate specific areas
765
- - collect and synthesize scout findings
766
- - delegate planning to a planner subagent
767
- - relay the planner's output to the user
768
-
769
- The prompt enforces strict delegation boundaries:
770
-
771
- - the orchestrator must not perform deep investigation itself
772
- - the orchestrator must not write the plan itself
773
- - detailed discovery belongs to scouts
774
- - plan creation belongs to the planner
775
-
776
- ### 14.3 Bundled review orchestration prompt
777
-
778
- File:
779
-
780
- - `prompts/pi-crew-review.md`
27
+ - Create subagent state records
28
+ - Bootstrap isolated subagent sessions
29
+ - Transition subagents between states
30
+ - Deliver results to owner sessions
781
31
 
782
- This prompt template is a good example of how `pi-crew` is meant to be consumed by higher-level orchestration prompts.
32
+ ### 2.2 Delivery coordinator
783
33
 
784
- It does not bypass the extension. Instead, it tells the orchestrating agent to gather review scope with normal repo inspection tools, then use the public `crew_*` tool surface to:
34
+ Routes subagent results to the correct session at the correct time. Key behaviors:
785
35
 
786
- - verify required subagents exist
787
- - spawn reviewers in parallel
788
- - wait for both result messages
789
- - merge results into a final report
36
+ - Tracks active session via `ActiveRuntimeBinding` (set on `session_start`, cleared on `session_shutdown`)
37
+ - Queues results when owner session is inactive
38
+ - Flushes queued results when owner session activates (`session_start` with `reason: "resume"` or `"fork"`)
39
+ - Uses `triggerTurn: false/true` split to preserve ordering between `crew-result` and `crew-remaining`
790
40
 
791
- This separation is important:
41
+ Underlying delivery: see pi's `sendMessage({ deliverAs, triggerTurn })` in extensions.md.
792
42
 
793
- - the extension provides orchestration primitives
794
- - prompts provide workflow policy
43
+ ### 2.3 Subagent registry
795
44
 
796
- ## 15. Failure handling and diagnostics
45
+ In-memory, process-scoped: `Map<subagentId, SubagentState>`
797
46
 
798
- ### 15.1 Discovery warnings
47
+ - Owner session filtering
48
+ - Runtime ID generation (`<name>-<hex>`)
799
49
 
800
- Relevant files:
50
+ Does not persist across process restarts. Subagent session files remain for post-hoc inspection.
801
51
 
802
- - `extension/agent-discovery.ts`
803
- - `extension/integration/register-tools.ts`
52
+ ## 3. Session bootstrapping
804
53
 
805
- Invalid or partially invalid subagent definition files do not crash the extension.
54
+ When `crew_spawn` executes:
806
55
 
807
- Instead:
56
+ 1. Resolve subagent definition from discovery sources
57
+ 2. Resolve model (fallback to caller session model if invalid)
58
+ 3. Resolve tools, skills
59
+ 4. Create `DefaultResourceLoader` with `extensionsOverride` that excludes `pi-crew`
60
+ 5. Call `sessionManager.newSession({ parentSession })` for parent-child linkage
61
+ 6. Create `AgentSession` with resolved configuration
62
+ 7. Send task prompt asynchronously
808
63
 
809
- - discovery accumulates warnings
810
- - `crew_list` includes those warnings in its text output
811
- - `crew_list` and `crew_spawn` both trigger one-time UI notifications when a UI is available
64
+ **Extension filtering:** Subagent sessions must not load `pi-crew` again. Prevents recursive orchestration loops.
812
65
 
813
- This section covers discovery-time warnings only. Unknown skill names are warned later during bootstrap via `console.warn` in `extension/bootstrap-session.ts`.
66
+ ## 4. Delivery model
814
67
 
815
- This keeps misconfigured subagents visible without breaking healthy ones.
68
+ ### 4.1 Owner-based routing
816
69
 
817
- ### 15.2 Bootstrap failures
70
+ Results belong to the session that spawned the subagent. Owner identity uses `getSessionId()`, not file path (in-memory sessions have undefined paths).
818
71
 
819
- Bootstrap failures are settled as subagent `error` results.
72
+ ### 4.2 Idle vs streaming
820
73
 
821
- In practice, this covers exceptions thrown while preparing the resource loader or creating the subagent session.
74
+ Check owner session state before delivery:
822
75
 
823
- The important behavior is that a failed spawn still resolves into a clear terminal subagent result rather than silently disappearing.
76
+ - **Idle (`isIdle() = true`):** Send with `triggerTurn: true`
77
+ - **Streaming (`isIdle() = false`):** Send with `deliverAs: "steer"` and `triggerTurn: true`
824
78
 
825
- ### 15.3 Prompt execution failures
79
+ Critical: `deliverAs: "steer"` to an idle session leaves the message unprocessed (no active turn loop).
826
80
 
827
- Prompt-cycle failures are also normalized into terminal `error` or `aborted` outcomes and delivered through the same result channel.
81
+ ### 4.3 Deferred flush
828
82
 
829
- This gives downstream orchestrators a single message model to watch, regardless of where the failure occurred.
83
+ Pending message flush after `session_start` is deferred to next macrotask. Synchronous delivery loses custom message persistence (pi-core emits `session_start` before reconnecting agent listener during resume).
830
84
 
831
- ## 16. What persists and what does not
85
+ ### 4.4 TTL cleanup
832
86
 
833
- ### Persists
87
+ Pending messages older than 24 hours are discarded during `flushPending`.
834
88
 
835
- - extension package installation metadata
836
- - bundled prompt template availability
837
- - copied subagent definition files in `~/.pi/agent/agents/`
838
- - pi session history files created for subagents
89
+ ## 5. Subagent state lifecycle
839
90
 
840
- ### Does not persist across process restarts
91
+ ### 5.1 States
841
92
 
842
- - in-memory active subagent registry
843
- - in-memory pending delivery queue
844
- - live status widget state
93
+ - `running` - Actively processing
94
+ - `waiting` - Interactive subagent awaiting `crew_respond` or `crew_done`
95
+ - `done` - Completed successfully
96
+ - `error` - Failed with error
97
+ - `aborted` - Cancelled
845
98
 
846
- This distinction matters when designing higher-level workflows. `pi-crew` preserves session artifacts for inspection, but its live orchestration state is runtime-memory-based.
99
+ ### 5.2 State transitions
847
100
 
848
- ## 17. Behavioral invariants for future maintainers
101
+ After prompt cycle completion, inspect assistant stop reason:
849
102
 
850
- These are the behaviors future changes must preserve:
103
+ - `stopReason: "error"` status `error`
104
+ - `stopReason: "aborted"` → status `aborted`
105
+ - Normal completion + `interactive: true` → status `waiting`
106
+ - Normal completion + non-interactive → status `done`
851
107
 
852
- 1. A spawned subagent must not block the caller session.
853
- 2. Results must route to the owning session, not merely the currently active one.
854
- 3. Ownership must be based on session ID.
855
- 4. Subagent sessions must not load `pi-crew` again.
856
- 5. Interactive subagents must remain alive after responding until explicitly closed.
857
- 6. `crew_respond` must return immediately and deliver its result asynchronously later.
858
- 7. `crew_done` must clean up only and must not emit a duplicate result message.
859
- 8. When the owner session is inactive, result delivery must be queued and later flushed.
860
- 9. Result messages must appear before any “remaining subagents” note.
861
- 10. Session shutdown must abort owned abortable subagents and clear pending queued messages for that owner.
862
- 11. Tool ownership restrictions must remain strict, except for the emergency abort command.
108
+ ### 5.3 Interactive subagents
863
109
 
864
- These rules are the extension’s real architecture. File names may move, but these contracts should not.
110
+ `interactive: true` subagents enter `waiting` after each response. They accept follow-up messages via `crew_respond` until explicitly closed with `crew_done`. Closing does NOT emit a duplicate `crew-result`.
865
111
 
866
- ## 18. Reading guide for coding agents
112
+ ## 6. Ownership and isolation
867
113
 
868
- If you need to understand or change behavior, start in this order:
114
+ Invariants:
869
115
 
870
- 1. `README.md`
871
- Public product surface and user contract.
116
+ 1. `crew_list`, `crew_abort`, `crew_respond`, `crew_done`, status widget: session-scoped. Only owner sees/controls.
117
+ 2. `/pi-crew-abort`: cross-session emergency escape hatch.
118
+ 3. `session_shutdown` deactivates delivery binding only; subagents continue running. Abort happens on process exit.
872
119
 
873
- 2. `AGENTS.md`
874
- Non-obvious architecture guardrails that must not regress.
120
+ ## 7. Subagent definition model
875
121
 
876
- 3. `extension/index.ts`
877
- Extension bootstrap and session event wiring.
122
+ Discovery priority:
878
123
 
879
- 4. `extension/crew-manager.ts`
880
- Main orchestration and state transitions.
124
+ 1. Project: `<cwd>/.pi/agents/*.md`
125
+ 2. User global: `~/.pi/agent/agents/*.md`
126
+ 3. Bundled: `agents/` in package
881
127
 
882
- 5. `extension/runtime/delivery-coordinator.ts`
883
- Owner routing, queueing, and turn-trigger behavior.
128
+ Higher priority wins. Same-name duplicates in same directory produce warning.
884
129
 
885
- 6. `extension/bootstrap-session.ts`
886
- Session construction, model/tool/skill resolution, and extension filtering.
130
+ Frontmatter: `name`, `description`, `model`, `thinking`, `tools`, `skills`, `compaction`, `interactive`
887
131
 
888
- 7. `extension/agent-discovery.ts`
889
- Subagent definition semantics and validation rules.
132
+ Tools/skills semantics:
890
133
 
891
- 8. `extension/integration/`
892
- pi-facing tools, command, and renderers.
134
+ - **Omitted:** Use full supported allowlist
135
+ - **Empty list (`tools: []`):** Grant none
893
136
 
894
- 9. `agents/` and `prompts/`
895
- Real workflow examples built on top of the runtime.
137
+ JSON overrides: `~/.pi/agent/pi-crew.json` (global), `<cwd>/.pi/pi-crew.json` (project). Project wins.
896
138
 
897
- ## 19. Verification
139
+ ## 8. Behavioral invariants
898
140
 
899
- For implementation changes that affect runtime behavior, the project-level verification commands are:
141
+ 1. Spawned subagent must not block caller session.
142
+ 2. Results route to owning session (by ID), not currently active session.
143
+ 3. Subagent sessions must not load `pi-crew`.
144
+ 4. `crew_respond` returns immediately; result delivered asynchronously.
145
+ 5. `crew_done` cleans up only; no duplicate result message.
146
+ 6. Queued results flush when owner session becomes active.
147
+ 7. `crew-result` messages appear before `crew-remaining` notes (ordering via `triggerTurn` split).
148
+ 8. Pending messages preserved for inactive sessions; TTL (24h) prevents memory leak.
149
+ 9. Active subagent state survives runtime replacement within same process.
900
150
 
901
- - `npm run typecheck`
902
- - `npm run build`
151
+ ## 9. Reading guide
903
152
 
904
- Source:
153
+ 1. `README.md` - Product surface
154
+ 2. `AGENTS.md` - Architecture guardrails
155
+ 3. `extension/index.ts` - Session event wiring
156
+ 4. `extension/runtime/crew-runtime.ts` - Orchestration, state transitions
157
+ 5. `extension/runtime/delivery-coordinator.ts` - Owner routing, queueing
158
+ 6. `extension/bootstrap-session.ts` - Session construction
159
+ 7. `extension/agent-discovery.ts` - Definition validation
160
+ 8. `extension/integration/` - Tools, command, renderers
905
161
 
906
- - `AGENTS.md`
162
+ ## 10. Verification
907
163
 
908
- For documentation-only changes, these commands are usually unnecessary unless the change is coupled with code edits.
164
+ ```bash
165
+ npm run typecheck
166
+ npm run build
167
+ ```