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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (317) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/CHANGELOG.md +17 -0
  4. package/dist/builtin/intercom/README.md +27 -21
  5. package/dist/builtin/intercom/broker/broker.ts +3 -6
  6. package/dist/builtin/intercom/broker/paths.ts +42 -5
  7. package/dist/builtin/intercom/broker/spawn.ts +78 -10
  8. package/dist/builtin/intercom/config.ts +6 -3
  9. package/dist/builtin/intercom/contact-supervisor-tool.ts +6 -4
  10. package/dist/builtin/intercom/index-heavy.ts +1 -1
  11. package/dist/builtin/intercom/index.ts +162 -130
  12. package/dist/builtin/intercom/intercom-tool.ts +9 -3
  13. package/dist/builtin/intercom/package.json +10 -1
  14. package/dist/builtin/intercom/result-renderers.ts +1 -1
  15. package/dist/builtin/intercom/ui/compose.ts +2 -2
  16. package/dist/builtin/intercom/ui/inline-message.ts +2 -2
  17. package/dist/builtin/intercom/ui/session-list.ts +2 -2
  18. package/dist/builtin/mcp/CHANGELOG.md +11 -0
  19. package/dist/builtin/mcp/README.md +6 -6
  20. package/dist/builtin/mcp/direct-tools.ts +41 -23
  21. package/dist/builtin/mcp/index.ts +31 -4
  22. package/dist/builtin/mcp/init.ts +4 -43
  23. package/dist/builtin/mcp/metadata-hydration.ts +37 -0
  24. package/dist/builtin/mcp/package.json +1 -1
  25. package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
  26. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  27. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  28. package/dist/builtin/subagents/CHANGELOG.md +18 -0
  29. package/dist/builtin/subagents/package.json +1 -1
  30. package/dist/builtin/subagents/src/extension/index.ts +14 -25
  31. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  32. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
  33. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
  34. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
  35. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
  36. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -1
  37. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +15 -48
  38. package/dist/builtin/subagents/src/tui/render-layout.ts +9 -0
  39. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +27 -25
  40. package/dist/builtin/subagents/src/tui/render-widget.ts +72 -66
  41. package/dist/builtin/subagents/src/tui/render.ts +1 -1
  42. package/dist/builtin/web-access/package.json +1 -1
  43. package/dist/builtin/workflows/CHANGELOG.md +29 -0
  44. package/dist/builtin/workflows/README.md +4 -2
  45. package/dist/builtin/workflows/builtin/goal-artifacts.ts +3 -1
  46. package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -2
  47. package/dist/builtin/workflows/builtin/goal-reducer.ts +21 -0
  48. package/dist/builtin/workflows/builtin/goal-review.ts +60 -6
  49. package/dist/builtin/workflows/builtin/goal-runner.ts +71 -22
  50. package/dist/builtin/workflows/builtin/goal-types.ts +7 -1
  51. package/dist/builtin/workflows/builtin/ralph-core.ts +52 -4
  52. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +10 -3
  53. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +23 -2
  54. package/dist/builtin/workflows/builtin/ralph-runner.ts +38 -16
  55. package/dist/builtin/workflows/builtin/review-convergence.ts +111 -0
  56. package/dist/builtin/workflows/package.json +1 -1
  57. package/dist/builtin/workflows/skills/create-spec/SKILL.md +2 -0
  58. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  59. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  60. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  61. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  62. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  63. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  64. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  65. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  66. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  67. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  68. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  69. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  70. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  71. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  72. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  73. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  74. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  75. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  76. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  77. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  78. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  79. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  80. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  81. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  82. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  83. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  84. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  85. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  86. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  87. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  88. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  89. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  90. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  91. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  92. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  93. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  94. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  95. package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
  96. package/dist/builtin/workflows/src/durable/dbos-backend.ts +63 -2
  97. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
  98. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  99. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  100. package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
  101. package/dist/builtin/workflows/src/durable/types.ts +30 -1
  102. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  103. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  104. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  105. package/dist/builtin/workflows/src/engine/run.ts +32 -32
  106. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  107. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  108. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  109. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  110. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  111. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  112. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  113. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  114. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  115. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
  116. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  117. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  118. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  119. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  120. package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
  121. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  122. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
  123. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -3
  124. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +18 -19
  125. package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +38 -0
  126. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  127. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  128. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  129. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +18 -1
  130. package/dist/builtin/workflows/src/shared/persistence-restore.ts +23 -5
  131. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  132. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  133. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  134. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  135. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  136. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  137. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  138. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  139. package/dist/bun/internal-intercom-broker.d.ts +6 -0
  140. package/dist/bun/internal-intercom-broker.d.ts.map +1 -0
  141. package/dist/bun/internal-intercom-broker.js +39 -0
  142. package/dist/bun/internal-intercom-broker.js.map +1 -0
  143. package/dist/bun/split-loader.d.ts.map +1 -1
  144. package/dist/bun/split-loader.js +21 -8
  145. package/dist/bun/split-loader.js.map +1 -1
  146. package/dist/core/agent-session-auto-compaction.d.ts +21 -2
  147. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  148. package/dist/core/agent-session-auto-compaction.js +163 -11
  149. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  150. package/dist/core/agent-session-events.d.ts.map +1 -1
  151. package/dist/core/agent-session-events.js +10 -1
  152. package/dist/core/agent-session-events.js.map +1 -1
  153. package/dist/core/agent-session-methods.d.ts +12 -3
  154. package/dist/core/agent-session-methods.d.ts.map +1 -1
  155. package/dist/core/agent-session-methods.js.map +1 -1
  156. package/dist/core/agent-session-models.d.ts +2 -2
  157. package/dist/core/agent-session-models.d.ts.map +1 -1
  158. package/dist/core/agent-session-models.js.map +1 -1
  159. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  160. package/dist/core/agent-session-prompt.js +1 -0
  161. package/dist/core/agent-session-prompt.js.map +1 -1
  162. package/dist/core/agent-session-retry.d.ts +2 -0
  163. package/dist/core/agent-session-retry.d.ts.map +1 -1
  164. package/dist/core/agent-session-retry.js +152 -4
  165. package/dist/core/agent-session-retry.js.map +1 -1
  166. package/dist/core/agent-session-services.d.ts +1 -0
  167. package/dist/core/agent-session-services.d.ts.map +1 -1
  168. package/dist/core/agent-session-services.js +1 -0
  169. package/dist/core/agent-session-services.js.map +1 -1
  170. package/dist/core/agent-session-types.d.ts +15 -1
  171. package/dist/core/agent-session-types.d.ts.map +1 -1
  172. package/dist/core/agent-session-types.js.map +1 -1
  173. package/dist/core/agent-session.d.ts +6 -0
  174. package/dist/core/agent-session.d.ts.map +1 -1
  175. package/dist/core/agent-session.js +6 -0
  176. package/dist/core/agent-session.js.map +1 -1
  177. package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
  178. package/dist/core/compaction/context-compaction-runner.js +36 -20
  179. package/dist/core/compaction/context-compaction-runner.js.map +1 -1
  180. package/dist/core/copilot-anthropic-sse-repair.d.ts +23 -0
  181. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -0
  182. package/dist/core/copilot-anthropic-sse-repair.js +340 -0
  183. package/dist/core/copilot-anthropic-sse-repair.js.map +1 -0
  184. package/dist/core/copilot-gemini-reasoning.d.ts +16 -8
  185. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  186. package/dist/core/copilot-gemini-reasoning.js +27 -22
  187. package/dist/core/copilot-gemini-reasoning.js.map +1 -1
  188. package/dist/core/copilot-hosts.d.ts +12 -0
  189. package/dist/core/copilot-hosts.d.ts.map +1 -0
  190. package/dist/core/copilot-hosts.js +33 -0
  191. package/dist/core/copilot-hosts.js.map +1 -0
  192. package/dist/core/extensions/agent-events.d.ts +1 -1
  193. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  194. package/dist/core/extensions/agent-events.js.map +1 -1
  195. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  196. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  197. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  198. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  199. package/dist/core/footer-data-provider.d.ts +6 -0
  200. package/dist/core/footer-data-provider.d.ts.map +1 -1
  201. package/dist/core/footer-data-provider.js +72 -29
  202. package/dist/core/footer-data-provider.js.map +1 -1
  203. package/dist/core/http-dispatcher.d.ts.map +1 -1
  204. package/dist/core/http-dispatcher.js +5 -5
  205. package/dist/core/http-dispatcher.js.map +1 -1
  206. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  207. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  208. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  209. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  210. package/dist/core/sdk-types.d.ts +2 -0
  211. package/dist/core/sdk-types.d.ts.map +1 -1
  212. package/dist/core/sdk-types.js.map +1 -1
  213. package/dist/core/sdk.d.ts.map +1 -1
  214. package/dist/core/sdk.js +1 -0
  215. package/dist/core/sdk.js.map +1 -1
  216. package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
  217. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  218. package/dist/core/settings-manager-basic-accessors.js +6 -0
  219. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  220. package/dist/core/settings-types.d.ts +1 -0
  221. package/dist/core/settings-types.d.ts.map +1 -1
  222. package/dist/core/settings-types.js.map +1 -1
  223. package/dist/core/slash-commands.d.ts +2 -0
  224. package/dist/core/slash-commands.d.ts.map +1 -1
  225. package/dist/core/slash-commands.js +141 -0
  226. package/dist/core/slash-commands.js.map +1 -1
  227. package/dist/index-extensions.d.ts +1 -1
  228. package/dist/index-extensions.d.ts.map +1 -1
  229. package/dist/index-extensions.js.map +1 -1
  230. package/dist/index.d.ts +1 -0
  231. package/dist/index.d.ts.map +1 -1
  232. package/dist/index.js +1 -0
  233. package/dist/index.js.map +1 -1
  234. package/dist/main-deferred-startup.d.ts +12 -0
  235. package/dist/main-deferred-startup.d.ts.map +1 -1
  236. package/dist/main-deferred-startup.js +23 -2
  237. package/dist/main-deferred-startup.js.map +1 -1
  238. package/dist/main-early-input.d.ts +41 -0
  239. package/dist/main-early-input.d.ts.map +1 -0
  240. package/dist/main-early-input.js +154 -0
  241. package/dist/main-early-input.js.map +1 -0
  242. package/dist/main.d.ts +0 -6
  243. package/dist/main.d.ts.map +1 -1
  244. package/dist/main.js +20 -14
  245. package/dist/main.js.map +1 -1
  246. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  247. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  248. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  249. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  250. package/dist/modes/interactive/components/chat-session-host-events.js +15 -0
  251. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  252. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  253. package/dist/modes/interactive/interactive-agent-events.js +25 -0
  254. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  255. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  256. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  257. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  258. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  259. package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
  260. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  261. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  262. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  263. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  264. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  265. package/dist/modes/interactive/interactive-input-handling.js +318 -185
  266. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  267. package/dist/modes/interactive/interactive-mode-base.d.ts +12 -0
  268. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  269. package/dist/modes/interactive/interactive-mode-base.js +39 -0
  270. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  271. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  272. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  273. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  274. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  275. package/dist/modes/interactive/interactive-mode-surface.d.ts +6 -2
  276. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  277. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  278. package/dist/modes/interactive/interactive-mode-types.d.ts +3 -0
  279. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  280. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  281. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  282. package/dist/modes/interactive/interactive-model-routing.js +3 -1
  283. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  284. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  285. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  286. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  287. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  288. package/dist/modes/interactive/interactive-render-chat.js +36 -10
  289. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  290. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  291. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  292. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  293. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  294. package/dist/modes/interactive/interactive-startup.js +26 -22
  295. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  296. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  297. package/dist/modes/interactive/theme/global-theme.js +26 -2
  298. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  299. package/dist/utils/fs-watch.d.ts +24 -1
  300. package/dist/utils/fs-watch.d.ts.map +1 -1
  301. package/dist/utils/fs-watch.js +60 -5
  302. package/dist/utils/fs-watch.js.map +1 -1
  303. package/docs/compaction.md +8 -0
  304. package/docs/development.md +4 -0
  305. package/docs/extensions.md +5 -0
  306. package/docs/json.md +3 -1
  307. package/docs/providers.md +1 -0
  308. package/docs/quickstart.md +1 -1
  309. package/docs/rpc.md +3 -1
  310. package/docs/settings.md +27 -1
  311. package/docs/subagents.md +4 -0
  312. package/docs/terminal-setup.md +4 -0
  313. package/docs/usage.md +2 -2
  314. package/docs/windows.md +4 -0
  315. package/docs/workflows.md +21 -9
  316. package/npm-shrinkwrap.json +513 -23
  317. package/package.json +3 -2
@@ -1,11 +1,5 @@
1
- import type { RunSnapshot } from "../shared/store-types.js";
2
- import type {
3
- WorkflowDefinition,
4
- WorkflowInputValues,
5
- WorkflowOutputValues,
6
- WorkflowRunContext,
7
- WorkflowSerializableValue,
8
- } from "../shared/types.js";
1
+ import type { RunSnapshot, StageSnapshot } from "../shared/store-types.js";
2
+ import type { WorkflowDefinition, WorkflowInputValues, WorkflowOutputValues, WorkflowRunContext } from "../shared/types.js";
9
3
  import type { WorkflowFailure } from "../shared/workflow-failures.js";
10
4
  import { classifyWorkflowFailure } from "../shared/workflow-failures.js";
11
5
  import { store as defaultStore } from "../shared/store.js";
@@ -25,7 +19,7 @@ import { stageControlRegistry as defaultStageControlRegistry } from "../runs/for
25
19
  import type { RunOpts, RunResult } from "../runs/foreground/executor-types.js";
26
20
  import { unknownErrorMessage, findWorkflowExitSignal, parentWorkflowExitAbortReason } from "../runs/foreground/executor-abort.js";
27
21
  import { resolveAndValidateInputs, resolveInputConcurrency, resolveInputRuntimeDefaults } from "../runs/foreground/executor-inputs.js";
28
- import { workflowCwdWithInputWorktree } from "../runs/foreground/executor-direct-helpers.js";
22
+ import { createGitWorktreeSetupCache, workflowCwdWithInputWorktree, workflowInvocationMetadata } from "../runs/foreground/executor-direct-helpers.js";
29
23
  import { createStageScheduler } from "../runs/foreground/executor-scheduler.js";
30
24
  import { createRunFinalizers } from "../runs/foreground/executor-run-finalizers.js";
31
25
  import { buildPromptNodeUiAdapter } from "../runs/foreground/executor-prompt-nodes.js";
@@ -45,12 +39,12 @@ import { classifyReturnedRunStatus } from "./run-returned-status.js";
45
39
  import { createToolPrimitive, createCheckpointIdGenerator } from "../durable/tool-primitive.js";
46
40
  import { persistDurableCacheEntry } from "../durable/resume-catalog.js";
47
41
  import { createDurableStagePrimitive, createDurableTaskPrimitive, recordStageCheckpoint, createStageReplayKeyGenerator, recordCachedStageWithTracker } from "../durable/stage-primitive.js";
42
+ import type { DurableCompletedStageCheckpoint } from "../durable/stage-primitive.js";
48
43
  import { createDurableChildWorkflowPrimitive } from "../durable/child-primitive.js";
49
44
  import { ScopedDurableBackend, type DurableScope } from "../durable/scoped-backend.js";
50
45
  import { finalizeDurableTerminalStatus } from "./run-durable-finalize.js";
51
46
  import { createDurableStageSessionRecorder } from "./run-durable-stage-session.js";
52
47
  import type { DurableWorkflowBackend } from "../durable/backend.js";
53
- import type { StageSnapshot } from "../shared/store-types.js";
54
48
 
55
49
  function nextEventLoopTurn(): Promise<void> {
56
50
  return new Promise((resolve) => setTimeout(resolve, 0));
@@ -158,11 +152,11 @@ export async function run<
158
152
 
159
153
  const tracker = new GraphFrontierTracker();
160
154
  const inputConcurrency = resolveInputConcurrency(def.inputs, resolvedInputs);
161
- const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs);
155
+ const inputRuntimeDefaults = resolveInputRuntimeDefaults(def, resolvedInputs), gitWorktreeSetupCache = createGitWorktreeSetupCache();
162
156
  const workflowInvocationCwd = opts.cwd ?? process.cwd();
163
157
  let workflowCwd: string | undefined;
164
158
  const resolveWorkflowCwd = (): string => {
165
- workflowCwd ??= workflowCwdWithInputWorktree(inputRuntimeDefaults, workflowInvocationCwd);
159
+ workflowCwd ??= workflowCwdWithInputWorktree(inputRuntimeDefaults, workflowInvocationCwd, gitWorktreeSetupCache);
166
160
  return workflowCwd;
167
161
  };
168
162
  const limiter = createRunLimiter(inputConcurrency ?? opts.config?.defaultConcurrency);
@@ -277,12 +271,14 @@ export async function run<
277
271
  limiter,
278
272
  inputRuntimeDefaults,
279
273
  workflowInvocationCwd,
280
- stageRegistry,
274
+ stageRegistry, gitWorktreeSetupCache,
281
275
  exit,
282
276
  classifyExecutorFailure,
283
277
  });
284
278
  const workflowBoundaryReplayCounts = new Map<string, number>();
285
279
  const nextWorkflowBoundaryReplayKey = (name: string): string => {
280
+ const durableScopePrefix = pendingChildDurableScope?.scopePrefix;
281
+ if (durableScopePrefix !== undefined && durableScopePrefix.startsWith(`workflow:${name}:`)) return durableScopePrefix;
286
282
  const next = (workflowBoundaryReplayCounts.get(name) ?? 0) + 1;
287
283
  workflowBoundaryReplayCounts.set(name, next);
288
284
  return `workflow:${name}:${next}`;
@@ -314,22 +310,19 @@ export async function run<
314
310
  },
315
311
  runWorkflow: run,
316
312
  });
317
-
318
- // Durable workflow registration — register this run for cross-session discovery.
313
+ const durableRootWorkflowRegistration = {
314
+ workflowId: runId,
315
+ name: def.name,
316
+ inputs: resolvedInputs as Record<string, import("../shared/types.js").WorkflowSerializableValue>,
317
+ createdAt: runSnapshot.startedAt,
318
+ status: "running" as const,
319
+ rootWorkflowId: runId,
320
+ resumable: true,
321
+ ...(opts.persistence !== undefined ? { sessionFile: undefined } : {}),
322
+ };
319
323
  if (opts.continuation === undefined && opts.parentRun === undefined) {
320
- // New root run: register it durably so a future session can discover it.
321
- durableBackend.registerWorkflow({
322
- workflowId: runId,
323
- name: def.name,
324
- inputs: resolvedInputs as Record<string, import("../shared/types.js").WorkflowSerializableValue>,
325
- createdAt: runSnapshot.startedAt,
326
- status: "running",
327
- rootWorkflowId: runId,
328
- resumable: true,
329
- ...(opts.persistence !== undefined ? { sessionFile: undefined } : {}),
330
- });
324
+ durableBackend.registerWorkflow({ ...durableRootWorkflowRegistration, invocationCwd: workflowInvocationCwd });
331
325
  } else if (opts.parentRun === undefined) {
332
- // Resuming a root workflow: mark it as running again in the backend.
333
326
  durableBackend.setWorkflowStatus(runId, "running");
334
327
  }
335
328
  const tool = createToolPrimitive({
@@ -346,12 +339,13 @@ export async function run<
346
339
 
347
340
  // Durable ctx.ui wrapper — caches completed user responses so a resumed workflow does not re-ask answered prompts.
348
341
  const durableUiDeps = { workflowId: runId, backend: durableBackend, nextCheckpointId: checkpointIdGenerator };
349
- const recordCachedStage = (name: string, replayKey: string, output: WorkflowSerializableValue): void =>
350
- recordCachedStageWithTracker(activeStore, tracker, runId, name, replayKey, output, completedStageReplayKeys);
342
+ const recordCachedStage = (name: string, replayKey: string, checkpoint: DurableCompletedStageCheckpoint): void =>
343
+ recordCachedStageWithTracker(activeStore, tracker, runId, name, replayKey, checkpoint, completedStageReplayKeys);
351
344
  const durableTask = createDurableTaskPrimitive({
352
345
  workflowId: runId, backend: durableBackend,
353
346
  nextReplayKey: (stageName) => stageReplayKeyGenerator(stageName), task: taskRunners.task,
354
- recordCachedTask: (name, replayKey, output) => recordCachedStage(name, replayKey, output),
347
+ recordCachedTask: (name, replayKey, checkpoint, scope) =>
348
+ recordCachedStageWithTracker(activeStore, tracker, runId, name, replayKey, checkpoint, completedStageReplayKeys, scope),
355
349
  });
356
350
  const durableWorkflow = createDurableChildWorkflowPrimitive({
357
351
  workflowId: runId, rootWorkflowId: opts.parentRun?.rootRunId ?? runId, backend: durableBackend,
@@ -411,6 +405,10 @@ export async function run<
411
405
  }
412
406
  }
413
407
 
408
+ if (opts.continuation === undefined && opts.parentRun === undefined) {
409
+ durableBackend.registerWorkflow({ ...durableRootWorkflowRegistration, ...workflowInvocationMetadata(inputRuntimeDefaults, workflowInvocationCwd, gitWorktreeSetupCache) });
410
+ }
411
+
414
412
  const rawResult = await def.run(ctx);
415
413
  if (ownController.signal.aborted) {
416
414
  const selectedExit = findWorkflowExitSignal(ownController.signal.reason, exitScope);
@@ -424,9 +422,9 @@ export async function run<
424
422
  assertWorkflowRunOutputs(def.name, result, def.outputs);
425
423
  assertWorkflowCreatedStage(runSnapshot);
426
424
  await durableBackend.flush?.();
427
- const returned = classifyReturnedRunStatus(result);
425
+ const returned = classifyReturnedRunStatus(result, runSnapshot);
428
426
  const recorded = activeStore.recordRunEnd(runId, returned.status, result, returned.error, returned.metadata);
429
- appendRunEndWhenRecorded(opts.persistence, recorded, { runId, status: returned.status, result, ...(returned.error !== undefined ? { error: returned.error } : {}), ...(returned.metadata ?? {}), ts: Date.now() });
427
+ appendRunEndWhenRecorded(opts.persistence, recorded, { runId, status: returned.status, result, ...(returned.error !== undefined ? { error: returned.error } : {}), ...(returned.metadata ?? {}), ...(runSnapshot.endedAt !== undefined ? { endedAt: runSnapshot.endedAt } : {}), ...(runSnapshot.durationMs !== undefined ? { durationMs: runSnapshot.durationMs } : {}), ts: Date.now() });
430
428
  durableBackend.setWorkflowStatus(runId, returned.status, undefined, returned.metadata?.resumable);
431
429
  await durableBackend.flush?.();
432
430
  if (opts.persistence && durableBackend.persistent) {
@@ -480,6 +478,8 @@ export async function run<
480
478
  ...(metadata.failedStageId !== undefined ? { failedStageId: metadata.failedStageId } : {}),
481
479
  resumable: metadata.resumable,
482
480
  ...(metadata.retryAfterMs !== undefined ? { retryAfterMs: metadata.retryAfterMs } : {}),
481
+ ...(runSnapshot.endedAt !== undefined ? { endedAt: runSnapshot.endedAt } : {}),
482
+ ...(runSnapshot.durationMs !== undefined ? { durationMs: runSnapshot.durationMs } : {}),
483
483
  ts: Date.now(),
484
484
  });
485
485
  return reconcileTerminalRunResult(runId, runSnapshot, activeStore, { status: "failed", error: metadata.errorMessage }, opts.onRunEnd);
@@ -13,6 +13,7 @@ import { createWorkflowStageFactory } from "../runs/foreground/executor-stage-fa
13
13
  import { createWorkflowBoundaryFactory, type WorkflowBoundaryStage } from "../runs/foreground/executor-child-boundary.js";
14
14
  import type { GraphFrontierTracker } from "./graph-inference.js";
15
15
  import type { EngineChildRunOptions, EngineStageRuntimeOptions, EngineWorkflowBoundaryOptions } from "./options.js";
16
+ import type { GitWorktreeSetupCache } from "../runs/foreground/executor-direct-helpers.js";
16
17
 
17
18
  export interface EngineRuntimeInput {
18
19
  readonly runId: string;
@@ -31,6 +32,7 @@ export interface EngineRuntimeInput {
31
32
  readonly limiter: ConcurrencyLimiter;
32
33
  readonly inputRuntimeDefaults: Partial<StageOptions>;
33
34
  readonly workflowInvocationCwd: string;
35
+ readonly gitWorktreeSetupCache: GitWorktreeSetupCache;
34
36
  readonly stageRegistry: StageControlRegistry;
35
37
  readonly exit: WorkflowExitManager;
36
38
  readonly classifyExecutorFailure: LiveStageRuntime["classifyExecutorFailure"];
@@ -73,6 +75,7 @@ export class EngineRuntime {
73
75
  readonly exit: WorkflowExitManager;
74
76
  readonly inputRuntimeDefaults: Partial<StageOptions>;
75
77
  readonly workflowInvocationCwd: string;
78
+ readonly gitWorktreeSetupCache: GitWorktreeSetupCache;
76
79
 
77
80
  private readonly spawnAgentStage: (
78
81
  name: string,
@@ -93,6 +96,7 @@ export class EngineRuntime {
93
96
  this.exit = input.exit;
94
97
  this.inputRuntimeDefaults = input.inputRuntimeDefaults;
95
98
  this.workflowInvocationCwd = input.workflowInvocationCwd;
99
+ this.gitWorktreeSetupCache = input.gitWorktreeSetupCache;
96
100
 
97
101
  // The runtime only wires host-injected ports; stage sessions are still
98
102
  // created lazily by the stage runner through input.adapters.agentSession.
@@ -108,6 +112,7 @@ export class EngineRuntime {
108
112
  limiter: input.limiter,
109
113
  inputRuntimeDefaults: input.inputRuntimeDefaults,
110
114
  workflowInvocationCwd: input.workflowInvocationCwd,
115
+ gitWorktreeSetupCache: input.gitWorktreeSetupCache,
111
116
  stageRegistry: input.stageRegistry,
112
117
  exit: input.exit,
113
118
  classifyExecutorFailure: input.classifyExecutorFailure,
@@ -85,6 +85,7 @@ function factory(pi: ExtensionAPI): void {
85
85
  (ctx) => runtimeState.runtimeForContext(ctx),
86
86
  () => runtimeState.persistenceRef.current,
87
87
  runtimeState.reloadWorkflowResources,
88
+ runtimeState.ensureWorkflowResourcesLoaded,
88
89
  );
89
90
 
90
91
  registerWorkflowTool(pi, executeWorkflowTool, runtimeState.runWithLifecycleSuppressedForPolicy);
@@ -93,12 +94,14 @@ function factory(pi: ExtensionAPI): void {
93
94
  runtimeForContext: runtimeState.runtimeForContext,
94
95
  overlay,
95
96
  reloadWorkflowResources: runtimeState.reloadWorkflowResources,
97
+ ensureWorkflowResourcesLoaded: runtimeState.ensureWorkflowResourcesLoaded,
96
98
  runWithLifecycleSuppressedForPolicy: runtimeState.runWithLifecycleSuppressedForPolicy,
97
99
  runControl: {
98
100
  pi,
99
101
  overlay,
100
102
  getPersistence: () => runtimeState.persistenceRef.current,
101
103
  runtimeForContext: runtimeState.runtimeForContext,
104
+ ensureWorkflowResourcesLoaded: runtimeState.ensureWorkflowResourcesLoaded,
102
105
  },
103
106
  });
104
107
  registerWorkflowMessageRenderers(pi);
@@ -58,7 +58,9 @@ export function registerWorkflowLifecycleHandlers(
58
58
  });
59
59
 
60
60
  pi.on("session_start", async (_event, ctx) => {
61
+ runtimeState.resetWorkflowDiscoveryForSession();
61
62
  deAdvertiseAskUserQuestionWhenHeadless(pi, ctx?.hasUI);
63
+ await runtimeState.ensureWorkflowConfigLoaded();
62
64
  killAllRuns({ store, cancellation: cancellationRegistry, persistence: runtimeState.persistenceRef.current });
63
65
  store.clear();
64
66
  clearForms();
@@ -66,10 +68,14 @@ export function registerWorkflowLifecycleHandlers(
66
68
  resetWorkflowHilAnswerNotificationState(runtimeState.hilAnswerNotificationState);
67
69
  stageControlRegistry.clear();
68
70
  runtimeState.setIntercomParentSession(registerIntercomParentSession(pi));
69
- await runtimeState.discoveryPromise;
70
71
  runtimeState.setNotificationsActive(true);
72
+ runtimeState.startWorkflowDiscoveryWarmup(() => {
73
+ if (!ctx?.ui) return;
74
+ const diagnostics = formatStartupDiagnostics(null, runtimeState.discoveryRef.current);
75
+ if (diagnostics !== null) ctx.ui.notify?.(diagnostics, "warning");
76
+ });
71
77
  if (ctx?.ui) {
72
- const diagnostics = formatStartupDiagnostics(runtimeState.configLoadRef.current, runtimeState.discoveryRef.current);
78
+ const diagnostics = formatStartupDiagnostics(runtimeState.configLoadRef.current, null);
73
79
  if (diagnostics !== null) ctx.ui.notify?.(diagnostics, "warning");
74
80
  deps.storeWidgetRef.current?.();
75
81
  deps.storeWidgetRef.current = installStoreWidget({ ui: ctx.ui }, store);
@@ -108,6 +114,7 @@ export function registerWorkflowLifecycleHandlers(
108
114
  }
109
115
  deps.storeWidgetRef.current?.();
110
116
  deps.storeWidgetRef.current = null;
117
+ runtimeState.resetWorkflowDiscoveryForSession();
111
118
  runtimeState.setNotificationsActive(false);
112
119
  });
113
120
  }
@@ -44,11 +44,14 @@ export interface WorkflowExtensionRuntimeState {
44
44
  runtimeProxy: ExtensionRuntime;
45
45
  configLoadRef: { current: ConfigLoadResult | null };
46
46
  discoveryRef: { current: DiscoveryResult | null };
47
- discoveryPromise: Promise<void>;
48
47
  lifecycleNotificationState: ReturnType<typeof createWorkflowLifecycleNotificationState>;
49
48
  hilAnswerNotificationState: ReturnType<typeof createWorkflowHilAnswerNotificationState>;
50
49
  runtimeForContext(ctx?: PiModelContext): ExtensionRuntime;
50
+ resetWorkflowDiscoveryForSession(): void;
51
+ ensureWorkflowConfigLoaded(): Promise<void>;
52
+ ensureWorkflowResourcesLoaded(options?: { allowInFlight?: boolean }): Promise<void>;
51
53
  reloadWorkflowResources(options?: { allowInFlight?: boolean }): Promise<void>;
54
+ startWorkflowDiscoveryWarmup(onSettled?: () => void): void;
52
55
  runWithLifecycleSuppressedForPolicy<T>(policy: WorkflowExecutionPolicy, fn: () => Promise<T>): Promise<T>;
53
56
  setNotificationsActive(active: boolean): void;
54
57
  setIntercomParentSession(session: string | null): void;
@@ -74,6 +77,7 @@ export function createWorkflowExtensionRuntimeState(
74
77
  let lifecycleNotificationsUnsubscribe: (() => void) | null = null;
75
78
  let hilAnswerNotificationsUnsubscribe: (() => void) | null = null;
76
79
  let notificationsActive = false;
80
+ let notificationGeneration = 0;
77
81
  const lifecycleNotificationState = createWorkflowLifecycleNotificationState();
78
82
  const hilAnswerNotificationState = createWorkflowHilAnswerNotificationState();
79
83
  const lifecycleNotificationConfigRef: { current: WorkflowLifecycleNotificationConfig } = {
@@ -185,32 +189,15 @@ export function createWorkflowExtensionRuntimeState(
185
189
  }
186
190
 
187
191
  let workflowReloadQueue: Promise<void> = Promise.resolve();
188
- async function reloadWorkflowResources(options?: { allowInFlight?: boolean }): Promise<void> {
189
- const reload = workflowReloadQueue.then(() => reloadWorkflowResourcesNow(options));
190
- workflowReloadQueue = reload.catch(() => {});
191
- await reload;
192
- }
193
- async function loadPackageWorkflowPaths(): Promise<string[]> {
194
- const packageResources = (await pi.refreshWorkflowResources?.()) ?? pi.getWorkflowResources?.() ?? [];
195
- return packageResources.filter((resource) => resource.enabled !== false).map((resource) => resource.path);
192
+
193
+ let lazyDiscoveryPromise: Promise<void> | null = null;
194
+ let workflowDiscoveryGeneration = 0;
195
+ function deferToMacrotask(): Promise<void> {
196
+ return new Promise((resolve) => setImmediate(resolve));
196
197
  }
197
- async function reloadWorkflowResourcesNow(options?: { allowInFlight?: boolean }): Promise<void> {
198
- const activeRuns = inFlightRunCount();
199
- if (options?.allowInFlight !== true && activeRuns > 0) {
200
- throw new WorkflowReloadBlockedError(reloadBlockedMessage(activeRuns));
201
- }
202
- if (options?.allowInFlight === true && activeRuns > 0 && process.env.ATOMIC_WORKFLOW_DEBUG === "1") {
203
- console.warn(`Workflow reload bypassed in-flight guard with ${activeRuns} active run(s).`);
204
- }
205
- const configResult = await loadWorkflowConfig();
198
+
199
+ function applyWorkflowConfig(configResult: ConfigLoadResult): void {
206
200
  configLoadRef.current = configResult;
207
- const hasGlobal = configResult.globalConfig != null;
208
- const hasProject = configResult.projectConfig != null;
209
- const discoveryConfig = hasGlobal || hasProject
210
- ? toScopedDiscoveryConfig(configResult.globalConfig ?? null, configResult.projectConfig ?? null, { projectRoot: process.cwd() })
211
- : undefined;
212
- const result = await discoverWorkflows({ config: discoveryConfig, packageWorkflowPaths: await loadPackageWorkflowPaths() });
213
- discoveryRef.current = result;
214
201
  const effectiveConfig = withWorkflowDefaults(configResult.config ?? {});
215
202
  runtimeConfigRef.current = {
216
203
  maxDepth: effectiveConfig.maxDepth,
@@ -224,8 +211,11 @@ export function createWorkflowExtensionRuntimeState(
224
211
  statusWriterRef.unsubscribe();
225
212
  statusWriterRef = createStatusWriter(store, runtimeConfigRef.current);
226
213
  persistenceRef.current = makePersistencePort(pi, effectiveConfig.persistRuns);
214
+ }
215
+
216
+ function rebuildRuntime(registry = runtimeRef.current.registry): void {
227
217
  runtimeRef.current = createExtensionRuntime({
228
- registry: result.registry,
218
+ registry,
229
219
  cwd: process.cwd(),
230
220
  adapters,
231
221
  cancellation: cancellationRegistry,
@@ -237,23 +227,143 @@ export function createWorkflowExtensionRuntimeState(
237
227
  });
238
228
  }
239
229
 
230
+ function isWorkflowDiscoveryCurrent(generation: number): boolean {
231
+ return generation === workflowDiscoveryGeneration;
232
+ }
233
+
234
+ function resetWorkflowDiscoveryForSession(): void {
235
+ workflowDiscoveryGeneration += 1;
236
+ discoveryRef.current = null;
237
+ lazyDiscoveryPromise = null;
238
+ workflowReloadQueue = Promise.resolve();
239
+ rebuildRuntime(startupDiscovery.registry);
240
+ }
241
+
242
+ async function ensureWorkflowConfigLoaded(): Promise<void> {
243
+ const generation = workflowDiscoveryGeneration;
244
+ const configResult = await loadWorkflowConfig();
245
+ if (!isWorkflowDiscoveryCurrent(generation)) return;
246
+ applyWorkflowConfig(configResult);
247
+ rebuildRuntime();
248
+ }
249
+
250
+ function queueWorkflowResourceReload(options: { allowInFlight?: boolean } | undefined, generation: number): Promise<void> {
251
+ const reload = workflowReloadQueue.then(() => reloadWorkflowResourcesNow(options, generation));
252
+ workflowReloadQueue = reload.catch(() => {});
253
+ return reload;
254
+ }
255
+
256
+ function trackLazyDiscovery(pending: Promise<void>): Promise<void> {
257
+ lazyDiscoveryPromise = pending;
258
+ void pending.finally(() => {
259
+ if (lazyDiscoveryPromise === pending) lazyDiscoveryPromise = null;
260
+ }).catch(() => {});
261
+ return pending;
262
+ }
263
+
264
+ function reloadWorkflowResources(options?: { allowInFlight?: boolean }): Promise<void> {
265
+ const generation = workflowDiscoveryGeneration;
266
+ return trackLazyDiscovery(queueWorkflowResourceReload(options, generation));
267
+ }
268
+ async function loadPackageWorkflowPaths(): Promise<string[]> {
269
+ const packageResources = (await pi.refreshWorkflowResources?.()) ?? pi.getWorkflowResources?.() ?? [];
270
+ return packageResources.filter((resource) => resource.enabled !== false).map((resource) => resource.path);
271
+ }
272
+ async function reloadWorkflowResourcesNow(options: { allowInFlight?: boolean } | undefined, generation: number): Promise<void> {
273
+ const activeRuns = inFlightRunCount();
274
+ if (options?.allowInFlight !== true && activeRuns > 0) {
275
+ throw new WorkflowReloadBlockedError(reloadBlockedMessage(activeRuns));
276
+ }
277
+ if (options?.allowInFlight === true && activeRuns > 0 && process.env.ATOMIC_WORKFLOW_DEBUG === "1") {
278
+ console.warn(`Workflow reload bypassed in-flight guard with ${activeRuns} active run(s).`);
279
+ }
280
+ const configResult = await loadWorkflowConfig();
281
+ if (!isWorkflowDiscoveryCurrent(generation)) return;
282
+ applyWorkflowConfig(configResult);
283
+ const hasGlobal = configResult.globalConfig != null;
284
+ const hasProject = configResult.projectConfig != null;
285
+ const discoveryConfig = hasGlobal || hasProject
286
+ ? toScopedDiscoveryConfig(configResult.globalConfig ?? null, configResult.projectConfig ?? null, { projectRoot: process.cwd() })
287
+ : undefined;
288
+ const packageWorkflowPaths = await loadPackageWorkflowPaths();
289
+ if (!isWorkflowDiscoveryCurrent(generation)) return;
290
+ const result = await discoverWorkflows({ config: discoveryConfig, packageWorkflowPaths });
291
+ if (!isWorkflowDiscoveryCurrent(generation)) return;
292
+ discoveryRef.current = result;
293
+ rebuildRuntime(result.registry);
294
+ }
295
+
296
+ async function ensureWorkflowResourcesLoaded(options?: { allowInFlight?: boolean }): Promise<void> {
297
+ while (!pi.disableAsyncDiscovery && discoveryRef.current === null) {
298
+ const generation = workflowDiscoveryGeneration;
299
+ const pending = lazyDiscoveryPromise ?? trackLazyDiscovery(
300
+ queueWorkflowResourceReload({ allowInFlight: options?.allowInFlight ?? true }, generation),
301
+ );
302
+ await pending;
303
+ if (!isWorkflowDiscoveryCurrent(generation)) continue;
304
+ if (discoveryRef.current === null) {
305
+ lazyDiscoveryPromise = null;
306
+ }
307
+ }
308
+ }
309
+
310
+ function startWorkflowDiscoveryWarmup(onSettled?: () => void): void {
311
+ if (pi.disableAsyncDiscovery || discoveryRef.current !== null || lazyDiscoveryPromise !== null) return;
312
+ const notificationStart = notificationGeneration;
313
+ const discoveryStart = workflowDiscoveryGeneration;
314
+ const pending = (async () => {
315
+ await deferToMacrotask();
316
+ if (!isWorkflowDiscoveryCurrent(discoveryStart)) return;
317
+ await queueWorkflowResourceReload({ allowInFlight: true }, discoveryStart);
318
+ })();
319
+ lazyDiscoveryPromise = pending;
320
+ const isCurrentWarmup = (): boolean => lazyDiscoveryPromise === pending
321
+ && notificationGeneration === notificationStart
322
+ && isWorkflowDiscoveryCurrent(discoveryStart);
323
+ void pending
324
+ .catch((error) => {
325
+ if (isCurrentWarmup() && process.env.ATOMIC_WORKFLOW_DEBUG === "1") {
326
+ const message = error instanceof Error ? error.message : String(error);
327
+ console.warn(`Workflow background discovery failed: ${message}`);
328
+ }
329
+ })
330
+ .finally(() => {
331
+ const current = isCurrentWarmup();
332
+ if (lazyDiscoveryPromise === pending) lazyDiscoveryPromise = null;
333
+ if (!current || !notificationsActive) return;
334
+ try {
335
+ onSettled?.();
336
+ } catch (error) {
337
+ if (process.env.ATOMIC_WORKFLOW_DEBUG === "1") {
338
+ const message = error instanceof Error ? error.message : String(error);
339
+ console.warn(`Workflow background discovery callback failed: ${message}`);
340
+ }
341
+ }
342
+ })
343
+ .catch(() => {});
344
+ }
345
+
240
346
  return {
241
347
  persistenceRef,
242
348
  mcpPort,
243
349
  runtimeProxy,
244
350
  configLoadRef,
245
351
  discoveryRef,
246
- discoveryPromise: pi.disableAsyncDiscovery ? Promise.resolve() : reloadWorkflowResources({ allowInFlight: true }),
247
352
  lifecycleNotificationState,
248
353
  hilAnswerNotificationState,
249
354
  runtimeForContext,
355
+ resetWorkflowDiscoveryForSession,
356
+ ensureWorkflowConfigLoaded,
357
+ ensureWorkflowResourcesLoaded,
250
358
  reloadWorkflowResources,
359
+ startWorkflowDiscoveryWarmup,
251
360
  runWithLifecycleSuppressedForPolicy(policy, fn) {
252
361
  return policy.mode !== "non_interactive" || policy.awaitTerminalRun !== true
253
362
  ? fn()
254
363
  : withWorkflowLifecycleNotificationsSuppressedAsync(lifecycleNotificationState, fn);
255
364
  },
256
365
  setNotificationsActive(active) {
366
+ notificationGeneration += 1;
257
367
  notificationsActive = active;
258
368
  reinstallLifecycleNotifications();
259
369
  reinstallHilAnswerNotifications();
@@ -15,6 +15,13 @@ import type {
15
15
  StoreSnapshot,
16
16
  } from "../shared/store-types.js";
17
17
  import { isTopLevelWorkflowRun } from "../shared/run-visibility.js";
18
+ import {
19
+ actionableReturnedStatusText,
20
+ effectiveRunStatus,
21
+ isReturnedBlockedWorkflowStatus,
22
+ normalizeReturnedWorkflowStatus,
23
+ structuredRecoverableWorkflowFailureText,
24
+ } from "../shared/returned-run-status.js";
18
25
  import { deriveGraphThemeFromPiTheme, type GraphTheme } from "../tui/graph-theme.js";
19
26
  import { renderWorkflowNoticeCard, type WorkflowNoticeTone } from "../tui/workflow-notice-card.js";
20
27
 
@@ -299,13 +306,13 @@ function makeTerminalNotice(
299
306
  const failedStage = run.failedStageId
300
307
  ? run.stages.find((stage) => stage.id === run.failedStageId)
301
308
  : undefined;
302
- const error = run.error ?? (kind === "blocked" ? run.exitReason : undefined);
309
+ const error = run.error ?? returnedNoticeError(run, kind) ?? (kind === "blocked" ? run.exitReason : undefined);
303
310
  return {
304
311
  kind,
305
312
  scope: "run",
306
313
  runId: run.id,
307
314
  workflowName: run.name,
308
- status: run.status,
315
+ status: effectiveRunStatus(run),
309
316
  ...(error ? { error: truncateSnippet(error) } : {}),
310
317
  ...(run.failedStageId ? { failedStageId: run.failedStageId } : {}),
311
318
  ...(failedStage ? { stageId: failedStage.id, stageName: failedStage.name } : {}),
@@ -330,13 +337,22 @@ function jsonString(value: string): string {
330
337
  }
331
338
 
332
339
  function terminalNoticeKind(run: RunSnapshot): "completed" | "failed" | "blocked" | undefined {
333
- if (run.status === "failed" || run.status === "blocked") return run.status;
334
- if (run.status !== "completed") return undefined;
335
- const returnedStatus = run.result?.["status"];
336
- if (returnedStatus === "failed" || returnedStatus === "blocked") return returnedStatus;
340
+ const status = effectiveRunStatus(run);
341
+ if (status === "failed" || status === "blocked") return status;
342
+ if (status !== "completed") return undefined;
337
343
  return "completed";
338
344
  }
339
345
 
346
+ function returnedNoticeError(run: RunSnapshot, kind: "completed" | "failed" | "blocked"): string | undefined {
347
+ const structuredFailureText = structuredRecoverableWorkflowFailureText(run);
348
+ if (kind === "blocked" && structuredFailureText !== undefined) return structuredFailureText;
349
+ const returnedStatus = normalizeReturnedWorkflowStatus(run.result?.["status"]);
350
+ if (returnedStatus === undefined) return undefined;
351
+ if (kind === "failed" && returnedStatus === "failed") return actionableReturnedStatusText(run.result);
352
+ if (kind === "blocked" && isReturnedBlockedWorkflowStatus(returnedStatus)) return actionableReturnedStatusText(run.result);
353
+ return undefined;
354
+ }
355
+
340
356
  function terminalRunKey(kind: "completed" | "failed" | "blocked", runId: string): string {
341
357
  return `${kind}:${runId}`;
342
358
  }
@@ -61,7 +61,7 @@ export type PiArgumentCompletionResult = PiArgumentCompletion[] | null;
61
61
  export interface PiCommandOptions {
62
62
  description: string;
63
63
  handler: (args: string, ctx: PiCommandContext) => Promise<void> | void;
64
- getArgumentCompletions?: (partial: string) => PiArgumentCompletionResult;
64
+ getArgumentCompletions?: (partial: string) => PiArgumentCompletionResult | Promise<PiArgumentCompletionResult>;
65
65
  }
66
66
 
67
67
  export interface PiRuntimeModel {
@@ -13,8 +13,9 @@ function completeToken(
13
13
  : Math.max(argumentText.lastIndexOf(" "), argumentText.lastIndexOf("\t")) + 1;
14
14
  const head = argumentText.slice(0, tokenStart);
15
15
  const token = argumentText.slice(tokenStart);
16
+ const normalizedToken = token.trimEnd();
16
17
  const filtered = candidates
17
- .filter((candidate) => candidate.value.startsWith(token))
18
+ .filter((candidate) => candidate.value.startsWith(token) && candidate.value.trimEnd() !== normalizedToken)
18
19
  .map((candidate) => ({ ...candidate, value: `${head}${candidate.value}` }));
19
20
  return filtered.length > 0 ? filtered : null;
20
21
  }
@@ -50,6 +51,17 @@ function runIdItems(): PiArgumentCompletion[] {
50
51
  }));
51
52
  }
52
53
 
54
+ export function workflowArgumentCompletionsNeedWorkflowResources(partial: string): boolean {
55
+ const parts = partial.trim().split(/\s+/).filter(Boolean);
56
+ const subcommand = parts[0] ?? "";
57
+ if (!partial.includes(" ")) return true;
58
+ if (!subcommand || subcommand === "inputs") return true;
59
+ if (["status", "connect", "resume", "attach", "pause", "interrupt", "kill", "reload"].includes(subcommand)) {
60
+ return false;
61
+ }
62
+ return true;
63
+ }
64
+
53
65
  export function workflowArgumentCompletions(
54
66
  partial: string,
55
67
  runtime: ExtensionRuntime,
@@ -12,7 +12,7 @@ import type { GraphOverlayPort } from "../tui/overlay-adapter.js";
12
12
  import type { ExtensionRuntime } from "./runtime.js";
13
13
  import type { WorkflowInputEntry, WorkflowToolResult } from "./render-result.js";
14
14
  import type { ExtensionAPI, PiCommandContext, PiArgumentCompletionResult } from "./public-types.js";
15
- import { workflowArgumentCompletions } from "./workflow-command-completions.js";
15
+ import { workflowArgumentCompletions, workflowArgumentCompletionsNeedWorkflowResources } from "./workflow-command-completions.js";
16
16
  import {
17
17
  createWorkflowCommandReporter,
18
18
  emitWorkflowCommandOutput,
@@ -23,7 +23,7 @@ import {
23
23
  type WorkflowCommandHandler,
24
24
  type WorkflowCommandOutputDetails,
25
25
  } from "./workflow-command-utils.js";
26
- import { emitTerminalRunDetailSurface } from "./workflow-command-surfaces.js";
26
+ import { emitTerminalRunDetailSurface, formatWorkflowResourceLoadWarning } from "./workflow-command-surfaces.js";
27
27
  import { handleRunControlCommand, type WorkflowRunControlDeps } from "./workflow-run-control-command.js";
28
28
  import { workflowPolicyFromContext } from "./workflow-policy.js";
29
29
  import {
@@ -39,6 +39,7 @@ export interface WorkflowSlashCommandDeps {
39
39
  runtimeForContext: (ctx?: PiCommandContext) => ExtensionRuntime;
40
40
  overlay: GraphOverlayPort;
41
41
  reloadWorkflowResources: () => Promise<void> | void;
42
+ ensureWorkflowResourcesLoaded: () => Promise<void> | void;
42
43
  runWithLifecycleSuppressedForPolicy: <T>(
43
44
  policy: WorkflowExecutionPolicy,
44
45
  fn: () => Promise<T>,
@@ -57,8 +58,11 @@ export function registerWorkflowSlashCommand(
57
58
  {
58
59
  description: "Run or inspect Atomic workflows. Usage: /workflow <name> [key=value…] | /workflow [list|status|connect|attach|interrupt|kill|pause|resume|inputs|reload] [args]",
59
60
  handler: (args, ctx) => workflowSlashHandler(args, ctx, pi, deps),
60
- getArgumentCompletions: (partial: string): PiArgumentCompletionResult =>
61
- workflowArgumentCompletions(partial, deps.runtimeProxy),
61
+ getArgumentCompletions: (partial: string): PiArgumentCompletionResult | Promise<PiArgumentCompletionResult> => {
62
+ const buildCompletions = (): PiArgumentCompletionResult => workflowArgumentCompletions(partial, deps.runtimeProxy);
63
+ if (!workflowArgumentCompletionsNeedWorkflowResources(partial)) return buildCompletions();
64
+ return Promise.resolve(deps.ensureWorkflowResourcesLoaded()).then(buildCompletions).catch(buildCompletions);
65
+ },
62
66
  },
63
67
  workflowCommands,
64
68
  );
@@ -76,10 +80,18 @@ async function workflowSlashHandler(
76
80
  const fail = (msg: string): void => reporter.error(msg);
77
81
  const withImplicitYesFlag = (tokens: string[]): string[] =>
78
82
  tokens.some((t) => t === "--yes" || t === "-y") ? tokens : [...tokens, "-y"];
83
+ const ensureWorkflowResourcesVisible = async (): Promise<void> => {
84
+ try {
85
+ await deps.ensureWorkflowResourcesLoaded();
86
+ } catch (error) {
87
+ ctx.ui?.notify(formatWorkflowResourceLoadWarning(error), "warning");
88
+ }
89
+ };
79
90
  const showWorkflowInputs = async (
80
91
  workflowName: string,
81
92
  command: WorkflowCommandOutputDetails["command"] = "inputs",
82
93
  ): Promise<void> => {
94
+ await ensureWorkflowResourcesVisible();
83
95
  const result = await deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: {}, action: "inputs" }, { policy });
84
96
  if (result.action !== "inputs" || !("inputs" in result)) return;
85
97
  const inputResult = result as Extract<WorkflowToolResult, { action: "inputs" }>;
@@ -99,6 +111,7 @@ async function workflowSlashHandler(
99
111
  return;
100
112
  }
101
113
  if (!subcommand || subcommand === "list") {
114
+ await ensureWorkflowResourcesVisible();
102
115
  const items = deps.runtimeProxy.registry.all().map((def) => ({
103
116
  name: def.normalizedName,
104
117
  description: def.description,
@@ -163,6 +176,7 @@ async function workflowSlashHandler(
163
176
  typeof ctx.ui?.setEditorComponent === "function" || typeof ctx.ui?.custom === "function"
164
177
  );
165
178
  if (canOpenPicker) {
179
+ await ensureWorkflowResourcesVisible();
166
180
  const schemaResult = await deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: {}, action: "inputs" }, { policy });
167
181
  const schema = schemaResult.action === "inputs" && "inputs" in schemaResult
168
182
  ? (schemaResult as Extract<WorkflowToolResult, { action: "inputs" }>)
@@ -185,6 +199,7 @@ async function workflowSlashHandler(
185
199
  }
186
200
  }
187
201
 
202
+ await ensureWorkflowResourcesVisible();
188
203
  const result = await deps.runWithLifecycleSuppressedForPolicy(policy, () =>
189
204
  deps.runtimeForContext(ctx).dispatch({ workflow: workflowName, inputs: mergedInputs, action: "run" }, { policy }),
190
205
  );