@duckmind/dm-windows-x64 0.61.4 → 0.61.9

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 (279) hide show
  1. package/dm.exe +0 -0
  2. package/extensions/.dm-extensions.json +211 -67
  3. package/extensions/dm-subagents/agents/claude-code-writer.md +15 -0
  4. package/extensions/dm-subagents/agents/claude-code.md +15 -0
  5. package/extensions/dm-subagents/agents/codex-exec-writer.md +15 -0
  6. package/extensions/dm-subagents/agents/codex-exec.md +15 -0
  7. package/extensions/dm-subagents/agents/cursor-agent-writer.md +14 -0
  8. package/extensions/dm-subagents/agents/cursor-agent.md +14 -0
  9. package/extensions/dm-subagents/agents/delegate.md +3 -2
  10. package/extensions/dm-subagents/agents/oracle.md +10 -5
  11. package/extensions/dm-subagents/agents/researcher.md +2 -2
  12. package/extensions/dm-subagents/agents/reviewer.md +17 -7
  13. package/extensions/dm-subagents/agents/scout.md +5 -5
  14. package/extensions/dm-subagents/agents/worker.md +6 -2
  15. package/extensions/dm-subagents/async-retention-discovery-worker.mjs +167 -0
  16. package/extensions/dm-subagents/index.js +4 -0
  17. package/extensions/dm-subagents/inspector-runner.mjs +10 -0
  18. package/extensions/dm-subagents/install.mjs +3 -2
  19. package/extensions/dm-subagents/package.json +2 -2
  20. package/extensions/dm-subagents/prompts/council.md +60 -0
  21. package/extensions/dm-subagents/prompts/parallel-review.md +5 -1
  22. package/extensions/dm-subagents/prompts/review-loop.md +13 -7
  23. package/extensions/dm-subagents/skills/council-mode/SKILL.md +59 -0
  24. package/extensions/dm-subagents/skills/council-mode/references/pass-contracts.md +150 -0
  25. package/extensions/dm-subagents/skills/dm-subagents/SKILL.md +96 -911
  26. package/extensions/dm-subagents/skills/dm-subagents/references/constraints-and-recipes.md +70 -0
  27. package/extensions/dm-subagents/skills/dm-subagents/references/execution-controls.md +539 -0
  28. package/extensions/dm-subagents/skills/dm-subagents/references/management-authoring-rpc.md +161 -0
  29. package/extensions/dm-subagents/skills/dm-subagents/references/multi-lane-orchestration.md +51 -0
  30. package/extensions/dm-subagents/skills/dm-subagents/references/prompting-and-roles.md +295 -0
  31. package/extensions/dm-subagents/skills/dm-subagents/references/review-and-validation.md +73 -0
  32. package/extensions/dm-subagents/src/agents/agent-management.js +716 -484
  33. package/extensions/dm-subagents/src/agents/agent-refinements.js +563 -0
  34. package/extensions/dm-subagents/src/agents/agent-serializer.js +70 -5
  35. package/extensions/dm-subagents/src/agents/agents.js +1447 -299
  36. package/extensions/dm-subagents/src/agents/builtin-names.js +15 -0
  37. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -7
  38. package/extensions/dm-subagents/src/agents/frontmatter.js +64 -14
  39. package/extensions/dm-subagents/src/agents/identity.js +1 -1
  40. package/extensions/dm-subagents/src/agents/proactive-skills.js +14 -11
  41. package/extensions/dm-subagents/src/agents/runtime-agent-events.js +49 -0
  42. package/extensions/dm-subagents/src/agents/runtime-agent-registry.js +412 -0
  43. package/extensions/dm-subagents/src/agents/skills.js +52 -35
  44. package/extensions/dm-subagents/src/api/agents.js +6 -0
  45. package/extensions/dm-subagents/src/api/background-work.js +151 -0
  46. package/extensions/dm-subagents/src/api/capability-ceiling.js +12 -0
  47. package/extensions/dm-subagents/src/api/control-channel.js +3 -0
  48. package/extensions/dm-subagents/src/api/delegation.js +5 -0
  49. package/extensions/dm-subagents/src/api/dm-args.js +3 -0
  50. package/extensions/dm-subagents/src/api/external-job-provider.js +137 -0
  51. package/extensions/dm-subagents/src/api/external-runs.js +233 -0
  52. package/extensions/dm-subagents/src/api/intercom-bridge.js +3 -0
  53. package/extensions/dm-subagents/src/api/preflight.js +322 -0
  54. package/extensions/dm-subagents/src/api/project-panes.js +11 -0
  55. package/extensions/dm-subagents/src/api/shared-types.js +4 -0
  56. package/extensions/dm-subagents/src/extension/config.js +175 -0
  57. package/extensions/dm-subagents/src/extension/control-notices.js +4 -43
  58. package/extensions/dm-subagents/src/extension/doctor.js +71 -17
  59. package/extensions/dm-subagents/src/extension/fanout-child.js +49 -32
  60. package/extensions/dm-subagents/src/extension/index.js +711 -265
  61. package/extensions/dm-subagents/src/extension/public-execution.js +114 -0
  62. package/extensions/dm-subagents/src/extension/rpc.js +427 -22
  63. package/extensions/dm-subagents/src/extension/schemas.js +158 -65
  64. package/extensions/dm-subagents/src/extension/steering-notices.js +23 -0
  65. package/extensions/dm-subagents/src/extension/subagent-guide.js +31 -0
  66. package/extensions/dm-subagents/src/extension/tool-description.js +92 -74
  67. package/extensions/dm-subagents/src/extension/tool-result.js +7 -0
  68. package/extensions/dm-subagents/src/inspectors/herdr/actions.js +218 -0
  69. package/extensions/dm-subagents/src/inspectors/herdr/client.js +123 -0
  70. package/extensions/dm-subagents/src/inspectors/herdr/focus.js +47 -0
  71. package/extensions/dm-subagents/src/inspectors/herdr/inspector-runner.js +160 -0
  72. package/extensions/dm-subagents/src/inspectors/herdr/project-panes.js +618 -0
  73. package/extensions/dm-subagents/src/inspectors/herdr/session-roots-codec.js +21 -0
  74. package/extensions/dm-subagents/src/inspectors/herdr/shell-command.js +15 -0
  75. package/extensions/dm-subagents/src/integrations/herdr-status.js +377 -0
  76. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +17 -13
  77. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +371 -79
  78. package/extensions/dm-subagents/src/intercom/result-intercom.js +47 -7
  79. package/extensions/dm-subagents/src/missions/actions.js +394 -0
  80. package/extensions/dm-subagents/src/missions/goal-driver.js +149 -0
  81. package/extensions/dm-subagents/src/missions/lifecycle.js +331 -0
  82. package/extensions/dm-subagents/src/missions/store.js +548 -0
  83. package/extensions/dm-subagents/src/missions/types.js +9 -0
  84. package/extensions/dm-subagents/src/missions/workflow-state.js +245 -0
  85. package/extensions/dm-subagents/src/policy/authority.js +37 -0
  86. package/extensions/dm-subagents/src/profiles/profiles.js +36 -18
  87. package/extensions/dm-subagents/src/runs/background/active-async-capacity.js +427 -0
  88. package/extensions/dm-subagents/src/runs/background/active-run-index.js +122 -0
  89. package/extensions/dm-subagents/src/runs/background/async-execution.js +925 -137
  90. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +515 -148
  91. package/extensions/dm-subagents/src/runs/background/async-resume.js +378 -51
  92. package/extensions/dm-subagents/src/runs/background/async-retention.js +828 -0
  93. package/extensions/dm-subagents/src/runs/background/async-status-snapshot.js +31 -0
  94. package/extensions/dm-subagents/src/runs/background/async-status.js +259 -22
  95. package/extensions/dm-subagents/src/runs/background/auto-drain.js +46 -0
  96. package/extensions/dm-subagents/src/runs/background/chain-append.js +50 -15
  97. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +67 -12
  98. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +5 -1
  99. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +3 -11
  100. package/extensions/dm-subagents/src/runs/background/completion-replay.js +245 -0
  101. package/extensions/dm-subagents/src/runs/background/control-channel.js +423 -36
  102. package/extensions/dm-subagents/src/runs/background/fleet-view.js +132 -59
  103. package/extensions/dm-subagents/src/runs/background/index-segment.js +38 -0
  104. package/extensions/dm-subagents/src/runs/background/inspect-rpc.js +373 -0
  105. package/extensions/dm-subagents/src/runs/background/notify.js +357 -57
  106. package/extensions/dm-subagents/src/runs/background/owned-process-tree.js +86 -0
  107. package/extensions/dm-subagents/src/runs/background/process-terminal.js +269 -0
  108. package/extensions/dm-subagents/src/runs/background/result-delivery-ownership.js +34 -0
  109. package/extensions/dm-subagents/src/runs/background/result-files.js +469 -0
  110. package/extensions/dm-subagents/src/runs/background/result-watcher.js +540 -75
  111. package/extensions/dm-subagents/src/runs/background/resume-guidance.js +44 -0
  112. package/extensions/dm-subagents/src/runs/background/retained-children.js +119 -0
  113. package/extensions/dm-subagents/src/runs/background/run-id-query.js +5 -0
  114. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +93 -9
  115. package/extensions/dm-subagents/src/runs/background/run-status.js +303 -32
  116. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +784 -376
  117. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +75 -34
  118. package/extensions/dm-subagents/src/runs/background/steering.js +221 -0
  119. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +3106 -844
  120. package/extensions/dm-subagents/src/runs/background/subagent-wait.js +529 -0
  121. package/extensions/dm-subagents/src/runs/background/terminal-run-index.js +106 -0
  122. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -1
  123. package/extensions/dm-subagents/src/runs/background/wait-completions.js +155 -0
  124. package/extensions/dm-subagents/src/runs/background/wait-config.js +46 -0
  125. package/extensions/dm-subagents/src/runs/background/wait-subscriptions.js +278 -0
  126. package/extensions/dm-subagents/src/runs/background/wait-tool.js +47 -0
  127. package/extensions/dm-subagents/src/runs/foreground/async-dismiss-action.js +81 -0
  128. package/extensions/dm-subagents/src/runs/foreground/async-steering-action.js +245 -0
  129. package/extensions/dm-subagents/src/runs/foreground/async-stop-action.js +74 -0
  130. package/extensions/dm-subagents/src/runs/foreground/execution.js +1401 -342
  131. package/extensions/dm-subagents/src/runs/foreground/foreground-control.js +133 -0
  132. package/extensions/dm-subagents/src/runs/foreground/foreground-history.js +148 -0
  133. package/extensions/dm-subagents/src/runs/foreground/prompt-audit.js +139 -0
  134. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +4278 -1441
  135. package/extensions/dm-subagents/src/runs/foreground/workflow-detach-reconcile.js +278 -0
  136. package/extensions/dm-subagents/src/runs/foreground/workflow-foreground-steering.js +155 -0
  137. package/extensions/dm-subagents/src/runs/shared/abort-recovery.js +97 -0
  138. package/extensions/dm-subagents/src/runs/shared/acceptance.js +597 -148
  139. package/extensions/dm-subagents/src/runs/shared/agent-contract.js +35 -0
  140. package/extensions/dm-subagents/src/runs/shared/async-status-projection.js +472 -0
  141. package/extensions/dm-subagents/src/runs/shared/background-process-options.js +6 -0
  142. package/extensions/dm-subagents/src/runs/shared/capability-ceiling.js +175 -0
  143. package/extensions/dm-subagents/src/runs/shared/child-identity.js +32 -0
  144. package/extensions/dm-subagents/src/runs/shared/child-launch-plan.js +65 -0
  145. package/extensions/dm-subagents/src/runs/shared/child-protocol.js +447 -0
  146. package/extensions/dm-subagents/src/runs/shared/claude-code-adapter.js +120 -0
  147. package/extensions/dm-subagents/src/runs/shared/codex-exec-adapter.js +129 -0
  148. package/extensions/dm-subagents/src/runs/shared/completion-evidence.js +40 -0
  149. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +140 -83
  150. package/extensions/dm-subagents/src/runs/shared/context-mode.js +38 -0
  151. package/extensions/dm-subagents/src/runs/shared/cursor-agent-adapter.js +101 -0
  152. package/extensions/dm-subagents/src/runs/shared/dm-args.js +445 -72
  153. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +27 -16
  154. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +19 -6
  155. package/extensions/dm-subagents/src/runs/shared/extension-bindings.js +81 -0
  156. package/extensions/dm-subagents/src/runs/shared/external-cli-contract.js +134 -0
  157. package/extensions/dm-subagents/src/runs/shared/external-cli-preflight.js +98 -0
  158. package/extensions/dm-subagents/src/runs/shared/external-cli-runner.js +419 -0
  159. package/extensions/dm-subagents/src/runs/shared/external-job-bridge.js +404 -0
  160. package/extensions/dm-subagents/src/runs/shared/external-job-runner.js +334 -0
  161. package/extensions/dm-subagents/src/runs/shared/fast-mode-extension.js +8 -0
  162. package/extensions/dm-subagents/src/runs/shared/host-step-status.js +228 -0
  163. package/extensions/dm-subagents/src/runs/shared/lane-metadata.js +104 -0
  164. package/extensions/dm-subagents/src/runs/shared/launch-cwd.js +17 -0
  165. package/extensions/dm-subagents/src/runs/shared/llm-intent-arbiter.js +190 -0
  166. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +48 -3
  167. package/extensions/dm-subagents/src/runs/shared/mcp-config-sources.js +387 -0
  168. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +212 -137
  169. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-grant.js +131 -0
  170. package/extensions/dm-subagents/src/runs/shared/model-exclusions.js +207 -0
  171. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +225 -55
  172. package/extensions/dm-subagents/src/runs/shared/model-scope.js +85 -28
  173. package/extensions/dm-subagents/src/runs/shared/mutation-evidence.js +182 -0
  174. package/extensions/dm-subagents/src/runs/shared/nested-events.js +264 -102
  175. package/extensions/dm-subagents/src/runs/shared/nested-render.js +15 -5
  176. package/extensions/dm-subagents/src/runs/shared/orca-progress-tabs.js +505 -0
  177. package/extensions/dm-subagents/src/runs/shared/parallel-handoff.js +653 -0
  178. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +29 -12
  179. package/extensions/dm-subagents/src/runs/shared/permissions.js +108 -0
  180. package/extensions/dm-subagents/src/runs/shared/process-signal.js +13 -0
  181. package/extensions/dm-subagents/src/runs/shared/run-fanout-budget.js +257 -0
  182. package/extensions/dm-subagents/src/runs/shared/run-history.js +133 -13
  183. package/extensions/dm-subagents/src/runs/shared/runtime-acknowledged-extensions.js +62 -0
  184. package/extensions/dm-subagents/src/runs/shared/session-lease.js +225 -0
  185. package/extensions/dm-subagents/src/runs/shared/single-output.js +129 -27
  186. package/extensions/dm-subagents/src/runs/shared/spawn-budget.js +95 -0
  187. package/extensions/dm-subagents/src/runs/shared/structured-output.js +129 -10
  188. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +66 -11
  189. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +548 -70
  190. package/extensions/dm-subagents/src/runs/shared/subagent-startup-retry.js +50 -0
  191. package/extensions/dm-subagents/src/runs/shared/task-intent.js +130 -0
  192. package/extensions/dm-subagents/src/runs/shared/tool-availability.js +59 -0
  193. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +7 -5
  194. package/extensions/dm-subagents/src/runs/shared/tool-timeout.js +64 -0
  195. package/extensions/dm-subagents/src/runs/shared/usage-budget.js +74 -0
  196. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +22 -0
  197. package/extensions/dm-subagents/src/runs/shared/worktree-cleanup-plan.js +721 -0
  198. package/extensions/dm-subagents/src/runs/shared/worktree.js +168 -19
  199. package/extensions/dm-subagents/src/shared/accessible-dir.js +35 -0
  200. package/extensions/dm-subagents/src/shared/agent-stream-options.js +3 -0
  201. package/extensions/dm-subagents/src/shared/artifacts.js +170 -11
  202. package/extensions/dm-subagents/src/shared/atomic-json.js +36 -38
  203. package/extensions/dm-subagents/src/shared/capacity-resilient-json.js +77 -0
  204. package/extensions/dm-subagents/src/shared/child-session-name.js +15 -0
  205. package/extensions/dm-subagents/src/shared/child-transcript.js +57 -2
  206. package/extensions/dm-subagents/src/shared/completion-owner.js +7 -0
  207. package/extensions/dm-subagents/src/shared/display-text.js +142 -0
  208. package/extensions/dm-subagents/src/shared/extension-context.js +17 -0
  209. package/extensions/dm-subagents/src/shared/file-coalescer.js +9 -0
  210. package/extensions/dm-subagents/src/shared/file-system-retry.js +56 -0
  211. package/extensions/dm-subagents/src/shared/fork-context.js +96 -33
  212. package/extensions/dm-subagents/src/shared/formatters.js +21 -7
  213. package/extensions/dm-subagents/src/shared/launch-contract.js +94 -0
  214. package/extensions/dm-subagents/src/shared/model-info.js +10 -5
  215. package/extensions/dm-subagents/src/shared/node-executable.js +19 -0
  216. package/extensions/dm-subagents/src/shared/prompt-resources.js +10 -0
  217. package/extensions/dm-subagents/src/shared/pruned-fork.js +427 -0
  218. package/extensions/dm-subagents/src/shared/session-file-trust.js +19 -0
  219. package/extensions/dm-subagents/src/shared/session-tokens.js +14 -3
  220. package/extensions/dm-subagents/src/shared/settings.js +39 -47
  221. package/extensions/dm-subagents/src/shared/shortcuts.js +16 -0
  222. package/extensions/dm-subagents/src/shared/status-format.js +9 -2
  223. package/extensions/dm-subagents/src/shared/thinking-ceiling.js +41 -0
  224. package/extensions/dm-subagents/src/shared/types.js +47 -7
  225. package/extensions/dm-subagents/src/shared/utf8.js +12 -0
  226. package/extensions/dm-subagents/src/shared/utils.js +151 -131
  227. package/extensions/dm-subagents/src/shared/watch-strategy.js +3 -0
  228. package/extensions/dm-subagents/src/shared/workflow-child-permit.js +84 -0
  229. package/extensions/dm-subagents/src/slash/delegation-adapters.js +274 -0
  230. package/extensions/dm-subagents/src/slash/delegation-json.js +113 -0
  231. package/extensions/dm-subagents/src/slash/delegation-request.js +152 -0
  232. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +294 -243
  233. package/extensions/dm-subagents/src/slash/prompt-workflows.js +35 -73
  234. package/extensions/dm-subagents/src/slash/selector.js +101 -0
  235. package/extensions/dm-subagents/src/slash/slash-bridge.js +17 -1
  236. package/extensions/dm-subagents/src/slash/slash-commands.js +722 -732
  237. package/extensions/dm-subagents/src/slash/slash-live-state.js +37 -19
  238. package/extensions/dm-subagents/src/slash/subagents-admin.js +410 -0
  239. package/extensions/dm-subagents/src/tui/fleet-status.js +824 -0
  240. package/extensions/dm-subagents/src/tui/fleet-transcript.js +479 -0
  241. package/extensions/dm-subagents/src/tui/fleet.js +1326 -0
  242. package/extensions/dm-subagents/src/tui/render-helpers.js +22 -0
  243. package/extensions/dm-subagents/src/tui/render.js +1511 -261
  244. package/extensions/dm-subagents/src/watchdog/change-signature.js +220 -0
  245. package/extensions/dm-subagents/src/watchdog/child-status.js +151 -0
  246. package/extensions/dm-subagents/src/watchdog/emission-guard.js +90 -0
  247. package/extensions/dm-subagents/src/watchdog/lsp-diagnostics.js +484 -0
  248. package/extensions/dm-subagents/src/watchdog/model-selection.js +154 -0
  249. package/extensions/dm-subagents/src/watchdog/permission-arbiter.js +138 -0
  250. package/extensions/dm-subagents/src/watchdog/register-child.js +112 -0
  251. package/extensions/dm-subagents/src/watchdog/register-main.js +419 -0
  252. package/extensions/dm-subagents/src/watchdog/render.js +54 -0
  253. package/extensions/dm-subagents/src/watchdog/review.js +251 -0
  254. package/extensions/dm-subagents/src/watchdog/runtime.js +803 -0
  255. package/extensions/dm-subagents/src/watchdog/scope.js +56 -0
  256. package/extensions/dm-subagents/src/watchdog/settings.js +515 -0
  257. package/extensions/dm-subagents/src/watchdog/tool-actions.js +151 -0
  258. package/extensions/dm-subagents/src/watchdog/turn-delta.js +169 -0
  259. package/extensions/dm-subagents/src/watchdog/types.js +29 -0
  260. package/extensions/dm-subagents/src/watchdog/warning-format.js +58 -0
  261. package/extensions/dm-subagents/src/workflows/chat-progress.js +116 -0
  262. package/extensions/dm-subagents/src/workflows/host-command.js +227 -0
  263. package/extensions/dm-subagents/src/workflows/scripted-workflow.js +2011 -0
  264. package/extensions/dm-subagents/src/workflows/workflow-child-summary.js +116 -0
  265. package/extensions/dm-subagents/src/workflows/workflow-preflight.js +243 -0
  266. package/extensions/dm-subagents/src/workflows/workflow-receipt.js +387 -0
  267. package/extensions/dm-subagents/src/workflows/workflow-settlement.js +189 -0
  268. package/package.json +4 -3
  269. package/extensions/dm-fff/package.json +0 -21
  270. package/extensions/dm-fff/src/index.js +0 -691
  271. package/extensions/dm-fff/src/query.js +0 -60
  272. package/extensions/dm-subagents/agents/context-builder.md +0 -46
  273. package/extensions/dm-subagents/agents/planner.md +0 -55
  274. package/extensions/dm-subagents/prompts/parallel-context-build.md +0 -55
  275. package/extensions/dm-subagents/prompts/parallel-handoff-plan.md +0 -61
  276. package/extensions/dm-subagents/src/runs/background/wait.js +0 -206
  277. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +0 -1013
  278. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +0 -981
  279. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +0 -50
@@ -0,0 +1,548 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as os from "node:os";
4
+ import * as path from "node:path";
5
+ import { writePrivateAtomicJson } from "../shared/atomic-json.js";
6
+ import { getAgentDir } from "../shared/utils.js";
7
+ import {
8
+ MISSION_STATUSES
9
+ } from "./types.js";
10
+ const MISSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
11
+ const MISSION_RUN_MODES = new Set(["single", "parallel", "chain", "workflow", "scheduled", "external"]);
12
+ const MISSION_ARTIFACT_KINDS = new Set(["status", "output", "patch", "manifest", "review", "note", "other"]);
13
+ const MISSION_RECEIPT_KINDS = new Set(["pull_request", "ci", "deployment", "release"]);
14
+ const MISSION_RECEIPT_STATUSES = new Set(["pending", "ready", "succeeded", "failed"]);
15
+ const MISSION_STATUS_SET = new Set(MISSION_STATUSES);
16
+ const TERMINAL_MISSION_STATUSES = new Set(["completed", "failed", "cancelled"]);
17
+ const DEFAULT_TERMINAL_MISSION_RETENTION = 200;
18
+ function asObject(value, label) {
19
+ if (!value || typeof value !== "object" || Array.isArray(value))
20
+ throw new Error(`${label} must be a JSON object`);
21
+ return value;
22
+ }
23
+ function requiredString(value, label) {
24
+ if (typeof value !== "string" || !value.trim())
25
+ throw new Error(`${label} must be a non-empty string`);
26
+ return value;
27
+ }
28
+ function optionalString(value, label) {
29
+ if (value === undefined)
30
+ return;
31
+ return requiredString(value, label);
32
+ }
33
+ function timestamp(value, label) {
34
+ const result = requiredString(value, label);
35
+ if (Number.isNaN(Date.parse(result)))
36
+ throw new Error(`${label} must be an ISO timestamp`);
37
+ return result;
38
+ }
39
+ function missionStatus(value, label) {
40
+ if (typeof value !== "string" || !MISSION_STATUS_SET.has(value)) {
41
+ throw new Error(`${label} must be one of ${MISSION_STATUSES.join(", ")}`);
42
+ }
43
+ return value;
44
+ }
45
+ function positiveTokenCount(value, label) {
46
+ if (!Number.isSafeInteger(value) || value < 1)
47
+ throw new Error(`${label} must be a positive integer`);
48
+ return value;
49
+ }
50
+ function nonNegativeTokenCount(value, label) {
51
+ if (!Number.isSafeInteger(value) || value < 0)
52
+ throw new Error(`${label} must be a non-negative integer`);
53
+ return value;
54
+ }
55
+ function parseGoal(value, label) {
56
+ const input = asObject(value, label);
57
+ if (input.status !== "active" && input.status !== "paused" && input.status !== "budget-exhausted")
58
+ throw new Error(`${label}.status is invalid`);
59
+ return { status: input.status };
60
+ }
61
+ function parseBudget(value, label) {
62
+ const input = asObject(value, label);
63
+ return { tokens: positiveTokenCount(input.tokens, `${label}.tokens`) };
64
+ }
65
+ function parseUsage(value, label) {
66
+ const input = asObject(value, label);
67
+ return { tokens: nonNegativeTokenCount(input.tokens, `${label}.tokens`) };
68
+ }
69
+ function stringArray(value, label) {
70
+ if (!Array.isArray(value))
71
+ throw new Error(`${label} must be an array of non-empty strings`);
72
+ const result = value.map((item, index) => requiredString(item, `${label}[${index}]`).trim());
73
+ return [...new Set(result)];
74
+ }
75
+ export function validateMissionId(value, label = "missionId") {
76
+ const id = requiredString(value, label);
77
+ if (!MISSION_ID_PATTERN.test(id) || id.includes("..")) {
78
+ throw new Error(`${label} must contain only letters, numbers, '.', '_', or '-' and cannot contain '..'`);
79
+ }
80
+ return id;
81
+ }
82
+ function parseRunLink(value, label) {
83
+ const input = asObject(value, label);
84
+ const runId = requiredString(input.runId, `${label}.runId`);
85
+ const mode = requiredString(input.mode, `${label}.mode`);
86
+ if (!MISSION_RUN_MODES.has(mode))
87
+ throw new Error(`${label}.mode is invalid`);
88
+ if (input.childIndex !== undefined && (!Number.isInteger(input.childIndex) || input.childIndex < 0)) {
89
+ throw new Error(`${label}.childIndex must be a non-negative integer`);
90
+ }
91
+ return {
92
+ runId,
93
+ mode,
94
+ ...optionalString(input.asyncDir, `${label}.asyncDir`) ? { asyncDir: input.asyncDir } : {},
95
+ ...input.childIndex !== undefined ? { childIndex: input.childIndex } : {},
96
+ ...optionalString(input.agent, `${label}.agent`) ? { agent: input.agent } : {},
97
+ ...optionalString(input.status, `${label}.status`) ? { status: input.status } : {},
98
+ ...input.startedAt !== undefined ? { startedAt: timestamp(input.startedAt, `${label}.startedAt`) } : {},
99
+ ...input.completedAt !== undefined ? { completedAt: timestamp(input.completedAt, `${label}.completedAt`) } : {},
100
+ ...input.usage !== undefined ? { usage: parseUsage(input.usage, `${label}.usage`) } : {}
101
+ };
102
+ }
103
+ function parseDecision(value, label) {
104
+ const input = asObject(value, label);
105
+ const status = input.status;
106
+ if (status !== "open" && status !== "resolved")
107
+ throw new Error(`${label}.status must be "open" or "resolved"`);
108
+ return {
109
+ id: validateMissionId(input.id, `${label}.id`),
110
+ status,
111
+ title: requiredString(input.title, `${label}.title`),
112
+ createdAt: timestamp(input.createdAt, `${label}.createdAt`),
113
+ ...optionalString(input.prompt, `${label}.prompt`) ? { prompt: input.prompt } : {},
114
+ ...input.options !== undefined ? { options: stringArray(input.options, `${label}.options`) } : {},
115
+ ...optionalString(input.recommendation, `${label}.recommendation`) ? { recommendation: input.recommendation } : {},
116
+ ...input.resolvedAt !== undefined ? { resolvedAt: timestamp(input.resolvedAt, `${label}.resolvedAt`) } : {},
117
+ ...optionalString(input.resolution, `${label}.resolution`) ? { resolution: input.resolution } : {}
118
+ };
119
+ }
120
+ function parseWorkflowChild(value, label) {
121
+ const input = asObject(value, label);
122
+ const artifactPaths = input.artifactPaths === undefined ? [] : stringArray(input.artifactPaths, `${label}.artifactPaths`);
123
+ const heartbeat = input.heartbeat === undefined ? undefined : asObject(input.heartbeat, `${label}.heartbeat`);
124
+ return {
125
+ workflowRunId: requiredString(input.workflowRunId, `${label}.workflowRunId`),
126
+ key: validateMissionId(input.key, `${label}.key`),
127
+ status: requiredString(input.status, `${label}.status`),
128
+ startedAt: timestamp(input.startedAt, `${label}.startedAt`),
129
+ updatedAt: timestamp(input.updatedAt, `${label}.updatedAt`),
130
+ artifactPaths,
131
+ ...optionalString(input.runId, `${label}.runId`) ? { runId: input.runId } : {},
132
+ ...optionalString(input.agent, `${label}.agent`) ? { agent: input.agent } : {},
133
+ ...optionalString(input.task, `${label}.task`) ? { task: input.task } : {},
134
+ ...optionalString(input.label, `${label}.label`) ? { label: input.label } : {},
135
+ ...optionalString(input.phase, `${label}.phase`) ? { phase: input.phase } : {},
136
+ ...input.completedAt !== undefined ? { completedAt: timestamp(input.completedAt, `${label}.completedAt`) } : {},
137
+ ...optionalString(input.sessionPath, `${label}.sessionPath`) ? { sessionPath: input.sessionPath } : {},
138
+ ...heartbeat ? { heartbeat: {
139
+ updatedAt: timestamp(heartbeat.updatedAt, `${label}.heartbeat.updatedAt`),
140
+ ...optionalString(heartbeat.status, `${label}.heartbeat.status`) ? { status: heartbeat.status } : {},
141
+ ...optionalString(heartbeat.phase, `${label}.heartbeat.phase`) ? { phase: heartbeat.phase } : {},
142
+ ...optionalString(heartbeat.message, `${label}.heartbeat.message`) ? { message: heartbeat.message } : {}
143
+ } } : {}
144
+ };
145
+ }
146
+ function parseArtifact(value, label) {
147
+ const input = asObject(value, label);
148
+ const kind = requiredString(input.kind, `${label}.kind`);
149
+ if (!MISSION_ARTIFACT_KINDS.has(kind))
150
+ throw new Error(`${label}.kind is invalid`);
151
+ return {
152
+ kind,
153
+ path: requiredString(input.path, `${label}.path`),
154
+ ...optionalString(input.description, `${label}.description`) ? { description: input.description } : {}
155
+ };
156
+ }
157
+ function parseReceipt(value, label) {
158
+ const input = asObject(value, label);
159
+ const kind = requiredString(input.kind, `${label}.kind`);
160
+ const status = requiredString(input.status, `${label}.status`);
161
+ if (!MISSION_RECEIPT_KINDS.has(kind))
162
+ throw new Error(`${label}.kind is invalid`);
163
+ if (!MISSION_RECEIPT_STATUSES.has(status))
164
+ throw new Error(`${label}.status is invalid`);
165
+ const url = requiredString(input.url, `${label}.url`);
166
+ try {
167
+ new URL(url);
168
+ } catch {
169
+ throw new Error(`${label}.url must be an absolute URL`);
170
+ }
171
+ return {
172
+ kind,
173
+ status,
174
+ title: requiredString(input.title, `${label}.title`),
175
+ url,
176
+ createdAt: timestamp(input.createdAt, `${label}.createdAt`),
177
+ ...optionalString(input.description, `${label}.description`) ? { description: input.description } : {}
178
+ };
179
+ }
180
+ export function parseMissionRecord(value, source = "mission record") {
181
+ const input = asObject(value, source);
182
+ if (input.schemaVersion !== 1)
183
+ throw new Error(`${source}.schemaVersion must be 1`);
184
+ if (!Array.isArray(input.runs))
185
+ throw new Error(`${source}.runs must be an array`);
186
+ if (input.workflowChildren !== undefined && !Array.isArray(input.workflowChildren))
187
+ throw new Error(`${source}.workflowChildren must be an array`);
188
+ if (!Array.isArray(input.decisions))
189
+ throw new Error(`${source}.decisions must be an array`);
190
+ if (!Array.isArray(input.artifacts))
191
+ throw new Error(`${source}.artifacts must be an array`);
192
+ if (input.receipts !== undefined && !Array.isArray(input.receipts))
193
+ throw new Error(`${source}.receipts must be an array`);
194
+ const runs = input.runs;
195
+ const workflowChildren = input.workflowChildren ?? [];
196
+ const decisions = input.decisions;
197
+ const artifacts = input.artifacts;
198
+ const receipts = input.receipts ?? [];
199
+ const goal = input.goal !== undefined ? parseGoal(input.goal, `${source}.goal`) : undefined;
200
+ const budget = input.budget !== undefined ? parseBudget(input.budget, `${source}.budget`) : undefined;
201
+ const usage = input.usage !== undefined ? parseUsage(input.usage, `${source}.usage`) : undefined;
202
+ const objective = optionalString(input.objective, `${source}.objective`)?.trim();
203
+ if (!objective)
204
+ throw new Error(`${source}.objective must be a non-empty string`);
205
+ if (goal && !budget)
206
+ throw new Error(`${source}.budget is required for a goal mission`);
207
+ return {
208
+ schemaVersion: 1,
209
+ id: validateMissionId(input.id, `${source}.id`),
210
+ title: requiredString(input.title, `${source}.title`),
211
+ objective,
212
+ ...goal ? { goal } : {},
213
+ ...budget ? { budget } : {},
214
+ ...usage ? { usage } : {},
215
+ status: missionStatus(input.status, `${source}.status`),
216
+ createdAt: timestamp(input.createdAt, `${source}.createdAt`),
217
+ updatedAt: timestamp(input.updatedAt, `${source}.updatedAt`),
218
+ runs: runs.map((item, index) => parseRunLink(item, `${source}.runs[${index}]`)),
219
+ workflowChildren: workflowChildren.map((item, index) => parseWorkflowChild(item, `${source}.workflowChildren[${index}]`)),
220
+ decisions: decisions.map((item, index) => parseDecision(item, `${source}.decisions[${index}]`)),
221
+ artifacts: artifacts.map((item, index) => parseArtifact(item, `${source}.artifacts[${index}]`)),
222
+ receipts: receipts.map((item, index) => parseReceipt(item, `${source}.receipts[${index}]`)),
223
+ ...optionalString(input.cwd, `${source}.cwd`) ? { cwd: input.cwd } : {},
224
+ ...optionalString(input.ownerSessionId, `${source}.ownerSessionId`) ? { ownerSessionId: input.ownerSessionId } : {},
225
+ ...optionalString(input.summary, `${source}.summary`) ? { summary: input.summary } : {},
226
+ ...input.acceptance !== undefined ? { acceptance: input.acceptance } : {},
227
+ ...input.labels !== undefined ? { labels: stringArray(input.labels, `${source}.labels`) } : {}
228
+ };
229
+ }
230
+ function expandConfiguredPath(value, projectRoot) {
231
+ const expanded = value.startsWith("~/") ? path.join(os.homedir(), value.slice(2)) : value;
232
+ return path.isAbsolute(expanded) ? path.normalize(expanded) : path.resolve(projectRoot, expanded);
233
+ }
234
+ function projectMissionDirectory(agentDir, projectRoot) {
235
+ const projectKey = createHash("sha256").update(projectRoot).digest("hex");
236
+ return path.join(agentDir, "missions", "projects", projectKey);
237
+ }
238
+ export function validateMissionStoreConfig(value, label = "config.missions") {
239
+ if (value === undefined)
240
+ return;
241
+ const input = asObject(value, label);
242
+ for (const key of Object.keys(input)) {
243
+ if (key !== "enabled" && key !== "directory" && key !== "globalIndex" && key !== "globalIndexDir" && key !== "retainTerminal") {
244
+ throw new Error(`${label}.${key} is unknown`);
245
+ }
246
+ }
247
+ if (input.enabled !== undefined && typeof input.enabled !== "boolean")
248
+ throw new Error(`${label}.enabled must be boolean`);
249
+ if (input.globalIndex !== undefined && typeof input.globalIndex !== "boolean")
250
+ throw new Error(`${label}.globalIndex must be boolean`);
251
+ if (input.retainTerminal !== undefined && (!Number.isInteger(input.retainTerminal) || input.retainTerminal < 1)) {
252
+ throw new Error(`${label}.retainTerminal must be a positive integer`);
253
+ }
254
+ const directory = optionalString(input.directory, `${label}.directory`);
255
+ const globalIndexDir = optionalString(input.globalIndexDir, `${label}.globalIndexDir`);
256
+ return {
257
+ ...typeof input.enabled === "boolean" ? { enabled: input.enabled } : {},
258
+ ...directory ? { directory } : {},
259
+ ...typeof input.globalIndex === "boolean" ? { globalIndex: input.globalIndex } : {},
260
+ ...globalIndexDir ? { globalIndexDir } : {},
261
+ ...input.retainTerminal !== undefined ? { retainTerminal: input.retainTerminal } : {}
262
+ };
263
+ }
264
+ export function resolveMissionStoreLocation(input) {
265
+ const projectRoot = path.resolve(input.projectRoot);
266
+ const agentDir = input.agentDir ?? getAgentDir();
267
+ const missionDir = input.config?.directory ? expandConfiguredPath(input.config.directory, projectRoot) : projectMissionDirectory(agentDir, projectRoot);
268
+ const globalIndexDir = input.config?.globalIndexDir ? expandConfiguredPath(input.config.globalIndexDir, projectRoot) : path.join(agentDir, "missions", "index");
269
+ return {
270
+ projectRoot,
271
+ missionDir,
272
+ globalIndexDir,
273
+ writeGlobalIndex: input.config?.globalIndex !== false,
274
+ ...input.config?.retainTerminal !== undefined ? { retainTerminal: input.config.retainTerminal } : {}
275
+ };
276
+ }
277
+ export function missionRecordPath(location, missionId) {
278
+ return path.join(location.missionDir, `${validateMissionId(missionId)}.json`);
279
+ }
280
+ function parseIndexEntry(value, source) {
281
+ const input = asObject(value, source);
282
+ if (input.schemaVersion !== 1)
283
+ throw new Error(`${source}.schemaVersion must be 1`);
284
+ return {
285
+ schemaVersion: 1,
286
+ missionId: validateMissionId(input.missionId, `${source}.missionId`),
287
+ projectRoot: requiredString(input.projectRoot, `${source}.projectRoot`),
288
+ recordPath: requiredString(input.recordPath, `${source}.recordPath`),
289
+ title: requiredString(input.title, `${source}.title`),
290
+ status: missionStatus(input.status, `${source}.status`),
291
+ updatedAt: timestamp(input.updatedAt, `${source}.updatedAt`),
292
+ ...optionalString(input.lastRunId, `${source}.lastRunId`) ? { lastRunId: input.lastRunId } : {}
293
+ };
294
+ }
295
+ function indexPath(location, record) {
296
+ const key = createHash("sha256").update(`${location.projectRoot}\x00${record.id}`).digest("hex");
297
+ return path.join(location.globalIndexDir, `${key}.json`);
298
+ }
299
+ function writeMission(location, record) {
300
+ const validated = parseMissionRecord(record);
301
+ writePrivateAtomicJson(missionRecordPath(location, validated.id), validated);
302
+ if (location.writeGlobalIndex) {
303
+ const lastRunId = validated.runs.at(-1)?.runId;
304
+ const entry = {
305
+ schemaVersion: 1,
306
+ missionId: validated.id,
307
+ projectRoot: location.projectRoot,
308
+ recordPath: missionRecordPath(location, validated.id),
309
+ title: validated.title,
310
+ status: validated.status,
311
+ updatedAt: validated.updatedAt,
312
+ ...lastRunId ? { lastRunId } : {}
313
+ };
314
+ writePrivateAtomicJson(indexPath(location, validated), entry);
315
+ }
316
+ return validated;
317
+ }
318
+ function pruneTerminalMissions(location, maxTerminal) {
319
+ const terminal = listMissions(location).records.filter((record) => TERMINAL_MISSION_STATUSES.has(record.status)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
320
+ for (const record of terminal.slice(maxTerminal)) {
321
+ try {
322
+ fs.rmSync(missionRecordPath(location, record.id), { force: true });
323
+ fs.rmSync(path.join(location.missionDir, record.id), { recursive: true, force: true });
324
+ if (location.writeGlobalIndex)
325
+ fs.rmSync(indexPath(location, record), { force: true });
326
+ } catch {}
327
+ }
328
+ }
329
+ export function createMission(location, input, now = new Date, retainTerminal = location.retainTerminal ?? DEFAULT_TERMINAL_MISSION_RETENTION) {
330
+ const createdAt = now.toISOString();
331
+ const record = {
332
+ schemaVersion: 1,
333
+ id: randomUUID(),
334
+ title: requiredString(input.title, "mission.title").trim(),
335
+ objective: requiredString(input.objective, "mission.objective").trim(),
336
+ ...input.goal === true ? { goal: { status: "active" } } : {},
337
+ ...input.budget ? { budget: parseBudget(input.budget, "mission.budget") } : {},
338
+ ...input.goal === true ? { usage: { tokens: 0 } } : {},
339
+ status: input.status ?? "planned",
340
+ createdAt,
341
+ updatedAt: createdAt,
342
+ cwd: location.projectRoot,
343
+ runs: [],
344
+ workflowChildren: [],
345
+ decisions: [],
346
+ artifacts: [],
347
+ receipts: [],
348
+ ...input.ownerSessionId ? { ownerSessionId: requiredString(input.ownerSessionId, "mission.ownerSessionId") } : {},
349
+ ...input.labels ? { labels: stringArray(input.labels, "mission.labels") } : {}
350
+ };
351
+ if (input.goal === true && !input.budget)
352
+ throw new Error("mission.budget is required when mission.goal is true");
353
+ const created = writeMission(location, record);
354
+ pruneTerminalMissions(location, retainTerminal);
355
+ return created;
356
+ }
357
+
358
+ export class MissionNotFoundError extends Error {
359
+ code = "MISSION_NOT_FOUND";
360
+ missionId;
361
+ missionDir;
362
+ constructor(missionId, location) {
363
+ super(`Mission '${missionId}' was not found in mission directory '${location.missionDir}' for project root '${location.projectRoot}'. If it was created in another worktree, run the request from that worktree.`);
364
+ this.name = "MissionNotFoundError";
365
+ this.missionId = missionId;
366
+ this.missionDir = location.missionDir;
367
+ }
368
+ }
369
+ export function readMission(location, missionId) {
370
+ const filePath = missionRecordPath(location, missionId);
371
+ let raw;
372
+ try {
373
+ raw = fs.readFileSync(filePath, "utf-8");
374
+ } catch (error) {
375
+ if (error.code === "ENOENT")
376
+ throw new MissionNotFoundError(missionId, location);
377
+ throw error;
378
+ }
379
+ try {
380
+ return parseMissionRecord(JSON.parse(raw), filePath);
381
+ } catch (error) {
382
+ throw new Error(`Invalid mission file '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
383
+ }
384
+ }
385
+ export function listMissions(location) {
386
+ if (!fs.existsSync(location.missionDir))
387
+ return { records: [], warnings: [] };
388
+ const records = [];
389
+ const warnings = [];
390
+ for (const name of fs.readdirSync(location.missionDir).filter((item) => item.endsWith(".json")).sort()) {
391
+ const filePath = path.join(location.missionDir, name);
392
+ try {
393
+ records.push(parseMissionRecord(JSON.parse(fs.readFileSync(filePath, "utf-8")), filePath));
394
+ } catch (error) {
395
+ warnings.push(`Skipped corrupt mission '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
396
+ }
397
+ }
398
+ records.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
399
+ return { records, warnings };
400
+ }
401
+ export function updateMission(location, missionId, update, now = new Date, retainTerminal = location.retainTerminal ?? DEFAULT_TERMINAL_MISSION_RETENTION) {
402
+ const current = readMission(location, missionId);
403
+ const runs = [...current.runs];
404
+ for (const candidate of update.addRuns ?? []) {
405
+ const run = parseRunLink(candidate, "mission.update.addRuns[]");
406
+ const existingIndex = runs.findIndex((item) => item.runId === run.runId && item.childIndex === run.childIndex);
407
+ if (existingIndex === -1)
408
+ runs.push(run);
409
+ else
410
+ runs[existingIndex] = { ...runs[existingIndex], ...run };
411
+ }
412
+ const workflowChildren = [...current.workflowChildren];
413
+ for (const candidate of update.upsertWorkflowChildren ?? []) {
414
+ const nowIso = now.toISOString();
415
+ const parsed = parseWorkflowChild({
416
+ ...candidate,
417
+ startedAt: candidate.startedAt ?? nowIso,
418
+ updatedAt: nowIso,
419
+ artifactPaths: candidate.artifactPaths ?? [],
420
+ ...candidate.heartbeat ? { heartbeat: { ...candidate.heartbeat, updatedAt: nowIso } } : {}
421
+ }, "mission.update.upsertWorkflowChildren[]");
422
+ const existingIndex = workflowChildren.findIndex((child) => child.workflowRunId === parsed.workflowRunId && child.key === parsed.key);
423
+ if (existingIndex === -1)
424
+ workflowChildren.push(parsed);
425
+ else {
426
+ const existing = workflowChildren[existingIndex];
427
+ workflowChildren[existingIndex] = parseWorkflowChild({
428
+ ...existing,
429
+ ...parsed,
430
+ startedAt: existing.startedAt,
431
+ artifactPaths: [...new Set([...existing.artifactPaths, ...parsed.artifactPaths])]
432
+ }, "mission.update.upsertWorkflowChildren[]");
433
+ }
434
+ }
435
+ const artifacts = [...current.artifacts];
436
+ for (const candidate of update.addArtifacts ?? []) {
437
+ const artifact = parseArtifact(candidate, "mission.update.addArtifacts[]");
438
+ const existingIndex = artifacts.findIndex((item) => item.kind === artifact.kind && path.resolve(item.path) === path.resolve(artifact.path));
439
+ if (existingIndex === -1)
440
+ artifacts.push(artifact);
441
+ else
442
+ artifacts[existingIndex] = { ...artifacts[existingIndex], ...artifact };
443
+ }
444
+ const createdAt = now.toISOString();
445
+ const receipts = [...current.receipts];
446
+ for (const candidate of update.addReceipts ?? []) {
447
+ const receipt = parseReceipt({ ...candidate, createdAt }, "mission.update.addReceipts[]");
448
+ const existingIndex = receipts.findIndex((item) => item.kind === receipt.kind && item.url === receipt.url);
449
+ if (existingIndex === -1)
450
+ receipts.push(receipt);
451
+ else
452
+ receipts[existingIndex] = { ...receipt, createdAt: receipts[existingIndex].createdAt };
453
+ }
454
+ const decisions = [
455
+ ...current.decisions,
456
+ ...(update.addDecisions ?? []).map((decision) => ({
457
+ id: randomUUID(),
458
+ status: "open",
459
+ title: requiredString(decision.title, "mission.update.addDecisions[].title"),
460
+ createdAt,
461
+ ...decision.prompt ? { prompt: requiredString(decision.prompt, "mission.update.addDecisions[].prompt") } : {},
462
+ ...decision.options ? { options: stringArray(decision.options, "mission.update.addDecisions[].options") } : {},
463
+ ...decision.recommendation ? { recommendation: requiredString(decision.recommendation, "mission.update.addDecisions[].recommendation") } : {}
464
+ }))
465
+ ];
466
+ if (update.resolveDecision) {
467
+ const decisionId = validateMissionId(update.resolveDecision.id, "mission.update.resolveDecision.id");
468
+ const decisionIndex = decisions.findIndex((decision) => decision.id === decisionId);
469
+ if (decisionIndex === -1)
470
+ throw new Error(`Decision '${decisionId}' was not found in mission '${missionId}'`);
471
+ if (decisions[decisionIndex].status === "resolved")
472
+ throw new Error(`Decision '${decisionId}' is already resolved`);
473
+ decisions[decisionIndex] = {
474
+ ...decisions[decisionIndex],
475
+ status: "resolved",
476
+ resolvedAt: createdAt,
477
+ resolution: requiredString(update.resolveDecision.resolution, "mission.update.resolveDecision.resolution").trim()
478
+ };
479
+ }
480
+ const budget = update.budget !== undefined ? parseBudget(update.budget, "mission.update.budget") : current.budget;
481
+ const usage = update.usage !== undefined ? parseUsage(update.usage, "mission.update.usage") : { tokens: runs.reduce((total, run) => total + (run.usage?.tokens ?? 0), 0) };
482
+ let goal = update.goal === false ? undefined : update.goal !== undefined ? parseGoal(update.goal, "mission.update.goal") : current.goal;
483
+ if (goal && !budget)
484
+ throw new Error("mission.update.budget is required when enabling a goal mission");
485
+ if (goal && budget) {
486
+ goal = usage.tokens >= budget.tokens ? { status: "budget-exhausted" } : goal.status === "budget-exhausted" ? { status: "active" } : goal;
487
+ }
488
+ const hasOpenDecisions = decisions.some((decision) => decision.status === "open");
489
+ const requestedStatus = update.status !== undefined ? missionStatus(update.status, "mission.update.status") : undefined;
490
+ const candidateStatus = requestedStatus ?? (update.addDecisions?.length && current.status === "active" ? "needs_decision" : update.resolveDecision && current.status === "needs_decision" && !hasOpenDecisions ? "active" : current.status);
491
+ const decisionStatus = hasOpenDecisions && (candidateStatus === "active" || candidateStatus === "completed") ? "needs_decision" : candidateStatus;
492
+ const next = {
493
+ ...current,
494
+ updatedAt: createdAt,
495
+ runs,
496
+ workflowChildren,
497
+ artifacts,
498
+ receipts,
499
+ decisions,
500
+ ...update.title !== undefined ? { title: requiredString(update.title, "mission.update.title").trim() } : {},
501
+ ...update.objective !== undefined ? { objective: requiredString(update.objective, "mission.update.objective").trim() } : {},
502
+ ...budget ? { budget } : {},
503
+ ...goal ? { goal, usage } : {},
504
+ status: decisionStatus,
505
+ ...update.summary !== undefined ? { summary: requiredString(update.summary, "mission.update.summary") } : {},
506
+ ...update.labels !== undefined ? { labels: stringArray(update.labels, "mission.update.labels") } : {},
507
+ ...update.acceptance !== undefined ? { acceptance: update.acceptance } : {}
508
+ };
509
+ if (!goal)
510
+ delete next.goal;
511
+ const updated = writeMission(location, next);
512
+ if (TERMINAL_MISSION_STATUSES.has(updated.status))
513
+ pruneTerminalMissions(location, retainTerminal);
514
+ return updated;
515
+ }
516
+ export function listGlobalMissions(globalIndexDir) {
517
+ if (!fs.existsSync(globalIndexDir))
518
+ return { entries: [], warnings: [] };
519
+ const entries = [];
520
+ const warnings = [];
521
+ for (const name of fs.readdirSync(globalIndexDir).filter((item) => item.endsWith(".json")).sort()) {
522
+ const filePath = path.join(globalIndexDir, name);
523
+ try {
524
+ const entry = parseIndexEntry(JSON.parse(fs.readFileSync(filePath, "utf-8")), filePath);
525
+ try {
526
+ const record = parseMissionRecord(JSON.parse(fs.readFileSync(entry.recordPath, "utf-8")), entry.recordPath);
527
+ if (record.id !== entry.missionId)
528
+ throw new Error(`record id '${record.id}' does not match index id '${entry.missionId}'`);
529
+ entries.push({ ...entry, stale: false });
530
+ } catch (error) {
531
+ if (error.code === "ENOENT") {
532
+ try {
533
+ fs.rmSync(filePath, { force: true });
534
+ warnings.push(`Removed stale global mission pointer '${filePath}' because '${entry.recordPath}' no longer exists.`);
535
+ } catch (removeError) {
536
+ warnings.push(`Failed to remove stale global mission pointer '${filePath}': ${removeError instanceof Error ? removeError.message : String(removeError)}`);
537
+ }
538
+ continue;
539
+ }
540
+ entries.push({ ...entry, stale: true, staleReason: error instanceof Error ? error.message : String(error) });
541
+ }
542
+ } catch (error) {
543
+ warnings.push(`Skipped corrupt global mission index entry '${filePath}': ${error instanceof Error ? error.message : String(error)}`);
544
+ }
545
+ }
546
+ entries.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
547
+ return { entries, warnings };
548
+ }
@@ -0,0 +1,9 @@
1
+ export const MISSION_STATUSES = [
2
+ "planned",
3
+ "active",
4
+ "waiting",
5
+ "needs_decision",
6
+ "completed",
7
+ "failed",
8
+ "cancelled"
9
+ ];