@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
@@ -2,9 +2,12 @@ import { spawnSync } from "node:child_process";
2
2
  import * as fs from "node:fs";
3
3
  import * as os from "node:os";
4
4
  import * as path from "node:path";
5
+ import { resolveAuthorityDecision } from "../../policy/authority.js";
6
+ import { PROJECT_SUBAGENTS_RELATIVE_DIR } from "../../shared/artifacts.js";
7
+ import { getAgentDir } from "../../shared/utils.js";
5
8
  const DEFAULT_WORKTREE_SETUP_HOOK_TIMEOUT_MS = 30000;
6
9
  function runGit(cwd, args) {
7
- const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8" });
10
+ const result = spawnSync("git", ["-C", cwd, ...args], { encoding: "utf-8", windowsHide: true });
8
11
  return {
9
12
  stdout: result.stdout ?? "",
10
13
  stderr: result.stderr ?? "",
@@ -23,7 +26,7 @@ function runGitChecked(cwd, args) {
23
26
  function resolveRepoState(cwd) {
24
27
  const cwdRelative = resolveRepoCwdRelative(cwd);
25
28
  const toplevel = runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim();
26
- const status = runGitChecked(toplevel, ["status", "--porcelain"]);
29
+ const status = runGitChecked(toplevel, ["status", "--porcelain", "--", `:!${PROJECT_SUBAGENTS_RELATIVE_DIR}`]);
27
30
  if (status.trim().length > 0) {
28
31
  throw new Error("worktree isolation requires a clean git working tree. Commit or stash changes first.");
29
32
  }
@@ -32,10 +35,18 @@ function resolveRepoState(cwd) {
32
35
  }
33
36
  function normalizeComparableCwd(cwd) {
34
37
  const resolved = path.resolve(cwd);
35
- try {
36
- return fs.realpathSync(resolved);
37
- } catch {
38
- return resolved;
38
+ let existing = resolved;
39
+ const missingSegments = [];
40
+ while (true) {
41
+ try {
42
+ return path.join(fs.realpathSync(existing), ...missingSegments.reverse());
43
+ } catch {
44
+ const parent = path.dirname(existing);
45
+ if (parent === existing)
46
+ return resolved;
47
+ missingSegments.push(path.basename(existing));
48
+ existing = parent;
49
+ }
39
50
  }
40
51
  }
41
52
  export function findWorktreeTaskCwdConflict(tasks, sharedCwd) {
@@ -69,6 +80,11 @@ function resolveWorktreeBaseDir(configuredBaseDir, repoRoot) {
69
80
  throw new Error("worktree base directory cannot be empty");
70
81
  const expanded = trimmed.startsWith("~/") ? path.join(os.homedir(), trimmed.slice(2)) : trimmed;
71
82
  const resolved = path.isAbsolute(expanded) ? expanded : path.resolve(repoRoot, expanded);
83
+ const extensionsDir = normalizeComparableCwd(path.join(getAgentDir(), "extensions"));
84
+ const relativeToExtensions = path.relative(extensionsDir, normalizeComparableCwd(resolved));
85
+ if (!relativeToExtensions || !relativeToExtensions.startsWith(`..${path.sep}`) && relativeToExtensions !== ".." && !path.isAbsolute(relativeToExtensions)) {
86
+ throw new Error(`worktree base directory cannot be inside DM extensions directory: ${extensionsDir}. Choose a directory outside it.`);
87
+ }
72
88
  try {
73
89
  fs.mkdirSync(resolved, { recursive: true });
74
90
  } catch (error) {
@@ -181,6 +197,7 @@ function parseWorktreeSetupHookOutput(rawStdout) {
181
197
  }
182
198
  function runWorktreeSetupHook(hook, input) {
183
199
  const result = spawnSync(hook.hookPath, [], {
200
+ windowsHide: true,
184
201
  cwd: input.worktreePath,
185
202
  encoding: "utf-8",
186
203
  input: JSON.stringify(input),
@@ -297,7 +314,7 @@ function removeSyntheticPathsBeforeDiff(worktree) {
297
314
  removeSyntheticPath(worktree, syntheticPath);
298
315
  }
299
316
  }
300
- function emptyDiff(index, agent, branch, patchPath) {
317
+ function emptyDiff(index, agent, branch, patchPath, error) {
301
318
  return {
302
319
  index,
303
320
  agent,
@@ -306,7 +323,8 @@ function emptyDiff(index, agent, branch, patchPath) {
306
323
  filesChanged: 0,
307
324
  insertions: 0,
308
325
  deletions: 0,
309
- patchPath
326
+ patchPath,
327
+ ...error ? { error } : {}
310
328
  };
311
329
  }
312
330
  function parseNumstat(numstat) {
@@ -354,13 +372,113 @@ function writeEmptyPatch(patchPath) {
354
372
  fs.writeFileSync(patchPath, "", "utf-8");
355
373
  } catch {}
356
374
  }
357
- function cleanupSingleWorktree(repoCwd, worktree) {
375
+ function handoffRecordsPatch(manifestPath, patchPath) {
376
+ if (!manifestPath || !fs.existsSync(manifestPath))
377
+ return false;
358
378
  try {
359
- runGitChecked(repoCwd, ["worktree", "remove", "--force", worktree.path]);
360
- } catch {}
379
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
380
+ if (manifest.version !== 1 || !Array.isArray(manifest.groups))
381
+ return false;
382
+ const resolvedPatchPath = path.resolve(patchPath);
383
+ return manifest.groups.some((group) => Array.isArray(group.children) && group.children.some((child) => child.patch?.error === undefined && typeof child.patch?.path === "string" && path.resolve(child.patch.path) === resolvedPatchPath));
384
+ } catch {
385
+ return false;
386
+ }
387
+ }
388
+ function cleanupSingleWorktree(setup, worktree, intent) {
389
+ const errors = [];
390
+ let worktreeRemoved = false;
391
+ let branchRemoved = false;
392
+ if (intent.kind === "preserve" && intent.cleanupBlocker) {
393
+ return {
394
+ index: worktree.index,
395
+ path: worktree.path,
396
+ branch: worktree.branch,
397
+ worktreeRemoved: false,
398
+ branchRemoved: false,
399
+ preserved: true,
400
+ reason: intent.cleanupBlocker
401
+ };
402
+ }
403
+ if (intent.kind !== "setup-rollback") {
404
+ try {
405
+ removeSyntheticPathsBeforeDiff(worktree);
406
+ } catch (error) {
407
+ errors.push(`synthetic path cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
408
+ }
409
+ const status = runGit(worktree.path, ["status", "--porcelain"]);
410
+ const baseDiff = runGit(worktree.path, ["diff", "--quiet", setup.baseCommit, "--"]);
411
+ if (status.status !== 0 || baseDiff.status !== 0 && baseDiff.status !== 1) {
412
+ const reason = status.status !== 0 ? status.stderr.trim() || status.stdout.trim() || "git status failed" : baseDiff.stderr.trim() || baseDiff.stdout.trim() || "git diff check failed";
413
+ return {
414
+ index: worktree.index,
415
+ path: worktree.path,
416
+ branch: worktree.branch,
417
+ worktreeRemoved: false,
418
+ branchRemoved: false,
419
+ preserved: true,
420
+ reason: "cleanup safety check failed",
421
+ errors: [...errors, `cleanup refused: ${reason}`]
422
+ };
423
+ }
424
+ const hasWork = status.stdout.trim().length > 0 || baseDiff.status === 1;
425
+ if (hasWork && intent.kind === "preserve") {
426
+ const captured = (intent.capturedDiffs ?? setup.capturedDiffs)?.find((diff) => diff.index === worktree.index);
427
+ const patchCaptured = captured !== undefined && captured.error === undefined && fs.existsSync(captured.patchPath) && fs.statSync(captured.patchPath).size > 0 && handoffRecordsPatch(intent.handoffManifestPath, captured.patchPath);
428
+ if (!patchCaptured) {
429
+ const reason = "worktree contains changes that are not represented by a captured handoff patch";
430
+ return {
431
+ index: worktree.index,
432
+ path: worktree.path,
433
+ branch: worktree.branch,
434
+ worktreeRemoved: false,
435
+ branchRemoved: false,
436
+ preserved: true,
437
+ reason,
438
+ errors: [...errors, `cleanup refused: ${reason}; preserved ${worktree.path}`]
439
+ };
440
+ }
441
+ }
442
+ if (hasWork && intent.kind === "discard") {
443
+ const decision = resolveAuthorityDecision({ action: "discardWorktree", policy: intent.authorization.policy });
444
+ const authorized = decision === "auto" || decision === "confirm" && intent.authorization.kind === "confirmed";
445
+ if (!authorized) {
446
+ const reason = decision === "forbid" ? "authority policy forbids worktree discard" : "worktree discard requires explicit user confirmation";
447
+ return {
448
+ index: worktree.index,
449
+ path: worktree.path,
450
+ branch: worktree.branch,
451
+ worktreeRemoved: false,
452
+ branchRemoved: false,
453
+ preserved: true,
454
+ reason,
455
+ errors: [...errors, `cleanup refused: ${reason}; preserved ${worktree.path}`]
456
+ };
457
+ }
458
+ }
459
+ }
361
460
  try {
362
- runGitChecked(repoCwd, ["branch", "-D", worktree.branch]);
363
- } catch {}
461
+ runGitChecked(setup.cwd, ["worktree", "remove", "--force", worktree.path]);
462
+ worktreeRemoved = true;
463
+ } catch (error) {
464
+ errors.push(`worktree removal failed: ${error instanceof Error ? error.message : String(error)}`);
465
+ }
466
+ if (worktreeRemoved) {
467
+ try {
468
+ runGitChecked(setup.cwd, ["branch", "-D", worktree.branch]);
469
+ branchRemoved = true;
470
+ } catch (error) {
471
+ errors.push(`branch removal failed: ${error instanceof Error ? error.message : String(error)}`);
472
+ }
473
+ }
474
+ return {
475
+ index: worktree.index,
476
+ path: worktree.path,
477
+ branch: worktree.branch,
478
+ worktreeRemoved,
479
+ branchRemoved,
480
+ ...errors.length ? { errors } : {}
481
+ };
364
482
  }
365
483
  function hasWorktreeChanges(diff) {
366
484
  return diff.filesChanged > 0 || diff.insertions > 0 || diff.deletions > 0 || diff.diffStat.trim().length > 0;
@@ -369,6 +487,22 @@ export function createWorktrees(cwd, runId, count, options) {
369
487
  const repo = resolveRepoState(cwd);
370
488
  const setupHook = resolveWorktreeSetupHook(repo.toplevel, options?.setupHook);
371
489
  const baseDir = resolveWorktreeBaseDir(options?.baseDir, repo.toplevel);
490
+ const plannedSetup = {
491
+ cwd: repo.toplevel,
492
+ baseCommit: repo.baseCommit,
493
+ worktrees: Array.from({ length: count }, (_, index) => {
494
+ const worktreePath = buildWorktreePath(baseDir, runId, index);
495
+ return {
496
+ path: worktreePath,
497
+ agentCwd: repo.cwdRelative ? path.join(worktreePath, repo.cwdRelative) : worktreePath,
498
+ branch: buildWorktreeBranch(runId, index),
499
+ index,
500
+ nodeModulesLinked: false,
501
+ syntheticPaths: []
502
+ };
503
+ })
504
+ };
505
+ options?.beforeCreate?.(plannedSetup);
372
506
  const worktrees = [];
373
507
  try {
374
508
  for (let index = 0;index < count; index++) {
@@ -379,7 +513,7 @@ export function createWorktrees(cwd, runId, count, options) {
379
513
  cwd: repo.toplevel,
380
514
  worktrees,
381
515
  baseCommit: repo.baseCommit
382
- });
516
+ }, { kind: "setup-rollback" });
383
517
  throw error;
384
518
  }
385
519
  return {
@@ -401,20 +535,35 @@ export function diffWorktrees(setup, agents, diffsDir) {
401
535
  const patchPath = path.join(diffsDir, `task-${index}-${safePatchAgentName(agent)}.patch`);
402
536
  try {
403
537
  diffs.push(captureWorktreeDiff(setup, worktree, agent, patchPath));
404
- } catch {
538
+ } catch (error) {
405
539
  writeEmptyPatch(patchPath);
406
- diffs.push(emptyDiff(index, agent, worktree.branch, patchPath));
540
+ diffs.push(emptyDiff(index, agent, worktree.branch, patchPath, error instanceof Error ? error.message : String(error)));
407
541
  }
408
542
  }
543
+ setup.capturedDiffs = diffs;
409
544
  return diffs;
410
545
  }
411
- export function cleanupWorktrees(setup) {
546
+ export function cleanupWorktrees(setup, intent = { kind: "preserve", ...setup.capturedDiffs ? { capturedDiffs: setup.capturedDiffs } : {} }) {
547
+ const tasks = [];
412
548
  for (let index = setup.worktrees.length - 1;index >= 0; index--) {
413
- cleanupSingleWorktree(setup.cwd, setup.worktrees[index]);
549
+ tasks.push(cleanupSingleWorktree(setup, setup.worktrees[index], intent));
414
550
  }
551
+ tasks.sort((left, right) => left.index - right.index);
552
+ const errors = [];
553
+ let pruned = false;
415
554
  try {
416
555
  runGitChecked(setup.cwd, ["worktree", "prune"]);
417
- } catch {}
556
+ pruned = true;
557
+ } catch (error) {
558
+ errors.push(`worktree prune failed: ${error instanceof Error ? error.message : String(error)}`);
559
+ }
560
+ const state = tasks.every((task) => task.worktreeRemoved && task.branchRemoved) && pruned ? "complete" : "partial";
561
+ return {
562
+ state,
563
+ tasks,
564
+ pruned,
565
+ ...errors.length ? { errors } : {}
566
+ };
418
567
  }
419
568
  export function formatWorktreeDiffSummary(diffs) {
420
569
  const changed = diffs.filter(hasWorktreeChanges);
@@ -0,0 +1,35 @@
1
+ import * as fs from "node:fs";
2
+ import { DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS, runFileSystemOperationWithRetry, waitForFileSystemRetry } from "./file-system-retry.js";
3
+ export function ensureAccessibleDir(dirPath, options = {}) {
4
+ const fsImpl = options.fs ?? fs;
5
+ const pid = options.pid ?? process.pid;
6
+ const retryDirectoryErrors = options.retryDirectoryErrors ?? process.platform === "win32";
7
+ const retryDelaysMs = retryDirectoryErrors ? options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS : [];
8
+ const wait = options.wait ?? waitForFileSystemRetry;
9
+ const mkdirWithRetry = (target) => {
10
+ runFileSystemOperationWithRetry(() => {
11
+ fsImpl.mkdirSync(target, { recursive: true });
12
+ }, { retryDelaysMs, wait });
13
+ };
14
+ const accessWithRetry = (target) => {
15
+ runFileSystemOperationWithRetry(() => {
16
+ fsImpl.accessSync(target, fs.constants.R_OK | fs.constants.W_OK);
17
+ }, { retryDelaysMs, wait });
18
+ };
19
+ const fallbackPath = () => {
20
+ const fallback = `${dirPath}-${pid}`;
21
+ mkdirWithRetry(fallback);
22
+ accessWithRetry(fallback);
23
+ return fallback;
24
+ };
25
+ try {
26
+ mkdirWithRetry(dirPath);
27
+ accessWithRetry(dirPath);
28
+ return dirPath;
29
+ } catch (error) {
30
+ const code = error?.code;
31
+ if (code !== "EPERM" && code !== "EACCES")
32
+ throw error;
33
+ return fallbackPath();
34
+ }
35
+ }
@@ -0,0 +1,3 @@
1
+ export function agentStreamOptions(streamFn) {
2
+ return { streamFunction: streamFn, streamFn };
3
+ }
@@ -1,11 +1,133 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { TEMP_ARTIFACTS_DIR } from "./types.js";
3
+ import { CHAIN_RUNS_DIR, TEMP_ARTIFACTS_DIR } from "./types.js";
4
4
  import { getAgentDir } from "./utils.js";
5
5
  const CLEANUP_MARKER_FILE = ".last-cleanup";
6
- const PROJECT_ARTIFACT_ROOT = ".dm-subagents";
6
+ export const PROJECT_SUBAGENTS_RELATIVE_DIR = ".dm/subagents";
7
+ const PROJECT_ARTIFACT_PATHS = [
8
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/output.md`,
9
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/run_worker_input.md`,
10
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/run_worker_output.md`,
11
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/run_worker.jsonl`,
12
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/run_worker_transcript.jsonl`,
13
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/run_worker_meta.json`,
14
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/progress/run/progress.md`,
15
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/outputs/output.md`,
16
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/artifacts/outputs/run/output.md`,
17
+ `${PROJECT_SUBAGENTS_RELATIVE_DIR}/chain-runs/run.json`
18
+ ];
19
+ function globMatchesPath(pattern, filePath) {
20
+ let expression = "^";
21
+ for (let index = 0;index < pattern.length; index += 1) {
22
+ const character = pattern[index];
23
+ if (character === undefined)
24
+ break;
25
+ if (character === "*" && pattern[index + 1] === "*") {
26
+ index += 1;
27
+ if (pattern[index + 1] === "/") {
28
+ index += 1;
29
+ expression += "(?:.*/)?";
30
+ } else {
31
+ expression += ".*";
32
+ }
33
+ } else if (character === "*") {
34
+ expression += "[^/]*";
35
+ } else if (character === "?") {
36
+ expression += "[^/]";
37
+ } else if (character === "[") {
38
+ const end = pattern.indexOf("]", index + 1);
39
+ if (end === -1)
40
+ expression += "\\[";
41
+ else {
42
+ expression += pattern.slice(index, end + 1);
43
+ index = end;
44
+ }
45
+ } else {
46
+ expression += character.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
47
+ }
48
+ }
49
+ try {
50
+ return new RegExp(`${expression}$`).test(filePath);
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ function normalizePattern(pattern) {
56
+ return pattern.replace(/^\.\//, "").replace(/^\//, "").replace(/\/+$/, "");
57
+ }
58
+ function patternMatchesArtifactPath(pattern, artifactPath) {
59
+ const normalized = normalizePattern(pattern);
60
+ return normalized === PROJECT_SUBAGENTS_RELATIVE_DIR || normalized === "*" || artifactPath.startsWith(`${normalized}/`) || globMatchesPath(normalized, artifactPath);
61
+ }
62
+ function ignoreFileExcludesProjectArtifacts(filePath) {
63
+ if (!fs.existsSync(filePath))
64
+ return false;
65
+ try {
66
+ const excluded = new Set;
67
+ for (const rawLine of fs.readFileSync(filePath, "utf-8").split(/\r?\n/)) {
68
+ const line = rawLine.trim();
69
+ if (!line || line.startsWith("#"))
70
+ continue;
71
+ const negated = line.startsWith("!");
72
+ const pattern = negated ? line.slice(1) : line;
73
+ for (const artifactPath of PROJECT_ARTIFACT_PATHS) {
74
+ if (!patternMatchesArtifactPath(pattern, artifactPath))
75
+ continue;
76
+ if (negated)
77
+ excluded.delete(artifactPath);
78
+ else
79
+ excluded.add(artifactPath);
80
+ }
81
+ }
82
+ return excluded.size === PROJECT_ARTIFACT_PATHS.length;
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
87
+ function filesIncludeProjectArtifacts(files) {
88
+ if (!Array.isArray(files))
89
+ return;
90
+ const included = new Set;
91
+ for (const entry of files) {
92
+ if (typeof entry !== "string")
93
+ continue;
94
+ const negated = entry.startsWith("!");
95
+ const pattern = negated ? entry.slice(1) : entry;
96
+ for (const artifactPath of PROJECT_ARTIFACT_PATHS) {
97
+ if (!patternMatchesArtifactPath(pattern, artifactPath))
98
+ continue;
99
+ if (negated)
100
+ included.delete(artifactPath);
101
+ else
102
+ included.add(artifactPath);
103
+ }
104
+ }
105
+ return included.size > 0;
106
+ }
107
+ export function getProjectArtifactPackagingWarning(cwd) {
108
+ const packagePath = path.join(cwd, "package.json");
109
+ if (!fs.existsSync(packagePath))
110
+ return;
111
+ let packageJson;
112
+ try {
113
+ const parsed = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
114
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
115
+ return;
116
+ packageJson = parsed;
117
+ } catch {
118
+ return;
119
+ }
120
+ const filesIncludeArtifacts = filesIncludeProjectArtifacts(packageJson.files);
121
+ if (filesIncludeArtifacts === false)
122
+ return;
123
+ const npmIgnorePath = path.join(cwd, ".npmignore");
124
+ const ignorePath = fs.existsSync(npmIgnorePath) ? npmIgnorePath : path.join(cwd, ".gitignore");
125
+ if (filesIncludeArtifacts === undefined && ignoreFileExcludesProjectArtifacts(ignorePath))
126
+ return;
127
+ return "Project-scoped subagent artifacts can be included when this package is published. Add '.dm/subagents/' to .npmignore, restrict package.json files, or set artifactDir to 'session' or 'temp'.";
128
+ }
7
129
  export function getProjectSubagentsDir(cwd) {
8
- return path.join(cwd, PROJECT_ARTIFACT_ROOT);
130
+ return path.join(cwd, PROJECT_SUBAGENTS_RELATIVE_DIR);
9
131
  }
10
132
  export function getProjectArtifactsDir(cwd) {
11
133
  return path.join(getProjectSubagentsDir(cwd), "artifacts");
@@ -13,14 +135,38 @@ export function getProjectArtifactsDir(cwd) {
13
135
  export function getProjectChainRunsDir(cwd) {
14
136
  return path.join(getProjectSubagentsDir(cwd), "chain-runs");
15
137
  }
16
- export function getArtifactsDir(sessionFile, projectCwd) {
17
- if (projectCwd)
18
- return getProjectArtifactsDir(projectCwd);
19
- if (sessionFile) {
20
- const sessionDir = path.dirname(sessionFile);
21
- return path.join(sessionDir, "subagent-artifacts");
138
+ export function getChainRunsDir(projectCwd, dirPreference = "session") {
139
+ switch (dirPreference) {
140
+ case "project":
141
+ return getProjectChainRunsDir(projectCwd);
142
+ case "session":
143
+ case "temp":
144
+ return CHAIN_RUNS_DIR;
145
+ default:
146
+ throw new Error(`Unsupported artifactDir ${JSON.stringify(dirPreference)}; expected "project", "session", or "temp".`);
147
+ }
148
+ }
149
+ export function getArtifactsDir(sessionFile, projectCwd, dirPreference = "session") {
150
+ switch (dirPreference) {
151
+ case "session":
152
+ if (sessionFile) {
153
+ const sessionDir = path.dirname(sessionFile);
154
+ return path.join(sessionDir, "subagent-artifacts");
155
+ }
156
+ return TEMP_ARTIFACTS_DIR;
157
+ case "temp":
158
+ return TEMP_ARTIFACTS_DIR;
159
+ case "project":
160
+ if (projectCwd)
161
+ return getProjectArtifactsDir(projectCwd);
162
+ if (sessionFile) {
163
+ const sessionDir = path.dirname(sessionFile);
164
+ return path.join(sessionDir, "subagent-artifacts");
165
+ }
166
+ return TEMP_ARTIFACTS_DIR;
167
+ default:
168
+ throw new Error(`Unsupported artifactDir ${JSON.stringify(dirPreference)}; expected "project", "session", or "temp".`);
22
169
  }
23
- return TEMP_ARTIFACTS_DIR;
24
170
  }
25
171
  export function getArtifactPaths(artifactsDir, runId, agent, index) {
26
172
  const suffix = index !== undefined ? `_${index}` : "";
@@ -38,9 +184,22 @@ export function ensureArtifactsDir(dir) {
38
184
  fs.mkdirSync(dir, { recursive: true });
39
185
  }
40
186
  export function writeArtifact(filePath, content) {
187
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
41
188
  fs.writeFileSync(filePath, content, "utf-8");
42
189
  }
190
+ export function formatOutputArtifactContent(input) {
191
+ if (input.output.trim() || !input.error)
192
+ return input.output;
193
+ const lines = ["Subagent run failed before producing output.", "", "Error:", input.error];
194
+ if (input.transcriptPath)
195
+ lines.push("", `Transcript: ${input.transcriptPath}`);
196
+ if (input.metadataPath)
197
+ lines.push(`Metadata: ${input.metadataPath}`);
198
+ return lines.join(`
199
+ `);
200
+ }
43
201
  export function writeMetadata(filePath, metadata) {
202
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
44
203
  fs.writeFileSync(filePath, JSON.stringify(metadata, null, 2), "utf-8");
45
204
  }
46
205
  export function appendJsonl(filePath, line) {
@@ -48,7 +207,7 @@ export function appendJsonl(filePath, line) {
48
207
  `);
49
208
  }
50
209
  export function cleanupOldArtifacts(dir, maxAgeDays) {
51
- if (!fs.existsSync(dir))
210
+ if (maxAgeDays <= 0 || !fs.existsSync(dir))
52
211
  return;
53
212
  const markerPath = path.join(dir, CLEANUP_MARKER_FILE);
54
213
  const now = Date.now();
@@ -1,55 +1,53 @@
1
+ import { createHash } from "node:crypto";
1
2
  import * as fs from "node:fs";
2
3
  import * as path from "node:path";
3
- const DEFAULT_RENAME_RETRY_DELAYS_MS = [10, 25, 50, 100, 200, 500, 1000, 2000, 4000];
4
- const RETRYABLE_RENAME_ERROR_CODES = new Set(["EACCES", "EBUSY", "EPERM"]);
5
- const WAIT_BUFFER = typeof SharedArrayBuffer !== "undefined" ? new SharedArrayBuffer(4) : undefined;
6
- const WAIT_VIEW = WAIT_BUFFER ? new Int32Array(WAIT_BUFFER) : undefined;
7
- function waitSync(delayMs) {
8
- if (delayMs <= 0)
9
- return;
10
- if (WAIT_VIEW) {
11
- try {
12
- Atomics.wait(WAIT_VIEW, 0, 0, delayMs);
13
- return;
14
- } catch {}
15
- }
16
- const end = Date.now() + delayMs;
17
- while (Date.now() < end) {}
18
- }
19
- function isRetryableRenameError(error) {
20
- const code = error?.code;
21
- return typeof code === "string" && RETRYABLE_RENAME_ERROR_CODES.has(code);
22
- }
4
+ import { DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS, runFileSystemOperationWithRetry, waitForFileSystemRetry } from "./file-system-retry.js";
5
+ const MAX_PATH_COMPONENT_BYTES = 255;
23
6
  function renameWithRetry(fsImpl, sourcePath, targetPath, retryDelaysMs, wait) {
24
- for (let attempt = 0;; attempt++) {
25
- try {
26
- fsImpl.renameSync(sourcePath, targetPath);
27
- return;
28
- } catch (error) {
29
- const delayMs = retryDelaysMs[attempt];
30
- if (delayMs === undefined || !isRetryableRenameError(error))
31
- throw error;
32
- wait(delayMs);
33
- }
34
- }
7
+ runFileSystemOperationWithRetry(() => {
8
+ fsImpl.renameSync(sourcePath, targetPath);
9
+ }, { retryDelaysMs, wait });
10
+ }
11
+ function tempBaseName(filePath, pid, nowMs, randomId) {
12
+ const suffix = `.${pid}.${nowMs}.${randomId}.tmp`;
13
+ const preferred = `.${path.basename(filePath)}${suffix}`;
14
+ if (Buffer.byteLength(preferred, "utf-8") <= MAX_PATH_COMPONENT_BYTES)
15
+ return preferred;
16
+ return `.${createHash("sha256").update(path.basename(filePath)).digest("hex")}${suffix}`;
35
17
  }
36
18
  export function createAtomicJsonWriter(options = {}) {
37
19
  const fsImpl = options.fs ?? fs;
38
20
  const now = options.now ?? Date.now;
39
21
  const pid = options.pid ?? process.pid;
40
22
  const random = options.random ?? Math.random;
23
+ const mode = options.mode;
41
24
  const retryRenameErrors = options.retryRenameErrors ?? process.platform === "win32";
42
- const retryDelaysMs = retryRenameErrors ? options.retryDelaysMs ?? DEFAULT_RENAME_RETRY_DELAYS_MS : [];
43
- const wait = options.wait ?? waitSync;
25
+ const retryDirectoryErrors = options.retryDirectoryErrors ?? retryRenameErrors;
26
+ const retryDelaysMs = options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS;
27
+ const renameRetryDelaysMs = retryRenameErrors ? retryDelaysMs : [];
28
+ const directoryRetryDelaysMs = retryDirectoryErrors ? retryDelaysMs : [];
29
+ const wait = options.wait ?? waitForFileSystemRetry;
44
30
  return (filePath, payload) => {
45
- fsImpl.mkdirSync(path.dirname(filePath), { recursive: true });
46
- const tempPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.${pid}.${now()}.${random().toString(36).slice(2)}.tmp`);
31
+ runFileSystemOperationWithRetry(() => {
32
+ fsImpl.mkdirSync(path.dirname(filePath), { recursive: true });
33
+ }, { retryDelaysMs: directoryRetryDelaysMs, wait });
34
+ const tempPath = path.join(path.dirname(filePath), tempBaseName(filePath, pid, now(), random().toString(36).slice(2)));
35
+ let writeError;
47
36
  try {
48
- fsImpl.writeFileSync(tempPath, JSON.stringify(payload, null, 2), "utf-8");
49
- renameWithRetry(fsImpl, tempPath, filePath, retryDelaysMs, wait);
37
+ fsImpl.writeFileSync(tempPath, JSON.stringify(payload, null, 2), mode === undefined ? "utf-8" : { encoding: "utf-8", mode });
38
+ renameWithRetry(fsImpl, tempPath, filePath, renameRetryDelaysMs, wait);
39
+ } catch (error) {
40
+ writeError = error;
41
+ throw error;
50
42
  } finally {
51
- fsImpl.rmSync(tempPath, { force: true });
43
+ try {
44
+ fsImpl.rmSync(tempPath, { force: true });
45
+ } catch (cleanupError) {
46
+ if (writeError === undefined)
47
+ throw cleanupError;
48
+ }
52
49
  }
53
50
  };
54
51
  }
55
52
  export const writeAtomicJson = createAtomicJsonWriter();
53
+ export const writePrivateAtomicJson = createAtomicJsonWriter({ mode: 384 });