@bastani/atomic 0.9.11-alpha.9 → 0.9.11

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 (285) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +1 -1
  3. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  4. package/dist/builtin/intercom/package.json +1 -1
  5. package/dist/builtin/intercom/subagent-relay.ts +8 -5
  6. package/dist/builtin/mcp/CHANGELOG.md +10 -0
  7. package/dist/builtin/mcp/package.json +1 -1
  8. package/dist/builtin/subagents/CHANGELOG.md +20 -0
  9. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  10. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  11. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  12. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
  13. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  14. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  15. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  16. package/dist/builtin/subagents/agents/worker.md +2 -2
  17. package/dist/builtin/subagents/package.json +1 -1
  18. package/dist/builtin/subagents/src/extension/notification-content.ts +14 -9
  19. package/dist/builtin/subagents/src/runs/background/notify.ts +20 -4
  20. package/dist/builtin/web-access/CHANGELOG.md +20 -0
  21. package/dist/builtin/web-access/content-tools.ts +1 -0
  22. package/dist/builtin/web-access/flat-string.ts +37 -0
  23. package/dist/builtin/web-access/github-api.ts +2 -1
  24. package/dist/builtin/web-access/github-extract.ts +3 -2
  25. package/dist/builtin/web-access/package.json +2 -2
  26. package/dist/builtin/workflows/CHANGELOG.md +57 -0
  27. package/dist/builtin/workflows/builtin/goal-ledger.ts +6 -4
  28. package/dist/builtin/workflows/builtin/goal-runner.ts +2 -1
  29. package/dist/builtin/workflows/builtin/goal.ts +1 -1
  30. package/dist/builtin/workflows/builtin/ralph-core.ts +8 -9
  31. package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +2 -4
  32. package/dist/builtin/workflows/builtin/ralph-models.ts +4 -4
  33. package/dist/builtin/workflows/builtin/ralph-runner.ts +5 -8
  34. package/dist/builtin/workflows/builtin/ralph.ts +2 -1
  35. package/dist/builtin/workflows/package.json +1 -1
  36. package/dist/builtin/workflows/src/durable/completed-catalog.ts +14 -8
  37. package/dist/builtin/workflows/src/durable/completed-inspection.ts +10 -11
  38. package/dist/builtin/workflows/src/durable/resume-catalog.ts +1 -1
  39. package/dist/builtin/workflows/src/durable/resume-eligibility.ts +42 -0
  40. package/dist/builtin/workflows/src/durable/resume-runtime.ts +36 -37
  41. package/dist/builtin/workflows/src/durable/tool-primitive.ts +10 -2
  42. package/dist/builtin/workflows/src/engine/run.ts +1 -0
  43. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +12 -8
  44. package/dist/builtin/workflows/src/extension/hil-answer-notifications.ts +5 -2
  45. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1966 -880
  46. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +6 -3
  47. package/dist/builtin/workflows/src/extension/postmortem-deps.ts +3 -2
  48. package/dist/builtin/workflows/src/extension/render-result.ts +4 -11
  49. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +17 -20
  50. package/dist/builtin/workflows/src/extension/runtime.ts +3 -3
  51. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +1 -1
  52. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +3 -7
  53. package/dist/builtin/workflows/src/extension/workflow-durable-resume-command.ts +21 -24
  54. package/dist/builtin/workflows/src/extension/workflow-resume-picker-rows.ts +38 -26
  55. package/dist/builtin/workflows/src/extension/workflow-resume-shadow.ts +2 -1
  56. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +56 -64
  57. package/dist/builtin/workflows/src/extension/workflow-schema.ts +2 -2
  58. package/dist/builtin/workflows/src/extension/workflow-status-summary.ts +0 -5
  59. package/dist/builtin/workflows/src/extension/workflow-targets.ts +17 -20
  60. package/dist/builtin/workflows/src/extension/workflow-tool-content.ts +1 -1
  61. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +24 -51
  62. package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +7 -29
  63. package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +5 -13
  64. package/dist/builtin/workflows/src/extension/workflow-tool.ts +4 -13
  65. package/dist/builtin/workflows/src/runs/background/quit.ts +4 -3
  66. package/dist/builtin/workflows/src/runs/background/run-inspect.ts +8 -8
  67. package/dist/builtin/workflows/src/runs/background/status.ts +2 -1
  68. package/dist/builtin/workflows/src/runs/background/workflow-lifecycle-aggregate.ts +2 -1
  69. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +5 -5
  70. package/dist/builtin/workflows/src/runs/foreground/executor-lifecycle.ts +4 -0
  71. package/dist/builtin/workflows/src/runs/foreground/executor-scheduler.ts +2 -0
  72. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +23 -8
  73. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +6 -2
  74. package/dist/builtin/workflows/src/runs/foreground/executor-task-prompts.ts +5 -0
  75. package/dist/builtin/workflows/src/runs/foreground/stage-runner-context.ts +56 -10
  76. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +6 -0
  77. package/dist/builtin/workflows/src/runs/foreground/stage-runner-output.ts +137 -6
  78. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +2 -0
  79. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +2 -0
  80. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +159 -10
  81. package/dist/builtin/workflows/src/shared/flat-string.ts +33 -0
  82. package/dist/builtin/workflows/src/shared/graph-store-snapshot.ts +144 -0
  83. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +2 -3
  84. package/dist/builtin/workflows/src/shared/run-id.ts +36 -0
  85. package/dist/builtin/workflows/src/shared/store-internal.ts +32 -3
  86. package/dist/builtin/workflows/src/shared/store-observation.ts +10 -0
  87. package/dist/builtin/workflows/src/shared/store-public-types.ts +8 -0
  88. package/dist/builtin/workflows/src/shared/store-run-methods.ts +14 -0
  89. package/dist/builtin/workflows/src/shared/store-types.ts +4 -0
  90. package/dist/builtin/workflows/src/shared/types.ts +2 -0
  91. package/dist/builtin/workflows/src/shared/workflow-artifacts.ts +261 -0
  92. package/dist/builtin/workflows/src/tui/dispatch-confirm.ts +39 -23
  93. package/dist/builtin/workflows/src/tui/graph-canvas.ts +26 -9
  94. package/dist/builtin/workflows/src/tui/graph-view-constants.ts +3 -0
  95. package/dist/builtin/workflows/src/tui/graph-view-graph-render.ts +95 -98
  96. package/dist/builtin/workflows/src/tui/graph-view-render.ts +7 -20
  97. package/dist/builtin/workflows/src/tui/graph-view-state.ts +160 -13
  98. package/dist/builtin/workflows/src/tui/layout.ts +8 -2
  99. package/dist/builtin/workflows/src/tui/node-card.ts +57 -45
  100. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +4 -2
  101. package/dist/builtin/workflows/src/tui/prompt-card-render.ts +291 -21
  102. package/dist/builtin/workflows/src/tui/prompt-card.ts +1 -1
  103. package/dist/builtin/workflows/src/tui/renderers.ts +0 -1
  104. package/dist/builtin/workflows/src/tui/run-detail.ts +52 -35
  105. package/dist/builtin/workflows/src/tui/run-identity-rows.ts +97 -0
  106. package/dist/builtin/workflows/src/tui/session-overlays.ts +24 -3
  107. package/dist/builtin/workflows/src/tui/session-picker.ts +94 -41
  108. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +190 -26
  109. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +52 -18
  110. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +9 -5
  111. package/dist/builtin/workflows/src/tui/stage-chat-view-state.ts +44 -4
  112. package/dist/builtin/workflows/src/tui/stage-chat-view-types.ts +3 -1
  113. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +3 -2
  114. package/dist/builtin/workflows/src/tui/status-list.ts +67 -45
  115. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +7 -6
  116. package/dist/builtin/workflows/src/tui/widget.ts +49 -40
  117. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +15 -10
  118. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +21 -8
  119. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  120. package/dist/core/agent-session-accessors.js +5 -0
  121. package/dist/core/agent-session-accessors.js.map +1 -1
  122. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  123. package/dist/core/agent-session-auto-compaction.js +5 -0
  124. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  125. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  126. package/dist/core/agent-session-compaction.js +3 -0
  127. package/dist/core/agent-session-compaction.js.map +1 -1
  128. package/dist/core/agent-session-events.d.ts.map +1 -1
  129. package/dist/core/agent-session-events.js +2 -1
  130. package/dist/core/agent-session-events.js.map +1 -1
  131. package/dist/core/agent-session-message-queue.d.ts +3 -7
  132. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  133. package/dist/core/agent-session-message-queue.js +27 -13
  134. package/dist/core/agent-session-message-queue.js.map +1 -1
  135. package/dist/core/agent-session-methods.d.ts +5 -3
  136. package/dist/core/agent-session-methods.d.ts.map +1 -1
  137. package/dist/core/agent-session-methods.js.map +1 -1
  138. package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
  139. package/dist/core/agent-session-persistent-custom-messages.js +6 -3
  140. package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
  141. package/dist/core/agent-session-post-tool-compaction.d.ts.map +1 -1
  142. package/dist/core/agent-session-post-tool-compaction.js +11 -2
  143. package/dist/core/agent-session-post-tool-compaction.js.map +1 -1
  144. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  145. package/dist/core/agent-session-runtime.js +5 -3
  146. package/dist/core/agent-session-runtime.js.map +1 -1
  147. package/dist/core/agent-session-tree.d.ts.map +1 -1
  148. package/dist/core/agent-session-tree.js +4 -0
  149. package/dist/core/agent-session-tree.js.map +1 -1
  150. package/dist/core/agent-session-types.d.ts +4 -0
  151. package/dist/core/agent-session-types.d.ts.map +1 -1
  152. package/dist/core/agent-session-types.js.map +1 -1
  153. package/dist/core/agent-session.d.ts +2 -1
  154. package/dist/core/agent-session.d.ts.map +1 -1
  155. package/dist/core/agent-session.js +1 -0
  156. package/dist/core/agent-session.js.map +1 -1
  157. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  158. package/dist/core/auth-storage-backends.js +12 -1
  159. package/dist/core/auth-storage-backends.js.map +1 -1
  160. package/dist/core/bounded-model-refresh.d.ts +14 -0
  161. package/dist/core/bounded-model-refresh.d.ts.map +1 -0
  162. package/dist/core/bounded-model-refresh.js +38 -0
  163. package/dist/core/bounded-model-refresh.js.map +1 -0
  164. package/dist/core/context-tool-pairing.d.ts +4 -4
  165. package/dist/core/context-tool-pairing.js +4 -4
  166. package/dist/core/context-tool-pairing.js.map +1 -1
  167. package/dist/core/http-dispatcher.d.ts.map +1 -1
  168. package/dist/core/http-dispatcher.js +26 -1
  169. package/dist/core/http-dispatcher.js.map +1 -1
  170. package/dist/core/messages.d.ts +18 -6
  171. package/dist/core/messages.d.ts.map +1 -1
  172. package/dist/core/messages.js +46 -18
  173. package/dist/core/messages.js.map +1 -1
  174. package/dist/core/model-refresh-timeout.d.ts +9 -0
  175. package/dist/core/model-refresh-timeout.d.ts.map +1 -0
  176. package/dist/core/model-refresh-timeout.js +9 -0
  177. package/dist/core/model-refresh-timeout.js.map +1 -0
  178. package/dist/core/model-runtime-snapshot.d.ts +3 -0
  179. package/dist/core/model-runtime-snapshot.d.ts.map +1 -1
  180. package/dist/core/model-runtime-snapshot.js +59 -0
  181. package/dist/core/model-runtime-snapshot.js.map +1 -1
  182. package/dist/core/model-runtime.d.ts +7 -1
  183. package/dist/core/model-runtime.d.ts.map +1 -1
  184. package/dist/core/model-runtime.js +72 -17
  185. package/dist/core/model-runtime.js.map +1 -1
  186. package/dist/core/sdk.d.ts.map +1 -1
  187. package/dist/core/sdk.js +4 -3
  188. package/dist/core/sdk.js.map +1 -1
  189. package/dist/core/session-manager-core.d.ts +1 -1
  190. package/dist/core/session-manager-core.d.ts.map +1 -1
  191. package/dist/core/session-manager-core.js +2 -2
  192. package/dist/core/session-manager-core.js.map +1 -1
  193. package/dist/core/session-manager-entries.d.ts +1 -1
  194. package/dist/core/session-manager-entries.d.ts.map +1 -1
  195. package/dist/core/session-manager-entries.js +2 -1
  196. package/dist/core/session-manager-entries.js.map +1 -1
  197. package/dist/core/session-manager-history.d.ts.map +1 -1
  198. package/dist/core/session-manager-history.js +2 -2
  199. package/dist/core/session-manager-history.js.map +1 -1
  200. package/dist/core/session-manager-types.d.ts +3 -0
  201. package/dist/core/session-manager-types.d.ts.map +1 -1
  202. package/dist/core/session-manager-types.js.map +1 -1
  203. package/dist/index.d.ts +1 -1
  204. package/dist/index.d.ts.map +1 -1
  205. package/dist/index.js.map +1 -1
  206. package/dist/main-app-mode.d.ts +1 -0
  207. package/dist/main-app-mode.d.ts.map +1 -1
  208. package/dist/main-app-mode.js +3 -0
  209. package/dist/main-app-mode.js.map +1 -1
  210. package/dist/main.d.ts.map +1 -1
  211. package/dist/main.js +8 -3
  212. package/dist/main.js.map +1 -1
  213. package/dist/modes/interactive/components/chat-session-host-actions.d.ts +7 -2
  214. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  215. package/dist/modes/interactive/components/chat-session-host-actions.js +13 -4
  216. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  217. package/dist/modes/interactive/components/chat-session-host-editor.d.ts +3 -1
  218. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  219. package/dist/modes/interactive/components/chat-session-host-editor.js +2 -2
  220. package/dist/modes/interactive/components/chat-session-host-editor.js.map +1 -1
  221. package/dist/modes/interactive/components/chat-session-host-events.d.ts +3 -0
  222. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  223. package/dist/modes/interactive/components/chat-session-host-events.js +5 -4
  224. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  225. package/dist/modes/interactive/components/chat-session-host.d.ts +16 -2
  226. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  227. package/dist/modes/interactive/components/chat-session-host.js +35 -5
  228. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  229. package/dist/modes/interactive/components/index.d.ts +1 -0
  230. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  231. package/dist/modes/interactive/components/index.js.map +1 -1
  232. package/dist/modes/interactive/components/model-selector.d.ts +0 -1
  233. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  234. package/dist/modes/interactive/components/model-selector.js +25 -35
  235. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  236. package/dist/modes/interactive/interactive-editor-actions.js +2 -1
  237. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  238. package/dist/modes/interactive/interactive-input-handling.js +1 -1
  239. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  240. package/dist/modes/interactive/interactive-mode-surface.d.ts +4 -1
  241. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  242. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  243. package/dist/modes/interactive/interactive-model-routing.js +4 -1
  244. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  245. package/dist/modes/interactive/interactive-pause.d.ts +3 -1
  246. package/dist/modes/interactive/interactive-pause.d.ts.map +1 -1
  247. package/dist/modes/interactive/interactive-pause.js +5 -1
  248. package/dist/modes/interactive/interactive-pause.js.map +1 -1
  249. package/dist/modes/interactive/interactive-queueing.js +3 -3
  250. package/dist/modes/interactive/interactive-queueing.js.map +1 -1
  251. package/dist/modes/interactive-engine/engine-health.d.ts +6 -0
  252. package/dist/modes/interactive-engine/engine-health.d.ts.map +1 -1
  253. package/dist/modes/interactive-engine/engine-health.js +15 -1
  254. package/dist/modes/interactive-engine/engine-health.js.map +1 -1
  255. package/dist/modes/interactive-engine/isolated-auth.js +1 -1
  256. package/dist/modes/interactive-engine/isolated-auth.js.map +1 -1
  257. package/dist/modes/interactive-engine/isolated-runtime.d.ts +1 -0
  258. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  259. package/dist/modes/interactive-engine/isolated-runtime.js +7 -1
  260. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  261. package/dist/modes/interactive-engine/remote-model-catalog.d.ts.map +1 -1
  262. package/dist/modes/interactive-engine/remote-model-catalog.js +5 -1
  263. package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
  264. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  265. package/dist/modes/rpc/rpc-command-handler.js +23 -7
  266. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  267. package/dist/modes/rpc/rpc-input-scheduler.js +1 -1
  268. package/dist/modes/rpc/rpc-input-scheduler.js.map +1 -1
  269. package/dist/modes/rpc/rpc-types.d.ts +2 -1
  270. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  271. package/dist/modes/rpc/rpc-types.js.map +1 -1
  272. package/docs/custom-provider.md +3 -3
  273. package/docs/extensions.md +3 -1
  274. package/docs/index.md +10 -0
  275. package/docs/keybindings.md +1 -1
  276. package/docs/models.md +2 -0
  277. package/docs/providers.md +7 -3
  278. package/docs/quickstart.md +7 -1
  279. package/docs/session-format.md +2 -0
  280. package/docs/termux.md +3 -1
  281. package/docs/usage.md +3 -2
  282. package/docs/workflows.md +82 -46
  283. package/npm-shrinkwrap.json +59 -27
  284. package/package.json +3 -3
  285. package/dist/builtin/workflows/src/tui/toast.ts +0 -107
@@ -7,6 +7,7 @@ import {
7
7
  } from "../shared/returned-run-status.js";
8
8
  import { isTopLevelWorkflowRun } from "../shared/run-visibility.js";
9
9
  import type { Store } from "../shared/store.js";
10
+ import { readGraphStoreSnapshot, subscribeStoreInvalidation } from "../shared/store-observation.js";
10
11
  import type {
11
12
  PendingPrompt,
12
13
  PromptKind,
@@ -175,7 +176,9 @@ export function installWorkflowLifecycleNotifications(options: WorkflowLifecycle
175
176
  const notifyOn = new Set<WorkflowLifecycleNoticeKind>(options.config.notifyOn);
176
177
  const state = options.state ?? createWorkflowLifecycleNotificationState();
177
178
  let delivery!: ReturnType<typeof createLifecycleNoticeDelivery>;
178
- if (options.seedExisting !== false) seedWorkflowLifecycleNotificationState(state, options.store.snapshot());
179
+ if (options.seedExisting !== false) {
180
+ seedWorkflowLifecycleNotificationState(state, readGraphStoreSnapshot(options.store));
181
+ }
179
182
 
180
183
  const emit = (details: WorkflowLifecycleNoticeDetails): boolean | Promise<boolean> => {
181
184
  try {
@@ -266,8 +269,8 @@ export function installWorkflowLifecycleNotifications(options: WorkflowLifecycle
266
269
  if (notifyOn.has(details.kind)) delivery.deliver(key, details);
267
270
  }
268
271
 
269
- const unsubscribe = options.store.subscribe(inspect);
270
- inspect(options.store.snapshot());
272
+ const unsubscribe = subscribeStoreInvalidation(options.store, () => inspect(readGraphStoreSnapshot(options.store)));
273
+ inspect(readGraphStoreSnapshot(options.store));
271
274
  return () => {
272
275
  unsubscribe();
273
276
  delivery.dispose();
@@ -22,6 +22,7 @@ import {
22
22
  import { stageControlRegistry } from "../runs/foreground/stage-control-registry.js";
23
23
  import type { StageAdapters } from "../runs/foreground/stage-runner.js";
24
24
  import { store } from "../shared/store.js";
25
+ import { readGraphStoreSnapshot } from "../shared/store-observation.js";
25
26
 
26
27
  export interface PostMortemResolverDeps {
27
28
  readonly adapters: StageAdapters;
@@ -33,7 +34,7 @@ function resolveStageCwd(runId: string): string | undefined {
33
34
  try {
34
35
  const backend = getDurableBackend();
35
36
  const owningHandle = backend.getWorkflow(runId);
36
- const run = store.snapshot().runs.find((candidate) => candidate.id === runId);
37
+ const run = readGraphStoreSnapshot(store).runs.find((candidate) => candidate.id === runId);
37
38
  const rootRunId = run?.rootRunId ?? owningHandle?.rootWorkflowId;
38
39
  const cwdHandle = rootRunId === undefined ? owningHandle : (backend.getWorkflow(rootRunId) ?? owningHandle);
39
40
  return cwdHandle?.workflowCwd ?? cwdHandle?.invocationCwd ?? undefined;
@@ -61,7 +62,7 @@ export function createPostMortemHandleResolver(
61
62
  deps: PostMortemResolverDeps,
62
63
  ): (runId: string, stageId: string) => EnsurePostMortemStageHandleResult | undefined {
63
64
  return (runId, stageId) => {
64
- const run = store.snapshot().runs.find((candidate) => candidate.id === runId);
65
+ const run = readGraphStoreSnapshot(store).runs.find((candidate) => candidate.id === runId);
65
66
  const stage = run?.stages.find((candidate) => candidate.id === stageId);
66
67
  if (stage === undefined) return undefined;
67
68
  return ensurePostMortemStageHandle(runId, stage, postMortemDepsForRun(runId, deps));
@@ -442,9 +442,7 @@ export function renderResult(result: WorkflowToolResult, opts?: RenderResultOpts
442
442
  const r = result as StageListResult;
443
443
  if (r.error) return renderNotice("WORKFLOW STAGES", `${r.runId || "(none)"}: ${r.error}`, opts, themed);
444
444
  const counts = r.stages
445
- .map(
446
- (s) => `${s.name} (${s.id.slice(0, 12)}): ${s.status}${s.skippedReason ? ` — ${s.skippedReason}` : ""}`,
447
- )
445
+ .map((s) => `${s.name} (${s.id}): ${s.status}${s.skippedReason ? ` — ${s.skippedReason}` : ""}`)
448
446
  .join("; ");
449
447
  return renderNotice("WORKFLOW STAGES", `${r.runId}: ${r.filter} — ${counts || "no stages"}`, opts, themed);
450
448
  }
@@ -456,7 +454,7 @@ export function renderResult(result: WorkflowToolResult, opts?: RenderResultOpts
456
454
  const extra = r.stage.error ? ` — ${r.stage.error}` : r.stage.result ? ` — ${r.stage.result}` : "";
457
455
  return renderNotice(
458
456
  "WORKFLOW STAGE",
459
- `${r.runId}: ${r.stage.name} (${r.stage.id.slice(0, 12)}) ${r.stage.status}${extra}`,
457
+ `${r.runId}: ${r.stage.name} (${r.stage.id}) ${r.stage.status}${extra}`,
460
458
  opts,
461
459
  themed,
462
460
  );
@@ -468,7 +466,7 @@ export function renderResult(result: WorkflowToolResult, opts?: RenderResultOpts
468
466
  const suffix = r.truncated ? " (truncated)" : "";
469
467
  return renderNotice(
470
468
  "WORKFLOW TRANSCRIPT",
471
- `${r.runId}/${r.stageId.slice(0, 12)} ${r.source}: ${text}${suffix}`,
469
+ `${r.runId}/${r.stageId} ${r.source}: ${text}${suffix}`,
472
470
  opts,
473
471
  themed,
474
472
  );
@@ -476,12 +474,7 @@ export function renderResult(result: WorkflowToolResult, opts?: RenderResultOpts
476
474
 
477
475
  case "send": {
478
476
  const r = result as SendResult;
479
- return renderNotice(
480
- "WORKFLOW SEND",
481
- `${r.runId}/${r.stageId.slice(0, 12)} ${r.delivery}: ${r.message}`,
482
- opts,
483
- themed,
484
- );
477
+ return renderNotice("WORKFLOW SEND", `${r.runId}/${r.stageId} ${r.delivery}: ${r.message}`, opts, themed);
485
478
  }
486
479
 
487
480
  case "pause": {
@@ -25,17 +25,17 @@ import { discoverWorkflows } from "./discovery.js";
25
25
 
26
26
  export interface DurableResumeRuntime {
27
27
  resumeDurableWorkflow(
28
- workflowIdOrPrefix: string,
28
+ workflowId: string,
29
29
  options?: { readonly policy?: WorkflowExecutionPolicy },
30
30
  ): Promise<ResumeDurableResult>;
31
31
  listDurableResumable(): readonly ResumableWorkflowEntry[];
32
- prepareDurableResumable(workflowIdOrPrefix?: string): Promise<readonly ResumableWorkflowEntry[]>;
32
+ prepareDurableResumable(workflowId?: string): Promise<readonly ResumableWorkflowEntry[]>;
33
33
  prepareDurableCatalog?(): Promise<DurableWorkflowCatalogEntries>;
34
34
  /** Hydrate a bounded set of known DBOS workflow ids. */
35
35
  prepareDurableResumableForIds?(workflowIds: readonly string[]): Promise<readonly ResumableWorkflowEntry[]>;
36
36
  prepareCompletedDurable?(): Promise<readonly ResumableWorkflowEntry[]>;
37
37
  openCompletedDurableWorkflow?(
38
- workflowIdOrPrefix: string,
38
+ workflowId: string,
39
39
  catalog?: readonly ResumableWorkflowEntry[],
40
40
  ): OpenCompletedDurableResult;
41
41
  }
@@ -60,18 +60,18 @@ export function createDurableResumeRuntime(deps: DurableResumeRuntimeDeps): Dura
60
60
  const ids = deps.store
61
61
  .runs()
62
62
  .map((run) => run.id)
63
- .filter((id) => target === undefined || id === target || id.startsWith(target));
63
+ .filter((id) => target === undefined || id === target);
64
64
  for (const id of ids) await backend.hydrateWorkflow(id);
65
65
  };
66
66
  let preparedCatalog: readonly ResumableWorkflowEntry[] = [];
67
67
  return {
68
- async resumeDurableWorkflow(workflowIdOrPrefix, options): Promise<ResumeDurableResult> {
68
+ async resumeDurableWorkflow(workflowId, options): Promise<ResumeDurableResult> {
69
69
  await deps.ensureReady();
70
70
  const backend = getDurableBackend();
71
71
  if (preparedCatalog.length === 0) {
72
- preparedCatalog = await prepareRuntimeDurableResumable(() => backend, workflowIdOrPrefix);
72
+ preparedCatalog = await prepareRuntimeDurableResumable(() => backend, workflowId);
73
73
  }
74
- const resolved = resolveCatalogEntry(workflowIdOrPrefix, preparedCatalog);
74
+ const resolved = resolveCatalogEntry(workflowId, preparedCatalog);
75
75
  if (resolved !== undefined) await backend.hydrateWorkflow(resolved.workflowId);
76
76
  const adapterDeps: ResumeDurableDeps = {
77
77
  registry: deps.registry,
@@ -81,17 +81,17 @@ export function createDurableResumeRuntime(deps: DurableResumeRuntimeDeps): Dura
81
81
  (await discoverWorkflows({ cwd: cwd ?? deps.runtimeCwd })).registry.get(name),
82
82
  ...(deps.jobs !== undefined ? { jobs: deps.jobs } : {}),
83
83
  };
84
- return await resumeDurableWorkflowAdapter(workflowIdOrPrefix, adapterDeps, preparedCatalog);
84
+ return await resumeDurableWorkflowAdapter(workflowId, adapterDeps, preparedCatalog);
85
85
  },
86
86
  listDurableResumable(): readonly ResumableWorkflowEntry[] {
87
87
  return getDurableBackend().listResumableWorkflows();
88
88
  },
89
- async prepareDurableResumable(workflowIdOrPrefix) {
89
+ async prepareDurableResumable(workflowId) {
90
90
  await deps.ensureReady();
91
91
  const backend = getDurableBackend();
92
92
  try {
93
- await hydrateStoredWorkflowCandidates(backend, workflowIdOrPrefix);
94
- preparedCatalog = await prepareRuntimeDurableResumable(() => backend, workflowIdOrPrefix);
93
+ await hydrateStoredWorkflowCandidates(backend, workflowId);
94
+ preparedCatalog = await prepareRuntimeDurableResumable(() => backend, workflowId);
95
95
  return preparedCatalog;
96
96
  } finally {
97
97
  purgeSuppressedWorkflowRuns(backend, deps.store);
@@ -131,12 +131,12 @@ export function createDurableResumeRuntime(deps: DurableResumeRuntimeDeps): Dura
131
131
  purgeSuppressedWorkflowRuns(backend, deps.store);
132
132
  }
133
133
  },
134
- openCompletedDurableWorkflow(workflowIdOrPrefix, catalog) {
134
+ openCompletedDurableWorkflow(workflowId, catalog) {
135
135
  const backend = getDurableBackend();
136
- const entry = resolveCatalogEntry(workflowIdOrPrefix, catalog ?? []);
137
- const handle = backend.getWorkflow(entry?.workflowId ?? workflowIdOrPrefix);
136
+ const entry = resolveCatalogEntry(workflowId, catalog ?? []);
137
+ const handle = backend.getWorkflow(entry?.workflowId ?? workflowId);
138
138
  return openCompletedSnapshot(
139
- workflowIdOrPrefix,
139
+ workflowId,
140
140
  {
141
141
  durableBackend: backend,
142
142
  store: deps.store,
@@ -152,11 +152,8 @@ export function createDurableResumeRuntime(deps: DurableResumeRuntimeDeps): Dura
152
152
  }
153
153
 
154
154
  function resolveCatalogEntry(
155
- workflowIdOrPrefix: string,
155
+ workflowId: string,
156
156
  catalog: readonly ResumableWorkflowEntry[],
157
157
  ): ResumableWorkflowEntry | undefined {
158
- const exact = catalog.find((entry) => entry.workflowId === workflowIdOrPrefix);
159
- if (exact !== undefined) return exact;
160
- const matches = catalog.filter((entry) => entry.workflowId.startsWith(workflowIdOrPrefix));
161
- return matches.length === 1 ? matches[0] : undefined;
158
+ return catalog.find((entry) => entry.workflowId === workflowId);
162
159
  }
@@ -172,11 +172,11 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
172
172
  }
173
173
 
174
174
  function matchesResumeStageIdentifier(stage: RunSnapshot["stages"][number], identifier: string): boolean {
175
- return stage.id === identifier || stage.name === identifier || stage.id.startsWith(identifier);
175
+ return stage.id === identifier || stage.name === identifier;
176
176
  }
177
177
 
178
178
  function stageLabel(stage: RunSnapshot["stages"][number]): string {
179
- return `${stage.name} (${stage.id.slice(0, 12)})`;
179
+ return `${stage.name} (${stage.id})`;
180
180
  }
181
181
 
182
182
  function resolveUniqueResumeStage(
@@ -261,7 +261,7 @@ export function createExtensionRuntime(opts: ExtensionRuntimeOpts = {}): Extensi
261
261
  };
262
262
  }
263
263
  const stageMessage = (verb: string, runId: string): string =>
264
- `${verb} workflow "${def.name}" from run ${source.id.slice(0, 8)} at stage ${resolvedStage.stageId.slice(0, 8)} (run ${runId.slice(0, 8)}).`;
264
+ `${verb} workflow "${def.name}" from run ${source.id} at stage ${resolvedStage.stageId} (run ${runId}).`;
265
265
  const launchContinuation = () =>
266
266
  launchDetachedUntilStartup(def, sourceInputs, {
267
267
  ...runOptions(options?.policy),
@@ -43,7 +43,7 @@ function workflowNameItems(runtime: ExtensionRuntime): PiArgumentCompletion[] {
43
43
  function runIdItems(): PiArgumentCompletion[] {
44
44
  return topLevelWorkflowRuns(store.runs()).map((run) => ({
45
45
  value: `${run.id} `,
46
- label: run.id.slice(0, 8),
46
+ label: run.id,
47
47
  description: `${run.name} — ${run.status}`,
48
48
  }));
49
49
  }
@@ -35,7 +35,7 @@ import {
35
35
  import { workflowPolicyFromContext } from "./workflow-policy.js";
36
36
  import { normalizeWorkflowReloadReport, type WorkflowReloadReport } from "./workflow-reload-report.js";
37
37
  import { handleRunControlCommand, type WorkflowRunControlDeps } from "./workflow-run-control-command.js";
38
- import { overlaySurfaceFromContext, reloadFailureMessage, resolveRunIdPrefix } from "./workflow-targets.js";
38
+ import { overlaySurfaceFromContext, reloadFailureMessage, resolveRunId } from "./workflow-targets.js";
39
39
 
40
40
  export interface WorkflowSlashCommandDeps {
41
41
  runtimeProxy: ExtensionRuntime;
@@ -150,13 +150,9 @@ async function workflowSlashHandler(
150
150
  if (subcommand === "status") {
151
151
  const target = parts[1];
152
152
  if (target && !target.startsWith("--")) {
153
- const resolved = resolveRunIdPrefix(target);
153
+ const resolved = resolveRunId(target);
154
+ if (resolved.kind === "malformed") return fail(resolved.message);
154
155
  if (resolved.kind === "not_found") return fail(`Run not found: ${target}`);
155
- if (resolved.kind === "ambiguous") {
156
- return fail(
157
- `Ambiguous run prefix "${target}" matches: ${resolved.matches.map((id) => id.slice(0, 12)).join(", ")}`,
158
- );
159
- }
160
156
  const inspected = inspectRun(resolved.runId);
161
157
  if (!inspected.ok) return fail(`Run not found: ${target}`);
162
158
  emitChatSurface(pi, { kind: "detail", detail: inspected.detail });
@@ -2,10 +2,13 @@ import { listOpenableCompletedWorkflows } from "../durable/completed-catalog.js"
2
2
  import { openCompletedDurableWorkflow } from "../durable/completed-inspection.js";
3
3
  import { getDurableBackend } from "../durable/factory.js";
4
4
  import { formatResumableWorkflowList } from "../durable/resume-catalog.js";
5
+ import { isWorkflowRunResumable } from "../durable/resume-eligibility.js";
5
6
  import { type DurableWorkflowDeleteOutcome, deleteDurableWorkflowIfSafe } from "../durable/retention-policy.js";
6
7
  import type { ResumableWorkflowEntry } from "../durable/types.js";
8
+ import { isFullRunId, malformedRunIdMessage } from "../shared/run-id.js";
7
9
  import { store } from "../shared/store.js";
8
10
  import type { RunSnapshot } from "../shared/store-types.js";
11
+ import { workflowRunResumeCandidate } from "../shared/workflow-artifacts.js";
9
12
  import type { GraphOverlayPort } from "../tui/overlay-adapter.js";
10
13
  import { openWorkflowResumeSelector } from "../tui/workflow-resume-selector.js";
11
14
  import type { ExtensionAPI, PiCommandContext } from "./public-types.js";
@@ -37,12 +40,12 @@ export interface WorkflowResumeTarget {
37
40
 
38
41
  export type WorkflowResumeTargetResolution =
39
42
  | WorkflowResumeTarget
40
- | { readonly kind: "ambiguous"; readonly matches: readonly WorkflowResumeTarget[] }
43
+ | { readonly kind: "malformed"; readonly message: string }
41
44
  | { readonly kind: "not_found" };
42
45
 
43
46
  export async function prepareWorkflowResumeCatalog(
44
47
  runtime: ExtensionRuntime,
45
- activeLiveIds: ReadonlySet<string>,
48
+ suppressedLiveIds: ReadonlySet<string>,
46
49
  target?: string,
47
50
  ): Promise<WorkflowResumeCatalog> {
48
51
  const shared = await runtime.prepareDurableCatalog?.();
@@ -50,8 +53,11 @@ export async function prepareWorkflowResumeCatalog(
50
53
  const backend = getDurableBackend();
51
54
  const isDisplayLoadable = (entry: ResumableWorkflowEntry): boolean =>
52
55
  shared !== undefined || backend.isWorkflowLoadable(entry.workflowId);
56
+ // The durable catalog is already produced by `listResumableWorkflows()`, which
57
+ // applies `isDurableWorkflowResumable`; re-filtering here would only drop rows
58
+ // a hydrating backend has not yet materialized.
53
59
  const resumable = filterSelectorDurableEntries(runtime, prepared).filter(
54
- (entry) => !activeLiveIds.has(entry.workflowId) && isDisplayLoadable(entry),
60
+ (entry) => !suppressedLiveIds.has(entry.workflowId) && isDisplayLoadable(entry),
55
61
  );
56
62
  const completed =
57
63
  shared?.completed ??
@@ -60,7 +66,7 @@ export async function prepareWorkflowResumeCatalog(
60
66
  : listOpenableCompletedWorkflows(backend));
61
67
  return {
62
68
  resumable,
63
- completed: completed.filter((entry) => !activeLiveIds.has(entry.workflowId) && isDisplayLoadable(entry)),
69
+ completed: completed.filter((entry) => isDisplayLoadable(entry)),
64
70
  };
65
71
  }
66
72
 
@@ -99,8 +105,8 @@ export async function handleDurableResume(
99
105
  // directory a second time for the same command invocation.
100
106
  catalog.completed,
101
107
  );
102
- if (resolved.kind === "ambiguous") {
103
- fail(`Ambiguous workflow prefix "${target}" matches: ${formatMatches(resolved.matches)}`);
108
+ if (resolved.kind === "malformed") {
109
+ fail(resolved.message);
104
110
  return true;
105
111
  }
106
112
  if (resolved.kind === "completed") {
@@ -191,21 +197,16 @@ export function resolveWorkflowResumeTarget(
191
197
  name: run.name,
192
198
  });
193
199
  }
200
+ if (!isFullRunId(target)) return { kind: "malformed", message: malformedRunIdMessage(target) };
194
201
  const exact = targets.get(target);
195
- if (exact !== undefined) return exact;
196
- const matches = [...targets.values()].filter((candidate) => candidate.workflowId.startsWith(target));
197
- if (matches.length === 0) return { kind: "not_found" };
198
- if (matches.length === 1) return matches[0]!;
199
- return { kind: "ambiguous", matches };
202
+ return exact ?? { kind: "not_found" };
200
203
  }
201
204
 
202
205
  function isExplicitResumeCandidate(run: RunSnapshot): boolean {
203
- if (run.status === "completed" || run.status === "paused" || run.exitReason === "quit") return true;
204
- if (run.stages.some((stage) => stage.status === "paused")) return true;
205
- if (run.status === "failed") return run.resumable !== false;
206
- if (run.endedAt !== undefined) return false;
207
- if (run.status === "running") return true;
208
- return run.resumable === true && run.failureRecoverability === "recoverable";
206
+ if (run.status === "completed") return true;
207
+ return (
208
+ isWorkflowRunResumable(workflowRunResumeCandidate(run)) || (run.endedAt === undefined && run.status === "running")
209
+ );
209
210
  }
210
211
 
211
212
  async function resumeDurableTarget(
@@ -247,14 +248,14 @@ function openCompletedTarget(
247
248
 
248
249
  function openCompleted(
249
250
  runtime: ExtensionRuntime,
250
- workflowIdOrPrefix: string,
251
+ workflowId: string,
251
252
  catalog: readonly ResumableWorkflowEntry[],
252
253
  beforeRestoreCompleted?: (snapshots: readonly RunSnapshot[]) => void,
253
254
  ) {
254
255
  return (
255
- runtime.openCompletedDurableWorkflow?.(workflowIdOrPrefix, catalog) ??
256
+ runtime.openCompletedDurableWorkflow?.(workflowId, catalog) ??
256
257
  openCompletedDurableWorkflow(
257
- workflowIdOrPrefix,
258
+ workflowId,
258
259
  {
259
260
  durableBackend: getDurableBackend(),
260
261
  store,
@@ -264,7 +265,3 @@ function openCompleted(
264
265
  )
265
266
  );
266
267
  }
267
-
268
- function formatMatches(entries: readonly WorkflowResumeTarget[]): string {
269
- return entries.map((entry) => `${entry.name} (${entry.workflowId.slice(0, 8)})`).join(", ");
270
- }
@@ -9,51 +9,63 @@
9
9
  */
10
10
 
11
11
  import { getDurableBackend } from "../durable/factory.js";
12
+ import { isWorkflowRunResumable } from "../durable/resume-eligibility.js";
12
13
  import type { ResumableWorkflowEntry } from "../durable/types.js";
13
14
  import { topLevelWorkflowRuns } from "../shared/run-visibility.js";
14
15
  import type { Store } from "../shared/store.js";
16
+ import { subscribeStoreInvalidation } from "../shared/store-observation.js";
15
17
  import type { RunSnapshot } from "../shared/store-types.js";
18
+ import { workflowRunResumeCandidate } from "../shared/workflow-artifacts.js";
16
19
  import type { WorkflowResumeRefresh } from "../tui/workflow-resume-selector.js";
17
20
  import type { ExtensionRuntime } from "./runtime.js";
18
21
  import { prepareWorkflowResumeCatalog } from "./workflow-durable-resume-command.js";
19
- import { reconcileDurableResumeShadow } from "./workflow-resume-shadow.js";
22
+ import { classifyDurableResumeShadow } from "./workflow-resume-shadow.js";
20
23
 
21
24
  export interface ResumePickerLiveSource {
22
25
  readonly liveRuns: readonly RunSnapshot[];
23
- readonly activeLiveIds: ReadonlySet<string>;
26
+ /** Live ids whose resumable durable duplicates must be suppressed. */
27
+ readonly suppressedLiveIds: ReadonlySet<string>;
24
28
  }
25
29
 
26
- function isActiveRecoverableBlock(run: RunSnapshot): boolean {
27
- return run.endedAt === undefined && run.resumable === true && run.failureRecoverability === "recoverable";
30
+ /** A live run is resumable when the shared predicate accepts it and the durable
31
+ * backend has not explicitly deleted it. A live paused/quit run resumes through
32
+ * its live stage controls, so it must NOT require a durable checkpoint here;
33
+ * a restored snapshot that depends on durable state is filtered by its shadow
34
+ * classification instead.
35
+ */
36
+ function isResumableLiveRun(run: RunSnapshot): boolean {
37
+ if (!getDurableBackend().isWorkflowLoadable(run.id)) return false;
38
+ return isWorkflowRunResumable(workflowRunResumeCandidate(run));
28
39
  }
29
40
 
30
41
  export function collectResumePickerLiveRuns(runStore: Store): ResumePickerLiveSource {
31
- const durableResumeShadows = new Set(
32
- topLevelWorkflowRuns(runStore.runs())
33
- .filter((run) => reconcileDurableResumeShadow(run, runStore))
34
- .map((run) => run.id),
35
- );
36
- const liveRuns = topLevelWorkflowRuns(runStore.runs()).filter(
37
- (run) =>
38
- !durableResumeShadows.has(run.id) &&
39
- (run.status === "paused" ||
40
- (run.status === "failed" && run.resumable !== false) ||
41
- isActiveRecoverableBlock(run)) &&
42
- getDurableBackend().isWorkflowLoadable(run.id),
43
- );
42
+ // `eligible` rows are re-listed from the durable catalog, and `ineligible`
43
+ // rows have no durable checkpoint or pending prompt progress, so the resume
44
+ // path would refuse them. Only `not_shadow` runs can actually be resumed here.
45
+ const runs = topLevelWorkflowRuns(runStore.runs());
46
+ const shadowClassification = new Map(runs.map((run) => [run.id, classifyDurableResumeShadow(run, runStore)]));
47
+ const isOfferable = (run: RunSnapshot): boolean => shadowClassification.get(run.id) === "not_shadow";
48
+ const resumableById = new Map(runs.map((run) => [run.id, isResumableLiveRun(run)]));
49
+ const liveRuns = runs.filter((run) => isOfferable(run) && resumableById.get(run.id) === true);
50
+ const isCurrentlyRunning = (run: RunSnapshot): boolean =>
51
+ run.endedAt === undefined && run.status === "running" && run.exitReason !== "quit";
44
52
  const activeLiveIds = new Set(
45
- topLevelWorkflowRuns(runStore.runs())
53
+ runs
46
54
  .filter(
47
55
  (run) =>
48
- !durableResumeShadows.has(run.id) &&
49
- run.endedAt === undefined &&
50
- run.status === "running" &&
51
- !isActiveRecoverableBlock(run) &&
52
- run.exitReason !== "quit",
56
+ shadowClassification.get(run.id) !== "eligible" &&
57
+ isCurrentlyRunning(run) &&
58
+ resumableById.get(run.id) !== true,
53
59
  )
54
60
  .map((run) => run.id),
55
61
  );
56
- return { liveRuns, activeLiveIds };
62
+ // A non-running snapshot rejected by the shared predicate must not reappear as
63
+ // a resumable durable row. Completed inspection rows remain visible separately.
64
+ const rejectedLiveIds = new Set(
65
+ runs.filter((run) => !isCurrentlyRunning(run) && resumableById.get(run.id) !== true).map((run) => run.id),
66
+ );
67
+ const suppressedLiveIds = new Set([...activeLiveIds, ...rejectedLiveIds]);
68
+ return { liveRuns, suppressedLiveIds };
57
69
  }
58
70
 
59
71
  export interface ResumePickerLiveUpdateOptions {
@@ -66,10 +78,10 @@ export function resumePickerLiveUpdateOptions(
66
78
  runtime: ExtensionRuntime,
67
79
  ): ResumePickerLiveUpdateOptions {
68
80
  return {
69
- watch: (onChange) => runStore.subscribe(() => onChange()),
81
+ watch: (onChange) => subscribeStoreInvalidation(runStore, onChange),
70
82
  refresh: async () => {
71
83
  const current = collectResumePickerLiveRuns(runStore);
72
- const catalog = await prepareWorkflowResumeCatalog(runtime, current.activeLiveIds);
84
+ const catalog = await prepareWorkflowResumeCatalog(runtime, current.suppressedLiveIds);
73
85
  return {
74
86
  liveRuns: current.liveRuns,
75
87
  catalog: { durable: catalog.resumable, completed: catalog.completed },
@@ -5,6 +5,7 @@ import { getLoadableDurableWorkflow } from "../durable/workflow-status-transitio
5
5
  import { type JobTracker, jobTracker } from "../runs/background/job-tracker.js";
6
6
  import { type StageControlRegistry, stageControlRegistry } from "../runs/foreground/stage-control-registry.js";
7
7
  import { expandWorkflowGraph } from "../shared/expanded-workflow-graph.js";
8
+ import { readGraphStoreSnapshot } from "../shared/store-observation.js";
8
9
  import type { Store } from "../shared/store-public-types.js";
9
10
  import type { RunSnapshot } from "../shared/store-types.js";
10
11
 
@@ -28,7 +29,7 @@ export function classifyDurableResumeShadow(
28
29
  const jobs = deps.jobs ?? jobTracker;
29
30
  if (jobs.has(run.id)) return "not_shadow";
30
31
  const controls = deps.stageControls ?? stageControlRegistry;
31
- const graph = expandWorkflowGraph(store.snapshot(), run.id);
32
+ const graph = expandWorkflowGraph(readGraphStoreSnapshot(store), run.id);
32
33
  const controlRunIds = new Set<string>([run.id]);
33
34
  for (const stage of graph.stages) controlRunIds.add(stage.workflowGraphTarget.runId);
34
35
  if ([...controlRunIds].some((runId) => controls.run(runId).stages().length > 0)) return "not_shadow";