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
@@ -1,12 +1,13 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
3
+ import { DEFAULT_OUTPUT_CONTEXT } from "../config/defaults.ts";
4
4
  import { writeArtifact } from "../state/artifact-store.ts";
5
+ import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
5
6
  import { resolveRealContainedPath } from "../utils/safe-paths.ts";
6
7
  import type { WorkflowStep } from "../workflows/workflow-config.ts";
7
- import { pruneToolOutputs, type ToolResultEntry, type FileEditEvent, DEFAULT_PRUNE_CONFIG } from "./tool-output-pruner.ts";
8
8
  import { applyCompactPipeline } from "./compact-pipeline.ts";
9
9
  import { ANSI_STRIP_STAGE, BLANK_COLLAPSE_STAGE, TruncationStage } from "./compact-stages/index.ts";
10
+ import { DEFAULT_PRUNE_CONFIG, type FileEditEvent, pruneToolOutputs, type ToolResultEntry } from "./tool-output-pruner.ts";
10
11
 
11
12
  export interface DependencyContextEntry {
12
13
  taskId: string;
@@ -23,6 +24,21 @@ export interface DependencyContextEntry {
23
24
  structuredResults?: Record<string, unknown>;
24
25
  artifactsProduced?: string[];
25
26
  usage?: { inputTokens: number; outputTokens: number; durationMs: number };
27
+ /** L4 total-budget: byte length of `resultSummary` (UTF-16 code units).
28
+ * Populated by `collectDependencyOutputContext` so the trim step can sum
29
+ * per-dep sizes without re-computing. Set to 0 when the dep is
30
+ * downgraded to path-only. Optional for backward-compat with existing
31
+ * test fixtures that build entry objects directly. */
32
+ inlineBytes?: number;
33
+ /** L4 priority sort key: derived from the upstream task's `finishedAt`
34
+ * (ms epoch). More recent = higher priority in the trim. Undefined
35
+ * when the dep task never finished (treated as 0). */
36
+ recency?: number;
37
+ /** L4 priority sort key: heuristic relevance in [0, 1]. Higher = more
38
+ * relevant. Derived from the upstream task's `dependsOn` position in
39
+ * the workflow (earlier deps = higher relevance because they were
40
+ * explicitly listed first). Defaults to 0.5 (neutral) when unknown. */
41
+ relevance?: number;
26
42
  }
27
43
 
28
44
  export interface DependencyOutputContext {
@@ -34,7 +50,12 @@ export interface DependencyOutputContext {
34
50
  * and the path is exposed via `fullOutputPath` so the downstream worker
35
51
  * can `read` it back if it needs the dropped middle.
36
52
  */
37
- sharedReads: Array<{ name: string; path: string; content: string; fullOutputPath?: string }>;
53
+ sharedReads: Array<{
54
+ name: string;
55
+ path: string;
56
+ content: string;
57
+ fullOutputPath?: string;
58
+ }>;
38
59
  }
39
60
 
40
61
  function containedExists(filePath: string, baseDir?: string): boolean {
@@ -53,8 +74,26 @@ function containedExists(filePath: string, baseDir?: string): boolean {
53
74
  * bounding memory. Larger than the old inconsistent per-call-site values
54
75
  * (24K/40K/80K) which truncated the same artifact differently depending on
55
76
  * which code path read it.
77
+ *
78
+ * Single source of truth: DEFAULT_OUTPUT_CONTEXT.maxResultInlineBytes
79
+ * (Round 25 / L6 — moved from hardcoded constant to config/defaults.ts).
80
+ */
81
+ export const MAX_RESULT_INLINE_BYTES = DEFAULT_OUTPUT_CONTEXT.maxResultInlineBytes;
82
+
83
+ /**
84
+ * L4 total-budget: cap on the SUM of inline bytes across ALL dependencies
85
+ * for a single downstream worker. 96KB is 3× the per-dep cap (3 × 32KB).
86
+ * Sized so a worker with 3 large dep outputs (e.g. 3 × 32KB truncated)
87
+ * stays below budget and the typical 1–2 dep case never hits the trim.
88
+ * When the sum exceeds this, the trim step in
89
+ * {@link collectDependencyOutputContext} downgrades the lowest-priority
90
+ * deps to path-only (resultPath + fullOutputPath, no inline text) so the
91
+ * downstream worker can `read` the full content on demand. The tee
92
+ * write happens BEFORE the trim, so fullOutputPath is always available.
93
+ *
94
+ * Single source of truth: DEFAULT_OUTPUT_CONTEXT.maxTotalDepInlineBytes.
56
95
  */
57
- export const MAX_RESULT_INLINE_BYTES = 32_000;
96
+ export const MAX_TOTAL_DEP_INLINE_BYTES = DEFAULT_OUTPUT_CONTEXT.maxTotalDepInlineBytes;
58
97
 
59
98
  /**
60
99
  * Tee-recovery multiplier (R2). A shared artifact is teed to disk — so the
@@ -63,8 +102,10 @@ export const MAX_RESULT_INLINE_BYTES = 32_000;
63
102
  * (64 KB) to 1.25 (40 KB) so the 32–64 KB band, where the head+tail split
64
103
  * is already materially lossy for structured content, also gets a recovery
65
104
  * path instead of losing the middle forever.
105
+ *
106
+ * Single source of truth: DEFAULT_OUTPUT_CONTEXT.teeThresholdMultiplier.
66
107
  */
67
- export const TEE_THRESHOLD_MULTIPLIER = 1.25;
108
+ export const TEE_THRESHOLD_MULTIPLIER = DEFAULT_OUTPUT_CONTEXT.teeThresholdMultiplier;
68
109
 
69
110
  /**
70
111
  * Read a file and return its content, truncating to a head+tail slice if it
@@ -161,7 +202,12 @@ export function readIfSmallWithTee(
161
202
  BLANK_COLLAPSE_STAGE,
162
203
  new TruncationStage(maxChars, {
163
204
  preserveImportant: true,
164
- marker: { verb: "truncated", unit: "chars", headSeparator: "\n\n", tailSeparator: "\n" },
205
+ marker: {
206
+ verb: "truncated",
207
+ unit: "chars",
208
+ headSeparator: "\n\n",
209
+ tailSeparator: "\n",
210
+ },
165
211
  }),
166
212
  ]);
167
213
  return fullOutputPath ? { content: result.text, fullOutputPath } : { content: result.text };
@@ -190,7 +236,8 @@ export function readIfSmall(filePath: string, baseDir?: string): string | undefi
190
236
 
191
237
  function safeSharedName(name: string): string {
192
238
  const normalized = name.replaceAll("\\", "/").replace(/^\.\/+/, "");
193
- if (!normalized || normalized.split("/").some((segment) => segment === "..") || path.isAbsolute(normalized)) throw new Error(`Invalid shared artifact name: ${name}`);
239
+ if (!normalized || normalized.split("/").some((segment) => segment === "..") || path.isAbsolute(normalized))
240
+ throw new Error(`Invalid shared artifact name: ${name}`);
194
241
  return normalized;
195
242
  }
196
243
 
@@ -232,6 +279,66 @@ function aggregateUsage(task: TeamTaskState): DependencyContextEntry["usage"] {
232
279
  return { inputTokens, outputTokens, durationMs };
233
280
  }
234
281
 
282
+ /**
283
+ * L4 total-budget trim. When the SUM of `inlineBytes` across all deps
284
+ * exceeds {@link MAX_TOTAL_DEP_INLINE_BYTES}, downgrade the lowest-priority
285
+ * deps to path-only (clear inline text, keep resultPath + fullOutputPath)
286
+ * so the downstream worker can `read` the full content on demand.
287
+ *
288
+ * Priority order (highest kept first):
289
+ * 1. status in {failed, needs-attention} (worker MUST see the failure)
290
+ * 2. recency descending (newer outputs more relevant)
291
+ * 3. relevance descending (workflow author-declared order)
292
+ * 4. tie-breaker: original array index (stable sort)
293
+ *
294
+ * Algorithm: sort by priority (descending), then walk top-down, keeping
295
+ * each dep until the running total would exceed the budget. The remaining
296
+ * deps are returned with `resultSummary=""` and `inlineBytes=0`; the
297
+ * `resultPath` and `fullOutputPath` fields are preserved so the worker
298
+ * can `read` them. Stable: the original input order is preserved for
299
+ * entries at the same priority level.
300
+ *
301
+ * Pure function — no I/O. Exported for unit testing.
302
+ */
303
+ export function enforceDependencyInlineBudget(dependencies: DependencyContextEntry[]): DependencyContextEntry[] {
304
+ if (dependencies.length === 0) return dependencies;
305
+ const totalInlineBytes = dependencies.reduce((sum, dep) => sum + (dep.inlineBytes ?? 0), 0);
306
+ if (totalInlineBytes <= MAX_TOTAL_DEP_INLINE_BYTES) return dependencies;
307
+ // Build an index-keyed array so we can sort AND preserve the original
308
+ // position (for stable output order). Each entry gets a numeric
309
+ // priority score (higher = more important) and a stable tie-breaker.
310
+ const indexed = dependencies.map((dep, originalIndex) => {
311
+ const highPriority = dep.status === "failed" || dep.status === "needs-attention";
312
+ // Score: (highPriority?1000:0) + (recency/1e10) + (relevance*10) - (index*0.001)
313
+ // The 1000 base ensures high-priority wins even with no recency/relevance.
314
+ // recency/1e10 keeps it in [0, 1] for ms timestamps up to 1e13 (~year 2286).
315
+ // relevance*10 puts it in [0, 10].
316
+ // index*0.001 keeps stable order within the same bucket.
317
+ const score = (highPriority ? 1000 : 0) + (dep.recency ?? 0) / 1e10 + (dep.relevance ?? 0) * 10 - originalIndex * 0.001;
318
+ return { dep, originalIndex, score };
319
+ });
320
+ const sorted = [...indexed].sort((a, b) => b.score - a.score);
321
+ let used = 0;
322
+ const keepSet = new Set<number>(); // originalIndex values to KEEP inline
323
+ for (const item of sorted) {
324
+ const next = used + (item.dep.inlineBytes ?? 0);
325
+ if (next <= MAX_TOTAL_DEP_INLINE_BYTES) {
326
+ used = next;
327
+ keepSet.add(item.originalIndex);
328
+ }
329
+ }
330
+ // Return entries in original order; downgrade ones NOT in keepSet.
331
+ return dependencies.map((dep, originalIndex) => {
332
+ if (keepSet.has(originalIndex)) return dep;
333
+ return {
334
+ ...dep,
335
+ resultSummary: "",
336
+ structuredResults: undefined,
337
+ inlineBytes: 0,
338
+ };
339
+ });
340
+ }
341
+
235
342
  /**
236
343
  * Apply staleness-aware pruning to shared reads before they are injected
237
344
  * into a downstream worker's prompt. Converts shared reads to generic
@@ -273,66 +380,123 @@ function pruneSharedReads(
273
380
  if (typeof artifact !== "string") continue;
274
381
  // Map artifact path (relative to artifactsRoot) to absolute and
275
382
  // check against read targets.
276
- fileEdits.push({ target: path.resolve(artifactsRoot, artifact), index: reads.length + depIndex });
383
+ fileEdits.push({
384
+ target: path.resolve(artifactsRoot, artifact),
385
+ index: reads.length + depIndex,
386
+ });
277
387
  }
278
388
  }
279
389
  const pruned = pruneToolOutputs(entries, DEFAULT_PRUNE_CONFIG);
280
390
  if (pruned.prunedCount === 0) return reads;
281
391
  // Map pruned entries back to the shared-read shape.
282
- return pruned.results.map((entry, index) => ({ ...reads[index]!, content: entry.content }));
392
+ return pruned.results.map((entry, index) => ({
393
+ ...reads[index]!,
394
+ content: entry.content,
395
+ }));
283
396
  }
284
397
 
285
- export function collectDependencyOutputContext(manifest: TeamRunManifest, tasks: TeamTaskState[], task: TeamTaskState, step: WorkflowStep): DependencyOutputContext {
398
+ export function collectDependencyOutputContext(
399
+ manifest: TeamRunManifest,
400
+ tasks: TeamTaskState[],
401
+ task: TeamTaskState,
402
+ step: WorkflowStep,
403
+ ): DependencyOutputContext {
286
404
  const byStep = new Map(tasks.map((item) => [item.stepId, item]).filter((entry): entry is [string, TeamTaskState] => Boolean(entry[0])));
287
405
  const byId = new Map(tasks.map((item) => [item.id, item]));
288
- const dependencies = task.dependsOn.map((dep) => byStep.get(dep) ?? byId.get(dep)).filter((item): item is TeamTaskState => Boolean(item)).map((item) => {
289
- const fullOutputPath = item.resultArtifact ? teePathForArtifact(manifest.artifactsRoot, task.id, item.id) : undefined;
290
- const teeResult = item.resultArtifact
291
- ? readIfSmallWithTee(item.resultArtifact.path, { baseDir: manifest.artifactsRoot, ...(fullOutputPath ? { tee: { fullOutputPath } } : {}) })
292
- : undefined;
293
- const resultText = teeResult?.content;
294
- return {
295
- taskId: item.id,
296
- role: item.role,
297
- status: item.status,
298
- resultSummary: resultText ?? "",
299
- resultPath: item.resultArtifact?.path,
300
- ...(teeResult?.fullOutputPath ? { fullOutputPath: teeResult.fullOutputPath } : {}),
301
- structuredResults: resultText ? tryParseJson(resultText) : undefined,
302
- artifactsProduced: listTaskArtifacts(manifest, item.id),
303
- usage: aggregateUsage(item),
304
- };
305
- });
306
- const rawSharedReads = (step.reads === false ? [] : step.reads ?? []).map((name) => {
307
- const filePath = sharedPath(manifest, name);
308
- // P1-A tee-recovery: when the shared artifact is large enough that the
309
- // 75/25 head+tail split is materially lossy (>2× MAX_RESULT_INLINE_BYTES),
310
- // tee the full content to ${artifactsRoot}/tee/${taskId}-${name}.full.txt
311
- // and expose the path so the downstream worker can `read` the full file
312
- // if it needs the dropped middle. The truncated content is still
313
- // included inline; tee is an enhancement, not a hard dependency. Tee
314
- // write is best-effort (writeTeeFile swallows I/O errors and the result
315
- // simply omits fullOutputPath in that case).
316
- const teePath = teePathForArtifact(manifest.artifactsRoot, task.id, name);
317
- const teeResult = readIfSmallWithTee(filePath, {
318
- baseDir: path.resolve(manifest.artifactsRoot, "shared"),
319
- tee: { fullOutputPath: teePath },
406
+ // L4 priority keys: build (a) the position of each dep in the workflow's
407
+ // `dependsOn:` list (earlier = more relevant because the workflow author
408
+ // declared it first), and (b) the finishedAt timestamp of each dep task
409
+ // (used for recency sort in the trim step). Both are looked up against
410
+ // `task.dependsOn` (the declared order) and `tasks` (finished timestamps).
411
+ const declaredOrder = new Map<string, number>();
412
+ task.dependsOn.forEach((depId, index) => declaredOrder.set(depId, index));
413
+ const recencyByTaskId = new Map<string, number>();
414
+ for (const item of tasks) {
415
+ if (!item.finishedAt) continue;
416
+ const ms = new Date(item.finishedAt).getTime();
417
+ if (Number.isFinite(ms)) recencyByTaskId.set(item.id, ms);
418
+ }
419
+ const dependencies = task.dependsOn
420
+ .map((dep) => byStep.get(dep) ?? byId.get(dep))
421
+ .filter((item): item is TeamTaskState => Boolean(item))
422
+ .map((item) => {
423
+ const fullOutputPath = item.resultArtifact ? teePathForArtifact(manifest.artifactsRoot, task.id, item.id) : undefined;
424
+ const teeResult = item.resultArtifact
425
+ ? readIfSmallWithTee(item.resultArtifact.path, {
426
+ baseDir: manifest.artifactsRoot,
427
+ ...(fullOutputPath ? { tee: { fullOutputPath } } : {}),
428
+ })
429
+ : undefined;
430
+ const resultText = teeResult?.content;
431
+ const inlineBytes = resultText?.length ?? 0;
432
+ // L4 priority keys: recency from the dep's finishedAt, relevance
433
+ // from the dep's declared position in `task.dependsOn` (inverted
434
+ // to [0,1]: position 0 → 1.0, position N → max(0, 1 - N*0.1)).
435
+ const position = declaredOrder.get(item.id) ?? declaredOrder.get(item.stepId ?? "") ?? 0;
436
+ const relevance = Math.max(0, 1 - position * 0.1);
437
+ return {
438
+ taskId: item.id,
439
+ role: item.role,
440
+ status: item.status,
441
+ resultSummary: resultText ?? "",
442
+ resultPath: item.resultArtifact?.path,
443
+ ...(teeResult?.fullOutputPath ? { fullOutputPath: teeResult.fullOutputPath } : {}),
444
+ structuredResults: resultText ? tryParseJson(resultText) : undefined,
445
+ artifactsProduced: listTaskArtifacts(manifest, item.id),
446
+ usage: aggregateUsage(item),
447
+ inlineBytes,
448
+ recency: recencyByTaskId.get(item.id),
449
+ relevance,
450
+ };
320
451
  });
321
- if (teeResult === undefined) return { name, path: filePath, content: "" };
322
- const entry: { name: string; path: string; content: string; fullOutputPath?: string } = {
323
- name,
324
- path: filePath,
325
- content: teeResult.content,
326
- };
327
- if (teeResult.fullOutputPath) entry.fullOutputPath = teeResult.fullOutputPath;
328
- return entry;
329
- }).filter((item) => item.content.trim().length > 0);
452
+ // L4 total-budget trim: when the SUM of inline bytes across all deps
453
+ // exceeds MAX_TOTAL_DEP_INLINE_BYTES, downgrade the lowest-priority
454
+ // deps to path-only (clear inline text, keep resultPath +
455
+ // fullOutputPath). The downstream worker can `read` the full content
456
+ // on demand. Priority order (highest kept first):
457
+ // 1. status in {failed, needs-attention} (worker MUST see the failure)
458
+ // 2. recency descending (newer outputs more relevant)
459
+ // 3. relevance descending (workflow author-declared order)
460
+ // Tie-breakers within the same priority bucket are stable: the
461
+ // declaration order in `task.dependsOn` (earlier = more relevant).
462
+ const trimmedDependencies = enforceDependencyInlineBudget(dependencies);
463
+ const rawSharedReads = (step.reads === false ? [] : (step.reads ?? []))
464
+ .map((name) => {
465
+ const filePath = sharedPath(manifest, name);
466
+ // P1-A tee-recovery: when the shared artifact is large enough that the
467
+ // 75/25 head+tail split is materially lossy (>2× MAX_RESULT_INLINE_BYTES),
468
+ // tee the full content to ${artifactsRoot}/tee/${taskId}-${name}.full.txt
469
+ // and expose the path so the downstream worker can `read` the full file
470
+ // if it needs the dropped middle. The truncated content is still
471
+ // included inline; tee is an enhancement, not a hard dependency. Tee
472
+ // write is best-effort (writeTeeFile swallows I/O errors and the result
473
+ // simply omits fullOutputPath in that case).
474
+ const teePath = teePathForArtifact(manifest.artifactsRoot, task.id, name);
475
+ const teeResult = readIfSmallWithTee(filePath, {
476
+ baseDir: path.resolve(manifest.artifactsRoot, "shared"),
477
+ tee: { fullOutputPath: teePath },
478
+ });
479
+ if (teeResult === undefined) return { name, path: filePath, content: "" };
480
+ const entry: {
481
+ name: string;
482
+ path: string;
483
+ content: string;
484
+ fullOutputPath?: string;
485
+ } = {
486
+ name,
487
+ path: filePath,
488
+ content: teeResult.content,
489
+ };
490
+ if (teeResult.fullOutputPath) entry.fullOutputPath = teeResult.fullOutputPath;
491
+ return entry;
492
+ })
493
+ .filter((item) => item.content.trim().length > 0);
330
494
  // Apply staleness-aware pruning to shared reads: drops superseded reads
331
495
  // (same file re-read with different selectors) and replaces stale large
332
496
  // outputs with compact digest notices before injecting into the worker
333
497
  // prompt. OPT-IN: default config protects recent results.
334
- const sharedReads = pruneSharedReads(rawSharedReads, dependencies, manifest.artifactsRoot);
335
- return { dependencies, sharedReads };
498
+ const sharedReads = pruneSharedReads(rawSharedReads, trimmedDependencies, manifest.artifactsRoot);
499
+ return { dependencies: trimmedDependencies, sharedReads };
336
500
  }
337
501
 
338
502
  export function renderDependencyOutputContext(context: DependencyOutputContext): string {
@@ -340,7 +504,14 @@ export function renderDependencyOutputContext(context: DependencyOutputContext):
340
504
  if (context.dependencies.length) {
341
505
  parts.push("# Dependency Outputs", "");
342
506
  for (const dep of context.dependencies) {
343
- parts.push(`## ${dep.taskId} (${dep.role})`, `Status: ${dep.status}`, dep.resultPath ? `Result artifact: ${dep.resultPath}` : "", "", dep.resultSummary?.trim() || "(no result output)", "");
507
+ parts.push(
508
+ `## ${dep.taskId} (${dep.role})`,
509
+ `Status: ${dep.status}`,
510
+ dep.resultPath ? `Result artifact: ${dep.resultPath}` : "",
511
+ "",
512
+ dep.resultSummary?.trim() || "(no result output)",
513
+ "",
514
+ );
344
515
  // P1-A dependency tee-recovery hint: when the dependency's result was
345
516
  // materially truncated (>1.25× MAX_RESULT_INLINE_BYTES) the full RAW
346
517
  // content was teed to fullOutputPath. Mirrors the sharedReads hint so the
@@ -348,7 +519,11 @@ export function renderDependencyOutputContext(context: DependencyOutputContext):
348
519
  if (dep.fullOutputPath) parts.push(`Full output (if you need the missing middle): ${dep.fullOutputPath}`, "");
349
520
  if (dep.structuredResults) parts.push("Structured results:", JSON.stringify(dep.structuredResults, null, 2), "");
350
521
  if (dep.artifactsProduced?.length) parts.push(`Artifacts produced: ${dep.artifactsProduced.join(", ")}`, "");
351
- if (dep.usage) parts.push(`Usage: ${dep.usage.inputTokens} input tokens, ${dep.usage.outputTokens} output tokens, ${dep.usage.durationMs}ms`, "");
522
+ if (dep.usage)
523
+ parts.push(
524
+ `Usage: ${dep.usage.inputTokens} input tokens, ${dep.usage.outputTokens} output tokens, ${dep.usage.durationMs}ms`,
525
+ "",
526
+ );
352
527
  }
353
528
  }
354
529
  if (context.sharedReads.length) {
@@ -379,7 +554,11 @@ export function writeTaskSharedOutput(manifest: TeamRunManifest, step: WorkflowS
379
554
  });
380
555
  }
381
556
 
382
- export function writeTaskInputsArtifact(manifest: TeamRunManifest, task: TeamTaskState, context: DependencyOutputContext): ArtifactDescriptor {
557
+ export function writeTaskInputsArtifact(
558
+ manifest: TeamRunManifest,
559
+ task: TeamTaskState,
560
+ context: DependencyOutputContext,
561
+ ): ArtifactDescriptor {
383
562
  return writeArtifact(manifest.artifactsRoot, {
384
563
  kind: "metadata",
385
564
  relativePath: `metadata/${task.id}.inputs.json`,
@@ -389,29 +568,34 @@ export function writeTaskInputsArtifact(manifest: TeamRunManifest, task: TeamTas
389
568
  }
390
569
 
391
570
  export function aggregateTaskOutputs(tasks: TeamTaskState[], manifest?: TeamRunManifest): string {
392
- return tasks.map((task, index) => {
393
- const body = task.resultArtifact ? readIfSmall(task.resultArtifact.path, manifest?.artifactsRoot) : undefined;
394
- const hasBody = Boolean(body?.trim());
395
- const expectedMissing = task.resultArtifact && !containedExists(task.resultArtifact.path, manifest?.artifactsRoot);
396
- const status = task.status === "skipped"
397
- ? "SKIPPED"
398
- : task.status === "failed"
399
- ? `FAILED${task.exitCode !== undefined ? ` (exit code ${task.exitCode ?? "null"})` : ""}${task.error ? `: ${task.error}` : ""}`
400
- : expectedMissing
401
- ? `EMPTY OUTPUT (expected result artifact missing: ${task.resultArtifact?.path})`
402
- : !hasBody
403
- ? "EMPTY OUTPUT (no textual response returned)"
404
- : task.status.toUpperCase();
405
- return [
406
- `=== Task ${index + 1}: ${task.id} (${task.agent}) ===`,
407
- `Status: ${status}`,
408
- task.role ? `Role: ${task.role}` : "",
409
- task.resultArtifact?.path ? `Result artifact: ${task.resultArtifact.path}` : "",
410
- task.logArtifact?.path ? `Log artifact: ${task.logArtifact.path}` : "",
411
- task.transcriptArtifact?.path ? `Transcript: ${task.transcriptArtifact.path}` : "",
412
- task.usage ? `Usage: ${JSON.stringify(task.usage)}` : "",
413
- "",
414
- hasBody ? body!.trim() : status,
415
- ].filter(Boolean).join("\n");
416
- }).join("\n\n");
571
+ return tasks
572
+ .map((task, index) => {
573
+ const body = task.resultArtifact ? readIfSmall(task.resultArtifact.path, manifest?.artifactsRoot) : undefined;
574
+ const hasBody = Boolean(body?.trim());
575
+ const expectedMissing = task.resultArtifact && !containedExists(task.resultArtifact.path, manifest?.artifactsRoot);
576
+ const status =
577
+ task.status === "skipped"
578
+ ? "SKIPPED"
579
+ : task.status === "failed"
580
+ ? `FAILED${task.exitCode !== undefined ? ` (exit code ${task.exitCode ?? "null"})` : ""}${task.error ? `: ${task.error}` : ""}`
581
+ : expectedMissing
582
+ ? `EMPTY OUTPUT (expected result artifact missing: ${task.resultArtifact?.path})`
583
+ : !hasBody
584
+ ? "EMPTY OUTPUT (no textual response returned)"
585
+ : task.status.toUpperCase();
586
+ return [
587
+ `=== Task ${index + 1}: ${task.id} (${task.agent}) ===`,
588
+ `Status: ${status}`,
589
+ task.role ? `Role: ${task.role}` : "",
590
+ task.resultArtifact?.path ? `Result artifact: ${task.resultArtifact.path}` : "",
591
+ task.logArtifact?.path ? `Log artifact: ${task.logArtifact.path}` : "",
592
+ task.transcriptArtifact?.path ? `Transcript: ${task.transcriptArtifact.path}` : "",
593
+ task.usage ? `Usage: ${JSON.stringify(task.usage)}` : "",
594
+ "",
595
+ hasBody ? body!.trim() : status,
596
+ ]
597
+ .filter(Boolean)
598
+ .join("\n");
599
+ })
600
+ .join("\n\n");
417
601
  }
@@ -1,5 +1,5 @@
1
1
  import * as path from "node:path";
2
- import type { TeamRunManifest, TaskPacket, TaskScope, VerificationContract } from "../state/types.ts";
2
+ import type { TaskPacket, TaskScope, TeamRunManifest, VerificationContract } from "../state/types.ts";
3
3
  import type { WorkflowStep } from "../workflows/workflow-config.ts";
4
4
  import { generateTaskHashId } from "./task-id.ts";
5
5
 
@@ -10,7 +10,6 @@ import { generateTaskHashId } from "./task-id.ts";
10
10
  // See: SECURITY-ISSUES.md SEC-007
11
11
  // ═══════════════════════════════════════════════════════════════════════════
12
12
 
13
-
14
13
  /**
15
14
  * Sanitize workflow step task text to reduce injection risk.
16
15
  *
@@ -33,7 +32,7 @@ export function sanitizeTaskText(task: string): string {
33
32
  // 2. Strip known prompt injection directive patterns
34
33
  sanitized = sanitized.replace(
35
34
  /^\s*(?:SYSTEM|INSTRUCTION|IGNORE(?:\s+ALL)?\s+INSTRUCTIONS|OVERRIDE|YOUR\s+ROLE\s+IS|MALICIOUS)\s*:.*$/gim,
36
- ""
35
+ "",
37
36
  );
38
37
 
39
38
  // 3. Strip base64/hex encoded command payloads
@@ -62,7 +61,7 @@ export interface TaskPacketValidationResult {
62
61
  }
63
62
 
64
63
  export function inferTaskScope(step: WorkflowStep): TaskScope {
65
- const reads = step.reads === false ? [] : step.reads ?? [];
64
+ const reads = step.reads === false ? [] : (step.reads ?? []);
66
65
  if (reads.length === 1) return "single_file";
67
66
  if (reads.length > 1) return "module";
68
67
  return "workspace";
@@ -78,7 +77,7 @@ export function defaultVerificationContract(step: WorkflowStep): VerificationCon
78
77
 
79
78
  export function buildTaskPacket(input: BuildTaskPacketInput): TaskPacket {
80
79
  const scope = inferTaskScope(input.step);
81
- const reads = input.step.reads === false ? [] : input.step.reads ?? [];
80
+ const reads = input.step.reads === false ? [] : (input.step.reads ?? []);
82
81
  const scopePath = reads.length === 1 ? reads[0] : reads.length > 1 ? reads.join(", ") : undefined;
83
82
  // SEC-007: Sanitize task text before inserting into task packet
84
83
  const sanitizedTask = sanitizeTaskText(input.step.task);
@@ -87,11 +86,7 @@ export function buildTaskPacket(input: BuildTaskPacketInput): TaskPacket {
87
86
  // Generate a deterministic hash-based task ID for traceability and logging.
88
87
  // Uses goal + step ID + run ID as content parts.
89
88
  // TODO: Once TaskPacket type gains a hashId field, include this in the packet.
90
- const _taskHashId = generateTaskHashId([
91
- input.manifest.goal,
92
- input.step.id,
93
- input.manifest.runId,
94
- ]);
89
+ const _taskHashId = generateTaskHashId([input.manifest.goal, input.step.id, input.manifest.runId]);
95
90
 
96
91
  return {
97
92
  objective: sanitizedTask.replaceAll("{goal}", sanitizedGoal),
@@ -99,11 +94,15 @@ export function buildTaskPacket(input: BuildTaskPacketInput): TaskPacket {
99
94
  scopePath,
100
95
  repo: path.basename(input.manifest.cwd) || input.manifest.cwd,
101
96
  worktree: input.worktreePath,
102
- branchPolicy: input.manifest.workspaceMode === "worktree" ? "Use the assigned task worktree and avoid modifying the leader checkout." : "Use the current checkout; do not create branches unless explicitly requested.",
97
+ branchPolicy:
98
+ input.manifest.workspaceMode === "worktree"
99
+ ? "Use the assigned task worktree and avoid modifying the leader checkout."
100
+ : "Use the current checkout; do not create branches unless explicitly requested.",
103
101
  acceptanceTests: [],
104
102
  commitPolicy: "Do not commit unless explicitly requested by the user or workflow.",
105
103
  reportingContract: "Report intended/changed files, verification evidence, blockers, conflict risks, and next recommended action.",
106
- escalationPolicy: "Stop and report if scope is ambiguous, destructive action is needed, permissions are missing, verification cannot be completed, or edits may overlap with another worker/task.",
104
+ escalationPolicy:
105
+ "Stop and report if scope is ambiguous, destructive action is needed, permissions are missing, verification cannot be completed, or edits may overlap with another worker/task.",
107
106
  constraints: [
108
107
  "Stay within the assigned task scope.",
109
108
  "Do not claim completion without verification evidence.",
@@ -265,12 +264,5 @@ export function parseHandoffFromOutput(output: string): ParsedHandoff {
265
264
  }
266
265
 
267
266
  export function renderTaskPacket(packet: TaskPacket): string {
268
- return [
269
- "# Task Packet",
270
- "",
271
- "```json",
272
- JSON.stringify(packet, null, 2),
273
- "```",
274
- "",
275
- ].join("\n");
267
+ return ["# Task Packet", "", "```json", JSON.stringify(packet, null, 2), "```", ""].join("\n");
276
268
  }
@@ -104,7 +104,8 @@ function isResultDescriptive(task: TeamTaskState): boolean {
104
104
  typeof task.diagnostics === "object" &&
105
105
  typeof task.diagnostics.result === "string" &&
106
106
  (task.diagnostics.result as string).trim().length > 0
107
- ) return true;
107
+ )
108
+ return true;
108
109
 
109
110
  return false;
110
111
  }
@@ -138,22 +139,13 @@ function isDurationReasonable(task: TeamTaskState): boolean {
138
139
  * @param artifactsDir - Optional path to the run artifacts directory
139
140
  * @returns TaskQualityScore with numeric score, breakdown, and letter grade
140
141
  */
141
- export function computeTaskQuality(
142
- task: TeamTaskState,
143
- artifactsDir?: string,
144
- ): TaskQualityScore {
142
+ export function computeTaskQuality(task: TeamTaskState, artifactsDir?: string): TaskQualityScore {
145
143
  const hasDiagnostics =
146
- task.diagnostics !== undefined &&
147
- typeof task.diagnostics === "object" &&
148
- Object.keys(task.diagnostics).length > 0;
144
+ task.diagnostics !== undefined && typeof task.diagnostics === "object" && Object.keys(task.diagnostics).length > 0;
149
145
 
150
- const hasMetrics =
151
- task.metrics !== undefined &&
152
- typeof task.metrics === "object" &&
153
- Object.keys(task.metrics).length > 0;
146
+ const hasMetrics = task.metrics !== undefined && typeof task.metrics === "object" && Object.keys(task.metrics).length > 0;
154
147
 
155
- const producedArtifacts =
156
- artifactsDir !== undefined && hasTaskArtifacts(task.id, artifactsDir);
148
+ const producedArtifacts = artifactsDir !== undefined && hasTaskArtifacts(task.id, artifactsDir);
157
149
 
158
150
  const hasDescription = isResultDescriptive(task);
159
151
 
@@ -20,7 +20,7 @@ export interface RetrievalQuery {
20
20
 
21
21
  export interface RelevanceEvaluation {
22
22
  path: string;
23
- relevance: number; // 0.0–1.0
23
+ relevance: number; // 0.0–1.0
24
24
  reason: string;
25
25
  missingContext: string[];
26
26
  }
@@ -45,11 +45,7 @@ export interface RetrievalResult {
45
45
  * @param keywords - Task-relevant keywords
46
46
  * @returns Relevance score 0.0–1.0
47
47
  */
48
- export function scoreRelevance(
49
- filePath: string,
50
- fileContent: string,
51
- keywords: string[],
52
- ): number {
48
+ export function scoreRelevance(filePath: string, fileContent: string, keywords: string[]): number {
53
49
  if (keywords.length === 0) return 0;
54
50
 
55
51
  const pathLower = filePath.toLowerCase();
@@ -96,9 +92,7 @@ export function hasConverged(evaluations: RelevanceEvaluation[]): boolean {
96
92
  if (highRelevance.length < CONVERGENCE_MIN_HIGH_RELEVANCE) return false;
97
93
 
98
94
  // Check for critical gaps — any evaluation with empty missingContext
99
- const criticalGaps = evaluations.some(
100
- (e) => e.relevance < 0.3 && e.missingContext.length > 0,
101
- );
95
+ const criticalGaps = evaluations.some((e) => e.relevance < 0.3 && e.missingContext.length > 0);
102
96
 
103
97
  return !criticalGaps;
104
98
  }
@@ -115,10 +109,7 @@ export function hasConverged(evaluations: RelevanceEvaluation[]): boolean {
115
109
  * @param evaluations - Results from the current cycle
116
110
  * @returns Refined query for the next cycle
117
111
  */
118
- export function refineQuery(
119
- query: RetrievalQuery,
120
- evaluations: RelevanceEvaluation[],
121
- ): RetrievalQuery {
112
+ export function refineQuery(query: RetrievalQuery, evaluations: RelevanceEvaluation[]): RetrievalQuery {
122
113
  const newKeywords = new Set(query.keywords);
123
114
  const newExcludes = new Set(query.excludes);
124
115
  const newFocusAreas = new Set(query.focusAreas ?? []);