@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,427 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { writePrivateAtomicJson } from "../../shared/atomic-json.js";
5
+ import { TEMP_ROOT_DIR } from "../../shared/types.js";
6
+ import { readStatus } from "../../shared/utils.js";
7
+ import { checkPidLiveness } from "./stale-run-reconciler.js";
8
+ import { readProcessTerminal } from "./process-terminal.js";
9
+ export const ACTIVE_ASYNC_CAPACITY_DIR = path.join(TEMP_ROOT_DIR, "session-active-async-capacity");
10
+ export const DEFAULT_ABANDONED_SLOT_RELEASE_AFTER_MS = 20 * 60 * 1000;
11
+ export const MIN_ABANDONED_SLOT_RELEASE_AFTER_MS = 5 * 60 * 1000;
12
+ export const MAX_ABANDONED_SLOT_RELEASE_AFTER_MS = 24 * 60 * 60 * 1000;
13
+
14
+ export class ActiveAsyncCapacityError extends Error {
15
+ snapshot;
16
+ constructor(snapshot) {
17
+ super(`Active async run capacity exhausted: ${snapshot.used}/${snapshot.limit} used.`);
18
+ this.name = "ActiveAsyncCapacityError";
19
+ this.snapshot = snapshot;
20
+ }
21
+ }
22
+ export function resolveMaxActiveAsyncRunsPerSession(value) {
23
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0)
24
+ return;
25
+ return value === 0 ? undefined : value;
26
+ }
27
+ export function resolveAbandonedSlotReleaseAfterMs(value) {
28
+ if (value === false)
29
+ return false;
30
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1)
31
+ return DEFAULT_ABANDONED_SLOT_RELEASE_AFTER_MS;
32
+ return value;
33
+ }
34
+ export function activeAsyncCapacitySessionKey(sessionId) {
35
+ return createHash("sha256").update(sessionId).digest("hex");
36
+ }
37
+ function sessionDir(sessionId, rootDir) {
38
+ return path.join(rootDir, activeAsyncCapacitySessionKey(sessionId));
39
+ }
40
+ function slotDir(poolDir, slot) {
41
+ return path.join(poolDir, `slot-${slot}`);
42
+ }
43
+ function parseOwner(value) {
44
+ if (!value || typeof value !== "object" || Array.isArray(value))
45
+ return;
46
+ const owner = value;
47
+ if (owner.version !== 1 || typeof owner.reservationToken !== "string" || !owner.reservationToken || typeof owner.ownerSessionId !== "string" || !owner.ownerSessionId || typeof owner.ownerSessionKey !== "string" || !owner.ownerSessionKey || typeof owner.slot !== "number" || !Number.isInteger(owner.slot) || owner.slot < 0 || typeof owner.runId !== "string" || !owner.runId || typeof owner.generation !== "number" || !Number.isInteger(owner.generation) || owner.generation < 0 || owner.kind !== "runner" && owner.kind !== "workflow" || typeof owner.asyncDir !== "string" || !owner.asyncDir || typeof owner.reservedAt !== "number" || !Number.isFinite(owner.reservedAt))
48
+ return;
49
+ if (owner.sourceRunId !== undefined && typeof owner.sourceRunId !== "string")
50
+ return;
51
+ if (owner.runnerProcessInstanceId !== undefined && (typeof owner.runnerProcessInstanceId !== "string" || !owner.runnerProcessInstanceId))
52
+ return;
53
+ if (owner.runnerStartedAt !== undefined && (typeof owner.runnerStartedAt !== "number" || !Number.isFinite(owner.runnerStartedAt)))
54
+ return;
55
+ return owner;
56
+ }
57
+ function readOwner(dir) {
58
+ try {
59
+ return parseOwner(JSON.parse(fs.readFileSync(path.join(dir, "owner.json"), "utf-8")));
60
+ } catch {
61
+ return;
62
+ }
63
+ }
64
+ function occupiedSlots(poolDir) {
65
+ try {
66
+ return fs.readdirSync(poolDir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && /^slot-\d+$/.test(entry.name)).map((entry) => path.join(poolDir, entry.name));
67
+ } catch (error) {
68
+ if (error.code === "ENOENT")
69
+ return [];
70
+ throw error;
71
+ }
72
+ }
73
+ function snapshotFor(sessionId, limit, rootDir) {
74
+ return { used: occupiedSlots(sessionDir(sessionId, rootDir)).length, limit: limit ?? 0 };
75
+ }
76
+ function matchingOwner(dir, expected) {
77
+ const owner = readOwner(dir);
78
+ return owner?.reservationToken === expected.reservationToken && owner.runId === expected.runId && owner.generation === expected.generation ? owner : undefined;
79
+ }
80
+ function withSlotClaim(dir, operation) {
81
+ const claimPath = path.join(dir, "capacity.claim");
82
+ const claimToken = randomUUID();
83
+ let claim;
84
+ try {
85
+ claim = fs.openSync(claimPath, "wx", 384);
86
+ fs.writeFileSync(claim, claimToken, "utf-8");
87
+ fs.closeSync(claim);
88
+ claim = undefined;
89
+ } catch (error) {
90
+ if (claim !== undefined)
91
+ fs.closeSync(claim);
92
+ if (error.code === "EEXIST" || error.code === "ENOENT")
93
+ return { acquired: false };
94
+ throw error;
95
+ }
96
+ try {
97
+ return { acquired: true, value: operation() };
98
+ } finally {
99
+ try {
100
+ if (fs.readFileSync(claimPath, "utf-8") === claimToken)
101
+ fs.rmSync(claimPath, { force: true });
102
+ } catch (error) {
103
+ if (error.code !== "ENOENT")
104
+ throw error;
105
+ }
106
+ }
107
+ }
108
+ function removeOwnedSlot(dir, expected, options, requireUnstarted = false, release) {
109
+ if (requireUnstarted && (expected.runnerProcessInstanceId || expected.runnerStartedAt))
110
+ return false;
111
+ const claimed = withSlotClaim(dir, () => {
112
+ const current = matchingOwner(dir, expected);
113
+ if (!current || requireUnstarted && (current.runnerProcessInstanceId || current.runnerStartedAt))
114
+ return false;
115
+ const releasedDir = path.join(path.dirname(dir), `.${path.basename(dir)}.released-${randomUUID()}`);
116
+ fs.renameSync(dir, releasedDir);
117
+ options.afterSlotRename?.(releasedDir);
118
+ if (release?.state === "releasable" && release.evidence) {
119
+ appendAbandonedReleaseEvent(expected.asyncDir, expected, release.evidence, options.now?.() ?? Date.now());
120
+ }
121
+ fs.rmSync(releasedDir, { recursive: true, force: true });
122
+ return true;
123
+ });
124
+ return claimed.acquired && claimed.value;
125
+ }
126
+ function appendAbandonedReleaseEvent(asyncDir, owner, evidence, now) {
127
+ try {
128
+ const eventsPath = path.join(asyncDir, "events.jsonl");
129
+ fs.mkdirSync(path.dirname(eventsPath), { recursive: true });
130
+ fs.appendFileSync(eventsPath, `${JSON.stringify({
131
+ type: "subagent.capacity.released",
132
+ ts: now,
133
+ runId: owner.runId,
134
+ sessionId: owner.ownerSessionId,
135
+ ...evidence
136
+ })}
137
+ `, "utf-8");
138
+ } catch {}
139
+ }
140
+ function terminalState(state) {
141
+ return state !== "queued" && state !== "running" && state !== "paused";
142
+ }
143
+ function runnerReleaseVerdict(owner, status, options) {
144
+ if (!status)
145
+ return { state: "retained", reason: "status file is missing or unreadable" };
146
+ if (!owner.runnerProcessInstanceId)
147
+ return { state: "retained", reason: "runner process identity has not been recorded" };
148
+ if (status.sessionId !== owner.ownerSessionId)
149
+ return { state: "retained", reason: `status session ${status.sessionId ?? "unknown"} does not match owner session ${owner.ownerSessionId}` };
150
+ if (status.runId !== owner.runId)
151
+ return { state: "retained", reason: `status run ${status.runId} does not match owner run ${owner.runId}` };
152
+ if (!terminalState(status.state))
153
+ return { state: "retained", reason: `run is still ${status.state}` };
154
+ if (status.processTerminal?.state === "not-started" && status.processTerminal.runId === owner.runId && status.processTerminal.runnerProcessInstanceId === owner.runnerProcessInstanceId && typeof status.error === "string" && status.error)
155
+ return { state: "releasable", reason: "run failed before child startup completed" };
156
+ const proof = readProcessTerminal(owner.asyncDir, {
157
+ runId: owner.runId,
158
+ runnerProcessInstanceId: owner.runnerProcessInstanceId
159
+ });
160
+ return proof?.state === "observed" && proof.runId === owner.runId && proof.runnerProcessInstanceId === owner.runnerProcessInstanceId ? { state: "releasable", reason: "matching observed process-terminal proof is present" } : abandonedRunnerReleaseVerdict(status, proof?.state ?? "missing", options);
161
+ }
162
+ function abandonedRunnerReleaseVerdict(status, proofState, options) {
163
+ const proofReason = `process-terminal proof is ${proofState}`;
164
+ const thresholdMs = resolveAbandonedSlotReleaseAfterMs(options.abandonedSlotReleaseAfterMs);
165
+ if (thresholdMs === false)
166
+ return { state: "retained", reason: `${proofReason}; abandoned-timeout policy is disabled` };
167
+ if (status.state !== "failed")
168
+ return { state: "retained", reason: `${proofReason}; abandoned-timeout policy requires a failed run, not ${status.state}` };
169
+ if (typeof status.pid !== "number" || !Number.isInteger(status.pid) || status.pid < 1)
170
+ return { state: "retained", reason: `${proofReason}; runner PID is missing or invalid` };
171
+ const liveness = (options.pidLiveness ?? checkPidLiveness)(status.pid);
172
+ if (liveness !== "dead")
173
+ return { state: "retained", reason: `${proofReason}; runner PID liveness is ${liveness}` };
174
+ const lastActivityAt = status.lastActivityAt ?? status.lastUpdate ?? status.endedAt;
175
+ if (typeof lastActivityAt !== "number" || !Number.isFinite(lastActivityAt))
176
+ return { state: "retained", reason: `${proofReason}; last activity timestamp is missing or invalid` };
177
+ const now = options.now?.() ?? Date.now();
178
+ const lastActivityAgeMs = Math.max(0, now - lastActivityAt);
179
+ if (lastActivityAgeMs <= thresholdMs)
180
+ return { state: "retained", reason: `${proofReason}; last activity age ${lastActivityAgeMs}ms has not exceeded abandoned-timeout ${thresholdMs}ms` };
181
+ const evidence = {
182
+ releasedBy: "abandoned-timeout",
183
+ processProof: "unknown",
184
+ runnerPid: "gone",
185
+ lastActivityAgeMs,
186
+ abandonedSlotReleaseAfterMs: thresholdMs
187
+ };
188
+ return {
189
+ state: "releasable",
190
+ reason: `${evidence.releasedBy}: ${proofReason}; runner PID is gone; process proof unknown; last activity age ${lastActivityAgeMs}ms exceeds ${thresholdMs}ms`,
191
+ evidence
192
+ };
193
+ }
194
+ function workflowReleaseVerdict(owner, status, liveWorkflowRunIds) {
195
+ if (!status)
196
+ return { state: "retained", reason: "status file is missing or unreadable" };
197
+ if (status.sessionId !== owner.ownerSessionId)
198
+ return { state: "retained", reason: `status session ${status.sessionId ?? "unknown"} does not match owner session ${owner.ownerSessionId}` };
199
+ if (status.runId !== owner.runId)
200
+ return { state: "retained", reason: `status run ${status.runId} does not match owner run ${owner.runId}` };
201
+ if (status.mode !== "workflow")
202
+ return { state: "retained", reason: `status mode is ${status.mode}, not workflow` };
203
+ if (!terminalState(status.state))
204
+ return { state: "retained", reason: `workflow is still ${status.state}` };
205
+ if (liveWorkflowRunIds.has(owner.runId))
206
+ return { state: "retained", reason: "workflow controller is still live" };
207
+ for (const step of status.steps ?? []) {
208
+ const label = step.workflowKey ?? step.agent;
209
+ if (step.status === "pending" || step.status === "running" || step.status === "paused")
210
+ return { state: "retained", reason: `workflow child ${label} is still ${step.status}` };
211
+ if (typeof step.async !== "boolean")
212
+ return { state: "retained", reason: `workflow child ${label} is missing async classification` };
213
+ if (!step.async)
214
+ continue;
215
+ if (!step.runId)
216
+ return { state: "retained", reason: `async workflow child ${label} is missing run id` };
217
+ const childDir = path.join(path.dirname(owner.asyncDir), step.runId);
218
+ if (!fs.existsSync(childDir))
219
+ return { state: "retained", reason: `async workflow child ${label} directory is missing` };
220
+ const childStatus = readStatus(childDir);
221
+ if (!childStatus)
222
+ return { state: "retained", reason: `async workflow child ${label} status is missing or unreadable` };
223
+ if (!terminalState(childStatus.state))
224
+ return { state: "retained", reason: `async workflow child ${label} is still ${childStatus.state}` };
225
+ if (!childStatus.processTerminal?.runnerProcessInstanceId)
226
+ return { state: "retained", reason: `async workflow child ${label} has no runner process identity` };
227
+ const proof = readProcessTerminal(childDir, {
228
+ runId: step.runId,
229
+ runnerProcessInstanceId: childStatus.processTerminal.runnerProcessInstanceId
230
+ });
231
+ if (proof?.state !== "observed" || proof.runId !== step.runId)
232
+ return { state: "retained", reason: `async workflow child ${label} process-terminal proof is ${proof?.state ?? "missing"}` };
233
+ }
234
+ return { state: "releasable", reason: "workflow is terminal, controller is gone, and async children have observed proof" };
235
+ }
236
+ function ownerReleaseVerdict(owner, liveWorkflowRunIds, options) {
237
+ const status = readStatus(owner.asyncDir);
238
+ return owner.kind === "runner" ? runnerReleaseVerdict(owner, status, options) : workflowReleaseVerdict(owner, status, liveWorkflowRunIds);
239
+ }
240
+ function capacitySessionDirs(rootDir, sessionId) {
241
+ if (sessionId)
242
+ return [sessionDir(sessionId, rootDir)];
243
+ try {
244
+ return fs.readdirSync(rootDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => path.join(rootDir, entry.name));
245
+ } catch (error) {
246
+ if (error.code === "ENOENT")
247
+ return [];
248
+ throw error;
249
+ }
250
+ }
251
+ export function inspectActiveAsyncCapacityOwner(input, options = {}) {
252
+ const rootDir = options.rootDir ?? ACTIVE_ASYNC_CAPACITY_DIR;
253
+ const liveWorkflowRunIds = options.liveWorkflowRunIds ?? new Set;
254
+ for (const poolDir of capacitySessionDirs(rootDir, input.sessionId)) {
255
+ for (const dir of occupiedSlots(poolDir)) {
256
+ const owner = readOwner(dir);
257
+ if (!owner)
258
+ continue;
259
+ const sameRun = owner.runId === input.runId || input.asyncDir !== undefined && path.resolve(owner.asyncDir) === path.resolve(input.asyncDir);
260
+ const sourceRun = owner.sourceRunId === input.runId;
261
+ if (!sameRun && !sourceRun)
262
+ continue;
263
+ if (sourceRun && !sameRun) {
264
+ return {
265
+ owner,
266
+ relation: "source",
267
+ slotDir: dir,
268
+ release: { state: "not-owned", reason: `slot was transferred to ${owner.runId}` }
269
+ };
270
+ }
271
+ return { owner, relation: "current", slotDir: dir, release: ownerReleaseVerdict(owner, liveWorkflowRunIds, options) };
272
+ }
273
+ }
274
+ return { relation: "none", release: { state: "not-owned", reason: "no active-capacity slot records this run" } };
275
+ }
276
+ export function reconcileActiveAsyncCapacity(sessionId, limit, options = {}) {
277
+ const rootDir = options.rootDir ?? ACTIVE_ASYNC_CAPACITY_DIR;
278
+ const poolDir = sessionDir(sessionId, rootDir);
279
+ const liveWorkflowRunIds = options.liveWorkflowRunIds ?? new Set;
280
+ for (const dir of occupiedSlots(poolDir)) {
281
+ const owner = readOwner(dir);
282
+ if (!owner || owner.ownerSessionId !== sessionId || owner.ownerSessionKey !== activeAsyncCapacitySessionKey(sessionId) || path.basename(dir) !== `slot-${owner.slot}`)
283
+ continue;
284
+ const release = ownerReleaseVerdict(owner, liveWorkflowRunIds, options);
285
+ if (release.state !== "releasable")
286
+ continue;
287
+ removeOwnedSlot(dir, owner, options, false, release);
288
+ }
289
+ return snapshotFor(sessionId, limit, rootDir);
290
+ }
291
+ export function getActiveAsyncCapacitySnapshot(sessionId, limit, options = {}) {
292
+ return reconcileActiveAsyncCapacity(sessionId, limit, options);
293
+ }
294
+ function createSlot(poolDir, owner) {
295
+ const destination = slotDir(poolDir, owner.slot);
296
+ fs.mkdirSync(poolDir, { recursive: true, mode: 448 });
297
+ try {
298
+ fs.mkdirSync(destination, { mode: 448 });
299
+ } catch (error) {
300
+ if (error.code === "EEXIST")
301
+ return false;
302
+ throw error;
303
+ }
304
+ fs.writeFileSync(path.join(destination, "owner.json"), `${JSON.stringify(owner, null, 2)}
305
+ `, { encoding: "utf-8", mode: 384 });
306
+ return true;
307
+ }
308
+ function handleFor(owner, limit, options, rollbackOwner) {
309
+ const rootDir = options.rootDir ?? ACTIVE_ASYNC_CAPACITY_DIR;
310
+ const dir = slotDir(sessionDir(owner.ownerSessionId, rootDir), owner.slot);
311
+ return {
312
+ owner,
313
+ markStarted(runnerProcessInstanceId) {
314
+ const claimed = withSlotClaim(dir, () => {
315
+ const current = matchingOwner(dir, owner);
316
+ if (!current)
317
+ return false;
318
+ const next = { ...current, runnerProcessInstanceId, runnerStartedAt: options.now?.() ?? Date.now() };
319
+ Object.assign(owner, next);
320
+ try {
321
+ writePrivateAtomicJson(path.join(dir, "owner.json"), next);
322
+ } catch (error) {
323
+ console.error(`Failed to bind active async capacity to runner '${runnerProcessInstanceId}'; capacity will remain occupied:`, error);
324
+ }
325
+ return true;
326
+ });
327
+ if (!claimed.acquired || !claimed.value)
328
+ throw new Error(`Active async capacity ownership changed for run '${owner.runId}'.`);
329
+ },
330
+ markWorkflowStarted() {
331
+ const claimed = withSlotClaim(dir, () => {
332
+ const current = matchingOwner(dir, owner);
333
+ if (!current || current.kind !== "workflow")
334
+ return false;
335
+ const next = { ...current, runnerStartedAt: options.now?.() ?? Date.now() };
336
+ Object.assign(owner, next);
337
+ try {
338
+ writePrivateAtomicJson(path.join(dir, "owner.json"), next);
339
+ } catch (error) {
340
+ console.error(`Failed to mark async workflow '${owner.runId}' started; capacity will remain occupied:`, error);
341
+ }
342
+ return true;
343
+ });
344
+ if (!claimed.acquired || !claimed.value)
345
+ throw new Error(`Active async capacity ownership changed for workflow '${owner.runId}'.`);
346
+ },
347
+ rollback() {
348
+ if (!rollbackOwner)
349
+ return removeOwnedSlot(dir, owner, options, true);
350
+ if (owner.runnerProcessInstanceId || owner.runnerStartedAt)
351
+ return false;
352
+ const claimed = withSlotClaim(dir, () => {
353
+ const current = matchingOwner(dir, owner);
354
+ if (!current || current.runnerProcessInstanceId || current.runnerStartedAt)
355
+ return false;
356
+ writePrivateAtomicJson(path.join(dir, "owner.json"), rollbackOwner);
357
+ Object.assign(owner, rollbackOwner);
358
+ return true;
359
+ });
360
+ return claimed.acquired && claimed.value;
361
+ },
362
+ reconcile(liveWorkflowRunIds) {
363
+ return reconcileActiveAsyncCapacity(owner.ownerSessionId, limit, { ...options, rootDir, liveWorkflowRunIds });
364
+ }
365
+ };
366
+ }
367
+ export function acquireActiveAsyncCapacity(input, options = {}) {
368
+ if (input.limit === undefined)
369
+ return;
370
+ const rootDir = options.rootDir ?? ACTIVE_ASYNC_CAPACITY_DIR;
371
+ const reconciled = reconcileActiveAsyncCapacity(input.sessionId, input.limit, options);
372
+ if (reconciled.used >= input.limit)
373
+ throw new ActiveAsyncCapacityError(reconciled);
374
+ const poolDir = sessionDir(input.sessionId, rootDir);
375
+ const token = options.token?.() ?? randomUUID();
376
+ for (let slot = 0;slot < input.limit; slot++) {
377
+ const owner = {
378
+ version: 1,
379
+ reservationToken: token,
380
+ ownerSessionId: input.sessionId,
381
+ ownerSessionKey: activeAsyncCapacitySessionKey(input.sessionId),
382
+ slot,
383
+ runId: input.runId,
384
+ generation: 0,
385
+ kind: input.kind,
386
+ asyncDir: input.asyncDir,
387
+ reservedAt: options.now?.() ?? Date.now()
388
+ };
389
+ if (createSlot(poolDir, owner))
390
+ return handleFor(owner, input.limit, { ...options, rootDir });
391
+ }
392
+ throw new ActiveAsyncCapacityError(snapshotFor(input.sessionId, input.limit, rootDir));
393
+ }
394
+ export function transferActiveAsyncCapacity(input, options = {}) {
395
+ const limit = input.limit ?? 0;
396
+ const rootDir = options.rootDir ?? ACTIVE_ASYNC_CAPACITY_DIR;
397
+ const poolDir = sessionDir(input.sessionId, rootDir);
398
+ for (const dir of occupiedSlots(poolDir)) {
399
+ const source = readOwner(dir);
400
+ if (!source || source.ownerSessionId !== input.sessionId || source.runId !== input.sourceRunId)
401
+ continue;
402
+ const claimed = withSlotClaim(dir, () => {
403
+ const current = matchingOwner(dir, source);
404
+ const status = readStatus(source.asyncDir);
405
+ if (!current || !status || status.runId !== input.sourceRunId || status.state === "queued" || status.state === "running") {
406
+ throw new Error(`Active async capacity source '${input.sourceRunId}' is not transferable.`);
407
+ }
408
+ const next = {
409
+ ...current,
410
+ runId: input.runId,
411
+ sourceRunId: input.sourceRunId,
412
+ generation: current.generation + 1,
413
+ kind: "runner",
414
+ asyncDir: input.asyncDir,
415
+ reservedAt: options.now?.() ?? Date.now()
416
+ };
417
+ delete next.runnerProcessInstanceId;
418
+ delete next.runnerStartedAt;
419
+ writePrivateAtomicJson(path.join(dir, "owner.json"), next);
420
+ return handleFor(next, limit, { ...options, rootDir }, current);
421
+ });
422
+ if (!claimed.acquired)
423
+ throw new Error(`Active async capacity transfer is already in progress for run '${input.sourceRunId}'.`);
424
+ return claimed.value;
425
+ }
426
+ return acquireActiveAsyncCapacity({ sessionId: input.sessionId, limit: input.limit, runId: input.runId, kind: "runner", asyncDir: input.asyncDir }, options);
427
+ }
@@ -0,0 +1,122 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { readStatus } from "../../shared/utils.js";
4
+ import { encodeIndexSegment } from "./index-segment.js";
5
+ import { updateTerminalRunIndex } from "./terminal-run-index.js";
6
+ import { isStorageCapacityError } from "../../shared/file-system-retry.js";
7
+ export const ACTIVE_RUN_INDEX_DIR = ".active-runs";
8
+ export const DEFAULT_STALE_TERMINAL_ACTIVE_MARKER_MS = 24 * 60 * 60 * 1000;
9
+ const TOOL_CALL_INDEX_DIR = "tool-calls";
10
+ function indexDir(asyncDirRoot) {
11
+ return path.join(asyncDirRoot, ACTIVE_RUN_INDEX_DIR);
12
+ }
13
+ function toolCallIndexDir(asyncDirRoot, toolCallId) {
14
+ return path.join(indexDir(asyncDirRoot), TOOL_CALL_INDEX_DIR, encodeIndexSegment(toolCallId));
15
+ }
16
+ function toolCallIndexPath(asyncDir, toolCallId) {
17
+ return path.join(toolCallIndexDir(path.dirname(asyncDir), toolCallId), path.basename(asyncDir));
18
+ }
19
+ function markerPath(asyncDir) {
20
+ return path.join(indexDir(path.dirname(asyncDir)), path.basename(asyncDir));
21
+ }
22
+ function removeEmptyAncestors(start, stop) {
23
+ let current = start;
24
+ while (current !== stop && current.startsWith(`${stop}${path.sep}`)) {
25
+ try {
26
+ fs.rmdirSync(current);
27
+ } catch {
28
+ return;
29
+ }
30
+ current = path.dirname(current);
31
+ }
32
+ }
33
+ export function isActiveAsyncState(state) {
34
+ return state === "queued" || state === "running";
35
+ }
36
+ function releaseToolCallAliases(asyncDir) {
37
+ const root = path.join(indexDir(path.dirname(asyncDir)), TOOL_CALL_INDEX_DIR);
38
+ let entries;
39
+ try {
40
+ entries = fs.readdirSync(root, { withFileTypes: true });
41
+ } catch (error) {
42
+ const code = error.code;
43
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
44
+ console.error(`Failed to inspect async active-run tool-call index root '${root}':`, error);
45
+ }
46
+ return;
47
+ }
48
+ for (const entry of entries) {
49
+ if (!entry.isDirectory())
50
+ continue;
51
+ const aliasMarker = path.join(root, entry.name, path.basename(asyncDir));
52
+ try {
53
+ fs.rmSync(aliasMarker, { force: true });
54
+ removeEmptyAncestors(path.dirname(aliasMarker), root);
55
+ } catch {}
56
+ }
57
+ }
58
+ export function releaseActiveRunIndex(asyncDir) {
59
+ try {
60
+ fs.rmSync(markerPath(asyncDir));
61
+ } catch (error) {
62
+ if (error.code !== "ENOENT")
63
+ throw error;
64
+ }
65
+ releaseToolCallAliases(asyncDir);
66
+ }
67
+ export function updateActiveRunIndex(asyncDir, state, toolCallId, options = {}) {
68
+ const marker = markerPath(asyncDir);
69
+ if (isActiveAsyncState(state)) {
70
+ fs.mkdirSync(path.dirname(marker), { recursive: true });
71
+ fs.writeFileSync(marker, "", { flag: "a" });
72
+ if (toolCallId) {
73
+ try {
74
+ const toolCallMarker = toolCallIndexPath(asyncDir, toolCallId);
75
+ fs.mkdirSync(path.dirname(toolCallMarker), { recursive: true });
76
+ fs.writeFileSync(toolCallMarker, "", { flag: "a" });
77
+ } catch (error) {
78
+ console.error(`Failed to write async active-run tool-call index for '${asyncDir}':`, error);
79
+ }
80
+ }
81
+ return;
82
+ }
83
+ releaseActiveRunIndex(asyncDir);
84
+ const status = readStatus(asyncDir);
85
+ if (status && status.state === state) {
86
+ try {
87
+ updateTerminalRunIndex(asyncDir, status);
88
+ } catch (error) {
89
+ if (options.retryCapacityErrors && isStorageCapacityError(error))
90
+ throw error;
91
+ console.error(`Failed to write async terminal-run index for '${asyncDir}':`, error);
92
+ }
93
+ }
94
+ }
95
+ export function activeRunMarkerAgeMs(asyncDir, now = Date.now()) {
96
+ try {
97
+ return Math.max(0, now - fs.statSync(markerPath(asyncDir)).mtimeMs);
98
+ } catch (error) {
99
+ if (error.code === "ENOENT")
100
+ return;
101
+ throw error;
102
+ }
103
+ }
104
+ export function readActiveRunIndex(asyncDirRoot) {
105
+ try {
106
+ return fs.readdirSync(indexDir(asyncDirRoot), { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
107
+ } catch (error) {
108
+ if (error.code === "ENOENT")
109
+ return;
110
+ throw error;
111
+ }
112
+ }
113
+ export function readActiveRunToolCallIndex(asyncDirRoot, toolCallId) {
114
+ try {
115
+ return fs.readdirSync(toolCallIndexDir(asyncDirRoot, toolCallId), { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
116
+ } catch (error) {
117
+ const code = error.code;
118
+ if (code === "ENOENT" || code === "ENOTDIR")
119
+ return [];
120
+ throw error;
121
+ }
122
+ }