pi-crew 0.9.55 → 0.9.57

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 (309) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +58 -0
  3. package/README.md +53 -5
  4. package/dist/index.mjs +5666 -4370
  5. package/docs/README.md +54 -0
  6. package/package.json +12 -13
  7. package/skills/real-test-pi-crew/SKILL.md +93 -3
  8. package/src/agents/discover-agents.ts +68 -10
  9. package/src/benchmark/feedback-loop.ts +1 -1
  10. package/src/config/config.ts +2 -1
  11. package/src/config/types.ts +12 -0
  12. package/src/extension/action-suggestions.ts +16 -63
  13. package/src/extension/async-notifier.ts +3 -3
  14. package/src/extension/command-completions.ts +2 -2
  15. package/src/extension/crew-cleanup.ts +3 -3
  16. package/src/extension/crew-vibes/footer.ts +4 -2
  17. package/src/extension/cross-extension-rpc.ts +10 -5
  18. package/src/extension/knowledge-injection.ts +57 -10
  19. package/src/extension/register.ts +2 -2
  20. package/src/extension/registration/artifact-cleanup.ts +41 -3
  21. package/src/extension/registration/commands.ts +4 -4
  22. package/src/extension/registration/context-builder.ts +10 -10
  23. package/src/extension/registration/crash-recovery-cache.ts +3 -3
  24. package/src/extension/registration/foreground-run-controller.ts +1 -1
  25. package/src/extension/registration/lazy-configurers.ts +2 -2
  26. package/src/extension/registration/lifecycle-handlers.ts +12 -12
  27. package/src/extension/registration/lifecycle.ts +2 -2
  28. package/src/extension/registration/observability.ts +2 -2
  29. package/src/extension/registration/registration-types.ts +3 -3
  30. package/src/extension/registration/runtime-cleanup.ts +1 -1
  31. package/src/extension/registration/subagent-helpers.ts +2 -2
  32. package/src/extension/registration/subagent-manager-setup.ts +2 -2
  33. package/src/extension/registration/subagent-tools.ts +21 -16
  34. package/src/extension/registration/team-tool.ts +109 -17
  35. package/src/extension/registration/viewers.ts +3 -4
  36. package/src/extension/registration/wire-cross-extension.ts +7 -7
  37. package/src/extension/rpc-hmac.ts +3 -2
  38. package/src/extension/run-bundle-schema.ts +13 -4
  39. package/src/extension/run-export.ts +2 -2
  40. package/src/extension/run-index.ts +2 -3
  41. package/src/extension/run-maintenance.ts +57 -1
  42. package/src/extension/team-manager-command.ts +3 -1
  43. package/src/extension/team-tool/anchor.ts +8 -2
  44. package/src/extension/team-tool/api.ts +117 -39
  45. package/src/extension/team-tool/cancel.ts +23 -8
  46. package/src/extension/team-tool/chain-executor.ts +1 -1
  47. package/src/extension/team-tool/dispatch/automate.ts +17 -2
  48. package/src/extension/team-tool/dispatch/control.ts +17 -2
  49. package/src/extension/team-tool/dispatch/manage.ts +34 -2
  50. package/src/extension/team-tool/dispatch/run.ts +17 -2
  51. package/src/extension/team-tool/dispatch/status.ts +37 -5
  52. package/src/extension/team-tool/doctor.ts +2 -2
  53. package/src/extension/team-tool/explain.ts +1 -1
  54. package/src/extension/team-tool/goal-wrap.ts +5 -5
  55. package/src/extension/team-tool/goal.ts +30 -9
  56. package/src/extension/team-tool/handle-schedule.ts +37 -19
  57. package/src/extension/team-tool/inspect.ts +21 -5
  58. package/src/extension/team-tool/lifecycle-actions.ts +53 -10
  59. package/src/extension/team-tool/parallel-dispatch.ts +14 -5
  60. package/src/extension/team-tool/param-error.ts +138 -0
  61. package/src/extension/team-tool/respond.ts +20 -6
  62. package/src/extension/team-tool/run.ts +27 -17
  63. package/src/extension/team-tool/status.ts +17 -6
  64. package/src/extension/team-tool.ts +26 -19
  65. package/src/hooks/registry.ts +1 -1
  66. package/src/i18n.ts +22 -0
  67. package/src/observability/event-bus.ts +1 -1
  68. package/src/prompt/prompt-runtime.ts +2 -2
  69. package/src/runtime/README.md +35 -0
  70. package/src/runtime/agent-control.ts +1 -1
  71. package/src/runtime/anchor-manager.ts +0 -2
  72. package/src/runtime/async-runner.ts +1 -1
  73. package/src/runtime/attention-events.ts +3 -2
  74. package/src/runtime/auto-summarize.ts +0 -2
  75. package/src/runtime/background-runner.ts +103 -30
  76. package/src/runtime/{crew-broker-child.ts → broker/crew-broker-child.ts} +2 -2
  77. package/src/runtime/{crew-broker-client.ts → broker/crew-broker-client.ts} +4 -7
  78. package/src/runtime/{crew-broker.ts → broker/crew-broker.ts} +13 -21
  79. package/src/runtime/chain-runner.ts +50 -5
  80. package/src/runtime/{child-pi-constants.ts → child-pi/child-pi-constants.ts} +1 -1
  81. package/src/runtime/{child-pi-kill.ts → child-pi/child-pi-kill.ts} +3 -3
  82. package/src/runtime/{child-pi-spawn.ts → child-pi/child-pi-spawn.ts} +28 -12
  83. package/src/runtime/{child-pi-steering.ts → child-pi/child-pi-steering.ts} +1 -1
  84. package/src/runtime/{child-pi-streams.ts → child-pi/child-pi-streams.ts} +23 -4
  85. package/src/runtime/{child-pi-transcript.ts → child-pi/child-pi-transcript.ts} +6 -6
  86. package/src/runtime/{child-pi.ts → child-pi/child-pi.ts} +47 -78
  87. package/src/runtime/compaction/compact-stages/bounded-tail.ts +84 -0
  88. package/src/runtime/{compact-stages → compaction/compact-stages}/index.ts +2 -2
  89. package/src/runtime/{compaction-summary.ts → compaction/compaction-summary.ts} +3 -3
  90. package/src/runtime/{tool-output-pruner.ts → compaction/tool-output-pruner.ts} +1 -1
  91. package/src/runtime/crew-agent-records.ts +48 -2
  92. package/src/runtime/custom-tools/irc-tool.ts +2 -2
  93. package/src/runtime/delivery-coordinator.ts +0 -3
  94. package/src/runtime/diagnostic-export.ts +5 -4
  95. package/src/runtime/foreground-control.ts +1 -1
  96. package/src/runtime/foreground-watchdog.ts +1 -1
  97. package/src/runtime/{adaptive-plan.ts → goal-workflow/adaptive-plan.ts} +8 -8
  98. package/src/runtime/{dynamic-workflow-context.ts → goal-workflow/dynamic-workflow-context.ts} +16 -16
  99. package/src/runtime/{dynamic-workflow-runner.ts → goal-workflow/dynamic-workflow-runner.ts} +10 -10
  100. package/src/runtime/{goal-achievement.ts → goal-workflow/goal-achievement.ts} +3 -3
  101. package/src/runtime/{goal-evaluator.ts → goal-workflow/goal-evaluator.ts} +8 -8
  102. package/src/runtime/{goal-loop-runner.ts → goal-workflow/goal-loop-runner.ts} +27 -25
  103. package/src/runtime/{goal-state-store.ts → goal-workflow/goal-state-store.ts} +8 -8
  104. package/src/runtime/{plan-templates.ts → goal-workflow/plan-templates.ts} +1 -1
  105. package/src/runtime/group-join.ts +3 -3
  106. package/src/runtime/handoff-manager.ts +0 -2
  107. package/src/runtime/{heartbeat-watcher.ts → heartbeat/heartbeat-watcher.ts} +7 -7
  108. package/src/runtime/iteration-hooks.ts +1 -1
  109. package/src/runtime/{live-agent-control.ts → live-session/live-agent-control.ts} +2 -2
  110. package/src/runtime/{live-agent-manager.ts → live-session/live-agent-manager.ts} +4 -4
  111. package/src/runtime/{live-session-runtime.ts → live-session/live-session-runtime.ts} +39 -41
  112. package/src/runtime/manifest-cache.ts +6 -2
  113. package/src/runtime/{model-fallback.ts → model/model-fallback.ts} +1 -1
  114. package/src/runtime/{pi-args.ts → model/pi-args.ts} +20 -5
  115. package/src/runtime/model/provider-extensions.ts +116 -0
  116. package/src/runtime/{runtime-policy.ts → model/runtime-policy.ts} +2 -2
  117. package/src/runtime/{runtime-resolver.ts → model/runtime-resolver.ts} +3 -3
  118. package/src/runtime/{runtime-warmup.ts → model/runtime-warmup.ts} +8 -8
  119. package/src/runtime/orphan-worker-registry.ts +1 -1
  120. package/src/runtime/{progress-tracker.ts → output/progress-tracker.ts} +1 -1
  121. package/src/runtime/{result-extractor.ts → output/result-extractor.ts} +4 -4
  122. package/src/runtime/{sidechain-output.ts → output/sidechain-output.ts} +2 -2
  123. package/src/runtime/{stream-preview.ts → output/stream-preview.ts} +1 -1
  124. package/src/runtime/{streaming-output.ts → output/streaming-output.ts} +2 -2
  125. package/src/runtime/{tool-progress.ts → output/tool-progress.ts} +1 -1
  126. package/src/runtime/parent-guard.ts +30 -10
  127. package/src/runtime/pipeline-runner.ts +2 -2
  128. package/src/runtime/policy-engine.ts +2 -2
  129. package/src/runtime/{cancellation.ts → process/cancellation.ts} +1 -1
  130. package/src/runtime/{post-exit-stdio-guard.ts → process/post-exit-stdio-guard.ts} +1 -1
  131. package/src/runtime/{process-lifecycle.ts → process/process-lifecycle.ts} +3 -3
  132. package/src/runtime/{checkpoint.ts → recovery/checkpoint.ts} +4 -4
  133. package/src/runtime/{crash-recovery.ts → recovery/crash-recovery.ts} +93 -21
  134. package/src/runtime/{overflow-recovery.ts → recovery/overflow-recovery.ts} +1 -1
  135. package/src/runtime/{recovery-recipes.ts → recovery/recovery-recipes.ts} +1 -1
  136. package/src/runtime/{retry-executor.ts → recovery/retry-executor.ts} +2 -2
  137. package/src/runtime/{retry-runner.ts → recovery/retry-runner.ts} +2 -8
  138. package/src/runtime/run-tracker.ts +1 -1
  139. package/src/runtime/run-worker.ts +3 -3
  140. package/src/runtime/{coalesce-tasks.ts → scheduling/coalesce-tasks.ts} +3 -3
  141. package/src/runtime/{concurrency.ts → scheduling/concurrency.ts} +9 -3
  142. package/src/runtime/{parallel-research.ts → scheduling/parallel-research.ts} +4 -2
  143. package/src/runtime/{run-coalesced-task-group.ts → scheduling/run-coalesced-task-group.ts} +95 -36
  144. package/src/runtime/{semaphore.ts → scheduling/semaphore.ts} +4 -1
  145. package/src/runtime/{task-graph-scheduler.ts → scheduling/task-graph-scheduler.ts} +1 -1
  146. package/src/runtime/session-resources.ts +1 -1
  147. package/src/runtime/settings-store.ts +1 -1
  148. package/src/runtime/single-agent-compose.ts +1 -0
  149. package/src/runtime/stale-reconciler.ts +1 -1
  150. package/src/runtime/subagent-manager.ts +1 -1
  151. package/src/runtime/supervisor-contact.ts +28 -13
  152. package/src/runtime/task-output-context.ts +7 -5
  153. package/src/runtime/task-runner/child-executor.ts +67 -31
  154. package/src/runtime/task-runner/live-executor.ts +8 -8
  155. package/src/runtime/task-runner/post-execution.ts +12 -12
  156. package/src/runtime/task-runner/pre-execution.ts +6 -6
  157. package/src/runtime/task-runner/progress.ts +1 -1
  158. package/src/runtime/task-runner/run-projection.ts +1 -1
  159. package/src/runtime/task-runner/scaffold-executor.ts +1 -1
  160. package/src/runtime/task-runner/state-helpers.ts +2 -2
  161. package/src/runtime/task-runner.ts +4 -4
  162. package/src/runtime/team-runner.ts +441 -175
  163. package/src/runtime/{completion-guard.ts → verification/completion-guard.ts} +1 -1
  164. package/src/runtime/{green-contract.ts → verification/green-contract.ts} +1 -1
  165. package/src/runtime/{post-checks.ts → verification/post-checks.ts} +4 -4
  166. package/src/runtime/{verification-gates.ts → verification/verification-gates.ts} +5 -5
  167. package/src/runtime/{verification-integrity.ts → verification/verification-integrity.ts} +1 -1
  168. package/src/schema/config-schema.ts +1 -0
  169. package/src/schema/team-tool-schema.ts +149 -185
  170. package/src/state/README.md +44 -0
  171. package/src/state/atomic-write.ts +24 -3
  172. package/src/state/{locks.ts → coordination/locks.ts} +118 -38
  173. package/src/state/{mailbox.ts → coordination/mailbox.ts} +14 -8
  174. package/src/state/{schedule.ts → coordination/schedule.ts} +3 -3
  175. package/src/state/{task-claims.ts → coordination/task-claims.ts} +1 -1
  176. package/src/state/decision-ledger.ts +1 -1
  177. package/src/state/{event-log-rotation.ts → event-log/event-log-rotation.ts} +189 -31
  178. package/src/state/{event-log.ts → event-log/event-log.ts} +77 -39
  179. package/src/state/{jsonl-writer.ts → event-log/jsonl-writer.ts} +14 -6
  180. package/src/state/hook-instinct-bridge.ts +2 -2
  181. package/src/state/{active-run-registry.ts → stores/active-run-registry.ts} +7 -7
  182. package/src/state/{artifact-store.ts → stores/artifact-store.ts} +62 -4
  183. package/src/state/{blob-store.ts → stores/blob-store.ts} +3 -3
  184. package/src/state/{health-store.ts → stores/health-store.ts} +3 -3
  185. package/src/state/{instinct-store.ts → stores/instinct-store.ts} +3 -3
  186. package/src/state/{observation-store.ts → stores/observation-store.ts} +2 -2
  187. package/src/state/{run-cache.ts → stores/run-cache.ts} +4 -4
  188. package/src/state/{run-graph.ts → stores/run-graph.ts} +4 -4
  189. package/src/state/{run-metrics.ts → stores/run-metrics.ts} +3 -3
  190. package/src/state/{state-store.ts → stores/state-store.ts} +254 -21
  191. package/src/state/types.ts +27 -3
  192. package/src/ui/component.ts +55 -0
  193. package/src/ui/crew-select-list.ts +3 -1
  194. package/src/ui/dashboard-panes/agents-pane.ts +1 -1
  195. package/src/ui/dwf-phase-display.ts +1 -1
  196. package/src/ui/heartbeat-aggregator.ts +1 -1
  197. package/src/ui/keybinding-map.ts +179 -6
  198. package/src/ui/layout-primitives.ts +9 -5
  199. package/src/ui/live-conversation-overlay.ts +1 -1
  200. package/src/ui/live-run-sidebar.ts +1 -1
  201. package/src/ui/overlay-stack.ts +148 -0
  202. package/src/ui/overlays/agent-picker-overlay.ts +1 -1
  203. package/src/ui/overlays/mailbox-compose-overlay.ts +1 -1
  204. package/src/ui/overlays/mailbox-detail-overlay.ts +2 -2
  205. package/src/ui/powerbar-publisher.ts +1 -1
  206. package/src/ui/render-scheduler.ts +1 -1
  207. package/src/ui/run-action-dispatcher.ts +3 -3
  208. package/src/ui/run-dashboard.ts +5 -33
  209. package/src/ui/run-event-bus.ts +3 -3
  210. package/src/ui/run-snapshot-cache.ts +37 -18
  211. package/src/ui/settings-overlay.ts +13 -21
  212. package/src/ui/snapshot-types.ts +1 -1
  213. package/src/ui/status-colors.ts +0 -2
  214. package/src/ui/terminal-status.ts +1 -1
  215. package/src/ui/theme-adapter.ts +33 -13
  216. package/src/ui/tool-renderers/index.ts +8 -0
  217. package/src/ui/transcript-viewer.ts +37 -13
  218. package/src/ui/widget/index.ts +32 -9
  219. package/src/ui/widget/widget-formatters.ts +62 -4
  220. package/src/ui/widget/widget-model.ts +2 -2
  221. package/src/ui/widget/widget-renderer.ts +1 -1
  222. package/src/utils/conflict-detect.ts +58 -5
  223. package/src/utils/ndjson.ts +2 -2
  224. package/src/utils/redaction.ts +39 -0
  225. package/src/utils/socket-path.ts +1 -1
  226. package/src/workflows/workflow-config.ts +1 -1
  227. package/src/worktree/cleanup.ts +1 -1
  228. package/src/worktree/worktree-manager.ts +111 -47
  229. package/workflows/chain.workflow.md +2 -2
  230. package/dist/build-meta.json +0 -16807
  231. package/index.bundle.ts +0 -25
  232. package/scripts/bench-check.mjs +0 -96
  233. package/scripts/bench-cold-start.mjs +0 -216
  234. package/scripts/build-bundle.mjs +0 -92
  235. package/scripts/check-all-skills.ts +0 -294
  236. package/scripts/check-bundle-size.mjs +0 -38
  237. package/scripts/check-bundle-staleness.mjs +0 -97
  238. package/scripts/check-conflict-markers.mjs +0 -91
  239. package/scripts/check-lazy-imports.mjs +0 -28
  240. package/scripts/dev-runner.mjs +0 -106
  241. package/scripts/profile-startup.mjs +0 -125
  242. package/scripts/release-smoke.mjs +0 -74
  243. package/scripts/run-bench.mjs +0 -47
  244. package/scripts/run-real-chain.ts +0 -41
  245. package/scripts/test-issue-29-crash.ts +0 -111
  246. package/scripts/test-issue-29-e2e.ts +0 -183
  247. package/scripts/test-issue-29-real-runtime.ts +0 -330
  248. package/scripts/test-issue-29-real-tasks.ts +0 -387
  249. package/scripts/test-issue-29-team-tool.ts +0 -105
  250. package/scripts/test-runner.mjs +0 -84
  251. package/scripts/verify-flicker-fix.ts +0 -109
  252. package/scripts/verify-skill.ts +0 -550
  253. package/scripts/watch-bundle.mjs +0 -259
  254. package/scripts/watchdog-harness.ts +0 -119
  255. package/src/adapters/claude-adapter.ts +0 -23
  256. package/src/adapters/codex-adapter.ts +0 -21
  257. package/src/adapters/cursor-adapter.ts +0 -17
  258. package/src/adapters/export-util.ts +0 -143
  259. package/src/adapters/index.ts +0 -15
  260. package/src/adapters/registry.ts +0 -18
  261. package/src/adapters/types.ts +0 -23
  262. package/src/runtime/code-summary.ts +0 -292
  263. package/src/runtime/cross-extension-rpc.ts +0 -143
  264. package/src/runtime/hidden-handoff.ts +0 -407
  265. package/src/runtime/prose-compressor.ts +0 -162
  266. package/src/subagents/async-entry.ts +0 -1
  267. package/src/subagents/index.ts +0 -3
  268. package/src/subagents/live/control.ts +0 -1
  269. package/src/subagents/live/manager.ts +0 -1
  270. package/src/subagents/live/realtime.ts +0 -1
  271. package/src/subagents/live/session-runtime.ts +0 -1
  272. package/src/subagents/manager.ts +0 -1
  273. package/src/subagents/spawn.ts +0 -1
  274. package/test-integration-check.ts +0 -118
  275. /package/src/runtime/{broker-issuer.ts → broker/broker-issuer.ts} +0 -0
  276. /package/src/runtime/{crew-broker-tokens.ts → broker/crew-broker-tokens.ts} +0 -0
  277. /package/src/runtime/{child-pi-pool.ts → child-pi/child-pi-pool.ts} +0 -0
  278. /package/src/runtime/{compact-pipeline.ts → compaction/compact-pipeline.ts} +0 -0
  279. /package/src/runtime/{compact-stages → compaction/compact-stages}/ansi-strip-stage.ts +0 -0
  280. /package/src/runtime/{compact-stages → compaction/compact-stages}/blank-collapse-stage.ts +0 -0
  281. /package/src/runtime/{compact-stages → compaction/compact-stages}/deduplicate-stage.ts +0 -0
  282. /package/src/runtime/{compact-stages → compaction/compact-stages}/head-snap-stage.ts +0 -0
  283. /package/src/runtime/{compact-stages → compaction/compact-stages}/tail-capture-stage.ts +0 -0
  284. /package/src/runtime/{compact-stages → compaction/compact-stages}/truncation-stage.ts +0 -0
  285. /package/src/runtime/{important-line-classifier.ts → compaction/important-line-classifier.ts} +0 -0
  286. /package/src/runtime/{heartbeat-gradient.ts → heartbeat/heartbeat-gradient.ts} +0 -0
  287. /package/src/runtime/{worker-heartbeat.ts → heartbeat/worker-heartbeat.ts} +0 -0
  288. /package/src/runtime/{worker-startup.ts → heartbeat/worker-startup.ts} +0 -0
  289. /package/src/runtime/{intercom-bridge.ts → live-session/intercom-bridge.ts} +0 -0
  290. /package/src/runtime/{live-control-realtime.ts → live-session/live-control-realtime.ts} +0 -0
  291. /package/src/runtime/{live-extension-bridge.ts → live-session/live-extension-bridge.ts} +0 -0
  292. /package/src/runtime/{live-irc.ts → live-session/live-irc.ts} +0 -0
  293. /package/src/runtime/{live-session-health.ts → live-session/live-session-health.ts} +0 -0
  294. /package/src/runtime/{model-resolver.ts → model/model-resolver.ts} +0 -0
  295. /package/src/runtime/{model-scope.ts → model/model-scope.ts} +0 -0
  296. /package/src/runtime/{output-validator.ts → output/output-validator.ts} +0 -0
  297. /package/src/runtime/{pi-json-output.ts → output/pi-json-output.ts} +0 -0
  298. /package/src/runtime/{progress-event-coalescer.ts → output/progress-event-coalescer.ts} +0 -0
  299. /package/src/runtime/{cancellation-token.ts → process/cancellation-token.ts} +0 -0
  300. /package/src/runtime/{zombie-scanner.ts → process/zombie-scanner.ts} +0 -0
  301. /package/src/runtime/{crash-classification.ts → recovery/crash-classification.ts} +0 -0
  302. /package/src/runtime/{batch-barrier.ts → scheduling/batch-barrier.ts} +0 -0
  303. /package/src/runtime/{global-worker-cap.ts → scheduling/global-worker-cap.ts} +0 -0
  304. /package/src/runtime/{parallel-utils.ts → scheduling/parallel-utils.ts} +0 -0
  305. /package/src/runtime/{scheduler.ts → scheduling/scheduler.ts} +0 -0
  306. /package/src/runtime/{task-graph.ts → scheduling/task-graph.ts} +0 -0
  307. /package/src/runtime/{verification-worktree.ts → verification/verification-worktree.ts} +0 -0
  308. /package/src/state/{event-reconstructor.ts → event-log/event-reconstructor.ts} +0 -0
  309. /package/src/state/{worker-atomic-writer.ts → event-log/worker-atomic-writer.ts} +0 -0
@@ -19,14 +19,14 @@
19
19
 
20
20
  import { loadConfig } from "../../config/config.ts";
21
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";
22
+ import { spawnBackgroundTeamRun } from "../../runtime/async-runner.ts";
23
+ import { GoalStore } from "../../runtime/goal-workflow/goal-state-store.ts";
24
+ import { snapshotManifests } from "../../runtime/verification/verification-integrity.ts";
24
25
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
25
26
  import { atomicWriteJson } from "../../state/atomic-write.ts";
26
- import { appendEvent } from "../../state/event-log.ts";
27
- import { createRunPaths, saveRunManifestAsync } from "../../state/state-store.ts";
27
+ import { appendEvent } from "../../state/event-log/event-log.ts";
28
+ import { createRunPaths, saveRunManifestAsync } from "../../state/stores/state-store.ts";
28
29
  import type { GoalLoopState, TeamRunManifest } from "../../state/types.ts";
29
- import { spawnBackgroundTeamRun } from "../../subagents/async-entry.ts";
30
30
  import { logInternalError } from "../../utils/internal-error.ts";
31
31
  import type { WorkflowConfig } from "../../workflows/workflow-config.ts";
32
32
  import { result, type TeamContext } from "./context.ts";
@@ -14,16 +14,17 @@
14
14
  * §0c C11 (control path: cooperative flag + handleCancel(currentRunId) via abortOwned).
15
15
  */
16
16
 
17
- import { GoalStore } from "../../runtime/goal-state-store.ts";
18
- import { snapshotManifests } from "../../runtime/verification-integrity.ts";
17
+ import { spawnBackgroundTeamRun } from "../../runtime/async-runner.ts";
18
+ import { GoalStore } from "../../runtime/goal-workflow/goal-state-store.ts";
19
+ import { snapshotManifests } from "../../runtime/verification/verification-integrity.ts";
19
20
  import { isWorkspaceBusy } from "../../runtime/workspace-lock.ts";
20
21
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
21
- import { appendEvent } from "../../state/event-log.ts";
22
- import { createRunPaths, saveRunManifestAsync } from "../../state/state-store.ts";
22
+ import { appendEvent } from "../../state/event-log/event-log.ts";
23
+ import { createRunPaths, saveRunManifestAsync } from "../../state/stores/state-store.ts";
23
24
  import type { GoalLoopState, GoalLoopStatus, TeamRunManifest } from "../../state/types.ts";
24
- import { spawnBackgroundTeamRun } from "../../subagents/async-entry.ts";
25
25
  import { logInternalError } from "../../utils/internal-error.ts";
26
26
  import { result, type TeamContext } from "./context.ts";
27
+ import { paramRequired } from "./param-error.ts";
27
28
 
28
29
  const MAX_GOAL_OBJECTIVE_CHARS = 4000;
29
30
 
@@ -296,7 +297,12 @@ function assertGoalOwnership(goal: GoalLoopState, ctx: TeamContext, action: stri
296
297
  function handleStateFlip(input: GoalSubActionInput, nextState: GoalLoopStatus, label: string): ReturnType<typeof result> {
297
298
  const { params, ctx, store } = input;
298
299
  const goalId = params.config?.goalId as string | undefined;
299
- if (!goalId) return result(`${label} requires config.goalId.`, { action: "goal", status: "error" }, true);
300
+ if (!goalId)
301
+ return result(
302
+ paramRequired(label, "config.goalId", "{ action: 'goal', config: { goalId: '...' } }"),
303
+ { action: "goal", status: "error" },
304
+ true,
305
+ );
300
306
  const existing = store.load(goalId);
301
307
  if (!existing) return result(`Goal '${goalId}' not found.`, { action: "goal", status: "error" }, true);
302
308
  if (params.force !== true) {
@@ -325,7 +331,12 @@ function handleStateFlip(input: GoalSubActionInput, nextState: GoalLoopStatus, l
325
331
  async function handleStop(input: GoalSubActionInput): Promise<ReturnType<typeof result>> {
326
332
  const { params, ctx, store } = input;
327
333
  const goalId = params.config?.goalId as string | undefined;
328
- if (!goalId) return result("stop requires config.goalId.", { action: "goal", status: "error" }, true);
334
+ if (!goalId)
335
+ return result(
336
+ paramRequired("stop", "config.goalId", "{ action: 'goal', config: { subAction: 'stop', goalId: '...' } }"),
337
+ { action: "goal", status: "error" },
338
+ true,
339
+ );
329
340
  const eventsPath = createRunPaths(ctx.cwd, goalId).eventsPath;
330
341
  const before = store.load(goalId);
331
342
  if (!before) return result(`Goal '${goalId}' not found.`, { action: "goal", status: "error" }, true);
@@ -386,7 +397,12 @@ async function handleStop(input: GoalSubActionInput): Promise<ReturnType<typeof
386
397
  async function handleResume(input: GoalSubActionInput): Promise<ReturnType<typeof result>> {
387
398
  const { params, ctx, store } = input;
388
399
  const goalId = params.config?.goalId as string | undefined;
389
- if (!goalId) return result("resume requires config.goalId.", { action: "goal", status: "error" }, true);
400
+ if (!goalId)
401
+ return result(
402
+ paramRequired("resume", "config.goalId", "{ action: 'goal', config: { subAction: 'resume', goalId: '...' } }"),
403
+ { action: "goal", status: "error" },
404
+ true,
405
+ );
390
406
  const existing = store.load(goalId);
391
407
  if (!existing) return result(`Goal '${goalId}' not found.`, { action: "goal", status: "error" }, true);
392
408
  if (params.force !== true) {
@@ -510,7 +526,12 @@ export async function handleGoal(params: TeamToolParamsValue, ctx: TeamContext):
510
526
  // Fix P1-3 + round-5 P2: remove the goal file. But refuse if the loop is still
511
527
  // running (would leave a zombie background process). Require stop first.
512
528
  const clearGoalId = params.config?.goalId as string | undefined;
513
- if (!clearGoalId) return result("clear requires config.goalId.", { action: "goal", status: "error" }, true);
529
+ if (!clearGoalId)
530
+ return result(
531
+ paramRequired("clear", "config.goalId", "{ action: 'goal', config: { subAction: 'clear', goalId: '...' } }"),
532
+ { action: "goal", status: "error" },
533
+ true,
534
+ );
514
535
  const existing = store.load(clearGoalId);
515
536
  if (!existing) return result(`Goal '${clearGoalId}' not found (already cleared?).`, { action: "goal", status: "error" }, true);
516
537
  if (params.force !== true) {
@@ -1,28 +1,39 @@
1
1
  import * as crypto from "node:crypto";
2
- import { humanizeSchedule, nextRunTime, parseSchedule } from "../../runtime/scheduler.ts";
2
+ import { humanizeSchedule, nextRunTime, parseSchedule } from "../../runtime/scheduling/scheduler.ts";
3
3
  import { type CrewSettings, updateCrewSettings } from "../../runtime/settings-store.ts";
4
4
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
5
5
  import type { PiTeamsToolResult } from "../tool-result.ts";
6
6
  import { result, type TeamContext } from "./context.ts";
7
+ import { paramRequired } from "./param-error.ts";
7
8
 
8
- // Global key for cross-module scheduler access.
9
- const CREW_SCHEDULER_KEY = Symbol.for("pi-crew:scheduler");
9
+ // Module-scoped scheduler reference — one per extension load (EXT-9).
10
+ // Previously this lived on `globalThis[Symbol.for("pi-crew:scheduler")]`, which
11
+ // was fragile (cross-realm, no lifecycle, peer extensions could overwrite it).
10
12
  type SchedulerRef = {
11
- add(job: import("../../runtime/scheduler.ts").ScheduledJob): void;
12
- list(): import("../../runtime/scheduler.ts").ScheduledJob[];
13
+ add(job: import("../../runtime/scheduling/scheduler.ts").ScheduledJob): void;
14
+ list(): import("../../runtime/scheduling/scheduler.ts").ScheduledJob[];
13
15
  remove(id: string): boolean;
14
16
  update(
15
17
  id: string,
16
- patch: Partial<import("../../runtime/scheduler.ts").ScheduledJob>,
17
- ): import("../../runtime/scheduler.ts").ScheduledJob | undefined;
18
+ patch: Partial<import("../../runtime/scheduling/scheduler.ts").ScheduledJob>,
19
+ ): import("../../runtime/scheduling/scheduler.ts").ScheduledJob | undefined;
18
20
  };
19
21
 
20
- function getCrewScheduler(): SchedulerRef | undefined {
21
- return (globalThis as Record<symbol | string, unknown>)[CREW_SCHEDULER_KEY] as SchedulerRef | undefined;
22
+ // Module-scoped scheduler instance — one per extension load (EXT-9).
23
+ let crewSchedulerInstance: SchedulerRef | undefined;
24
+
25
+ /** @internal — exported for lifecycle tests. */
26
+ export function getCrewScheduler(): SchedulerRef | undefined {
27
+ return crewSchedulerInstance;
22
28
  }
23
29
 
24
30
  export function registerCrewScheduler(scheduler: SchedulerRef): void {
25
- (globalThis as Record<symbol | string, unknown>)[CREW_SCHEDULER_KEY] = scheduler;
31
+ crewSchedulerInstance = scheduler;
32
+ }
33
+
34
+ /** Remove the scheduler singleton. Call during session cleanup. */
35
+ export function unregisterCrewScheduler(): void {
36
+ crewSchedulerInstance = undefined;
26
37
  }
27
38
 
28
39
  interface ScheduleParams {
@@ -35,9 +46,9 @@ interface ScheduleParams {
35
46
  }
36
47
 
37
48
  function buildScheduleSpec(params: ScheduleParams): {
38
- spec: import("../../runtime/scheduler.ts").ScheduleSpec;
49
+ spec: import("../../runtime/scheduling/scheduler.ts").ScheduleSpec;
39
50
  schedule: string;
40
- scheduleType: import("../../runtime/scheduler.ts").ScheduleType;
51
+ scheduleType: import("../../runtime/scheduling/scheduler.ts").ScheduleType;
41
52
  intervalMs?: number;
42
53
  } {
43
54
  // Priority: cron > interval > once
@@ -100,7 +111,12 @@ export function handleSchedule(params: TeamToolParamsValue, ctx: TeamContext): P
100
111
 
101
112
  const team = params.team ?? "default";
102
113
  const goal = params.goal ?? params.task ?? "";
103
- if (!goal) return result("Schedule requires goal or task.", { action: "schedule", status: "error" }, true);
114
+ if (!goal)
115
+ return result(
116
+ paramRequired("schedule", "goal or task", "{ action: 'schedule', goal: '...', cron: '0 9 * * *' }"),
117
+ { action: "schedule", status: "error" },
118
+ true,
119
+ );
104
120
 
105
121
  let specResult: ReturnType<typeof buildScheduleSpec>;
106
122
  try {
@@ -121,7 +137,7 @@ export function handleSchedule(params: TeamToolParamsValue, ctx: TeamContext): P
121
137
  if ("error" in next) return result(next.error, { action: "schedule", status: "error" }, true);
122
138
 
123
139
  // Build the ScheduledJob
124
- const job: import("../../runtime/scheduler.ts").ScheduledJob = {
140
+ const job: import("../../runtime/scheduling/scheduler.ts").ScheduledJob = {
125
141
  id: crypto.randomUUID(),
126
142
  name: `${team}: ${goal.slice(0, 60)}`,
127
143
  description: `Scheduled run for team '${team}'`,
@@ -190,11 +206,13 @@ export function handleSchedule(params: TeamToolParamsValue, ctx: TeamContext): P
190
206
  );
191
207
  }
192
208
 
193
- function scheduledJobsOf(settings: CrewSettings): import("../../runtime/scheduler.ts").ScheduledJob[] {
194
- return Array.isArray(settings.scheduledJobs) ? (settings.scheduledJobs as import("../../runtime/scheduler.ts").ScheduledJob[]) : [];
209
+ function scheduledJobsOf(settings: CrewSettings): import("../../runtime/scheduling/scheduler.ts").ScheduledJob[] {
210
+ return Array.isArray(settings.scheduledJobs)
211
+ ? (settings.scheduledJobs as import("../../runtime/scheduling/scheduler.ts").ScheduledJob[])
212
+ : [];
195
213
  }
196
214
 
197
- function persistScheduledJob(cwd: string, job: import("../../runtime/scheduler.ts").ScheduledJob): void {
215
+ function persistScheduledJob(cwd: string, job: import("../../runtime/scheduling/scheduler.ts").ScheduledJob): void {
198
216
  try {
199
217
  updateCrewSettings(cwd, (settings) => ({
200
218
  ...settings,
@@ -206,7 +224,7 @@ function persistScheduledJob(cwd: string, job: import("../../runtime/scheduler.t
206
224
  }
207
225
 
208
226
  /** Update an existing scheduled job in persistent settings. */
209
- export function persistScheduledJobUpdate(cwd: string, job: import("../../runtime/scheduler.ts").ScheduledJob): void {
227
+ export function persistScheduledJobUpdate(cwd: string, job: import("../../runtime/scheduling/scheduler.ts").ScheduledJob): void {
210
228
  try {
211
229
  updateCrewSettings(cwd, (settings) => ({
212
230
  ...settings,
@@ -309,7 +327,7 @@ export function handleUpdateScheduled(params: TeamToolParamsValue, ctx: TeamCont
309
327
  return result("Scheduler not running.", { action: "schedule", status: "error" }, true);
310
328
  }
311
329
 
312
- const patch: Partial<import("../../runtime/scheduler.ts").ScheduledJob> = {};
330
+ const patch: Partial<import("../../runtime/scheduling/scheduler.ts").ScheduledJob> = {};
313
331
  if (subAction === "disable") patch.enabled = false;
314
332
  if (subAction === "enable") patch.enabled = true;
315
333
  // For generic "update", allow patch of cron / interval / goal.
@@ -1,15 +1,21 @@
1
1
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
2
- import { readEvents } from "../../state/event-log.ts";
3
- import { loadRunManifestById } from "../../state/state-store.ts";
2
+ import { readEvents } from "../../state/event-log/event-log.ts";
3
+ import { loadRunManifestById } from "../../state/stores/state-store.ts";
4
4
  import { aggregateUsage, formatCostReport, formatUsage } from "../../state/usage.ts";
5
5
  import { locateRunCwd } from "../team-tool.ts";
6
6
  import type { PiTeamsToolResult } from "../tool-result.ts";
7
7
  import { result, type TeamContext } from "./context.ts";
8
8
  import { formatFailurePatterns } from "./failure-patterns.ts";
9
+ import { paramRequired } from "./param-error.ts";
9
10
  import { RUN_NOT_FOUND_HINT } from "./run-not-found.ts";
10
11
 
11
12
  export function handleEvents(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
12
- if (!params.runId) return result("Events requires runId.", { action: "events", status: "error" }, true);
13
+ if (!params.runId)
14
+ return result(
15
+ paramRequired("events", "runId", "{ action: 'events', runId: 'team_...' }"),
16
+ { action: "events", status: "error" },
17
+ true,
18
+ );
13
19
  const runCwd = locateRunCwd(params.runId, ctx.cwd);
14
20
  if (!runCwd) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "events", status: "error" }, true);
15
21
  const loaded = loadRunManifestById(runCwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
@@ -33,7 +39,12 @@ export function handleEvents(params: TeamToolParamsValue, ctx: TeamContext): PiT
33
39
  }
34
40
 
35
41
  export function handleArtifacts(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
36
- if (!params.runId) return result("Artifacts requires runId.", { action: "artifacts", status: "error" }, true);
42
+ if (!params.runId)
43
+ return result(
44
+ paramRequired("artifacts", "runId", "{ action: 'artifacts', runId: 'team_...' }"),
45
+ { action: "artifacts", status: "error" },
46
+ true,
47
+ );
37
48
  const runCwd = locateRunCwd(params.runId, ctx.cwd);
38
49
  if (!runCwd) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "artifacts", status: "error" }, true);
39
50
  const loaded = loadRunManifestById(runCwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
@@ -56,7 +67,12 @@ export function handleArtifacts(params: TeamToolParamsValue, ctx: TeamContext):
56
67
  }
57
68
 
58
69
  export function handleSummary(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
59
- if (!params.runId) return result("Summary requires runId.", { action: "summary", status: "error" }, true);
70
+ if (!params.runId)
71
+ return result(
72
+ paramRequired("summary", "runId", "{ action: 'summary', runId: 'team_...' }"),
73
+ { action: "summary", status: "error" },
74
+ true,
75
+ );
60
76
  const runCwd = locateRunCwd(params.runId, ctx.cwd);
61
77
  if (!runCwd) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "summary", status: "error" }, true);
62
78
  const loaded = loadRunManifestById(runCwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
@@ -2,11 +2,11 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { removeGuidance } from "../../config/markers.ts";
4
4
  import { appendHookEvent, executeHook } from "../../hooks/registry.ts";
5
- import { killProcessPid } from "../../runtime/child-pi.ts";
6
- import { terminateLiveAgentsForRun } from "../../runtime/live-agent-manager.ts";
5
+ import { killProcessPid } from "../../runtime/child-pi/child-pi.ts";
6
+ import { terminateLiveAgentsForRun } from "../../runtime/live-session/live-agent-manager.ts";
7
7
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
8
- import { appendEvent } from "../../state/event-log.ts";
9
- import { loadRunManifestById } from "../../state/state-store.ts";
8
+ import { appendEvent } from "../../state/event-log/event-log.ts";
9
+ import { loadRunManifestById } from "../../state/stores/state-store.ts";
10
10
  import { logInternalError } from "../../utils/internal-error.ts";
11
11
  import { projectCrewRoot, userCrewRoot, userPiRoot } from "../../utils/paths.ts";
12
12
  import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
@@ -18,10 +18,16 @@ import { pruneFinishedRuns } from "../run-maintenance.ts";
18
18
  import type { PiTeamsToolResult } from "../tool-result.ts";
19
19
  import { configRecord, result, type TeamContext } from "./context.ts";
20
20
  import { enforceDestructiveIntent, intentFromConfig } from "./intent-policy.ts";
21
+ import { paramRequired } from "./param-error.ts";
21
22
  import { RUN_NOT_FOUND_HINT } from "./run-not-found.ts";
22
23
 
23
24
  export function handleWorktrees(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
24
- if (!params.runId) return result("Worktrees requires runId.", { action: "worktrees", status: "error" }, true);
25
+ if (!params.runId)
26
+ return result(
27
+ paramRequired("worktrees", "runId", "{ action: 'worktrees', runId: 'team_...' }"),
28
+ { action: "worktrees", status: "error" },
29
+ true,
30
+ );
25
31
  const loaded = loadRunManifestById(ctx.cwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
26
32
  if (!loaded) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "worktrees", status: "error" }, true);
27
33
  const withWorktrees = loaded.tasks.filter((task) => task.worktree);
@@ -59,7 +65,16 @@ export function handleImports(_params: TeamToolParamsValue, ctx: TeamContext): P
59
65
  export function handleImport(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
60
66
  const cfg = configRecord(params.config);
61
67
  const bundlePath = typeof cfg.path === "string" ? cfg.path : typeof cfg.bundlePath === "string" ? cfg.bundlePath : undefined;
62
- if (!bundlePath) return result("Import requires config.path pointing at run-export.json.", { action: "import", status: "error" }, true);
68
+ if (!bundlePath)
69
+ return result(
70
+ paramRequired(
71
+ "import",
72
+ "config.path pointing at run-export.json",
73
+ "{ action: 'import', config: { path: '/path/to/run-export.json' } }",
74
+ ),
75
+ { action: "import", status: "error" },
76
+ true,
77
+ );
63
78
  const scope = cfg.scope === "user" ? "user" : "project";
64
79
  try {
65
80
  const imported = importRunBundle(ctx.cwd, bundlePath, scope);
@@ -74,7 +89,12 @@ export function handleImport(params: TeamToolParamsValue, ctx: TeamContext): PiT
74
89
  }
75
90
 
76
91
  export async function handleExport(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
77
- if (!params.runId) return result("Export requires runId.", { action: "export", status: "error" }, true);
92
+ if (!params.runId)
93
+ return result(
94
+ paramRequired("export", "runId", "{ action: 'export', runId: 'team_...' }"),
95
+ { action: "export", status: "error" },
96
+ true,
97
+ );
78
98
  const loaded = loadRunManifestById(ctx.cwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
79
99
  if (!loaded) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "export", status: "error" }, true);
80
100
 
@@ -126,7 +146,12 @@ export async function handleExport(params: TeamToolParamsValue, ctx: TeamContext
126
146
  export async function handlePrune(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
127
147
  const intentError = enforceDestructiveIntent("prune", params, ctx.config);
128
148
  if (intentError) return intentError;
129
- if (!params.confirm) return result("prune requires confirm: true.", { action: "prune", status: "error" }, true);
149
+ if (!params.confirm)
150
+ return result(
151
+ paramRequired("prune", "confirm: true", "{ action: 'prune', confirm: true }"),
152
+ { action: "prune", status: "error" },
153
+ true,
154
+ );
130
155
  const keep = params.keep ?? 20;
131
156
  if (keep < 0 || !Number.isInteger(keep)) return result("keep must be an integer >= 0.", { action: "prune", status: "error" }, true);
132
157
  const intent = intentFromConfig(params.config);
@@ -164,8 +189,18 @@ export async function handlePrune(params: TeamToolParamsValue, ctx: TeamContext)
164
189
  export async function handleForget(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
165
190
  const intentError = enforceDestructiveIntent("forget", params, ctx.config);
166
191
  if (intentError) return intentError;
167
- if (!params.runId) return result("Forget requires runId.", { action: "forget", status: "error" }, true);
168
- if (!params.confirm) return result("forget requires confirm: true.", { action: "forget", status: "error" }, true);
192
+ if (!params.runId)
193
+ return result(
194
+ paramRequired("forget", "runId", "{ action: 'forget', runId: 'team_...', confirm: true }"),
195
+ { action: "forget", status: "error" },
196
+ true,
197
+ );
198
+ if (!params.confirm)
199
+ return result(
200
+ paramRequired("forget", "confirm: true", "{ action: 'forget', runId: 'team_...', confirm: true }"),
201
+ { action: "forget", status: "error" },
202
+ true,
203
+ );
169
204
  const loaded = loadRunManifestById(ctx.cwd, params.runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency
170
205
  if (!loaded) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "forget", status: "error" }, true);
171
206
 
@@ -269,6 +304,14 @@ export async function handleCleanup(params: TeamToolParamsValue, ctx: TeamContex
269
304
  // can stay focused on their own logic.
270
305
  const intentError = enforceDestructiveIntent("cleanup", params, ctx.config);
271
306
  if (intentError) return intentError;
307
+ // SEC-5: self-enforce confirm:true (defense-in-depth), matching handlePrune/handleForget.
308
+ // dryRun:true is a non-destructive preview and is always allowed.
309
+ if (params.confirm !== true && params.dryRun !== true)
310
+ return result(
311
+ paramRequired("cleanup", "confirm: true", "{ action: 'cleanup', confirm: true }"),
312
+ { action: "cleanup", status: "error" },
313
+ true,
314
+ );
272
315
  // Three cleanup modes:
273
316
  // 1. WITH runId → per-run worktree cleanup (existing behavior).
274
317
  // 2. WITHOUT runId, scope=project (default) → PROJECT-LEVEL uninstall:
@@ -8,11 +8,11 @@
8
8
  */
9
9
  import { discoverAgents } from "../../agents/discover-agents.ts";
10
10
  import { loadConfig } from "../../config/config.ts";
11
- import { resolveCrewRuntime } from "../../runtime/runtime-resolver.ts";
11
+ import { spawnBackgroundTeamRun } from "../../runtime/async-runner.ts";
12
+ import { resolveCrewRuntime } from "../../runtime/model/runtime-resolver.ts";
12
13
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
13
- import { appendEvent } from "../../state/event-log.ts";
14
- import { createRunManifest } from "../../state/state-store.ts";
15
- import { spawnBackgroundTeamRun } from "../../subagents/async-entry.ts";
14
+ import { appendEvent } from "../../state/event-log/event-log.ts";
15
+ import { createRunManifest } from "../../state/stores/state-store.ts";
16
16
  import { discoverTeams } from "../../teams/discover-teams.ts";
17
17
  import type { TeamConfig } from "../../teams/team-config.ts";
18
18
  import { discoverWorkflows } from "../../workflows/discover-workflows.ts";
@@ -20,6 +20,7 @@ import type { WorkflowConfig } from "../../workflows/workflow-config.ts";
20
20
  import { resolveCwdOverride } from "../registration/team-tool.ts";
21
21
  import type { PiTeamsToolResult } from "../tool-result.ts";
22
22
  import { result, type TeamContext } from "./context.ts";
23
+ import { paramRequired } from "./param-error.ts";
23
24
 
24
25
  const MAX_CONCURRENCY = 8;
25
26
  const DEFAULT_CONCURRENCY = 4;
@@ -29,7 +30,15 @@ const DEFAULT_AGENT = "explorer";
29
30
  export async function handleParallel(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
30
31
  const tasksParam = params.config?.tasks;
31
32
  if (!Array.isArray(tasksParam) || tasksParam.length === 0) {
32
- return result("parallel action requires config.tasks: [{goal, agent?}]", { action: "parallel", status: "error" }, true);
33
+ return result(
34
+ paramRequired(
35
+ "parallel",
36
+ "config.tasks: [{goal, agent?}]",
37
+ "{ action: 'parallel', config: { tasks: [{ goal: '...', agent: 'explorer' }] } }",
38
+ ),
39
+ { action: "parallel", status: "error" },
40
+ true,
41
+ );
33
42
  }
34
43
 
35
44
  const concurrency = Math.min(Math.max(1, Math.floor((params.config?.concurrency as number) ?? DEFAULT_CONCURRENCY)), MAX_CONCURRENCY);
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Friendly team-tool parameter validation error formatting.
3
+ *
4
+ * The team tool validates every call with `Value.Check(TeamToolParams, ...)`.
5
+ * On failure it used to return the opaque `"Invalid team tool parameters"` —
6
+ * which gave calling agents (LLMs) no clue WHAT was wrong, so they retried the
7
+ * same malformed shape indefinitely (observed in the wild: agents passing
8
+ * `goal` as an array/object, or `action` with wrong casing, then looping).
9
+ *
10
+ * `formatTeamToolParamError` produces an actionable message:
11
+ * 1. Targeted heuristics for the COMMON mistakes (action casing; goal/team/
12
+ * runId passed as a non-string) — pinpoint the exact field, because the
13
+ * TypeBox Union reports only a root-level "Expected union value".
14
+ * 2. TypeBox `Value.Errors` output (catches anything the heuristics miss).
15
+ * 3. Concrete correct-call examples for the common actions.
16
+ * Agents can self-correct from this instead of looping.
17
+ */
18
+
19
+ import type { TSchema } from "@sinclair/typebox";
20
+ import { Value } from "@sinclair/typebox/value";
21
+
22
+ /** Short human description of a received value (for error messages). */
23
+ function describeValue(v: unknown): string {
24
+ if (v === null) return "null";
25
+ if (Array.isArray(v)) return `array (length ${v.length})`;
26
+ if (typeof v === "object") return "object";
27
+ if (typeof v === "string") return `string "${v.length > 40 ? `${v.slice(0, 37)}...` : v}"`;
28
+ return `${typeof v} (${String(v).slice(0, 40)})`;
29
+ }
30
+
31
+ /** Pinpoint the common agent mistakes that TypeBox Union can't field-locate. */
32
+ function diagnoseCommonMistakes(params: unknown): string[] {
33
+ const hints: string[] = [];
34
+ if (typeof params !== "object" || params === null || Array.isArray(params)) {
35
+ return hints;
36
+ }
37
+ const p = params as Record<string, unknown>;
38
+
39
+ // action: casing / non-string
40
+ if ("action" in p) {
41
+ const a = p.action;
42
+ if (typeof a !== "string") {
43
+ hints.push(` • 'action': must be a lowercase string (e.g. "run", "status", "list") — got ${describeValue(a)}.`);
44
+ } else if (a !== a.toLowerCase()) {
45
+ hints.push(` • 'action': "${a}" has wrong casing — action is case-sensitive. Use "${a.toLowerCase()}".`);
46
+ }
47
+ }
48
+
49
+ // Flat-string fields that agents commonly pass as objects/arrays
50
+ const stringFields = ["goal", "team", "runId", "task", "role", "agent", "workflow", "model", "cwd", "chain"];
51
+ for (const f of stringFields) {
52
+ if (!(f in p)) continue;
53
+ const v = p[f];
54
+ if (typeof v === "string") continue;
55
+ if (Array.isArray(v)) {
56
+ hints.push(
57
+ ` • '${f}': must be a single flat string — got an array (length ${v.length}). Pass e.g. "${f}": "<value>", not a list.`,
58
+ );
59
+ } else if (typeof v === "object" && v !== null) {
60
+ hints.push(` • '${f}': must be a single flat string — got an object. Pass e.g. "${f}": "<value>".`);
61
+ } else {
62
+ hints.push(` • '${f}': must be a string — got ${describeValue(v)}.`);
63
+ }
64
+ }
65
+ return hints;
66
+ }
67
+
68
+ /**
69
+ * Build a helpful validation-failure message for a team-tool call.
70
+ *
71
+ * @param schema The TypeBox schema (TeamToolParams) that rejected the params.
72
+ * @param params The raw params the caller passed.
73
+ * @returns A multi-line message: what went wrong + correct-call examples.
74
+ */
75
+ export function formatTeamToolParamError(schema: TSchema, params: unknown): string {
76
+ const issues = [...diagnoseCommonMistakes(params)];
77
+
78
+ try {
79
+ const errors = [...Value.Errors(schema, params)];
80
+ const seen = new Set<string>();
81
+ for (const e of errors) {
82
+ if (issues.length >= 8) break;
83
+ const key = `${e.path}|${e.type}`;
84
+ if (seen.has(key)) continue;
85
+ seen.add(key);
86
+ const pathLabel = e.path ? `'${e.path}'` : "(schema)";
87
+ issues.push(` • ${pathLabel}: ${e.message} — received ${describeValue(e.value)}`);
88
+ }
89
+ } catch {
90
+ /* Value.Errors itself threw — the heuristic issues are still shown */
91
+ }
92
+
93
+ if (issues.length === 0) {
94
+ issues.push(" • params did not match any valid team-tool action shape");
95
+ }
96
+
97
+ return [
98
+ "Invalid team tool parameters — the call was rejected by schema validation.",
99
+ "",
100
+ "What went wrong:",
101
+ ...issues,
102
+ "",
103
+ "Correct call shapes — `action` is case-sensitive (lowercase); `goal`,",
104
+ "`team`, `runId`, `task`, `role` MUST be flat strings, never objects/arrays:",
105
+ ' • Run a team: { "action": "run", "goal": "<what to achieve>", "team": "implementation" }',
106
+ ' • Suggest a team: { "action": "recommend", "goal": "<what to achieve>" }',
107
+ ' • List recent runs: { "action": "list" }',
108
+ ' • Run status: { "action": "status", "runId": "<runId>" }',
109
+ ' • Direct one agent: { "action": "run", "role": "<agent>", "task": "<one task>" }',
110
+ "",
111
+ 'Common mistakes: `action` uppercase (use "run" not "Run"); `goal` passed',
112
+ "as an array/object instead of a single string; params nested instead of flat.",
113
+ ].join("\n");
114
+ }
115
+
116
+ /**
117
+ * Build a runtime handler error string for a MISSING REQUIRED FIELD, including
118
+ * a concrete example shape so the calling agent (LLM) can self-correct instead
119
+ * of looping on the same malformed call.
120
+ *
121
+ * The schema-validation path (`formatTeamToolParamError` above) already
122
+ * provides rich examples, but runtime handler errors used to return a plain
123
+ * `"X requires Y."` with NO example — giving LLMs worse guidance for the MORE
124
+ * COMMON failure (valid action, missing field). This helper closes that
125
+ * two-tier error-quality gap (AUDIT-2026-07-30 EXT-2).
126
+ *
127
+ * @param action The team-tool action (e.g. "status", "cancel").
128
+ * @param field The required field/fields (e.g. "runId", "goal or task").
129
+ * @param example Optional concrete example shape,
130
+ * e.g. `{ action: 'status', runId: 'team_...' }`.
131
+ * @returns An error string like
132
+ * `"Status requires runId. Example: { action: 'status', runId: 'team_...' }"`.
133
+ */
134
+ export function paramRequired(action: string, field: string, example?: string): string {
135
+ const subject = action.charAt(0).toUpperCase() + action.slice(1);
136
+ const base = `${subject} requires ${field}.`;
137
+ return example ? `${base} Example: ${example}` : base;
138
+ }
@@ -1,13 +1,14 @@
1
1
  import { readCrewAgents, recordFromTask, saveCrewAgents } from "../../runtime/crew-agent-records.ts";
2
2
  import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
3
- import { appendEvent } from "../../state/event-log.ts";
4
- import { withRunLockSync } from "../../state/locks.ts";
5
- import { appendMailboxMessage, updateMailboxMessageReply } from "../../state/mailbox.ts";
6
- import { loadRunManifestById, saveRunTasks, updateRunStatus } from "../../state/state-store.ts";
3
+ import { withRunLockSync } from "../../state/coordination/locks.ts";
4
+ import { appendMailboxMessage, updateMailboxMessageReply } from "../../state/coordination/mailbox.ts";
5
+ import { appendEvent } from "../../state/event-log/event-log.ts";
6
+ import { loadRunManifestById, saveRunTasks, updateRunStatus } from "../../state/stores/state-store.ts";
7
7
  import { logInternalError } from "../../utils/internal-error.ts";
8
8
  import { locateRunCwd } from "../team-tool.ts";
9
9
  import type { PiTeamsToolResult } from "../tool-result.ts";
10
10
  import { result, type TeamContext } from "./context.ts";
11
+ import { paramRequired } from "./param-error.ts";
11
12
  import { RUN_NOT_FOUND_HINT } from "./run-not-found.ts";
12
13
 
13
14
  /**
@@ -16,9 +17,22 @@ import { RUN_NOT_FOUND_HINT } from "./run-not-found.ts";
16
17
  * mailbox and the task is re-queued for durable scheduler resume.
17
18
  */
18
19
  export function handleRespond(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
19
- if (!params.runId) return result("Respond requires runId.", { action: "respond", status: "error" }, true);
20
+ if (!params.runId)
21
+ return result(
22
+ paramRequired("respond", "runId", "{ action: 'respond', runId: 'team_...', message: '...' }"),
23
+ { action: "respond", status: "error" },
24
+ true,
25
+ );
20
26
  if (!params.message && !params.taskId)
21
- return result("Respond requires taskId and/or message.", { action: "respond", status: "error" }, true);
27
+ return result(
28
+ paramRequired(
29
+ "respond",
30
+ "taskId and/or message",
31
+ "{ action: 'respond', runId: 'team_...', taskId: '01_agent', message: '...' }",
32
+ ),
33
+ { action: "respond", status: "error" },
34
+ true,
35
+ );
22
36
 
23
37
  const runCwd = locateRunCwd(params.runId, ctx.cwd);
24
38
  if (!runCwd) return result(`Run '${params.runId}' not found.${RUN_NOT_FOUND_HINT}`, { action: "respond", status: "error" }, true);