@bastani/atomic 0.9.13-alpha.2 → 0.9.13-alpha.3

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 (255) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/dist/builtin/i-have-adhd/package.json +1 -1
  4. package/dist/builtin/intercom/CHANGELOG.md +14 -0
  5. package/dist/builtin/intercom/README.md +12 -12
  6. package/dist/builtin/intercom/incoming-message-delivery.ts +1 -1
  7. package/dist/builtin/intercom/index.ts +1 -1
  8. package/dist/builtin/intercom/intercom-tool.ts +1 -1
  9. package/dist/builtin/intercom/intercom-utils.ts +3 -6
  10. package/dist/builtin/intercom/package.json +1 -1
  11. package/dist/builtin/intercom/result-renderers.ts +1 -1
  12. package/dist/builtin/intercom/session-target.ts +1 -15
  13. package/dist/builtin/intercom/skills/intercom/SKILL.md +6 -6
  14. package/dist/builtin/intercom/terminal-ordering-barrier.ts +2 -2
  15. package/dist/builtin/intercom/ui/inline-message.ts +7 -5
  16. package/dist/builtin/intercom/ui/session-list.ts +23 -10
  17. package/dist/builtin/mcp/package.json +1 -1
  18. package/dist/builtin/subagents/CHANGELOG.md +19 -0
  19. package/dist/builtin/subagents/README.md +29 -73
  20. package/dist/builtin/subagents/package.json +2 -2
  21. package/dist/builtin/subagents/prompts/parallel-cleanup.md +1 -1
  22. package/dist/builtin/subagents/prompts/parallel-review.md +1 -1
  23. package/dist/builtin/subagents/prompts/review-loop.md +2 -2
  24. package/dist/builtin/subagents/skills/subagent/SKILL.md +23 -59
  25. package/dist/builtin/subagents/skills/tmux/SKILL.md +1 -1
  26. package/dist/builtin/subagents/src/extension/control-notices.ts +2 -3
  27. package/dist/builtin/subagents/src/extension/doctor.ts +2 -14
  28. package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -17
  29. package/dist/builtin/subagents/src/extension/index.ts +9 -70
  30. package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
  31. package/dist/builtin/subagents/src/extension/prompt-guidance.ts +2 -2
  32. package/dist/builtin/subagents/src/extension/schemas.ts +2 -8
  33. package/dist/builtin/subagents/src/extension/tool-description.ts +2 -3
  34. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -1
  35. package/dist/builtin/subagents/src/intercom/result-intercom.ts +5 -46
  36. package/dist/builtin/subagents/src/runs/{background → foreground}/completion-notification.ts +2 -2
  37. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +25 -88
  38. package/dist/builtin/subagents/src/runs/{background → foreground}/notify.ts +5 -5
  39. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +11 -28
  40. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +3 -0
  41. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +2 -0
  42. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +0 -5
  43. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +30 -20
  44. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +4 -4
  45. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +0 -13
  46. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +104 -48
  47. package/dist/builtin/subagents/src/runs/inprocess/index.ts +1 -1
  48. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +5 -5
  49. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-api.ts +0 -7
  50. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-core.ts +0 -10
  51. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-projection.ts +3 -108
  52. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-sanitize.ts +0 -4
  53. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +1 -1
  54. package/dist/builtin/subagents/src/shared/formatters.ts +3 -5
  55. package/dist/builtin/subagents/src/shared/model-resolution.ts +78 -0
  56. package/dist/builtin/subagents/src/shared/status-format.ts +4 -11
  57. package/dist/builtin/subagents/src/shared/types-config.ts +10 -9
  58. package/dist/builtin/subagents/src/shared/types-nested.ts +127 -0
  59. package/dist/builtin/subagents/src/shared/types-results.ts +2 -8
  60. package/dist/builtin/subagents/src/shared/types-runtime.ts +0 -9
  61. package/dist/builtin/subagents/src/shared/types.ts +1 -1
  62. package/dist/builtin/subagents/src/shared/utils.ts +1 -109
  63. package/dist/builtin/subagents/src/slash/slash-commands.ts +7 -17
  64. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -252
  65. package/dist/builtin/subagents/src/tui/render-layout.ts +4 -4
  66. package/dist/builtin/subagents/src/tui/render-result.ts +0 -9
  67. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -34
  68. package/dist/builtin/subagents/src/tui/render.ts +0 -2
  69. package/dist/builtin/web-access/package.json +1 -1
  70. package/dist/builtin/workflows/CHANGELOG.md +12 -0
  71. package/dist/builtin/workflows/README.md +3 -3
  72. package/dist/builtin/workflows/package.json +1 -1
  73. package/dist/builtin/workflows/src/durable/backend.ts +11 -0
  74. package/dist/builtin/workflows/src/durable/completed-catalog-stage-groups.ts +63 -25
  75. package/dist/builtin/workflows/src/durable/completed-catalog.ts +166 -25
  76. package/dist/builtin/workflows/src/durable/dbos-backend.ts +42 -24
  77. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +2 -1
  78. package/dist/builtin/workflows/src/durable/run-timing.ts +28 -7
  79. package/dist/builtin/workflows/src/durable/scoped-backend.ts +4 -0
  80. package/dist/builtin/workflows/src/durable/targeted-inspection.ts +110 -0
  81. package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +2 -2
  82. package/dist/builtin/workflows/src/engine/runtime.ts +19 -2
  83. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +3 -0
  84. package/dist/builtin/workflows/src/extension/index.bundle.mjs +680 -152
  85. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -0
  86. package/dist/builtin/workflows/src/extension/ui-surface.ts +2 -0
  87. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +6 -1
  88. package/dist/builtin/workflows/src/extension/workflow-targets.ts +18 -10
  89. package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +31 -16
  90. package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +75 -3
  91. package/dist/builtin/workflows/src/extension/workflow-tool.ts +53 -6
  92. package/dist/builtin/workflows/src/runs/background/run-inspect.ts +5 -1
  93. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +94 -60
  94. package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +1 -1
  95. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +7 -7
  96. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +25 -4
  97. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +4 -1
  98. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +2 -0
  99. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +142 -4
  100. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +2 -0
  101. package/dist/builtin/workflows/src/runs/foreground/stage-session-heartbeat.ts +88 -0
  102. package/dist/builtin/workflows/src/tui/run-detail.ts +25 -12
  103. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +1 -0
  104. package/dist/core/agent-session-events.d.ts.map +1 -1
  105. package/dist/core/agent-session-events.js +16 -5
  106. package/dist/core/agent-session-events.js.map +1 -1
  107. package/dist/core/agent-session-export.d.ts.map +1 -1
  108. package/dist/core/agent-session-export.js +2 -1
  109. package/dist/core/agent-session-export.js.map +1 -1
  110. package/dist/core/agent-session-methods.d.ts +8 -4
  111. package/dist/core/agent-session-methods.d.ts.map +1 -1
  112. package/dist/core/agent-session-methods.js.map +1 -1
  113. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  114. package/dist/core/agent-session-prompt.js +5 -0
  115. package/dist/core/agent-session-prompt.js.map +1 -1
  116. package/dist/core/agent-session-summary.d.ts +21 -0
  117. package/dist/core/agent-session-summary.d.ts.map +1 -0
  118. package/dist/core/agent-session-summary.js +156 -0
  119. package/dist/core/agent-session-summary.js.map +1 -0
  120. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  121. package/dist/core/agent-session-tool-registry.js +0 -4
  122. package/dist/core/agent-session-tool-registry.js.map +1 -1
  123. package/dist/core/agent-session-transfer.d.ts.map +1 -1
  124. package/dist/core/agent-session-transfer.js +0 -2
  125. package/dist/core/agent-session-transfer.js.map +1 -1
  126. package/dist/core/agent-session-tree.d.ts.map +1 -1
  127. package/dist/core/agent-session-tree.js +6 -0
  128. package/dist/core/agent-session-tree.js.map +1 -1
  129. package/dist/core/agent-session.d.ts +7 -3
  130. package/dist/core/agent-session.d.ts.map +1 -1
  131. package/dist/core/agent-session.js +8 -5
  132. package/dist/core/agent-session.js.map +1 -1
  133. package/dist/core/atomic-guide-command.js +2 -2
  134. package/dist/core/atomic-guide-command.js.map +1 -1
  135. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  136. package/dist/core/compaction/branch-summarization.js +1 -0
  137. package/dist/core/compaction/branch-summarization.js.map +1 -1
  138. package/dist/core/compaction/index.d.ts +1 -0
  139. package/dist/core/compaction/index.d.ts.map +1 -1
  140. package/dist/core/compaction/index.js +1 -0
  141. package/dist/core/compaction/index.js.map +1 -1
  142. package/dist/core/compaction/session-summarization.d.ts +41 -0
  143. package/dist/core/compaction/session-summarization.d.ts.map +1 -0
  144. package/dist/core/compaction/session-summarization.js +97 -0
  145. package/dist/core/compaction/session-summarization.js.map +1 -0
  146. package/dist/core/extensions/ui-types.d.ts +2 -0
  147. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  148. package/dist/core/extensions/ui-types.js.map +1 -1
  149. package/dist/core/session-manager-core.d.ts +3 -1
  150. package/dist/core/session-manager-core.d.ts.map +1 -1
  151. package/dist/core/session-manager-core.js +9 -1
  152. package/dist/core/session-manager-core.js.map +1 -1
  153. package/dist/core/session-manager-entries.d.ts +24 -1
  154. package/dist/core/session-manager-entries.d.ts.map +1 -1
  155. package/dist/core/session-manager-entries.js +48 -0
  156. package/dist/core/session-manager-entries.js.map +1 -1
  157. package/dist/core/session-manager-list.d.ts.map +1 -1
  158. package/dist/core/session-manager-list.js +10 -0
  159. package/dist/core/session-manager-list.js.map +1 -1
  160. package/dist/core/session-manager-types.d.ts +10 -1
  161. package/dist/core/session-manager-types.d.ts.map +1 -1
  162. package/dist/core/session-manager-types.js.map +1 -1
  163. package/dist/core/settings-manager-basic-accessors.d.ts +3 -0
  164. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  165. package/dist/core/settings-manager-basic-accessors.js +5 -0
  166. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  167. package/dist/core/settings-manager.d.ts +1 -1
  168. package/dist/core/settings-manager.d.ts.map +1 -1
  169. package/dist/core/settings-manager.js.map +1 -1
  170. package/dist/core/settings-types.d.ts +4 -0
  171. package/dist/core/settings-types.d.ts.map +1 -1
  172. package/dist/core/settings-types.js.map +1 -1
  173. package/dist/core/slash-commands.js +2 -2
  174. package/dist/core/slash-commands.js.map +1 -1
  175. package/dist/core/tools/bash.d.ts +0 -15
  176. package/dist/core/tools/bash.d.ts.map +1 -1
  177. package/dist/core/tools/bash.js +3 -81
  178. package/dist/core/tools/bash.js.map +1 -1
  179. package/dist/core/tools/fetch-url.d.ts +12 -0
  180. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  181. package/dist/core/tools/fetch-url.js +76 -10
  182. package/dist/core/tools/fetch-url.js.map +1 -1
  183. package/dist/core/tools/index.js +4 -4
  184. package/dist/core/tools/index.js.map +1 -1
  185. package/dist/core/tools/session-temp-dir.d.ts +2 -2
  186. package/dist/core/tools/session-temp-dir.d.ts.map +1 -1
  187. package/dist/core/tools/session-temp-dir.js +6 -7
  188. package/dist/core/tools/session-temp-dir.js.map +1 -1
  189. package/dist/core/usage-totals.js +1 -1
  190. package/dist/core/usage-totals.js.map +1 -1
  191. package/dist/modes/interactive/components/footer.js +1 -1
  192. package/dist/modes/interactive/components/footer.js.map +1 -1
  193. package/dist/modes/interactive/components/host-session-picker.d.ts.map +1 -1
  194. package/dist/modes/interactive/components/host-session-picker.js +1 -0
  195. package/dist/modes/interactive/components/host-session-picker.js.map +1 -1
  196. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  197. package/dist/modes/interactive/components/session-selector-list.js +29 -4
  198. package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
  199. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  200. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  201. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  202. package/dist/modes/interactive/components/tree-selector-content.js +6 -0
  203. package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
  204. package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
  205. package/dist/modes/interactive-engine/protocol.js +4 -1
  206. package/dist/modes/interactive-engine/protocol.js.map +1 -1
  207. package/docs/environment-variables.md +1 -1
  208. package/docs/intercom.md +20 -21
  209. package/docs/quickstart.md +5 -1
  210. package/docs/sdk.md +1 -1
  211. package/docs/session-format.md +15 -0
  212. package/docs/sessions.md +8 -0
  213. package/docs/settings.md +6 -0
  214. package/docs/subagents.md +19 -31
  215. package/docs/tools.md +3 -5
  216. package/docs/usage.md +1 -1
  217. package/docs/windows.md +1 -1
  218. package/docs/workflows.md +60 -32
  219. package/npm-shrinkwrap.json +29 -29
  220. package/package.json +2 -2
  221. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +0 -331
  222. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +0 -161
  223. package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +0 -195
  224. package/dist/builtin/subagents/src/runs/inprocess/background.ts +0 -90
  225. package/dist/builtin/subagents/src/shared/types-async.ts +0 -247
  226. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -217
  227. package/dist/builtin/subagents/src/tui/render-widget.ts +0 -366
  228. package/dist/core/async/format.d.ts +0 -3
  229. package/dist/core/async/format.d.ts.map +0 -1
  230. package/dist/core/async/format.js +0 -52
  231. package/dist/core/async/format.js.map +0 -1
  232. package/dist/core/async/job-manager.d.ts +0 -40
  233. package/dist/core/async/job-manager.d.ts.map +0 -1
  234. package/dist/core/async/job-manager.js +0 -293
  235. package/dist/core/async/job-manager.js.map +0 -1
  236. package/dist/core/async/session-manager.d.ts +0 -16
  237. package/dist/core/async/session-manager.d.ts.map +0 -1
  238. package/dist/core/async/session-manager.js +0 -34
  239. package/dist/core/async/session-manager.js.map +0 -1
  240. package/dist/core/async/types.d.ts +0 -23
  241. package/dist/core/async/types.d.ts.map +0 -1
  242. package/dist/core/async/types.js +0 -2
  243. package/dist/core/async/types.js.map +0 -1
  244. package/dist/core/tools/bash-async-execution.d.ts +0 -27
  245. package/dist/core/tools/bash-async-execution.d.ts.map +0 -1
  246. package/dist/core/tools/bash-async-execution.js +0 -81
  247. package/dist/core/tools/bash-async-execution.js.map +0 -1
  248. package/dist/core/tools/bash-async-jobs.d.ts +0 -30
  249. package/dist/core/tools/bash-async-jobs.d.ts.map +0 -1
  250. package/dist/core/tools/bash-async-jobs.js +0 -87
  251. package/dist/core/tools/bash-async-jobs.js.map +0 -1
  252. package/dist/core/tools/bash-async-output.d.ts +0 -13
  253. package/dist/core/tools/bash-async-output.d.ts.map +0 -1
  254. package/dist/core/tools/bash-async-output.js +0 -122
  255. package/dist/core/tools/bash-async-output.js.map +0 -1
@@ -37555,6 +37555,8 @@ function summaryRows(detail, now) {
37555
37555
  rows.push(["error", detail.error.split(`
37556
37556
  `)[0] ?? ""]);
37557
37557
  }
37558
+ if (detail.resumeGuidance)
37559
+ rows.push(["guidance", detail.resumeGuidance]);
37558
37560
  return rows;
37559
37561
  }
37560
37562
  function artifactRowsFor(detail) {
@@ -37668,6 +37670,8 @@ function stateBadges(detail, theme) {
37668
37670
  switch (detail.status) {
37669
37671
  case "running":
37670
37672
  return [{ text: "● running", fg: theme.warning }];
37673
+ case "crashed":
37674
+ return [{ text: detail.resumable === true ? "✗ crashed · resumable" : "✗ crashed", fg: theme.error }];
37671
37675
  case "paused":
37672
37676
  return [{ text: "❚❚ paused", fg: theme.warning }];
37673
37677
  case "completed":
@@ -37690,6 +37694,8 @@ function stateLabel(detail) {
37690
37694
  switch (detail.status) {
37691
37695
  case "running":
37692
37696
  return "● running";
37697
+ case "crashed":
37698
+ return detail.resumable === true ? "✗ crashed · resumable" : "✗ crashed";
37693
37699
  case "paused":
37694
37700
  return "❚❚ paused";
37695
37701
  case "completed":
@@ -37729,8 +37735,10 @@ function renderIdentifierRows2(id, width, theme) {
37729
37735
  });
37730
37736
  }
37731
37737
  function renderDetailHintRows(detail, width, theme) {
37732
- const prefix = detail.endedAt === undefined ? detail.status === "paused" ? " workflow resume id=" : " workflow interrupt id=" : " workflow resume id=";
37733
- const suffix = detail.endedAt === undefined ? detail.status === "paused" ? " continue workflow " : " cancel " : " reopen graph ";
37738
+ const resumable = detail.status === "paused" && detail.resumable !== false || detail.resumable === true && (detail.status === "crashed" || detail.status === "failed" || detail.status === "blocked");
37739
+ const inspectOnly = detail.ownerActiveElsewhere === true || detail.status === "crashed" && !resumable || detail.endedAt !== undefined && !resumable;
37740
+ const prefix = inspectOnly ? " ▸ workflow status id=" : resumable ? " ▸ workflow resume id=" : " ▸ workflow interrupt id=";
37741
+ const suffix = inspectOnly ? detail.ownerActiveElsewhere === true ? " inspect; owner active elsewhere " : " inspect retained state " : resumable ? " continue workflow " : " cancel ";
37734
37742
  const continuation = " ";
37735
37743
  const rows = wrapIdentifierLines(detail.runId, width, prefix, continuation);
37736
37744
  const last = rows[rows.length - 1];
@@ -47839,7 +47847,7 @@ function isModelUsage(value2) {
47839
47847
  if (typeof value2 !== "object" || value2 === null || Array.isArray(value2))
47840
47848
  return false;
47841
47849
  const usage = value2;
47842
- return ["input", "output", "cacheRead", "cacheWrite", "cost", "turns"].every((key2) => usage[key2] === undefined || typeof usage[key2] === "number" && Number.isFinite(usage[key2]));
47850
+ return ["input", "output", "cacheRead", "cacheWrite", "cost", "turns"].every((key2) => usage[key2] === undefined || typeof usage[key2] === "number" && Number.isFinite(usage[key2]) && usage[key2] >= 0);
47843
47851
  }
47844
47852
  function isUiPromptKind(value2) {
47845
47853
  return value2 === "input" || value2 === "confirm" || value2 === "select" || value2 === "editor" || value2 === "custom";
@@ -48165,6 +48173,12 @@ class InMemoryDurableBackend {
48165
48173
  return this.getWorkflow(workflowId);
48166
48174
  }
48167
48175
  async hydrateWorkflow(_workflowId) {}
48176
+ async hydrateWorkflowForInspection(workflowId) {
48177
+ const handle = this.getLoadableWorkflow(workflowId);
48178
+ if (handle !== undefined)
48179
+ return { kind: "current", handle };
48180
+ return { kind: this.deletedWorkflowIds.has(workflowId) ? "deleted" : "absent" };
48181
+ }
48168
48182
  async hydrateResumableWorkflows() {}
48169
48183
  async prepareWorkflowCatalog() {
48170
48184
  return { resumable: this.listResumableWorkflows(), completed: this.listCompletedWorkflows() };
@@ -50300,17 +50314,24 @@ class DbosDurableBackend {
50300
50314
  throw first;
50301
50315
  }
50302
50316
  async hydrateWorkflow(workflowId) {
50303
- if (this.locallyRegistered.has(workflowId))
50304
- return;
50305
- const info = await this.sdk.retrieveWorkflow(workflowId);
50306
- if (info !== undefined) {
50307
- await this.hydrateInfo(info);
50308
- return;
50317
+ await this.hydrateWorkflowForInspection(workflowId);
50318
+ }
50319
+ async hydrateWorkflowForInspection(workflowId) {
50320
+ if (this.locallyRegistered.has(workflowId)) {
50321
+ const handle = this.getLoadableWorkflow(workflowId);
50322
+ return handle === undefined ? { kind: "malformed" } : { kind: "current", handle };
50309
50323
  }
50324
+ const info = await this.sdk.retrieveWorkflow(workflowId);
50325
+ if (info !== undefined)
50326
+ return await this.hydrateInfo(info);
50310
50327
  const records = await this.sdk.listStepRecords(workflowId);
50311
50328
  const deletion = classifyDbosDeletionTombstone(records, workflowId);
50312
- if (deletion !== "absent")
50313
- await this.suppressWorkflow(workflowId);
50329
+ if (deletion === "absent" && records.length === 0)
50330
+ return { kind: "absent" };
50331
+ await this.suppressWorkflow(workflowId);
50332
+ if (deletion === "absent")
50333
+ return { kind: "malformed" };
50334
+ return { kind: deletion === "current" ? "deleted" : "malformed" };
50314
50335
  }
50315
50336
  async hydrateResumableWorkflows() {
50316
50337
  const all = await this.sdk.listAllWorkflows();
@@ -50322,10 +50343,15 @@ class DbosDurableBackend {
50322
50343
  }
50323
50344
  async hydrateInfo(info) {
50324
50345
  const records = await this.sdk.listStepRecords(info.workflowId);
50346
+ const deletion = classifyDbosDeletionTombstone(records, info.workflowId);
50347
+ if (deletion !== "absent") {
50348
+ await this.suppressWorkflow(info.workflowId);
50349
+ return { kind: deletion === "current" ? "deleted" : "malformed" };
50350
+ }
50325
50351
  const metadata = classifyLatestMetadata(records, info.workflowId);
50326
50352
  if (metadata.kind !== "current") {
50327
50353
  await this.suppressWorkflow(info.workflowId);
50328
- return;
50354
+ return { kind: "malformed" };
50329
50355
  }
50330
50356
  const checkpoints = [];
50331
50357
  for (const record of records) {
@@ -50334,7 +50360,7 @@ class DbosDurableBackend {
50334
50360
  const classified = classifyCheckpointPayload(info.workflowId, record.stepName, record.output);
50335
50361
  if (classified.kind === "unknown") {
50336
50362
  await this.suppressWorkflow(info.workflowId);
50337
- return;
50363
+ return { kind: "malformed" };
50338
50364
  }
50339
50365
  checkpoints.push(classified.checkpoint);
50340
50366
  }
@@ -50345,14 +50371,21 @@ class DbosDurableBackend {
50345
50371
  for (const checkpoint of checkpoints)
50346
50372
  this.mem.recordCheckpoint(checkpoint);
50347
50373
  const current = this.mem.getWorkflow(info.workflowId);
50348
- if (current !== undefined) {
50349
- const pendingPrompts2 = this.promptReservations.hydrate(info.workflowId, metadata.metadata.pendingPrompts, records, metadata.metadata.promptReservationEpoch);
50350
- this.applyMetadata(info.workflowId, {
50351
- ...metadata.metadata,
50352
- pendingPrompts: pendingPrompts2,
50353
- completedCheckpoints: current.completedCheckpoints
50354
- });
50374
+ if (current === undefined) {
50375
+ await this.suppressWorkflow(info.workflowId);
50376
+ return { kind: "malformed" };
50355
50377
  }
50378
+ const pendingPrompts2 = this.promptReservations.hydrate(info.workflowId, metadata.metadata.pendingPrompts, records, metadata.metadata.promptReservationEpoch);
50379
+ this.applyMetadata(info.workflowId, {
50380
+ ...metadata.metadata,
50381
+ pendingPrompts: pendingPrompts2,
50382
+ completedCheckpoints: current.completedCheckpoints
50383
+ });
50384
+ const handle = this.getLoadableWorkflow(info.workflowId);
50385
+ if (handle !== undefined)
50386
+ return { kind: "current", handle };
50387
+ await this.suppressWorkflow(info.workflowId);
50388
+ return { kind: "malformed" };
50356
50389
  }
50357
50390
  async suppressWorkflow(workflowId) {
50358
50391
  this.invalid.add(workflowId);
@@ -51066,20 +51099,34 @@ function priorRunElapsedMs(backend, workflowId) {
51066
51099
  return elapsedMs;
51067
51100
  }
51068
51101
  function recordRunTimingCheckpoint(backend, run, options) {
51102
+ const checkpoint = runTimingCheckpoint(backend, run, options);
51103
+ if (checkpoint === undefined)
51104
+ return false;
51105
+ backend.recordCheckpoint(checkpoint);
51106
+ return true;
51107
+ }
51108
+ async function recordRunTimingCheckpointAsync(backend, run, options) {
51109
+ const checkpoint = runTimingCheckpoint(backend, run, options);
51110
+ if (checkpoint === undefined)
51111
+ return false;
51112
+ await backend.recordCheckpointAsync(checkpoint);
51113
+ return true;
51114
+ }
51115
+ function runTimingCheckpoint(backend, run, options) {
51069
51116
  const now = options?.now ?? Date.now();
51070
51117
  const elapsedMs = elapsedRunMs(run, now);
51071
51118
  if (elapsedMs <= 0)
51072
- return false;
51119
+ return;
51073
51120
  if (backend.listCheckpoints(run.id).length === 0)
51074
- return false;
51121
+ return;
51075
51122
  const recorded = priorRunElapsedMs(backend, run.id);
51076
51123
  if (recorded !== undefined) {
51077
51124
  if (elapsedMs <= recorded)
51078
- return false;
51125
+ return;
51079
51126
  if (options?.debounce === true && timingBucket(elapsedMs) === timingBucket(recorded))
51080
- return false;
51127
+ return;
51081
51128
  }
51082
- const checkpoint = {
51129
+ return {
51083
51130
  kind: "tool",
51084
51131
  workflowId: run.id,
51085
51132
  checkpointId: `run-timing:${elapsedMs}`,
@@ -51088,8 +51135,6 @@ function recordRunTimingCheckpoint(backend, run, options) {
51088
51135
  output: { elapsedMs },
51089
51136
  completedAt: now
51090
51137
  };
51091
- backend.recordCheckpoint(checkpoint);
51092
- return true;
51093
51138
  }
51094
51139
  function inheritedRunElapsedMs(input) {
51095
51140
  const now = input.now ?? Date.now();
@@ -52992,33 +53037,33 @@ function isRunStatus(value2) {
52992
53037
  return false;
52993
53038
  }
52994
53039
  }
52995
- function resolveRunId(target) {
53040
+ function resolveRunId(target, activeStore = store) {
52996
53041
  if (!isFullRunId(target))
52997
53042
  return { kind: "malformed", message: malformedRunIdMessage(target) };
52998
- const exact = store.runs().find((r) => r.id === target);
53043
+ const exact = activeStore.runs().find((r) => r.id === target);
52999
53044
  if (exact)
53000
53045
  return { kind: "exact", runId: exact.id };
53001
53046
  return { kind: "not_found" };
53002
53047
  }
53003
- function resolveToolRunTarget(args, emptyMessage) {
53048
+ function resolveToolRunTarget(args, emptyMessage, activeStore = store) {
53004
53049
  const rawTarget = args.runId?.trim() ?? "";
53005
53050
  if (args.all === true || rawTarget === "--all")
53006
53051
  return { kind: "all" };
53007
- const target = rawTarget || store.activeRunId() || "";
53052
+ const target = rawTarget || activeStore.activeRunId() || "";
53008
53053
  if (!target)
53009
53054
  return { kind: "not_found", target: rawTarget, message: emptyMessage };
53010
- const resolved = resolveRunId(target);
53055
+ const resolved = resolveRunId(target, activeStore);
53011
53056
  if (resolved.kind === "exact")
53012
53057
  return { kind: "run", runId: resolved.runId };
53013
53058
  if (resolved.kind === "malformed")
53014
53059
  return { kind: "malformed", target, message: resolved.message };
53015
53060
  return { kind: "not_found", target, message: `Run not found: ${target}` };
53016
53061
  }
53017
- function resolveStageTarget(runId, stageTarget) {
53062
+ function resolveStageTarget(runId, stageTarget, activeStore = store) {
53018
53063
  const target = stageTarget?.trim();
53019
53064
  if (!target)
53020
53065
  return { ok: true, runId };
53021
- const graph = expandWorkflowGraph(readGraphStoreSnapshot(store), runId);
53066
+ const graph = expandWorkflowGraph(readGraphStoreSnapshot(activeStore), runId);
53022
53067
  const exactVirtualIds = graph.stages.filter((stage) => stage.id === target);
53023
53068
  if (exactVirtualIds.length === 1)
53024
53069
  return resolvedStageTarget(exactVirtualIds[0]);
@@ -53054,8 +53099,8 @@ function ambiguousStageTarget(target, stages) {
53054
53099
  message: `Ambiguous stage identifier "${target}" matches: ${stages.map(expandedStageLabel).join(", ")}`
53055
53100
  };
53056
53101
  }
53057
- function resolveToolStageTarget(runId, stageTarget) {
53058
- return resolveStageTarget(runId, stageTarget);
53102
+ function resolveToolStageTarget(runId, stageTarget, activeStore = store) {
53103
+ return resolveStageTarget(runId, stageTarget, activeStore);
53059
53104
  }
53060
53105
  function resolveControlNodeTarget(runId, stageTarget) {
53061
53106
  const target = stageTarget?.trim();
@@ -61101,6 +61146,9 @@ class ScopedDurableBackend {
61101
61146
  hydrateWorkflow(_workflowId) {
61102
61147
  return Promise.resolve();
61103
61148
  }
61149
+ hydrateWorkflowForInspection(_workflowId) {
61150
+ return Promise.resolve({ kind: "absent" });
61151
+ }
61104
61152
  hydrateResumableWorkflows() {
61105
61153
  return Promise.resolve();
61106
61154
  }
@@ -64784,7 +64832,7 @@ function createDurableStageSessionRecorder(input) {
64784
64832
  if (stageRunId === input.runId) {
64785
64833
  await recordStageSessionCheckpoint(input.deps, snapshot, { force: options?.forceDurable === true });
64786
64834
  if (input.runSnapshot !== undefined) {
64787
- recordRunTimingCheckpoint(input.deps.backend, input.runSnapshot, {
64835
+ await recordRunTimingCheckpointAsync(input.deps.backend, input.runSnapshot, {
64788
64836
  debounce: options?.forceDurable !== true
64789
64837
  });
64790
64838
  }
@@ -64833,7 +64881,7 @@ function hasUsableContentBlock(block) {
64833
64881
  }
64834
64882
 
64835
64883
  // dist/builtin/workflows/src/durable/completed-catalog-stage-groups.ts
64836
- function validBoundaryRecordSet(checkpoints, strict) {
64884
+ function validBoundaryRecordSet(checkpoints, strict, requireActiveFingerprint = false) {
64837
64885
  const stages = checkpoints.filter((item) => item.kind === "stage");
64838
64886
  const boundaries = stages.filter((item) => item.topology?.boundary !== undefined);
64839
64887
  const startsByReplay = new Map;
@@ -64848,42 +64896,59 @@ function validBoundaryRecordSet(checkpoints, strict) {
64848
64896
  return false;
64849
64897
  if (boundaries.some((checkpoint) => checkpoint.topology.boundary.event === "terminal" && startsByReplay.get(checkpoint.replayKey)?.length !== 1))
64850
64898
  return false;
64851
- return [...startsByReplay.values()].every((starts) => boundaryTransitionError(starts[0], stages, strict) === undefined);
64899
+ return [...startsByReplay.values()].every((starts) => {
64900
+ const start = starts[0];
64901
+ if (boundaryTransitionError(start, stages, strict) !== undefined)
64902
+ return false;
64903
+ if (!requireActiveFingerprint || start.topology.boundary.invocationFingerprint !== undefined)
64904
+ return true;
64905
+ return stages.some((stage) => stage.replayKey === start.replayKey && stage.topology?.boundary?.event === "terminal" && stage.topology.boundary.status === "completed") && boundaryTransitionError(start, stages, true) === undefined;
64906
+ });
64852
64907
  }
64853
- function validStageGroup(drafts, runId, allowedNodeIds = new Set) {
64908
+ function validStageGroup(drafts, runId, rootRunId, tools = []) {
64854
64909
  if (drafts.some((draft) => draft.topology.stageId.length === 0))
64855
64910
  return false;
64856
- const stageIds = new Set(drafts.map((draft) => draft.topology.stageId));
64857
- if (stageIds.size !== drafts.length)
64911
+ if (tools.some((tool) => tool.topology !== undefined && (tool.topology.version !== DURABLE_TOOL_TOPOLOGY_VERSION || tool.topology.nodeId.length === 0)))
64858
64912
  return false;
64859
- const ids = new Set([...allowedNodeIds, ...stageIds]);
64860
- if (ids.size !== allowedNodeIds.size + stageIds.size)
64913
+ const stageIds = drafts.map((draft) => draft.topology.stageId);
64914
+ const toolIds = tools.map((tool) => tool.topology?.nodeId ?? tool.checkpointId);
64915
+ const ids = new Set([...stageIds, ...toolIds]);
64916
+ if (ids.size !== stageIds.length + toolIds.length)
64861
64917
  return false;
64862
64918
  const orders = drafts.flatMap((draft) => draft.topology.sourceOrder === undefined ? [] : [draft.topology.sourceOrder]);
64863
64919
  if (new Set(orders).size !== orders.length)
64864
64920
  return false;
64865
- if (drafts.some((draft) => draft.topology.parentIds.some((parentId) => !ids.has(parentId))))
64921
+ const parentEntries = [
64922
+ ...drafts.map((draft) => [draft.topology.stageId, draft.topology.parentIds]),
64923
+ ...tools.map((tool) => [tool.topology?.nodeId ?? tool.checkpointId, tool.topology?.parentIds ?? []])
64924
+ ];
64925
+ if (parentEntries.some(([, parentIds]) => parentIds.some((parentId) => !ids.has(parentId))))
64866
64926
  return false;
64867
- const runs = drafts.flatMap((draft) => draft.topology?.run === undefined ? [] : [draft.topology.run]);
64927
+ const runs = [
64928
+ ...drafts.flatMap((draft) => draft.topology?.run === undefined ? [] : [draft.topology.run]),
64929
+ ...tools.flatMap((tool) => tool.topology?.run === undefined ? [] : [tool.topology.run])
64930
+ ];
64868
64931
  const first = runs[0];
64869
64932
  if (runs.some((run) => run.runId !== runId || first !== undefined && (run.runName !== first.runName || run.parentRunId !== first.parentRunId || run.parentStageId !== first.parentStageId || run.rootRunId !== first.rootRunId)))
64870
64933
  return false;
64871
- const parents = new Map(drafts.map((draft) => [draft.topology.stageId, draft.topology.parentIds]));
64934
+ if (runId === rootRunId && runs.some((run) => run.parentRunId !== undefined || run.parentStageId !== undefined || run.rootRunId !== undefined))
64935
+ return false;
64936
+ const parents = new Map(parentEntries);
64872
64937
  const visiting = new Set;
64873
64938
  const visited = new Set;
64874
- const cyclic = (stageId) => {
64875
- if (visiting.has(stageId))
64939
+ const cyclic = (nodeId) => {
64940
+ if (visiting.has(nodeId))
64876
64941
  return true;
64877
- if (visited.has(stageId))
64942
+ if (visited.has(nodeId))
64878
64943
  return false;
64879
- visiting.add(stageId);
64880
- if ((parents.get(stageId) ?? []).some(cyclic))
64944
+ visiting.add(nodeId);
64945
+ if ((parents.get(nodeId) ?? []).some(cyclic))
64881
64946
  return true;
64882
- visiting.delete(stageId);
64883
- visited.add(stageId);
64947
+ visiting.delete(nodeId);
64948
+ visited.add(nodeId);
64884
64949
  return false;
64885
64950
  };
64886
- return ![...stageIds].some(cyclic);
64951
+ return ![...ids].some(cyclic);
64887
64952
  }
64888
64953
  function compareDraftSourceOrder(left, right) {
64889
64954
  const leftOrder = left.topology?.sourceOrder;
@@ -65425,7 +65490,7 @@ function completedWorkflowRunSnapshots(backend, entry) {
65425
65490
  const checkpoints = backend.listCheckpoints(entry.workflowId);
65426
65491
  if (checkpoints.length === 0)
65427
65492
  return [];
65428
- const runs = runSnapshotsFromCheckpoints(checkpoints, handle.workflowId, handle.name, handle.updatedAt).map((run2) => ({ ...run2, stages: run2.stages.map(validatedStageTranscript) }));
65493
+ const runs = runSnapshotsFromCheckpoints(checkpoints, handle.workflowId, handle.name, handle.updatedAt, COMPLETED_RECONSTRUCTION).map((run2) => ({ ...run2, stages: run2.stages.map(validatedStageTranscript) }));
65429
65494
  const rootIndex = runs.findIndex((run2) => run2.id === handle.workflowId);
65430
65495
  if (rootIndex < 0)
65431
65496
  return [];
@@ -65449,11 +65514,45 @@ function completedWorkflowRunSnapshots(backend, entry) {
65449
65514
  };
65450
65515
  return [root, ...runs.filter((_, index) => index !== rootIndex)];
65451
65516
  }
65517
+ function durableWorkflowRunSnapshots(backend, handle) {
65518
+ const checkpoints = backend.listCheckpoints(handle.workflowId);
65519
+ if (checkpoints.length === 0) {
65520
+ return [rootSnapshot(backend, handle, { stages: [], toolNodes: [] })];
65521
+ }
65522
+ const requiresTerminalTopology = handle.status === "completed" || handle.status === "failed";
65523
+ const runs = runSnapshotsFromCheckpoints(checkpoints, handle.workflowId, handle.name, handle.updatedAt, requiresTerminalTopology ? COMPLETED_RECONSTRUCTION : ACTIVE_INSPECTION_RECONSTRUCTION);
65524
+ const rootIndex = runs.findIndex((run2) => run2.id === handle.workflowId);
65525
+ if (rootIndex < 0)
65526
+ return [];
65527
+ return [rootSnapshot(backend, handle, runs[rootIndex]), ...runs.filter((_, index) => index !== rootIndex)];
65528
+ }
65529
+ function rootSnapshot(backend, handle, graph) {
65530
+ const terminal = handle.status === "completed" || handle.status === "failed" || handle.status === "cancelled";
65531
+ const priorDurationMs = priorRunElapsedMs(backend, handle.workflowId);
65532
+ const terminalDurationMs = priorDurationMs ?? Math.max(0, handle.updatedAt - handle.createdAt);
65533
+ return {
65534
+ id: handle.workflowId,
65535
+ name: handle.name,
65536
+ inputs: { ...handle.inputs },
65537
+ status: handle.status,
65538
+ stages: graph.stages,
65539
+ toolNodes: graph.toolNodes,
65540
+ startedAt: handle.createdAt,
65541
+ ...terminal ? { endedAt: handle.updatedAt, durationMs: terminalDurationMs } : priorDurationMs === undefined ? {} : { accumulatedDurationMs: priorDurationMs },
65542
+ ...handle.error !== undefined ? { error: handle.error } : {},
65543
+ ...handle.failureKind !== undefined ? { failureKind: handle.failureKind } : {},
65544
+ ...handle.failureCode !== undefined ? { failureCode: handle.failureCode } : {},
65545
+ ...handle.failureRecoverability !== undefined ? { failureRecoverability: handle.failureRecoverability } : {},
65546
+ ...handle.failureDisposition !== undefined ? { failureDisposition: handle.failureDisposition } : {},
65547
+ ...handle.failedToolNodeId !== undefined ? { failedToolNodeId: handle.failedToolNodeId } : {},
65548
+ resumable: isDurableWorkflowResumable(handle)
65549
+ };
65550
+ }
65452
65551
  function durableNestedRunSnapshots(backend, rootWorkflowId) {
65453
65552
  const handle = backend.getWorkflow(rootWorkflowId);
65454
65553
  if (handle === undefined)
65455
65554
  return [];
65456
- return runSnapshotsFromCheckpoints(backend.listCheckpoints(rootWorkflowId), rootWorkflowId, handle.name, handle.updatedAt, false).filter((run2) => run2.id !== rootWorkflowId);
65555
+ return runSnapshotsFromCheckpoints(backend.listCheckpoints(rootWorkflowId), rootWorkflowId, handle.name, handle.updatedAt, LEGACY_ACTIVE_RECONSTRUCTION).filter((run2) => run2.id !== rootWorkflowId);
65457
65556
  }
65458
65557
  function validatedStageTranscript(stage) {
65459
65558
  if (stage.sessionFile === undefined || isReopenableSessionTranscript(stage.sessionFile))
@@ -65523,15 +65622,36 @@ function summarizeCompletedToolResult(value2) {
65523
65622
  return flattenTruncatedString(String(value2).slice(0, 240));
65524
65623
  return serialized.length <= 240 ? serialized : `${flattenTruncatedString(serialized.slice(0, 237))}...`;
65525
65624
  }
65526
- function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallbackCompletedAt, strict = true) {
65527
- if (!validBoundaryRecordSet(checkpoints, strict))
65625
+ var COMPLETED_RECONSTRUCTION = {
65626
+ requireTerminal: true,
65627
+ failClosed: true,
65628
+ retainActive: false,
65629
+ failOnUnreachable: false,
65630
+ requireActiveBoundaryFingerprint: false
65631
+ };
65632
+ var ACTIVE_INSPECTION_RECONSTRUCTION = {
65633
+ requireTerminal: false,
65634
+ failClosed: true,
65635
+ retainActive: true,
65636
+ failOnUnreachable: true,
65637
+ requireActiveBoundaryFingerprint: true
65638
+ };
65639
+ var LEGACY_ACTIVE_RECONSTRUCTION = {
65640
+ requireTerminal: false,
65641
+ failClosed: false,
65642
+ retainActive: true,
65643
+ failOnUnreachable: false,
65644
+ requireActiveBoundaryFingerprint: false
65645
+ };
65646
+ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallbackCompletedAt, policy = COMPLETED_RECONSTRUCTION) {
65647
+ if (!validBoundaryRecordSet(checkpoints, policy.requireTerminal, policy.requireActiveBoundaryFingerprint))
65528
65648
  return [];
65529
65649
  const stageRecords = checkpoints.filter((checkpoint) => checkpoint.kind === "stage");
65530
65650
  const identityRecords = stageRecords.filter((stage) => stage.topology?.sourceOrder !== undefined || stage.topology?.status !== undefined || stage.topology?.occurrenceKey !== undefined || stage.topology?.boundary !== undefined);
65531
65651
  if (immutableStageGroupError(identityRecords) !== undefined)
65532
65652
  return [];
65533
65653
  const recordsByGroup = groupByDurableStageKey(stageRecords);
65534
- const candidates = [...recordsByGroup.values()].map((records) => mergeStageGroup(records, stageRecords, checkpoints)).filter((draft) => !strict || draft.topology?.run === undefined || draft.endedAt !== undefined || draft.output !== undefined).map((draft) => draft.topology !== undefined ? draft : {
65654
+ const candidates = [...recordsByGroup.values()].map((records) => mergeStageGroup(records, stageRecords, checkpoints)).filter((draft) => policy.retainActive || draft.topology?.run === undefined || draft.endedAt !== undefined || draft.output !== undefined).map((draft) => draft.topology !== undefined ? draft : {
65535
65655
  ...draft,
65536
65656
  topology: {
65537
65657
  version: DURABLE_STAGE_TOPOLOGY_VERSION,
@@ -65542,13 +65662,36 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65542
65662
  }).sort(compareDraftSourceOrder);
65543
65663
  const drafts = checkpointDrafts(checkpoints);
65544
65664
  const toolCheckpoints = drafts.tools;
65665
+ if (policy.failClosed) {
65666
+ for (const draft of candidates) {
65667
+ const boundary = draft.topology?.boundary;
65668
+ if (boundary === undefined || childRunIdFromDraft(draft) === undefined)
65669
+ continue;
65670
+ if (boundary.replayScope !== draft.replayKey)
65671
+ return [];
65672
+ const expectedChildRun = {
65673
+ runId: boundary.child.runId,
65674
+ runName: boundary.child.runName,
65675
+ parentRunId: boundary.child.parentRunId,
65676
+ parentStageId: boundary.child.parentStageId,
65677
+ rootRunId: boundary.child.rootRunId
65678
+ };
65679
+ const childToolIds = new Set(toolCheckpoints.flatMap((checkpoint) => checkpoint.topology?.run?.runId === expectedChildRun.runId ? [checkpoint.topology.nodeId] : []));
65680
+ if (directChildTopologyError(stageRecords, boundary.replayScope, expectedChildRun, true, childToolIds) !== undefined)
65681
+ return [];
65682
+ }
65683
+ }
65545
65684
  if (candidates.length === 0 && toolCheckpoints.length === 0) {
65546
- return strict ? [] : [syntheticRun(rootRunId, rootRunName, checkpoints.length, fallbackCompletedAt)];
65685
+ const supportedNonGraphState = checkpoints.every((checkpoint) => checkpoint.kind === "ui" || checkpoint.kind === "tool" && checkpoint.argsHash === RUN_TIMING_CHECKPOINT_NAME);
65686
+ if (policy.failClosed && supportedNonGraphState) {
65687
+ return [emptyGraphRun(rootRunId, rootRunName, fallbackCompletedAt)];
65688
+ }
65689
+ return policy.failClosed ? [] : [syntheticRun(rootRunId, rootRunName, checkpoints.length, fallbackCompletedAt)];
65547
65690
  }
65548
65691
  const grouped = new Map;
65549
65692
  for (const draft of candidates) {
65550
65693
  if (draft.topology?.version !== DURABLE_STAGE_TOPOLOGY_VERSION) {
65551
- if (strict)
65694
+ if (policy.failClosed)
65552
65695
  return [];
65553
65696
  continue;
65554
65697
  }
@@ -65564,7 +65707,10 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65564
65707
  }
65565
65708
  for (const group of grouped.values())
65566
65709
  group.sort(compareDraftSourceOrder);
65710
+ const groupedCount = grouped.size;
65567
65711
  retainReachableRunGroups(grouped, rootRunId);
65712
+ if (policy.failOnUnreachable && grouped.size !== groupedCount)
65713
+ return [];
65568
65714
  if (!validateRunGroups(grouped, rootRunId, toolCheckpoints, checkpoints))
65569
65715
  return [];
65570
65716
  const syntheticChildren = [];
@@ -65574,7 +65720,7 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65574
65720
  if (child?.exited !== true || grouped.has(child.runId))
65575
65721
  continue;
65576
65722
  if (!isSyntheticExitedChild(draft, parentRunId, child.runId, rootRunId) || childScopedEvidenceExists(checkpoints, draft.replayKey, child.runId)) {
65577
- if (strict)
65723
+ if (policy.failClosed)
65578
65724
  return [];
65579
65725
  continue;
65580
65726
  }
@@ -65583,8 +65729,8 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65583
65729
  }
65584
65730
  const idMaps = new Map;
65585
65731
  for (const [runId, runDrafts] of grouped) {
65586
- const ownedToolIds = new Set(toolCheckpoints.flatMap((checkpoint) => (checkpoint.topology?.run?.runId ?? rootRunId) === runId ? [checkpoint.topology?.nodeId ?? checkpoint.checkpointId] : []));
65587
- if (!validStageGroup(runDrafts, runId, ownedToolIds))
65732
+ const ownedTools = toolCheckpoints.filter((checkpoint) => (checkpoint.topology?.run?.runId ?? rootRunId) === runId);
65733
+ if (!validStageGroup(runDrafts, runId, rootRunId, ownedTools))
65588
65734
  return [];
65589
65735
  const ids = new Map;
65590
65736
  let hasIdentityConflict = false;
@@ -65600,7 +65746,7 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65600
65746
  }
65601
65747
  });
65602
65748
  if (hasIdentityConflict) {
65603
- if (strict)
65749
+ if (policy.failClosed)
65604
65750
  return [];
65605
65751
  grouped.delete(runId);
65606
65752
  continue;
@@ -65620,7 +65766,7 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65620
65766
  const ownedTools = toolCheckpoints.filter((checkpoint) => (checkpoint.topology?.run?.runId ?? rootRunId) === runId);
65621
65767
  const hasUnknownParents = runDrafts.some((draft) => draft.topology.parentIds.some((parentId) => !ids.has(parentId))) || ownedTools.some((checkpoint) => checkpoint.topology?.parentIds.some((parentId) => !ids.has(parentId)) === true);
65622
65768
  if (hasUnknownParents) {
65623
- if (strict)
65769
+ if (policy.failClosed)
65624
65770
  return [];
65625
65771
  continue;
65626
65772
  }
@@ -65630,6 +65776,8 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65630
65776
  const startedAt = Math.min(...stages.map((stage) => stage.startedAt ?? fallbackCompletedAt), ...toolNodes.map((tool) => tool.startedAt ?? tool.endedAt ?? fallbackCompletedAt));
65631
65777
  const endedAt = Math.max(...stages.map((stage) => stage.endedAt ?? fallbackCompletedAt), ...toolNodes.map((tool) => tool.endedAt ?? fallbackCompletedAt));
65632
65778
  const owner = runId === rootRunId ? undefined : boundaryOwner(grouped, runId);
65779
+ const status = owner === undefined ? "completed" : childRunStatus(owner);
65780
+ const terminal = status !== "running" && status !== "pending" && status !== "paused" && status !== "blocked";
65633
65781
  const parentRunId = run2?.parentRunId ?? rootRunId;
65634
65782
  const boundarySourceId = grouped.get(parentRunId)?.find((draft) => childRunIdFromDraft(draft) === runId)?.topology?.stageId;
65635
65783
  const declaredParentStageId = run2?.parentStageId === undefined ? undefined : idMaps.get(parentRunId)?.get(run2.parentStageId);
@@ -65639,12 +65787,11 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65639
65787
  id: runId,
65640
65788
  name: run2?.runName ?? rootRunName,
65641
65789
  inputs: {},
65642
- status: owner === undefined ? "completed" : childRunStatus(owner),
65790
+ status,
65643
65791
  stages,
65644
65792
  toolNodes,
65645
65793
  startedAt,
65646
- endedAt,
65647
- durationMs: Math.max(0, endedAt - startedAt),
65794
+ ...terminal ? { endedAt, durationMs: Math.max(0, endedAt - startedAt) } : {},
65648
65795
  ...run2?.parentRunId !== undefined ? { parentRunId: run2.parentRunId } : {},
65649
65796
  ...parentStageId !== undefined ? { parentStageId } : {},
65650
65797
  ...run2?.rootRunId !== undefined ? { rootRunId: run2.rootRunId } : {},
@@ -65659,7 +65806,7 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65659
65806
  }
65660
65807
  for (const { parentRunId, owner, child } of syntheticChildren) {
65661
65808
  if (!emittedRunIds.has(parentRunId)) {
65662
- if (strict)
65809
+ if (policy.failClosed)
65663
65810
  return [];
65664
65811
  continue;
65665
65812
  }
@@ -65667,7 +65814,7 @@ function runSnapshotsFromCheckpoints(checkpoints, rootRunId, rootRunName, fallba
65667
65814
  const boundary = topology.boundary;
65668
65815
  const parentStageId = idMaps.get(parentRunId)?.get(topology.stageId);
65669
65816
  if (parentStageId === undefined) {
65670
- if (strict)
65817
+ if (policy.failClosed)
65671
65818
  return [];
65672
65819
  continue;
65673
65820
  }
@@ -65736,6 +65883,20 @@ function stageResult(draft) {
65736
65883
  return;
65737
65884
  return typeof draft.output === "string" ? draft.output : JSON.stringify(draft.output);
65738
65885
  }
65886
+ function emptyGraphRun(runId, runName, completedAt) {
65887
+ return {
65888
+ id: runId,
65889
+ name: runName,
65890
+ inputs: {},
65891
+ status: "completed",
65892
+ stages: [],
65893
+ toolNodes: [],
65894
+ startedAt: completedAt,
65895
+ endedAt: completedAt,
65896
+ durationMs: 0,
65897
+ resumable: false
65898
+ };
65899
+ }
65739
65900
  function syntheticRun(runId, runName, checkpointCount, completedAt) {
65740
65901
  return {
65741
65902
  id: runId,
@@ -66619,7 +66780,7 @@ function createTrackedStageCaller(input) {
66619
66780
  if (suppressQueuedContinuation)
66620
66781
  runtime.state.suppressQueuedUserMessageContinuation = true;
66621
66782
  try {
66622
- result = await raceAbort(runtime.innerCtx.prompt(RESUME_CONTINUATION_PROMPT), runtime.signal);
66783
+ result = await runtime.raceStageSessionHeartbeat(raceAbort(runtime.innerCtx.prompt(RESUME_CONTINUATION_PROMPT), runtime.signal));
66623
66784
  } finally {
66624
66785
  if (suppressQueuedContinuation)
66625
66786
  runtime.state.suppressQueuedUserMessageContinuation = false;
@@ -66646,46 +66807,51 @@ function createTrackedStageCaller(input) {
66646
66807
  throw err;
66647
66808
  }
66648
66809
  const trackStageLifecycle = !runtime.state.stageFinalized;
66649
- let refreshedParentIds;
66650
- if (trackStageLifecycle && !input.hasContinuation && runtime.stageSnapshot.startedAt === undefined && !input.hasScopedParents) {
66651
- const actualParentIds = runtime.scheduler.tracker.currentParents();
66652
- const sameParents = actualParentIds.length === runtime.stageSnapshot.parentIds.length && actualParentIds.every((value2) => runtime.stageSnapshot.parentIds.includes(value2));
66653
- if (!sameParents) {
66654
- runtime.scheduler.tracker.replaceParents(runtime.stageId, actualParentIds);
66655
- refreshedParentIds = actualParentIds;
66656
- }
66657
- }
66658
- if (trackStageLifecycle) {
66659
- const now = Date.now();
66660
- const hasNoExplicitModelConfig = input.options?.model === undefined && input.options?.fallbackModels === undefined;
66661
- const promptAdapterHandlesInitialPrompt = input.adapters.prompt !== undefined;
66662
- if (callOptions.eagerSession && !promptAdapterHandlesInitialPrompt && (hasNoExplicitModelConfig || await hasExplicitFastModeCandidate({
66663
- model: input.options?.model,
66664
- fallbackModels: input.options?.fallbackModels,
66665
- models: runtime.opts.models
66666
- }))) {
66667
- try {
66668
- await runtime.innerCtx.__ensureSession();
66669
- runtime.captureStageSessionMeta();
66670
- } catch (err) {
66671
- if (!(err instanceof Error && err.message.includes("prompt adapter not configured")))
66672
- throw err;
66810
+ let applyTerminalStageState;
66811
+ let terminalStateIsSuccess = false;
66812
+ try {
66813
+ let refreshedParentIds;
66814
+ if (trackStageLifecycle && !input.hasContinuation && runtime.stageSnapshot.startedAt === undefined && !input.hasScopedParents) {
66815
+ const actualParentIds = runtime.scheduler.tracker.currentParents();
66816
+ const sameParents = actualParentIds.length === runtime.stageSnapshot.parentIds.length && actualParentIds.every((value2) => runtime.stageSnapshot.parentIds.includes(value2));
66817
+ if (!sameParents) {
66818
+ runtime.scheduler.tracker.replaceParents(runtime.stageId, actualParentIds);
66819
+ refreshedParentIds = actualParentIds;
66673
66820
  }
66674
66821
  }
66675
- if (refreshedParentIds !== undefined) {
66676
- runtime.scheduler.setStageParentIds(runtime.stageSnapshot, refreshedParentIds);
66822
+ if (trackStageLifecycle) {
66823
+ const now = Date.now();
66824
+ const hasNoExplicitModelConfig = input.options?.model === undefined && input.options?.fallbackModels === undefined;
66825
+ const promptAdapterHandlesInitialPrompt = input.adapters.prompt !== undefined;
66826
+ if (callOptions.eagerSession && !promptAdapterHandlesInitialPrompt && (hasNoExplicitModelConfig || await hasExplicitFastModeCandidate({
66827
+ model: input.options?.model,
66828
+ fallbackModels: input.options?.fallbackModels,
66829
+ models: runtime.opts.models
66830
+ }))) {
66831
+ try {
66832
+ await runtime.innerCtx.__ensureSession();
66833
+ } catch (err) {
66834
+ if (!(err instanceof Error && err.message.includes("prompt adapter not configured")))
66835
+ throw err;
66836
+ }
66837
+ }
66838
+ if (refreshedParentIds !== undefined) {
66839
+ runtime.scheduler.setStageParentIds(runtime.stageSnapshot, refreshedParentIds);
66840
+ }
66841
+ runtime.stageSnapshot.status = "running";
66842
+ runtime.stageSnapshot.startedAt ??= rebasedStageStartedAt(input.options?.durableAccumulatedDurationMs, now);
66843
+ runtime.applyModelFallbackMeta(runtime.innerCtx.__modelFallbackMeta());
66844
+ runtime.activeStore.recordStageStart(runtime.runId, runtime.stageSnapshot);
66845
+ runtime.appendStageStartOnce();
66846
+ const sessionMeta = runtime.innerCtx.__sessionMeta();
66847
+ if (sessionMeta.sessionId !== undefined || sessionMeta.sessionFile !== undefined) {
66848
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
66849
+ }
66850
+ runtime.startStageSessionHeartbeat();
66851
+ } else {
66852
+ runtime.applyModelFallbackMeta(runtime.innerCtx.__modelFallbackMeta());
66677
66853
  }
66678
- runtime.stageSnapshot.status = "running";
66679
- runtime.stageSnapshot.startedAt ??= rebasedStageStartedAt(input.options?.durableAccumulatedDurationMs, now);
66680
- runtime.applyModelFallbackMeta(runtime.innerCtx.__modelFallbackMeta());
66681
- runtime.activeStore.recordStageStart(runtime.runId, runtime.stageSnapshot);
66682
- runtime.appendStageStartOnce();
66683
- } else {
66684
- runtime.applyModelFallbackMeta(runtime.innerCtx.__modelFallbackMeta());
66685
- }
66686
- runtime.mcpScope.apply();
66687
- let applyTerminalStageState;
66688
- try {
66854
+ runtime.mcpScope.apply();
66689
66855
  const abortSession = () => {
66690
66856
  runtime.innerCtx.abort().catch(() => {});
66691
66857
  };
@@ -66697,7 +66863,7 @@ function createTrackedStageCaller(input) {
66697
66863
  try {
66698
66864
  runtime.state.askUserQuestionObservedThisTurn = false;
66699
66865
  runtime.state.chatAnswerObservedThisTurn = false;
66700
- result = await raceAbort(call(), runtime.signal);
66866
+ result = await runtime.raceStageSessionHeartbeat(raceAbort(call(), runtime.signal));
66701
66867
  const initialDrain = await drainResumeContinuations(result);
66702
66868
  result = initialDrain.result;
66703
66869
  let repeatReadinessAfterChatTurn = initialDrain.chatAnswerObserved;
@@ -66721,10 +66887,10 @@ function createTrackedStageCaller(input) {
66721
66887
  runtime.state.chatAnswerObservedThisTurn = false;
66722
66888
  runtime.state.waitingForStageChatTurn = true;
66723
66889
  try {
66724
- await raceAbort(new Promise((resolve12) => {
66890
+ await runtime.raceStageSessionHeartbeat(raceAbort(new Promise((resolve12) => {
66725
66891
  resolveNextTurnEnd = resolve12;
66726
66892
  runtime.state.wakeWaitingForStageChatTurn = resolve12;
66727
- }), runtime.signal);
66893
+ }), runtime.signal));
66728
66894
  } finally {
66729
66895
  runtime.state.wakeWaitingForStageChatTurn = undefined;
66730
66896
  runtime.state.waitingForStageChatTurn = false;
@@ -66748,7 +66914,7 @@ function createTrackedStageCaller(input) {
66748
66914
  runtime.signal.removeEventListener("abort", abortSession);
66749
66915
  }
66750
66916
  await runtime.innerCtx.__closeGeneration();
66751
- runtime.captureStageSessionMeta();
66917
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
66752
66918
  runtime.applyModelFallbackMeta(runtime.innerCtx.__modelFallbackMeta());
66753
66919
  if (trackStageLifecycle && runtime.stageFailFastScope?.failed === true && runtime.stageFailFastScope.activeStages.has(runtime.stageId)) {
66754
66920
  runtime.markSkippedForParallelFailFast();
@@ -66758,6 +66924,7 @@ function createTrackedStageCaller(input) {
66758
66924
  throw runtime.parallelFailFastError();
66759
66925
  if (trackStageLifecycle) {
66760
66926
  const assistantText = runtime.innerCtx.__getLastAssistantText();
66927
+ terminalStateIsSuccess = true;
66761
66928
  applyTerminalStageState = () => {
66762
66929
  runtime.stageSnapshot.status = "completed";
66763
66930
  if (assistantText !== undefined)
@@ -66782,15 +66949,30 @@ function createTrackedStageCaller(input) {
66782
66949
  }
66783
66950
  throw err;
66784
66951
  } finally {
66785
- runtime.mcpScope.clear();
66786
66952
  let finalizationError;
66953
+ let durableCheckpointError;
66787
66954
  try {
66788
- await runtime.innerCtx.__closeGeneration();
66789
- runtime.captureStageSessionMeta();
66955
+ runtime.mcpScope.clear();
66790
66956
  } catch (err) {
66791
66957
  finalizationError = { thrown: true, error: err };
66792
66958
  }
66959
+ try {
66960
+ await runtime.drainStageSessionHeartbeat();
66961
+ } catch (err) {
66962
+ durableCheckpointError = { thrown: true, error: err };
66963
+ }
66964
+ try {
66965
+ await runtime.innerCtx.__closeGeneration();
66966
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
66967
+ } catch (err) {
66968
+ durableCheckpointError ??= { thrown: true, error: err };
66969
+ }
66970
+ finalizationError ??= durableCheckpointError;
66793
66971
  if (trackStageLifecycle) {
66972
+ if (durableCheckpointError !== undefined && terminalStateIsSuccess) {
66973
+ const failure3 = runtime.classifyExecutorFailure(durableCheckpointError.error);
66974
+ applyTerminalStageState = () => applyFailureToStage(runtime.stageSnapshot, failure3);
66975
+ }
66794
66976
  if (!runtime.state.stageFinalized)
66795
66977
  applyTerminalStageState?.();
66796
66978
  try {
@@ -67027,7 +67209,7 @@ function createStageControlHandle(runtime) {
67027
67209
  if (sessionFile === undefined)
67028
67210
  return;
67029
67211
  await runtime.innerCtx.__ensureSessionFromFile(sessionFile);
67030
- runtime.captureStageSessionMeta();
67212
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
67031
67213
  };
67032
67214
  const toolExecutions = new StageToolExecutionBuffer;
67033
67215
  const queuedUserMessages = new StageQueuedUserMessageBuffer;
@@ -67071,7 +67253,7 @@ function createStageControlHandle(runtime) {
67071
67253
  await ensureMessagingSession();
67072
67254
  await runtime.innerCtx.__ensureSession();
67073
67255
  runtime.throwIfStageMutationBlocked();
67074
- runtime.captureStageSessionMeta();
67256
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
67075
67257
  },
67076
67258
  async sendUserMessage(text, options, beforeDelivery) {
67077
67259
  runtime.throwIfStageMutationBlocked();
@@ -68229,6 +68411,28 @@ function terminatingToolCallId(event) {
68229
68411
  }
68230
68412
 
68231
68413
  // dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts
68414
+ function hasMeaningfulUsage(usage) {
68415
+ if (usage === undefined)
68416
+ return false;
68417
+ const values = [usage.input, usage.output, usage.cacheRead, usage.cacheWrite, usage.totalTokens, usage.cost.total];
68418
+ return values.every((value2) => Number.isFinite(value2) && value2 >= 0) && values.some((value2) => value2 > 0);
68419
+ }
68420
+ function usageForAttempt(messages, start, discarded) {
68421
+ const totals = { ...discarded };
68422
+ let meaningful = totals.turns > 0;
68423
+ for (const message of messages.slice(start)) {
68424
+ if (message.role !== "assistant" || !hasMeaningfulUsage(message.usage))
68425
+ continue;
68426
+ totals.input += message.usage.input;
68427
+ totals.output += message.usage.output;
68428
+ totals.cacheRead += message.usage.cacheRead;
68429
+ totals.cacheWrite += message.usage.cacheWrite;
68430
+ totals.cost += message.usage.cost.total;
68431
+ totals.turns += 1;
68432
+ meaningful = true;
68433
+ }
68434
+ return meaningful ? totals : undefined;
68435
+ }
68232
68436
  function isSessionCreationPauseResult(value2) {
68233
68437
  return "kind" in value2 && value2.kind === "paused";
68234
68438
  }
@@ -68291,6 +68495,15 @@ class StageSessionController {
68291
68495
  sessionPromise;
68292
68496
  reattachSessionFile;
68293
68497
  lastPromptStartIndex;
68498
+ attemptUsageStartIndex;
68499
+ discardedAttemptUsage = {
68500
+ input: 0,
68501
+ output: 0,
68502
+ cacheRead: 0,
68503
+ cacheWrite: 0,
68504
+ cost: 0,
68505
+ turns: 0
68506
+ };
68294
68507
  terminatingToolCallIds = new Set;
68295
68508
  latestStructuredOutputToolErrorValue;
68296
68509
  unsubscribeTerminateWatcher;
@@ -68490,6 +68703,7 @@ class StageSessionController {
68490
68703
  this.activeCandidateIndex = index;
68491
68704
  this.selectedModel = candidate.id;
68492
68705
  this.notifyModelFallbackMetaChange();
68706
+ this.beginAttemptUsage(activeSession);
68493
68707
  const { terminalScanStartIndex } = await this.promptWithThrownErrorRetry(activeSession, promptText, sdkOptions);
68494
68708
  const terminalFailure = latestTerminalAssistantFailureSince(activeSession.messages, terminalScanStartIndex);
68495
68709
  if (terminalFailure !== undefined) {
@@ -68628,9 +68842,38 @@ class StageSessionController {
68628
68842
  }
68629
68843
  return;
68630
68844
  }
68845
+ beginAttemptUsage(session) {
68846
+ this.attemptUsageStartIndex = session.messages.length;
68847
+ this.discardedAttemptUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
68848
+ }
68849
+ takeAttemptUsage(session) {
68850
+ const start = this.attemptUsageStartIndex;
68851
+ const usage = session === undefined || start === undefined ? undefined : usageForAttempt(session.messages, start, this.discardedAttemptUsage);
68852
+ this.clearAttemptUsage();
68853
+ return usage;
68854
+ }
68855
+ clearAttemptUsage() {
68856
+ this.attemptUsageStartIndex = undefined;
68857
+ this.discardedAttemptUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
68858
+ }
68859
+ accrueDiscardedAttemptUsage(message) {
68860
+ if (!hasMeaningfulUsage(message.usage))
68861
+ return;
68862
+ this.discardedAttemptUsage.input += message.usage.input;
68863
+ this.discardedAttemptUsage.output += message.usage.output;
68864
+ this.discardedAttemptUsage.cacheRead += message.usage.cacheRead;
68865
+ this.discardedAttemptUsage.cacheWrite += message.usage.cacheWrite;
68866
+ this.discardedAttemptUsage.cost += message.usage.cost.total;
68867
+ this.discardedAttemptUsage.turns += 1;
68868
+ }
68631
68869
  restoreSessionMessages(session, snapshot, promptText, keepPrompt) {
68632
68870
  const snapshotMessages = new Set(snapshot);
68633
68871
  const admitted = session.messages.filter((message) => !snapshotMessages.has(message));
68872
+ for (const message of admitted) {
68873
+ if (message.role !== "assistant" || message.stopReason !== "error")
68874
+ continue;
68875
+ this.accrueDiscardedAttemptUsage(message);
68876
+ }
68634
68877
  const failedAssistantIndex = admitted.findLastIndex((message) => message.role === "assistant" && message.stopReason === "error");
68635
68878
  const promptUser = admitted.slice(0, failedAssistantIndex < 0 ? admitted.length : failedAssistantIndex).findLast((message) => message.role === "user" && stageUserMessageText(message) === promptText);
68636
68879
  const retainedMessages = admitted.filter((message) => {
@@ -68979,7 +69222,11 @@ class StageSessionController {
68979
69222
  throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68980
69223
  throw this.staleCreationReason(startGeneration);
68981
69224
  }
68982
- return attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
69225
+ const session = attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
69226
+ if (session instanceof Promise)
69227
+ return await session;
69228
+ await this.opts.onSessionReady?.();
69229
+ return session;
68983
69230
  }
68984
69231
  attachSession(created) {
68985
69232
  const result = normalizeSessionCreateResult(created);
@@ -69087,11 +69334,17 @@ class StageSessionController {
69087
69334
  const resumedSession = this.session;
69088
69335
  const resumedLabel = this.selectedModel ?? workflowModelId(resumedSession.model) ?? candidates[0].id;
69089
69336
  this.notifyModelFallbackMetaChange();
69337
+ this.beginAttemptUsage(resumedSession);
69090
69338
  try {
69091
69339
  const { terminalScanStartIndex } = await this.promptWithThrownErrorRetry(resumedSession, text, sdkOptions);
69092
69340
  const terminalFailure = latestTerminalAssistantFailureSince(resumedSession.messages, terminalScanStartIndex);
69093
69341
  if (terminalFailure === undefined || this.capturedStructuredOutputForAttempt()) {
69094
- this.modelAttempts.push({ model: resumedLabel, success: true });
69342
+ const usage = this.takeAttemptUsage(resumedSession);
69343
+ this.modelAttempts.push({
69344
+ model: resumedLabel,
69345
+ success: true,
69346
+ ...usage === undefined ? {} : { usage }
69347
+ });
69095
69348
  this.pendingFallbackWarnings.length = 0;
69096
69349
  this.resumeCurrentSession = true;
69097
69350
  return true;
@@ -69099,13 +69352,24 @@ class StageSessionController {
69099
69352
  throw new WorkflowPromptModelFailure(terminalFailure);
69100
69353
  } catch (err) {
69101
69354
  if (this.capturedStructuredOutputForAttempt() && isRetryableModelFailure(err)) {
69102
- this.modelAttempts.push({ model: resumedLabel, success: true });
69355
+ const usage2 = this.takeAttemptUsage(resumedSession);
69356
+ this.modelAttempts.push({
69357
+ model: resumedLabel,
69358
+ success: true,
69359
+ ...usage2 === undefined ? {} : { usage: usage2 }
69360
+ });
69103
69361
  this.pendingFallbackWarnings.length = 0;
69104
69362
  this.resumeCurrentSession = true;
69105
69363
  return true;
69106
69364
  }
69107
69365
  const message = errorMessage3(err);
69108
- this.modelAttempts.push({ model: resumedLabel, success: false, error: message });
69366
+ const usage = this.takeAttemptUsage(resumedSession);
69367
+ this.modelAttempts.push({
69368
+ model: resumedLabel,
69369
+ success: false,
69370
+ ...usage === undefined ? {} : { usage },
69371
+ error: message
69372
+ });
69109
69373
  if (this.opts.signal?.aborted || !isRetryableModelFailure(err)) {
69110
69374
  this.modelWarnings.push(...this.pendingFallbackWarnings);
69111
69375
  this.pendingFallbackWarnings.length = 0;
@@ -69131,10 +69395,12 @@ class StageSessionController {
69131
69395
  this.recordSuccessfulAttempt(candidate);
69132
69396
  return "handled";
69133
69397
  }
69398
+ const usage = this.takeAttemptUsage(this.session);
69134
69399
  this.modelAttempts.push({
69135
69400
  model: candidate.id,
69136
69401
  success: false,
69137
69402
  ...modelAttemptReasoning(candidate, this.effectiveStageOptions?.thinkingLevel),
69403
+ ...usage === undefined ? {} : { usage },
69138
69404
  error: message
69139
69405
  });
69140
69406
  if (this.opts.signal?.aborted || !isRetryableModelFailure(err) || index === candidates.length - 1) {
@@ -69152,10 +69418,12 @@ class StageSessionController {
69152
69418
  return this.structuredOutputCapture?.called === true && this.opts.signal?.aborted !== true;
69153
69419
  }
69154
69420
  recordSuccessfulAttempt(candidate) {
69421
+ const usage = this.takeAttemptUsage(this.session);
69155
69422
  this.modelAttempts.push({
69156
69423
  model: candidate.id,
69157
69424
  success: true,
69158
- ...modelAttemptReasoning(candidate, this.effectiveStageOptions?.thinkingLevel)
69425
+ ...modelAttemptReasoning(candidate, this.effectiveStageOptions?.thinkingLevel),
69426
+ ...usage === undefined ? {} : { usage }
69159
69427
  });
69160
69428
  this.pendingFallbackWarnings.length = 0;
69161
69429
  this.resumeCurrentSession = true;
@@ -69592,6 +69860,81 @@ function createStageContext2(opts) {
69592
69860
  }
69593
69861
  };
69594
69862
  }
69863
+ // dist/builtin/workflows/src/runs/foreground/stage-session-heartbeat.ts
69864
+ var STAGE_SESSION_HEARTBEAT_INTERVAL_MS = 30000;
69865
+ function createStageSessionHeartbeat(checkpoint, intervalMs = STAGE_SESSION_HEARTBEAT_INTERVAL_MS) {
69866
+ let active2 = false;
69867
+ let timer;
69868
+ let generation = 0;
69869
+ let nextDeadline = 0;
69870
+ let failure3;
69871
+ let inFlight;
69872
+ let rejectFailure;
69873
+ const failureSignal = new Promise((_resolve, reject) => {
69874
+ rejectFailure = reject;
69875
+ });
69876
+ failureSignal.catch(() => {
69877
+ return;
69878
+ });
69879
+ const stopScheduling = () => {
69880
+ active2 = false;
69881
+ generation += 1;
69882
+ if (timer !== undefined)
69883
+ clearTimeout(timer);
69884
+ timer = undefined;
69885
+ };
69886
+ const fail = (error) => {
69887
+ if (failure3 !== undefined)
69888
+ return;
69889
+ failure3 = error;
69890
+ stopScheduling();
69891
+ rejectFailure(error);
69892
+ };
69893
+ const schedule = (scheduledGeneration) => {
69894
+ if (!active2 || failure3 !== undefined || scheduledGeneration !== generation)
69895
+ return;
69896
+ const delayMs = Math.max(0, nextDeadline - Date.now());
69897
+ timer = setTimeout(() => {
69898
+ timer = undefined;
69899
+ const startedAt = Date.now();
69900
+ do
69901
+ nextDeadline += intervalMs;
69902
+ while (nextDeadline <= startedAt);
69903
+ const running = checkpoint().catch(fail).finally(() => {
69904
+ if (inFlight === running)
69905
+ inFlight = undefined;
69906
+ schedule(scheduledGeneration);
69907
+ });
69908
+ inFlight = running;
69909
+ }, delayMs);
69910
+ timer.unref?.();
69911
+ };
69912
+ const drain = async () => {
69913
+ stopScheduling();
69914
+ await inFlight;
69915
+ if (failure3 !== undefined)
69916
+ throw failure3;
69917
+ };
69918
+ return {
69919
+ start() {
69920
+ if (active2 || failure3 !== undefined)
69921
+ return;
69922
+ active2 = true;
69923
+ generation += 1;
69924
+ nextDeadline = Date.now() + intervalMs;
69925
+ schedule(generation);
69926
+ },
69927
+ stop: stopScheduling,
69928
+ drain,
69929
+ fail,
69930
+ race(work) {
69931
+ if (failure3 !== undefined)
69932
+ return Promise.reject(failure3);
69933
+ return Promise.race([work, failureSignal]);
69934
+ }
69935
+ };
69936
+ }
69937
+
69595
69938
  // dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts
69596
69939
  function createWorkflowStageFactory(input) {
69597
69940
  return (name, options, stageFailFastScope) => {
@@ -69672,6 +70015,15 @@ function createWorkflowStageFactory(input) {
69672
70015
  if (meta2.modelAttempts !== undefined)
69673
70016
  stageSnapshot.modelAttempts = meta2.modelAttempts;
69674
70017
  };
70018
+ let runtime;
70019
+ const stageSessionHeartbeat = createStageSessionHeartbeat(async () => {
70020
+ try {
70021
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
70022
+ } catch (error) {
70023
+ await runtime.innerCtx.abort().catch(() => {});
70024
+ throw error;
70025
+ }
70026
+ });
69675
70027
  const innerCtx = createStageContext2({
69676
70028
  stageId,
69677
70029
  stageName: name,
@@ -69687,6 +70039,12 @@ function createWorkflowStageFactory(input) {
69687
70039
  applyModelFallbackMeta(meta2);
69688
70040
  if (stageSnapshot.status === "running")
69689
70041
  input.activeStore.recordStageStart(input.runId, stageSnapshot);
70042
+ },
70043
+ async onSessionReady() {
70044
+ if (stageSnapshot.status !== "running")
70045
+ return;
70046
+ await runtime.captureStageSessionMeta({ awaitDurable: true });
70047
+ runtime.startStageSessionHeartbeat();
69690
70048
  }
69691
70049
  });
69692
70050
  const state2 = {
@@ -69749,8 +70107,7 @@ function createWorkflowStageFactory(input) {
69749
70107
  stageUiBroker.clearStagePrompt(input.runId, stageId);
69750
70108
  await innerCtx.__dispose();
69751
70109
  };
69752
- let runtime;
69753
- const captureStageSessionMeta = (checkpointOptions) => {
70110
+ const captureStageSessionMeta = async (checkpointOptions) => {
69754
70111
  const meta2 = innerCtx.__sessionMeta();
69755
70112
  if (meta2.sessionId !== undefined)
69756
70113
  stageSnapshot.sessionId = meta2.sessionId;
@@ -69759,15 +70116,17 @@ function createWorkflowStageFactory(input) {
69759
70116
  if (meta2.sessionId !== undefined || meta2.sessionFile !== undefined)
69760
70117
  input.activeStore.recordStageSession(input.runId, stageId, meta2);
69761
70118
  const pending = input.opts.onStageSession?.(input.runId, stageSnapshot, checkpointOptions);
69762
- if (checkpointOptions?.forceDurable === true)
69763
- return pending;
70119
+ if (checkpointOptions?.forceDurable === true || checkpointOptions?.awaitDurable === true) {
70120
+ await pending;
70121
+ return;
70122
+ }
69764
70123
  Promise.resolve(pending).catch(() => {});
69765
- return;
69766
70124
  };
69767
70125
  const releaseLiveHandle = async () => {
69768
70126
  if (state2.liveHandleReleased)
69769
70127
  return;
69770
70128
  state2.liveHandleReleased = true;
70129
+ stageSessionHeartbeat.stop();
69771
70130
  runtime.dropStageControlHandle();
69772
70131
  runtime.unregisterStageHandle();
69773
70132
  await disposeInnerContext();
@@ -69800,6 +70159,7 @@ function createWorkflowStageFactory(input) {
69800
70159
  });
69801
70160
  };
69802
70161
  const finalizeStageSnapshot = async () => {
70162
+ stageSessionHeartbeat.stop();
69803
70163
  if (state2.stageFinalized)
69804
70164
  return false;
69805
70165
  if (stageSnapshot.endedAt !== undefined && isTerminalStage(stageSnapshot)) {
@@ -69872,6 +70232,9 @@ function createWorkflowStageFactory(input) {
69872
70232
  dropStageControlHandle: () => {},
69873
70233
  unregisterWorkflowExitCleanup: () => {},
69874
70234
  captureStageSessionMeta,
70235
+ startStageSessionHeartbeat: stageSessionHeartbeat.start,
70236
+ drainStageSessionHeartbeat: stageSessionHeartbeat.drain,
70237
+ raceStageSessionHeartbeat: stageSessionHeartbeat.race,
69875
70238
  applyModelFallbackMeta,
69876
70239
  appendStageStartOnce,
69877
70240
  finalizeStageSnapshot,
@@ -70024,9 +70387,22 @@ class EngineRuntime {
70024
70387
  apply: () => {
70025
70388
  if (!this.childRunOptions.mcp || !hasScope)
70026
70389
  return;
70027
- if (depth === 0)
70390
+ if (depth > 0) {
70391
+ depth += 1;
70392
+ return;
70393
+ }
70394
+ depth = 1;
70395
+ try {
70028
70396
  this.childRunOptions.mcp.setScope(stageId, allow, deny);
70029
- depth += 1;
70397
+ } catch (error) {
70398
+ depth = 0;
70399
+ try {
70400
+ this.childRunOptions.mcp.clearScope(stageId);
70401
+ } catch (cleanupError) {
70402
+ throw new AggregateError([error, cleanupError], "MCP scope setup failed and compensating cleanup failed");
70403
+ }
70404
+ throw error;
70405
+ }
70030
70406
  },
70031
70407
  clear: () => {
70032
70408
  if (!this.childRunOptions.mcp || !hasScope)
@@ -71369,6 +71745,72 @@ function disposeCompletedChatHandle(handle) {
71369
71745
  });
71370
71746
  }
71371
71747
 
71748
+ // dist/builtin/workflows/src/durable/targeted-inspection.ts
71749
+ async function inspectTargetedDurableWorkflow(backend, workflowId, now = Date.now()) {
71750
+ const hydrated = backend.hydrateWorkflowForInspection ? await backend.hydrateWorkflowForInspection(workflowId) : await hydrateWithoutClassification(backend, workflowId);
71751
+ if (hydrated.kind !== "current")
71752
+ return hydrationFailure(workflowId, hydrated.kind);
71753
+ const handle = hydrated.handle;
71754
+ if (handle.rootWorkflowId !== undefined && handle.rootWorkflowId !== handle.workflowId) {
71755
+ return malformed(workflowId, "the requested id belongs to a nested workflow rather than a durable root");
71756
+ }
71757
+ const reconstructed = durableWorkflowRunSnapshots(backend, handle);
71758
+ if (reconstructed.length === 0)
71759
+ return malformed(workflowId, "durable checkpoint topology is malformed or incomplete");
71760
+ const runs = structuredClone(reconstructed);
71761
+ const inspectionStore = createStore();
71762
+ for (const run2 of runs)
71763
+ inspectionStore.recordRunStart(structuredClone(run2));
71764
+ const inspected = inspectRun(workflowId, { store: inspectionStore });
71765
+ if (!inspected.ok)
71766
+ return malformed(workflowId, "the durable checkpoint graph has no reciprocal root");
71767
+ const foreignLive = isForeignLiveWorkflow(handle, getAtomicExecutorId(), now);
71768
+ const live = isLiveRunningWorkflow(handle, now);
71769
+ const crashed = handle.status === "running" && !live;
71770
+ const resumeGuidance = crashed ? inspected.detail.resumable === true ? `This workflow appears to have crashed and is resumable. Resume it explicitly with /workflow resume ${workflowId}.` : "This workflow appears to have crashed, but its retained state is not resumable." : foreignLive ? "This workflow is actively running in another Atomic session. Inspect it here, but control it from its owner session." : live ? "This workflow still has a fresh durable heartbeat. Inspect it here without starting another executor." : terminalGuidance(handle.status, workflowId, inspected.detail.resumable === true);
71771
+ return {
71772
+ kind: "found",
71773
+ runs,
71774
+ store: inspectionStore,
71775
+ detail: {
71776
+ ...inspected.detail,
71777
+ ...crashed ? { status: "crashed" } : {},
71778
+ ...live ? { resumable: false } : {},
71779
+ ...foreignLive ? { ownerActiveElsewhere: true } : {},
71780
+ resumeGuidance
71781
+ }
71782
+ };
71783
+ }
71784
+ function terminalGuidance(status, workflowId, resumable) {
71785
+ if (resumable)
71786
+ return `This retained workflow is ${status} and resumable. Resume it explicitly with /workflow resume ${workflowId}.`;
71787
+ return `This retained workflow is ${status} and available for read-only inspection.`;
71788
+ }
71789
+ async function hydrateWithoutClassification(backend, workflowId) {
71790
+ await backend.hydrateWorkflow(workflowId);
71791
+ const handle = backend.getLoadableWorkflow(workflowId);
71792
+ return handle === undefined ? { kind: "absent" } : { kind: "current", handle };
71793
+ }
71794
+ function hydrationFailure(workflowId, kind) {
71795
+ switch (kind) {
71796
+ case "absent":
71797
+ return {
71798
+ kind,
71799
+ message: `Run not found: ${workflowId} (not present in the current session or durable store).`
71800
+ };
71801
+ case "deleted":
71802
+ return { kind, message: `Durable workflow was deleted: ${workflowId}` };
71803
+ case "malformed":
71804
+ return malformed(workflowId, "current DBOS metadata or checkpoints are malformed");
71805
+ }
71806
+ }
71807
+ function malformed(workflowId, reason) {
71808
+ return {
71809
+ kind: "malformed",
71810
+ message: `Durable workflow ${workflowId} cannot be inspected safely: ${reason}.`
71811
+ };
71812
+ }
71813
+
71372
71814
  // dist/builtin/workflows/src/extension/runtime-durable-resume.ts
71373
71815
  function createDurableResumeRuntime(deps) {
71374
71816
  const hydrateStoredWorkflowCandidates = async (backend, target) => {
@@ -71378,6 +71820,10 @@ function createDurableResumeRuntime(deps) {
71378
71820
  };
71379
71821
  let preparedCatalog = [];
71380
71822
  return {
71823
+ async inspectDurableWorkflow(workflowId) {
71824
+ await deps.ensureReady();
71825
+ return await inspectTargetedDurableWorkflow(getDurableBackend(), workflowId);
71826
+ },
71381
71827
  async resumeDurableWorkflow(workflowId, options) {
71382
71828
  await deps.ensureReady();
71383
71829
  const backend = getDurableBackend();
@@ -72052,6 +72498,9 @@ function createWorkflowExtensionRuntimeState(pi, adapters, resolveCwd = () => pi
72052
72498
  resumeDurableWorkflow(workflowId, options) {
72053
72499
  return runtimeRef.current.resumeDurableWorkflow(workflowId, options);
72054
72500
  },
72501
+ inspectDurableWorkflow(workflowId) {
72502
+ return runtimeRef.current.inspectDurableWorkflow(workflowId);
72503
+ },
72055
72504
  listDurableResumable() {
72056
72505
  return runtimeRef.current.listDurableResumable();
72057
72506
  },
@@ -73721,6 +74170,7 @@ function toPickerRow(session) {
73721
74170
  modifiedAt: session.modified.getTime(),
73722
74171
  messageCount: session.messageCount,
73723
74172
  firstMessage: session.firstMessage,
74173
+ ...session.summary !== undefined ? { summary: session.summary } : {},
73724
74174
  allMessagesText: session.allMessagesText,
73725
74175
  ...session.name !== undefined ? { name: session.name } : {},
73726
74176
  ...session.messageColor !== undefined ? { messageColor: session.messageColor } : {}
@@ -74570,8 +75020,13 @@ Available: ${formatAvailableWorkflowNames(deps.runtimeProxy.registry.names())}`)
74570
75020
  const resolved = resolveRunId(target);
74571
75021
  if (resolved.kind === "malformed")
74572
75022
  return fail(resolved.message);
74573
- if (resolved.kind === "not_found")
74574
- return fail(`Run not found: ${target}`);
75023
+ if (resolved.kind === "not_found") {
75024
+ const durable = await deps.runtimeForContext(ctx).inspectDurableWorkflow(target);
75025
+ if (durable.kind !== "found")
75026
+ return fail(durable.message);
75027
+ emitChatSurface(pi, { kind: "detail", detail: durable.detail });
75028
+ return;
75029
+ }
74575
75030
  const inspected = inspectRun(resolved.runId);
74576
75031
  if (!inspected.ok)
74577
75032
  return fail(`Run not found: ${target}`);
@@ -75645,8 +76100,9 @@ function snapshotTranscriptEntryCount(snapshot) {
75645
76100
  }
75646
76101
 
75647
76102
  // dist/builtin/workflows/src/extension/workflow-tool-inspection.ts
75648
- function workflowStagesResult(args) {
75649
- const target = resolveToolRunTarget(args, "No active run to inspect.");
76103
+ function workflowStagesResult(args, source) {
76104
+ const activeStore = source?.store ?? store;
76105
+ const target = resolveToolRunTarget(args, "No active run to inspect.", activeStore);
75650
76106
  const filter = args.statusFilter ?? "all";
75651
76107
  if (target.kind === "all") {
75652
76108
  return {
@@ -75666,19 +76122,20 @@ function workflowStagesResult(args) {
75666
76122
  error: target.message
75667
76123
  };
75668
76124
  }
75669
- const run2 = store.runs().find((r) => r.id === target.runId);
75670
- const stages = (run2?.stages ?? []).filter((stage) => filter === "all" || stage.status === filter).map(summarizeStage);
76125
+ const stageSnapshots = source?.store === undefined ? activeStore.runs().find((run2) => run2.id === target.runId)?.stages ?? [] : expandWorkflowGraph(readGraphStoreSnapshot(activeStore), target.runId).stages;
76126
+ const stages = stageSnapshots.filter((stage) => filter === "all" || stage.status === filter).map(summarizeStage);
75671
76127
  return { action: "stages", runId: target.runId, filter, stages };
75672
76128
  }
75673
- function workflowStageResult(args) {
75674
- const target = resolveToolRunTarget(args, "No active run to inspect.");
76129
+ function workflowStageResult(args, source) {
76130
+ const activeStore = source?.store ?? store;
76131
+ const target = resolveToolRunTarget(args, "No active run to inspect.", activeStore);
75675
76132
  if (target.kind === "all") {
75676
76133
  return { action: "stage", runId: "--all", error: "Stage inspection requires a single run." };
75677
76134
  }
75678
76135
  if (target.kind === "malformed" || target.kind === "not_found") {
75679
76136
  return { action: "stage", runId: target.target, error: target.message };
75680
76137
  }
75681
- const stage = resolveToolStageTarget(target.runId, args.stageId);
76138
+ const stage = resolveToolStageTarget(target.runId, args.stageId, activeStore);
75682
76139
  if (!stage.ok || stage.stageId === undefined) {
75683
76140
  return {
75684
76141
  action: "stage",
@@ -75687,7 +76144,7 @@ function workflowStageResult(args) {
75687
76144
  };
75688
76145
  }
75689
76146
  const stageRunId = stage.runId ?? target.runId;
75690
- const run2 = store.runs().find((r) => r.id === stageRunId);
76147
+ const run2 = activeStore.runs().find((r) => r.id === stageRunId);
75691
76148
  const snapshot = run2?.stages.find((s) => s.id === stage.stageId);
75692
76149
  return snapshot ? { action: "stage", runId: stageRunId, stage: cloneStage(snapshot) } : {
75693
76150
  action: "stage",
@@ -75695,8 +76152,9 @@ function workflowStageResult(args) {
75695
76152
  error: `Stage not found in run ${stageRunId}: ${stage.stageId}`
75696
76153
  };
75697
76154
  }
75698
- function workflowTranscriptResult(args) {
75699
- const target = resolveToolRunTarget(args, "No active run to inspect.");
76155
+ function workflowTranscriptResult(args, source) {
76156
+ const activeStore = source?.store ?? store;
76157
+ const target = resolveToolRunTarget(args, "No active run to inspect.", activeStore);
75700
76158
  if (target.kind === "all") {
75701
76159
  return {
75702
76160
  action: "transcript",
@@ -75717,7 +76175,7 @@ function workflowTranscriptResult(args) {
75717
76175
  truncated: false
75718
76176
  };
75719
76177
  }
75720
- const stage = resolveToolStageTarget(target.runId, args.stageId);
76178
+ const stage = resolveToolStageTarget(target.runId, args.stageId, activeStore);
75721
76179
  if (!stage.ok || stage.stageId === undefined) {
75722
76180
  return {
75723
76181
  action: "transcript",
@@ -75729,9 +76187,9 @@ function workflowTranscriptResult(args) {
75729
76187
  };
75730
76188
  }
75731
76189
  const stageRunId = stage.runId ?? target.runId;
75732
- const run2 = store.runs().find((r) => r.id === stageRunId);
76190
+ const run2 = activeStore.runs().find((r) => r.id === stageRunId);
75733
76191
  const snapshot = run2?.stages.find((s) => s.id === stage.stageId);
75734
- const liveHandle2 = stageControlRegistry.get(stageRunId, stage.stageId);
76192
+ const liveHandle2 = source?.allowLiveHandles === false ? undefined : stageControlRegistry.get(stageRunId, stage.stageId);
75735
76193
  if (liveHandle2 !== undefined) {
75736
76194
  const sessionFile = liveHandle2.sessionFile ?? snapshot?.sessionFile;
75737
76195
  const sessionId = liveHandle2.sessionId ?? snapshot?.sessionId;
@@ -75791,6 +76249,41 @@ function brokerAnswerFromArgs(args) {
75791
76249
  const text = textPayloadFromArgs(args);
75792
76250
  return text !== undefined ? { text } : {};
75793
76251
  }
76252
+ function answersPendingPrompt(delivery, promptId) {
76253
+ return delivery === "answer" || delivery === "auto" || promptId !== undefined;
76254
+ }
76255
+ function answerablePromptIds(stage) {
76256
+ const target = stage.workflowGraphTarget;
76257
+ const ids = [];
76258
+ const brokered = stageUiBroker.peekStagePrompt(target.runId, target.stageId);
76259
+ if (brokered !== undefined)
76260
+ ids.push(brokered.id);
76261
+ if (stage.pendingPrompt !== undefined)
76262
+ ids.push(stage.pendingPrompt.id);
76263
+ if (stage.status === "awaiting_input" && stage.promptFootprint?.kind === "custom") {
76264
+ ids.push(stage.promptFootprint.id);
76265
+ }
76266
+ return ids;
76267
+ }
76268
+ function inferPromptStageTarget(runId, delivery, promptId) {
76269
+ if (!answersPendingPrompt(delivery, promptId))
76270
+ return { ok: true, runId };
76271
+ const pending = expandWorkflowGraph(readGraphStoreSnapshot(store), runId).stages.filter((stage) => {
76272
+ const ids = answerablePromptIds(stage);
76273
+ return promptId === undefined ? ids.length > 0 : ids.includes(promptId);
76274
+ });
76275
+ const only = pending[0];
76276
+ if (pending.length === 1 && only !== undefined) {
76277
+ return { ok: true, runId: only.workflowGraphTarget.runId, stageId: only.workflowGraphTarget.stageId };
76278
+ }
76279
+ if (pending.length === 0) {
76280
+ return promptId === undefined ? { ok: true, runId } : { ok: false, message: `No pending prompt ${promptId} in run ${runId}.` };
76281
+ }
76282
+ return {
76283
+ ok: false,
76284
+ message: `${pending.length} prompts pending; pass stageId: ${pending.map(expandedStageLabel).join(", ")}`
76285
+ };
76286
+ }
75794
76287
  function workflowSendResult(runId, stageId, delivery, status, message) {
75795
76288
  return { action: "send", runId, stageId, delivery, status, message };
75796
76289
  }
@@ -75851,7 +76344,8 @@ async function workflowSendAction(args, deps = {}) {
75851
76344
  if (rootRun !== undefined && isTerminalRunStatus(rootRun.status)) {
75852
76345
  return terminalWorkflowSendResult(rootRun.id, args.stageId?.trim() ?? "", rootRun.status);
75853
76346
  }
75854
- const stage = resolveToolStageTarget(target.runId, args.stageId);
76347
+ const requested = resolveToolStageTarget(target.runId, args.stageId);
76348
+ const stage = requested.ok && requested.stageId === undefined ? inferPromptStageTarget(target.runId, requestedDelivery, args.promptId) : requested;
75855
76349
  if (!stage.ok || stage.stageId === undefined) {
75856
76350
  return workflowSendResult(target.runId, "", requestedDelivery, "noop", stage.ok ? "Stage id or name is required." : stage.message);
75857
76351
  }
@@ -75992,6 +76486,32 @@ async function workflowSendAction(args, deps = {}) {
75992
76486
  }
75993
76487
 
75994
76488
  // dist/builtin/workflows/src/extension/workflow-tool.ts
76489
+ async function resolveDurableInspectionSource(args, runtime) {
76490
+ const target = args.runId?.trim();
76491
+ if (args.all === true || target === undefined || target.length === 0 || target === "--all")
76492
+ return { kind: "local" };
76493
+ const local = resolveRunId(target);
76494
+ if (local.kind !== "not_found")
76495
+ return { kind: "local" };
76496
+ const durable = await runtime.inspectDurableWorkflow(target);
76497
+ if (durable.kind !== "found")
76498
+ return { kind: "error", message: durable.message };
76499
+ return { kind: "durable", source: { store: durable.store, allowLiveHandles: false } };
76500
+ }
76501
+ function durableInspectionError(action, runId, message) {
76502
+ if (action === "stages")
76503
+ return { action, runId, filter: "all", stages: [], error: message };
76504
+ if (action === "stage")
76505
+ return { action, runId, error: message };
76506
+ return {
76507
+ action,
76508
+ runId,
76509
+ stageId: "",
76510
+ source: "error",
76511
+ entries: [{ role: "notice", text: message }],
76512
+ truncated: false
76513
+ };
76514
+ }
75995
76515
  function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflowResourcesLoaded = () => {}, sendDeps = {}) {
75996
76516
  return async function executeWorkflowTool(args, ctx) {
75997
76517
  const action = args.action ?? "run";
@@ -76047,7 +76567,8 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
76047
76567
  return { action: "statusDetail", runId: target, error: resolved.message };
76048
76568
  }
76049
76569
  if (resolved.kind === "not_found") {
76050
- return { action: "statusDetail", runId: target, error: `run not found: ${target}` };
76570
+ const durable = await getRuntime().inspectDurableWorkflow(target);
76571
+ return durable.kind === "found" ? { action: "statusDetail", runId: target, detail: durable.detail } : { action: "statusDetail", runId: target, error: durable.message };
76051
76572
  }
76052
76573
  const result2 = inspectRun(resolved.runId);
76053
76574
  return result2.ok ? { action: "statusDetail", runId: result2.runId, detail: result2.detail } : { action: "statusDetail", runId: target, error: `run not found: ${target}` };
@@ -76063,11 +76584,18 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
76063
76584
  return result;
76064
76585
  }
76065
76586
  case "stages":
76066
- return workflowStagesResult(args);
76067
76587
  case "stage":
76068
- return workflowStageResult(args);
76069
- case "transcript":
76070
- return workflowTranscriptResult(args);
76588
+ case "transcript": {
76589
+ const resolved = await resolveDurableInspectionSource(args, getRuntime());
76590
+ if (resolved.kind === "error")
76591
+ return durableInspectionError(action, args.runId ?? "", resolved.message);
76592
+ const source = resolved.kind === "durable" ? resolved.source : undefined;
76593
+ if (action === "stages")
76594
+ return workflowStagesResult(args, source);
76595
+ if (action === "stage")
76596
+ return workflowStageResult(args, source);
76597
+ return workflowTranscriptResult(args, source);
76598
+ }
76071
76599
  case "send":
76072
76600
  return workflowSendAction(args, sendDeps);
76073
76601
  case "pause":