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.
- package/CHANGELOG.md +183 -0
- package/README.md +17 -1
- package/docs/architecture.md +2 -0
- package/docs/bugs/cross-session-notification-leakage.md +82 -0
- package/docs/coding-agent-optimization.md +268 -0
- package/docs/deep-review-report.md +384 -0
- package/docs/distillation/cybersecurity-patterns.md +294 -0
- package/docs/migration-v0.4-v0.5.md +208 -0
- package/docs/optimization-plan.md +642 -0
- package/docs/pi-crew-v0.5.5-audit-fix-plan.md +133 -0
- package/docs/pi-mono-opportunities.md +969 -0
- package/docs/pi-mono-review.md +291 -0
- package/docs/skills/REFERENCE.md +144 -0
- package/package.json +12 -9
- package/skills/artifact-analysis-loop/SKILL.md +302 -0
- package/skills/async-worker-recovery/SKILL.md +19 -1
- package/skills/child-pi-spawning/SKILL.md +19 -6
- package/skills/context-artifact-hygiene/SKILL.md +19 -2
- package/skills/delegation-patterns/SKILL.md +68 -3
- package/skills/detection-pipeline-design/SKILL.md +285 -0
- package/skills/event-log-tracing/SKILL.md +20 -6
- package/skills/git-master/SKILL.md +20 -6
- package/skills/hunting-investigation-loop/SKILL.md +401 -0
- package/skills/incident-playbook-construction/SKILL.md +383 -0
- package/skills/live-agent-lifecycle/SKILL.md +20 -6
- package/skills/mailbox-interactive/SKILL.md +19 -6
- package/skills/model-routing-context/SKILL.md +19 -1
- package/skills/multi-perspective-review/SKILL.md +19 -4
- package/skills/observability-reliability/SKILL.md +19 -2
- package/skills/orchestration/SKILL.md +20 -2
- package/skills/ownership-session-security/SKILL.md +20 -2
- package/skills/pi-extension-lifecycle/SKILL.md +20 -2
- package/skills/post-mortem/SKILL.md +7 -2
- package/skills/read-only-explorer/SKILL.md +20 -6
- package/skills/requirements-to-task-packet/SKILL.md +23 -3
- package/skills/resource-discovery-config/SKILL.md +20 -2
- package/skills/runtime-state-reader/SKILL.md +20 -2
- package/skills/safe-bash/SKILL.md +21 -6
- package/skills/scrutinize/SKILL.md +20 -2
- package/skills/secure-agent-orchestration-review/SKILL.md +29 -2
- package/skills/security-review/SKILL.md +560 -0
- package/skills/state-mutation-locking/SKILL.md +22 -2
- package/skills/systematic-debugging/SKILL.md +8 -6
- package/skills/threat-hypothesis-framework/SKILL.md +175 -0
- package/skills/ui-render-performance/SKILL.md +20 -2
- package/skills/verification-before-done/SKILL.md +17 -2
- package/skills/widget-rendering/SKILL.md +21 -6
- package/skills/workspace-isolation/SKILL.md +20 -6
- package/skills/worktree-isolation/SKILL.md +20 -6
- package/src/agents/agent-config.ts +40 -1
- package/src/benchmark/benchmark-runner.ts +45 -0
- package/src/benchmark/feedback-loop.ts +5 -0
- package/src/config/config.ts +32 -5
- package/src/config/role-tools.ts +82 -0
- package/src/config/suggestions.ts +8 -0
- package/src/config/types.ts +4 -0
- package/src/extension/async-notifier.ts +10 -1
- package/src/extension/crew-cleanup.ts +114 -0
- package/src/extension/cross-extension-rpc.ts +1 -1
- package/src/extension/notification-router.ts +18 -0
- package/src/extension/register.ts +27 -19
- package/src/extension/registration/subagent-tools.ts +1 -1
- package/src/extension/team-tool/anchor.ts +201 -0
- package/src/extension/team-tool/api.ts +2 -1
- package/src/extension/team-tool/auto-summarize.ts +154 -0
- package/src/extension/team-tool/run.ts +42 -7
- package/src/extension/team-tool.ts +44 -2
- package/src/hooks/registry.ts +1 -3
- package/src/observability/event-bus.ts +69 -0
- package/src/observability/event-to-metric.ts +0 -2
- package/src/runtime/anchor-manager.ts +473 -0
- package/src/runtime/async-runner.ts +8 -4
- package/src/runtime/auto-summarize.ts +350 -0
- package/src/runtime/background-runner.ts +10 -3
- package/src/runtime/budget-tracker.ts +354 -0
- package/src/runtime/chain-runner.ts +507 -0
- package/src/runtime/child-pi.ts +123 -35
- package/src/runtime/crash-recovery.ts +5 -4
- package/src/runtime/crew-agent-runtime.ts +1 -0
- package/src/runtime/custom-tools/irc-tool.ts +13 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +3 -2
- package/src/runtime/delivery-coordinator.ts +10 -3
- package/src/runtime/dynamic-script-runner.ts +482 -0
- package/src/runtime/foreground-control.ts +87 -17
- package/src/runtime/handoff-manager.ts +589 -0
- package/src/runtime/hidden-handoff.ts +424 -0
- package/src/runtime/live-agent-manager.ts +20 -4
- package/src/runtime/live-session-runtime.ts +39 -4
- package/src/runtime/manifest-cache.ts +2 -1
- package/src/runtime/model-resolver.ts +16 -4
- package/src/runtime/phase-tracker.ts +373 -0
- package/src/runtime/pi-args.ts +11 -1
- package/src/runtime/pi-json-output.ts +31 -0
- package/src/runtime/pipeline-runner.ts +514 -0
- package/src/runtime/progress-tracker.ts +124 -0
- package/src/runtime/retry-runner.ts +354 -0
- package/src/runtime/sandbox.ts +252 -0
- package/src/runtime/scheduler.ts +7 -2
- package/src/runtime/skill-effectiveness.ts +473 -0
- package/src/runtime/skill-instructions.ts +37 -3
- package/src/runtime/subagent-manager.ts +1 -1
- package/src/runtime/task-graph.ts +11 -1
- package/src/runtime/task-runner.ts +92 -18
- package/src/runtime/team-runner.ts +13 -12
- package/src/runtime/tool-progress.ts +10 -3
- package/src/runtime/verification-gates.ts +367 -0
- package/src/schema/team-tool-schema.ts +37 -0
- package/src/skills/discover-skills.ts +5 -0
- package/src/state/active-run-registry.ts +9 -2
- package/src/state/contracts.ts +9 -0
- package/src/state/crew-init.ts +3 -3
- package/src/state/decision-ledger.ts +98 -55
- package/src/state/event-log-rotation.ts +2 -2
- package/src/state/event-log.ts +144 -10
- package/src/state/hook-instinct-bridge.ts +5 -5
- package/src/state/mailbox.ts +10 -0
- package/src/state/run-cache.ts +18 -8
- package/src/state/state-store.ts +3 -1
- package/src/state/types.ts +4 -0
- package/src/tools/safe-bash-extension.ts +1 -0
- package/src/tools/safe-bash.ts +152 -20
- package/src/types/new-api-types.ts +34 -0
- package/src/ui/agent-management-overlay.ts +5 -1
- package/src/ui/crew-widget.ts +29 -15
- package/src/ui/overlays/mailbox-detail-overlay.ts +13 -2
- package/src/ui/powerbar-publisher.ts +101 -7
- package/src/ui/tool-render.ts +15 -15
- package/src/ui/transcript-cache.ts +13 -0
- package/src/utils/bm25-search.ts +16 -8
- package/src/utils/env-filter.ts +8 -5
- package/src/utils/redaction.ts +169 -15
- package/src/utils/session-utils.ts +52 -0
- package/src/utils/sse-parser.ts +10 -1
- package/src/worktree/cleanup.ts +6 -1
- package/src/worktree/worktree-manager.ts +32 -13
- package/workflows/chain.workflow.md +252 -0
- 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 |
|