@duckmind/dm-darwin-x64 0.43.2 → 0.43.8

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 (256) hide show
  1. package/dm +0 -0
  2. package/extensions/.dm-extensions.json +242 -49
  3. package/extensions/dm-9router-ext/package.json +2 -19
  4. package/extensions/dm-9router-ext/src/index.js +5 -0
  5. package/extensions/dm-ask-user/index.js +10 -0
  6. package/extensions/dm-ask-user/package.json +1 -21
  7. package/extensions/dm-cli-anything/package.json +0 -6
  8. package/extensions/dm-context/package.json +2 -12
  9. package/extensions/dm-context/src/context.js +1 -0
  10. package/extensions/dm-context/src/index.js +7 -0
  11. package/extensions/dm-context/src/utils.js +1 -0
  12. package/extensions/dm-cua/package.json +0 -7
  13. package/extensions/dm-fff/package.json +1 -28
  14. package/extensions/dm-fff/src/index.js +15 -0
  15. package/extensions/dm-fff/src/query.js +1 -0
  16. package/extensions/dm-goal/package.json +2 -31
  17. package/extensions/dm-goal/src/goal.js +43 -0
  18. package/extensions/dm-grill-me/index.js +174 -0
  19. package/extensions/dm-grill-me/package.json +1 -26
  20. package/extensions/dm-image2/package.json +0 -7
  21. package/extensions/dm-subagents/package.json +1 -32
  22. package/extensions/dm-subagents/src/agents/agent-management.js +37 -0
  23. package/extensions/dm-subagents/src/agents/agent-memory.js +6 -0
  24. package/extensions/dm-subagents/src/agents/agent-scope.js +1 -0
  25. package/extensions/dm-subagents/src/agents/agent-selection.js +1 -0
  26. package/extensions/dm-subagents/src/agents/agent-serializer.js +7 -0
  27. package/extensions/dm-subagents/src/agents/agents.js +11 -0
  28. package/extensions/dm-subagents/src/agents/chain-serializer.js +12 -0
  29. package/extensions/dm-subagents/src/agents/frontmatter.js +6 -0
  30. package/extensions/dm-subagents/src/agents/identity.js +1 -0
  31. package/extensions/dm-subagents/src/agents/proactive-skills.js +1 -0
  32. package/extensions/dm-subagents/src/agents/skills.js +6 -0
  33. package/extensions/dm-subagents/src/extension/config.js +2 -0
  34. package/extensions/dm-subagents/src/extension/control-notices.js +4 -0
  35. package/extensions/dm-subagents/src/extension/doctor.js +16 -0
  36. package/extensions/dm-subagents/src/extension/fanout-child.js +231 -0
  37. package/extensions/dm-subagents/src/extension/index.js +367 -0
  38. package/extensions/dm-subagents/src/extension/rpc.js +8 -0
  39. package/extensions/dm-subagents/src/extension/schemas.js +1 -0
  40. package/extensions/dm-subagents/src/extension/{tool-description.ts → tool-description.js} +6 -126
  41. package/extensions/dm-subagents/src/intercom/intercom-bridge.js +13 -0
  42. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.js +5 -0
  43. package/extensions/dm-subagents/src/intercom/result-intercom.js +3 -0
  44. package/extensions/dm-subagents/src/profiles/profiles.js +3 -0
  45. package/extensions/dm-subagents/src/runs/background/async-execution.js +46 -0
  46. package/extensions/dm-subagents/src/runs/background/async-job-tracker.js +14 -0
  47. package/extensions/dm-subagents/src/runs/background/async-resume.js +8 -0
  48. package/extensions/dm-subagents/src/runs/background/async-status.js +12 -0
  49. package/extensions/dm-subagents/src/runs/background/chain-append.js +2 -0
  50. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.js +1 -0
  51. package/extensions/dm-subagents/src/runs/background/completion-batcher.js +1 -0
  52. package/extensions/dm-subagents/src/runs/background/completion-dedupe.js +1 -0
  53. package/extensions/dm-subagents/src/runs/background/control-channel.js +1 -0
  54. package/extensions/dm-subagents/src/runs/background/fleet-view.js +17 -0
  55. package/extensions/dm-subagents/src/runs/background/notify.js +3 -0
  56. package/extensions/dm-subagents/src/runs/background/parallel-groups.js +1 -0
  57. package/extensions/dm-subagents/src/runs/background/result-watcher.js +8 -0
  58. package/extensions/dm-subagents/src/runs/background/run-id-resolver.js +4 -0
  59. package/extensions/dm-subagents/src/runs/background/run-status.js +23 -0
  60. package/extensions/dm-subagents/src/runs/background/scheduled-runs.js +4 -0
  61. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.js +11 -0
  62. package/extensions/dm-subagents/src/runs/background/subagent-runner.js +78 -0
  63. package/extensions/dm-subagents/src/runs/background/top-level-async.js +1 -0
  64. package/extensions/dm-subagents/src/runs/background/wait.js +11 -0
  65. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.js +12 -0
  66. package/extensions/dm-subagents/src/runs/foreground/chain-execution.js +102 -0
  67. package/extensions/dm-subagents/src/runs/foreground/execution.js +51 -0
  68. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.js +228 -0
  69. package/extensions/dm-subagents/src/runs/shared/acceptance.js +3 -0
  70. package/extensions/dm-subagents/src/runs/shared/chain-outputs.js +1 -0
  71. package/extensions/dm-subagents/src/runs/shared/completion-guard.js +3 -0
  72. package/extensions/dm-subagents/src/runs/shared/dm-args.js +1 -0
  73. package/extensions/dm-subagents/src/runs/shared/dm-spawn.js +1 -0
  74. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.js +1 -0
  75. package/extensions/dm-subagents/src/runs/shared/long-running-guard.js +1 -0
  76. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.js +1 -0
  77. package/extensions/dm-subagents/src/runs/shared/model-fallback.js +1 -0
  78. package/extensions/dm-subagents/src/runs/shared/model-scope.js +1 -0
  79. package/extensions/dm-subagents/src/runs/shared/nested-events.js +8 -0
  80. package/extensions/dm-subagents/src/runs/shared/nested-path.js +1 -0
  81. package/extensions/dm-subagents/src/runs/shared/nested-render.js +1 -0
  82. package/extensions/dm-subagents/src/runs/shared/parallel-utils.js +5 -0
  83. package/extensions/dm-subagents/src/runs/shared/run-history.js +5 -0
  84. package/extensions/dm-subagents/src/runs/shared/single-output.js +14 -0
  85. package/extensions/dm-subagents/src/runs/shared/structured-output.js +1 -0
  86. package/extensions/dm-subagents/src/runs/shared/subagent-control.js +5 -0
  87. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.js +22 -0
  88. package/extensions/dm-subagents/src/runs/shared/tool-budget.js +1 -0
  89. package/extensions/dm-subagents/src/runs/shared/turn-budget.js +7 -0
  90. package/extensions/dm-subagents/src/runs/shared/workflow-graph.js +1 -0
  91. package/extensions/dm-subagents/src/runs/shared/worktree.js +3 -0
  92. package/extensions/dm-subagents/src/shared/artifacts.js +2 -0
  93. package/extensions/dm-subagents/src/shared/atomic-json.js +1 -0
  94. package/extensions/dm-subagents/src/shared/child-transcript.js +5 -0
  95. package/extensions/dm-subagents/src/shared/file-coalescer.js +1 -0
  96. package/extensions/dm-subagents/src/shared/fork-context.js +6 -0
  97. package/extensions/dm-subagents/src/shared/formatters.js +9 -0
  98. package/extensions/dm-subagents/src/shared/jsonl-writer.js +2 -0
  99. package/extensions/dm-subagents/src/shared/model-info.js +1 -0
  100. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.js +1 -0
  101. package/extensions/dm-subagents/src/shared/session-identity.js +1 -0
  102. package/extensions/dm-subagents/src/shared/session-tokens.js +2 -0
  103. package/extensions/dm-subagents/src/shared/settings.js +23 -0
  104. package/extensions/dm-subagents/src/shared/status-format.js +1 -0
  105. package/extensions/dm-subagents/src/shared/types.js +8 -0
  106. package/extensions/dm-subagents/src/shared/utils.js +2 -0
  107. package/extensions/dm-subagents/src/slash/prompt-template-bridge.js +1 -0
  108. package/extensions/dm-subagents/src/slash/prompt-workflows.js +7 -0
  109. package/extensions/dm-subagents/src/slash/slash-bridge.js +1 -0
  110. package/extensions/dm-subagents/src/slash/slash-commands.js +38 -0
  111. package/extensions/dm-subagents/src/slash/slash-live-state.js +6 -0
  112. package/extensions/dm-subagents/src/tui/render-helpers.js +1 -0
  113. package/extensions/dm-subagents/src/tui/render.js +4 -0
  114. package/extensions/dm-tasks/package.json +1 -30
  115. package/extensions/dm-tasks/src/auto-clear.js +1 -0
  116. package/extensions/dm-tasks/src/index.js +212 -0
  117. package/extensions/dm-tasks/src/process-tracker.js +1 -0
  118. package/extensions/dm-tasks/src/task-store.js +1 -0
  119. package/extensions/dm-tasks/src/tasks-config.js +1 -0
  120. package/extensions/dm-tasks/src/types.js +0 -0
  121. package/extensions/dm-tasks/src/ui/settings-menu.js +1 -0
  122. package/extensions/dm-tasks/src/ui/task-widget.js +1 -0
  123. package/extensions/dm-usage/index.js +9 -0
  124. package/extensions/dm-usage/package.json +1 -18
  125. package/extensions/greedysearch-dm/bin/search.mjs +135 -135
  126. package/extensions/greedysearch-dm/index.js +20 -0
  127. package/extensions/greedysearch-dm/package.json +1 -22
  128. package/extensions/greedysearch-dm/src/formatters/results.js +8 -0
  129. package/extensions/greedysearch-dm/src/formatters/sources.js +1 -0
  130. package/extensions/greedysearch-dm/src/formatters/synthesis.js +1 -0
  131. package/extensions/greedysearch-dm/src/search/engines.mjs +9 -9
  132. package/extensions/greedysearch-dm/src/search/paths.mjs +1 -0
  133. package/extensions/greedysearch-dm/src/search/research.mjs +100 -100
  134. package/extensions/greedysearch-dm/src/search/synthesis-runner.mjs +10 -10
  135. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.js +20 -0
  136. package/extensions/greedysearch-dm/src/tools/shared.js +9 -0
  137. package/extensions/greedysearch-dm/src/types.js +0 -0
  138. package/extensions/greedysearch-dm/src/utils/helpers.js +1 -0
  139. package/package.json +1 -1
  140. package/extensions/dm-9router-ext/src/index.ts +0 -541
  141. package/extensions/dm-ask-user/index.ts +0 -857
  142. package/extensions/dm-context/src/context.ts +0 -158
  143. package/extensions/dm-context/src/index.ts +0 -439
  144. package/extensions/dm-context/src/utils.ts +0 -6
  145. package/extensions/dm-fff/src/index.ts +0 -1023
  146. package/extensions/dm-fff/src/query.ts +0 -87
  147. package/extensions/dm-goal/src/goal.ts +0 -1073
  148. package/extensions/dm-grill-me/index.ts +0 -1085
  149. package/extensions/dm-subagents/src/agents/agent-management.ts +0 -1052
  150. package/extensions/dm-subagents/src/agents/agent-memory.ts +0 -254
  151. package/extensions/dm-subagents/src/agents/agent-scope.ts +0 -6
  152. package/extensions/dm-subagents/src/agents/agent-selection.ts +0 -25
  153. package/extensions/dm-subagents/src/agents/agent-serializer.ts +0 -121
  154. package/extensions/dm-subagents/src/agents/agents.ts +0 -1553
  155. package/extensions/dm-subagents/src/agents/chain-serializer.ts +0 -277
  156. package/extensions/dm-subagents/src/agents/frontmatter.ts +0 -93
  157. package/extensions/dm-subagents/src/agents/identity.ts +0 -30
  158. package/extensions/dm-subagents/src/agents/proactive-skills.ts +0 -191
  159. package/extensions/dm-subagents/src/agents/skills.ts +0 -729
  160. package/extensions/dm-subagents/src/extension/config.ts +0 -39
  161. package/extensions/dm-subagents/src/extension/control-notices.ts +0 -92
  162. package/extensions/dm-subagents/src/extension/doctor.ts +0 -214
  163. package/extensions/dm-subagents/src/extension/fanout-child.ts +0 -172
  164. package/extensions/dm-subagents/src/extension/index.ts +0 -656
  165. package/extensions/dm-subagents/src/extension/rpc.ts +0 -369
  166. package/extensions/dm-subagents/src/extension/schemas.ts +0 -309
  167. package/extensions/dm-subagents/src/intercom/intercom-bridge.ts +0 -180
  168. package/extensions/dm-subagents/src/intercom/native-supervisor-channel.ts +0 -519
  169. package/extensions/dm-subagents/src/intercom/result-intercom.ts +0 -377
  170. package/extensions/dm-subagents/src/profiles/profiles.ts +0 -637
  171. package/extensions/dm-subagents/src/runs/background/async-execution.ts +0 -1065
  172. package/extensions/dm-subagents/src/runs/background/async-job-tracker.ts +0 -441
  173. package/extensions/dm-subagents/src/runs/background/async-resume.ts +0 -391
  174. package/extensions/dm-subagents/src/runs/background/async-status.ts +0 -395
  175. package/extensions/dm-subagents/src/runs/background/chain-append.ts +0 -282
  176. package/extensions/dm-subagents/src/runs/background/chain-root-attachment.ts +0 -191
  177. package/extensions/dm-subagents/src/runs/background/completion-batcher.ts +0 -166
  178. package/extensions/dm-subagents/src/runs/background/completion-dedupe.ts +0 -63
  179. package/extensions/dm-subagents/src/runs/background/control-channel.ts +0 -332
  180. package/extensions/dm-subagents/src/runs/background/fleet-view.ts +0 -515
  181. package/extensions/dm-subagents/src/runs/background/notify.ts +0 -225
  182. package/extensions/dm-subagents/src/runs/background/parallel-groups.ts +0 -45
  183. package/extensions/dm-subagents/src/runs/background/result-watcher.ts +0 -315
  184. package/extensions/dm-subagents/src/runs/background/run-id-resolver.ts +0 -84
  185. package/extensions/dm-subagents/src/runs/background/run-status.ts +0 -434
  186. package/extensions/dm-subagents/src/runs/background/scheduled-runs.ts +0 -514
  187. package/extensions/dm-subagents/src/runs/background/stale-run-reconciler.ts +0 -368
  188. package/extensions/dm-subagents/src/runs/background/subagent-runner.ts +0 -3171
  189. package/extensions/dm-subagents/src/runs/background/top-level-async.ts +0 -13
  190. package/extensions/dm-subagents/src/runs/background/wait.ts +0 -353
  191. package/extensions/dm-subagents/src/runs/foreground/chain-clarify.ts +0 -1333
  192. package/extensions/dm-subagents/src/runs/foreground/chain-execution.ts +0 -1313
  193. package/extensions/dm-subagents/src/runs/foreground/execution.ts +0 -1239
  194. package/extensions/dm-subagents/src/runs/foreground/subagent-executor.ts +0 -3613
  195. package/extensions/dm-subagents/src/runs/shared/acceptance.ts +0 -879
  196. package/extensions/dm-subagents/src/runs/shared/chain-outputs.ts +0 -116
  197. package/extensions/dm-subagents/src/runs/shared/completion-guard.ts +0 -147
  198. package/extensions/dm-subagents/src/runs/shared/dm-args.ts +0 -271
  199. package/extensions/dm-subagents/src/runs/shared/dm-spawn.ts +0 -147
  200. package/extensions/dm-subagents/src/runs/shared/dynamic-fanout.ts +0 -295
  201. package/extensions/dm-subagents/src/runs/shared/long-running-guard.ts +0 -175
  202. package/extensions/dm-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +0 -365
  203. package/extensions/dm-subagents/src/runs/shared/model-fallback.ts +0 -292
  204. package/extensions/dm-subagents/src/runs/shared/model-scope.ts +0 -128
  205. package/extensions/dm-subagents/src/runs/shared/nested-events.ts +0 -908
  206. package/extensions/dm-subagents/src/runs/shared/nested-path.ts +0 -52
  207. package/extensions/dm-subagents/src/runs/shared/nested-render.ts +0 -115
  208. package/extensions/dm-subagents/src/runs/shared/parallel-utils.ts +0 -198
  209. package/extensions/dm-subagents/src/runs/shared/run-history.ts +0 -60
  210. package/extensions/dm-subagents/src/runs/shared/single-output.ts +0 -180
  211. package/extensions/dm-subagents/src/runs/shared/structured-output.ts +0 -77
  212. package/extensions/dm-subagents/src/runs/shared/subagent-control.ts +0 -223
  213. package/extensions/dm-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -342
  214. package/extensions/dm-subagents/src/runs/shared/tool-budget.ts +0 -74
  215. package/extensions/dm-subagents/src/runs/shared/turn-budget.ts +0 -52
  216. package/extensions/dm-subagents/src/runs/shared/workflow-graph.ts +0 -206
  217. package/extensions/dm-subagents/src/runs/shared/worktree.ts +0 -600
  218. package/extensions/dm-subagents/src/shared/artifacts.ts +0 -113
  219. package/extensions/dm-subagents/src/shared/atomic-json.ts +0 -86
  220. package/extensions/dm-subagents/src/shared/child-transcript.ts +0 -212
  221. package/extensions/dm-subagents/src/shared/file-coalescer.ts +0 -40
  222. package/extensions/dm-subagents/src/shared/fork-context.ts +0 -194
  223. package/extensions/dm-subagents/src/shared/formatters.ts +0 -133
  224. package/extensions/dm-subagents/src/shared/jsonl-writer.ts +0 -81
  225. package/extensions/dm-subagents/src/shared/model-info.ts +0 -78
  226. package/extensions/dm-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  227. package/extensions/dm-subagents/src/shared/session-identity.ts +0 -10
  228. package/extensions/dm-subagents/src/shared/session-tokens.ts +0 -44
  229. package/extensions/dm-subagents/src/shared/settings.ts +0 -450
  230. package/extensions/dm-subagents/src/shared/status-format.ts +0 -49
  231. package/extensions/dm-subagents/src/shared/types.ts +0 -1257
  232. package/extensions/dm-subagents/src/shared/utils.ts +0 -554
  233. package/extensions/dm-subagents/src/slash/prompt-template-bridge.ts +0 -420
  234. package/extensions/dm-subagents/src/slash/prompt-workflows.ts +0 -330
  235. package/extensions/dm-subagents/src/slash/slash-bridge.ts +0 -176
  236. package/extensions/dm-subagents/src/slash/slash-commands.ts +0 -1296
  237. package/extensions/dm-subagents/src/slash/slash-live-state.ts +0 -292
  238. package/extensions/dm-subagents/src/tui/render-helpers.ts +0 -80
  239. package/extensions/dm-subagents/src/tui/render.ts +0 -1748
  240. package/extensions/dm-tasks/src/auto-clear.ts +0 -91
  241. package/extensions/dm-tasks/src/index.ts +0 -1145
  242. package/extensions/dm-tasks/src/process-tracker.ts +0 -140
  243. package/extensions/dm-tasks/src/task-store.ts +0 -305
  244. package/extensions/dm-tasks/src/tasks-config.ts +0 -23
  245. package/extensions/dm-tasks/src/types.ts +0 -40
  246. package/extensions/dm-tasks/src/ui/settings-menu.ts +0 -100
  247. package/extensions/dm-tasks/src/ui/task-widget.ts +0 -265
  248. package/extensions/dm-usage/index.ts +0 -1718
  249. package/extensions/greedysearch-dm/index.ts +0 -177
  250. package/extensions/greedysearch-dm/src/formatters/results.ts +0 -163
  251. package/extensions/greedysearch-dm/src/formatters/sources.ts +0 -116
  252. package/extensions/greedysearch-dm/src/formatters/synthesis.ts +0 -87
  253. package/extensions/greedysearch-dm/src/tools/greedy-search-handler.ts +0 -370
  254. package/extensions/greedysearch-dm/src/tools/shared.ts +0 -187
  255. package/extensions/greedysearch-dm/src/types.ts +0 -110
  256. package/extensions/greedysearch-dm/src/utils/helpers.ts +0 -40
@@ -1,1239 +0,0 @@
1
- /**
2
- * Core execution logic for running subagents
3
- */
4
-
5
- import { spawn } from "node:child_process";
6
- import { existsSync, unlinkSync } from "node:fs";
7
- import type { Message } from "@duckmind/dm-ai";
8
- import type { AgentConfig } from "../../agents/agents.ts";
9
- import {
10
- ensureArtifactsDir,
11
- getArtifactPaths,
12
- writeArtifact,
13
- writeMetadata,
14
- } from "../../shared/artifacts.ts";
15
- import { createChildTranscriptWriter, type ChildTranscriptWriter } from "../../shared/child-transcript.ts";
16
- import {
17
- type AgentProgress,
18
- type ArtifactPaths,
19
- type ControlEvent,
20
- type ModelAttempt,
21
- type RunSyncOptions,
22
- type SingleResult,
23
- type Usage,
24
- DEFAULT_MAX_OUTPUT,
25
- INTERCOM_DETACH_REQUEST_EVENT,
26
- INTERCOM_DETACH_RESPONSE_EVENT,
27
- type AcceptanceLedger,
28
- type ResolvedAcceptanceConfig,
29
- truncateOutput,
30
- getSubagentDepthEnv,
31
- } from "../../shared/types.ts";
32
- import {
33
- DEFAULT_CONTROL_CONFIG,
34
- buildControlEvent,
35
- claimControlNotification,
36
- deriveActivityState,
37
- shouldNotifyControlEvent,
38
- } from "../shared/subagent-control.ts";
39
- import {
40
- getFinalOutput,
41
- findLatestSessionFile,
42
- detectSubagentError,
43
- extractToolArgsPreview,
44
- extractTextFromContent,
45
- } from "../../shared/utils.ts";
46
- import { buildSkillInjection, resolveSkillsWithFallback } from "../../agents/skills.ts";
47
- import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
48
- import { evaluateCompletionMutationGuard } from "../shared/completion-guard.ts";
49
- import { getPiSpawnCommand } from "../shared/dm-spawn.ts";
50
- import { createJsonlWriter } from "../../shared/jsonl-writer.ts";
51
- import { attachPostExitStdioGuard, trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
52
- import { applyThinkingSuffix, buildPiArgs, cleanupTempDir } from "../shared/dm-args.ts";
53
- import { readStructuredOutput } from "../shared/structured-output.ts";
54
- import { captureSingleOutputSnapshot, formatSavedOutputReference, injectOutputPathSystemPrompt, resolveSingleOutput, validateFileOnlyOutputMode, type SingleOutputSnapshot } from "../shared/single-output.ts";
55
- import {
56
- buildModelCandidates,
57
- formatModelAttemptNote,
58
- isRetryableModelFailure,
59
- } from "../shared/model-fallback.ts";
60
- import {
61
- createMutatingFailureState,
62
- didMutatingToolFail,
63
- isMutatingTool,
64
- nextLongRunningTrigger,
65
- recordMutatingFailure,
66
- resetMutatingFailureState,
67
- resolveCurrentPath,
68
- shouldEscalateMutatingFailures,
69
- summarizeRecentMutatingFailures,
70
- } from "../shared/long-running-guard.ts";
71
- import { acceptanceFailureMessage, evaluateAcceptance, formatAcceptancePrompt, resolveEffectiveAcceptance, stripAcceptanceReport } from "../shared/acceptance.ts";
72
- import { appendTurnBudgetSystemPrompt, formatTurnBudgetOutput, initialTurnBudgetState, shouldAbortForTurnBudget, turnBudgetExceededMessage, turnBudgetSoftNote, turnBudgetState } from "../shared/turn-budget.ts";
73
- import { initialToolBudgetState, toolBudgetState } from "../shared/tool-budget.ts";
74
-
75
- const artifactOutputByResult = new WeakMap<SingleResult, string>();
76
- const acceptanceOutputByResult = new WeakMap<SingleResult, string>();
77
-
78
- function emptyUsage(): Usage {
79
- return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
80
- }
81
-
82
- function sumUsage(target: Usage, source: Usage): void {
83
- target.input += source.input;
84
- target.output += source.output;
85
- target.cacheRead += source.cacheRead;
86
- target.cacheWrite += source.cacheWrite;
87
- target.cost += source.cost;
88
- target.turns += source.turns;
89
- }
90
-
91
- function formatTimeoutMessage(timeoutMs: number): string {
92
- return `Subagent timed out after ${timeoutMs}ms.`;
93
- }
94
-
95
- function resolveAttemptTimeout(options: RunSyncOptions): { timeoutMs: number; remainingMs: number; message: string } | undefined {
96
- if (options.timeoutMs === undefined) return undefined;
97
- const deadlineAt = options.deadlineAt ?? Date.now() + options.timeoutMs;
98
- return {
99
- timeoutMs: options.timeoutMs,
100
- remainingMs: Math.max(0, deadlineAt - Date.now()),
101
- message: formatTimeoutMessage(options.timeoutMs),
102
- };
103
- }
104
-
105
- function buildSkippedAcceptanceLedger(acceptance: ResolvedAcceptanceConfig, input: { id: string; message: string }): AcceptanceLedger {
106
- return {
107
- status: acceptance.level === "none" ? "not-required" : "rejected",
108
- explicit: acceptance.explicit,
109
- effectiveAcceptance: acceptance,
110
- inferredReason: acceptance.inferredReason,
111
- criteria: acceptance.criteria,
112
- runtimeChecks: acceptance.level === "none"
113
- ? []
114
- : [{ id: input.id, status: "failed", message: input.message }],
115
- verifyRuns: [],
116
- };
117
- }
118
-
119
- function appendRecentOutput(progress: AgentProgress, lines: string[]): void {
120
- if (lines.length === 0) return;
121
- progress.recentOutput.push(...lines.filter((line) => line.trim()));
122
- if (progress.recentOutput.length > 50) {
123
- progress.recentOutput.splice(0, progress.recentOutput.length - 50);
124
- }
125
- }
126
-
127
- function stripAcceptanceReportsFromMessages(messages: Message[] | undefined): void {
128
- for (const message of messages ?? []) {
129
- if (message.role !== "assistant" || !Array.isArray(message.content)) continue;
130
- for (const part of message.content) {
131
- if (part.type === "text" && "text" in part && typeof part.text === "string") {
132
- part.text = stripAcceptanceReport(part.text);
133
- }
134
- }
135
- }
136
- }
137
-
138
- function snapshotProgress(progress: AgentProgress): AgentProgress {
139
- return {
140
- ...progress,
141
- skills: progress.skills ? [...progress.skills] : undefined,
142
- recentTools: progress.recentTools.map((tool) => ({ ...tool })),
143
- recentOutput: [...progress.recentOutput],
144
- };
145
- }
146
-
147
- function snapshotResult(result: SingleResult, progress: AgentProgress): SingleResult {
148
- return {
149
- ...result,
150
- messages: result.outputMode === "file-only" && result.savedOutputPath ? undefined : result.messages ? [...result.messages] : undefined,
151
- usage: { ...result.usage },
152
- skills: result.skills ? [...result.skills] : undefined,
153
- attemptedModels: result.attemptedModels ? [...result.attemptedModels] : undefined,
154
- modelAttempts: result.modelAttempts
155
- ? result.modelAttempts.map((attempt) => ({
156
- ...attempt,
157
- usage: attempt.usage ? { ...attempt.usage } : undefined,
158
- }))
159
- : undefined,
160
- controlEvents: result.controlEvents ? result.controlEvents.map((event) => ({ ...event })) : undefined,
161
- progress,
162
- progressSummary: result.progressSummary ? { ...result.progressSummary } : undefined,
163
- artifactPaths: result.artifactPaths ? { ...result.artifactPaths } : undefined,
164
- truncation: result.truncation ? { ...result.truncation } : undefined,
165
- outputReference: result.outputReference ? { ...result.outputReference } : undefined,
166
- };
167
- }
168
-
169
- async function runSingleAttempt(
170
- runtimeCwd: string,
171
- agent: AgentConfig,
172
- task: string,
173
- model: string | undefined,
174
- options: RunSyncOptions,
175
- shared: {
176
- sessionEnabled: boolean;
177
- systemPrompt: string;
178
- resolvedSkillNames?: string[];
179
- skillsWarning?: string;
180
- jsonlPath?: string;
181
- artifactPaths?: ArtifactPaths;
182
- transcriptWriter?: ChildTranscriptWriter;
183
- attemptNotes: string[];
184
- outputSnapshot?: SingleOutputSnapshot;
185
- originalTask?: string;
186
- },
187
- ): Promise<SingleResult> {
188
- const effectiveThinking = options.thinkingOverride ?? agent.thinking;
189
- const modelArg = applyThinkingSuffix(model, effectiveThinking, options.thinkingOverride !== undefined);
190
- const { args, env: sharedEnv, tempDir } = buildPiArgs({
191
- baseArgs: ["--mode", "json", "-p"],
192
- task,
193
- sessionEnabled: shared.sessionEnabled,
194
- sessionDir: options.sessionDir,
195
- sessionFile: options.sessionFile,
196
- model: modelArg,
197
- thinking: effectiveThinking,
198
- systemPromptMode: agent.systemPromptMode,
199
- inheritProjectContext: agent.inheritProjectContext,
200
- inheritSkills: agent.inheritSkills,
201
- requireReadTool: Boolean(shared.resolvedSkillNames?.length),
202
- tools: agent.tools,
203
- extensions: agent.extensions,
204
- subagentOnlyExtensions: agent.subagentOnlyExtensions,
205
- systemPrompt: appendTurnBudgetSystemPrompt(shared.systemPrompt, options.turnBudget),
206
- mcpDirectTools: agent.mcpDirectTools,
207
- cwd: options.cwd ?? runtimeCwd,
208
- promptFileStem: agent.name,
209
- intercomSessionName: options.intercomSessionName,
210
- orchestratorIntercomTarget: options.orchestratorIntercomTarget,
211
- runId: options.runId,
212
- childAgentName: agent.name,
213
- childIndex: options.index ?? 0,
214
- parentEventSink: options.nestedRoute?.eventSink,
215
- parentControlInbox: options.nestedRoute?.controlInbox,
216
- parentRootRunId: options.nestedRoute?.rootRunId,
217
- parentCapabilityToken: options.nestedRoute?.capabilityToken,
218
- parentSessionId: options.parentSessionId,
219
- structuredOutput: options.structuredOutput,
220
- toolBudget: options.toolBudget,
221
- });
222
-
223
- const result: SingleResult = {
224
- agent: agent.name,
225
- task: shared.originalTask ?? task,
226
- exitCode: 0,
227
- messages: [],
228
- usage: emptyUsage(),
229
- model: modelArg,
230
- artifactPaths: shared.artifactPaths,
231
- transcriptPath: shared.transcriptWriter ? shared.artifactPaths?.transcriptPath : undefined,
232
- skills: shared.resolvedSkillNames,
233
- skillsWarning: shared.skillsWarning,
234
- ...(options.turnBudget ? { turnBudget: initialTurnBudgetState(options.turnBudget) } : {}),
235
- ...(options.toolBudget ? { toolBudget: initialToolBudgetState(options.toolBudget) } : {}),
236
- };
237
- const startTime = Date.now();
238
- if (options.structuredOutput) {
239
- try {
240
- if (existsSync(options.structuredOutput.outputPath)) unlinkSync(options.structuredOutput.outputPath);
241
- } catch {
242
- // Missing/stale structured-output files are handled after the child exits.
243
- }
244
- }
245
- const controlConfig = options.controlConfig ?? DEFAULT_CONTROL_CONFIG;
246
- let interruptedByControl = false;
247
- const allControlEvents: ControlEvent[] = [];
248
- let pendingControlEvents: ControlEvent[] = [];
249
- const emittedControlEventKeys = new Set<string>();
250
- const emitControlEvent = (event: ControlEvent) => {
251
- if (!shouldNotifyControlEvent(controlConfig, event)) return;
252
- if (!claimControlNotification(controlConfig, event, emittedControlEventKeys)) return;
253
- allControlEvents.push(event);
254
- pendingControlEvents.push(event);
255
- options.onControlEvent?.(event);
256
- };
257
-
258
- const progress: AgentProgress = {
259
- index: options.index ?? 0,
260
- agent: agent.name,
261
- status: "running",
262
- task,
263
- skills: shared.resolvedSkillNames,
264
- recentTools: [],
265
- recentOutput: [...shared.attemptNotes],
266
- toolCount: 0,
267
- tokens: 0,
268
- durationMs: 0,
269
- lastActivityAt: startTime,
270
- };
271
- result.progress = progress;
272
- const attemptTimeout = resolveAttemptTimeout(options);
273
- if (attemptTimeout?.remainingMs === 0) {
274
- result.exitCode = 1;
275
- result.timedOut = true;
276
- result.error = attemptTimeout.message;
277
- result.finalOutput = attemptTimeout.message;
278
- progress.status = "failed";
279
- progress.error = attemptTimeout.message;
280
- result.progressSummary = {
281
- toolCount: progress.toolCount,
282
- tokens: progress.tokens,
283
- durationMs: progress.durationMs,
284
- };
285
- return result;
286
- }
287
- const spawnEnv = { ...process.env, ...sharedEnv, ...getSubagentDepthEnv(options.maxSubagentDepth) };
288
- let observedMutationAttempt = false;
289
-
290
- const exitCode = await new Promise<number>((resolve) => {
291
- const spawnSpec = getPiSpawnCommand(args);
292
- const proc = spawn(spawnSpec.command, spawnSpec.args, {
293
- cwd: options.cwd ?? runtimeCwd,
294
- env: spawnEnv,
295
- stdio: ["ignore", "pipe", "pipe"],
296
- windowsHide: true,
297
- });
298
- const jsonlWriter = createJsonlWriter(shared.jsonlPath, proc.stdout);
299
- let buf = "";
300
- let processClosed = false;
301
- let settled = false;
302
- let detached = false;
303
- let intercomStarted = false;
304
- let assistantError: string | undefined;
305
- let removeAbortListener: (() => void) | undefined;
306
- let removeInterruptListener: (() => void) | undefined;
307
- let activityTimer: NodeJS.Timeout | undefined;
308
- let timeoutTimer: NodeJS.Timeout | undefined;
309
- let timeoutTerminationTimer: NodeJS.Timeout | undefined;
310
- let timeoutHardKillTimer: NodeJS.Timeout | undefined;
311
- let turnBudgetSoftReached = false;
312
- let turnBudgetTerminationTimer: NodeJS.Timeout | undefined;
313
- let turnBudgetHardKillTimer: NodeJS.Timeout | undefined;
314
- const clearTurnBudgetTimers = () => {
315
- if (turnBudgetTerminationTimer) {
316
- clearTimeout(turnBudgetTerminationTimer);
317
- turnBudgetTerminationTimer = undefined;
318
- }
319
- if (turnBudgetHardKillTimer) {
320
- clearTimeout(turnBudgetHardKillTimer);
321
- turnBudgetHardKillTimer = undefined;
322
- }
323
- };
324
- const clearTimeoutTimers = () => {
325
- if (timeoutTimer) {
326
- clearTimeout(timeoutTimer);
327
- timeoutTimer = undefined;
328
- }
329
- if (timeoutTerminationTimer) {
330
- clearTimeout(timeoutTerminationTimer);
331
- timeoutTerminationTimer = undefined;
332
- }
333
- if (timeoutHardKillTimer) {
334
- clearTimeout(timeoutHardKillTimer);
335
- timeoutHardKillTimer = undefined;
336
- }
337
- };
338
-
339
- const detachForIntercom = () => {
340
- detached = true;
341
- processClosed = true;
342
- result.detached = true;
343
- result.detachedReason = "intercom coordination";
344
- progress.status = "detached";
345
- progress.durationMs = Date.now() - startTime;
346
- result.progressSummary = {
347
- toolCount: progress.toolCount,
348
- tokens: progress.tokens,
349
- durationMs: progress.durationMs,
350
- };
351
- finish(-2);
352
- };
353
-
354
- // If the child emits a terminal assistant stop but never exits,
355
- // give it a short grace period to flush naturally, then clean it up.
356
- const FINAL_STOP_GRACE_MS = 1000;
357
- const HARD_KILL_MS = 3000;
358
- let childExited = false;
359
- let forcedTerminationSignal = false;
360
- let cleanTerminalAssistantStopReceived = false;
361
- let finalDrainTimer: NodeJS.Timeout | undefined;
362
- let finalHardKillTimer: NodeJS.Timeout | undefined;
363
- const clearFinalDrainTimers = () => {
364
- if (finalDrainTimer) {
365
- clearTimeout(finalDrainTimer);
366
- finalDrainTimer = undefined;
367
- }
368
- if (finalHardKillTimer) {
369
- clearTimeout(finalHardKillTimer);
370
- finalHardKillTimer = undefined;
371
- }
372
- };
373
- const startFinalDrain = () => {
374
- if (childExited || finalDrainTimer || settled || processClosed || detached) return;
375
- finalDrainTimer = setTimeout(() => {
376
- if (settled || processClosed || detached) return;
377
- const termSent = trySignalChild(proc, "SIGTERM");
378
- if (!termSent) return;
379
- forcedTerminationSignal = true;
380
- if (!cleanTerminalAssistantStopReceived && !assistantError) {
381
- result.error = result.error ?? `Subagent process did not exit within ${FINAL_STOP_GRACE_MS}ms after its final message. Forcing termination.`;
382
- }
383
- finalHardKillTimer = setTimeout(() => {
384
- if (settled || processClosed || detached) return;
385
- forcedTerminationSignal = trySignalChild(proc, "SIGKILL") || forcedTerminationSignal;
386
- }, HARD_KILL_MS);
387
- finalHardKillTimer.unref?.();
388
- }, FINAL_STOP_GRACE_MS);
389
- finalDrainTimer.unref?.();
390
- };
391
-
392
- const unsubscribeIntercomDetach = options.intercomEvents?.on?.(INTERCOM_DETACH_REQUEST_EVENT, (payload) => {
393
- if (!options.allowIntercomDetach || detached || processClosed || !intercomStarted) return;
394
- if (!payload || typeof payload !== "object") return;
395
- const requestId = (payload as { requestId?: unknown }).requestId;
396
- if (typeof requestId !== "string" || requestId.length === 0) return;
397
- options.intercomEvents?.emit(INTERCOM_DETACH_RESPONSE_EVENT, { requestId, accepted: true });
398
- detachForIntercom();
399
- });
400
-
401
- const finish = (code: number) => {
402
- if (settled) return;
403
- settled = true;
404
- clearFinalDrainTimers();
405
- clearStdioGuard();
406
- clearTimeoutTimers();
407
- clearTurnBudgetTimers();
408
- if (activityTimer) {
409
- clearInterval(activityTimer);
410
- activityTimer = undefined;
411
- }
412
- unsubscribeIntercomDetach?.();
413
- removeAbortListener?.();
414
- removeInterruptListener?.();
415
- resolve(code);
416
- };
417
-
418
- const drainPendingControlEvents = (): ControlEvent[] | undefined => {
419
- if (pendingControlEvents.length === 0) return undefined;
420
- const events = pendingControlEvents;
421
- pendingControlEvents = [];
422
- return events;
423
- };
424
-
425
- let activeLongRunningNotified = false;
426
- let pendingToolResult: { tool: string; path?: string; mutates: boolean; startedAt?: number } | undefined;
427
- const mutatingFailures = createMutatingFailureState();
428
- const mutatingFailureWindowMs = 5 * 60_000;
429
- const currentToolDurationMs = (now: number) => progress.currentToolStartedAt ? Math.max(0, now - progress.currentToolStartedAt) : undefined;
430
- const emitNeedsAttention = (now: number, input: { message?: string; reason?: ControlEvent["reason"]; recentFailureSummary?: string; currentTool?: string; currentPath?: string; currentToolDurationMs?: number } = {}): boolean => {
431
- if (!controlConfig.enabled) return false;
432
- const previous = progress.activityState;
433
- progress.activityState = "needs_attention";
434
- const event = buildControlEvent({
435
- type: "needs_attention",
436
- from: previous,
437
- to: "needs_attention",
438
- runId: options.runId,
439
- agent: agent.name,
440
- index: options.index,
441
- ts: now,
442
- lastActivityAt: progress.lastActivityAt,
443
- message: input.message,
444
- reason: input.reason ?? "idle",
445
- turns: result.usage.turns,
446
- tokens: progress.tokens,
447
- toolCount: progress.toolCount,
448
- currentTool: input.currentTool ?? progress.currentTool,
449
- currentToolDurationMs: input.currentToolDurationMs ?? currentToolDurationMs(now),
450
- currentPath: input.currentPath ?? progress.currentPath,
451
- recentFailureSummary: input.recentFailureSummary,
452
- });
453
- emitControlEvent(event);
454
- return previous !== "needs_attention";
455
- };
456
- const emitActiveLongRunning = (now: number, reason: ControlEvent["reason"]): boolean => {
457
- if (!controlConfig.enabled || activeLongRunningNotified || progress.activityState === "needs_attention") return false;
458
- activeLongRunningNotified = true;
459
- const previous = progress.activityState;
460
- progress.activityState = "active_long_running";
461
- emitControlEvent(buildControlEvent({
462
- type: "active_long_running",
463
- from: previous,
464
- to: "active_long_running",
465
- runId: options.runId,
466
- agent: agent.name,
467
- index: options.index,
468
- ts: now,
469
- message: `${agent.name} is still active but long-running`,
470
- reason,
471
- turns: result.usage.turns,
472
- tokens: progress.tokens,
473
- toolCount: progress.toolCount,
474
- currentTool: progress.currentTool,
475
- currentToolDurationMs: currentToolDurationMs(now),
476
- currentPath: progress.currentPath,
477
- elapsedMs: now - startTime,
478
- }));
479
- return true;
480
- };
481
- const requestTurnBudgetAbort = (turnCount: number) => {
482
- const budget = options.turnBudget;
483
- if (!budget || result.timedOut || result.turnBudgetExceeded || interruptedByControl || processClosed || settled || detached) return;
484
- const message = turnBudgetExceededMessage(budget, turnCount);
485
- result.turnBudgetExceeded = true;
486
- result.wrapUpRequested = true;
487
- result.turnBudget = turnBudgetState(budget, turnCount, true);
488
- result.error = message;
489
- result.finalOutput = message;
490
- progress.status = "failed";
491
- progress.error = message;
492
- progress.durationMs = Date.now() - startTime;
493
- fireUpdate();
494
- trySignalChild(proc, "SIGINT");
495
- turnBudgetTerminationTimer = setTimeout(() => {
496
- if (processClosed || settled || detached || result.timedOut) return;
497
- trySignalChild(proc, "SIGTERM");
498
- }, 1000);
499
- turnBudgetTerminationTimer.unref?.();
500
- turnBudgetHardKillTimer = setTimeout(() => {
501
- if (processClosed || settled || detached || result.timedOut) return;
502
- trySignalChild(proc, "SIGKILL");
503
- }, 4000);
504
- turnBudgetHardKillTimer.unref?.();
505
- };
506
-
507
- const updateTurnBudget = (turnCount: number, terminalAssistantStop: boolean) => {
508
- const budget = options.turnBudget;
509
- if (!budget || result.timedOut || result.turnBudgetExceeded) return;
510
- if (turnCount < budget.maxTurns) {
511
- result.turnBudget = { ...budget, outcome: "within-budget", turnCount };
512
- return;
513
- }
514
- if (!turnBudgetSoftReached) {
515
- turnBudgetSoftReached = true;
516
- result.wrapUpRequested = true;
517
- appendRecentOutput(progress, [turnBudgetSoftNote(budget, turnCount)]);
518
- }
519
- result.turnBudget = turnBudgetState(budget, turnCount, false);
520
- if (shouldAbortForTurnBudget(budget, turnCount, terminalAssistantStop)) {
521
- requestTurnBudgetAbort(turnCount);
522
- }
523
- };
524
-
525
- const updateActivityState = (now: number): boolean => {
526
- if (!controlConfig.enabled) return false;
527
- const idleState = deriveActivityState({
528
- config: controlConfig,
529
- startedAt: startTime,
530
- lastActivityAt: progress.lastActivityAt,
531
- now,
532
- });
533
- if (idleState === "needs_attention") {
534
- return progress.activityState === "needs_attention" ? false : emitNeedsAttention(now);
535
- }
536
- const activeReason = nextLongRunningTrigger(controlConfig, {
537
- startedAt: startTime,
538
- now,
539
- turns: result.usage.turns,
540
- tokens: progress.tokens,
541
- });
542
- return activeReason ? emitActiveLongRunning(now, activeReason) : false;
543
- };
544
-
545
-
546
- const emitUpdateSnapshot = (text: string) => {
547
- if (!options.onUpdate || processClosed) return;
548
- const progressSnapshot = snapshotProgress(progress);
549
- const resultSnapshot = snapshotResult(result, progressSnapshot);
550
- const controlEvents = drainPendingControlEvents();
551
- options.onUpdate({
552
- content: [{ type: "text", text }],
553
- details: {
554
- mode: "single",
555
- results: [resultSnapshot],
556
- progress: [progressSnapshot],
557
- controlEvents,
558
- },
559
- });
560
- };
561
-
562
- const fireUpdate = () => {
563
- if (!options.onUpdate || processClosed) return;
564
- progress.durationMs = Date.now() - startTime;
565
- const output = (result.timedOut || result.turnBudgetExceeded) && result.finalOutput ? result.finalOutput : getFinalOutput(result.messages);
566
- emitUpdateSnapshot(output || "(running...)");
567
- };
568
-
569
- const processLine = (line: string) => {
570
- if (!line.trim()) return;
571
- jsonlWriter.writeLine(line);
572
- let evt: { type?: string; message?: Message; toolName?: string; args?: unknown };
573
- try {
574
- evt = JSON.parse(line) as { type?: string; message?: Message; toolName?: string; args?: unknown };
575
- } catch {
576
- shared.transcriptWriter?.writeStdoutLine(line);
577
- // Non-JSON stdout lines are expected; only structured events are parsed.
578
- return;
579
- }
580
- shared.transcriptWriter?.writeChildEvent(evt);
581
-
582
- const now = Date.now();
583
- progress.durationMs = now - startTime;
584
- progress.lastActivityAt = now;
585
- updateActivityState(now);
586
-
587
- if (evt.type === "tool_execution_start") {
588
- const toolArgs = evt.args && typeof evt.args === "object" && !Array.isArray(evt.args)
589
- ? evt.args as Record<string, unknown>
590
- : {};
591
- let shouldDetachForBlockingIntercom = false;
592
- if (options.allowIntercomDetach && (evt.toolName === "intercom" || evt.toolName === "contact_supervisor")) {
593
- intercomStarted = true;
594
- shouldDetachForBlockingIntercom = (evt.toolName === "intercom" && toolArgs.action === "ask")
595
- || (evt.toolName === "contact_supervisor" && (toolArgs.reason === "need_decision" || toolArgs.reason === "interview_request"));
596
- }
597
- progress.toolCount++;
598
- if (options.toolBudget) {
599
- result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount);
600
- }
601
- progress.currentTool = evt.toolName;
602
- progress.currentToolArgs = extractToolArgsPreview(toolArgs);
603
- progress.currentToolStartedAt = now;
604
- progress.currentPath = resolveCurrentPath(evt.toolName, toolArgs);
605
- const mutates = isMutatingTool(evt.toolName, toolArgs);
606
- observedMutationAttempt = observedMutationAttempt || mutates;
607
- pendingToolResult = { tool: evt.toolName ?? "tool", path: progress.currentPath, mutates, startedAt: now };
608
- fireUpdate();
609
- if (shouldDetachForBlockingIntercom && !detached && !processClosed) {
610
- detachForIntercom();
611
- }
612
- }
613
-
614
- if (evt.type === "tool_execution_end") {
615
- if (progress.currentTool) {
616
- progress.recentTools.push({
617
- tool: progress.currentTool,
618
- args: progress.currentToolArgs || "",
619
- endMs: now,
620
- });
621
- }
622
- progress.currentTool = undefined;
623
- progress.currentToolArgs = undefined;
624
- progress.currentToolStartedAt = undefined;
625
- progress.currentPath = undefined;
626
- fireUpdate();
627
- }
628
-
629
- if (evt.type === "message_end" && evt.message) {
630
- result.messages.push(evt.message);
631
- if (evt.message.role === "assistant") {
632
- result.usage.turns++;
633
- progress.turnCount = result.usage.turns;
634
- const stopReason = (evt.message as { stopReason?: string }).stopReason;
635
- const hasToolCall = Array.isArray(evt.message.content)
636
- && evt.message.content.some((part) => (part as { type?: string }).type === "toolCall");
637
- const terminalAssistantStop = stopReason === "stop" && !hasToolCall;
638
- updateTurnBudget(result.usage.turns, terminalAssistantStop);
639
- const u = evt.message.usage;
640
- if (u) {
641
- result.usage.input += u.input || 0;
642
- result.usage.output += u.output || 0;
643
- result.usage.cacheRead += u.cacheRead || 0;
644
- result.usage.cacheWrite += u.cacheWrite || 0;
645
- result.usage.cost += u.cost?.total || 0;
646
- progress.tokens = result.usage.input + result.usage.output;
647
- }
648
- if (!result.model && evt.message.model) result.model = evt.message.model;
649
- if (evt.message.errorMessage) assistantError = evt.message.errorMessage;
650
- const assistantText = extractTextFromContent(evt.message.content);
651
- appendRecentOutput(progress, assistantText.split("\n").slice(-10));
652
- // Final assistant message: start the exit drain window.
653
- if (terminalAssistantStop) {
654
- if (!evt.message.errorMessage && assistantText.trim()) assistantError = undefined;
655
- cleanTerminalAssistantStopReceived ||= !evt.message.errorMessage;
656
- startFinalDrain();
657
- }
658
- }
659
- updateActivityState(now);
660
- fireUpdate();
661
- }
662
-
663
- if (evt.type === "tool_result_end" && evt.message) {
664
- result.messages.push(evt.message);
665
- const resultText = extractTextFromContent(evt.message.content);
666
- if (options.toolBudget && pendingToolResult && resultText.includes("Tool budget hard limit reached")) {
667
- result.toolBudgetBlocked = true;
668
- result.toolBudget = toolBudgetState(options.toolBudget, progress.toolCount, pendingToolResult.tool);
669
- }
670
- appendRecentOutput(progress, resultText.split("\n").slice(-10));
671
- const toolSnapshot = pendingToolResult;
672
- pendingToolResult = undefined;
673
- if (toolSnapshot?.mutates && didMutatingToolFail(resultText)) {
674
- recordMutatingFailure(mutatingFailures, {
675
- tool: toolSnapshot.tool,
676
- path: toolSnapshot.path,
677
- error: resultText.split("\n").find((line) => line.trim())?.trim().slice(0, 180) ?? "mutating tool failed",
678
- ts: now,
679
- }, mutatingFailureWindowMs);
680
- if (shouldEscalateMutatingFailures(mutatingFailures, controlConfig.failedToolAttemptsBeforeAttention)) {
681
- emitNeedsAttention(now, {
682
- message: `${agent.name} needs attention after repeated mutating tool failures`,
683
- reason: "tool_failures",
684
- currentTool: toolSnapshot.tool,
685
- currentPath: toolSnapshot.path,
686
- currentToolDurationMs: toolSnapshot.startedAt ? Math.max(0, now - toolSnapshot.startedAt) : undefined,
687
- recentFailureSummary: summarizeRecentMutatingFailures(mutatingFailures),
688
- });
689
- }
690
- } else if (toolSnapshot?.mutates) {
691
- resetMutatingFailureState(mutatingFailures);
692
- }
693
- fireUpdate();
694
- }
695
- };
696
-
697
- if (controlConfig.enabled) {
698
- activityTimer = setInterval(() => {
699
- if (processClosed || settled || detached) return;
700
- const now = Date.now();
701
- if (updateActivityState(now)) {
702
- progress.durationMs = now - startTime;
703
- fireUpdate();
704
- }
705
- }, 1000);
706
- activityTimer.unref?.();
707
- }
708
-
709
- if (attemptTimeout) {
710
- timeoutTimer = setTimeout(() => {
711
- if (processClosed || settled || detached || interruptedByControl) return;
712
- result.timedOut = true;
713
- result.error = attemptTimeout.message;
714
- result.finalOutput = attemptTimeout.message;
715
- progress.status = "failed";
716
- progress.error = attemptTimeout.message;
717
- progress.durationMs = Date.now() - startTime;
718
- fireUpdate();
719
- trySignalChild(proc, "SIGINT");
720
- timeoutTerminationTimer = setTimeout(() => {
721
- if (processClosed || settled || detached) return;
722
- trySignalChild(proc, "SIGTERM");
723
- }, 1000);
724
- timeoutTerminationTimer.unref?.();
725
- timeoutHardKillTimer = setTimeout(() => {
726
- if (processClosed || settled || detached) return;
727
- trySignalChild(proc, "SIGKILL");
728
- }, 4000);
729
- timeoutHardKillTimer.unref?.();
730
- }, attemptTimeout.remainingMs);
731
- timeoutTimer.unref?.();
732
- }
733
-
734
- let stderrBuf = "";
735
-
736
- const clearStdioGuard = attachPostExitStdioGuard(proc, { idleMs: 2000, hardMs: 8000 });
737
- proc.stdout.on("data", (d) => {
738
- buf += d.toString();
739
- const lines = buf.split("\n");
740
- buf = lines.pop() || "";
741
- lines.forEach(processLine);
742
- });
743
- proc.stderr.on("data", (d) => {
744
- stderrBuf += d.toString();
745
- });
746
- proc.on("exit", () => {
747
- childExited = true;
748
- clearFinalDrainTimers();
749
- });
750
- proc.on("close", (code, signal) => {
751
- clearFinalDrainTimers();
752
- clearStdioGuard();
753
- void jsonlWriter.close().catch(() => {
754
- // JSONL artifact flush is best effort.
755
- });
756
- cleanupTempDir(tempDir);
757
- if (buf.trim()) processLine(buf);
758
- if (stderrBuf.trim()) shared.transcriptWriter?.writeStderrText(stderrBuf);
759
- if (!result.error && assistantError) result.error = assistantError;
760
- const forcedDrainAfterFinalSuccess = forcedTerminationSignal && cleanTerminalAssistantStopReceived && !result.error;
761
- if (code !== 0 && stderrBuf.trim() && !result.error && !forcedDrainAfterFinalSuccess) {
762
- result.error = stderrBuf.trim();
763
- }
764
- const finalCode = forcedDrainAfterFinalSuccess ? 0 : forcedTerminationSignal || signal ? (code ?? 1) : (code ?? 0);
765
- if (detached) {
766
- result.exitCode = result.error && finalCode === 0 ? 1 : finalCode;
767
- progress.status = result.exitCode === 0 ? "completed" : "failed";
768
- progress.durationMs = Date.now() - startTime;
769
- if (result.error) progress.error = result.error;
770
- result.progressSummary = {
771
- toolCount: progress.toolCount,
772
- tokens: progress.tokens,
773
- durationMs: progress.durationMs,
774
- };
775
- const finalOutput = getFinalOutput(result.messages);
776
- result.finalOutput = finalOutput.trim() || result.error || result.finalOutput || "Detached child exited without final output.";
777
- if (result.artifactPaths && options.artifactConfig?.enabled !== false && options.artifactConfig?.includeOutput !== false) {
778
- try {
779
- writeArtifact(result.artifactPaths.outputPath, result.finalOutput);
780
- } catch {
781
- // Detached children may outlive test/temp cleanup; recovered status is best-effort.
782
- }
783
- }
784
- options.onDetachedExit?.(snapshotResult(result, snapshotProgress(progress)));
785
- finish(-2);
786
- return;
787
- }
788
- processClosed = true;
789
- finish(finalCode);
790
- });
791
- proc.on("error", (error) => {
792
- clearFinalDrainTimers();
793
- clearStdioGuard();
794
- void jsonlWriter.close().catch(() => {
795
- // JSONL artifact flush is best effort.
796
- });
797
- cleanupTempDir(tempDir);
798
- if (stderrBuf.trim()) shared.transcriptWriter?.writeStderrText(stderrBuf);
799
- if (!result.error) {
800
- result.error = error instanceof Error ? error.message : String(error);
801
- }
802
- finish(1);
803
- });
804
-
805
- if (options.signal) {
806
- const kill = () => {
807
- if (processClosed || detached) return;
808
- if (options.allowIntercomDetach && intercomStarted && !detached) {
809
- detachForIntercom();
810
- return;
811
- }
812
- proc.kill("SIGTERM");
813
- setTimeout(() => !proc.killed && proc.kill("SIGKILL"), 3000);
814
- };
815
- if (options.signal.aborted) kill();
816
- else {
817
- options.signal.addEventListener("abort", kill, { once: true });
818
- removeAbortListener = () => options.signal?.removeEventListener("abort", kill);
819
- }
820
- }
821
-
822
- if (options.interruptSignal) {
823
- const interrupt = () => {
824
- if (processClosed || detached || settled) return;
825
- if (result.timedOut) return;
826
- interruptedByControl = true;
827
- clearTimeoutTimers();
828
- progress.status = "running";
829
- progress.durationMs = Date.now() - startTime;
830
- result.interrupted = true;
831
- result.finalOutput = "Interrupted. Waiting for explicit next action.";
832
- progress.activityState = undefined;
833
- fireUpdate();
834
- trySignalChild(proc, "SIGINT");
835
- setTimeout(() => {
836
- if (settled || processClosed || detached) return;
837
- trySignalChild(proc, "SIGTERM");
838
- }, 1000).unref?.();
839
- };
840
- if (options.interruptSignal.aborted) interrupt();
841
- else {
842
- options.interruptSignal.addEventListener("abort", interrupt, { once: true });
843
- removeInterruptListener = () => options.interruptSignal?.removeEventListener("abort", interrupt);
844
- }
845
- }
846
- });
847
- result.exitCode = exitCode;
848
- if (interruptedByControl) {
849
- result.exitCode = 0;
850
- result.interrupted = true;
851
- result.error = undefined;
852
- result.finalOutput = result.finalOutput || "Interrupted. Waiting for explicit next action.";
853
- result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
854
- progress.activityState = undefined;
855
- progress.durationMs = Date.now() - startTime;
856
- result.progressSummary = {
857
- toolCount: progress.toolCount,
858
- tokens: progress.tokens,
859
- durationMs: progress.durationMs,
860
- };
861
- return result;
862
- }
863
- if (result.detached) {
864
- result.exitCode = 0;
865
- result.finalOutput = "Detached for intercom coordination.";
866
- return result;
867
- }
868
-
869
- if (result.error && result.exitCode === 0) {
870
- result.exitCode = 1;
871
- }
872
- if (result.exitCode === 0 && !result.error) {
873
- const errInfo = detectSubagentError(result.messages);
874
- if (errInfo.hasError) {
875
- result.exitCode = errInfo.exitCode ?? 1;
876
- result.error = errInfo.details
877
- ? `${errInfo.errorType} failed (exit ${errInfo.exitCode}): ${errInfo.details}`
878
- : `${errInfo.errorType} failed with exit code ${errInfo.exitCode}`;
879
- }
880
- }
881
- if (result.exitCode === 0 && !result.error) {
882
- const finalText = getFinalOutput(result.messages);
883
- const missingStructuredOutput = options.structuredOutput
884
- ? !existsSync(options.structuredOutput.outputPath)
885
- : false;
886
- if (!finalText?.trim() && (!options.structuredOutput || missingStructuredOutput)) {
887
- result.exitCode = 1;
888
- result.error = "Subagent produced no output (possible model cold-start or empty response).";
889
- }
890
- }
891
- if (options.structuredOutput && result.exitCode === 0 && !result.error) {
892
- const structured = readStructuredOutput({
893
- schema: options.structuredOutput.schema,
894
- schemaPath: options.structuredOutput.schemaPath,
895
- outputPath: options.structuredOutput.outputPath,
896
- });
897
- result.structuredOutputSchemaPath = options.structuredOutput.schemaPath;
898
- result.structuredOutputPath = options.structuredOutput.outputPath;
899
- if (structured.error) {
900
- result.exitCode = 1;
901
- result.error = structured.error;
902
- } else {
903
- result.structuredOutput = structured.value;
904
- }
905
- }
906
-
907
- progress.status = result.exitCode === 0 ? "completed" : "failed";
908
- progress.durationMs = Date.now() - startTime;
909
- if (result.error) {
910
- progress.error = result.error;
911
- if (progress.currentTool) {
912
- progress.failedTool = progress.currentTool;
913
- }
914
- }
915
-
916
- result.progressSummary = {
917
- toolCount: progress.toolCount,
918
- tokens: progress.tokens,
919
- durationMs: progress.durationMs,
920
- };
921
-
922
- const acceptanceOutput = getFinalOutput(result.messages);
923
- let fullOutput = stripAcceptanceReport(acceptanceOutput);
924
- if (result.timedOut) {
925
- const timeoutMessage = formatTimeoutMessage(options.timeoutMs ?? 0);
926
- fullOutput = fullOutput.trim()
927
- ? `${timeoutMessage}\n\nPartial output before timeout:\n${fullOutput}`
928
- : timeoutMessage;
929
- } else if (result.turnBudgetExceeded && result.turnBudget) {
930
- fullOutput = formatTurnBudgetOutput(turnBudgetExceededMessage(result.turnBudget, result.turnBudget.turnCount), fullOutput);
931
- } else if (result.wrapUpRequested && result.turnBudget?.outcome === "wrap-up-requested") {
932
- const note = turnBudgetSoftNote(result.turnBudget, result.turnBudget.wrapUpRequestedAtTurn ?? result.turnBudget.turnCount);
933
- fullOutput = fullOutput.trim() ? `${note}\n\n${fullOutput}` : note;
934
- }
935
- const completionGuard = result.exitCode === 0 && !result.error && agent.completionGuard !== false
936
- ? evaluateCompletionMutationGuard({
937
- agent: agent.name,
938
- task: shared.originalTask ?? task,
939
- messages: result.messages,
940
- tools: agent.tools,
941
- mcpDirectTools: agent.mcpDirectTools,
942
- })
943
- : undefined;
944
- if (completionGuard?.triggered && !observedMutationAttempt) {
945
- result.exitCode = 1;
946
- result.error = "Subagent completed without making edits for an implementation task.\nIt appears to have returned planning or scratchpad output instead of applying changes.";
947
- progress.status = "failed";
948
- progress.error = result.error;
949
- emitControlEvent(buildControlEvent({
950
- from: progress.activityState,
951
- to: "needs_attention",
952
- runId: options.runId ?? agent.name,
953
- agent: agent.name,
954
- index: options.index,
955
- ts: Date.now(),
956
- message: `${agent.name} completed without making edits for an implementation task`,
957
- reason: "completion_guard",
958
- }));
959
- }
960
- if (options.outputPath && result.exitCode === 0) {
961
- const resolvedOutput = resolveSingleOutput(options.outputPath, fullOutput, shared.outputSnapshot);
962
- fullOutput = stripAcceptanceReport(resolvedOutput.fullOutput);
963
- result.savedOutputPath = resolvedOutput.savedPath;
964
- result.outputSaveError = resolvedOutput.saveError;
965
- if (resolvedOutput.savedPath) {
966
- result.outputReference = formatSavedOutputReference(resolvedOutput.savedPath, fullOutput);
967
- }
968
- }
969
- artifactOutputByResult.set(result, fullOutput);
970
- acceptanceOutputByResult.set(result, acceptanceOutput);
971
- result.outputMode = options.outputMode ?? "inline";
972
- result.finalOutput = options.outputMode === "file-only" && result.savedOutputPath && result.outputReference
973
- ? result.outputReference.message
974
- : fullOutput;
975
- result.controlEvents = allControlEvents.length ? allControlEvents : undefined;
976
- if (options.onUpdate) {
977
- const finalText = result.finalOutput || result.error || "(no output)";
978
- const progressSnapshot = snapshotProgress(progress);
979
- const resultSnapshot = snapshotResult(result, progressSnapshot);
980
- options.onUpdate({
981
- content: [{ type: "text", text: finalText }],
982
- details: {
983
- mode: "single",
984
- results: [resultSnapshot],
985
- progress: [progressSnapshot],
986
- controlEvents: allControlEvents.length ? allControlEvents : undefined,
987
- },
988
- });
989
- }
990
- return result;
991
- }
992
-
993
- /**
994
- * Run a subagent synchronously (blocking until complete)
995
- */
996
- export async function runSync(
997
- runtimeCwd: string,
998
- agents: AgentConfig[],
999
- agentName: string,
1000
- task: string,
1001
- options: RunSyncOptions,
1002
- ): Promise<SingleResult> {
1003
- const agent = agents.find((a) => a.name === agentName);
1004
- if (!agent) {
1005
- return {
1006
- agent: agentName,
1007
- task,
1008
- exitCode: 1,
1009
- messages: [],
1010
- usage: emptyUsage(),
1011
- error: `Unknown agent: ${agentName}`,
1012
- };
1013
- }
1014
- const outputModeValidationError = validateFileOnlyOutputMode(options.outputMode, options.outputPath, `Single run (${agentName})`);
1015
- if (outputModeValidationError) {
1016
- return {
1017
- agent: agentName,
1018
- task,
1019
- exitCode: 1,
1020
- messages: [],
1021
- usage: emptyUsage(),
1022
- outputMode: options.outputMode,
1023
- error: outputModeValidationError,
1024
- };
1025
- }
1026
-
1027
- const shareEnabled = options.share === true;
1028
- const effectiveAcceptance = resolveEffectiveAcceptance({
1029
- explicit: options.acceptance,
1030
- agentName,
1031
- task,
1032
- mode: options.acceptanceContext?.mode ?? "single",
1033
- async: options.acceptanceContext?.async,
1034
- dynamic: options.acceptanceContext?.dynamic,
1035
- dynamicGroup: options.acceptanceContext?.dynamicGroup,
1036
- });
1037
- const acceptancePrompt = formatAcceptancePrompt(effectiveAcceptance);
1038
- const taskWithAcceptance = acceptancePrompt ? `${task}\n${acceptancePrompt}` : task;
1039
- const sessionEnabled = Boolean(options.sessionFile || options.sessionDir) || shareEnabled;
1040
- const skillNames = options.skills ?? agent.skills ?? [];
1041
- const skillCwd = options.cwd ?? runtimeCwd;
1042
- const { resolved: resolvedSkills, missing: missingSkills } = resolveSkillsWithFallback(skillNames, skillCwd, runtimeCwd);
1043
- if (skillNames.some((skill) => skill.trim() === "dm-subagents") && missingSkills.includes("dm-subagents")) {
1044
- return {
1045
- agent: agentName,
1046
- task,
1047
- exitCode: 1,
1048
- messages: [],
1049
- usage: emptyUsage(),
1050
- error: "Skills not found: dm-subagents",
1051
- };
1052
- }
1053
- let systemPrompt = agent.systemPrompt?.trim() || "";
1054
- if (resolvedSkills.length > 0) {
1055
- const skillInjection = buildSkillInjection(resolvedSkills);
1056
- systemPrompt = systemPrompt ? `${systemPrompt}\n\n${skillInjection}` : skillInjection;
1057
- }
1058
- const memoryInjection = buildAgentMemoryInjection(agent, skillCwd);
1059
- if (memoryInjection) {
1060
- systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
1061
- }
1062
- systemPrompt = injectOutputPathSystemPrompt(systemPrompt, options.outputPath);
1063
-
1064
- const candidates = buildModelCandidates(
1065
- options.modelOverride ?? agent.model,
1066
- agent.fallbackModels,
1067
- options.availableModels,
1068
- options.preferredModelProvider,
1069
- { scope: options.modelScope },
1070
- );
1071
- const attemptedModels: string[] = [];
1072
- const modelAttempts: ModelAttempt[] = [];
1073
- const aggregateUsage = emptyUsage();
1074
- const attemptNotes: string[] = [];
1075
- let totalToolCount = 0;
1076
- let totalDurationMs = 0;
1077
-
1078
- let artifactPathsResult: ArtifactPaths | undefined;
1079
- let jsonlPath: string | undefined;
1080
- let transcriptWriter: ChildTranscriptWriter | undefined;
1081
- if (options.artifactsDir && options.artifactConfig?.enabled !== false) {
1082
- artifactPathsResult = getArtifactPaths(options.artifactsDir, options.runId, agentName, options.index);
1083
- ensureArtifactsDir(options.artifactsDir);
1084
- if (options.artifactConfig?.includeInput !== false) {
1085
- writeArtifact(artifactPathsResult.inputPath, `# Task for ${agentName}\n\n${taskWithAcceptance}`);
1086
- }
1087
- if (options.artifactConfig?.includeJsonl !== false) {
1088
- jsonlPath = artifactPathsResult.jsonlPath;
1089
- }
1090
- if (options.artifactConfig?.includeTranscript !== false) {
1091
- transcriptWriter = createChildTranscriptWriter({
1092
- transcriptPath: artifactPathsResult.transcriptPath,
1093
- source: "foreground",
1094
- runId: options.runId,
1095
- agent: agentName,
1096
- childIndex: options.index,
1097
- cwd: options.cwd ?? runtimeCwd,
1098
- });
1099
- transcriptWriter.writeInitialUserMessage(taskWithAcceptance);
1100
- }
1101
- }
1102
-
1103
- let lastResult: SingleResult | undefined;
1104
- const modelsToTry = candidates.length > 0 ? candidates : [undefined];
1105
- for (let i = 0; i < modelsToTry.length; i++) {
1106
- const candidate = modelsToTry[i];
1107
- const outputSnapshot = captureSingleOutputSnapshot(options.outputPath);
1108
- const result = await runSingleAttempt(runtimeCwd, agent, taskWithAcceptance, candidate, options, {
1109
- sessionEnabled,
1110
- systemPrompt,
1111
- resolvedSkillNames: resolvedSkills.length > 0 ? resolvedSkills.map((skill) => skill.name) : undefined,
1112
- skillsWarning: missingSkills.length > 0 ? `Skills not found: ${missingSkills.join(", ")}` : undefined,
1113
- jsonlPath,
1114
- artifactPaths: artifactPathsResult,
1115
- transcriptWriter,
1116
- attemptNotes,
1117
- outputSnapshot,
1118
- originalTask: task,
1119
- });
1120
- lastResult = result;
1121
- if (result.model) attemptedModels.push(result.model);
1122
- else if (candidate) attemptedModels.push(candidate);
1123
- sumUsage(aggregateUsage, result.usage);
1124
- totalToolCount += result.progressSummary?.toolCount ?? 0;
1125
- totalDurationMs += result.progressSummary?.durationMs ?? 0;
1126
- const attemptSucceeded = result.exitCode === 0 && !result.error;
1127
- const attempt: ModelAttempt = {
1128
- model: result.model ?? candidate ?? agent.model ?? "default",
1129
- success: attemptSucceeded,
1130
- exitCode: result.exitCode,
1131
- error: result.error,
1132
- usage: { ...result.usage },
1133
- };
1134
- modelAttempts.push(attempt);
1135
- if (result.timedOut || result.turnBudgetExceeded) {
1136
- break;
1137
- }
1138
- if (attemptSucceeded) {
1139
- break;
1140
- }
1141
- if (!isRetryableModelFailure(result.error) || i === modelsToTry.length - 1) {
1142
- break;
1143
- }
1144
- attemptNotes.push(formatModelAttemptNote(attempt, modelsToTry[i + 1]));
1145
- }
1146
-
1147
- const result = lastResult ?? {
1148
- agent: agentName,
1149
- task,
1150
- exitCode: 1,
1151
- messages: [],
1152
- usage: emptyUsage(),
1153
- error: "Subagent did not produce a result.",
1154
- } satisfies SingleResult;
1155
-
1156
- result.usage = aggregateUsage;
1157
- result.attemptedModels = attemptedModels.length > 0 ? attemptedModels : undefined;
1158
- result.modelAttempts = modelAttempts.length > 0 ? modelAttempts : undefined;
1159
- result.progressSummary = {
1160
- toolCount: totalToolCount,
1161
- tokens: aggregateUsage.input + aggregateUsage.output,
1162
- durationMs: totalDurationMs,
1163
- };
1164
- if (attemptNotes.length > 0 && result.progress) {
1165
- result.progress.recentOutput = [...attemptNotes, ...result.progress.recentOutput];
1166
- if (result.progress.recentOutput.length > 50) {
1167
- result.progress.recentOutput.splice(50);
1168
- }
1169
- }
1170
-
1171
- if (transcriptWriter) result.transcriptPath = artifactPathsResult?.transcriptPath;
1172
- if (transcriptWriter?.getError()) result.transcriptError = transcriptWriter.getError();
1173
-
1174
- if (artifactPathsResult && options.artifactConfig?.enabled !== false) {
1175
- result.artifactPaths = artifactPathsResult;
1176
- if (options.artifactConfig?.includeOutput !== false) {
1177
- writeArtifact(artifactPathsResult.outputPath, artifactOutputByResult.get(result) ?? result.finalOutput ?? "");
1178
- }
1179
- if (options.artifactConfig?.includeMetadata !== false) {
1180
- writeMetadata(artifactPathsResult.metadataPath, {
1181
- runId: options.runId,
1182
- agent: agentName,
1183
- task,
1184
- exitCode: result.exitCode,
1185
- usage: result.usage,
1186
- model: result.model,
1187
- attemptedModels: result.attemptedModels,
1188
- modelAttempts: result.modelAttempts,
1189
- durationMs: result.progressSummary?.durationMs,
1190
- toolCount: result.progressSummary?.toolCount,
1191
- error: result.error,
1192
- ...(transcriptWriter ? { transcriptPath: artifactPathsResult.transcriptPath } : {}),
1193
- transcriptError: result.transcriptError,
1194
- skills: result.skills,
1195
- skillsWarning: result.skillsWarning,
1196
- timestamp: Date.now(),
1197
- });
1198
- }
1199
-
1200
- if (options.maxOutput) {
1201
- const config = { ...DEFAULT_MAX_OUTPUT, ...options.maxOutput };
1202
- const truncationResult = truncateOutput(result.finalOutput ?? "", config, artifactPathsResult.outputPath);
1203
- if (truncationResult.truncated) result.truncation = truncationResult;
1204
- }
1205
- } else if (options.maxOutput) {
1206
- const config = { ...DEFAULT_MAX_OUTPUT, ...options.maxOutput };
1207
- const truncationResult = truncateOutput(result.finalOutput ?? "", config);
1208
- if (truncationResult.truncated) result.truncation = truncationResult;
1209
- }
1210
-
1211
- if (options.sessionFile && (existsSync(options.sessionFile) || result.messages?.length)) {
1212
- result.sessionFile = options.sessionFile;
1213
- } else if (shareEnabled && options.sessionDir) {
1214
- const sessionFile = findLatestSessionFile(options.sessionDir);
1215
- if (sessionFile) result.sessionFile = sessionFile;
1216
- }
1217
-
1218
- result.acceptance = result.timedOut
1219
- ? buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "timeout", message: "Acceptance was not evaluated because the subagent timed out." })
1220
- : result.turnBudgetExceeded
1221
- ? buildSkippedAcceptanceLedger(effectiveAcceptance, { id: "turn-budget", message: "Acceptance was not evaluated because the subagent exceeded its turn budget." })
1222
- : await evaluateAcceptance({
1223
- acceptance: effectiveAcceptance,
1224
- output: acceptanceOutputByResult.get(result) ?? result.finalOutput ?? "",
1225
- cwd: options.cwd ?? runtimeCwd,
1226
- });
1227
- const acceptanceFailure = acceptanceFailureMessage(result.acceptance);
1228
- stripAcceptanceReportsFromMessages(result.messages);
1229
- if (acceptanceFailure && result.acceptance.explicit && result.exitCode === 0 && !result.detached && !result.interrupted && !result.timedOut) {
1230
- result.exitCode = 1;
1231
- result.error = result.error ? `${result.error}\n${acceptanceFailure}` : acceptanceFailure;
1232
- if (result.progress) {
1233
- result.progress.status = "failed";
1234
- result.progress.error = result.error;
1235
- }
1236
- }
1237
-
1238
- return result;
1239
- }