pi-crew 0.9.16 → 0.9.17

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 (432) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21790 -0
  4. package/dist/index.mjs +83070 -0
  5. package/dist/index.mjs.map +7 -0
  6. package/docs/A +358 -0
  7. package/docs/M-A +357 -0
  8. package/docs/REVIEW-FINDINGS-2026-06 +357 -0
  9. package/docs/Y +357 -0
  10. package/docs/a +357 -0
  11. package/docs/aA +358 -0
  12. package/docs/archive/README.md +91 -0
  13. package/docs/patterns/command-agent-skill.md +1 -1
  14. package/docs/skills/REFERENCE.md +0 -17
  15. package/docs//303/242mmaAAA/303/242 +357 -0
  16. package/index.bundle.ts +25 -0
  17. package/index.ts +95 -4
  18. package/package.json +15 -4
  19. package/skills/iterative-audit/SKILL.md +0 -1
  20. package/skills/widget-rendering/SKILL.md +17 -0
  21. package/src/adapters/claude-adapter.ts +1 -3
  22. package/src/adapters/export-util.ts +16 -16
  23. package/src/adapters/index.ts +5 -5
  24. package/src/agents/agent-config.ts +20 -14
  25. package/src/agents/agent-serializer.ts +1 -1
  26. package/src/agents/discover-agents.ts +83 -39
  27. package/src/benchmark/benchmark-runner.ts +51 -51
  28. package/src/benchmark/feedback-loop.ts +1 -1
  29. package/src/config/config.ts +166 -590
  30. package/src/config/defaults.ts +28 -2
  31. package/src/config/drift-detector.ts +11 -14
  32. package/src/config/markers.ts +201 -203
  33. package/src/config/resilient-parser.ts +14 -6
  34. package/src/config/role-tools.ts +3 -3
  35. package/src/config/suggestions.ts +5 -12
  36. package/src/config/types.ts +9 -25
  37. package/src/errors.ts +151 -157
  38. package/src/extension/action-suggestions.ts +54 -9
  39. package/src/extension/async-notifier.ts +43 -13
  40. package/src/extension/autonomous-policy.ts +77 -37
  41. package/src/extension/command-completions.ts +9 -8
  42. package/src/extension/context-status-injection.ts +14 -12
  43. package/src/extension/crew-autocomplete.ts +8 -16
  44. package/src/extension/crew-cleanup.ts +2 -1
  45. package/src/extension/crew-shortcuts.ts +9 -6
  46. package/src/extension/cross-extension-rpc.ts +136 -42
  47. package/src/extension/help.ts +1 -1
  48. package/src/extension/import-index.ts +10 -5
  49. package/src/extension/knowledge-injection.ts +88 -15
  50. package/src/extension/management.ts +89 -349
  51. package/src/extension/message-renderers.ts +3 -4
  52. package/src/extension/notification-router.ts +22 -5
  53. package/src/extension/notification-sink.ts +6 -3
  54. package/src/extension/pi-api.ts +17 -8
  55. package/src/extension/plan-orchestrate.ts +8 -27
  56. package/src/extension/project-init.ts +21 -6
  57. package/src/extension/register.ts +329 -975
  58. package/src/extension/registration/artifact-cleanup.ts +9 -4
  59. package/src/extension/registration/command-utils.ts +5 -1
  60. package/src/extension/registration/commands.ts +806 -314
  61. package/src/extension/registration/compaction-guard.ts +15 -15
  62. package/src/extension/registration/lifecycle.ts +259 -0
  63. package/src/extension/registration/observability.ts +293 -0
  64. package/src/extension/registration/subagent-helpers.ts +29 -7
  65. package/src/extension/registration/subagent-tools.ts +253 -44
  66. package/src/extension/registration/team-tool.ts +19 -84
  67. package/src/extension/registration/ui.ts +168 -0
  68. package/src/extension/registration/viewers.ts +64 -24
  69. package/src/extension/result-watcher.ts +18 -7
  70. package/src/extension/run-bundle-schema.ts +21 -6
  71. package/src/extension/run-export.ts +15 -6
  72. package/src/extension/run-import.ts +64 -29
  73. package/src/extension/run-index.ts +28 -9
  74. package/src/extension/run-maintenance.ts +32 -12
  75. package/src/extension/session-summary.ts +1 -1
  76. package/src/extension/team-manager-command.ts +64 -7
  77. package/src/extension/team-onboard.ts +151 -150
  78. package/src/extension/team-recommendation.ts +121 -21
  79. package/src/extension/team-tool/anchor.ts +31 -64
  80. package/src/extension/team-tool/api.ts +929 -141
  81. package/src/extension/team-tool/auto-summarize.ts +14 -26
  82. package/src/extension/team-tool/cache-control.ts +1 -5
  83. package/src/extension/team-tool/cancel.ts +138 -38
  84. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  85. package/src/extension/team-tool/chain-executor.ts +29 -51
  86. package/src/extension/team-tool/config-patch.ts +1 -1
  87. package/src/extension/team-tool/context.ts +40 -20
  88. package/src/extension/team-tool/destructive-gate.ts +10 -5
  89. package/src/extension/team-tool/doctor.ts +170 -51
  90. package/src/extension/team-tool/explain.ts +228 -214
  91. package/src/extension/team-tool/failure-patterns.ts +3 -9
  92. package/src/extension/team-tool/goal-wrap.ts +71 -29
  93. package/src/extension/team-tool/goal.ts +185 -35
  94. package/src/extension/team-tool/handle-schedule.ts +34 -15
  95. package/src/extension/team-tool/handle-settings.ts +94 -56
  96. package/src/extension/team-tool/health-monitor.ts +27 -108
  97. package/src/extension/team-tool/inspect.ts +42 -9
  98. package/src/extension/team-tool/intent-policy.ts +4 -1
  99. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  100. package/src/extension/team-tool/orchestrate.ts +7 -18
  101. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  102. package/src/extension/team-tool/plan.ts +29 -5
  103. package/src/extension/team-tool/respond.ts +49 -16
  104. package/src/extension/team-tool/run-not-found.ts +3 -8
  105. package/src/extension/team-tool/run.ts +80 -317
  106. package/src/extension/team-tool/status.ts +135 -43
  107. package/src/extension/team-tool/workflow-manage.ts +92 -25
  108. package/src/extension/team-tool-types.ts +8 -1
  109. package/src/extension/team-tool.ts +142 -551
  110. package/src/extension/validate-resources.ts +39 -8
  111. package/src/hooks/registry.ts +66 -17
  112. package/src/hooks/types.ts +1 -1
  113. package/src/i18n.ts +25 -11
  114. package/src/observability/correlation.ts +17 -3
  115. package/src/observability/event-bus.ts +51 -56
  116. package/src/observability/event-to-metric.ts +117 -15
  117. package/src/observability/exporters/otlp-exporter.ts +56 -17
  118. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  119. package/src/observability/metric-registry.ts +16 -4
  120. package/src/observability/metric-retention.ts +8 -2
  121. package/src/observability/metric-sink.ts +11 -3
  122. package/src/observability/metrics-primitives.ts +42 -9
  123. package/src/plugins/plugin-define.ts +2 -2
  124. package/src/plugins/plugin-registry.ts +25 -25
  125. package/src/plugins/plugins/index.ts +1 -1
  126. package/src/plugins/plugins/nextjs.ts +16 -16
  127. package/src/plugins/plugins/vite.ts +7 -11
  128. package/src/plugins/plugins/vitest.ts +6 -11
  129. package/src/runtime/adaptive-plan.ts +225 -40
  130. package/src/runtime/agent-control.ts +58 -15
  131. package/src/runtime/agent-memory.ts +15 -9
  132. package/src/runtime/agent-observability.ts +10 -2
  133. package/src/runtime/anchor-manager.ts +27 -25
  134. package/src/runtime/async-marker.ts +7 -1
  135. package/src/runtime/async-runner.ts +27 -23
  136. package/src/runtime/auto-summarize.ts +7 -13
  137. package/src/runtime/background-runner.ts +189 -251
  138. package/src/runtime/batch-barrier.ts +18 -16
  139. package/src/runtime/cancellation-token.ts +16 -6
  140. package/src/runtime/cancellation.ts +46 -8
  141. package/src/runtime/capability-inventory.ts +6 -4
  142. package/src/runtime/chain-parser.ts +44 -11
  143. package/src/runtime/chain-runner.ts +63 -69
  144. package/src/runtime/checkpoint.ts +12 -56
  145. package/src/runtime/child-pi.ts +343 -78
  146. package/src/runtime/coalesce-tasks.ts +268 -0
  147. package/src/runtime/code-summary.ts +71 -26
  148. package/src/runtime/compact-stages/index.ts +15 -5
  149. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  150. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  151. package/src/runtime/compaction-summary.ts +17 -10
  152. package/src/runtime/completion-guard.ts +33 -16
  153. package/src/runtime/crash-classification.ts +28 -7
  154. package/src/runtime/crash-recovery.ts +177 -37
  155. package/src/runtime/crew-agent-records.ts +145 -47
  156. package/src/runtime/crew-hooks.ts +9 -24
  157. package/src/runtime/cross-extension-rpc.ts +25 -23
  158. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  159. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  160. package/src/runtime/delivery-coordinator.ts +34 -7
  161. package/src/runtime/delta-conflict.ts +9 -21
  162. package/src/runtime/deterministic-ast.ts +1 -1
  163. package/src/runtime/diagnostic-export.ts +53 -20
  164. package/src/runtime/direct-run.ts +12 -2
  165. package/src/runtime/dwf-state-store.ts +1 -1
  166. package/src/runtime/dynamic-workflow-context.ts +186 -58
  167. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  168. package/src/runtime/effectiveness.ts +16 -8
  169. package/src/runtime/errors/crew-errors.ts +7 -11
  170. package/src/runtime/event-stream-bridge.ts +9 -3
  171. package/src/runtime/foreground-control.ts +54 -14
  172. package/src/runtime/foreground-watchdog.ts +9 -6
  173. package/src/runtime/goal-achievement.ts +27 -10
  174. package/src/runtime/goal-evaluator.ts +54 -19
  175. package/src/runtime/goal-loop-runner.ts +280 -99
  176. package/src/runtime/goal-state-store.ts +27 -17
  177. package/src/runtime/green-contract.ts +11 -2
  178. package/src/runtime/group-join.ts +64 -31
  179. package/src/runtime/handoff-manager.ts +37 -42
  180. package/src/runtime/heartbeat-gradient.ts +10 -2
  181. package/src/runtime/heartbeat-watcher.ts +32 -6
  182. package/src/runtime/hidden-handoff.ts +12 -30
  183. package/src/runtime/important-line-classifier.ts +12 -2
  184. package/src/runtime/iteration-hooks.ts +25 -15
  185. package/src/runtime/live-agent-control.ts +55 -7
  186. package/src/runtime/live-agent-manager.ts +130 -27
  187. package/src/runtime/live-control-realtime.ts +23 -3
  188. package/src/runtime/live-irc.ts +4 -1
  189. package/src/runtime/live-session-health.ts +12 -4
  190. package/src/runtime/live-session-runtime.ts +379 -117
  191. package/src/runtime/manifest-cache.ts +28 -12
  192. package/src/runtime/mcp-proxy.ts +8 -16
  193. package/src/runtime/metric-parser.ts +1 -5
  194. package/src/runtime/model-fallback.ts +55 -18
  195. package/src/runtime/model-resolver.ts +2 -6
  196. package/src/runtime/model-scope.ts +19 -3
  197. package/src/runtime/notebook-helpers.ts +60 -62
  198. package/src/runtime/orphan-worker-registry.ts +37 -26
  199. package/src/runtime/output-validator.ts +18 -3
  200. package/src/runtime/overflow-recovery.ts +5 -4
  201. package/src/runtime/parallel-research.ts +29 -5
  202. package/src/runtime/parallel-utils.ts +9 -11
  203. package/src/runtime/path-overlap.ts +150 -0
  204. package/src/runtime/peer-dep.ts +12 -17
  205. package/src/runtime/per-write-validator.ts +1 -3
  206. package/src/runtime/phase-tracker.ts +342 -330
  207. package/src/runtime/pi-args.ts +26 -8
  208. package/src/runtime/pi-json-output.ts +1 -1
  209. package/src/runtime/pi-spawn.ts +30 -19
  210. package/src/runtime/pipeline-runner.ts +56 -55
  211. package/src/runtime/plan-templates.ts +5 -6
  212. package/src/runtime/policy-engine.ts +43 -7
  213. package/src/runtime/post-checks.ts +12 -4
  214. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  215. package/src/runtime/process-lifecycle.ts +20 -10
  216. package/src/runtime/process-status.ts +16 -5
  217. package/src/runtime/progress-event-coalescer.ts +2 -1
  218. package/src/runtime/progress-tracker.ts +103 -103
  219. package/src/runtime/prose-compressor.ts +9 -11
  220. package/src/runtime/recovery-recipes.ts +118 -24
  221. package/src/runtime/replace.ts +25 -10
  222. package/src/runtime/resilient-edit.ts +10 -25
  223. package/src/runtime/result-extractor.ts +1 -2
  224. package/src/runtime/retry-executor.ts +20 -4
  225. package/src/runtime/retry-runner.ts +26 -50
  226. package/src/runtime/role-permission.ts +6 -1
  227. package/src/runtime/run-coalesced-task-group.ts +256 -0
  228. package/src/runtime/run-drift.ts +14 -15
  229. package/src/runtime/run-tracker.ts +6 -28
  230. package/src/runtime/runtime-policy.ts +1 -6
  231. package/src/runtime/runtime-resolver.ts +80 -15
  232. package/src/runtime/scheduler.ts +47 -18
  233. package/src/runtime/semaphore.ts +5 -7
  234. package/src/runtime/sensitive-paths.ts +2 -1
  235. package/src/runtime/session-usage.ts +1 -1
  236. package/src/runtime/settings-store.ts +16 -12
  237. package/src/runtime/sidechain-output.ts +6 -2
  238. package/src/runtime/single-agent-compose.ts +1 -4
  239. package/src/runtime/skill-effectiveness.ts +29 -97
  240. package/src/runtime/skill-instructions.ts +40 -83
  241. package/src/runtime/stale-reconciler.ts +73 -197
  242. package/src/runtime/stream-preview.ts +1 -1
  243. package/src/runtime/streaming-output.ts +1 -1
  244. package/src/runtime/subagent-manager.ts +44 -169
  245. package/src/runtime/subprocess-tool-registry.ts +4 -1
  246. package/src/runtime/supervisor-contact.ts +10 -5
  247. package/src/runtime/task-display.ts +19 -5
  248. package/src/runtime/task-graph-scheduler.ts +95 -21
  249. package/src/runtime/task-graph.ts +5 -11
  250. package/src/runtime/task-health.ts +54 -47
  251. package/src/runtime/task-id.ts +11 -18
  252. package/src/runtime/task-output-context.ts +265 -81
  253. package/src/runtime/task-packet.ts +12 -20
  254. package/src/runtime/task-quality.ts +6 -14
  255. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  256. package/src/runtime/task-runner/live-executor.ts +114 -36
  257. package/src/runtime/task-runner/output-splitter.ts +152 -0
  258. package/src/runtime/task-runner/progress.ts +50 -12
  259. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  260. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  261. package/src/runtime/task-runner/result-utils.ts +3 -1
  262. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  263. package/src/runtime/task-runner/run-projection.ts +27 -8
  264. package/src/runtime/task-runner/state-helpers.ts +29 -8
  265. package/src/runtime/task-runner/tail-read.ts +2 -6
  266. package/src/runtime/task-runner.ts +180 -326
  267. package/src/runtime/team-runner-artifacts.ts +13 -0
  268. package/src/runtime/team-runner.ts +456 -974
  269. package/src/runtime/tool-output-pruner.ts +6 -9
  270. package/src/runtime/tool-progress.ts +11 -21
  271. package/src/runtime/verification-gates.ts +33 -24
  272. package/src/runtime/verification-integrity.ts +1 -4
  273. package/src/runtime/verification-worktree.ts +62 -12
  274. package/src/runtime/worker-heartbeat.ts +5 -1
  275. package/src/runtime/worker-startup.ts +29 -5
  276. package/src/runtime/workflow-state.ts +17 -9
  277. package/src/runtime/workspace-lock.ts +30 -35
  278. package/src/runtime/workspace-tree.ts +20 -32
  279. package/src/runtime/yield-handler.ts +43 -9
  280. package/src/runtime/zombie-scanner.ts +5 -4
  281. package/src/schema/config-schema.ts +266 -172
  282. package/src/schema/team-tool-schema.ts +29 -74
  283. package/src/schema/validation-types.ts +25 -17
  284. package/src/skills/discover-skills.ts +35 -10
  285. package/src/skills/skill-templates.ts +109 -27
  286. package/src/skills/validate.ts +26 -8
  287. package/src/state/active-run-registry.ts +87 -28
  288. package/src/state/artifact-store.ts +9 -5
  289. package/src/state/atomic-write-v2.ts +85 -63
  290. package/src/state/atomic-write.ts +105 -26
  291. package/src/state/blob-store.ts +47 -20
  292. package/src/state/contracts.ts +20 -5
  293. package/src/state/crew-init.ts +5 -22
  294. package/src/state/decision-ledger.ts +19 -73
  295. package/src/state/event-log-rotation.ts +41 -15
  296. package/src/state/event-log.ts +168 -53
  297. package/src/state/event-reconstructor.ts +11 -1
  298. package/src/state/gitignore-manager.ts +2 -8
  299. package/src/state/health-store.ts +57 -57
  300. package/src/state/hook-instinct-bridge.ts +1 -1
  301. package/src/state/hook-integrations.ts +1 -1
  302. package/src/state/instinct-store.ts +17 -5
  303. package/src/state/jsonl-writer.ts +1 -1
  304. package/src/state/locks.ts +23 -9
  305. package/src/state/mailbox.ts +151 -28
  306. package/src/state/observation-store.ts +20 -14
  307. package/src/state/run-cache.ts +142 -135
  308. package/src/state/run-graph.ts +6 -15
  309. package/src/state/run-metrics.ts +5 -18
  310. package/src/state/schedule.ts +15 -9
  311. package/src/state/state-store.ts +129 -45
  312. package/src/state/task-claims.ts +12 -2
  313. package/src/state/tiered-eval.ts +52 -43
  314. package/src/state/types-eval.ts +2 -2
  315. package/src/state/types.ts +24 -20
  316. package/src/state/usage.ts +20 -4
  317. package/src/state/worker-atomic-writer.ts +6 -3
  318. package/src/subagents/index.ts +2 -2
  319. package/src/teams/discover-teams.ts +21 -6
  320. package/src/tools/safe-bash-extension.ts +5 -6
  321. package/src/tools/safe-bash.ts +10 -11
  322. package/src/types/new-api-types.ts +6 -10
  323. package/src/ui/agent-management-overlay.ts +52 -40
  324. package/src/ui/card-colors.ts +13 -4
  325. package/src/ui/crew-footer.ts +8 -7
  326. package/src/ui/crew-select-list.ts +1 -1
  327. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  328. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  329. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  330. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  331. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  332. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  333. package/src/ui/heartbeat-aggregator.ts +15 -4
  334. package/src/ui/keybinding-map.ts +40 -7
  335. package/src/ui/live-conversation-overlay.ts +36 -20
  336. package/src/ui/live-duration.ts +1 -4
  337. package/src/ui/live-run-sidebar.ts +88 -25
  338. package/src/ui/loaders.ts +2 -8
  339. package/src/ui/mascot.ts +20 -36
  340. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  341. package/src/ui/overlays/confirm-overlay.ts +4 -2
  342. package/src/ui/overlays/help-overlay.ts +25 -14
  343. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  344. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  345. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  346. package/src/ui/pi-ui-compat.ts +7 -7
  347. package/src/ui/powerbar-publisher.ts +120 -42
  348. package/src/ui/render-diff.ts +10 -3
  349. package/src/ui/render-scheduler.ts +12 -4
  350. package/src/ui/run-action-dispatcher.ts +81 -18
  351. package/src/ui/run-dashboard.ts +172 -76
  352. package/src/ui/run-event-bus.ts +59 -37
  353. package/src/ui/run-snapshot-cache.ts +276 -86
  354. package/src/ui/settings-overlay.ts +361 -74
  355. package/src/ui/status-colors.ts +12 -1
  356. package/src/ui/syntax-highlight.ts +1 -1
  357. package/src/ui/theme-adapter.ts +16 -14
  358. package/src/ui/theme-discovery.ts +13 -2
  359. package/src/ui/tool-progress-formatter.ts +7 -7
  360. package/src/ui/tool-render.ts +128 -56
  361. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  362. package/src/ui/tool-renderers/index.ts +109 -43
  363. package/src/ui/transcript-cache.ts +32 -6
  364. package/src/ui/transcript-entries.ts +25 -23
  365. package/src/ui/transcript-viewer.ts +78 -29
  366. package/src/ui/widget/index.ts +99 -40
  367. package/src/ui/widget/widget-formatters.ts +13 -6
  368. package/src/ui/widget/widget-model.ts +19 -8
  369. package/src/ui/widget/widget-renderer.ts +23 -13
  370. package/src/ui/widget/widget-types.ts +1 -1
  371. package/src/utils/bm25-search.ts +199 -199
  372. package/src/utils/conflict-detect.ts +22 -21
  373. package/src/utils/env-filter.ts +14 -7
  374. package/src/utils/file-coalescer.ts +5 -1
  375. package/src/utils/fingerprint.ts +3 -6
  376. package/src/utils/frontmatter.ts +3 -1
  377. package/src/utils/fs-watch.ts +2 -6
  378. package/src/utils/gh-protocol.ts +119 -44
  379. package/src/utils/git.ts +13 -15
  380. package/src/utils/guards.ts +2 -5
  381. package/src/utils/ids.ts +9 -2
  382. package/src/utils/incremental-reader.ts +14 -3
  383. package/src/utils/internal-error.ts +2 -1
  384. package/src/utils/names.ts +12 -3
  385. package/src/utils/paths.ts +49 -5
  386. package/src/utils/project-detector.ts +2 -2
  387. package/src/utils/redaction.ts +29 -19
  388. package/src/utils/resolve-shell.ts +9 -7
  389. package/src/utils/run-watcher-registry.ts +19 -31
  390. package/src/utils/safe-paths.ts +45 -24
  391. package/src/utils/scan-cache.ts +9 -2
  392. package/src/utils/session-utils.ts +2 -4
  393. package/src/utils/sleep.ts +12 -5
  394. package/src/utils/sse-parser.ts +5 -17
  395. package/src/utils/visual.ts +52 -32
  396. package/src/workflows/discover-workflows.ts +41 -109
  397. package/src/workflows/intermediate-store.ts +5 -21
  398. package/src/workflows/preflight-validator.ts +6 -33
  399. package/src/workflows/topology-analyzer.ts +5 -21
  400. package/src/workflows/workflow-config.ts +7 -6
  401. package/src/worktree/branch-freshness.ts +66 -8
  402. package/src/worktree/cleanup.ts +130 -20
  403. package/src/worktree/worktree-manager.ts +212 -69
  404. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  405. package/skills/detection-pipeline-design/SKILL.md +0 -286
  406. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  407. package/skills/incident-playbook-construction/SKILL.md +0 -384
  408. package/skills/security-review/SKILL.md +0 -561
  409. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  410. package/skills/ui-render-performance/SKILL.md +0 -58
  411. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  412. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  413. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  414. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  415. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  416. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  417. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  418. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  419. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  420. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  421. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  422. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  430. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  431. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  432. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -3,26 +3,16 @@ import * as path from "node:path";
3
3
  import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
4
4
  import { loadConfig } from "../config/config.ts";
5
5
  import { appendEvent } from "../state/event-log.ts";
6
- import {
7
- withRunLockSync,
8
- } from "../state/locks.ts";
9
- import {
10
- createRunPaths,
11
- loadRunManifestById,
12
- saveRunManifest,
13
- updateRunStatus,
14
- } from "../state/state-store.ts";
6
+ import { withRunLockSync } from "../state/locks.ts";
7
+ import { createRunPaths, loadRunManifestById, saveRunManifest, updateRunStatus } from "../state/state-store.ts";
8
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
15
9
  import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
16
10
  import { projectCrewRoot } from "../utils/paths.ts";
17
- import {
18
- allWorkflows,
19
- discoverWorkflows,
20
- } from "../workflows/discover-workflows.ts";
11
+ import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
21
12
  // Heavy runtime — lazy-loaded to avoid pulling team-runner into background-runner
22
13
  // at module load time. Only needed when a background run actually starts.
23
14
  import { primePeerDep } from "./peer-dep.ts";
24
15
  import type { executeTeamRun as ExecuteTeamRunFn } from "./team-runner.ts";
25
- import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
26
16
 
27
17
  let _cachedExecuteTeamRun: typeof ExecuteTeamRunFn | undefined;
28
18
 
@@ -38,9 +28,7 @@ const MAX_BACKGROUND_RUN_MS = (() => {
38
28
  const env = Number.parseInt(process.env.PI_CREW_MAX_RUN_MS ?? "", 10);
39
29
  return Number.isFinite(env) && env > 0 ? env : 2 * 60 * 60 * 1000;
40
30
  })();
41
- async function executeTeamRun(
42
- ...args: Parameters<typeof ExecuteTeamRunFn>
43
- ): Promise<Awaited<ReturnType<typeof ExecuteTeamRunFn>>> {
31
+ async function executeTeamRun(...args: Parameters<typeof ExecuteTeamRunFn>): Promise<Awaited<ReturnType<typeof ExecuteTeamRunFn>>> {
44
32
  if (!_cachedExecuteTeamRun) {
45
33
  // FIX (split-scope install): prime the ESM peer dep BEFORE team-runner is
46
34
  // imported, so its transitive skill-instructions.ts can read getAgentDir()
@@ -56,14 +44,11 @@ async function executeTeamRun(
56
44
  import { logInternalError } from "../utils/internal-error.ts";
57
45
  import { writeAsyncStartMarker } from "./async-marker.ts";
58
46
  import { terminateActiveChildPiProcesses } from "./child-pi.ts";
59
- import { unregisterWorker } from "./orphan-worker-registry.ts";
60
47
  import { directTeamAndWorkflowFromRun } from "./direct-run.ts";
48
+ import { unregisterWorker } from "./orphan-worker-registry.ts";
61
49
  import { expandParallelResearchWorkflow } from "./parallel-research.ts";
62
50
  import { startParentGuard, stopParentGuard } from "./parent-guard.ts";
63
- import {
64
- resolveCrewRuntime,
65
- runtimeResolutionState,
66
- } from "./runtime-resolver.ts";
51
+ import { resolveCrewRuntime, runtimeResolutionState } from "./runtime-resolver.ts";
67
52
 
68
53
  /**
69
54
  * Debug logger gated behind PI_CREW_DEBUG env var. Writes to background.log
@@ -80,11 +65,7 @@ function debugLog(message: string): void {
80
65
  * Without this, the reconciler relies solely on process.kill(pid, 0) which can
81
66
  * false-positive when a process is SIGKILLed and the PID hasn't been recycled yet.
82
67
  */
83
- function startHeartbeat(
84
- stateRoot: string,
85
- eventsPath: string,
86
- runId: string,
87
- ): () => void {
68
+ function startHeartbeat(stateRoot: string, eventsPath: string, runId: string): () => void {
88
69
  const heartbeatPath = path.join(stateRoot, "heartbeat.json");
89
70
  const writeHeartbeat = (): void => {
90
71
  try {
@@ -133,36 +114,29 @@ function startInterruptGuard(
133
114
  abortController: AbortController,
134
115
  stopParentGuard: () => void,
135
116
  ): () => void {
136
- const controlPath = path.join(
137
- manifest.stateRoot,
138
- "foreground-control.json",
139
- );
117
+ const controlPath = path.join(manifest.stateRoot, "foreground-control.json");
140
118
  // FIX: Made configurable via PI_CREW_INTERRUPT_GUARD_INTERVAL_MS env var.
141
119
  // Default 250ms balances fast SIGINT response against filesystem overhead.
142
- const interruptGuardInterval =
143
- Number(process.env.PI_CREW_INTERRUPT_GUARD_INTERVAL_MS) || 250;
120
+ const interruptGuardInterval = Number(process.env.PI_CREW_INTERRUPT_GUARD_INTERVAL_MS) || 250;
144
121
  const interval = setInterval(() => {
145
122
  try {
146
123
  if (!fs.existsSync(controlPath)) return;
147
- const parsed = JSON.parse(
148
- fs.readFileSync(controlPath, "utf-8"),
149
- ) as { requests?: Array<{ type: string; acknowledged?: boolean }> };
124
+ const parsed = JSON.parse(fs.readFileSync(controlPath, "utf-8")) as {
125
+ requests?: Array<{ type: string; acknowledged?: boolean }>;
126
+ };
150
127
  const last = parsed.requests?.at(-1);
151
128
  if (last?.type === "interrupt" && last?.acknowledged !== true) {
152
129
  appendEvent(manifest.eventsPath, {
153
130
  type: "async.interrupt_detected",
154
131
  runId: manifest.runId,
155
- message:
156
- "Background runner detected foreground interrupt — killing child processes and exiting.",
132
+ message: "Background runner detected foreground interrupt — killing child processes and exiting.",
157
133
  });
158
134
  // FIX: Terminate ALL child-pi processes IMMEDIATELY before exiting.
159
135
  // Previously this was missing, causing orphaned child processes to run forever
160
136
  // after the background-runner exited. terminateActiveChildPiProcesses sends
161
137
  // SIGTERM then SIGKILL (after HARD_KILL_MS=3s) to every active child.
162
138
  const killed = terminateActiveChildPiProcesses();
163
- console.log(
164
- `[background-runner] interrupt: killed ${killed} child processes`,
165
- );
139
+ console.log(`[background-runner] interrupt: killed ${killed} child processes`);
166
140
  // Also abort the run signal so executeTeamRun exits quickly via its signal check.
167
141
  abortController.abort();
168
142
  // FIX Issue #1: Call stopParentGuard() here since process.exit(130) bypasses
@@ -201,13 +175,9 @@ function setupUnhandledRejectionGuard(
201
175
  setExitFlag: () => void,
202
176
  ): void {
203
177
  process.on("unhandledRejection", (reason, promise) => {
204
- const message =
205
- reason instanceof Error ? reason.message : String(reason);
178
+ const message = reason instanceof Error ? reason.message : String(reason);
206
179
  console.error("[background-runner] UNHANDLED REJECTION:", reason);
207
- console.error(
208
- "[background-runner] Stack:",
209
- reason instanceof Error ? reason.stack : "N/A",
210
- );
180
+ console.error("[background-runner] Stack:", reason instanceof Error ? reason.stack : "N/A");
211
181
  try {
212
182
  if (state.eventsPath && state.runId) {
213
183
  appendEvent(state.eventsPath, {
@@ -216,17 +186,13 @@ function setupUnhandledRejectionGuard(
216
186
  message: `Unhandled rejection: ${message}`,
217
187
  data: {
218
188
  reason: String(reason),
219
- stack:
220
- reason instanceof Error ? reason.stack : undefined,
189
+ stack: reason instanceof Error ? reason.stack : undefined,
221
190
  handled: false,
222
191
  },
223
192
  });
224
193
  }
225
194
  } catch (appendErr) {
226
- console.error(
227
- "[background-runner] Failed to write async.failed event:",
228
- appendErr,
229
- );
195
+ console.error("[background-runner] Failed to write async.failed event:", appendErr);
230
196
  }
231
197
  // FIX Issues #2& #4: Signal child processes to terminate via abortController,
232
198
  // set the exit flag so main() exits after the finally block runs cleanup.
@@ -251,8 +217,7 @@ function runCleanup(
251
217
  exitDueToRejection: boolean,
252
218
  eventsPath?: string,
253
219
  ): void {
254
- console.log(`[background-runner] runCleanup, exitDueToRejection=${exitDueToRejection}`,
255
- );
220
+ console.log(`[background-runner] runCleanup, exitDueToRejection=${exitDueToRejection}`);
256
221
  stopInterruptGuard();
257
222
  stopParentGuard();
258
223
  stopHeartbeat();
@@ -280,9 +245,7 @@ function runCleanup(
280
245
  try {
281
246
  unregisterWorker(process.pid);
282
247
  } catch (error) {
283
- console.log(
284
- `[background-runner] runCleanup: unregisterWorker error: ${error instanceof Error ? error.message : String(error)}`,
285
- );
248
+ console.log(`[background-runner] runCleanup: unregisterWorker error: ${error instanceof Error ? error.message : String(error)}`);
286
249
  if (eventsPath) {
287
250
  try {
288
251
  appendEvent(eventsPath, {
@@ -330,13 +293,7 @@ async function main(): Promise<void> {
330
293
  // Use projectCrewRoot() so the background log lives next to the
331
294
  // manifest in either .crew/state/runs/ or .pi/teams/state/runs/
332
295
  // depending on the project's chosen layout (issue #29).
333
- const logPath = path.join(
334
- projectCrewRoot(_cwd),
335
- "state",
336
- "runs",
337
- _runId,
338
- "background.log",
339
- );
296
+ const logPath = path.join(projectCrewRoot(_cwd), "state", "runs", _runId, "background.log");
340
297
  logFd = fs.openSync(logPath, "a");
341
298
  const origWrite =
342
299
  (_prefix: string) =>
@@ -398,23 +355,14 @@ async function main(): Promise<void> {
398
355
  const runId = argValue("--run-id");
399
356
  if (!cwd || !runId) return undefined;
400
357
  // Use projectCrewRoot() to honour the .pi/teams/ fallback (issue #29).
401
- return path.join(
402
- projectCrewRoot(cwd),
403
- "state",
404
- "runs",
405
- runId,
406
- "exit-code.txt",
407
- );
358
+ return path.join(projectCrewRoot(cwd), "state", "runs", runId, "exit-code.txt");
408
359
  })();
409
360
  if (exitCodePath) {
410
361
  process.on("exit", (code) => {
411
362
  // Only log non-zero exit codes to avoid noise in exit-code.txt
412
363
  if (code === 0 || code === undefined) return;
413
364
  try {
414
- fs.appendFileSync(
415
- exitCodePath,
416
- `${new Date().toISOString()} exit_code=${code} pid=${process.pid}\n`,
417
- );
365
+ fs.appendFileSync(exitCodePath, `${new Date().toISOString()} exit_code=${code} pid=${process.pid}\n`);
418
366
  } catch {}
419
367
  });
420
368
  }
@@ -423,10 +371,7 @@ async function main(): Promise<void> {
423
371
  // are installed, since the handlers reference manifest.eventsPath and abortController.
424
372
  const cwd = argValue("--cwd");
425
373
  const runId = argValue("--run-id");
426
- if (!cwd || !runId)
427
- throw new Error(
428
- "Usage: background-runner.ts --cwd <cwd> --run-id <runId>",
429
- );
374
+ if (!cwd || !runId) throw new Error("Usage: background-runner.ts --cwd <cwd> --run-id <runId>");
430
375
  // FIX Issue #3: Wrap in withRunLockSync to prevent concurrent background-runners
431
376
  // for the same runId from reading stale manifest state. If lock cannot be
432
377
  // be acquired within 5s, fail immediately rather than proceeding with stale data.
@@ -442,11 +387,9 @@ async function main(): Promise<void> {
442
387
  const bootstrapStateRoot = createRunPaths(cwd, runId).stateRoot;
443
388
  let loaded: { manifest: TeamRunManifest; tasks: TeamTaskState[] } | undefined;
444
389
  try {
445
- loaded = withRunLockSync(
446
- { stateRoot: bootstrapStateRoot, runId, cwd } as TeamRunManifest,
447
- () => loadRunManifestById(cwd, runId),
448
- { staleMs: 30_000 },
449
- );
390
+ loaded = withRunLockSync({ stateRoot: bootstrapStateRoot, runId, cwd } as TeamRunManifest, () => loadRunManifestById(cwd, runId), {
391
+ staleMs: 30_000,
392
+ });
450
393
  } catch (lockErr) {
451
394
  throw new Error(`Failed to acquire lock for run '${runId}': ${lockErr instanceof Error ? lockErr.message : String(lockErr)}`);
452
395
  }
@@ -560,17 +503,12 @@ async function main(): Promise<void> {
560
503
  runId: manifest.runId,
561
504
  data: { pid: process.pid },
562
505
  });
563
- debugLog(`[background-runner] async.started written, pid=${process.pid}`,
564
- );
506
+ debugLog(`[background-runner] async.started written, pid=${process.pid}`);
565
507
  writeAsyncStartMarker(manifest, {
566
508
  pid: process.pid,
567
509
  startedAt: new Date().toISOString(),
568
510
  });
569
- const stopHeartbeat = startHeartbeat(
570
- manifest.stateRoot,
571
- manifest.eventsPath,
572
- manifest.runId,
573
- );
511
+ const stopHeartbeat = startHeartbeat(manifest.stateRoot, manifest.eventsPath, manifest.runId);
574
512
  const stopInterruptGuard = startInterruptGuard(manifest, abortController, stopParentGuard);
575
513
  debugLog(`[background-runner] heartbeat+interrupt guard started`);
576
514
  // NOTE: Keep-alive interval is NOT unref'd (unlike heartbeat and interrupt
@@ -594,7 +532,9 @@ async function main(): Promise<void> {
594
532
  message: `Run exceeded ${MAX_BACKGROUND_RUN_MS}ms and was force-aborted to prevent a zombie background-runner process.`,
595
533
  data: { maxRunMs: MAX_BACKGROUND_RUN_MS },
596
534
  });
597
- } catch { /* best-effort event log */ }
535
+ } catch {
536
+ /* best-effort event log */
537
+ }
598
538
  // Signal the finally block to exit(1) after cleanup.
599
539
  exitDueToRejection = true;
600
540
  // Abort the in-flight team run via the shared signal (propagates to
@@ -605,7 +545,11 @@ async function main(): Promise<void> {
605
545
  const forceExit = setTimeout(() => {
606
546
  console.error(`[background-runner] WATCHDOG: abort did not propagate within grace period — force-exiting`);
607
547
  stopParentGuard();
608
- try { terminateActiveChildPiProcesses(); } catch { /* best-effort */ }
548
+ try {
549
+ terminateActiveChildPiProcesses();
550
+ } catch {
551
+ /* best-effort */
552
+ }
609
553
  process.exit(1);
610
554
  }, 15_000);
611
555
  forceExit.unref();
@@ -614,15 +558,24 @@ async function main(): Promise<void> {
614
558
  try {
615
559
  debugLog(`[background-runner] about to call discoverAgents`);
616
560
  const agents = allAgents(discoverAgents(cwd));
617
- debugLog(`[background-runner] discoverAgents done, ${agents.length} agents`,
618
- );
561
+ debugLog(`[background-runner] discoverAgents done, ${agents.length} agents`);
619
562
  // Round 27 (BUG 2): openSync returned an fd that was never closed → FD
620
563
  // leak per background runner startup. Close it in a finally (matches the
621
564
  // canonical pattern in checkpoint.ts:83 and event-log.ts:582).
622
565
  try {
623
566
  const fd = fs.openSync(manifest.eventsPath, "a");
624
- try { fs.fsyncSync(fd); } finally { try { fs.closeSync(fd); } catch { /* best-effort */ } }
625
- } catch { /* best-effort */ } // FORCE flush so we see this before death
567
+ try {
568
+ fs.fsyncSync(fd);
569
+ } finally {
570
+ try {
571
+ fs.closeSync(fd);
572
+ } catch {
573
+ /* best-effort */
574
+ }
575
+ }
576
+ } catch {
577
+ /* best-effort */
578
+ } // FORCE flush so we see this before death
626
579
  // Fix round-4 CRITICAL: goal-loop and dynamic-workflow manifests use SYNTHETIC
627
580
  // team/workflow names not in discoverTeams/discoverWorkflows. The team+workflow
628
581
  // lookup below would throw "Team not found" BEFORE the runKind switch, making the
@@ -631,37 +584,64 @@ async function main(): Promise<void> {
631
584
  let earlyResult: { manifest: TeamRunManifest; tasks: TeamTaskState[] } | undefined;
632
585
  let result: { manifest: TeamRunManifest; tasks: TeamTaskState[] } | undefined;
633
586
  if (manifest.runKind === "goal-loop" || manifest.runKind === "dynamic-workflow") {
634
- debugLog(`[background-runner] short-circuiting ${manifest.runKind} (synthetic team/workflow)`,
635
- );
587
+ debugLog(`[background-runner] short-circuiting ${manifest.runKind} (synthetic team/workflow)`);
636
588
  if (manifest.runKind === "goal-loop") {
637
- // LAZY: defer dynamic import of ./goal-loop-runner.ts to its call site.
589
+ // LAZY: defer dynamic import of ./goal-loop-runner.ts to its call site.
638
590
  const { runGoalLoop } = await import("./goal-loop-runner.ts");
639
- // LAZY: defer dynamic import of ./goal-state-store.ts to its call site.
591
+ // LAZY: defer dynamic import of ./goal-state-store.ts to its call site.
640
592
  const { GoalStore } = await import("./goal-state-store.ts");
641
- // LAZY: defer dynamic import of ../agents/discover-agents.ts to its call site.
593
+ // LAZY: defer dynamic import of ../agents/discover-agents.ts to its call site.
642
594
  const { discoverAgents, allAgents } = await import("../agents/discover-agents.ts");
643
595
  const store = new GoalStore(manifest.cwd);
644
596
  const goalState = store.load(manifest.runId);
645
- if (!goalState) throw new Error(`runKind="goal-loop" but GoalLoopState '${manifest.runId}' not found (cwd=${manifest.cwd})`);
646
- const goalResult = await runGoalLoop({ goalState, manifest, signal: abortController.signal, deps: { discoverAgents: (c: string) => allAgents(discoverAgents(c)) } });
597
+ if (!goalState)
598
+ throw new Error(`runKind="goal-loop" but GoalLoopState '${manifest.runId}' not found (cwd=${manifest.cwd})`);
599
+ const goalResult = await runGoalLoop({
600
+ goalState,
601
+ manifest,
602
+ signal: abortController.signal,
603
+ deps: {
604
+ discoverAgents: (c: string) => allAgents(discoverAgents(c)),
605
+ },
606
+ });
647
607
  // Fix P1-1 + round-6 #5: persist terminal status reflecting the goal's actual outcome,
648
608
  // not a blanket 'completed'. Map goal state → manifest status.
649
609
  const goalStatusToRunStatus: Record<string, TeamRunManifest["status"]> = {
650
- achieved: "completed", max_turns: "completed", budget_exceeded: "completed",
651
- blocked: "blocked", cancelled: "cancelled", paused: "blocked", running: "running",
610
+ achieved: "completed",
611
+ max_turns: "completed",
612
+ budget_exceeded: "completed",
613
+ blocked: "blocked",
614
+ cancelled: "cancelled",
615
+ paused: "blocked",
616
+ running: "running",
652
617
  };
653
618
  const runStatus = goalStatusToRunStatus[goalResult.goalState.state] ?? "completed";
654
- const finalGoalManifest: TeamRunManifest = { ...goalResult.manifest, status: runStatus, updatedAt: new Date().toISOString() };
619
+ const finalGoalManifest: TeamRunManifest = {
620
+ ...goalResult.manifest,
621
+ status: runStatus,
622
+ updatedAt: new Date().toISOString(),
623
+ };
655
624
  saveRunManifest(finalGoalManifest);
656
- earlyResult = { manifest: finalGoalManifest, tasks: goalResult.tasks };
625
+ earlyResult = {
626
+ manifest: finalGoalManifest,
627
+ tasks: goalResult.tasks,
628
+ };
657
629
  } else {
658
- // LAZY: defer dynamic import of ./dynamic-workflow-runner.ts to its call site.
630
+ // LAZY: defer dynamic import of ./dynamic-workflow-runner.ts to its call site.
659
631
  const { runDynamicWorkflow } = await import("./dynamic-workflow-runner.ts");
660
- // LAZY: defer dynamic import of ../workflows/discover-workflows.ts to its call site.
632
+ // LAZY: defer dynamic import of ../workflows/discover-workflows.ts to its call site.
661
633
  const { allWorkflows, discoverWorkflows } = await import("../workflows/discover-workflows.ts");
662
634
  const wf = allWorkflows(discoverWorkflows(manifest.cwd)).find((w) => w.name === manifest.workflow);
663
- if (!wf || wf.runtime !== "dynamic" || !wf.dynamicScript) throw new Error(`runKind="dynamic-workflow" but workflow '${manifest.workflow}' is not dynamic (runId=${manifest.runId})`);
664
- const dwfResult = await runDynamicWorkflow({ manifest, workflow: wf as import("../workflows/workflow-config.ts").DynamicWorkflowConfig, signal: abortController.signal, tokenBudget: wf.maxTokenBudget });
635
+ if (!wf || wf.runtime !== "dynamic" || !wf.dynamicScript)
636
+ throw new Error(
637
+ `runKind="dynamic-workflow" but workflow '${manifest.workflow}' is not dynamic (runId=${manifest.runId})`,
638
+ );
639
+ const dwfResult = await runDynamicWorkflow({
640
+ manifest,
641
+ workflow: wf as import("../workflows/workflow-config.ts").DynamicWorkflowConfig,
642
+ signal: abortController.signal,
643
+ tokenBudget: wf.maxTokenBudget,
644
+ });
665
645
  saveRunManifest(dwfResult.manifest);
666
646
  earlyResult = dwfResult;
667
647
  }
@@ -669,115 +649,97 @@ async function main(): Promise<void> {
669
649
  result = earlyResult;
670
650
  }
671
651
  if (!earlyResult) {
672
- debugLog(`[background-runner] calling directTeamAndWorkflowFromRun`,
673
- );
674
- const direct = directTeamAndWorkflowFromRun(manifest, tasks, agents);
675
- debugLog(`[background-runner] direct done, finding team`);
676
- const team =
677
- direct?.team ??
678
- allTeams(discoverTeams(cwd)).find(
679
- (candidate) => candidate.name === manifest.team,
680
- );
681
- if (!team) throw new Error(`Team '${manifest.team}' not found.`);
682
- debugLog(`[background-runner] team=${team.name}, finding workflow`,
683
- );
684
- const baseWorkflow =
685
- direct?.workflow ??
686
- allWorkflows(discoverWorkflows(cwd)).find(
687
- (candidate) => candidate.name === manifest.workflow,
688
- );
689
- if (!baseWorkflow)
690
- throw new Error(`Workflow '${manifest.workflow ?? ""}' not found.`);
691
- debugLog(`[background-runner] workflow=${baseWorkflow.name}`);
692
- const workflow = expandParallelResearchWorkflow(baseWorkflow, cwd);
693
- debugLog(`[background-runner] loading config`);
694
- const loadedConfig = loadConfig(cwd);
695
- const runConfig =
696
- manifest.runConfig &&
697
- typeof manifest.runConfig === "object" &&
698
- !Array.isArray(manifest.runConfig)
699
- ? (manifest.runConfig as typeof loadedConfig.config)
700
- : loadedConfig.config;
701
- const runtime = manifest.runtimeResolution
702
- ? {
703
- kind: manifest.runtimeResolution.kind,
704
- requestedMode: manifest.runtimeResolution.requestedMode,
705
- available: manifest.runtimeResolution.available,
706
- fallback: manifest.runtimeResolution.fallback,
707
- steer: manifest.runtimeResolution.kind === "live-session",
708
- resume: manifest.runtimeResolution.kind === "live-session",
709
- liveToolActivity:
710
- manifest.runtimeResolution.kind === "live-session",
711
- transcript: manifest.runtimeResolution.kind !== "scaffold",
712
- reason: manifest.runtimeResolution.reason,
713
- safety: manifest.runtimeResolution.safety,
714
- }
715
- : await resolveCrewRuntime(runConfig);
716
- const runtimeResolution =
717
- manifest.runtimeResolution ?? runtimeResolutionState(runtime);
718
- manifest = {
719
- ...manifest,
720
- runtimeResolution,
721
- runConfig,
722
- updatedAt: new Date().toISOString(),
723
- };
724
- saveRunManifest(manifest);
725
- appendEvent(manifest.eventsPath, {
726
- type: "runtime.resolved",
727
- runId: manifest.runId,
728
- message: `Runtime resolved: ${runtime.kind} safety=${runtime.safety}`,
729
- data: { runtimeResolution, async: true },
730
- });
731
- if (runtime.safety === "blocked")
732
- throw new Error(
733
- runtime.reason ??
734
- "Child worker execution is disabled; refusing to create no-op scaffold subagents.",
735
- );
736
- const executeWorkers = runtime.kind !== "scaffold";
737
- // Use ownerSessionId for workspaceId to ensure agents are only visible to the session that spawned them.
738
- // manifest.cwd would cause cross-session visibility since all sessions share the same project directory.
739
- // Mark this as background mode so task-runner writes events to background.log for debugging.
740
- process.env.PI_CREW_BACKGROUND_MODE = "1";
741
- // BUG #17: Keep-alive interval (NOT unref'd) prevents event loop from exiting
742
- // during jiti compilation of team-runner.ts. Without this, the event loop
743
- // can drain when import() blocks, causing the process to exit prematurely.
744
- // NOTE: abortController is already created above (before heartbeat/interrupt guard start)
745
- // so it is available here and its signal is passed through to executeTeamRun → child-pi.
652
+ debugLog(`[background-runner] calling directTeamAndWorkflowFromRun`);
653
+ const direct = directTeamAndWorkflowFromRun(manifest, tasks, agents);
654
+ debugLog(`[background-runner] direct done, finding team`);
655
+ const team = direct?.team ?? allTeams(discoverTeams(cwd)).find((candidate) => candidate.name === manifest.team);
656
+ if (!team) throw new Error(`Team '${manifest.team}' not found.`);
657
+ debugLog(`[background-runner] team=${team.name}, finding workflow`);
658
+ const baseWorkflow =
659
+ direct?.workflow ?? allWorkflows(discoverWorkflows(cwd)).find((candidate) => candidate.name === manifest.workflow);
660
+ if (!baseWorkflow) throw new Error(`Workflow '${manifest.workflow ?? ""}' not found.`);
661
+ debugLog(`[background-runner] workflow=${baseWorkflow.name}`);
662
+ const workflow = expandParallelResearchWorkflow(baseWorkflow, cwd);
663
+ debugLog(`[background-runner] loading config`);
664
+ const loadedConfig = loadConfig(cwd);
665
+ const runConfig =
666
+ manifest.runConfig && typeof manifest.runConfig === "object" && !Array.isArray(manifest.runConfig)
667
+ ? (manifest.runConfig as typeof loadedConfig.config)
668
+ : loadedConfig.config;
669
+ const runtime = manifest.runtimeResolution
670
+ ? {
671
+ kind: manifest.runtimeResolution.kind,
672
+ requestedMode: manifest.runtimeResolution.requestedMode,
673
+ available: manifest.runtimeResolution.available,
674
+ fallback: manifest.runtimeResolution.fallback,
675
+ steer: manifest.runtimeResolution.kind === "live-session",
676
+ resume: manifest.runtimeResolution.kind === "live-session",
677
+ liveToolActivity: manifest.runtimeResolution.kind === "live-session",
678
+ transcript: manifest.runtimeResolution.kind !== "scaffold",
679
+ reason: manifest.runtimeResolution.reason,
680
+ safety: manifest.runtimeResolution.safety,
681
+ }
682
+ : await resolveCrewRuntime(runConfig);
683
+ const runtimeResolution = manifest.runtimeResolution ?? runtimeResolutionState(runtime);
684
+ manifest = {
685
+ ...manifest,
686
+ runtimeResolution,
687
+ runConfig,
688
+ updatedAt: new Date().toISOString(),
689
+ };
690
+ saveRunManifest(manifest);
691
+ appendEvent(manifest.eventsPath, {
692
+ type: "runtime.resolved",
693
+ runId: manifest.runId,
694
+ message: `Runtime resolved: ${runtime.kind} safety=${runtime.safety}`,
695
+ data: { runtimeResolution, async: true },
696
+ });
697
+ if (runtime.safety === "blocked")
698
+ throw new Error(runtime.reason ?? "Child worker execution is disabled; refusing to create no-op scaffold subagents.");
699
+ const executeWorkers = runtime.kind !== "scaffold";
700
+ // Use ownerSessionId for workspaceId to ensure agents are only visible to the session that spawned them.
701
+ // manifest.cwd would cause cross-session visibility since all sessions share the same project directory.
702
+ // Mark this as background mode so task-runner writes events to background.log for debugging.
703
+ process.env.PI_CREW_BACKGROUND_MODE = "1";
704
+ // BUG #17: Keep-alive interval (NOT unref'd) prevents event loop from exiting
705
+ // during jiti compilation of team-runner.ts. Without this, the event loop
706
+ // can drain when import() blocks, causing the process to exit prematurely.
707
+ // NOTE: abortController is already created above (before heartbeat/interrupt guard start)
708
+ // so it is available here and its signal is passed through to executeTeamRun → child-pi.
746
709
 
747
- debugLog(`[background-runner] dispatching runKind=${manifest.runKind ?? "team-run"}`);
748
- try {
749
- // Fix round-4: goal-loop/dynamic-workflow handled by the short-circuit above.
750
- // This switch now only carries the traditional team-run path.
751
- switch (manifest.runKind ?? "team-run") {
752
- default: {
753
- // Existing "team-run" path — unchanged behavior.
754
- result = await executeTeamRun({
755
- manifest,
756
- tasks,
757
- team,
758
- workflow,
759
- agents,
760
- executeWorkers,
761
- limits: runConfig.limits,
762
- runtime,
763
- runtimeConfig: runConfig.runtime,
764
- skillOverride: manifest.skillOverride,
765
- reliability: runConfig.reliability,
766
- workspaceId: manifest.ownerSessionId ?? manifest.cwd,
767
- signal: abortController.signal,
768
- });
769
- break;
710
+ debugLog(`[background-runner] dispatching runKind=${manifest.runKind ?? "team-run"}`);
711
+ try {
712
+ // Fix round-4: goal-loop/dynamic-workflow handled by the short-circuit above.
713
+ // This switch now only carries the traditional team-run path.
714
+ switch (manifest.runKind ?? "team-run") {
715
+ default: {
716
+ // Existing "team-run" path — unchanged behavior.
717
+ result = await executeTeamRun({
718
+ manifest,
719
+ tasks,
720
+ team,
721
+ workflow,
722
+ agents,
723
+ executeWorkers,
724
+ limits: runConfig.limits,
725
+ runtime,
726
+ runtimeConfig: runConfig.runtime,
727
+ skillOverride: manifest.skillOverride,
728
+ reliability: runConfig.reliability,
729
+ workspaceId: manifest.ownerSessionId ?? manifest.cwd,
730
+ signal: abortController.signal,
731
+ });
732
+ break;
733
+ }
770
734
  }
735
+ console.log(`[background-runner] executeTeamRun returned, status=${result.manifest.status}`);
736
+ } catch (execError) {
737
+ console.log(
738
+ `[background-runner] executeTeamRun THREW: ${execError instanceof Error ? execError.message : String(execError)}`,
739
+ );
740
+ console.log(`[background-runner] stack: ${execError instanceof Error ? execError.stack : "N/A"}`);
741
+ throw execError;
771
742
  }
772
- console.log(`[background-runner] executeTeamRun returned, status=${result.manifest.status}`,
773
- );
774
- } catch (execError) {
775
- console.log(`[background-runner] executeTeamRun THREW: ${execError instanceof Error ? execError.message : String(execError)}`,
776
- );
777
- console.log(`[background-runner] stack: ${execError instanceof Error ? execError.stack : "N/A"}`,
778
- );
779
- throw execError;
780
- }
781
743
  } // close if (!earlyResult) — team-run setup+execute done; earlyResult path skips to here
782
744
  manifest = result!.manifest;
783
745
  tasks = result!.tasks;
@@ -786,40 +748,19 @@ async function main(): Promise<void> {
786
748
  runId: manifest.runId,
787
749
  data: { status: manifest.status, tasks: tasks.length },
788
750
  });
789
- console.log(`[background-runner] async.completed written, status=${manifest.status}`,
790
- );
791
- if (
792
- manifest.status === "failed" ||
793
- manifest.status === "cancelled" ||
794
- manifest.status === "blocked"
795
- )
796
- process.exitCode = 1;
751
+ console.log(`[background-runner] async.completed written, status=${manifest.status}`);
752
+ if (manifest.status === "failed" || manifest.status === "cancelled" || manifest.status === "blocked") process.exitCode = 1;
797
753
  } catch (error) {
798
754
  // Terminate live agents on failure too — agents are done when the run fails
799
755
  try {
800
- const loaded = withRunLockSync(
801
- manifest,
802
- () => loadRunManifestById(cwd, runId),
803
- { staleMs: 30_000 },
804
- ); // Use withRunLockSync to prevent race with concurrent writers (e.g., stale reconciler)
756
+ const loaded = withRunLockSync(manifest, () => loadRunManifestById(cwd, runId), { staleMs: 30_000 }); // Use withRunLockSync to prevent race with concurrent writers (e.g., stale reconciler)
805
757
  // between the read and the subsequent save.
806
758
  const manifestToUse = loaded?.manifest ?? manifest;
807
759
  if (manifestToUse) {
808
760
  // LAZY: live-agent-manager only needed on failure cleanup path; avoid module load at hot path.
809
- const { terminateLiveAgentsForRun } = await import(
810
- "./live-agent-manager.ts"
811
- );
812
- void terminateLiveAgentsForRun(
813
- manifestToUse.runId,
814
- "failed",
815
- appendEvent,
816
- manifestToUse.eventsPath,
817
- ).catch((error) =>
818
- logInternalError(
819
- "background-runner.terminate",
820
- error,
821
- `runId=${manifestToUse.runId}`,
822
- ),
761
+ const { terminateLiveAgentsForRun } = await import("./live-agent-manager.ts");
762
+ void terminateLiveAgentsForRun(manifestToUse.runId, "failed", appendEvent, manifestToUse.eventsPath).catch((error) =>
763
+ logInternalError("background-runner.terminate", error, `runId=${manifestToUse.runId}`),
823
764
  );
824
765
  }
825
766
  } catch {
@@ -833,8 +774,7 @@ async function main(): Promise<void> {
833
774
  message,
834
775
  });
835
776
  process.exitCode = 1;
836
- console.log(`[background-runner] catch block, error=${error instanceof Error ? error.message : String(error)}`,
837
- );
777
+ console.log(`[background-runner] catch block, error=${error instanceof Error ? error.message : String(error)}`);
838
778
  } finally {
839
779
  // FIX Issue #4: Use shared runCleanup() function for consistent cleanup
840
780
  // across all exit paths (normal, unhandled rejection, main() exception).
@@ -872,9 +812,7 @@ async function main(): Promise<void> {
872
812
  try {
873
813
  await main();
874
814
  } catch (err) {
875
- console.error(
876
- `[background-runner] DEBUG: main() uncaught: ${err instanceof Error ? err.message : String(err)}`,
877
- );
815
+ console.error(`[background-runner] DEBUG: main() uncaught: ${err instanceof Error ? err.message : String(err)}`);
878
816
  // FIX Issue #1: Set the flag so the finally block's runCleanup() call
879
817
  // will trigger process.exit(1) after cleanup completes. Previously this
880
818
  // called process.exit(1) directly, bypassing the finally block and leaving