@bastani/atomic 0.9.5-alpha.7 → 0.9.5-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/CHANGELOG.md +17 -0
  4. package/dist/builtin/intercom/README.md +27 -21
  5. package/dist/builtin/intercom/broker/broker.ts +3 -6
  6. package/dist/builtin/intercom/broker/paths.ts +42 -5
  7. package/dist/builtin/intercom/broker/spawn.ts +78 -10
  8. package/dist/builtin/intercom/config.ts +6 -3
  9. package/dist/builtin/intercom/contact-supervisor-tool.ts +6 -4
  10. package/dist/builtin/intercom/index-heavy.ts +1 -1
  11. package/dist/builtin/intercom/index.ts +162 -130
  12. package/dist/builtin/intercom/intercom-tool.ts +9 -3
  13. package/dist/builtin/intercom/package.json +10 -1
  14. package/dist/builtin/intercom/result-renderers.ts +1 -1
  15. package/dist/builtin/intercom/ui/compose.ts +2 -2
  16. package/dist/builtin/intercom/ui/inline-message.ts +2 -2
  17. package/dist/builtin/intercom/ui/session-list.ts +2 -2
  18. package/dist/builtin/mcp/CHANGELOG.md +11 -0
  19. package/dist/builtin/mcp/README.md +6 -6
  20. package/dist/builtin/mcp/direct-tools.ts +41 -23
  21. package/dist/builtin/mcp/index.ts +31 -4
  22. package/dist/builtin/mcp/init.ts +4 -43
  23. package/dist/builtin/mcp/metadata-hydration.ts +37 -0
  24. package/dist/builtin/mcp/package.json +1 -1
  25. package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
  26. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  27. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  28. package/dist/builtin/subagents/CHANGELOG.md +18 -0
  29. package/dist/builtin/subagents/package.json +1 -1
  30. package/dist/builtin/subagents/src/extension/index.ts +14 -25
  31. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  32. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
  33. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
  34. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
  35. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
  36. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -1
  37. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +15 -48
  38. package/dist/builtin/subagents/src/tui/render-layout.ts +9 -0
  39. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +27 -25
  40. package/dist/builtin/subagents/src/tui/render-widget.ts +72 -66
  41. package/dist/builtin/subagents/src/tui/render.ts +1 -1
  42. package/dist/builtin/web-access/package.json +1 -1
  43. package/dist/builtin/workflows/CHANGELOG.md +29 -0
  44. package/dist/builtin/workflows/README.md +4 -2
  45. package/dist/builtin/workflows/builtin/goal-artifacts.ts +3 -1
  46. package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -2
  47. package/dist/builtin/workflows/builtin/goal-reducer.ts +21 -0
  48. package/dist/builtin/workflows/builtin/goal-review.ts +60 -6
  49. package/dist/builtin/workflows/builtin/goal-runner.ts +71 -22
  50. package/dist/builtin/workflows/builtin/goal-types.ts +7 -1
  51. package/dist/builtin/workflows/builtin/ralph-core.ts +52 -4
  52. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +10 -3
  53. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +23 -2
  54. package/dist/builtin/workflows/builtin/ralph-runner.ts +38 -16
  55. package/dist/builtin/workflows/builtin/review-convergence.ts +111 -0
  56. package/dist/builtin/workflows/package.json +1 -1
  57. package/dist/builtin/workflows/skills/create-spec/SKILL.md +2 -0
  58. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  59. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  60. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  61. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  62. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  63. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  64. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  65. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  66. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  67. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  68. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  69. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  70. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  71. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  72. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  73. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  74. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  75. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  76. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  77. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  78. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  79. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  80. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  81. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  82. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  83. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  84. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  85. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  86. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  87. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  88. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  89. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  90. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  91. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  92. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  93. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  94. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  95. package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
  96. package/dist/builtin/workflows/src/durable/dbos-backend.ts +63 -2
  97. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
  98. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  99. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  100. package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
  101. package/dist/builtin/workflows/src/durable/types.ts +30 -1
  102. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  103. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  104. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  105. package/dist/builtin/workflows/src/engine/run.ts +32 -32
  106. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  107. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  108. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  109. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  110. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  111. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  112. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  113. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  114. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  115. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
  116. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  117. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  118. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  119. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  120. package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
  121. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  122. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
  123. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -3
  124. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +18 -19
  125. package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +38 -0
  126. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  127. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  128. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  129. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +18 -1
  130. package/dist/builtin/workflows/src/shared/persistence-restore.ts +23 -5
  131. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  132. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  133. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  134. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  135. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  136. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  137. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  138. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  139. package/dist/bun/internal-intercom-broker.d.ts +6 -0
  140. package/dist/bun/internal-intercom-broker.d.ts.map +1 -0
  141. package/dist/bun/internal-intercom-broker.js +39 -0
  142. package/dist/bun/internal-intercom-broker.js.map +1 -0
  143. package/dist/bun/split-loader.d.ts.map +1 -1
  144. package/dist/bun/split-loader.js +21 -8
  145. package/dist/bun/split-loader.js.map +1 -1
  146. package/dist/core/agent-session-auto-compaction.d.ts +21 -2
  147. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  148. package/dist/core/agent-session-auto-compaction.js +163 -11
  149. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  150. package/dist/core/agent-session-events.d.ts.map +1 -1
  151. package/dist/core/agent-session-events.js +10 -1
  152. package/dist/core/agent-session-events.js.map +1 -1
  153. package/dist/core/agent-session-methods.d.ts +12 -3
  154. package/dist/core/agent-session-methods.d.ts.map +1 -1
  155. package/dist/core/agent-session-methods.js.map +1 -1
  156. package/dist/core/agent-session-models.d.ts +2 -2
  157. package/dist/core/agent-session-models.d.ts.map +1 -1
  158. package/dist/core/agent-session-models.js.map +1 -1
  159. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  160. package/dist/core/agent-session-prompt.js +1 -0
  161. package/dist/core/agent-session-prompt.js.map +1 -1
  162. package/dist/core/agent-session-retry.d.ts +2 -0
  163. package/dist/core/agent-session-retry.d.ts.map +1 -1
  164. package/dist/core/agent-session-retry.js +152 -4
  165. package/dist/core/agent-session-retry.js.map +1 -1
  166. package/dist/core/agent-session-services.d.ts +1 -0
  167. package/dist/core/agent-session-services.d.ts.map +1 -1
  168. package/dist/core/agent-session-services.js +1 -0
  169. package/dist/core/agent-session-services.js.map +1 -1
  170. package/dist/core/agent-session-types.d.ts +15 -1
  171. package/dist/core/agent-session-types.d.ts.map +1 -1
  172. package/dist/core/agent-session-types.js.map +1 -1
  173. package/dist/core/agent-session.d.ts +6 -0
  174. package/dist/core/agent-session.d.ts.map +1 -1
  175. package/dist/core/agent-session.js +6 -0
  176. package/dist/core/agent-session.js.map +1 -1
  177. package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
  178. package/dist/core/compaction/context-compaction-runner.js +36 -20
  179. package/dist/core/compaction/context-compaction-runner.js.map +1 -1
  180. package/dist/core/copilot-anthropic-sse-repair.d.ts +23 -0
  181. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -0
  182. package/dist/core/copilot-anthropic-sse-repair.js +340 -0
  183. package/dist/core/copilot-anthropic-sse-repair.js.map +1 -0
  184. package/dist/core/copilot-gemini-reasoning.d.ts +16 -8
  185. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  186. package/dist/core/copilot-gemini-reasoning.js +27 -22
  187. package/dist/core/copilot-gemini-reasoning.js.map +1 -1
  188. package/dist/core/copilot-hosts.d.ts +12 -0
  189. package/dist/core/copilot-hosts.d.ts.map +1 -0
  190. package/dist/core/copilot-hosts.js +33 -0
  191. package/dist/core/copilot-hosts.js.map +1 -0
  192. package/dist/core/extensions/agent-events.d.ts +1 -1
  193. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  194. package/dist/core/extensions/agent-events.js.map +1 -1
  195. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  196. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  197. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  198. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  199. package/dist/core/footer-data-provider.d.ts +6 -0
  200. package/dist/core/footer-data-provider.d.ts.map +1 -1
  201. package/dist/core/footer-data-provider.js +72 -29
  202. package/dist/core/footer-data-provider.js.map +1 -1
  203. package/dist/core/http-dispatcher.d.ts.map +1 -1
  204. package/dist/core/http-dispatcher.js +5 -5
  205. package/dist/core/http-dispatcher.js.map +1 -1
  206. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  207. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  208. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  209. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  210. package/dist/core/sdk-types.d.ts +2 -0
  211. package/dist/core/sdk-types.d.ts.map +1 -1
  212. package/dist/core/sdk-types.js.map +1 -1
  213. package/dist/core/sdk.d.ts.map +1 -1
  214. package/dist/core/sdk.js +1 -0
  215. package/dist/core/sdk.js.map +1 -1
  216. package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
  217. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  218. package/dist/core/settings-manager-basic-accessors.js +6 -0
  219. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  220. package/dist/core/settings-types.d.ts +1 -0
  221. package/dist/core/settings-types.d.ts.map +1 -1
  222. package/dist/core/settings-types.js.map +1 -1
  223. package/dist/core/slash-commands.d.ts +2 -0
  224. package/dist/core/slash-commands.d.ts.map +1 -1
  225. package/dist/core/slash-commands.js +141 -0
  226. package/dist/core/slash-commands.js.map +1 -1
  227. package/dist/index-extensions.d.ts +1 -1
  228. package/dist/index-extensions.d.ts.map +1 -1
  229. package/dist/index-extensions.js.map +1 -1
  230. package/dist/index.d.ts +1 -0
  231. package/dist/index.d.ts.map +1 -1
  232. package/dist/index.js +1 -0
  233. package/dist/index.js.map +1 -1
  234. package/dist/main-deferred-startup.d.ts +12 -0
  235. package/dist/main-deferred-startup.d.ts.map +1 -1
  236. package/dist/main-deferred-startup.js +23 -2
  237. package/dist/main-deferred-startup.js.map +1 -1
  238. package/dist/main-early-input.d.ts +41 -0
  239. package/dist/main-early-input.d.ts.map +1 -0
  240. package/dist/main-early-input.js +154 -0
  241. package/dist/main-early-input.js.map +1 -0
  242. package/dist/main.d.ts +0 -6
  243. package/dist/main.d.ts.map +1 -1
  244. package/dist/main.js +20 -14
  245. package/dist/main.js.map +1 -1
  246. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  247. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  248. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  249. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  250. package/dist/modes/interactive/components/chat-session-host-events.js +15 -0
  251. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  252. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  253. package/dist/modes/interactive/interactive-agent-events.js +25 -0
  254. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  255. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  256. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  257. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  258. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  259. package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
  260. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  261. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  262. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  263. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  264. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  265. package/dist/modes/interactive/interactive-input-handling.js +318 -185
  266. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  267. package/dist/modes/interactive/interactive-mode-base.d.ts +12 -0
  268. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  269. package/dist/modes/interactive/interactive-mode-base.js +39 -0
  270. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  271. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  272. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  273. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  274. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  275. package/dist/modes/interactive/interactive-mode-surface.d.ts +6 -2
  276. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  277. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  278. package/dist/modes/interactive/interactive-mode-types.d.ts +3 -0
  279. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  280. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  281. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  282. package/dist/modes/interactive/interactive-model-routing.js +3 -1
  283. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  284. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  285. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  286. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  287. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  288. package/dist/modes/interactive/interactive-render-chat.js +36 -10
  289. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  290. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  291. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  292. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  293. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  294. package/dist/modes/interactive/interactive-startup.js +26 -22
  295. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  296. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  297. package/dist/modes/interactive/theme/global-theme.js +26 -2
  298. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  299. package/dist/utils/fs-watch.d.ts +24 -1
  300. package/dist/utils/fs-watch.d.ts.map +1 -1
  301. package/dist/utils/fs-watch.js +60 -5
  302. package/dist/utils/fs-watch.js.map +1 -1
  303. package/docs/compaction.md +8 -0
  304. package/docs/development.md +4 -0
  305. package/docs/extensions.md +5 -0
  306. package/docs/json.md +3 -1
  307. package/docs/providers.md +1 -0
  308. package/docs/quickstart.md +1 -1
  309. package/docs/rpc.md +3 -1
  310. package/docs/settings.md +27 -1
  311. package/docs/subagents.md +4 -0
  312. package/docs/terminal-setup.md +4 -0
  313. package/docs/usage.md +2 -2
  314. package/docs/windows.md +4 -0
  315. package/docs/workflows.md +21 -9
  316. package/npm-shrinkwrap.json +513 -23
  317. package/package.json +3 -2
@@ -359,6 +359,10 @@ export class DbosDurableBackend implements DurableWorkflowBackend {
359
359
  ...(entry.label !== undefined ? { label: entry.label } : {}),
360
360
  ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
361
361
  ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
362
+ ...(entry.invocationCwd !== undefined ? { invocationCwd: entry.invocationCwd } : {}),
363
+ ...(entry.workflowCwd !== undefined ? { workflowCwd: entry.workflowCwd } : {}),
364
+ ...(entry.repositoryRoot !== undefined ? { repositoryRoot: entry.repositoryRoot } : {}),
365
+ ...(entry.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: entry.gitWorktreeRoot } : {}),
362
366
  });
363
367
  }
364
368
  }
@@ -377,7 +381,7 @@ function isMetadataStep(stepName: string): boolean {
377
381
  interface DbosMetadataEnvelope {
378
382
  readonly __atomicDurableMetadata: true;
379
383
  readonly version: 1;
380
- readonly entry: DurableCheckpointEntry;
384
+ readonly entry: WorkflowSerializableValue;
381
385
  }
382
386
 
383
387
  function encodeMetadata(entry: DurableCheckpointEntry): WorkflowSerializableValue {
@@ -395,6 +399,10 @@ function encodeMetadata(entry: DurableCheckpointEntry): WorkflowSerializableValu
395
399
  ...(entry.label !== undefined ? { label: entry.label } : {}),
396
400
  ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
397
401
  ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
402
+ ...(entry.invocationCwd !== undefined ? { invocationCwd: entry.invocationCwd } : {}),
403
+ ...(entry.workflowCwd !== undefined ? { workflowCwd: entry.workflowCwd } : {}),
404
+ ...(entry.repositoryRoot !== undefined ? { repositoryRoot: entry.repositoryRoot } : {}),
405
+ ...(entry.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: entry.gitWorktreeRoot } : {}),
398
406
  ts: entry.ts,
399
407
  },
400
408
  };
@@ -404,7 +412,60 @@ function decodeMetadata(value: WorkflowSerializableValue): DurableCheckpointEntr
404
412
  if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
405
413
  const raw = value as Partial<DbosMetadataEnvelope>;
406
414
  if (raw.__atomicDurableMetadata !== true || raw.version !== METADATA_VERSION) return undefined;
407
- return raw.entry;
415
+ const entry = raw.entry;
416
+ if (entry === undefined) return undefined;
417
+ return parseDurableCheckpointEntry(entry);
418
+ }
419
+
420
+ function parseDurableCheckpointEntry(value: WorkflowSerializableValue): DurableCheckpointEntry | undefined {
421
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
422
+ const entry = value as Partial<DurableCheckpointEntry>;
423
+ if (entry.type !== "workflow.durable.checkpoint"
424
+ || typeof entry.workflowId !== "string"
425
+ || typeof entry.name !== "string"
426
+ || typeof entry.inputs !== "object"
427
+ || entry.inputs === null
428
+ || Array.isArray(entry.inputs)
429
+ || typeof entry.status !== "string"
430
+ || !isDurableWorkflowStatus(entry.status)
431
+ || typeof entry.completedCheckpoints !== "number"
432
+ || typeof entry.pendingPrompts !== "number"
433
+ || typeof entry.ts !== "number"
434
+ || (entry.label !== undefined && typeof entry.label !== "string")
435
+ || (entry.rootWorkflowId !== undefined && typeof entry.rootWorkflowId !== "string")
436
+ || (entry.resumable !== undefined && typeof entry.resumable !== "boolean")
437
+ || (entry.invocationCwd !== undefined && typeof entry.invocationCwd !== "string")
438
+ || (entry.workflowCwd !== undefined && typeof entry.workflowCwd !== "string")
439
+ || (entry.repositoryRoot !== undefined && typeof entry.repositoryRoot !== "string")
440
+ || (entry.gitWorktreeRoot !== undefined && typeof entry.gitWorktreeRoot !== "string")) {
441
+ return undefined;
442
+ }
443
+ return {
444
+ type: "workflow.durable.checkpoint",
445
+ workflowId: entry.workflowId,
446
+ name: entry.name,
447
+ inputs: entry.inputs,
448
+ status: entry.status,
449
+ completedCheckpoints: entry.completedCheckpoints,
450
+ pendingPrompts: entry.pendingPrompts,
451
+ ts: entry.ts,
452
+ ...(entry.label !== undefined ? { label: entry.label } : {}),
453
+ ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
454
+ ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
455
+ ...(entry.invocationCwd !== undefined ? { invocationCwd: entry.invocationCwd } : {}),
456
+ ...(entry.workflowCwd !== undefined ? { workflowCwd: entry.workflowCwd } : {}),
457
+ ...(entry.repositoryRoot !== undefined ? { repositoryRoot: entry.repositoryRoot } : {}),
458
+ ...(entry.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: entry.gitWorktreeRoot } : {}),
459
+ };
460
+ }
461
+
462
+ function isDurableWorkflowStatus(value: string): value is DurableWorkflowStatus {
463
+ return value === "running"
464
+ || value === "paused"
465
+ || value === "completed"
466
+ || value === "failed"
467
+ || value === "cancelled"
468
+ || value === "blocked";
408
469
  }
409
470
 
410
471
  function dbosStatusToDurable(status: string): DurableWorkflowStatus {
@@ -51,6 +51,14 @@ export interface DbosCheckpointEnvelope extends WorkflowSerializableObject {
51
51
  readonly sessionId?: string;
52
52
  readonly sessionFile?: string;
53
53
  readonly completedAt: number;
54
+ readonly startedAt?: number;
55
+ readonly endedAt?: number;
56
+ readonly durationMs?: number;
57
+ readonly result?: string;
58
+ readonly model?: string;
59
+ readonly fastMode?: boolean;
60
+ readonly attemptedModels?: WorkflowSerializableValue;
61
+ readonly modelAttempts?: WorkflowSerializableValue;
54
62
  }
55
63
 
56
64
  /**
@@ -82,6 +90,14 @@ export function encodeCheckpoint(cp: DurableCheckpoint): DbosCheckpointEnvelope
82
90
  replayKey: s.replayKey,
83
91
  ...(s.sessionId !== undefined ? { sessionId: s.sessionId } : {}),
84
92
  ...(s.sessionFile !== undefined ? { sessionFile: s.sessionFile } : {}),
93
+ ...(s.startedAt !== undefined ? { startedAt: s.startedAt } : {}),
94
+ ...(s.endedAt !== undefined ? { endedAt: s.endedAt } : {}),
95
+ ...(s.durationMs !== undefined ? { durationMs: s.durationMs } : {}),
96
+ ...(s.result !== undefined ? { result: s.result } : {}),
97
+ ...(s.model !== undefined ? { model: s.model } : {}),
98
+ ...(s.fastMode !== undefined ? { fastMode: s.fastMode } : {}),
99
+ ...(s.attemptedModels !== undefined ? { attemptedModels: [...s.attemptedModels] } : {}),
100
+ ...(s.modelAttempts !== undefined ? { modelAttempts: s.modelAttempts as WorkflowSerializableValue } : {}),
85
101
  };
86
102
  }
87
103
 
@@ -144,9 +160,21 @@ function decodeEnvelope(workflowId: string, env: DbosCheckpointEnvelope): Durabl
144
160
  ...(env.hasOutput !== false && env.output !== undefined ? { output: env.output } : {}),
145
161
  ...(env.sessionId !== undefined ? { sessionId: env.sessionId } : {}),
146
162
  ...(env.sessionFile !== undefined ? { sessionFile: env.sessionFile } : {}),
163
+ ...(typeof env.startedAt === "number" ? { startedAt: env.startedAt } : {}),
164
+ ...(typeof env.endedAt === "number" ? { endedAt: env.endedAt } : {}),
165
+ ...(typeof env.durationMs === "number" ? { durationMs: env.durationMs } : {}),
166
+ ...(typeof env.result === "string" ? { result: env.result } : {}),
167
+ ...(typeof env.model === "string" ? { model: env.model } : {}),
168
+ ...(typeof env.fastMode === "boolean" ? { fastMode: env.fastMode } : {}),
169
+ ...(isStringArray(env.attemptedModels) ? { attemptedModels: env.attemptedModels } : {}),
170
+ ...(Array.isArray(env.modelAttempts) ? { modelAttempts: env.modelAttempts as DurableStageCheckpoint["modelAttempts"] } : {}),
147
171
  } as DurableStageCheckpoint;
148
172
  }
149
173
 
174
+
175
+ function isStringArray(value: WorkflowSerializableValue | undefined): value is readonly string[] {
176
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
177
+ }
150
178
  function decodeLegacy(
151
179
  workflowId: string,
152
180
  stepName: string,
@@ -107,6 +107,10 @@ function parseDurableEntry(raw: Record<string, unknown>): DurableCheckpointEntry
107
107
  ...(typeof raw["label"] === "string" ? { label: raw["label"] } : {}),
108
108
  ...(typeof raw["rootWorkflowId"] === "string" ? { rootWorkflowId: raw["rootWorkflowId"] } : {}),
109
109
  ...(typeof raw["resumable"] === "boolean" ? { resumable: raw["resumable"] } : {}),
110
+ ...(typeof raw["invocationCwd"] === "string" ? { invocationCwd: raw["invocationCwd"] } : {}),
111
+ ...(typeof raw["workflowCwd"] === "string" ? { workflowCwd: raw["workflowCwd"] } : {}),
112
+ ...(typeof raw["repositoryRoot"] === "string" ? { repositoryRoot: raw["repositoryRoot"] } : {}),
113
+ ...(typeof raw["gitWorktreeRoot"] === "string" ? { gitWorktreeRoot: raw["gitWorktreeRoot"] } : {}),
110
114
  ts,
111
115
  };
112
116
  }
@@ -142,6 +146,10 @@ function entryToResumable(entry: DurableCheckpointEntry, sessionFile: string): R
142
146
  ...(entry.label !== undefined ? { label: entry.label } : {}),
143
147
  ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
144
148
  ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
149
+ ...(entry.invocationCwd !== undefined ? { invocationCwd: entry.invocationCwd } : {}),
150
+ ...(entry.workflowCwd !== undefined ? { workflowCwd: entry.workflowCwd } : {}),
151
+ ...(entry.repositoryRoot !== undefined ? { repositoryRoot: entry.repositoryRoot } : {}),
152
+ ...(entry.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: entry.gitWorktreeRoot } : {}),
145
153
  createdAt: entry.ts,
146
154
  updatedAt: entry.ts,
147
155
  };
@@ -197,6 +205,10 @@ export function persistDurableCacheEntry(
197
205
  ...(entry.label !== undefined ? { label: entry.label } : {}),
198
206
  ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
199
207
  ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
208
+ ...(entry.invocationCwd !== undefined ? { invocationCwd: entry.invocationCwd } : {}),
209
+ ...(entry.workflowCwd !== undefined ? { workflowCwd: entry.workflowCwd } : {}),
210
+ ...(entry.repositoryRoot !== undefined ? { repositoryRoot: entry.repositoryRoot } : {}),
211
+ ...(entry.gitWorktreeRoot !== undefined ? { gitWorktreeRoot: entry.gitWorktreeRoot } : {}),
200
212
  ts: entry.ts,
201
213
  });
202
214
  }
@@ -167,11 +167,14 @@ export function resumeDurableWorkflow(
167
167
  // ORIGINAL workflow id as the run id so durable checkpoints replay.
168
168
  backend.setWorkflowStatus(resolved.workflowId, "running");
169
169
 
170
- const accepted: DetachedAccepted = runDetached(def, inputs, {
170
+ const resumeRunOpts: RunOpts = {
171
171
  ...deps.baseRunOpts,
172
+ ...(handle.invocationCwd !== undefined ? { cwd: handle.invocationCwd } : {}),
172
173
  runId: resolved.workflowId,
173
174
  durableBackend: backend,
174
- });
175
+ };
176
+
177
+ const accepted: DetachedAccepted = runDetached(def, inputs, resumeRunOpts);
175
178
 
176
179
  return {
177
180
  ok: true,
@@ -1,6 +1,6 @@
1
1
  /** Durable `ctx.stage` / `ctx.task` replay and checkpoint helpers. */
2
2
 
3
- import type { StageContext, StageOptions, WorkflowTaskOptions, WorkflowTaskResult } from "../shared/types.js";
3
+ import type { StageContext, StageOptions, WorkflowChildResult, WorkflowOutputValues, WorkflowTaskOptions, WorkflowTaskResult } from "../shared/types.js";
4
4
  import type { StageSnapshot } from "../shared/store-types.js";
5
5
  import type { WorkflowSerializableValue } from "../shared/types.js";
6
6
  import type { DurableWorkflowBackend } from "./backend.js";
@@ -8,6 +8,7 @@ import type { ParallelFailFastScope } from "../runs/foreground/executor-types.js
8
8
  import { durableHash } from "./backend.js";
9
9
  import { recordCheckpointDurably } from "./tool-primitive.js";
10
10
  import type { DurableStageCheckpoint } from "./types.js";
11
+ export type DurableCompletedStageCheckpoint = DurableStageCheckpoint & { readonly output: WorkflowSerializableValue };
11
12
 
12
13
  export interface DurableStageDeps {
13
14
  readonly workflowId: string;
@@ -20,16 +21,28 @@ export interface DurableStageDeps {
20
21
  export async function recordStageCheckpoint(deps: DurableStageDeps, stage: StageSnapshot): Promise<boolean> {
21
22
  if (stage.status !== "completed") return false;
22
23
  const replayKey = deps.replayKeyForCompletedStage?.(stage) ?? stage.replayKey ?? deps.nextReplayKey(stage.name);
23
- if (deps.backend.getStageOutput(deps.workflowId, replayKey) !== undefined) return false;
24
- const checkpoint: DurableStageCheckpoint = {
25
- kind: "stage",
26
- workflowId: deps.workflowId,
27
- checkpointId: stableCheckpointId("stage", replayKey),
28
- name: stage.name,
29
- replayKey,
30
- output: stageOutput(stage),
31
- completedAt: stage.endedAt ?? Date.now(),
32
- };
24
+ const metadata = checkpointMetadata(stage);
25
+ const hasExistingOutput = deps.backend.getStageOutput(deps.workflowId, replayKey) !== undefined;
26
+ const checkpoint: DurableStageCheckpoint = hasExistingOutput
27
+ ? {
28
+ kind: "stage",
29
+ workflowId: deps.workflowId,
30
+ checkpointId: stageMetadataCheckpointId(replayKey, stage),
31
+ name: stage.name,
32
+ replayKey,
33
+ completedAt: stage.endedAt ?? Date.now(),
34
+ ...metadata,
35
+ }
36
+ : {
37
+ kind: "stage",
38
+ workflowId: deps.workflowId,
39
+ checkpointId: stableCheckpointId("stage", replayKey),
40
+ name: stage.name,
41
+ replayKey,
42
+ output: stageOutput(stage),
43
+ completedAt: stage.endedAt ?? Date.now(),
44
+ ...metadata,
45
+ };
33
46
  await recordCheckpointDurably(deps.backend, checkpoint);
34
47
  return true;
35
48
  }
@@ -79,14 +92,14 @@ export function createDurableStagePrimitive(input: {
79
92
  readonly backend: DurableWorkflowBackend;
80
93
  readonly nextReplayKey: (stageName: string) => string;
81
94
  readonly stage: (name: string, options: StageOptions | undefined, replayKey: string) => StageContext;
82
- readonly recordCachedStage?: (name: string, replayKey: string, output: WorkflowSerializableValue) => void;
95
+ readonly recordCachedStage?: (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint) => void;
83
96
  }): (name: string, options?: StageOptions) => StageContext {
84
97
  return (name: string, options?: StageOptions): StageContext => {
85
98
  const replayKey = input.nextReplayKey(name);
86
- const cached = input.backend.getStageOutput(input.workflowId, replayKey);
99
+ const cached = stageCheckpointWithOutput(input.backend, input.workflowId, replayKey);
87
100
  if (cached !== undefined) {
88
101
  input.recordCachedStage?.(name, replayKey, cached);
89
- return createCachedStageContext(name, cached);
102
+ return createCachedStageContext(name, cached.output, cached.result);
90
103
  }
91
104
  const session = input.backend.getStageSession(input.workflowId, replayKey);
92
105
  const isMidSessionResume = session?.sessionFile !== undefined;
@@ -112,14 +125,14 @@ export function createDurableTaskPrimitive(input: {
112
125
  readonly backend: DurableWorkflowBackend;
113
126
  readonly nextReplayKey: (stageName: string) => string;
114
127
  readonly task: (name: string, options: WorkflowTaskOptions, stageFailFastScope?: ParallelFailFastScope) => Promise<WorkflowTaskResult>;
115
- readonly recordCachedTask?: (name: string, replayKey: string, output: WorkflowTaskResult) => void;
128
+ readonly recordCachedTask?: (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint, stageFailFastScope?: ParallelFailFastScope) => void;
116
129
  }): (name: string, options: WorkflowTaskOptions) => Promise<WorkflowTaskResult> {
117
130
  return async (name: string, options: WorkflowTaskOptions, stageFailFastScope?: ParallelFailFastScope): Promise<WorkflowTaskResult> => {
118
131
  const replayKey = input.nextReplayKey(`task:${name}`);
119
- const cached = input.backend.getStageOutput(input.workflowId, replayKey);
120
- if (cached !== undefined && isWorkflowTaskResult(cached)) {
121
- input.recordCachedTask?.(name, replayKey, cached);
122
- return cached;
132
+ const cached = stageCheckpointWithOutput(input.backend, input.workflowId, replayKey, isWorkflowTaskResult);
133
+ if (cached !== undefined && isWorkflowTaskResult(cached.output)) {
134
+ input.recordCachedTask?.(name, replayKey, cached, stageFailFastScope);
135
+ return cached.output;
123
136
  }
124
137
  const session = input.backend.getStageSession(input.workflowId, replayKey);
125
138
  const taskOptions: WorkflowTaskOptions = {
@@ -136,6 +149,7 @@ export function createDurableTaskPrimitive(input: {
136
149
  replayKey,
137
150
  output: result,
138
151
  completedAt: Date.now(),
152
+ ...taskCheckpointMetadata(result),
139
153
  });
140
154
  return result;
141
155
  };
@@ -174,8 +188,8 @@ function wrapSchemaStageForDurability(input: {
174
188
  return wrapped;
175
189
  }
176
190
 
177
- function createCachedStageContext(name: string, output: WorkflowSerializableValue): StageContext {
178
- const text = typeof output === "string" ? output : JSON.stringify(output);
191
+ function createCachedStageContext(name: string, output: WorkflowSerializableValue, result?: string): StageContext {
192
+ const text = result ?? (typeof output === "string" ? output : JSON.stringify(output));
179
193
  const unsupported = async (): Promise<never> => { throw new Error(`Stage "${name}" was replayed from a durable checkpoint; live session operations are unavailable.`); };
180
194
  const cached = {
181
195
  name,
@@ -213,6 +227,82 @@ function stageOutput(stage: StageSnapshot): WorkflowSerializableValue {
213
227
  return { status: stage.status, stageId: stage.id };
214
228
  }
215
229
 
230
+ function checkpointMetadata(stage: StageSnapshot): Partial<DurableStageCheckpoint> {
231
+ return {
232
+ ...(stage.startedAt !== undefined ? { startedAt: stage.startedAt } : {}),
233
+ ...(stage.endedAt !== undefined ? { endedAt: stage.endedAt } : {}),
234
+ ...(stage.durationMs !== undefined ? { durationMs: stage.durationMs } : {}),
235
+ ...(stage.result !== undefined ? { result: stage.result } : {}),
236
+ ...(stage.sessionId !== undefined ? { sessionId: stage.sessionId } : {}),
237
+ ...(stage.sessionFile !== undefined ? { sessionFile: stage.sessionFile } : {}),
238
+ ...(stage.model !== undefined ? { model: stage.model } : {}),
239
+ ...(stage.fastMode !== undefined ? { fastMode: stage.fastMode } : {}),
240
+ ...(stage.attemptedModels !== undefined ? { attemptedModels: [...stage.attemptedModels] } : {}),
241
+ ...(stage.modelAttempts !== undefined ? { modelAttempts: [...stage.modelAttempts] } : {}),
242
+ };
243
+ }
244
+
245
+ function taskCheckpointMetadata(result: WorkflowTaskResult): Partial<DurableStageCheckpoint> {
246
+ return {
247
+ result: result.text,
248
+ ...(result.sessionId !== undefined ? { sessionId: result.sessionId } : {}),
249
+ ...(result.sessionFile !== undefined ? { sessionFile: result.sessionFile } : {}),
250
+ ...(result.model !== undefined ? { model: result.model } : {}),
251
+ ...(result.fastMode !== undefined ? { fastMode: result.fastMode } : {}),
252
+ ...(result.attemptedModels !== undefined ? { attemptedModels: [...result.attemptedModels] } : {}),
253
+ ...(result.modelAttempts !== undefined ? { modelAttempts: [...result.modelAttempts] } : {}),
254
+ };
255
+ }
256
+
257
+ export function stageCheckpointWithOutput(
258
+ backend: DurableWorkflowBackend,
259
+ workflowId: string,
260
+ replayKey: string,
261
+ matchesOutput?: (value: WorkflowSerializableValue) => boolean,
262
+ ): DurableCompletedStageCheckpoint | undefined {
263
+ const checkpoints = backend.listCheckpoints(workflowId)
264
+ .filter((checkpoint): checkpoint is DurableStageCheckpoint =>
265
+ checkpoint.kind === "stage" && checkpoint.replayKey === replayKey,
266
+ );
267
+ const outputCheckpoints = checkpoints.filter((checkpoint): checkpoint is DurableCompletedStageCheckpoint =>
268
+ checkpoint.output !== undefined,
269
+ );
270
+ const replayValueCheckpoint = matchesOutput === undefined
271
+ ? outputCheckpoints[0]
272
+ : outputCheckpoints.find((checkpoint) => matchesOutput(checkpoint.output));
273
+ if (replayValueCheckpoint === undefined) return undefined;
274
+ return mergeCheckpointHydrationMetadata(replayValueCheckpoint, checkpoints);
275
+ }
276
+
277
+ function mergeCheckpointHydrationMetadata(
278
+ replayValueCheckpoint: DurableCompletedStageCheckpoint,
279
+ checkpoints: readonly DurableStageCheckpoint[],
280
+ ): DurableCompletedStageCheckpoint {
281
+ if (checkpoints.length === 0) return replayValueCheckpoint;
282
+ return {
283
+ ...replayValueCheckpoint,
284
+ ...(replayValueCheckpoint.startedAt === undefined ? metadataValue(checkpoints, "startedAt") : {}),
285
+ ...(replayValueCheckpoint.endedAt === undefined ? metadataValue(checkpoints, "endedAt") : {}),
286
+ ...(replayValueCheckpoint.durationMs === undefined ? metadataValue(checkpoints, "durationMs") : {}),
287
+ ...(replayValueCheckpoint.result === undefined ? metadataValue(checkpoints, "result") : {}),
288
+ ...(replayValueCheckpoint.sessionId === undefined ? metadataValue(checkpoints, "sessionId") : {}),
289
+ ...(replayValueCheckpoint.sessionFile === undefined ? metadataValue(checkpoints, "sessionFile") : {}),
290
+ ...(replayValueCheckpoint.model === undefined ? metadataValue(checkpoints, "model") : {}),
291
+ ...(replayValueCheckpoint.fastMode === undefined ? metadataValue(checkpoints, "fastMode") : {}),
292
+ ...(replayValueCheckpoint.attemptedModels === undefined ? metadataValue(checkpoints, "attemptedModels") : {}),
293
+ ...(replayValueCheckpoint.modelAttempts === undefined ? metadataValue(checkpoints, "modelAttempts") : {}),
294
+ };
295
+ }
296
+
297
+ function metadataValue<K extends keyof DurableStageCheckpoint>(
298
+ checkpoints: readonly DurableStageCheckpoint[],
299
+ key: K,
300
+ ): Pick<DurableStageCheckpoint, K> | Record<string, never> {
301
+ const value = checkpoints.find((checkpoint) => checkpoint[key] !== undefined)?.[key];
302
+ return value === undefined ? {} : { [key]: value } as Pick<DurableStageCheckpoint, K>;
303
+ }
304
+
305
+
216
306
  export function createStageReplayKeyGenerator(_workflowId: string): (stageName: string, stageId?: string) => string {
217
307
  const counts = new Map<string, number>();
218
308
  return (stageName: string, _stageId?: string): string => {
@@ -236,6 +326,15 @@ function stageSessionCheckpointId(replayKey: string, stage: StageSnapshot): stri
236
326
  export function cachedStageId(runId: string, replayKey: string): string {
237
327
  return `durable-${durableHash({ runId, replayKey })}`;
238
328
  }
329
+ function stageMetadataCheckpointId(replayKey: string, stage: StageSnapshot): string {
330
+ return `${stableCheckpointId("stage-meta", replayKey)}:${durableHash({
331
+ stageId: stage.id,
332
+ endedAt: stage.endedAt ?? 0,
333
+ durationMs: stage.durationMs ?? 0,
334
+ result: stage.result ?? "",
335
+ })}`;
336
+ }
337
+
239
338
 
240
339
  export function recordCachedStageIntoStore(
241
340
  store: import("../shared/store.js").Store,
@@ -245,13 +344,24 @@ export function recordCachedStageIntoStore(
245
344
  output: WorkflowSerializableValue,
246
345
  completedStageReplayKeys: Map<string, string>,
247
346
  parentIds?: readonly string[],
347
+ checkpoint?: DurableCompletedStageCheckpoint,
248
348
  ): void {
249
349
  const now = Date.now();
250
350
  const stageId = cachedStageId(runId, replayKey);
251
- const result = typeof output === "string" ? output : JSON.stringify(output);
351
+ const result = checkpoint?.result ?? (typeof output === "string" ? output : JSON.stringify(output));
352
+ const endedAt = checkpoint?.endedAt ?? checkpoint?.completedAt ?? now;
353
+ const workflowChild = isWorkflowChildResult(output) ? workflowChildSnapshotFromResult(output) : undefined;
252
354
  const snapshot: StageSnapshot = {
253
- id: stageId, name, status: "completed", parentIds: parentIds !== undefined ? Object.freeze([...parentIds]) : [], startedAt: now, endedAt: now, durationMs: 0, result,
355
+ id: stageId, name, status: "completed", parentIds: parentIds !== undefined ? Object.freeze([...parentIds]) : [],
356
+ startedAt: checkpoint?.startedAt ?? endedAt, endedAt, durationMs: checkpoint?.durationMs ?? 0, result,
254
357
  replayKey, replayed: true, skippedReason: "durable checkpoint replay", toolEvents: [], attachable: false,
358
+ ...(workflowChild !== undefined ? { workflowChild } : {}),
359
+ ...(checkpoint?.sessionId !== undefined ? { sessionId: checkpoint.sessionId } : {}),
360
+ ...(checkpoint?.sessionFile !== undefined ? { sessionFile: checkpoint.sessionFile } : {}),
361
+ ...(checkpoint?.model !== undefined ? { model: checkpoint.model } : {}),
362
+ ...(checkpoint?.fastMode !== undefined ? { fastMode: checkpoint.fastMode } : {}),
363
+ ...(checkpoint?.attemptedModels !== undefined ? { attemptedModels: checkpoint.attemptedModels } : {}),
364
+ ...(checkpoint?.modelAttempts !== undefined ? { modelAttempts: checkpoint.modelAttempts } : {}),
255
365
  };
256
366
  store.recordStageStart(runId, snapshot);
257
367
  store.recordStageEnd(runId, snapshot);
@@ -269,16 +379,44 @@ export function recordCachedStageWithTracker(
269
379
  runId: string,
270
380
  name: string,
271
381
  replayKey: string,
272
- output: WorkflowSerializableValue,
382
+ checkpoint: DurableCompletedStageCheckpoint,
273
383
  completedStageReplayKeys: Map<string, string>,
384
+ stageFailFastScope?: ParallelFailFastScope,
274
385
  ): void {
275
386
  const stageId = cachedStageId(runId, replayKey);
276
- const parentIds = tracker.onSpawn(stageId, name);
277
- recordCachedStageIntoStore(store, runId, name, replayKey, output, completedStageReplayKeys, parentIds);
387
+ let parentIds = tracker.onSpawn(stageId, name);
388
+ const scopeParentIds = stageFailFastScope?.parentIds ?? [];
389
+ if (stageFailFastScope !== undefined) {
390
+ tracker.replaceParents(stageId, scopeParentIds);
391
+ parentIds = [...scopeParentIds];
392
+ }
393
+ recordCachedStageIntoStore(store, runId, name, replayKey, checkpoint.output, completedStageReplayKeys, parentIds, checkpoint);
278
394
  tracker.onSettle(stageId);
279
395
  }
280
-
281
396
  function isWorkflowTaskResult(value: WorkflowSerializableValue): value is WorkflowTaskResult {
282
397
  if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
283
398
  return typeof (value as Record<string, WorkflowSerializableValue>)["text"] === "string";
284
399
  }
400
+
401
+ function isWorkflowChildResult(value: WorkflowSerializableValue): value is WorkflowChildResult<WorkflowOutputValues> {
402
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
403
+ const objectValue = value as Record<string, WorkflowSerializableValue | undefined>;
404
+ return typeof objectValue["workflow"] === "string"
405
+ && typeof objectValue["runId"] === "string"
406
+ && typeof objectValue["status"] === "string"
407
+ && typeof objectValue["outputs"] === "object"
408
+ && objectValue["outputs"] !== null
409
+ && !Array.isArray(objectValue["outputs"]);
410
+ }
411
+
412
+ function workflowChildSnapshotFromResult(result: WorkflowChildResult<WorkflowOutputValues>): StageSnapshot["workflowChild"] {
413
+ return {
414
+ alias: result.workflow,
415
+ workflow: result.workflow,
416
+ runId: result.runId,
417
+ status: result.status,
418
+ ...(result.exited !== undefined ? { exited: result.exited } : {}),
419
+ outputs: result.outputs,
420
+ ...(typeof result.exitReason === "string" ? { exitReason: result.exitReason } : {}),
421
+ };
422
+ }
@@ -9,7 +9,7 @@
9
9
  * cross-ref: issue #1498
10
10
  */
11
11
 
12
- import type { WorkflowSerializableValue } from "../shared/types.js";
12
+ import type { WorkflowModelAttempt, WorkflowSerializableValue } from "../shared/types.js";
13
13
 
14
14
  // ---------------------------------------------------------------------------
15
15
  // Top-level workflow identity
@@ -32,6 +32,14 @@ export interface DurableWorkflowHandle {
32
32
  readonly updatedAt: number;
33
33
  /** Current durable status. */
34
34
  readonly status: DurableWorkflowStatus;
35
+ /** Original invocation cwd used to resolve repo-relative workflow defaults on resume. */
36
+ readonly invocationCwd?: string;
37
+ /** Resolved workflow cwd when an input-bound reusable worktree was set up. */
38
+ readonly workflowCwd?: string;
39
+ /** Invoking repository root used for reusable worktree validation. */
40
+ readonly repositoryRoot?: string;
41
+ /** Resolved reusable git worktree root, when setup happened at workflow start. */
42
+ readonly gitWorktreeRoot?: string;
35
43
  /** Session file path that caches this workflow's durable metadata. */
36
44
  readonly sessionFile?: string;
37
45
  /** Number of completed durable checkpoints. */
@@ -113,6 +121,19 @@ export interface DurableStageCheckpoint {
113
121
  readonly sessionId?: string;
114
122
  readonly sessionFile?: string;
115
123
  readonly completedAt: number;
124
+ /** Original stage start timestamp, when available. */
125
+ readonly startedAt?: number;
126
+ /** Original stage end timestamp, when available. */
127
+ readonly endedAt?: number;
128
+ /** Original stage duration, when available. */
129
+ readonly durationMs?: number;
130
+ /** Display/result text recorded for the completed stage. */
131
+ readonly result?: string;
132
+ /** Completed stage/task model metadata used to hydrate replayed snapshots. */
133
+ readonly model?: string;
134
+ readonly fastMode?: boolean;
135
+ readonly attemptedModels?: readonly string[];
136
+ readonly modelAttempts?: readonly WorkflowModelAttempt[];
116
137
  }
117
138
 
118
139
  export type UiPromptKind = "input" | "confirm" | "select" | "editor" | "custom";
@@ -145,6 +166,10 @@ export interface DurableCheckpointEntry {
145
166
  readonly label?: string;
146
167
  readonly rootWorkflowId?: string;
147
168
  readonly resumable?: boolean;
169
+ readonly invocationCwd?: string;
170
+ readonly workflowCwd?: string;
171
+ readonly repositoryRoot?: string;
172
+ readonly gitWorktreeRoot?: string;
148
173
  readonly ts: number;
149
174
  }
150
175
 
@@ -163,6 +188,10 @@ export interface ResumableWorkflowEntry {
163
188
  readonly label?: string;
164
189
  readonly rootWorkflowId?: string;
165
190
  readonly resumable?: boolean;
191
+ readonly invocationCwd?: string;
192
+ readonly workflowCwd?: string;
193
+ readonly repositoryRoot?: string;
194
+ readonly gitWorktreeRoot?: string;
166
195
  readonly createdAt: number;
167
196
  readonly updatedAt: number;
168
197
  }
@@ -41,6 +41,7 @@ function createTaskPrimitive(runtime: EngineRuntime): WorkflowTaskPrimitive {
41
41
  const resolvedTaskOptions = stageOptionsWithGitWorktree(
42
42
  stageOptionsWithInputDefaults(taskOptions, runtime.inputRuntimeDefaults),
43
43
  runtime.workflowInvocationCwd,
44
+ runtime.gitWorktreeSetupCache,
44
45
  ) ?? taskOptions;
45
46
  const stageOptions = taskStageOptions(resolvedTaskOptions);
46
47
  const stageHandle = runtime.spawnStage(name, {
@@ -38,8 +38,7 @@ export async function finalizeDurableTerminalStatus(input: DurableTerminalFinali
38
38
 
39
39
  const durableStatus = toDurableStatus(status);
40
40
  if (durableStatus !== undefined) {
41
- const resumable = status === "blocked" ? false : input.runSnapshot.resumable;
42
- input.durableBackend.setWorkflowStatus(input.runId, durableStatus, undefined, resumable);
41
+ input.durableBackend.setWorkflowStatus(input.runId, durableStatus, undefined, input.runSnapshot.resumable);
43
42
  }
44
43
  try {
45
44
  await input.durableBackend.flush?.();
@@ -1,5 +1,13 @@
1
1
  import type { RunEndMetadata } from "../shared/store-public-types.js";
2
2
  import type { WorkflowOutputValues } from "../shared/types.js";
3
+ import type { RunSnapshot } from "../shared/store-types.js";
4
+ import {
5
+ actionableReturnedStatusText,
6
+ isReturnedBlockedWorkflowStatus,
7
+ isReturnedResumableBlockedWorkflowStatus,
8
+ normalizeReturnedWorkflowStatus,
9
+ structuredRecoverableWorkflowFailure,
10
+ } from "../shared/returned-run-status.js";
3
11
 
4
12
  export interface ReturnedRunStatus {
5
13
  readonly status: "completed" | "failed" | "blocked";
@@ -7,23 +15,44 @@ export interface ReturnedRunStatus {
7
15
  readonly metadata?: RunEndMetadata;
8
16
  }
9
17
 
10
- export function classifyReturnedRunStatus(result: WorkflowOutputValues | undefined): ReturnedRunStatus {
11
- const returnedStatus = result?.["status"];
12
- if (returnedStatus !== "failed" && returnedStatus !== "blocked") {
18
+ export function classifyReturnedRunStatus(result: WorkflowOutputValues | undefined, runSnapshot?: RunSnapshot): ReturnedRunStatus {
19
+ const structuredFailure = runSnapshot !== undefined ? structuredRecoverableWorkflowFailure(runSnapshot) : undefined;
20
+ if (structuredFailure !== undefined) {
21
+ return {
22
+ status: "blocked",
23
+ error: structuredFailure.error,
24
+ metadata: structuredFailure.metadata,
25
+ };
26
+ }
27
+ const returnedStatus = normalizeReturnedWorkflowStatus(result?.["status"]);
28
+ if (returnedStatus === undefined || returnedStatus === "completed" || returnedStatus === "complete") {
13
29
  return { status: "completed" };
14
30
  }
15
31
 
16
- const summary = result?.["summary"];
17
- const error = typeof summary === "string" && summary.trim().length > 0
18
- ? summary.trim()
19
- : `Workflow returned status ${JSON.stringify(returnedStatus)}.`;
20
- return {
21
- status: returnedStatus,
22
- error,
23
- metadata: returnedStatus === "failed"
24
- ? returnedFailureMetadata(error)
25
- : returnedBlockedMetadata(),
26
- };
32
+ const error = returnedStatusError(result, returnedStatus);
33
+ if (returnedStatus === "failed") {
34
+ return {
35
+ status: "failed",
36
+ error,
37
+ metadata: returnedFailureMetadata(error),
38
+ };
39
+ }
40
+ if (isReturnedBlockedWorkflowStatus(returnedStatus)) {
41
+ const metadata = isReturnedResumableBlockedWorkflowStatus(returnedStatus)
42
+ ? returnedRecoverableBlockedMetadata(error)
43
+ : { resumable: false };
44
+ return {
45
+ status: "blocked",
46
+ error,
47
+ metadata,
48
+ };
49
+ }
50
+
51
+ return { status: "completed" };
52
+ }
53
+
54
+ function returnedStatusError(result: WorkflowOutputValues | undefined, returnedStatus: string): string {
55
+ return actionableReturnedStatusText(result) ?? `Workflow returned status ${JSON.stringify(returnedStatus)}.`;
27
56
  }
28
57
 
29
58
  function returnedFailureMetadata(error: string): RunEndMetadata {
@@ -36,6 +65,11 @@ function returnedFailureMetadata(error: string): RunEndMetadata {
36
65
  };
37
66
  }
38
67
 
39
- function returnedBlockedMetadata(): RunEndMetadata {
40
- return { resumable: false };
68
+ function returnedRecoverableBlockedMetadata(error: string): RunEndMetadata {
69
+ return {
70
+ failureRecoverability: "recoverable",
71
+ failureDisposition: "active_blocked",
72
+ failureMessage: error,
73
+ resumable: true,
74
+ };
41
75
  }