pi-crew 0.9.15 → 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 +145 -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 +193 -40
  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 +86 -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
@@ -0,0 +1,268 @@
1
+ /**
2
+ * coalesce-tasks.ts — micro-task coalescing for scheduler batching (M6).
3
+ *
4
+ * Implements the workflow-level `coalesceMicroTasks` opt-in (plan §11
5
+ * decision #7): default = false; when true, multiple ready tasks sharing
6
+ * `role` and `cwd` are grouped into a single "coalesced task group" so the
7
+ * scheduler dispatches ONE worker call instead of N. Trades individual task
8
+ * observability for fewer cold-starts when many small tasks of the same kind
9
+ * are queued simultaneously.
10
+ *
11
+ * Safety guards (per plan §5 M6):
12
+ * - Only groups tasks that share role + cwd (so the grouped worker can use
13
+ * a single worker prompt without per-task permission prompts).
14
+ * - Tasks that declare a non-false `output` are NEVER coalesced with
15
+ * another task that has the same write path (write-write conflict).
16
+ * - When the flag is off or the list is empty, returns the input list
17
+ * unchanged (no-op for production; zero overhead).
18
+ *
19
+ * Real-dispatch (v0.9.17+ follow-up, see m6-real-dispatch-design.md):
20
+ * - MVP only coalesces READ_ONLY roles (permissionForRole === "read_only").
21
+ * This eliminates write-path conflicts, worktree concerns, and mutation
22
+ * guards. Use case: parallel-research with 4 explorers → 1 worker.
23
+ * - MVP rejects coalescing when any step has: worktree: true, preStepScript,
24
+ * verify: true, or non-false output. These are per-step concerns that
25
+ * don't compose in a single multi-task worker.
26
+ * - maxGroupSize cap (default 5) prevents context-budget overflow when
27
+ * grouping many micro-tasks into one worker prompt.
28
+ */
29
+
30
+ import { permissionForRole } from "./role-permission.ts";
31
+ import type { TeamTaskState } from "../state/types.ts";
32
+ import type { WorkflowConfig, WorkflowStep } from "../workflows/workflow-config.ts";
33
+
34
+ /** Default cap on group size to prevent context-budget overflow. */
35
+ export const DEFAULT_MAX_GROUP_SIZE = 5;
36
+
37
+ /** Default cap on combined estimated prompt size (bytes). Conservative
38
+ * estimate — real prompt includes the stable prefix (~5KB) plus per-task
39
+ * sections. 100KB matches the budget heuristic in the design doc
40
+ * (m6-real-dispatch-design.md §R4). */
41
+ export const DEFAULT_MAX_GROUP_BYTES = 100_000;
42
+
43
+ export interface CoalescedGroup {
44
+ /** Stable id derived from the group membership; deterministic for tests. */
45
+ id: string;
46
+ role: string;
47
+ cwd: string;
48
+ /** Tasks to be coalesced into one worker invocation, in input order. */
49
+ tasks: TeamTaskState[];
50
+ }
51
+
52
+ /**
53
+ * Group ready tasks by (role, cwd) while respecting write-path safety.
54
+ *
55
+ * Returns an array of `CoalescedGroup`. Each group contains tasks that:
56
+ * - share the same role and cwd
57
+ * - have NO write-path conflict with any other task in the group
58
+ *
59
+ * Singletons (groups with one task) are preserved — the caller can decide
60
+ * whether to dispatch them as-is or skip the coalesce entirely.
61
+ *
62
+ * If `enabled` is false, returns `[]` (signal to the caller to skip
63
+ * coalescing — use the original list).
64
+ */
65
+ export function planCoalescedGroups(
66
+ readyTaskIds: string[],
67
+ tasks: TeamTaskState[],
68
+ workflow: WorkflowConfig,
69
+ enabled: boolean,
70
+ maxGroupSize: number = DEFAULT_MAX_GROUP_SIZE,
71
+ maxGroupBytes: number = DEFAULT_MAX_GROUP_BYTES,
72
+ ): CoalescedGroup[] {
73
+ if (!enabled || readyTaskIds.length === 0) return [];
74
+ const taskById = new Map<string, TeamTaskState>(tasks.map((task) => [task.id, task]));
75
+ const stepById = new Map<string, WorkflowStep>(workflow.steps.map((step) => [step.id, step]));
76
+
77
+ // Bucket: key = `${role}\0${cwd}` → task list preserving input order
78
+ const buckets = new Map<string, TeamTaskState[]>();
79
+ for (const taskId of readyTaskIds) {
80
+ const task = taskById.get(taskId);
81
+ if (!task || !task.stepId) continue;
82
+ const step = stepById.get(task.stepId);
83
+ if (!step) continue;
84
+
85
+ // MVP constraint (real-dispatch safety): only coalesce READ_ONLY roles.
86
+ // Writing roles (executor, writer, implementer) require per-step hooks
87
+ // (worktree, preStepScript, verify) that don't compose in one worker.
88
+ if (permissionForRole(task.role) !== "read_only") continue;
89
+
90
+ // MVP constraint: reject if step has any per-step concern that
91
+ // doesn't compose in a multi-task worker. These flags set up side
92
+ // effects per-task that can't be combined.
93
+ if (step.worktree) continue;
94
+ if (step.preStepScript) continue;
95
+ if (step.verify) continue;
96
+ if (step.output !== undefined && step.output !== false && step.output !== "") continue;
97
+
98
+ const key = `${task.role}\0${task.cwd}`;
99
+ const list = buckets.get(key);
100
+ if (list) list.push(task);
101
+ else buckets.set(key, [task]);
102
+ }
103
+
104
+ // Within each bucket, split further by write-path safety (groups of tasks
105
+ // that all have distinct write outputs), cap at maxGroupSize, AND cap
106
+ // combined estimated prompt size at maxGroupBytes. This addresses
107
+ // design-doc risk R4 (context-budget overflow): a worker handling N
108
+ // micro-tasks with large dependency contexts could exceed the model's
109
+ // context window. The byte cap is a heuristic, not a guarantee — the
110
+ // actual prompt includes rendered workspace trees and skill blocks too.
111
+ const groups: CoalescedGroup[] = [];
112
+ for (const [key, bucketTasks] of buckets) {
113
+ const [role, cwd] = key.split("\0");
114
+ const subgroups = splitByWriteSafety(bucketTasks, stepById);
115
+ for (const subgroup of subgroups) {
116
+ // Chunk by BOTH count and bytes. Greedy: keep adding tasks while
117
+ // adding the next one wouldn't exceed either limit. When a
118
+ // single task exceeds the byte limit by itself (rare), include
119
+ // it anyway as a singleton group so it's at least eligible.
120
+ let chunkStart = 0;
121
+ let chunkBytes = 0;
122
+ for (let i = 0; i < subgroup.length; i += 1) {
123
+ const task = subgroup[i]!;
124
+ const step = task.stepId ? stepById.get(task.stepId) : undefined;
125
+ const taskSize = estimateTaskPromptSize(task, step);
126
+ const wouldBeCount = i - chunkStart + 1;
127
+ const wouldBeBytes = chunkBytes + taskSize;
128
+ const overCount = wouldBeCount > maxGroupSize;
129
+ const overBytes = i > chunkStart && wouldBeBytes > maxGroupBytes;
130
+ if ((overCount || overBytes) && i > chunkStart) {
131
+ // Flush current chunk as a group.
132
+ const slice = subgroup.slice(chunkStart, i);
133
+ groups.push(buildGroup(slice, role, cwd));
134
+ chunkStart = i;
135
+ chunkBytes = taskSize;
136
+ } else {
137
+ chunkBytes += taskSize;
138
+ }
139
+ }
140
+ // Flush final chunk.
141
+ if (chunkStart < subgroup.length) {
142
+ const slice = subgroup.slice(chunkStart);
143
+ groups.push(buildGroup(slice, role, cwd));
144
+ }
145
+ }
146
+ }
147
+ return groups;
148
+ }
149
+
150
+ function buildGroup(tasks: TeamTaskState[], role: string, cwd: string): CoalescedGroup {
151
+ return {
152
+ id: tasks.length === 1 ? tasks[0]!.id : tasks.map((t) => t.id).join("+"),
153
+ role,
154
+ cwd,
155
+ tasks,
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Estimate the prompt-bytes contribution of a single task within a
161
+ * coalesced group. The actual prompt is much larger (workspace tree,
162
+ * skill block, dependency context) but the task-specific contribution
163
+ * is roughly proportional to the step's task text + a fixed per-task
164
+ * overhead for delimiters and headers.
165
+ */
166
+ function estimateTaskPromptSize(task: TeamTaskState, step: WorkflowStep | undefined): number {
167
+ const taskText = step?.task?.length ?? 0;
168
+ const packetEstimate = 500; // task packet / scope / verification estimate
169
+ const delimiterOverhead = 200; // `<<<TASK_RESULT:id>>>` + section header
170
+ return taskText + packetEstimate + delimiterOverhead;
171
+ }
172
+
173
+ /**
174
+ * Split a bucket of tasks into write-safe subgroups: a subgroup may include
175
+ * tasks only if no two of them have the same `step.output`.
176
+ *
177
+ * Greedy: first task always goes to current group; subsequent tasks join the
178
+ * current group only if they don't conflict with any already-in-group member.
179
+ * Otherwise, they start (and extend) the next subgroup.
180
+ */
181
+ function splitByWriteSafety(bucketTasks: TeamTaskState[], stepById: Map<string, WorkflowStep>): TeamTaskState[][] {
182
+ const result: TeamTaskState[][] = [];
183
+ for (const task of bucketTasks) {
184
+ const step = task.stepId ? stepById.get(task.stepId) : undefined;
185
+ const writePath = step?.output;
186
+ let placed = false;
187
+ for (const group of result) {
188
+ let conflict = false;
189
+ for (const other of group) {
190
+ const otherStep = other.stepId ? stepById.get(other.stepId) : undefined;
191
+ if (typeof writePath === "string" && typeof otherStep?.output === "string" && writePath === otherStep.output) {
192
+ conflict = true;
193
+ break;
194
+ }
195
+ }
196
+ if (!conflict) {
197
+ group.push(task);
198
+ placed = true;
199
+ break;
200
+ }
201
+ }
202
+ if (!placed) result.push([task]);
203
+ }
204
+ return result;
205
+ }
206
+
207
+ /**
208
+ * Flatten a list of `CoalescedGroup` back into a flat task-id list (one entry
209
+ * per group). For a group of N tasks, this returns N task IDs in order. When
210
+ * M6 is enabled upstream, the caller can use this to count "effective ready
211
+ * units" (groups vs raw tasks).
212
+ */
213
+ export function flattenGroupIds(groups: CoalescedGroup[]): string[] {
214
+ return groups.flatMap((group) => group.tasks.map((task) => task.id));
215
+ }
216
+
217
+ /**
218
+ * Find the coalesced group that contains the given task ID.
219
+ * Returns undefined if the task is not in any group (e.g., it was excluded
220
+ * by MVP constraints or wasn't part of the coalesced set).
221
+ *
222
+ * Used by the team-runner dispatch loop to look up a task's group metadata
223
+ * (role, cwd, group ID, sibling task IDs) when iterating the flat ready
224
+ * list and discovering which tasks should be batched together.
225
+ */
226
+ export function findGroupContainingTask(groups: CoalescedGroup[], taskId: string): CoalescedGroup | undefined {
227
+ for (const group of groups) {
228
+ if (group.tasks.some((task) => task.id === taskId)) return group;
229
+ }
230
+ return undefined;
231
+ }
232
+
233
+ /**
234
+ * Expand a list of task IDs into a list of dispatch units, where each unit
235
+ * is either a single task (for singletons / non-coalescable tasks) or a
236
+ * CoalescedGroup (for multi-task batches). The returned units can be fed
237
+ * directly into mapConcurrent.
238
+ *
239
+ * Tasks not present in any coalesced group are returned as singleton units.
240
+ */
241
+ export type DispatchUnit = { kind: "singleton"; taskId: string } | { kind: "group"; group: CoalescedGroup };
242
+
243
+ export function buildDispatchUnits(readyTaskIds: string[], coalescedGroups: CoalescedGroup[]): DispatchUnit[] {
244
+ const groupByTaskId = new Map<string, CoalescedGroup>();
245
+ for (const group of coalescedGroups) {
246
+ if (group.tasks.length < 2) continue; // singletons handled separately
247
+ for (const task of group.tasks) {
248
+ groupByTaskId.set(task.id, group);
249
+ }
250
+ }
251
+ const visited = new Set<string>();
252
+ const units: DispatchUnit[] = [];
253
+ // First pass: emit groups (one unit per group, in input order)
254
+ for (const taskId of readyTaskIds) {
255
+ const group = groupByTaskId.get(taskId);
256
+ if (!group) continue;
257
+ if (visited.has(group.id)) continue;
258
+ visited.add(group.id);
259
+ for (const t of group.tasks) visited.add(t.id);
260
+ units.push({ kind: "group", group });
261
+ }
262
+ // Second pass: emit singletons (and tasks whose group is size 1)
263
+ for (const taskId of readyTaskIds) {
264
+ if (visited.has(taskId)) continue;
265
+ units.push({ kind: "singleton", taskId });
266
+ }
267
+ return units;
268
+ }
@@ -23,17 +23,21 @@ export interface SummaryResult {
23
23
  }
24
24
 
25
25
  export interface SummaryOptions {
26
- minBodyLines?: number; // default 4
26
+ minBodyLines?: number; // default 4
27
27
  minCommentLines?: number; // default 6
28
28
  }
29
29
 
30
30
  // ── Language detection ──
31
31
 
32
32
  const EXT_MAP: ReadonlyMap<string, string> = new Map([
33
- [".ts", "typescript"], [".tsx", "typescript"],
34
- [".js", "javascript"], [".jsx", "javascript"],
35
- [".mjs", "javascript"], [".cjs", "javascript"],
36
- [".py", "python"], [".rs", "rust"],
33
+ [".ts", "typescript"],
34
+ [".tsx", "typescript"],
35
+ [".js", "javascript"],
36
+ [".jsx", "javascript"],
37
+ [".mjs", "javascript"],
38
+ [".cjs", "javascript"],
39
+ [".py", "python"],
40
+ [".rs", "rust"],
37
41
  ]);
38
42
 
39
43
  export function detectLanguage(filePath: string): string | null {
@@ -44,7 +48,10 @@ export function detectLanguage(filePath: string): string | null {
44
48
 
45
49
  // ── Internal range helpers ──
46
50
 
47
- interface Range { start: number; end: number; }
51
+ interface Range {
52
+ start: number;
53
+ end: number;
54
+ }
48
55
 
49
56
  function mergeRanges(ranges: Range[]): Range[] {
50
57
  if (ranges.length === 0) return [];
@@ -73,8 +80,12 @@ function findBraceRanges(lines: string[], openPattern: RegExp, minBody: number):
73
80
  const start = i;
74
81
  for (let j = i; j < lines.length; j++) {
75
82
  for (const ch of lines[j]) {
76
- if (ch === "{") { depth++; foundOpen = true; }
77
- else if (ch === "}") { depth--; }
83
+ if (ch === "{") {
84
+ depth++;
85
+ foundOpen = true;
86
+ } else if (ch === "}") {
87
+ depth--;
88
+ }
78
89
  }
79
90
  if (foundOpen && depth <= 0) {
80
91
  if (j - start - 1 >= minBody) ranges.push({ start: start + 1, end: j - 1 });
@@ -107,12 +118,14 @@ function blockCommentRanges(lines: string[], minComment: number): Range[] {
107
118
  let i = 0;
108
119
  while (i < lines.length) {
109
120
  const idx = lines[i].indexOf("/*");
110
- if (idx === -1 || lines[i].includes("*/", idx + 2)) { i++; continue; }
121
+ if (idx === -1 || lines[i].includes("*/", idx + 2)) {
122
+ i++;
123
+ continue;
124
+ }
111
125
  const openLine = i;
112
126
  let j = i + 1;
113
127
  while (j < lines.length && !lines[j].includes("*/")) j++;
114
- if (j < lines.length && j - openLine - 1 >= minComment)
115
- ranges.push({ start: openLine + 1, end: j - 1 });
128
+ if (j < lines.length && j - openLine - 1 >= minComment) ranges.push({ start: openLine + 1, end: j - 1 });
116
129
  i = j + 1;
117
130
  }
118
131
  return ranges;
@@ -125,10 +138,17 @@ const PY_IMPORT_RE = /^\s*(import\s|from\s+\S+\s+import\s)/;
125
138
 
126
139
  function importGroupRanges(lines: string[], pattern: RegExp): Range[] {
127
140
  const groups: Array<{ start: number; end: number }> = [];
128
- let gs = -1, last = -1;
141
+ let gs = -1,
142
+ last = -1;
129
143
  for (let i = 0; i < lines.length; i++) {
130
- if (pattern.test(lines[i])) { if (gs === -1) gs = i; last = i; }
131
- else if (gs !== -1 && i > last) { groups.push({ start: gs, end: last }); gs = -1; last = -1; }
144
+ if (pattern.test(lines[i])) {
145
+ if (gs === -1) gs = i;
146
+ last = i;
147
+ } else if (gs !== -1 && i > last) {
148
+ groups.push({ start: gs, end: last });
149
+ gs = -1;
150
+ last = -1;
151
+ }
132
152
  }
133
153
  if (gs !== -1) groups.push({ start: gs, end: last });
134
154
  const ranges: Range[] = [];
@@ -146,7 +166,8 @@ function pythonRanges(lines: string[], minBody: number): Range[] {
146
166
  const m = /^(\s*)(async\s+)?def\s/.exec(lines[i]) || /^(\s*)class\s/.exec(lines[i]);
147
167
  if (!m) continue;
148
168
  const base = m[1].length;
149
- let bs = -1, be = -1;
169
+ let bs = -1,
170
+ be = -1;
150
171
  for (let j = i + 1; j < lines.length; j++) {
151
172
  if (lines[j].trim() === "") continue;
152
173
  const indent = lines[j].length - lines[j].trimStart().length;
@@ -180,22 +201,26 @@ function rustRanges(lines: string[], minBody: number): Range[] {
180
201
 
181
202
  function fullResult(language: string | null, totalLines: number, code: string): SummaryResult {
182
203
  return {
183
- language, totalLines, elided: false,
204
+ language,
205
+ totalLines,
206
+ elided: false,
184
207
  segments: [{ kind: "kept", startLine: 1, endLine: totalLines, text: code }],
185
208
  rendered: code,
186
209
  };
187
210
  }
188
211
 
189
- export function summarizeCode(
190
- code: string,
191
- language: string | null,
192
- options?: SummaryOptions,
193
- ): SummaryResult {
212
+ export function summarizeCode(code: string, language: string | null, options?: SummaryOptions): SummaryResult {
194
213
  const minBody = options?.minBodyLines ?? 4;
195
214
  const minComment = options?.minCommentLines ?? 6;
196
215
 
197
216
  if (!code || code.trim() === "") {
198
- return { language, totalLines: 0, elided: false, segments: [], rendered: "" };
217
+ return {
218
+ language,
219
+ totalLines: 0,
220
+ elided: false,
221
+ segments: [],
222
+ rendered: "",
223
+ };
199
224
  }
200
225
 
201
226
  const lines = code.split("\n");
@@ -227,13 +252,27 @@ export function summarizeCode(
227
252
  let cursor = 0;
228
253
  for (const r of ranges) {
229
254
  if (cursor < r.start) {
230
- segments.push({ kind: "kept", startLine: cursor + 1, endLine: r.start, text: lines.slice(cursor, r.start).join("\n") });
255
+ segments.push({
256
+ kind: "kept",
257
+ startLine: cursor + 1,
258
+ endLine: r.start,
259
+ text: lines.slice(cursor, r.start).join("\n"),
260
+ });
231
261
  }
232
- segments.push({ kind: "elided", startLine: r.start + 1, endLine: r.end + 1 });
262
+ segments.push({
263
+ kind: "elided",
264
+ startLine: r.start + 1,
265
+ endLine: r.end + 1,
266
+ });
233
267
  cursor = r.end + 1;
234
268
  }
235
269
  if (cursor < totalLines) {
236
- segments.push({ kind: "kept", startLine: cursor + 1, endLine: totalLines, text: lines.slice(cursor).join("\n") });
270
+ segments.push({
271
+ kind: "kept",
272
+ startLine: cursor + 1,
273
+ endLine: totalLines,
274
+ text: lines.slice(cursor).join("\n"),
275
+ });
237
276
  }
238
277
 
239
278
  // Render
@@ -243,5 +282,11 @@ export function summarizeCode(
243
282
  else parts.push(` ... ${seg.endLine - seg.startLine + 1} lines elided ...`);
244
283
  }
245
284
 
246
- return { language, totalLines, elided: true, segments, rendered: parts.join("\n") };
285
+ return {
286
+ language,
287
+ totalLines,
288
+ elided: true,
289
+ segments,
290
+ rendered: parts.join("\n"),
291
+ };
247
292
  }
@@ -5,9 +5,19 @@
5
5
  * `../../runtime/compact-stages/`) rather than reaching into individual
6
6
  * stage files, so internal refactors do not break the public surface.
7
7
  */
8
- export { AnsiStripStage, ANSI_STRIP_STAGE } from "./ansi-strip-stage.ts";
9
- export { BlankCollapseStage, BLANK_COLLAPSE_STAGE } from "./blank-collapse-stage.ts";
10
- export { DeduplicateStage, DEDUPLICATE_STAGE } from "./deduplicate-stage.ts";
11
- export { TruncationStage, type TruncationMarkerConfig } from "./truncation-stage.ts";
8
+ export { ANSI_STRIP_STAGE, AnsiStripStage } from "./ansi-strip-stage.ts";
9
+ export {
10
+ BLANK_COLLAPSE_STAGE,
11
+ BlankCollapseStage,
12
+ } from "./blank-collapse-stage.ts";
13
+ export { DEDUPLICATE_STAGE, DeduplicateStage } from "./deduplicate-stage.ts";
12
14
  export { HeadSnapStage, type HeadSnapStageConfig } from "./head-snap-stage.ts";
13
- export { TailCaptureStage, TAIL_CAPTURE_STREAM_STAGE, type TailCaptureStageConfig } from "./tail-capture-stage.ts";
15
+ export {
16
+ TAIL_CAPTURE_STREAM_STAGE,
17
+ TailCaptureStage,
18
+ type TailCaptureStageConfig,
19
+ } from "./tail-capture-stage.ts";
20
+ export {
21
+ type TruncationMarkerConfig,
22
+ TruncationStage,
23
+ } from "./truncation-stage.ts";
@@ -42,7 +42,9 @@ export class TailCaptureStage implements ICompactStage {
42
42
  const hasChars = typeof config.maxChars === "number";
43
43
  const hasBytes = typeof config.maxBytes === "number";
44
44
  if (hasChars === hasBytes) {
45
- throw new Error(`TailCaptureStage requires exactly one of maxChars or maxBytes (got chars=${config.maxChars} bytes=${config.maxBytes})`);
45
+ throw new Error(
46
+ `TailCaptureStage requires exactly one of maxChars or maxBytes (got chars=${config.maxChars} bytes=${config.maxBytes})`,
47
+ );
46
48
  }
47
49
  if (hasChars && (config.maxChars as number) <= 0) throw new Error(`TailCaptureStage: maxChars must be > 0, got ${config.maxChars}`);
48
50
  if (hasBytes && (config.maxBytes as number) <= 0) throw new Error(`TailCaptureStage: maxBytes must be > 0, got ${config.maxBytes}`);
@@ -69,4 +71,7 @@ export class TailCaptureStage implements ICompactStage {
69
71
  }
70
72
 
71
73
  /** Singleton: char-cap tail capture with no marker (for `stream-preview.ts` textBuffer). */
72
- export const TAIL_CAPTURE_STREAM_STAGE = new TailCaptureStage({ maxChars: 16_384, id: "tail-capture-stream" });
74
+ export const TAIL_CAPTURE_STREAM_STAGE = new TailCaptureStage({
75
+ maxChars: 16_384,
76
+ id: "tail-capture-stream",
77
+ });
@@ -41,7 +41,10 @@ export class TruncationStage implements ICompactStage {
41
41
  private readonly marker: TruncationMarkerConfig;
42
42
  constructor(
43
43
  maxChars: number,
44
- opts: { preserveImportant?: boolean; marker?: Partial<TruncationMarkerConfig> } = {},
44
+ opts: {
45
+ preserveImportant?: boolean;
46
+ marker?: Partial<TruncationMarkerConfig>;
47
+ } = {},
45
48
  ) {
46
49
  if (!Number.isFinite(maxChars) || maxChars <= 0) {
47
50
  throw new Error(`TruncationStage: maxChars must be a positive finite number, got ${maxChars}`);
@@ -5,13 +5,18 @@
5
5
  * Distilled from pi-autoresearch's compaction-summary pattern.
6
6
  */
7
7
 
8
- class GiantLineFallbackError extends Error { constructor() { super("GIANT_LINE_FALLBACK"); this.name = "GiantLineFallbackError"; } }
8
+ class GiantLineFallbackError extends Error {
9
+ constructor() {
10
+ super("GIANT_LINE_FALLBACK");
11
+ this.name = "GiantLineFallbackError";
12
+ }
13
+ }
9
14
 
10
15
  import * as fs from "node:fs";
11
16
  import * as path from "node:path";
12
- import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
13
17
  import { readJsonFile } from "../state/atomic-write.ts";
14
18
  import type { TeamEvent } from "../state/event-log.ts";
19
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
15
20
 
16
21
  /** Maximum number of events to read from the tail of events.jsonl. */
17
22
  const MAX_TAIL_EVENTS = 100;
@@ -57,7 +62,10 @@ function readTailLines(filePath: string, maxLines: number): string[] {
57
62
  // For small files, just read everything
58
63
  if (fileSize <= TAIL_MAX_READ) {
59
64
  const content = fs.readFileSync(filePath, "utf-8");
60
- return content.split("\n").filter((line) => line.trim().length > 0).slice(-maxLines);
65
+ return content
66
+ .split("\n")
67
+ .filter((line) => line.trim().length > 0)
68
+ .slice(-maxLines);
61
69
  }
62
70
 
63
71
  // For large files, read only the last chunk.
@@ -98,7 +106,10 @@ function readTailLines(filePath: string, maxLines: number): string[] {
98
106
  const stat = fs.statSync(filePath);
99
107
  if (stat.size > MAX_FALLBACK_READ) return [];
100
108
  const content = fs.readFileSync(filePath, "utf-8");
101
- return content.split("\n").filter((line) => line.trim().length > 0).slice(-maxLines);
109
+ return content
110
+ .split("\n")
111
+ .filter((line) => line.trim().length > 0)
112
+ .slice(-maxLines);
102
113
  }
103
114
  return [];
104
115
  }
@@ -212,9 +223,7 @@ export function buildCompactionSummary(stateRoot: string): string {
212
223
  }
213
224
 
214
225
  // Section: Recent Task Results
215
- const completedTasks = tasks
216
- .filter((t) => t.status === "completed" || t.status === "failed")
217
- .slice(-MAX_RECENT_RESULTS);
226
+ const completedTasks = tasks.filter((t) => t.status === "completed" || t.status === "failed").slice(-MAX_RECENT_RESULTS);
218
227
 
219
228
  if (completedTasks.length > 0) {
220
229
  sections.push("");
@@ -242,9 +251,7 @@ export function buildCompactionSummary(stateRoot: string): string {
242
251
 
243
252
  // Section: Next Steps (pending/queued tasks)
244
253
  const pendingStatuses = new Set(["queued", "waiting", "running"]);
245
- const pendingTasks = tasks.filter(
246
- (t) => pendingStatuses.has(t.status),
247
- );
254
+ const pendingTasks = tasks.filter((t) => pendingStatuses.has(t.status));
248
255
  if (pendingTasks.length > 0) {
249
256
  sections.push("");
250
257
  sections.push("## Next Steps");
@@ -1,5 +1,5 @@
1
1
  import * as fs from "node:fs";
2
- import type { TeamTaskState, TeamRunManifest } from "../state/types.ts";
2
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
3
3
 
4
4
  // ============================================================================
5
5
  // Phase 1.2: Completion Mutation Guard — detects tasks that claim success but
@@ -21,13 +21,27 @@ export interface CompletionMutationGuardResult {
21
21
  }
22
22
 
23
23
  const MUTATING_ROLES = new Set(["executor", "test-engineer"]);
24
- const MUTATING_TOOLS = new Set(["edit", "write", "multi_edit", "apply_patch", "replace_in_file", "insert", "delete_files", "create_file", "overwrite", "patch"]);
25
- const READ_ONLY_COMMANDS = /^(pwd|ls|dir|cat|type|sed|grep|rg|find|git\s+(status|diff|log|show|branch|remote|rev-parse|ls-files)|npm\s+(test|run\s+(typecheck|check|lint|test|ci))|node\s+--test)\b/i;
26
- const MUTATING_COMMANDS = /\b(rm\s+-|del\s+|erase\s+|mv\s+|move\s+|cp\s+|copy\s+|mkdir\b|touch\b|git\s+(add|commit|push|reset|clean|checkout|switch|merge|rebase|stash)|npm\s+(install|i|uninstall|publish|version)|pnpm\s+(add|install|remove)|yarn\s+(add|install|remove)|python\b.*>|node\b.*>|echo\b.*>|Set-Content|Out-File|sed\s+-i|tee\b|dd\b.*of=|wget\b.*-O|curl\b.*-o)\b/i;
27
- const READ_ONLY_HINTS = /\b(read-only|no edits?|do not edit|không sửa|khong sua|chỉ đọc|chi doc|plan only|chỉ lập plan|review only|audit only)\b/i;
24
+ const MUTATING_TOOLS = new Set([
25
+ "edit",
26
+ "write",
27
+ "multi_edit",
28
+ "apply_patch",
29
+ "replace_in_file",
30
+ "insert",
31
+ "delete_files",
32
+ "create_file",
33
+ "overwrite",
34
+ "patch",
35
+ ]);
36
+ const READ_ONLY_COMMANDS =
37
+ /^(pwd|ls|dir|cat|type|sed|grep|rg|find|git\s+(status|diff|log|show|branch|remote|rev-parse|ls-files)|npm\s+(test|run\s+(typecheck|check|lint|test|ci))|node\s+--test)\b/i;
38
+ const MUTATING_COMMANDS =
39
+ /\b(rm\s+-|del\s+|erase\s+|mv\s+|move\s+|cp\s+|copy\s+|mkdir\b|touch\b|git\s+(add|commit|push|reset|clean|checkout|switch|merge|rebase|stash)|npm\s+(install|i|uninstall|publish|version)|pnpm\s+(add|install|remove)|yarn\s+(add|install|remove)|python\b.*>|node\b.*>|echo\b.*>|Set-Content|Out-File|sed\s+-i|tee\b|dd\b.*of=|wget\b.*-O|curl\b.*-o)\b/i;
40
+ const READ_ONLY_HINTS =
41
+ /\b(read-only|no edits?|do not edit|không sửa|khong sua|chỉ đọc|chi doc|plan only|chỉ lập plan|review only|audit only)\b/i;
28
42
 
29
43
  function asRecord(value: unknown): Record<string, unknown> | undefined {
30
- return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : undefined;
44
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
31
45
  }
32
46
 
33
47
  function commandText(value: unknown): string {
@@ -60,7 +74,10 @@ export function collectToolCallsFromEvent(event: unknown): Array<{ tool: string;
60
74
  if (!record) return [];
61
75
  const calls: Array<{ tool: string; args?: unknown }> = [];
62
76
  const directTool = record.toolName ?? record.name ?? record.tool;
63
- if (typeof directTool === "string" && (record.type === "tool_execution_start" || record.type === "toolCall" || record.type === "tool_call")) {
77
+ if (
78
+ typeof directTool === "string" &&
79
+ (record.type === "tool_execution_start" || record.type === "toolCall" || record.type === "tool_call")
80
+ ) {
64
81
  calls.push({ tool: directTool, args: record.args ?? record.input });
65
82
  }
66
83
  const content = Array.isArray(record.content) ? record.content : asRecord(record.message)?.content;
@@ -69,7 +86,8 @@ export function collectToolCallsFromEvent(event: unknown): Array<{ tool: string;
69
86
  const item = asRecord(part);
70
87
  if (!item) continue;
71
88
  const tool = item.name ?? item.toolName ?? item.tool;
72
- if (typeof tool === "string" && (item.type === "toolCall" || item.type === "tool_call" || item.type === "tool_execution_start")) calls.push({ tool, args: item.input ?? item.args });
89
+ if (typeof tool === "string" && (item.type === "toolCall" || item.type === "tool_call" || item.type === "tool_execution_start"))
90
+ calls.push({ tool, args: item.input ?? item.args });
73
91
  }
74
92
  }
75
93
  return calls;
@@ -94,7 +112,11 @@ export function evaluateCompletionMutationGuard(input: CompletionMutationGuardIn
94
112
  const trimmed = line.trim();
95
113
  if (!trimmed) continue;
96
114
  let event: unknown;
97
- try { event = JSON.parse(trimmed); } catch { continue; }
115
+ try {
116
+ event = JSON.parse(trimmed);
117
+ } catch {
118
+ continue;
119
+ }
98
120
  for (const call of collectToolCallsFromEvent(event)) {
99
121
  observedTools.push(call.tool);
100
122
  if (isMutatingTool(call.tool, call.args)) observedMutation = true;
@@ -133,10 +155,7 @@ function isTrivialError(error: string | undefined): boolean {
133
155
  return error.trim().length === 0;
134
156
  }
135
157
 
136
- export function verifyTaskCompletion(
137
- task: TeamTaskState,
138
- manifest: TeamRunManifest,
139
- ): CompletionVerifyResult {
158
+ export function verifyTaskCompletion(task: TeamTaskState, manifest: TeamRunManifest): CompletionVerifyResult {
140
159
  const warnings: string[] = [];
141
160
  let greenLevel = 0;
142
161
 
@@ -156,9 +175,7 @@ export function verifyTaskCompletion(
156
175
  }
157
176
 
158
177
  // Check 4: For implementation tasks, verify artifacts were actually produced
159
- const runArtifacts = manifest.artifacts.filter(
160
- (a) => a.producer === task.id || a.producer === task.agent,
161
- );
178
+ const runArtifacts = manifest.artifacts.filter((a) => a.producer === task.id || a.producer === task.agent);
162
179
  if (runArtifacts.length > 0) {
163
180
  greenLevel += 1;
164
181
  } else if (greenLevel < 3) {