@bastani/atomic 0.9.2 → 0.9.3-alpha.2

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 (606) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +2 -2
  3. package/dist/builtin/cursor/CHANGELOG.md +15 -0
  4. package/dist/builtin/cursor/README.md +2 -1
  5. package/dist/builtin/cursor/package.json +2 -2
  6. package/dist/builtin/cursor/src/cursor-models-raw.json +2 -9
  7. package/dist/builtin/cursor/src/model-mapper.ts +14 -3
  8. package/dist/builtin/cursor/src/proto/protobuf-codec-base64.ts +22 -0
  9. package/dist/builtin/cursor/src/proto/protobuf-codec-request.ts +53 -13
  10. package/dist/builtin/cursor/src/proto/protobuf-codec-wire.ts +24 -7
  11. package/dist/builtin/cursor/src/proto/protobuf-codec.ts +3 -2
  12. package/dist/builtin/cursor/src/stream.ts +5 -11
  13. package/dist/builtin/cursor/src/transport-types.ts +3 -0
  14. package/dist/builtin/cursor/src/transport.ts +1 -0
  15. package/dist/builtin/intercom/package.json +1 -1
  16. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  17. package/dist/builtin/mcp/direct-tools.ts +4 -2
  18. package/dist/builtin/mcp/package.json +1 -1
  19. package/dist/builtin/mcp/proxy-call.ts +3 -1
  20. package/dist/builtin/mcp/utils.ts +18 -7
  21. package/dist/builtin/subagents/CHANGELOG.md +20 -0
  22. package/dist/builtin/subagents/README.md +6 -6
  23. package/dist/builtin/subagents/agents/code-simplifier.md +7 -6
  24. package/dist/builtin/subagents/agents/codebase-analyzer.md +5 -4
  25. package/dist/builtin/subagents/agents/codebase-locator.md +3 -3
  26. package/dist/builtin/subagents/agents/codebase-online-researcher.md +10 -10
  27. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +4 -4
  28. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +3 -3
  29. package/dist/builtin/subagents/agents/codebase-research-locator.md +4 -4
  30. package/dist/builtin/subagents/agents/debugger.md +5 -5
  31. package/dist/builtin/subagents/agents/worker.md +56 -0
  32. package/dist/builtin/subagents/package.json +1 -1
  33. package/dist/builtin/subagents/skills/subagent/SKILL.md +11 -11
  34. package/dist/builtin/subagents/src/agents/agent-loaders.ts +3 -5
  35. package/dist/builtin/subagents/src/agents/agent-management-helpers.ts +3 -3
  36. package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -0
  37. package/dist/builtin/subagents/src/extension/index.ts +6 -3
  38. package/dist/builtin/subagents/src/extension/schemas.ts +2 -7
  39. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -3
  40. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -4
  41. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +15 -1
  42. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +35 -1
  43. package/dist/builtin/subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +1 -1
  44. package/dist/builtin/subagents/src/runs/shared/nested-render.ts +2 -2
  45. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -1
  46. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +4 -2
  47. package/dist/builtin/subagents/src/shared/types-async.ts +1 -0
  48. package/dist/builtin/subagents/src/shared/types-depth.ts +5 -5
  49. package/dist/builtin/subagents/src/shared/types-runtime.ts +2 -1
  50. package/dist/builtin/subagents/src/slash/prompt-template-bridge.ts +27 -5
  51. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -2
  52. package/dist/builtin/subagents/src/tui/render-layout.ts +27 -4
  53. package/dist/builtin/subagents/src/tui/render-result-animation.ts +22 -31
  54. package/dist/builtin/subagents/src/tui/render-result-compact.ts +6 -6
  55. package/dist/builtin/subagents/src/tui/render-result.ts +20 -19
  56. package/dist/builtin/subagents/src/tui/render-status-progress.ts +3 -3
  57. package/dist/builtin/subagents/src/tui/render-widget.ts +46 -7
  58. package/dist/builtin/subagents/src/tui/render.ts +2 -2
  59. package/dist/builtin/web-access/package.json +1 -1
  60. package/dist/builtin/workflows/CHANGELOG.md +56 -0
  61. package/dist/builtin/workflows/README.md +3 -3
  62. package/dist/builtin/workflows/builtin/goal-artifacts.ts +11 -6
  63. package/dist/builtin/workflows/builtin/goal-ledger.ts +33 -1
  64. package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -28
  65. package/dist/builtin/workflows/builtin/goal-reducer.ts +2 -2
  66. package/dist/builtin/workflows/builtin/goal-reports.ts +2 -5
  67. package/dist/builtin/workflows/builtin/goal-review.ts +1 -1
  68. package/dist/builtin/workflows/builtin/goal-runner.ts +10 -17
  69. package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +3 -3
  70. package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +1 -3
  71. package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +1 -1
  72. package/dist/builtin/workflows/builtin/ralph-core.ts +7 -17
  73. package/dist/builtin/workflows/builtin/ralph-runner.ts +11 -18
  74. package/dist/builtin/workflows/builtin/shared-prompts.ts +1 -1
  75. package/dist/builtin/workflows/package.json +1 -1
  76. package/dist/builtin/workflows/src/authoring.d.ts +1 -1
  77. package/dist/builtin/workflows/src/durable/backend.ts +343 -0
  78. package/dist/builtin/workflows/src/durable/child-primitive.ts +79 -0
  79. package/dist/builtin/workflows/src/durable/dbos-backend.ts +421 -0
  80. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +171 -0
  81. package/dist/builtin/workflows/src/durable/factory.ts +96 -0
  82. package/dist/builtin/workflows/src/durable/file-backend.ts +433 -0
  83. package/dist/builtin/workflows/src/durable/index.ts +73 -0
  84. package/dist/builtin/workflows/src/durable/resume-catalog.ts +217 -0
  85. package/dist/builtin/workflows/src/durable/resume-runtime.ts +299 -0
  86. package/dist/builtin/workflows/src/durable/scoped-backend.ts +171 -0
  87. package/dist/builtin/workflows/src/durable/stage-primitive.ts +284 -0
  88. package/dist/builtin/workflows/src/durable/tool-primitive.ts +180 -0
  89. package/dist/builtin/workflows/src/durable/types.ts +168 -0
  90. package/dist/builtin/workflows/src/durable/ui-primitive.ts +96 -0
  91. package/dist/builtin/workflows/src/engine/options.ts +3 -0
  92. package/dist/builtin/workflows/src/engine/primitives/parallel.ts +2 -2
  93. package/dist/builtin/workflows/src/engine/primitives/task.ts +4 -4
  94. package/dist/builtin/workflows/src/engine/primitives/ui.ts +22 -8
  95. package/dist/builtin/workflows/src/engine/primitives/workflow.ts +8 -0
  96. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +69 -0
  97. package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +31 -0
  98. package/dist/builtin/workflows/src/engine/run.ts +148 -6
  99. package/dist/builtin/workflows/src/engine/runtime.ts +8 -2
  100. package/dist/builtin/workflows/src/extension/config-loader.ts +35 -15
  101. package/dist/builtin/workflows/src/extension/discovery.ts +20 -8
  102. package/dist/builtin/workflows/src/extension/extension-factory.ts +6 -12
  103. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +5 -1
  104. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +4 -2
  105. package/dist/builtin/workflows/src/extension/runtime.ts +48 -9
  106. package/dist/builtin/workflows/src/extension/wiring.ts +1 -1
  107. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +143 -4
  108. package/dist/builtin/workflows/src/runs/background/quit.ts +61 -0
  109. package/dist/builtin/workflows/src/runs/background/status.ts +1 -0
  110. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
  111. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +74 -33
  112. package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +20 -1
  113. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +8 -7
  114. package/dist/builtin/workflows/src/runs/foreground/executor-stage-replay.ts +1 -0
  115. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +1 -1
  116. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +19 -2
  117. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +4 -0
  118. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +10 -10
  119. package/dist/builtin/workflows/src/runs/foreground/stage-runner-options.ts +5 -1
  120. package/dist/builtin/workflows/src/runs/foreground/stage-runner-send-user-message.ts +25 -0
  121. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +3 -0
  122. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +16 -0
  123. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +20 -0
  124. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +23 -1
  125. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +30 -1
  126. package/dist/builtin/workflows/src/shared/store-public-types.ts +6 -2
  127. package/dist/builtin/workflows/src/shared/store-run-methods.ts +12 -6
  128. package/dist/builtin/workflows/src/shared/types.ts +55 -0
  129. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +11 -10
  130. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +1 -1
  131. package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +41 -0
  132. package/dist/builtin/workflows/src/tui/graph-view-input.ts +82 -24
  133. package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -0
  134. package/dist/builtin/workflows/src/tui/graph-view-state.ts +22 -2
  135. package/dist/builtin/workflows/src/tui/graph-view-types.ts +4 -5
  136. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +9 -11
  137. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +9 -3
  138. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +11 -2
  139. package/dist/builtin/workflows/src/tui/stage-chat-view-live-events.ts +35 -0
  140. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +51 -17
  141. package/dist/builtin/workflows/src/tui/stage-chat-view-status.ts +36 -0
  142. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +5 -1
  143. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -1
  144. package/dist/builtin/workflows/src/tui/status-list.ts +14 -2
  145. package/dist/builtin/workflows/src/tui/widget.ts +23 -8
  146. package/dist/builtin/workflows/src/tui/workflow-attach-pane-types.ts +5 -4
  147. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +8 -8
  148. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +151 -0
  149. package/dist/cli/args.d.ts.map +1 -1
  150. package/dist/cli/args.js +9 -9
  151. package/dist/cli/args.js.map +1 -1
  152. package/dist/config-self-update.d.ts.map +1 -1
  153. package/dist/config-self-update.js +3 -4
  154. package/dist/config-self-update.js.map +1 -1
  155. package/dist/config.d.ts.map +1 -1
  156. package/dist/config.js +4 -5
  157. package/dist/config.js.map +1 -1
  158. package/dist/core/agent-session-bash.d.ts +1 -0
  159. package/dist/core/agent-session-bash.d.ts.map +1 -1
  160. package/dist/core/agent-session-bash.js +1 -0
  161. package/dist/core/agent-session-bash.js.map +1 -1
  162. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  163. package/dist/core/agent-session-tool-registry.js +23 -0
  164. package/dist/core/agent-session-tool-registry.js.map +1 -1
  165. package/dist/core/bash-executor.d.ts +2 -0
  166. package/dist/core/bash-executor.d.ts.map +1 -1
  167. package/dist/core/bash-executor.js +1 -0
  168. package/dist/core/bash-executor.js.map +1 -1
  169. package/dist/core/compaction/compaction.d.ts +29 -0
  170. package/dist/core/compaction/compaction.d.ts.map +1 -1
  171. package/dist/core/compaction/compaction.js +36 -1
  172. package/dist/core/compaction/compaction.js.map +1 -1
  173. package/dist/core/compaction/context-compaction-metrics.d.ts +14 -2
  174. package/dist/core/compaction/context-compaction-metrics.d.ts.map +1 -1
  175. package/dist/core/compaction/context-compaction-metrics.js +50 -1
  176. package/dist/core/compaction/context-compaction-metrics.js.map +1 -1
  177. package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
  178. package/dist/core/compaction/context-compaction-prompt.js +2 -0
  179. package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
  180. package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
  181. package/dist/core/compaction/context-compaction-runner.js +1 -1
  182. package/dist/core/compaction/context-compaction-runner.js.map +1 -1
  183. package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
  184. package/dist/core/compaction/context-deletion-application.js +5 -5
  185. package/dist/core/compaction/context-deletion-application.js.map +1 -1
  186. package/dist/core/compaction/context-deletion-targets.d.ts +2 -0
  187. package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
  188. package/dist/core/compaction/context-deletion-targets.js +23 -3
  189. package/dist/core/compaction/context-deletion-targets.js.map +1 -1
  190. package/dist/core/compaction/context-deletion-tool-definitions.d.ts +6 -0
  191. package/dist/core/compaction/context-deletion-tool-definitions.d.ts.map +1 -1
  192. package/dist/core/compaction/context-deletion-tool-definitions.js.map +1 -1
  193. package/dist/core/compaction/context-deletion-tools.d.ts.map +1 -1
  194. package/dist/core/compaction/context-deletion-tools.js +18 -10
  195. package/dist/core/compaction/context-deletion-tools.js.map +1 -1
  196. package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
  197. package/dist/core/compaction/context-transcript-analysis.js +2 -4
  198. package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
  199. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  200. package/dist/core/copilot-gemini-tool-arguments.js +2 -60
  201. package/dist/core/copilot-gemini-tool-arguments.js.map +1 -1
  202. package/dist/core/extensions/context-types.d.ts +2 -0
  203. package/dist/core/extensions/context-types.d.ts.map +1 -1
  204. package/dist/core/extensions/context-types.js.map +1 -1
  205. package/dist/core/extensions/index.d.ts +2 -2
  206. package/dist/core/extensions/index.d.ts.map +1 -1
  207. package/dist/core/extensions/index.js +1 -1
  208. package/dist/core/extensions/index.js.map +1 -1
  209. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  210. package/dist/core/extensions/loader-virtual-modules.js +57 -32
  211. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  212. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  213. package/dist/core/extensions/runner-context.js +11 -0
  214. package/dist/core/extensions/runner-context.js.map +1 -1
  215. package/dist/core/extensions/tool-events.d.ts +13 -13
  216. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  217. package/dist/core/extensions/tool-events.js +3 -3
  218. package/dist/core/extensions/tool-events.js.map +1 -1
  219. package/dist/core/extensions/types.d.ts +1 -1
  220. package/dist/core/extensions/types.d.ts.map +1 -1
  221. package/dist/core/extensions/types.js +1 -1
  222. package/dist/core/extensions/types.js.map +1 -1
  223. package/dist/core/flattened-tool-arguments.d.ts +18 -0
  224. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  225. package/dist/core/flattened-tool-arguments.js +104 -0
  226. package/dist/core/flattened-tool-arguments.js.map +1 -1
  227. package/dist/core/messages.d.ts +1 -0
  228. package/dist/core/messages.d.ts.map +1 -1
  229. package/dist/core/messages.js +46 -1
  230. package/dist/core/messages.js.map +1 -1
  231. package/dist/core/sdk-exports.d.ts +1 -1
  232. package/dist/core/sdk-exports.d.ts.map +1 -1
  233. package/dist/core/sdk-exports.js +1 -1
  234. package/dist/core/sdk-exports.js.map +1 -1
  235. package/dist/core/sdk-types.d.ts +2 -2
  236. package/dist/core/sdk-types.d.ts.map +1 -1
  237. package/dist/core/sdk-types.js.map +1 -1
  238. package/dist/core/sdk.d.ts.map +1 -1
  239. package/dist/core/sdk.js +12 -0
  240. package/dist/core/sdk.js.map +1 -1
  241. package/dist/core/session-manager-core.d.ts +15 -7
  242. package/dist/core/session-manager-core.d.ts.map +1 -1
  243. package/dist/core/session-manager-core.js +20 -9
  244. package/dist/core/session-manager-core.js.map +1 -1
  245. package/dist/core/session-manager-entries.d.ts +2 -2
  246. package/dist/core/session-manager-entries.d.ts.map +1 -1
  247. package/dist/core/session-manager-entries.js +9 -3
  248. package/dist/core/session-manager-entries.js.map +1 -1
  249. package/dist/core/session-manager-history.d.ts.map +1 -1
  250. package/dist/core/session-manager-history.js +2 -1
  251. package/dist/core/session-manager-history.js.map +1 -1
  252. package/dist/core/session-manager-list.d.ts +3 -3
  253. package/dist/core/session-manager-list.d.ts.map +1 -1
  254. package/dist/core/session-manager-list.js +27 -8
  255. package/dist/core/session-manager-list.js.map +1 -1
  256. package/dist/core/session-manager-storage.d.ts +3 -1
  257. package/dist/core/session-manager-storage.d.ts.map +1 -1
  258. package/dist/core/session-manager-storage.js +55 -12
  259. package/dist/core/session-manager-storage.js.map +1 -1
  260. package/dist/core/session-manager-tool-dependencies.d.ts +10 -0
  261. package/dist/core/session-manager-tool-dependencies.d.ts.map +1 -0
  262. package/dist/core/session-manager-tool-dependencies.js +133 -0
  263. package/dist/core/session-manager-tool-dependencies.js.map +1 -0
  264. package/dist/core/session-manager-types.d.ts +22 -0
  265. package/dist/core/session-manager-types.d.ts.map +1 -1
  266. package/dist/core/session-manager-types.js.map +1 -1
  267. package/dist/core/session-manager.d.ts +2 -2
  268. package/dist/core/session-manager.d.ts.map +1 -1
  269. package/dist/core/session-manager.js +1 -1
  270. package/dist/core/session-manager.js.map +1 -1
  271. package/dist/core/settings-manager-basic-accessors.d.ts +4 -0
  272. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  273. package/dist/core/settings-manager-basic-accessors.js +18 -0
  274. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  275. package/dist/core/settings-manager-resource-accessors.d.ts +4 -0
  276. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  277. package/dist/core/settings-manager-resource-accessors.js +15 -0
  278. package/dist/core/settings-manager-resource-accessors.js.map +1 -1
  279. package/dist/core/settings-types.d.ts +11 -0
  280. package/dist/core/settings-types.d.ts.map +1 -1
  281. package/dist/core/settings-types.js.map +1 -1
  282. package/dist/core/system-prompt.d.ts +1 -1
  283. package/dist/core/system-prompt.d.ts.map +1 -1
  284. package/dist/core/system-prompt.js +3 -2
  285. package/dist/core/system-prompt.js.map +1 -1
  286. package/dist/core/tools/artifact-protocol.d.ts +11 -0
  287. package/dist/core/tools/artifact-protocol.d.ts.map +1 -0
  288. package/dist/core/tools/artifact-protocol.js +76 -0
  289. package/dist/core/tools/artifact-protocol.js.map +1 -0
  290. package/dist/core/tools/artifacts.d.ts +18 -0
  291. package/dist/core/tools/artifacts.d.ts.map +1 -0
  292. package/dist/core/tools/artifacts.js +90 -0
  293. package/dist/core/tools/artifacts.js.map +1 -0
  294. package/dist/core/tools/bash-async-jobs.d.ts +20 -0
  295. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -0
  296. package/dist/core/tools/bash-async-jobs.js +59 -0
  297. package/dist/core/tools/bash-async-jobs.js.map +1 -0
  298. package/dist/core/tools/bash-async-output.d.ts +10 -0
  299. package/dist/core/tools/bash-async-output.d.ts.map +1 -0
  300. package/dist/core/tools/bash-async-output.js +80 -0
  301. package/dist/core/tools/bash-async-output.js.map +1 -0
  302. package/dist/core/tools/bash-interceptor.d.ts +10 -0
  303. package/dist/core/tools/bash-interceptor.d.ts.map +1 -0
  304. package/dist/core/tools/bash-interceptor.js +39 -0
  305. package/dist/core/tools/bash-interceptor.js.map +1 -0
  306. package/dist/core/tools/bash-leading-cd.d.ts +7 -0
  307. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -0
  308. package/dist/core/tools/bash-leading-cd.js +59 -0
  309. package/dist/core/tools/bash-leading-cd.js.map +1 -0
  310. package/dist/core/tools/bash-pty-native.d.ts +14 -0
  311. package/dist/core/tools/bash-pty-native.d.ts.map +1 -0
  312. package/dist/core/tools/bash-pty-native.js +71 -0
  313. package/dist/core/tools/bash-pty-native.js.map +1 -0
  314. package/dist/core/tools/bash.d.ts +28 -17
  315. package/dist/core/tools/bash.d.ts.map +1 -1
  316. package/dist/core/tools/bash.js +152 -35
  317. package/dist/core/tools/bash.js.map +1 -1
  318. package/dist/core/tools/block-resolver.d.ts +16 -0
  319. package/dist/core/tools/block-resolver.d.ts.map +1 -0
  320. package/dist/core/tools/block-resolver.js +74 -0
  321. package/dist/core/tools/block-resolver.js.map +1 -0
  322. package/dist/core/tools/conflict-registry.d.ts +16 -0
  323. package/dist/core/tools/conflict-registry.d.ts.map +1 -0
  324. package/dist/core/tools/conflict-registry.js +44 -0
  325. package/dist/core/tools/conflict-registry.js.map +1 -0
  326. package/dist/core/tools/directory-tree.d.ts +13 -0
  327. package/dist/core/tools/directory-tree.d.ts.map +1 -0
  328. package/dist/core/tools/directory-tree.js +81 -0
  329. package/dist/core/tools/directory-tree.js.map +1 -0
  330. package/dist/core/tools/edit.d.ts +4 -29
  331. package/dist/core/tools/edit.d.ts.map +1 -1
  332. package/dist/core/tools/edit.js +136 -228
  333. package/dist/core/tools/edit.js.map +1 -1
  334. package/dist/core/tools/fetch-url.d.ts +74 -0
  335. package/dist/core/tools/fetch-url.d.ts.map +1 -0
  336. package/dist/core/tools/fetch-url.js +518 -0
  337. package/dist/core/tools/fetch-url.js.map +1 -0
  338. package/dist/core/tools/find.d.ts +27 -9
  339. package/dist/core/tools/find.d.ts.map +1 -1
  340. package/dist/core/tools/find.js +400 -176
  341. package/dist/core/tools/find.js.map +1 -1
  342. package/dist/core/tools/glob-path-utils.d.ts +8 -0
  343. package/dist/core/tools/glob-path-utils.d.ts.map +1 -0
  344. package/dist/core/tools/glob-path-utils.js +26 -0
  345. package/dist/core/tools/glob-path-utils.js.map +1 -0
  346. package/dist/core/tools/grep.d.ts +12 -0
  347. package/dist/core/tools/grep.d.ts.map +1 -1
  348. package/dist/core/tools/grep.js +141 -17
  349. package/dist/core/tools/grep.js.map +1 -1
  350. package/dist/core/tools/hashline-engine/apply.d.ts +11 -0
  351. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -0
  352. package/dist/core/tools/hashline-engine/apply.js +752 -0
  353. package/dist/core/tools/hashline-engine/apply.js.map +1 -0
  354. package/dist/core/tools/hashline-engine/block.d.ts +40 -0
  355. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -0
  356. package/dist/core/tools/hashline-engine/block.js +117 -0
  357. package/dist/core/tools/hashline-engine/block.js.map +1 -0
  358. package/dist/core/tools/hashline-engine/diff-preview.d.ts +15 -0
  359. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -0
  360. package/dist/core/tools/hashline-engine/diff-preview.js +98 -0
  361. package/dist/core/tools/hashline-engine/diff-preview.js.map +1 -0
  362. package/dist/core/tools/hashline-engine/format.d.ts +71 -0
  363. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -0
  364. package/dist/core/tools/hashline-engine/format.js +178 -0
  365. package/dist/core/tools/hashline-engine/format.js.map +1 -0
  366. package/dist/core/tools/hashline-engine/fs.d.ts +81 -0
  367. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -0
  368. package/dist/core/tools/hashline-engine/fs.js +143 -0
  369. package/dist/core/tools/hashline-engine/fs.js.map +1 -0
  370. package/dist/core/tools/hashline-engine/index.d.ts +18 -0
  371. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -0
  372. package/dist/core/tools/hashline-engine/index.js +20 -0
  373. package/dist/core/tools/hashline-engine/index.js.map +1 -0
  374. package/dist/core/tools/hashline-engine/input.d.ts +101 -0
  375. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -0
  376. package/dist/core/tools/hashline-engine/input.js +398 -0
  377. package/dist/core/tools/hashline-engine/input.js.map +1 -0
  378. package/dist/core/tools/hashline-engine/messages.d.ts +99 -0
  379. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -0
  380. package/dist/core/tools/hashline-engine/messages.js +144 -0
  381. package/dist/core/tools/hashline-engine/messages.js.map +1 -0
  382. package/dist/core/tools/hashline-engine/mismatch.d.ts +45 -0
  383. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -0
  384. package/dist/core/tools/hashline-engine/mismatch.js +90 -0
  385. package/dist/core/tools/hashline-engine/mismatch.js.map +1 -0
  386. package/dist/core/tools/hashline-engine/normalize.d.ts +21 -0
  387. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -0
  388. package/dist/core/tools/hashline-engine/normalize.js +33 -0
  389. package/dist/core/tools/hashline-engine/normalize.js.map +1 -0
  390. package/dist/core/tools/hashline-engine/parser.d.ts +24 -0
  391. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -0
  392. package/dist/core/tools/hashline-engine/parser.js +381 -0
  393. package/dist/core/tools/hashline-engine/parser.js.map +1 -0
  394. package/dist/core/tools/hashline-engine/patcher.d.ts +118 -0
  395. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -0
  396. package/dist/core/tools/hashline-engine/patcher.js +341 -0
  397. package/dist/core/tools/hashline-engine/patcher.js.map +1 -0
  398. package/dist/core/tools/hashline-engine/prefixes.d.ts +43 -0
  399. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -0
  400. package/dist/core/tools/hashline-engine/prefixes.js +135 -0
  401. package/dist/core/tools/hashline-engine/prefixes.js.map +1 -0
  402. package/dist/core/tools/hashline-engine/recovery.d.ts +41 -0
  403. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -0
  404. package/dist/core/tools/hashline-engine/recovery.js +168 -0
  405. package/dist/core/tools/hashline-engine/recovery.js.map +1 -0
  406. package/dist/core/tools/hashline-engine/snapshots.d.ts +65 -0
  407. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -0
  408. package/dist/core/tools/hashline-engine/snapshots.js +108 -0
  409. package/dist/core/tools/hashline-engine/snapshots.js.map +1 -0
  410. package/dist/core/tools/hashline-engine/stream.d.ts +3 -0
  411. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -0
  412. package/dist/core/tools/hashline-engine/stream.js +111 -0
  413. package/dist/core/tools/hashline-engine/stream.js.map +1 -0
  414. package/dist/core/tools/hashline-engine/tokenizer.d.ts +69 -0
  415. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -0
  416. package/dist/core/tools/hashline-engine/tokenizer.js +430 -0
  417. package/dist/core/tools/hashline-engine/tokenizer.js.map +1 -0
  418. package/dist/core/tools/hashline-engine/types.d.ts +166 -0
  419. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -0
  420. package/dist/core/tools/hashline-engine/types.js +9 -0
  421. package/dist/core/tools/hashline-engine/types.js.map +1 -0
  422. package/dist/core/tools/hashline.d.ts +29 -0
  423. package/dist/core/tools/hashline.d.ts.map +1 -0
  424. package/dist/core/tools/hashline.js +110 -0
  425. package/dist/core/tools/hashline.js.map +1 -0
  426. package/dist/core/tools/index.d.ts +6 -4
  427. package/dist/core/tools/index.d.ts.map +1 -1
  428. package/dist/core/tools/index.js +52 -35
  429. package/dist/core/tools/index.js.map +1 -1
  430. package/dist/core/tools/notebook.d.ts +38 -0
  431. package/dist/core/tools/notebook.d.ts.map +1 -0
  432. package/dist/core/tools/notebook.js +125 -0
  433. package/dist/core/tools/notebook.js.map +1 -0
  434. package/dist/core/tools/read-document-extract.d.ts +9 -0
  435. package/dist/core/tools/read-document-extract.d.ts.map +1 -0
  436. package/dist/core/tools/read-document-extract.js +212 -0
  437. package/dist/core/tools/read-document-extract.js.map +1 -0
  438. package/dist/core/tools/read-selectors.d.ts +24 -0
  439. package/dist/core/tools/read-selectors.d.ts.map +1 -0
  440. package/dist/core/tools/read-selectors.js +277 -0
  441. package/dist/core/tools/read-selectors.js.map +1 -0
  442. package/dist/core/tools/read-url.d.ts +37 -0
  443. package/dist/core/tools/read-url.d.ts.map +1 -0
  444. package/dist/core/tools/read-url.js +39 -0
  445. package/dist/core/tools/read-url.js.map +1 -0
  446. package/dist/core/tools/read.d.ts +11 -11
  447. package/dist/core/tools/read.d.ts.map +1 -1
  448. package/dist/core/tools/read.js +224 -94
  449. package/dist/core/tools/read.js.map +1 -1
  450. package/dist/core/tools/resource-selectors.d.ts +44 -0
  451. package/dist/core/tools/resource-selectors.d.ts.map +1 -0
  452. package/dist/core/tools/resource-selectors.js +808 -0
  453. package/dist/core/tools/resource-selectors.js.map +1 -0
  454. package/dist/core/tools/search-details.d.ts +26 -0
  455. package/dist/core/tools/search-details.d.ts.map +1 -0
  456. package/dist/core/tools/search-details.js +24 -0
  457. package/dist/core/tools/search-details.js.map +1 -0
  458. package/dist/core/tools/search-line-ranges.d.ts +11 -0
  459. package/dist/core/tools/search-line-ranges.d.ts.map +1 -0
  460. package/dist/core/tools/search-line-ranges.js +65 -0
  461. package/dist/core/tools/search-line-ranges.js.map +1 -0
  462. package/dist/core/tools/search-native.d.ts +97 -0
  463. package/dist/core/tools/search-native.d.ts.map +1 -0
  464. package/dist/core/tools/search-native.js +27 -0
  465. package/dist/core/tools/search-native.js.map +1 -0
  466. package/dist/core/tools/search.d.ts +24 -0
  467. package/dist/core/tools/search.d.ts.map +1 -0
  468. package/dist/core/tools/search.js +573 -0
  469. package/dist/core/tools/search.js.map +1 -0
  470. package/dist/core/tools/truncate.d.ts +4 -4
  471. package/dist/core/tools/truncate.d.ts.map +1 -1
  472. package/dist/core/tools/truncate.js +3 -3
  473. package/dist/core/tools/truncate.js.map +1 -1
  474. package/dist/core/tools/url-ip-guards.d.ts +4 -0
  475. package/dist/core/tools/url-ip-guards.d.ts.map +1 -0
  476. package/dist/core/tools/url-ip-guards.js +126 -0
  477. package/dist/core/tools/url-ip-guards.js.map +1 -0
  478. package/dist/core/tools/write.d.ts +12 -2
  479. package/dist/core/tools/write.d.ts.map +1 -1
  480. package/dist/core/tools/write.js +166 -14
  481. package/dist/core/tools/write.js.map +1 -1
  482. package/dist/core/trust-manager.d.ts.map +1 -1
  483. package/dist/core/trust-manager.js +2 -3
  484. package/dist/core/trust-manager.js.map +1 -1
  485. package/dist/index-extensions.d.ts +2 -2
  486. package/dist/index-extensions.d.ts.map +1 -1
  487. package/dist/index-extensions.js +1 -1
  488. package/dist/index-extensions.js.map +1 -1
  489. package/dist/index.d.ts +3 -3
  490. package/dist/index.d.ts.map +1 -1
  491. package/dist/index.js +3 -3
  492. package/dist/index.js.map +1 -1
  493. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts +1 -0
  494. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  495. package/dist/modes/interactive/components/chat-session-host-runtime.js +12 -0
  496. package/dist/modes/interactive/components/chat-session-host-runtime.js.map +1 -1
  497. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts +4 -0
  498. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -0
  499. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js +131 -0
  500. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.js.map +1 -0
  501. package/dist/modes/interactive/components/chat-session-host.d.ts +2 -0
  502. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  503. package/dist/modes/interactive/components/chat-session-host.js +7 -1
  504. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  505. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  506. package/dist/modes/interactive/components/chat-transcript.js +15 -4
  507. package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
  508. package/dist/modes/interactive/components/custom-editor.d.ts +1 -0
  509. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  510. package/dist/modes/interactive/components/custom-editor.js +9 -2
  511. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  512. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  513. package/dist/modes/interactive/components/settings-selector-handlers.js +3 -0
  514. package/dist/modes/interactive/components/settings-selector-handlers.js.map +1 -1
  515. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  516. package/dist/modes/interactive/components/settings-selector-items.js +7 -0
  517. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  518. package/dist/modes/interactive/components/settings-selector-types.d.ts +2 -0
  519. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  520. package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
  521. package/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  522. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  523. package/dist/modes/interactive/components/tool-execution.js +26 -0
  524. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  525. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  526. package/dist/modes/interactive/components/tree-selector-content.js +0 -5
  527. package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
  528. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  529. package/dist/modes/interactive/interactive-auth-login.js +1 -0
  530. package/dist/modes/interactive/interactive-auth-login.js.map +1 -1
  531. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  532. package/dist/modes/interactive/interactive-autocomplete.js +80 -2
  533. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  534. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  535. package/dist/modes/interactive/interactive-hotkeys-debug.js +3 -0
  536. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  537. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  538. package/dist/modes/interactive/interactive-input-handling.js +51 -0
  539. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  540. package/dist/modes/interactive/interactive-mode-base.d.ts +5 -0
  541. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  542. package/dist/modes/interactive/interactive-mode-base.js +5 -0
  543. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  544. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  545. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  546. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  547. package/dist/modes/interactive/interactive-mode-surface.d.ts +12 -0
  548. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  549. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  550. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  551. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  552. package/dist/modes/interactive/interactive-mode.js +1 -0
  553. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  554. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  555. package/dist/modes/interactive/interactive-model-routing.js +4 -1
  556. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  557. package/dist/modes/interactive/interactive-onboarding.d.ts +11 -0
  558. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -0
  559. package/dist/modes/interactive/interactive-onboarding.js +220 -0
  560. package/dist/modes/interactive/interactive-onboarding.js.map +1 -0
  561. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  562. package/dist/modes/interactive/interactive-selectors.js +4 -0
  563. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  564. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  565. package/dist/modes/interactive/interactive-session-routing.js +6 -0
  566. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  567. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  568. package/dist/modes/interactive/interactive-slash-commands.js +9 -4
  569. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  570. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  571. package/dist/modes/interactive/interactive-startup.js +28 -0
  572. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  573. package/dist/utils/child-process.d.ts.map +1 -1
  574. package/dist/utils/child-process.js +21 -1
  575. package/dist/utils/child-process.js.map +1 -1
  576. package/dist/utils/markit.d.ts +8 -0
  577. package/dist/utils/markit.d.ts.map +1 -0
  578. package/dist/utils/markit.js +53 -0
  579. package/dist/utils/markit.js.map +1 -0
  580. package/dist/utils/paths.d.ts +2 -1
  581. package/dist/utils/paths.d.ts.map +1 -1
  582. package/dist/utils/paths.js +14 -1
  583. package/dist/utils/paths.js.map +1 -1
  584. package/docs/compaction.md +18 -1
  585. package/docs/containerization.md +1 -1
  586. package/docs/docs.json +1 -0
  587. package/docs/extensions.md +25 -36
  588. package/docs/models.md +1 -1
  589. package/docs/providers.md +2 -1
  590. package/docs/quickstart.md +11 -6
  591. package/docs/sdk.md +5 -5
  592. package/docs/session-format.md +6 -0
  593. package/docs/sessions.md +6 -0
  594. package/docs/settings.md +7 -0
  595. package/docs/subagents.md +3 -2
  596. package/docs/tools.md +49 -0
  597. package/docs/usage.md +3 -3
  598. package/docs/workflows.md +112 -8
  599. package/examples/extensions/subagent/README.md +5 -5
  600. package/examples/extensions/subagent/agents/planner.md +1 -1
  601. package/examples/extensions/subagent/agents/reviewer.md +1 -1
  602. package/examples/extensions/subagent/agents/scout.md +2 -2
  603. package/examples/extensions/subagent/display.ts +3 -3
  604. package/examples/sdk/05-tools.ts +3 -3
  605. package/examples/sdk/README.md +1 -1
  606. package/package.json +5 -3
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Cross-session resume catalog.
3
+ *
4
+ * Builds the list of resumable workflows for the `/workflow resume` selector by
5
+ * scanning session JSONL files for `workflow.durable.checkpoint` entries. This
6
+ * is the session-file cache described by the issue — it lets a new session
7
+ * discover workflows started in prior sessions without requiring a live DBOS
8
+ * system database connection.
9
+ *
10
+ * The catalog reads session files lazily and caches results per scan. DBOS
11
+ * remains the checkpoint source of truth; this catalog provides the discovery
12
+ * index for the selector UI.
13
+ *
14
+ * cross-ref: issue #1498 — "/workflow resume should show a selector for
15
+ * resumable workflow sessions, analogous to the /resume session selector."
16
+ */
17
+
18
+ import { readFileSync, existsSync, readdirSync } from "node:fs";
19
+ import { join } from "node:path";
20
+ import type { DurableCheckpointEntry, DurableWorkflowStatus, ResumableWorkflowEntry } from "./types.js";
21
+ import type { DurableWorkflowBackend } from "./backend.js";
22
+ import type { WorkflowSerializableValue } from "../shared/types.js";
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Session file scanning
26
+ // ---------------------------------------------------------------------------
27
+
28
+ /**
29
+ * Scan a session directory for `workflow.durable.checkpoint` entries and build
30
+ * a list of resumable workflows.
31
+ *
32
+ * @param sessionDir Directory containing session JSONL files.
33
+ * @returns Resumable workflow entries, most recently updated first.
34
+ */
35
+ export function scanResumableWorkflows(sessionDir: string): readonly ResumableWorkflowEntry[] {
36
+ if (!existsSync(sessionDir)) return [];
37
+ const entries = new Map<string, ResumableWorkflowEntry>();
38
+ let files: string[];
39
+ try {
40
+ files = readdirSync(sessionDir).filter((f) => f.endsWith(".jsonl"));
41
+ } catch {
42
+ return [];
43
+ }
44
+ for (const file of files) {
45
+ const filePath = join(sessionDir, file);
46
+ const fileEntries = readDurableEntriesFromFile(filePath);
47
+ const sessionFile = filePath;
48
+ for (const entry of fileEntries) {
49
+ const existing = entries.get(entry.workflowId);
50
+ if (!existing || entry.ts > existing.updatedAt) {
51
+ entries.set(entry.workflowId, entryToResumable(entry, sessionFile));
52
+ }
53
+ }
54
+ }
55
+ return [...entries.values()].filter(isResumableEntry).sort((a, b) => b.updatedAt - a.updatedAt);
56
+ }
57
+
58
+ function readDurableEntriesFromFile(filePath: string): readonly DurableCheckpointEntry[] {
59
+ let content: string;
60
+ try {
61
+ content = readFileSync(filePath, "utf-8");
62
+ } catch {
63
+ return [];
64
+ }
65
+ const results: DurableCheckpointEntry[] = [];
66
+ for (const line of content.split("\n")) {
67
+ const trimmed = line.trim();
68
+ if (!trimmed) continue;
69
+ try {
70
+ const parsed = JSON.parse(trimmed) as Record<string, unknown>;
71
+ const rawEntry = durablePayloadFromJsonlEntry(parsed);
72
+ if (rawEntry !== undefined) {
73
+ const entry = parseDurableEntry(rawEntry);
74
+ if (entry) results.push(entry);
75
+ }
76
+ } catch {
77
+ // Skip malformed lines.
78
+ }
79
+ }
80
+ return results;
81
+ }
82
+
83
+ function durablePayloadFromJsonlEntry(parsed: Record<string, unknown>): Record<string, unknown> | undefined {
84
+ if (parsed["type"] === "workflow.durable.checkpoint") return parsed;
85
+ if (parsed["type"] !== "custom" || parsed["customType"] !== "workflow.durable.checkpoint") return undefined;
86
+ const data = parsed["data"];
87
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return undefined;
88
+ return data as Record<string, unknown>;
89
+ }
90
+
91
+ function parseDurableEntry(raw: Record<string, unknown>): DurableCheckpointEntry | undefined {
92
+ const workflowId = raw["workflowId"];
93
+ const name = raw["name"];
94
+ const inputs = raw["inputs"];
95
+ const status = raw["status"];
96
+ const ts = raw["ts"];
97
+ if (typeof workflowId !== "string" || typeof name !== "string" || typeof status !== "string" || typeof ts !== "number") return undefined;
98
+ if (!isWorkflowSerializableObject(inputs)) return undefined;
99
+ return {
100
+ type: "workflow.durable.checkpoint",
101
+ workflowId,
102
+ name,
103
+ inputs,
104
+ status: status as DurableWorkflowStatus,
105
+ completedCheckpoints: typeof raw["completedCheckpoints"] === "number" ? raw["completedCheckpoints"] : 0,
106
+ pendingPrompts: typeof raw["pendingPrompts"] === "number" ? raw["pendingPrompts"] : 0,
107
+ ...(typeof raw["label"] === "string" ? { label: raw["label"] } : {}),
108
+ ...(typeof raw["rootWorkflowId"] === "string" ? { rootWorkflowId: raw["rootWorkflowId"] } : {}),
109
+ ...(typeof raw["resumable"] === "boolean" ? { resumable: raw["resumable"] } : {}),
110
+ ts,
111
+ };
112
+ }
113
+
114
+ function isWorkflowSerializableObject(value: unknown): value is Readonly<Record<string, WorkflowSerializableValue>> {
115
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
116
+ const obj = value as Record<string, unknown>;
117
+ for (const key of Object.keys(obj)) {
118
+ if (!isSerializableValue(obj[key])) return false;
119
+ }
120
+ return true;
121
+ }
122
+
123
+ function isSerializableValue(value: unknown): value is WorkflowSerializableValue {
124
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
125
+ if (Array.isArray(value)) return value.every(isSerializableValue);
126
+ if (typeof value === "object") {
127
+ const obj = value as Record<string, unknown>;
128
+ return Object.keys(obj).every((k) => isSerializableValue(obj[k]));
129
+ }
130
+ return false;
131
+ }
132
+
133
+ function entryToResumable(entry: DurableCheckpointEntry, sessionFile: string): ResumableWorkflowEntry {
134
+ return {
135
+ workflowId: entry.workflowId,
136
+ name: entry.name,
137
+ inputs: entry.inputs,
138
+ status: entry.status,
139
+ completedCheckpoints: entry.completedCheckpoints,
140
+ pendingPrompts: entry.pendingPrompts,
141
+ sessionFile,
142
+ ...(entry.label !== undefined ? { label: entry.label } : {}),
143
+ ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
144
+ ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
145
+ createdAt: entry.ts,
146
+ updatedAt: entry.ts,
147
+ };
148
+ }
149
+
150
+ function isResumableStatus(status: DurableWorkflowStatus): boolean {
151
+ // `running`/`paused` are both resumable at the catalog level. A `running`
152
+ // durable handle may be a crashed process (cross-session crash recovery);
153
+ // same-session double-resume is filtered out by the command layer.
154
+ return status === "running" || status === "paused";
155
+ }
156
+ function hasResumeProgress(entry: ResumableWorkflowEntry): boolean {
157
+ return entry.completedCheckpoints > 0 || entry.pendingPrompts > 0;
158
+ }
159
+
160
+
161
+ function isResumableEntry(entry: ResumableWorkflowEntry): boolean {
162
+ const isRoot = entry.rootWorkflowId === undefined || entry.rootWorkflowId === entry.workflowId;
163
+ if (!isRoot) return false;
164
+ if (entry.status === "failed" || entry.status === "blocked") return entry.resumable !== false;
165
+ return isResumableStatus(entry.status) && hasResumeProgress(entry);
166
+ }
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // Backend-backed catalog
170
+ // ---------------------------------------------------------------------------
171
+
172
+ /**
173
+ * List resumable workflows from a durable backend (in-memory or file-backed).
174
+ * Used when the session file scan is not available (e.g. same-process resume).
175
+ */
176
+ export function listResumableFromBackend(backend: DurableWorkflowBackend): readonly ResumableWorkflowEntry[] {
177
+ return backend.listResumableWorkflows();
178
+ }
179
+
180
+ /**
181
+ * Append a durable checkpoint entry to a session JSONL persistence port.
182
+ * This caches the top-level workflow metadata so a future session can discover
183
+ * it via {@link scanResumableWorkflows}.
184
+ */
185
+ export function persistDurableCacheEntry(
186
+ persistence: { appendEntry?: (type: string, payload: Record<string, unknown>) => string | undefined },
187
+ entry: DurableCheckpointEntry,
188
+ ): void {
189
+ if (typeof persistence.appendEntry !== "function") return;
190
+ persistence.appendEntry("workflow.durable.checkpoint", {
191
+ workflowId: entry.workflowId,
192
+ name: entry.name,
193
+ inputs: entry.inputs as Record<string, unknown>,
194
+ status: entry.status,
195
+ completedCheckpoints: entry.completedCheckpoints,
196
+ pendingPrompts: entry.pendingPrompts,
197
+ ...(entry.label !== undefined ? { label: entry.label } : {}),
198
+ ...(entry.rootWorkflowId !== undefined ? { rootWorkflowId: entry.rootWorkflowId } : {}),
199
+ ...(entry.resumable !== undefined ? { resumable: entry.resumable } : {}),
200
+ ts: entry.ts,
201
+ });
202
+ }
203
+
204
+ /**
205
+ * Format the resumable workflow list for display in the selector.
206
+ */
207
+ export function formatResumableWorkflowList(entries: readonly ResumableWorkflowEntry[]): string {
208
+ if (entries.length === 0) return "No resumable workflows found.";
209
+ const lines = entries.map((e, i) => {
210
+ const id = e.workflowId.slice(0, 8);
211
+ const status = e.status.padEnd(8);
212
+ const checkpoints = `${e.completedCheckpoints} checkpoint${e.completedCheckpoints === 1 ? "" : "s"}`;
213
+ const label = e.label ? ` "${e.label}"` : "";
214
+ return ` ${i + 1}. ${id} ${status} ${e.name}${label} (${checkpoints})`;
215
+ });
216
+ return `Resumable workflows:\n${lines.join("\n")}`;
217
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Cross-session durable workflow resume adapter.
3
+ *
4
+ * Resumes a workflow whose durable checkpoints live in the durable backend
5
+ * (and are mirrored to the session JSONL cache) but whose in-process run is no
6
+ * longer live. This is the production path behind `/workflow resume <id>` when
7
+ * the id names a durable workflow that is not present in the live run store.
8
+ *
9
+ * Resume semantics (DBOS-aligned):
10
+ * 1. Look up the durable catalog entry (workflow name + cached inputs).
11
+ * 2. Resolve the workflow definition from the registry.
12
+ * 3. Re-dispatch the workflow as a new background run, reusing the ORIGINAL
13
+ * top-level workflow id as the run id. Because durable checkpoints are
14
+ * keyed by workflow id, every `ctx.tool` / `ctx.ui` / `ctx.stage` call
15
+ * inside the resumed run returns its cached result instead of re-executing
16
+ * — completed side effects are not repeated, exactly like DBOS replay.
17
+ *
18
+ * The adapter deliberately re-dispatches through `runDetached` rather than
19
+ * reconstructing an in-memory snapshot, so it works across processes and
20
+ * sessions without a live store entry.
21
+ *
22
+ * cross-ref: issue #1498 — "/workflow resume connects/attempts resume by
23
+ * top-level workflow id."
24
+ */
25
+
26
+ import type { WorkflowInputValues } from "../shared/types.js";
27
+ import type { WorkflowRegistry } from "../workflows/registry.js";
28
+ import type { RunOpts } from "../runs/foreground/executor-types.js";
29
+ import { runDetached, type DetachedAccepted } from "../runs/background/runner.js";
30
+ import { resolveAndValidateInputs } from "../runs/foreground/executor-inputs.js";
31
+ import { getDurableBackend } from "./factory.js";
32
+ import type { DurableWorkflowBackend } from "./backend.js";
33
+ import type { ResumableWorkflowEntry } from "./types.js";
34
+ import { workflowDefinitionRequirementMessage } from "../runs/foreground/executor-child-helpers.js";
35
+ import { isWorkflowDefinition } from "../runs/foreground/executor-child-helpers.js";
36
+ import type { RunSnapshot } from "../shared/store-types.js";
37
+
38
+ export type ResumeDurableResult =
39
+ | { ok: true; runId: string; workflowId: string; name: string; message: string }
40
+ | { ok: false; reason: "workflow_not_found" | "not_resumable" | "invalid_inputs" | "not_registered" | "stale"; message: string };
41
+
42
+ export interface ResumeDurableDeps {
43
+ readonly registry: WorkflowRegistry;
44
+ /** Base run options forwarded to the detached runner (store, persistence, …). */
45
+ readonly baseRunOpts: RunOpts;
46
+ /** Durable backend override (defaults to the global singleton). */
47
+ readonly durableBackend?: DurableWorkflowBackend;
48
+ }
49
+
50
+ /**
51
+ * Prepare a durable resume: hydrate the backend's in-memory mirror from the
52
+ * persistent store (DBOS) so synchronous reads in {@link resumeDurableWorkflow}
53
+ * find the workflow and its checkpoints. No-op for backends without hydration.
54
+ *
55
+ * Must be awaited before calling {@link resumeDurableWorkflow} when the backend
56
+ * might be a fresh DBOS process.
57
+ */
58
+ export async function prepareDurableResume(
59
+ workflowIdOrPrefix: string | undefined,
60
+ deps: ResumeDurableDeps,
61
+ ): Promise<readonly ResumableWorkflowEntry[]> {
62
+ const backend = deps.durableBackend ?? getDurableBackend();
63
+ // Hydrate all resumable workflows first so the catalog is complete.
64
+ if (backend.hydrateResumableWorkflows !== undefined) {
65
+ await backend.hydrateResumableWorkflows();
66
+ }
67
+ const catalog = backend.listResumableWorkflows();
68
+ // If a specific target was requested, hydrate that workflow too (it might
69
+ // be resumable but not yet in the resumable filter — e.g. recently failed).
70
+ if (workflowIdOrPrefix !== undefined) {
71
+ const resolved = resolveDurableEntry(workflowIdOrPrefix, catalog);
72
+ if (resolved !== undefined && !("kind" in resolved)) {
73
+ if (backend.hydrateWorkflow !== undefined) {
74
+ await backend.hydrateWorkflow(resolved.workflowId);
75
+ }
76
+ }
77
+ }
78
+ return backend.listResumableWorkflows();
79
+ }
80
+
81
+ /**
82
+ * Resolve a durable catalog entry for a workflow id (full or prefix match).
83
+ * Prefers the durable backend's resumable list; falls back to an explicit
84
+ * session-scan catalog when provided by the caller.
85
+ */
86
+ export function resolveDurableEntry(
87
+ workflowIdOrPrefix: string,
88
+ catalog: readonly ResumableWorkflowEntry[],
89
+ ): ResumableWorkflowEntry | { kind: "ambiguous"; matches: readonly ResumableWorkflowEntry[] } | undefined {
90
+ const exact = catalog.find((entry) => entry.workflowId === workflowIdOrPrefix);
91
+ if (exact !== undefined) return exact;
92
+ const prefixMatches = catalog.filter((entry) => entry.workflowId.startsWith(workflowIdOrPrefix));
93
+ if (prefixMatches.length === 0) return undefined;
94
+ if (prefixMatches.length === 1) return prefixMatches[0];
95
+ return { kind: "ambiguous", matches: prefixMatches };
96
+ }
97
+
98
+ /**
99
+ * Resume a durable workflow by top-level workflow id. Re-dispatches the workflow
100
+ * with the cached inputs and the original workflow id so durable checkpoints
101
+ * replay (skipping completed side effects).
102
+ */
103
+ export function resumeDurableWorkflow(
104
+ workflowIdOrPrefix: string,
105
+ deps: ResumeDurableDeps,
106
+ catalog?: readonly ResumableWorkflowEntry[],
107
+ ): ResumeDurableResult {
108
+ const backend = deps.durableBackend ?? getDurableBackend();
109
+ const resolvedCatalog = catalog ?? backend.listResumableWorkflows();
110
+ const resolved = resolveDurableEntry(workflowIdOrPrefix, resolvedCatalog);
111
+ if (resolved === undefined) {
112
+ // Not in the (filtered) resumable catalog. It may still be a workflow the
113
+ // backend knows about. Only surface "already running" when there is a live,
114
+ // actively-executing run for it in this session; otherwise a `running`
115
+ // durable handle is a crashed process and falls through to not-found.
116
+ const direct = backend.getWorkflow(workflowIdOrPrefix);
117
+ if (direct !== undefined && direct.status === "running" && hasActiveLiveRun(deps.baseRunOpts.store, direct.workflowId)) {
118
+ return alreadyRunningResult(direct.name, direct.workflowId, deps.baseRunOpts.store);
119
+ }
120
+ return { ok: false, reason: "not_registered", message: `No durable workflow found for id/prefix: ${workflowIdOrPrefix}` };
121
+ }
122
+ if ("kind" in resolved) {
123
+ return {
124
+ ok: false,
125
+ reason: "not_registered",
126
+ message: `Ambiguous workflow prefix "${workflowIdOrPrefix}" matches: ${resolved.matches.map((m) => `${m.name} (${m.workflowId.slice(0, 8)})`).join(", ")}`,
127
+ };
128
+ }
129
+ // Authoritative backend-handle check: a cache-only entry (no handle) is
130
+ // "stale" regardless of its cached status. A `running` handle is only
131
+ // refused when there is a live, actively-executing run for it in THIS
132
+ // session — otherwise it is a crashed process and cross-session crash
133
+ // recovery should proceed.
134
+ const handle = backend.getWorkflow(resolved.workflowId);
135
+ if (handle === undefined) {
136
+ return {
137
+ ok: false,
138
+ reason: "stale",
139
+ message: `Workflow ${resolved.workflowId.slice(0, 8)} has only session-cache metadata and no durable checkpoint state; resume would re-run from scratch. Re-run the workflow to start fresh.`,
140
+ };
141
+ }
142
+ if (handle.status === "running" && hasActiveLiveRun(deps.baseRunOpts.store, resolved.workflowId)) {
143
+ return alreadyRunningResult(resolved.name, resolved.workflowId, deps.baseRunOpts.store);
144
+ }
145
+ if (!isResumableEntry(resolved)) {
146
+ return { ok: false, reason: "not_resumable", message: `Workflow ${resolved.workflowId.slice(0, 8)} is ${resolved.status}, not resumable.` };
147
+ }
148
+
149
+ const def = deps.registry.get(resolved.name);
150
+ if (def === undefined) {
151
+ return { ok: false, reason: "workflow_not_found", message: `Workflow definition not found: ${resolved.name}` };
152
+ }
153
+ if (!isWorkflowDefinition(def)) {
154
+ return { ok: false, reason: "workflow_not_found", message: workflowDefinitionRequirementMessage("resumeDurableWorkflow", def) };
155
+ }
156
+
157
+ const inputs: Record<string, unknown> = { ...handle.inputs };
158
+ try {
159
+ resolveAndValidateInputs(def.inputs, inputs as WorkflowInputValues, `workflow "${def.name}"`);
160
+ } catch (err) {
161
+ return { ok: false, reason: "invalid_inputs", message: `invalid_inputs: ${err instanceof Error ? err.message : String(err)}` };
162
+ }
163
+ removeDurableResumeShadowRuns(deps.baseRunOpts.store, resolved.workflowId);
164
+
165
+
166
+ // Mark the workflow as resuming in the backend, then re-dispatch with the
167
+ // ORIGINAL workflow id as the run id so durable checkpoints replay.
168
+ backend.setWorkflowStatus(resolved.workflowId, "running");
169
+
170
+ const accepted: DetachedAccepted = runDetached(def, inputs, {
171
+ ...deps.baseRunOpts,
172
+ runId: resolved.workflowId,
173
+ durableBackend: backend,
174
+ });
175
+
176
+ return {
177
+ ok: true,
178
+ runId: accepted.runId,
179
+ workflowId: resolved.workflowId,
180
+ name: resolved.name,
181
+ message: `Resuming durable workflow "${resolved.name}" (${resolved.workflowId.slice(0, 8)}) — completed checkpoints will be replayed.`,
182
+ };
183
+ }
184
+
185
+ function isDurableResumeShadow(run: RunSnapshot): boolean {
186
+ return run.endedAt !== undefined || run.exitReason === "quit" || run.status === "paused";
187
+ }
188
+
189
+ function removeDurableResumeShadowRuns(store: RunOpts["store"], workflowId: string): void {
190
+ if (store === undefined) return;
191
+ for (;;) {
192
+ const existing = store.runs().find((run) => run.id === workflowId);
193
+ if (existing === undefined || !isDurableResumeShadow(existing)) return;
194
+ if (!store.removeRun(workflowId)) return;
195
+ }
196
+ }
197
+
198
+ function alreadyRunningResult(name: string, workflowId: string, store: RunOpts["store"]): ResumeDurableResult {
199
+ const here = store?.runs().some((r) => r.id === workflowId && r.endedAt === undefined) === true;
200
+ return {
201
+ ok: false,
202
+ reason: "not_resumable",
203
+ message: `Workflow "${name}" (${workflowId.slice(0, 8)}) is already running${
204
+ here ? " in this session" : " in another session"
205
+ }. Attach with \`/workflow connect ${workflowId.slice(0, 8)}\`, or if that session has ended, clear it with \`/workflow kill ${workflowId.slice(0, 8)}\` and re-run.`,
206
+ };
207
+ }
208
+
209
+ function hasResumeProgress(entry: ResumableWorkflowEntry): boolean {
210
+ return entry.completedCheckpoints > 0 || entry.pendingPrompts > 0;
211
+ }
212
+
213
+ function isResumableEntry(entry: ResumableWorkflowEntry): boolean {
214
+ const isRoot = entry.rootWorkflowId === undefined || entry.rootWorkflowId === entry.workflowId;
215
+ if (!isRoot) return false;
216
+ if (entry.status === "failed" || entry.status === "blocked") return entry.resumable !== false;
217
+ // `running` is resumable at this layer: a `running` durable handle may be a
218
+ // crashed process. Same-session double-resume is blocked separately via
219
+ // `hasActiveLiveRun` before dispatch.
220
+ return (entry.status === "running" || entry.status === "paused") && hasResumeProgress(entry);
221
+ }
222
+
223
+ /**
224
+ * True when the live run store has an actively-executing (not ended, not quit)
225
+ * run for `workflowId`. This is the only reliable signal that a durable
226
+ * `running` handle is genuinely live in THIS process — distinguishing a real
227
+ * double-resume from cross-session crash recovery.
228
+ */
229
+ function hasActiveLiveRun(store: RunOpts["store"] | undefined, workflowId: string): boolean {
230
+ if (store === undefined) return false;
231
+ return store.runs().some((r) => r.id === workflowId && r.endedAt === undefined && r.exitReason !== "quit");
232
+ }
233
+
234
+ /**
235
+ * Check whether the durable backend records a TERMINAL (non-resumable) status
236
+ * for the given workflow id. Terminal status suppresses stale session-cache
237
+ * entries so a completed/cancelled workflow is not resurrected as resumable.
238
+ *
239
+ * Returns true only when the backend has a registered handle whose status is
240
+ * definitively terminal (completed, cancelled, or failed-and-non-resumable).
241
+ */
242
+ export function isBackendTerminal(backend: DurableWorkflowBackend, workflowId: string): boolean {
243
+ const handle = backend.getWorkflow(workflowId);
244
+ if (handle === undefined) return false;
245
+ const status = handle.status;
246
+ if (status === "completed" || status === "cancelled") return true;
247
+ if (status === "failed" || status === "blocked") return handle.resumable === false;
248
+ return false;
249
+ }
250
+
251
+ function hasBackendResumeState(backend: DurableWorkflowBackend, workflowId: string): boolean {
252
+ return backend.getWorkflow(workflowId) !== undefined;
253
+ }
254
+
255
+ /**
256
+ * Runtime-facing async preparation: hydrate the durable backend from DBOS
257
+ * (when supported) then list resumable workflows with optional session-dir
258
+ * scan merge. Used by the ExtensionRuntime's `prepareDurableResumable`.
259
+ *
260
+ * Stale-cache suppression: when a session JSONL cache entry has no matching
261
+ * durable backend handle, it came from an older/non-checkpointed workflow
262
+ * engine and is hidden from selectors. When the backend knows a workflow is
263
+ * terminal (completed/cancelled/non-resumable), stale cache rows for that id
264
+ * are also suppressed. cross-ref: issue #1498.
265
+ */
266
+ export async function prepareRuntimeDurableResumable(
267
+ getBackend: () => DurableWorkflowBackend,
268
+ resolveSessionDir: () => string | undefined,
269
+ workflowIdOrPrefix?: string,
270
+ sessionDir?: string,
271
+ ): Promise<readonly ResumableWorkflowEntry[]> {
272
+ const backend = getBackend();
273
+ if (backend.hydrateResumableWorkflows !== undefined) {
274
+ await backend.hydrateResumableWorkflows();
275
+ }
276
+ if (workflowIdOrPrefix !== undefined && backend.hydrateWorkflow !== undefined) {
277
+ const catalog = backend.listResumableWorkflows();
278
+ const resolved = resolveDurableEntry(workflowIdOrPrefix, catalog);
279
+ if (resolved !== undefined && !("kind" in resolved)) {
280
+ await backend.hydrateWorkflow(resolved.workflowId);
281
+ }
282
+ }
283
+ const live = backend.listResumableWorkflows();
284
+ const effectiveSessionDir = sessionDir ?? resolveSessionDir();
285
+ if (effectiveSessionDir === undefined) return live;
286
+ const { scanResumableWorkflows } = await import("./resume-catalog.js");
287
+ const scanned = scanResumableWorkflows(effectiveSessionDir);
288
+ const liveIds = new Set(live.map((e) => e.workflowId));
289
+ // Suppress cache-only entries from older workflow engines. Without a
290
+ // durable backend handle/checkpoint state, selecting the row can only fail
291
+ // as stale (or risk re-running from scratch), so it should not clutter the
292
+ // resume selector.
293
+ const suppressed = scanned.filter((e) =>
294
+ !liveIds.has(e.workflowId) &&
295
+ hasBackendResumeState(backend, e.workflowId) &&
296
+ !isBackendTerminal(backend, e.workflowId)
297
+ );
298
+ return [...live, ...suppressed];
299
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Scoped durable backend for child workflow runs.
3
+ *
4
+ * A child workflow launched via `ctx.workflow(child)` runs with its own run id,
5
+ * but its internal `ctx.tool` / `ctx.ui` / `ctx.stage` side effects must be
6
+ * checkpointed under the PARENT (root) durable workflow so that an interrupted
7
+ * child does not re-execute completed side effects when the parent is resumed.
8
+ *
9
+ * Without scoping, child checkpoints are written under a fresh per-run UUID
10
+ * that is never recovered on resume, so a re-dispatched child loses all of its
11
+ * prior checkpoints and re-runs side effects (split-brain). {@link ScopedDurableBackend}
12
+ * remaps every checkpoint identity to the root workflow id, prefixed by a stable
13
+ * child boundary key, so the same side effects are recovered on resume.
14
+ *
15
+ * Only checkpoint read/write methods are scoped. Lifecycle methods
16
+ * (`registerWorkflow`, `setWorkflowStatus`, `listResumableWorkflows`,
17
+ * `toCacheEntry`, `getWorkflow`) are no-ops for scoped children because child
18
+ * runs are never independently resumable — only the root workflow is resumed.
19
+ *
20
+ * cross-ref: issue #1498 — child side effects under the root durable workflow.
21
+ */
22
+
23
+ import type { DurableCheckpoint, DurableWorkflowStatus, ResumableWorkflowEntry } from "./types.js";
24
+ import type { WorkflowSerializableValue } from "../shared/types.js";
25
+ import type { DurableWorkflowBackend, WorkflowRegistrationInput } from "./backend.js";
26
+
27
+ /**
28
+ * Durable scope for a child workflow run.
29
+ *
30
+ * - `rootWorkflowId`: the top-level workflow id under which checkpoints persist.
31
+ * - `scopePrefix`: a stable boundary key (e.g. `workflow:<name>:<ordinal>`)
32
+ * unique within the root so multiple children (and the root itself) do not
33
+ * collide.
34
+ */
35
+ export interface DurableScope {
36
+ readonly rootWorkflowId: string;
37
+ readonly scopePrefix: string;
38
+ }
39
+
40
+ /**
41
+ * Wrap a durable backend so all checkpoint identities for a child run are
42
+ * namespaced under the root workflow. The wrapped backend is the source of
43
+ * truth; this wrapper only translates keys.
44
+ */
45
+ export class ScopedDurableBackend implements DurableWorkflowBackend {
46
+ public readonly persistent: boolean;
47
+ private readonly inner: DurableWorkflowBackend;
48
+ private readonly scope: DurableScope;
49
+
50
+ constructor(inner: DurableWorkflowBackend, scope: DurableScope) {
51
+ this.inner = inner;
52
+ this.scope = scope;
53
+ this.persistent = inner.persistent;
54
+ }
55
+
56
+ registerWorkflow(_handle: WorkflowRegistrationInput): void {
57
+ // Child runs are not independently resumable; only the root is registered.
58
+ }
59
+
60
+ recordCheckpoint(checkpoint: DurableCheckpoint): void {
61
+ this.inner.recordCheckpoint(this.remap(checkpoint));
62
+ }
63
+
64
+ async recordCheckpointAsync(checkpoint: DurableCheckpoint): Promise<void> {
65
+ if (this.inner.recordCheckpointAsync !== undefined) {
66
+ await this.inner.recordCheckpointAsync(this.remap(checkpoint));
67
+ return;
68
+ }
69
+ this.inner.recordCheckpoint(this.remap(checkpoint));
70
+ await this.inner.flush?.();
71
+ }
72
+
73
+ flush(): Promise<void> {
74
+ return this.inner.flush?.() ?? Promise.resolve();
75
+ }
76
+
77
+ getToolOutput(_workflowId: string, argsHash: string): WorkflowSerializableValue | undefined {
78
+ return this.inner.getToolOutput(this.scope.rootWorkflowId, this.scopeKey(argsHash));
79
+ }
80
+
81
+ getUiResponse(_workflowId: string, promptHash: string): WorkflowSerializableValue | undefined {
82
+ return this.inner.getUiResponse(this.scope.rootWorkflowId, this.scopeKey(promptHash));
83
+ }
84
+
85
+ getStageOutput(_workflowId: string, replayKey: string): WorkflowSerializableValue | undefined {
86
+ return this.inner.getStageOutput(this.scope.rootWorkflowId, this.scopeKey(replayKey));
87
+ }
88
+
89
+ getStageSession(_workflowId: string, replayKey: string): { sessionId?: string; sessionFile?: string } | undefined {
90
+ return this.inner.getStageSession(this.scope.rootWorkflowId, this.scopeKey(replayKey));
91
+ }
92
+
93
+ listCheckpoints(_workflowId: string): readonly DurableCheckpoint[] {
94
+ const all = this.inner.listCheckpoints(this.scope.rootWorkflowId);
95
+ const prefix = `${this.scope.scopePrefix}:`;
96
+ // Checkpoints are stored with their scope prefix already embedded in their
97
+ // ids (see remap()). Filter by the stored id directly — NOT by re-prefixing
98
+ // — so sibling scopes (e.g. "workflow:child:1") are excluded when the
99
+ // current scope is "workflow:child:2". Re-prefixing would prepend the
100
+ // current scope prefix to every id, causing sibling ids to falsely match.
101
+ return all.filter((cp) => storedScopeId(cp).startsWith(prefix));
102
+ }
103
+
104
+ getWorkflow(_workflowId: string): undefined {
105
+ // Child runs have no independent resumable handle.
106
+ return undefined;
107
+ }
108
+
109
+ setWorkflowStatus(_workflowId: string, _status: DurableWorkflowStatus, _pendingPrompts?: number, _resumable?: boolean): void {
110
+ // No-op: child status is reflected via the root workflow boundary.
111
+ }
112
+
113
+ listResumableWorkflows(): readonly ResumableWorkflowEntry[] {
114
+ return [];
115
+ }
116
+
117
+ toCacheEntry(_workflowId: string): undefined {
118
+ return undefined;
119
+ }
120
+
121
+ reset(): void {
122
+ // No-op: scoped backends never own root state.
123
+ }
124
+
125
+ hydrateWorkflow(_workflowId: string): Promise<void> {
126
+ return Promise.resolve();
127
+ }
128
+
129
+ hydrateResumableWorkflows(): Promise<void> {
130
+ return Promise.resolve();
131
+ }
132
+
133
+ private scopeKey(key: string): string {
134
+ return `${this.scope.scopePrefix}:${key}`;
135
+ }
136
+
137
+ private remap(checkpoint: DurableCheckpoint): DurableCheckpoint {
138
+ const workflowId = this.scope.rootWorkflowId;
139
+ if (checkpoint.kind === "tool") {
140
+ return {
141
+ ...checkpoint,
142
+ workflowId,
143
+ checkpointId: this.scopeKey(checkpoint.checkpointId),
144
+ argsHash: this.scopeKey(checkpoint.argsHash),
145
+ };
146
+ }
147
+ if (checkpoint.kind === "ui") {
148
+ return {
149
+ ...checkpoint,
150
+ workflowId,
151
+ checkpointId: this.scopeKey(checkpoint.checkpointId),
152
+ promptHash: this.scopeKey(checkpoint.promptHash),
153
+ };
154
+ }
155
+ return {
156
+ ...checkpoint,
157
+ workflowId,
158
+ checkpointId: this.scopeKey(checkpoint.checkpointId),
159
+ replayKey: this.scopeKey(checkpoint.replayKey),
160
+ };
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Return the checkpoint's stored scope-qualified id, which includes any scope
166
+ * prefix embedded by {@link ScopedDurableBackend.remap}. This is the raw stored
167
+ * value used for prefix filtering in {@link ScopedDurableBackend.listCheckpoints}.
168
+ */
169
+ function storedScopeId(cp: DurableCheckpoint): string {
170
+ return cp.kind === "stage" ? cp.replayKey : cp.checkpointId;
171
+ }