@bastani/atomic 0.9.3 → 0.9.4-alpha.10

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 (312) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +30 -0
  3. package/dist/builtin/cursor/package.json +4 -4
  4. package/dist/builtin/intercom/CHANGELOG.md +30 -0
  5. package/dist/builtin/intercom/package.json +2 -2
  6. package/dist/builtin/mcp/CHANGELOG.md +30 -0
  7. package/dist/builtin/mcp/package.json +4 -4
  8. package/dist/builtin/subagents/CHANGELOG.md +51 -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/debugger.md +1 -1
  17. package/dist/builtin/subagents/agents/worker.md +2 -2
  18. package/dist/builtin/subagents/package.json +4 -4
  19. package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +11 -1
  20. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +11 -1
  21. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +2 -0
  22. package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +27 -4
  23. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +25 -0
  24. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
  25. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
  26. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
  27. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +1 -0
  28. package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +2 -0
  29. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +3 -1
  30. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +22 -0
  31. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +15 -5
  32. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +5 -1
  33. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  34. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -1
  35. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +4 -1
  36. package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +126 -0
  37. package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +53 -0
  38. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +52 -53
  39. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +2 -0
  40. package/dist/builtin/subagents/src/shared/model-info.ts +16 -0
  41. package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
  42. package/dist/builtin/web-access/CHANGELOG.md +30 -0
  43. package/dist/builtin/web-access/package.json +2 -2
  44. package/dist/builtin/workflows/CHANGELOG.md +54 -0
  45. package/dist/builtin/workflows/README.md +4 -4
  46. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +24 -22
  47. package/dist/builtin/workflows/builtin/goal-runner.ts +12 -23
  48. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +4 -4
  49. package/dist/builtin/workflows/builtin/ralph-models.ts +53 -75
  50. package/dist/builtin/workflows/package.json +2 -2
  51. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +4 -2
  52. package/dist/builtin/workflows/src/engine/run-returned-status.ts +41 -0
  53. package/dist/builtin/workflows/src/engine/run.ts +6 -4
  54. package/dist/builtin/workflows/src/extension/config-file-loader.ts +1 -1
  55. package/dist/builtin/workflows/src/extension/config-loader.ts +1 -1
  56. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +37 -12
  57. package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +59 -1
  58. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +1 -1
  59. package/dist/builtin/workflows/src/shared/store-public-types.ts +3 -2
  60. package/dist/builtin/workflows/src/shared/store-run-methods.ts +6 -1
  61. package/dist/bun/cli.d.ts.map +1 -1
  62. package/dist/bun/cli.js +3 -2
  63. package/dist/bun/cli.js.map +1 -1
  64. package/dist/cli/file-processor.d.ts.map +1 -1
  65. package/dist/cli/file-processor.js +13 -25
  66. package/dist/cli/file-processor.js.map +1 -1
  67. package/dist/cli.d.ts.map +1 -1
  68. package/dist/cli.js +15 -5
  69. package/dist/cli.js.map +1 -1
  70. package/dist/config.d.ts +10 -3
  71. package/dist/config.d.ts.map +1 -1
  72. package/dist/config.js +18 -1
  73. package/dist/config.js.map +1 -1
  74. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  75. package/dist/core/agent-session-auto-compaction.js +8 -1
  76. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  77. package/dist/core/agent-session-events.d.ts.map +1 -1
  78. package/dist/core/agent-session-events.js +14 -8
  79. package/dist/core/agent-session-events.js.map +1 -1
  80. package/dist/core/agent-session-export.d.ts.map +1 -1
  81. package/dist/core/agent-session-export.js +4 -2
  82. package/dist/core/agent-session-export.js.map +1 -1
  83. package/dist/core/agent-session-extension-bindings.d.ts +5 -1
  84. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  85. package/dist/core/agent-session-extension-bindings.js +18 -7
  86. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  87. package/dist/core/agent-session-methods.d.ts +9 -2
  88. package/dist/core/agent-session-methods.d.ts.map +1 -1
  89. package/dist/core/agent-session-methods.js.map +1 -1
  90. package/dist/core/agent-session-prompt.d.ts +2 -0
  91. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  92. package/dist/core/agent-session-prompt.js +15 -2
  93. package/dist/core/agent-session-prompt.js.map +1 -1
  94. package/dist/core/agent-session-retry.d.ts +2 -0
  95. package/dist/core/agent-session-retry.d.ts.map +1 -1
  96. package/dist/core/agent-session-retry.js +66 -7
  97. package/dist/core/agent-session-retry.js.map +1 -1
  98. package/dist/core/agent-session-state.d.ts.map +1 -1
  99. package/dist/core/agent-session-state.js +2 -2
  100. package/dist/core/agent-session-state.js.map +1 -1
  101. package/dist/core/agent-session-tool-hooks.d.ts +7 -1
  102. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  103. package/dist/core/agent-session-tool-hooks.js +26 -4
  104. package/dist/core/agent-session-tool-hooks.js.map +1 -1
  105. package/dist/core/agent-session-tree.d.ts.map +1 -1
  106. package/dist/core/agent-session-tree.js +3 -1
  107. package/dist/core/agent-session-tree.js.map +1 -1
  108. package/dist/core/agent-session-types.d.ts +4 -0
  109. package/dist/core/agent-session-types.d.ts.map +1 -1
  110. package/dist/core/agent-session-types.js.map +1 -1
  111. package/dist/core/agent-session.d.ts +1 -0
  112. package/dist/core/agent-session.d.ts.map +1 -1
  113. package/dist/core/agent-session.js +1 -0
  114. package/dist/core/agent-session.js.map +1 -1
  115. package/dist/core/copilot-model-catalog.d.ts +38 -74
  116. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  117. package/dist/core/copilot-model-catalog.js +177 -68
  118. package/dist/core/copilot-model-catalog.js.map +1 -1
  119. package/dist/core/copilot-model-static-fallbacks.d.ts +43 -0
  120. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -0
  121. package/dist/core/copilot-model-static-fallbacks.js +50 -0
  122. package/dist/core/copilot-model-static-fallbacks.js.map +1 -0
  123. package/dist/core/copilot-model-synthesis.d.ts +10 -0
  124. package/dist/core/copilot-model-synthesis.d.ts.map +1 -0
  125. package/dist/core/copilot-model-synthesis.js +89 -0
  126. package/dist/core/copilot-model-synthesis.js.map +1 -0
  127. package/dist/core/extensions/api-types.d.ts +2 -1
  128. package/dist/core/extensions/api-types.d.ts.map +1 -1
  129. package/dist/core/extensions/api-types.js.map +1 -1
  130. package/dist/core/extensions/index.d.ts +1 -1
  131. package/dist/core/extensions/index.d.ts.map +1 -1
  132. package/dist/core/extensions/index.js.map +1 -1
  133. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  134. package/dist/core/extensions/loader-core.js +3 -3
  135. package/dist/core/extensions/loader-core.js.map +1 -1
  136. package/dist/core/extensions/loader-virtual-modules.d.ts +11 -0
  137. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  138. package/dist/core/extensions/loader-virtual-modules.js +79 -11
  139. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  140. package/dist/core/extensions/session-events.d.ts +7 -1
  141. package/dist/core/extensions/session-events.d.ts.map +1 -1
  142. package/dist/core/extensions/session-events.js.map +1 -1
  143. package/dist/core/http-dispatcher.d.ts +5 -3
  144. package/dist/core/http-dispatcher.d.ts.map +1 -1
  145. package/dist/core/http-dispatcher.js +56 -10
  146. package/dist/core/http-dispatcher.js.map +1 -1
  147. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  148. package/dist/core/model-registry-builtins.js +40 -6
  149. package/dist/core/model-registry-builtins.js.map +1 -1
  150. package/dist/core/model-registry.d.ts.map +1 -1
  151. package/dist/core/model-registry.js +4 -3
  152. package/dist/core/model-registry.js.map +1 -1
  153. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  154. package/dist/core/model-resolver-defaults.js +1 -1
  155. package/dist/core/model-resolver-defaults.js.map +1 -1
  156. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  157. package/dist/core/resource-loader-reload.js +6 -3
  158. package/dist/core/resource-loader-reload.js.map +1 -1
  159. package/dist/core/resource-loader-types.d.ts +6 -0
  160. package/dist/core/resource-loader-types.d.ts.map +1 -1
  161. package/dist/core/resource-loader-types.js.map +1 -1
  162. package/dist/core/session-manager-core.d.ts +2 -12
  163. package/dist/core/session-manager-core.d.ts.map +1 -1
  164. package/dist/core/session-manager-core.js +8 -16
  165. package/dist/core/session-manager-core.js.map +1 -1
  166. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  167. package/dist/core/settings-manager-basic-accessors.js +1 -1
  168. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  169. package/dist/core/settings-manager-resource-accessors.d.ts +1 -0
  170. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  171. package/dist/core/settings-manager-resource-accessors.js +11 -0
  172. package/dist/core/settings-manager-resource-accessors.js.map +1 -1
  173. package/dist/core/settings-manager-ui-accessors.d.ts +2 -0
  174. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  175. package/dist/core/settings-manager-ui-accessors.js +9 -0
  176. package/dist/core/settings-manager-ui-accessors.js.map +1 -1
  177. package/dist/core/settings-types.d.ts +2 -0
  178. package/dist/core/settings-types.d.ts.map +1 -1
  179. package/dist/core/settings-types.js.map +1 -1
  180. package/dist/core/timings.d.ts +7 -5
  181. package/dist/core/timings.d.ts.map +1 -1
  182. package/dist/core/timings.js +42 -24
  183. package/dist/core/timings.js.map +1 -1
  184. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  185. package/dist/core/tools/bash-pty-native.js +2 -2
  186. package/dist/core/tools/bash-pty-native.js.map +1 -1
  187. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  188. package/dist/core/tools/read-selectors.js +19 -1
  189. package/dist/core/tools/read-selectors.js.map +1 -1
  190. package/dist/core/tools/read.d.ts.map +1 -1
  191. package/dist/core/tools/read.js +12 -23
  192. package/dist/core/tools/read.js.map +1 -1
  193. package/dist/core/tools/search-native.d.ts.map +1 -1
  194. package/dist/core/tools/search-native.js +2 -2
  195. package/dist/core/tools/search-native.js.map +1 -1
  196. package/dist/index-extensions.d.ts +1 -1
  197. package/dist/index-extensions.d.ts.map +1 -1
  198. package/dist/index-extensions.js.map +1 -1
  199. package/dist/index.d.ts +1 -1
  200. package/dist/index.d.ts.map +1 -1
  201. package/dist/index.js.map +1 -1
  202. package/dist/main-session.d.ts.map +1 -1
  203. package/dist/main-session.js +14 -5
  204. package/dist/main-session.js.map +1 -1
  205. package/dist/main.d.ts.map +1 -1
  206. package/dist/main.js +56 -32
  207. package/dist/main.js.map +1 -1
  208. package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  209. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  210. package/dist/modes/interactive/components/assistant-message.js +22 -15
  211. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  212. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
  213. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  214. package/dist/modes/interactive/components/chat-message-renderer.js +5 -5
  215. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  216. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/chat-session-host-events.js +8 -0
  218. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  219. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  220. package/dist/modes/interactive/components/chat-session-host-rendering.js +1 -0
  221. package/dist/modes/interactive/components/chat-session-host-rendering.js.map +1 -1
  222. package/dist/modes/interactive/components/user-message.d.ts +6 -2
  223. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  224. package/dist/modes/interactive/components/user-message.js +16 -5
  225. package/dist/modes/interactive/components/user-message.js.map +1 -1
  226. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  227. package/dist/modes/interactive/interactive-agent-events.js +6 -1
  228. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  229. package/dist/modes/interactive/interactive-deferred-startup.d.ts +2 -0
  230. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -0
  231. package/dist/modes/interactive/interactive-deferred-startup.js +59 -0
  232. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -0
  233. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  234. package/dist/modes/interactive/interactive-editor-actions.js +1 -0
  235. package/dist/modes/interactive/interactive-editor-actions.js.map +1 -1
  236. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  237. package/dist/modes/interactive/interactive-extension-context.js +1 -0
  238. package/dist/modes/interactive/interactive-extension-context.js.map +1 -1
  239. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  240. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  241. package/dist/modes/interactive/interactive-mode-base.js +6 -0
  242. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  243. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  244. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  245. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  246. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  247. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -0
  248. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  249. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  250. package/dist/modes/interactive/interactive-mode-types.d.ts +2 -0
  251. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  252. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  253. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  254. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  255. package/dist/modes/interactive/interactive-mode.js +1 -0
  256. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  257. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  258. package/dist/modes/interactive/interactive-model-routing.js +2 -4
  259. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  260. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  261. package/dist/modes/interactive/interactive-render-chat.js +4 -3
  262. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  263. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  264. package/dist/modes/interactive/interactive-session-runtime.js +5 -2
  265. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  266. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  267. package/dist/modes/interactive/interactive-startup.js +11 -1
  268. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  269. package/dist/modes/print-mode.d.ts.map +1 -1
  270. package/dist/modes/print-mode.js +15 -13
  271. package/dist/modes/print-mode.js.map +1 -1
  272. package/dist/modes/rpc/rpc-client.d.ts +28 -51
  273. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  274. package/dist/modes/rpc/rpc-client.js +27 -51
  275. package/dist/modes/rpc/rpc-client.js.map +1 -1
  276. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  277. package/dist/modes/rpc/rpc-command-handler.js +19 -0
  278. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  279. package/dist/modes/rpc/rpc-types.d.ts +26 -0
  280. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  281. package/dist/modes/rpc/rpc-types.js.map +1 -1
  282. package/dist/rpc-entry.d.ts +3 -0
  283. package/dist/rpc-entry.d.ts.map +1 -0
  284. package/dist/rpc-entry.js +12 -0
  285. package/dist/rpc-entry.js.map +1 -0
  286. package/dist/utils/image-convert.d.ts +1 -0
  287. package/dist/utils/image-convert.d.ts.map +1 -1
  288. package/dist/utils/image-convert.js +21 -15
  289. package/dist/utils/image-convert.js.map +1 -1
  290. package/dist/utils/image-process.d.ts +18 -0
  291. package/dist/utils/image-process.d.ts.map +1 -0
  292. package/dist/utils/image-process.js +83 -0
  293. package/dist/utils/image-process.js.map +1 -0
  294. package/dist/utils/mime.d.ts.map +1 -1
  295. package/dist/utils/mime.js +41 -0
  296. package/dist/utils/mime.js.map +1 -1
  297. package/dist/utils/module-require.d.ts +11 -0
  298. package/dist/utils/module-require.d.ts.map +1 -0
  299. package/dist/utils/module-require.js +16 -0
  300. package/dist/utils/module-require.js.map +1 -0
  301. package/docs/compaction.md +2 -0
  302. package/docs/development.md +10 -0
  303. package/docs/extensions.md +11 -0
  304. package/docs/models.md +3 -3
  305. package/docs/providers.md +2 -1
  306. package/docs/rpc.md +60 -2
  307. package/docs/sdk.md +1 -1
  308. package/docs/settings.md +8 -5
  309. package/docs/subagents.md +6 -0
  310. package/docs/workflows.md +9 -5
  311. package/npm-shrinkwrap.json +4266 -0
  312. package/package.json +23 -13
@@ -1,27 +1,42 @@
1
1
  import { reviewDecisionSchema } from "./ralph-core.js";
2
2
 
3
+ // Model chains are curated from Atomic's agentic-coding benchmark
4
+ // (pass@1 / avg $ per task, 2026-07-02):
5
+ // - Placement principle: REVIEWERS get best-in-class verification quality
6
+ // (fable-5:xhigh 70%/$13.41 leads reviewer-A and the goal reviewer);
7
+ // EVERY OTHER stage gets the best measured performance-per-dollar
8
+ // (gpt-5.5:xhigh 67%/$7.23 for hard stages, gpt-5.5:medium 54%/$2.75 for
9
+ // workhorse stages, gpt-5.5:low $1.20 for retrieval).
10
+ // - Pareto frontier: gpt-5.5 low ($1.20/27%) → gpt-5.5 medium ($2.75/54%) →
11
+ // fable-5 low ($3.76/60%) → gpt-5.5 high ($5.10/64%) → fable-5 medium
12
+ // ($6.09/65%) → gpt-5.5 xhigh ($7.23/67%) → fable-5 high ($9.18/69%) →
13
+ // fable-5 xhigh ($13.41/70%).
14
+ // - Dropped as strictly dominated: claude-sonnet-5 (40-54% at $4-26, up to
15
+ // 268 steps), claude-sonnet-4.6 (30%/$5.52), gemini-3.1-pro (12%/$9.48),
16
+ // gemini-3.5-flash (37%/$7.34, 276k output tokens).
17
+ // - claude-opus-4.8 rides at :high — its value point (52%/$4.28); :xhigh
18
+ // doubles the cost for +2pts.
19
+ // - glm-5.2 is reviewer-C's diversity primary only (third model family
20
+ // decorrelates review errors); elsewhere it is a budget fallback. Note:
21
+ // GLM-5.2 has only two real reasoning tiers — its thinkingLevelMap collapses
22
+ // minimal/low/medium/high to "high" and xhigh to "max" — so chains only use
23
+ // :high (budget tier, 36%/$2.84) or :xhigh (best tier, 44%/$3.92); the
24
+ // openrouter/z-ai mirror maps :xhigh exclusively, so it is always :xhigh.
25
+
3
26
  export const promptEngineerModelConfig = {
4
- model: "anthropic/claude-fable-5:xhigh",
27
+ model: "openai-codex/gpt-5.5:xhigh",
5
28
  fallbackModels: [
6
- "openai-codex/gpt-5.5:xhigh",
7
29
  "github-copilot/gpt-5.5:xhigh",
8
30
  "openai/gpt-5.5:xhigh",
9
- "github-copilot/claude-opus-4.8 (1m):xhigh",
10
- "anthropic/claude-opus-4-8:xhigh",
31
+ "anthropic/claude-fable-5:xhigh",
32
+ "github-copilot/claude-opus-4.8 (1m):high",
33
+ "anthropic/claude-opus-4-8:high",
11
34
  "zai/glm-5.2:xhigh",
12
35
  "zai-coding-cn/glm-5.2:xhigh",
13
- "github-copilot/gemini-3.5-flash (1m):high",
14
- "google/gemini-3.5-flash:high",
15
- "google-vertex/gemini-3.5-flash:high",
16
- "github-copilot/gemini-3.1-pro-preview (1m):high",
17
- "google/gemini-3.1-pro-preview:high",
18
- "google-vertex/gemini-3.1-pro-preview:high",
19
- "openrouter/anthropic/claude-fable-5:xhigh",
20
36
  "openrouter/openai/gpt-5.5:xhigh",
21
- "openrouter/anthropic/claude-opus-4-8:xhigh",
22
- "openrouter/z-ai/glm-5.2:xhigh",
23
- "openrouter/google/gemini-3.5-flash:high",
24
- "openrouter/google/gemini-3.1-pro-preview:high"
37
+ "openrouter/anthropic/claude-fable-5:xhigh",
38
+ "openrouter/anthropic/claude-opus-4-8:high",
39
+ "openrouter/z-ai/glm-5.2:xhigh"
25
40
  ],
26
41
  excludedTools: ["ask_user_question"],
27
42
  };
@@ -31,21 +46,15 @@ export const researchModelConfig = {
31
46
  fallbackModels: [
32
47
  "github-copilot/gpt-5.5:medium",
33
48
  "openai/gpt-5.5:medium",
49
+ "anthropic/claude-fable-5:low",
34
50
  "github-copilot/claude-opus-4.8 (1m):medium",
35
51
  "anthropic/claude-opus-4-8:medium",
36
- "zai/glm-5.2:medium",
37
- "zai-coding-cn/glm-5.2:medium",
38
- "github-copilot/gemini-3.5-flash (1m):medium",
39
- "google/gemini-3.5-flash:medium",
40
- "google-vertex/gemini-3.5-flash:medium",
41
- "github-copilot/gemini-3.1-pro-preview (1m):medium",
42
- "google/gemini-3.1-pro-preview:medium",
43
- "google-vertex/gemini-3.1-pro-preview:medium",
52
+ "zai/glm-5.2:high",
53
+ "zai-coding-cn/glm-5.2:high",
44
54
  "openrouter/openai/gpt-5.5:medium",
55
+ "openrouter/anthropic/claude-fable-5:low",
45
56
  "openrouter/anthropic/claude-opus-4-8:medium",
46
- "openrouter/z-ai/glm-5.2:medium",
47
- "openrouter/google/gemini-3.5-flash:medium",
48
- "openrouter/google/gemini-3.1-pro-preview:medium"
57
+ "openrouter/z-ai/glm-5.2:xhigh"
49
58
  ],
50
59
  excludedTools: ["ask_user_question"],
51
60
  };
@@ -55,21 +64,15 @@ export const orchestratorModelConfig = {
55
64
  fallbackModels: [
56
65
  "github-copilot/gpt-5.5:medium",
57
66
  "openai/gpt-5.5:medium",
67
+ "anthropic/claude-fable-5:low",
58
68
  "github-copilot/claude-opus-4.8 (1m):medium",
59
69
  "anthropic/claude-opus-4-8:medium",
60
- "zai/glm-5.2:medium",
61
- "zai-coding-cn/glm-5.2:medium",
62
- "github-copilot/gemini-3.5-flash (1m):medium",
63
- "google/gemini-3.5-flash:medium",
64
- "google-vertex/gemini-3.5-flash:medium",
65
- "github-copilot/gemini-3.1-pro-preview (1m):medium",
66
- "google/gemini-3.1-pro-preview:medium",
67
- "google-vertex/gemini-3.1-pro-preview:medium",
70
+ "zai/glm-5.2:high",
71
+ "zai-coding-cn/glm-5.2:high",
68
72
  "openrouter/openai/gpt-5.5:medium",
73
+ "openrouter/anthropic/claude-fable-5:low",
69
74
  "openrouter/anthropic/claude-opus-4-8:medium",
70
- "openrouter/z-ai/glm-5.2:medium",
71
- "openrouter/google/gemini-3.5-flash:medium",
72
- "openrouter/google/gemini-3.1-pro-preview:medium"
75
+ "openrouter/z-ai/glm-5.2:xhigh"
73
76
  ],
74
77
  excludedTools: ["ask_user_question"],
75
78
  };
@@ -77,25 +80,17 @@ export const orchestratorModelConfig = {
77
80
  export const reviewerAModelConfig = {
78
81
  model: "anthropic/claude-fable-5:xhigh",
79
82
  fallbackModels: [
80
- "github-copilot/claude-opus-4.8 (1m):xhigh",
81
- "anthropic/claude-opus-4-8:xhigh",
82
83
  "openai-codex/gpt-5.5:xhigh",
83
84
  "github-copilot/gpt-5.5:xhigh",
84
85
  "openai/gpt-5.5:xhigh",
86
+ "github-copilot/claude-opus-4.8 (1m):high",
87
+ "anthropic/claude-opus-4-8:high",
85
88
  "zai/glm-5.2:xhigh",
86
89
  "zai-coding-cn/glm-5.2:xhigh",
87
- "github-copilot/gemini-3.5-flash (1m):high",
88
- "google/gemini-3.5-flash:high",
89
- "google-vertex/gemini-3.5-flash:high",
90
- "github-copilot/gemini-3.1-pro-preview (1m):high",
91
- "google/gemini-3.1-pro-preview:high",
92
- "google-vertex/gemini-3.1-pro-preview:high",
93
90
  "openrouter/anthropic/claude-fable-5:xhigh",
94
- "openrouter/anthropic/claude-opus-4-8:xhigh",
95
91
  "openrouter/openai/gpt-5.5:xhigh",
96
- "openrouter/z-ai/glm-5.2:xhigh",
97
- "openrouter/google/gemini-3.5-flash:high",
98
- "openrouter/google/gemini-3.1-pro-preview:high"
92
+ "openrouter/anthropic/claude-opus-4-8:high",
93
+ "openrouter/z-ai/glm-5.2:xhigh"
99
94
  ],
100
95
  excludedTools: ["ask_user_question"],
101
96
  schema: reviewDecisionSchema,
@@ -107,22 +102,14 @@ export const reviewerBModelConfig = {
107
102
  "github-copilot/gpt-5.5:xhigh",
108
103
  "openai/gpt-5.5:xhigh",
109
104
  "anthropic/claude-fable-5:xhigh",
110
- "github-copilot/claude-opus-4.8 (1m):xhigh",
111
- "anthropic/claude-opus-4-8:xhigh",
105
+ "github-copilot/claude-opus-4.8 (1m):high",
106
+ "anthropic/claude-opus-4-8:high",
112
107
  "zai/glm-5.2:xhigh",
113
108
  "zai-coding-cn/glm-5.2:xhigh",
114
- "github-copilot/gemini-3.5-flash (1m):high",
115
- "google/gemini-3.5-flash:high",
116
- "google-vertex/gemini-3.5-flash:high",
117
- "github-copilot/gemini-3.1-pro-preview (1m):high",
118
- "google/gemini-3.1-pro-preview:high",
119
- "google-vertex/gemini-3.1-pro-preview:high",
120
109
  "openrouter/openai/gpt-5.5:xhigh",
121
110
  "openrouter/anthropic/claude-fable-5:xhigh",
122
- "openrouter/anthropic/claude-opus-4-8:xhigh",
123
- "openrouter/z-ai/glm-5.2:xhigh",
124
- "openrouter/google/gemini-3.5-flash:high",
125
- "openrouter/google/gemini-3.1-pro-preview:high"
111
+ "openrouter/anthropic/claude-opus-4-8:high",
112
+ "openrouter/z-ai/glm-5.2:xhigh"
126
113
  ],
127
114
  excludedTools: ["ask_user_question"],
128
115
  schema: reviewDecisionSchema,
@@ -132,26 +119,17 @@ export const reviewerCModelConfig = {
132
119
  model: "zai/glm-5.2:xhigh",
133
120
  fallbackModels: [
134
121
  "zai-coding-cn/glm-5.2:xhigh",
135
- "github-copilot/gemini-3.5-flash (1m):high",
136
- "google/gemini-3.5-flash:high",
137
- "google-vertex/gemini-3.5-flash:high",
138
- "github-copilot/gemini-3.1-pro-preview (1m):high",
139
- "google/gemini-3.1-pro-preview:high",
140
- "google-vertex/gemini-3.1-pro-preview:high",
122
+ "openrouter/z-ai/glm-5.2:xhigh",
141
123
  "openai-codex/gpt-5.5:xhigh",
142
124
  "github-copilot/gpt-5.5:xhigh",
143
125
  "openai/gpt-5.5:xhigh",
144
126
  "anthropic/claude-fable-5:xhigh",
145
- "github-copilot/claude-opus-4.8 (1m):xhigh",
146
- "anthropic/claude-opus-4-8:xhigh",
147
- "openrouter/anthropic/claude-fable-5:xhigh",
148
- "openrouter/anthropic/claude-opus-4-8:xhigh",
127
+ "github-copilot/claude-opus-4.8 (1m):high",
128
+ "anthropic/claude-opus-4-8:high",
149
129
  "openrouter/openai/gpt-5.5:xhigh",
150
- "openrouter/z-ai/glm-5.2:xhigh",
151
- "openrouter/google/gemini-3.5-flash:high",
152
- "openrouter/google/gemini-3.1-pro-preview:high"
130
+ "openrouter/anthropic/claude-fable-5:xhigh",
131
+ "openrouter/anthropic/claude-opus-4-8:high"
153
132
  ],
154
133
  excludedTools: ["ask_user_question"],
155
134
  schema: reviewDecisionSchema,
156
135
  };
157
-
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/workflows",
3
- "version": "0.9.3",
3
+ "version": "0.9.4-alpha.10",
4
4
  "private": true,
5
5
  "description": "Atomic extension for multi-stage workflow authoring and execution.",
6
6
  "contributors": [
@@ -83,7 +83,7 @@
83
83
  },
84
84
  "peerDependencies": {
85
85
  "@bastani/atomic": "*",
86
- "@earendil-works/pi-tui": "^0.80.2"
86
+ "@earendil-works/pi-tui": "^0.80.3"
87
87
  },
88
88
  "peerDependenciesMeta": {
89
89
  "@bastani/atomic": {
@@ -33,11 +33,13 @@ export async function finalizeDurableTerminalStatus(input: DurableTerminalFinali
33
33
  if (!input.isRoot) return;
34
34
  const status = input.runSnapshot.status;
35
35
  const isExitTerminal = input.runSnapshot.exited === true && status !== "running";
36
- if (status !== "failed" && status !== "killed" && !isExitTerminal) return;
36
+ const isReturnedBlockedTerminal = status === "blocked" && input.runSnapshot.endedAt !== undefined;
37
+ if (status !== "failed" && status !== "killed" && !isExitTerminal && !isReturnedBlockedTerminal) return;
37
38
 
38
39
  const durableStatus = toDurableStatus(status);
39
40
  if (durableStatus !== undefined) {
40
- input.durableBackend.setWorkflowStatus(input.runId, durableStatus, undefined, input.runSnapshot.resumable);
41
+ const resumable = status === "blocked" ? false : input.runSnapshot.resumable;
42
+ input.durableBackend.setWorkflowStatus(input.runId, durableStatus, undefined, resumable);
41
43
  }
42
44
  try {
43
45
  await input.durableBackend.flush?.();
@@ -0,0 +1,41 @@
1
+ import type { RunEndMetadata } from "../shared/store-public-types.js";
2
+ import type { WorkflowOutputValues } from "../shared/types.js";
3
+
4
+ export interface ReturnedRunStatus {
5
+ readonly status: "completed" | "failed" | "blocked";
6
+ readonly error?: string;
7
+ readonly metadata?: RunEndMetadata;
8
+ }
9
+
10
+ export function classifyReturnedRunStatus(result: WorkflowOutputValues | undefined): ReturnedRunStatus {
11
+ const returnedStatus = result?.["status"];
12
+ if (returnedStatus !== "failed" && returnedStatus !== "blocked") {
13
+ return { status: "completed" };
14
+ }
15
+
16
+ const summary = result?.["summary"];
17
+ const error = typeof summary === "string" && summary.trim().length > 0
18
+ ? summary.trim()
19
+ : `Workflow returned status ${JSON.stringify(returnedStatus)}.`;
20
+ return {
21
+ status: returnedStatus,
22
+ error,
23
+ metadata: returnedStatus === "failed"
24
+ ? returnedFailureMetadata(error)
25
+ : returnedBlockedMetadata(),
26
+ };
27
+ }
28
+
29
+ function returnedFailureMetadata(error: string): RunEndMetadata {
30
+ return {
31
+ failureKind: "unknown",
32
+ failureRecoverability: "non_recoverable",
33
+ failureDisposition: "terminal_failed",
34
+ failureMessage: error,
35
+ resumable: false,
36
+ };
37
+ }
38
+
39
+ function returnedBlockedMetadata(): RunEndMetadata {
40
+ return { resumable: false };
41
+ }
@@ -41,6 +41,7 @@ import {
41
41
  import { assertWorkflowRunOutputs, normalizeWorkflowRunOutput } from "../runs/foreground/executor-outputs.js";
42
42
  import { isWorkflowDefinition, workflowDefinitionRequirementMessage } from "../runs/foreground/executor-child-helpers.js";
43
43
  import { getDurableBackend } from "../durable/factory.js";
44
+ import { classifyReturnedRunStatus } from "./run-returned-status.js";
44
45
  import { createToolPrimitive, createCheckpointIdGenerator } from "../durable/tool-primitive.js";
45
46
  import { persistDurableCacheEntry } from "../durable/resume-catalog.js";
46
47
  import { createDurableStagePrimitive, createDurableTaskPrimitive, recordStageCheckpoint, createStageReplayKeyGenerator, recordCachedStageWithTracker } from "../durable/stage-primitive.js";
@@ -423,15 +424,16 @@ export async function run<
423
424
  assertWorkflowRunOutputs(def.name, result, def.outputs);
424
425
  assertWorkflowCreatedStage(runSnapshot);
425
426
  await durableBackend.flush?.();
426
- const recorded = activeStore.recordRunEnd(runId, "completed", result);
427
- appendRunEndWhenRecorded(opts.persistence, recorded, { runId, status: "completed", result, ts: Date.now() });
428
- durableBackend.setWorkflowStatus(runId, "completed");
427
+ const returned = classifyReturnedRunStatus(result);
428
+ 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() });
430
+ durableBackend.setWorkflowStatus(runId, returned.status, undefined, returned.metadata?.resumable);
429
431
  await durableBackend.flush?.();
430
432
  if (opts.persistence && durableBackend.persistent) {
431
433
  const cacheEntry = durableBackend.toCacheEntry(runId);
432
434
  if (cacheEntry) persistDurableCacheEntry(opts.persistence, cacheEntry);
433
435
  }
434
- return reconcileTerminalRunResult(runId, runSnapshot, activeStore, { status: "completed", result }, opts.onRunEnd);
436
+ return reconcileTerminalRunResult(runId, runSnapshot, activeStore, { status: returned.status, result, error: returned.error }, opts.onRunEnd);
435
437
  } catch (err) {
436
438
  const selectedExit = findWorkflowExitSignal(err, exitScope) ?? findWorkflowExitSignal(ownController.signal.reason, exitScope);
437
439
  if (selectedExit !== undefined) return await finalizers.finalizeWorkflowExit(selectedExit);
@@ -68,7 +68,7 @@ function validateConfig(value: unknown): string | null {
68
68
  }
69
69
  for (const item of notifyOn) {
70
70
  if (!isWorkflowLifecycleNoticeKind(item)) {
71
- return `"workflowNotifications.notifyOn" entries must be "completed", "failed", or "awaiting_input", got ${JSON.stringify(item)}`;
71
+ return `"workflowNotifications.notifyOn" entries must be "completed", "failed", "blocked", or "awaiting_input", got ${JSON.stringify(item)}`;
72
72
  }
73
73
  }
74
74
  }
@@ -182,7 +182,7 @@ export const WORKFLOW_CONFIG_DEFAULTS = {
182
182
  resumeInFlight: "ask" as const,
183
183
  workflowNotifications: {
184
184
  enabled: true,
185
- notifyOn: ["completed", "failed", "awaiting_input"] as const,
185
+ notifyOn: ["completed", "failed", "blocked", "awaiting_input"] as const,
186
186
  },
187
187
  } as const;
188
188
 
@@ -21,11 +21,12 @@ import { renderWorkflowNoticeCard, type WorkflowNoticeTone } from "../tui/workfl
21
21
  export const LIFECYCLE_NOTICE_CUSTOM_TYPE = "workflows:lifecycle-notice";
22
22
  export const LIFECYCLE_NOTICE_SNIPPET_LIMIT = 240;
23
23
 
24
- export type WorkflowLifecycleNoticeKind = "completed" | "failed" | "awaiting_input";
24
+ export type WorkflowLifecycleNoticeKind = "completed" | "failed" | "blocked" | "awaiting_input";
25
25
 
26
26
  export const WORKFLOW_LIFECYCLE_NOTICE_KINDS = [
27
27
  "completed",
28
28
  "failed",
29
+ "blocked",
29
30
  "awaiting_input",
30
31
  ] as const satisfies readonly WorkflowLifecycleNoticeKind[];
31
32
 
@@ -95,8 +96,9 @@ export function seedWorkflowLifecycleNotificationState(
95
96
  ): void {
96
97
  for (const run of snapshot.runs) {
97
98
  if (!isTopLevelWorkflowRun(run)) continue;
98
- if ((run.status === "completed" || run.status === "failed") && run.endedAt !== undefined) {
99
- state.deliveredTerminalRuns.add(terminalRunKey(run.status, run.id));
99
+ const noticeKind = terminalNoticeKind(run);
100
+ if (noticeKind !== undefined && run.endedAt !== undefined) {
101
+ state.deliveredTerminalRuns.add(terminalRunKey(noticeKind, run.id));
100
102
  }
101
103
  if (run.pendingPrompt !== undefined) {
102
104
  state.deliveredInputPrompts.add(runAwaitingInputKey(run.id, run.pendingPrompt));
@@ -186,9 +188,10 @@ export function installWorkflowLifecycleNotifications(
186
188
 
187
189
  const emitTerminalNoticeOnce = (
188
190
  run: RunSnapshot,
189
- kind: "completed" | "failed",
191
+ kind: "completed" | "failed" | "blocked",
190
192
  ): void => {
191
- if (run.status !== kind || run.endedAt === undefined || !notifyOn.has(kind)) {
193
+ const noticeKind = terminalNoticeKind(run);
194
+ if (noticeKind !== kind || run.endedAt === undefined || !notifyOn.has(kind)) {
192
195
  return;
193
196
  }
194
197
 
@@ -231,6 +234,7 @@ export function installWorkflowLifecycleNotifications(
231
234
  if (!isTopLevelWorkflowRun(run)) continue;
232
235
  emitTerminalNoticeOnce(run, "completed");
233
236
  emitTerminalNoticeOnce(run, "failed");
237
+ emitTerminalNoticeOnce(run, "blocked");
234
238
 
235
239
  if (!notifyOn.has("awaiting_input")) continue;
236
240
  emitRunAwaitingInputNoticeOnce(run);
@@ -273,6 +277,10 @@ export function formatWorkflowLifecycleNoticeText(details: WorkflowLifecycleNoti
273
277
  const errorText = details.error ? `: ${details.error}` : "";
274
278
  return `✗ Workflow "${workflowName}" failed (run ${details.runId}${stageText})${errorText}. Inspect: /workflow status ${details.runId}`;
275
279
  }
280
+ if (details.kind === "blocked") {
281
+ const errorText = details.error ? `: ${details.error}` : "";
282
+ return `! Workflow "${workflowName}" ended blocked (run ${details.runId})${errorText}. Inspect: /workflow status ${details.runId}`;
283
+ }
276
284
  const prompt = details.promptMessage ? ` Prompt: ${details.promptMessage}` : "";
277
285
  if (details.scope === "run") {
278
286
  return `? Workflow "${workflowName}" needs input (run ${details.runId}).${prompt} Respond: /workflow connect ${details.runId} to answer this run-level prompt.`;
@@ -286,18 +294,19 @@ export function formatWorkflowLifecycleNoticeText(details: WorkflowLifecycleNoti
286
294
 
287
295
  function makeTerminalNotice(
288
296
  run: RunSnapshot,
289
- kind: "completed" | "failed",
297
+ kind: "completed" | "failed" | "blocked",
290
298
  ): WorkflowLifecycleNoticeDetails {
291
299
  const failedStage = run.failedStageId
292
300
  ? run.stages.find((stage) => stage.id === run.failedStageId)
293
301
  : undefined;
302
+ const error = run.error ?? (kind === "blocked" ? run.exitReason : undefined);
294
303
  return {
295
304
  kind,
296
305
  scope: "run",
297
306
  runId: run.id,
298
307
  workflowName: run.name,
299
308
  status: run.status,
300
- ...(run.error ? { error: truncateSnippet(run.error) } : {}),
309
+ ...(error ? { error: truncateSnippet(error) } : {}),
301
310
  ...(run.failedStageId ? { failedStageId: run.failedStageId } : {}),
302
311
  ...(failedStage ? { stageId: failedStage.id, stageName: failedStage.name } : {}),
303
312
  ...(run.durationMs !== undefined ? { durationMs: run.durationMs } : {}),
@@ -320,7 +329,15 @@ function jsonString(value: string): string {
320
329
  return JSON.stringify(value);
321
330
  }
322
331
 
323
- function terminalRunKey(kind: "completed" | "failed", runId: string): string {
332
+ 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;
337
+ return "completed";
338
+ }
339
+
340
+ function terminalRunKey(kind: "completed" | "failed" | "blocked", runId: string): string {
324
341
  return `${kind}:${runId}`;
325
342
  }
326
343
 
@@ -361,19 +378,27 @@ function renderLifecycleNoticeCard(
361
378
  details: WorkflowLifecycleNoticeDetails,
362
379
  opts: { width: number; theme?: GraphTheme; fallbackText: string },
363
380
  ): string[] {
364
- const tone: WorkflowNoticeTone = details.kind === "failed" ? "error" : details.kind === "awaiting_input" ? "warning" : "success";
381
+ const tone: WorkflowNoticeTone = details.kind === "failed"
382
+ ? "error"
383
+ : details.kind === "awaiting_input" || details.kind === "blocked"
384
+ ? "warning"
385
+ : "success";
365
386
  const title = details.kind === "failed"
366
387
  ? "WORKFLOW FAILED"
367
388
  : details.kind === "awaiting_input"
368
389
  ? "WORKFLOW INPUT"
369
- : "WORKFLOW COMPLETE";
370
- const glyph = details.kind === "failed" ? "✗" : details.kind === "awaiting_input" ? "?" : "✓";
390
+ : details.kind === "blocked"
391
+ ? "WORKFLOW BLOCKED"
392
+ : "WORKFLOW COMPLETE";
393
+ const glyph = details.kind === "failed" ? "✗" : details.kind === "awaiting_input" ? "?" : details.kind === "blocked" ? "!" : "✓";
371
394
  const stage = details.stageName ?? details.failedStageId ?? details.stageId;
372
395
  const headline = details.kind === "failed"
373
396
  ? `Workflow "${details.workflowName}" failed`
374
397
  : details.kind === "awaiting_input"
375
398
  ? `Workflow "${details.workflowName}" needs input`
376
- : `Workflow "${details.workflowName}" completed`;
399
+ : details.kind === "blocked"
400
+ ? `Workflow "${details.workflowName}" ended blocked`
401
+ : `Workflow "${details.workflowName}" completed`;
377
402
  return renderWorkflowNoticeCard({
378
403
  title,
379
404
  glyph,
@@ -49,7 +49,9 @@ export type ModelFallbackFailureKind =
49
49
  | "rate_limit"
50
50
  | "provider_unavailable"
51
51
  | "network_timeout"
52
+ | "transport_error"
52
53
  | "model_unavailable"
54
+ | "request_incompatible"
53
55
  | "cancelled"
54
56
  | "task_failure"
55
57
  | "unknown";
@@ -76,7 +78,9 @@ const FALLBACKABLE_FAILURE_KINDS: ReadonlySet<ModelFallbackFailureKind> = new Se
76
78
  "rate_limit",
77
79
  "provider_unavailable",
78
80
  "network_timeout",
81
+ "transport_error",
79
82
  "model_unavailable",
83
+ "request_incompatible",
80
84
  ]);
81
85
 
82
86
  function asRecord(value: unknown): Record<string, unknown> | undefined {
@@ -155,6 +159,9 @@ function normalizeCode(value: string | number | undefined): string | undefined {
155
159
 
156
160
  function kindFromStatus(status: number | undefined): ModelFallbackFailureKind | undefined {
157
161
  switch (status) {
162
+ case 400:
163
+ case 413: case 422:
164
+ return "request_incompatible";
158
165
  case 401:
159
166
  case 403:
160
167
  return "auth_on_candidate_provider";
@@ -191,6 +198,15 @@ function refusalKindFromCode(code: string | number | undefined): ModelFallbackFa
191
198
  }
192
199
  }
193
200
 
201
+ const REQUEST_INCOMPATIBLE_CODES: ReadonlySet<string> = new Set([
202
+ "invalid_request", "invalid_request_error", "bad_request", "context_length_exceeded",
203
+ "request_too_large", "too_large", "request_entity_too_large", "max_tokens",
204
+ "max_context_length", "context_window_exceeded",
205
+ ]);
206
+ function requestIncompatibleKindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
207
+ const normalizedCode = normalizeCode(code);
208
+ return normalizedCode !== undefined && REQUEST_INCOMPATIBLE_CODES.has(normalizedCode) ? "request_incompatible" : undefined;
209
+ }
194
210
  function kindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
195
211
  const normalizedCode = normalizeCode(code);
196
212
  if (normalizedCode === undefined) return undefined;
@@ -198,6 +214,8 @@ function kindFromCode(code: string | number | undefined): ModelFallbackFailureKi
198
214
  if (refusalKind !== undefined) return refusalKind;
199
215
  const httpStatusKind = kindFromStatus(integerFrom(code));
200
216
  if (httpStatusKind !== undefined) return httpStatusKind;
217
+ const requestIncompatibleKind = requestIncompatibleKindFromCode(code);
218
+ if (requestIncompatibleKind !== undefined) return requestIncompatibleKind;
201
219
 
202
220
  switch (normalizedCode) {
203
221
  case "auth":
@@ -246,6 +264,18 @@ function kindFromCode(code: string | number | undefined): ModelFallbackFailureKi
246
264
  }
247
265
  }
248
266
 
267
+ const REQUEST_INCOMPATIBLE_FAILURE_PATTERNS: readonly RegExp[] = [
268
+ /\bcontext[_\s-]?length(?:[_\s-]?exceeded)?\b/i,
269
+ /\bcontext[_\s-]?window(?:[_\s-]?exceeded)?\b/i,
270
+ /\bmax[_\s-]?context\b/i,
271
+ /\bmax[_\s-]?tokens?\b/i,
272
+ /\brequest(?:[_\s-]?entity)?[_\s-]?too[_\s-]?large\b/i,
273
+ /\btoo[_\s-]?large\b/i,
274
+ /\b(?:unsupported|unknown|invalid)\s+(?:tool|parameter|function)\b/i,
275
+ /\b(?:tool|parameter|function)\s+(?:not\s+(?:supported|found|allowed)|unknown|invalid)\b/i,
276
+ /\binvalid[_\s-]?request(?:[_\s-]?error)?\b/i,
277
+ /\bbad[_\s-]?request\b/i,
278
+ ];
249
279
  const PROVIDER_REFUSAL_FAILURE_PATTERNS: readonly RegExp[] = [
250
280
  /\bfinish[_\s-]?reason\b[^\n]*\bcontent[_\s-]?filter\b/i,
251
281
  /\bcontent[_\s-]?filter(?:ed|ing)?\b/i,
@@ -258,6 +288,17 @@ const PROVIDER_REFUSAL_FAILURE_PATTERNS: readonly RegExp[] = [
258
288
  /\bprovider\b[^\n]*\brefus(?:e|al|ed|es|ing)?\b[^\n]*\b(?:prompt|request|content|policy|safety)\b/i,
259
289
  ];
260
290
 
291
+ const TRANSPORT_OUTAGE_FAILURE_PATTERNS: readonly RegExp[] = [
292
+ /^connection\s+error\.?$/i,
293
+ /^fetch\s+failed\.?$/i,
294
+ ];
295
+
296
+ function transportOutageKindFromMessage(message: string): ModelFallbackFailureKind | undefined {
297
+ return TRANSPORT_OUTAGE_FAILURE_PATTERNS.some((pattern) => pattern.test(message.trim()))
298
+ ? "transport_error"
299
+ : undefined;
300
+ }
301
+
261
302
  function refusalKindFromMessage(message: string): ModelFallbackFailureKind | undefined {
262
303
  if (CANCELLED_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "cancelled";
263
304
  if (NON_RETRYABLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "task_failure";
@@ -268,6 +309,9 @@ function refusalKindFromMessage(message: string): ModelFallbackFailureKind | und
268
309
  function fallbackKindFromMessage(message: string, name: string | undefined): ModelFallbackFailureKind | undefined {
269
310
  const refusalKind = refusalKindFromMessage(message);
270
311
  if (refusalKind !== undefined) return refusalKind;
312
+ const transportOutageKind = transportOutageKindFromMessage(message);
313
+ if (transportOutageKind !== undefined) return transportOutageKind;
314
+ if (REQUEST_INCOMPATIBLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "request_incompatible";
271
315
  const nameKind = kindFromCode(name);
272
316
  if (nameKind !== undefined) return nameKind;
273
317
  if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return undefined;
@@ -305,6 +349,16 @@ function makeSignal(
305
349
  };
306
350
  }
307
351
 
352
+ function fallbackSignalFromDirectMessage(
353
+ value: unknown,
354
+ source: ModelFallbackFailureSource | undefined,
355
+ ): ModelFallbackFailureSignal | undefined {
356
+ const message = directMessageFrom(value);
357
+ if (message === undefined) return undefined;
358
+ const kind = fallbackKindFromMessage(message, errorName(value));
359
+ return kind === undefined ? undefined : makeSignal(kind, value, source);
360
+ }
361
+
308
362
  function fallbackSignalFromMessage(
309
363
  value: unknown,
310
364
  source: ModelFallbackFailureSource | undefined,
@@ -359,7 +413,6 @@ function structuredSignal(
359
413
  if (isRefusalSignal(diagnosticSignal)) return diagnosticSignal;
360
414
  firstNestedFallbackSignal ??= diagnosticSignal;
361
415
  }
362
-
363
416
  const cause = causeOf(value);
364
417
  const causeSignal = structuredSignal(cause, nestedSeen, source)
365
418
  ?? fallbackSignalFromMessage(cause, source);
@@ -368,6 +421,11 @@ function structuredSignal(
368
421
  firstNestedFallbackSignal ??= causeSignal;
369
422
  }
370
423
 
424
+ // Direct-message classification runs after nested traversal so a generic wrapper
425
+ // ("invalid request"/"400 bad request") cannot mask a non-retryable nested signal.
426
+ const directMessageSignal = fallbackSignalFromDirectMessage(value, source);
427
+ if (directMessageSignal !== undefined) return directMessageSignal;
428
+
371
429
  const statusKind = kindFromStatus(statusFrom(value));
372
430
  if (statusKind !== undefined) return makeSignal(statusKind, value, source);
373
431
  if (codeKind !== undefined) return makeSignal(codeKind, value, source);
@@ -335,7 +335,7 @@ export function restoreTerminalRuns(entries: readonly SessionEntry[], store: Sto
335
335
  const exitReason = end["exitReason"];
336
336
  const resumable = end["resumable"];
337
337
  const restoredAuthorExit = isWorkflowExitTerminalStatus(status) &&
338
- (exited === true || status !== "completed" || typeof exitReason === "string" || resumable === false);
338
+ (exited === true || typeof exitReason === "string");
339
339
  if (status === "completed" && !restoredAuthorExit && stages.some((stage) => stage.status !== "completed")) continue;
340
340
  store.recordRunStart({
341
341
  id: runId,
@@ -83,8 +83,9 @@ export interface Store {
83
83
  * Records the end of a run.
84
84
  * Returns `true` if state changed, `false` if the run was not found or
85
85
  * already in a terminal state (completed | failed | killed | skipped | cancelled | blocked).
86
- * `result` is applied for intentional success/exit statuses (completed | skipped | cancelled | blocked).
87
- * `error` is only applied for status "failed" | "killed".
86
+ * `result` is applied for intentional success/exit statuses (completed | skipped | cancelled | blocked)
87
+ * and for workflows that intentionally return a failed status with structured outputs.
88
+ * `error` is applied for status "failed" | "killed" | "blocked".
88
89
  */
89
90
  recordRunEnd(
90
91
  runId: string,
@@ -82,11 +82,12 @@ export function createRunStoreMethods(context: StoreContext): RunStoreMethods {
82
82
  run.pausedAt = undefined;
83
83
  }
84
84
  run.durationMs = elapsedRunMs(run, run.endedAt);
85
+ if (result !== undefined && shouldStoreRunResult(status)) run.result = result;
85
86
  const wasBlocked = run.blockedAt !== undefined || run.failureDisposition === "active_blocked";
86
87
  delete run.blockedAt;
87
88
  if (status === "completed" || status === "skipped" || status === "cancelled" || status === "blocked") {
88
- if (result !== undefined) run.result = result;
89
89
  clearRunFailureMetadata(run);
90
+ if (status === "blocked" && error !== undefined) run.error = error;
90
91
  if (metadata !== undefined) applyRunEndMetadata(run, metadata);
91
92
  } else {
92
93
  if (wasBlocked && error === undefined) delete run.error;
@@ -227,3 +228,7 @@ export function createRunStoreMethods(context: StoreContext): RunStoreMethods {
227
228
  },
228
229
  };
229
230
  }
231
+
232
+ function shouldStoreRunResult(status: RunStatus): boolean {
233
+ return status === "completed" || status === "skipped" || status === "cancelled" || status === "blocked" || status === "failed";
234
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/bun/cli.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\nimport { APP_NAME } from \"../config.ts\";\n\nprocess.title = APP_NAME;\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\nimport { restoreSandboxEnv } from \"./restore-sandbox-env.ts\";\n\nrestoreSandboxEnv();\n\nawait import(\"./register-bedrock.ts\");\nawait import(\"../cli.ts\");\n"]}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/bun/cli.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\nimport { APP_NAME } from \"../config.ts\";\n\nprocess.title = APP_NAME;\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\nimport { restoreSandboxEnv } from \"./restore-sandbox-env.ts\";\n\nrestoreSandboxEnv();\n\n// No top-level await: the compiled binary is built with --bytecode (CJS),\n// which forbids TLA anywhere in the bundled graph.\nvoid import(\"./register-bedrock.ts\").then(() => import(\"../cli.ts\"));\n"]}