@bastani/atomic 0.9.12 → 0.9.13-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/README.md +5 -12
  4. package/dist/builtin/intercom/broker/client.ts +11 -2
  5. package/dist/builtin/intercom/contact-supervisor-tool.ts +13 -3
  6. package/dist/builtin/intercom/index-heavy.ts +13 -3
  7. package/dist/builtin/intercom/index.ts +43 -19
  8. package/dist/builtin/intercom/intercom-utils.ts +15 -29
  9. package/dist/builtin/intercom/package.json +1 -1
  10. package/dist/builtin/intercom/source-ownership.ts +9 -12
  11. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  12. package/dist/builtin/mcp/README.md +3 -1
  13. package/dist/builtin/mcp/index.ts +10 -16
  14. package/dist/builtin/mcp/package.json +1 -1
  15. package/dist/builtin/mcp/startup-warmup.ts +20 -8
  16. package/dist/builtin/subagents/CHANGELOG.md +30 -0
  17. package/dist/builtin/subagents/README.md +22 -30
  18. package/dist/builtin/subagents/package.json +1 -1
  19. package/dist/builtin/subagents/src/extension/doctor.ts +1 -1
  20. package/dist/builtin/subagents/src/extension/fanout-child.ts +76 -56
  21. package/dist/builtin/subagents/src/extension/index.ts +23 -32
  22. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +25 -44
  23. package/dist/builtin/subagents/src/intercom/result-intercom.ts +7 -7
  24. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +166 -434
  25. package/dist/builtin/subagents/src/runs/background/notify.ts +29 -8
  26. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +2 -2
  27. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +4 -4
  28. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +2 -2
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +2 -2
  30. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +1 -281
  31. package/dist/builtin/subagents/src/runs/foreground/execution-structured-retries.ts +11 -75
  32. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +457 -0
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +119 -246
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +1 -1
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +18 -9
  36. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +1 -1
  37. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -4
  38. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +1 -646
  39. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +2 -7
  40. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +5 -5
  41. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +7 -8
  42. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +14 -5
  43. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +158 -93
  44. package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +179 -0
  45. package/dist/builtin/subagents/src/runs/inprocess/background.ts +89 -0
  46. package/dist/builtin/subagents/src/runs/inprocess/child-policy.ts +27 -0
  47. package/dist/builtin/subagents/src/runs/inprocess/control-registry.ts +55 -0
  48. package/dist/builtin/subagents/src/runs/inprocess/control-status.ts +106 -0
  49. package/dist/builtin/subagents/src/runs/inprocess/index.ts +50 -0
  50. package/dist/builtin/subagents/src/runs/inprocess/nested-routing.ts +218 -0
  51. package/dist/builtin/subagents/src/runs/{shared/subagent-prompt-runtime.ts → inprocess/prompt-behavior.ts} +37 -75
  52. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +1138 -0
  53. package/dist/builtin/subagents/src/runs/{shared/nested-events.ts → inprocess/runtime-support/nested-api.ts} +13 -7
  54. package/dist/builtin/subagents/src/runs/{shared/nested-events-control.ts → inprocess/runtime-support/nested-control.ts} +18 -17
  55. package/dist/builtin/subagents/src/runs/{shared/nested-events-core.ts → inprocess/runtime-support/nested-core.ts} +14 -61
  56. package/dist/builtin/subagents/src/runs/{shared/nested-events-projection.ts → inprocess/runtime-support/nested-projection.ts} +4 -10
  57. package/dist/builtin/subagents/src/runs/{shared/nested-events-registry.ts → inprocess/runtime-support/nested-registry.ts} +19 -4
  58. package/dist/builtin/subagents/src/runs/{shared/nested-render.ts → inprocess/runtime-support/nested-rendering.ts} +3 -3
  59. package/dist/builtin/subagents/src/runs/{shared/nested-events-sanitize.ts → inprocess/runtime-support/nested-sanitize.ts} +3 -3
  60. package/dist/builtin/subagents/src/runs/inprocess.ts +1 -0
  61. package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +10 -4
  62. package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +0 -1
  63. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +15 -492
  64. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +13 -11
  65. package/dist/builtin/subagents/src/runs/shared/run-history.ts +3 -5
  66. package/dist/builtin/subagents/src/runs/shared/single-output.ts +4 -4
  67. package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +5 -5
  68. package/dist/builtin/subagents/src/shared/types-async.ts +0 -1
  69. package/dist/builtin/subagents/src/shared/types-config.ts +18 -0
  70. package/dist/builtin/subagents/src/shared/types-results.ts +9 -2
  71. package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +2 -3
  72. package/dist/builtin/subagents/src/slash/slash-live-state.ts +2 -2
  73. package/dist/builtin/subagents/src/tui/render-chain-graph.ts +10 -4
  74. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +12 -11
  75. package/dist/builtin/subagents/src/tui/render-result-compact.ts +49 -12
  76. package/dist/builtin/subagents/src/tui/render-result.ts +46 -17
  77. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -1
  78. package/dist/builtin/subagents/src/tui/render-status-progress.ts +8 -5
  79. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -8
  80. package/dist/builtin/subagents/src/tui/render-widget.ts +0 -24
  81. package/dist/builtin/web-access/package.json +1 -1
  82. package/dist/builtin/workflows/CHANGELOG.md +25 -0
  83. package/dist/builtin/workflows/README.md +13 -3
  84. package/dist/builtin/workflows/package.json +1 -1
  85. package/dist/builtin/workflows/src/durable/backend.ts +7 -0
  86. package/dist/builtin/workflows/src/durable/dbos-metadata.ts +11 -1
  87. package/dist/builtin/workflows/src/durable/resume-runtime.ts +1 -0
  88. package/dist/builtin/workflows/src/durable/tool-primitive.ts +4 -28
  89. package/dist/builtin/workflows/src/durable/types.ts +5 -0
  90. package/dist/builtin/workflows/src/engine/run.ts +17 -1
  91. package/dist/builtin/workflows/src/extension/atomic-stage-session.ts +12 -39
  92. package/dist/builtin/workflows/src/extension/config-file-loader.ts +10 -1
  93. package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
  94. package/dist/builtin/workflows/src/extension/dispatcher.ts +4 -0
  95. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1151 -737
  96. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +303 -38
  97. package/dist/builtin/workflows/src/extension/render-result.ts +4 -0
  98. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -3
  99. package/dist/builtin/workflows/src/extension/runtime.ts +7 -1
  100. package/dist/builtin/workflows/src/extension/wiring.ts +53 -11
  101. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +12 -3
  102. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +5 -2
  103. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +9 -7
  104. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +11 -0
  105. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +18 -1
  106. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +10 -8
  107. package/dist/builtin/workflows/src/extension/workflow-tool.ts +9 -4
  108. package/dist/builtin/workflows/src/runs/background/quit.ts +28 -4
  109. package/dist/builtin/workflows/src/runs/background/resume-acknowledgements.ts +2 -1
  110. package/dist/builtin/workflows/src/runs/background/status.ts +55 -8
  111. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +4 -0
  112. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -1
  113. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +1 -1
  114. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +1 -1
  115. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +688 -38
  116. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -1
  117. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +19 -514
  118. package/dist/builtin/workflows/src/runs/shared/retry.ts +44 -0
  119. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +5 -0
  120. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +6 -0
  121. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +5 -1
  122. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +5 -0
  123. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +5 -0
  124. package/dist/builtin/workflows/src/shared/persistence-restore.ts +3 -0
  125. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  126. package/dist/builtin/workflows/src/shared/run-indicator-status.ts +75 -0
  127. package/dist/builtin/workflows/src/shared/store-public-types.ts +43 -7
  128. package/dist/builtin/workflows/src/shared/store-run-methods.ts +42 -8
  129. package/dist/builtin/workflows/src/shared/store-stage-methods.ts +31 -8
  130. package/dist/builtin/workflows/src/shared/store-types.ts +34 -0
  131. package/dist/builtin/workflows/src/shared/timing.ts +10 -0
  132. package/dist/builtin/workflows/src/tui/chat-surface-message.ts +22 -2
  133. package/dist/builtin/workflows/src/tui/session-list.ts +1 -1
  134. package/dist/builtin/workflows/src/tui/session-overlays.ts +14 -10
  135. package/dist/builtin/workflows/src/tui/session-picker.ts +20 -4
  136. package/dist/builtin/workflows/src/tui/status-list.ts +31 -47
  137. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +9 -1
  138. package/dist/builtin/workflows/src/tui/widget.ts +16 -46
  139. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  140. package/dist/core/agent-session-auto-compaction.js +54 -11
  141. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  142. package/dist/core/agent-session-events.d.ts.map +1 -1
  143. package/dist/core/agent-session-events.js +49 -14
  144. package/dist/core/agent-session-events.js.map +1 -1
  145. package/dist/core/agent-session-methods.d.ts +13 -0
  146. package/dist/core/agent-session-methods.d.ts.map +1 -1
  147. package/dist/core/agent-session-methods.js.map +1 -1
  148. package/dist/core/agent-session-models.d.ts.map +1 -1
  149. package/dist/core/agent-session-models.js +12 -1
  150. package/dist/core/agent-session-models.js.map +1 -1
  151. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  152. package/dist/core/agent-session-prompt.js +4 -0
  153. package/dist/core/agent-session-prompt.js.map +1 -1
  154. package/dist/core/agent-session-retry.d.ts +21 -4
  155. package/dist/core/agent-session-retry.d.ts.map +1 -1
  156. package/dist/core/agent-session-retry.js +232 -106
  157. package/dist/core/agent-session-retry.js.map +1 -1
  158. package/dist/core/agent-session-state.d.ts.map +1 -1
  159. package/dist/core/agent-session-state.js +2 -1
  160. package/dist/core/agent-session-state.js.map +1 -1
  161. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  162. package/dist/core/agent-session-tool-hooks.js +12 -0
  163. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  164. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  165. package/dist/core/agent-session-tool-registry.js +1 -1
  166. package/dist/core/agent-session-tool-registry.js.map +1 -1
  167. package/dist/core/agent-session-types.d.ts +3 -1
  168. package/dist/core/agent-session-types.d.ts.map +1 -1
  169. package/dist/core/agent-session-types.js.map +1 -1
  170. package/dist/core/agent-session.d.ts +12 -1
  171. package/dist/core/agent-session.d.ts.map +1 -1
  172. package/dist/core/agent-session.js +7 -0
  173. package/dist/core/agent-session.js.map +1 -1
  174. package/dist/core/extensions/context-types.d.ts +25 -0
  175. package/dist/core/extensions/context-types.d.ts.map +1 -1
  176. package/dist/core/extensions/context-types.js.map +1 -1
  177. package/dist/core/extensions/index.d.ts +1 -1
  178. package/dist/core/extensions/index.d.ts.map +1 -1
  179. package/dist/core/extensions/index.js.map +1 -1
  180. package/dist/core/extensions/runner-context.d.ts +2 -1
  181. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  182. package/dist/core/extensions/runner-context.js +4 -0
  183. package/dist/core/extensions/runner-context.js.map +1 -1
  184. package/dist/core/extensions/runner.d.ts +3 -2
  185. package/dist/core/extensions/runner.d.ts.map +1 -1
  186. package/dist/core/extensions/runner.js +3 -1
  187. package/dist/core/extensions/runner.js.map +1 -1
  188. package/dist/core/model-fallback-failures.d.ts +30 -0
  189. package/dist/core/model-fallback-failures.d.ts.map +1 -0
  190. package/dist/core/model-fallback-failures.js +610 -0
  191. package/dist/core/model-fallback-failures.js.map +1 -0
  192. package/dist/core/retry-policy.d.ts +30 -0
  193. package/dist/core/retry-policy.d.ts.map +1 -0
  194. package/dist/core/retry-policy.js +25 -0
  195. package/dist/core/retry-policy.js.map +1 -0
  196. package/dist/core/sdk-types.d.ts +8 -2
  197. package/dist/core/sdk-types.d.ts.map +1 -1
  198. package/dist/core/sdk-types.js.map +1 -1
  199. package/dist/core/sdk.d.ts.map +1 -1
  200. package/dist/core/sdk.js +6 -1
  201. package/dist/core/sdk.js.map +1 -1
  202. package/dist/core/session-manager-core.d.ts +2 -0
  203. package/dist/core/session-manager-core.d.ts.map +1 -1
  204. package/dist/core/session-manager-core.js +7 -0
  205. package/dist/core/session-manager-core.js.map +1 -1
  206. package/dist/index-extensions.d.ts +1 -1
  207. package/dist/index-extensions.d.ts.map +1 -1
  208. package/dist/index-extensions.js.map +1 -1
  209. package/dist/index.d.ts +3 -0
  210. package/dist/index.d.ts.map +1 -1
  211. package/dist/index.js +2 -0
  212. package/dist/index.js.map +1 -1
  213. package/docs/intercom.md +5 -10
  214. package/docs/settings.md +7 -1
  215. package/docs/subagents.md +19 -19
  216. package/docs/workflows.md +21 -5
  217. package/examples/extensions/gondolin/package-lock.json +3 -3
  218. package/npm-shrinkwrap.json +29 -29
  219. package/package.json +2 -2
  220. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +0 -112
  221. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +0 -543
  222. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +0 -140
  223. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +0 -279
  224. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +0 -99
  225. package/dist/builtin/subagents/src/runs/background/async-execution.ts +0 -17
  226. package/dist/builtin/subagents/src/runs/background/async-resume.ts +0 -402
  227. package/dist/builtin/subagents/src/runs/background/async-status.ts +0 -411
  228. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +0 -196
  229. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +0 -102
  230. package/dist/builtin/subagents/src/runs/background/parallel-groups.ts +0 -59
  231. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +0 -300
  232. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +0 -166
  233. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +0 -77
  234. package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +0 -48
  235. package/dist/builtin/subagents/src/runs/background/result-status.ts +0 -87
  236. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +0 -72
  237. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +0 -320
  238. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +0 -105
  239. package/dist/builtin/subagents/src/runs/background/run-status.ts +0 -358
  240. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +0 -457
  241. package/dist/builtin/subagents/src/runs/background/subagent-runner-dynamic.ts +0 -376
  242. package/dist/builtin/subagents/src/runs/background/subagent-runner-finalize.ts +0 -150
  243. package/dist/builtin/subagents/src/runs/background/subagent-runner-output.ts +0 -109
  244. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel-helpers.ts +0 -128
  245. package/dist/builtin/subagents/src/runs/background/subagent-runner-parallel.ts +0 -309
  246. package/dist/builtin/subagents/src/runs/background/subagent-runner-sequential.ts +0 -173
  247. package/dist/builtin/subagents/src/runs/background/subagent-runner-state.ts +0 -578
  248. package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +0 -330
  249. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +0 -330
  250. package/dist/builtin/subagents/src/runs/background/subagent-runner-types.ts +0 -210
  251. package/dist/builtin/subagents/src/runs/background/subagent-runner-utils.ts +0 -69
  252. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +0 -90
  253. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +0 -12
  254. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-control.ts +0 -140
  255. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +0 -108
  256. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-types.ts +0 -17
  257. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +0 -558
  258. package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +0 -126
  259. package/dist/builtin/subagents/src/runs/shared/final-drain.ts +0 -39
  260. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +0 -339
  261. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +0 -165
  262. package/dist/builtin/subagents/src/runs/shared/spawn-env.ts +0 -21
  263. package/dist/builtin/subagents/src/shared/post-exit-stdio-guard.ts +0 -86
  264. /package/dist/builtin/subagents/src/runs/{shared/nested-path.ts → inprocess/runtime-support/nested-paths.ts} +0 -0
@@ -36157,6 +36157,10 @@ function topLevelWorkflowRuns(runs) {
36157
36157
  function nonNegative(ms) {
36158
36158
  return Math.max(0, ms);
36159
36159
  }
36160
+ function nextControlTimestamp(requestedAt, previousAt) {
36161
+ const candidate = requestedAt ?? Date.now();
36162
+ return previousAt === undefined || candidate > previousAt ? candidate : previousAt + 1;
36163
+ }
36160
36164
  function rebasedStageStartedAt(accumulatedDurationMs, resumedAt) {
36161
36165
  return resumedAt - nonNegative(accumulatedDurationMs ?? 0);
36162
36166
  }
@@ -36333,38 +36337,65 @@ function createRunStoreMethods(context) {
36333
36337
  if (TERMINAL_STATUSES.has(run.status))
36334
36338
  return false;
36335
36339
  const wasPaused = run.status === "paused";
36336
- const enteringQuit = metadata?.exitReason === "quit" && run.exitReason !== "quit";
36337
36340
  if (!wasPaused) {
36338
36341
  run.status = "paused";
36339
- run.pausedAt = pausedAt ?? Date.now();
36342
+ run.pausedAt = nextControlTimestamp(pausedAt, run.resumedAt);
36340
36343
  run.resumedAt = undefined;
36344
+ delete run.pauseActor;
36345
+ delete run.resumeActor;
36346
+ delete run.resumeSource;
36341
36347
  }
36342
36348
  if (metadata?.resumable !== undefined)
36343
36349
  run.resumable = metadata.resumable;
36344
36350
  if (metadata?.exitReason !== undefined)
36345
36351
  run.exitReason = metadata.exitReason;
36346
- if (enteringQuit)
36347
- run.quitAt = Date.now();
36352
+ if (metadata?.exitReason === "quit" && run.quitAt === undefined)
36353
+ run.quitAt = nextControlTimestamp(undefined, run.pausedAt);
36354
+ if (metadata?.actor !== undefined)
36355
+ run.pauseActor = metadata.actor;
36348
36356
  if (wasPaused && metadata === undefined)
36349
36357
  return false;
36350
36358
  context.bumpAndNotify();
36351
36359
  return true;
36352
36360
  },
36353
- recordRunResumed(runId, resumedAt) {
36361
+ recordRunResumed(runId, resumedAt, metadata) {
36354
36362
  const run = context.findRun(runId);
36355
36363
  if (!run)
36356
36364
  return false;
36357
36365
  if (TERMINAL_STATUSES.has(run.status))
36358
36366
  return false;
36359
- if (run.status !== "paused")
36367
+ const claimsRunControl = metadata?.source === "run_control";
36368
+ if (run.status !== "paused") {
36369
+ if (!claimsRunControl || run.status !== "running" || run.resumedAt === undefined)
36370
+ return false;
36371
+ if (run.resumeSource === "run_control" && run.resumeActor === metadata?.actor)
36372
+ return false;
36373
+ run.resumeSource = "run_control";
36374
+ if (metadata?.actor === undefined)
36375
+ delete run.resumeActor;
36376
+ else
36377
+ run.resumeActor = metadata.actor;
36378
+ context.bumpAndNotify();
36360
36379
  return false;
36361
- const resumedTs = resumedAt ?? Date.now();
36380
+ }
36381
+ const resumedTs = nextControlTimestamp(resumedAt, run.pausedAt);
36362
36382
  run.status = "running";
36363
36383
  run.pausedDurationMs = accumulatePausedDurationMs(run.pausedDurationMs, run.pausedAt, resumedTs);
36364
36384
  run.resumedAt = resumedTs;
36365
36385
  run.pausedAt = undefined;
36366
36386
  delete run.quitAt;
36367
36387
  delete run.exitReason;
36388
+ delete run.pauseActor;
36389
+ if (metadata === undefined) {
36390
+ delete run.resumeActor;
36391
+ delete run.resumeSource;
36392
+ } else {
36393
+ run.resumeSource = metadata.source;
36394
+ if (metadata.actor === undefined)
36395
+ delete run.resumeActor;
36396
+ else
36397
+ run.resumeActor = metadata.actor;
36398
+ }
36368
36399
  context.bumpAndNotify();
36369
36400
  return true;
36370
36401
  },
@@ -36717,7 +36748,7 @@ function createStageStoreMethods(context) {
36717
36748
  context.bumpAndNotify();
36718
36749
  return true;
36719
36750
  },
36720
- recordStagePaused(runId, stageId, pausedAt) {
36751
+ recordStagePaused(runId, stageId, pausedAt, metadata) {
36721
36752
  const run = context.findRun(runId);
36722
36753
  if (!run)
36723
36754
  return false;
@@ -36726,16 +36757,28 @@ function createStageStoreMethods(context) {
36726
36757
  const stage = context.findStage(run, stageId);
36727
36758
  if (!stage)
36728
36759
  return false;
36729
- if (cannotPause(stage.status))
36760
+ if (cannotPause(stage.status)) {
36761
+ if (stage.status !== "paused" || metadata?.actor === undefined)
36762
+ return false;
36763
+ if (stage.pauseActor === metadata.actor)
36764
+ return false;
36765
+ stage.pauseActor = metadata.actor;
36766
+ context.bumpAndNotify();
36730
36767
  return false;
36768
+ }
36731
36769
  stage.status = "paused";
36732
- stage.pausedAt = pausedAt ?? Date.now();
36770
+ stage.pausedAt = nextControlTimestamp(pausedAt, stage.resumedAt);
36733
36771
  stage.resumedAt = undefined;
36734
36772
  delete stage.awaitingInputSince;
36773
+ delete stage.resumeActor;
36774
+ if (metadata?.actor === undefined)
36775
+ delete stage.pauseActor;
36776
+ else
36777
+ stage.pauseActor = metadata.actor;
36735
36778
  context.bumpAndNotify();
36736
36779
  return true;
36737
36780
  },
36738
- recordStageResumed(runId, stageId, resumedAt) {
36781
+ recordStageResumed(runId, stageId, resumedAt, metadata) {
36739
36782
  const run = context.findRun(runId);
36740
36783
  if (!run)
36741
36784
  return false;
@@ -36744,9 +36787,17 @@ function createStageStoreMethods(context) {
36744
36787
  const stage = context.findStage(run, stageId);
36745
36788
  if (!stage)
36746
36789
  return false;
36747
- if (stage.status !== "paused" && stage.status !== "blocked")
36790
+ if (stage.status !== "paused" && stage.status !== "blocked") {
36791
+ if (stage.status !== "running" || stage.resumedAt === undefined || metadata?.actor === undefined) {
36792
+ return false;
36793
+ }
36794
+ if (stage.resumeActor === metadata.actor)
36795
+ return false;
36796
+ stage.resumeActor = metadata.actor;
36797
+ context.bumpAndNotify();
36748
36798
  return false;
36749
- const resumedTs = resumedAt ?? Date.now();
36799
+ }
36800
+ const resumedTs = nextControlTimestamp(resumedAt, stage.pausedAt);
36750
36801
  stage.status = "running";
36751
36802
  if (stage.startedAt !== undefined) {
36752
36803
  stage.pausedDurationMs = accumulatePausedDurationMs(stage.pausedDurationMs, stage.pausedAt, resumedTs);
@@ -36755,6 +36806,11 @@ function createStageStoreMethods(context) {
36755
36806
  stage.pausedAt = undefined;
36756
36807
  delete stage.blockedByStageId;
36757
36808
  delete stage.awaitingInputSince;
36809
+ delete stage.pauseActor;
36810
+ if (metadata?.actor === undefined)
36811
+ delete stage.resumeActor;
36812
+ else
36813
+ stage.resumeActor = metadata.actor;
36758
36814
  context.bumpAndNotify();
36759
36815
  return true;
36760
36816
  }
@@ -37724,6 +37780,51 @@ function stringResultField(result, key) {
37724
37780
  return typeof value2 === "string" && value2.trim().length > 0 ? value2.trim() : undefined;
37725
37781
  }
37726
37782
 
37783
+ // dist/builtin/workflows/src/shared/run-indicator-status.ts
37784
+ var TERMINAL_OR_BLOCKED = new Set(["completed", "failed", "killed", "cancelled", "skipped", "blocked"]);
37785
+ function runIndicatorStatus(run, allRuns = [run]) {
37786
+ const status = effectiveRunStatus(run);
37787
+ if (TERMINAL_OR_BLOCKED.has(status))
37788
+ return status;
37789
+ if (runHasPendingInput(run))
37790
+ return "awaiting_input";
37791
+ const runsById = new Map(allRuns.map((candidate) => [candidate.id, candidate]));
37792
+ for (const candidate of allRuns) {
37793
+ if (candidate.id === run.id || !runBelongsTo(candidate, run, runsById))
37794
+ continue;
37795
+ const candidateStatus = effectiveRunStatus(candidate);
37796
+ if (!TERMINAL_OR_BLOCKED.has(candidateStatus) && runHasPendingInput(candidate))
37797
+ return "awaiting_input";
37798
+ }
37799
+ return status;
37800
+ }
37801
+ function resolveRunIndicatorStatuses(runs, allRuns) {
37802
+ const statuses = {};
37803
+ for (const run of runs)
37804
+ statuses[run.id] = runIndicatorStatus(run, allRuns);
37805
+ return statuses;
37806
+ }
37807
+ function runHasPendingInput(run) {
37808
+ if (run.pendingPrompt !== undefined)
37809
+ return true;
37810
+ return run.stages.some((stage) => stage.status === "awaiting_input" || stage.awaitingInputSince !== undefined || stage.pendingPrompt !== undefined || stage.inputRequest !== undefined);
37811
+ }
37812
+ function runBelongsTo(candidate, ancestor, runsById) {
37813
+ if (candidate.rootRunId === ancestor.id)
37814
+ return true;
37815
+ const visited = new Set;
37816
+ let current = candidate;
37817
+ while (current !== undefined && current.parentRunId !== undefined) {
37818
+ if (current.parentRunId === ancestor.id)
37819
+ return true;
37820
+ if (visited.has(current.id))
37821
+ return false;
37822
+ visited.add(current.id);
37823
+ current = runsById.get(current.parentRunId);
37824
+ }
37825
+ return false;
37826
+ }
37827
+
37727
37828
  // dist/builtin/workflows/src/tui/status-list.ts
37728
37829
  var STAGE_LABEL_BUDGET = 24;
37729
37830
  function isQuitRun(run) {
@@ -37744,7 +37845,7 @@ function renderStatusList(runs, opts = {}) {
37744
37845
  for (let i = 0;i < sorted.length; i++) {
37745
37846
  if (i > 0)
37746
37847
  body.push("");
37747
- body.push(...renderRunEntry(sorted[i], now, cardWidth, opts.theme));
37848
+ body.push(...renderRunEntry(sorted[i], now, cardWidth, opts.theme, opts.allRuns ?? runs, opts.indicatorStatuses));
37748
37849
  }
37749
37850
  }
37750
37851
  if (opts.showDetailHint !== false && sorted.length > 0) {
@@ -37759,11 +37860,12 @@ function renderStatusList(runs, opts = {}) {
37759
37860
  width
37760
37861
  });
37761
37862
  }
37762
- function renderRunEntry(run, now, width, theme) {
37863
+ function renderRunEntry(run, now, width, theme, allRuns, indicatorStatuses) {
37763
37864
  const bodyWidth = effectiveWidth2(width);
37764
37865
  const interior = Math.max(8, bodyWidth - 4);
37765
- const glyph = statusIconForRun(run);
37766
- const glyphFg = theme ? hexToAnsi(runAccent(run, theme)) : "";
37866
+ const indicatorStatus = indicatorStatuses?.[run.id] ?? runIndicatorStatus(run, allRuns);
37867
+ const glyph = statusIconForRun(run, indicatorStatus);
37868
+ const glyphFg = theme ? hexToAnsi(runAccent(run, theme, indicatorStatus)) : "";
37767
37869
  const accent = theme ? hexToAnsi(theme.accent) : "";
37768
37870
  const text = theme ? hexToAnsi(theme.text) : "";
37769
37871
  const muted = theme ? hexToAnsi(theme.textMuted) : "";
@@ -37799,31 +37901,12 @@ function renderRunEntry(run, now, width, theme) {
37799
37901
  const metaLine = ` ${modeSeg} ${strip}${" ".repeat(gap)}${metaSeg} `;
37800
37902
  return [...identityRows, identity, metaLine];
37801
37903
  }
37802
- function runAccent(run, theme) {
37904
+ function runAccent(run, theme, indicatorStatus) {
37803
37905
  if (!theme)
37804
37906
  return "#000000";
37805
37907
  if (isQuitRun(run))
37806
37908
  return theme.warning;
37807
- switch (effectiveRunStatus(run)) {
37808
- case "completed":
37809
- return theme.success;
37810
- case "running":
37811
- return theme.warning;
37812
- case "paused":
37813
- return theme.warning;
37814
- case "skipped":
37815
- return theme.dim;
37816
- case "cancelled":
37817
- return theme.dim;
37818
- case "blocked":
37819
- return theme.dim;
37820
- case "failed":
37821
- return theme.error;
37822
- case "killed":
37823
- return theme.error;
37824
- default:
37825
- return theme.dim;
37826
- }
37909
+ return statusColor(indicatorStatus, theme);
37827
37910
  }
37828
37911
  function runTrailing(run, theme) {
37829
37912
  if (isQuitRun(run))
@@ -38074,28 +38157,10 @@ function emptyStateLine(theme) {
38074
38157
  return " no workflow runs in current session";
38075
38158
  return ` ${hexToAnsi(theme.dim)}no workflow runs in current session${RESET}`;
38076
38159
  }
38077
- function statusIconForRun(run) {
38160
+ function statusIconForRun(run, indicatorStatus) {
38078
38161
  if (isQuitRun(run))
38079
- return "";
38080
- switch (effectiveRunStatus(run)) {
38081
- case "completed":
38082
- return "✓";
38083
- case "skipped":
38084
- case "cancelled":
38085
- return "⊘";
38086
- case "blocked":
38087
- return "↑";
38088
- case "running":
38089
- return "●";
38090
- case "paused":
38091
- return "❚❚";
38092
- case "failed":
38093
- return "✗";
38094
- case "killed":
38095
- return "⊘";
38096
- default:
38097
- return "○";
38098
- }
38162
+ return statusIcon("pending");
38163
+ return statusIcon(indicatorStatus);
38099
38164
  }
38100
38165
 
38101
38166
  // dist/builtin/workflows/src/tui/workflow-list.ts
@@ -38235,7 +38300,12 @@ function renderChatSurfacePlainText(payload, options = {}) {
38235
38300
  `);
38236
38301
  }
38237
38302
  case "status": {
38238
- const rendered = renderStatusList(payload.runs, { width, now, ...themed });
38303
+ const rendered = renderStatusList(payload.runs, {
38304
+ width,
38305
+ now,
38306
+ ...themed,
38307
+ indicatorStatuses: payload.indicatorStatuses
38308
+ });
38239
38309
  if (payload.runs.length === 0)
38240
38310
  return rendered;
38241
38311
  return [
@@ -38325,7 +38395,12 @@ function renderPayload(payload, theme, width, now) {
38325
38395
  width
38326
38396
  });
38327
38397
  case "status":
38328
- return renderStatusList(payload.runs, { theme, width, now });
38398
+ return renderStatusList(payload.runs, {
38399
+ theme,
38400
+ width,
38401
+ now,
38402
+ indicatorStatuses: payload.indicatorStatuses
38403
+ });
38329
38404
  case "list":
38330
38405
  return renderWorkflowList(payload.entries, { theme, width });
38331
38406
  case "detail":
@@ -46389,13 +46464,8 @@ function quitExpiryTimestamp(run) {
46389
46464
  function recentlyQuit(run, now) {
46390
46465
  return isQuitRun2(run) && now - quitExpiryTimestamp(run) <= RECENT_ENDED_WINDOW_MS;
46391
46466
  }
46392
- function runAwaitsInput(run) {
46393
- return run.endedAt === undefined && (run.pendingPrompt !== undefined || run.stages.some((s) => s.status === "awaiting_input"));
46394
- }
46395
46467
  function subtreeAwaitsInput(root, allRuns) {
46396
- if (runAwaitsInput(root))
46397
- return true;
46398
- return allRuns.some((run) => run.rootRunId === root.id && runAwaitsInput(run));
46468
+ return runIndicatorStatus(root, allRuns) === "awaiting_input";
46399
46469
  }
46400
46470
  function countRuns(runs, allRuns = runs) {
46401
46471
  const counts = { active: 0, paused: 0, quit: 0, done: 0, blocked: 0, failed: 0, awaiting: 0 };
@@ -46413,7 +46483,7 @@ function countRuns(runs, allRuns = runs) {
46413
46483
  counts.done++;
46414
46484
  else if (status === "failed" || status === "killed")
46415
46485
  counts.failed++;
46416
- if (r.endedAt === undefined && subtreeAwaitsInput(r, allRuns))
46486
+ if (r.endedAt === undefined && !isQuitRun2(r) && subtreeAwaitsInput(r, allRuns))
46417
46487
  counts.awaiting++;
46418
46488
  }
46419
46489
  return counts;
@@ -46445,32 +46515,17 @@ function selectDisplayRuns(snap, now) {
46445
46515
  const sort = (xs) => [...xs].sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
46446
46516
  return [...sort(active), ...sort(recent)];
46447
46517
  }
46448
- function statusGlyph(run) {
46518
+ function statusGlyph(run, allRuns) {
46449
46519
  if (isQuitRun2(run))
46450
- return "";
46451
- switch (effectiveRunStatus(run)) {
46452
- case "running":
46453
- return "●";
46454
- case "paused":
46455
- return "❚❚";
46456
- case "completed":
46457
- return "✓";
46458
- case "skipped":
46459
- case "cancelled":
46460
- return "⊘";
46461
- case "blocked":
46462
- return "↑";
46463
- case "failed":
46464
- return "✗";
46465
- case "killed":
46466
- return "⊘";
46467
- default:
46468
- return "○";
46469
- }
46520
+ return statusIcon("pending");
46521
+ return statusIcon(runIndicatorStatus(run, allRuns));
46470
46522
  }
46471
- function statusFg(run, theme) {
46523
+ function statusFg(run, theme, allRuns) {
46472
46524
  if (isQuitRun2(run))
46473
46525
  return theme.warning;
46526
+ const indicatorStatus = runIndicatorStatus(run, allRuns);
46527
+ if (indicatorStatus === "awaiting_input")
46528
+ return statusColor(indicatorStatus, theme);
46474
46529
  switch (effectiveRunStatus(run)) {
46475
46530
  case "running":
46476
46531
  case "paused":
@@ -46568,25 +46623,25 @@ function formatTitleBadges(badges, theme, themed) {
46568
46623
  const fallbackFg = hexToAnsi(theme.border);
46569
46624
  return badges.map((b) => `${b.fg ? hexToAnsi(b.fg) : fallbackFg}${b.text}${RESET}${fallbackFg}`).join(" ");
46570
46625
  }
46571
- function themedRunLines(run, now, theme) {
46626
+ function themedRunLines(run, now, theme, allRuns) {
46572
46627
  const meta = metaLine(run, now);
46573
46628
  const metaColor = effectiveRunStatus(run) === "running" ? theme.textMuted : theme.dim;
46574
46629
  return renderRunIdentityRows({
46575
46630
  runId: run.id,
46576
46631
  name: run.name,
46577
46632
  meta,
46578
- glyph: statusGlyph(run),
46579
- glyphColor: statusFg(run, theme),
46633
+ glyph: statusGlyph(run, allRuns),
46634
+ glyphColor: statusFg(run, theme, allRuns),
46580
46635
  metaColor,
46581
46636
  theme
46582
46637
  });
46583
46638
  }
46584
- function plainRunLines(run, now) {
46639
+ function plainRunLines(run, now, allRuns) {
46585
46640
  return renderRunIdentityRows({
46586
46641
  runId: run.id,
46587
46642
  name: run.name,
46588
46643
  meta: metaLine(run, now),
46589
- glyph: statusGlyph(run)
46644
+ glyph: statusGlyph(run, allRuns)
46590
46645
  });
46591
46646
  }
46592
46647
  function themedCollapsed(counts, theme) {
@@ -46635,7 +46690,7 @@ function buildThemedWidgetLines(snap, piTheme, width = 120, now = Date.now()) {
46635
46690
  const body = [];
46636
46691
  for (let i = 0;i < display.length; i++) {
46637
46692
  const run = display[i];
46638
- const runLines = themed ? themedRunLines(run, now, graphTheme) : plainRunLines(run, now);
46693
+ const runLines = themed ? themedRunLines(run, now, graphTheme, snap.runs) : plainRunLines(run, now, snap.runs);
46639
46694
  body.push(...runLines);
46640
46695
  if (i < display.length - 1)
46641
46696
  body.push("");
@@ -46659,6 +46714,13 @@ var STALE_CONTEXT = "This extension ctx is stale";
46659
46714
  function isStale(err) {
46660
46715
  return err instanceof Error && err.message.includes(STALE_CONTEXT);
46661
46716
  }
46717
+ function liveWidgetSnapshot(storeInstance) {
46718
+ return {
46719
+ runs: storeInstance.runs(),
46720
+ notices: storeInstance.notices(),
46721
+ version: 0
46722
+ };
46723
+ }
46662
46724
  function installStoreWidget(pi, storeInstance, timers = defaultTimerApi) {
46663
46725
  const ui = pi.ui;
46664
46726
  if (!ui?.setWidget)
@@ -46672,7 +46734,7 @@ function installStoreWidget(pi, storeInstance, timers = defaultTimerApi) {
46672
46734
  key: WIDGET_KEY,
46673
46735
  placement: "belowEditor",
46674
46736
  timers,
46675
- getSnapshot: () => readGraphStoreSnapshot(storeInstance),
46737
+ getSnapshot: () => liveWidgetSnapshot(storeInstance),
46676
46738
  subscribe: (listener) => subscribeStoreInvalidation(storeInstance, listener),
46677
46739
  getPreviewLines: (snap, now) => buildThemedWidgetLines(snap, undefined, 120, now),
46678
46740
  render: (snap, { theme, width, now }) => buildThemedWidgetLines(snap, theme, width, now),
@@ -47745,6 +47807,7 @@ class InMemoryDurableBackend {
47745
47807
  createdAt: handle.createdAt,
47746
47808
  status: handle.status,
47747
47809
  ...handle.invocationCwd !== undefined ? { invocationCwd: handle.invocationCwd } : existing?.handle.invocationCwd !== undefined ? { invocationCwd: existing.handle.invocationCwd } : {},
47810
+ ...handle.origin !== undefined ? { origin: handle.origin } : existing?.handle.origin !== undefined ? { origin: existing.handle.origin } : {},
47748
47811
  ...handle.workflowCwd !== undefined ? { workflowCwd: handle.workflowCwd } : existing?.handle.workflowCwd !== undefined ? { workflowCwd: existing.handle.workflowCwd } : {},
47749
47812
  ...handle.repositoryRoot !== undefined ? { repositoryRoot: handle.repositoryRoot } : existing?.handle.repositoryRoot !== undefined ? { repositoryRoot: existing.handle.repositoryRoot } : {},
47750
47813
  ...handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: handle.gitWorktreeRoot } : existing?.handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: existing.handle.gitWorktreeRoot } : {},
@@ -47959,6 +48022,7 @@ class InMemoryDurableBackend {
47959
48022
  ...h.resumable !== undefined ? { resumable: h.resumable } : {},
47960
48023
  ...workflowFailureFields(h),
47961
48024
  ...h.invocationCwd !== undefined ? { invocationCwd: h.invocationCwd } : {},
48025
+ ...h.origin !== undefined ? { origin: h.origin } : {},
47962
48026
  ...h.workflowCwd !== undefined ? { workflowCwd: h.workflowCwd } : {},
47963
48027
  ...h.repositoryRoot !== undefined ? { repositoryRoot: h.repositoryRoot } : {},
47964
48028
  ...h.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: h.gitWorktreeRoot } : {},
@@ -48026,6 +48090,7 @@ function toResumableEntry(handle) {
48026
48090
  ...handle.resumable !== undefined ? { resumable: handle.resumable } : {},
48027
48091
  ...workflowFailureFields(handle),
48028
48092
  ...handle.invocationCwd !== undefined ? { invocationCwd: handle.invocationCwd } : {},
48093
+ ...handle.origin !== undefined ? { origin: handle.origin } : {},
48029
48094
  ...handle.workflowCwd !== undefined ? { workflowCwd: handle.workflowCwd } : {},
48030
48095
  ...handle.repositoryRoot !== undefined ? { repositoryRoot: handle.repositoryRoot } : {},
48031
48096
  ...handle.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: handle.gitWorktreeRoot } : {},
@@ -49235,6 +49300,7 @@ function encodeMetadata(metadata) {
49235
49300
  ...metadata.failureRecoverability !== undefined ? { failureRecoverability: metadata.failureRecoverability } : {},
49236
49301
  ...metadata.failureDisposition !== undefined ? { failureDisposition: metadata.failureDisposition } : {},
49237
49302
  ...metadata.failedToolNodeId !== undefined ? { failedToolNodeId: metadata.failedToolNodeId } : {},
49303
+ ...metadata.origin !== undefined ? { origin: metadata.origin } : {},
49238
49304
  ...metadata.invocationCwd !== undefined ? { invocationCwd: metadata.invocationCwd } : {},
49239
49305
  ...metadata.workflowCwd !== undefined ? { workflowCwd: metadata.workflowCwd } : {},
49240
49306
  ...metadata.repositoryRoot !== undefined ? { repositoryRoot: metadata.repositoryRoot } : {},
@@ -49280,7 +49346,14 @@ function parseDurableWorkflowMetadata(value2, workflowId) {
49280
49346
  const metadata = value2;
49281
49347
  if (metadata.workflowId !== workflowId || typeof metadata.workflowId !== "string" || typeof metadata.name !== "string" || typeof metadata.inputs !== "object" || metadata.inputs === null || Array.isArray(metadata.inputs) || typeof metadata.status !== "string" || !isDurableWorkflowStatus(metadata.status) || typeof metadata.completedCheckpoints !== "number" || typeof metadata.createdAt !== "number" || typeof metadata.pendingPrompts !== "number" || typeof metadata.promptReservationEpoch !== "string" || typeof metadata.updatedAt !== "number" || metadata.ownerExecutorId !== undefined && typeof metadata.ownerExecutorId !== "string" || metadata.transitionClaimId !== undefined && typeof metadata.transitionClaimId !== "string" || metadata.sessionFile !== undefined && typeof metadata.sessionFile !== "string" || metadata.label !== undefined && typeof metadata.label !== "string" || metadata.rootWorkflowId !== undefined && typeof metadata.rootWorkflowId !== "string" || metadata.resumable !== undefined && typeof metadata.resumable !== "boolean" || metadata.error !== undefined && typeof metadata.error !== "string" || metadata.failureKind !== undefined && !isWorkflowFailureKind(metadata.failureKind) || metadata.failureCode !== undefined && !isWorkflowFailureCode(metadata.failureCode) || metadata.failureRecoverability !== undefined && !isWorkflowFailureRecoverability(metadata.failureRecoverability) || metadata.failureDisposition !== undefined && !isWorkflowFailureDisposition(metadata.failureDisposition) || metadata.failedToolNodeId !== undefined && typeof metadata.failedToolNodeId !== "string" || metadata.invocationCwd !== undefined && typeof metadata.invocationCwd !== "string" || metadata.workflowCwd !== undefined && typeof metadata.workflowCwd !== "string" || metadata.repositoryRoot !== undefined && typeof metadata.repositoryRoot !== "string" || metadata.gitWorktreeRoot !== undefined && typeof metadata.gitWorktreeRoot !== "string")
49282
49348
  return;
49283
- return metadata;
49349
+ const { origin, ...metadataWithoutOrigin } = metadata;
49350
+ return {
49351
+ ...metadataWithoutOrigin,
49352
+ ...isWorkflowActor(origin) ? { origin } : {}
49353
+ };
49354
+ }
49355
+ function isWorkflowActor(value2) {
49356
+ return value2 === "user" || value2 === "agent";
49284
49357
  }
49285
49358
  function isDurableWorkflowStatus(value2) {
49286
49359
  return value2 === "running" || value2 === "paused" || value2 === "completed" || value2 === "failed" || value2 === "cancelled" || value2 === "blocked";
@@ -51041,7 +51114,7 @@ async function quitRun(runId, opts) {
51041
51114
  return { ok: false, runId, reason: "already_ended" };
51042
51115
  const suspendedByAbort = toolHandles.length > 0;
51043
51116
  const publish = (resumable) => {
51044
- publishLocalQuit(activeStore, runId, pausedRunIds, resumable);
51117
+ publishLocalQuit(activeStore, runId, pausedRunIds, resumable, opts?.actor);
51045
51118
  if (abandonedTools.length > 0)
51046
51119
  jobs.detach(runId, jobs.get(runId));
51047
51120
  };
@@ -51062,10 +51135,17 @@ async function quitRun(runId, opts) {
51062
51135
  publish(true);
51063
51136
  return { ok: true, runId, paused, cancelledTools, abandonedTools };
51064
51137
  }
51065
- function publishLocalQuit(activeStore, runId, pausedRunIds, resumable) {
51066
- for (const pausedRunId of pausedRunIds)
51138
+ function publishLocalQuit(activeStore, runId, pausedRunIds, resumable, actor) {
51139
+ for (const pausedRunId of pausedRunIds) {
51140
+ if (pausedRunId === runId)
51141
+ continue;
51067
51142
  activeStore.recordRunPaused(pausedRunId);
51068
- activeStore.recordRunPaused(runId, undefined, { exitReason: "quit", resumable });
51143
+ }
51144
+ activeStore.recordRunPaused(runId, undefined, {
51145
+ exitReason: "quit",
51146
+ resumable,
51147
+ ...actor === undefined ? {} : { actor }
51148
+ });
51069
51149
  }
51070
51150
  function unrecordedDurableQuitMessage(error) {
51071
51151
  const detail = error instanceof Error ? error.message : String(error);
@@ -51148,7 +51228,8 @@ async function quitAllRuns(opts) {
51148
51228
  store: activeStore,
51149
51229
  stageControlRegistry: opts?.stageControlRegistry,
51150
51230
  toolControlRegistry: opts?.toolControlRegistry,
51151
- jobs: opts?.jobs
51231
+ jobs: opts?.jobs,
51232
+ ...opts?.actor === undefined ? {} : { actor: opts.actor }
51152
51233
  }));
51153
51234
  const settled = await Promise.allSettled(attempts);
51154
51235
  return settled.map((result, index) => {
@@ -51202,6 +51283,7 @@ function appendRunStart(api, payload) {
51202
51283
  ...payload.parentStageId !== undefined ? { parentStageId: payload.parentStageId } : {},
51203
51284
  ...payload.rootRunId !== undefined ? { rootRunId: payload.rootRunId } : {},
51204
51285
  ...payload.resumedFromRunId !== undefined ? { resumedFromRunId: payload.resumedFromRunId } : {},
51286
+ ...payload.origin !== undefined ? { origin: payload.origin } : {},
51205
51287
  ...payload.resumeFromStageId !== undefined ? { resumeFromStageId: payload.resumeFromStageId } : {},
51206
51288
  ...payload.accumulatedDurationMs !== undefined ? { accumulatedDurationMs: payload.accumulatedDurationMs } : {},
51207
51289
  ts: payload.ts
@@ -51405,7 +51487,7 @@ async function settleResumeAcknowledgements(store2, targets, message) {
51405
51487
  failures.push(qualified);
51406
51488
  });
51407
51489
  for (const controlRunId of resumedRunIds)
51408
- store2.recordRunResumed(controlRunId);
51490
+ store2.recordRunResumed(controlRunId, undefined, { source: "acknowledgement" });
51409
51491
  return { resumed, acknowledged, failures, lateFailures };
51410
51492
  }
51411
51493
  async function waitForResumeReconciliation(acknowledged) {
@@ -51526,9 +51608,17 @@ async function resumeRun(runId, opts) {
51526
51608
  const currentRun2 = activeStore.runs().find((candidate) => candidate.id === runId);
51527
51609
  const hasPausedDescendant = workflowHasPausedStages(activeStore, runId);
51528
51610
  if (acknowledgements.acknowledged > 0 || handles.length === 0 && acknowledgements.failures.length === 0 && !hasPausedDescendant && currentRun2?.status === "paused") {
51529
- activeStore.recordRunResumed(runId);
51530
- if (aggregateRootRunId !== runId)
51531
- activeStore.recordRunResumed(aggregateRootRunId);
51611
+ const attributeStage = opts?.stageId !== undefined && (hasPausedDescendant || currentRun2?.resumedAt === undefined);
51612
+ activeStore.recordRunResumed(runId, undefined, {
51613
+ source: "run_control",
51614
+ ...opts?.actor === undefined || attributeStage ? {} : { actor: opts.actor }
51615
+ });
51616
+ if (aggregateRootRunId !== runId) {
51617
+ activeStore.recordRunResumed(aggregateRootRunId, undefined, { source: "run_control" });
51618
+ }
51619
+ if (attributeStage && opts?.actor !== undefined && opts.stageId !== undefined) {
51620
+ activeStore.recordStageResumed(runId, opts.stageId, undefined, { actor: opts.actor });
51621
+ }
51532
51622
  }
51533
51623
  const reconciledRoot = activeStore.runs().find((candidate) => candidate.id === runId);
51534
51624
  if (acknowledgements.failures.length > 0) {
@@ -51603,6 +51693,7 @@ async function pauseRun(runId, opts) {
51603
51693
  const activeStore = opts?.store ?? store;
51604
51694
  const registry = opts?.stageControlRegistry ?? stageControlRegistry;
51605
51695
  const run = activeStore.runs().find((candidate) => candidate.id === runId);
51696
+ const actorMetadata = opts?.actor === undefined ? undefined : { actor: opts.actor };
51606
51697
  if (!run)
51607
51698
  return { ok: false, runId, reason: "not_found" };
51608
51699
  if (run.endedAt !== undefined)
@@ -51621,7 +51712,9 @@ async function pauseRun(runId, opts) {
51621
51712
  const paused2 = stage === undefined ? [] : [structuredClone(stage)];
51622
51713
  const stillActive = currentRun2?.stages.some((candidate) => candidate.id !== opts.stageId && (candidate.status === "running" || candidate.status === "pending")) ?? false;
51623
51714
  if (!stillActive)
51624
- activeStore.recordRunPaused(runId);
51715
+ activeStore.recordRunPaused(runId, undefined, actorMetadata);
51716
+ else if (actorMetadata !== undefined)
51717
+ activeStore.recordStagePaused(runId, opts.stageId, undefined, actorMetadata);
51625
51718
  return { ok: true, runId, paused: paused2 };
51626
51719
  }
51627
51720
  const controlRunIds = expandedControlRunIds(activeStore, runId);
@@ -51639,15 +51732,22 @@ async function pauseRun(runId, opts) {
51639
51732
  if (stage !== undefined)
51640
51733
  paused.push(structuredClone(stage));
51641
51734
  }
51642
- for (const pausedRunId of pausedRunIds)
51735
+ for (const pausedRunId of pausedRunIds) {
51736
+ if (pausedRunId === runId)
51737
+ continue;
51643
51738
  activeStore.recordRunPaused(pausedRunId);
51644
- activeStore.recordRunPaused(runId);
51739
+ }
51740
+ activeStore.recordRunPaused(runId, undefined, actorMetadata);
51645
51741
  return { ok: true, runId, paused };
51646
51742
  }
51647
51743
  async function pauseAllRuns(opts) {
51648
51744
  const activeStore = opts?.store ?? store;
51649
51745
  const inFlight = topLevelWorkflowRuns(activeStore.runs()).filter((run) => run.endedAt === undefined);
51650
- return Promise.all(inFlight.map((run) => pauseRun(run.id, { store: activeStore, stageControlRegistry: opts?.stageControlRegistry })));
51746
+ return Promise.all(inFlight.map((run) => pauseRun(run.id, {
51747
+ store: activeStore,
51748
+ stageControlRegistry: opts?.stageControlRegistry,
51749
+ ...opts?.actor === undefined ? {} : { actor: opts.actor }
51750
+ })));
51651
51751
  }
51652
51752
  async function interruptRun(runId, opts) {
51653
51753
  return pauseRun(runId, opts);
@@ -51995,10 +52095,14 @@ function createLifecycleNoticeDelivery(options) {
51995
52095
  var LIFECYCLE_NOTICE_CUSTOM_TYPE = "workflows:lifecycle-notice";
51996
52096
  var LIFECYCLE_NOTICE_SNIPPET_LIMIT = 240;
51997
52097
  var WORKFLOW_LIFECYCLE_NOTICE_KINDS = [
52098
+ "started",
51998
52099
  "completed",
51999
52100
  "failed",
52000
52101
  "blocked",
52001
- "awaiting_input"
52102
+ "awaiting_input",
52103
+ "paused",
52104
+ "quit",
52105
+ "resumed"
52002
52106
  ];
52003
52107
  var rendererRegisteredHosts4 = new WeakSet;
52004
52108
  function createWorkflowLifecycleNotificationState() {
@@ -52032,6 +52136,12 @@ function seedWorkflowLifecycleNotificationState(state2, snapshot) {
52032
52136
  state2.deliveredTerminalRuns.add(key2);
52033
52137
  }
52034
52138
  }
52139
+ for (const occurrence of controlOccurrences(run)) {
52140
+ const controlKey = controlOccurrenceKey(run, occurrence);
52141
+ if (!state2.pendingTerminalRuns.has(controlKey) && !state2.retryableTerminalRuns.has(controlKey)) {
52142
+ state2.deliveredTerminalRuns.add(controlKey);
52143
+ }
52144
+ }
52035
52145
  if (run.pendingPrompt !== undefined) {
52036
52146
  state2.deliveredInputPrompts.add(runAwaitingInputKey(run.id, run.pendingPrompt));
52037
52147
  }
@@ -52102,6 +52212,22 @@ function installWorkflowLifecycleNotifications(options) {
52102
52212
  }
52103
52213
  delivery.deliver(key2, makeTerminalNotice(run, kind));
52104
52214
  };
52215
+ const emitControlNoticesOnce = (run) => {
52216
+ for (const occurrence of controlOccurrences(run)) {
52217
+ if (!notifyOn.has(occurrence.kind))
52218
+ continue;
52219
+ const key2 = controlOccurrenceKey(run, occurrence);
52220
+ if (state2.deliveredTerminalRuns.has(key2) || state2.pendingTerminalRuns.has(key2) || state2.retryableTerminalRuns.has(key2))
52221
+ continue;
52222
+ if (state2.suppressionDepth > 0) {
52223
+ state2.deliveredTerminalRuns.add(key2);
52224
+ state2.retryableTerminalRuns.delete(key2);
52225
+ state2.retryableTerminalNotices.delete(key2);
52226
+ continue;
52227
+ }
52228
+ delivery.deliver(key2, makeControlNotice(run, occurrence));
52229
+ }
52230
+ };
52105
52231
  const emitStageAwaitingInputNoticeOnce = (run, stage) => {
52106
52232
  if (stage.status !== "awaiting_input")
52107
52233
  return;
@@ -52125,6 +52251,7 @@ function installWorkflowLifecycleNotifications(options) {
52125
52251
  emitTerminalNoticeOnce(run, "completed");
52126
52252
  emitTerminalNoticeOnce(run, "failed");
52127
52253
  emitTerminalNoticeOnce(run, "blocked");
52254
+ emitControlNoticesOnce(run);
52128
52255
  if (!notifyOn.has("awaiting_input"))
52129
52256
  continue;
52130
52257
  emitRunAwaitingInputNoticeOnce(run);
@@ -52163,20 +52290,41 @@ function registerLifecycleNoticeRenderer(options) {
52163
52290
  }
52164
52291
  function formatWorkflowLifecycleNoticeText(details) {
52165
52292
  const workflowName = escapeQuotedText2(details.workflowName);
52293
+ const origin = details.origin === undefined ? "" : details.origin === "agent" ? ", which you started" : ", which the user started";
52294
+ const actor = details.actor === "agent" ? "You" : "The user";
52295
+ if (details.kind === "started") {
52296
+ return `▶ ${actor} started workflow "${workflowName}" (run ${details.runId}). It is running in the background; you will be notified when it completes.`;
52297
+ }
52166
52298
  if (details.kind === "completed") {
52167
- return `✓ Workflow "${workflowName}" completed (run ${details.runId}). Inspect: /workflow status ${details.runId}`;
52299
+ return `✓ Workflow "${workflowName}" completed (run ${details.runId})${origin}. Inspect: /workflow status ${details.runId}`;
52168
52300
  }
52169
52301
  if (details.kind === "failed") {
52170
52302
  const stage2 = details.stageName ?? details.failedStageId ?? details.stageId;
52171
52303
  const tool = lifecycleToolOrigin(details);
52172
- const originText = stage2 ? `, stage ${stage2}` : tool !== undefined ? `, tool ${tool}` : "";
52304
+ const failureSite = stage2 ? `, stage ${stage2}` : tool !== undefined ? `, tool ${tool}` : "";
52173
52305
  const errorText = details.error ? `: ${details.error}` : "";
52174
- return `✗ Workflow "${workflowName}" failed (run ${details.runId}${originText})${errorText}. Inspect: /workflow status ${details.runId}`;
52306
+ return `✗ Workflow "${workflowName}" failed (run ${details.runId}${failureSite})${origin}${errorText}. Inspect: /workflow status ${details.runId}`;
52175
52307
  }
52176
52308
  if (details.kind === "blocked") {
52177
52309
  const errorText = details.error ? `: ${details.error}` : "";
52178
52310
  const stateText = details.active === true ? "is blocked" : "ended blocked";
52179
- return `! Workflow "${workflowName}" ${stateText} (run ${details.runId})${errorText}. Inspect: /workflow status ${details.runId}`;
52311
+ return `! Workflow "${workflowName}" ${stateText} (run ${details.runId})${origin}${errorText}. Inspect: /workflow status ${details.runId}`;
52312
+ }
52313
+ if (details.kind === "paused" || details.kind === "quit") {
52314
+ const stopStage = details.stageName ?? details.stageId;
52315
+ const stageText = stopStage ? `${origin === "" ? "" : ","} at stage ${stopStage}` : "";
52316
+ const scopeText = details.scope === "stage" ? "stage of workflow" : "workflow";
52317
+ const verb = details.kind === "paused" ? "paused" : "quit";
52318
+ const noun = details.kind === "paused" ? "pause" : "stop";
52319
+ const glyph = details.kind === "paused" ? "⏸" : "⏹";
52320
+ return `${glyph} ${actor} ${verb} the ${scopeText} "${workflowName}" (run ${details.runId})${origin}${stageText}. This ${noun} was deliberate and user-requested; do not resume it or take over the work unless asked. Resume: /workflow resume ${details.runId}`;
52321
+ }
52322
+ if (details.kind === "resumed") {
52323
+ const resumedStage = details.stageName ?? details.stageId;
52324
+ const stageText = resumedStage ? `${origin === "" ? "" : ","} at stage ${resumedStage}` : "";
52325
+ const scopeText = details.scope === "stage" ? "stage of workflow" : "workflow";
52326
+ const continues = details.continuedFromRunId === undefined ? "" : `, continuing run ${details.continuedFromRunId}`;
52327
+ return `▶ ${actor} resumed the ${scopeText} "${workflowName}" (run ${details.runId}${continues})${origin}${stageText}. It is running again in the background.`;
52180
52328
  }
52181
52329
  const prompt = details.promptMessage ? ` Prompt: ${details.promptMessage}` : "";
52182
52330
  if (details.scope === "run") {
@@ -52205,9 +52353,31 @@ function makeTerminalNotice(run, kind) {
52205
52353
  ...failedToolNodeId !== undefined ? { toolNodeId: failedToolNodeId } : {},
52206
52354
  ...failedTool !== undefined ? { toolName: failedTool.name } : {},
52207
52355
  ...run.durationMs !== undefined ? { durationMs: run.durationMs } : {},
52356
+ ...run.origin !== undefined ? { origin: run.origin } : {},
52208
52357
  createdAt: lifecycleOccurrenceAt(run, kind) ?? Date.now()
52209
52358
  };
52210
52359
  }
52360
+ function makeControlNotice(run, occurrence) {
52361
+ const stage = occurrence.stage ?? restingStage(run);
52362
+ const elapsedMs = run.durationMs ?? (occurrence.at >= run.startedAt ? occurrence.at - run.startedAt : undefined);
52363
+ return {
52364
+ kind: occurrence.kind,
52365
+ scope: occurrence.scope,
52366
+ runId: run.id,
52367
+ workflowName: run.name,
52368
+ status: occurrence.stage !== undefined ? occurrence.stage.status : run.status,
52369
+ ...stage !== undefined ? { stageId: stage.id, stageName: stage.name } : {},
52370
+ ...elapsedMs !== undefined ? { durationMs: elapsedMs } : {},
52371
+ ...occurrence.kind === "quit" && run.resumable !== undefined ? { resumable: run.resumable } : {},
52372
+ ...occurrence.actor !== undefined ? { actor: occurrence.actor } : {},
52373
+ ...occurrence.kind !== "started" && run.origin !== undefined ? { origin: run.origin } : {},
52374
+ ...occurrence.kind === "resumed" && run.resumedFromRunId !== undefined ? { continuedFromRunId: run.resumedFromRunId } : {},
52375
+ createdAt: occurrence.at
52376
+ };
52377
+ }
52378
+ function restingStage(run) {
52379
+ return run.stages.find((stage) => stage.status === "paused") ?? run.stages.find((stage) => stage.status === "running");
52380
+ }
52211
52381
  function warnLifecycleSendFailure(error) {
52212
52382
  if (process.env.ATOMIC_WORKFLOW_DEBUG !== "1")
52213
52383
  return;
@@ -52255,6 +52425,54 @@ function terminalRunKey(kind, run) {
52255
52425
  const occurrence = kind === "blocked" ? run.blockedAt ?? lifecycleOccurrenceAt(run, kind) : "";
52256
52426
  return occurrence === undefined ? `${kind}:${run.id}` : `${kind}:${run.id}:${occurrence}`;
52257
52427
  }
52428
+ function controlOccurrences(run) {
52429
+ const occurrences = [];
52430
+ const resumedRun = run.resumeSource === "run_control" || run.resumedFromRunId !== undefined;
52431
+ const userResumed = run.resumeSource === "run_control" && run.resumeActor === "user";
52432
+ const continuation = userResumed && run.resumedAt === undefined;
52433
+ if (run.origin === "user" && !resumedRun) {
52434
+ occurrences.push({ kind: "started", scope: "run", at: run.startedAt, actor: "user" });
52435
+ }
52436
+ if (userResumed && run.status === "running") {
52437
+ occurrences.push({
52438
+ kind: "resumed",
52439
+ scope: "run",
52440
+ at: continuation ? run.startedAt : run.resumedAt ?? run.startedAt,
52441
+ actor: "user"
52442
+ });
52443
+ }
52444
+ if (run.endedAt === undefined && run.status === "paused" && run.pauseActor === "user") {
52445
+ if (run.exitReason === "quit") {
52446
+ const quitAt = run.quitAt ?? run.pausedAt;
52447
+ if (quitAt !== undefined)
52448
+ occurrences.push({ kind: "quit", scope: "run", at: quitAt, actor: "user" });
52449
+ } else if (run.pausedAt !== undefined) {
52450
+ occurrences.push({ kind: "paused", scope: "run", at: run.pausedAt, actor: "user" });
52451
+ }
52452
+ }
52453
+ const runScoped = new Set(occurrences.map((occurrence) => occurrence.kind));
52454
+ if (run.endedAt !== undefined)
52455
+ return occurrences;
52456
+ for (const stage of run.stages) {
52457
+ if (stage.status === "paused" && stage.pauseActor === "user" && stage.pausedAt !== undefined) {
52458
+ if (!runScoped.has("paused") && !runScoped.has("quit")) {
52459
+ occurrences.push({ kind: "paused", scope: "stage", at: stage.pausedAt, actor: "user", stage });
52460
+ }
52461
+ }
52462
+ if (stage.status === "running" && stage.resumeActor === "user" && stage.resumedAt !== undefined) {
52463
+ if (!runScoped.has("resumed")) {
52464
+ occurrences.push({ kind: "resumed", scope: "stage", at: stage.resumedAt, actor: "user", stage });
52465
+ }
52466
+ }
52467
+ }
52468
+ return occurrences;
52469
+ }
52470
+ function controlOccurrenceKey(run, occurrence) {
52471
+ if (occurrence.scope === "stage" && occurrence.stage !== undefined) {
52472
+ return `${occurrence.kind}:${run.id}:stage:${occurrence.stage.id}:${occurrence.at}`;
52473
+ }
52474
+ return `${occurrence.kind}:${run.id}:${occurrence.at}`;
52475
+ }
52258
52476
  function awaitingInputKey(runId, stage) {
52259
52477
  const promptId = stage.pendingPrompt?.id ?? stage.inputRequest?.id;
52260
52478
  if (promptId)
@@ -52283,12 +52501,12 @@ function makeNoticeComponent2(details, theme) {
52283
52501
  };
52284
52502
  }
52285
52503
  function renderLifecycleNoticeCard(details, opts) {
52286
- const tone = details.kind === "failed" ? "error" : details.kind === "awaiting_input" || details.kind === "blocked" ? "warning" : "success";
52287
- const title = details.kind === "failed" ? "WORKFLOW FAILED" : details.kind === "awaiting_input" ? "WORKFLOW INPUT" : details.kind === "blocked" ? "WORKFLOW BLOCKED" : "WORKFLOW COMPLETE";
52288
- const glyph = details.kind === "failed" ? "✗" : details.kind === "awaiting_input" ? "?" : details.kind === "blocked" ? "!" : "✓";
52504
+ const tone = lifecycleNoticeTone(details.kind);
52505
+ const title = lifecycleNoticeTitle(details.kind);
52506
+ const glyph = lifecycleNoticeGlyph(details.kind);
52289
52507
  const stage = details.stageName ?? details.failedStageId ?? details.stageId;
52290
52508
  const tool = stage === undefined ? lifecycleToolOrigin(details) : undefined;
52291
- const headline = details.kind === "failed" ? `Workflow "${details.workflowName}" failed` : details.kind === "awaiting_input" ? `Workflow "${details.workflowName}" needs input` : details.kind === "blocked" ? `Workflow "${details.workflowName}" ${details.active === true ? "is blocked" : "ended blocked"}` : `Workflow "${details.workflowName}" completed`;
52509
+ const headline = lifecycleNoticeHeadline(details);
52292
52510
  return renderWorkflowNoticeCard({
52293
52511
  title,
52294
52512
  glyph,
@@ -52297,20 +52515,108 @@ function renderLifecycleNoticeCard(details, opts) {
52297
52515
  fields: [
52298
52516
  { label: "workflow", value: details.workflowName },
52299
52517
  { label: "run", value: details.runId },
52518
+ { label: "continues", value: details.continuedFromRunId },
52300
52519
  { label: "stage", value: stage },
52301
52520
  { label: "tool", value: tool },
52521
+ { label: "actor", value: details.actor, tone: "muted" },
52522
+ { label: "launched", value: details.origin, tone: "muted" },
52302
52523
  { label: "prompt", value: details.promptMessage, tone: "muted" },
52303
52524
  { label: "error", value: details.error, tone: "error" },
52525
+ { label: "resumable", value: resumableFieldValue(details), tone: "muted" },
52304
52526
  { label: "duration", value: formatDurationMs(details.durationMs), tone: "muted" }
52305
52527
  ],
52306
- hints: [
52307
- details.kind === "awaiting_input" ? `/workflow connect ${details.runId}` : `/workflow status ${details.runId}`
52308
- ],
52528
+ hints: [lifecycleNoticeHint(details)],
52309
52529
  fallbackText: opts.fallbackText,
52310
52530
  width: opts.width,
52311
52531
  ...opts.theme ? { theme: opts.theme } : {}
52312
52532
  });
52313
52533
  }
52534
+ function lifecycleNoticeTone(kind) {
52535
+ switch (kind) {
52536
+ case "failed":
52537
+ return "error";
52538
+ case "blocked":
52539
+ case "awaiting_input":
52540
+ case "paused":
52541
+ case "quit":
52542
+ return "warning";
52543
+ default:
52544
+ return "success";
52545
+ }
52546
+ }
52547
+ function lifecycleNoticeTitle(kind) {
52548
+ switch (kind) {
52549
+ case "started":
52550
+ return "WORKFLOW STARTED";
52551
+ case "failed":
52552
+ return "WORKFLOW FAILED";
52553
+ case "awaiting_input":
52554
+ return "WORKFLOW INPUT";
52555
+ case "blocked":
52556
+ return "WORKFLOW BLOCKED";
52557
+ case "paused":
52558
+ return "WORKFLOW PAUSED";
52559
+ case "quit":
52560
+ return "WORKFLOW QUIT";
52561
+ case "resumed":
52562
+ return "WORKFLOW RESUMED";
52563
+ default:
52564
+ return "WORKFLOW COMPLETE";
52565
+ }
52566
+ }
52567
+ function lifecycleNoticeGlyph(kind) {
52568
+ switch (kind) {
52569
+ case "failed":
52570
+ return "✗";
52571
+ case "awaiting_input":
52572
+ return "?";
52573
+ case "blocked":
52574
+ return "!";
52575
+ case "paused":
52576
+ return "⏸";
52577
+ case "quit":
52578
+ return "⏹";
52579
+ case "started":
52580
+ case "resumed":
52581
+ return "▶";
52582
+ default:
52583
+ return "✓";
52584
+ }
52585
+ }
52586
+ function lifecycleNoticeHeadline(details) {
52587
+ const name = details.workflowName;
52588
+ const scope = details.scope === "stage" ? "Stage of workflow" : "Workflow";
52589
+ switch (details.kind) {
52590
+ case "started":
52591
+ return `Workflow "${name}" started`;
52592
+ case "failed":
52593
+ return `Workflow "${name}" failed`;
52594
+ case "awaiting_input":
52595
+ return `Workflow "${name}" needs input`;
52596
+ case "blocked":
52597
+ return `Workflow "${name}" ${details.active === true ? "is blocked" : "ended blocked"}`;
52598
+ case "paused":
52599
+ return `${scope} "${name}" paused`;
52600
+ case "quit":
52601
+ return `Workflow "${name}" quit`;
52602
+ case "resumed":
52603
+ return `${scope} "${name}" resumed`;
52604
+ default:
52605
+ return `Workflow "${name}" completed`;
52606
+ }
52607
+ }
52608
+ function lifecycleNoticeHint(details) {
52609
+ if (details.kind === "awaiting_input")
52610
+ return `/workflow connect ${details.runId}`;
52611
+ if (details.kind === "paused" || details.kind === "quit")
52612
+ return `/workflow resume ${details.runId}`;
52613
+ return `/workflow status ${details.runId}`;
52614
+ }
52615
+ function resumableFieldValue(details) {
52616
+ if (details.resumable === undefined)
52617
+ return;
52618
+ return details.resumable ? "yes" : "no";
52619
+ }
52314
52620
  function formatDurationMs(durationMs) {
52315
52621
  if (durationMs === undefined)
52316
52622
  return;
@@ -52686,6 +52992,11 @@ import { CONFIG_DIR_NAME, CONFIG_DIR_NAMES, getAgentDir, getAgentDirs, getProjec
52686
52992
 
52687
52993
  // dist/builtin/workflows/src/extension/config-file-loader.ts
52688
52994
  var WORKFLOW_LIFECYCLE_NOTICE_KIND_SET = new Set(WORKFLOW_LIFECYCLE_NOTICE_KINDS);
52995
+ var WORKFLOW_LIFECYCLE_NOTICE_KIND_LIST = (() => {
52996
+ const quoted = WORKFLOW_LIFECYCLE_NOTICE_KINDS.map((kind) => JSON.stringify(kind));
52997
+ const last = quoted[quoted.length - 1] ?? "";
52998
+ return quoted.length <= 1 ? last : `${quoted.slice(0, -1).join(", ")}, or ${last}`;
52999
+ })();
52689
53000
  async function tryReadFile(filePath) {
52690
53001
  const { readFile } = await import("node:fs/promises");
52691
53002
  try {
@@ -52741,7 +53052,7 @@ function validateConfig(value2) {
52741
53052
  }
52742
53053
  for (const item of notifyOn) {
52743
53054
  if (!isWorkflowLifecycleNoticeKind(item)) {
52744
- return `"workflowNotifications.notifyOn" entries must be "completed", "failed", "blocked", or "awaiting_input", got ${JSON.stringify(item)}`;
53055
+ return `"workflowNotifications.notifyOn" entries must be ${WORKFLOW_LIFECYCLE_NOTICE_KIND_LIST}, got ${JSON.stringify(item)}`;
52745
53056
  }
52746
53057
  }
52747
53058
  }
@@ -52852,7 +53163,7 @@ var WORKFLOW_CONFIG_DEFAULTS = {
52852
53163
  resumeInFlight: "ask",
52853
53164
  workflowNotifications: {
52854
53165
  enabled: true,
52855
- notifyOn: ["completed", "failed", "blocked", "awaiting_input"]
53166
+ notifyOn: ["started", "completed", "failed", "blocked", "awaiting_input", "paused", "quit", "resumed"]
52856
53167
  },
52857
53168
  worktree: {
52858
53169
  symlinkDirectories: ["node_modules"]
@@ -59478,6 +59789,40 @@ function durableStageCheckpointMetadata(stage, run, sourceOrder) {
59478
59789
  // dist/builtin/workflows/src/durable/tool-primitive.ts
59479
59790
  import { runCallback } from "@bastani/atomic";
59480
59791
 
59792
+ // dist/builtin/workflows/src/runs/shared/retry.ts
59793
+ import { nextRetryDecision } from "@bastani/atomic";
59794
+ function abortReason(signal) {
59795
+ const reason = signal?.reason;
59796
+ if (reason instanceof Error || reason instanceof DOMException || typeof reason === "string")
59797
+ return reason;
59798
+ return new Error("atomic-workflows: workflow cancelled");
59799
+ }
59800
+ function sleepOrAbort(ms, signal) {
59801
+ if (signal?.aborted)
59802
+ return Promise.reject(abortReason(signal));
59803
+ return new Promise((resolve4, reject) => {
59804
+ let settled = false;
59805
+ const cleanup = () => signal?.removeEventListener("abort", onAbort);
59806
+ const finish = () => {
59807
+ if (settled)
59808
+ return;
59809
+ settled = true;
59810
+ cleanup();
59811
+ resolve4();
59812
+ };
59813
+ const fail = (error) => {
59814
+ if (settled)
59815
+ return;
59816
+ settled = true;
59817
+ clearTimeout(timer);
59818
+ cleanup();
59819
+ reject(error);
59820
+ };
59821
+ const timer = setTimeout(finish, Math.max(0, ms));
59822
+ const onAbort = () => fail(abortReason(signal));
59823
+ signal?.addEventListener("abort", onAbort, { once: true });
59824
+ });
59825
+ }
59481
59826
  // dist/builtin/workflows/src/durable/tool-failure-checkpoint.ts
59482
59827
  async function recordThrowingToolFailure(input, node, identity, error, attempts) {
59483
59828
  const message = workflowToolFailure(error, attempts, false).error.message;
@@ -59905,32 +60250,6 @@ async function executeWithRetries(fn, options, throwIfCancelled, signal) {
59905
60250
  }
59906
60251
  throw lastError ?? new Error("ctx.tool: retries exhausted");
59907
60252
  }
59908
- function sleepOrAbort(ms, signal) {
59909
- if (signal?.aborted)
59910
- return Promise.reject(signal.reason instanceof Error ? signal.reason : new Error("atomic-workflows: workflow cancelled"));
59911
- return new Promise((resolve4, reject) => {
59912
- let settled = false;
59913
- const cleanup = () => signal?.removeEventListener("abort", onAbort);
59914
- const finish = () => {
59915
- if (settled)
59916
- return;
59917
- settled = true;
59918
- cleanup();
59919
- resolve4();
59920
- };
59921
- const fail = (err) => {
59922
- if (settled)
59923
- return;
59924
- settled = true;
59925
- clearTimeout(timer);
59926
- cleanup();
59927
- reject(err);
59928
- };
59929
- const timer = setTimeout(finish, ms);
59930
- const onAbort = () => fail(signal?.reason instanceof Error ? signal.reason : new Error("atomic-workflows: workflow cancelled"));
59931
- signal?.addEventListener("abort", onAbort, { once: true });
59932
- });
59933
- }
59934
60253
  function createCheckpointIdGenerator() {
59935
60254
  let counter = 0;
59936
60255
  return () => `cp-${++counter}`;
@@ -60753,455 +61072,13 @@ async function buildModelCandidatesFromCatalog(input) {
60753
61072
  }
60754
61073
  }
60755
61074
  // dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts
60756
- var RETRYABLE_MODEL_FAILURE_PATTERNS = [
60757
- /rate\s*limit/i,
60758
- /too\s*many\s*requests/i,
60759
- /\b429\b/,
60760
- /quota/i,
60761
- /usage[\s_-]*limit/i,
60762
- /billing/i,
60763
- /credit/i,
60764
- /auth(?:entication|orization)?/i,
60765
- /unauthori[sz]ed/i,
60766
- /\b40[13]\b/,
60767
- /api\s*key/i,
60768
- /token\s*expired/i,
60769
- /forbidden/i,
60770
- /invalid\s*key/i,
60771
- /model.*(?:unavailable|disabled|not\s*found|unknown)/i,
60772
- /(?:unavailable|disabled|not\s*found|unknown).*model/i,
60773
- /overloaded/i,
60774
- /temporarily\s*unavailable/i,
60775
- /service\s*unavailable/i,
60776
- /network/i,
60777
- /fetch/i,
60778
- /socket/i,
60779
- /connection\s*refused/i,
60780
- /upstream/i,
60781
- /timeout/i,
60782
- /timed\s*out/i,
60783
- /\b50[0-4]\b/
60784
- ];
60785
- var NON_RETRYABLE_FAILURE_PATTERNS = [
60786
- /command failed/i,
60787
- /tests? failed/i,
60788
- /shell/i,
60789
- /missing file/i,
60790
- /no such file/i,
60791
- /cancel/i,
60792
- /abort/i,
60793
- /interrupted/i
60794
- ];
60795
- var CANCELLED_FAILURE_PATTERNS = [/cancel/i, /abort/i, /interrupted/i];
60796
- var FALLBACKABLE_FAILURE_KINDS = new Set([
60797
- "auth_on_candidate_provider",
60798
- "rate_limit",
60799
- "provider_unavailable",
60800
- "network_timeout",
60801
- "transport_error",
60802
- "model_unavailable",
60803
- "request_incompatible"
60804
- ]);
60805
- function asRecord2(value2) {
60806
- return value2 !== null && typeof value2 === "object" ? value2 : undefined;
60807
- }
60808
- function field2(value2, key2) {
60809
- return asRecord2(value2)?.[key2];
60810
- }
60811
- function stringField3(value2, key2) {
60812
- const raw = field2(value2, key2);
60813
- return typeof raw === "string" && raw.trim().length > 0 ? raw : undefined;
60814
- }
60815
- function errorName2(value2) {
60816
- return value2 instanceof Error ? value2.name : stringField3(value2, "name");
60817
- }
60818
- function directMessageFrom(value2) {
60819
- return stringField3(value2, "errorMessage") ?? stringField3(value2, "message") ?? stringField3(value2, "statusText");
60820
- }
60821
- function integerFrom2(value2) {
60822
- if (typeof value2 === "number" && Number.isInteger(value2))
60823
- return value2;
60824
- if (typeof value2 !== "string" || value2.trim().length === 0)
60825
- return;
60826
- const parsed = Number(value2.trim());
60827
- return Number.isInteger(parsed) ? parsed : undefined;
60828
- }
60829
- function statusFrom(value2) {
60830
- return integerFrom2(field2(value2, "status")) ?? integerFrom2(field2(value2, "statusCode")) ?? integerFrom2(field2(value2, "httpStatus"));
60831
- }
60832
- function codeFrom(value2) {
60833
- const rawCode = field2(value2, "code");
60834
- return typeof rawCode === "string" || typeof rawCode === "number" ? rawCode : undefined;
60835
- }
60836
- function stopReasonFrom(value2) {
60837
- return stringField3(value2, "stopReason");
60838
- }
60839
- function finishReasonFrom(value2) {
60840
- return stringField3(value2, "finish_reason") ?? stringField3(value2, "finishReason");
60841
- }
60842
- function causeOf2(value2) {
60843
- return value2 instanceof Error ? value2.cause : field2(value2, "cause");
60844
- }
60845
- function diagnosticErrors2(value2) {
60846
- const diagnostics = field2(value2, "diagnostics");
60847
- if (!Array.isArray(diagnostics))
60848
- return [];
60849
- const errors = [];
60850
- for (const diagnostic of diagnostics) {
60851
- const diagnosticError = field2(diagnostic, "error");
60852
- errors.push(diagnosticError ?? diagnostic);
60853
- }
60854
- return errors;
60855
- }
60856
- function normalizeCode2(value2) {
60857
- if (value2 === undefined)
60858
- return;
60859
- const normalized = String(value2).trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
60860
- return normalized.length > 0 ? normalized : undefined;
60861
- }
60862
- function kindFromStatus(status) {
60863
- switch (status) {
60864
- case 400:
60865
- case 413:
60866
- case 422:
60867
- return "request_incompatible";
60868
- case 401:
60869
- case 403:
60870
- return "auth_on_candidate_provider";
60871
- case 408:
60872
- return "network_timeout";
60873
- case 404:
60874
- return "model_unavailable";
60875
- case 429:
60876
- return "rate_limit";
60877
- default:
60878
- if (status !== undefined && status >= 500 && status <= 599)
60879
- return "provider_unavailable";
60880
- return;
60881
- }
60882
- }
60883
- function refusalKindFromCode(code) {
60884
- const normalizedCode = normalizeCode2(code);
60885
- if (normalizedCode === undefined)
60886
- return;
60887
- if (normalizedCode.includes("content_filter") || normalizedCode.includes("contentfilter"))
60888
- return "task_failure";
60889
- if (normalizedCode.includes("safety") || normalizedCode.includes("policy"))
60890
- return "task_failure";
60891
- switch (normalizedCode) {
60892
- case "blocked":
60893
- case "blocked_by_provider":
60894
- case "blocked_by_safety":
60895
- case "blocked_by_policy":
60896
- case "provider_refusal":
60897
- case "refusal":
60898
- case "tool_refusal":
60899
- case "tool_call_refusal":
60900
- case "tool_use_refusal":
60901
- return "task_failure";
60902
- default:
60903
- return;
60904
- }
60905
- }
60906
- var REQUEST_INCOMPATIBLE_CODES = new Set([
60907
- "invalid_request",
60908
- "invalid_request_error",
60909
- "bad_request",
60910
- "context_length_exceeded",
60911
- "request_too_large",
60912
- "too_large",
60913
- "request_entity_too_large",
60914
- "max_tokens",
60915
- "max_context_length",
60916
- "context_window_exceeded"
60917
- ]);
60918
- function requestIncompatibleKindFromCode(code) {
60919
- const normalizedCode = normalizeCode2(code);
60920
- return normalizedCode !== undefined && REQUEST_INCOMPATIBLE_CODES.has(normalizedCode) ? "request_incompatible" : undefined;
60921
- }
60922
- function kindFromCode(code) {
60923
- const normalizedCode = normalizeCode2(code);
60924
- if (normalizedCode === undefined)
60925
- return;
60926
- const refusalKind = refusalKindFromCode(code);
60927
- if (refusalKind !== undefined)
60928
- return refusalKind;
60929
- const httpStatusKind = kindFromStatus(integerFrom2(code));
60930
- if (httpStatusKind !== undefined)
60931
- return httpStatusKind;
60932
- const requestIncompatibleKind = requestIncompatibleKindFromCode(code);
60933
- if (requestIncompatibleKind !== undefined)
60934
- return requestIncompatibleKind;
60935
- switch (normalizedCode) {
60936
- case "auth":
60937
- case "auth_required":
60938
- case "authentication_required":
60939
- case "unauthorized":
60940
- case "forbidden":
60941
- case "invalid_api_key":
60942
- case "missing_api_key":
60943
- case "invalid_key":
60944
- return "auth_on_candidate_provider";
60945
- case "etimedout":
60946
- case "econnreset":
60947
- case "econnrefused":
60948
- case "enotfound":
60949
- case "eai_again":
60950
- case "fetch_failed":
60951
- case "network_error":
60952
- case "timeout":
60953
- case "timeout_error":
60954
- case "und_err_connect_timeout":
60955
- return "network_timeout";
60956
- case "rate_limit":
60957
- case "rate_limit_exceeded":
60958
- case "too_many_requests":
60959
- case "quota_exceeded":
60960
- case "insufficient_quota":
60961
- case "usage_limit":
60962
- case "usage_limit_reached":
60963
- case "usage_limit_exceeded":
60964
- return "rate_limit";
60965
- case "aborterror":
60966
- case "aborted":
60967
- case "cancelled":
60968
- case "canceled":
60969
- return "cancelled";
60970
- case "model_not_found":
60971
- case "model_unavailable":
60972
- case "model_disabled":
60973
- case "unknown_model":
60974
- return "model_unavailable";
60975
- case "provider_error":
60976
- case "api_error":
60977
- case "service_unavailable":
60978
- case "temporarily_unavailable":
60979
- case "overloaded":
60980
- return "provider_unavailable";
60981
- default:
60982
- return;
60983
- }
60984
- }
60985
- var REQUEST_INCOMPATIBLE_FAILURE_PATTERNS = [
60986
- /\bcontext[_\s-]?length(?:[_\s-]?exceeded)?\b/i,
60987
- /\bcontext[_\s-]?window(?:[_\s-]?exceeded)?\b/i,
60988
- /\bmax[_\s-]?context\b/i,
60989
- /\bmax[_\s-]?tokens?\b/i,
60990
- /\brequest(?:[_\s-]?entity)?[_\s-]?too[_\s-]?large\b/i,
60991
- /\btoo[_\s-]?large\b/i,
60992
- /\b(?:unsupported|unknown|invalid)\s+(?:tool|parameter|function)\b/i,
60993
- /\b(?:tool|parameter|function)\s+(?:not\s+(?:supported|found|allowed)|unknown|invalid)\b/i,
60994
- /\binvalid[_\s-]?request(?:[_\s-]?error)?\b/i,
60995
- /\bbad[_\s-]?request\b/i
60996
- ];
60997
- var PROVIDER_REFUSAL_FAILURE_PATTERNS = [
60998
- /\bfinish[_\s-]?reason\b[^\n]*\bcontent[_\s-]?filter\b/i,
60999
- /\bcontent[_\s-]?filter(?:ed|ing)?\b/i,
61000
- /\b(?:safety|policy)\b[^\n]*\b(?:refus(?:e|al|ed|es|ing)?|block(?:ed|ing)?|filter(?:ed|ing)?|violat(?:e|ion|ed|ing)?|disallow(?:ed|ing)?|reject(?:ed|ion|ing)?)\b/i,
61001
- /\b(?:refus(?:e|al|ed|es|ing)?|block(?:ed|ing)?|filter(?:ed|ing)?|violat(?:e|ion|ed|ing)?|disallow(?:ed|ing)?|reject(?:ed|ion|ing)?)\b[^\n]*\b(?:safety|policy)\b/i,
61002
- /\btool[_\s-]?(?:call|use)?[_\s-]?refus(?:e|al|ed|es|ing)?\b/i,
61003
- /\btool(?:\s+call|\s+use)?\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b/i,
61004
- /\brefus(?:e|al|ed|es|ing)?\b[^\n]*\btool(?:\s+call|\s+use)?\b/i,
61005
- /\bprovider[_\s-]?refus(?:e|al|ed|es|ing)?\b/i,
61006
- /\bprovider\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b[^\n]*\b(?:prompt|request|content|policy|safety)\b/i
61007
- ];
61008
- var TRANSPORT_OUTAGE_FAILURE_PATTERNS = [/^connection\s+error\.?$/i, /^fetch\s+failed\.?$/i];
61009
- function transportOutageKindFromMessage(message) {
61010
- return TRANSPORT_OUTAGE_FAILURE_PATTERNS.some((pattern) => pattern.test(message.trim())) ? "transport_error" : undefined;
61011
- }
61012
- function refusalKindFromMessage(message) {
61013
- if (CANCELLED_FAILURE_PATTERNS.some((pattern) => pattern.test(message)))
61014
- return "cancelled";
61015
- if (NON_RETRYABLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message)))
61016
- return "task_failure";
61017
- if (PROVIDER_REFUSAL_FAILURE_PATTERNS.some((pattern) => pattern.test(message)))
61018
- return "task_failure";
61019
- return;
61020
- }
61021
- function fallbackKindFromMessage(message, name) {
61022
- const refusalKind = refusalKindFromMessage(message);
61023
- if (refusalKind !== undefined)
61024
- return refusalKind;
61025
- const transportOutageKind = transportOutageKindFromMessage(message);
61026
- if (transportOutageKind !== undefined)
61027
- return transportOutageKind;
61028
- if (REQUEST_INCOMPATIBLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message)))
61029
- return "request_incompatible";
61030
- const nameKind = kindFromCode(name);
61031
- if (nameKind !== undefined)
61032
- return nameKind;
61033
- if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message)))
61034
- return;
61035
- if (/rate\s*limit|too\s*many\s*requests|\b429\b|quota|usage[\s_-]*limit|billing|credit/i.test(message))
61036
- return "rate_limit";
61037
- if (/auth|unauthori[sz]ed|\b40[13]\b|api\s*key|token\s*expired|forbidden|invalid\s*key/i.test(message))
61038
- return "auth_on_candidate_provider";
61039
- if (/model.*(?:unavailable|disabled|not\s*found|unknown)|(?:unavailable|disabled|not\s*found|unknown).*model/i.test(message))
61040
- return "model_unavailable";
61041
- if (/network|fetch|socket|connection\s*refused|timeout|timed\s*out/i.test(message))
61042
- return "network_timeout";
61043
- return "provider_unavailable";
61044
- }
61045
- function signalSource(value2, fallback) {
61046
- if (fallback !== undefined)
61047
- return fallback;
61048
- if (stopReasonFrom(value2) !== undefined || diagnosticErrors2(value2).length > 0)
61049
- return "assistant_message";
61050
- if (value2 instanceof Error)
61051
- return "throw";
61052
- return "structured";
61053
- }
61054
- function makeSignal(kind, value2, source) {
61055
- const status = statusFrom(value2);
61056
- const code = codeFrom(value2);
61057
- const name = errorName2(value2);
61058
- const stopReason = stopReasonFrom(value2);
61059
- return {
61060
- kind,
61061
- message: errorMessage3(value2),
61062
- source: signalSource(value2, source),
61063
- ...stopReason !== undefined ? { stopReason } : {},
61064
- ...status !== undefined ? { status } : {},
61065
- ...code !== undefined ? { code } : {},
61066
- ...name !== undefined ? { name } : {}
61067
- };
61068
- }
61069
- function fallbackSignalFromDirectMessage(value2, source) {
61070
- const message = directMessageFrom(value2);
61071
- if (message === undefined)
61072
- return;
61073
- const kind = fallbackKindFromMessage(message, errorName2(value2));
61074
- return kind === undefined ? undefined : makeSignal(kind, value2, source);
61075
- }
61076
- function fallbackSignalFromMessage(value2, source) {
61077
- const message = errorMessage3(value2);
61078
- if (!message.trim())
61079
- return;
61080
- const kind = fallbackKindFromMessage(message, errorName2(value2));
61081
- return kind === undefined ? undefined : makeSignal(kind, value2, source);
61082
- }
61083
- function classifyAssistantRefusalSignal(value2, source) {
61084
- const codeRefusalKind = refusalKindFromCode(codeFrom(value2)) ?? refusalKindFromCode(errorName2(value2)) ?? refusalKindFromCode(finishReasonFrom(value2));
61085
- if (codeRefusalKind !== undefined)
61086
- return makeSignal(codeRefusalKind, value2, source);
61087
- const messageRefusalKind = refusalKindFromMessage(directMessageFrom(value2) ?? "");
61088
- return messageRefusalKind === undefined ? undefined : makeSignal(messageRefusalKind, value2, source);
61089
- }
61090
- function isRefusalSignal(signal) {
61091
- return signal.kind === "cancelled" || signal.kind === "task_failure";
61092
- }
61093
- function structuredSignal2(value2, seen, source) {
61094
- if (value2 === undefined || value2 === null || seen.has(value2))
61095
- return;
61096
- if (typeof value2 === "object")
61097
- seen.add(value2);
61098
- const stopReason = stopReasonFrom(value2)?.toLowerCase();
61099
- if (stopReason === "aborted")
61100
- return makeSignal("cancelled", value2, source);
61101
- const directRefusalSignal = classifyAssistantRefusalSignal(value2, source);
61102
- if (directRefusalSignal !== undefined)
61103
- return directRefusalSignal;
61104
- const codeKind = kindFromCode(codeFrom(value2));
61105
- const nameKind = kindFromCode(errorName2(value2));
61106
- if (codeKind === "cancelled" || nameKind === "cancelled")
61107
- return makeSignal("cancelled", value2, source);
61108
- let firstNestedFallbackSignal;
61109
- const nestedSeen = new Set(seen);
61110
- for (const diagnosticError of diagnosticErrors2(value2)) {
61111
- const diagnosticSignal = structuredSignal2(diagnosticError, nestedSeen, "diagnostic") ?? fallbackSignalFromMessage(diagnosticError, "diagnostic");
61112
- if (diagnosticSignal === undefined)
61113
- continue;
61114
- if (isRefusalSignal(diagnosticSignal))
61115
- return diagnosticSignal;
61116
- firstNestedFallbackSignal ??= diagnosticSignal;
61117
- }
61118
- const cause = causeOf2(value2);
61119
- const causeSignal = structuredSignal2(cause, nestedSeen, source) ?? fallbackSignalFromMessage(cause, source);
61120
- if (causeSignal !== undefined) {
61121
- if (isRefusalSignal(causeSignal))
61122
- return causeSignal;
61123
- firstNestedFallbackSignal ??= causeSignal;
61124
- }
61125
- const directMessageSignal = fallbackSignalFromDirectMessage(value2, source);
61126
- if (directMessageSignal !== undefined)
61127
- return directMessageSignal;
61128
- const statusKind = kindFromStatus(statusFrom(value2));
61129
- if (statusKind !== undefined)
61130
- return makeSignal(statusKind, value2, source);
61131
- if (codeKind !== undefined)
61132
- return makeSignal(codeKind, value2, source);
61133
- if (nameKind !== undefined)
61134
- return makeSignal(nameKind, value2, source);
61135
- if (firstNestedFallbackSignal !== undefined)
61136
- return firstNestedFallbackSignal;
61137
- if (stopReason === "error")
61138
- return makeSignal("provider_unavailable", value2, source);
61139
- return;
61140
- }
61141
- function messageFromUnknown(value2, seen) {
61142
- if (value2 === undefined || value2 === null || seen.has(value2))
61143
- return;
61144
- if (typeof value2 === "string")
61145
- return value2.trim().length > 0 ? value2 : undefined;
61146
- if (typeof value2 === "number" || typeof value2 === "boolean" || typeof value2 === "bigint")
61147
- return String(value2);
61148
- if (typeof value2 === "symbol" || typeof value2 === "function")
61149
- return;
61150
- seen.add(value2);
61151
- if (value2 instanceof Error && value2.message.trim().length > 0)
61152
- return value2.message;
61153
- const directMessage = directMessageFrom(value2);
61154
- if (directMessage !== undefined)
61155
- return directMessage;
61156
- for (const diagnosticError of diagnosticErrors2(value2)) {
61157
- const diagnosticMessage = messageFromUnknown(diagnosticError, seen);
61158
- if (diagnosticMessage !== undefined)
61159
- return diagnosticMessage;
61160
- }
61161
- const causeMessage = messageFromUnknown(causeOf2(value2), seen);
61162
- if (causeMessage !== undefined)
61163
- return causeMessage;
61164
- const stopReason = stopReasonFrom(value2);
61165
- if (stopReason !== undefined)
61166
- return `Assistant message ended with stopReason:${stopReason}`;
61167
- const finishReason = finishReasonFrom(value2);
61168
- if (finishReason !== undefined)
61169
- return `Model request finished with finish_reason:${finishReason}`;
61170
- const status = statusFrom(value2);
61171
- if (status !== undefined)
61172
- return `Model request failed with status ${status}`;
61173
- const code = codeFrom(value2);
61174
- if (code !== undefined)
61175
- return `Model request failed with code ${String(code)}`;
61176
- return;
61177
- }
61178
- function errorMessage3(error) {
61179
- const structuredMessage = messageFromUnknown(error, new Set);
61180
- if (structuredMessage !== undefined)
61181
- return structuredMessage;
61182
- const rendered = String(error);
61183
- return rendered === "[object Object]" ? "Model request failed" : rendered;
61184
- }
61185
- function normalizeModelFailureSignal(error) {
61186
- const structured2 = structuredSignal2(error, new Set);
61187
- if (structured2 !== undefined)
61188
- return structured2;
61189
- const message = errorMessage3(error);
61190
- const name = errorName2(error);
61191
- const fallbackKind = message.trim().length > 0 ? fallbackKindFromMessage(message, name) : undefined;
61192
- return {
61193
- kind: fallbackKind ?? "unknown",
61194
- message,
61195
- source: "string_fallback",
61196
- ...name !== undefined ? { name } : {}
61197
- };
61198
- }
61199
- function isRetryableModelFailure(error) {
61200
- if (error === undefined)
61201
- return false;
61202
- const signal = normalizeModelFailureSignal(error);
61203
- return FALLBACKABLE_FAILURE_KINDS.has(signal.kind);
61204
- }
61075
+ import {
61076
+ errorMessage as errorMessage3,
61077
+ isRetryableModelFailure,
61078
+ isRetryableSameModelFailure,
61079
+ modelFailureMessage,
61080
+ normalizeModelFailureSignal
61081
+ } from "@bastani/atomic";
61205
61082
  // dist/builtin/workflows/src/runs/shared/worktree-git.ts
61206
61083
  import * as fs3 from "node:fs";
61207
61084
  import * as path3 from "node:path";
@@ -62541,7 +62418,7 @@ function truncateTaskOutput(text, maxOutput) {
62541
62418
  [workflow output truncated; limits: ${limits.bytes} bytes, ${limits.lines} lines]`;
62542
62419
  }
62543
62420
  function withoutUndefinedProperties(value2) {
62544
- return Object.fromEntries(Object.entries(value2).filter(([, field3]) => field3 !== undefined));
62421
+ return Object.fromEntries(Object.entries(value2).filter(([, field2]) => field2 !== undefined));
62545
62422
  }
62546
62423
  function sharedTaskDefaultsFromOptions(options) {
62547
62424
  const {
@@ -62680,11 +62557,12 @@ function setupWorkflowInputGitWorktree(inputDefaults, workflowInvocationCwd, cac
62680
62557
  function workflowCwdWithInputWorktree(inputDefaults, workflowInvocationCwd, cache) {
62681
62558
  return setupWorkflowInputGitWorktree(inputDefaults, workflowInvocationCwd, cache)?.cwd ?? workflowInvocationCwd;
62682
62559
  }
62683
- function workflowInvocationMetadata(inputDefaults, workflowInvocationCwd, cache) {
62560
+ function workflowInvocationMetadata(inputDefaults, workflowInvocationCwd, cache, origin) {
62684
62561
  const setup = setupWorkflowInputGitWorktree(inputDefaults, workflowInvocationCwd, cache);
62685
62562
  return {
62686
62563
  invocationCwd: workflowInvocationCwd,
62687
- ...setup !== undefined ? { workflowCwd: setup.cwd, repositoryRoot: setup.repositoryRoot, gitWorktreeRoot: setup.worktreeRoot } : {}
62564
+ ...setup !== undefined ? { workflowCwd: setup.cwd, repositoryRoot: setup.repositoryRoot, gitWorktreeRoot: setup.worktreeRoot } : {},
62565
+ ...origin !== undefined ? { origin } : {}
62688
62566
  };
62689
62567
  }
62690
62568
  function resolvedTaskCwd(cwd, workflowInvocationCwd) {
@@ -63409,7 +63287,7 @@ function buildPromptNodeUiAdapter(input) {
63409
63287
  },
63410
63288
  async resume() {
63411
63289
  input.activeStore.recordStageResumed(input.runId, stageId);
63412
- input.activeStore.recordRunResumed(input.runId);
63290
+ input.activeStore.recordRunResumed(input.runId, undefined, { source: "prompt_answer" });
63413
63291
  const currentPauseGate = pauseGate;
63414
63292
  pauseGate = undefined;
63415
63293
  currentPauseGate?.resolve();
@@ -63643,8 +63521,8 @@ function createRunFinalizers(input) {
63643
63521
  ...signal.reason !== undefined ? { exitReason: signal.reason } : {}
63644
63522
  }, input.opts.onRunEnd);
63645
63523
  };
63646
- const finalizeParentWorkflowExitCancellation = async (abortReason) => {
63647
- const parentReason = abortReason.workflowExitReason;
63524
+ const finalizeParentWorkflowExitCancellation = async (abortReason2) => {
63525
+ const parentReason = abortReason2.workflowExitReason;
63648
63526
  await input.drainWorkflowExitCleanups(parentReason);
63649
63527
  const exitReason = parentWorkflowExitRunReason(parentReason);
63650
63528
  const metadata = { resumable: false, exited: true, exitReason };
@@ -65043,6 +64921,7 @@ async function resumeDurableWorkflow(workflowId, deps, catalog) {
65043
64921
  const resumeRunOpts = {
65044
64922
  ...deps.baseRunOpts,
65045
64923
  ...handle.invocationCwd !== undefined ? { cwd: handle.invocationCwd } : {},
64924
+ ...handle.origin !== undefined ? { origin: handle.origin } : {},
65046
64925
  runId: resolved.workflowId,
65047
64926
  durableBackend: backend
65048
64927
  };
@@ -66858,7 +66737,7 @@ function createStageControlHandle(runtime) {
66858
66737
  if (changed) {
66859
66738
  runtime.scheduler.releaseStageBarrier(runtime.stageId);
66860
66739
  await runtime.scheduler.cascadeResumeFrom(runtime.stageId);
66861
- runtime.activeStore.recordRunResumed(runtime.runId);
66740
+ runtime.activeStore.recordRunResumed(runtime.runId, undefined, { source: "stage_control" });
66862
66741
  }
66863
66742
  if (wakeReleasedIdleStageChat)
66864
66743
  runtime.state.wakeWaitingForStageChatTurn?.();
@@ -67028,6 +66907,7 @@ import { createStructuredOutputCapture, runCallback as runCallback2 } from "@bas
67028
66907
 
67029
66908
  // dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts
67030
66909
  import {
66910
+ convertToLlm,
67031
66911
  shouldApplyCodexFastModeForScope
67032
66912
  } from "@bastani/atomic";
67033
66913
 
@@ -67915,6 +67795,54 @@ function terminatingToolCallId(event) {
67915
67795
  }
67916
67796
 
67917
67797
  // dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts
67798
+ function isSessionCreationPauseResult(value2) {
67799
+ return "kind" in value2 && value2.kind === "paused";
67800
+ }
67801
+
67802
+ class StageSessionCreationCancelled extends Error {
67803
+ constructor() {
67804
+ super("atomic-workflows: stage session creation was cancelled while paused");
67805
+ this.name = "StageSessionCreationCancelled";
67806
+ }
67807
+ }
67808
+ function stageUserMessageText(message) {
67809
+ if (message.role !== "user")
67810
+ return;
67811
+ if (typeof message.content === "string")
67812
+ return message.content;
67813
+ return message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
67814
+ }
67815
+ function retrySettingsManagerFromError(error) {
67816
+ if (error === null || typeof error !== "object")
67817
+ return;
67818
+ const manager = error.settingsManager;
67819
+ if (manager === null || typeof manager !== "object")
67820
+ return;
67821
+ const candidate = manager;
67822
+ return typeof candidate.getCodexFastModeSettings === "function" ? candidate : undefined;
67823
+ }
67824
+ function retryableAgentSession(activeSession) {
67825
+ const session = asAgentSession(activeSession);
67826
+ if (session === undefined)
67827
+ return;
67828
+ const candidate = session;
67829
+ return typeof candidate._runAgentContinue === "function" ? session : undefined;
67830
+ }
67831
+ function canContinueFromTranscript(activeSession) {
67832
+ const converted = convertToLlm([...activeSession.messages]);
67833
+ const last = converted[converted.length - 1];
67834
+ return last !== undefined && (last.role === "user" || last.role === "toolResult");
67835
+ }
67836
+
67837
+ class ThrownErrorRetryPaused extends Error {
67838
+ resume;
67839
+ constructor(resume) {
67840
+ super("atomic-workflows: thrown-error retry paused");
67841
+ this.resume = resume;
67842
+ this.name = "ThrownErrorRetryPaused";
67843
+ }
67844
+ }
67845
+
67918
67846
  class StageSessionController {
67919
67847
  opts;
67920
67848
  meta;
@@ -67922,6 +67850,10 @@ class StageSessionController {
67922
67850
  structuredOutputCapture;
67923
67851
  session;
67924
67852
  activeCreation;
67853
+ ownedCreationPromise;
67854
+ abortGeneration = 0;
67855
+ abortReason;
67856
+ abortReasonGeneration = 0;
67925
67857
  sessionPromise;
67926
67858
  reattachSessionFile;
67927
67859
  lastPromptStartIndex;
@@ -67947,7 +67879,10 @@ class StageSessionController {
67947
67879
  pendingFallbackWarnings = [];
67948
67880
  modelCatalog;
67949
67881
  sessionSettingsManager;
67882
+ thrownErrorRetryStates = new Set;
67883
+ creationPauseObservers = new Set;
67950
67884
  replacement = new StageSessionReplacement;
67885
+ pendingCreationResumeMessage;
67951
67886
  messageAdmission = new StageMessageAdmission;
67952
67887
  deliveryActivity = new StageDeliveryActivity;
67953
67888
  constructor(opts, meta2, effectiveStageOptions, structuredOutputCapture) {
@@ -68005,8 +67940,20 @@ class StageSessionController {
68005
67940
  throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68006
67941
  if (this.session !== undefined)
68007
67942
  return this.session;
68008
- if (!this.sessionPromise)
68009
- this.sessionPromise = this.createInitialSession(consumer);
67943
+ if (!this.sessionPromise) {
67944
+ const pending = this.createInitialSession(consumer);
67945
+ this.sessionPromise = pending;
67946
+ this.ownedCreationPromise = pending;
67947
+ const release = () => {
67948
+ if (this.ownedCreationPromise === pending)
67949
+ this.ownedCreationPromise = undefined;
67950
+ };
67951
+ pending.then(release, () => {
67952
+ release();
67953
+ if (this.sessionPromise === pending)
67954
+ this.sessionPromise = undefined;
67955
+ });
67956
+ }
68010
67957
  return this.sessionPromise;
68011
67958
  }
68012
67959
  async ensureSessionFromFile(sessionFile, consumer = "prompt") {
@@ -68025,7 +67972,15 @@ class StageSessionController {
68025
67972
  }
68026
67973
  preparation?.beforePreparation?.();
68027
67974
  const sessionFile = preparation?.sessionFile;
68028
- const deliver = async (activity) => sendStageUserMessage(sessionFile === undefined ? await this.ensureSession("prompt") : await this.ensureSessionFromFile(sessionFile, "prompt"), content, options, beforeDelivery, release, this.messageAdmission, activity);
67975
+ const deliver = async (activity) => {
67976
+ const activeSession = sessionFile === undefined ? await this.ensureSession("prompt") : await this.ensureSessionFromFile(sessionFile, "prompt");
67977
+ const pausedDelivery2 = this.pauseControl.deferRunnerOwnedDelivery(() => sendStageUserMessage(activeSession, content, options, beforeDelivery, release, this.messageAdmission, activity));
67978
+ if (pausedDelivery2 !== undefined) {
67979
+ release();
67980
+ return pausedDelivery2;
67981
+ }
67982
+ return sendStageUserMessage(activeSession, content, options, beforeDelivery, release, this.messageAdmission, activity);
67983
+ };
68029
67984
  if (this.session === undefined || sessionFile !== undefined)
68030
67985
  return this.deliveryActivity.runWithLease(() => deliver());
68031
67986
  return deliver(this.deliveryActivity);
@@ -68047,25 +68002,61 @@ class StageSessionController {
68047
68002
  }
68048
68003
  async promptWithFallback(text, sdkOptions, consumer = "prompt") {
68049
68004
  if (!this.hasExplicitModelFallbackConfig) {
68050
- await this.promptWithPauseResume(await this.ensureSession(consumer), text, sdkOptions);
68005
+ try {
68006
+ const activeSession = await this.ensureSession(consumer);
68007
+ const resumedText2 = this.pendingCreationResumeMessage;
68008
+ this.pendingCreationResumeMessage = undefined;
68009
+ await this.promptWithThrownErrorRetry(activeSession, resumedText2 ?? text, sdkOptions);
68010
+ } catch (error) {
68011
+ if (error instanceof StageSessionCreationCancelled)
68012
+ return;
68013
+ throw error;
68014
+ }
68051
68015
  return;
68052
68016
  }
68053
68017
  const candidates = await this.modelCandidates();
68054
68018
  if (candidates.length === 0) {
68055
- await this.promptWithPauseResume(await this.ensureSession(consumer), text, sdkOptions);
68019
+ try {
68020
+ const activeSession = await this.ensureSession(consumer);
68021
+ const resumedText2 = this.pendingCreationResumeMessage;
68022
+ this.pendingCreationResumeMessage = undefined;
68023
+ await this.promptWithThrownErrorRetry(activeSession, resumedText2 ?? text, sdkOptions);
68024
+ } catch (error) {
68025
+ if (error instanceof StageSessionCreationCancelled)
68026
+ return;
68027
+ throw error;
68028
+ }
68056
68029
  return;
68057
68030
  }
68058
- if (await this.tryResumeCurrentSession(text, sdkOptions, candidates))
68031
+ if (this.session === undefined && this.sessionPromise !== undefined) {
68032
+ try {
68033
+ await this.sessionPromise;
68034
+ } catch (error) {
68035
+ if (error instanceof StageSessionCreationCancelled)
68036
+ return;
68037
+ }
68038
+ }
68039
+ const resumedText = this.pendingCreationResumeMessage;
68040
+ this.pendingCreationResumeMessage = undefined;
68041
+ let promptText = resumedText ?? text;
68042
+ if (await this.tryResumeCurrentSession(promptText, sdkOptions, candidates))
68059
68043
  return;
68060
68044
  let index = this.activeCandidateIndex ?? 0;
68061
68045
  while (index < candidates.length) {
68062
68046
  const candidate = candidates[index];
68063
- const activeSession = this.session && this.activeCandidateIndex === index ? this.session : await this.createSession(candidate, consumer);
68064
- this.activeCandidateIndex = index;
68065
- this.selectedModel = candidate.id;
68066
- this.notifyModelFallbackMetaChange();
68067
68047
  try {
68068
- const { terminalScanStartIndex } = await this.promptWithPauseResume(activeSession, text, sdkOptions);
68048
+ const created = this.session && this.activeCandidateIndex === index ? this.session : await this.createSessionWithThrownErrorRetry(candidate, consumer);
68049
+ if (isSessionCreationPauseResult(created)) {
68050
+ if (created.resumeMessage === undefined)
68051
+ return;
68052
+ promptText = created.resumeMessage;
68053
+ continue;
68054
+ }
68055
+ const activeSession = created;
68056
+ this.activeCandidateIndex = index;
68057
+ this.selectedModel = candidate.id;
68058
+ this.notifyModelFallbackMetaChange();
68059
+ const { terminalScanStartIndex } = await this.promptWithThrownErrorRetry(activeSession, promptText, sdkOptions);
68069
68060
  const terminalFailure = latestTerminalAssistantFailureSince(activeSession.messages, terminalScanStartIndex);
68070
68061
  if (terminalFailure !== undefined) {
68071
68062
  if (this.capturedStructuredOutputForAttempt()) {
@@ -68100,6 +68091,9 @@ class StageSessionController {
68100
68091
  }
68101
68092
  async disposeAll() {
68102
68093
  this.disposed = true;
68094
+ const reason = new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68095
+ this.markAbort(reason);
68096
+ this.pauseControl.reject(reason);
68103
68097
  for (const unsubscribe of this.listenerUnsubscribes.values())
68104
68098
  unsubscribe();
68105
68099
  this.listenerUnsubscribes.clear();
@@ -68112,8 +68106,25 @@ class StageSessionController {
68112
68106
  await this.replacement.dispose();
68113
68107
  await disposeStageSession(this.session);
68114
68108
  }
68109
+ async abort() {
68110
+ const reason = new DOMException("stage aborted", "AbortError");
68111
+ this.markAbort(reason);
68112
+ this.pauseControl.reject(reason);
68113
+ await this.session?.abort();
68114
+ }
68115
68115
  requestPause() {
68116
- return this.pauseControl.requestPause();
68116
+ const pause = this.pauseControl.requestPause();
68117
+ const resume = this.pauseControl.currentResume();
68118
+ if (resume !== undefined) {
68119
+ this.pauseThrownErrorRetries(resume);
68120
+ for (const observer of this.creationPauseObservers) {
68121
+ if (observer.pauseResume !== undefined)
68122
+ continue;
68123
+ observer.pauseResume = resume;
68124
+ this.latchCreationResume(resume);
68125
+ }
68126
+ }
68127
+ return pause;
68117
68128
  }
68118
68129
  resume(message, beforeResolve, beforeRelease) {
68119
68130
  return this.pauseControl.resume(message, beforeResolve, beforeRelease);
@@ -68134,21 +68145,224 @@ class StageSessionController {
68134
68145
  const { signal } = this.opts;
68135
68146
  if (!signal)
68136
68147
  return;
68137
- const abortReason = () => {
68148
+ const abortReason2 = () => {
68138
68149
  const reason = signal.reason;
68139
68150
  if (reason instanceof Error || reason instanceof DOMException || typeof reason === "string")
68140
68151
  return reason;
68141
68152
  return new DOMException("workflow killed", "AbortError");
68142
68153
  };
68143
68154
  const onAbort = () => {
68155
+ const reason = abortReason2();
68156
+ this.markAbort(reason);
68144
68157
  this.session?.abort().catch(() => {});
68145
- this.pauseControl.reject(abortReason());
68158
+ this.pauseControl.reject(reason);
68146
68159
  };
68147
68160
  if (signal.aborted)
68148
68161
  onAbort();
68149
68162
  else
68150
68163
  signal.addEventListener("abort", onAbort, { once: true });
68151
68164
  }
68165
+ markAbort(reason) {
68166
+ this.abortGeneration += 1;
68167
+ this.abortReason = reason;
68168
+ this.abortReasonGeneration = this.abortGeneration;
68169
+ this.abortThrownErrorRetries(reason);
68170
+ }
68171
+ pauseThrownErrorRetries(resume) {
68172
+ for (const state2 of this.thrownErrorRetryStates) {
68173
+ if (state2.pauseResume !== undefined)
68174
+ continue;
68175
+ state2.pauseResume = resume;
68176
+ state2.controller.abort(new ThrownErrorRetryPaused(resume));
68177
+ }
68178
+ }
68179
+ abortThrownErrorRetries(reason) {
68180
+ for (const state2 of this.thrownErrorRetryStates)
68181
+ state2.controller.abort(reason);
68182
+ this.thrownErrorRetryStates.clear();
68183
+ }
68184
+ retrySettings() {
68185
+ const managers = [
68186
+ this.sessionSettingsManager,
68187
+ this.session?.settingsManager,
68188
+ this.effectiveStageOptions?.settingsManager
68189
+ ];
68190
+ for (const manager of managers) {
68191
+ if (manager === undefined || typeof manager.getRetrySettings !== "function")
68192
+ continue;
68193
+ return manager.getRetrySettings();
68194
+ }
68195
+ return;
68196
+ }
68197
+ restoreSessionMessages(session, snapshot, promptText, keepPrompt) {
68198
+ const snapshotMessages = new Set(snapshot);
68199
+ const admitted = session.messages.filter((message) => !snapshotMessages.has(message));
68200
+ const failedAssistantIndex = admitted.findLastIndex((message) => message.role === "assistant" && message.stopReason === "error");
68201
+ const promptUser = admitted.slice(0, failedAssistantIndex < 0 ? admitted.length : failedAssistantIndex).findLast((message) => message.role === "user" && stageUserMessageText(message) === promptText);
68202
+ const retainedMessages = admitted.filter((message) => {
68203
+ if (message === promptUser)
68204
+ return keepPrompt;
68205
+ if (message.role === "assistant")
68206
+ return message.stopReason !== "error";
68207
+ return ["user", "toolResult", "custom", "bashExecution", "branchSummary"].includes(message.role);
68208
+ });
68209
+ session.messages.splice(0, session.messages.length, ...snapshot, ...retainedMessages);
68210
+ return keepPrompt ? promptUser : undefined;
68211
+ }
68212
+ dropRetainedPrompt(session, retained) {
68213
+ if (retained === undefined)
68214
+ return;
68215
+ const index = session.messages.indexOf(retained);
68216
+ if (index >= 0)
68217
+ session.messages.splice(index, 1);
68218
+ }
68219
+ async sleepForThrownErrorRetry(delayMs, state2) {
68220
+ this.thrownErrorRetryStates.add(state2);
68221
+ const currentResume = this.pauseControl.currentResume();
68222
+ if (currentResume !== undefined) {
68223
+ state2.pauseResume = currentResume;
68224
+ state2.controller.abort(new ThrownErrorRetryPaused(currentResume));
68225
+ }
68226
+ try {
68227
+ await sleepOrAbort(delayMs, state2.controller.signal);
68228
+ } finally {
68229
+ this.thrownErrorRetryStates.delete(state2);
68230
+ }
68231
+ }
68232
+ async continueWithPauseResume(continuationSession) {
68233
+ const settlePause = async (resume) => {
68234
+ const resumed = await resume;
68235
+ await resumed.runnerOwnedDeliverySettlement;
68236
+ return { kind: "paused", ...resumed.message === undefined ? {} : { message: resumed.message } };
68237
+ };
68238
+ const pauseBeforeContinue = this.pauseControl.currentResume();
68239
+ if (pauseBeforeContinue !== undefined)
68240
+ return settlePause(pauseBeforeContinue);
68241
+ const state2 = { controller: new AbortController };
68242
+ this.thrownErrorRetryStates.add(state2);
68243
+ try {
68244
+ await continuationSession._runAgentContinue();
68245
+ } catch (error) {
68246
+ const resume = state2.pauseResume ?? this.pauseControl.currentResume();
68247
+ if (resume === undefined)
68248
+ throw error;
68249
+ return settlePause(resume);
68250
+ } finally {
68251
+ this.thrownErrorRetryStates.delete(state2);
68252
+ }
68253
+ const pauseAfterContinue = state2.pauseResume ?? this.pauseControl.currentResume();
68254
+ if (pauseAfterContinue !== undefined)
68255
+ return settlePause(pauseAfterContinue);
68256
+ return { kind: "continued" };
68257
+ }
68258
+ async promptWithThrownErrorRetry(activeSession, text, sdkOptions) {
68259
+ let retryAttempt = 0;
68260
+ let nextText = text;
68261
+ let retryAdmittedPrompt = false;
68262
+ let retainedPrompt;
68263
+ let terminalScanStartIndex;
68264
+ while (true) {
68265
+ const messagesBeforeAttempt = [...activeSession.messages];
68266
+ try {
68267
+ if (retryAdmittedPrompt) {
68268
+ const continuationSession = retryableAgentSession(activeSession);
68269
+ if (continuationSession !== undefined) {
68270
+ const outcome = await this.continueWithPauseResume(continuationSession);
68271
+ if (outcome.kind === "continued") {
68272
+ return {
68273
+ terminalScanStartIndex: terminalScanStartIndex ?? this.lastPromptStartIndex ?? messagesBeforeAttempt.length
68274
+ };
68275
+ }
68276
+ this.dropRetainedPrompt(activeSession, retainedPrompt);
68277
+ retainedPrompt = undefined;
68278
+ retryAdmittedPrompt = false;
68279
+ retryAttempt = 0;
68280
+ terminalScanStartIndex = undefined;
68281
+ if (outcome.message === undefined) {
68282
+ return { terminalScanStartIndex: activeSession.messages.length };
68283
+ }
68284
+ nextText = outcome.message;
68285
+ continue;
68286
+ }
68287
+ this.dropRetainedPrompt(activeSession, retainedPrompt);
68288
+ retainedPrompt = undefined;
68289
+ retryAdmittedPrompt = false;
68290
+ }
68291
+ const result = await this.promptWithPauseResume(activeSession, nextText, sdkOptions);
68292
+ return {
68293
+ terminalScanStartIndex: terminalScanStartIndex ?? result.terminalScanStartIndex
68294
+ };
68295
+ } catch (error) {
68296
+ const errorSettingsManager = retrySettingsManagerFromError(error);
68297
+ if (errorSettingsManager !== undefined)
68298
+ this.sessionSettingsManager = errorSettingsManager;
68299
+ const retryableFailure = isRetryableModelFailure(error);
68300
+ const sameCandidateRetryable = isRetryableSameModelFailure(error) && !isUnresolvedContextOverflowFailure(error);
68301
+ const decision = nextRetryDecision(this.retrySettings(), retryAttempt, sameCandidateRetryable);
68302
+ const continuationSession = retryableAgentSession(activeSession);
68303
+ const admittedMessages = activeSession.messages.length > messagesBeforeAttempt.length;
68304
+ const willRetry = decision !== undefined && !this.disposed && this.opts.signal?.aborted !== true && !this.capturedStructuredOutputForAttempt();
68305
+ const willContinue = continuationSession !== undefined && admittedMessages;
68306
+ if (retryableFailure && willRetry) {
68307
+ retainedPrompt = this.restoreSessionMessages(activeSession, messagesBeforeAttempt, nextText, willContinue) ?? retainedPrompt;
68308
+ }
68309
+ if (!willRetry)
68310
+ throw error;
68311
+ terminalScanStartIndex ??= this.lastPromptStartIndex ?? messagesBeforeAttempt.length;
68312
+ retryAttempt = decision.attempt;
68313
+ const state2 = { controller: new AbortController };
68314
+ let pauseResume;
68315
+ try {
68316
+ await this.sleepForThrownErrorRetry(decision.delayMs, state2);
68317
+ } catch (sleepError) {
68318
+ if (sleepError instanceof ThrownErrorRetryPaused)
68319
+ pauseResume = sleepError.resume;
68320
+ else {
68321
+ if (this.opts.signal?.aborted)
68322
+ throw this.workflowAbortReason();
68323
+ if (this.disposed)
68324
+ throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68325
+ throw sleepError;
68326
+ }
68327
+ }
68328
+ if (pauseResume !== undefined) {
68329
+ const resumed = await pauseResume;
68330
+ retryAttempt = 0;
68331
+ retryAdmittedPrompt = false;
68332
+ terminalScanStartIndex = undefined;
68333
+ this.dropRetainedPrompt(activeSession, retainedPrompt);
68334
+ retainedPrompt = undefined;
68335
+ if (resumed.message === undefined) {
68336
+ return { terminalScanStartIndex: activeSession.messages.length };
68337
+ }
68338
+ nextText = resumed.message;
68339
+ continue;
68340
+ }
68341
+ if (this.disposed)
68342
+ throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68343
+ if (this.opts.signal?.aborted)
68344
+ throw this.workflowAbortReason();
68345
+ retryAdmittedPrompt = willContinue && canContinueFromTranscript(activeSession);
68346
+ if (!retryAdmittedPrompt) {
68347
+ this.dropRetainedPrompt(activeSession, retainedPrompt);
68348
+ retainedPrompt = undefined;
68349
+ }
68350
+ }
68351
+ }
68352
+ }
68353
+ workflowAbortReason() {
68354
+ const reason = this.opts.signal?.reason;
68355
+ if (reason instanceof Error || reason instanceof DOMException || typeof reason === "string")
68356
+ return reason;
68357
+ return new DOMException("workflow killed", "AbortError");
68358
+ }
68359
+ staleCreationReason(startGeneration) {
68360
+ if (this.opts.signal?.aborted)
68361
+ return this.workflowAbortReason();
68362
+ if (this.abortReasonGeneration > startGeneration && this.abortReason !== undefined)
68363
+ return this.abortReason;
68364
+ return new DOMException("stage aborted", "AbortError");
68365
+ }
68152
68366
  modelCandidates() {
68153
68367
  if (!this.candidatesPromise) {
68154
68368
  this.candidatesPromise = buildModelCandidatesFromCatalog({
@@ -68161,12 +68375,14 @@ class StageSessionController {
68161
68375
  return this.candidatesPromise;
68162
68376
  }
68163
68377
  async createInitialSession(consumer) {
68164
- if (!this.hasExplicitModelFallbackConfig)
68165
- return this.createSession(undefined, consumer);
68378
+ if (!this.hasExplicitModelFallbackConfig) {
68379
+ return this.createSessionObservingPause(undefined, consumer).catch((error) => this.createInitialSessionWithRetry(undefined, consumer, { error }));
68380
+ }
68166
68381
  const candidates = await this.modelCandidates();
68167
68382
  const first = candidates[0];
68168
- if (first === undefined)
68169
- return this.createSession(undefined, consumer);
68383
+ if (first === undefined) {
68384
+ return this.createSessionObservingPause(undefined, consumer).catch((error) => this.createInitialSessionWithRetry(undefined, consumer, { error }));
68385
+ }
68170
68386
  if (this.reattachSessionFile !== undefined) {
68171
68387
  const resumed = await this.createSession(undefined, consumer, { restoreSavedModel: true });
68172
68388
  const restoredId = workflowModelId(resumed.model);
@@ -68178,7 +68394,119 @@ class StageSessionController {
68178
68394
  }
68179
68395
  this.activeCandidateIndex = 0;
68180
68396
  this.selectedModel = first.id;
68181
- return this.createSession(first, consumer);
68397
+ return this.createSessionObservingPause(first, consumer).catch((error) => this.createInitialSessionCandidateWalk(candidates, consumer, 0, { error }));
68398
+ }
68399
+ async createInitialSessionCandidateWalk(candidates, consumer, startIndex, initialFailure) {
68400
+ let index = startIndex;
68401
+ let pendingFailure = initialFailure;
68402
+ let lastError = initialFailure?.error;
68403
+ while (index < candidates.length) {
68404
+ const candidate = candidates[index];
68405
+ this.activeCandidateIndex = index;
68406
+ this.selectedModel = candidate.id;
68407
+ try {
68408
+ const created = await this.createSessionWithThrownErrorRetry(candidate, consumer, pendingFailure);
68409
+ pendingFailure = undefined;
68410
+ if (!isSessionCreationPauseResult(created)) {
68411
+ this.notifyModelFallbackMetaChange();
68412
+ return created;
68413
+ }
68414
+ if (created.resumeMessage === undefined) {
68415
+ this.pendingCreationResumeMessage = undefined;
68416
+ this.sessionPromise = undefined;
68417
+ throw new StageSessionCreationCancelled;
68418
+ }
68419
+ this.pendingCreationResumeMessage = created.resumeMessage;
68420
+ } catch (error) {
68421
+ if (error instanceof StageSessionCreationCancelled)
68422
+ throw error;
68423
+ pendingFailure = undefined;
68424
+ lastError = error;
68425
+ if (await this.handleCandidateFailure(error, candidate, candidates, index) !== "retry")
68426
+ throw error;
68427
+ index += 1;
68428
+ }
68429
+ }
68430
+ throw lastError ?? new Error(`atomic-workflows: stage "${this.opts.stageName}" has no usable model candidate`);
68431
+ }
68432
+ async createInitialSessionWithRetry(candidate, consumer, initialFailure) {
68433
+ let pendingFailure = initialFailure;
68434
+ while (true) {
68435
+ const created = await this.createSessionWithThrownErrorRetry(candidate, consumer, pendingFailure);
68436
+ pendingFailure = undefined;
68437
+ if (!isSessionCreationPauseResult(created))
68438
+ return created;
68439
+ if (created.resumeMessage === undefined) {
68440
+ this.pendingCreationResumeMessage = undefined;
68441
+ this.sessionPromise = undefined;
68442
+ throw new StageSessionCreationCancelled;
68443
+ }
68444
+ this.pendingCreationResumeMessage = created.resumeMessage;
68445
+ }
68446
+ }
68447
+ latchCreationResume(resume) {
68448
+ resume.then(async (resolved) => {
68449
+ await resolved.runnerOwnedDeliverySettlement;
68450
+ if (resolved.message !== undefined)
68451
+ this.pendingCreationResumeMessage = resolved.message;
68452
+ }).catch(() => {});
68453
+ }
68454
+ createSessionObservingPause(candidate, consumer) {
68455
+ const activePause = this.pauseControl.currentResume();
68456
+ const observer = { pauseResume: activePause };
68457
+ if (activePause !== undefined)
68458
+ this.latchCreationResume(activePause);
68459
+ this.creationPauseObservers.add(observer);
68460
+ const creation = this.createSession(candidate, consumer);
68461
+ const settle = () => {
68462
+ this.creationPauseObservers.delete(observer);
68463
+ };
68464
+ creation.then(settle, settle);
68465
+ return creation;
68466
+ }
68467
+ async createSessionWithThrownErrorRetry(candidate, consumer, initialFailure) {
68468
+ let retryAttempt = 0;
68469
+ let pendingFailure = initialFailure;
68470
+ while (true) {
68471
+ try {
68472
+ if (pendingFailure !== undefined) {
68473
+ const failure3 = pendingFailure;
68474
+ pendingFailure = undefined;
68475
+ throw failure3.error;
68476
+ }
68477
+ return await this.createSessionObservingPause(candidate, consumer);
68478
+ } catch (error) {
68479
+ const errorSettingsManager = retrySettingsManagerFromError(error);
68480
+ if (errorSettingsManager !== undefined)
68481
+ this.sessionSettingsManager = errorSettingsManager;
68482
+ const decision = nextRetryDecision(this.retrySettings(), retryAttempt, isRetryableSameModelFailure(error));
68483
+ if (decision === undefined || this.disposed || this.opts.signal?.aborted === true || this.capturedStructuredOutputForAttempt()) {
68484
+ throw error;
68485
+ }
68486
+ retryAttempt = decision.attempt;
68487
+ const state2 = { controller: new AbortController };
68488
+ try {
68489
+ await this.sleepForThrownErrorRetry(decision.delayMs, state2);
68490
+ } catch (sleepError) {
68491
+ if (sleepError instanceof ThrownErrorRetryPaused) {
68492
+ const resumed = await sleepError.resume;
68493
+ return {
68494
+ kind: "paused",
68495
+ ...resumed.message === undefined ? {} : { resumeMessage: resumed.message }
68496
+ };
68497
+ }
68498
+ if (this.opts.signal?.aborted)
68499
+ throw this.workflowAbortReason();
68500
+ if (this.disposed)
68501
+ throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68502
+ throw sleepError;
68503
+ }
68504
+ if (this.disposed)
68505
+ throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68506
+ if (this.opts.signal?.aborted)
68507
+ throw this.workflowAbortReason();
68508
+ }
68509
+ }
68182
68510
  }
68183
68511
  createSession(candidate, consumer, resumeOptions) {
68184
68512
  const creation = this.createSessionAttempt(candidate, consumer, resumeOptions);
@@ -68190,6 +68518,7 @@ class StageSessionController {
68190
68518
  return creation;
68191
68519
  }
68192
68520
  async createSessionAttempt(candidate, consumer, resumeOptions) {
68521
+ const startGeneration = this.abortGeneration;
68193
68522
  this.applyCandidateThinking(candidate);
68194
68523
  const stageOptions = buildStageSessionOptions({
68195
68524
  effectiveStageOptions: this.effectiveStageOptions,
@@ -68198,11 +68527,24 @@ class StageSessionController {
68198
68527
  reattachSessionFile: this.reattachSessionFile,
68199
68528
  sharedModelRuntime: this.sharedModelRuntime
68200
68529
  });
68201
- const created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta), {
68202
- ...this.meta,
68203
- stageOptions,
68204
- ...this.sharedOrchestrationContext !== undefined ? { orchestrationContext: this.sharedOrchestrationContext } : {}
68205
- }) : missingAdapter(consumer);
68530
+ let created;
68531
+ try {
68532
+ created = this.opts.adapters.agentSession ? await this.opts.adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptions, this.opts.defaultSessionDir, this.meta), {
68533
+ ...this.meta,
68534
+ stageOptions,
68535
+ ...this.sharedOrchestrationContext !== undefined ? { orchestrationContext: this.sharedOrchestrationContext } : {}
68536
+ }) : missingAdapter(consumer);
68537
+ } catch (error) {
68538
+ if (this.disposed || this.opts.signal?.aborted === true || this.abortGeneration !== startGeneration)
68539
+ throw this.disposed ? new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`) : this.staleCreationReason(startGeneration);
68540
+ throw error;
68541
+ }
68542
+ if (this.disposed || this.opts.signal?.aborted === true || this.abortGeneration !== startGeneration) {
68543
+ await disposeStageSession(normalizeSessionCreateResult(created).session).catch(() => {});
68544
+ if (this.disposed)
68545
+ throw new Error(`atomic-workflows: stage "${this.opts.stageName}" session has been disposed`);
68546
+ throw this.staleCreationReason(startGeneration);
68547
+ }
68206
68548
  return attachCreatedStageSession(created, this.disposed, this.opts.stageName, (result) => this.attachSession(result));
68207
68549
  }
68208
68550
  attachSession(created) {
@@ -68242,11 +68584,13 @@ class StageSessionController {
68242
68584
  return result.session;
68243
68585
  }
68244
68586
  async disposeCurrentSession() {
68587
+ this.abortThrownErrorRetries(new Error(`atomic-workflows: stage "${this.opts.stageName}" session was replaced`));
68245
68588
  const current = this.session;
68246
68589
  this.messageAdmission.reset();
68247
68590
  this.replacement.retire(current);
68248
68591
  this.session = undefined;
68249
- this.sessionPromise = undefined;
68592
+ if (this.sessionPromise !== this.ownedCreationPromise)
68593
+ this.sessionPromise = undefined;
68250
68594
  this.sessionSettingsManager = undefined;
68251
68595
  this.resumeCurrentSession = false;
68252
68596
  for (const unsubscribe of this.listenerUnsubscribes.values())
@@ -68310,7 +68654,7 @@ class StageSessionController {
68310
68654
  const resumedLabel = this.selectedModel ?? workflowModelId(resumedSession.model) ?? candidates[0].id;
68311
68655
  this.notifyModelFallbackMetaChange();
68312
68656
  try {
68313
- const { terminalScanStartIndex } = await this.promptWithPauseResume(resumedSession, text, sdkOptions);
68657
+ const { terminalScanStartIndex } = await this.promptWithThrownErrorRetry(resumedSession, text, sdkOptions);
68314
68658
  const terminalFailure = latestTerminalAssistantFailureSince(resumedSession.messages, terminalScanStartIndex);
68315
68659
  if (terminalFailure === undefined || this.capturedStructuredOutputForAttempt()) {
68316
68660
  this.modelAttempts.push({ model: resumedLabel, success: true });
@@ -68765,7 +69109,7 @@ function createStageContext2(opts) {
68765
69109
  controller.currentSession?.abortCompaction();
68766
69110
  },
68767
69111
  async abort() {
68768
- await controller.currentSession?.abort();
69112
+ await controller.abort();
68769
69113
  },
68770
69114
  async __dispose() {
68771
69115
  await controller.disposeAll();
@@ -69314,6 +69658,7 @@ async function run(def, inputs, opts = {}) {
69314
69658
  const rootBackend = opts.durableRootBackend ?? backendView;
69315
69659
  const durableBackend = opts.durableScope !== undefined ? new ScopedDurableBackend(backendView, opts.durableScope) : backendView;
69316
69660
  const inheritedElapsedMs = opts.parentRun === undefined ? inheritedRunElapsedMs({ backend: durableBackend, runId, continuationSource: opts.continuation?.source }) : undefined;
69661
+ const continuationOrigin = opts.continuation !== undefined ? opts.continuation.source.origin : opts.origin;
69317
69662
  const runSnapshot = {
69318
69663
  id: runId,
69319
69664
  name: def.name,
@@ -69331,6 +69676,8 @@ async function run(def, inputs, opts = {}) {
69331
69676
  resumedFromRunId: opts.continuation.source.id,
69332
69677
  resumeFromStageId: opts.continuation.resumeFromStageId
69333
69678
  } : {},
69679
+ ...continuationOrigin !== undefined ? { origin: continuationOrigin } : {},
69680
+ ...opts.resumeActor !== undefined ? { resumeActor: opts.resumeActor, resumeSource: "run_control" } : {},
69334
69681
  ...inheritedElapsedMs !== undefined ? { accumulatedDurationMs: inheritedElapsedMs } : {}
69335
69682
  };
69336
69683
  const classifiedFailures = new Map;
@@ -69361,6 +69708,7 @@ async function run(def, inputs, opts = {}) {
69361
69708
  ...runSnapshot.parentStageId !== undefined ? { parentStageId: runSnapshot.parentStageId } : {},
69362
69709
  ...runSnapshot.rootRunId !== undefined ? { rootRunId: runSnapshot.rootRunId } : {},
69363
69710
  ...runSnapshot.resumedFromRunId !== undefined ? { resumedFromRunId: runSnapshot.resumedFromRunId } : {},
69711
+ ...runSnapshot.origin !== undefined ? { origin: runSnapshot.origin } : {},
69364
69712
  ...runSnapshot.resumeFromStageId !== undefined ? { resumeFromStageId: runSnapshot.resumeFromStageId } : {},
69365
69713
  ...runSnapshot.accumulatedDurationMs !== undefined ? { accumulatedDurationMs: runSnapshot.accumulatedDurationMs } : {},
69366
69714
  ts: runSnapshot.startedAt
@@ -69664,7 +70012,7 @@ async function run(def, inputs, opts = {}) {
69664
70012
  isChildRun: opts.parentRun !== undefined,
69665
70013
  registration: durableRootRegistration === undefined ? undefined : {
69666
70014
  ...durableRootRegistration,
69667
- ...workflowInvocationMetadata(inputRuntimeDefaults, workflowInvocationCwd, gitWorktreeSetupCache)
70015
+ ...workflowInvocationMetadata(inputRuntimeDefaults, workflowInvocationCwd, gitWorktreeSetupCache, runSnapshot.origin)
69668
70016
  }
69669
70017
  });
69670
70018
  if (opts.deferWorkflowStart === true)
@@ -69989,21 +70337,21 @@ function validateConfig2(config) {
69989
70337
  return "config must be an object";
69990
70338
  }
69991
70339
  const c = config;
69992
- for (const field3 of ["projectWorkflows", "globalWorkflows"]) {
69993
- const val = c[field3];
70340
+ for (const field2 of ["projectWorkflows", "globalWorkflows"]) {
70341
+ const val = c[field2];
69994
70342
  if (val !== undefined) {
69995
70343
  if (Array.isArray(val)) {
69996
70344
  for (const entry of val) {
69997
70345
  if (typeof entry !== "string")
69998
- return `config.${field3} entries must be strings`;
70346
+ return `config.${field2} entries must be strings`;
69999
70347
  }
70000
70348
  } else if (typeof val === "object" && val !== null) {
70001
70349
  for (const [key2, entry] of Object.entries(val)) {
70002
70350
  if (typeof entry !== "string")
70003
- return `config.${field3}["${key2}"] must be a string path`;
70351
+ return `config.${field2}["${key2}"] must be a string path`;
70004
70352
  }
70005
70353
  } else {
70006
- return `config.${field3} must be a string array or a Record<string, string> map`;
70354
+ return `config.${field2} must be a string array or a Record<string, string> map`;
70007
70355
  }
70008
70356
  }
70009
70357
  }
@@ -70261,6 +70609,7 @@ async function dispatch(args, opts) {
70261
70609
  executionMode: policy.mode,
70262
70610
  cwd: opts.cwd,
70263
70611
  defaultSessionDir: opts.defaultSessionDir,
70612
+ ...opts.origin === undefined ? {} : { origin: opts.origin },
70264
70613
  runId
70265
70614
  });
70266
70615
  } catch (error) {
@@ -70606,7 +70955,10 @@ function createDurableResumeRuntime(deps) {
70606
70955
  await backend.hydrateWorkflow(resolved.workflowId);
70607
70956
  const adapterDeps = {
70608
70957
  registry: deps.registry,
70609
- baseRunOpts: deps.baseRunOpts(options?.policy),
70958
+ baseRunOpts: {
70959
+ ...deps.baseRunOpts(options?.policy),
70960
+ ...options?.actor === undefined ? {} : { resumeActor: options.actor }
70961
+ },
70610
70962
  durableBackend: backend,
70611
70963
  resolveDefinition: async (name, cwd) => (await discoverWorkflows({ cwd: cwd ?? deps.runtimeCwd })).registry.get(name),
70612
70964
  ...deps.jobs !== undefined ? { jobs: deps.jobs } : {}
@@ -70791,6 +71143,7 @@ function createExtensionRuntime(opts = {}) {
70791
71143
  const launchContinuation = () => launchDetachedUntilStartup(def, sourceInputs, {
70792
71144
  ...runOptions(options?.policy),
70793
71145
  continuation: { source, resumeFromStageId: resolvedStage.stageId },
71146
+ ...options?.actor === undefined ? {} : { resumeActor: options.actor },
70794
71147
  ...jobs !== undefined ? { jobs } : {}
70795
71148
  });
70796
71149
  if (isActiveBlockedResumable) {
@@ -70907,6 +71260,7 @@ function createExtensionRuntime(opts = {}) {
70907
71260
  config,
70908
71261
  models,
70909
71262
  policy: options?.policy,
71263
+ ...options?.origin === undefined ? {} : { origin: options.origin },
70910
71264
  cwd: runtimeCwd,
70911
71265
  ...defaultSessionDir !== undefined ? { defaultSessionDir } : {}
70912
71266
  });
@@ -71567,6 +71921,12 @@ function renderRunSummary(payload) {
71567
71921
 
71568
71922
  // dist/builtin/workflows/src/extension/atomic-stage-session.ts
71569
71923
  import { basename as basename5 } from "node:path";
71924
+ var WORKFLOW_STAGE_SUBAGENT_POLICY = {
71925
+ managementActions: "full",
71926
+ fanoutAuthorized: false,
71927
+ inheritProjectContext: true,
71928
+ inheritSkills: true
71929
+ };
71570
71930
  function resolveSessionCwd(options) {
71571
71931
  return options?.cwd ?? options?.sessionManager?.getCwd() ?? process.cwd();
71572
71932
  }
@@ -71579,6 +71939,7 @@ async function prepareAtomicStageSessionOptions(options, sdk, prepareOptions = {
71579
71939
  const hasAgentDirOverride = atomicOptions?.agentDir !== undefined;
71580
71940
  const agentDir = atomicOptions?.agentDir ?? sdk.getAgentDir();
71581
71941
  const settingsManager = atomicOptions?.settingsManager ?? sdk.SettingsManager.create(cwd, agentDir, inheritanceSnapshot?.projectTrusted === undefined ? undefined : { projectTrusted: inheritanceSnapshot.projectTrusted });
71942
+ prepareOptions.onSettingsManager?.(settingsManager);
71582
71943
  const inheritedBuiltinPackagePaths = inheritanceSnapshot?.builtinPackagePaths;
71583
71944
  const builtinPackagePaths = inheritedBuiltinPackagePaths === undefined ? sdk.getBuiltinPackagePaths?.() ?? [] : [...inheritedBuiltinPackagePaths];
71584
71945
  const resourceLoader = new sdk.DefaultResourceLoader({
@@ -71594,7 +71955,8 @@ async function prepareAtomicStageSessionOptions(options, sdk, prepareOptions = {
71594
71955
  cwd,
71595
71956
  ...hasAgentDirOverride ? { agentDir } : {},
71596
71957
  settingsManager,
71597
- resourceLoader
71958
+ resourceLoader,
71959
+ subagentPolicy: WORKFLOW_STAGE_SUBAGENT_POLICY
71598
71960
  };
71599
71961
  }
71600
71962
  function clonePackageSource(source) {
@@ -71623,54 +71985,67 @@ function stageBuiltinPackagePaths(paths) {
71623
71985
  return basename5(packageSourcePath(cloned)) === "workflows" ? disablePackageExtensions(cloned) : cloned;
71624
71986
  });
71625
71987
  }
71626
- var SUBAGENT_CHILD_EXTENSION_ENV_KEYS = [
71627
- "ATOMIC_SUBAGENT_CHILD",
71628
- "ATOMIC_SUBAGENT_FANOUT_CHILD",
71629
- "PI_SUBAGENT_CHILD",
71630
- "PI_SUBAGENT_FANOUT_CHILD"
71631
- ];
71632
71988
  var workflowStageResourceReloadQueue = Promise.resolve();
71633
71989
  async function reloadWorkflowStageResources(resourceLoader) {
71634
- const queuedReload = workflowStageResourceReloadQueue.then(() => reloadWorkflowStageResourcesWithEnvIsolation(resourceLoader));
71990
+ const queuedReload = workflowStageResourceReloadQueue.then(() => resourceLoader.reload());
71635
71991
  workflowStageResourceReloadQueue = queuedReload.catch(() => {
71636
71992
  return;
71637
71993
  });
71638
71994
  return queuedReload;
71639
71995
  }
71640
- async function reloadWorkflowStageResourcesWithEnvIsolation(resourceLoader) {
71641
- const previousValues = new Map;
71642
- for (const key2 of SUBAGENT_CHILD_EXTENSION_ENV_KEYS) {
71643
- previousValues.set(key2, process.env[key2]);
71644
- delete process.env[key2];
71645
- }
71646
- try {
71647
- await resourceLoader.reload();
71648
- } finally {
71649
- for (const key2 of SUBAGENT_CHILD_EXTENSION_ENV_KEYS) {
71650
- const previousValue = previousValues.get(key2);
71651
- if (previousValue === undefined)
71652
- delete process.env[key2];
71653
- else
71654
- process.env[key2] = previousValue;
71655
- }
71656
- }
71657
- }
71658
71996
 
71659
71997
  // dist/builtin/workflows/src/extension/wiring.ts
71660
71998
  var LATE_STAGE_MESSAGE_EVENT2 = "atomic:workflow-stage-late-message";
71661
71999
  function isTestContext() {
71662
72000
  return process.env.NODE_TEST_CONTEXT !== undefined || false;
71663
72001
  }
72002
+ function attachSettingsManager(error, settingsManager) {
72003
+ if (error !== null && (typeof error === "object" || typeof error === "function")) {
72004
+ try {
72005
+ if (Object.isExtensible(error)) {
72006
+ Object.defineProperty(error, "settingsManager", {
72007
+ configurable: true,
72008
+ enumerable: false,
72009
+ value: settingsManager,
72010
+ writable: false
72011
+ });
72012
+ return error;
72013
+ }
72014
+ } catch {}
72015
+ }
72016
+ const wrapped = new Error(error instanceof Error ? error.message : String(error), { cause: error });
72017
+ Object.defineProperty(wrapped, "settingsManager", {
72018
+ configurable: true,
72019
+ enumerable: false,
72020
+ value: settingsManager,
72021
+ writable: false
72022
+ });
72023
+ return wrapped;
72024
+ }
71664
72025
  async function createPiSdkAgentSession(options, prepareOptions) {
71665
72026
  const sdk = await import("@bastani/atomic");
71666
- const sessionOptions = await prepareAtomicStageSessionOptions(options, sdk, prepareOptions);
71667
- const result = await sdk.createAgentSession(sessionOptions);
71668
- const resultSettingsManager = result.session.settingsManager;
71669
- const settingsManager = sessionOptions?.settingsManager ?? resultSettingsManager;
71670
- return {
71671
- session: result.session,
71672
- ...settingsManager?.getCodexFastModeSettings !== undefined ? { settingsManager } : {}
71673
- };
72027
+ let settingsManager;
72028
+ try {
72029
+ const sessionOptions = await prepareAtomicStageSessionOptions(options, sdk, {
72030
+ ...prepareOptions,
72031
+ onSettingsManager: (manager) => {
72032
+ settingsManager = manager;
72033
+ prepareOptions?.onSettingsManager?.(manager);
72034
+ }
72035
+ });
72036
+ settingsManager = sessionOptions?.settingsManager ?? settingsManager;
72037
+ const result = await sdk.createAgentSession(sessionOptions);
72038
+ const resultSettingsManager = result.session.settingsManager;
72039
+ settingsManager = sessionOptions?.settingsManager ?? resultSettingsManager ?? settingsManager;
72040
+ return {
72041
+ session: result.session,
72042
+ ...settingsManager?.getCodexFastModeSettings !== undefined ? { settingsManager } : {}
72043
+ };
72044
+ } catch (error) {
72045
+ if (settingsManager !== undefined)
72046
+ throw attachSettingsManager(error, settingsManager);
72047
+ throw error;
72048
+ }
71674
72049
  }
71675
72050
  async function createTestAgentSession(_options) {
71676
72051
  let lastAssistantText;
@@ -71965,23 +72340,23 @@ function renderPretty(name, inputs, theme, width) {
71965
72340
  width: boxWidth
71966
72341
  });
71967
72342
  }
71968
- function renderInputRows(field3, theme) {
71969
- const tagLabel = field3.required ? "required" : "optional";
71970
- const heading = theme ? ` ${paint(field3.name, theme.text, { bold: true })} ${paint(field3.type, theme.dim)} · ${paint(tagLabel, field3.required ? theme.warning : theme.dim)} ` : ` ${field3.name} ${field3.type} · ${tagLabel} `;
72343
+ function renderInputRows(field2, theme) {
72344
+ const tagLabel = field2.required ? "required" : "optional";
72345
+ const heading = theme ? ` ${paint(field2.name, theme.text, { bold: true })} ${paint(field2.type, theme.dim)} · ${paint(tagLabel, field2.required ? theme.warning : theme.dim)} ` : ` ${field2.name} ${field2.type} · ${tagLabel} `;
71971
72346
  const lines = [heading];
71972
- if (field3.description) {
71973
- lines.push(` ${theme ? paint(field3.description, theme.textMuted) : field3.description} `);
72347
+ if (field2.description) {
72348
+ lines.push(` ${theme ? paint(field2.description, theme.textMuted) : field2.description} `);
71974
72349
  }
71975
- if (field3.choices && field3.choices.length > 0) {
71976
- const values = field3.choices.join(" · ");
72350
+ if (field2.choices && field2.choices.length > 0) {
72351
+ const values = field2.choices.join(" · ");
71977
72352
  lines.push(` ${theme ? paint("values: ", theme.dim) + paint(values, theme.text) : `values: ${values}`} `);
71978
72353
  }
71979
- if (field3.default !== undefined) {
71980
- const value2 = JSON.stringify(field3.default);
72354
+ if (field2.default !== undefined) {
72355
+ const value2 = JSON.stringify(field2.default);
71981
72356
  lines.push(` ${theme ? paint("default: ", theme.dim) + paint(value2, theme.text) : `default: ${value2}`} `);
71982
72357
  }
71983
- if (field3.placeholder) {
71984
- lines.push(` ${theme ? paint("placeholder: ", theme.dim) + paint(field3.placeholder, theme.textMuted) : `placeholder: ${field3.placeholder}`} `);
72358
+ if (field2.placeholder) {
72359
+ lines.push(` ${theme ? paint("placeholder: ", theme.dim) + paint(field2.placeholder, theme.textMuted) : `placeholder: ${field2.placeholder}`} `);
71985
72360
  }
71986
72361
  return lines;
71987
72362
  }
@@ -72006,14 +72381,14 @@ function supportedType(type) {
72006
72381
  return "string";
72007
72382
  }
72008
72383
  }
72009
- function initialValue(field3, prefilled) {
72010
- if (prefilled[field3.name] !== undefined)
72011
- return String(prefilled[field3.name]);
72012
- if (field3.default !== undefined)
72013
- return String(field3.default);
72014
- if (field3.type === "select" && field3.choices && field3.choices.length > 0)
72015
- return field3.choices[0];
72016
- if (field3.type === "boolean")
72384
+ function initialValue(field2, prefilled) {
72385
+ if (prefilled[field2.name] !== undefined)
72386
+ return String(prefilled[field2.name]);
72387
+ if (field2.default !== undefined)
72388
+ return String(field2.default);
72389
+ if (field2.type === "select" && field2.choices && field2.choices.length > 0)
72390
+ return field2.choices[0];
72391
+ if (field2.type === "boolean")
72017
72392
  return "false";
72018
72393
  return "";
72019
72394
  }
@@ -72022,14 +72397,14 @@ async function openHostInputsForm(ui, options) {
72022
72397
  if (typeof open !== "function")
72023
72398
  return { kind: "unsupported" };
72024
72399
  const prefilled = options.prefilled ?? {};
72025
- const fields = options.fields.map((field3) => ({
72026
- name: field3.name,
72027
- type: supportedType(field3.type),
72028
- initialValue: initialValue(field3, prefilled),
72029
- ...field3.description !== undefined ? { description: field3.description } : {},
72030
- ...field3.required !== undefined ? { required: field3.required } : {},
72031
- ...field3.choices !== undefined ? { choices: [...field3.choices] } : {},
72032
- ...field3.placeholder !== undefined ? { placeholder: field3.placeholder } : {}
72400
+ const fields = options.fields.map((field2) => ({
72401
+ name: field2.name,
72402
+ type: supportedType(field2.type),
72403
+ initialValue: initialValue(field2, prefilled),
72404
+ ...field2.description !== undefined ? { description: field2.description } : {},
72405
+ ...field2.required !== undefined ? { required: field2.required } : {},
72406
+ ...field2.choices !== undefined ? { choices: [...field2.choices] } : {},
72407
+ ...field2.placeholder !== undefined ? { placeholder: field2.placeholder } : {}
72033
72408
  }));
72034
72409
  try {
72035
72410
  const raw = await open.call(ui, {
@@ -72140,6 +72515,9 @@ function openInputsPicker(ui, opts) {
72140
72515
  // dist/builtin/workflows/src/tui/session-picker.ts
72141
72516
  import { keyText as keyText2 } from "@bastani/atomic";
72142
72517
  var ESCAPE_CODE = 27;
72518
+ function isQuitRun3(run2) {
72519
+ return run2.endedAt === undefined && run2.status === "paused" && run2.exitReason === "quit";
72520
+ }
72143
72521
  var DOUBLE_ESCAPE_SEQUENCE = String.fromCharCode(ESCAPE_CODE, ESCAPE_CODE);
72144
72522
  function createSessionPickerState() {
72145
72523
  return { query: "", selectedIndex: 0, includeAll: true, filterFocused: false };
@@ -72280,12 +72658,13 @@ function stageProgress(run2) {
72280
72658
  const done = run2.stages.filter((s) => s.status === "completed" || s.status === "failed").length;
72281
72659
  return `${done}/${total} stages`;
72282
72660
  }
72283
- function renderRunRow(row, isSelected, inner, theme, now) {
72661
+ function renderRunRow(row, isSelected, inner, theme, now, allRuns) {
72284
72662
  const border = hexToAnsi(theme.border);
72285
72663
  const panelBg = hexBg(theme.bg);
72286
72664
  const run2 = row.run;
72287
- const icon = statusIcon(run2.status);
72288
- const iconColor = hexToAnsi(statusColor(run2.status, theme));
72665
+ const indicatorStatus = isQuitRun3(run2) ? run2.status : runIndicatorStatus(run2, allRuns);
72666
+ const icon = statusIcon(indicatorStatus);
72667
+ const iconColor = hexToAnsi(statusColor(indicatorStatus, theme));
72289
72668
  const dim = hexToAnsi(theme.dim);
72290
72669
  const text = hexToAnsi(theme.text);
72291
72670
  const muted = hexToAnsi(theme.textMuted);
@@ -72339,6 +72718,7 @@ function renderSessionPicker(opts) {
72339
72718
  const start = Math.max(0, Math.min(sel - Math.floor(VIEWPORT / 2), rows.length - VIEWPORT));
72340
72719
  const visible = rows.slice(Math.max(0, start), Math.max(0, start) + VIEWPORT);
72341
72720
  let prevBucket = null;
72721
+ const allRuns = opts.allRuns ?? rows.map(({ run: run2 }) => run2);
72342
72722
  for (let i = 0;i < visible.length; i++) {
72343
72723
  const row = visible[i];
72344
72724
  if (row.bucket !== prevBucket) {
@@ -72346,7 +72726,7 @@ function renderSessionPicker(opts) {
72346
72726
  prevBucket = row.bucket;
72347
72727
  }
72348
72728
  const absIndex = Math.max(0, start) + i;
72349
- lines.push(...renderRunRow(row, absIndex === sel, inner, theme, now));
72729
+ lines.push(...renderRunRow(row, absIndex === sel, inner, theme, now, allRuns));
72350
72730
  }
72351
72731
  lines.push(renderBlankRow(inner, theme));
72352
72732
  lines.push(renderBottomBorder(width, theme));
@@ -72686,7 +73066,7 @@ function renderSessionList(runs, opts) {
72686
73066
  toolNodes: graph.tools.map((tool) => structuredClone(tool))
72687
73067
  };
72688
73068
  });
72689
- return renderStatusList(filtered, { theme: opts.theme, now });
73069
+ return renderStatusList(filtered, { theme: opts.theme, now, allRuns: runs });
72690
73070
  }
72691
73071
 
72692
73072
  // dist/builtin/workflows/src/tui/session-overlays.ts
@@ -72707,6 +73087,7 @@ function openSessionPicker(ui, store2, theme, intent = "connect") {
72707
73087
  const state2 = createSessionPickerState();
72708
73088
  let settled = false;
72709
73089
  let unsubscribe = null;
73090
+ let pickerRevision = 0;
72710
73091
  const resumeCandidateCache = createSessionPickerResumeCandidateCache();
72711
73092
  const factory = (tui, _theme, _keys, done) => {
72712
73093
  const finish = (result) => {
@@ -72719,15 +73100,18 @@ function openSessionPicker(ui, store2, theme, intent = "connect") {
72719
73100
  resolve14(result);
72720
73101
  };
72721
73102
  const selectRows = () => {
72722
- const snapshot = readGraphStoreSnapshot(store2);
72723
- const resumeCandidateLookup = intent === "resume" ? resumeCandidateCache({ ...snapshot, runs: store2.runs() }) : undefined;
72724
- return selectRunsForPicker(snapshot.runs, state2.query, state2.includeAll, Date.now(), intent, resumeCandidateLookup);
73103
+ const liveRuns = store2.runs();
73104
+ const resumeCandidateLookup = intent === "resume" ? resumeCandidateCache({ runs: liveRuns, notices: store2.notices(), version: pickerRevision }) : undefined;
73105
+ return selectRunsForPicker(liveRuns, state2.query, state2.includeAll, Date.now(), intent, resumeCandidateLookup);
72725
73106
  };
72726
- unsubscribe = subscribeStoreInvalidation(store2, () => tui.requestRender?.());
73107
+ unsubscribe = subscribeStoreInvalidation(store2, () => {
73108
+ pickerRevision++;
73109
+ tui.requestRender?.();
73110
+ });
72727
73111
  return {
72728
73112
  render: (width) => {
72729
73113
  const rows = selectRows();
72730
- return renderSessionPicker({ width, theme, rows, state: state2 });
73114
+ return renderSessionPicker({ width, theme, rows, state: state2, allRuns: store2.runs() });
72731
73115
  },
72732
73116
  handleInput: (data) => {
72733
73117
  const rows = selectRows();
@@ -73056,7 +73440,7 @@ async function handleDurableResume(target, ctx, reporter, deps, preparedCatalog)
73056
73440
  fail(completedAttempt.message);
73057
73441
  return true;
73058
73442
  }
73059
- const result = await runtime.resumeDurableWorkflow(target, { policy });
73443
+ const result = await runtime.resumeDurableWorkflow(target, { policy, actor: "user" });
73060
73444
  fail(allOpenable.length === 0 ? result.message : `${result.message}
73061
73445
 
73062
73446
  ${formatResumableWorkflowList(allOpenable)}`);
@@ -73125,7 +73509,10 @@ function isExplicitResumeCandidate(run2) {
73125
73509
  return isWorkflowRunResumable(workflowRunResumeCandidate(run2)) || run2.endedAt === undefined && run2.status === "running";
73126
73510
  }
73127
73511
  async function resumeDurableTarget(workflowId, ctx, reporter, deps, runtime) {
73128
- const result = await runtime.resumeDurableWorkflow(workflowId, { policy: workflowPolicyFromContext(ctx) });
73512
+ const result = await runtime.resumeDurableWorkflow(workflowId, {
73513
+ policy: workflowPolicyFromContext(ctx),
73514
+ actor: "user"
73515
+ });
73129
73516
  if (!result.ok)
73130
73517
  reporter.error(result.message);
73131
73518
  else {
@@ -73300,7 +73687,7 @@ ${renderSessionList(store.runs(), { theme, includeAll: true })}`);
73300
73687
  return true;
73301
73688
  }
73302
73689
  }
73303
- const results = action === "quit" ? await quitAllRuns() : await interruptAllRuns();
73690
+ const results = action === "quit" ? await quitAllRuns({ actor: "user" }) : await interruptAllRuns();
73304
73691
  const successes = results.filter((result) => result.ok);
73305
73692
  const changed = successes.length;
73306
73693
  const failures = results.filter((result) => !result.ok);
@@ -73334,7 +73721,7 @@ ${renderSessionList(store.runs(), { theme, includeAll: true })}`);
73334
73721
  return true;
73335
73722
  }
73336
73723
  try {
73337
- const result = await quitRun(resolved.runId);
73724
+ const result = await quitRun(resolved.runId, { actor: "user" });
73338
73725
  if (result.ok)
73339
73726
  print(`Run ${result.runId} quit and can be resumed with /workflow resume.`);
73340
73727
  else if (result.reason === "already_ended")
@@ -73427,11 +73814,11 @@ Picker requires an interactive UI surface. Pass a runId: /workflow attach <id> [
73427
73814
  const isResumableContinuation2 = run3 !== undefined && !isPaused3 && run3.exitReason !== "quit" && isWorkflowRunResumable(workflowRunResumeCandidate(run3));
73428
73815
  if (isResumableContinuation2) {
73429
73816
  await ensureWorkflowResourcesVisible();
73430
- const continuation = await deps.runtimeForContext(ctx).resumeFailedRun(resolved.runId, undefined, { policy });
73817
+ const continuation = await deps.runtimeForContext(ctx).resumeFailedRun(resolved.runId, undefined, { policy, actor: "user" });
73431
73818
  continuation.ok ? print(continuation.message) : fail(continuation.message);
73432
73819
  } else {
73433
73820
  try {
73434
- const result2 = await resumeRun(resolved.runId, {});
73821
+ const result2 = await resumeRun(resolved.runId, { actor: "user" });
73435
73822
  if (result2.ok && !isPaused3 && result2.mode === "snapshot" && run3?.exitReason === "quit") {
73436
73823
  return await handleDurableResume(resolved.runId, ctx, reporter, deps);
73437
73824
  }
@@ -73541,7 +73928,7 @@ Picker requires an interactive UI surface. Pass a runId: /workflow attach <id> [
73541
73928
  const stageRunId = resolvedStage.runId ?? runId;
73542
73929
  if (action === "pause") {
73543
73930
  try {
73544
- const result2 = await pauseRun(stageRunId, { stageId });
73931
+ const result2 = await pauseRun(stageRunId, { stageId, actor: "user" });
73545
73932
  if (!result2.ok) {
73546
73933
  fail(result2.reason === "not_found" ? `Run not found: ${stageRunId}` : result2.reason === "already_ended" ? `Run ${stageRunId} already ended.` : result2.reason === "no_active_stages" ? `No pausable stages on run ${stageRunId}.` : `Stage not found: ${stageTarget ?? "(unknown)"}`);
73547
73934
  return true;
@@ -73566,7 +73953,7 @@ Picker requires an interactive UI surface. Pass a runId: /workflow attach <id> [
73566
73953
  }
73567
73954
  if (isResumableContinuation) {
73568
73955
  await ensureWorkflowResourcesVisible();
73569
- const continuation = await deps.runtimeForContext(ctx).resumeFailedRun(stageRunId, stageId, { policy });
73956
+ const continuation = await deps.runtimeForContext(ctx).resumeFailedRun(stageRunId, stageId, { policy, actor: "user" });
73570
73957
  continuation.ok ? print(continuation.message) : fail(continuation.message);
73571
73958
  return true;
73572
73959
  }
@@ -73575,7 +73962,7 @@ Picker requires an interactive UI surface. Pass a runId: /workflow attach <id> [
73575
73962
  }
73576
73963
  let result;
73577
73964
  try {
73578
- result = await resumeRun(stageRunId, { stageId, message });
73965
+ result = await resumeRun(stageRunId, { stageId, message, actor: "user" });
73579
73966
  } catch (error) {
73580
73967
  fail(`Failed to resume run ${stageRunId}: ${error instanceof Error ? error.message : String(error)}`);
73581
73968
  return true;
@@ -73699,8 +74086,14 @@ Available: ${formatAvailableWorkflowNames(deps.runtimeProxy.registry.names())}`)
73699
74086
  emitChatSurface(pi, { kind: "detail", detail: inspected.detail });
73700
74087
  return;
73701
74088
  }
73702
- const rows = selectRunsForPicker(store.runs(), "", true, Date.now());
73703
- emitChatSurface(pi, { kind: "status", runs: rows.map((r) => r.run) });
74089
+ const capturedRuns = store.graphSnapshot().runs;
74090
+ const rows = selectRunsForPicker(capturedRuns, "", true, Date.now());
74091
+ const visibleRuns = rows.map((r) => r.run);
74092
+ emitChatSurface(pi, {
74093
+ kind: "status",
74094
+ runs: visibleRuns,
74095
+ indicatorStatuses: resolveRunIndicatorStatuses(visibleRuns, capturedRuns)
74096
+ });
73704
74097
  return;
73705
74098
  }
73706
74099
  if (subcommand === "reload") {
@@ -73775,7 +74168,7 @@ Available: ${formatAvailableWorkflowNames(deps.runtimeProxy.registry.names())}`)
73775
74168
  }
73776
74169
  }
73777
74170
  await ensureWorkflowResourcesVisible();
73778
- const result = await deps.runWithLifecycleSuppressedForPolicy(policy, () => deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: mergedInputs, action: "run" }, { policy }));
74171
+ const result = await deps.runWithLifecycleSuppressedForPolicy(policy, () => deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: mergedInputs, action: "run" }, { policy, origin: "user" }));
73779
74172
  if (result.action !== "run" || !("runId" in result))
73780
74173
  return;
73781
74174
  const runResult = result;
@@ -73806,6 +74199,13 @@ Available: ${formatAvailableWorkflowNames(deps.runtimeProxy.registry.names())}`)
73806
74199
  import { getSupportedThinkingLevels } from "@earendil-works/pi-ai/compat";
73807
74200
 
73808
74201
  // dist/builtin/workflows/src/extension/workflow-status-summary.ts
74202
+ var workflowStatusRenderRuns = new WeakMap;
74203
+ function setWorkflowStatusRenderRuns(result, runs) {
74204
+ workflowStatusRenderRuns.set(result, runs);
74205
+ }
74206
+ function getWorkflowStatusRenderRuns(result) {
74207
+ return workflowStatusRenderRuns.get(result);
74208
+ }
73809
74209
  function stageIsActive(stage) {
73810
74210
  return stage.status === "running" || stage.status === "awaiting_input";
73811
74211
  }
@@ -73949,7 +74349,17 @@ function statusAwaitingInputLine(entry) {
73949
74349
  const message = entry.message !== undefined ? ` — "${truncateStatusText(entry.message)}"` : "";
73950
74350
  return ` awaiting input: ${target}${prompt}${message}`;
73951
74351
  }
74352
+ function statusRunIcon(run2, snapshot, allRuns) {
74353
+ if (snapshot === undefined)
74354
+ return statusIcon(run2.status);
74355
+ const indicatorStatus = runIndicatorStatus(snapshot, allRuns);
74356
+ if (snapshot.endedAt === undefined && snapshot.status === "paused" && snapshot.exitReason === "quit") {
74357
+ return statusIcon("pending");
74358
+ }
74359
+ return statusIcon(indicatorStatus);
74360
+ }
73952
74361
  function renderStatusToolContent(result) {
74362
+ const allRuns = getWorkflowStatusRenderRuns(result) ?? result.snapshots;
73953
74363
  const lines = ["action: status", `filter: ${result.filter}`];
73954
74364
  if (result.runs.length === 0) {
73955
74365
  lines.push(result.filter === "all" ? "runs: none" : `runs: none (statusFilter: ${result.filter})`);
@@ -73962,6 +74372,7 @@ function renderStatusToolContent(result) {
73962
74372
  const hint = statusRunHint(run2);
73963
74373
  const summaryLine = [
73964
74374
  `[${index + 1}]`,
74375
+ statusRunIcon(run2, result.snapshots[index], allRuns),
73965
74376
  run2.runId,
73966
74377
  run2.name,
73967
74378
  run2.status,
@@ -74196,7 +74607,7 @@ async function workflowPauseAction(args) {
74196
74607
  return { action, runId: "--all", status: "noop", message: allStageConflictMessage("pause") };
74197
74608
  }
74198
74609
  try {
74199
- const results = await pauseAllRuns();
74610
+ const results = await pauseAllRuns({ actor: "agent" });
74200
74611
  const paused = results.filter((result) => result.ok).length;
74201
74612
  return {
74202
74613
  action,
@@ -74226,7 +74637,7 @@ async function workflowPauseAction(args) {
74226
74637
  return { action, runId: target.runId, status: "noop", message: stage.message };
74227
74638
  const stageRunId = stage.runId ?? target.runId;
74228
74639
  try {
74229
- const result = await pauseRun(stageRunId, { stageId: stage.stageId });
74640
+ const result = await pauseRun(stageRunId, { stageId: stage.stageId, actor: "agent" });
74230
74641
  return result.ok ? {
74231
74642
  action,
74232
74643
  runId: result.runId,
@@ -74315,7 +74726,7 @@ async function workflowQuitAction(args) {
74315
74726
  if (args.stageId !== undefined && args.stageId.length > 0) {
74316
74727
  return { action, runId: "--all", status: "noop", message: allStageConflictMessage("quit") };
74317
74728
  }
74318
- const results = await quitAllRuns();
74729
+ const results = await quitAllRuns({ actor: "agent" });
74319
74730
  const successes = results.filter((result) => result.ok);
74320
74731
  const quitCount = successes.length;
74321
74732
  const failures = results.filter((result) => !result.ok);
@@ -74335,7 +74746,7 @@ async function workflowQuitAction(args) {
74335
74746
  if (controlNode.kind === "tool")
74336
74747
  return quitToolNodeAction(controlNode.runId, controlNode.nodeId, action);
74337
74748
  try {
74338
- const result = await quitRun(target.runId);
74749
+ const result = await quitRun(target.runId, { actor: "agent" });
74339
74750
  if (result.ok) {
74340
74751
  return {
74341
74752
  action,
@@ -74417,7 +74828,7 @@ async function resumeDurableShadow(runId, deps) {
74417
74828
  } catch (error) {
74418
74829
  warning = formatWorkflowResourceLoadWarning(error);
74419
74830
  }
74420
- const resumed = await runtime.resumeDurableWorkflow(runId, { policy: deps.policy });
74831
+ const resumed = await runtime.resumeDurableWorkflow(runId, { policy: deps.policy, actor: "agent" });
74421
74832
  const message = warning === undefined ? resumed.message : `${warning}
74422
74833
 
74423
74834
  ${resumed.message}`;
@@ -74430,7 +74841,7 @@ ${resumed.message}`;
74430
74841
  }
74431
74842
  async function resumePreparedDurableTarget(runId, deps) {
74432
74843
  try {
74433
- const resumed = await deps.getRuntime().resumeDurableWorkflow(runId, { policy: deps.policy });
74844
+ const resumed = await deps.getRuntime().resumeDurableWorkflow(runId, { policy: deps.policy, actor: "agent" });
74434
74845
  return {
74435
74846
  action: "resume",
74436
74847
  runId: resumed.ok ? resumed.runId : runId,
@@ -74538,7 +74949,7 @@ async function workflowResumeAction(args, deps) {
74538
74949
  } catch (error) {
74539
74950
  warning = formatWorkflowResourceLoadWarning(error);
74540
74951
  }
74541
- const continuation = await deps.getRuntime().resumeFailedRun(stageRunId, stage.stageId, { policy: deps.policy });
74952
+ const continuation = await deps.getRuntime().resumeFailedRun(stageRunId, stage.stageId, { policy: deps.policy, actor: "agent" });
74542
74953
  const message = warning === undefined ? continuation.message : `${warning}
74543
74954
 
74544
74955
  ${continuation.message}`;
@@ -74550,7 +74961,7 @@ ${continuation.message}`;
74550
74961
  };
74551
74962
  }
74552
74963
  try {
74553
- const result = await resumeRun(stageRunId, { stageId: stage.stageId, message: args.message });
74964
+ const result = await resumeRun(stageRunId, { stageId: stage.stageId, message: args.message, actor: "agent" });
74554
74965
  if (result.ok) {
74555
74966
  const runLevelResumed = hadPausedRunState && !hadPausedStageState && stage.stageId === undefined && result.snapshot.status === "running";
74556
74967
  const noPausedProgress = isPaused2 && result.resumed.length === 0 && result.message === undefined && !runLevelResumed;
@@ -75131,7 +75542,7 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
75131
75542
  }
75132
75543
  case "run": {
75133
75544
  await ensureWorkflowResourcesVisible();
75134
- return getRuntime().dispatch(args, { policy });
75545
+ return getRuntime().dispatch(args, { policy, origin: "agent" });
75135
75546
  }
75136
75547
  case "status": {
75137
75548
  const target = args.runId;
@@ -75143,16 +75554,18 @@ function makeExecuteWorkflowTool(runtime, reloadWorkflowResources, ensureWorkflo
75143
75554
  if (resolved.kind === "not_found") {
75144
75555
  return { action: "statusDetail", runId: target, error: `run not found: ${target}` };
75145
75556
  }
75146
- const result = inspectRun(resolved.runId);
75147
- return result.ok ? { action: "statusDetail", runId: result.runId, detail: result.detail } : { action: "statusDetail", runId: target, error: `run not found: ${target}` };
75557
+ const result2 = inspectRun(resolved.runId);
75558
+ return result2.ok ? { action: "statusDetail", runId: result2.runId, detail: result2.detail } : { action: "statusDetail", runId: target, error: `run not found: ${target}` };
75148
75559
  }
75149
75560
  const listing = buildWorkflowStatusListing(topLevelExpandedSnapshots(), args.statusFilter ?? "all");
75150
- return {
75561
+ const result = {
75151
75562
  action: "status",
75152
75563
  filter: listing.filter,
75153
75564
  runs: listing.runs,
75154
75565
  snapshots: listing.snapshots
75155
75566
  };
75567
+ setWorkflowStatusRenderRuns(result, store.graphSnapshot().runs);
75568
+ return result;
75156
75569
  }
75157
75570
  case "stages":
75158
75571
  return workflowStagesResult(args);
@@ -75319,7 +75732,8 @@ function renderResult(result, opts) {
75319
75732
  return renderStatusList(r.snapshots, {
75320
75733
  theme: themed ? deriveGraphTheme({}) : undefined,
75321
75734
  width: opts?.width,
75322
- now: opts?.now
75735
+ now: opts?.now,
75736
+ allRuns: opts?.allRuns ?? getWorkflowStatusRenderRuns(r) ?? r.snapshots
75323
75737
  });
75324
75738
  }
75325
75739
  case "statusDetail": {