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,350 @@
1
+ /**
2
+ * AutoSummarizeService - Enables auto-summarization with token/tool thresholds.
3
+ *
4
+ * Based on pi-boomerang's autoBoomerang pattern:
5
+ * - toggle() enables/disables auto-summarization
6
+ * - shouldAutoSummarize() checks if task should auto-summarize
7
+ * - Token and tool thresholds control when summarization triggers
8
+ *
9
+ * @see docs/pi-boomerang-integration-plan.md
10
+ */
11
+
12
+ import type { TaskPacket, TaskResult } from "./handoff-manager.ts";
13
+
14
+ /**
15
+ * Configuration for AutoSummarizeService.
16
+ */
17
+ export interface AutoSummarizeConfig {
18
+ /** Whether auto-summarize is enabled */
19
+ enabled: boolean;
20
+ /** Token threshold to trigger summarization */
21
+ threshold: number;
22
+ /** Minimum tools used to trigger summarization (default: 5) */
23
+ minToolsUsed?: number;
24
+ /** Whether to collapse context after summarization */
25
+ collapseContext?: boolean;
26
+ }
27
+
28
+ /**
29
+ * Default configuration values.
30
+ */
31
+ export const DEFAULT_AUTO_SUMMARIZE_CONFIG: Required<Omit<AutoSummarizeConfig, "enabled">> = {
32
+ threshold: 5000,
33
+ minToolsUsed: 5,
34
+ collapseContext: true,
35
+ };
36
+
37
+ /**
38
+ * Options for AutoSummarizeService.
39
+ */
40
+ export interface AutoSummarizeServiceOptions {
41
+ /** Initial configuration */
42
+ config?: Partial<AutoSummarizeConfig>;
43
+ /** Custom event emitter */
44
+ eventEmitter?: AutoSummarizeEventEmitter;
45
+ }
46
+
47
+ /**
48
+ * Event emitter for auto-summarize events.
49
+ */
50
+ export interface AutoSummarizeEventEmitter {
51
+ emit(event: string, data: unknown): void;
52
+ }
53
+
54
+ /**
55
+ * Event data for auto-summarize toggle event.
56
+ */
57
+ export interface AutoSummarizeToggledEventData {
58
+ enabled: boolean;
59
+ previousEnabled: boolean;
60
+ }
61
+
62
+ /**
63
+ * Event data for auto-summarize triggered event.
64
+ */
65
+ export interface AutoSummarizeTriggeredEventData {
66
+ packet: TaskPacket;
67
+ result: TaskResult;
68
+ trigger: AutoSummarizeTrigger;
69
+ tokenCount: number;
70
+ }
71
+
72
+ /**
73
+ * What triggered the auto-summarize.
74
+ */
75
+ export type AutoSummarizeTrigger =
76
+ | "token_threshold"
77
+ | "tools_threshold"
78
+ | "manual"
79
+ | "high_usage";
80
+
81
+ /**
82
+ * AutoSummarizeService enables automatic summarization based on configurable thresholds.
83
+ * When enabled, it monitors task completion and triggers summarization for tasks
84
+ * that exceed token or tool usage thresholds.
85
+ */
86
+ export class AutoSummarizeService {
87
+ private config: AutoSummarizeConfig & Required<Omit<AutoSummarizeConfig, "enabled">>;
88
+ private eventEmitter: AutoSummarizeEventEmitter | null = null;
89
+
90
+ constructor(options: AutoSummarizeServiceOptions = {}) {
91
+ this.config = {
92
+ enabled: options.config?.enabled ?? false,
93
+ threshold: options.config?.threshold ?? DEFAULT_AUTO_SUMMARIZE_CONFIG.threshold,
94
+ minToolsUsed: options.config?.minToolsUsed ?? DEFAULT_AUTO_SUMMARIZE_CONFIG.minToolsUsed,
95
+ collapseContext: options.config?.collapseContext ?? DEFAULT_AUTO_SUMMARIZE_CONFIG.collapseContext,
96
+ };
97
+
98
+ if (options.eventEmitter) {
99
+ this.eventEmitter = options.eventEmitter;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Check if auto-summarization is currently enabled.
105
+ */
106
+ isEnabled(): boolean {
107
+ return this.config.enabled;
108
+ }
109
+
110
+ /**
111
+ * Toggle auto-summarize mode.
112
+ * Returns the new enabled state.
113
+ */
114
+ toggle(): boolean {
115
+ const previousEnabled = this.config.enabled;
116
+ this.config.enabled = !this.config.enabled;
117
+
118
+ this.eventEmitter?.emit("auto-summarize:toggled", {
119
+ enabled: this.config.enabled,
120
+ previousEnabled,
121
+ } as AutoSummarizeToggledEventData);
122
+
123
+ return this.config.enabled;
124
+ }
125
+
126
+ /**
127
+ * Enable auto-summarize.
128
+ */
129
+ enable(): void {
130
+ if (!this.config.enabled) {
131
+ this.toggle();
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Disable auto-summarize.
137
+ */
138
+ disable(): void {
139
+ if (this.config.enabled) {
140
+ this.toggle();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Check if a task should auto-summarize.
146
+ *
147
+ * @param packet - The task packet
148
+ * @param result - The task result
149
+ * @returns True if the task should auto-summarize
150
+ */
151
+ shouldAutoSummarize(packet: TaskPacket, result: TaskResult): boolean {
152
+ if (!this.config.enabled) {
153
+ return false;
154
+ }
155
+
156
+ const tokenCount = result.usage?.totalTokens ?? 0;
157
+
158
+ // Check token threshold
159
+ if (tokenCount >= this.config.threshold) {
160
+ return true;
161
+ }
162
+
163
+ // Check tools threshold
164
+ const toolsUsed = result.toolsUsed?.length ?? 0;
165
+ if (toolsUsed >= (this.config.minToolsUsed ?? 5)) {
166
+ return true;
167
+ }
168
+
169
+ // High usage check: high token count relative to tools
170
+ // More tokens per tool suggests complex work that should be summarized
171
+ if (tokenCount > 2000 && toolsUsed >= 3) {
172
+ const tokensPerTool = tokenCount / toolsUsed;
173
+ if (tokensPerTool > 1000) {
174
+ return true;
175
+ }
176
+ }
177
+
178
+ return false;
179
+ }
180
+
181
+ /**
182
+ * Get the reason why a task should (or should not) auto-summarize.
183
+ *
184
+ * @param packet - The task packet
185
+ * @param result - The task result
186
+ * @returns Object with shouldSummarize flag and reason
187
+ */
188
+ getAutoSummarizeDecision(packet: TaskPacket, result: TaskResult): AutoSummarizeDecision {
189
+ if (!this.config.enabled) {
190
+ return {
191
+ shouldSummarize: false,
192
+ reason: "auto-summarize is disabled",
193
+ trigger: undefined,
194
+ tokenCount: result.usage?.totalTokens ?? 0,
195
+ toolsUsed: result.toolsUsed?.length ?? 0,
196
+ };
197
+ }
198
+
199
+ const tokenCount = result.usage?.totalTokens ?? 0;
200
+ const toolsUsed = result.toolsUsed?.length ?? 0;
201
+
202
+ // Check token threshold
203
+ if (tokenCount >= this.config.threshold) {
204
+ return {
205
+ shouldSummarize: true,
206
+ reason: `Token count ${tokenCount} exceeds threshold ${this.config.threshold}`,
207
+ trigger: "token_threshold",
208
+ tokenCount,
209
+ toolsUsed,
210
+ };
211
+ }
212
+
213
+ // Check tools threshold
214
+ const minTools = this.config.minToolsUsed ?? 5;
215
+ if (toolsUsed >= minTools) {
216
+ return {
217
+ shouldSummarize: true,
218
+ reason: `Tool count ${toolsUsed} meets minimum ${minTools}`,
219
+ trigger: "tools_threshold",
220
+ tokenCount,
221
+ toolsUsed,
222
+ };
223
+ }
224
+
225
+ // High usage check
226
+ if (tokenCount > 2000 && toolsUsed >= 3) {
227
+ const tokensPerTool = tokenCount / toolsUsed;
228
+ if (tokensPerTool > 1000) {
229
+ return {
230
+ shouldSummarize: true,
231
+ reason: `High token-to-tool ratio: ${Math.round(tokensPerTool)} tokens/tool`,
232
+ trigger: "high_usage",
233
+ tokenCount,
234
+ toolsUsed,
235
+ };
236
+ }
237
+ }
238
+
239
+ return {
240
+ shouldSummarize: false,
241
+ reason: `Below thresholds (tokens: ${tokenCount}/${this.config.threshold}, tools: ${toolsUsed}/${minTools})`,
242
+ trigger: undefined,
243
+ tokenCount,
244
+ toolsUsed,
245
+ };
246
+ }
247
+
248
+ /**
249
+ * Get the current configuration.
250
+ */
251
+ getConfig(): AutoSummarizeConfig & Required<Omit<AutoSummarizeConfig, "enabled">> {
252
+ return { ...this.config };
253
+ }
254
+
255
+ /**
256
+ * Update configuration.
257
+ */
258
+ updateConfig(config: Partial<AutoSummarizeConfig>): void {
259
+ const previousEnabled = this.config.enabled;
260
+
261
+ if (config.enabled !== undefined) {
262
+ this.config.enabled = config.enabled;
263
+ }
264
+ if (config.threshold !== undefined) {
265
+ this.config.threshold = config.threshold;
266
+ }
267
+ if (config.minToolsUsed !== undefined) {
268
+ this.config.minToolsUsed = config.minToolsUsed;
269
+ }
270
+ if (config.collapseContext !== undefined) {
271
+ this.config.collapseContext = config.collapseContext;
272
+ }
273
+
274
+ // Emit event if enabled state changed
275
+ if (config.enabled !== undefined && config.enabled !== previousEnabled) {
276
+ this.eventEmitter?.emit("auto-summarize:toggled", {
277
+ enabled: this.config.enabled,
278
+ previousEnabled,
279
+ } as AutoSummarizeToggledEventData);
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Get current threshold value.
285
+ */
286
+ getThreshold(): number {
287
+ return this.config.threshold;
288
+ }
289
+
290
+ /**
291
+ * Set token threshold.
292
+ */
293
+ setThreshold(threshold: number): void {
294
+ if (threshold < 0) {
295
+ throw new Error("Threshold must be non-negative");
296
+ }
297
+ this.config.threshold = threshold;
298
+ }
299
+
300
+ /**
301
+ * Get current minToolsUsed value.
302
+ */
303
+ getMinToolsUsed(): number {
304
+ return this.config.minToolsUsed ?? 5;
305
+ }
306
+
307
+ /**
308
+ * Set minimum tools threshold.
309
+ */
310
+ setMinToolsUsed(minTools: number): void {
311
+ if (minTools < 0) {
312
+ throw new Error("minToolsUsed must be non-negative");
313
+ }
314
+ this.config.minToolsUsed = minTools;
315
+ }
316
+
317
+ /**
318
+ * Check if context should be collapsed after summarization.
319
+ */
320
+ shouldCollapseContext(): boolean {
321
+ return this.config.collapseContext ?? true;
322
+ }
323
+
324
+ /**
325
+ * Set event emitter.
326
+ */
327
+ setEventEmitter(eventEmitter: AutoSummarizeEventEmitter): void {
328
+ this.eventEmitter = eventEmitter;
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Decision result from shouldAutoSummarize check.
334
+ */
335
+ export interface AutoSummarizeDecision {
336
+ shouldSummarize: boolean;
337
+ reason: string;
338
+ trigger: AutoSummarizeTrigger | undefined;
339
+ tokenCount: number;
340
+ toolsUsed: number;
341
+ }
342
+
343
+ /**
344
+ * Create an AutoSummarizeService with default options.
345
+ */
346
+ export function createAutoSummarizeService(
347
+ options?: AutoSummarizeServiceOptions,
348
+ ): AutoSummarizeService {
349
+ return new AutoSummarizeService(options);
350
+ }
@@ -24,6 +24,7 @@ import { directTeamAndWorkflowFromRun } from "./direct-run.ts";
24
24
  import { expandParallelResearchWorkflow } from "./parallel-research.ts";
25
25
  import { writeAsyncStartMarker } from "./async-marker.ts";
26
26
  import { startParentGuard, stopParentGuard } from "./parent-guard.ts";
27
+ import { logInternalError } from "../utils/internal-error.ts";
27
28
 
28
29
  /**
29
30
  * Heartbeat mechanism: periodically write a heartbeat file so the stale reconciler
@@ -127,6 +128,8 @@ function setupUnhandledRejectionGuard(state: { cwd?: string; runId?: string; eve
127
128
  }
128
129
 
129
130
  async function main(): Promise<void> {
131
+ // FIX: Store logFd so it can be closed on exit to prevent file descriptor leak
132
+ let logFd: number | undefined;
130
133
  // Redirect console to background.log since stdio is "ignore" in detached mode.
131
134
  // Must be BEFORE any console.log/console.error calls.
132
135
  const _cwd = argValue("--cwd");
@@ -134,13 +137,17 @@ async function main(): Promise<void> {
134
137
  if (_cwd && _runId) {
135
138
  try {
136
139
  const logPath = path.join(_cwd, ".crew/state/runs", _runId, "background.log");
137
- const logFd = fs.openSync(logPath, "a");
140
+ logFd = fs.openSync(logPath, "a");
138
141
  const origWrite = (prefix: string) => (data: any, ...args: any[]) => {
139
142
  const msg = [data, ...args].map(String).join(" ") + "\n";
140
- fs.writeSync(logFd, msg);
143
+ fs.writeSync(logFd!, msg);
141
144
  };
142
145
  console.log = origWrite("OUT");
143
146
  console.error = origWrite("ERR");
147
+ // FIX: Close logFd on process exit to prevent file descriptor leak
148
+ process.on("exit", () => {
149
+ try { if (logFd !== undefined) fs.closeSync(logFd); } catch { /* ignore */ }
150
+ });
144
151
  } catch { /* best-effort */ }
145
152
  }
146
153
 
@@ -317,7 +324,7 @@ async function main(): Promise<void> {
317
324
  if (loaded) {
318
325
  // LAZY: live-agent-manager only needed on failure cleanup path; avoid module load at hot path.
319
326
  const { terminateLiveAgentsForRun } = await import("./live-agent-manager.ts");
320
- void terminateLiveAgentsForRun(loaded.manifest.runId, "failed", appendEvent, loaded.manifest.eventsPath).catch(() => {});
327
+ void terminateLiveAgentsForRun(loaded.manifest.runId, "failed", appendEvent, loaded.manifest.eventsPath).catch((error) => logInternalError("background-runner.terminate", error, `runId=${loaded.manifest.runId}`));
321
328
  }
322
329
  } catch { /* best-effort */ }
323
330
  const message = error instanceof Error ? error.message : String(error);