pi-crew 0.5.2 → 0.5.6

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 (137) hide show
  1. package/CHANGELOG.md +183 -0
  2. package/README.md +17 -1
  3. package/docs/architecture.md +2 -0
  4. package/docs/bugs/cross-session-notification-leakage.md +82 -0
  5. package/docs/coding-agent-optimization.md +268 -0
  6. package/docs/deep-review-report.md +384 -0
  7. package/docs/distillation/cybersecurity-patterns.md +294 -0
  8. package/docs/migration-v0.4-v0.5.md +208 -0
  9. package/docs/optimization-plan.md +642 -0
  10. package/docs/pi-crew-v0.5.5-audit-fix-plan.md +133 -0
  11. package/docs/pi-mono-opportunities.md +969 -0
  12. package/docs/pi-mono-review.md +291 -0
  13. package/docs/skills/REFERENCE.md +144 -0
  14. package/package.json +12 -9
  15. package/skills/artifact-analysis-loop/SKILL.md +302 -0
  16. package/skills/async-worker-recovery/SKILL.md +19 -1
  17. package/skills/child-pi-spawning/SKILL.md +19 -6
  18. package/skills/context-artifact-hygiene/SKILL.md +19 -2
  19. package/skills/delegation-patterns/SKILL.md +68 -3
  20. package/skills/detection-pipeline-design/SKILL.md +285 -0
  21. package/skills/event-log-tracing/SKILL.md +20 -6
  22. package/skills/git-master/SKILL.md +20 -6
  23. package/skills/hunting-investigation-loop/SKILL.md +401 -0
  24. package/skills/incident-playbook-construction/SKILL.md +383 -0
  25. package/skills/live-agent-lifecycle/SKILL.md +20 -6
  26. package/skills/mailbox-interactive/SKILL.md +19 -6
  27. package/skills/model-routing-context/SKILL.md +19 -1
  28. package/skills/multi-perspective-review/SKILL.md +19 -4
  29. package/skills/observability-reliability/SKILL.md +19 -2
  30. package/skills/orchestration/SKILL.md +20 -2
  31. package/skills/ownership-session-security/SKILL.md +20 -2
  32. package/skills/pi-extension-lifecycle/SKILL.md +20 -2
  33. package/skills/post-mortem/SKILL.md +7 -2
  34. package/skills/read-only-explorer/SKILL.md +20 -6
  35. package/skills/requirements-to-task-packet/SKILL.md +23 -3
  36. package/skills/resource-discovery-config/SKILL.md +20 -2
  37. package/skills/runtime-state-reader/SKILL.md +20 -2
  38. package/skills/safe-bash/SKILL.md +21 -6
  39. package/skills/scrutinize/SKILL.md +20 -2
  40. package/skills/secure-agent-orchestration-review/SKILL.md +29 -2
  41. package/skills/security-review/SKILL.md +560 -0
  42. package/skills/state-mutation-locking/SKILL.md +22 -2
  43. package/skills/systematic-debugging/SKILL.md +8 -6
  44. package/skills/threat-hypothesis-framework/SKILL.md +175 -0
  45. package/skills/ui-render-performance/SKILL.md +20 -2
  46. package/skills/verification-before-done/SKILL.md +17 -2
  47. package/skills/widget-rendering/SKILL.md +21 -6
  48. package/skills/workspace-isolation/SKILL.md +20 -6
  49. package/skills/worktree-isolation/SKILL.md +20 -6
  50. package/src/agents/agent-config.ts +40 -1
  51. package/src/benchmark/benchmark-runner.ts +45 -0
  52. package/src/benchmark/feedback-loop.ts +5 -0
  53. package/src/config/config.ts +32 -5
  54. package/src/config/role-tools.ts +82 -0
  55. package/src/config/suggestions.ts +8 -0
  56. package/src/config/types.ts +4 -0
  57. package/src/extension/async-notifier.ts +10 -1
  58. package/src/extension/crew-cleanup.ts +114 -0
  59. package/src/extension/cross-extension-rpc.ts +1 -1
  60. package/src/extension/notification-router.ts +18 -0
  61. package/src/extension/register.ts +27 -19
  62. package/src/extension/registration/subagent-tools.ts +1 -1
  63. package/src/extension/team-tool/anchor.ts +201 -0
  64. package/src/extension/team-tool/api.ts +2 -1
  65. package/src/extension/team-tool/auto-summarize.ts +154 -0
  66. package/src/extension/team-tool/run.ts +42 -7
  67. package/src/extension/team-tool.ts +44 -2
  68. package/src/hooks/registry.ts +1 -3
  69. package/src/observability/event-bus.ts +69 -0
  70. package/src/observability/event-to-metric.ts +0 -2
  71. package/src/runtime/anchor-manager.ts +473 -0
  72. package/src/runtime/async-runner.ts +8 -4
  73. package/src/runtime/auto-summarize.ts +350 -0
  74. package/src/runtime/background-runner.ts +10 -3
  75. package/src/runtime/budget-tracker.ts +354 -0
  76. package/src/runtime/chain-runner.ts +507 -0
  77. package/src/runtime/child-pi.ts +123 -35
  78. package/src/runtime/crash-recovery.ts +5 -4
  79. package/src/runtime/crew-agent-runtime.ts +1 -0
  80. package/src/runtime/custom-tools/irc-tool.ts +13 -0
  81. package/src/runtime/custom-tools/submit-result-tool.ts +3 -2
  82. package/src/runtime/delivery-coordinator.ts +10 -3
  83. package/src/runtime/dynamic-script-runner.ts +482 -0
  84. package/src/runtime/foreground-control.ts +87 -17
  85. package/src/runtime/handoff-manager.ts +589 -0
  86. package/src/runtime/hidden-handoff.ts +424 -0
  87. package/src/runtime/live-agent-manager.ts +20 -4
  88. package/src/runtime/live-session-runtime.ts +39 -4
  89. package/src/runtime/manifest-cache.ts +2 -1
  90. package/src/runtime/model-resolver.ts +16 -4
  91. package/src/runtime/phase-tracker.ts +373 -0
  92. package/src/runtime/pi-args.ts +11 -1
  93. package/src/runtime/pi-json-output.ts +31 -0
  94. package/src/runtime/pipeline-runner.ts +514 -0
  95. package/src/runtime/progress-tracker.ts +124 -0
  96. package/src/runtime/retry-runner.ts +354 -0
  97. package/src/runtime/sandbox.ts +252 -0
  98. package/src/runtime/scheduler.ts +7 -2
  99. package/src/runtime/skill-effectiveness.ts +473 -0
  100. package/src/runtime/skill-instructions.ts +37 -3
  101. package/src/runtime/subagent-manager.ts +1 -1
  102. package/src/runtime/task-graph.ts +11 -1
  103. package/src/runtime/task-runner.ts +92 -18
  104. package/src/runtime/team-runner.ts +13 -12
  105. package/src/runtime/tool-progress.ts +10 -3
  106. package/src/runtime/verification-gates.ts +367 -0
  107. package/src/schema/team-tool-schema.ts +37 -0
  108. package/src/skills/discover-skills.ts +5 -0
  109. package/src/state/active-run-registry.ts +9 -2
  110. package/src/state/contracts.ts +9 -0
  111. package/src/state/crew-init.ts +3 -3
  112. package/src/state/decision-ledger.ts +98 -55
  113. package/src/state/event-log-rotation.ts +2 -2
  114. package/src/state/event-log.ts +144 -10
  115. package/src/state/hook-instinct-bridge.ts +5 -5
  116. package/src/state/mailbox.ts +10 -0
  117. package/src/state/run-cache.ts +18 -8
  118. package/src/state/state-store.ts +3 -1
  119. package/src/state/types.ts +4 -0
  120. package/src/tools/safe-bash-extension.ts +1 -0
  121. package/src/tools/safe-bash.ts +152 -20
  122. package/src/types/new-api-types.ts +34 -0
  123. package/src/ui/agent-management-overlay.ts +5 -1
  124. package/src/ui/crew-widget.ts +29 -15
  125. package/src/ui/overlays/mailbox-detail-overlay.ts +13 -2
  126. package/src/ui/powerbar-publisher.ts +101 -7
  127. package/src/ui/tool-render.ts +15 -15
  128. package/src/ui/transcript-cache.ts +13 -0
  129. package/src/utils/bm25-search.ts +16 -8
  130. package/src/utils/env-filter.ts +8 -5
  131. package/src/utils/redaction.ts +169 -15
  132. package/src/utils/session-utils.ts +52 -0
  133. package/src/utils/sse-parser.ts +10 -1
  134. package/src/worktree/cleanup.ts +6 -1
  135. package/src/worktree/worktree-manager.ts +32 -13
  136. package/workflows/chain.workflow.md +252 -0
  137. package/workflows/pipeline.workflow.md +27 -0
@@ -0,0 +1,642 @@
1
+ # pi-crew Optimization Plan: coding-agent Integration
2
+
3
+ **Date:** 2026-05-28
4
+ **Based on:** 133 coding-agent commits (May 2026), direct source analysis
5
+ **Goal:** Implement 4 optimization opportunities for pi-crew
6
+
7
+ ---
8
+
9
+ ## Implementation Order
10
+
11
+ | # | Optimization | Priority | Effort | Files to Change |
12
+ |---|--------------|----------|--------|-----------------|
13
+ | 1 | Child Process Exit Handling | P0 | Medium | `src/runtime/child-pi.ts` |
14
+ | 2 | `excludeFromContext` Flag | P1 | Small | `src/runtime/child-pi.ts`, `src/config/types.ts` |
15
+ | 3 | Edit Tool Patch Capture | P2 | Small | `src/runtime/task-runner.ts` |
16
+ | 4 | Session ID Alignment | P2 | Small | `src/state/crew-init.ts`, `src/runtime/child-pi.ts` |
17
+
18
+ ---
19
+
20
+ ## Optimization 1: Child Process Exit Handling (P0)
21
+
22
+ ### Problem
23
+
24
+ When a child Pi process exits unexpectedly (OOM, crash, SIGKILL), pi-crew:
25
+ - ✅ Captures exit code
26
+ - ✅ Logs to event log
27
+ - ❌ **Does NOT reject pending operations** (if any are in-flight)
28
+ - ❌ **Does NOT include stderr context** in error reporting
29
+ - ❌ **No clear error message** for unexpected exits
30
+
31
+ ### Current Behavior (child-pi.ts ~line 625)
32
+
33
+ ```typescript
34
+ // Only emits lifecycle event, doesn't track pending state
35
+ input.onLifecycleEvent?.({ type: "exit", pid: child.pid, exitCode: code, ts: ... });
36
+
37
+ // settle() handles exit but with limited context
38
+ settle({
39
+ exitCode: finalExitCode,
40
+ stdout,
41
+ stderr,
42
+ ...(timeoutError ? { error: timeoutError.error } : {}),
43
+ ...
44
+ });
45
+ ```
46
+
47
+ ### Reference: pi's RpcClient approach (rpc-client.ts ~line 89)
48
+
49
+ ```typescript
50
+ childProcess.once("exit", (code, signal) => {
51
+ const error = this.createProcessExitError(code, signal);
52
+ this.exitError = error;
53
+ this.rejectPendingRequests(error); // ← key missing in pi-crew
54
+ });
55
+ ```
56
+
57
+ ### Implementation
58
+
59
+ **File:** `src/runtime/child-pi.ts`
60
+
61
+ #### Step 1: Add pending operations tracker
62
+
63
+ After line ~416 (`let turnCount = 0;`), add:
64
+
65
+ ```typescript
66
+ // Track in-flight operations for proper rejection on unexpected exit
67
+ interface PendingOperation {
68
+ id: string;
69
+ type: "prompt" | "steer" | "json_event";
70
+ startedAt: number;
71
+ }
72
+ const pendingOperations = new Map<string, PendingOperation>();
73
+ let operationIdCounter = 0;
74
+
75
+ const startOperation = (type: PendingOperation["type"]): string => {
76
+ const id = `op-${++operationIdCounter}`;
77
+ pendingOperations.set(id, { id, type, startedAt: Date.now() });
78
+ return id;
79
+ };
80
+
81
+ const rejectPendingOperations = (error: Error): void => {
82
+ for (const [id, op] of pendingOperations) {
83
+ logInternalError(
84
+ "child-pi.pending-operation-rejected",
85
+ error,
86
+ `opId=${id} type=${op.type} elapsed=${Date.now() - op.startedAt}ms`,
87
+ );
88
+ }
89
+ pendingOperations.clear();
90
+ };
91
+
92
+ const completeOperation = (id: string): void => {
93
+ pendingOperations.delete(id);
94
+ };
95
+ ```
96
+
97
+ #### Step 2: Track operations in onJsonEvent
98
+
99
+ In the `onJsonEvent` callback (line ~453), add:
100
+
101
+ ```typescript
102
+ onJsonEvent: (event) => {
103
+ restartNoResponseTimer();
104
+
105
+ // Track json events as operations
106
+ const eventOpId = startOperation("json_event");
107
+
108
+ try {
109
+ // ... existing turn-count logic ...
110
+
111
+ if (event && typeof event === "object" && !Array.isArray(event)) {
112
+ const obj = event as Record<string, unknown>;
113
+ if (obj.type === "turn_end") {
114
+ // ...
115
+ }
116
+ }
117
+
118
+ // Complete the operation after processing
119
+ completeOperation(eventOpId);
120
+ } catch (err) {
121
+ completeOperation(eventOpId);
122
+ throw err;
123
+ }
124
+
125
+ input.onJsonEvent?.(event);
126
+ // ...
127
+ }
128
+ ```
129
+
130
+ #### Step 3: Enhance exit handling
131
+
132
+ Replace the exit handler (line ~604):
133
+
134
+ ```typescript
135
+ // OLD:
136
+ child.on("exit", (code) => {
137
+ if (child.pid) {
138
+ activeChildProcesses.delete(child.pid);
139
+ clearHardKillTimer(child.pid);
140
+ }
141
+ try {
142
+ input.onLifecycleEvent?.({ type: "exit", pid: child.pid, exitCode: code, ts: new Date().toISOString() });
143
+ } catch (err) {
144
+ logInternalError("child-pi.on-lifecycle-event", err, `event=exit, pid=${child.pid}`);
145
+ }
146
+ // ...
147
+ });
148
+
149
+ // NEW:
150
+ child.on("exit", (code, signal) => {
151
+ if (child.pid) {
152
+ activeChildProcesses.delete(child.pid);
153
+ clearHardKillTimer(child.pid);
154
+ }
155
+
156
+ // Build comprehensive exit error
157
+ const isUnexpectedExit = !childExited && !settled && !responseTimeoutHit && !abortRequested;
158
+ const exitError = isUnexpectedExit
159
+ ? new Error(
160
+ `Child Pi process exited unexpectedly (code=${code ?? "null"} signal=${signal ?? "null"}). ` +
161
+ `Stderr: ${stderr.slice(-1000) || "(none)"}`,
162
+ )
163
+ : null;
164
+
165
+ // Reject any pending operations with context
166
+ if (exitError) {
167
+ rejectPendingOperations(exitError);
168
+ }
169
+
170
+ try {
171
+ input.onLifecycleEvent?.({
172
+ type: "exit",
173
+ pid: child.pid,
174
+ exitCode: code,
175
+ ts: new Date().toISOString(),
176
+ error: exitError?.message, // Include error context
177
+ stderrExcerpt: stderr.slice(-500), // Last 500 chars of stderr
178
+ });
179
+ } catch (err) {
180
+ logInternalError("child-pi.on-lifecycle-event", err, `event=exit, pid=${child.pid}`);
181
+ }
182
+
183
+ // ... rest unchanged ...
184
+ });
185
+ ```
186
+
187
+ #### Step 4: Enhance error handler
188
+
189
+ Update the `child.on("error")` handler (line ~571):
190
+
191
+ ```typescript
192
+ // OLD:
193
+ child.on("error", (error) => {
194
+ settle({ exitCode: null, stdout, stderr, error: error.message });
195
+ });
196
+
197
+ // NEW:
198
+ child.on("error", (error) => {
199
+ // Reject pending operations with process error context
200
+ const processError = new Error(
201
+ `Child Pi process error: ${error.message}. Stderr: ${stderr.slice(-500) || "(none)"}`,
202
+ );
203
+ rejectPendingOperations(processError);
204
+
205
+ try {
206
+ input.onLifecycleEvent?.({
207
+ type: "spawn_error",
208
+ pid: child.pid,
209
+ error: processError.message,
210
+ ts: new Date().toISOString(),
211
+ stderrExcerpt: stderr.slice(-500),
212
+ });
213
+ } catch (err) {
214
+ logInternalError("child-pi.on-lifecycle-event", err, `event=error, pid=${child.pid}`);
215
+ }
216
+
217
+ settle({ exitCode: null, stdout, stderr, error: processError.message });
218
+ });
219
+ ```
220
+
221
+ #### Step 5: Update ChildPiLifecycleEvent type
222
+
223
+ **File:** `src/runtime/child-pi.ts` (type definition around line ~109)
224
+
225
+ ```typescript
226
+ export interface ChildPiLifecycleEvent {
227
+ type: "spawned" | "spawn_error" | "response_timeout" | "final_drain" | "hard_kill" | "exit" | "close";
228
+ pid?: number;
229
+ exitCode?: number | null;
230
+ error?: string; // NEW: error message for unexpected exits
231
+ stderr?: string;
232
+ stderrExcerpt?: string; // NEW: last N chars of stderr for error context
233
+ ts: string;
234
+ }
235
+ ```
236
+
237
+ ### Test Plan
238
+
239
+ **File:** `test/unit/child-pi-exit.test.ts` (new)
240
+
241
+ ```typescript
242
+ import { describe, it, beforeEach, afterEach, mock } from "node:test";
243
+ import assert from "node:assert/strict";
244
+ import * as path from "node:path";
245
+ import * as fs from "node:fs";
246
+ import { runChildPi } from "../../src/runtime/child-pi.ts";
247
+
248
+ // Mock child-pi for exit testing
249
+ describe("Child process exit handling", () => {
250
+ it("rejects pending operations on unexpected exit", async () => {
251
+ // Create a mock child that exits immediately with non-zero code
252
+ const mockChild = {
253
+ pid: 99999,
254
+ stdin: { write: () => {}, on: () => {} },
255
+ stdout: { on: () => {}, pause: () => {}, resume: () => {}, removeAllListeners: () => {} },
256
+ stderr: { on: () => {} },
257
+ on: (event: string, cb: (...args: unknown[]) => void) => {
258
+ if (event === "exit") cb(1, null);
259
+ return mockChild;
260
+ },
261
+ kill: () => {},
262
+ };
263
+
264
+ // Verify that pending operations are rejected
265
+ // with stderr context included in error
266
+ });
267
+
268
+ it("includes stderr excerpt in exit error", async () => {
269
+ // Child exits with crash message in stderr
270
+ // Verify exit error includes stderr content
271
+ });
272
+
273
+ it("does NOT reject pending operations on graceful exit", async () => {
274
+ // Child exits with code 0 after normal completion
275
+ // Verify no error is logged for pending ops
276
+ });
277
+ });
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Optimization 2: `excludeFromContext` Flag (P1)
283
+
284
+ ### Problem
285
+
286
+ pi now supports `excludeFromContext: true` on bash commands — output is not included in agent context. pi-crew could leverage this for noisy commands.
287
+
288
+ ### Use Cases
289
+
290
+ 1. **Intermediate/staging commands** — commands that prepare data but don't need to be remembered
291
+ 2. **Debug commands** — `ls`, `find`, `grep` that are useful but consume context tokens
292
+ 3. **Status checks** — health checks, version checks that are transient
293
+
294
+ ### Implementation
295
+
296
+ #### Step 1: Add config flag
297
+
298
+ **File:** `src/config/types.ts`
299
+
300
+ ```typescript
301
+ export interface PiTeamsAutonomousConfig {
302
+ // ... existing fields ...
303
+
304
+ /**
305
+ * When true, certain agent commands (ls, find, status checks) will be
306
+ * marked with `excludeFromContext: true` to reduce context overhead.
307
+ * Default: false
308
+ */
309
+ excludeContextBash?: boolean;
310
+ }
311
+ ```
312
+
313
+ #### Step 2: Add CLI option to schema
314
+
315
+ **File:** `src/schema/team-tool-schema.ts`
316
+
317
+ In the `run` action schema, add:
318
+
319
+ ```typescript
320
+ excludeContextBash?: {
321
+ type: "boolean",
322
+ description: "Mark certain commands as excludeFromContext to reduce context tokens",
323
+ default: false,
324
+ };
325
+ ```
326
+
327
+ #### Step 3: Pass to child-pi
328
+
329
+ **File:** `src/runtime/child-pi.ts`
330
+
331
+ Add to `ChildPiRunInput` (around line ~133):
332
+
333
+ ```typescript
334
+ export interface ChildPiRunInput {
335
+ // ... existing fields ...
336
+
337
+ /** Pass to pi to mark certain commands as context-excluded */
338
+ excludeContextBash?: boolean;
339
+ }
340
+ ```
341
+
342
+ #### Step 4: Document in register.ts
343
+
344
+ **File:** `src/extension/register.ts`
345
+
346
+ Add the config to documentation.
347
+
348
+ ### Impact Assessment
349
+
350
+ **Before:** All bash output consumes context tokens.
351
+
352
+ **After:** Selected commands don't count against context.
353
+
354
+ **Estimation:** 10-20% context token reduction for typical tasks with many `ls`/`find` operations.
355
+
356
+ ---
357
+
358
+ ## Optimization 3: Edit Tool Patch Capture (P2)
359
+
360
+ ### Problem
361
+
362
+ pi's edit tool now returns both `diff` (display) and `patch` (standard unified format). pi-crew currently captures only `diff`.
363
+
364
+ ### Benefits
365
+
366
+ 1. **Rollback capability** — can use `git apply` on patch
367
+ 2. **Precise change tracking** — patch is deterministic
368
+ 3. **Better visualization** — can render with standard diff tools
369
+
370
+ ### Implementation
371
+
372
+ #### Step 1: Update artifact structure
373
+
374
+ **File:** `src/runtime/task-runner.ts`
375
+
376
+ Currently (line ~882):
377
+ ```typescript
378
+ const diffArtifact = workspace.worktreePath
379
+ ? writeArtifact(manifest.artifactsRoot, {
380
+ kind: "diff",
381
+ relativePath: `diffs/${task.id}.diff`,
382
+ content: buildDiffFromArtifacts(task, manifest),
383
+ producer: task.id,
384
+ })
385
+ : undefined;
386
+ ```
387
+
388
+ **Change:** Also capture `patch` artifacts from tool results.
389
+
390
+ Add after line ~882:
391
+
392
+ ```typescript
393
+ // Capture unified patches from edit tool results
394
+ const extractPatchFromToolResult = (events: unknown[]): string => {
395
+ for (const event of events) {
396
+ if (typeof event !== "object" || event === null) continue;
397
+ const obj = event as Record<string, unknown>;
398
+ if (obj.type === "tool_result") {
399
+ const content = obj.content as Array<Record<string, unknown>> | undefined;
400
+ if (content) {
401
+ const patchBlock = content.find(
402
+ (c: Record<string, unknown>) => (c as Record<string, unknown>).type === "text" &&
403
+ typeof (c as Record<string, unknown>).text === "string" &&
404
+ ((c as Record<string, unknown>).text as string).includes("--- a/")
405
+ );
406
+ if (patchBlock) {
407
+ return (patchBlock as Record<string, unknown>).text as string;
408
+ }
409
+ }
410
+ }
411
+ }
412
+ return "";
413
+ };
414
+
415
+ const patchArtifact = workspace.worktreePath && parsedOutput?.jsonEvents
416
+ ? writeArtifact(manifest.artifactsRoot, {
417
+ kind: "patch",
418
+ relativePath: `patches/${task.id}.patch`,
419
+ content: extractPatchFromToolResult(parsedOutput.jsonEvents as unknown[]),
420
+ producer: task.id,
421
+ })
422
+ : undefined;
423
+ ```
424
+
425
+ #### Step 2: Include in result bundle
426
+
427
+ Around line ~1105, add `patchArtifact` to artifact list:
428
+
429
+ ```typescript
430
+ return {
431
+ manifest,
432
+ tasks,
433
+ artifacts: [
434
+ resultArtifact,
435
+ logArtifact,
436
+ // ... existing ...
437
+ ...(patchArtifact ? [patchArtifact] : []),
438
+ ],
439
+ };
440
+ ```
441
+
442
+ ### Test Plan
443
+
444
+ **File:** `test/unit/edit-patch-capture.test.ts` (new)
445
+
446
+ ```typescript
447
+ import { describe, it } from "node:test";
448
+ import assert from "node:assert/strict";
449
+
450
+ describe("Edit tool patch capture", () => {
451
+ it("extracts unified patch from tool results", async () => {
452
+ // Mock jsonEvents with tool_result containing patch
453
+ const patch = extractPatchFromToolResult([{
454
+ type: "tool_result",
455
+ content: [{
456
+ type: "text",
457
+ text: `--- a/file.ts\n+++ b/file.ts\n@@ -1,3 +1,4 @@\n line1\n+newLine\n line2\n`
458
+ }]
459
+ }]);
460
+
461
+ assert.ok(patch.includes("--- a/"));
462
+ assert.ok(patch.includes("+++ b/"));
463
+ assert.ok(patch.includes("@@"));
464
+ });
465
+ });
466
+ ```
467
+
468
+ ---
469
+
470
+ ## Optimization 4: Session ID Alignment (P2)
471
+
472
+ ### Problem
473
+
474
+ pi-crew run IDs and pi session IDs are not aligned. Cross-referencing is difficult.
475
+
476
+ ### Current State
477
+
478
+ - **pi-crew run ID:** `run-{uuid}` format (e.g., `run-01J8X...`)
479
+ - **pi session ID:** uuidv7 format (e.g., `01J8Y...`)
480
+
481
+ ### Implementation
482
+
483
+ #### Step 1: Add session ID to manifest
484
+
485
+ **File:** `src/state/crew-init.ts`
486
+
487
+ When creating a run manifest, pass a named session to pi:
488
+
489
+ ```typescript
490
+ import { assertValidSessionId } from "../utils/session-utils.ts"; // New
491
+
492
+ // In createRunManifest() or wherever session is created
493
+ const sessionId = `crew-${manifest.runId.replace(/[^A-Za-z0-9]/g, "").slice(0, 12)}`;
494
+
495
+ // Validate that it's safe for pi
496
+ try {
497
+ assertValidSessionId(sessionId);
498
+ } catch {
499
+ // Fallback to uuid
500
+ sessionId = `crew-${Date.now().toString(36)}`;
501
+ }
502
+
503
+ // Store in manifest
504
+ manifest.sessionId = sessionId;
505
+ ```
506
+
507
+ **File:** `src/utils/session-utils.ts` (new)
508
+
509
+ ```typescript
510
+ /**
511
+ * Validate session ID format per pi's requirements.
512
+ * Format: ^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$
513
+ */
514
+ export function assertValidSessionId(id: string): void {
515
+ if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(id)) {
516
+ throw new Error(
517
+ `Invalid session id: must be non-empty, alphanumeric with '-', '_', '.' and start/end with alphanumeric`,
518
+ );
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Convert a pi-crew run ID to a valid pi session ID.
524
+ */
525
+ export function toPiSessionId(runId: string): string {
526
+ // Strip non-alphanumeric, lowercase, prefix with "crew-"
527
+ const sanitized = runId.replace(/[^A-Za-z0-9]/g, "").toLowerCase();
528
+ return `crew-${sanitized.slice(0, 16)}`;
529
+ }
530
+ ```
531
+
532
+ #### Step 2: Pass to child-pi
533
+
534
+ **File:** `src/runtime/child-pi.ts`
535
+
536
+ In `ChildPiRunInput`:
537
+
538
+ ```typescript
539
+ export interface ChildPiRunInput {
540
+ // ... existing fields ...
541
+
542
+ /** Session ID for pi session naming (aligns with pi-crew run ID) */
543
+ sessionId?: string;
544
+ }
545
+ ```
546
+
547
+ In the `runChildPi` function, add to `buildCommand`:
548
+
549
+ ```typescript
550
+ const buildCommand = (): string => {
551
+ const args = [
552
+ input.cwd,
553
+ "--agent", input.agent.name,
554
+ "--goal", input.task,
555
+ ];
556
+
557
+ if (input.sessionId) {
558
+ args.push("--session-id", input.sessionId);
559
+ }
560
+
561
+ // ... rest
562
+ };
563
+ ```
564
+
565
+ #### Step 3: Read session ID in register.ts
566
+
567
+ **File:** `src/extension/register.ts`
568
+
569
+ Document the session ID format for users who want to resume pi sessions.
570
+
571
+ ### Benefits
572
+
573
+ 1. **Easy cross-reference:** User sees `crew-run-abc123` in both pi and pi-crew
574
+ 2. **Named resume:** `pi --session crew-run-abc123` works
575
+ 3. **Debugging:** Match logs between pi and pi-crew by session ID
576
+ 4. **Persistence:** Sessions persist in `.pi/sessions/` with human-readable names
577
+
578
+ ---
579
+
580
+ ## Rollout Plan
581
+
582
+ ### Phase 1: Child Process Exit Handling (Week 1)
583
+
584
+ 1. Add `PendingOperation` tracker
585
+ 2. Update `ChildPiLifecycleEvent` type
586
+ 3. Enhance exit/error handlers
587
+ 4. Add tests
588
+
589
+ **Risk:** Low — internal changes, no API change.
590
+
591
+ ### Phase 2: Session ID Alignment (Week 1-2)
592
+
593
+ 1. Create `session-utils.ts`
594
+ 2. Update `crew-init.ts` to generate session IDs
595
+ 3. Pass to `child-pi.ts`
596
+ 4. Document in `register.ts`
597
+
598
+ **Risk:** Low — additive feature.
599
+
600
+ ### Phase 3: `excludeFromContext` (Week 2-3)
601
+
602
+ 1. Add config flag to types
603
+ 2. Update schema
604
+ 3. Pass to child-pi
605
+ 4. Document
606
+
607
+ **Risk:** Medium — user-facing config change.
608
+
609
+ ### Phase 4: Edit Patch Capture (Week 3-4)
610
+
611
+ 1. Update `task-runner.ts` to extract patches
612
+ 2. Add artifact generation
613
+ 3. Add tests
614
+
615
+ **Risk:** Low — additive feature.
616
+
617
+ ---
618
+
619
+ ## Success Metrics
620
+
621
+ | Optimization | Metric | Target |
622
+ |--------------|--------|--------|
623
+ | Child Exit Handling | % of unexpected exits with error context | 100% |
624
+ | Session ID Alignment | % of runs with named sessions | 100% |
625
+ | `excludeFromContext` | Context token reduction for typical task | 10-20% |
626
+ | Edit Patch Capture | % of edit operations with patch artifact | 100% |
627
+
628
+ ---
629
+
630
+ ## Files Summary
631
+
632
+ | File | Changes |
633
+ |------|---------|
634
+ | `src/runtime/child-pi.ts` | Pending ops tracker, exit handling, session ID, excludeContext |
635
+ | `src/runtime/task-runner.ts` | Patch artifact capture |
636
+ | `src/state/crew-init.ts` | Session ID generation |
637
+ | `src/utils/session-utils.ts` | New: session ID utilities |
638
+ | `src/config/types.ts` | `excludeContextBash` flag |
639
+ | `src/schema/team-tool-schema.ts` | CLI option for exclude context |
640
+ | `src/extension/register.ts` | Documentation |
641
+ | `test/unit/child-pi-exit.test.ts` | New: exit handling tests |
642
+ | `test/unit/edit-patch-capture.test.ts` | New: patch capture tests |