pi-crew 0.9.15 → 0.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21790 -0
  4. package/dist/index.mjs +83070 -0
  5. package/dist/index.mjs.map +7 -0
  6. package/docs/A +358 -0
  7. package/docs/M-A +357 -0
  8. package/docs/REVIEW-FINDINGS-2026-06 +357 -0
  9. package/docs/Y +357 -0
  10. package/docs/a +357 -0
  11. package/docs/aA +358 -0
  12. package/docs/archive/README.md +91 -0
  13. package/docs/patterns/command-agent-skill.md +1 -1
  14. package/docs/skills/REFERENCE.md +0 -17
  15. package/docs//303/242mmaAAA/303/242 +357 -0
  16. package/index.bundle.ts +25 -0
  17. package/index.ts +95 -4
  18. package/package.json +15 -4
  19. package/skills/iterative-audit/SKILL.md +0 -1
  20. package/skills/widget-rendering/SKILL.md +17 -0
  21. package/src/adapters/claude-adapter.ts +1 -3
  22. package/src/adapters/export-util.ts +16 -16
  23. package/src/adapters/index.ts +5 -5
  24. package/src/agents/agent-config.ts +20 -14
  25. package/src/agents/agent-serializer.ts +1 -1
  26. package/src/agents/discover-agents.ts +83 -39
  27. package/src/benchmark/benchmark-runner.ts +51 -51
  28. package/src/benchmark/feedback-loop.ts +1 -1
  29. package/src/config/config.ts +166 -590
  30. package/src/config/defaults.ts +28 -2
  31. package/src/config/drift-detector.ts +11 -14
  32. package/src/config/markers.ts +201 -203
  33. package/src/config/resilient-parser.ts +14 -6
  34. package/src/config/role-tools.ts +3 -3
  35. package/src/config/suggestions.ts +5 -12
  36. package/src/config/types.ts +9 -25
  37. package/src/errors.ts +151 -157
  38. package/src/extension/action-suggestions.ts +54 -9
  39. package/src/extension/async-notifier.ts +43 -13
  40. package/src/extension/autonomous-policy.ts +77 -37
  41. package/src/extension/command-completions.ts +9 -8
  42. package/src/extension/context-status-injection.ts +14 -12
  43. package/src/extension/crew-autocomplete.ts +8 -16
  44. package/src/extension/crew-cleanup.ts +2 -1
  45. package/src/extension/crew-shortcuts.ts +9 -6
  46. package/src/extension/cross-extension-rpc.ts +136 -42
  47. package/src/extension/help.ts +1 -1
  48. package/src/extension/import-index.ts +10 -5
  49. package/src/extension/knowledge-injection.ts +88 -15
  50. package/src/extension/management.ts +89 -349
  51. package/src/extension/message-renderers.ts +3 -4
  52. package/src/extension/notification-router.ts +22 -5
  53. package/src/extension/notification-sink.ts +6 -3
  54. package/src/extension/pi-api.ts +17 -8
  55. package/src/extension/plan-orchestrate.ts +8 -27
  56. package/src/extension/project-init.ts +21 -6
  57. package/src/extension/register.ts +329 -975
  58. package/src/extension/registration/artifact-cleanup.ts +9 -4
  59. package/src/extension/registration/command-utils.ts +5 -1
  60. package/src/extension/registration/commands.ts +806 -314
  61. package/src/extension/registration/compaction-guard.ts +15 -15
  62. package/src/extension/registration/lifecycle.ts +259 -0
  63. package/src/extension/registration/observability.ts +293 -0
  64. package/src/extension/registration/subagent-helpers.ts +29 -7
  65. package/src/extension/registration/subagent-tools.ts +253 -44
  66. package/src/extension/registration/team-tool.ts +19 -84
  67. package/src/extension/registration/ui.ts +168 -0
  68. package/src/extension/registration/viewers.ts +64 -24
  69. package/src/extension/result-watcher.ts +18 -7
  70. package/src/extension/run-bundle-schema.ts +21 -6
  71. package/src/extension/run-export.ts +15 -6
  72. package/src/extension/run-import.ts +64 -29
  73. package/src/extension/run-index.ts +28 -9
  74. package/src/extension/run-maintenance.ts +32 -12
  75. package/src/extension/session-summary.ts +1 -1
  76. package/src/extension/team-manager-command.ts +64 -7
  77. package/src/extension/team-onboard.ts +151 -150
  78. package/src/extension/team-recommendation.ts +121 -21
  79. package/src/extension/team-tool/anchor.ts +31 -64
  80. package/src/extension/team-tool/api.ts +929 -141
  81. package/src/extension/team-tool/auto-summarize.ts +14 -26
  82. package/src/extension/team-tool/cache-control.ts +1 -5
  83. package/src/extension/team-tool/cancel.ts +138 -38
  84. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  85. package/src/extension/team-tool/chain-executor.ts +29 -51
  86. package/src/extension/team-tool/config-patch.ts +1 -1
  87. package/src/extension/team-tool/context.ts +40 -20
  88. package/src/extension/team-tool/destructive-gate.ts +10 -5
  89. package/src/extension/team-tool/doctor.ts +170 -51
  90. package/src/extension/team-tool/explain.ts +228 -214
  91. package/src/extension/team-tool/failure-patterns.ts +3 -9
  92. package/src/extension/team-tool/goal-wrap.ts +71 -29
  93. package/src/extension/team-tool/goal.ts +185 -35
  94. package/src/extension/team-tool/handle-schedule.ts +34 -15
  95. package/src/extension/team-tool/handle-settings.ts +94 -56
  96. package/src/extension/team-tool/health-monitor.ts +27 -108
  97. package/src/extension/team-tool/inspect.ts +42 -9
  98. package/src/extension/team-tool/intent-policy.ts +4 -1
  99. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  100. package/src/extension/team-tool/orchestrate.ts +7 -18
  101. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  102. package/src/extension/team-tool/plan.ts +29 -5
  103. package/src/extension/team-tool/respond.ts +49 -16
  104. package/src/extension/team-tool/run-not-found.ts +3 -8
  105. package/src/extension/team-tool/run.ts +80 -317
  106. package/src/extension/team-tool/status.ts +135 -43
  107. package/src/extension/team-tool/workflow-manage.ts +92 -25
  108. package/src/extension/team-tool-types.ts +8 -1
  109. package/src/extension/team-tool.ts +142 -551
  110. package/src/extension/validate-resources.ts +39 -8
  111. package/src/hooks/registry.ts +66 -17
  112. package/src/hooks/types.ts +1 -1
  113. package/src/i18n.ts +25 -11
  114. package/src/observability/correlation.ts +17 -3
  115. package/src/observability/event-bus.ts +51 -56
  116. package/src/observability/event-to-metric.ts +117 -15
  117. package/src/observability/exporters/otlp-exporter.ts +56 -17
  118. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  119. package/src/observability/metric-registry.ts +16 -4
  120. package/src/observability/metric-retention.ts +8 -2
  121. package/src/observability/metric-sink.ts +11 -3
  122. package/src/observability/metrics-primitives.ts +42 -9
  123. package/src/plugins/plugin-define.ts +2 -2
  124. package/src/plugins/plugin-registry.ts +25 -25
  125. package/src/plugins/plugins/index.ts +1 -1
  126. package/src/plugins/plugins/nextjs.ts +16 -16
  127. package/src/plugins/plugins/vite.ts +7 -11
  128. package/src/plugins/plugins/vitest.ts +6 -11
  129. package/src/runtime/adaptive-plan.ts +225 -40
  130. package/src/runtime/agent-control.ts +58 -15
  131. package/src/runtime/agent-memory.ts +15 -9
  132. package/src/runtime/agent-observability.ts +10 -2
  133. package/src/runtime/anchor-manager.ts +27 -25
  134. package/src/runtime/async-marker.ts +7 -1
  135. package/src/runtime/async-runner.ts +27 -23
  136. package/src/runtime/auto-summarize.ts +7 -13
  137. package/src/runtime/background-runner.ts +189 -251
  138. package/src/runtime/batch-barrier.ts +18 -16
  139. package/src/runtime/cancellation-token.ts +16 -6
  140. package/src/runtime/cancellation.ts +46 -8
  141. package/src/runtime/capability-inventory.ts +6 -4
  142. package/src/runtime/chain-parser.ts +44 -11
  143. package/src/runtime/chain-runner.ts +63 -69
  144. package/src/runtime/checkpoint.ts +12 -56
  145. package/src/runtime/child-pi.ts +343 -78
  146. package/src/runtime/coalesce-tasks.ts +268 -0
  147. package/src/runtime/code-summary.ts +71 -26
  148. package/src/runtime/compact-stages/index.ts +15 -5
  149. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  150. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  151. package/src/runtime/compaction-summary.ts +17 -10
  152. package/src/runtime/completion-guard.ts +33 -16
  153. package/src/runtime/crash-classification.ts +28 -7
  154. package/src/runtime/crash-recovery.ts +177 -37
  155. package/src/runtime/crew-agent-records.ts +193 -40
  156. package/src/runtime/crew-hooks.ts +9 -24
  157. package/src/runtime/cross-extension-rpc.ts +25 -23
  158. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  159. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  160. package/src/runtime/delivery-coordinator.ts +34 -7
  161. package/src/runtime/delta-conflict.ts +9 -21
  162. package/src/runtime/deterministic-ast.ts +1 -1
  163. package/src/runtime/diagnostic-export.ts +53 -20
  164. package/src/runtime/direct-run.ts +12 -2
  165. package/src/runtime/dwf-state-store.ts +1 -1
  166. package/src/runtime/dynamic-workflow-context.ts +186 -58
  167. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  168. package/src/runtime/effectiveness.ts +16 -8
  169. package/src/runtime/errors/crew-errors.ts +7 -11
  170. package/src/runtime/event-stream-bridge.ts +9 -3
  171. package/src/runtime/foreground-control.ts +54 -14
  172. package/src/runtime/foreground-watchdog.ts +9 -6
  173. package/src/runtime/goal-achievement.ts +27 -10
  174. package/src/runtime/goal-evaluator.ts +54 -19
  175. package/src/runtime/goal-loop-runner.ts +280 -99
  176. package/src/runtime/goal-state-store.ts +27 -17
  177. package/src/runtime/green-contract.ts +11 -2
  178. package/src/runtime/group-join.ts +64 -31
  179. package/src/runtime/handoff-manager.ts +37 -42
  180. package/src/runtime/heartbeat-gradient.ts +10 -2
  181. package/src/runtime/heartbeat-watcher.ts +32 -6
  182. package/src/runtime/hidden-handoff.ts +12 -30
  183. package/src/runtime/important-line-classifier.ts +12 -2
  184. package/src/runtime/iteration-hooks.ts +25 -15
  185. package/src/runtime/live-agent-control.ts +55 -7
  186. package/src/runtime/live-agent-manager.ts +130 -27
  187. package/src/runtime/live-control-realtime.ts +23 -3
  188. package/src/runtime/live-irc.ts +4 -1
  189. package/src/runtime/live-session-health.ts +12 -4
  190. package/src/runtime/live-session-runtime.ts +379 -117
  191. package/src/runtime/manifest-cache.ts +28 -12
  192. package/src/runtime/mcp-proxy.ts +8 -16
  193. package/src/runtime/metric-parser.ts +1 -5
  194. package/src/runtime/model-fallback.ts +55 -18
  195. package/src/runtime/model-resolver.ts +2 -6
  196. package/src/runtime/model-scope.ts +19 -3
  197. package/src/runtime/notebook-helpers.ts +60 -62
  198. package/src/runtime/orphan-worker-registry.ts +37 -26
  199. package/src/runtime/output-validator.ts +18 -3
  200. package/src/runtime/overflow-recovery.ts +5 -4
  201. package/src/runtime/parallel-research.ts +29 -5
  202. package/src/runtime/parallel-utils.ts +9 -11
  203. package/src/runtime/path-overlap.ts +150 -0
  204. package/src/runtime/peer-dep.ts +12 -17
  205. package/src/runtime/per-write-validator.ts +1 -3
  206. package/src/runtime/phase-tracker.ts +342 -330
  207. package/src/runtime/pi-args.ts +26 -8
  208. package/src/runtime/pi-json-output.ts +1 -1
  209. package/src/runtime/pi-spawn.ts +30 -19
  210. package/src/runtime/pipeline-runner.ts +56 -55
  211. package/src/runtime/plan-templates.ts +5 -6
  212. package/src/runtime/policy-engine.ts +43 -7
  213. package/src/runtime/post-checks.ts +12 -4
  214. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  215. package/src/runtime/process-lifecycle.ts +20 -10
  216. package/src/runtime/process-status.ts +16 -5
  217. package/src/runtime/progress-event-coalescer.ts +2 -1
  218. package/src/runtime/progress-tracker.ts +103 -103
  219. package/src/runtime/prose-compressor.ts +9 -11
  220. package/src/runtime/recovery-recipes.ts +118 -24
  221. package/src/runtime/replace.ts +25 -10
  222. package/src/runtime/resilient-edit.ts +10 -25
  223. package/src/runtime/result-extractor.ts +1 -2
  224. package/src/runtime/retry-executor.ts +20 -4
  225. package/src/runtime/retry-runner.ts +26 -50
  226. package/src/runtime/role-permission.ts +6 -1
  227. package/src/runtime/run-coalesced-task-group.ts +256 -0
  228. package/src/runtime/run-drift.ts +14 -15
  229. package/src/runtime/run-tracker.ts +6 -28
  230. package/src/runtime/runtime-policy.ts +1 -6
  231. package/src/runtime/runtime-resolver.ts +80 -15
  232. package/src/runtime/scheduler.ts +47 -18
  233. package/src/runtime/semaphore.ts +5 -7
  234. package/src/runtime/sensitive-paths.ts +2 -1
  235. package/src/runtime/session-usage.ts +1 -1
  236. package/src/runtime/settings-store.ts +16 -12
  237. package/src/runtime/sidechain-output.ts +6 -2
  238. package/src/runtime/single-agent-compose.ts +1 -4
  239. package/src/runtime/skill-effectiveness.ts +29 -97
  240. package/src/runtime/skill-instructions.ts +40 -83
  241. package/src/runtime/stale-reconciler.ts +73 -197
  242. package/src/runtime/stream-preview.ts +1 -1
  243. package/src/runtime/streaming-output.ts +1 -1
  244. package/src/runtime/subagent-manager.ts +86 -169
  245. package/src/runtime/subprocess-tool-registry.ts +4 -1
  246. package/src/runtime/supervisor-contact.ts +10 -5
  247. package/src/runtime/task-display.ts +19 -5
  248. package/src/runtime/task-graph-scheduler.ts +95 -21
  249. package/src/runtime/task-graph.ts +5 -11
  250. package/src/runtime/task-health.ts +54 -47
  251. package/src/runtime/task-id.ts +11 -18
  252. package/src/runtime/task-output-context.ts +265 -81
  253. package/src/runtime/task-packet.ts +12 -20
  254. package/src/runtime/task-quality.ts +6 -14
  255. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  256. package/src/runtime/task-runner/live-executor.ts +114 -36
  257. package/src/runtime/task-runner/output-splitter.ts +152 -0
  258. package/src/runtime/task-runner/progress.ts +50 -12
  259. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  260. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  261. package/src/runtime/task-runner/result-utils.ts +3 -1
  262. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  263. package/src/runtime/task-runner/run-projection.ts +27 -8
  264. package/src/runtime/task-runner/state-helpers.ts +29 -8
  265. package/src/runtime/task-runner/tail-read.ts +2 -6
  266. package/src/runtime/task-runner.ts +180 -326
  267. package/src/runtime/team-runner-artifacts.ts +13 -0
  268. package/src/runtime/team-runner.ts +456 -974
  269. package/src/runtime/tool-output-pruner.ts +6 -9
  270. package/src/runtime/tool-progress.ts +11 -21
  271. package/src/runtime/verification-gates.ts +33 -24
  272. package/src/runtime/verification-integrity.ts +1 -4
  273. package/src/runtime/verification-worktree.ts +62 -12
  274. package/src/runtime/worker-heartbeat.ts +5 -1
  275. package/src/runtime/worker-startup.ts +29 -5
  276. package/src/runtime/workflow-state.ts +17 -9
  277. package/src/runtime/workspace-lock.ts +30 -35
  278. package/src/runtime/workspace-tree.ts +20 -32
  279. package/src/runtime/yield-handler.ts +43 -9
  280. package/src/runtime/zombie-scanner.ts +5 -4
  281. package/src/schema/config-schema.ts +266 -172
  282. package/src/schema/team-tool-schema.ts +29 -74
  283. package/src/schema/validation-types.ts +25 -17
  284. package/src/skills/discover-skills.ts +35 -10
  285. package/src/skills/skill-templates.ts +109 -27
  286. package/src/skills/validate.ts +26 -8
  287. package/src/state/active-run-registry.ts +87 -28
  288. package/src/state/artifact-store.ts +9 -5
  289. package/src/state/atomic-write-v2.ts +85 -63
  290. package/src/state/atomic-write.ts +105 -26
  291. package/src/state/blob-store.ts +47 -20
  292. package/src/state/contracts.ts +20 -5
  293. package/src/state/crew-init.ts +5 -22
  294. package/src/state/decision-ledger.ts +19 -73
  295. package/src/state/event-log-rotation.ts +41 -15
  296. package/src/state/event-log.ts +168 -53
  297. package/src/state/event-reconstructor.ts +11 -1
  298. package/src/state/gitignore-manager.ts +2 -8
  299. package/src/state/health-store.ts +57 -57
  300. package/src/state/hook-instinct-bridge.ts +1 -1
  301. package/src/state/hook-integrations.ts +1 -1
  302. package/src/state/instinct-store.ts +17 -5
  303. package/src/state/jsonl-writer.ts +1 -1
  304. package/src/state/locks.ts +23 -9
  305. package/src/state/mailbox.ts +151 -28
  306. package/src/state/observation-store.ts +20 -14
  307. package/src/state/run-cache.ts +142 -135
  308. package/src/state/run-graph.ts +6 -15
  309. package/src/state/run-metrics.ts +5 -18
  310. package/src/state/schedule.ts +15 -9
  311. package/src/state/state-store.ts +129 -45
  312. package/src/state/task-claims.ts +12 -2
  313. package/src/state/tiered-eval.ts +52 -43
  314. package/src/state/types-eval.ts +2 -2
  315. package/src/state/types.ts +24 -20
  316. package/src/state/usage.ts +20 -4
  317. package/src/state/worker-atomic-writer.ts +6 -3
  318. package/src/subagents/index.ts +2 -2
  319. package/src/teams/discover-teams.ts +21 -6
  320. package/src/tools/safe-bash-extension.ts +5 -6
  321. package/src/tools/safe-bash.ts +10 -11
  322. package/src/types/new-api-types.ts +6 -10
  323. package/src/ui/agent-management-overlay.ts +52 -40
  324. package/src/ui/card-colors.ts +13 -4
  325. package/src/ui/crew-footer.ts +8 -7
  326. package/src/ui/crew-select-list.ts +1 -1
  327. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  328. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  329. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  330. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  331. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  332. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  333. package/src/ui/heartbeat-aggregator.ts +15 -4
  334. package/src/ui/keybinding-map.ts +40 -7
  335. package/src/ui/live-conversation-overlay.ts +36 -20
  336. package/src/ui/live-duration.ts +1 -4
  337. package/src/ui/live-run-sidebar.ts +88 -25
  338. package/src/ui/loaders.ts +2 -8
  339. package/src/ui/mascot.ts +20 -36
  340. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  341. package/src/ui/overlays/confirm-overlay.ts +4 -2
  342. package/src/ui/overlays/help-overlay.ts +25 -14
  343. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  344. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  345. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  346. package/src/ui/pi-ui-compat.ts +7 -7
  347. package/src/ui/powerbar-publisher.ts +120 -42
  348. package/src/ui/render-diff.ts +10 -3
  349. package/src/ui/render-scheduler.ts +12 -4
  350. package/src/ui/run-action-dispatcher.ts +81 -18
  351. package/src/ui/run-dashboard.ts +172 -76
  352. package/src/ui/run-event-bus.ts +59 -37
  353. package/src/ui/run-snapshot-cache.ts +276 -86
  354. package/src/ui/settings-overlay.ts +361 -74
  355. package/src/ui/status-colors.ts +12 -1
  356. package/src/ui/syntax-highlight.ts +1 -1
  357. package/src/ui/theme-adapter.ts +16 -14
  358. package/src/ui/theme-discovery.ts +13 -2
  359. package/src/ui/tool-progress-formatter.ts +7 -7
  360. package/src/ui/tool-render.ts +128 -56
  361. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  362. package/src/ui/tool-renderers/index.ts +109 -43
  363. package/src/ui/transcript-cache.ts +32 -6
  364. package/src/ui/transcript-entries.ts +25 -23
  365. package/src/ui/transcript-viewer.ts +78 -29
  366. package/src/ui/widget/index.ts +99 -40
  367. package/src/ui/widget/widget-formatters.ts +13 -6
  368. package/src/ui/widget/widget-model.ts +19 -8
  369. package/src/ui/widget/widget-renderer.ts +23 -13
  370. package/src/ui/widget/widget-types.ts +1 -1
  371. package/src/utils/bm25-search.ts +199 -199
  372. package/src/utils/conflict-detect.ts +22 -21
  373. package/src/utils/env-filter.ts +14 -7
  374. package/src/utils/file-coalescer.ts +5 -1
  375. package/src/utils/fingerprint.ts +3 -6
  376. package/src/utils/frontmatter.ts +3 -1
  377. package/src/utils/fs-watch.ts +2 -6
  378. package/src/utils/gh-protocol.ts +119 -44
  379. package/src/utils/git.ts +13 -15
  380. package/src/utils/guards.ts +2 -5
  381. package/src/utils/ids.ts +9 -2
  382. package/src/utils/incremental-reader.ts +14 -3
  383. package/src/utils/internal-error.ts +2 -1
  384. package/src/utils/names.ts +12 -3
  385. package/src/utils/paths.ts +49 -5
  386. package/src/utils/project-detector.ts +2 -2
  387. package/src/utils/redaction.ts +29 -19
  388. package/src/utils/resolve-shell.ts +9 -7
  389. package/src/utils/run-watcher-registry.ts +19 -31
  390. package/src/utils/safe-paths.ts +45 -24
  391. package/src/utils/scan-cache.ts +9 -2
  392. package/src/utils/session-utils.ts +2 -4
  393. package/src/utils/sleep.ts +12 -5
  394. package/src/utils/sse-parser.ts +5 -17
  395. package/src/utils/visual.ts +52 -32
  396. package/src/workflows/discover-workflows.ts +41 -109
  397. package/src/workflows/intermediate-store.ts +5 -21
  398. package/src/workflows/preflight-validator.ts +6 -33
  399. package/src/workflows/topology-analyzer.ts +5 -21
  400. package/src/workflows/workflow-config.ts +7 -6
  401. package/src/worktree/branch-freshness.ts +66 -8
  402. package/src/worktree/cleanup.ts +130 -20
  403. package/src/worktree/worktree-manager.ts +212 -69
  404. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  405. package/skills/detection-pipeline-design/SKILL.md +0 -286
  406. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  407. package/skills/incident-playbook-construction/SKILL.md +0 -384
  408. package/skills/security-review/SKILL.md +0 -561
  409. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  410. package/skills/ui-render-performance/SKILL.md +0 -58
  411. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  412. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  413. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  414. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  415. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  416. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  417. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  418. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  419. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  420. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  421. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  422. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  430. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  431. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  432. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -17,26 +17,22 @@
17
17
  * nonce-token feedback, worker cap, workspace lock) all apply.
18
18
  */
19
19
 
20
- import { createRunPaths, saveRunManifest } from "../../state/state-store.ts";
20
+ import { loadConfig } from "../../config/config.ts";
21
+ import type { GoalWrapWorkflowConfig } from "../../config/types.ts";
22
+ import { GoalStore } from "../../runtime/goal-state-store.ts";
23
+ import { snapshotManifests } from "../../runtime/verification-integrity.ts";
24
+ import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
21
25
  import { atomicWriteJson } from "../../state/atomic-write.ts";
22
26
  import { appendEvent } from "../../state/event-log.ts";
27
+ import { createRunPaths, saveRunManifest } from "../../state/state-store.ts";
28
+ import type { GoalLoopState, TeamRunManifest } from "../../state/types.ts";
23
29
  import { spawnBackgroundTeamRun } from "../../subagents/async-entry.ts";
24
- import { GoalStore } from "../../runtime/goal-state-store.ts";
25
- import { snapshotManifests } from "../../runtime/verification-integrity.ts";
26
30
  import { logInternalError } from "../../utils/internal-error.ts";
27
- import { loadConfig } from "../../config/config.ts";
28
- import type { GoalLoopState, TeamRunManifest } from "../../state/types.ts";
29
- import type { GoalWrapWorkflowConfig } from "../../config/types.ts";
30
- import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
31
31
  import type { WorkflowConfig } from "../../workflows/workflow-config.ts";
32
32
  import { result, type TeamContext } from "./context.ts";
33
33
 
34
34
  /** Builtin workflows eligible for goal-wrap (have a clear "done" condition). */
35
- export const GOAL_WRAP_ELIGIBLE_BUILTINS = new Set([
36
- "implementation",
37
- "fast-fix",
38
- "default",
39
- ]);
35
+ export const GOAL_WRAP_ELIGIBLE_BUILTINS = new Set(["implementation", "fast-fix", "default"]);
40
36
 
41
37
  /**
42
38
  * Maximum number of workflow steps allowed for goal-wrap.
@@ -63,10 +59,7 @@ export const GOAL_WRAP_MAX_STEPS = 1;
63
59
  export type { GoalWrapWorkflowConfig };
64
60
 
65
61
  /** Read the goal-wrap config for a given workflow name (merged user + project config). */
66
- export function readGoalWrapConfig(
67
- cwd: string,
68
- workflowName: string,
69
- ): GoalWrapWorkflowConfig | undefined {
62
+ export function readGoalWrapConfig(cwd: string, workflowName: string): GoalWrapWorkflowConfig | undefined {
70
63
  const loaded = loadConfig(cwd);
71
64
  const cfg = loaded.config?.goalWrap as Record<string, GoalWrapWorkflowConfig> | undefined;
72
65
  if (!cfg) return undefined;
@@ -84,9 +77,7 @@ export function isGoalWrapEnabled(cwd: string, workflowName: string): boolean {
84
77
  * Validate a goal-wrap config entry. Returns an error string if invalid, undefined if OK.
85
78
  * Mirrors the Phase 1 validation: budget required (or unlimited), evaluatorModel required.
86
79
  */
87
- export function validateGoalWrapConfig(
88
- wc: GoalWrapWorkflowConfig,
89
- ): string | undefined {
80
+ export function validateGoalWrapConfig(wc: GoalWrapWorkflowConfig): string | undefined {
90
81
  if (!wc.evaluatorModel) {
91
82
  return "goalWrap config requires evaluatorModel (the goal-judge model). No silent default.";
92
83
  }
@@ -125,7 +116,11 @@ export function persistAsyncOnGoalLoopManifest(
125
116
  ): void {
126
117
  const asyncGoalManifest = {
127
118
  ...manifest,
128
- async: { pid: spawned.pid, logPath: spawned.logPath, spawnedAt: new Date().toISOString() },
119
+ async: {
120
+ pid: spawned.pid,
121
+ logPath: spawned.logPath,
122
+ spawnedAt: new Date().toISOString(),
123
+ },
129
124
  };
130
125
  atomicWriteJson(manifestPath, asyncGoalManifest);
131
126
  }
@@ -146,14 +141,27 @@ export function persistAsyncOnGoalLoopManifest(
146
141
  * goal-wrap is unsafe for a given workflow, silently fall back so the user
147
142
  * still gets the workflow run they asked for.
148
143
  */
149
- export function shouldGoalWrap(cwd: string, workflow: WorkflowConfig): { enabled: true } | { enabled: false; reason: "config-off" | "multi-step" | "invalid-config"; message?: string } {
144
+ export function shouldGoalWrap(
145
+ cwd: string,
146
+ workflow: WorkflowConfig,
147
+ ):
148
+ | { enabled: true }
149
+ | {
150
+ enabled: false;
151
+ reason: "config-off" | "multi-step" | "invalid-config";
152
+ message?: string;
153
+ } {
150
154
  const wc = readGoalWrapConfig(cwd, workflow.name);
151
155
  if (!wc || wc.enabled !== true) {
152
156
  return { enabled: false, reason: "config-off" };
153
157
  }
154
158
  const validationError = validateGoalWrapConfig(wc);
155
159
  if (validationError) {
156
- return { enabled: false, reason: "invalid-config", message: validationError };
160
+ return {
161
+ enabled: false,
162
+ reason: "invalid-config",
163
+ message: validationError,
164
+ };
157
165
  }
158
166
  if (workflow.steps.length > GOAL_WRAP_MAX_STEPS) {
159
167
  return {
@@ -174,7 +182,11 @@ export async function startGoalWrappedRun(
174
182
  const cwd = ctx.cwd;
175
183
  const wc = readGoalWrapConfig(cwd, workflow.name);
176
184
  if (!wc || wc.enabled !== true) {
177
- return result(`goal-wrap is not enabled for workflow '${workflow.name}' in .crew/config.json.`, { action: "run", status: "error" }, true);
185
+ return result(
186
+ `goal-wrap is not enabled for workflow '${workflow.name}' in .crew/config.json.`,
187
+ { action: "run", status: "error" },
188
+ true,
189
+ );
178
190
  }
179
191
  const validationError = validateGoalWrapConfig(wc);
180
192
  if (validationError) {
@@ -196,7 +208,10 @@ export async function startGoalWrappedRun(
196
208
  verificationIntegrity = "none-text-only";
197
209
  } else {
198
210
  try {
199
- verificationIntegrity = { snapshot: snapshotManifests(cwd), takenAt: now };
211
+ verificationIntegrity = {
212
+ snapshot: snapshotManifests(cwd),
213
+ takenAt: now,
214
+ };
200
215
  } catch (error) {
201
216
  logInternalError("goal-wrap.integritySnapshot", error, `goalId=${goalId}`);
202
217
  verificationIntegrity = "none-text-only";
@@ -254,7 +269,16 @@ export async function startGoalWrappedRun(
254
269
  runKind: "goal-loop",
255
270
  };
256
271
  saveRunManifest(goalLoopManifest);
257
- appendEvent(paths.eventsPath, { type: "goal.loop_start", runId: goalId, data: { goalId, objective: goal, maxTurns, goalWrapWorkflow: workflow.name } });
272
+ appendEvent(paths.eventsPath, {
273
+ type: "goal.loop_start",
274
+ runId: goalId,
275
+ data: {
276
+ goalId,
277
+ objective: goal,
278
+ maxTurns,
279
+ goalWrapWorkflow: workflow.name,
280
+ },
281
+ });
258
282
 
259
283
  const spawned = await spawnBackgroundTeamRun(goalLoopManifest);
260
284
  const pid = spawned.pid ?? 0;
@@ -264,8 +288,18 @@ export async function startGoalWrappedRun(
264
288
  // background runner dies (it currently dies silently due to a multi-step
265
289
  // atomic-write bug — see investigation report). Mirrors run.ts:371-372 which
266
290
  // writes asyncManifest = { ...effectiveManifest, async: {...} } to manifestPath.
267
- persistAsyncOnGoalLoopManifest(paths.manifestPath, goalLoopManifest, { pid, logPath: spawned.logPath });
268
- const withAsync = { ...goalState, async: { pid, logPath: spawned.logPath, spawnedAt: new Date().toISOString() } };
291
+ persistAsyncOnGoalLoopManifest(paths.manifestPath, goalLoopManifest, {
292
+ pid,
293
+ logPath: spawned.logPath,
294
+ });
295
+ const withAsync = {
296
+ ...goalState,
297
+ async: {
298
+ pid,
299
+ logPath: spawned.logPath,
300
+ spawnedAt: new Date().toISOString(),
301
+ },
302
+ };
269
303
  store.save(withAsync);
270
304
 
271
305
  return result(
@@ -273,12 +307,20 @@ export async function startGoalWrappedRun(
273
307
  `Goal-wrapped '${workflow.name}' started (background pid=${pid}).`,
274
308
  `Goal ${goalId} [running] — worker = '${workflow.name}' workflow, judged each turn by ${wc.evaluatorModel}.`,
275
309
  ` turn: 0/${maxTurns} budget: ${wc.budgetUnlimited ? "∞ (unlimited)" : `${wc.budgetTotal}`}`,
276
- verification?.commands?.length ? ` verification: ${verification.commands.join(", ")}` : " verification: text-only (no objective oracle)",
310
+ verification?.commands?.length
311
+ ? ` verification: ${verification.commands.join(", ")}`
312
+ : " verification: text-only (no objective oracle)",
277
313
  ``,
278
314
  `Next: \`team action='goal' config.subAction='status' config.goalId='${goalId}'\`.`,
279
315
  `Log: ${spawned.logPath}`,
280
316
  ].join("\n"),
281
- { action: "run", status: "ok", runId: goalId, artifactsRoot: paths.artifactsRoot, data: { goalId, goalWrap: true, workflow: workflow.name, pid } },
317
+ {
318
+ action: "run",
319
+ status: "ok",
320
+ runId: goalId,
321
+ artifactsRoot: paths.artifactsRoot,
322
+ data: { goalId, goalWrap: true, workflow: workflow.name, pid },
323
+ },
282
324
  false,
283
325
  );
284
326
  } catch (error) {
@@ -14,16 +14,16 @@
14
14
  * §0c C11 (control path: cooperative flag + handleCancel(currentRunId) via abortOwned).
15
15
  */
16
16
 
17
- import { result, type TeamContext } from "./context.ts";
18
- import { createRunPaths, saveRunManifest } from "../../state/state-store.ts";
19
- import { appendEvent } from "../../state/event-log.ts";
20
- import { spawnBackgroundTeamRun } from "../../subagents/async-entry.ts";
21
17
  import { GoalStore } from "../../runtime/goal-state-store.ts";
22
- import { logInternalError } from "../../utils/internal-error.ts";
23
18
  import { snapshotManifests } from "../../runtime/verification-integrity.ts";
24
19
  import { acquireWorkspaceLock, isWorkspaceBusy, type WorkspaceLockHandle } from "../../runtime/workspace-lock.ts";
25
- import type { GoalLoopState, GoalLoopStatus, TeamRunManifest } from "../../state/types.ts";
26
20
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
21
+ import { appendEvent } from "../../state/event-log.ts";
22
+ import { createRunPaths, saveRunManifest } from "../../state/state-store.ts";
23
+ import type { GoalLoopState, GoalLoopStatus, TeamRunManifest } from "../../state/types.ts";
24
+ import { spawnBackgroundTeamRun } from "../../subagents/async-entry.ts";
25
+ import { logInternalError } from "../../utils/internal-error.ts";
26
+ import { result, type TeamContext } from "./context.ts";
27
27
 
28
28
  const MAX_GOAL_OBJECTIVE_CHARS = 4000;
29
29
 
@@ -63,10 +63,14 @@ function formatGoalStatus(goal: GoalLoopState): string {
63
63
  ` objective: ${goal.objective.slice(0, 200)}${goal.objective.length > 200 ? "…" : ""}`,
64
64
  ` turn: ${goal.turnsUsed}/${goal.maxTurns} budget: ${goal.budgetUsed}/${goal.budgetTotal ?? "∞"} (${budgetPct})`,
65
65
  ` evaluator: ${goal.evaluatorModel} worker: ${goal.workerAgent ?? "executor"}`,
66
- last ? ` last verdict (turn ${last.turn}): ${last.achieved ? "ACHIEVED" : "not-achieved"} — ${last.reason.slice(0, 300)}` : " (no verdicts yet)",
66
+ last
67
+ ? ` last verdict (turn ${last.turn}): ${last.achieved ? "ACHIEVED" : "not-achieved"} — ${last.reason.slice(0, 300)}`
68
+ : " (no verdicts yet)",
67
69
  goal.nextTurnFeedback ? ` next-turn feedback: ${goal.nextTurnFeedback.slice(0, 200)}` : "",
68
70
  goal.currentRunId ? ` current turn runId: ${goal.currentRunId}` : "",
69
- ].filter(Boolean).join("\n");
71
+ ]
72
+ .filter(Boolean)
73
+ .join("\n");
70
74
  }
71
75
 
72
76
  /** `goal start` — create state + spawn background process (P0: writes state; spawn wiring TBD per host). */
@@ -79,16 +83,16 @@ async function handleStart(input: GoalSubActionInput): Promise<ReturnType<typeof
79
83
  const goalId = store.createGoalId();
80
84
  const ownerSessionId = ctx.sessionId ?? "unknown";
81
85
  const now = new Date().toISOString();
82
- const maxTurns = typeof params.config?.maxTurns === "number" && params.config.maxTurns > 0
83
- ? params.config.maxTurns
84
- : 20; // Claude/Codex parity default
86
+ const maxTurns = typeof params.config?.maxTurns === "number" && params.config.maxTurns > 0 ? params.config.maxTurns : 20; // Claude/Codex parity default
85
87
  // P1d (RFC v0.5 §P1d): budget is REQUIRED. Either an explicit budgetTotal (>=1000, enforced
86
88
  // by schema) OR an explicit budgetUnlimited:true opt-out (audit-logged). No silent unbounded
87
89
  // default — without a cap the loop could spend unboundedly across many turns × workers.
88
90
  const budgetUnlimited = params.config?.budgetUnlimited === true;
89
91
  const hasBudgetTotal = typeof params.budgetTotal === "number" && params.budgetTotal >= 1000;
90
92
  if (!budgetUnlimited && !hasBudgetTotal) {
91
- throw new Error("`goal start` requires either config.budgetTotal (>=1000, schema-enforced) OR config.budgetUnlimited:true (audit-logged opt-out). No silent unbounded-spend default.");
93
+ throw new Error(
94
+ "`goal start` requires either config.budgetTotal (>=1000, schema-enforced) OR config.budgetUnlimited:true (audit-logged opt-out). No silent unbounded-spend default.",
95
+ );
92
96
  }
93
97
  // Cold-review #2 nit: reject the mutually-exclusive combination. If both are set the
94
98
  // runner silently lets budgetUnlimited win, surprising the user.
@@ -101,9 +105,17 @@ async function handleStart(input: GoalSubActionInput): Promise<ReturnType<typeof
101
105
  // acquiring. The authoritative acquisition happens in runGoalLoop.
102
106
  const busyOwner = isWorkspaceBusy(cwd);
103
107
  if (busyOwner) {
104
- throw new Error(`Workspace '${cwd}' is already locked by goal '${busyOwner}'. Concurrent goals on the same single-workspace cwd are serialized to prevent edit clobbering. Stop the other goal first, or use a separate workspace.`);
108
+ throw new Error(
109
+ `Workspace '${cwd}' is already locked by goal '${busyOwner}'. Concurrent goals on the same single-workspace cwd are serialized to prevent edit clobbering. Stop the other goal first, or use a separate workspace.`,
110
+ );
105
111
  }
106
- const verification = params.config?.verification as { commands: string[]; allowManualEvidence?: boolean; mode?: string } | undefined;
112
+ const verification = params.config?.verification as
113
+ | {
114
+ commands: string[];
115
+ allowManualEvidence?: boolean;
116
+ mode?: string;
117
+ }
118
+ | undefined;
107
119
  const isTextOnly = verification?.mode === "text-only";
108
120
  // P1a (RFC v0.5 §P1a): take manifest-integrity snapshot at start IF verification.commands
109
121
  // declared. For text-only mode (no objective oracle), mark "none-text-only" explicitly so
@@ -180,22 +192,46 @@ async function handleStart(input: GoalSubActionInput): Promise<ReturnType<typeof
180
192
  runKind: "goal-loop",
181
193
  };
182
194
  saveRunManifest(goalLoopManifest);
183
- appendEvent(paths.eventsPath, { type: "goal.loop_start", runId: goalId, data: { goalId, objective, maxTurns, statePath: `${cwd}/.crew/state/goals/${goalId}.json` } });
195
+ appendEvent(paths.eventsPath, {
196
+ type: "goal.loop_start",
197
+ runId: goalId,
198
+ data: {
199
+ goalId,
200
+ objective,
201
+ maxTurns,
202
+ statePath: `${cwd}/.crew/state/goals/${goalId}.json`,
203
+ },
204
+ });
184
205
 
185
206
  try {
186
207
  const spawned = await spawnBackgroundTeamRun(goalLoopManifest);
187
208
  const pid = spawned.pid ?? 0;
188
- const withAsync = { ...goalState, async: { pid, logPath: spawned.logPath, spawnedAt: new Date().toISOString() } };
209
+ const withAsync = {
210
+ ...goalState,
211
+ async: {
212
+ pid,
213
+ logPath: spawned.logPath,
214
+ spawnedAt: new Date().toISOString(),
215
+ },
216
+ };
189
217
  store.save(withAsync);
190
218
  return result(
191
219
  `Goal loop started (background pid=${pid}).\n${formatGoalStatus(withAsync)}\n\nNext: \`team action='goal' config.subAction='status' config.goalId='${goalId}'\`. The loop runs up to ${maxTurns} turns, judging each against the objective. Log: ${spawned.logPath}`,
192
- { action: "goal", status: "ok", data: { goalId, state: goalState.state, maxTurns, pid } },
220
+ {
221
+ action: "goal",
222
+ status: "ok",
223
+ data: { goalId, state: goalState.state, maxTurns, pid },
224
+ },
193
225
  false,
194
226
  );
195
227
  } catch (spawnError) {
196
228
  const message = spawnError instanceof Error ? spawnError.message : String(spawnError);
197
229
  store.setStatus(goalId, "blocked", paths.eventsPath);
198
- return result(`goal start: state saved but background spawn failed: ${message}`, { action: "goal", status: "error", data: { goalId } }, true);
230
+ return result(
231
+ `goal start: state saved but background spawn failed: ${message}`,
232
+ { action: "goal", status: "error", data: { goalId } },
233
+ true,
234
+ );
199
235
  }
200
236
  } catch (error) {
201
237
  const message = error instanceof Error ? error.message : String(error);
@@ -210,11 +246,28 @@ function handleStatus(input: GoalSubActionInput): ReturnType<typeof result> {
210
246
  if (goalId) {
211
247
  const goal = store.load(goalId);
212
248
  if (!goal) return result(`Goal '${goalId}' not found.`, { action: "goal", status: "error" }, true);
213
- return result(formatGoalStatus(goal), { action: "goal", status: "ok", data: { goalId, state: goal.state, turnsUsed: goal.turnsUsed } }, false);
249
+ return result(
250
+ formatGoalStatus(goal),
251
+ {
252
+ action: "goal",
253
+ status: "ok",
254
+ data: { goalId, state: goal.state, turnsUsed: goal.turnsUsed },
255
+ },
256
+ false,
257
+ );
214
258
  }
215
259
  const goals = store.list();
216
- if (goals.length === 0) return result("No goals found. Start one with `team action='goal' config.subAction='start' config.objective='...' config.evaluatorModel='...'`.", { action: "goal", status: "ok" }, false);
217
- return result(`Goals (${goals.length}):\n\n${goals.map(formatGoalStatus).join("\n\n")}`, { action: "goal", status: "ok", data: { count: goals.length } }, false);
260
+ if (goals.length === 0)
261
+ return result(
262
+ "No goals found. Start one with `team action='goal' config.subAction='start' config.objective='...' config.evaluatorModel='...'`.",
263
+ { action: "goal", status: "ok" },
264
+ false,
265
+ );
266
+ return result(
267
+ `Goals (${goals.length}):\n\n${goals.map(formatGoalStatus).join("\n\n")}`,
268
+ { action: "goal", status: "ok", data: { count: goals.length } },
269
+ false,
270
+ );
218
271
  }
219
272
 
220
273
  /** Cooperative pause/resume/stop/clear — flip GoalLoopState.state. */
@@ -227,7 +280,15 @@ function assertGoalOwnership(goal: GoalLoopState, ctx: TeamContext, action: stri
227
280
  // an in-flight turn potentially). Previously only 'running' was gated — a paused goal
228
281
  // with an active currentRunId could be stop/cancelled by a foreign session without force.
229
282
  if (owner && current && owner !== current && (goal.state === "running" || goal.state === "paused")) {
230
- return result(`Goal '${goal.goalId}' belongs to session '${owner}' (you are '${current}') and is still running. Use force:true to override.`, { action, status: "error", data: { goalId: goal.goalId, ownerSessionId: owner } }, true);
283
+ return result(
284
+ `Goal '${goal.goalId}' belongs to session '${owner}' (you are '${current}') and is still running. Use force:true to override.`,
285
+ {
286
+ action,
287
+ status: "error",
288
+ data: { goalId: goal.goalId, ownerSessionId: owner },
289
+ },
290
+ true,
291
+ );
231
292
  }
232
293
  return undefined;
233
294
  }
@@ -245,7 +306,15 @@ function handleStateFlip(input: GoalSubActionInput, nextState: GoalLoopStatus, l
245
306
  const eventsPath = createRunPaths(ctx.cwd, goalId).eventsPath;
246
307
  const updated = store.setStatus(goalId, nextState, eventsPath);
247
308
  if (!updated) return result(`Goal '${goalId}' not found.`, { action: "goal", status: "error" }, true);
248
- return result(`Goal ${goalId} ${label} (state='${updated.state}').`, { action: "goal", status: "ok", data: { goalId, state: updated.state } }, false);
309
+ return result(
310
+ `Goal ${goalId} ${label} (state='${updated.state}').`,
311
+ {
312
+ action: "goal",
313
+ status: "ok",
314
+ data: { goalId, state: updated.state },
315
+ },
316
+ false,
317
+ );
249
318
  }
250
319
 
251
320
  /**
@@ -269,15 +338,35 @@ async function handleStop(input: GoalSubActionInput): Promise<ReturnType<typeof
269
338
  let cancelMsg = "";
270
339
  if (updated.currentRunId) {
271
340
  try {
272
- // LAZY: defer dynamic import of ./cancel.ts to its call site.
341
+ // LAZY: defer dynamic import of ./cancel.ts to its call site.
273
342
  const { handleCancel } = await import("./cancel.ts");
274
- const cancelResult = await handleCancel({ action: "cancel", runId: updated.currentRunId, force: true, config: { intent: "user requested goal stop" } }, ctx);
343
+ const cancelResult = await handleCancel(
344
+ {
345
+ action: "cancel",
346
+ runId: updated.currentRunId,
347
+ force: true,
348
+ config: { intent: "user requested goal stop" },
349
+ },
350
+ ctx,
351
+ );
275
352
  cancelMsg = ` In-flight turn ${updated.currentRunId} cancel: ${(cancelResult.content[0] as { text?: string } | undefined)?.text ?? "ok"}.`;
276
353
  } catch (error) {
277
354
  cancelMsg = ` (in-flight turn ${updated.currentRunId} cancel failed: ${error instanceof Error ? error.message : String(error)}; the loop will still exit at the next turn boundary.)`;
278
355
  }
279
356
  }
280
- return result(`Goal ${goalId} stopped (state='cancelled').${cancelMsg}`, { action: "goal", status: "ok", data: { goalId, state: "cancelled", cancelledRunId: updated.currentRunId } }, false);
357
+ return result(
358
+ `Goal ${goalId} stopped (state='cancelled').${cancelMsg}`,
359
+ {
360
+ action: "goal",
361
+ status: "ok",
362
+ data: {
363
+ goalId,
364
+ state: "cancelled",
365
+ cancelledRunId: updated.currentRunId,
366
+ },
367
+ },
368
+ false,
369
+ );
281
370
  }
282
371
 
283
372
  /**
@@ -307,21 +396,37 @@ async function handleResume(input: GoalSubActionInput): Promise<ReturnType<typeo
307
396
  // Only paused/stuck goals are resumable. (running = already running; terminal states
308
397
  // achieved/max_turns/blocked/cancelled/budget_exceeded are done.)
309
398
  if (existing.state !== "paused" && existing.state !== "stuck") {
310
- return result(`Goal '${goalId}' is in state '${existing.state}' — only 'paused' or 'stuck' goals can be resumed.`, { action: "goal", status: "error", data: { goalId, state: existing.state } }, true);
399
+ return result(
400
+ `Goal '${goalId}' is in state '${existing.state}' — only 'paused' or 'stuck' goals can be resumed.`,
401
+ {
402
+ action: "goal",
403
+ status: "error",
404
+ data: { goalId, state: existing.state },
405
+ },
406
+ true,
407
+ );
311
408
  }
312
409
  const hint = typeof params.config?.hint === "string" ? params.config.hint.trim() : undefined;
313
410
  const eventsPath = createRunPaths(ctx.cwd, goalId).eventsPath;
314
411
  // CAS: only resume if the state is still what we loaded. A concurrent stop/cancel wins.
315
412
  const updated = store.compareAndSetStatus(goalId, existing.state, "running", eventsPath);
316
413
  if (!updated) {
317
- return result(`Goal '${goalId}' state changed concurrently (resume aborted; another actor won the race).`, { action: "goal", status: "error", data: { goalId } }, true);
414
+ return result(
415
+ `Goal '${goalId}' state changed concurrently (resume aborted; another actor won the race).`,
416
+ { action: "goal", status: "error", data: { goalId } },
417
+ true,
418
+ );
318
419
  }
319
420
  // Inject the hint as next-turn feedback (applies to turn N+1's worker prompt).
320
421
  let withHint = updated;
321
422
  if (hint) {
322
423
  withHint = store.patch(goalId, { nextTurnFeedback: hint }, eventsPath) ?? updated;
323
424
  }
324
- appendEvent(eventsPath, { type: "goal.resumed", runId: goalId, data: { goalId, fromState: existing.state, hint: hint?.slice(0, 200) } });
425
+ appendEvent(eventsPath, {
426
+ type: "goal.resumed",
427
+ runId: goalId,
428
+ data: { goalId, fromState: existing.state, hint: hint?.slice(0, 200) },
429
+ });
325
430
  // Re-spawn the background loop. The loop checks goal.state === "running" before each
326
431
  // turn; since we just set it to running, it proceeds.
327
432
  try {
@@ -346,15 +451,40 @@ async function handleResume(input: GoalSubActionInput): Promise<ReturnType<typeo
346
451
  runKind: "goal-loop",
347
452
  };
348
453
  const spawned = await spawnBackgroundTeamRun(manifest);
349
- return result(`Goal ${goalId} resumed from '${existing.state}' (background pid=${spawned.pid ?? 0}).${hint ? ` Hint injected for next turn.` : ""}`, { action: "goal", status: "ok", data: { goalId, state: "running", fromState: existing.state, pid: spawned.pid } }, false);
454
+ return result(
455
+ `Goal ${goalId} resumed from '${existing.state}' (background pid=${spawned.pid ?? 0}).${hint ? ` Hint injected for next turn.` : ""}`,
456
+ {
457
+ action: "goal",
458
+ status: "ok",
459
+ data: {
460
+ goalId,
461
+ state: "running",
462
+ fromState: existing.state,
463
+ pid: spawned.pid,
464
+ },
465
+ },
466
+ false,
467
+ );
350
468
  } catch (spawnError) {
351
469
  const msg = spawnError instanceof Error ? spawnError.message : String(spawnError);
352
470
  // Cold-review #2 nit: roll back to the PRIOR state (paused/stuck) so the user can retry
353
471
  // 'goal resume' (which requires paused/stuck). Leaving it at 'running' with no process made
354
472
  // the goal un-resumable — the user had to pause-then-resume as a workaround.
355
473
  store.compareAndSetStatus(goalId, "running", existing.state, eventsPath);
356
- appendEvent(eventsPath, { type: "goal.resume_spawn_failed", runId: goalId, data: { goalId, error: msg, rolledBackTo: existing.state } });
357
- return result(`Goal ${goalId} background re-spawn failed: ${msg}. State rolled back to '${existing.state}'. Retry 'goal resume' to re-attempt.`, { action: "goal", status: "error", data: { goalId, spawnFailed: true, state: existing.state } }, true);
474
+ appendEvent(eventsPath, {
475
+ type: "goal.resume_spawn_failed",
476
+ runId: goalId,
477
+ data: { goalId, error: msg, rolledBackTo: existing.state },
478
+ });
479
+ return result(
480
+ `Goal ${goalId} background re-spawn failed: ${msg}. State rolled back to '${existing.state}'. Retry 'goal resume' to re-attempt.`,
481
+ {
482
+ action: "goal",
483
+ status: "error",
484
+ data: { goalId, spawnFailed: true, state: existing.state },
485
+ },
486
+ true,
487
+ );
358
488
  }
359
489
  }
360
490
 
@@ -388,17 +518,37 @@ export async function handleGoal(params: TeamToolParamsValue, ctx: TeamContext):
388
518
  if (denied) return denied;
389
519
  }
390
520
  if (existing.state === "running" || existing.state === "paused") {
391
- return result(`Goal '${clearGoalId}' is still ${existing.state}. Stop it first (team action='goal' subAction='stop' goalId='${clearGoalId}'), then clear.`, { action: "goal", status: "error", data: { goalId: clearGoalId, state: existing.state } }, true);
521
+ return result(
522
+ `Goal '${clearGoalId}' is still ${existing.state}. Stop it first (team action='goal' subAction='stop' goalId='${clearGoalId}'), then clear.`,
523
+ {
524
+ action: "goal",
525
+ status: "error",
526
+ data: { goalId: clearGoalId, state: existing.state },
527
+ },
528
+ true,
529
+ );
392
530
  }
393
531
  const removed = store.remove(clearGoalId);
394
532
  if (!removed) return result(`Goal '${clearGoalId}' could not be removed.`, { action: "goal", status: "error" }, true);
395
- return result(`Goal '${clearGoalId}' cleared (file removed).`, { action: "goal", status: "ok", data: { goalId: clearGoalId, cleared: true } }, false);
533
+ return result(
534
+ `Goal '${clearGoalId}' cleared (file removed).`,
535
+ {
536
+ action: "goal",
537
+ status: "ok",
538
+ data: { goalId: clearGoalId, cleared: true },
539
+ },
540
+ false,
541
+ );
396
542
  }
397
543
  case "step":
398
544
  // P0: step is a status-only stub — single-turn execution lands with P1.
399
545
  return handleStatus(input);
400
546
  default:
401
- return result(`Unknown goal subAction '${subAction}'. Known: start, status, pause, resume, stop, step, clear.`, { action: "goal", status: "error" }, true);
547
+ return result(
548
+ `Unknown goal subAction '${subAction}'. Known: start, status, pause, resume, stop, step, clear.`,
549
+ { action: "goal", status: "error" },
550
+ true,
551
+ );
402
552
  }
403
553
  }
404
554
 
@@ -1,13 +1,16 @@
1
1
  import * as crypto from "node:crypto";
2
- import type { PiTeamsToolResult } from "../tool-result.ts";
3
- import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
4
- import { result, type TeamContext } from "./context.ts";
5
2
  import { humanizeSchedule, nextRunTime, parseSchedule } from "../../runtime/scheduler.ts";
6
3
  import { loadCrewSettings, saveCrewSettings } from "../../runtime/settings-store.ts";
4
+ import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
5
+ import type { PiTeamsToolResult } from "../tool-result.ts";
6
+ import { result, type TeamContext } from "./context.ts";
7
7
 
8
8
  // Global key for cross-module scheduler access.
9
9
  const CREW_SCHEDULER_KEY = Symbol.for("pi-crew:scheduler");
10
- type SchedulerRef = { add(job: import("../../runtime/scheduler.ts").ScheduledJob): void; list(): import("../../runtime/scheduler.ts").ScheduledJob[] };
10
+ type SchedulerRef = {
11
+ add(job: import("../../runtime/scheduler.ts").ScheduledJob): void;
12
+ list(): import("../../runtime/scheduler.ts").ScheduledJob[];
13
+ };
11
14
 
12
15
  function getCrewScheduler(): SchedulerRef | undefined {
13
16
  return (globalThis as Record<symbol | string, unknown>)[CREW_SCHEDULER_KEY] as SchedulerRef | undefined;
@@ -36,13 +39,22 @@ function buildScheduleSpec(params: ScheduleParams): {
36
39
  if (params.cron) {
37
40
  const parsed = parseSchedule(params.cron);
38
41
  if ("error" in parsed) throw new Error(parsed.error);
39
- return { spec: parsed, schedule: params.cron, scheduleType: "cron" as const };
42
+ return {
43
+ spec: parsed,
44
+ schedule: params.cron,
45
+ scheduleType: "cron" as const,
46
+ };
40
47
  }
41
48
  if (params.interval !== undefined && params.interval > 0) {
42
49
  const specStr = `${params.interval}ms`;
43
50
  const spec = parseSchedule(specStr);
44
51
  if ("error" in spec) throw new Error(spec.error);
45
- return { spec, schedule: specStr, scheduleType: "interval" as const, intervalMs: params.interval };
52
+ return {
53
+ spec,
54
+ schedule: specStr,
55
+ scheduleType: "interval" as const,
56
+ intervalMs: params.interval,
57
+ };
46
58
  }
47
59
  if (params.once !== undefined) {
48
60
  const ts = typeof params.once === "number" ? new Date(params.once).toISOString() : params.once;
@@ -155,7 +167,10 @@ function persistScheduledJob(cwd: string, job: import("../../runtime/scheduler.t
155
167
  ? ((settings as Record<string, unknown>).scheduledJobs as import("../../runtime/scheduler.ts").ScheduledJob[])
156
168
  : [];
157
169
  saveCrewSettings(
158
- { ...settings, scheduledJobs: [...existingJobs, job] } as Parameters<typeof saveCrewSettings>[0],
170
+ {
171
+ ...settings,
172
+ scheduledJobs: [...existingJobs, job],
173
+ } as Parameters<typeof saveCrewSettings>[0],
159
174
  cwd,
160
175
  );
161
176
  } catch {
@@ -172,11 +187,8 @@ export function persistScheduledJobUpdate(cwd: string, job: import("../../runtim
172
187
  )
173
188
  ? ((settings as Record<string, unknown>).scheduledJobs as import("../../runtime/scheduler.ts").ScheduledJob[])
174
189
  : [];
175
- const updated = existingJobs.map((j) => j.id === job.id ? job : j);
176
- saveCrewSettings(
177
- { ...settings, scheduledJobs: updated } as Parameters<typeof saveCrewSettings>[0],
178
- cwd,
179
- );
190
+ const updated = existingJobs.map((j) => (j.id === job.id ? job : j));
191
+ saveCrewSettings({ ...settings, scheduledJobs: updated } as Parameters<typeof saveCrewSettings>[0], cwd);
180
192
  } catch {
181
193
  /* best-effort persistence */
182
194
  }
@@ -192,7 +204,10 @@ function persistScheduledJobRemove(cwd: string, jobId: string): void {
192
204
  ? ((settings as Record<string, unknown>).scheduledJobs as import("../../runtime/scheduler.ts").ScheduledJob[])
193
205
  : [];
194
206
  saveCrewSettings(
195
- { ...settings, scheduledJobs: existingJobs.filter((j) => j.id !== jobId) } as Parameters<typeof saveCrewSettings>[0],
207
+ {
208
+ ...settings,
209
+ scheduledJobs: existingJobs.filter((j) => j.id !== jobId),
210
+ } as Parameters<typeof saveCrewSettings>[0],
196
211
  cwd,
197
212
  );
198
213
  } catch {
@@ -204,7 +219,11 @@ export function handleListScheduled(_params: TeamToolParamsValue, ctx: TeamConte
204
219
  const scheduler = getCrewScheduler();
205
220
  if (!scheduler) return result("Scheduler not running.", { action: "scheduled", status: "error" }, true);
206
221
  const jobs = scheduler.list();
207
- if (jobs.length === 0) return result("No scheduled jobs.", { action: "scheduled", status: "ok" });
222
+ if (jobs.length === 0)
223
+ return result("No scheduled jobs.", {
224
+ action: "scheduled",
225
+ status: "ok",
226
+ });
208
227
  const lines: string[] = [`Scheduled jobs (${jobs.length}):`];
209
228
  for (const job of jobs) {
210
229
  lines.push(
@@ -219,4 +238,4 @@ export function handleListScheduled(_params: TeamToolParamsValue, ctx: TeamConte
219
238
  }
220
239
  }
221
240
  return result(lines.join("\n"), { action: "scheduled", status: "ok" });
222
- }
241
+ }