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
@@ -14,32 +14,26 @@
14
14
  * Each turn spawns one executeTeamRun; the loop is the outer coordinator.
15
15
  */
16
16
 
17
- import { createRunManifest, saveRunTasks } from "../state/state-store.ts";
18
- import { appendEvent } from "../state/event-log.ts";
19
- import { collectRunMetrics } from "../state/run-metrics.ts";
20
- import { registerActiveRun, unregisterActiveRun } from "../state/active-run-registry.ts";
21
- import { executeTeamRun } from "./team-runner.ts";
22
- import { GoalStore } from "./goal-state-store.ts";
23
- import { evaluateGoal, bundleEvidence } from "./goal-evaluator.ts";
24
- import { withWorkerSlot } from "./global-worker-cap.ts";
25
- import { acquireWorkspaceLock, type WorkspaceLockHandle } from "./workspace-lock.ts";
26
- import { existsSync, readdirSync } from "node:fs";
27
17
  import { randomBytes } from "node:crypto";
28
- import { logInternalError } from "../utils/internal-error.ts";
18
+ import { existsSync, readdirSync } from "node:fs";
19
+ import type { AgentConfig } from "../agents/agent-config.ts";
29
20
  import { loadConfig } from "../config/config.ts";
30
21
  import { effectiveRunConfig } from "../extension/team-tool/config-patch.ts";
31
- import { resolveCrewRuntime } from "./runtime-resolver.ts";
32
- import { snapshotManifests, compareSnapshot } from "./verification-integrity.ts";
33
- import type {
34
- GoalLoopState,
35
- GoalLoopStatus,
36
- GoalVerdict,
37
- TeamRunManifest,
38
- TeamTaskState,
39
- } from "../state/types.ts";
22
+ import { registerActiveRun, unregisterActiveRun } from "../state/active-run-registry.ts";
23
+ import { appendEvent } from "../state/event-log.ts";
24
+ import { collectRunMetrics } from "../state/run-metrics.ts";
25
+ import { createRunManifest, saveRunTasks } from "../state/state-store.ts";
26
+ import type { GoalLoopState, GoalLoopStatus, GoalVerdict, TeamRunManifest, TeamTaskState } from "../state/types.ts";
40
27
  import type { TeamConfig } from "../teams/team-config.ts";
28
+ import { logInternalError } from "../utils/internal-error.ts";
41
29
  import type { WorkflowConfig } from "../workflows/workflow-config.ts";
42
- import type { AgentConfig } from "../agents/agent-config.ts";
30
+ import { withWorkerSlot } from "./global-worker-cap.ts";
31
+ import { bundleEvidence, evaluateGoal } from "./goal-evaluator.ts";
32
+ import { GoalStore } from "./goal-state-store.ts";
33
+ import { resolveCrewRuntime } from "./runtime-resolver.ts";
34
+ import { executeTeamRun } from "./team-runner.ts";
35
+ import { compareSnapshot, snapshotManifests } from "./verification-integrity.ts";
36
+ import { acquireWorkspaceLock, type WorkspaceLockHandle } from "./workspace-lock.ts";
43
37
 
44
38
  /** Required minimal shape for the worker + agents discovery (P0 uses the goal's workerAgent). */
45
39
  export interface GoalLoopRuntimeDeps {
@@ -65,7 +59,13 @@ export interface RunGoalLoopResult {
65
59
  * Kept for unit tests of the loop's max_turns exit path. The production loop
66
60
  * uses `realGoalEvaluator` (P1) which calls the LLM judge.
67
61
  */
68
- export const stubGoalEvaluator = async (goal: GoalLoopState, _turnRunId: string, _m?: import("../state/types.ts").TeamRunManifest, _t?: import("../state/types.ts").TeamTaskState[], _s?: AbortSignal): Promise<GoalVerdict> => ({
62
+ export const stubGoalEvaluator = async (
63
+ goal: GoalLoopState,
64
+ _turnRunId: string,
65
+ _m?: import("../state/types.ts").TeamRunManifest,
66
+ _t?: import("../state/types.ts").TeamTaskState[],
67
+ _s?: AbortSignal,
68
+ ): Promise<GoalVerdict> => ({
69
69
  turn: goal.turnsUsed,
70
70
  achieved: false,
71
71
  reason: `not-achieved: stub evaluator (P0). Turn ${goal.turnsUsed}/${goal.maxTurns} completed; P1 will judge against objective + verification.`,
@@ -113,7 +113,15 @@ export const realGoalEvaluator = async (
113
113
  const drift = compareSnapshot(snapshot.snapshot, current);
114
114
  if (drift.length > 0) {
115
115
  verificationCompromised = drift;
116
- appendEvent(turnManifest.eventsPath, { type: "goal.verification_compromised", runId: turnRunId, data: { goalId: goal.goalId, driftedFiles: drift, phase: "T_snap" } });
116
+ appendEvent(turnManifest.eventsPath, {
117
+ type: "goal.verification_compromised",
118
+ runId: turnRunId,
119
+ data: {
120
+ goalId: goal.goalId,
121
+ driftedFiles: drift,
122
+ phase: "T_snap",
123
+ },
124
+ });
117
125
  }
118
126
  } catch (error) {
119
127
  logInternalError("goal-loop.integritySnap", error, `goalId=${goal.goalId} phase=T_snap`);
@@ -121,9 +129,13 @@ export const realGoalEvaluator = async (
121
129
  }
122
130
  if (!verificationCompromised) {
123
131
  try {
124
- // LAZY: defer dynamic import of ./verification-gates.ts to its call site.
132
+ // LAZY: defer dynamic import of ./verification-gates.ts to its call site.
125
133
  const { executeVerificationCommands } = await import("./verification-gates.ts");
126
- const contract = { requiredGreenLevel: "none" as const, commands: goal.verification.commands, allowManualEvidence: goal.verification.allowManualEvidence ?? false };
134
+ const contract = {
135
+ requiredGreenLevel: "none" as const,
136
+ commands: goal.verification.commands,
137
+ allowManualEvidence: goal.verification.allowManualEvidence ?? false,
138
+ };
127
139
  // Phase 1.5 #2 (RFC 16): run verification in a pristine git worktree at
128
140
  // T_snap commit SHA when opt-in + clean git repo. Closes the round-trip
129
141
  // manifest tamper residual (MAJ#2) and invoked-script tampering residual
@@ -132,7 +144,8 @@ export const realGoalEvaluator = async (
132
144
  let worktreeCwd: string | undefined;
133
145
  let worktreeCleanup: (() => void) | undefined;
134
146
  try {
135
- // LAZY: defer dynamic import of ./verification-worktree.ts to its call site.
147
+ // LAZY: defer dynamic import of ./verification-worktree.ts to its call site.
148
+ // LAZY: defer dynamic import of ./verification-worktree.ts to its call site. Multi-line form breaks scripts/check-lazy-imports.mjs (which does `lines[lineNum - 2]`), so keep destructuring + await import on one line.
136
149
  const { checkWorktreeSandboxAvailable, prepareVerificationWorktree } = await import("./verification-worktree.ts");
137
150
  const availability = checkWorktreeSandboxAvailable(goal.cwd);
138
151
  if (availability.available) {
@@ -141,33 +154,57 @@ export const realGoalEvaluator = async (
141
154
  worktreeCleanup = wt.cleanup;
142
155
  } else {
143
156
  // Graceful fallback — log reason so the bypass is traceable.
144
- logInternalError("goal-loop.worktreeSandboxBypassed", new Error(availability.reason), `goalId=${goal.goalId} cwd=${goal.cwd}`);
157
+ logInternalError(
158
+ "goal-loop.worktreeSandboxBypassed",
159
+ new Error(availability.reason),
160
+ `goalId=${goal.goalId} cwd=${goal.cwd}`,
161
+ );
145
162
  }
146
163
  } catch (error) {
147
164
  logInternalError("goal-loop.worktreeSandboxPrep", error, `goalId=${goal.goalId}`);
148
165
  }
149
166
  try {
150
- const cmdResults = await executeVerificationCommands(contract, goal.cwd, turnRunId, "goal-verify", turnManifest.artifactsRoot, signal, worktreeCwd);
151
- verificationResults = cmdResults.map((r) => ({ command: r.cmd, exitCode: r.exitCode ?? null, passed: r.status === "passed" }));
152
- // T_verify_done: re-hash AFTER the commands ran. If the manifest changed between
153
- // command-launch and command-exit (worker edited it mid-run, then the test "passed"
154
- // against the modified graph), treat the result as compromised even if exit was 0.
155
- // NOTE (RFC v0.5 §P1a residual): this catches PERSISTENT edits only — a round-trip
156
- // (edit -> run -> revert) leaves the hash unchanged and is NOT caught here.
157
- // Phase 1.5 #2 (RFC 16) closes this residual when worktree sandbox is active:
158
- // verification ran in a pristine checkout, so worker edits are invisible.
159
- if (snapshot && snapshot !== "none-text-only") {
160
- try {
161
- const post = snapshotManifests(goal.cwd);
162
- const postDrift = compareSnapshot(snapshot.snapshot, post);
163
- if (postDrift.length > 0) {
164
- verificationCompromised = postDrift;
165
- appendEvent(turnManifest.eventsPath, { type: "goal.verification_compromised", runId: turnRunId, data: { goalId: goal.goalId, driftedFiles: postDrift, phase: "T_verify_done" } });
167
+ const cmdResults = await executeVerificationCommands(
168
+ contract,
169
+ goal.cwd,
170
+ turnRunId,
171
+ "goal-verify",
172
+ turnManifest.artifactsRoot,
173
+ signal,
174
+ worktreeCwd,
175
+ );
176
+ verificationResults = cmdResults.map((r) => ({
177
+ command: r.cmd,
178
+ exitCode: r.exitCode ?? null,
179
+ passed: r.status === "passed",
180
+ }));
181
+ // T_verify_done: re-hash AFTER the commands ran. If the manifest changed between
182
+ // command-launch and command-exit (worker edited it mid-run, then the test "passed"
183
+ // against the modified graph), treat the result as compromised even if exit was 0.
184
+ // NOTE (RFC v0.5 §P1a residual): this catches PERSISTENT edits only — a round-trip
185
+ // (edit -> run -> revert) leaves the hash unchanged and is NOT caught here.
186
+ // Phase 1.5 #2 (RFC 16) closes this residual when worktree sandbox is active:
187
+ // verification ran in a pristine checkout, so worker edits are invisible.
188
+ if (snapshot && snapshot !== "none-text-only") {
189
+ try {
190
+ const post = snapshotManifests(goal.cwd);
191
+ const postDrift = compareSnapshot(snapshot.snapshot, post);
192
+ if (postDrift.length > 0) {
193
+ verificationCompromised = postDrift;
194
+ appendEvent(turnManifest.eventsPath, {
195
+ type: "goal.verification_compromised",
196
+ runId: turnRunId,
197
+ data: {
198
+ goalId: goal.goalId,
199
+ driftedFiles: postDrift,
200
+ phase: "T_verify_done",
201
+ },
202
+ });
203
+ }
204
+ } catch (error) {
205
+ logInternalError("goal-loop.integritySnap", error, `goalId=${goal.goalId} phase=T_verify_done`);
166
206
  }
167
- } catch (error) {
168
- logInternalError("goal-loop.integritySnap", error, `goalId=${goal.goalId} phase=T_verify_done`);
169
207
  }
170
- }
171
208
  } catch (error) {
172
209
  logInternalError("goal-loop.verification", error, `goalId=${goal.goalId}`);
173
210
  verificationResults = [];
@@ -223,10 +260,15 @@ function resolveGoalTurnWorkflow(goal: GoalLoopState): WorkflowConfig {
223
260
  const wrapName = (goal as GoalLoopState & { goalWrapWorkflow?: string }).goalWrapWorkflow;
224
261
  if (!wrapName) return buildTurnWorkflow();
225
262
  try {
226
- const { discoverWorkflows, allWorkflows } = require("../workflows/discover-workflows.ts") as typeof import("../workflows/discover-workflows.ts");
263
+ const { discoverWorkflows, allWorkflows } =
264
+ require("../workflows/discover-workflows.ts") as typeof import("../workflows/discover-workflows.ts");
227
265
  const found = allWorkflows(discoverWorkflows(goal.cwd)).find((w) => w.name === wrapName && w.source === "builtin");
228
266
  if (found) return found;
229
- logInternalError("goal-loop.goalWrapWorkflow", new Error(`builtin workflow '${wrapName}' not found; falling back to goal-turn`), `goalId=${goal.goalId}`);
267
+ logInternalError(
268
+ "goal-loop.goalWrapWorkflow",
269
+ new Error(`builtin workflow '${wrapName}' not found; falling back to goal-turn`),
270
+ `goalId=${goal.goalId}`,
271
+ );
230
272
  } catch (error) {
231
273
  logInternalError("goal-loop.goalWrapWorkflow", error, `goalId=${goal.goalId} wrapName=${wrapName}`);
232
274
  }
@@ -253,7 +295,13 @@ export function buildGoalTeam(goal: GoalLoopState): TeamConfig {
253
295
  description: `Synthetic team for goal loop ${goal.goalId} (worker=${workerAgent}).`,
254
296
  source: "dynamic",
255
297
  filePath: "<goal-loop>",
256
- roles: [{ name: workerAgent, agent: workerAgent, description: `Worker for goal ${goal.goalId}` }],
298
+ roles: [
299
+ {
300
+ name: workerAgent,
301
+ agent: workerAgent,
302
+ description: `Worker for goal ${goal.goalId}`,
303
+ },
304
+ ],
257
305
  workspaceMode: "single",
258
306
  };
259
307
  }
@@ -302,9 +350,10 @@ function composeGoalPrompt(goal: GoalLoopState): string {
302
350
  if (reasons[i] === currentReason) priorMatches++;
303
351
  else break; // count consecutive tail matches only (oscillation = exact repeat)
304
352
  }
305
- const recurrenceNote = priorMatches >= 1
306
- ? `\n_Note: this same issue has now been raised ${priorMatches + 1} time(s). If you genuinely cannot resolve it, stop attempting the same fix and explain the blocker instead._`
307
- : "";
353
+ const recurrenceNote =
354
+ priorMatches >= 1
355
+ ? `\n_Note: this same issue has now been raised ${priorMatches + 1} time(s). If you genuinely cannot resolve it, stop attempting the same fix and explain the blocker instead._`
356
+ : "";
308
357
  // P1e: per-turn unpredictable nonce. randomBytes(6) -> 12 hex chars (48 bits of entropy),
309
358
  // comfortably unguessable. The worker is told the contents are DATA only.
310
359
  const nonce = randomBytes(6).toString("hex");
@@ -343,13 +392,24 @@ async function yieldBetweenTurns(goal: GoalLoopState, signal: AbortSignal, ms =
343
392
 
344
393
  /** Fix round-7: re-read disk before applying terminal state. If an external actor
345
394
  * (goal stop/pause) already changed the state, don't overwrite — external cancel wins. */
346
- function safeSetStatus(store: GoalStore, goalId: string, proposed: GoalLoopStatus, fallback: GoalLoopState, eventsPath: string): GoalLoopState {
395
+ function safeSetStatus(
396
+ store: GoalStore,
397
+ goalId: string,
398
+ proposed: GoalLoopStatus,
399
+ fallback: GoalLoopState,
400
+ eventsPath: string,
401
+ ): GoalLoopState {
347
402
  const current = store.load(goalId);
348
403
  if (current && current.state !== "running") {
349
404
  // External actor already set a terminal/paused state — respect it.
350
405
  return current;
351
406
  }
352
- return store.setStatus(goalId, proposed, eventsPath) ?? { ...fallback, state: proposed };
407
+ return (
408
+ store.setStatus(goalId, proposed, eventsPath) ?? {
409
+ ...fallback,
410
+ state: proposed,
411
+ }
412
+ );
353
413
  }
354
414
 
355
415
  /**
@@ -361,10 +421,7 @@ function safeSetStatus(store: GoalStore, goalId: string, proposed: GoalLoopStatu
361
421
  *
362
422
  * Exported for unit testing.
363
423
  */
364
- export function detectOscillation(
365
- verdicts: Array<{ reason: string }>,
366
- opts?: { window?: number; threshold?: number },
367
- ): boolean {
424
+ export function detectOscillation(verdicts: Array<{ reason: string }>, opts?: { window?: number; threshold?: number }): boolean {
368
425
  const window = Math.max(2, opts?.window ?? 3);
369
426
  const threshold = opts?.threshold ?? 0.8;
370
427
  if (verdicts.length < window) return false;
@@ -380,7 +437,11 @@ export function detectOscillation(
380
437
 
381
438
  function normalizeForSimilarity(s: string): Set<string> {
382
439
  // Lowercase, split into word 3-shingles (trigrams of words). Skip non-word tokens.
383
- const words = s.toLowerCase().replace(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean);
440
+ const words = s
441
+ .toLowerCase()
442
+ .replace(/[^\w\s]/g, " ")
443
+ .split(/\s+/)
444
+ .filter(Boolean);
384
445
  if (words.length < 3) return new Set(words);
385
446
  const shingles = new Set<string>();
386
447
  for (let i = 0; i <= words.length - 3; i++) {
@@ -413,7 +474,9 @@ export function deriveTranscriptPath(artifactsRoot: string, tasks: import("../st
413
474
  try {
414
475
  const matches = readdirSync(transcriptsDir).filter((f) => f.startsWith(`${firstTask.id}.attempt-`));
415
476
  if (matches.length) return `${transcriptsDir}/${matches.sort().pop()}`;
416
- } catch { /* dir missing — fall through */ }
477
+ } catch {
478
+ /* dir missing — fall through */
479
+ }
417
480
  }
418
481
  // Fallback: any transcript in the dir (newest).
419
482
  try {
@@ -445,7 +508,15 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
445
508
  const workflow = resolveGoalTurnWorkflow(goal);
446
509
  const agents = input.deps.discoverAgents(goal.cwd);
447
510
 
448
- appendEvent(eventsPath, { type: "goal.loop_start", runId: manifest.runId, data: { goalId: goal.goalId, objective: goal.objective, maxTurns: goal.maxTurns } });
511
+ appendEvent(eventsPath, {
512
+ type: "goal.loop_start",
513
+ runId: manifest.runId,
514
+ data: {
515
+ goalId: goal.goalId,
516
+ objective: goal.objective,
517
+ maxTurns: goal.maxTurns,
518
+ },
519
+ });
449
520
 
450
521
  // P1g (RFC v0.5 §P1g, cold-review #2 BLOCKING fix): acquire the workspace lock for the
451
522
  // goal's lifetime. This serializes concurrent goals targeting the same cwd (workspaceMode:
@@ -455,11 +526,20 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
455
526
  // for a good error message; this acquisition is the authoritative claim.
456
527
  let workspaceLock: WorkspaceLockHandle | undefined;
457
528
  try {
458
- workspaceLock = await acquireWorkspaceLock(goal.cwd, goal.goalId, { signal });
529
+ workspaceLock = await acquireWorkspaceLock(goal.cwd, goal.goalId, {
530
+ signal,
531
+ });
459
532
  } catch (error) {
460
533
  logInternalError("goal-loop.workspaceLock", error, `goalId=${goal.goalId} cwd=${goal.cwd}`);
461
534
  goal = safeSetStatus(store, goal.goalId, "blocked", goal, eventsPath);
462
- appendEvent(eventsPath, { type: "goal.workspace_lock_failed", runId: manifest.runId, data: { goalId: goal.goalId, error: error instanceof Error ? error.message : String(error) } });
535
+ appendEvent(eventsPath, {
536
+ type: "goal.workspace_lock_failed",
537
+ runId: manifest.runId,
538
+ data: {
539
+ goalId: goal.goalId,
540
+ error: error instanceof Error ? error.message : String(error),
541
+ },
542
+ });
463
543
  return { manifest, tasks: [], goalState: goal };
464
544
  }
465
545
 
@@ -477,16 +557,42 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
477
557
  if (goal.budgetUnlimited !== true && goal.budgetTotal !== undefined && goal.budgetTotal > 0 && goal.budgetAbort !== undefined) {
478
558
  if (goal.budgetUsed >= goal.budgetAbort * goal.budgetTotal) {
479
559
  goal = safeSetStatus(store, goal.goalId, "budget_exceeded", goal, eventsPath);
480
- appendEvent(eventsPath, { type: "goal.budget_warning", runId: manifest.runId, data: { goalId: goal.goalId, budgetUsed: goal.budgetUsed, budgetTotal: goal.budgetTotal, threshold: "abort" } });
560
+ appendEvent(eventsPath, {
561
+ type: "goal.budget_warning",
562
+ runId: manifest.runId,
563
+ data: {
564
+ goalId: goal.goalId,
565
+ budgetUsed: goal.budgetUsed,
566
+ budgetTotal: goal.budgetTotal,
567
+ threshold: "abort",
568
+ },
569
+ });
481
570
  break;
482
571
  }
483
572
  if (goal.budgetUsed >= (goal.budgetWarning ?? 0.8) * goal.budgetTotal) {
484
- appendEvent(eventsPath, { type: "goal.budget_warning", runId: manifest.runId, data: { goalId: goal.goalId, budgetUsed: goal.budgetUsed, budgetTotal: goal.budgetTotal, threshold: "warning" } });
573
+ appendEvent(eventsPath, {
574
+ type: "goal.budget_warning",
575
+ runId: manifest.runId,
576
+ data: {
577
+ goalId: goal.goalId,
578
+ budgetUsed: goal.budgetUsed,
579
+ budgetTotal: goal.budgetTotal,
580
+ threshold: "warning",
581
+ },
582
+ });
485
583
  }
486
584
  }
487
585
 
488
586
  const turnIndex = goal.turnsUsed + 1;
489
- appendEvent(eventsPath, { type: "goal.turn_start", runId: manifest.runId, data: { goalId: goal.goalId, turn: turnIndex, maxTurns: goal.maxTurns } });
587
+ appendEvent(eventsPath, {
588
+ type: "goal.turn_start",
589
+ runId: manifest.runId,
590
+ data: {
591
+ goalId: goal.goalId,
592
+ turn: turnIndex,
593
+ maxTurns: goal.maxTurns,
594
+ },
595
+ });
490
596
 
491
597
  // ── TURN: fresh manifest per turn (G2) + executeTeamRun ──────────────────
492
598
  const turnGoalText = composeGoalPrompt(goal);
@@ -499,15 +605,34 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
499
605
  ownerSessionId: goal.ownerSessionId,
500
606
  runKind: "team-run", // §0a v2 note: turns are normal team-runs; the OUTER loop is goal-loop
501
607
  });
502
- goal = store.patch(goal.goalId, { currentRunId: created.manifest.runId, turnsUsed: turnIndex }, eventsPath) ?? goal;
608
+ goal =
609
+ store.patch(
610
+ goal.goalId,
611
+ {
612
+ currentRunId: created.manifest.runId,
613
+ turnsUsed: turnIndex,
614
+ },
615
+ eventsPath,
616
+ ) ?? goal;
503
617
  // Fix round-6: re-check state AFTER patching (user may have paused/stopped in the inter-turn gap).
504
618
  // Without this, a pause that lands between store.patch and executeTeamRun lets one extra turn run.
505
619
  if (goal.state !== "running") {
506
- appendEvent(eventsPath, { type: "goal.loop_end", runId: manifest.runId, data: { goalId: goal.goalId, state: goal.state, reason: "state changed before turn spawn" } });
620
+ appendEvent(eventsPath, {
621
+ type: "goal.loop_end",
622
+ runId: manifest.runId,
623
+ data: {
624
+ goalId: goal.goalId,
625
+ state: goal.state,
626
+ reason: "state changed before turn spawn",
627
+ },
628
+ });
507
629
  break;
508
630
  }
509
631
  registerActiveRun(created.manifest);
510
- let turnResult: { manifest: TeamRunManifest; tasks: TeamTaskState[] };
632
+ let turnResult: {
633
+ manifest: TeamRunManifest;
634
+ tasks: TeamTaskState[];
635
+ };
511
636
  try {
512
637
  // P1g (RFC v0.5 §P1g): route the worker turn through the GLOBAL worker cap so that
513
638
  // many concurrent goals / dynamic-workflows / fanOuts cannot fork-storm. The JUDGE is
@@ -520,20 +645,22 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
520
645
  const turnConfig = loadConfig(goal.cwd);
521
646
  const turnExecutedConfig = effectiveRunConfig(turnConfig.config, {});
522
647
  const turnRuntime = await resolveCrewRuntime(turnExecutedConfig);
523
- turnResult = await withWorkerSlot(() => executeTeamRun({
524
- manifest: created.manifest,
525
- tasks: created.tasks,
526
- team,
527
- workflow,
528
- agents,
529
- executeWorkers: true,
530
- limits: turnExecutedConfig.limits,
531
- runtime: turnRuntime,
532
- runtimeConfig: turnExecutedConfig.runtime,
533
- reliability: turnExecutedConfig.reliability,
534
- workspaceId: goal.ownerSessionId ?? goal.cwd,
535
- signal,
536
- }));
648
+ turnResult = await withWorkerSlot(() =>
649
+ executeTeamRun({
650
+ manifest: created.manifest,
651
+ tasks: created.tasks,
652
+ team,
653
+ workflow,
654
+ agents,
655
+ executeWorkers: true,
656
+ limits: turnExecutedConfig.limits,
657
+ runtime: turnRuntime,
658
+ runtimeConfig: turnExecutedConfig.runtime,
659
+ reliability: turnExecutedConfig.reliability,
660
+ workspaceId: goal.ownerSessionId ?? goal.cwd,
661
+ signal,
662
+ }),
663
+ );
537
664
  } finally {
538
665
  unregisterActiveRun(created.manifest.runId);
539
666
  }
@@ -549,20 +676,53 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
549
676
  const updatedBudget = accumulateBudget(goal, created.manifest.runId);
550
677
 
551
678
  // ── EVALUATE (P1: real LLM judge; pass turn manifest + tasks for transcript lookup) ──
552
- const verdict = await evaluator({ ...goal, budgetUsed: updatedBudget }, created.manifest.runId, turnResult.manifest, turnResult.tasks, signal);
553
- const historyEntry = { runId: created.manifest.runId, outcome: verdict.achieved ? "achieved" : "not-achieved", learnedAt: new Date().toISOString(), turn: turnIndex };
554
- goal = store.patch(goal.goalId, {
555
- budgetUsed: updatedBudget,
556
- verdicts: [...goal.verdicts, verdict],
557
- history: [...goal.history, historyEntry],
558
- currentRunId: undefined,
559
- // G1/A3: feedback feeds turn N+1's prompt via manifest.goal (NOT session.steer)
560
- nextTurnFeedback: verdict.achieved ? undefined : verdict.reason,
561
- }, eventsPath) ?? goal;
562
-
563
- appendEvent(eventsPath, { type: "goal.turn_evaluated", runId: manifest.runId, data: { goalId: goal.goalId, turn: turnIndex, achieved: verdict.achieved, reason: verdict.reason } });
679
+ const verdict = await evaluator(
680
+ { ...goal, budgetUsed: updatedBudget },
681
+ created.manifest.runId,
682
+ turnResult.manifest,
683
+ turnResult.tasks,
684
+ signal,
685
+ );
686
+ const historyEntry = {
687
+ runId: created.manifest.runId,
688
+ outcome: verdict.achieved ? "achieved" : "not-achieved",
689
+ learnedAt: new Date().toISOString(),
690
+ turn: turnIndex,
691
+ };
692
+ goal =
693
+ store.patch(
694
+ goal.goalId,
695
+ {
696
+ budgetUsed: updatedBudget,
697
+ verdicts: [...goal.verdicts, verdict],
698
+ history: [...goal.history, historyEntry],
699
+ currentRunId: undefined,
700
+ // G1/A3: feedback feeds turn N+1's prompt via manifest.goal (NOT session.steer)
701
+ nextTurnFeedback: verdict.achieved ? undefined : verdict.reason,
702
+ },
703
+ eventsPath,
704
+ ) ?? goal;
705
+
706
+ appendEvent(eventsPath, {
707
+ type: "goal.turn_evaluated",
708
+ runId: manifest.runId,
709
+ data: {
710
+ goalId: goal.goalId,
711
+ turn: turnIndex,
712
+ achieved: verdict.achieved,
713
+ reason: verdict.reason,
714
+ },
715
+ });
564
716
  if (!verdict.achieved && goal.nextTurnFeedback) {
565
- appendEvent(eventsPath, { type: "goal.feedback_steered", runId: manifest.runId, data: { goalId: goal.goalId, turn: turnIndex, feedback: goal.nextTurnFeedback } });
717
+ appendEvent(eventsPath, {
718
+ type: "goal.feedback_steered",
719
+ runId: manifest.runId,
720
+ data: {
721
+ goalId: goal.goalId,
722
+ turn: turnIndex,
723
+ feedback: goal.nextTurnFeedback,
724
+ },
725
+ });
566
726
  }
567
727
 
568
728
  // ── STOP CONDITIONS (round-7: re-read disk before applying — external cancel/pause wins) ─
@@ -589,7 +749,15 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
589
749
  const stuck = store.compareAndSetStatus(goal.goalId, "running", "stuck", eventsPath);
590
750
  if (stuck) {
591
751
  goal = stuck;
592
- appendEvent(eventsPath, { type: "goal.stuck", runId: manifest.runId, data: { goalId: goal.goalId, turn: goal.turnsUsed, lastReasons: goal.verdicts.slice(-3).map((v) => v.reason.slice(0, 200)) } });
752
+ appendEvent(eventsPath, {
753
+ type: "goal.stuck",
754
+ runId: manifest.runId,
755
+ data: {
756
+ goalId: goal.goalId,
757
+ turn: goal.turnsUsed,
758
+ lastReasons: goal.verdicts.slice(-3).map((v) => v.reason.slice(0, 200)),
759
+ },
760
+ });
593
761
  break;
594
762
  }
595
763
  }
@@ -606,8 +774,21 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
606
774
  goal = safeSetStatus(store, goal.goalId, "blocked", goal, eventsPath);
607
775
  } finally {
608
776
  // P1g: release the workspace lock (held since loop start).
609
- try { workspaceLock?.release(); } catch { /* best-effort */ }
610
- appendEvent(eventsPath, { type: "goal.loop_end", runId: manifest.runId, data: { goalId: goal.goalId, state: goal.state, turnsUsed: goal.turnsUsed, budgetUsed: goal.budgetUsed } });
777
+ try {
778
+ workspaceLock?.release();
779
+ } catch {
780
+ /* best-effort */
781
+ }
782
+ appendEvent(eventsPath, {
783
+ type: "goal.loop_end",
784
+ runId: manifest.runId,
785
+ data: {
786
+ goalId: goal.goalId,
787
+ state: goal.state,
788
+ turnsUsed: goal.turnsUsed,
789
+ budgetUsed: goal.budgetUsed,
790
+ },
791
+ });
611
792
  }
612
793
 
613
794
  return { manifest, tasks: [], goalState: goal };
@@ -13,15 +13,15 @@
13
13
  * per-turn usage stays on each turn's TeamRunManifest/tasks.json.
14
14
  */
15
15
 
16
- import { mkdirSync, existsSync, readFileSync, writeFileSync, readdirSync, unlinkSync, openSync, closeSync, statSync } from "node:fs";
16
+ import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
17
17
  import { dirname } from "node:path";
18
18
  import { atomicWriteJson } from "../state/atomic-write.ts";
19
19
  import { appendEvent } from "../state/event-log.ts";
20
- import { assertSafePathId } from "../utils/safe-paths.ts";
20
+ import type { GoalLoopState, GoalLoopStatus } from "../state/types.ts";
21
21
  import { createRunId } from "../utils/ids.ts";
22
- import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
23
22
  import { logInternalError } from "../utils/internal-error.ts";
24
- import type { GoalLoopState, GoalLoopStatus } from "../state/types.ts";
23
+ import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
24
+ import { assertSafePathId } from "../utils/safe-paths.ts";
25
25
 
26
26
  /** Default state-root resolver: project scope if a project crew-root exists, else user scope. */
27
27
  function resolveGoalsRoot(cwd: string): string {
@@ -81,7 +81,11 @@ export class GoalStore {
81
81
  mkdirSync(dirname(path), { recursive: true });
82
82
  atomicWriteJson(path, next);
83
83
  if (eventsPath) {
84
- appendEvent(eventsPath, { type: "goal.state_changed", runId: state.goalId, data: { goalId: state.goalId, state: state.state } });
84
+ appendEvent(eventsPath, {
85
+ type: "goal.state_changed",
86
+ runId: state.goalId,
87
+ data: { goalId: state.goalId, state: state.state },
88
+ });
85
89
  }
86
90
  } catch (error) {
87
91
  logInternalError("goal-state-store.save", error, `goalId=${state.goalId}`);
@@ -93,7 +97,12 @@ export class GoalStore {
93
97
  patch(goalId: string, patch: Partial<GoalLoopState>, eventsPath?: string): GoalLoopState | undefined {
94
98
  const current = this.load(goalId);
95
99
  if (!current) return undefined;
96
- const next: GoalLoopState = { ...current, ...patch, goalId: current.goalId, createdAt: current.createdAt };
100
+ const next: GoalLoopState = {
101
+ ...current,
102
+ ...patch,
103
+ goalId: current.goalId,
104
+ createdAt: current.createdAt,
105
+ };
97
106
  this.save(next, eventsPath);
98
107
  return next;
99
108
  }
@@ -126,12 +135,7 @@ export class GoalStore {
126
135
  * OS level — only one process can create the lockfile. The operation is fast (ms), so stale
127
136
  * lockfiles are rare; a 5s age guard force-clears them (crash recovery).
128
137
  */
129
- compareAndSetStatus(
130
- goalId: string,
131
- expected: GoalLoopStatus,
132
- next: GoalLoopStatus,
133
- eventsPath?: string,
134
- ): GoalLoopState | undefined {
138
+ compareAndSetStatus(goalId: string, expected: GoalLoopStatus, next: GoalLoopStatus, eventsPath?: string): GoalLoopState | undefined {
135
139
  const lockPath = `${goalFilePath(this.cwd, goalId)}.cas.lock`;
136
140
  if (!this.acquireCasLock(lockPath)) {
137
141
  return undefined; // Another process holds the CAS lock — caller treats as CAS-failed.
@@ -144,7 +148,11 @@ export class GoalStore {
144
148
  this.save(updated, eventsPath);
145
149
  return updated;
146
150
  } finally {
147
- try { unlinkSync(lockPath); } catch { /* best-effort; may already be gone */ }
151
+ try {
152
+ unlinkSync(lockPath);
153
+ } catch {
154
+ /* best-effort; may already be gone */
155
+ }
148
156
  }
149
157
  }
150
158
 
@@ -162,11 +170,13 @@ export class GoalStore {
162
170
  const stat = statSync(lockPath);
163
171
  if (Date.now() - stat.mtimeMs > 5000) {
164
172
  unlinkSync(lockPath);
165
- const fd = openSync(lockPath, "wx");
166
- closeSync(fd);
167
- return true;
173
+ const fd = openSync(lockPath, "wx");
174
+ closeSync(fd);
175
+ return true;
176
+ }
177
+ } catch {
178
+ /* fall through */
168
179
  }
169
- } catch { /* fall through */ }
170
180
  return false;
171
181
  }
172
182
  }
@@ -35,12 +35,21 @@ export function inferGreenLevelFromTask(success: boolean, contract: Verification
35
35
 
36
36
  export function createVerificationEvidence(contract: VerificationContract, success: boolean, notes: string): VerificationEvidence {
37
37
  const observedGreenLevel = inferGreenLevelFromTask(success, contract);
38
- const outcome = evaluateGreenContract(contract, { requiredGreenLevel: contract.requiredGreenLevel, observedGreenLevel, satisfied: false, commands: [], notes });
38
+ const outcome = evaluateGreenContract(contract, {
39
+ requiredGreenLevel: contract.requiredGreenLevel,
40
+ observedGreenLevel,
41
+ satisfied: false,
42
+ commands: [],
43
+ notes,
44
+ });
39
45
  return {
40
46
  requiredGreenLevel: contract.requiredGreenLevel,
41
47
  observedGreenLevel,
42
48
  satisfied: outcome.satisfied,
43
- commands: contract.commands.map((cmd) => ({ cmd, status: "not_run" as const })),
49
+ commands: contract.commands.map((cmd) => ({
50
+ cmd,
51
+ status: "not_run" as const,
52
+ })),
44
53
  notes,
45
54
  };
46
55
  }