@bastani/atomic 0.9.13-alpha.2 → 0.9.13-alpha.4

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 +30 -0
  2. package/README.md +1 -1
  3. package/dist/builtin/i-have-adhd/package.json +1 -1
  4. package/dist/builtin/intercom/CHANGELOG.md +14 -0
  5. package/dist/builtin/intercom/README.md +12 -12
  6. package/dist/builtin/intercom/incoming-message-delivery.ts +1 -1
  7. package/dist/builtin/intercom/index.ts +1 -1
  8. package/dist/builtin/intercom/intercom-tool.ts +1 -1
  9. package/dist/builtin/intercom/intercom-utils.ts +3 -6
  10. package/dist/builtin/intercom/package.json +1 -1
  11. package/dist/builtin/intercom/result-renderers.ts +1 -1
  12. package/dist/builtin/intercom/session-target.ts +1 -15
  13. package/dist/builtin/intercom/skills/intercom/SKILL.md +6 -6
  14. package/dist/builtin/intercom/terminal-ordering-barrier.ts +2 -2
  15. package/dist/builtin/intercom/ui/inline-message.ts +7 -5
  16. package/dist/builtin/intercom/ui/session-list.ts +23 -10
  17. package/dist/builtin/mcp/package.json +1 -1
  18. package/dist/builtin/subagents/CHANGELOG.md +19 -0
  19. package/dist/builtin/subagents/README.md +29 -73
  20. package/dist/builtin/subagents/package.json +2 -2
  21. package/dist/builtin/subagents/prompts/parallel-cleanup.md +1 -1
  22. package/dist/builtin/subagents/prompts/parallel-review.md +1 -1
  23. package/dist/builtin/subagents/prompts/review-loop.md +2 -2
  24. package/dist/builtin/subagents/skills/subagent/SKILL.md +23 -59
  25. package/dist/builtin/subagents/skills/tmux/SKILL.md +1 -1
  26. package/dist/builtin/subagents/src/extension/control-notices.ts +2 -3
  27. package/dist/builtin/subagents/src/extension/doctor.ts +2 -14
  28. package/dist/builtin/subagents/src/extension/fanout-child.ts +1 -17
  29. package/dist/builtin/subagents/src/extension/index.ts +9 -70
  30. package/dist/builtin/subagents/src/extension/notification-content.ts +1 -1
  31. package/dist/builtin/subagents/src/extension/prompt-guidance.ts +2 -2
  32. package/dist/builtin/subagents/src/extension/schemas.ts +2 -8
  33. package/dist/builtin/subagents/src/extension/tool-description.ts +2 -3
  34. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +1 -1
  35. package/dist/builtin/subagents/src/intercom/result-intercom.ts +5 -46
  36. package/dist/builtin/subagents/src/runs/{background → foreground}/completion-notification.ts +2 -2
  37. package/dist/builtin/subagents/src/runs/foreground/inprocess-run-sync.ts +25 -88
  38. package/dist/builtin/subagents/src/runs/{background → foreground}/notify.ts +5 -5
  39. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +11 -28
  40. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +3 -0
  41. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +2 -0
  42. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-runtime.ts +0 -5
  43. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +30 -20
  44. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +4 -4
  45. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +0 -13
  46. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +104 -48
  47. package/dist/builtin/subagents/src/runs/inprocess/index.ts +1 -1
  48. package/dist/builtin/subagents/src/runs/inprocess/runner.ts +5 -5
  49. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-api.ts +0 -7
  50. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-core.ts +0 -10
  51. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-projection.ts +3 -108
  52. package/dist/builtin/subagents/src/runs/inprocess/runtime-support/nested-sanitize.ts +0 -4
  53. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +1 -1
  54. package/dist/builtin/subagents/src/shared/formatters.ts +3 -5
  55. package/dist/builtin/subagents/src/shared/model-resolution.ts +78 -0
  56. package/dist/builtin/subagents/src/shared/status-format.ts +4 -11
  57. package/dist/builtin/subagents/src/shared/types-config.ts +10 -9
  58. package/dist/builtin/subagents/src/shared/types-nested.ts +127 -0
  59. package/dist/builtin/subagents/src/shared/types-results.ts +2 -8
  60. package/dist/builtin/subagents/src/shared/types-runtime.ts +0 -9
  61. package/dist/builtin/subagents/src/shared/types.ts +1 -1
  62. package/dist/builtin/subagents/src/shared/utils.ts +1 -109
  63. package/dist/builtin/subagents/src/slash/slash-commands.ts +7 -17
  64. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +2 -252
  65. package/dist/builtin/subagents/src/tui/render-layout.ts +4 -4
  66. package/dist/builtin/subagents/src/tui/render-result.ts +0 -9
  67. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -34
  68. package/dist/builtin/subagents/src/tui/render.ts +0 -2
  69. package/dist/builtin/web-access/package.json +1 -1
  70. package/dist/builtin/workflows/CHANGELOG.md +23 -0
  71. package/dist/builtin/workflows/README.md +3 -3
  72. package/dist/builtin/workflows/package.json +1 -1
  73. package/dist/builtin/workflows/src/durable/backend.ts +11 -0
  74. package/dist/builtin/workflows/src/durable/completed-catalog-stage-groups.ts +63 -25
  75. package/dist/builtin/workflows/src/durable/completed-catalog.ts +166 -25
  76. package/dist/builtin/workflows/src/durable/dbos-backend.ts +42 -24
  77. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +2 -1
  78. package/dist/builtin/workflows/src/durable/run-timing.ts +28 -7
  79. package/dist/builtin/workflows/src/durable/scoped-backend.ts +4 -0
  80. package/dist/builtin/workflows/src/durable/targeted-inspection.ts +110 -0
  81. package/dist/builtin/workflows/src/engine/run-durable-stage-session.ts +2 -2
  82. package/dist/builtin/workflows/src/engine/runtime.ts +19 -2
  83. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +3 -0
  84. package/dist/builtin/workflows/src/extension/index.bundle.mjs +750 -168
  85. package/dist/builtin/workflows/src/extension/runtime-durable-resume.ts +6 -0
  86. package/dist/builtin/workflows/src/extension/ui-surface.ts +4 -0
  87. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +6 -1
  88. package/dist/builtin/workflows/src/extension/workflow-targets.ts +18 -10
  89. package/dist/builtin/workflows/src/extension/workflow-tool-inspection.ts +31 -16
  90. package/dist/builtin/workflows/src/extension/workflow-tool-send.ts +75 -3
  91. package/dist/builtin/workflows/src/extension/workflow-tool.ts +53 -6
  92. package/dist/builtin/workflows/src/runs/background/run-inspect.ts +5 -1
  93. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +94 -60
  94. package/dist/builtin/workflows/src/runs/foreground/executor-stage-context.ts +1 -1
  95. package/dist/builtin/workflows/src/runs/foreground/executor-stage-control.ts +7 -7
  96. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +25 -4
  97. package/dist/builtin/workflows/src/runs/foreground/executor-stage-types.ts +4 -1
  98. package/dist/builtin/workflows/src/runs/foreground/executor-types.ts +2 -0
  99. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +142 -4
  100. package/dist/builtin/workflows/src/runs/foreground/stage-runner-types.ts +2 -0
  101. package/dist/builtin/workflows/src/runs/foreground/stage-session-heartbeat.ts +88 -0
  102. package/dist/builtin/workflows/src/tui/keybindings-adapter.ts +1 -0
  103. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +1 -0
  104. package/dist/builtin/workflows/src/tui/run-detail.ts +25 -12
  105. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +27 -8
  106. package/dist/builtin/workflows/src/tui/stage-chat-view-input.ts +17 -1
  107. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +20 -3
  108. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +10 -1
  109. package/dist/builtin/workflows/src/tui/workflow-resume-selector.ts +1 -0
  110. package/dist/core/agent-session-events.d.ts.map +1 -1
  111. package/dist/core/agent-session-events.js +22 -5
  112. package/dist/core/agent-session-events.js.map +1 -1
  113. package/dist/core/agent-session-export.d.ts.map +1 -1
  114. package/dist/core/agent-session-export.js +2 -1
  115. package/dist/core/agent-session-export.js.map +1 -1
  116. package/dist/core/agent-session-methods.d.ts +10 -4
  117. package/dist/core/agent-session-methods.d.ts.map +1 -1
  118. package/dist/core/agent-session-methods.js.map +1 -1
  119. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  120. package/dist/core/agent-session-prompt.js +68 -0
  121. package/dist/core/agent-session-prompt.js.map +1 -1
  122. package/dist/core/agent-session-queue-pause.d.ts.map +1 -1
  123. package/dist/core/agent-session-queue-pause.js +6 -0
  124. package/dist/core/agent-session-queue-pause.js.map +1 -1
  125. package/dist/core/agent-session-summary.d.ts +21 -0
  126. package/dist/core/agent-session-summary.d.ts.map +1 -0
  127. package/dist/core/agent-session-summary.js +156 -0
  128. package/dist/core/agent-session-summary.js.map +1 -0
  129. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  130. package/dist/core/agent-session-tool-registry.js +0 -4
  131. package/dist/core/agent-session-tool-registry.js.map +1 -1
  132. package/dist/core/agent-session-transfer.d.ts.map +1 -1
  133. package/dist/core/agent-session-transfer.js +0 -2
  134. package/dist/core/agent-session-transfer.js.map +1 -1
  135. package/dist/core/agent-session-tree.d.ts.map +1 -1
  136. package/dist/core/agent-session-tree.js +6 -0
  137. package/dist/core/agent-session-tree.js.map +1 -1
  138. package/dist/core/agent-session.d.ts +21 -3
  139. package/dist/core/agent-session.d.ts.map +1 -1
  140. package/dist/core/agent-session.js +22 -5
  141. package/dist/core/agent-session.js.map +1 -1
  142. package/dist/core/atomic-guide-command.js +2 -2
  143. package/dist/core/atomic-guide-command.js.map +1 -1
  144. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  145. package/dist/core/compaction/branch-summarization.js +1 -0
  146. package/dist/core/compaction/branch-summarization.js.map +1 -1
  147. package/dist/core/compaction/index.d.ts +1 -0
  148. package/dist/core/compaction/index.d.ts.map +1 -1
  149. package/dist/core/compaction/index.js +1 -0
  150. package/dist/core/compaction/index.js.map +1 -1
  151. package/dist/core/compaction/session-summarization.d.ts +41 -0
  152. package/dist/core/compaction/session-summarization.d.ts.map +1 -0
  153. package/dist/core/compaction/session-summarization.js +97 -0
  154. package/dist/core/compaction/session-summarization.js.map +1 -0
  155. package/dist/core/extensions/ui-types.d.ts +4 -0
  156. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  157. package/dist/core/extensions/ui-types.js.map +1 -1
  158. package/dist/core/keybindings.d.ts +0 -5
  159. package/dist/core/keybindings.d.ts.map +1 -1
  160. package/dist/core/keybindings.js +0 -2
  161. package/dist/core/keybindings.js.map +1 -1
  162. package/dist/core/session-manager-core.d.ts +3 -1
  163. package/dist/core/session-manager-core.d.ts.map +1 -1
  164. package/dist/core/session-manager-core.js +9 -1
  165. package/dist/core/session-manager-core.js.map +1 -1
  166. package/dist/core/session-manager-entries.d.ts +24 -1
  167. package/dist/core/session-manager-entries.d.ts.map +1 -1
  168. package/dist/core/session-manager-entries.js +48 -0
  169. package/dist/core/session-manager-entries.js.map +1 -1
  170. package/dist/core/session-manager-list.d.ts.map +1 -1
  171. package/dist/core/session-manager-list.js +10 -0
  172. package/dist/core/session-manager-list.js.map +1 -1
  173. package/dist/core/session-manager-types.d.ts +10 -1
  174. package/dist/core/session-manager-types.d.ts.map +1 -1
  175. package/dist/core/session-manager-types.js.map +1 -1
  176. package/dist/core/settings-manager-basic-accessors.d.ts +3 -0
  177. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  178. package/dist/core/settings-manager-basic-accessors.js +5 -0
  179. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  180. package/dist/core/settings-manager.d.ts +1 -1
  181. package/dist/core/settings-manager.d.ts.map +1 -1
  182. package/dist/core/settings-manager.js.map +1 -1
  183. package/dist/core/settings-types.d.ts +4 -0
  184. package/dist/core/settings-types.d.ts.map +1 -1
  185. package/dist/core/settings-types.js.map +1 -1
  186. package/dist/core/slash-commands.js +2 -2
  187. package/dist/core/slash-commands.js.map +1 -1
  188. package/dist/core/tools/bash.d.ts +0 -15
  189. package/dist/core/tools/bash.d.ts.map +1 -1
  190. package/dist/core/tools/bash.js +3 -81
  191. package/dist/core/tools/bash.js.map +1 -1
  192. package/dist/core/tools/fetch-url.d.ts +12 -0
  193. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  194. package/dist/core/tools/fetch-url.js +76 -10
  195. package/dist/core/tools/fetch-url.js.map +1 -1
  196. package/dist/core/tools/index.js +4 -4
  197. package/dist/core/tools/index.js.map +1 -1
  198. package/dist/core/tools/session-temp-dir.d.ts +2 -2
  199. package/dist/core/tools/session-temp-dir.d.ts.map +1 -1
  200. package/dist/core/tools/session-temp-dir.js +6 -7
  201. package/dist/core/tools/session-temp-dir.js.map +1 -1
  202. package/dist/core/usage-totals.js +1 -1
  203. package/dist/core/usage-totals.js.map +1 -1
  204. package/dist/index.d.ts +1 -1
  205. package/dist/index.d.ts.map +1 -1
  206. package/dist/index.js +1 -1
  207. package/dist/index.js.map +1 -1
  208. package/dist/modes/interactive/components/footer.js +1 -1
  209. package/dist/modes/interactive/components/footer.js.map +1 -1
  210. package/dist/modes/interactive/components/host-session-picker.d.ts.map +1 -1
  211. package/dist/modes/interactive/components/host-session-picker.js +1 -0
  212. package/dist/modes/interactive/components/host-session-picker.js.map +1 -1
  213. package/dist/modes/interactive/components/index.d.ts +1 -0
  214. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  215. package/dist/modes/interactive/components/index.js +1 -0
  216. package/dist/modes/interactive/components/index.js.map +1 -1
  217. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  218. package/dist/modes/interactive/components/session-selector-list.js +29 -4
  219. package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
  220. package/dist/modes/interactive/components/session-selector-search.js +1 -1
  221. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  222. package/dist/modes/interactive/components/transcript-follow-indicator.d.ts +14 -0
  223. package/dist/modes/interactive/components/transcript-follow-indicator.d.ts.map +1 -0
  224. package/dist/modes/interactive/components/transcript-follow-indicator.js +36 -0
  225. package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -0
  226. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  227. package/dist/modes/interactive/components/tree-selector-content.js +6 -0
  228. package/dist/modes/interactive/components/tree-selector-content.js.map +1 -1
  229. package/dist/modes/interactive/interactive-extension-custom-ui.js +3 -0
  230. package/dist/modes/interactive/interactive-extension-custom-ui.js.map +1 -1
  231. package/dist/modes/interactive/interactive-input-handling.js +0 -3
  232. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  233. package/dist/modes/interactive/interactive-mode-base.d.ts +1 -0
  234. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  235. package/dist/modes/interactive/interactive-mode-base.js +7 -1
  236. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  237. package/dist/modes/interactive/interactive-mode-surface.d.ts +3 -3
  238. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  239. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  240. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  241. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  242. package/dist/modes/interactive/interactive-mode.js +1 -0
  243. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  244. package/dist/modes/interactive/interactive-slash-commands.js +2 -5
  245. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  246. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  247. package/dist/modes/interactive/interactive-startup.js +6 -0
  248. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  249. package/dist/modes/interactive/interactive-transcript-follow.d.ts +2 -0
  250. package/dist/modes/interactive/interactive-transcript-follow.d.ts.map +1 -0
  251. package/dist/modes/interactive/interactive-transcript-follow.js +6 -0
  252. package/dist/modes/interactive/interactive-transcript-follow.js.map +1 -0
  253. package/dist/modes/interactive/interactive-tui.d.ts +13 -1
  254. package/dist/modes/interactive/interactive-tui.d.ts.map +1 -1
  255. package/dist/modes/interactive/interactive-tui.js +59 -1
  256. package/dist/modes/interactive/interactive-tui.js.map +1 -1
  257. package/dist/modes/interactive-engine/engine-custom-ui.d.ts +2 -0
  258. package/dist/modes/interactive-engine/engine-custom-ui.d.ts.map +1 -1
  259. package/dist/modes/interactive-engine/engine-custom-ui.js +1 -0
  260. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  261. package/dist/modes/interactive-engine/protocol.d.ts +1 -0
  262. package/dist/modes/interactive-engine/protocol.d.ts.map +1 -1
  263. package/dist/modes/interactive-engine/protocol.js +5 -1
  264. package/dist/modes/interactive-engine/protocol.js.map +1 -1
  265. package/dist/modes/interactive-engine/remote-component.d.ts.map +1 -1
  266. package/dist/modes/interactive-engine/remote-component.js +21 -9
  267. package/dist/modes/interactive-engine/remote-component.js.map +1 -1
  268. package/docs/environment-variables.md +1 -1
  269. package/docs/intercom.md +20 -21
  270. package/docs/keybindings.md +2 -5
  271. package/docs/quickstart.md +5 -1
  272. package/docs/sdk.md +1 -1
  273. package/docs/session-format.md +15 -0
  274. package/docs/sessions.md +8 -0
  275. package/docs/settings.md +6 -0
  276. package/docs/subagents.md +19 -31
  277. package/docs/tools.md +3 -5
  278. package/docs/usage.md +3 -1
  279. package/docs/windows.md +1 -1
  280. package/docs/workflows.md +60 -32
  281. package/npm-shrinkwrap.json +29 -29
  282. package/package.json +2 -2
  283. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +0 -331
  284. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +0 -161
  285. package/dist/builtin/subagents/src/runs/inprocess/background-single.ts +0 -195
  286. package/dist/builtin/subagents/src/runs/inprocess/background.ts +0 -90
  287. package/dist/builtin/subagents/src/shared/types-async.ts +0 -247
  288. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +0 -217
  289. package/dist/builtin/subagents/src/tui/render-widget.ts +0 -366
  290. package/dist/core/async/format.d.ts +0 -3
  291. package/dist/core/async/format.d.ts.map +0 -1
  292. package/dist/core/async/format.js +0 -52
  293. package/dist/core/async/format.js.map +0 -1
  294. package/dist/core/async/job-manager.d.ts +0 -40
  295. package/dist/core/async/job-manager.d.ts.map +0 -1
  296. package/dist/core/async/job-manager.js +0 -293
  297. package/dist/core/async/job-manager.js.map +0 -1
  298. package/dist/core/async/session-manager.d.ts +0 -16
  299. package/dist/core/async/session-manager.d.ts.map +0 -1
  300. package/dist/core/async/session-manager.js +0 -34
  301. package/dist/core/async/session-manager.js.map +0 -1
  302. package/dist/core/async/types.d.ts +0 -23
  303. package/dist/core/async/types.d.ts.map +0 -1
  304. package/dist/core/async/types.js +0 -2
  305. package/dist/core/async/types.js.map +0 -1
  306. package/dist/core/tools/bash-async-execution.d.ts +0 -27
  307. package/dist/core/tools/bash-async-execution.d.ts.map +0 -1
  308. package/dist/core/tools/bash-async-execution.js +0 -81
  309. package/dist/core/tools/bash-async-execution.js.map +0 -1
  310. package/dist/core/tools/bash-async-jobs.d.ts +0 -30
  311. package/dist/core/tools/bash-async-jobs.d.ts.map +0 -1
  312. package/dist/core/tools/bash-async-jobs.js +0 -87
  313. package/dist/core/tools/bash-async-jobs.js.map +0 -1
  314. package/dist/core/tools/bash-async-output.d.ts +0 -13
  315. package/dist/core/tools/bash-async-output.d.ts.map +0 -1
  316. package/dist/core/tools/bash-async-output.js +0 -122
  317. package/dist/core/tools/bash-async-output.js.map +0 -1
@@ -5,7 +5,7 @@ This page gets you from install to a useful first Atomic session. Atomic is the
5
5
  ## Prerequisites
6
6
 
7
7
  - **Node.js 24 LTS or newer** — Atomic requires the latest Node LTS runtime. Check with `node --version`.
8
- - **A package manager** — use npm (included with Node), pnpm, Yarn, or Bun. Use Bun 1.3.14+ for Bun installs or workflow-authoring examples.
8
+ - **A package manager** — use npm (included with Node), pnpm, Yarn, or Bun. Bun installs need Bun 1.3.14+.
9
9
  - **Model-provider access** — Use `/login` after startup. Supports provider subscriptions and APIs.
10
10
 
11
11
  ## Install
@@ -32,6 +32,10 @@ bun add -g @bastani/atomic
32
32
 
33
33
  Atomic does not require package install scripts. If you want to disable dependency lifecycle scripts during the Atomic install, you can add `--ignore-scripts` to the install command.
34
34
 
35
+ ### Which runtime runs your workflows
36
+
37
+ How you install Atomic decides which runtime hosts it: a package-manager install runs under Node, while the standalone binaries are Bun-compiled and run under Bun. Authored workflows execute inside whichever host is active, so a workflow that reaches for a `Bun.*` global runs only under the standalone binary and fails with `Bun is not defined` under an npm install. Installing Bun separately does not change that — the npm install still runs on Node. Write workflow code against APIs both hosts provide, such as `node:child_process` and `node:fs`; see [Workflows](/workflows) for the rule and worked examples.
38
+
35
39
  ### Alpine and musl Linux archives
36
40
 
37
41
  For Alpine Linux, use `atomic-linux-x64-musl.tar.gz` on x64 or `atomic-linux-arm64-musl.tar.gz` on arm64. These archives provide native search and PTY bindings. Install their runtime libraries with `apk add --no-cache libgcc libstdc++`, then see the [Alpine and musl Linux archive notes](/index#alpine-and-musl-linux-archives) for the clipboard fallback and external Postgres or Docker requirement for durable workflows.
package/docs/sdk.md CHANGED
@@ -297,7 +297,7 @@ await session.followUp("After you're done, also do this");
297
297
 
298
298
  Both `steer()` and `followUp()` expand file-based prompt templates but error on extension commands (extension commands cannot be queued).
299
299
 
300
- `pauseQueuedMessages()` is a synchronous admission gate. It moves existing raw steering/follow-up entries into a hold before an abort boundary and keeps later context-bearing arrivals—including trigger-turn custom messages, batches, interrupts, async job delivery, `sendUserMessage()`, and ordinary `prompt()` calls—queued without starting a provider turn. Content blocks, optional data, duplicate identities, raw text, message types, and the existing order within each queue kind are retained. Non-trigger custom messages remain history-only and do not invent a turn.
300
+ `pauseQueuedMessages()` is a synchronous admission gate. It moves existing raw steering/follow-up entries into a hold before an abort boundary and keeps later context-bearing arrivals—including trigger-turn custom messages, batches, interrupts, `sendUserMessage()`, and ordinary `prompt()` calls—queued without starting a provider turn. Content blocks, optional data, duplicate identities, raw text, message types, and the existing order within each queue kind are retained. Non-trigger custom messages remain history-only and do not invent a turn.
301
301
 
302
302
  `resumeQueuedMessages()` releases that hold exactly once but does **not** itself start or continue a model turn. Its promise resolves to `true` only when raw held steering/follow-up work was released, and to `false` when no held raw work existed. The caller must use its existing explicit resume action (for example, the interactive chat submission or workflow resume boundary) to drive execution. `clearQueue()` clears the paused flag when it explicitly removes the final unowned held item; if a protected or interrupt-owned item remains, the gate stays paused.
303
303
 
@@ -307,6 +307,20 @@ Session metadata (e.g., user-defined display name). Set via `/name`, `--name` /
307
307
 
308
308
  The session name is displayed in the session selector (`/resume`) instead of the first message when set.
309
309
 
310
+ ### SessionSummaryEntry
311
+
312
+ A generated one-line description of the session, written automatically once the agent goes idle and shown in the session selector. Never sent to the LLM.
313
+
314
+ ```json
315
+ {"type":"session_summary","id":"l2m3n4o5","parentId":"k1l2m3n4","timestamp":"2024-12-03T14:36:00.000Z","summary":"Refactored auth module token refresh and added retry tests","summarizedThroughId":"j0k1l2m3","usage":{"input":812,"output":21}}
316
+ ```
317
+
318
+ - `summary`: The stored line. Whitespace is collapsed and length is clamped before writing.
319
+ - `summarizedThroughId`: Entry ID of the last user/assistant message the summary covers. The selector shows the summary only while this is still the newest conversation message; anything newer makes it stale and the selector falls back to the session name or first message. Tool results do not count.
320
+ - `usage`: Optional token usage from the model call, counted toward session usage totals.
321
+
322
+ A `branch_summary` written after a `session_summary` also retires it, because the branch it described was abandoned.
323
+
310
324
  ## Tree Structure
311
325
 
312
326
  Entries form a tree:
@@ -420,6 +434,7 @@ for (const stage of stages.filter((session) => session.internal)) {
420
434
  - `appendCompaction(compactedText, firstKeptEntryId, tokensBefore, details)` - Add a durable verbatim-line compaction boundary; pass `null` when no pre-boundary message is retained
421
435
  - `appendCustomEntry(customType, data?)` - Extension state (not in context)
422
436
  - `appendSessionInfo(name)` - Set session display name
437
+ - `appendSessionSummary(summary, summarizedThroughId, usage?)` - Store a generated resume-picker summary, anchored to the message it describes
423
438
  - `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
424
439
  - `appendLabelChange(targetId, label)` - Set/clear label
425
440
 
package/docs/sessions.md CHANGED
@@ -57,6 +57,14 @@ In the picker you can:
57
57
 
58
58
  When available, Atomic uses the `trash` CLI for deletion instead of permanently removing files.
59
59
 
60
+ ### Session summaries
61
+
62
+ Each row shows a short generated description of what the session was about in its own column, beside the session name or first message, so you can recognize a conversation without opening it. Atomic writes one after the agent goes idle, using the model the session is already configured with, and stores it in the session file as a `session_summary` entry.
63
+
64
+ A summary describes the conversation up to a specific message. Once a newer message arrives it is considered stale and the summary column shows "No summary available." instead — the same placeholder you get when a summary has not been generated yet, could not be generated, or is still in flight. The session name and first message are never displaced by a summary, and the column only appears once at least one listed session has a summary. Summaries are also searchable along with the rest of the session text.
65
+
66
+ Generation is best-effort and never blocks a turn: it is skipped for very short sessions, for workflow stage sessions, and in `--print` and JSON modes, it is cancelled when you send the next message or quit, and its failures are silent. Set `sessionSummary.enabled` to `false` to turn it off entirely.
67
+
60
68
  The picker opens instantly: its header, search field, and loading indicator paint on the first frame, then sessions are discovered and parsed off the terminal's UI loop. Large session directories are scanned in cooperative batches and a single very large transcript is parsed in yielding chunks, so search, navigation, and cancel stay responsive and no individual session can freeze the picker while it loads. Closing the picker cancels any in-flight scan and discards stale results, so a slow load that finishes after you leave never updates the list.
61
69
 
62
70
  ### Internal (workflow) sessions
package/docs/settings.md CHANGED
@@ -196,6 +196,12 @@ The model emits numbered line ranges only; Atomic reconstructs retained text mec
196
196
  | `branchSummary.reserveTokens` | number | `16384` | Tokens reserved for branch summarization |
197
197
  | `branchSummary.skipPrompt` | boolean | `false` | Skip "Summarize branch?" prompt on `/tree` navigation (defaults to no summary) |
198
198
 
199
+ ### Session Summary
200
+
201
+ | Setting | Type | Default | Description |
202
+ |---------|------|---------|-------------|
203
+ | `sessionSummary.enabled` | boolean | `true` | Generate a one-line summary of each session for the `/resume` picker once the agent goes idle |
204
+
199
205
  ### Retry
200
206
 
201
207
  | Setting | Type | Default | Description |
package/docs/subagents.md CHANGED
@@ -25,28 +25,28 @@ Run a parallel review composition: one pass for current behavior, one for failur
25
25
  Research the upstream library behavior online, then compare it with our local implementation.
26
26
  ```
27
27
 
28
- Atomic decides whether delegation adds value, which specialist fits each bounded part, and whether the work should run as a single child, parallel group, foreground run, or selective background run. Multiple steps, files, tests, validation, or parallelism alone do not require a workflow; clearly delegated long-running autonomous work that needs durable stages, checkpoints, resumability, HIL, gates, retries, or loops is usually better served by a workflow.
28
+ Atomic decides whether delegation adds value, which specialist fits each bounded part, and whether the work should run as a single child, parallel group, or forked-context run. Multiple steps, files, tests, validation, or parallelism alone do not require a workflow; clearly delegated long-running autonomous work that needs durable stages, checkpoints, resumability, HIL, gates, retries, or loops is usually better served by a workflow.
29
29
 
30
30
  ## Subagent execution is non-interactive
31
31
 
32
- Supported subagent launches start immediately without opening a preview/editor prompt or waiting for terminal input. This applies to single, parallel, foreground, background, fanout, prompt-template, and human-entered `/run` and `/parallel` execution. Ask any necessary questions in the parent conversation before delegating.
32
+ Supported subagent launches start immediately without opening a preview/editor prompt or waiting for terminal input. This applies to single, parallel, forked, fanout, prompt-template, and human-entered `/run` and `/parallel` execution. Ask any necessary questions in the parent conversation before delegating.
33
33
 
34
- The human slash commands remain registered and continue to use their separate parsing and event-bridge path, including background and fork flags.
34
+ The human slash commands remain registered and continue to use their separate parsing and event-bridge path, including fork flags.
35
35
  Prompt-template delegation comes from the separately installed `pi-prompt-template-model` extension, whose `requestDelegatedRun` emits `prompt-template:subagent:request`. If that caller must survive an extension reload, import `registerPromptTemplateBridgeRequestSettlement` from `@bastani/subagents`, register it before the emit, and unregister it from the normal response, cancellation, or abort path. The hook rejects the caller only when the old bridge drops a stale response emit; normal completion still arrives through `prompt-template:subagent:response`. Atomic cannot register this opt-in for an out-of-tree emitter.
36
36
 
37
37
  Subagents now run and return their results directly. Atomic does not infer acceptance gates from prompt wording, inject `acceptance-report` instructions into child prompts, parse or strip `acceptance-report` blocks, or reject completed child runs because changed-file, test, or review evidence is missing. Put any evidence or validation requirements directly in the task text you give the parent or child agent.
38
38
 
39
39
  ## Foreground supervisor coordination
40
40
 
41
- When a foreground child sends `intercom.ask`, `intercom.send`, or `contact_supervisor` coordination, Atomic first probes for the exact foreground owner. Only an exact live child reserves the request; Atomic then sends a generation-scoped detach commit and waits for that child to acknowledge it before placing the message in the parent's model-visible steering queue. This first-refusal ordering also applies when the parent is a busy workflow stage: detach completes before the request enters the stage AgentSession generation boundary, breaking the child-waits-for-reply / stage-waits-for-child cycle. Unmatched and background-child messages retain existing routing—ordinary parents queue until idle, while open workflow stages fall back to their native generation admission. Blocking `need_decision` and `interview_request` calls remain actionable through [Intercom](/intercom)'s pending/reply tracker, and the exact threaded reply resumes the retained child without delayed duplicate delivery.
41
+ When a foreground child sends `intercom.ask`, `intercom.send`, or `contact_supervisor` coordination, Atomic first probes for the exact foreground owner. Only an exact live child reserves the request; Atomic then sends a generation-scoped detach commit and waits for that child to acknowledge it before placing the message in the parent's model-visible steering queue. This first-refusal ordering also applies when the parent is a busy workflow stage: detach completes before the request enters the stage AgentSession generation boundary, breaking the child-waits-for-reply / stage-waits-for-child cycle. Blocking `need_decision` and `interview_request` calls remain actionable through [Intercom](/intercom)'s pending/reply tracker, and the exact threaded reply resumes the retained child without delayed duplicate delivery. Unmatched messages retain existing routing—ordinary parents queue until idle, while open workflow stages fall back to their native generation admission.
42
42
 
43
- Only the matching foreground child can authorize release of the parent `subagent` tool. For a parallel foreground group, that accepted commit releases foreground supervision for every active sibling as one unit, so a long-running sibling cannot keep a blocking child request trapped behind the aggregate tool call; tasks still waiting behind the concurrency limit are skipped and never launched unsupervised. Children are in-process `AgentSession` instances governed by the shared Rust control plane: there is no child OS process, idle watchdog, stdout drain, or detached placeholder to recover. A detached call becomes `continued` through `continue_in_background`; its canonical child remains live in the jobs widget and later delivers one terminal result. Fire-and-forget `intercom.send` and `progress_update` also release foreground supervision promptly, but do not create a reply waiter.
43
+ Only the matching foreground child can authorize release of the parent `subagent` tool. For a parallel foreground group, that accepted commit releases foreground supervision for every active sibling as one unit, so a long-running sibling cannot keep a blocking child request trapped behind the aggregate tool call; tasks still waiting behind the concurrency limit are skipped and never launched unsupervised. Children are in-process `AgentSession` instances governed by the shared Rust control plane: there is no child OS process, idle watchdog, stdout drain, or detached placeholder to recover. A detached call becomes `continued` through `continue_detached`; its canonical child remains live and later delivers one terminal result. Fire-and-forget `intercom.send` and `progress_update` also release foreground supervision promptly, but do not create a reply waiter.
44
44
 
45
45
  Blocking coordination is race-safe: a session holds at most one outbound reply waiter, and concurrent blocking requests (parallel `intercom.ask` calls, or `intercom.ask` racing `contact_supervisor`) settle atomically. One request wins the reservation; every other concurrent call returns a normal "Already waiting for a reply" tool error without crashing the agent process or disturbing the pending ask. Cancellation and send failures release only their own waiter, and threaded replies still resolve the exact winning request.
46
46
 
47
47
  Subagent result announcements are also resilient in sessions that never receive an extension `session_start` (for example non-interactive in-process child sessions): the lazy Intercom runtime initializes from the most recent turn/tool lifecycle context and delivers self-addressed results locally. If no context is available at all, the relay acknowledges the announcement as undelivered — the `subagent` tool then falls back to returning results inline — instead of recording connection errors in the session transcript.
48
48
 
49
- Intercom connection remains tool-driven. Foreground and background launches do not import the heavy Intercom runtime or connect either the parent or bridged child automatically. If live child-to-parent coordination is needed, the parent model should invoke `intercom({ action: "status" })` before launch; the child then connects on its first `contact_supervisor` or `intercom` call. Cancellation or session replacement still invalidates the handshake generation, so stale acknowledgements cannot surface or detach a child.
49
+ Intercom connection remains tool-driven. Foreground launches do not import the heavy Intercom runtime or connect either the parent or bridged child automatically. If live child-to-parent coordination is needed, the parent model should invoke `intercom({ action: "status" })` before launch; the child then connects on its first `contact_supervisor` or `intercom` call. Cancellation or session replacement still invalidates the handshake generation, so stale acknowledgements cannot surface or detach a child.
50
50
 
51
51
  Atomic's implementation adapts the prompt foreground release and later-result recovery contracts proven in `nicobailon/pi-subagents` commits `1b55c8c`, `589e51e`, `68fb528`, and `9dfe3df`; it retains Atomic's broker and raw-TypeScript architecture rather than copying upstream's filesystem transport.
52
52
 
@@ -98,44 +98,33 @@ Review the current diff with fresh-context specialists: analyze correctness, ins
98
98
 
99
99
  Useful prompt templates include `/parallel-review`, `/review-loop`, `/parallel-research`, `/parallel-context-build`, `/parallel-handoff-plan`, and `/parallel-cleanup`. Treat them as reusable compositions, not as separate bundled agent names. Their task templates define the requested outcome, evidence and delegation boundaries, downstream output shape, and an explicit stop rule; preserve those contracts when adapting a template.
100
100
 
101
- ## Background work and control
101
+ ## Foreground work and control
102
102
 
103
- Foreground subagents stream progress in the conversation and are the right default when the parent needs the result before proceeding. Use background subagents selectively for genuinely long-running or independently useful bounded delegation; they keep working after control returns and report completion later.
103
+ Foreground subagents stream progress in the conversation and return their results before the call completes.
104
104
 
105
105
  Natural-language examples:
106
106
 
107
107
  ```text
108
- Run the local research scan in the background.
108
+ Run the local research scan.
109
109
  ```
110
110
 
111
111
  ```text
112
- Show me the current async subagent runs.
112
+ Show me the current subagent status.
113
113
  ```
114
114
 
115
115
  Tool examples:
116
116
 
117
117
  ```ts
118
- subagent({ agent: "codebase-analyzer", task: "Trace the auth flow with file references.", async: true })
119
- subagent({ action: "status" })
120
- subagent({ action: "status", id: "<run-id>" })
121
- subagent({ action: "interrupt", id: "<run-id>" })
122
- subagent({ action: "resume", id: "<run-id>", message: "continue with the test failures" })
123
- subagent({ action: "doctor" })
118
+ subagent({ agent: "codebase-analyzer", task: "Trace the auth flow with file references." })
124
119
  ```
125
120
 
126
- Use `interrupt` when you want a resumable stop. Use `resume` to send a follow-up to a reachable child, or to cold-reload a completed child from its saved session. Use `doctor` for read-only setup diagnostics.
127
-
128
- `async: true` means **do not wait**. Atomic admits an in-process child, returns its canonical child path immediately, and tracks the live child through the jobs widget. Intercom detach uses this exact same continuation mechanism, so a foreground child that asks its supervisor to coordinate becomes `continued` instead of returning a detached placeholder.
129
-
130
- **`async: true` does not survive parent exit. The live child is owned by the parent process; quitting Atomic ends any in-flight async run. Only the persisted canonical identity and session file survive, and a later session can list that cold identity and resume it.**
121
+ Use `interrupt` when you want a resumable stop. Use `resume` for a follow-up to a reachable or retained child. Use `doctor` for read-only setup diagnostics.
131
122
 
132
123
  Status, interrupt, list, and resume use the Rust registry and status watch for live children; terminal delivery is an in-memory bounded envelope with the artifact and run-history record persisted once. There is no PID polling, result-claim file, stale-run reconciliation, or detached runner process.
133
124
 
134
- Inside workflow stages, completion delivery observes the stage generation boundary. A completion received before the boundary closes is queued through the stage AgentSession and processed before the stage publishes its terminal snapshot. A completion that arrives after close is routed once to the parent/main chat and cannot reopen or append to the completed stage transcript. Producers that are still running do not hold the stage open, so background work remains non-blocking; explicit post-mortem stage chat is still available separately.
135
-
136
- When a workflow graph overlay is open, Atomic also publishes the live async subagent summary into the shared status surface. The below-editor async widget remains available when the workflow overlay is hidden, and the overlay statusline keeps the run count/state visible while the graph fills the terminal.
125
+ Inside workflow stages, completion delivery observes the stage generation boundary. A completion received before the boundary closes is queued through the stage AgentSession and processed before the stage publishes its terminal snapshot. A completion that arrives after close is routed once to the parent/main chat and cannot reopen or append to the completed stage transcript. Explicit post-mortem stage chat is still available separately.
137
126
 
138
- While a child is running, the jobs widget shows its resolved model, effective reasoning level, and applied Codex fast-mode marker for each step. The same badge appears in the completed result, including after a model fallback; parallel steps keep their metadata separate.
127
+ Live progress and completed results show each step's resolved model, effective reasoning level, and applied Codex fast-mode marker, including after a model fallback; parallel steps keep their metadata separate.
139
128
 
140
129
  ## Orchestrator model and group policy
141
130
 
@@ -143,7 +132,7 @@ Atomic applies the same delegation policy to any parent chat or workflow stage t
143
132
 
144
133
  If an agent declares no model or fallback policy, the orchestrator consults the role guidance in [Model selection](/models/model-selection), then calls `workflow({ action: "models" })` when that tool is available. It may pin only a returned `fullId` and may add a thinking suffix only when the model entry lists that level. When the catalog tool is unavailable, the catalog is empty, or no recommended model is present, the child stays unpinned and the orchestrator reports the limit instead of inventing a model or inspecting credentials.
145
134
 
146
- Each workflow invocation automatically receives one stable, non-`"default"` Intercom group as typed admission policy. Its stages and delegated children carry that group across single, parallel, async, and follow-up work unless a call explicitly overrides `group`. Outside workflows, children inherit the launching session's resolved group. This isolates workflow runs from unrelated runs and the main chat while `contact_supervisor` retains its authorized cross-group route.
135
+ Each workflow invocation automatically receives one stable, non-`"default"` Intercom group as typed admission policy. Its stages and delegated children carry that group across single, parallel, and follow-up work unless a call explicitly overrides `group`. Outside workflows, children inherit the launching session's resolved group. This isolates workflow runs from unrelated runs and the main chat while `contact_supervisor` retains its authorized cross-group route.
147
136
 
148
137
  ## Context and execution modes
149
138
 
@@ -160,17 +149,16 @@ Fresh child sessions use normal Atomic package discovery when an agent omits `ex
160
149
 
161
150
  Top-level parallel calls support up to 50 subagents after expanding each task's optional `count`. The extension's `parallel.maxTasks` setting defaults to 50 and can enforce a lower task limit; `parallel.concurrency` independently controls how many of those children run at once, while the Rust turn limiter admits at most four running turns per parent.
162
151
 
163
- Subagent tasks, parallel items, and the top-level call accept a `group` field that sets the spawned child's [Intercom](/intercom) home group, so same-group subagents can intercom each other while staying isolated from other groups. A named string joins that group; `true` auto-generates one shared UUID group per parallel set. Precedence is `explicit subagent group > inherited current-session group > config > "default"`. Workflow stages carry their runtime-owned invocation group, so children launched without `group` automatically join the workflow group; callers do not need to copy or generate an ID. In other sessions, omission inherits that launching session's resolved group. The child group is applied only when the child has Intercom access (the peer `intercom` tool or subagent-only `contact_supervisor` tool); a child without Intercom receives no group. `contact_supervisor` still reaches the supervisor across group boundaries because Atomic requests a broker capability during typed admission and binds the child's registration to the issuing supervisor. Foreground and single-child paths use exact child scopes; asynchronous runs use bounded per-child slots. The lightweight Intercom wrapper lazy-loads the authorization provider; provider failures abort launch, while hosts without a provider omit supervisor metadata instead of exposing a broken channel.
152
+ Subagent tasks, parallel items, and the top-level call accept a `group` field that sets the spawned child's [Intercom](/intercom) home group, so same-group subagents can intercom each other while staying isolated from other groups. A named string joins that group; `true` auto-generates one shared UUID group per parallel set. Precedence is `explicit subagent group > inherited current-session group > config > "default"`. Workflow stages carry their runtime-owned invocation group, so children launched without `group` automatically join the workflow group; callers do not need to copy or generate an ID. In other sessions, omission inherits that launching session's resolved group. The child group is applied only when the child has Intercom access (the peer `intercom` tool or subagent-only `contact_supervisor` tool); a child without Intercom receives no group. `contact_supervisor` still reaches the supervisor across group boundaries because Atomic requests a broker capability during typed admission and binds the child's registration to the issuing supervisor. Foreground paths use exact child scopes. The lightweight Intercom wrapper lazy-loads the authorization provider; provider failures abort launch, while hosts without a provider omit supervisor metadata instead of exposing a broken channel.
164
153
 
165
- When a subagent call, parallel task, or background run uses a `cwd`, Atomic validates that working directory before starting the child runtime. Missing or non-directory paths are reported as `cwd` problems instead of lower-level runtime errors.
154
+ When a subagent call or parallel task uses a `cwd`, Atomic validates that working directory before starting the child runtime. Missing or non-directory paths are reported as `cwd` problems instead of lower-level runtime errors.
166
155
 
167
- Single-agent calls also accept `reads: string[] | false`. Atomic prepends those files as read context for foreground and background execution through the same in-process session path, including `/run agent[reads=a.md+b.md]`. Relative entries resolve against the effective child `cwd` (including a relative top-level `cwd` resolved from the parent); absolute entries are unchanged. Invalid values fail before the child session starts.
156
+ Single-agent calls also accept `reads: string[] | false`. Atomic prepends those files as read context for foreground execution through the same in-process session path, including `/run agent[reads=a.md+b.md]`. Relative entries resolve against the effective child `cwd` (including a relative top-level `cwd` resolved from the parent); absolute entries are unchanged. Invalid values fail before the child session starts.
168
157
 
169
- Single-agent calls accept `progress: boolean` in foreground, background, and revived/resumed mode. `progress: true` creates a run-scoped `progress.md` under isolated subagent artifact storage and instructs the child to maintain it without writing `progress.md` into the child `cwd`; `progress: false` disables an agent's `defaultProgress`. When `progress` is omitted, the agent's default is inherited, except that inherited progress is suppressed for read-only tasks (`progress: true` still explicitly opts in). Foreground runs remove this run-owned progress storage after the child exits when `artifacts: false`, including children temporarily detached for intercom coordination. This is separate from `includeProgress: true`, which only includes detailed runtime progress telemetry in the final tool result and does not create or maintain a file.
158
+ Single-agent calls accept `progress: boolean` in foreground and resumed mode. `progress: true` creates a run-scoped `progress.md` under isolated subagent artifact storage and instructs the child to maintain it without writing `progress.md` into the child `cwd`; `progress: false` disables an agent's `defaultProgress`. When `progress` is omitted, the agent's default is inherited, except that inherited progress is suppressed for read-only tasks (`progress: true` still explicitly opts in). Foreground runs remove this run-owned progress storage after the child exits when `artifacts: false`, including children temporarily detached for intercom coordination. This is separate from `includeProgress: true`, which only includes detailed runtime progress telemetry in the final tool result and does not create or maintain a file.
170
159
 
171
160
  ```ts
172
161
  subagent({ agent: "worker", task: "Implement the approved fix.", progress: true })
173
- subagent({ agent: "worker", task: "Implement it in the background.", progress: true, async: true })
174
162
  ```
175
163
 
176
164
  ## Nested and fanout boundaries
package/docs/tools.md CHANGED
@@ -28,11 +28,9 @@ Before writing, Atomic verifies the current file against the tagged snapshot. If
28
28
 
29
29
  ## `bash` and `bashInterceptor`
30
30
 
31
- The `bash` tool executes shell commands in the session workspace, with optional PTY or background-job handling. When `pty: true` is requested, local execution uses the bundled Rust-backed PTY session so commands see a real terminal, including headless/tool-only and async job calls; if the native PTY package is unavailable, Atomic degrades to normal pipe execution. Set `PI_NO_PTY=1` or `ATOMIC_NO_PTY=1` to force normal pipe execution. Completed foreground results include oh-my-pi-style `timeoutSeconds`, `requestedTimeoutSeconds`, `wallTimeMs`, and non-zero `exitCode` metadata; background jobs use `details.async: { state, jobId, type: "bash" }`, can be polled with `bash({"command":"__atomic_bash_job <id>"})`, can be cancelled with `bash({"command":"__atomic_bash_job_cancel <id>"})`, and preserve overflow output in a temporary `fullOutputPath` when polling output is truncated.
31
+ The `bash` tool executes shell commands in the session workspace, with optional PTY handling. When `pty: true` is requested, local execution uses the bundled Rust-backed PTY session; if the native PTY package is unavailable, Atomic degrades to normal pipe execution. Set `PI_NO_PTY=1` or `ATOMIC_NO_PTY=1` to force normal pipe execution. Foreground results include oh-my-pi-style `timeoutSeconds`, `requestedTimeoutSeconds`, `wallTimeMs`, and non-zero `exitCode` metadata, and preserve overflow output in a temporary `fullOutputPath` when output is truncated.
32
32
 
33
- When a session-managed background bash job completes or fails, Atomic sends an `async-job-result` custom follow-up into the conversation automatically (`display: true`, delivered as a follow-up turn). Small results are inlined, and results whose raw output stays below the persistence threshold remain fully inline even if the formatted follow-up header pushes the message over the preview limit; persisted large results include a preview plus the retained `fullOutputPath` (persisted before the normal polling truncation limit so 12KB–50KB outputs remain recoverable). If the model explicitly polls a completed job with `__atomic_bash_job <id>` before the queued follow-up is delivered, or cancels a job with `__atomic_bash_job_cancel <id>`, Atomic acknowledges the result and suppresses duplicate auto-delivery while keeping the job pollable until normal bounded retention/TTL cleanup. Suppression is tied to the retained job rather than a short timer, so disposed-session jobs cannot later fall back into another session after a long-running command completes. Session delivery attempts are non-blocking across sessions: a live streaming session can defer its own follow-up until the stream boundary without delaying unrelated completed jobs. Session disposal removes that session's pending async delivery handlers; a shared manager remains alive while other live sessions still own active jobs, then cleans up when the last session is disposed. Direct SDK/tool-factory uses only get automatic delivery when they provide an async job manager/delivery handler; otherwise async jobs remain manually pollable.
34
-
35
- When explicitly enabled in settings, built-in bash interceptor rules block common shell substitutes for first-class tools (`cat`/`grep`/`find`/in-place `sed`/redirection, etc.) only when the corresponding tool is available. Enabled bash tool calls are also offered to `user_bash` extension handlers before local execution. Atomic checks the original command, the internal-URL-expanded command, configured-prefix forms, `spawnHook`-rewritten commands, and a leading `cd path && command` or `cd path; command`-stripped form only when structured `cwd` was omitted, so interceptors can route commands by effective working directory without overriding explicit `cwd`. The bash schema accepts `cwd`, `env`, `timeout`, `pty`, and `async`; `cwd` and `env` are honored by the local executor. Omitting `timeout` uses the 300-second default. An explicit timeout must be finite, greater than zero, and no more than Atomic's deliberate 3600-second ceiling; invalid values fail before execution instead of being defaulted or clamped. Valid fractional values are rounded down with a one-second floor. Normal sessions enable tracked async jobs with bounded retention. `bashInterceptor.enabled` defaults to `false`; interception is not auto-enabled.
33
+ When explicitly enabled in settings, built-in bash interceptor rules block common shell substitutes for first-class tools (`cat`/`grep`/`find`/in-place `sed`/redirection, etc.) only when the corresponding tool is available. Enabled bash tool calls are also offered to `user_bash` extension handlers before local execution. Atomic checks the original command, the internal-URL-expanded command, configured-prefix forms, `spawnHook`-rewritten commands, and a leading `cd path && command` or `cd path; command`-stripped form only when structured `cwd` was omitted, so interceptors can route commands by effective working directory without overriding explicit `cwd`. The bash schema accepts `cwd`, `env`, `timeout`, and `pty`; `cwd` and `env` are honored by the local executor. Omitting `timeout` uses the 300-second default. An explicit timeout must be finite, greater than zero, and no more than Atomic's deliberate 3600-second ceiling; invalid values fail before execution instead of being defaulted or clamped. Valid fractional values are rounded down with a one-second floor. `bashInterceptor.enabled` defaults to `false`; interception is not auto-enabled.
36
34
 
37
35
  ```json
38
36
  {
@@ -54,7 +52,7 @@ Plain URL reads follow oh-my-pi's fetch-pipeline truncation contract: unselected
54
52
 
55
53
  Output that does not fit in a tool result is written to a file, and the result points at it — `Full output: <path>` for `bash`, `Full output saved to: <path>` for any tool result that crosses the persistence threshold. Those files are storage, so Atomic bounds where they go, how large they get, and how long they live.
56
54
 
57
- **Where.** A session that persists to disk keeps its tool results in `<sessionDir>/tool-results/`, unchanged. Everything else — `bash` overflow logs, background-job logs, streamed spill files, and tool results for in-memory sessions — goes under one owner- and session-scoped temp tree:
55
+ **Where.** A session that persists to disk keeps its tool results in `<sessionDir>/tool-results/`, unchanged. Everything else — `bash` overflow logs, streamed spill files, and tool results for in-memory sessions — goes under one owner- and session-scoped temp tree:
58
56
 
59
57
  ```text
60
58
  <tmpdir>/atomic-<uid>/<session-id>/
package/docs/usage.md CHANGED
@@ -74,6 +74,8 @@ You can submit messages while the agent is still working:
74
74
  - **Ctrl+C** aborts active/queued work and pauses queued messages in place. They remain queued, in their original per-queue order, until you submit the next ordinary chat message; that submission resumes the chat and makes each queued item eligible once. After the abort settles, a later idle Ctrl+C clears the editor without releasing the hold, and a second quick idle press exits.
75
75
  - **ALT+Up** explicitly retrieves queued messages back to the editor without aborting active work or resuming a paused session. Even when retrieval empties the queue, the pause remains active until the next ordinary submission.
76
76
 
77
+ Both interrupts hold a queued message only while it is still waiting in the queue. A message the agent has already picked up is written into the transcript, and an interrupt that cancels its reply before any output appears no longer strands it: Atomic answers it instead of returning it to the editor. A reply that had already started printing is left as-is and is not restarted. Sending a message while that recovered reply is still streaming is safe — it is delivered as soon as the reply finishes.
78
+
77
79
  Both abort routes are cooperative: they ask the agent to stop and wait for it — Escape waits as long as the agent needs — and never terminate the engine that runs your tools. Ctrl+C additionally acts as an escape hatch: it always reaches Atomic when an extension's custom UI has taken over the screen — closing that UI if it does not handle the key itself — and it replaces the engine when it stops answering entirely, including a replacement that hangs before it finishes starting or one that failed to start. A message that could not be sent comes back to the editor rather than being lost: exactly as you typed it, with pasted content intact, placed above anything you typed while the send was pending and separated by a blank line, together with anything still queued behind it in the order you entered it. Atomic does not also show a red error for it. See [Keybindings](/keybindings#application).
78
80
 
79
81
  On Windows Terminal, ALT+Enter is fullscreen by default. Remap it as described in [Terminal setup](/terminal-setup) if you want Atomic to receive the shortcut.
@@ -377,7 +379,7 @@ atomic --tools read,search,find,ls -p "Review the code"
377
379
  | `ATOMIC_NO_PTY` | Set to `1` to disable PTY use for bash commands (`PI_NO_PTY` is a legacy alias) |
378
380
  | `VISUAL`, `EDITOR` | External editor for CTRL+G |
379
381
 
380
- Every foreground or background bash execution receives one execution-time snapshot of the active session:
382
+ Every bash execution runs in the foreground and receives one execution-time snapshot of the active session:
381
383
 
382
384
  | Atomic variable | Exact compatibility alias | Value |
383
385
  |-----------------|---------------------------|-------|
package/docs/windows.md CHANGED
@@ -20,7 +20,7 @@ Paths copied from Git Bash, MSYS2, Cygwin, or WSL are accepted anywhere Atomic r
20
20
 
21
21
  ## Filesystem Watchers
22
22
 
23
- On Windows, Atomic canonicalizes paths before starting native filesystem watchers. If a watcher target cannot be canonicalized or still contains an unsafe 8.3 short-name component such as `USERNA~1`, Atomic avoids native `fs.watch` for that target and uses polling where the feature supports it. This protects long-running sessions, async subagent result notifications, footer git status refreshes, and custom theme reloads from Windows/libuv path-prefix assertion crashes.
23
+ On Windows, Atomic canonicalizes paths before starting native filesystem watchers. If a watcher target cannot be canonicalized or still contains an unsafe 8.3 short-name component such as `USERNA~1`, Atomic avoids native `fs.watch` for that target and uses polling where the feature supports it. This protects long-running sessions, footer git status refreshes, and custom theme reloads from Windows/libuv path-prefix assertion crashes.
24
24
 
25
25
  ## Self-Update Behavior
26
26
 
package/docs/workflows.md CHANGED
@@ -385,6 +385,7 @@ A natural-language request for a worktree does not configure runner isolation. I
385
385
 
386
386
  ```ts
387
387
  // .atomic/workflows/issue-to-pr.ts
388
+ import { spawnSync } from "node:child_process";
388
389
  import { workflow } from "@bastani/workflows";
389
390
  import { Type, type Static } from "typebox";
390
391
 
@@ -396,12 +397,22 @@ const reviewDecision = Type.Object(
396
397
  { additionalProperties: false },
397
398
  );
398
399
 
400
+ function spawnCommand(argv: readonly string[], cwd: string) {
401
+ const [command, ...args] = argv;
402
+ if (command === undefined) throw new Error("spawnCommand requires a command");
403
+ const result = spawnSync(command, args, { cwd, encoding: "utf8" });
404
+ // A command that could not be spawned at all arrives on `error` with a null
405
+ // status, so it has to be raised here or it reads as an ordinary failure.
406
+ if (result.error) throw result.error;
407
+ return result;
408
+ }
409
+
399
410
  function runCommand(argv: readonly string[], cwd: string): string {
400
- const result = Bun.spawnSync([...argv], { cwd, stdout: "pipe", stderr: "pipe" });
401
- const stdout = result.stdout.toString().trim();
402
- const stderr = result.stderr.toString().trim();
403
- if (result.exitCode !== 0) {
404
- throw new Error(`${argv.join(" ")} failed (${result.exitCode})\n${stderr || stdout}`);
411
+ const result = spawnCommand(argv, cwd);
412
+ const stdout = (result.stdout ?? "").trim();
413
+ const stderr = (result.stderr ?? "").trim();
414
+ if (result.status !== 0) {
415
+ throw new Error(`${argv.join(" ")} failed (${result.status})\n${stderr || stdout}`);
405
416
  }
406
417
  return stdout;
407
418
  }
@@ -430,12 +441,12 @@ export default workflow({
430
441
  const baseRef = ctx.inputs.base_ref;
431
442
 
432
443
  await ctx.tool("select-feature-branch", { branch, base_ref: baseRef }, async () => {
433
- const probe = Bun.spawnSync(
444
+ const probe = spawnCommand(
434
445
  ["git", "show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
435
- { cwd, stdout: "pipe", stderr: "pipe" },
446
+ cwd,
436
447
  );
437
- if (probe.exitCode === 0) return runCommand(["git", "switch", branch], cwd);
438
- if (probe.exitCode !== 1) throw new Error(probe.stderr.toString().trim());
448
+ if (probe.status === 0) return runCommand(["git", "switch", branch], cwd);
449
+ if (probe.status !== 1) throw new Error((probe.stderr ?? "").trim());
439
450
  return runCommand(["git", "switch", "-c", branch, baseRef], cwd);
440
451
  });
441
452
 
@@ -849,6 +860,8 @@ If required inputs are missing or ambiguous, Atomic asks for them or opens the i
849
860
 
850
861
  ## Writing a Workflow
851
862
 
863
+ **A workflow executes inside whichever host is running Atomic, so its code has to work on both.** Standalone binaries are Bun-compiled while npm installs run under Node, and the active host is what loads your workflow file — so a `Bun.*` global reaches a workflow only when Atomic itself is running under Bun, and fails with `Bun is not defined` otherwise. Installing Bun separately does not change this. Write workflow code against APIs both hosts provide: `node:child_process` instead of `Bun.spawn`/`Bun.spawnSync`, `node:fs` instead of `Bun.file`, `node:path` instead of Bun's path helpers. Every example on this page follows that rule; a snippet that deliberately requires one host is marked with a `host-specific:` comment naming it.
864
+
852
865
  Workflow files are TypeScript modules that export a workflow definition:
853
866
 
854
867
  ```ts
@@ -1208,7 +1221,7 @@ interface StageSessionRuntime {
1208
1221
 
1209
1222
  A session announces its queue by `queue_update`, so a queue that exists before Atomic's listeners reach that session is announced to nobody — which happens when a retiring session hands its pending messages to the session replacing it, and when a retained session is reopened for post-mortem chat holding what it was queued. Atomic reads these two methods once, as it attaches a session, and replays the missed snapshot to that stage's listeners; every later change still arrives as an ordinary event. An adapter that omits them loses nothing it had before: only a queue predating the attach is invisible, and a session that starts empty never had one.
1210
1223
 
1211
- Externally produced traffic has a separate lifecycle rule. Intercom messages and async bash/subagent completion notices received while a workflow stage generation is still open are admitted through the stage AgentSession's native steering/follow-up queue. For a busy stage, admission into the generation boundary happens synchronously before the exact foreground subagent owner's probe/commit detach handshake; model-visible queue insertion waits inside that admitted delivery until the handshake is claimed or falls back after an unclaimed/vanished owner. A commit accepted within a parallel foreground group releases aggregate supervision for every active sibling while retaining their process and eventual-result ownership. Reserving admission before the asynchronous handshake prevents terminal close from overtaking an in-flight Intercom delivery, while waiting inside the reservation prevents a blocking child request from queueing behind either a single foreground tool call or a parallel aggregate still waiting on another child. The stage drains already-admitted work before publishing its terminal snapshot, including schema-backed turns that have already called `structured_output`.
1224
+ Externally produced traffic has a separate lifecycle rule. Intercom messages and subagent completion notices received while a workflow stage generation is still open are admitted through the stage AgentSession's native steering/follow-up queue. For a busy stage, admission into the generation boundary happens synchronously before the exact foreground subagent owner's probe/commit detach handshake; model-visible queue insertion waits inside that admitted delivery until the handshake is claimed or falls back after an unclaimed/vanished owner. A commit accepted within a parallel foreground group releases aggregate supervision for every active sibling while retaining their process and eventual-result ownership. Reserving admission before the asynchronous handshake prevents terminal close from overtaking an in-flight Intercom delivery, while waiting inside the reservation prevents a blocking child request from queueing behind either a single foreground tool call or a parallel aggregate still waiting on another child. The stage drains already-admitted work before publishing its terminal snapshot, including schema-backed turns that have already called `structured_output`.
1212
1225
 
1213
1226
  Closing the generation is atomic with admission: a notification admitted first belongs to that stage, while ordinary detached notifications arriving after close cannot reopen or mutate the completed stage and are surfaced once through the main-chat notification path instead. A blocking sibling `intercom.ask` is the deliberate exception: when the completed stage retains a valid conversation, Atomic schedules a post-mortem turn in that conversation so it can inspect the exact ask and reply without changing terminal workflow state. Failed running-stage admission and failed post-mortem admission return correlated actionable errors to the asker instead of consuming the full reply timeout.
1214
1227
 
@@ -2407,7 +2420,7 @@ Writes stage/task output to a path or disables output persistence with `false`.
2407
2420
 
2408
2421
  The runner writes the stage's **final assistant message** to `output` after the stage ends, so that path belongs to the runner. A stage that declares `output:` also automatically gets a full, rendered, line-oriented transcript of its session, and one appended instruction telling the model that its final message becomes the artifact — the workflow definition does not need to describe any of this.
2409
2422
 
2410
- An admitted external turn (for example, an async subagent completion) can arrive while the stage is still running and remains visible both to the model and in the companion transcript. The runner does not try to work out which turn was "really" the deliverable: that is an inference about intent, and an earlier revision that scored candidates by byte size got it wrong in both directions. If a late turn displaces the intended content, the transcript still holds it.
2423
+ An admitted external turn (for example, a subagent completion) can arrive while the stage is still running and remains visible both to the model and in the companion transcript. The runner does not try to work out which turn was "really" the deliverable: that is an inference about intent, and an earlier revision that scored candidates by byte size got it wrong in both directions. If a late turn displaces the intended content, the transcript still holds it.
2411
2424
 
2412
2425
  The companion transcript is written once under the durable Atomic config root at `~/.atomic/workflows/runs/<runId>/transcripts/` (or the equivalent configured agent root; `ATOMIC_WORKFLOW_ARTIFACT_DIR` overrides that root). It is never placed inside the repository tree or OS temporary storage: a home-scoped durable location survives both worktree deletion and OS temp purges, and staying outside the repo keeps full tool output — which may contain secrets — from being committed accidentally. Run-scoped artifact directories are pruned only when their durable/live run record is terminal (or the directory is an unowned orphan) and older than the exported `WORKFLOW_ARTIFACT_RETENTION_MS` policy. Running, paused, quit, blocked, and awaiting-input runs are exempt indefinitely because their artifacts are live resume dependencies. A **failed** run is terminal and does age out: it stays retryable, but the retention window is the grace period it gets, otherwise repeated recoverable failures would accumulate artifacts forever. When a terminal durable owner is aged out, the durable entry is deleted first; if authoritative deletion is unavailable or refuses, the artifact directory is preserved. Goal ledgers, Ralph implementation notes, and QA video paths share that same durable root and retention policy. The receipt names both absolute paths. Search the transcript with `rg`, then read only the narrow line ranges you need; do not read the whole transcript into a downstream prompt. The transcript is a secondary searchable record; the output artifact remains the curated handoff.
2413
2426
 
@@ -2631,7 +2644,7 @@ Sends a normal follow-on user turn to the retained stage session. This method st
2631
2644
 
2632
2645
  Native sessions accept strings or text/image content blocks. Non-native fallback adapters accept only strings and reject block arrays; `deliverAs` affects streaming delivery only, and follow-on turns retain the stage MCP scope.
2633
2646
 
2634
- Externally produced Intercom and async bash/subagent notices admitted before the generation closes drain through the same session. When a busy stage owns a foreground subagent, exact-owner detach gets first refusal before Intercom enters this boundary; unclaimed traffic then uses normal stage admission. Traffic arriving after the atomic close boundary cannot reopen the completed stage and is surfaced once through the main-chat path instead.
2647
+ Externally produced Intercom and subagent notices admitted before the generation closes drain through the same session. When a busy stage owns a foreground subagent, exact-owner detach gets first refusal before Intercom enters this boundary; unclaimed traffic then uses normal stage admission. Traffic arriving after the atomic close boundary cannot reopen the completed stage and is surfaced once through the main-chat path instead.
2635
2648
 
2636
2649
  See [Stage follow-on user messages](#stage-follow-on-user-messages) for the full lifecycle and schema-backed example.
2637
2650
 
@@ -2763,6 +2776,8 @@ interface WorkflowModelAttempt extends WorkflowSerializableObject {
2763
2776
  }
2764
2777
  ```
2765
2778
 
2779
+ When a stage explicitly configures `model` or `fallbackModels`, each recorded attempt can include usage aggregated from meaningful assistant responses in that attempt. The four token buckets remain separate, `cost` sums the provider-reported total cost, and `turns` counts the assistant usage records included in the aggregate. Usage from earlier retained or reattached session history is excluded, while billed error responses removed during a same-model retry remain attributed to that attempt. The `usage` property is omitted when the provider reports no meaningful token or cost signal. Stages that use only the default model without explicit fallback configuration do not currently create model-attempt records.
2780
+
2766
2781
  ### `WorkflowDetails`
2767
2782
 
2768
2783
  ```typescript
@@ -2979,7 +2994,7 @@ Surface behavior:
2979
2994
  - **Tool and node detail** - Attached stage chats match main chat's tool-detail expansion behavior while keeping expansion state local to the workflow UI context. Press Ctrl+O (the configurable `app.tools.expand` binding) to expand every visible workflow node and tool card, including single, parallel, and nested subagent progress, current tool activity, and artifact paths; press it again to collapse them. The toggle works for active, completed, and archived stage views, including at the supported 40-column terminal minimum. A mounted prompt, custom question, or other input-owning overlay keeps the key instead of changing it.
2980
2995
  - **Footer context** - An attached live stage chat carries the main chat's current-folder and Git-branch identity into its themed footer and mirrors live extension status lines such as the MCP server indicator. Branch changes trigger a repaint through the host's cached footer provider, and extension status changes are read from that same provider rather than recomputed by the workflow UI.
2981
2996
  - **Working animation lifecycle** - Ordinary attached-stage work keeps the same exact one-cell `∀` visible while following the active workflow theme's dark → accent → bright/bold → accent → dark luminance ramp every 88ms. Every agent and SDK turn resets to the dark regular phase with a fresh lifecycle-relative cadence; turn, terminal, error, replacement, and disposal cleanup stop the active timer without stale repaint. In an eligible retained-stage chat, every accepted idle follow-up — including a workflow-authored `stage.sendUserMessage(...)` after a prior turn ended — shows Working on admission or attach, including while Atomic restores a saved retained conversation, and keeps it through prompt startup, pre-turn compaction, and agent handoff. Attaching or remounting mid-delivery paints immediately rather than waiting for the turn's first event. A message queued into a live turn with `followUp`/`steer` uses that turn's existing status instead of starting a new one. A no-turn result, prompt or restore error, or terminal completion removes it; once the last accepted post-terminal delivery settles, a leftover start cannot bring it back. An accepted manual retry clears stale status from the prior prompt before showing new pre-stream activity. `NO_COLOR` retains regular/bold activity without foreground-color escapes. Reduced motion uses a static regular accent `∀` without an animation timer; factual automatic retry, fallback, compaction, cancellation, and error copy retains precedence.
2982
- - **Async statusline** - If an async/background subagent is running while the fullscreen workflow graph is open, the graph statusline mirrors the async summary so the background run remains visible; hide the graph with `h`, leave it with `ctrl+x`, or reconnect later to return to the full below-editor async widget.
2997
+ - **Subagent statusline** - If a subagent is running while the fullscreen workflow graph is open, the graph statusline mirrors its summary so the run remains visible; hide the graph with `h`, leave it with `ctrl+x`, or reconnect later to return to the full below-editor widget.
2983
2998
  - **Run control** - Use `interrupt`, `pause`, and `resume` for resumable live work. Pause/interrupt holds a stage's queued steering and follow-up items in place without dequeuing them or starting continuation; `resume` releases those items once in their existing per-queue order, but queue release alone does not start a model turn. `resume` on a non-paused run reopens the saved snapshot or overlay. Use `quit` to pause a live run gracefully while preserving it for `/workflow resume`.
2984
2999
  - **Rediscovery** - Use `/workflow reload` after adding, editing, installing, or removing workflow resources or package manifest workflow entries and you want Atomic to rediscover them in-process ([Reloading workflow resources](#reloading-workflow-resources)).
2985
3000
  - **Status listing** - `/workflow status` lists all retained active and terminal top-level runs by default; implementation-owned nested child runs are flattened into their parent workflow rather than listed separately. `/workflow status --all` is retained as a compatibility alias.
@@ -3038,6 +3053,7 @@ workflow({ action: "reload", reason: "added team workflow" })
3038
3053
  Control behavior:
3039
3054
 
3040
3055
  - `runId` requires the full 36-character run UUID for every lifecycle and inspection action, including `status`. User-facing status surfaces print that exact value, so pass it back verbatim; typed prefixes are rejected with a distinct `Run id must be a full 36-character UUID` diagnostic rather than resolved. Because ids are matched exactly and are unique, no run target is ambiguous. Status lists and run pickers show top-level user-launched workflows; nested child runs are implementation details of the expanded parent graph.
3056
+ - `status`, `stages`, `stage`, and `transcript` with an explicit full `runId` first use the current session store, then perform one exact DBOS hydration when that id is absent locally. This is inspection only: Atomic does not claim ownership, change status, run workflow code, or resume the workflow. A stale durable `running` root is shown as `crashed` with its resumability and an explicit `/workflow resume <id>` hint; fresh work owned by another Atomic process remains `running`, offers read-only status guidance, and stays protected from local control or resume. Deleted/tombstoned, absent, malformed, cyclic, orphaned, nonreciprocal, out-of-scope, and duplicate-node records report distinct failures instead of inventing a partial graph. `status` without `runId` remains current-session-only and never scans durable history.
3041
3057
  - `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `pause`/`resume`/`interrupt`/`quit`/`send` accept, so an orchestrating agent can list runs and act on them directly.
3042
3058
  - `statusFilter` narrows the `status` run listing: run statuses (`pending`, `running`, `paused`, `blocked`, `completed`, `failed`, `skipped`, `cancelled`, `killed`) match runs directly, `awaiting_input` selects runs with at least one stage awaiting input or pending human prompt, and `all` (the default) includes everything.
3043
3059
  - `format: "json"` on data-bearing inspection actions (`status`, `stages`, `stage`, `transcript`) returns the full structured result; the default text output for `status` is the concise per-run summary list.
@@ -3065,6 +3081,7 @@ Control behavior:
3065
3081
  - `pause` is useful for pausing a live run or a single live stage without treating it as a destructive abort.
3066
3082
  - `resume` can target a stage with `stageId`; the target may be an exact stage id or an exact stage name. `message` is forwarded to paused work. For a live interrupted streaming prompt, Atomic preserves the existing prompt loop without duplicating the user message and injects `Continue where you left off. If you believe you are finished with your original task (or a redefined task if the user told you), stop.` when required before normal readiness-gate completion. For a paused stage that was idle waiting for a new stage-chat turn, a non-empty message resumes the stage and starts exactly one fresh prompt containing that message; an empty resume releases the pause without creating a prompt.
3067
3083
  - An explicit workflow-tool `resume` target that is absent from the current session store triggers targeted DBOS discovery before Atomic returns `Run not found`. The target must be a full run UUID; an eligible exact ID resumes under the original workflow ID, and a malformed target is rejected before any durable lookup happens. Resource-loading and durable-backend failures remain visible. Ordinary workflow-tool `status` listing stays session-local and does not eagerly hydrate durable history.
3084
+ - Exact-id durable inspection is separate from resume. `status`, `stages`, `stage`, and `transcript` may hydrate one missing-local root for read-only inspection, but they never claim it or execute replay. Only an explicit `resume` action enters the claim-and-dispatch path.
3068
3085
  - Run-level `quit` gracefully pauses in-flight work, marks the run resumable, and leaves it available to `/workflow resume`. A run whose only in-flight work is a `ctx.tool` node is quit like any other: it pauses as resumable instead of reporting that there are no controllable stages.
3069
3086
  - `reload` refreshes discovered workflow resources in-process; the optional `reason` is echoed in the result.
3070
3087
 
@@ -3192,7 +3209,7 @@ Atomic workflows use **DBOS/Postgres as their sole persistent workflow backend**
3192
3209
 
3193
3210
  When the embedded binaries are unavailable for the platform, Atomic falls back to DBOS's reusable `dbos-db` Docker container. If no durable backend can be provisioned at all, workflows **degrade to a process-local in-memory backend with a loud warning** instead of refusing to run: the run executes normally, but its state does not survive the process and `/workflow resume` after exit has nothing to restore. Set `DBOS_SYSTEM_DATABASE_URL` to an existing Postgres to restore durability.
3194
3211
 
3195
- **Multiple concurrent Atomic sessions.** Every Atomic process launches DBOS with a unique executor id, and running root workflows carry owner/heartbeat metadata refreshed by ordinary 30-second stage-timing checkpoints. **Running workflows are never resume targets**: a running row with a fresh heartbeat is hidden from every session's picker and refused by direct `/workflow resume <id>` — resuming a workflow that is executing elsewhere would double-dispatch it. Once the heartbeat goes stale (about two minutes after a crash), the workflow surfaces as a red `crashed` row.
3212
+ **Multiple concurrent Atomic sessions.** Every Atomic process launches DBOS with a unique executor id, and running root workflows carry owner/heartbeat metadata. Once an active model stage has a session path, Atomic records that identity after the stage-start record and awaits the checkpoint before the first model use, then runs serialized, unref'd liveness checkpoints on a bounded 30-second cadence for the root and nested scoped workflows. Each accepted checkpoint refreshes root metadata; timers stop on every stage exit and cannot keep Atomic alive. A persistent checkpoint fault fails the active stage instead of disappearing in a detached timer. A stage that is shutting down drains the checkpoint still in flight rather than abandoning it, so a failure that lands after the model turn finished is reported instead of discarded, and a stage whose final durability checkpoint fails is recorded as `failed` rather than `completed` — its caller receives the error and its concurrency slot is released either way. **Running workflows are never resume targets**: a running row with a fresh heartbeat is hidden from every session's picker and refused by direct `/workflow resume <id>` — resuming a workflow that is executing elsewhere would double-dispatch it. Once the heartbeat goes stale (about two minutes after a crash), an exact inspection or the resume picker reports the workflow as `crashed`.
3196
3213
 
3197
3214
  When two sessions race to resume the same paused workflow, a durable first-writer-wins claim decides exactly one winner; the loser reconciles to the authoritative state and reports that the workflow changed while resume was pending.
3198
3215
 
@@ -3203,7 +3220,7 @@ When two sessions race to resume the same paused workflow, a durable first-write
3203
3220
  - **Durable child identity before dispatch**: before a nested `ctx.workflow(...)` can run child code or a child side effect, Atomic persists and awaits a versioned boundary-start record containing its stable boundary and child run ids, root/parent ownership, source order and parents, composed replay scope, alias, workflow, lifecycle state, and a deterministic fingerprint of the definition plus exact validated inputs. Distinct-input parallel calls keep stable independent scopes even when restart reverses dispatch order; identical calls share that fingerprint and use their own ordinal. Replay validates and reuses that identity before allocating any UUID.
3204
3221
  - **Symmetric nested scopes**: child effects stay stored under the durable root, while every child sees only its own local checkpoint view. Each nesting layer strips exactly one scope and never suffix-matches sibling or root data, so the rule composes at any depth.
3205
3222
  - **Stable durable graph**: tool, stage, task, chain, parallel, and child-workflow checkpoints preserve stable source identity/order, parent DAG edges, actual status, owning-run/boundary metadata, timing, output summary, model, retained chat-session references, and exact `{ runId, stageId }` targets. Fresh-process resume and completed inspection reconstruct tool-only, nested-child, mixed, and parallel topology directly from DBOS.
3206
- - **DBOS-only discovery**: `/workflow resume`, `/workflows`, completed inspection, deletion, and targeted lookup hydrate/query DBOS. Session JSONL remains only a chat transcript referenced by a current checkpoint; it is not a workflow catalog or discovery source.
3223
+ - **DBOS-only discovery and exact inspection**: `/workflow resume`, `/workflows`, completed inspection, deletion, and targeted lookup hydrate/query DBOS. An explicit full run id on `status`, `stages`, `stage`, or `transcript` hydrates only that root when the current-session store misses; a no-id status listing stays session-local. Session JSONL remains only a chat transcript referenced by a current checkpoint; it is not a workflow catalog or discovery source.
3207
3224
  - **Fail-closed compatibility**: prior local and pre-current records are not converted. A completed current-format child boundary created before boundary-start or invocation-fingerprint identity is accepted only when child checkpoints reciprocally prove the same root, parent run, boundary, child, and scope. Active records without a provable invocation fingerprint, and malformed, duplicate, stale, nonreciprocal, mixed, aliased, cyclic, orphaned, or unsupported topology, are hidden or refused before cache/control/child dispatch without inventing a child link or executing repair work.
3208
3225
  - **Topology validation boundary**: authoring and discovery guidance cannot prove dynamic acyclicity. Runtime topology work must validate each materialized parent edge incrementally during execution and replay, and DBOS hydration must reject cyclic restored topology before exposing cache, control, or child dispatch.
3209
3226
  - **Cross-session safety**: per-process executor identity, owner/heartbeat liveness on running handles, and claim-guarded status transitions prevent double dispatch when several Atomic sessions share the database.
@@ -3212,7 +3229,7 @@ When two sessions race to resume the same paused workflow, a durable first-write
3212
3229
 
3213
3230
  **Resume after editing a workflow.** Replay identity combines the workflow id with stable content hashes and call order. Child calls additionally bind the child definition to the exact validated input value, with a per-identical-invocation ordinal. Editing definitions, inputs, or `ctx.*` call structure can intentionally invalidate matches. Finish or delete retained runs before deploying incompatible workflow changes. Atomic refuses a stored child boundary whose fingerprint, replay scope, alias, workflow, ownership, source order, or parentage no longer matches instead of attaching it to the changed call site.
3214
3231
 
3215
- Durable `/workflow resume` preserves completed stage metadata, active-stage elapsed time, total run elapsed time, source order and parent edges, actual lifecycle status, nested ownership, and exact control targets. A completed nested boundary, its completed child stages, `ctx.tool` effects, and answered `ctx.ui` responses are cache hits; only incomplete child or downstream parent work continues. Raw stage-chat prompt answers represented by `StageSnapshot.promptAnswerState` remain live-memory-only and are not DBOS-persisted. While an LM stage or task is active, repeated durable checkpoints refresh its accumulated pause-adjusted duration even when its session file does not change, and refresh the run's total accumulated elapsed time alongside it. Graceful quit forces an exact stage and run timing checkpoint even inside the ordinary 30-second update bucket; normal completion also persists the final accumulated run total.
3232
+ Durable `/workflow resume` preserves completed stage metadata, active-stage elapsed time, total run elapsed time, source order and parent edges, actual lifecycle status, nested ownership, and exact control targets. A completed nested boundary, its completed child stages, `ctx.tool` effects, and answered `ctx.ui` responses are cache hits; only incomplete child or downstream parent work continues. Raw stage-chat prompt answers represented by `StageSnapshot.promptAnswerState` remain live-memory-only and are not DBOS-persisted. While a model stage or task is active, Atomic persists its session identity as soon as the path exists and refreshes pause-adjusted duration plus root liveness at most once per serialized 30-second heartbeat. Nested stages route the same checkpoint through their scoped backend to the durable root. Graceful quit forces an exact stage and run timing checkpoint even inside the ordinary update bucket; normal completion also persists the final accumulated run total.
3216
3233
 
3217
3234
  Each new Atomic process that reopens unfinished work starts from the latest saved baseline, so repeated process-boundary resumes keep stable boundary/child ids, status, graph, and lifecycle duration cumulative without double-counting pauses. A stage paused at ten seconds resumes at ten seconds, and the main-chat dashboard reports prior-session elapsed plus current-session elapsed. Completed inspection uses that same accumulated run timing rather than DBOS record wall-clock age.
3218
3235
 
@@ -3314,7 +3331,7 @@ Fresh completed inspection does not currently persist the workflow's declared ro
3314
3331
 
3315
3332
  Targets resolve across top-level live, resumable durable, and completed entries as one namespace, matched by full UUID only. An exact loadable paused top-level live target resumes directly from in-session state without enumerating the durable completed-history catalog; this keeps explicit live resume responsive even when retained durable history is large and preserves live-over-durable precedence for duplicate IDs. If a stale or concurrent catalog view presents the same failed root as both resumable and read-only history, the resumable durable target wins. Nested child runs remain excluded from this top-level target namespace even when addressed by an exact ID.
3316
3333
 
3317
- The non-interactive `workflow({ action: "resume", runId: "<full-run-uuid>" })` surface uses the same durable resumable-target lookup behavior for explicit targets. If the target is absent locally, Atomic loads workflow resources, queries the authoritative DBOS resumable catalog, and only then reports a missing run. This targeted hydration does not change `workflow({ action: "status" })`: an empty session-local status before explicit resume does not imply that DBOS deleted the workflow.
3334
+ The non-interactive `workflow` surface uses exact targeted DBOS lookup for explicit ids. `resume` loads workflow resources, queries and revalidates the authoritative resumable record, then claims and dispatches only when the caller explicitly requested resume. `status`, `stages`, `stage`, and `transcript` hydrate one exact missing-local root into an isolated read-only snapshot and never dispatch. This targeted path does not change `workflow({ action: "status" })` without a run id: an empty session-local listing neither scans DBOS nor implies that DBOS deleted the workflow.
3318
3335
 
3319
3336
  A target that is not a full UUID is rejected before the combined catalog is consulted, so a truncated id never reaches durable lookup. Read-only inspection behavior is otherwise unchanged. A current completed or non-resumable failed backend row with valid graph checkpoints remains inspectable even if every retained stage conversation is unavailable. Missing, empty, directory, context-empty, or partially malformed transcript paths are stripped from chat attachment while the graph stays read-only and visible.
3320
3337
 
@@ -3327,7 +3344,7 @@ Validation uses the final retained transcript for a repeated stage replay key, s
3327
3344
  | **Internally cancelled workflow** | Marked `cancelled` in durable state and excluded from `/workflow resume` discovery. Start a new workflow run if you intentionally want to retry cancelled work. |
3328
3345
  | **Stage failure (recoverable)** | Workflow marked `failed` or `blocked` and remains resumable by default. `/workflow resume <id>` continues from the last completed checkpoint unless durable metadata explicitly sets `resumable: false`. |
3329
3346
  | **Stage failure (non-recoverable)** | Workflow marked `failed` or `blocked` with `resumable: false`, so it cannot resume execution. A failed root with saved checkpoint progress may still appear in read-only history for inspection; a blocked root does not. |
3330
- | **Process crash** | Workflow remains `running` in durable state. On next session start, it appears in resume discovery when it has a durable checkpoint or pending prompt. Resume re-executes from the last completed checkpoint. |
3347
+ | **Process crash** | Workflow remains `running` in durable state. Exact-id status/inspection reconstructs its retained checkpoint DAG as `crashed` once the owner heartbeat is stale and shows whether explicit resume is available. `/workflow resume <id>` is still required to claim the root and continue from the last completed checkpoint. |
3331
3348
  | **`ctx.tool` retry/default failure** | When `retriesAllowed: true`, the tool function is retried with exponential backoff. Cancellation is checked before each attempt, during retry backoff, and through the callback's own `signal`. Without `failureMode: "return"`, an exhausted callback error propagates and the workflow fails. |
3332
3349
  | **Recoverable `ctx.tool` failure** | With `failureMode: "return"`, exhausted callback failures are durably returned after retries. The tool node remains failed, downstream handoff is explicit, and replay returns the same outcome with `cached: true`. Cancellation and storage faults still throw. |
3333
3350
  | **`ctx.tool` node quit/interrupt** | `quit`/`interrupt` with a tool node id or name aborts that call's signal, marks the node `cancelled`, and leaves sibling stages and tools running. The action returns `status: "cancelled"` with the separately observed `workflowStatus`; it never reports the run as paused. No replayable `tool:` checkpoint and no `return_failure` outcome are written — return mode writes only inspection metadata — so resume re-runs exactly that call at the same ordinal and node id. |
@@ -4678,17 +4695,28 @@ The parent should verify each child before creating the next boundary. If a gate
4678
4695
  The calls below are deliberately unrolled. Repeat the downstream shape for the planned slices, giving every call a fresh child boundary and distinct tracked nodes; do not reopen an ancestor or add a back-edge.
4679
4696
 
4680
4697
  ```ts
4698
+ import { spawnSync } from "node:child_process";
4681
4699
  import { resolve } from "node:path";
4682
4700
  import { Type } from "typebox";
4683
4701
  import { workflow } from "@bastani/workflows";
4684
4702
  import { goal } from "@bastani/workflows/builtin";
4685
4703
 
4704
+ function spawnCommand(argv: readonly string[], cwd: string) {
4705
+ const [command, ...args] = argv;
4706
+ if (command === undefined) throw new Error("spawnCommand requires a command");
4707
+ const result = spawnSync(command, args, { cwd, encoding: "utf8" });
4708
+ // A command that could not be spawned at all arrives on `error` with a null
4709
+ // status, so it has to be raised here or it reads as an ordinary failure.
4710
+ if (result.error) throw result.error;
4711
+ return result;
4712
+ }
4713
+
4686
4714
  function runCommand(argv: readonly string[], cwd: string): string {
4687
- const result = Bun.spawnSync([...argv], { cwd, stdout: "pipe", stderr: "pipe" });
4688
- const stdout = result.stdout.toString().trim();
4689
- const stderr = result.stderr.toString().trim();
4690
- if (result.exitCode !== 0) {
4691
- throw new Error(`${argv.join(" ")} failed (${result.exitCode})\n${stderr || stdout}`);
4715
+ const result = spawnCommand(argv, cwd);
4716
+ const stdout = (result.stdout ?? "").trim();
4717
+ const stderr = (result.stderr ?? "").trim();
4718
+ if (result.status !== 0) {
4719
+ throw new Error(`${argv.join(" ")} failed (${result.status})\n${stderr || stdout}`);
4692
4720
  }
4693
4721
  return stdout;
4694
4722
  }
@@ -4719,28 +4747,28 @@ export default workflow({
4719
4747
  toolName,
4720
4748
  { branch, base_branch: baseBranch, git_worktree_dir: gitWorktreeDir },
4721
4749
  async () => {
4722
- const current = Bun.spawnSync(
4750
+ const current = spawnCommand(
4723
4751
  ["git", "-C", worktreePath, "branch", "--show-current"],
4724
- { cwd: repoRoot, stdout: "pipe", stderr: "pipe" },
4752
+ repoRoot,
4725
4753
  );
4726
- if (current.exitCode === 0) {
4727
- const checkedOutBranch = current.stdout.toString().trim();
4754
+ if (current.status === 0) {
4755
+ const checkedOutBranch = (current.stdout ?? "").trim();
4728
4756
  if (checkedOutBranch !== branch) {
4729
4757
  throw new Error(`${worktreePath} is checked out on ${checkedOutBranch || "detached HEAD"}, expected ${branch}`);
4730
4758
  }
4731
4759
  return { branch, worktree: worktreePath };
4732
4760
  }
4733
4761
 
4734
- const branchProbe = Bun.spawnSync(
4762
+ const branchProbe = spawnCommand(
4735
4763
  ["git", "show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
4736
- { cwd: repoRoot, stdout: "pipe", stderr: "pipe" },
4764
+ repoRoot,
4737
4765
  );
4738
- if (branchProbe.exitCode === 0) {
4766
+ if (branchProbe.status === 0) {
4739
4767
  runCommand(["git", "worktree", "add", worktreePath, branch], repoRoot);
4740
- } else if (branchProbe.exitCode === 1) {
4768
+ } else if (branchProbe.status === 1) {
4741
4769
  runCommand(["git", "worktree", "add", "-b", branch, worktreePath, baseBranch], repoRoot);
4742
4770
  } else {
4743
- throw new Error(branchProbe.stderr.toString().trim() || `could not inspect branch ${branch}`);
4771
+ throw new Error((branchProbe.stderr ?? "").trim() || `could not inspect branch ${branch}`);
4744
4772
  }
4745
4773
  return { branch, worktree: worktreePath };
4746
4774
  },