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,22 +1,29 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as fsp from "node:fs/promises";
3
3
  import * as path from "node:path";
4
- import { DEFAULT_CACHE, DEFAULT_PATHS } from "../config/defaults.ts";
5
- import { errors } from "../errors.ts";
6
- import type { TeamConfig } from "../teams/team-config.ts";
7
- import { createRunId, createTaskId } from "../utils/ids.ts";
8
- import { logInternalError } from "../utils/internal-error.ts";
9
- import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
10
- import { assertSafePathId, resolveContainedRelativePath, resolveRealContainedPath } from "../utils/safe-paths.ts";
11
- import { toPiSessionId } from "../utils/session-utils.ts";
12
- import type { WorkflowConfig } from "../workflows/workflow-config.ts";
4
+ import { DEFAULT_CACHE, DEFAULT_PATHS } from "../../config/defaults.ts";
5
+ import { errors } from "../../errors.ts";
6
+ import type { TeamConfig } from "../../teams/team-config.ts";
7
+ import { createRunId, createTaskId } from "../../utils/ids.ts";
8
+ import { logInternalError } from "../../utils/internal-error.ts";
9
+ import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../../utils/paths.ts";
10
+ import { assertSafePathId, resolveContainedRelativePath, resolveRealContainedPath } from "../../utils/safe-paths.ts";
11
+ import { toPiSessionId } from "../../utils/session-utils.ts";
12
+ import type { WorkflowConfig } from "../../workflows/workflow-config.ts";
13
+ import {
14
+ atomicWriteJson,
15
+ atomicWriteJsonAsync,
16
+ atomicWriteJsonCoalesced,
17
+ flushPendingAtomicWrites,
18
+ readJsonFile,
19
+ } from "../atomic-write.ts";
20
+ import { canTransitionRunStatus, isTeamTaskStatus } from "../contracts.ts";
21
+ import { withRunLock, withRunLockSync } from "../coordination/locks.ts";
22
+ import { appendEvent } from "../event-log/event-log.ts";
23
+ import { reconstructTasksFromEvents } from "../event-log/event-reconstructor.ts";
24
+ import type { TeamRunManifest, TeamTaskState } from "../types.ts";
25
+ import { CURRENT_SCHEMA_VERSION, CURRENT_TASKS_SCHEMA_VERSION } from "../types.ts";
13
26
  import { unregisterActiveRun } from "./active-run-registry.ts";
14
- import { atomicWriteJson, atomicWriteJsonAsync, atomicWriteJsonCoalesced, flushPendingAtomicWrites, readJsonFile } from "./atomic-write.ts";
15
- import { canTransitionRunStatus } from "./contracts.ts";
16
- import { appendEvent } from "./event-log.ts";
17
- import { withRunLock, withRunLockSync } from "./locks.ts";
18
- import type { TeamRunManifest, TeamTaskState } from "./types.ts";
19
- import { CURRENT_SCHEMA_VERSION } from "./types.ts";
20
27
 
21
28
  /**
22
29
  * stat() the manifest with a brief retry on Windows for the AV-scan window.
@@ -151,19 +158,33 @@ function scopeBaseRoot(cwd: string): string {
151
158
  return useProjectState(cwd) ? projectCrewRoot(cwd) : userCrewRoot();
152
159
  }
153
160
 
161
+ // P1-12: the containment verdict (resolveRealContainedPath ancestor walk, ~8-12
162
+ // syscalls) cannot change for a run dir that isn't replaced — memoize positive
163
+ // results per (cwd, runId) with a short TTL. Negatives are NOT cached (so a
164
+ // newly-created run is found promptly). Downstream manifest stat still catches
165
+ // deletions, so a stale positive is harmless.
166
+ const runStateRootCache = new Map<string, { root: string; expiresAt: number }>();
167
+ const RUN_STATE_ROOT_TTL_MS = 10_000;
168
+ const RUN_STATE_ROOT_CACHE_MAX = 256;
169
+
154
170
  function resolveRunStateRoot(cwd: string, runId: string): string | undefined {
155
171
  assertSafePathId("runId", runId);
172
+ const key = `${cwd}\0${runId}`;
173
+ const now = Date.now();
174
+ const cached = runStateRootCache.get(key);
175
+ if (cached && cached.expiresAt > now) return cached.root;
156
176
  const runsRoot = path.join(scopeBaseRoot(cwd), DEFAULT_PATHS.state.runsSubdir);
157
177
  const scopedPath = resolveContainedRelativePath(runsRoot, runId, "runId");
158
178
  try {
159
- // Single atomic validation: resolves through symlinks via realpath,
160
- // verifies containment within runsRoot, and throws ENOENT if missing.
161
- // Eliminates the TOCTOU window from the previous existsSync + lstatSync
162
- // + resolveRealContainedPath sequence.
163
179
  resolveRealContainedPath(runsRoot, runId);
164
180
  } catch {
165
181
  return undefined;
166
182
  }
183
+ if (runStateRootCache.size >= RUN_STATE_ROOT_CACHE_MAX) {
184
+ const oldest = runStateRootCache.keys().next().value;
185
+ if (oldest !== undefined) runStateRootCache.delete(oldest);
186
+ }
187
+ runStateRootCache.set(key, { root: scopedPath, expiresAt: now + RUN_STATE_ROOT_TTL_MS });
167
188
  return scopedPath;
168
189
  }
169
190
 
@@ -410,7 +431,32 @@ export async function saveRunManifestAsync(manifest: TeamRunManifest): Promise<v
410
431
  });
411
432
  }
412
433
 
434
+ /**
435
+ * ST-4: Defense-in-depth guard — refuse to persist an empty tasks array over
436
+ * a previously non-empty tasks file. Prevents the cascade where a corrupt
437
+ * tasks.json (→ [] on load via readJsonFile catching SyntaxError) is then
438
+ * persisted as [], permanently destroying the data. If the on-disk file has
439
+ * tasks and the incoming array is empty, we log a warning and refuse.
440
+ *
441
+ * @returns true if the write should proceed, false if it was refused.
442
+ */
443
+ function shouldPersistTasks(manifest: TeamRunManifest, tasks: TeamTaskState[]): boolean {
444
+ if (tasks.length > 0) return true;
445
+ const existing = extractTaskArray(readJsonFile<unknown>(manifest.tasksPath));
446
+ if (existing.length > 0) {
447
+ console.warn(
448
+ `[state-store] refusing to persist empty tasks over ${existing.length} existing task(s) — possible corrupt-load cascade (runId=${manifest.runId})`,
449
+ );
450
+ return false;
451
+ }
452
+ return true;
453
+ }
454
+
413
455
  export function saveRunTasks(manifest: TeamRunManifest, tasks: TeamTaskState[]): void {
456
+ // ST-4: refuse to persist [] over a previously-non-empty tasks file.
457
+ // Prevents the cascade where a corrupt tasks.json (→ [] on load) is then
458
+ // persisted as [], permanently destroying the data.
459
+ if (!shouldPersistTasks(manifest, tasks)) return;
414
460
  // FIX: Invalidate cache BEFORE atomic write to prevent stale cache serving.
415
461
  invalidateRunCache(manifest.stateRoot);
416
462
 
@@ -487,6 +533,8 @@ export function saveRunTasks(manifest: TeamRunManifest, tasks: TeamTaskState[]):
487
533
  */
488
534
  /** @internal */
489
535
  export function saveRunTasksCoalesced(manifest: TeamRunManifest, tasks: TeamTaskState[], skipCoalesce: boolean = false): void {
536
+ // ST-4: refuse to persist [] over a previously-non-empty tasks file.
537
+ if (!shouldPersistTasks(manifest, tasks)) return;
490
538
  // FIX: Invalidate cache BEFORE atomic write to prevent stale cache serving.
491
539
  invalidateRunCache(manifest.stateRoot);
492
540
  try {
@@ -498,6 +546,8 @@ export function saveRunTasksCoalesced(manifest: TeamRunManifest, tasks: TeamTask
498
546
  }
499
547
 
500
548
  export async function saveRunTasksAsync(manifest: TeamRunManifest, tasks: TeamTaskState[]): Promise<void> {
549
+ // ST-4: refuse to persist [] over a previously-non-empty tasks file.
550
+ if (!shouldPersistTasks(manifest, tasks)) return;
501
551
  // FIX: Invalidate cache BEFORE atomic write to prevent stale cache serving.
502
552
  invalidateRunCache(manifest.stateRoot);
503
553
  try {
@@ -719,6 +769,185 @@ async function readJsonFileAsync<T>(filePath: string): Promise<T | undefined> {
719
769
  }
720
770
  }
721
771
 
772
+ /**
773
+ * ST-4: Rename a corrupt file to a quarantine path (`.corrupt-<ts>`) so it is
774
+ * preserved for debugging but no longer read as the primary source of truth.
775
+ */
776
+ function quarantineCorruptFile(filePath: string): void {
777
+ try {
778
+ fs.renameSync(filePath, `${filePath}.corrupt-${Date.now()}`);
779
+ } catch {
780
+ // Best-effort — if rename fails (file already gone, permission, etc.),
781
+ // we still proceed with reconstruction from the event log.
782
+ }
783
+ }
784
+
785
+ /**
786
+ * ST-4: Convert event-reconstructor output into TeamTaskState[].
787
+ * Reconstructed tasks carry lifecycle data (id, status, timing) from the
788
+ * event log; auxiliary fields (role, agent, title) are filled with defaults
789
+ * since they are not present in lifecycle events.
790
+ */
791
+ function reconstructTasksFromEventLog(eventsPath: string, runId: string): TeamTaskState[] {
792
+ try {
793
+ const result = reconstructTasksFromEvents(eventsPath);
794
+ const tasks: TeamTaskState[] = [];
795
+ for (const [, rt] of result.tasks) {
796
+ tasks.push({
797
+ id: rt.id,
798
+ runId,
799
+ role: "unknown",
800
+ agent: "unknown",
801
+ title: "reconstructed from events",
802
+ status: isTeamTaskStatus(rt.status) ? rt.status : "queued",
803
+ dependsOn: [],
804
+ cwd: "",
805
+ startedAt: rt.startedAt,
806
+ finishedAt: rt.finishedAt,
807
+ error: rt.error,
808
+ segment: rt.segment,
809
+ diagnostics: rt.diagnostics,
810
+ metrics: rt.metrics,
811
+ });
812
+ }
813
+ return tasks;
814
+ } catch {
815
+ return [];
816
+ }
817
+ }
818
+
819
+ /**
820
+ * ST-4: Load tasks.json with corruption recovery (sync path).
821
+ *
822
+ * Distinguishes:
823
+ * - ENOENT / ENOTDIR → legitimate empty → [] (NOT quarantined).
824
+ * - SyntaxError (parse failure) → corrupt → quarantine `.corrupt-<ts>` AND
825
+ * reconstruct from events.jsonl. If reconstruction yields tasks, persist
826
+ * them so subsequent loads see a valid file.
827
+ * - Non-array JSON (e.g. `{}`) → corrupt → same as SyntaxError.
828
+ * - Valid array → return as-is.
829
+ */
830
+
831
+ /**
832
+ * ST-9: Extract the task array from a tasks.json payload.
833
+ *
834
+ * Accepts both the v0 legacy bare-array format and the v1+ envelope
835
+ * `{ schemaVersion, tasks }`. Returns [] for unrecognized shapes.
836
+ */
837
+ function extractTaskArray(raw: unknown): TeamTaskState[] {
838
+ if (Array.isArray(raw)) return raw as TeamTaskState[];
839
+ if (raw !== null && typeof raw === "object" && "tasks" in raw) {
840
+ const envelope = raw as { tasks?: unknown };
841
+ if (Array.isArray(envelope.tasks)) return envelope.tasks as TeamTaskState[];
842
+ }
843
+ return [];
844
+ }
845
+
846
+ /**
847
+ * ST-9: Whether `parsed` is a recognizable tasks.json shape (v0 bare array
848
+ * or v1+ envelope). Used to distinguish legitimate formats from corruption.
849
+ */
850
+ function isRecognizableTasksPayload(parsed: unknown): boolean {
851
+ if (Array.isArray(parsed)) return true;
852
+ if (parsed !== null && typeof parsed === "object" && "tasks" in parsed) {
853
+ return Array.isArray((parsed as { tasks?: unknown }).tasks);
854
+ }
855
+ return false;
856
+ }
857
+
858
+ /**
859
+ * ST-9: Version-check + migration hook for tasks.json.
860
+ *
861
+ * tasks.json has two on-disk shapes:
862
+ * - v0 (current): bare JSON array `TeamTaskState[]` — what saveRunTasks*
863
+ * write today (backward-compatible; no schemaVersion envelope).
864
+ * - v1+ (future): envelope `{ schemaVersion: number, tasks: TeamTaskState[] }`
865
+ * — read-supported defensively for a future write-side switch.
866
+ *
867
+ * This detects the shape and returns the task array. v0 needs NO migration
868
+ * (it IS the current write format). For v1+ envelopes, a schemaVersion
869
+ * mismatch warns (mirroring the manifest check). Future breaking changes
870
+ * add real migration logic here.
871
+ */
872
+ function migrateTasksFile(parsed: unknown, runId: string): TeamTaskState[] {
873
+ // v0 current: bare array (no schemaVersion envelope) — what writers produce.
874
+ if (Array.isArray(parsed)) {
875
+ // v0 bare array is the CURRENT write format (saveRunTasks* write the
876
+ // array directly, by design — backward compat). Nothing to migrate:
877
+ // return as-is. (v1+ envelope read-support below is defensive, for a
878
+ // future write-side switch.) Do NOT warn here — it would fire for 100%
879
+ // of runs on every load and flood the UI on startup.
880
+ return parsed as TeamTaskState[];
881
+ }
882
+ // v1+ envelope: { schemaVersion, tasks }.
883
+ if (parsed !== null && typeof parsed === "object" && "tasks" in parsed) {
884
+ const envelope = parsed as { schemaVersion?: unknown; tasks?: unknown };
885
+ const detected = typeof envelope.schemaVersion === "number" ? envelope.schemaVersion : 0;
886
+ if (detected !== CURRENT_TASKS_SCHEMA_VERSION) {
887
+ console.warn(
888
+ `[state-store] tasks.json schemaVersion mismatch: expected ${CURRENT_TASKS_SCHEMA_VERSION}, got ${detected}. Run ${runId} may be incompatible.`,
889
+ );
890
+ }
891
+ }
892
+ return extractTaskArray(parsed);
893
+ }
894
+ function loadTasksWithRecovery(tasksPath: string, eventsPath: string, runId: string): TeamTaskState[] {
895
+ let content: string;
896
+ try {
897
+ content = fs.readFileSync(tasksPath, "utf-8");
898
+ } catch {
899
+ // ENOENT / ENOTDIR / other read errors → empty (retry loop handles
900
+ // transient instability; ENOENT is a legitimate empty run).
901
+ return [];
902
+ }
903
+ let parsed: unknown;
904
+ try {
905
+ parsed = JSON.parse(content);
906
+ } catch {
907
+ // SyntaxError — corrupt file.
908
+ quarantineCorruptFile(tasksPath);
909
+ const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
910
+ if (reconstructed.length > 0) atomicWriteJson(tasksPath, reconstructed);
911
+ return reconstructed;
912
+ }
913
+ if (!isRecognizableTasksPayload(parsed)) {
914
+ // Neither v0 bare array nor v1+ envelope (e.g. `{}`) — corrupt.
915
+ quarantineCorruptFile(tasksPath);
916
+ const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
917
+ if (reconstructed.length > 0) atomicWriteJson(tasksPath, reconstructed);
918
+ return reconstructed;
919
+ }
920
+ return migrateTasksFile(parsed, runId);
921
+ }
922
+
923
+ /**
924
+ * ST-4: async twin of {@link loadTasksWithRecovery}.
925
+ */
926
+ async function loadTasksWithRecoveryAsync(tasksPath: string, eventsPath: string, runId: string): Promise<TeamTaskState[]> {
927
+ let content: string;
928
+ try {
929
+ content = await fs.promises.readFile(tasksPath, "utf-8");
930
+ } catch {
931
+ return [];
932
+ }
933
+ let parsed: unknown;
934
+ try {
935
+ parsed = JSON.parse(content);
936
+ } catch {
937
+ quarantineCorruptFile(tasksPath);
938
+ const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
939
+ if (reconstructed.length > 0) await atomicWriteJsonAsync(tasksPath, reconstructed);
940
+ return reconstructed;
941
+ }
942
+ if (!isRecognizableTasksPayload(parsed)) {
943
+ quarantineCorruptFile(tasksPath);
944
+ const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
945
+ if (reconstructed.length > 0) await atomicWriteJsonAsync(tasksPath, reconstructed);
946
+ return reconstructed;
947
+ }
948
+ return migrateTasksFile(parsed, runId);
949
+ }
950
+
722
951
  /**
723
952
  * Load a run manifest and its tasks by runId.
724
953
  * WARNING: This function provides best-effort consistency only. The sentinel-based
@@ -799,7 +1028,7 @@ export function loadRunManifestById(cwd: string, runId: string): { manifest: Tea
799
1028
  const freshStat = fs.statSync(manifestPath);
800
1029
  manifest = readJsonFile<TeamRunManifest>(manifestPath);
801
1030
  const freshTasksStat = fs.existsSync(tasksPath) ? fs.statSync(tasksPath) : undefined;
802
- tasks = readJsonFile<TeamTaskState[]>(tasksPath) ?? [];
1031
+ tasks = loadTasksWithRecovery(tasksPath, manifest?.eventsPath ?? path.join(stateRoot, "events.jsonl"), manifest?.runId ?? runId);
803
1032
  // If size/mtime didn't change between stat and read, we're consistent.
804
1033
  if (
805
1034
  freshStat.mtimeMs === manifestStat.mtimeMs &&
@@ -919,7 +1148,11 @@ export async function loadRunManifestByIdAsync(
919
1148
  const freshStat = await fs.promises.stat(manifestPath);
920
1149
  manifest = await readJsonFileAsync<TeamRunManifest>(manifestPath);
921
1150
  const freshTasksStat = await fs.promises.stat(tasksPath).catch(() => undefined);
922
- tasks = (await readJsonFileAsync<TeamTaskState[]>(tasksPath)) ?? [];
1151
+ tasks = await loadTasksWithRecoveryAsync(
1152
+ tasksPath,
1153
+ manifest?.eventsPath ?? path.join(stateRoot, "events.jsonl"),
1154
+ manifest?.runId ?? runId,
1155
+ );
923
1156
  // If size/mtime didn't change between stat and read, we're consistent.
924
1157
  if (
925
1158
  freshStat.mtimeMs === manifestStat.mtimeMs &&
@@ -1,9 +1,9 @@
1
- import type { CrashClass } from "../runtime/crash-classification.ts";
2
1
  import type { CrewAgentProgress } from "../runtime/crew-agent-runtime.ts";
3
- import type { WorkerHeartbeatState } from "../runtime/worker-heartbeat.ts";
2
+ import type { WorkerHeartbeatState } from "../runtime/heartbeat/worker-heartbeat.ts";
3
+ import type { CrashClass } from "../runtime/recovery/crash-classification.ts";
4
4
  import type { TeamRunStatus, TeamTaskStatus } from "./contracts.ts";
5
+ import type { TaskClaimState } from "./coordination/task-claims.ts";
5
6
  import type { CoherenceMark, RolloutEntry } from "./decision-ledger.ts";
6
- import type { TaskClaimState } from "./task-claims.ts";
7
7
 
8
8
  export type { TeamRunStatus, TeamTaskStatus } from "./contracts.ts";
9
9
  export type { CoherenceMark, CrewAgentProgress, RolloutEntry };
@@ -183,6 +183,30 @@ export interface CrewAttentionEventData {
183
183
  */
184
184
  export const CURRENT_SCHEMA_VERSION = 1 as const;
185
185
 
186
+ /**
187
+ * ST-9: schemaVersion sentinel for tasks.json (and future goal-state/mailbox
188
+ * files). Mirrors {@link CURRENT_SCHEMA_VERSION} for the manifest.
189
+ *
190
+ * tasks.json has two on-disk shapes:
191
+ * - v0 (legacy): bare JSON array `TeamTaskState[]` — no schemaVersion.
192
+ * - v1+ (current): envelope `{ schemaVersion, tasks }` — see {@link TasksFileData}.
193
+ *
194
+ * The reader detects the shape, warns on version mismatch, and applies
195
+ * migration logic in state-store.ts (the structured migration hook).
196
+ */
197
+ export const CURRENT_TASKS_SCHEMA_VERSION = 1 as const;
198
+
199
+ /**
200
+ * ST-9: Versioned envelope for tasks.json on disk.
201
+ * Writers produce this shape; readers accept it OR the legacy v0 bare array.
202
+ * `TeamTaskState` is defined later in this module but interface declarations
203
+ * are type-hoisted so the forward reference is valid.
204
+ */
205
+ export interface TasksFileData {
206
+ schemaVersion: typeof CURRENT_TASKS_SCHEMA_VERSION;
207
+ tasks: TeamTaskState[];
208
+ }
209
+
186
210
  export interface TeamRunManifest {
187
211
  schemaVersion: typeof CURRENT_SCHEMA_VERSION;
188
212
  runId: string;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * PR-G3 (UI-3) — single shared Component contract for the UI layer.
3
+ *
4
+ * Five parallel "Component" shapes previously existed across src/ui/:
5
+ *
6
+ * - `RenderableComponent` (layout-primitives.ts)
7
+ * - `WidgetComponent` (widget/index.ts)
8
+ * - `DashboardComponent` (run-dashboard.ts)
9
+ * - `type Component` (transcript-viewer.ts)
10
+ * - `type Component = Container|Text` (tool-renderers/index.ts)
11
+ *
12
+ * They all share the same two core methods (render + invalidate) but could
13
+ * not be used interchangeably, which blocked polymorphic dispatch and shared
14
+ * testing. This module names that shared contract so any UI component can be
15
+ * treated uniformly.
16
+ *
17
+ * This is purely additive: every existing component already structurally
18
+ * satisfies these interfaces (render + invalidate are required everywhere;
19
+ * handleInput is present on the interactive ones). Adapting the existing
20
+ * declarations to `extends`/alias these types changes NO runtime behavior —
21
+ * it only makes the relationship explicit and compiler-enforced.
22
+ *
23
+ * Note: @earendil-works/pi-tui also declares a `Component` interface with the
24
+ * same render+invalidate core (plus an optional handleInput). `CrewComponent`
25
+ * is a structural supertype of it, so pi-tui components opt in automatically.
26
+ */
27
+
28
+ /** Minimum shared contract every UI component satisfies. */
29
+ export interface CrewComponent {
30
+ /**
31
+ * Drop cached render state so the next `render` recomputes from fresh data.
32
+ * Called on theme changes, resize, or any state mutation that invalidates
33
+ * previously produced lines.
34
+ */
35
+ invalidate(): void;
36
+ /**
37
+ * Produce rendered lines for the given terminal width.
38
+ * @param width - available terminal width in cells
39
+ * @returns one string per output row (already padded/ANSI-colored)
40
+ */
41
+ render(width: number): string[];
42
+ }
43
+
44
+ /**
45
+ * A component that also accepts raw key/input data. Interactive viewers
46
+ * (RunDashboard, DurableTranscriptViewer) and overlays (ConfirmOverlay,
47
+ * MailboxDetailOverlay, AgentPickerOverlay) implement this; purely
48
+ * presentational components (Container, Text, Spacer, Box) do not.
49
+ */
50
+ export interface InteractiveComponent extends CrewComponent {
51
+ /** Optional stable id (used by overlay stacks for lookup/debug). */
52
+ id?: string;
53
+ /** Consume a raw input chunk (a keypress escape sequence). */
54
+ handleInput(data: string): void;
55
+ }
@@ -29,7 +29,9 @@ export class CrewSelectList<T = string> {
29
29
  this.selectedIndex = this.items.length ? 0 : -1;
30
30
  }
31
31
 
32
- invalidate(): void {}
32
+ invalidate(): void {
33
+ /* no-op */
34
+ }
33
35
 
34
36
  getSelected(): CrewSelectItem<T> | undefined {
35
37
  return this.selectedIndex >= 0 ? this.items[this.selectedIndex] : undefined;
@@ -1,5 +1,5 @@
1
1
  import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
2
- import { type LiveAgentHandle, listLiveAgents, listLiveAgentsByWorkspace } from "../../runtime/live-agent-manager.ts";
2
+ import { type LiveAgentHandle, listLiveAgents, listLiveAgentsByWorkspace } from "../../runtime/live-session/live-agent-manager.ts";
3
3
  import { formatCost } from "../../state/usage.ts";
4
4
  import { visibleWidth } from "../../utils/visual.ts";
5
5
  import { computeLiveDurationMs } from "../live-duration.ts";
@@ -8,7 +8,7 @@
8
8
  * tailed by `run-snapshot-cache.ts`. Non-DWF runs (no `dwf.phase_*` events)
9
9
  * yield `null`, so the progress pane stays unchanged for them.
10
10
  */
11
- import type { TeamEvent } from "../state/event-log.ts";
11
+ import type { TeamEvent } from "../state/event-log/event-log.ts";
12
12
 
13
13
  // ---------------------------------------------------------------------------
14
14
  // Types
@@ -1,5 +1,5 @@
1
1
  import type { MetricRegistry } from "../observability/metric-registry.ts";
2
- import { classifyHeartbeat, heartbeatAgeMs } from "../runtime/heartbeat-gradient.ts";
2
+ import { classifyHeartbeat, heartbeatAgeMs } from "../runtime/heartbeat/heartbeat-gradient.ts";
3
3
  import type { TeamTaskState } from "../state/types.ts";
4
4
  import type { RunUiSnapshot } from "./snapshot-types.ts";
5
5