@bastani/atomic 0.9.27 → 0.9.28-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +6 -0
  3. package/dist/builtin/intercom/broker/broker.ts +13 -1
  4. package/dist/builtin/intercom/package.json +2 -2
  5. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  6. package/dist/builtin/mcp/package.json +1 -1
  7. package/dist/builtin/subagents/package.json +3 -3
  8. package/dist/builtin/web-access/index.bundle.mjs +194 -44
  9. package/dist/builtin/web-access/package.json +3 -3
  10. package/dist/builtin/workflows/CHANGELOG.md +31 -0
  11. package/dist/builtin/workflows/builtin/{chunk-gw7rghda.js → chunk-aqv8ny38.js} +257 -259
  12. package/dist/builtin/workflows/builtin/{chunk-gts8wgjr.js → chunk-br4ac9a3.js} +1 -1
  13. package/dist/builtin/workflows/builtin/{chunk-9bhs02gm.js → chunk-p3xmajmj.js} +439 -40
  14. package/dist/builtin/workflows/builtin/chunk-p6jagvyf.js +8 -0
  15. package/dist/builtin/workflows/builtin/goal.js +2 -2
  16. package/dist/builtin/workflows/builtin/index.js +4 -4
  17. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  18. package/dist/builtin/workflows/package.json +2 -2
  19. package/dist/builtin/workflows/src/authoring.d.ts +14 -0
  20. package/dist/builtin/workflows/src/extension/index.bundle.mjs +108110 -107201
  21. package/dist/builtin/workflows/src/index.js +42226 -41077
  22. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +16 -11
  23. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +16 -1
  24. package/dist/cli/args.d.ts +1 -0
  25. package/dist/cli/args.d.ts.map +1 -1
  26. package/dist/cli/args.js +12 -5
  27. package/dist/cli/args.js.map +1 -1
  28. package/dist/cli/credential-print.d.ts +1 -1
  29. package/dist/cli/credential-print.d.ts.map +1 -1
  30. package/dist/cli/credential-print.js +2 -56
  31. package/dist/cli/credential-print.js.map +1 -1
  32. package/dist/codemode-worker.js +99 -3
  33. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  34. package/dist/core/agent-session-accessors.js +6 -1
  35. package/dist/core/agent-session-accessors.js.map +1 -1
  36. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  37. package/dist/core/agent-session-compaction.js +20 -3
  38. package/dist/core/agent-session-compaction.js.map +1 -1
  39. package/dist/core/agent-session-events.d.ts.map +1 -1
  40. package/dist/core/agent-session-events.js +3 -2
  41. package/dist/core/agent-session-events.js.map +1 -1
  42. package/dist/core/agent-session-extension-bindings.d.ts +6 -0
  43. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  44. package/dist/core/agent-session-extension-bindings.js +96 -12
  45. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  46. package/dist/core/agent-session-methods.d.ts +4 -1
  47. package/dist/core/agent-session-methods.d.ts.map +1 -1
  48. package/dist/core/agent-session-methods.js.map +1 -1
  49. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  50. package/dist/core/agent-session-prompt.js +19 -3
  51. package/dist/core/agent-session-prompt.js.map +1 -1
  52. package/dist/core/agent-session-services.d.ts +1 -0
  53. package/dist/core/agent-session-services.d.ts.map +1 -1
  54. package/dist/core/agent-session-services.js +1 -0
  55. package/dist/core/agent-session-services.js.map +1 -1
  56. package/dist/core/agent-session-state.d.ts.map +1 -1
  57. package/dist/core/agent-session-state.js +3 -2
  58. package/dist/core/agent-session-state.js.map +1 -1
  59. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  60. package/dist/core/agent-session-tool-registry.js +11 -16
  61. package/dist/core/agent-session-tool-registry.js.map +1 -1
  62. package/dist/core/agent-session-types.d.ts +1 -0
  63. package/dist/core/agent-session-types.d.ts.map +1 -1
  64. package/dist/core/agent-session-types.js.map +1 -1
  65. package/dist/core/agent-session.d.ts +2 -0
  66. package/dist/core/agent-session.d.ts.map +1 -1
  67. package/dist/core/agent-session.js +5 -0
  68. package/dist/core/agent-session.js.map +1 -1
  69. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  70. package/dist/core/auth-storage-backends.js +2 -1
  71. package/dist/core/auth-storage-backends.js.map +1 -1
  72. package/dist/core/auth-storage.d.ts +1 -1
  73. package/dist/core/auth-storage.d.ts.map +1 -1
  74. package/dist/core/auth-storage.js +15 -7
  75. package/dist/core/auth-storage.js.map +1 -1
  76. package/dist/core/azure-provider-compat.d.ts +8 -0
  77. package/dist/core/azure-provider-compat.d.ts.map +1 -0
  78. package/dist/core/azure-provider-compat.js +65 -0
  79. package/dist/core/azure-provider-compat.js.map +1 -0
  80. package/dist/core/child-session-options.d.ts +1 -1
  81. package/dist/core/child-session-options.d.ts.map +1 -1
  82. package/dist/core/child-session-options.js +11 -2
  83. package/dist/core/child-session-options.js.map +1 -1
  84. package/dist/core/compaction/classifier-compaction.d.ts +13 -0
  85. package/dist/core/compaction/classifier-compaction.d.ts.map +1 -0
  86. package/dist/core/compaction/classifier-compaction.js +110 -0
  87. package/dist/core/compaction/classifier-compaction.js.map +1 -0
  88. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -1
  89. package/dist/core/compaction/compaction-boundary.js +2 -6
  90. package/dist/core/compaction/compaction-boundary.js.map +1 -1
  91. package/dist/core/compaction/compaction-runner.d.ts +16 -1
  92. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  93. package/dist/core/compaction/compaction-runner.js +97 -12
  94. package/dist/core/compaction/compaction-runner.js.map +1 -1
  95. package/dist/core/compaction/compaction-types.d.ts +18 -1
  96. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  97. package/dist/core/compaction/compaction-types.js +1 -1
  98. package/dist/core/compaction/compaction-types.js.map +1 -1
  99. package/dist/core/compaction/fallback-planner.d.ts +1 -0
  100. package/dist/core/compaction/fallback-planner.d.ts.map +1 -1
  101. package/dist/core/compaction/fallback-planner.js +2 -0
  102. package/dist/core/compaction/fallback-planner.js.map +1 -1
  103. package/dist/core/compaction/index.d.ts +3 -0
  104. package/dist/core/compaction/index.d.ts.map +1 -1
  105. package/dist/core/compaction/index.js +3 -0
  106. package/dist/core/compaction/index.js.map +1 -1
  107. package/dist/core/compaction/model-resolver.d.ts +16 -0
  108. package/dist/core/compaction/model-resolver.d.ts.map +1 -0
  109. package/dist/core/compaction/model-resolver.js +18 -0
  110. package/dist/core/compaction/model-resolver.js.map +1 -0
  111. package/dist/core/compaction/morph-compaction.d.ts +14 -0
  112. package/dist/core/compaction/morph-compaction.d.ts.map +1 -0
  113. package/dist/core/compaction/morph-compaction.js +99 -0
  114. package/dist/core/compaction/morph-compaction.js.map +1 -0
  115. package/dist/core/compaction/pi-summary-fallback.d.ts +34 -0
  116. package/dist/core/compaction/pi-summary-fallback.d.ts.map +1 -0
  117. package/dist/core/compaction/pi-summary-fallback.js +357 -0
  118. package/dist/core/compaction/pi-summary-fallback.js.map +1 -0
  119. package/dist/core/compaction/planner-outcome.d.ts +10 -6
  120. package/dist/core/compaction/planner-outcome.d.ts.map +1 -1
  121. package/dist/core/compaction/planner-outcome.js +11 -4
  122. package/dist/core/compaction/planner-outcome.js.map +1 -1
  123. package/dist/core/compaction/range-planner-diagnostics.d.ts +3 -1
  124. package/dist/core/compaction/range-planner-diagnostics.d.ts.map +1 -1
  125. package/dist/core/compaction/range-planner-diagnostics.js +11 -0
  126. package/dist/core/compaction/range-planner-diagnostics.js.map +1 -1
  127. package/dist/core/compaction/range-planner.d.ts +3 -3
  128. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  129. package/dist/core/compaction/range-planner.js +59 -28
  130. package/dist/core/compaction/range-planner.js.map +1 -1
  131. package/dist/core/compaction/region-trimming.d.ts.map +1 -1
  132. package/dist/core/compaction/region-trimming.js +7 -0
  133. package/dist/core/compaction/region-trimming.js.map +1 -1
  134. package/dist/core/compaction/structured-compaction-input.d.ts +19 -0
  135. package/dist/core/compaction/structured-compaction-input.d.ts.map +1 -0
  136. package/dist/core/compaction/structured-compaction-input.js +70 -0
  137. package/dist/core/compaction/structured-compaction-input.js.map +1 -0
  138. package/dist/core/compaction/transcript-serialization.d.ts +1 -0
  139. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -1
  140. package/dist/core/compaction/transcript-serialization.js +26 -0
  141. package/dist/core/compaction/transcript-serialization.js.map +1 -1
  142. package/dist/core/extensions/api-types.d.ts +3 -2
  143. package/dist/core/extensions/api-types.d.ts.map +1 -1
  144. package/dist/core/extensions/api-types.js.map +1 -1
  145. package/dist/core/extensions/context-types.d.ts +1 -0
  146. package/dist/core/extensions/context-types.d.ts.map +1 -1
  147. package/dist/core/extensions/context-types.js.map +1 -1
  148. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  149. package/dist/core/extensions/loader-api.js +5 -0
  150. package/dist/core/extensions/loader-api.js.map +1 -1
  151. package/dist/core/extensions/loader-host-modules.d.ts +1 -0
  152. package/dist/core/extensions/loader-host-modules.d.ts.map +1 -1
  153. package/dist/core/extensions/loader-host-modules.js +12 -0
  154. package/dist/core/extensions/loader-host-modules.js.map +1 -1
  155. package/dist/core/extensions/runner-context.d.ts +10 -4
  156. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  157. package/dist/core/extensions/runner-context.js +47 -3
  158. package/dist/core/extensions/runner-context.js.map +1 -1
  159. package/dist/core/extensions/runner.d.ts +11 -4
  160. package/dist/core/extensions/runner.d.ts.map +1 -1
  161. package/dist/core/extensions/runner.js +38 -9
  162. package/dist/core/extensions/runner.js.map +1 -1
  163. package/dist/core/extensions/tool-types.d.ts +2 -0
  164. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  165. package/dist/core/extensions/tool-types.js.map +1 -1
  166. package/dist/core/extensions/workflow-events.d.ts +49 -0
  167. package/dist/core/extensions/workflow-events.d.ts.map +1 -1
  168. package/dist/core/extensions/workflow-events.js.map +1 -1
  169. package/dist/core/extensions/workflow-run-control-hub.d.ts +16 -4
  170. package/dist/core/extensions/workflow-run-control-hub.d.ts.map +1 -1
  171. package/dist/core/extensions/workflow-run-control-hub.js +72 -1
  172. package/dist/core/extensions/workflow-run-control-hub.js.map +1 -1
  173. package/dist/core/extensions/workflow-run-control.d.ts +11 -3
  174. package/dist/core/extensions/workflow-run-control.d.ts.map +1 -1
  175. package/dist/core/extensions/workflow-run-control.js.map +1 -1
  176. package/dist/core/fast-model-variants.d.ts +1 -1
  177. package/dist/core/fast-model-variants.js +1 -1
  178. package/dist/core/fast-model-variants.js.map +1 -1
  179. package/dist/core/keybindings.d.ts +4 -4
  180. package/dist/core/messages.d.ts +1 -0
  181. package/dist/core/messages.d.ts.map +1 -1
  182. package/dist/core/messages.js +5 -1
  183. package/dist/core/messages.js.map +1 -1
  184. package/dist/core/model-config.d.ts.map +1 -1
  185. package/dist/core/model-config.js +2 -1
  186. package/dist/core/model-config.js.map +1 -1
  187. package/dist/core/model-fallback-failures.d.ts.map +1 -1
  188. package/dist/core/model-fallback-failures.js +5 -1
  189. package/dist/core/model-fallback-failures.js.map +1 -1
  190. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  191. package/dist/core/model-resolver-cli.js +5 -1
  192. package/dist/core/model-resolver-cli.js.map +1 -1
  193. package/dist/core/model-resolver-defaults.js +1 -1
  194. package/dist/core/model-resolver-defaults.js.map +1 -1
  195. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  196. package/dist/core/model-resolver-initial.js +6 -1
  197. package/dist/core/model-resolver-initial.js.map +1 -1
  198. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  199. package/dist/core/model-resolver-patterns.js +2 -1
  200. package/dist/core/model-resolver-patterns.js.map +1 -1
  201. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  202. package/dist/core/model-resolver-scope.js +3 -1
  203. package/dist/core/model-resolver-scope.js.map +1 -1
  204. package/dist/core/model-runtime-streaming.d.ts.map +1 -1
  205. package/dist/core/model-runtime-streaming.js +2 -0
  206. package/dist/core/model-runtime-streaming.js.map +1 -1
  207. package/dist/core/model-runtime.d.ts.map +1 -1
  208. package/dist/core/model-runtime.js +48 -22
  209. package/dist/core/model-runtime.js.map +1 -1
  210. package/dist/core/provider-composer-internal.d.ts.map +1 -1
  211. package/dist/core/provider-composer-internal.js +4 -1
  212. package/dist/core/provider-composer-internal.js.map +1 -1
  213. package/dist/core/provider-display-names.js +1 -1
  214. package/dist/core/provider-display-names.js.map +1 -1
  215. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  216. package/dist/core/remote-catalog-provider.js +3 -0
  217. package/dist/core/remote-catalog-provider.js.map +1 -1
  218. package/dist/core/resource-loader-core.d.ts +1 -0
  219. package/dist/core/resource-loader-core.d.ts.map +1 -1
  220. package/dist/core/resource-loader-core.js +4 -0
  221. package/dist/core/resource-loader-core.js.map +1 -1
  222. package/dist/core/resource-loader-internals.d.ts +1 -0
  223. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  224. package/dist/core/resource-loader-internals.js.map +1 -1
  225. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  226. package/dist/core/resource-loader-reload.js +28 -5
  227. package/dist/core/resource-loader-reload.js.map +1 -1
  228. package/dist/core/resource-loader-types.d.ts +2 -0
  229. package/dist/core/resource-loader-types.d.ts.map +1 -1
  230. package/dist/core/resource-loader-types.js.map +1 -1
  231. package/dist/core/sdk-types.d.ts +6 -4
  232. package/dist/core/sdk-types.d.ts.map +1 -1
  233. package/dist/core/sdk-types.js.map +1 -1
  234. package/dist/core/sdk.d.ts.map +1 -1
  235. package/dist/core/sdk.js +5 -3
  236. package/dist/core/sdk.js.map +1 -1
  237. package/dist/core/session-lifecycle-work.d.ts +2 -1
  238. package/dist/core/session-lifecycle-work.d.ts.map +1 -1
  239. package/dist/core/session-lifecycle-work.js +15 -1
  240. package/dist/core/session-lifecycle-work.js.map +1 -1
  241. package/dist/core/session-manager-history.d.ts.map +1 -1
  242. package/dist/core/session-manager-history.js +4 -2
  243. package/dist/core/session-manager-history.js.map +1 -1
  244. package/dist/core/settings-manager-basic-accessors.d.ts +2 -0
  245. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  246. package/dist/core/settings-manager-basic-accessors.js +33 -4
  247. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  248. package/dist/core/settings-manager-core.d.ts.map +1 -1
  249. package/dist/core/settings-manager-core.js +2 -0
  250. package/dist/core/settings-manager-core.js.map +1 -1
  251. package/dist/core/settings-storage.d.ts.map +1 -1
  252. package/dist/core/settings-storage.js +2 -1
  253. package/dist/core/settings-storage.js.map +1 -1
  254. package/dist/core/settings-types.d.ts +1 -0
  255. package/dist/core/settings-types.d.ts.map +1 -1
  256. package/dist/core/settings-types.js.map +1 -1
  257. package/dist/core/skills.d.ts +1 -1
  258. package/dist/core/skills.d.ts.map +1 -1
  259. package/dist/core/skills.js +3 -1
  260. package/dist/core/skills.js.map +1 -1
  261. package/dist/core/system-prompt.d.ts +3 -0
  262. package/dist/core/system-prompt.d.ts.map +1 -1
  263. package/dist/core/system-prompt.js +7 -3
  264. package/dist/core/system-prompt.js.map +1 -1
  265. package/dist/core/tool-loadout.d.ts.map +1 -1
  266. package/dist/core/tool-loadout.js +8 -1
  267. package/dist/core/tool-loadout.js.map +1 -1
  268. package/dist/core/tool-selection.d.ts +13 -0
  269. package/dist/core/tool-selection.d.ts.map +1 -0
  270. package/dist/core/tool-selection.js +43 -0
  271. package/dist/core/tool-selection.js.map +1 -0
  272. package/dist/core/tools/read.d.ts +7 -0
  273. package/dist/core/tools/read.d.ts.map +1 -1
  274. package/dist/core/tools/read.js +11 -1
  275. package/dist/core/tools/read.js.map +1 -1
  276. package/dist/core/transcript.d.ts +58 -0
  277. package/dist/core/transcript.d.ts.map +1 -0
  278. package/dist/core/transcript.js +185 -0
  279. package/dist/core/transcript.js.map +1 -0
  280. package/dist/extensions/codemode/execute.d.ts.map +1 -1
  281. package/dist/extensions/codemode/execute.js +2 -1
  282. package/dist/extensions/codemode/execute.js.map +1 -1
  283. package/dist/extensions/codemode/index.d.ts.map +1 -1
  284. package/dist/extensions/codemode/index.js +1 -0
  285. package/dist/extensions/codemode/index.js.map +1 -1
  286. package/dist/extensions/codemode/tool.d.ts +4 -1
  287. package/dist/extensions/codemode/tool.d.ts.map +1 -1
  288. package/dist/extensions/codemode/tool.js +6 -3
  289. package/dist/extensions/codemode/tool.js.map +1 -1
  290. package/dist/extensions/herdr/index.d.ts.map +1 -1
  291. package/dist/extensions/herdr/index.js +15 -10
  292. package/dist/extensions/herdr/index.js.map +1 -1
  293. package/dist/extensions/herdr/pane-owner.d.ts +6 -2
  294. package/dist/extensions/herdr/pane-owner.d.ts.map +1 -1
  295. package/dist/extensions/herdr/pane-owner.js +19 -4
  296. package/dist/extensions/herdr/pane-owner.js.map +1 -1
  297. package/dist/extensions/mcp/client/oauth/flow.d.ts.map +1 -1
  298. package/dist/extensions/mcp/client/oauth/flow.js +14 -1
  299. package/dist/extensions/mcp/client/oauth/flow.js.map +1 -1
  300. package/dist/extensions/mcp/client/oauth/types.d.ts +1 -0
  301. package/dist/extensions/mcp/client/oauth/types.d.ts.map +1 -1
  302. package/dist/extensions/mcp/client/oauth/types.js.map +1 -1
  303. package/dist/extensions/mcp/index.js.map +1 -1
  304. package/dist/extensions/mcp/runtime.d.ts +5 -2
  305. package/dist/extensions/mcp/runtime.d.ts.map +1 -1
  306. package/dist/extensions/mcp/runtime.js +18 -4
  307. package/dist/extensions/mcp/runtime.js.map +1 -1
  308. package/dist/index.d.ts +2 -0
  309. package/dist/index.d.ts.map +1 -1
  310. package/dist/index.js +2 -0
  311. package/dist/index.js.map +1 -1
  312. package/dist/main.d.ts.map +1 -1
  313. package/dist/main.js +2 -0
  314. package/dist/main.js.map +1 -1
  315. package/dist/migrations-azure.d.ts +4 -0
  316. package/dist/migrations-azure.d.ts.map +1 -0
  317. package/dist/migrations-azure.js +41 -0
  318. package/dist/migrations-azure.js.map +1 -0
  319. package/dist/migrations.d.ts.map +1 -1
  320. package/dist/migrations.js +2 -0
  321. package/dist/migrations.js.map +1 -1
  322. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  323. package/dist/modes/interactive/components/chat-session-host-state.js +2 -1
  324. package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
  325. package/dist/modes/interactive/components/chat-transcript.d.ts +2 -0
  326. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  327. package/dist/modes/interactive/components/chat-transcript.js +9 -3
  328. package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
  329. package/dist/modes/interactive/components/compaction-boundary-message.d.ts +2 -0
  330. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -1
  331. package/dist/modes/interactive/components/compaction-boundary-message.js +15 -3
  332. package/dist/modes/interactive/components/compaction-boundary-message.js.map +1 -1
  333. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  334. package/dist/modes/interactive/components/settings-selector-items.js +43 -0
  335. package/dist/modes/interactive/components/settings-selector-items.js.map +1 -1
  336. package/dist/modes/interactive/components/settings-selector-types.d.ts +6 -0
  337. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  338. package/dist/modes/interactive/components/settings-selector-types.js.map +1 -1
  339. package/dist/modes/interactive/interactive-prompt-turn.js +6 -4
  340. package/dist/modes/interactive/interactive-prompt-turn.js.map +1 -1
  341. package/dist/modes/interactive/interactive-render-chat.js +13 -1
  342. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  343. package/dist/modes/interactive/interactive-selectors.js +8 -0
  344. package/dist/modes/interactive/interactive-selectors.js.map +1 -1
  345. package/dist/modes/interactive/interactive-session-runtime.js +2 -0
  346. package/dist/modes/interactive/interactive-session-runtime.js.map +1 -1
  347. package/dist/modes/interactive/interactive-startup.js +1 -1
  348. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  349. package/dist/modes/interactive/theme/tui-theme.d.ts.map +1 -1
  350. package/dist/modes/interactive/theme/tui-theme.js +1 -0
  351. package/dist/modes/interactive/theme/tui-theme.js.map +1 -1
  352. package/dist/modes/interactive-engine/engine-args.d.ts.map +1 -1
  353. package/dist/modes/interactive-engine/engine-args.js +1 -0
  354. package/dist/modes/interactive-engine/engine-args.js.map +1 -1
  355. package/dist/utils/credential-redaction.d.ts +2 -0
  356. package/dist/utils/credential-redaction.d.ts.map +1 -0
  357. package/dist/utils/credential-redaction.js +20 -0
  358. package/dist/utils/credential-redaction.js.map +1 -0
  359. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  360. package/dist/utils/syntax-highlight.js +6 -1
  361. package/dist/utils/syntax-highlight.js.map +1 -1
  362. package/docs/codemode.md +3 -2
  363. package/docs/compaction/reference.md +10 -4
  364. package/docs/compaction.md +40 -8
  365. package/docs/docs.json +1 -0
  366. package/docs/extensions/api-reference.md +18 -0
  367. package/docs/extensions/authoring.md +1 -1
  368. package/docs/extensions/events.md +2 -2
  369. package/docs/extensions.md +2 -0
  370. package/docs/keybindings.md +9 -9
  371. package/docs/mcp-servers.md +4 -0
  372. package/docs/providers.md +33 -3
  373. package/docs/reference/cli.md +10 -3
  374. package/docs/sdk/reference.md +16 -1
  375. package/docs/sdk/web-server.md +325 -0
  376. package/docs/sdk.md +83 -1
  377. package/docs/security.md +1 -1
  378. package/docs/workflows/api-reference.md +74 -4
  379. package/docs/workflows/authoring.md +6 -2
  380. package/docs/workflows/builtins.md +4 -0
  381. package/docs/workflows/operations.md +12 -4
  382. package/npm-shrinkwrap.json +78 -74
  383. package/package.json +9 -9
@@ -0,0 +1,325 @@
1
+ ---
2
+ title: "Embedding Atomic in a web server"
3
+ sidebarTitle: "Web server"
4
+ description: "Run Atomic sessions and workflows behind a Next.js App Router route and stream their output to a browser."
5
+ ---
6
+
7
+ # Embedding Atomic in a web server
8
+
9
+ This guide puts an Atomic agent behind an HTTP endpoint, using a Next.js App Router project as the example. The same pattern works in any long-running Node.js server: keep one session per conversation, start a prompt per request, and stream the session's events back as newline-delimited JSON (NDJSON).
10
+
11
+ Read the [SDK](/sdk) page first for `createAgentSession()` and session events.
12
+
13
+ The examples use `createTranscript()`, `createAgentSessionAdapter()`, and `RunOpts.onStageSessionEvent`, which require an Atomic release newer than 0.9.28-alpha.1. Check the [changelog](/changelog) for your installed version.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @bastani/atomic
19
+ ```
20
+
21
+ Atomic needs Node.js 22.19 or newer and does not require dependency install scripts.
22
+
23
+ Bun blocks the install scripts of some dependencies, such as `@embedded-postgres/<platform>`, `protobufjs`, and `@google/genai`, and reports them after `bun add @bastani/atomic`. You don't need to add them to `trustedDependencies` in `package.json`: Atomic prepares the embedded Postgres runtime itself the first time it starts it, and it doesn't depend on the other scripts.
24
+
25
+ ## Keep Atomic out of the server bundle
26
+
27
+ Atomic finds its bundled resources and native modules relative to its installed package, so it must load from `node_modules` rather than from Next.js's server bundle. List it in `serverExternalPackages` (Next.js 15 and later):
28
+
29
+ ```ts
30
+ // next.config.ts
31
+ import type { NextConfig } from "next";
32
+
33
+ const nextConfig: NextConfig = {
34
+ serverExternalPackages: ["@bastani/atomic", "@bastani/atomic-natives", "@bastani/pi-ai", "typebox"],
35
+ };
36
+
37
+ export default nextConfig;
38
+ ```
39
+
40
+ Atomic's own dependencies load from `node_modules` once `@bastani/atomic` is external. The other entries cover packages your app may import directly, such as `typebox` for workflow schemas, so your code and Atomic share one copy. Add any other Atomic dependency you import yourself.
41
+
42
+ Every route that imports Atomic must use the Node.js runtime:
43
+
44
+ ```ts
45
+ export const runtime = "nodejs";
46
+ ```
47
+
48
+ Import Atomic only from server code. Client components may use `import type` from `@bastani/atomic`, which adds nothing to the browser bundle.
49
+
50
+ ## Keep one session per conversation
51
+
52
+ An `AgentSession` holds the conversation's history, so create one per conversation and reuse it across requests. Keep sessions on `globalThis`: in `next dev`, module-level variables reset on every hot reload, but `globalThis` survives, so open conversations don't vanish and old sessions aren't leaked. Restart the dev server after changing session options; existing sessions keep the options they were created with.
53
+
54
+ ```ts
55
+ // lib/atomic.ts
56
+ import "server-only";
57
+ import { type AgentSession, createAgentSession, ModelRuntime, SessionManager } from "@bastani/atomic";
58
+
59
+ export const WORKSPACE = process.env.AGENT_WORKSPACE ?? "/srv/agent-workspace";
60
+
61
+ export interface Conversation {
62
+ session: AgentSession;
63
+ busy: boolean;
64
+ }
65
+
66
+ interface AtomicState {
67
+ modelRuntime: Promise<ModelRuntime>;
68
+ conversations: Map<string, Promise<Conversation>>;
69
+ }
70
+
71
+ const shared = globalThis as typeof globalThis & { atomicState?: AtomicState };
72
+ const state = (shared.atomicState ??= {
73
+ modelRuntime: ModelRuntime.create(),
74
+ conversations: new Map(),
75
+ });
76
+
77
+ export function getConversation(id: string): Promise<Conversation> {
78
+ let conversation = state.conversations.get(id);
79
+ if (!conversation) {
80
+ conversation = state.modelRuntime.then(async (modelRuntime) => {
81
+ const { session } = await createAgentSession({
82
+ cwd: WORKSPACE,
83
+ sessionManager: SessionManager.inMemory(WORKSPACE),
84
+ modelRuntime,
85
+ tools: ["read", "find", "search"],
86
+ builtins: { workflows: false, subagents: false, mcp: false, "web-access": false, intercom: false },
87
+ });
88
+ return { session, busy: false };
89
+ });
90
+ conversation.catch(() => state.conversations.delete(id));
91
+ state.conversations.set(id, conversation);
92
+ }
93
+ return conversation;
94
+ }
95
+
96
+ export async function closeConversation(id: string): Promise<void> {
97
+ const conversation = state.conversations.get(id);
98
+ state.conversations.delete(id);
99
+ if (conversation) await (await conversation).session.dispose();
100
+ }
101
+ ```
102
+
103
+ `SessionManager.inMemory()` keeps history in process memory. To keep conversations across restarts, pass a file-backed `SessionManager` instead; see [Sessions](/sessions). Call `closeConversation()` when a conversation ends or has been idle for a while, because each open session holds resources until it is disposed.
104
+
105
+ Sessions on `globalThis` live in one server process. Run this on a long-lived Node.js server, and route each conversation to the same instance if you run several. Short-lived serverless functions lose the session between requests.
106
+
107
+ ## Stream a prompt
108
+
109
+ The route below starts one prompt and streams the assistant's output as NDJSON. `createTranscript()` turns session events into ordered, JSON-serializable parts (text, thinking, and tool calls joined to their results), so each line carries the complete output so far and the client simply replaces what it shows.
110
+
111
+ ```ts
112
+ // app/api/chat/route.ts
113
+ import { createTranscript, type TranscriptPart } from "@bastani/atomic";
114
+ import { getConversation } from "@/lib/atomic";
115
+ import { authenticate } from "@/lib/auth";
116
+
117
+ export const runtime = "nodejs";
118
+
119
+ export type ChatLine =
120
+ | { type: "parts"; parts: TranscriptPart[] }
121
+ | { type: "done" }
122
+ | { type: "error"; message: string };
123
+
124
+ export async function POST(request: Request): Promise<Response> {
125
+ const user = await authenticate(request);
126
+ if (!user) return new Response("Unauthorized", { status: 401 });
127
+
128
+ const { conversationId, message } = (await request.json()) as { conversationId: string; message: string };
129
+ const conversation = await getConversation(`${user.id}:${conversationId}`);
130
+ const { session } = conversation;
131
+ if (conversation.busy || session.isStreaming) {
132
+ return new Response("This conversation is already answering a prompt.", { status: 409 });
133
+ }
134
+ if (request.signal.aborted) return new Response(null, { status: 499 });
135
+ conversation.busy = true;
136
+
137
+ const encoder = new TextEncoder();
138
+ const transcript = createTranscript();
139
+ let open = true;
140
+
141
+ const stream = new ReadableStream<Uint8Array>({
142
+ start(controller) {
143
+ const send = (line: ChatLine) => {
144
+ if (open) controller.enqueue(encoder.encode(`${JSON.stringify(line)}\n`));
145
+ };
146
+ const stop = () => void session.abort();
147
+ request.signal.addEventListener("abort", stop, { once: true });
148
+
149
+ const unsubscribe = session.subscribe((event) => {
150
+ transcript.apply(event);
151
+ if (
152
+ event.type === "message_update" ||
153
+ event.type === "message_end" ||
154
+ event.type === "tool_execution_update" ||
155
+ event.type === "tool_execution_end"
156
+ ) {
157
+ send({ type: "parts", parts: transcript.parts() });
158
+ }
159
+ });
160
+
161
+ session
162
+ .prompt(message)
163
+ .then(() => send({ type: "done" }))
164
+ .catch((error: Error) => send({ type: "error", message: error.message }))
165
+ .finally(() => {
166
+ unsubscribe();
167
+ request.signal.removeEventListener("abort", stop);
168
+ conversation.busy = false;
169
+ if (open) {
170
+ open = false;
171
+ controller.close();
172
+ }
173
+ });
174
+ },
175
+ cancel() {
176
+ open = false;
177
+ void session.abort();
178
+ },
179
+ });
180
+
181
+ return new Response(stream, {
182
+ headers: { "Content-Type": "application/x-ndjson; charset=utf-8", "Cache-Control": "no-cache" },
183
+ });
184
+ }
185
+ ```
186
+
187
+ A few details matter here:
188
+
189
+ - **One prompt at a time.** `session.prompt()` rejects while the session is already streaming. The route answers `409` instead. `busy` is set synchronously, so two requests that arrive together can't both start a prompt. To queue a message instead of rejecting it, call `session.prompt(message, { streamingBehavior: "followUp" })`.
190
+ - **Stop on disconnect.** When the browser disconnects or aborts its `fetch`, `request.signal` aborts and the stream is cancelled. Both call `session.abort()`, which stops the current turn and its tools but keeps the session usable.
191
+ - **Payload size.** Each line repeats the whole output so far. For long answers, send lines on a timer instead of on every event, or send only the parts that changed.
192
+
193
+ `createTranscript()` reports a tool result's text by default. To send a tool's structured `details` instead, pass `createTranscript({ toolResult: (toolName) => (toolName === "todo" ? "details" : "content") })`.
194
+
195
+ ## Read the stream in the browser
196
+
197
+ ```tsx
198
+ // app/chat/send-message.ts
199
+ import type { TranscriptPart } from "@bastani/atomic";
200
+ import type { ChatLine } from "@/app/api/chat/route";
201
+
202
+ export async function sendMessage(
203
+ conversationId: string,
204
+ message: string,
205
+ onParts: (parts: TranscriptPart[]) => void,
206
+ signal?: AbortSignal,
207
+ ): Promise<void> {
208
+ const response = await fetch("/api/chat", {
209
+ method: "POST",
210
+ headers: { "Content-Type": "application/json" },
211
+ body: JSON.stringify({ conversationId, message }),
212
+ signal,
213
+ });
214
+ if (!response.ok || !response.body) throw new Error(await response.text());
215
+
216
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
217
+ let buffered = "";
218
+ for (;;) {
219
+ const { value, done } = await reader.read();
220
+ if (done) return;
221
+ buffered += value;
222
+ const lines = buffered.split("\n");
223
+ buffered = lines.pop() ?? "";
224
+ for (const text of lines) {
225
+ if (!text) continue;
226
+ const line = JSON.parse(text) as ChatLine;
227
+ if (line.type === "parts") onParts(line.parts);
228
+ if (line.type === "error") throw new Error(line.message);
229
+ }
230
+ }
231
+ }
232
+ ```
233
+
234
+ Render each part by `type`: `text` and `thinking` are strings, and a `toolCall` part has `name`, `arguments`, and, once the tool reports progress or finishes, a `result` with `content`, `isError`, and `isPartial`. Pass an `AbortController`'s signal to stop the agent from the page.
235
+
236
+ ## Run a workflow from a route
237
+
238
+ `run()` from `@bastani/atomic/workflows` runs a workflow definition in the server process. Each stage runs as an in-process Atomic session, so `run()` needs no adapters. Use `createAgentSessionAdapter()` when every stage should share session options, such as the same tool restrictions as your chat sessions:
239
+
240
+ ```ts
241
+ // app/api/review/route.ts
242
+ import { createTranscript, type Transcript } from "@bastani/atomic";
243
+ import { createAgentSessionAdapter, run } from "@bastani/atomic/workflows";
244
+ import { WORKSPACE } from "@/lib/atomic";
245
+ import { authenticate } from "@/lib/auth";
246
+ import { reviewWorkflow } from "@/workflows/review";
247
+
248
+ export const runtime = "nodejs";
249
+
250
+ export async function POST(request: Request): Promise<Response> {
251
+ if (!(await authenticate(request))) return new Response("Unauthorized", { status: 401 });
252
+ const { topic } = (await request.json()) as { topic: string };
253
+ if (request.signal.aborted) return new Response(null, { status: 499 });
254
+
255
+ const encoder = new TextEncoder();
256
+ const abort = new AbortController();
257
+ request.signal.addEventListener("abort", () => abort.abort(), { once: true });
258
+
259
+ const stream = new ReadableStream<Uint8Array>({
260
+ async start(controller) {
261
+ const send = (line: object) => {
262
+ if (!abort.signal.aborted) controller.enqueue(encoder.encode(`${JSON.stringify(line)}\n`));
263
+ };
264
+ const stages = new Map<string, Transcript>();
265
+
266
+ try {
267
+ const result = await run(reviewWorkflow, { topic }, {
268
+ cwd: WORKSPACE,
269
+ signal: abort.signal,
270
+ durability: { mode: "memory" },
271
+ adapters: {
272
+ agentSession: createAgentSessionAdapter({ cwd: WORKSPACE, tools: ["read", "find", "search"] }),
273
+ },
274
+ onStageSessionEvent: (runId, stageId, event) => {
275
+ let transcript = stages.get(stageId);
276
+ if (!transcript) stages.set(stageId, (transcript = createTranscript()));
277
+ transcript.apply(event);
278
+ if (event.type === "message_update" || event.type === "tool_execution_end") {
279
+ send({ type: "stage", runId, stageId, parts: transcript.parts() });
280
+ }
281
+ },
282
+ });
283
+ send({ type: "done", status: result.status, result: result.result, error: result.error });
284
+ } catch (error) {
285
+ send({ type: "error", message: error instanceof Error ? error.message : String(error) });
286
+ } finally {
287
+ if (!abort.signal.aborted) controller.close();
288
+ }
289
+ },
290
+ cancel() {
291
+ abort.abort();
292
+ },
293
+ });
294
+
295
+ return new Response(stream, { headers: { "Content-Type": "application/x-ndjson; charset=utf-8" } });
296
+ }
297
+ ```
298
+
299
+ `onStageSessionEvent` receives every stage's session events tagged with its run and stage ids, including stages that switch to a fallback model and stages of nested workflows. Aborting `signal`, here when the client disconnects, cancels the run.
300
+
301
+ ### Choose workflow durability
302
+
303
+ By default, `run()` makes workflow state durable by starting a managed local Postgres, so a run can resume after the process exits. Request-scoped servers rarely want that:
304
+
305
+ - **`durability: { mode: "memory" }`** keeps state in memory for this call only. No Postgres starts, and the run can't resume after the process exits. Use it when the request owns the run.
306
+ - **`durability: { mode: "durable", systemDatabaseUrl }`** stores state in a Postgres database you run, such as a hosted one, and Atomic doesn't start its own. Use it in deployments that need runs to survive restarts. `DBOS_SYSTEM_DATABASE_URL`, when set, overrides the URL.
307
+
308
+ One process uses one workflow database. See [Choosing the durable backend](/workflows/api-reference#choosing-the-durable-backend) for the full rules, and [Workflows](/workflows) for writing workflow definitions.
309
+
310
+ ## Secure the endpoint
311
+
312
+ A route that drives an agent session lets whoever calls it run tools on your server with the server process's permissions. Atomic has [no built-in sandbox](/security#no-built-in-sandbox).
313
+
314
+ - **Require authentication** on every route that creates sessions, sends prompts, or starts workflows, and scope conversation ids to the authenticated user, as `getConversation()` does above.
315
+ - **Allow only the tools you need.** `tools` is an allowlist; the examples allow only the read-only `read`, `find`, and `search`. Leave out `bash`, `edit`, and `write` unless the agent must change files or run commands. `excludedTools` removes tools, and `noTools: "all"` exposes none. `ask_user_question` needs a person to answer, so leave it out of headless sessions. See [Tools](/sdk/reference#tools).
316
+ - **Disable builtins you don't use.** `builtins` turns off the shipped workflows, subagents, MCP, web access, and Intercom packages, along with their tools.
317
+ - **Choose the working directory deliberately.** `cwd` sets the starting directory for tool paths and controls which project settings, context files, and extensions Atomic discovers. It does not restrict file access: tools can use absolute paths and paths outside `cwd`. Point it at a directory you control, never at an upload or a user-supplied path.
318
+ - **Isolate the process** in a container, VM, or restricted OS account when callers must not access other server files, even with read-only tools, or when the agent can write files or run commands. See [Containerization](/containerization).
319
+ - **Keep credentials on the server.** Provider keys come from the server's `ModelRuntime`. Never send them to the browser or accept them from requests.
320
+
321
+ ## Next steps
322
+
323
+ - [SDK](/sdk) for sessions, prompting, and events.
324
+ - [SDK API reference](/sdk/reference) for every `createAgentSession()` option.
325
+ - [Workflow API reference](/workflows/api-reference) for `run()` and `RunOpts`.
package/docs/sdk.md CHANGED
@@ -26,6 +26,8 @@ For a custom host that runs background work, see [Owner-bound task supervisor](/
26
26
 
27
27
  Not sure the SDK is the right integration mode? Compare it with RPC and JSON mode on [Programmatic use](/programmatic).
28
28
 
29
+ To serve an agent over HTTP, see [Embedding Atomic in a web server](/sdk/web-server), which walks through a Next.js App Router route that streams session output to a browser.
30
+
29
31
  ## Quick Start
30
32
 
31
33
  ```typescript
@@ -241,6 +243,7 @@ interface SessionWorkflows {
241
243
  pause(target: { all: true }): Promise<WorkflowRunControlOutcome>;
242
244
  quit(target: string | { all: true }): Promise<WorkflowRunControlOutcome>;
243
245
  resume(runId: string, options?: { stageId?: string; message?: string }): Promise<WorkflowRunControlOutcome>;
246
+ observe(observer: WorkflowActivityObserver): WorkflowActivitySubscription;
244
247
  }
245
248
  ```
246
249
 
@@ -313,7 +316,38 @@ async function onResumeClicked() {
313
316
  }
314
317
  ```
315
318
 
316
- `pause` holds the live run and keeps it resumable in this process; `quit` retires the executor at a durability boundary so a later process can resume it. After `quit`, or when resuming a run from a previous process, `resume` follows the durable resume path: keep the workflow definition and durable storage available. See [pausing, quitting, and resuming](/workflows/operations#pausing-quitting-and-resuming) for what each action guarantees. To watch runs change instead of polling `listRuns()`, use the observation contract in [Workflow activity and lifecycle hooks](/extensions/events#workflow-activity-and-lifecycle-hooks).
319
+ `pause` holds the live run and keeps it resumable in this process; `quit` retires the executor at a durability boundary so a later process can resume it. After `quit`, or when resuming a run from a previous process, `resume` follows the durable resume path: keep the workflow definition and durable storage available. See [pausing, quitting, and resuming](/workflows/operations#pausing-quitting-and-resuming) for what each action guarantees.
320
+
321
+ #### Observing workflow runs
322
+
323
+ To follow runs instead of polling `listRuns()`, call `session.workflows.observe(observer)`. It covers runs that the agent launches with the `workflow` tool, and needs no extension. The first frame is a `snapshot`. After it come `changed` and `removed` frames, delivered in order. Each callback finishes before the next one starts. Call `dispose()` on the returned subscription to stop. The frames and cursors follow the [workflow activity contract](/extensions/events#workflow-activity-and-lifecycle-hooks).
324
+
325
+ Each root summary carries a `graph` with the nodes of that root's workflow, including the nodes of nested child workflows. Nodes are `stage` nodes and `ctx.tool` nodes. Each one has an `id`, `kind`, `name`, `status`, `parentIds`, `runId`, `nodeId` (its run-local id), `depth`, and `executionOrder` when it is known. Tool nodes add their `ordinal`. A stage that is waiting on a prompt adds `pendingPrompt` with the prompt's `id`, `kind`, `message`, `choices`, and `createdAt`. A prompt that a run raises outside any stage appears in `graph.runPrompts` with the same fields and the `runId` that owns it. A node status change produces a `changed` frame. Frames are plain JSON-serializable objects.
326
+
327
+ ```typescript
328
+ import type { WorkflowRootActivity } from "@bastani/atomic";
329
+
330
+ const roots = new Map<string, WorkflowRootActivity>();
331
+ const subscription = session.workflows.observe((frame) => {
332
+ if (frame.kind === "snapshot") {
333
+ roots.clear();
334
+ // `recovering` or `unavailable` means activity is unknown, not idle.
335
+ if (frame.availability === "ready") for (const root of frame.roots) roots.set(root.rootRunId, root);
336
+ } else if (frame.kind === "changed") {
337
+ roots.set(frame.root.rootRunId, frame.root);
338
+ for (const node of frame.root.graph?.nodes ?? []) {
339
+ renderNode(node.id, node.name, node.status, node.parentIds);
340
+ }
341
+ } else {
342
+ roots.delete(frame.rootRunId);
343
+ }
344
+ });
345
+
346
+ // Later:
347
+ subscription.dispose();
348
+ ```
349
+
350
+ Observation can start before the workflows package has finished loading; the first snapshot then reports `availability: "unavailable"`, and a `ready` snapshot follows. The subscription survives `session.reload()`. After a reload, the observer receives a new snapshot with a new cursor `epoch`, so replace everything you knew. When the session is disposed, each observer receives a final `snapshot` with `availability: "unavailable"` before `dispose()` resolves; after that, `observe()` throws `WorkflowRunControlUnavailableError`. This stream reports root activity and graph state. It does not carry the events inside each stage.
317
351
 
318
352
  ### Workflow and subagent children
319
353
 
@@ -403,6 +437,8 @@ A `ShutdownFailed` error contains component failures in `errors`; cleanup still
403
437
 
404
438
  Captured extension APIs also refuse new execution, mutations and registrations as soon as close or reload begins. Already-admitted `pi.exec()` calls remain part of the awaited drain; provide `signal` or `timeout` when invoking subprocesses that might not finish on their own. `abort()` alone does not retire the API. A rejected transactional reload restores the surviving generation's action admission.
405
439
 
440
+ Custom messages queued with `persistWhenStreaming: true` survive a successful reload and receive their model turn through the replacement extensions. An explicitly paused queue remains paused; call `resumeQueuedMessages()` before sending the next prompt. A rejected transactional reload retains the notices for the next prompt on the surviving generation.
441
+
406
442
  #### Finishing admitted work
407
443
 
408
444
  Disposal and reload wait for admitted callbacks, resource refreshes, and subprocesses. Ensure callbacks can settle independently of disposal, including work started by cleanup handlers. Give `pi.exec()` calls a timeout or cancellation signal, and await them in the handler so you can inspect failures. Never make cleanup wait for disposal itself.
@@ -415,6 +451,24 @@ Strict transactional reload failure leaves the original generation usable. Ordin
415
451
 
416
452
  Prefer acquiring extension resources in `session_start`. If a factory acquires them earlier, register `session_shutdown` immediately, even if `extensionsOverride` may later omit that factory. Capture cleanup handles when acquiring resources rather than rereading loader getters during cleanup.
417
453
 
454
+ Transactional reload dispatches candidate `session_start` before commit. Use `publishExtensionContextEffect(ctx, effect)` to publish shared lifecycle state only after commit and retiring-generation cleanup; rollback discards the effect. Outside a transactional candidate, it runs immediately. Awaiting the helper queues the effect, not its eventual execution, so startup must not depend on the effect's result.
455
+
456
+ For synchronous identity handoff, use `publishExtensionContextEffect(ctx, effect, "commit")`. This phase runs after the transaction commits and before predecessor authority is revoked or any retirement work is awaited. It must not return a promise or start asynchronous work; asynchronous callbacks are rejected and reported. The default `"start"` phase still runs after retirement, when reporter and task-host startup is safe.
457
+
458
+ Register generation-owned observer disposal with `registerExtensionContextRetirementEffect(ctx, dispose)` during `session_start`. Disposal is synchronous, runs before authority revocation and before rollback invalidation, and is independent of `session_shutdown` handler order. The returned function unregisters the effect. Keep awaited resource cleanup in `session_shutdown`; observer disposal must not quit or checkpoint preserved workflows.
459
+
460
+ ```typescript
461
+ import { publishExtensionContextEffect, type ExtensionFactory } from "@bastani/atomic";
462
+
463
+ const extension: ExtensionFactory = (pi) => {
464
+ pi.on("session_start", (_event, ctx) =>
465
+ publishExtensionContextEffect(ctx, () => {
466
+ pi.events.emit("example:session-ready", { sessionId: ctx.sessionManager.getSessionId() });
467
+ }),
468
+ );
469
+ };
470
+ ```
471
+
418
472
  Do not cache dialog functions across reload attempts. Retiring functions refuse new questions; after a rejected transaction, use the surviving session's current `ctx.ui`.
419
473
 
420
474
  Shared loaders, event buses, settings, and `SessionManager` instances do not share live task ownership. Closing one session leaves another session's commands and borrowed discovery resources alone.
@@ -678,6 +732,34 @@ To rebuild an assistant message from deltas, accumulate them in your own message
678
732
 
679
733
  If you subscribe part-way through a turn, seed your object from `session.agent.state.streamingMessage`, if present, to include the deltas you missed.
680
734
 
735
+ ### Collecting assistant output
736
+
737
+ `createTranscript()` does that bookkeeping for you. It turns session events into the assistant's output as ordered, JSON-serializable parts:
738
+
739
+ ```typescript
740
+ import { createTranscript } from "@bastani/atomic";
741
+
742
+ const transcript = createTranscript();
743
+ session.subscribe((event) => transcript.apply(event));
744
+
745
+ await session.prompt("List the files here");
746
+ console.log(JSON.stringify(transcript.parts()));
747
+ ```
748
+
749
+ `parts()` returns a fresh copy of the parts of every assistant message seen so far, including one still streaming:
750
+
751
+ - `{ type: "text", text }`
752
+ - `{ type: "thinking", thinking }`. Redacted thinking is left out.
753
+ - `{ type: "toolCall", id, name, arguments, result? }`. `result` appears once the tool reports progress or finishes. It holds `isError` and `isPartial`, which stays `true` until the tool finishes. If the message is aborted or fails, tool calls without a final result get an error result carrying the error message.
754
+
755
+ Events from tools called inside other tools (those with `parentToolCallId`) are ignored. By default, `result.content` holds the result's text content. To get a tool's structured `details` instead, pass `toolResult`:
756
+
757
+ ```typescript
758
+ const transcript = createTranscript({
759
+ toolResult: (toolName) => (toolName === "todo" ? "details" : "content"),
760
+ });
761
+ ```
762
+
681
763
  ## Options Reference
682
764
 
683
765
  Moved to [SDK API reference](/sdk/reference#options-reference).
package/docs/security.md CHANGED
@@ -21,7 +21,7 @@ Trusting a project allows Atomic to load trust-gated project inputs, including:
21
21
  - missing project packages configured through project settings
22
22
  - project-local extensions and project package-managed extensions
23
23
 
24
- Declining trust skips protected resources, including project-local discovery of `AGENTS.override.md`, `AGENTS.md`, and `CLAUDE.md`. Global context and explicitly supplied CLI resources remain available.
24
+ Declining trust skips protected resources, including project-local discovery of `AGENTS.override.md`, `AGENTS.md`, and `CLAUDE.md`. Global context and explicitly supplied CLI resources remain available. Interactive sessions in an untrusted project show a warning that project resources are ignored; use `/trust` to save a decision, then restart Atomic.
25
25
 
26
26
  Before resolving trust, Atomic loads only user/global extensions and explicit CLI `-e` package-level extensions. Those trusted extensions can handle the `project_trust` event; the first to return a yes/no decision owns it.
27
27
 
@@ -1163,18 +1163,29 @@ interface StageSnapshot extends WorkflowSerializableObject {
1163
1163
  readonly id: string;
1164
1164
  readonly name: string;
1165
1165
  readonly status: StageStatus;
1166
+ readonly parentIds: readonly string[];
1167
+ readonly executionOrder?: number;
1168
+ readonly model?: string;
1169
+ readonly startedAt?: number;
1170
+ readonly endedAt?: number;
1171
+ readonly durationMs?: number;
1166
1172
  readonly result?: WorkflowSerializableValue;
1167
1173
  readonly error?: string;
1174
+ readonly routerSelection?: ModelRouterOutput;
1168
1175
  }
1169
1176
  ```
1170
1177
 
1171
1178
  Programmatic `run(...)` returns this type. `exited` identifies `ctx.exit(...)` termination, and `stages` contains the final stage snapshots.
1172
1179
 
1180
+ Each stage snapshot carries its graph position: `parentIds` lists the stages it depends on, and `executionOrder` is its admission order among the run's stages and tool nodes. `model` is the effective model after fallback resolution; `routerSelection` is the automatic routing choice. Timestamps are epoch milliseconds and are absent until the stage starts or ends. The runtime may replace `parentIds` before a stage starts, so read it from the latest snapshot rather than caching it.
1181
+
1173
1182
  ## Programmatic usage
1174
1183
 
1175
1184
  `@bastani/atomic/workflows` is Atomic's published workflow SDK. Import `workflow` from that specifier, import `Type` from `typebox`, and export the definition returned by `workflow({...})`. Workflow helpers may also import the host TypeBox runtime subpaths `typebox/compile` and `typebox/value`; the legacy `@sinclair/typebox` root and matching subpaths remain supported. Keep runtime helpers such as widget factories and shared utilities in a subdirectory outside the top-level discovery scan, such as `.atomic/workflows/lib/`; see [Workflow Locations](/workflows/operations#workflow-locations).
1176
1185
 
1177
- Package authors list both `@bastani/atomic` and `typebox` in `peerDependencies`. The `@bastani/atomic` package publishes compiled JavaScript and declarations for `@bastani/atomic/workflows`, `@bastani/atomic/workflows/builtin`, and each `@bastani/atomic/workflows/builtin/*` module. TypeScript resolves those exports directly under `moduleResolution: NodeNext`. When Atomic executes a workflow file or an imported helper, its runtime loader resolves the workflow SDK and supported TypeBox aliases to the same in-memory host modules. It intentionally does not expose extension-only host modules such as the agent core, UI, provider transport, or lockfile implementation.
1186
+ Package authors list both `@bastani/atomic` and `typebox` in `peerDependencies`. The `@bastani/atomic` package publishes compiled JavaScript and declarations for `@bastani/atomic/workflows`, `@bastani/atomic/workflows/builtin`, and each `@bastani/atomic/workflows/builtin/*` module. TypeScript resolves those exports directly under `moduleResolution: NodeNext`. When Atomic executes a workflow file or an imported helper, its runtime loader resolves the workflow SDK and supported TypeBox aliases to the same in-memory host modules.
1187
+
1188
+ The loader also shares the host's modules for the exact specifiers `@bastani/atomic`, `@bastani/pi-ai`, and `@bastani/pi-ai/providers/all`. These exceptions expose Atomic's public root API and the AI compatibility API and provider transports. The host's versions take precedence over project-local copies, including in standalone-binary installs without project packages. Workflow files and relative imports still refresh on reload. Other deep subpaths resolve from disk. The workflow loader does not share the extension loader's remaining host aliases, including the separate agent-core and UI packages, other AI subpaths, and `proper-lockfile`. Older or alternative hosts without [`getWorkflowHostModules()`](/extensions/api-reference#pi-getworkflowhostmodules) retain filesystem resolution for the three specifiers.
1178
1189
 
1179
1190
  ```ts
1180
1191
  import { workflow } from "@bastani/atomic/workflows";
@@ -1273,6 +1284,52 @@ function run<
1273
1284
 
1274
1285
  Executes a compiled definition programmatically with validated inputs. Empty-input workflows accept an empty readonly record.
1275
1286
 
1287
+ #### Stage sessions and adapters
1288
+
1289
+ When `opts.adapters` supplies neither `agentSession` nor `prompt`, `run()` uses `createAgentSessionAdapter({ cwd: opts.cwd })`. Each stage then runs its own in-process Atomic `createAgentSession` session with an in-memory `SessionManager`, unless the stage sets `sessionDir` or resumes or forks a session. Stage sessions resolve models, credentials, settings, and resources the same way `createAgentSession` does. A `prompt` adapter on its own answers stage prompts itself, so `run()` adds no default session adapter beside it.
1290
+
1291
+ ```typescript
1292
+ function createAgentSessionAdapter(
1293
+ baseOptions?: Omit<CreateAgentSessionOptions, "sessionManager">,
1294
+ ): AgentSessionAdapter;
1295
+
1296
+ interface AgentSessionAdapter {
1297
+ create(
1298
+ options: StageSessionCreateOptions,
1299
+ meta?: StageExecutionMeta,
1300
+ ): Promise<StageSessionRuntime | StageSessionCreateResult>;
1301
+ }
1302
+ ```
1303
+
1304
+ Call `createAgentSessionAdapter(baseOptions)` yourself to apply the same `createAgentSession` options, such as `agentDir`, `modelRuntime`, or `settingsManager`, to every stage. Options set on an individual stage override `baseOptions`. `baseOptions` cannot set `sessionManager`, because every stage needs its own. A `cwd` in `baseOptions` applies to every stage that doesn't set its own, and session files saved through `sessionDir` record that directory.
1305
+
1306
+ A custom adapter's `create(options, meta)` receives the stage's session options, including any session manager Atomic opened for `sessionDir`, resume, or fork. It must resolve to `{ session }` or to the session itself, where `session` implements `StageSessionRuntime`. To change options per stage while keeping the stage policy that `createAgentSessionAdapter` applies, such as hiding the `workflow` tool from stage sessions, delegate to it:
1307
+
1308
+ ```ts
1309
+ import { createAgentSessionAdapter, run } from "@bastani/atomic/workflows";
1310
+
1311
+ // Default: in-process stage sessions with in-memory session managers.
1312
+ await run(helloWorld, { name: "Atomic" }, { cwd: process.cwd() });
1313
+
1314
+ // Shared options for every stage.
1315
+ await run(helloWorld, { name: "Atomic" }, {
1316
+ adapters: { agentSession: createAgentSessionAdapter({ agentDir: "/srv/atomic-agent" }) },
1317
+ });
1318
+
1319
+ // Per-stage options on top of the default adapter.
1320
+ const stageSessions = createAgentSessionAdapter();
1321
+ await run(helloWorld, { name: "Atomic" }, {
1322
+ adapters: {
1323
+ agentSession: {
1324
+ create: (options, meta) =>
1325
+ stageSessions.create(meta?.stageName === "review" ? { ...options, cwd: "/srv/review" } : options, meta),
1326
+ },
1327
+ },
1328
+ });
1329
+ ```
1330
+
1331
+ Without an attached workflow UI, a stage that calls `ask_user_question` waits for an answer until the run is aborted through `opts.signal`. For unattended runs, pass `executionMode: "non_interactive"`; stages then run without `ask_user_question`.
1332
+
1276
1333
  ### `RunOpts`
1277
1334
 
1278
1335
  ```typescript
@@ -1307,6 +1364,7 @@ interface RunOpts {
1307
1364
  readonly onRunStart?: (snapshot: RunSnapshot) => void;
1308
1365
  readonly onStageStart?: (runId: string, snapshot: StageSnapshot) => void;
1309
1366
  readonly onStageEnd?: (runId: string, snapshot: StageSnapshot) => unknown;
1367
+ readonly onStageSessionEvent?: (runId: string, stageId: string, event: AgentSessionEvent) => void;
1310
1368
  readonly onRunEnd?: (
1311
1369
  runId: string,
1312
1370
  status: RunStatus,
@@ -1319,6 +1377,8 @@ interface RunOpts {
1319
1377
 
1320
1378
  Supplies runtime adapters, execution policy, persistence, MCP, cancellation, graph/store integration, continuation metadata, and lifecycle callbacks to `run(...)`. Every field is optional.
1321
1379
 
1380
+ `onStageSessionEvent` streams every agent-session event from each stage (for example `message_update`, `tool_execution_start`, and `tool_execution_end`), tagged with the stage's `runId` and `stageId`. `AgentSessionEvent` is exported by `@bastani/atomic`. The stream continues when a fallback model replaces a stage's session. Stages in nested `ctx.workflow(...)` runs report the child run's `runId`. Exceptions thrown by the listener, and rejections from an async listener, are ignored and do not affect the stage.
1381
+
1322
1382
  The public authoring declaration intentionally excludes runtime-only executor fields such as `defaultSessionDir`, `gitWorktreeSetupCache`, `durableBackend`, `durableScope`, and `onStageSession`.
1323
1383
 
1324
1384
  #### Choosing the durable backend
@@ -1341,11 +1401,10 @@ One process uses one workflow database: DBOS runs a single executor per process,
1341
1401
  ```ts
1342
1402
  import { run, WorkflowDurabilityRequiredError } from "@bastani/atomic/workflows";
1343
1403
 
1344
- await run(helloWorld, { name: "Atomic" }, { adapters, durability: { mode: "memory" } });
1404
+ await run(helloWorld, { name: "Atomic" }, { durability: { mode: "memory" } });
1345
1405
 
1346
1406
  try {
1347
1407
  await run(helloWorld, { name: "Atomic" }, {
1348
- adapters,
1349
1408
  durability: { mode: "durable", systemDatabaseUrl: process.env.WORKFLOW_DATABASE_URL },
1350
1409
  });
1351
1410
  } catch (error) {
@@ -1451,12 +1510,23 @@ interface Store {
1451
1510
  removeRun(runId: string): boolean;
1452
1511
  recordNotice(notice: WorkflowNotice): void;
1453
1512
  ackNotice(id: string): boolean;
1513
+ snapshot(): StoreSnapshot;
1514
+ graphSnapshot(): StoreSnapshot;
1515
+ subscribe(listener: (snapshot: StoreSnapshot) => void): () => void;
1516
+ }
1517
+
1518
+ interface StoreSnapshot {
1519
+ readonly runs: readonly RunSnapshot[];
1520
+ readonly notices: readonly WorkflowNotice[];
1521
+ readonly version: number;
1454
1522
  }
1455
1523
  ```
1456
1524
 
1457
1525
  `createStore()` returns an isolated workflow state store. `store` is the default singleton exported by the SDK authoring surface.
1458
1526
 
1459
- This is the stable core exposed by the standalone authoring declaration. Atomic's runtime store also has graph, prompt, session, pause/resume, snapshot, and subscription methods used by embedded integrations; those richer runtime controls are not part of the lean workflow-package `Store` contract shown here.
1527
+ `snapshot()` returns a JSON copy of every run and notice; `version` increases on every store change. `graphSnapshot()` returns a cached, size-bounded copy for graph rendering that stays the same object until the store changes. `subscribe(listener)` calls `listener` with a fresh `snapshot()` after each change and returns an unsubscribe function.
1528
+
1529
+ Atomic's runtime store also has prompt, session, and pause/resume methods used by embedded integrations; those controls are not part of the published `Store` contract shown here.
1460
1530
 
1461
1531
  ### `createCancellationRegistry()` / `cancellationRegistry`
1462
1532
 
@@ -716,12 +716,16 @@ A `verified` result is cached durably, so a resumed run does not click again. A
716
716
 
717
717
  Workflow stages run chat language models. For a schema-backed decision, use the stage's `schema` and `structured_output` tool. It resolves an exact chat or registered classifier ID through the stage model registry and falls back on a classifier runtime failure. The direct `@bastani/pi-ai` examples below are for cases that need the classifier's probabilities or image generation from workflow TypeScript inside `ctx.tool(name, args, fn, { timeoutMs })`. Those calls are durable, replayable nodes. `model: "auto"` selects only chat language models for stage execution; `routerModel` may select a classifier for routing, but a classifier never executes a chat stage.
718
718
 
719
- Discovery loads a workflow file with the project that owns it as the module root. The host provides `@bastani/atomic/workflows` and `typebox`; other imports, including `@bastani/atomic` for `AuthStorage`, resolve from that project's `node_modules` in npm and standalone-binary installs alike. Install both libraries next to the workflow at the version `atomic --version` prints, because older releases have no `classify()` or `generateImages()`:
719
+ Atomic provides `@bastani/atomic/workflows`, `typebox`, and the exact imports `@bastani/atomic`, `@bastani/pi-ai`, and `@bastani/pi-ai/providers/all` to workflow files. The latter three use the running CLI's module instances and version, including `AuthStorage`. These examples need no project-local package installation, in npm or standalone-binary installs. A project copy does not override these host-provided imports.
720
+
721
+ For editor type checking, install the packages as dev dependencies at the version `atomic --version` prints. Atomic still runs these imports from the CLI, not from the project copy:
720
722
 
721
723
  ```sh
722
724
  npm install --save-dev @bastani/atomic@"$(atomic --version)" @bastani/pi-ai@"$(atomic --version)"
723
725
  ```
724
726
 
727
+ Other imports, including unlisted deep package subpaths, still resolve from the owning project's `node_modules`. Install those dependencies next to the workflow. `/workflow reload` reloads workflow files and their relative imports, but does not replace the shared host modules; restart Atomic to use a different host version.
728
+
725
729
  Pass `AuthStorage.create()` to `builtinModels()` so classifier and image requests use credentials saved by Atomic's `/login` flows in `auth.json`; this is the recommended default for workflows running inside Atomic. Ambient provider environment variables such as `TYPESAFE_API_KEY` and `OPENROUTER_API_KEY` continue to work. Never read, print, or pass credential values in workflow state, prompts, or artifacts. Both operations resolve instead of throwing: check `stopReason === "stop"` before using a result, and forward the tool's `signal` so a quit or targeted abort cancels the request.
726
730
 
727
731
  #### Classify a request before choosing a stage
@@ -777,7 +781,7 @@ export default workflow({
777
781
  });
778
782
  ```
779
783
 
780
- The cached `{ choice, confidence }` is what a resumed run replays; the classifier is not asked again. For a consequential action, add policy checks or `ctx.ui.confirm(...)` before the side effect. Without the extra install, a schema-backed stage or task (`ctx.task(name, { schema, prompt })`, as the `classify-and-act` builtin does) can also make a validated decision. Its `structured_output` call can name a registered classifier or omit `model` and use the current chat model. A classifier probability is available only from a direct `classify()` call like the example above; a chat result does not supply one.
784
+ The cached `{ choice, confidence }` is what a resumed run replays; the classifier is not asked again. For a consequential action, add policy checks or `ctx.ui.confirm(...)` before the side effect. A schema-backed stage or task (`ctx.task(name, { schema, prompt })`, as the `classify-and-act` builtin does) can also make a validated decision. Its `structured_output` call can name a registered classifier or omit `model` and use the current chat model. A classifier probability is available only from a direct `classify()` call like the example above; a chat result does not supply one.
781
785
 
782
786
  #### Generate an image artifact in a durable tool step
783
787