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
@@ -1,18 +1,20 @@
1
- import type { AgentConfig } from "../agents/agent-config.ts";
2
- import { writeArtifact } from "../state/artifact-store.ts";
3
- import { appendEventAsync } from "../state/event-log.ts";
4
- import { saveRunTasksAsync, updateRunStatus } from "../state/state-store.ts";
5
- import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
6
- import type { WorkflowStep } from "../workflows/workflow-config.ts";
7
- import { DEFAULT_RETRY_POLICY, executeWithRetry } from "./retry-executor.ts";
8
- import { permissionForRole } from "./role-permission.ts";
9
- import { runWorker } from "./run-worker.ts";
10
- import type { CrewRuntimeMode } from "./runtime-resolver.ts";
11
- import { sanitizeTaskText } from "./task-packet.ts";
12
- import { splitCoalescedOutput } from "./task-runner/output-splitter.ts";
13
- import { mergeArtifacts } from "./team-runner-artifacts.ts";
14
- import { createWorkerHeartbeat, touchWorkerHeartbeat } from "./worker-heartbeat.ts";
15
- import { buildWorkspaceTree } from "./workspace-tree.ts";
1
+ import type { AgentConfig } from "../../agents/agent-config.ts";
2
+ import type { CrewReliabilityConfig, CrewRuntimeConfig } from "../../config/types.ts";
3
+ import { appendEventAsync } from "../../state/event-log/event-log.ts";
4
+ import { writeArtifact } from "../../state/stores/artifact-store.ts";
5
+ import { saveRunTasksAsync, updateRunStatus } from "../../state/stores/state-store.ts";
6
+ import type { TeamRunManifest, TeamTaskState } from "../../state/types.ts";
7
+ import type { WorkflowStep } from "../../workflows/workflow-config.ts";
8
+ import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../heartbeat/worker-heartbeat.ts";
9
+ import type { CrewRuntimeMode } from "../model/runtime-resolver.ts";
10
+ import type { RetryPolicy } from "../recovery/retry-executor.ts";
11
+ import { DEFAULT_RETRY_POLICY, executeWithRetry } from "../recovery/retry-executor.ts";
12
+ import { permissionForRole } from "../role-permission.ts";
13
+ import { runWorker } from "../run-worker.ts";
14
+ import { sanitizeTaskText } from "../task-packet.ts";
15
+ import { splitCoalescedOutput } from "../task-runner/output-splitter.ts";
16
+ import { mergeArtifacts } from "../team-runner-artifacts.ts";
17
+ import { buildWorkspaceTree } from "../workspace-tree.ts";
16
18
 
17
19
  export interface CoalescedTaskGroupInput {
18
20
  manifest: TeamRunManifest;
@@ -27,6 +29,10 @@ export interface CoalescedTaskGroupInput {
27
29
  onJsonEvent?: (taskId: string, runId: string, event: unknown) => void;
28
30
  teamRole?: unknown;
29
31
  perTaskRuntime?: CrewRuntimeMode;
32
+ /** RT-5: runtime config for maxTurns, graceTurns, taskTimeoutMs (mirror singleton). */
33
+ runtimeConfig?: CrewRuntimeConfig;
34
+ /** RT-5: reliability config for autoRetry + retryPolicy (mirror singleton). */
35
+ reliability?: CrewReliabilityConfig;
30
36
  }
31
37
 
32
38
  export interface CoalescedTaskGroupResult {
@@ -80,6 +86,9 @@ export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Pro
80
86
 
81
87
  let rawOutput = "";
82
88
  let success = false;
89
+ // RT-5 #1: track cancellation separately from failure so the status
90
+ // mapping can branch cancel → "cancelled" (not "failed").
91
+ let cancelled = false;
83
92
  // FIND-06: serialize heartbeat saves and retain the active save so terminal
84
93
  // results can drain it before their final write.
85
94
  let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
@@ -110,7 +119,7 @@ export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Pro
110
119
  if (!taskIds.includes(t.id)) return t;
111
120
  // FIND-06 belt-and-suspenders: never replace terminal state or its
112
121
  // resultArtifact with a heartbeat-only snapshot.
113
- if (t.status === "completed" || t.status === "failed") return t;
122
+ if (t.status === "completed" || t.status === "failed" || t.status === "cancelled") return t;
114
123
  return {
115
124
  ...t,
116
125
  heartbeat: touchWorkerHeartbeat(t.heartbeat ?? createWorkerHeartbeat(t.id), { alive: true }),
@@ -135,26 +144,76 @@ export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Pro
135
144
  }
136
145
  })();
137
146
  }, 15_000);
147
+ // RT-5 #3/#4/#5: compute maxTurns, wall-clock timeout, and retry policy
148
+ // from config instead of hardcoding — mirrors the singleton path
149
+ // (team-runner.ts:1548-1578 + child-executor.ts:395-430).
150
+ const taskTimeoutMs = input.runtimeConfig?.taskTimeoutMs ?? 0;
151
+ const useRetry = input.reliability?.autoRetry !== false;
152
+ const policy: RetryPolicy = {
153
+ ...DEFAULT_RETRY_POLICY,
154
+ ...(input.reliability?.retryPolicy ?? {}),
155
+ };
156
+ // RT-5 #3: pass maxTurns from config instead of hardcoded 5.
157
+ // RT-5 #4: arm a per-attempt wall-clock timeout mirroring the singleton's
158
+ // AbortController+setTimeout pattern (child-executor.ts:395-430). The
159
+ // timeoutController is linked to the run-level signal so an external
160
+ // abort also aborts the worker. Its signal is passed to runWorker; the
161
+ // existing SIGTERM→SIGKILL escalation in child-pi.ts handles cleanup.
162
+ const runOnce = async () => {
163
+ const timeoutController = new AbortController();
164
+ let externalAbortListener: (() => void) | undefined;
165
+ if (signal) {
166
+ if (signal.aborted) {
167
+ timeoutController.abort(signal.reason);
168
+ } else {
169
+ externalAbortListener = () => timeoutController.abort(signal!.reason);
170
+ signal.addEventListener("abort", externalAbortListener, { once: true });
171
+ }
172
+ }
173
+ let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
174
+ if (taskTimeoutMs > 0 && !timeoutController.signal.aborted) {
175
+ timeoutHandle = setTimeout(() => {
176
+ if (!timeoutController.signal.aborted) {
177
+ timeoutController.abort(new Error(`Task exceeded wall-clock timeout of ${taskTimeoutMs}ms`));
178
+ }
179
+ }, taskTimeoutMs);
180
+ timeoutHandle.unref?.();
181
+ }
182
+ try {
183
+ return await runWorker({
184
+ cwd: firstTask.cwd,
185
+ task: combinedPrompt,
186
+ agent,
187
+ signal: timeoutController.signal,
188
+ excludeContextBash: true,
189
+ maxTurns: input.runtimeConfig?.maxTurns,
190
+ graceTurns: input.runtimeConfig?.graceTurns,
191
+ onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e),
192
+ });
193
+ } finally {
194
+ if (timeoutHandle) clearTimeout(timeoutHandle);
195
+ if (externalAbortListener && signal) {
196
+ signal.removeEventListener("abort", externalAbortListener);
197
+ }
198
+ }
199
+ };
138
200
  try {
139
- const result = await executeWithRetry(
140
- async () => {
141
- return await runWorker({
142
- cwd: firstTask.cwd,
143
- task: combinedPrompt,
144
- agent,
145
- signal,
146
- excludeContextBash: true,
147
- maxTurns: 5,
148
- onJsonEvent: (e) => input.onJsonEvent?.(firstTask.id, manifest.runId, e),
149
- });
150
- },
151
- DEFAULT_RETRY_POLICY,
152
- { signal },
153
- );
201
+ const result = useRetry ? await executeWithRetry(runOnce, policy, { signal }) : await runOnce();
154
202
  rawOutput = result.rawFinalText ?? result.stdout ?? "";
155
- success = result.exitStatus?.exitCode === 0;
203
+ // RT-5 #1/#2: distinguish cancel from failure. Cancel = run-level
204
+ // signal aborted OR the child reports cooperative cancellation.
205
+ // success requires exitCode===0 AND no error AND not cancelled
206
+ // (previously success = exitCode===0 only, which misreported
207
+ // depth-guard exitCode:1 and ignored the error field).
208
+ cancelled = signal?.aborted === true || result.exitStatus?.cancelled === true;
209
+ success = !cancelled && result.exitCode === 0 && !result.error;
156
210
  } catch (err) {
157
- rawOutput = `Worker dispatch failed: ${err instanceof Error ? err.message : String(err)}`;
211
+ // RT-5 #1: detect cancel in the throw path (e.g. executeWithRetry's
212
+ // throwIfCancelled when the run-level signal is already aborted).
213
+ cancelled = signal?.aborted === true;
214
+ rawOutput = cancelled
215
+ ? `Worker dispatch cancelled: ${err instanceof Error ? err.message : String(err)}`
216
+ : `Worker dispatch failed: ${err instanceof Error ? err.message : String(err)}`;
158
217
  success = false;
159
218
  } finally {
160
219
  if (heartbeatTimer !== null) clearInterval(heartbeatTimer);
@@ -186,7 +245,7 @@ export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Pro
186
245
  newArtifacts.push(resultArtifact);
187
246
  return {
188
247
  ...t,
189
- status: ok ? ("completed" as const) : ("failed" as const),
248
+ status: ok ? ("completed" as const) : cancelled ? ("cancelled" as const) : ("failed" as const),
190
249
  finishedAt,
191
250
  result: {
192
251
  text,
@@ -232,8 +291,8 @@ export async function runCoalescedTaskGroup(input: CoalescedTaskGroupInput): Pro
232
291
  await appendEventAsync(updatedManifest.eventsPath, {
233
292
  type: "task.coalesced_dispatch_end",
234
293
  runId: manifest.runId,
235
- message: `Coalesced dispatch ${success ? "completed" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
236
- data: { groupId, taskIds, success, strategy: split[0]?.strategy },
294
+ message: `Coalesced dispatch ${success ? "completed" : cancelled ? "cancelled" : "failed"} (${taskIds.length} tasks, ${split[0]?.strategy ?? "broadcast"} split)`,
295
+ data: { groupId, taskIds, success, cancelled, strategy: split[0]?.strategy },
237
296
  });
238
297
 
239
298
  return { manifest: updatedManifest, tasks: updatedTasks, taskIds, rawOutput, success };
@@ -33,7 +33,10 @@ export class Semaphore {
33
33
  // implementation let #queue grow without bound, risking memory
34
34
  // exhaustion under sustained high concurrency with slow releases.
35
35
  if (this.#queue.length >= Semaphore.MAX_QUEUE) {
36
- throw new Error(`Semaphore queue full: ${this.#queue.length} waiters (max ${Semaphore.MAX_QUEUE}); cannot acquire slot`);
36
+ // P1-7: reject (don't throw) so callers can backpressure instead of crashing.
37
+ return Promise.reject(
38
+ new Error(`Semaphore queue full: ${this.#queue.length} waiters (max ${Semaphore.MAX_QUEUE}); cannot acquire slot`),
39
+ );
37
40
  }
38
41
  const { promise, resolve } = (() => {
39
42
  let res: () => void;
@@ -1,4 +1,4 @@
1
- import type { TeamTaskState } from "../state/types.ts";
1
+ import type { TeamTaskState } from "../../state/types.ts";
2
2
 
3
3
  export interface TaskGraphSchedulerSnapshot {
4
4
  ready: string[];
@@ -12,7 +12,7 @@ export function tryRegisterSessionCleanup(pi: ExtensionAPI, cleanup: () => void)
12
12
  const registerFn = api.registerSessionResourceCleanup;
13
13
  if (typeof registerFn === "function") {
14
14
  try {
15
- const unregister = (registerFn as (fn: () => void) => (() => void) | void)(cleanup);
15
+ const unregister = (registerFn as (fn: () => void) => (() => void) | undefined)(cleanup);
16
16
  if (typeof unregister === "function") return unregister;
17
17
  // API returned void — cleanup is registered but cannot be unregistered
18
18
  return undefined;
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
2
2
  import { homedir } from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { atomicWriteJson } from "../state/atomic-write.ts";
5
- import { withFileLockSync } from "../state/locks.ts";
5
+ import { withFileLockSync } from "../state/coordination/locks.ts";
6
6
  import { logInternalError } from "../utils/internal-error.ts";
7
7
  import type { JoinMode } from "./group-join.ts";
8
8
 
@@ -72,6 +72,7 @@ export function composeSingleAgentPrompt(workflow: WorkflowConfig, goal: string)
72
72
  if (step.output) {
73
73
  lines.push(`Output: write your result to \`${step.output}\``);
74
74
  }
75
+ // biome-ignore lint/complexity/useOptionalChain: reads is `string[] | false | undefined` — optional chain doesn't narrow out `false` (no .length on boolean)
75
76
  if (step.reads && step.reads.length) {
76
77
  lines.push(`Read first: ${step.reads.join(", ")}`);
77
78
  }
@@ -3,7 +3,7 @@ import * as os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { errors } from "../errors.ts";
5
5
  import { atomicWriteFile, atomicWriteJson } from "../state/atomic-write.ts";
6
- import { saveRunManifest } from "../state/state-store.ts";
6
+ import { saveRunManifest } from "../state/stores/state-store.ts";
7
7
  import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
8
8
  import { recordFromTask, upsertCrewAgent } from "./crew-agent-records.ts";
9
9
  import { checkProcessLiveness } from "./process-status.ts";
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { DEFAULT_PATHS, DEFAULT_SUBAGENT } from "../config/defaults.ts";
4
4
  import type { PiTeamsToolResult } from "../extension/tool-result.ts";
5
- import { loadRunManifestById } from "../state/state-store.ts";
5
+ import { loadRunManifestById } from "../state/stores/state-store.ts";
6
6
  import { logInternalError } from "../utils/internal-error.ts";
7
7
  import { projectCrewRoot } from "../utils/paths.ts";
8
8
  import { redactSecrets } from "../utils/redaction.ts";
@@ -1,4 +1,4 @@
1
- import { appendEvent } from "../state/event-log.ts";
1
+ import { appendEvent } from "../state/event-log/event-log.ts";
2
2
  import type { TeamRunManifest } from "../state/types.ts";
3
3
  import { logInternalError } from "../utils/internal-error.ts";
4
4
 
@@ -37,22 +37,21 @@ export function recordSupervisorContact(manifest: TeamRunManifest, payload: Omit
37
37
  * Parse a supervisor contact request from child Pi stdout.
38
38
  * Detects structured JSON lines with type "supervisor_contact".
39
39
  */
40
- export function parseSupervisorContactFromLine(line: string): Omit<SupervisorContactPayload, "timestamp" | "runId"> | undefined {
41
- if (!line.trim()) return undefined;
42
- let parsed: unknown;
43
- try {
44
- parsed = JSON.parse(line);
45
- } catch {
46
- return undefined;
47
- }
48
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
49
- const record = parsed as Record<string, unknown>;
40
+ const SUPERVISOR_CONTACT_REASONS = ["decision_needed", "clarification", "approval", "error_escalation", "custom"] as const;
41
+
42
+ /**
43
+ * Validate a parsed event object as a supervisor-contact payload. Shared by the
44
+ * event-based (current) and line-based (deprecated) entry points. Returns the
45
+ * normalized payload, or undefined if the event is not a supervisor contact.
46
+ */
47
+ export function supervisorContactFromEvent(event: unknown): Omit<SupervisorContactPayload, "timestamp" | "runId"> | undefined {
48
+ if (!event || typeof event !== "object" || Array.isArray(event)) return undefined;
49
+ const record = event as Record<string, unknown>;
50
50
  if (record.type !== "supervisor_contact" && record.type !== "crew_supervisor_contact") return undefined;
51
51
  return {
52
52
  taskId: typeof record.taskId === "string" ? record.taskId : "",
53
53
  reason:
54
- typeof record.reason === "string" &&
55
- ["decision_needed", "clarification", "approval", "error_escalation", "custom"].includes(record.reason)
54
+ typeof record.reason === "string" && (SUPERVISOR_CONTACT_REASONS as readonly string[]).includes(record.reason)
56
55
  ? (record.reason as SupervisorContactPayload["reason"])
57
56
  : "custom",
58
57
  message: typeof record.message === "string" ? record.message : String(record.message ?? ""),
@@ -62,3 +61,19 @@ export function parseSupervisorContactFromLine(line: string): Omit<SupervisorCon
62
61
  : undefined,
63
62
  };
64
63
  }
64
+
65
+ /**
66
+ * @deprecated The compact pipeline now passes `supervisor_contact` events through
67
+ * to `onJsonEvent` with their full payload (P2-25 fix). Prefer
68
+ * `supervisorContactFromEvent` on the parsed event. Kept for backward compat.
69
+ */
70
+ export function parseSupervisorContactFromLine(line: string): Omit<SupervisorContactPayload, "timestamp" | "runId"> | undefined {
71
+ if (!line.trim()) return undefined;
72
+ let parsed: unknown;
73
+ try {
74
+ parsed = JSON.parse(line);
75
+ } catch {
76
+ return undefined;
77
+ }
78
+ return supervisorContactFromEvent(parsed);
79
+ }
@@ -1,14 +1,14 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { DEFAULT_OUTPUT_CONTEXT } from "../config/defaults.ts";
4
- import { writeArtifact } from "../state/artifact-store.ts";
5
4
  import { atomicWriteFile } from "../state/atomic-write.ts";
5
+ import { writeArtifact } from "../state/stores/artifact-store.ts";
6
6
  import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
7
7
  import { resolveRealContainedPath } from "../utils/safe-paths.ts";
8
8
  import type { WorkflowStep } from "../workflows/workflow-config.ts";
9
- import { applyCompactPipeline } from "./compact-pipeline.ts";
10
- import { ANSI_STRIP_STAGE, BLANK_COLLAPSE_STAGE, TruncationStage } from "./compact-stages/index.ts";
11
- import { DEFAULT_PRUNE_CONFIG, type FileEditEvent, pruneToolOutputs, type ToolResultEntry } from "./tool-output-pruner.ts";
9
+ import { applyCompactPipeline } from "./compaction/compact-pipeline.ts";
10
+ import { ANSI_STRIP_STAGE, BLANK_COLLAPSE_STAGE, TruncationStage } from "./compaction/compact-stages/index.ts";
11
+ import { DEFAULT_PRUNE_CONFIG, type FileEditEvent, pruneToolOutputs, type ToolResultEntry } from "./compaction/tool-output-pruner.ts";
12
12
 
13
13
  export interface DependencyContextEntry {
14
14
  taskId: string;
@@ -410,7 +410,9 @@ export function collectDependencyOutputContext(
410
410
  // (used for recency sort in the trim step). Both are looked up against
411
411
  // `task.dependsOn` (the declared order) and `tasks` (finished timestamps).
412
412
  const declaredOrder = new Map<string, number>();
413
- task.dependsOn.forEach((depId, index) => declaredOrder.set(depId, index));
413
+ task.dependsOn.forEach((depId, index) => {
414
+ declaredOrder.set(depId, index);
415
+ });
414
416
  const recencyByTaskId = new Map<string, number>();
415
417
  for (const item of tasks) {
416
418
  if (!item.finishedAt) continue;
@@ -29,13 +29,12 @@ import * as fs from "node:fs";
29
29
  import * as path from "node:path";
30
30
  import { loadConfig } from "../../config/config.ts";
31
31
  import { errors } from "../../errors.ts";
32
- import { writeArtifact } from "../../state/artifact-store.ts";
33
- import { appendEventAsync, appendEventBuffered } from "../../state/event-log.ts";
32
+ import { appendEventAsync, appendEventBuffered } from "../../state/event-log/event-log.ts";
33
+ import { writeArtifact } from "../../state/stores/artifact-store.ts";
34
34
  import type { ArtifactDescriptor, OperationTerminalEvidence, TeamRunManifest } from "../../state/types.ts";
35
35
  import { logInternalError } from "../../utils/internal-error.ts";
36
36
  import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
37
- import { buildSyntheticTerminalEvidence, cancellationReasonFromSignal } from "../cancellation.ts";
38
- import type { ChildPiLifecycleEvent } from "../child-pi.ts";
37
+ import type { ChildPiLifecycleEvent, ChildPiRunResult } from "../child-pi/child-pi.ts";
39
38
  import {
40
39
  appendCrewAgentEvent,
41
40
  appendCrewAgentOutput,
@@ -45,21 +44,22 @@ import {
45
44
  } from "../crew-agent-records.ts";
46
45
  import { crewHooks } from "../crew-hooks.ts";
47
46
  import { bridgeEventFromJsonEvent } from "../event-stream-bridge.ts";
47
+ import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../heartbeat/worker-heartbeat.ts";
48
+ import { createStartupEvidence } from "../heartbeat/worker-startup.ts";
48
49
  import {
49
50
  buildConfiguredModelRouting,
50
51
  formatModelAttemptNote,
51
52
  isRetryableModelFailure,
52
53
  type ModelAttemptSummary,
53
- } from "../model-fallback.ts";
54
- import { readEnabledModelsPatterns } from "../model-scope.ts";
55
- import { type ParsedPiJsonOutput, parsePiJsonOutput } from "../pi-json-output.ts";
56
- import { type ProgressEventSummary, shouldAppendProgressEventUpdate } from "../progress-event-coalescer.ts";
57
- import { DEFAULT_RETRY_POLICY } from "../retry-executor.ts";
54
+ } from "../model/model-fallback.ts";
55
+ import { readEnabledModelsPatterns } from "../model/model-scope.ts";
56
+ import { type ParsedPiJsonOutput, parsePiJsonOutput } from "../output/pi-json-output.ts";
57
+ import { type ProgressEventSummary, shouldAppendProgressEventUpdate } from "../output/progress-event-coalescer.ts";
58
+ import { buildSyntheticTerminalEvidence, cancellationReasonFromSignal } from "../process/cancellation.ts";
59
+ import { DEFAULT_RETRY_POLICY } from "../recovery/retry-executor.ts";
58
60
  import { runWorker } from "../run-worker.ts";
59
61
  import { parseSessionUsage } from "../session-usage.ts";
60
- import { parseSupervisorContactFromLine, recordSupervisorContact } from "../supervisor-contact.ts";
61
- import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../worker-heartbeat.ts";
62
- import { createStartupEvidence } from "../worker-startup.ts";
62
+ import { recordSupervisorContact, supervisorContactFromEvent } from "../supervisor-contact.ts";
63
63
  import type { TaskExecutionResult } from "./post-execution.ts";
64
64
  import type { StreamBridgeHandle, TaskExecutionContext } from "./pre-execution.ts";
65
65
  import { applyAgentProgressEvent, applyUsageToProgress, progressEventSummary, shouldFlushProgressEvent } from "./progress.ts";
@@ -115,6 +115,35 @@ async function resolveTaskScopeModelsPatterns(cwd: string): Promise<string[]> {
115
115
  return readEnabledModelsPatterns(cwd);
116
116
  }
117
117
 
118
+ /**
119
+ * RT-6: resolve the configured retry policy's maxAttempts from the project
120
+ * reliability config (`reliability.retryPolicy.maxAttempts`), falling back to
121
+ * {@link DEFAULT_RETRY_POLICY}.maxAttempts when config is unavailable or the
122
+ * field is unset. Mirrors `retryPolicyFromConfig` in team-runner.ts so the
123
+ * spawn-budget math and the actual retry loop (executeWithRetry) agree on the
124
+ * same ceiling. Best-effort: any config read failure silently falls back to
125
+ * the default so a misconfiguration never blocks the spawn.
126
+ */
127
+ export function resolveConfiguredMaxAttempts(cwd: string): number {
128
+ try {
129
+ return loadConfig(cwd).config.reliability?.retryPolicy?.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts;
130
+ } catch {
131
+ return DEFAULT_RETRY_POLICY.maxAttempts;
132
+ }
133
+ }
134
+
135
+ /**
136
+ * RT-6: pure spawn-budget max formula. `attemptModelsCount × (maxAttempts + 1)`
137
+ * — always ≥ 1 full attempt above the theoretical max of
138
+ * `maxAttempts × attemptModelsCount`. Exported for direct unit testing so the
139
+ * configured-vs-default maxAttempts distinction is locked independently of
140
+ * the retry loop (previously hard-coded DEFAULT_RETRY_POLICY.maxAttempts = 3,
141
+ * silently halving retries when a higher maxAttempts — up to 10 — was set).
142
+ */
143
+ export function computeSpawnBudgetMax(attemptModelsCount: number, configuredMaxAttempts: number): number {
144
+ return attemptModelsCount * (configuredMaxAttempts + 1);
145
+ }
146
+
118
147
  /**
119
148
  * 429/rate-limit detection (PI_CREW_TOOLING_429_NOTE.md).
120
149
  *
@@ -215,8 +244,6 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
215
244
  const collectedJsonEvents = ctx.collectedJsonEvents;
216
245
  const streamBridge: StreamBridgeHandle | undefined = ctx.streamBridge;
217
246
 
218
- let resultArtifact: ArtifactDescriptor;
219
- let logArtifact: ArtifactDescriptor | undefined;
220
247
  let transcriptArtifact: ArtifactDescriptor | undefined;
221
248
  let exitCode: number | null = 0;
222
249
  let error: string | undefined;
@@ -246,8 +273,13 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
246
273
  // Budget = attemptModels.length × (maxAttempts + 1) — always one
247
274
  // full attempt-worth above the theoretical maximum of
248
275
  // maxAttempts × attemptModels.length. Only computes once (max=0 guard).
276
+ // RT-6: use the configured retry policy's maxAttempts (from project
277
+ // reliability config), NOT DEFAULT_RETRY_POLICY (3). The retry loop in
278
+ // team-runner.ts wraps runTeamTask in executeWithRetry with the same
279
+ // configured maxAttempts (up to 10); hard-coding the default here silently
280
+ // halved the spawn budget relative to the real retry ceiling.
249
281
  if (input.spawnBudget && input.spawnBudget.max === 0) {
250
- input.spawnBudget.max = attemptModels.length * (DEFAULT_RETRY_POLICY.maxAttempts + 1);
282
+ input.spawnBudget.max = computeSpawnBudgetMax(attemptModels.length, resolveConfiguredMaxAttempts(task.cwd));
251
283
  }
252
284
  const logs: string[] = [];
253
285
  let finalStderr = "";
@@ -390,7 +422,7 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
390
422
  }, taskTimeoutMs);
391
423
  timeoutHandle.unref?.();
392
424
  }
393
- let childResult;
425
+ let childResult: ChildPiRunResult;
394
426
  try {
395
427
  childResult = await runWorker({
396
428
  cwd: task.cwd,
@@ -419,7 +451,10 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
419
451
  const steeringDir = `${manifest.artifactsRoot}/steering`;
420
452
  // Fire-and-forget async write for steering events
421
453
  void appendSteeringAsync(steeringDir, task.id, task.pendingSteers);
422
- task.pendingSteers = [];
454
+ // RT-8: spread before clearing pendingSteers instead of mutating
455
+ // in place — preserves the immutable-snapshot invariant (the same
456
+ // object may already be referenced by the tasks array / snapshots).
457
+ task = { ...task, pendingSteers: [] };
423
458
  tasks = persistSingleTaskUpdate(manifest, tasks, task);
424
459
  }
425
460
  } catch (err) {
@@ -438,19 +473,15 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
438
473
  onStdoutLine: (line) => {
439
474
  appendCrewAgentOutput(manifest, task.id, line);
440
475
  persistHeartbeat();
441
- // Check for supervisor contact requests from child Pi
442
- const contact = parseSupervisorContactFromLine(line);
443
- if (contact) {
444
- recordSupervisorContact(manifest, {
445
- runId: manifest.runId,
446
- ...contact,
447
- });
448
- }
449
476
  },
450
477
  onJsonEvent: (event) => {
451
478
  // Top-level error boundary: prevent any single event from crashing the task.
452
479
  // Errors are logged but processing continues so subsequent events still update state.
453
480
  try {
481
+ // P2-25: handle supervisor_contact here (compact pipeline now passes
482
+ // the full payload through); was dead on the old displayLine path.
483
+ const contact = supervisorContactFromEvent(event);
484
+ if (contact) recordSupervisorContact(manifest, { runId: manifest.runId, ...contact });
454
485
  appendCrewAgentEvent(manifest, task.id, event);
455
486
  if (collectedJsonEvents && event && typeof event === "object" && !Array.isArray(event))
456
487
  collectedJsonEvents.push(event as Record<string, unknown>);
@@ -463,10 +494,15 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
463
494
  if (msg?.role === "assistant") {
464
495
  const usage = msg.usage as Record<string, number> | undefined;
465
496
  if (usage) {
466
- task.lifetimeUsage = {
467
- input: (task.lifetimeUsage?.input ?? 0) + (usage.input ?? 0),
468
- output: (task.lifetimeUsage?.output ?? 0) + (usage.output ?? 0),
469
- cacheWrite: (task.lifetimeUsage?.cacheWrite ?? 0) + (usage.cacheWrite ?? 0),
497
+ // RT-8: spread before accumulating lifetimeUsage instead of
498
+ // mutating in place — preserves the immutable-snapshot invariant.
499
+ task = {
500
+ ...task,
501
+ lifetimeUsage: {
502
+ input: (task.lifetimeUsage?.input ?? 0) + (usage.input ?? 0),
503
+ output: (task.lifetimeUsage?.output ?? 0) + (usage.output ?? 0),
504
+ cacheWrite: (task.lifetimeUsage?.cacheWrite ?? 0) + (usage.cacheWrite ?? 0),
505
+ },
470
506
  };
471
507
  }
472
508
  }
@@ -680,7 +716,7 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
680
716
  });
681
717
  }
682
718
  }
683
- resultArtifact = writeArtifact(manifest.artifactsRoot, {
719
+ const resultArtifact = writeArtifact(manifest.artifactsRoot, {
684
720
  kind: "result",
685
721
  relativePath: `results/${task.id}.txt`,
686
722
  content:
@@ -699,7 +735,7 @@ export async function runChildProcessTask(ctx: TaskExecutionContext): Promise<Ta
699
735
  "(no output)",
700
736
  producer: task.id,
701
737
  });
702
- logArtifact = writeArtifact(manifest.artifactsRoot, {
738
+ const logArtifact = writeArtifact(manifest.artifactsRoot, {
703
739
  kind: "log",
704
740
  relativePath: `logs/${task.id}.log`,
705
741
  content: [
@@ -1,9 +1,9 @@
1
1
  import * as fs from "node:fs";
2
2
  import type { AgentConfig } from "../../agents/agent-config.ts";
3
3
  import type { CrewRuntimeConfig } from "../../config/config.ts";
4
- import { writeArtifact } from "../../state/artifact-store.ts";
5
- import { appendEventFireAndForget } from "../../state/event-log.ts";
6
- import { loadRunManifestById } from "../../state/state-store.ts";
4
+ import { appendEventFireAndForget } from "../../state/event-log/event-log.ts";
5
+ import { writeArtifact } from "../../state/stores/artifact-store.ts";
6
+ import { loadRunManifestById } from "../../state/stores/state-store.ts";
7
7
  import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../../state/types.ts";
8
8
  import type { WorkflowStep } from "../../workflows/workflow-config.ts";
9
9
  import {
@@ -13,11 +13,11 @@ import {
13
13
  recordFromTask,
14
14
  upsertCrewAgent,
15
15
  } from "../crew-agent-records.ts";
16
- import { runLiveSessionTask } from "../live-session-runtime.ts";
17
- import type { ParsedPiJsonOutput } from "../pi-json-output.ts";
18
- import { type ProgressEventSummary, shouldAppendProgressEventUpdate } from "../progress-event-coalescer.ts";
19
- import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../worker-heartbeat.ts";
20
- import { createStartupEvidence, type WorkerStartupEvidence } from "../worker-startup.ts";
16
+ import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../heartbeat/worker-heartbeat.ts";
17
+ import { createStartupEvidence, type WorkerStartupEvidence } from "../heartbeat/worker-startup.ts";
18
+ import { runLiveSessionTask } from "../live-session/live-session-runtime.ts";
19
+ import type { ParsedPiJsonOutput } from "../output/pi-json-output.ts";
20
+ import { type ProgressEventSummary, shouldAppendProgressEventUpdate } from "../output/progress-event-coalescer.ts";
21
21
  import { applyAgentProgressEvent, applyUsageToProgress, progressEventSummary, shouldFlushProgressEvent } from "./progress.ts";
22
22
  import { persistSingleTaskUpdate } from "./state-helpers.ts";
23
23
 
@@ -15,10 +15,10 @@
15
15
  * exactly (char scenarios 5-7, 9, 10 cover them).
16
16
  */
17
17
  import { appendHookEvent, executeHook } from "../../hooks/registry.ts";
18
- import { writeArtifact } from "../../state/artifact-store.ts";
19
- import { appendEventAsync } from "../../state/event-log.ts";
20
- import { withRunLock } from "../../state/locks.ts";
21
- import { saveRunManifestAsync } from "../../state/state-store.ts";
18
+ import { withRunLock } from "../../state/coordination/locks.ts";
19
+ import { appendEventAsync } from "../../state/event-log/event-log.ts";
20
+ import { writeArtifact } from "../../state/stores/artifact-store.ts";
21
+ import { saveRunManifestAsync } from "../../state/stores/state-store.ts";
22
22
  import type {
23
23
  ArtifactDescriptor,
24
24
  OperationTerminalEvidence,
@@ -29,16 +29,16 @@ import type {
29
29
  import { captureWorktreeDiffAsync, captureWorktreeDiffStatAsync } from "../../worktree/worktree-manager.ts";
30
30
  import { appendTaskAttentionEvent } from "../attention-events.ts";
31
31
  import { extractCommandTrace } from "../command-trace.ts";
32
- import { evaluateCompletionMutationGuard } from "../completion-guard.ts";
33
32
  import { emptyCrewAgentProgress, recordFromTask, upsertCrewAgent } from "../crew-agent-records.ts";
34
33
  import { crewHooks } from "../crew-hooks.ts";
35
- import { createVerificationEvidence } from "../green-contract.ts";
36
- import type { ModelAttemptSummary } from "../model-fallback.ts";
37
- import { type OutputValidationResult, validateWorkerOutput } from "../output-validator.ts";
38
- import type { ParsedPiJsonOutput } from "../pi-json-output.ts";
34
+ import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../heartbeat/worker-heartbeat.ts";
35
+ import type { ModelAttemptSummary } from "../model/model-fallback.ts";
36
+ import { type OutputValidationResult, validateWorkerOutput } from "../output/output-validator.ts";
37
+ import type { ParsedPiJsonOutput } from "../output/pi-json-output.ts";
39
38
  import { writeTaskSharedOutput } from "../task-output-context.ts";
40
- import { computeGreenLevelFromResults, executeVerificationCommands } from "../verification-gates.ts";
41
- import { createWorkerHeartbeat, touchWorkerHeartbeat } from "../worker-heartbeat.ts";
39
+ import { evaluateCompletionMutationGuard } from "../verification/completion-guard.ts";
40
+ import { createVerificationEvidence } from "../verification/green-contract.ts";
41
+ import { computeGreenLevelFromResults, executeVerificationCommands } from "../verification/verification-gates.ts";
42
42
  import { extractYieldResult, hasYieldInOutput, isYieldEvent, type YieldResult } from "../yield-handler.ts";
43
43
  import { buildWorkerCapabilityInventory } from "./capabilities.ts";
44
44
  import type { TaskExecutionContext } from "./pre-execution.ts";
@@ -64,7 +64,7 @@ export interface TaskExecutionResult {
64
64
  finalStdout: string;
65
65
  transcriptPath: string | undefined;
66
66
  terminalEvidence: OperationTerminalEvidence[];
67
- startupEvidence: import("../worker-startup.ts").WorkerStartupEvidence;
67
+ startupEvidence: import("../heartbeat/worker-startup.ts").WorkerStartupEvidence;
68
68
  }
69
69
 
70
70
  /** Final persisted task state returned by finalizeTaskResult. */