@bastani/atomic 0.9.12 → 0.9.13-alpha.1

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 (264) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +5 -12
  4. package/dist/builtin/intercom/broker/client.ts +11 -2
  5. package/dist/builtin/intercom/contact-supervisor-tool.ts +13 -3
  6. package/dist/builtin/intercom/index-heavy.ts +13 -3
  7. package/dist/builtin/intercom/index.ts +43 -19
  8. package/dist/builtin/intercom/intercom-utils.ts +15 -29
  9. package/dist/builtin/intercom/package.json +1 -1
  10. package/dist/builtin/intercom/source-ownership.ts +9 -12
  11. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  12. package/dist/builtin/mcp/README.md +3 -1
  13. package/dist/builtin/mcp/index.ts +10 -16
  14. package/dist/builtin/mcp/package.json +1 -1
  15. package/dist/builtin/mcp/startup-warmup.ts +20 -8
  16. package/dist/builtin/subagents/CHANGELOG.md +30 -0
  17. package/dist/builtin/subagents/README.md +22 -30
  18. package/dist/builtin/subagents/package.json +1 -1
  19. package/dist/builtin/subagents/src/extension/doctor.ts +1 -1
  20. package/dist/builtin/subagents/src/extension/fanout-child.ts +76 -56
  21. package/dist/builtin/subagents/src/extension/index.ts +23 -32
  22. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +25 -44
  23. package/dist/builtin/subagents/src/intercom/result-intercom.ts +7 -7
  24. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +166 -434
  25. package/dist/builtin/subagents/src/runs/background/notify.ts +29 -8
  26. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +2 -2
  27. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +4 -4
  28. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +2 -2
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -2
  30. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +1 -281
  31. package/dist/builtin/subagents/src/runs/foreground/execution-structured-retries.ts +11 -75
  32. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +457 -0
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +119 -246
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +1 -1
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +18 -9
  36. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +1 -1
  37. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -4
  38. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +1 -646
  39. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +2 -7
  40. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +5 -5
  41. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +7 -8
  42. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +14 -5
  43. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +158 -93
  44. package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +179 -0
  45. package/dist/builtin/subagents/src/runs/inprocess/background.ts +89 -0
  46. package/dist/builtin/subagents/src/runs/inprocess/child-policy.ts +27 -0
  47. package/dist/builtin/subagents/src/runs/inprocess/control-registry.ts +55 -0
  48. package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +106 -0
  49. package/dist/builtin/subagents/src/runs/inprocess/index.ts +50 -0
  50. package/dist/builtin/subagents/src/runs/inprocess/nested-routing.ts +218 -0
  51. package/dist/builtin/subagents/src/runs/{shared/subagent-prompt-runtime.ts → inprocess/prompt-behavior.ts} +37 -75
  52. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +1138 -0
  53. package/dist/builtin/subagents/src/runs/{shared/nested-events.ts → inprocess/runtime-support/nested-api.ts} +13 -7
  54. package/dist/builtin/subagents/src/runs/{shared/nested-events-control.ts → inprocess/runtime-support/nested-control.ts} +18 -17
  55. package/dist/builtin/subagents/src/runs/{shared/nested-events-core.ts → inprocess/runtime-support/nested-core.ts} +14 -61
  56. package/dist/builtin/subagents/src/runs/{shared/nested-events-projection.ts → inprocess/runtime-support/nested-projection.ts} +4 -10
  57. package/dist/builtin/subagents/src/runs/{shared/nested-events-registry.ts → inprocess/runtime-support/nested-registry.ts} +19 -4
  58. package/dist/builtin/subagents/src/runs/{shared/nested-render.ts → inprocess/runtime-support/nested-rendering.ts} +3 -3
  59. package/dist/builtin/subagents/src/runs/{shared/nested-events-sanitize.ts → inprocess/runtime-support/nested-sanitize.ts} +3 -3
  60. package/dist/builtin/subagents/src/runs/inprocess.ts +1 -0
  61. package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +10 -4
  62. package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +0 -1
  63. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +15 -492
  64. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +13 -11
  65. package/dist/builtin/subagents/src/runs/shared/run-history.ts +3 -5
  66. package/dist/builtin/subagents/src/runs/shared/single-output.ts +4 -4
  67. package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +5 -5
  68. package/dist/builtin/subagents/src/shared/types-async.ts +0 -1
  69. package/dist/builtin/subagents/src/shared/types-config.ts +18 -0
  70. package/dist/builtin/subagents/src/shared/types-results.ts +9 -2
  71. package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +2 -3
  72. package/dist/builtin/subagents/src/slash/slash-live-state.ts +2 -2
  73. package/dist/builtin/subagents/src/tui/render-chain-graph.ts +10 -4
  74. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +12 -11
  75. package/dist/builtin/subagents/src/tui/render-result-compact.ts +49 -12
  76. package/dist/builtin/subagents/src/tui/render-result.ts +46 -17
  77. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -1
  78. package/dist/builtin/subagents/src/tui/render-status-progress.ts +8 -5
  79. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -8
  80. package/dist/builtin/subagents/src/tui/render-widget.ts +0 -24
  81. package/dist/builtin/web-access/package.json +1 -1
  82. package/dist/builtin/workflows/CHANGELOG.md +25 -0
  83. package/dist/builtin/workflows/README.md +13 -3
  84. package/dist/builtin/workflows/package.json +1 -1
  85. package/dist/builtin/workflows/src/durable/backend.ts +7 -0
  86. package/dist/builtin/workflows/src/durable/dbos-metadata.ts +11 -1
  87. package/dist/builtin/workflows/src/durable/resume-runtime.ts +1 -0
  88. package/dist/builtin/workflows/src/durable/tool-primitive.ts +4 -28
  89. package/dist/builtin/workflows/src/durable/types.ts +5 -0
  90. package/dist/builtin/workflows/src/engine/run.ts +17 -1
  91. package/dist/builtin/workflows/src/extension/atomic-stage-session.ts +12 -39
  92. package/dist/builtin/workflows/src/extension/config-file-loader.ts +10 -1
  93. package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
  94. package/dist/builtin/workflows/src/extension/dispatcher.ts +4 -0
  95. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1151 -737
  96. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +303 -38
  97. package/dist/builtin/workflows/src/extension/render-result.ts +4 -0
  98. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -3
  99. package/dist/builtin/workflows/src/extension/runtime.ts +7 -1
  100. package/dist/builtin/workflows/src/extension/wiring.ts +53 -11
  101. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +12 -3
  102. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +5 -2
  103. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +9 -7
  104. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +11 -0
  105. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +18 -1
  106. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +10 -8
  107. package/dist/builtin/workflows/src/extension/workflow-tool.ts +9 -4
  108. package/dist/builtin/workflows/src/runs/background/quit.ts +28 -4
  109. package/dist/builtin/workflows/src/runs/background/resume-acknowledgements.ts +2 -1
  110. package/dist/builtin/workflows/src/runs/background/status.ts +55 -8
  111. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +4 -0
  112. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -1
  113. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +1 -1
  114. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +1 -1
  115. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +688 -38
  116. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -1
  117. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +19 -514
  118. package/dist/builtin/workflows/src/runs/shared/retry.ts +44 -0
  119. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +5 -0
  120. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +6 -0
  121. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +5 -1
  122. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +5 -0
  123. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +5 -0
  124. package/dist/builtin/workflows/src/shared/persistence-restore.ts +3 -0
  125. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  126. package/dist/builtin/workflows/src/shared/run-indicator-status.ts +75 -0
  127. package/dist/builtin/workflows/src/shared/store-public-types.ts +43 -7
  128. package/dist/builtin/workflows/src/shared/store-run-methods.ts +42 -8
  129. package/dist/builtin/workflows/src/shared/store-stage-methods.ts +31 -8
  130. package/dist/builtin/workflows/src/shared/store-types.ts +34 -0
  131. package/dist/builtin/workflows/src/shared/timing.ts +10 -0
  132. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +22 -2
  133. package/dist/builtin/workflows/src/tui/session-list.ts +1 -1
  134. package/dist/builtin/workflows/src/tui/session-overlays.ts +14 -10
  135. package/dist/builtin/workflows/src/tui/session-picker.ts +20 -4
  136. package/dist/builtin/workflows/src/tui/status-list.ts +31 -47
  137. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +9 -1
  138. package/dist/builtin/workflows/src/tui/widget.ts +16 -46
  139. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  140. package/dist/core/agent-session-auto-compaction.js +54 -11
  141. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  142. package/dist/core/agent-session-events.d.ts.map +1 -1
  143. package/dist/core/agent-session-events.js +49 -14
  144. package/dist/core/agent-session-events.js.map +1 -1
  145. package/dist/core/agent-session-methods.d.ts +13 -0
  146. package/dist/core/agent-session-methods.d.ts.map +1 -1
  147. package/dist/core/agent-session-methods.js.map +1 -1
  148. package/dist/core/agent-session-models.d.ts.map +1 -1
  149. package/dist/core/agent-session-models.js +12 -1
  150. package/dist/core/agent-session-models.js.map +1 -1
  151. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  152. package/dist/core/agent-session-prompt.js +4 -0
  153. package/dist/core/agent-session-prompt.js.map +1 -1
  154. package/dist/core/agent-session-retry.d.ts +21 -4
  155. package/dist/core/agent-session-retry.d.ts.map +1 -1
  156. package/dist/core/agent-session-retry.js +232 -106
  157. package/dist/core/agent-session-retry.js.map +1 -1
  158. package/dist/core/agent-session-state.d.ts.map +1 -1
  159. package/dist/core/agent-session-state.js +2 -1
  160. package/dist/core/agent-session-state.js.map +1 -1
  161. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  162. package/dist/core/agent-session-tool-hooks.js +12 -0
  163. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  164. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  165. package/dist/core/agent-session-tool-registry.js +1 -1
  166. package/dist/core/agent-session-tool-registry.js.map +1 -1
  167. package/dist/core/agent-session-types.d.ts +3 -1
  168. package/dist/core/agent-session-types.d.ts.map +1 -1
  169. package/dist/core/agent-session-types.js.map +1 -1
  170. package/dist/core/agent-session.d.ts +12 -1
  171. package/dist/core/agent-session.d.ts.map +1 -1
  172. package/dist/core/agent-session.js +7 -0
  173. package/dist/core/agent-session.js.map +1 -1
  174. package/dist/core/extensions/context-types.d.ts +25 -0
  175. package/dist/core/extensions/context-types.d.ts.map +1 -1
  176. package/dist/core/extensions/context-types.js.map +1 -1
  177. package/dist/core/extensions/index.d.ts +1 -1
  178. package/dist/core/extensions/index.d.ts.map +1 -1
  179. package/dist/core/extensions/index.js.map +1 -1
  180. package/dist/core/extensions/runner-context.d.ts +2 -1
  181. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  182. package/dist/core/extensions/runner-context.js +4 -0
  183. package/dist/core/extensions/runner-context.js.map +1 -1
  184. package/dist/core/extensions/runner.d.ts +3 -2
  185. package/dist/core/extensions/runner.d.ts.map +1 -1
  186. package/dist/core/extensions/runner.js +3 -1
  187. package/dist/core/extensions/runner.js.map +1 -1
  188. package/dist/core/model-fallback-failures.d.ts +30 -0
  189. package/dist/core/model-fallback-failures.d.ts.map +1 -0
  190. package/dist/core/model-fallback-failures.js +610 -0
  191. package/dist/core/model-fallback-failures.js.map +1 -0
  192. package/dist/core/retry-policy.d.ts +30 -0
  193. package/dist/core/retry-policy.d.ts.map +1 -0
  194. package/dist/core/retry-policy.js +25 -0
  195. package/dist/core/retry-policy.js.map +1 -0
  196. package/dist/core/sdk-types.d.ts +8 -2
  197. package/dist/core/sdk-types.d.ts.map +1 -1
  198. package/dist/core/sdk-types.js.map +1 -1
  199. package/dist/core/sdk.d.ts.map +1 -1
  200. package/dist/core/sdk.js +6 -1
  201. package/dist/core/sdk.js.map +1 -1
  202. package/dist/core/session-manager-core.d.ts +2 -0
  203. package/dist/core/session-manager-core.d.ts.map +1 -1
  204. package/dist/core/session-manager-core.js +7 -0
  205. package/dist/core/session-manager-core.js.map +1 -1
  206. package/dist/index-extensions.d.ts +1 -1
  207. package/dist/index-extensions.d.ts.map +1 -1
  208. package/dist/index-extensions.js.map +1 -1
  209. package/dist/index.d.ts +3 -0
  210. package/dist/index.d.ts.map +1 -1
  211. package/dist/index.js +2 -0
  212. package/dist/index.js.map +1 -1
  213. package/docs/intercom.md +5 -10
  214. package/docs/settings.md +7 -1
  215. package/docs/subagents.md +19 -19
  216. package/docs/workflows.md +21 -5
  217. package/examples/extensions/gondolin/package-lock.json +3 -3
  218. package/npm-shrinkwrap.json +29 -29
  219. package/package.json +2 -2
  220. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +0 -112
  221. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +0 -543
  222. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +0 -140
  223. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +0 -279
  224. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +0 -99
  225. package/dist/builtin/subagents/src/runs/background/async-execution.ts +0 -17
  226. package/dist/builtin/subagents/src/runs/background/async-resume.ts +0 -402
  227. package/dist/builtin/subagents/src/runs/background/async-status.ts +0 -411
  228. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +0 -196
  229. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +0 -102
  230. package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +0 -59
  231. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +0 -300
  232. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +0 -166
  233. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +0 -77
  234. package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +0 -48
  235. package/dist/builtin/subagents/src/runs/background/result-status.ts +0 -87
  236. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +0 -72
  237. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +0 -320
  238. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +0 -105
  239. package/dist/builtin/subagents/src/runs/background/run-status.ts +0 -358
  240. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +0 -457
  241. package/dist/builtin/subagents/src/runs/background/subagent-runner-dynamic.ts +0 -376
  242. package/dist/builtin/subagents/src/runs/background/subagent-runner-finalize.ts +0 -150
  243. package/dist/builtin/subagents/src/runs/background/subagent-runner-output.ts +0 -109
  244. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel-helpers.ts +0 -128
  245. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel.ts +0 -309
  246. package/dist/builtin/subagents/src/runs/background/subagent-runner-sequential.ts +0 -173
  247. package/dist/builtin/subagents/src/runs/background/subagent-runner-state.ts +0 -578
  248. package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +0 -330
  249. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +0 -330
  250. package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +0 -210
  251. package/dist/builtin/subagents/src/runs/background/subagent-runner-utils.ts +0 -69
  252. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +0 -90
  253. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +0 -12
  254. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-control.ts +0 -140
  255. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +0 -108
  256. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-types.ts +0 -17
  257. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +0 -558
  258. package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +0 -126
  259. package/dist/builtin/subagents/src/runs/shared/final-drain.ts +0 -39
  260. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -339
  261. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +0 -165
  262. package/dist/builtin/subagents/src/runs/shared/spawn-env.ts +0 -21
  263. package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +0 -86
  264. /package/dist/builtin/subagents/src/runs/{shared/nested-path.ts → inprocess/runtime-support/nested-paths.ts} +0 -0
@@ -1,48 +0,0 @@
1
- export interface RetrySchedulerTimers {
2
- setTimeout: typeof setTimeout;
3
- clearTimeout: typeof clearTimeout;
4
- }
5
-
6
- export interface RetryScheduler {
7
- has(key: string): boolean;
8
- attempt(key: string): number;
9
- schedule(key: string, callback: () => void): boolean;
10
- clear(key: string): void;
11
- clearAll(): void;
12
- }
13
-
14
- export function createRetryScheduler(
15
- timers: RetrySchedulerTimers,
16
- baseDelayMs: number,
17
- maxDelayMs: number,
18
- ): RetryScheduler {
19
- const pending = new Map<string, ReturnType<typeof setTimeout>>();
20
- const attempts = new Map<string, number>();
21
- const clear = (key: string) => {
22
- const timer = pending.get(key);
23
- if (timer) timers.clearTimeout(timer);
24
- pending.delete(key);
25
- attempts.delete(key);
26
- };
27
- return {
28
- has: (key) => pending.has(key),
29
- attempt: (key) => attempts.get(key) ?? 0,
30
- schedule(key, callback) {
31
- if (pending.has(key)) return false;
32
- const attempt = (attempts.get(key) ?? 0) + 1;
33
- attempts.set(key, attempt);
34
- const delay = Math.min(maxDelayMs, baseDelayMs * 2 ** Math.min(attempt - 1, 30));
35
- const timer = timers.setTimeout(() => {
36
- pending.delete(key);
37
- callback();
38
- }, delay);
39
- timer.unref?.();
40
- pending.set(key, timer);
41
- return true;
42
- },
43
- clear,
44
- clearAll() {
45
- for (const key of [...pending.keys()]) clear(key);
46
- },
47
- };
48
- }
@@ -1,87 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { ASYNC_DIR } from "../../shared/types.ts";
4
- import { NESTED_RUNS_DIR } from "../shared/nested-events.ts";
5
- import type { ResultFileData } from "./result-watcher-data.js";
6
-
7
- const TERMINAL_ASYNC_STATES = new Set(["complete", "failed", "paused"]);
8
- export const DEFAULT_MAX_STATUS_BYTES = 1024 * 1024;
9
-
10
- export interface ResultStatusFs {
11
- realpathSync: typeof fs.realpathSync;
12
- lstatSync: typeof fs.lstatSync;
13
- openSync: typeof fs.openSync;
14
- fstatSync: typeof fs.fstatSync;
15
- readSync: typeof fs.readSync;
16
- closeSync: typeof fs.closeSync;
17
- }
18
-
19
- export interface ResultStatusOptions {
20
- allowedRoots?: string[];
21
- maxBytes?: number;
22
- }
23
-
24
- function contained(root: string, candidate: string): boolean {
25
- const relative = path.relative(root, candidate);
26
- return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
27
- }
28
-
29
- function canonicalRoots(fsApi: ResultStatusFs, roots: string[]): string[] {
30
- const resolved: string[] = [];
31
- for (const root of roots) {
32
- try {
33
- resolved.push(fsApi.realpathSync(root));
34
- } catch {
35
- /* unavailable roots cannot authorize a status path */
36
- }
37
- }
38
- return resolved;
39
- }
40
-
41
- function readBoundedRegularFile(statusPath: string, fsApi: ResultStatusFs, maxBytes: number): string | undefined {
42
- const linkStat = fsApi.lstatSync(statusPath);
43
- if (linkStat.isSymbolicLink() || !linkStat.isFile() || linkStat.size > maxBytes) return undefined;
44
- const noFollow = typeof fs.constants.O_NOFOLLOW === "number" ? fs.constants.O_NOFOLLOW : 0;
45
- const handle = fsApi.openSync(statusPath, fs.constants.O_RDONLY | noFollow);
46
- try {
47
- const stat = fsApi.fstatSync(handle);
48
- if (!stat.isFile() || stat.size > maxBytes) return undefined;
49
- const buffer = Buffer.alloc(stat.size);
50
- let offset = 0;
51
- while (offset < buffer.length) {
52
- const count = fsApi.readSync(handle, buffer, offset, buffer.length - offset, offset);
53
- if (count === 0) break;
54
- offset += count;
55
- }
56
- return buffer.subarray(0, offset).toString("utf-8");
57
- } finally {
58
- fsApi.closeSync(handle);
59
- }
60
- }
61
-
62
- export function modernResultHasTerminalStatus(
63
- data: ResultFileData,
64
- fsApi: ResultStatusFs = fs,
65
- options: ResultStatusOptions = {},
66
- ): boolean {
67
- if (!Object.hasOwn(data, "asyncDir")) return true;
68
- const asyncDir = data.asyncDir?.trim();
69
- const resultRunId = data.runId?.trim() || data.id?.trim();
70
- if (!asyncDir || !resultRunId) return false;
71
- try {
72
- const canonicalAsyncDir = fsApi.realpathSync(asyncDir);
73
- const roots = canonicalRoots(fsApi, options.allowedRoots ?? [ASYNC_DIR, NESTED_RUNS_DIR]);
74
- if (!roots.some((root) => contained(root, canonicalAsyncDir))) return false;
75
- const statusPath = path.join(canonicalAsyncDir, "status.json");
76
- const content = readBoundedRegularFile(statusPath, fsApi, options.maxBytes ?? DEFAULT_MAX_STATUS_BYTES);
77
- if (content === undefined) return false;
78
- const status = JSON.parse(content) as { state?: string; runId?: string };
79
- return (
80
- status.runId?.trim() === resultRunId &&
81
- typeof status.state === "string" &&
82
- TERMINAL_ASYNC_STATES.has(status.state)
83
- );
84
- } catch {
85
- return false;
86
- }
87
- }
@@ -1,72 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import type { NestedRunSummary } from "../../shared/types.ts";
3
- import { sanitizeSummary } from "../shared/nested-events.ts";
4
-
5
- export interface ResultFileChild {
6
- agent?: string;
7
- output?: string;
8
- error?: string;
9
- success?: boolean;
10
- sessionFile?: string;
11
- artifactPaths?: { outputPath?: string };
12
- intercomTarget?: string;
13
- children?: unknown;
14
- }
15
-
16
- export interface ResultFileData {
17
- id?: string;
18
- runId?: string;
19
- agent?: string;
20
- success?: boolean;
21
- state?: string;
22
- mode?: string;
23
- summary?: string;
24
- results?: ResultFileChild[];
25
- nestedChildren?: unknown;
26
- sessionId?: string;
27
- cwd?: string;
28
- sessionFile?: string;
29
- asyncDir?: string;
30
- intercomTarget?: string;
31
- }
32
-
33
- function stableStringify(value: unknown): string {
34
- if (value === null || value === undefined || typeof value !== "object") {
35
- const serialized = JSON.stringify(value);
36
- return serialized === undefined ? "undefined" : serialized;
37
- }
38
- if (Array.isArray(value)) return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
39
- const object = value as Record<string, unknown>;
40
- return `{${Object.keys(object)
41
- .sort()
42
- .map((key) => `${JSON.stringify(key)}:${stableStringify(object[key])}`)
43
- .join(",")}}`;
44
- }
45
-
46
- /** Stable identity for every value exposed through local or Intercom completion delivery. */
47
- export function buildCompletionSignature(delivery: Record<string, unknown>): string {
48
- return createHash("sha256").update(stableStringify(delivery)).digest("hex");
49
- }
50
-
51
- export function sanitizeNestedResultChildren(
52
- value: unknown,
53
- resultPath: string,
54
- label: string,
55
- ): NestedRunSummary[] | undefined {
56
- if (value === undefined) return undefined;
57
- if (!Array.isArray(value)) {
58
- console.error(
59
- `Ignoring invalid nested children in subagent result file '${resultPath}' at ${label}: expected an array.`,
60
- );
61
- return undefined;
62
- }
63
- const children = value
64
- .map((child) => sanitizeSummary(child))
65
- .filter((child): child is NestedRunSummary => Boolean(child));
66
- if (children.length !== value.length) {
67
- console.error(
68
- `Ignoring ${value.length - children.length} invalid nested child record(s) in subagent result file '${resultPath}' at ${label}.`,
69
- );
70
- }
71
- return children.length ? children : undefined;
72
- }
@@ -1,320 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { isSafeFsWatchPathError, watchWithErrorHandler } from "@bastani/atomic";
4
- import { createFileCoalescer } from "../../shared/file-coalescer.js";
5
- import type { IntercomEventBus, SubagentState } from "../../shared/types.ts";
6
- import { processResultEntry, type ResultProcessorFs } from "./result-delivery-processor.js";
7
- import { claimIdFromScheduleKey, claimScheduleKey, listResultClaims } from "./result-file-claims.js";
8
- import { createRetryScheduler } from "./result-retry-scheduler.js";
9
- import type { ResultFileData } from "./result-watcher-data.js";
10
-
11
- const WATCHER_RESTART_DELAY_MS = 3000;
12
- const POLL_INTERVAL_MS = 3000;
13
- const DIRECTORY_RESCAN_DELAY_MS = 50;
14
- const STATUS_RECHECK_BASE_MS = 250;
15
- const STATUS_RECHECK_MAX_MS = 30_000;
16
- const DELIVERY_RETRY_BASE_MS = 1000;
17
- const DELIVERY_RETRY_MAX_MS = 30_000;
18
-
19
- type ResultWatcherFs = ResultProcessorFs & Pick<typeof fs, "existsSync" | "readdirSync" | "mkdirSync" | "watch">;
20
- type ResultWatcherTimers = {
21
- setTimeout: typeof setTimeout;
22
- clearTimeout: typeof clearTimeout;
23
- setInterval: typeof setInterval;
24
- clearInterval: typeof clearInterval;
25
- };
26
- type ResultWatcherSafeWatch = typeof watchWithErrorHandler;
27
-
28
- type ResultWatcherDeps = {
29
- fs?: ResultWatcherFs;
30
- timers?: ResultWatcherTimers;
31
- safeWatch?: ResultWatcherSafeWatch;
32
- statusRecheckBaseMs?: number;
33
- statusRecheckMaxMs?: number;
34
- deliveryRetryBaseMs?: number;
35
- deliveryRetryMaxMs?: number;
36
- maxNoProgressFailures?: number;
37
- intercomTimeoutMs?: number | false;
38
- allowedStatusRoots?: string[];
39
- maxStatusBytes?: number;
40
- };
41
-
42
- function getErrorCode(error: unknown): string | undefined {
43
- return typeof error === "object" && error !== null && "code" in error
44
- ? (error as NodeJS.ErrnoException).code
45
- : undefined;
46
- }
47
-
48
- function isNotFoundError(error: unknown): boolean {
49
- return getErrorCode(error) === "ENOENT";
50
- }
51
-
52
- function shouldFallBackToPolling(error: unknown): boolean {
53
- const code = getErrorCode(error);
54
- return code === "EMFILE" || code === "ENOSPC" || isSafeFsWatchPathError(error);
55
- }
56
-
57
- export function createResultWatcher(
58
- pi: { events: IntercomEventBus },
59
- state: SubagentState,
60
- resultsDir: string,
61
- completionTtlMs: number,
62
- deps: ResultWatcherDeps = {},
63
- ): { startResultWatcher: () => void; primeExistingResults: () => void; stopResultWatcher: () => void } {
64
- const fsApi = deps.fs ?? fs;
65
- const timers = deps.timers ?? { setTimeout, clearTimeout, setInterval, clearInterval };
66
- const safeWatch = deps.safeWatch ?? watchWithErrorHandler;
67
- let directoryRescanTimer: ReturnType<typeof setTimeout> | null = null;
68
- let stopped = false;
69
- let processingEpoch = 0;
70
- let watcherInstallation = 0;
71
- let activeWatcherInstallation = 0;
72
- const inFlight = new Set<string>();
73
- const rerunAfterFlight = new Set<string>();
74
- const statusRetries = createRetryScheduler(
75
- timers,
76
- deps.statusRecheckBaseMs ?? STATUS_RECHECK_BASE_MS,
77
- deps.statusRecheckMaxMs ?? STATUS_RECHECK_MAX_MS,
78
- );
79
- const deliveryRetries = createRetryScheduler(
80
- timers,
81
- deps.deliveryRetryBaseMs ?? DELIVERY_RETRY_BASE_MS,
82
- deps.deliveryRetryMaxMs ?? DELIVERY_RETRY_MAX_MS,
83
- );
84
- const isEpochActive = (epoch: number): boolean => !stopped && epoch === processingEpoch;
85
- const entryExists = (entry: string): boolean => {
86
- const claimId = claimIdFromScheduleKey(entry);
87
- return claimId
88
- ? Boolean(listResultClaims(resultsDir, fsApi).some((claim) => claim.id === claimId))
89
- : fsApi.existsSync(path.join(resultsDir, entry));
90
- };
91
- const ownsResult = (data: ResultFileData): boolean =>
92
- !stopped &&
93
- (!data.sessionId || data.sessionId === state.currentSessionId) &&
94
- Boolean(data.sessionId || !data.cwd || (state.baseCwd && data.cwd === state.baseCwd));
95
-
96
- const coalescerKey = (entry: string, epoch: number): string => `${epoch}\0${entry}`;
97
- const parseCoalescerKey = (key: string): { epoch: number; entry: string } => {
98
- const split = key.indexOf("\0");
99
- return { epoch: Number(key.slice(0, split)), entry: key.slice(split + 1) };
100
- };
101
- const scheduleResultEntry = (entry: string, delay = 0, epoch = processingEpoch): boolean => {
102
- if (!isEpochActive(epoch) || statusRetries.has(entry) || deliveryRetries.has(entry)) return false;
103
- return state.resultFileCoalescer.schedule(coalescerKey(entry, epoch), delay);
104
- };
105
- const scheduleStatusRecheck = (entry: string, epoch: number) => {
106
- if (!isEpochActive(epoch)) return;
107
- statusRetries.schedule(entry, () => {
108
- if (!isEpochActive(epoch) || !entryExists(entry)) {
109
- statusRetries.clear(entry);
110
- return;
111
- }
112
- scheduleResultEntry(entry, 0, epoch);
113
- });
114
- };
115
- const scheduleDeliveryRetry = (entry: string, epoch: number) => {
116
- if (!isEpochActive(epoch)) return;
117
- deliveryRetries.schedule(entry, () => {
118
- if (!isEpochActive(epoch) || !entryExists(entry)) {
119
- deliveryRetries.clear(entry);
120
- return;
121
- }
122
- scheduleResultEntry(entry, 0, epoch);
123
- });
124
- };
125
-
126
- const handleResult = async (entry: string, epoch: number) => {
127
- if (!isEpochActive(epoch)) return;
128
- const flightKey = coalescerKey(entry, epoch);
129
- if (inFlight.has(flightKey)) {
130
- rerunAfterFlight.add(flightKey);
131
- return;
132
- }
133
- inFlight.add(flightKey);
134
- try {
135
- const outcome = await processResultEntry(entry, {
136
- pi,
137
- state,
138
- resultsDir,
139
- completionTtlMs,
140
- fsApi,
141
- allowedStatusRoots: deps.allowedStatusRoots,
142
- maxStatusBytes: deps.maxStatusBytes,
143
- maxNoProgressFailures: deps.maxNoProgressFailures,
144
- intercomTimeoutMs: deps.intercomTimeoutMs,
145
- isActive: () => isEpochActive(epoch),
146
- ownsResult: (data) => isEpochActive(epoch) && ownsResult(data),
147
- });
148
- const nextEntry = outcome.entry ?? entry;
149
- if (outcome.status === "status-pending") scheduleStatusRecheck(nextEntry, epoch);
150
- else if (outcome.status === "delivery-retry") scheduleDeliveryRetry(nextEntry, epoch);
151
- else {
152
- statusRetries.clear(entry);
153
- deliveryRetries.clear(entry);
154
- }
155
- } catch (error) {
156
- if (!isNotFoundError(error)) {
157
- console.error(`Failed to process subagent result entry '${entry}':`, error);
158
- if (isEpochActive(epoch) && entryExists(entry)) scheduleDeliveryRetry(entry, epoch);
159
- }
160
- } finally {
161
- inFlight.delete(flightKey);
162
- if (rerunAfterFlight.delete(flightKey) && entryExists(entry)) scheduleResultEntry(entry, 0, epoch);
163
- }
164
- };
165
-
166
- state.resultFileCoalescer = createFileCoalescer(
167
- (key) => {
168
- const { epoch, entry } = parseCoalescerKey(key);
169
- void handleResult(entry, epoch);
170
- },
171
- 50,
172
- { setTimeout: timers.setTimeout, clearTimeout: timers.clearTimeout },
173
- );
174
-
175
- const primeExistingResults = () => {
176
- if (stopped) return;
177
- const epoch = processingEpoch;
178
- try {
179
- for (const file of fsApi.readdirSync(resultsDir))
180
- if (file.endsWith(".json")) scheduleResultEntry(file, 0, epoch);
181
- for (const claim of listResultClaims(resultsDir, fsApi))
182
- scheduleResultEntry(claimScheduleKey(claim.id), 0, epoch);
183
- } catch (error) {
184
- if (!isNotFoundError(error)) console.error(`Failed to scan subagent result directory '${resultsDir}':`, error);
185
- }
186
- };
187
- const scheduleDirectoryRescan = (epoch: number) => {
188
- if (!isEpochActive(epoch)) return;
189
- if (directoryRescanTimer) timers.clearTimeout(directoryRescanTimer);
190
- directoryRescanTimer = timers.setTimeout(() => {
191
- directoryRescanTimer = null;
192
- if (isEpochActive(epoch)) primeExistingResults();
193
- }, DIRECTORY_RESCAN_DELAY_MS);
194
- directoryRescanTimer.unref?.();
195
- };
196
-
197
- const startPollingFallback = (reason: unknown, epoch: number, installation?: number) => {
198
- if (!isEpochActive(epoch) || (installation !== undefined && installation !== activeWatcherInstallation)) return;
199
- state.watcher?.close();
200
- state.watcher = null;
201
- activeWatcherInstallation = 0;
202
- if (state.watcherRestartTimer) return;
203
- console.error(
204
- `Subagent result watcher for '${resultsDir}' fell back to polling because native fs.watch is unavailable (${getErrorCode(reason) ?? "unknown error"}).`,
205
- );
206
- primeExistingResults();
207
- state.watcherRestartTimer = timers.setInterval(() => {
208
- if (isEpochActive(epoch)) primeExistingResults();
209
- }, POLL_INTERVAL_MS);
210
- state.watcherRestartTimer.unref?.();
211
- };
212
-
213
- function scheduleRestart(epoch: number): void {
214
- if (!isEpochActive(epoch) || state.watcherRestartTimer) return;
215
- let timer!: ReturnType<typeof setTimeout>;
216
- timer = timers.setTimeout(() => {
217
- if (state.watcherRestartTimer !== timer || !isEpochActive(epoch)) return;
218
- state.watcherRestartTimer = null;
219
- try {
220
- fsApi.mkdirSync(resultsDir, { recursive: true });
221
- openResultWatcher(epoch);
222
- } catch (error) {
223
- if (!isEpochActive(epoch)) return;
224
- if (shouldFallBackToPolling(error)) startPollingFallback(error, epoch);
225
- else {
226
- console.error(`Failed to restart subagent result watcher for '${resultsDir}':`, error);
227
- scheduleRestart(epoch);
228
- }
229
- }
230
- }, WATCHER_RESTART_DELAY_MS);
231
- state.watcherRestartTimer = timer;
232
- timer.unref?.();
233
- }
234
-
235
- function openResultWatcher(epoch: number): void {
236
- if (!isEpochActive(epoch) || state.watcher) return;
237
- const installation = ++watcherInstallation;
238
- let handle: fs.FSWatcher | null = null;
239
- let pendingSynchronousError: Error | undefined;
240
- const handleWatcherError = (error: Error) => {
241
- if (!handle) {
242
- pendingSynchronousError = error;
243
- return;
244
- }
245
- if (!isEpochActive(epoch) || installation !== activeWatcherInstallation || state.watcher !== handle) return;
246
- if (shouldFallBackToPolling(error)) {
247
- startPollingFallback(error, epoch, installation);
248
- return;
249
- }
250
- console.error(`Subagent result watcher failed for '${resultsDir}':`, error);
251
- handle.close();
252
- if (state.watcher === handle) state.watcher = null;
253
- activeWatcherInstallation = 0;
254
- scheduleRestart(epoch);
255
- };
256
- try {
257
- handle = safeWatch(
258
- resultsDir,
259
- (_event, file) => {
260
- if (!isEpochActive(epoch) || installation !== activeWatcherInstallation) return;
261
- if (file) {
262
- const name = file.toString();
263
- if (name.endsWith(".json")) scheduleResultEntry(name, 50, epoch);
264
- }
265
- scheduleDirectoryRescan(epoch);
266
- },
267
- handleWatcherError,
268
- { watch: fsApi.watch, realpathSyncNative: fsApi.realpathSync?.native },
269
- );
270
- if (!handle) throw pendingSynchronousError ?? new Error("Result watcher installation returned no handle");
271
- if (!isEpochActive(epoch)) {
272
- handle.close();
273
- return;
274
- }
275
- state.watcher = handle;
276
- activeWatcherInstallation = installation;
277
- handle.unref?.();
278
- if (pendingSynchronousError) handleWatcherError(pendingSynchronousError);
279
- } catch (error) {
280
- handle?.close();
281
- if (state.watcher === handle) state.watcher = null;
282
- activeWatcherInstallation = 0;
283
- if (!isEpochActive(epoch)) return;
284
- if (shouldFallBackToPolling(error)) startPollingFallback(error, epoch);
285
- else {
286
- console.error(`Failed to start subagent result watcher for '${resultsDir}':`, error);
287
- scheduleRestart(epoch);
288
- }
289
- }
290
- }
291
-
292
- const startResultWatcher = () => {
293
- stopped = false;
294
- processingEpoch += 1;
295
- if (state.watcherRestartTimer) {
296
- timers.clearTimeout(state.watcherRestartTimer);
297
- timers.clearInterval(state.watcherRestartTimer);
298
- state.watcherRestartTimer = null;
299
- }
300
- openResultWatcher(processingEpoch);
301
- };
302
- const stopResultWatcher = () => {
303
- stopped = true;
304
- processingEpoch += 1;
305
- state.watcher?.close();
306
- state.watcher = null;
307
- activeWatcherInstallation = 0;
308
- if (state.watcherRestartTimer) {
309
- timers.clearTimeout(state.watcherRestartTimer);
310
- timers.clearInterval(state.watcherRestartTimer);
311
- }
312
- state.watcherRestartTimer = null;
313
- if (directoryRescanTimer) timers.clearTimeout(directoryRescanTimer);
314
- directoryRescanTimer = null;
315
- statusRetries.clearAll();
316
- deliveryRetries.clearAll();
317
- state.resultFileCoalescer.clear();
318
- };
319
- return { startResultWatcher, primeExistingResults, stopResultWatcher };
320
- }
@@ -1,105 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { ASYNC_DIR, RESULTS_DIR, type SubagentState } from "../../shared/types.ts";
4
- import {
5
- assertSafeNestedId,
6
- findNestedRunMatchesById,
7
- type NestedRoute,
8
- type NestedRunMatch,
9
- type NestedRunResolutionScope,
10
- } from "../shared/nested-events.ts";
11
- import { type AsyncRunLocation, findAsyncRunPrefixMatches } from "./async-resume.ts";
12
-
13
- export type ResolvedSubagentRunId =
14
- | { kind: "foreground"; id: string }
15
- | { kind: "async"; id: string; location: AsyncRunLocation }
16
- | { kind: "nested"; id: string; match: NestedRunMatch };
17
-
18
- export interface ResolveSubagentRunIdDeps {
19
- state?: SubagentState;
20
- asyncDirRoot?: string;
21
- resultsDir?: string;
22
- nested?: NestedRunResolutionScope;
23
- }
24
-
25
- function exactAsyncLocation(id: string, asyncDirRoot: string, resultsDir: string): AsyncRunLocation | undefined {
26
- const asyncDir = path.join(asyncDirRoot, id);
27
- const resultPath = path.join(resultsDir, `${id}.json`);
28
- if (!fs.existsSync(asyncDir) && !fs.existsSync(resultPath)) return undefined;
29
- return {
30
- asyncDir: fs.existsSync(asyncDir) ? asyncDir : null,
31
- resultPath: fs.existsSync(resultPath) ? resultPath : null,
32
- resolvedId: id,
33
- };
34
- }
35
-
36
- function foregroundIds(state: SubagentState | undefined): string[] {
37
- if (!state) return [];
38
- return [...new Set([...state.foregroundControls.keys(), ...(state.foregroundRuns?.keys() ?? [])])];
39
- }
40
-
41
- export function nestedScopeFromState(state: SubagentState | undefined): NestedRunResolutionScope | undefined {
42
- if (!state) return undefined;
43
- const routes: NestedRoute[] = [];
44
- const seen = new Set<string>();
45
- const add = (route: NestedRoute | undefined) => {
46
- if (!route) return;
47
- const key = `${route.rootRunId}:${route.eventSink}:${route.controlInbox}`;
48
- if (seen.has(key)) return;
49
- seen.add(key);
50
- routes.push(route);
51
- };
52
- for (const control of state.foregroundControls.values()) add(control.nestedRoute as NestedRoute | undefined);
53
- for (const job of state.asyncJobs.values()) add(job.nestedRoute as NestedRoute | undefined);
54
- return { routes };
55
- }
56
-
57
- function asyncPrefixMatches(
58
- prefix: string,
59
- asyncDirRoot: string,
60
- resultsDir: string,
61
- ): Array<{ id: string; location: AsyncRunLocation }> {
62
- return findAsyncRunPrefixMatches(prefix, asyncDirRoot, resultsDir);
63
- }
64
-
65
- export function resolveSubagentRunId(
66
- id: string,
67
- deps: ResolveSubagentRunIdDeps = {},
68
- ): ResolvedSubagentRunId | undefined {
69
- assertSafeNestedId("id", id);
70
- const asyncDirRoot = deps.asyncDirRoot ?? ASYNC_DIR;
71
- const resultsDir = deps.resultsDir ?? RESULTS_DIR;
72
-
73
- const nestedScope = deps.nested ?? nestedScopeFromState(deps.state);
74
- if (deps.state?.foregroundControls.has(id) || deps.state?.foregroundRuns?.has(id)) return { kind: "foreground", id };
75
- const exactAsync = exactAsyncLocation(id, asyncDirRoot, resultsDir);
76
- if (exactAsync) return { kind: "async", id, location: exactAsync };
77
- const exactNested = findNestedRunMatchesById(id, nestedScope ? { scope: nestedScope } : {});
78
- if (exactNested.length > 1)
79
- throw new Error(
80
- `Nested run id '${id}' is ambiguous across authorized registries. Provide a more specific authorized run id.`,
81
- );
82
- if (exactNested[0]) return { kind: "nested", id, match: exactNested[0] };
83
-
84
- const matches: ResolvedSubagentRunId[] = [];
85
- for (const foregroundId of foregroundIds(deps.state).filter((candidate) => candidate.startsWith(id))) {
86
- matches.push({ kind: "foreground", id: foregroundId });
87
- }
88
- for (const match of asyncPrefixMatches(id, asyncDirRoot, resultsDir)) {
89
- matches.push({ kind: "async", id: match.id, location: match.location });
90
- }
91
- for (const match of findNestedRunMatchesById(
92
- id,
93
- nestedScope ? { prefix: true, scope: nestedScope } : { prefix: true },
94
- )) {
95
- matches.push({ kind: "nested", id: match.run.id, match });
96
- }
97
- const unique = new Map(matches.map((match) => [`${match.kind}:${match.id}`, match]));
98
- const values = [...unique.values()];
99
- if (values.length > 1) {
100
- throw new Error(
101
- `Ambiguous subagent run id prefix '${id}' matched: ${values.map((match) => `${match.kind}:${match.id}`).join(", ")}. Provide a longer id.`,
102
- );
103
- }
104
- return values[0];
105
- }