pi-crew 0.9.16 → 0.9.18

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 (435) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21784 -0
  4. package/dist/index.mjs +83169 -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/migration/atomic-write-v2-migration.md +297 -0
  14. package/docs/patterns/command-agent-skill.md +1 -1
  15. package/docs/perf/performance-review-2026-07.md +287 -0
  16. package/docs/skills/REFERENCE.md +0 -17
  17. package/docs//303/242mmaAAA/303/242 +357 -0
  18. package/index.bundle.ts +25 -0
  19. package/index.ts +95 -4
  20. package/package.json +15 -4
  21. package/skills/iterative-audit/SKILL.md +0 -1
  22. package/skills/widget-rendering/SKILL.md +17 -0
  23. package/src/adapters/claude-adapter.ts +1 -3
  24. package/src/adapters/export-util.ts +16 -16
  25. package/src/adapters/index.ts +5 -5
  26. package/src/agents/agent-config.ts +20 -14
  27. package/src/agents/agent-serializer.ts +1 -1
  28. package/src/agents/discover-agents.ts +83 -39
  29. package/src/benchmark/benchmark-runner.ts +51 -51
  30. package/src/benchmark/feedback-loop.ts +1 -1
  31. package/src/config/config.ts +303 -591
  32. package/src/config/defaults.ts +28 -2
  33. package/src/config/drift-detector.ts +11 -14
  34. package/src/config/markers.ts +201 -203
  35. package/src/config/resilient-parser.ts +14 -6
  36. package/src/config/role-tools.ts +3 -3
  37. package/src/config/suggestions.ts +5 -12
  38. package/src/config/types.ts +9 -25
  39. package/src/errors.ts +151 -157
  40. package/src/extension/action-suggestions.ts +54 -9
  41. package/src/extension/async-notifier.ts +43 -13
  42. package/src/extension/autonomous-policy.ts +77 -37
  43. package/src/extension/command-completions.ts +9 -8
  44. package/src/extension/context-status-injection.ts +14 -12
  45. package/src/extension/crew-autocomplete.ts +8 -16
  46. package/src/extension/crew-cleanup.ts +2 -1
  47. package/src/extension/crew-shortcuts.ts +9 -6
  48. package/src/extension/cross-extension-rpc.ts +136 -42
  49. package/src/extension/help.ts +1 -1
  50. package/src/extension/import-index.ts +10 -5
  51. package/src/extension/knowledge-injection.ts +88 -15
  52. package/src/extension/management.ts +89 -349
  53. package/src/extension/message-renderers.ts +3 -4
  54. package/src/extension/notification-router.ts +22 -5
  55. package/src/extension/notification-sink.ts +6 -3
  56. package/src/extension/pi-api.ts +17 -8
  57. package/src/extension/plan-orchestrate.ts +8 -27
  58. package/src/extension/project-init.ts +21 -6
  59. package/src/extension/register.ts +329 -975
  60. package/src/extension/registration/artifact-cleanup.ts +9 -4
  61. package/src/extension/registration/command-utils.ts +5 -1
  62. package/src/extension/registration/commands.ts +806 -314
  63. package/src/extension/registration/compaction-guard.ts +15 -15
  64. package/src/extension/registration/lifecycle.ts +259 -0
  65. package/src/extension/registration/observability.ts +293 -0
  66. package/src/extension/registration/subagent-helpers.ts +29 -7
  67. package/src/extension/registration/subagent-tools.ts +253 -44
  68. package/src/extension/registration/team-tool.ts +19 -84
  69. package/src/extension/registration/ui.ts +168 -0
  70. package/src/extension/registration/viewers.ts +64 -24
  71. package/src/extension/result-watcher.ts +18 -7
  72. package/src/extension/run-bundle-schema.ts +21 -6
  73. package/src/extension/run-export.ts +15 -6
  74. package/src/extension/run-import.ts +64 -29
  75. package/src/extension/run-index.ts +28 -9
  76. package/src/extension/run-maintenance.ts +32 -12
  77. package/src/extension/session-summary.ts +1 -1
  78. package/src/extension/team-manager-command.ts +64 -7
  79. package/src/extension/team-onboard.ts +149 -150
  80. package/src/extension/team-recommendation.ts +121 -21
  81. package/src/extension/team-tool/anchor.ts +31 -64
  82. package/src/extension/team-tool/api.ts +929 -141
  83. package/src/extension/team-tool/auto-summarize.ts +14 -26
  84. package/src/extension/team-tool/cache-control.ts +1 -5
  85. package/src/extension/team-tool/cancel.ts +138 -38
  86. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  87. package/src/extension/team-tool/chain-executor.ts +29 -51
  88. package/src/extension/team-tool/config-patch.ts +1 -1
  89. package/src/extension/team-tool/context.ts +40 -20
  90. package/src/extension/team-tool/destructive-gate.ts +10 -5
  91. package/src/extension/team-tool/doctor.ts +170 -51
  92. package/src/extension/team-tool/explain.ts +228 -214
  93. package/src/extension/team-tool/failure-patterns.ts +3 -9
  94. package/src/extension/team-tool/goal-wrap.ts +71 -29
  95. package/src/extension/team-tool/goal.ts +185 -35
  96. package/src/extension/team-tool/handle-schedule.ts +34 -15
  97. package/src/extension/team-tool/handle-settings.ts +94 -56
  98. package/src/extension/team-tool/health-monitor.ts +27 -108
  99. package/src/extension/team-tool/inspect.ts +42 -9
  100. package/src/extension/team-tool/intent-policy.ts +4 -1
  101. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  102. package/src/extension/team-tool/orchestrate.ts +7 -18
  103. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  104. package/src/extension/team-tool/plan.ts +29 -5
  105. package/src/extension/team-tool/respond.ts +49 -16
  106. package/src/extension/team-tool/run-not-found.ts +3 -8
  107. package/src/extension/team-tool/run.ts +80 -317
  108. package/src/extension/team-tool/status.ts +135 -43
  109. package/src/extension/team-tool/workflow-manage.ts +92 -25
  110. package/src/extension/team-tool-types.ts +8 -1
  111. package/src/extension/team-tool.ts +142 -551
  112. package/src/extension/validate-resources.ts +39 -8
  113. package/src/hooks/registry.ts +66 -17
  114. package/src/hooks/types.ts +1 -1
  115. package/src/i18n.ts +25 -11
  116. package/src/observability/correlation.ts +17 -3
  117. package/src/observability/event-bus.ts +51 -56
  118. package/src/observability/event-to-metric.ts +117 -15
  119. package/src/observability/exporters/otlp-exporter.ts +56 -17
  120. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  121. package/src/observability/metric-registry.ts +16 -4
  122. package/src/observability/metric-retention.ts +8 -2
  123. package/src/observability/metric-sink.ts +11 -3
  124. package/src/observability/metrics-primitives.ts +42 -9
  125. package/src/plugins/plugin-define.ts +2 -2
  126. package/src/plugins/plugin-registry.ts +25 -25
  127. package/src/plugins/plugins/index.ts +1 -1
  128. package/src/plugins/plugins/nextjs.ts +16 -16
  129. package/src/plugins/plugins/vite.ts +7 -11
  130. package/src/plugins/plugins/vitest.ts +6 -11
  131. package/src/runtime/adaptive-plan.ts +225 -40
  132. package/src/runtime/agent-control.ts +58 -15
  133. package/src/runtime/agent-memory.ts +15 -9
  134. package/src/runtime/agent-observability.ts +10 -2
  135. package/src/runtime/anchor-manager.ts +27 -25
  136. package/src/runtime/async-marker.ts +7 -1
  137. package/src/runtime/async-runner.ts +39 -24
  138. package/src/runtime/auto-summarize.ts +7 -13
  139. package/src/runtime/background-runner.ts +189 -251
  140. package/src/runtime/batch-barrier.ts +18 -16
  141. package/src/runtime/cancellation-token.ts +16 -6
  142. package/src/runtime/cancellation.ts +46 -8
  143. package/src/runtime/capability-inventory.ts +6 -4
  144. package/src/runtime/chain-parser.ts +44 -11
  145. package/src/runtime/chain-runner.ts +63 -69
  146. package/src/runtime/checkpoint.ts +12 -56
  147. package/src/runtime/child-pi.ts +344 -79
  148. package/src/runtime/coalesce-tasks.ts +268 -0
  149. package/src/runtime/code-summary.ts +71 -26
  150. package/src/runtime/compact-stages/index.ts +15 -5
  151. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  152. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  153. package/src/runtime/compaction-summary.ts +17 -10
  154. package/src/runtime/completion-guard.ts +33 -16
  155. package/src/runtime/crash-classification.ts +28 -7
  156. package/src/runtime/crash-recovery.ts +177 -37
  157. package/src/runtime/crew-agent-records.ts +145 -47
  158. package/src/runtime/crew-hooks.ts +9 -24
  159. package/src/runtime/cross-extension-rpc.ts +25 -23
  160. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  161. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  162. package/src/runtime/delivery-coordinator.ts +34 -7
  163. package/src/runtime/delta-conflict.ts +9 -21
  164. package/src/runtime/deterministic-ast.ts +1 -1
  165. package/src/runtime/diagnostic-export.ts +53 -20
  166. package/src/runtime/direct-run.ts +12 -2
  167. package/src/runtime/dwf-state-store.ts +1 -1
  168. package/src/runtime/dynamic-workflow-context.ts +187 -59
  169. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  170. package/src/runtime/effectiveness.ts +16 -8
  171. package/src/runtime/errors/crew-errors.ts +7 -11
  172. package/src/runtime/event-stream-bridge.ts +9 -3
  173. package/src/runtime/foreground-control.ts +54 -14
  174. package/src/runtime/foreground-watchdog.ts +9 -6
  175. package/src/runtime/goal-achievement.ts +27 -10
  176. package/src/runtime/goal-evaluator.ts +54 -19
  177. package/src/runtime/goal-loop-runner.ts +280 -99
  178. package/src/runtime/goal-state-store.ts +27 -17
  179. package/src/runtime/green-contract.ts +11 -2
  180. package/src/runtime/group-join.ts +64 -31
  181. package/src/runtime/handoff-manager.ts +37 -42
  182. package/src/runtime/heartbeat-gradient.ts +10 -2
  183. package/src/runtime/heartbeat-watcher.ts +32 -6
  184. package/src/runtime/hidden-handoff.ts +13 -31
  185. package/src/runtime/important-line-classifier.ts +12 -2
  186. package/src/runtime/iteration-hooks.ts +25 -15
  187. package/src/runtime/live-agent-control.ts +55 -7
  188. package/src/runtime/live-agent-manager.ts +130 -27
  189. package/src/runtime/live-control-realtime.ts +23 -3
  190. package/src/runtime/live-irc.ts +4 -1
  191. package/src/runtime/live-session-health.ts +12 -4
  192. package/src/runtime/live-session-runtime.ts +379 -117
  193. package/src/runtime/manifest-cache.ts +28 -12
  194. package/src/runtime/mcp-proxy.ts +10 -18
  195. package/src/runtime/metric-parser.ts +1 -5
  196. package/src/runtime/model-fallback.ts +55 -18
  197. package/src/runtime/model-resolver.ts +2 -6
  198. package/src/runtime/model-scope.ts +19 -3
  199. package/src/runtime/notebook-helpers.ts +60 -62
  200. package/src/runtime/orphan-worker-registry.ts +37 -26
  201. package/src/runtime/output-validator.ts +18 -3
  202. package/src/runtime/overflow-recovery.ts +5 -4
  203. package/src/runtime/parallel-research.ts +29 -5
  204. package/src/runtime/parallel-utils.ts +9 -11
  205. package/src/runtime/path-overlap.ts +150 -0
  206. package/src/runtime/peer-dep.ts +12 -17
  207. package/src/runtime/per-write-validator.ts +1 -3
  208. package/src/runtime/phase-tracker.ts +342 -330
  209. package/src/runtime/pi-args.ts +26 -8
  210. package/src/runtime/pi-json-output.ts +1 -1
  211. package/src/runtime/pi-spawn.ts +30 -19
  212. package/src/runtime/pipeline-runner.ts +56 -55
  213. package/src/runtime/plan-templates.ts +5 -6
  214. package/src/runtime/policy-engine.ts +43 -7
  215. package/src/runtime/post-checks.ts +12 -4
  216. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  217. package/src/runtime/process-lifecycle.ts +20 -10
  218. package/src/runtime/process-status.ts +16 -5
  219. package/src/runtime/progress-event-coalescer.ts +2 -1
  220. package/src/runtime/progress-tracker.ts +103 -103
  221. package/src/runtime/prose-compressor.ts +9 -11
  222. package/src/runtime/recovery-recipes.ts +118 -24
  223. package/src/runtime/replace.ts +25 -10
  224. package/src/runtime/resilient-edit.ts +10 -25
  225. package/src/runtime/result-extractor.ts +2 -6
  226. package/src/runtime/retry-executor.ts +20 -4
  227. package/src/runtime/retry-runner.ts +27 -51
  228. package/src/runtime/role-permission.ts +6 -1
  229. package/src/runtime/run-coalesced-task-group.ts +256 -0
  230. package/src/runtime/run-drift.ts +14 -15
  231. package/src/runtime/run-tracker.ts +6 -28
  232. package/src/runtime/runtime-policy.ts +1 -6
  233. package/src/runtime/runtime-resolver.ts +80 -15
  234. package/src/runtime/scheduler.ts +47 -18
  235. package/src/runtime/semaphore.ts +5 -7
  236. package/src/runtime/sensitive-paths.ts +2 -1
  237. package/src/runtime/session-usage.ts +1 -1
  238. package/src/runtime/settings-store.ts +16 -12
  239. package/src/runtime/sidechain-output.ts +6 -2
  240. package/src/runtime/single-agent-compose.ts +1 -4
  241. package/src/runtime/skill-effectiveness.ts +29 -97
  242. package/src/runtime/skill-instructions.ts +40 -83
  243. package/src/runtime/stale-reconciler.ts +73 -197
  244. package/src/runtime/stream-preview.ts +1 -1
  245. package/src/runtime/streaming-output.ts +1 -1
  246. package/src/runtime/subagent-manager.ts +44 -169
  247. package/src/runtime/subprocess-tool-registry.ts +4 -1
  248. package/src/runtime/supervisor-contact.ts +10 -5
  249. package/src/runtime/task-display.ts +19 -5
  250. package/src/runtime/task-graph-scheduler.ts +95 -21
  251. package/src/runtime/task-graph.ts +5 -11
  252. package/src/runtime/task-health.ts +54 -47
  253. package/src/runtime/task-id.ts +12 -19
  254. package/src/runtime/task-output-context.ts +265 -81
  255. package/src/runtime/task-packet.ts +12 -20
  256. package/src/runtime/task-quality.ts +6 -14
  257. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  258. package/src/runtime/task-runner/live-executor.ts +114 -36
  259. package/src/runtime/task-runner/output-splitter.ts +152 -0
  260. package/src/runtime/task-runner/progress.ts +50 -12
  261. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  262. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  263. package/src/runtime/task-runner/result-utils.ts +3 -1
  264. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  265. package/src/runtime/task-runner/run-projection.ts +27 -8
  266. package/src/runtime/task-runner/state-helpers.ts +50 -14
  267. package/src/runtime/task-runner/tail-read.ts +2 -6
  268. package/src/runtime/task-runner.ts +180 -326
  269. package/src/runtime/team-runner-artifacts.ts +13 -0
  270. package/src/runtime/team-runner.ts +456 -974
  271. package/src/runtime/tool-output-pruner.ts +6 -9
  272. package/src/runtime/tool-progress.ts +11 -21
  273. package/src/runtime/verification-gates.ts +33 -24
  274. package/src/runtime/verification-integrity.ts +2 -7
  275. package/src/runtime/verification-worktree.ts +62 -12
  276. package/src/runtime/worker-heartbeat.ts +5 -1
  277. package/src/runtime/worker-startup.ts +29 -5
  278. package/src/runtime/workflow-state.ts +17 -9
  279. package/src/runtime/workspace-lock.ts +30 -35
  280. package/src/runtime/workspace-tree.ts +20 -32
  281. package/src/runtime/yield-handler.ts +43 -9
  282. package/src/runtime/zombie-scanner.ts +5 -4
  283. package/src/schema/config-schema.ts +266 -172
  284. package/src/schema/team-tool-schema.ts +29 -74
  285. package/src/schema/validation-types.ts +25 -17
  286. package/src/skills/discover-skills.ts +35 -10
  287. package/src/skills/skill-templates.ts +109 -27
  288. package/src/skills/validate.ts +26 -8
  289. package/src/state/active-run-registry.ts +87 -28
  290. package/src/state/artifact-store.ts +9 -5
  291. package/src/state/atomic-write-v2.ts +85 -63
  292. package/src/state/atomic-write.ts +106 -27
  293. package/src/state/blob-store.ts +47 -20
  294. package/src/state/contracts.ts +20 -5
  295. package/src/state/crew-init.ts +5 -22
  296. package/src/state/decision-ledger.ts +19 -73
  297. package/src/state/event-log-rotation.ts +41 -15
  298. package/src/state/event-log.ts +168 -53
  299. package/src/state/event-reconstructor.ts +11 -1
  300. package/src/state/gitignore-manager.ts +2 -8
  301. package/src/state/health-store.ts +57 -57
  302. package/src/state/hook-instinct-bridge.ts +1 -1
  303. package/src/state/hook-integrations.ts +1 -1
  304. package/src/state/instinct-store.ts +17 -5
  305. package/src/state/jsonl-writer.ts +1 -1
  306. package/src/state/locks.ts +23 -9
  307. package/src/state/mailbox.ts +151 -28
  308. package/src/state/observation-store.ts +20 -14
  309. package/src/state/run-cache.ts +142 -135
  310. package/src/state/run-graph.ts +6 -15
  311. package/src/state/run-metrics.ts +5 -18
  312. package/src/state/schedule.ts +15 -9
  313. package/src/state/state-store.ts +137 -47
  314. package/src/state/task-claims.ts +12 -2
  315. package/src/state/tiered-eval.ts +52 -43
  316. package/src/state/types-eval.ts +2 -2
  317. package/src/state/types.ts +24 -20
  318. package/src/state/usage.ts +20 -4
  319. package/src/state/worker-atomic-writer.ts +6 -3
  320. package/src/subagents/index.ts +2 -2
  321. package/src/teams/discover-teams.ts +21 -6
  322. package/src/tools/safe-bash-extension.ts +5 -6
  323. package/src/tools/safe-bash.ts +10 -11
  324. package/src/types/new-api-types.ts +6 -10
  325. package/src/ui/agent-management-overlay.ts +52 -40
  326. package/src/ui/card-colors.ts +13 -4
  327. package/src/ui/crew-footer.ts +8 -7
  328. package/src/ui/crew-select-list.ts +1 -1
  329. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  330. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  331. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  332. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  333. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  334. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  335. package/src/ui/heartbeat-aggregator.ts +15 -4
  336. package/src/ui/keybinding-map.ts +40 -7
  337. package/src/ui/live-conversation-overlay.ts +36 -20
  338. package/src/ui/live-duration.ts +1 -4
  339. package/src/ui/live-run-sidebar.ts +88 -25
  340. package/src/ui/loaders.ts +2 -8
  341. package/src/ui/mascot.ts +20 -36
  342. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  343. package/src/ui/overlays/confirm-overlay.ts +4 -2
  344. package/src/ui/overlays/help-overlay.ts +25 -14
  345. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  346. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  347. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  348. package/src/ui/pi-ui-compat.ts +7 -7
  349. package/src/ui/powerbar-publisher.ts +120 -42
  350. package/src/ui/render-diff.ts +10 -3
  351. package/src/ui/render-scheduler.ts +12 -4
  352. package/src/ui/run-action-dispatcher.ts +81 -18
  353. package/src/ui/run-dashboard.ts +172 -76
  354. package/src/ui/run-event-bus.ts +59 -37
  355. package/src/ui/run-snapshot-cache.ts +276 -86
  356. package/src/ui/settings-overlay.ts +361 -74
  357. package/src/ui/status-colors.ts +12 -1
  358. package/src/ui/syntax-highlight.ts +1 -1
  359. package/src/ui/terminal-status.ts +1 -3
  360. package/src/ui/theme-adapter.ts +16 -14
  361. package/src/ui/theme-discovery.ts +13 -2
  362. package/src/ui/tool-progress-formatter.ts +7 -7
  363. package/src/ui/tool-render.ts +128 -56
  364. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  365. package/src/ui/tool-renderers/index.ts +109 -43
  366. package/src/ui/transcript-cache.ts +32 -6
  367. package/src/ui/transcript-entries.ts +25 -23
  368. package/src/ui/transcript-viewer.ts +78 -29
  369. package/src/ui/widget/index.ts +99 -40
  370. package/src/ui/widget/widget-formatters.ts +13 -6
  371. package/src/ui/widget/widget-model.ts +19 -8
  372. package/src/ui/widget/widget-renderer.ts +23 -13
  373. package/src/ui/widget/widget-types.ts +1 -1
  374. package/src/utils/bm25-search.ts +199 -199
  375. package/src/utils/conflict-detect.ts +22 -21
  376. package/src/utils/env-filter.ts +14 -7
  377. package/src/utils/file-coalescer.ts +5 -1
  378. package/src/utils/fingerprint.ts +3 -6
  379. package/src/utils/frontmatter.ts +3 -1
  380. package/src/utils/fs-watch.ts +2 -6
  381. package/src/utils/gh-protocol.ts +119 -44
  382. package/src/utils/git.ts +13 -15
  383. package/src/utils/guards.ts +2 -5
  384. package/src/utils/ids.ts +9 -2
  385. package/src/utils/incremental-reader.ts +14 -3
  386. package/src/utils/internal-error.ts +2 -1
  387. package/src/utils/names.ts +12 -3
  388. package/src/utils/paths.ts +49 -5
  389. package/src/utils/project-detector.ts +2 -2
  390. package/src/utils/redaction.ts +29 -19
  391. package/src/utils/resolve-shell.ts +9 -7
  392. package/src/utils/run-watcher-registry.ts +19 -31
  393. package/src/utils/safe-paths.ts +46 -29
  394. package/src/utils/scan-cache.ts +9 -2
  395. package/src/utils/session-utils.ts +2 -4
  396. package/src/utils/sleep.ts +12 -5
  397. package/src/utils/sse-parser.ts +5 -17
  398. package/src/utils/visual.ts +52 -32
  399. package/src/workflows/discover-workflows.ts +41 -109
  400. package/src/workflows/intermediate-store.ts +5 -21
  401. package/src/workflows/preflight-validator.ts +6 -33
  402. package/src/workflows/topology-analyzer.ts +5 -21
  403. package/src/workflows/workflow-config.ts +7 -6
  404. package/src/worktree/branch-freshness.ts +66 -8
  405. package/src/worktree/cleanup.ts +130 -20
  406. package/src/worktree/worktree-manager.ts +212 -69
  407. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  408. package/skills/detection-pipeline-design/SKILL.md +0 -286
  409. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  410. package/skills/incident-playbook-construction/SKILL.md +0 -384
  411. package/skills/security-review/SKILL.md +0 -561
  412. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  413. package/skills/ui-render-performance/SKILL.md +0 -58
  414. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  415. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  416. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  417. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  418. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  419. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  420. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  421. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  422. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  430. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  431. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  432. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  433. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  434. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  435. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -54,7 +54,9 @@ export function deliverGroupJoin(input: {
54
54
  }): CrewGroupJoinDelivery | undefined {
55
55
  if (!shouldGroupJoin(input.mode, input.batch)) return undefined;
56
56
  const taskIds = input.batch.map((task) => task.id);
57
- const latest = taskIds.map((id) => input.allTasks.find((task) => task.id === id)).filter((task): task is TeamTaskState => Boolean(task));
57
+ const latest = taskIds
58
+ .map((id) => input.allTasks.find((task) => task.id === id))
59
+ .filter((task): task is TeamTaskState => Boolean(task));
58
60
  const completed = statusList(latest, "completed");
59
61
  const failed = statusList(latest, "failed");
60
62
  const skipped = statusList(latest, "skipped");
@@ -64,8 +66,22 @@ export function deliverGroupJoin(input: {
64
66
  const summary = aggregateTaskOutputs(latest, input.manifest);
65
67
  const requestId = requestIdFor(input.manifest.runId, batchId, partial);
66
68
  const existingMailbox = findMailboxMessageByRequestId(input.manifest, requestId);
67
- const existingStatus = existingMailbox ? readDeliveryState(input.manifest).messages[existingMailbox.id] ?? existingMailbox.status : undefined;
68
- const delivery: CrewGroupJoinDelivery = { batchId, mode: input.mode, partial, taskIds, completed, failed, skipped, remaining, requestId, ackRequired: true, ackStatus: existingStatus === "acknowledged" ? "acknowledged" : "pending" };
69
+ const existingStatus = existingMailbox
70
+ ? (readDeliveryState(input.manifest).messages[existingMailbox.id] ?? existingMailbox.status)
71
+ : undefined;
72
+ const delivery: CrewGroupJoinDelivery = {
73
+ batchId,
74
+ mode: input.mode,
75
+ partial,
76
+ taskIds,
77
+ completed,
78
+ failed,
79
+ skipped,
80
+ remaining,
81
+ requestId,
82
+ ackRequired: true,
83
+ ackStatus: existingStatus === "acknowledged" ? "acknowledged" : "pending",
84
+ };
69
85
  const content = `${JSON.stringify({ ...delivery, createdAt: new Date().toISOString() }, null, 2)}\n`;
70
86
  const artifact = writeArtifact(input.manifest.artifactsRoot, {
71
87
  kind: "metadata",
@@ -73,35 +89,55 @@ export function deliverGroupJoin(input: {
73
89
  producer: "group-join",
74
90
  content,
75
91
  });
76
- const mailbox = existingMailbox ?? appendMailboxMessage(input.manifest, {
77
- direction: "outbox",
78
- from: "group-join",
79
- to: "leader",
80
- body: [
81
- `Group join ${partial ? "partial" : "completed"}: ${taskIds.join(", ")}`,
82
- `Request: ${requestId}`,
83
- `Completed: ${completed.join(", ") || "none"}`,
84
- `Failed: ${failed.join(", ") || "none"}`,
85
- `Skipped: ${skipped.join(", ") || "none"}`,
86
- `Remaining: ${remaining.join(", ") || "none"}`,
87
- "",
88
- summary,
89
- ].join("\n"),
90
- status: "delivered",
91
- data: { kind: "group_join", requestId, batchId, partial, ackRequired: true, taskIds, completed, failed, skipped, remaining },
92
- });
92
+ const mailbox =
93
+ existingMailbox ??
94
+ appendMailboxMessage(input.manifest, {
95
+ direction: "outbox",
96
+ from: "group-join",
97
+ to: "leader",
98
+ body: [
99
+ `Group join ${partial ? "partial" : "completed"}: ${taskIds.join(", ")}`,
100
+ `Request: ${requestId}`,
101
+ `Completed: ${completed.join(", ") || "none"}`,
102
+ `Failed: ${failed.join(", ") || "none"}`,
103
+ `Skipped: ${skipped.join(", ") || "none"}`,
104
+ `Remaining: ${remaining.join(", ") || "none"}`,
105
+ "",
106
+ summary,
107
+ ].join("\n"),
108
+ status: "delivered",
109
+ data: {
110
+ kind: "group_join",
111
+ requestId,
112
+ batchId,
113
+ partial,
114
+ ackRequired: true,
115
+ taskIds,
116
+ completed,
117
+ failed,
118
+ skipped,
119
+ remaining,
120
+ },
121
+ });
93
122
  appendEvent(input.manifest.eventsPath, {
94
123
  type: partial ? "agent.group_join.partial" : "agent.group_join.completed",
95
124
  runId: input.manifest.runId,
96
125
  message: `Group join ${partial ? "partial" : "completed"} for ${taskIds.length} task(s).`,
97
- data: { ...delivery, artifactPath: artifact.path, messageId: mailbox.id, fallback: "mailbox-delivered", reused: Boolean(existingMailbox) },
98
- });
99
- if (existingMailbox) appendEvent(input.manifest.eventsPath, {
100
- type: "agent.group_join.delivery_reused",
101
- runId: input.manifest.runId,
102
- message: `Reused group join mailbox delivery for ${taskIds.length} task(s).`,
103
- data: { requestId, messageId: mailbox.id, batchId, partial },
126
+ data: {
127
+ ...delivery,
128
+ artifactPath: artifact.path,
129
+ messageId: mailbox.id,
130
+ fallback: "mailbox-delivered",
131
+ reused: Boolean(existingMailbox),
132
+ },
104
133
  });
134
+ if (existingMailbox)
135
+ appendEvent(input.manifest.eventsPath, {
136
+ type: "agent.group_join.delivery_reused",
137
+ runId: input.manifest.runId,
138
+ message: `Reused group join mailbox delivery for ${taskIds.length} task(s).`,
139
+ data: { requestId, messageId: mailbox.id, batchId, partial },
140
+ });
105
141
  return { ...delivery, artifact, messageId: mailbox.id };
106
142
  }
107
143
 
@@ -129,10 +165,7 @@ export class GroupJoinManager {
129
165
  private deliverCb: DeliveryCallback;
130
166
  private groupTimeout: number;
131
167
 
132
- constructor(
133
- deliverCb: DeliveryCallback,
134
- groupTimeout = DEFAULT_TIMEOUT,
135
- ) {
168
+ constructor(deliverCb: DeliveryCallback, groupTimeout = DEFAULT_TIMEOUT) {
136
169
  this.deliverCb = deliverCb;
137
170
  this.groupTimeout = groupTimeout;
138
171
  }
@@ -8,18 +8,18 @@ const MAX_PENDING_HANDOFFS = 1000; // Prevent unbounded growth
8
8
  * Type guard for HandoffSummary structure validation.
9
9
  */
10
10
  export function isValidHandoffSummary(value: unknown): value is HandoffSummary {
11
- if (!value || typeof value !== 'object') {
11
+ if (!value || typeof value !== "object") {
12
12
  return false;
13
13
  }
14
14
  const obj = value as Record<string, unknown>;
15
15
 
16
16
  // Check required fields
17
- if (typeof obj.taskId !== 'string' || !obj.taskId) return false;
18
- if (typeof obj.runId !== 'string' || !obj.runId) return false;
19
- if (typeof obj.timestamp !== 'number') return false;
20
- if (typeof obj.task !== 'string' || !obj.task) return false;
21
- if (typeof obj.outcome !== 'string') return false;
22
- if (!['success', 'failure', 'partial'].includes(obj.outcome)) return false;
17
+ if (typeof obj.taskId !== "string" || !obj.taskId) return false;
18
+ if (typeof obj.runId !== "string" || !obj.runId) return false;
19
+ if (typeof obj.timestamp !== "number") return false;
20
+ if (typeof obj.task !== "string" || !obj.task) return false;
21
+ if (typeof obj.outcome !== "string") return false;
22
+ if (!["success", "failure", "partial"].includes(obj.outcome)) return false;
23
23
 
24
24
  // Check arrays
25
25
  if (!Array.isArray(obj.filesCreated)) return false;
@@ -30,27 +30,27 @@ export function isValidHandoffSummary(value: unknown): value is HandoffSummary {
30
30
  if (!Array.isArray(obj.nextSteps)) return false;
31
31
 
32
32
  // Check metrics object
33
- if (!obj.metrics || typeof obj.metrics !== 'object') return false;
33
+ if (!obj.metrics || typeof obj.metrics !== "object") return false;
34
34
  const metrics = obj.metrics as Record<string, unknown>;
35
- if (typeof metrics.tokensUsed !== 'number') return false;
36
- if (typeof metrics.duration !== 'number') return false;
37
- if (typeof metrics.iterations !== 'number') return false;
35
+ if (typeof metrics.tokensUsed !== "number") return false;
36
+ if (typeof metrics.duration !== "number") return false;
37
+ if (typeof metrics.iterations !== "number") return false;
38
38
  if (!Array.isArray(metrics.toolsUsed)) return false;
39
39
 
40
40
  // Check contextSnapshot
41
- if (typeof obj.contextSnapshot !== 'string') return false;
41
+ if (typeof obj.contextSnapshot !== "string") return false;
42
42
 
43
43
  return true;
44
44
  }
45
45
 
46
46
  /**
47
47
  * HandoffManager - Generates structured summaries for agent handoffs.
48
- *
48
+ *
49
49
  * Based on pi-boomerang's session_before_tree hook pattern:
50
50
  * - Detects task completion via agent_end hook
51
51
  * - Generates structured summaries with token metrics, artifacts, decisions
52
52
  * - Optionally collapses context to reduce token usage
53
- *
53
+ *
54
54
  * @see docs/pi-boomerang-integration-plan.md
55
55
  */
56
56
 
@@ -172,7 +172,7 @@ export interface SummarizeDecision {
172
172
  /**
173
173
  * HandoffManager generates structured summaries when agents complete tasks,
174
174
  * enabling efficient context passing to subsequent agents.
175
- *
175
+ *
176
176
  * H1: Includes memory management to prevent unbounded growth of pendingHandoffs Map.
177
177
  */
178
178
  export class HandoffManager {
@@ -247,8 +247,7 @@ export class HandoffManager {
247
247
  // Also enforce max handoffs limit
248
248
  if (this.pendingHandoffs.size > MAX_PENDING_HANDOFFS) {
249
249
  // Remove oldest entries
250
- const sortedEntries = [...this.handoffTimestamps.entries()]
251
- .sort((a, b) => a[1] - b[1]);
250
+ const sortedEntries = [...this.handoffTimestamps.entries()].sort((a, b) => a[1] - b[1]);
252
251
  const removeCount = sortedEntries.length - MAX_PENDING_HANDOFFS;
253
252
  for (let i = 0; i < removeCount; i++) {
254
253
  const sessionId = sortedEntries[i][0];
@@ -261,7 +260,7 @@ export class HandoffManager {
261
260
  /**
262
261
  * Hook: agent_end
263
262
  * Called when agent completes a task.
264
- *
263
+ *
265
264
  * @param packet - The task packet
266
265
  * @param result - The task result
267
266
  */
@@ -294,7 +293,10 @@ export class HandoffManager {
294
293
  }
295
294
 
296
295
  // Emit handoff event
297
- this.options.eventEmitter?.emit("handoff:generated", { packet, summary });
296
+ this.options.eventEmitter?.emit("handoff:generated", {
297
+ packet,
298
+ summary,
299
+ });
298
300
 
299
301
  // Optionally collapse context
300
302
  if (packet.collapseContext) {
@@ -308,7 +310,7 @@ export class HandoffManager {
308
310
  * Hook: session_before_tree
309
311
  * Called before navigating to tree view.
310
312
  * Injects pending handoff summaries into the tree.
311
- *
313
+ *
312
314
  * @param sessionId - The session ID
313
315
  * @param targetId - The target tree node ID
314
316
  */
@@ -335,19 +337,19 @@ export class HandoffManager {
335
337
  */
336
338
  shouldSummarize(packet: TaskPacket, result: TaskResult): SummarizeDecision {
337
339
  // H7: Validate packet structure
338
- if (!packet || typeof packet.taskId !== 'string' || !packet.taskId) {
340
+ if (!packet || typeof packet.taskId !== "string" || !packet.taskId) {
339
341
  return {
340
342
  shouldSummarize: false,
341
- reason: 'Invalid task packet structure',
343
+ reason: "Invalid task packet structure",
342
344
  tokenCount: 0,
343
345
  };
344
346
  }
345
347
 
346
348
  // H7: Validate result structure
347
- if (!result || typeof result.outcome !== 'string') {
349
+ if (!result || typeof result.outcome !== "string") {
348
350
  return {
349
351
  shouldSummarize: false,
350
- reason: 'Invalid task result structure',
352
+ reason: "Invalid task result structure",
351
353
  tokenCount: 0,
352
354
  };
353
355
  }
@@ -384,8 +386,7 @@ export class HandoffManager {
384
386
  }
385
387
 
386
388
  // 4. Task has significant artifacts or decisions
387
- const hasArtifacts = (result.filesCreated?.length ?? 0) > 0 ||
388
- (result.filesModified?.length ?? 0) > 0;
389
+ const hasArtifacts = (result.filesCreated?.length ?? 0) > 0 || (result.filesModified?.length ?? 0) > 0;
389
390
  const hasDecisions = (result.decisions?.length ?? 0) > 0;
390
391
 
391
392
  if (hasArtifacts || hasDecisions) {
@@ -419,11 +420,7 @@ export class HandoffManager {
419
420
  const artifacts = this.extractArtifacts(result);
420
421
  // Use extractDecisionsFromResult to handle empty array and generate defaults
421
422
  const decisions = this.extractDecisionsFromResult(result);
422
- const contextSnapshot = await this.generateContextSnapshot(
423
- packet.runId,
424
- packet.taskId,
425
- result
426
- );
423
+ const contextSnapshot = await this.generateContextSnapshot(packet.runId, packet.taskId, result);
427
424
 
428
425
  return {
429
426
  taskId: packet.taskId,
@@ -531,11 +528,13 @@ export class HandoffManager {
531
528
 
532
529
  // Generate a default decision for failure outcomes
533
530
  if (result.outcome === "failure") {
534
- return [{
535
- rationale: "Task failed",
536
- outcome: result.error ?? "Unknown error",
537
- alternativesConsidered: [],
538
- }];
531
+ return [
532
+ {
533
+ rationale: "Task failed",
534
+ outcome: result.error ?? "Unknown error",
535
+ alternativesConsidered: [],
536
+ },
537
+ ];
539
538
  }
540
539
 
541
540
  return [];
@@ -544,11 +543,7 @@ export class HandoffManager {
544
543
  /**
545
544
  * Generate context snapshot for handoff.
546
545
  */
547
- private async generateContextSnapshot(
548
- runId: string,
549
- taskId: string,
550
- result: TaskResult
551
- ): Promise<string> {
546
+ private async generateContextSnapshot(runId: string, taskId: string, result: TaskResult): Promise<string> {
552
547
  const parts: string[] = [];
553
548
 
554
549
  parts.push(`Task: ${taskId}`);
@@ -602,4 +597,4 @@ export class HandoffManager {
602
597
  */
603
598
  export function createHandoffManager(options?: HandoffManagerOptions): HandoffManager {
604
599
  return new HandoffManager(options);
605
- }
600
+ }
@@ -8,7 +8,11 @@ export interface GradientThresholds {
8
8
  deadMs: number;
9
9
  }
10
10
 
11
- export const DEFAULT_GRADIENT_THRESHOLDS: GradientThresholds = { warnMs: 30_000, staleMs: 60_000, deadMs: 300_000 };
11
+ export const DEFAULT_GRADIENT_THRESHOLDS: GradientThresholds = {
12
+ warnMs: 30_000,
13
+ staleMs: 60_000,
14
+ deadMs: 300_000,
15
+ };
12
16
 
13
17
  export function heartbeatAgeMs(heartbeat: WorkerHeartbeatState | undefined, now = Date.now()): number {
14
18
  if (!heartbeat) return Number.POSITIVE_INFINITY;
@@ -16,7 +20,11 @@ export function heartbeatAgeMs(heartbeat: WorkerHeartbeatState | undefined, now
16
20
  return Number.isFinite(lastSeen) ? Math.max(0, now - lastSeen) : Number.POSITIVE_INFINITY;
17
21
  }
18
22
 
19
- export function classifyHeartbeat(heartbeat: WorkerHeartbeatState | undefined, thresholds: GradientThresholds = DEFAULT_GRADIENT_THRESHOLDS, now = Date.now()): HeartbeatLevel {
23
+ export function classifyHeartbeat(
24
+ heartbeat: WorkerHeartbeatState | undefined,
25
+ thresholds: GradientThresholds = DEFAULT_GRADIENT_THRESHOLDS,
26
+ now = Date.now(),
27
+ ): HeartbeatLevel {
20
28
  if (!heartbeat) return "dead";
21
29
  if (heartbeat.alive === false) return "dead";
22
30
  const elapsed = heartbeatAgeMs(heartbeat, now);
@@ -5,8 +5,8 @@ import { appendEvent } from "../state/event-log.ts";
5
5
  import { loadRunManifestById } from "../state/state-store.ts";
6
6
  import type { TeamRunManifest } from "../state/types.ts";
7
7
  import { logInternalError } from "../utils/internal-error.ts";
8
+ import { DEFAULT_GRADIENT_THRESHOLDS, type GradientThresholds, type HeartbeatLevel, heartbeatAgeMs } from "./heartbeat-gradient.ts";
8
9
  import type { ManifestCache } from "./manifest-cache.ts";
9
- import { DEFAULT_GRADIENT_THRESHOLDS, heartbeatAgeMs, type GradientThresholds, type HeartbeatLevel } from "./heartbeat-gradient.ts";
10
10
 
11
11
  export interface HeartbeatWatcherRouter {
12
12
  enqueue(notification: NotificationDescriptor): boolean;
@@ -126,18 +126,44 @@ export class HeartbeatWatcher {
126
126
  // Process is dead
127
127
  }
128
128
  }
129
- let level: HeartbeatLevel = elapsed > thresholds.deadMs ? "dead" : elapsed > thresholds.staleMs ? "stale" : elapsed > thresholds.warnMs ? "warn" : "healthy";
129
+ let level: HeartbeatLevel =
130
+ elapsed > thresholds.deadMs
131
+ ? "dead"
132
+ : elapsed > thresholds.staleMs
133
+ ? "stale"
134
+ : elapsed > thresholds.warnMs
135
+ ? "warn"
136
+ : "healthy";
130
137
  if (level === "dead" && isProcessAlive) {
131
138
  level = "stale";
132
139
  }
133
- this.opts.registry.gauge("crew.heartbeat.staleness_ms", "Heartbeat elapsed since last seen, milliseconds").set({ runId: run.runId, taskId: task.id }, Number.isFinite(elapsed) ? elapsed : thresholds.deadMs);
134
- this.opts.registry.counter("crew.heartbeat.level_total", "Heartbeat classifications by level").inc({ runId: run.runId, level });
140
+ this.opts.registry
141
+ .gauge("crew.heartbeat.staleness_ms", "Heartbeat elapsed since last seen, milliseconds")
142
+ .set({ runId: run.runId, taskId: task.id }, Number.isFinite(elapsed) ? elapsed : thresholds.deadMs);
143
+ this.opts.registry
144
+ .counter("crew.heartbeat.level_total", "Heartbeat classifications by level")
145
+ .inc({ runId: run.runId, level });
135
146
  const previous = this.lastLevel.get(key);
136
147
  this.lastLevel.set(key, level);
137
148
  if (level === "dead" && previous !== "dead") {
138
149
  this.opts.registry.counter("crew.heartbeat.dead_total", "Dead heartbeat detections").inc({ runId: run.runId });
139
- appendEvent(loaded.manifest.eventsPath, { type: "crew.task.heartbeat_dead", runId: run.runId, taskId: task.id, message: `Task ${task.id} heartbeat dead.`, data: { elapsedMs: Number.isFinite(elapsed) ? elapsed : undefined } });
140
- this.opts.router.enqueue({ id: `dead_${run.runId}_${task.id}`, severity: "warning", source: "heartbeat-watcher", runId: run.runId, title: `Task ${task.id} heartbeat dead`, body: "Background watcher detected a stuck worker." });
150
+ appendEvent(loaded.manifest.eventsPath, {
151
+ type: "crew.task.heartbeat_dead",
152
+ runId: run.runId,
153
+ taskId: task.id,
154
+ message: `Task ${task.id} heartbeat dead.`,
155
+ data: {
156
+ elapsedMs: Number.isFinite(elapsed) ? elapsed : undefined,
157
+ },
158
+ });
159
+ this.opts.router.enqueue({
160
+ id: `dead_${run.runId}_${task.id}`,
161
+ severity: "warning",
162
+ source: "heartbeat-watcher",
163
+ runId: run.runId,
164
+ title: `Task ${task.id} heartbeat dead`,
165
+ body: "Background watcher detected a stuck worker.",
166
+ });
141
167
  this.opts.onDead?.(run.runId, task.id, Number.isFinite(elapsed) ? elapsed : thresholds.deadMs);
142
168
  }
143
169
  if (level === "dead") {
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * HiddenHandoffService - Sends hidden boomerang-handoff messages to parent agents.
3
- *
3
+ *
4
4
  * Based on pi-boomerang's triggerHiddenOrchestratorHandoff() pattern:
5
5
  * - Sends customType: "boomerang-handoff" messages
6
6
  * - Full handoff details in hidden message to parent
7
7
  * - Orchestrator immediately reads summary
8
- *
8
+ *
9
9
  * @see docs/pi-boomerang-integration-plan.md
10
10
  */
11
11
 
12
- import type { HandoffSummary } from "./handoff-manager.ts";
13
12
  import { logInternalError } from "../utils/internal-error.ts";
13
+ import type { HandoffSummary } from "./handoff-manager.ts";
14
14
 
15
15
  /**
16
16
  * Type of hidden handoff message.
@@ -161,21 +161,18 @@ export class HiddenHandoffService {
161
161
 
162
162
  /**
163
163
  * Send a hidden handoff to the parent agent or specified recipient.
164
- *
164
+ *
165
165
  * @param summary - The handoff summary to send
166
166
  * @param options - Send options
167
167
  */
168
- sendHandoff(
169
- summary: HandoffSummary,
170
- options: SendHandoffOptions = {},
171
- ): void {
168
+ sendHandoff(summary: HandoffSummary, options: SendHandoffOptions = {}): void {
172
169
  if (!this.enabled) {
173
170
  return;
174
171
  }
175
172
 
176
173
  const priority = options.priority ?? this.inferPriority(summary);
177
174
  const content = this.buildContent(summary);
178
- let recipient = options.to ?? this.getParentAgentId();
175
+ const recipient = options.to ?? this.getParentAgentId();
179
176
 
180
177
  if (!recipient) {
181
178
  // No parent to send to, but we still emit the event
@@ -233,10 +230,7 @@ export class HiddenHandoffService {
233
230
  /**
234
231
  * Send a hidden handoff immediately (fire and forget).
235
232
  */
236
- sendHandoffAsync(
237
- summary: HandoffSummary,
238
- options?: SendHandoffOptions,
239
- ): void {
233
+ sendHandoffAsync(summary: HandoffSummary, options?: SendHandoffOptions): void {
240
234
  // Fire and forget - no await
241
235
  try {
242
236
  this.sendHandoff(summary, options);
@@ -289,10 +283,7 @@ export class HiddenHandoffService {
289
283
  * Build summary text from summary.
290
284
  */
291
285
  private buildSummaryText(summary: HandoffSummary): string {
292
- const parts: string[] = [
293
- `Completed: ${summary.task}`,
294
- `Outcome: ${summary.outcome}`,
295
- ];
286
+ const parts: string[] = [`Completed: ${summary.task}`, `Outcome: ${summary.outcome}`];
296
287
 
297
288
  if (summary.filesCreated.length > 0) {
298
289
  parts.push(`Files created: ${summary.filesCreated.join(", ")}`);
@@ -310,10 +301,7 @@ export class HiddenHandoffService {
310
301
  parts.push(`Next steps: ${summary.nextSteps.join("; ")}`);
311
302
  }
312
303
 
313
- parts.push(
314
- `Tokens: ${summary.metrics.tokensUsed}`,
315
- `Duration: ${Math.round(summary.metrics.duration / 1000)}s`,
316
- );
304
+ parts.push(`Tokens: ${summary.metrics.tokensUsed}`, `Duration: ${Math.round(summary.metrics.duration / 1000)}s`);
317
305
 
318
306
  return parts.join("\n");
319
307
  }
@@ -361,9 +349,7 @@ export class HiddenHandoffService {
361
349
  const timestamps = this.sendTimestamps.get(recipient) ?? [];
362
350
 
363
351
  // Filter out old timestamps outside the window
364
- const recentTimestamps = timestamps.filter(
365
- (t) => now - t < this.RATE_LIMIT_WINDOW_MS,
366
- );
352
+ const recentTimestamps = timestamps.filter((t) => now - t < this.RATE_LIMIT_WINDOW_MS);
367
353
 
368
354
  // HIGH-11: If no recent timestamps, remove the empty key to prevent unbounded growth
369
355
  if (recentTimestamps.length === 0) {
@@ -384,9 +370,7 @@ export class HiddenHandoffService {
384
370
  timestamps.push(now);
385
371
 
386
372
  // MEDIUM-14: Use RATE_LIMIT_WINDOW_MS consistently for both filter and record
387
- const recentTimestamps = timestamps.filter(
388
- (t) => now - t < this.RATE_LIMIT_WINDOW_MS,
389
- );
373
+ const recentTimestamps = timestamps.filter((t) => now - t < this.RATE_LIMIT_WINDOW_MS);
390
374
 
391
375
  this.sendTimestamps.set(recipient, recentTimestamps);
392
376
  }
@@ -418,8 +402,6 @@ export interface SendHandoffOptions {
418
402
  /**
419
403
  * Create a HiddenHandoffService with default options.
420
404
  */
421
- export function createHiddenHandoffService(
422
- options?: HiddenHandoffServiceOptions,
423
- ): HiddenHandoffService {
405
+ export function createHiddenHandoffService(options?: HiddenHandoffServiceOptions): HiddenHandoffService {
424
406
  return new HiddenHandoffService(options);
425
- }
407
+ }
@@ -107,7 +107,12 @@ export function splitWithImportantLines(value: string, maxChars: number, opts: S
107
107
  const tail = value.slice(value.length - tailLen);
108
108
 
109
109
  if (opts.preserveImportant === false) {
110
- return { head, tail, importantLines: [], baseDropped: value.length - maxChars };
110
+ return {
111
+ head,
112
+ tail,
113
+ importantLines: [],
114
+ baseDropped: value.length - maxChars,
115
+ };
111
116
  }
112
117
 
113
118
  const slackFactor = opts.slackFactor ?? 0.15;
@@ -126,5 +131,10 @@ export function splitWithImportantLines(value: string, maxChars: number, opts: S
126
131
  used += addLen;
127
132
  }
128
133
 
129
- return { head, tail, importantLines: chosen, baseDropped: value.length - maxChars };
134
+ return {
135
+ head,
136
+ tail,
137
+ importantLines: chosen,
138
+ baseDropped: value.length - maxChars,
139
+ };
130
140
  }
@@ -8,9 +8,9 @@ import { spawn } from "node:child_process";
8
8
  import * as fs from "node:fs";
9
9
  import * as path from "node:path";
10
10
  import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
11
- import { HeadSnapStage } from "./compact-stages/index.ts";
12
- import { resolveShellForScript } from "../utils/resolve-shell.ts";
13
11
  import { sanitizeEnvSecrets } from "../utils/env-filter.ts";
12
+ import { resolveShellForScript } from "../utils/resolve-shell.ts";
13
+ import { HeadSnapStage } from "./compact-stages/index.ts";
14
14
  import { DENIED_METRIC_NAMES } from "./metric-parser.ts";
15
15
 
16
16
  /** Hook execution stage. */
@@ -139,7 +139,14 @@ export async function runIterationHook(
139
139
  options?: { timeoutMs?: number },
140
140
  ): Promise<HookResult> {
141
141
  if (!isAllowedHookPath(hookScriptPath)) {
142
- return { fired: false, stdout: "", stderr: "hook path not allowed: " + hookScriptPath, exitCode: null, timedOut: false, durationMs: 0 };
142
+ return {
143
+ fired: false,
144
+ stdout: "",
145
+ stderr: "hook path not allowed: " + hookScriptPath,
146
+ exitCode: null,
147
+ timedOut: false,
148
+ durationMs: 0,
149
+ };
143
150
  }
144
151
  // Resolve relative paths relative to cwd
145
152
  const resolvedScript = path.isAbsolute(hookScriptPath) ? hookScriptPath : path.join(payload.cwd, hookScriptPath);
@@ -156,7 +163,12 @@ export async function runIterationHook(
156
163
  const { command, args } = resolveShellForScript(resolvedScript);
157
164
  const child = spawn(command, args, {
158
165
  cwd: payload.cwd,
159
- env: { ...sanitizeEnvSecrets(process.env, { allowList: ["PATH", "HOME", "USER", ...WINDOWS_ESSENTIAL_ENV_VARS, "TMPDIR", "LANG", "LC_ALL", "PI_CREW_*"] }), PI_CREW_HOOK: "1" },
166
+ env: {
167
+ ...sanitizeEnvSecrets(process.env, {
168
+ allowList: ["PATH", "HOME", "USER", ...WINDOWS_ESSENTIAL_ENV_VARS, "TMPDIR", "LANG", "LC_ALL", "PI_CREW_*"],
169
+ }),
170
+ PI_CREW_HOOK: "1",
171
+ },
160
172
  stdio: ["pipe", "pipe", "pipe"],
161
173
  });
162
174
 
@@ -184,7 +196,9 @@ export async function runIterationHook(
184
196
  // then apply the byte-cap stage with newline-snap so partial lines
185
197
  // never appear in the captured preview. HeadSnapStage is byte-cap-safe
186
198
  // (walks back partial UTF-8 sequences at the cut boundary).
187
- const stdoutText = new HeadSnapStage({ maxBytes: MAX_STDOUT_BYTES }).apply(rawStdout.toString("utf-8"));
199
+ const stdoutText = new HeadSnapStage({
200
+ maxBytes: MAX_STDOUT_BYTES,
201
+ }).apply(rawStdout.toString("utf-8"));
188
202
 
189
203
  const rawStderr = Buffer.concat(stderrChunks);
190
204
 
@@ -214,7 +228,9 @@ export async function runIterationHook(
214
228
  // Write payload to stdin and close it.
215
229
  // Handle EPIPE errors gracefully (occurs if the hook script exits before
216
230
  // reading all of stdin, which is normal for some hook scripts on certain OS).
217
- child.stdin.on("error", () => { /* ignore EPIPE — hook exited early */ });
231
+ child.stdin.on("error", () => {
232
+ /* ignore EPIPE — hook exited early */
233
+ });
218
234
  try {
219
235
  child.stdin.write(stdinJson, "utf-8");
220
236
  child.stdin.end();
@@ -232,10 +248,7 @@ export async function runIterationHook(
232
248
  * - Empty stdout → null (no steer)
233
249
  * - Otherwise → trimmed stdout content
234
250
  */
235
- export function steerMessageFromHook(
236
- stage: HookStage,
237
- result: HookResult,
238
- ): string | null {
251
+ export function steerMessageFromHook(stage: HookStage, result: HookResult): string | null {
239
252
  if (!result.fired) return null;
240
253
 
241
254
  if (result.timedOut) {
@@ -267,10 +280,7 @@ export function steerMessageFromHook(
267
280
  /**
268
281
  * Build a log entry for recording hook execution in events.jsonl.
269
282
  */
270
- export function hookLogEntry(
271
- stage: HookStage,
272
- result: HookResult,
273
- ): Record<string, unknown> {
283
+ export function hookLogEntry(stage: HookStage, result: HookResult): Record<string, unknown> {
274
284
  const entry: Record<string, unknown> = {
275
285
  type: "iteration-hook",
276
286
  stage,
@@ -292,4 +302,4 @@ export function hookLogEntry(
292
302
  }
293
303
 
294
304
  return entry;
295
- }
305
+ }