@bastani/atomic 0.8.28-alpha.1 → 0.8.28-alpha.3

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 (428) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +120 -118
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/package.json +1 -1
  5. package/dist/builtin/subagents/package.json +1 -1
  6. package/dist/builtin/web-access/package.json +1 -1
  7. package/dist/builtin/workflows/CHANGELOG.md +26 -0
  8. package/dist/builtin/workflows/README.md +1 -1
  9. package/dist/builtin/workflows/builtin/open-claude-design.ts +150 -13
  10. package/dist/builtin/workflows/package.json +1 -1
  11. package/dist/builtin/workflows/src/authoring.d.ts +5 -2
  12. package/dist/builtin/workflows/src/extension/dispatcher.ts +2 -0
  13. package/dist/builtin/workflows/src/extension/index.ts +8 -0
  14. package/dist/builtin/workflows/src/extension/render-result.ts +5 -2
  15. package/dist/builtin/workflows/src/extension/workflow-schema.ts +18 -0
  16. package/dist/builtin/workflows/src/runs/background/status.ts +4 -0
  17. package/dist/builtin/workflows/src/runs/foreground/executor.ts +1251 -110
  18. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +34 -10
  19. package/dist/builtin/workflows/src/shared/expanded-workflow-graph.ts +10 -2
  20. package/dist/builtin/workflows/src/shared/persistence-restore.ts +28 -9
  21. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +9 -3
  22. package/dist/builtin/workflows/src/shared/store-types.ts +10 -3
  23. package/dist/builtin/workflows/src/shared/store.ts +29 -7
  24. package/dist/builtin/workflows/src/shared/types.ts +12 -10
  25. package/dist/builtin/workflows/src/tui/chat-surface.ts +32 -33
  26. package/dist/builtin/workflows/src/tui/run-detail.ts +23 -4
  27. package/dist/builtin/workflows/src/tui/status-helpers.ts +4 -0
  28. package/dist/builtin/workflows/src/tui/status-list.ts +47 -3
  29. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +1 -1
  30. package/dist/builtin/workflows/src/tui/widget.ts +12 -3
  31. package/dist/builtin/workflows/src/workflows/define-workflow.ts +3 -3
  32. package/dist/cli/args.d.ts +4 -0
  33. package/dist/cli/args.d.ts.map +1 -1
  34. package/dist/cli/args.js +35 -0
  35. package/dist/cli/args.js.map +1 -1
  36. package/dist/cli/project-trust.d.ts +10 -0
  37. package/dist/cli/project-trust.d.ts.map +1 -0
  38. package/dist/cli/project-trust.js +36 -0
  39. package/dist/cli/project-trust.js.map +1 -0
  40. package/dist/cli/startup-ui.d.ts +7 -0
  41. package/dist/cli/startup-ui.d.ts.map +1 -0
  42. package/dist/cli/startup-ui.js +57 -0
  43. package/dist/cli/startup-ui.js.map +1 -0
  44. package/dist/config.d.ts.map +1 -1
  45. package/dist/config.js +24 -3
  46. package/dist/config.js.map +1 -1
  47. package/dist/core/agent-session-runtime.d.ts +3 -1
  48. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  49. package/dist/core/agent-session-runtime.js +1 -0
  50. package/dist/core/agent-session-runtime.js.map +1 -1
  51. package/dist/core/agent-session-services.d.ts +3 -1
  52. package/dist/core/agent-session-services.d.ts.map +1 -1
  53. package/dist/core/agent-session-services.js +3 -2
  54. package/dist/core/agent-session-services.js.map +1 -1
  55. package/dist/core/agent-session.d.ts +9 -1
  56. package/dist/core/agent-session.d.ts.map +1 -1
  57. package/dist/core/agent-session.js +70 -21
  58. package/dist/core/agent-session.js.map +1 -1
  59. package/dist/core/auth-storage.d.ts.map +1 -1
  60. package/dist/core/auth-storage.js +4 -3
  61. package/dist/core/auth-storage.js.map +1 -1
  62. package/dist/core/compaction/branch-summarization.d.ts +3 -1
  63. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  64. package/dist/core/compaction/branch-summarization.js +9 -3
  65. package/dist/core/compaction/branch-summarization.js.map +1 -1
  66. package/dist/core/compaction/compaction.d.ts.map +1 -1
  67. package/dist/core/compaction/compaction.js +18 -24
  68. package/dist/core/compaction/compaction.js.map +1 -1
  69. package/dist/core/compaction/utils.d.ts +1 -1
  70. package/dist/core/compaction/utils.d.ts.map +1 -1
  71. package/dist/core/compaction/utils.js +1 -1
  72. package/dist/core/compaction/utils.js.map +1 -1
  73. package/dist/core/experimental.d.ts +2 -0
  74. package/dist/core/experimental.d.ts.map +1 -0
  75. package/dist/core/experimental.js +5 -0
  76. package/dist/core/experimental.js.map +1 -0
  77. package/dist/core/export-html/template.js +19 -6
  78. package/dist/core/extensions/index.d.ts +1 -1
  79. package/dist/core/extensions/index.d.ts.map +1 -1
  80. package/dist/core/extensions/index.js.map +1 -1
  81. package/dist/core/extensions/loader.d.ts +1 -1
  82. package/dist/core/extensions/loader.d.ts.map +1 -1
  83. package/dist/core/extensions/loader.js +6 -4
  84. package/dist/core/extensions/loader.js.map +1 -1
  85. package/dist/core/extensions/runner.d.ts +11 -4
  86. package/dist/core/extensions/runner.d.ts.map +1 -1
  87. package/dist/core/extensions/runner.js +53 -3
  88. package/dist/core/extensions/runner.js.map +1 -1
  89. package/dist/core/extensions/types.d.ts +34 -4
  90. package/dist/core/extensions/types.d.ts.map +1 -1
  91. package/dist/core/extensions/types.js.map +1 -1
  92. package/dist/core/footer-data-provider.d.ts +2 -0
  93. package/dist/core/footer-data-provider.d.ts.map +1 -1
  94. package/dist/core/footer-data-provider.js +27 -1
  95. package/dist/core/footer-data-provider.js.map +1 -1
  96. package/dist/core/index.d.ts +2 -0
  97. package/dist/core/index.d.ts.map +1 -1
  98. package/dist/core/index.js +2 -0
  99. package/dist/core/index.js.map +1 -1
  100. package/dist/core/model-registry.d.ts.map +1 -1
  101. package/dist/core/model-registry.js +64 -7
  102. package/dist/core/model-registry.js.map +1 -1
  103. package/dist/core/model-resolver.d.ts.map +1 -1
  104. package/dist/core/model-resolver.js +1 -0
  105. package/dist/core/model-resolver.js.map +1 -1
  106. package/dist/core/output-guard.d.ts +1 -0
  107. package/dist/core/output-guard.d.ts.map +1 -1
  108. package/dist/core/output-guard.js +52 -22
  109. package/dist/core/output-guard.js.map +1 -1
  110. package/dist/core/package-manager.d.ts +1 -0
  111. package/dist/core/package-manager.d.ts.map +1 -1
  112. package/dist/core/package-manager.js +20 -8
  113. package/dist/core/package-manager.js.map +1 -1
  114. package/dist/core/project-trust.d.ts +15 -0
  115. package/dist/core/project-trust.d.ts.map +1 -0
  116. package/dist/core/project-trust.js +58 -0
  117. package/dist/core/project-trust.js.map +1 -0
  118. package/dist/core/prompt-templates.d.ts +5 -4
  119. package/dist/core/prompt-templates.d.ts.map +1 -1
  120. package/dist/core/prompt-templates.js +30 -29
  121. package/dist/core/prompt-templates.js.map +1 -1
  122. package/dist/core/provider-attribution.d.ts +4 -0
  123. package/dist/core/provider-attribution.d.ts.map +1 -0
  124. package/dist/core/provider-attribution.js +73 -0
  125. package/dist/core/provider-attribution.js.map +1 -0
  126. package/dist/core/provider-display-names.d.ts.map +1 -1
  127. package/dist/core/provider-display-names.js +3 -0
  128. package/dist/core/provider-display-names.js.map +1 -1
  129. package/dist/core/resolve-config-value.d.ts +9 -1
  130. package/dist/core/resolve-config-value.d.ts.map +1 -1
  131. package/dist/core/resolve-config-value.js +134 -11
  132. package/dist/core/resolve-config-value.js.map +1 -1
  133. package/dist/core/resource-loader.d.ts +12 -2
  134. package/dist/core/resource-loader.d.ts.map +1 -1
  135. package/dist/core/resource-loader.js +108 -18
  136. package/dist/core/resource-loader.js.map +1 -1
  137. package/dist/core/sdk.d.ts +4 -2
  138. package/dist/core/sdk.d.ts.map +1 -1
  139. package/dist/core/sdk.js +13 -42
  140. package/dist/core/sdk.js.map +1 -1
  141. package/dist/core/session-manager.d.ts +6 -7
  142. package/dist/core/session-manager.d.ts.map +1 -1
  143. package/dist/core/session-manager.js +99 -35
  144. package/dist/core/session-manager.js.map +1 -1
  145. package/dist/core/settings-manager.d.ts +15 -2
  146. package/dist/core/settings-manager.d.ts.map +1 -1
  147. package/dist/core/settings-manager.js +69 -10
  148. package/dist/core/settings-manager.js.map +1 -1
  149. package/dist/core/slash-commands.d.ts.map +1 -1
  150. package/dist/core/slash-commands.js +1 -0
  151. package/dist/core/slash-commands.js.map +1 -1
  152. package/dist/core/system-prompt.d.ts.map +1 -1
  153. package/dist/core/system-prompt.js +0 -3
  154. package/dist/core/system-prompt.js.map +1 -1
  155. package/dist/core/tools/ask-user-question/state/inline-input.d.ts +28 -0
  156. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -0
  157. package/dist/core/tools/ask-user-question/state/inline-input.js +56 -0
  158. package/dist/core/tools/ask-user-question/state/inline-input.js.map +1 -0
  159. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  160. package/dist/core/tools/ask-user-question/state/key-router.js +30 -4
  161. package/dist/core/tools/ask-user-question/state/key-router.js.map +1 -1
  162. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  163. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +9 -8
  164. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  165. package/dist/core/tools/ask-user-question/state/row-intent.d.ts +3 -2
  166. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  167. package/dist/core/tools/ask-user-question/state/row-intent.js +1 -1
  168. package/dist/core/tools/ask-user-question/state/row-intent.js.map +1 -1
  169. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts +2 -0
  170. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  171. package/dist/core/tools/ask-user-question/state/selectors/contract.js.map +1 -1
  172. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  173. package/dist/core/tools/ask-user-question/state/selectors/projections.js +2 -0
  174. package/dist/core/tools/ask-user-question/state/selectors/projections.js.map +1 -1
  175. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  176. package/dist/core/tools/ask-user-question/state/state-reducer.js +36 -24
  177. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  178. package/dist/core/tools/ask-user-question/state/state.d.ts +8 -0
  179. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  180. package/dist/core/tools/ask-user-question/state/state.js.map +1 -1
  181. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts +6 -0
  182. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  183. package/dist/core/tools/ask-user-question/tool/format-answer.js +19 -1
  184. package/dist/core/tools/ask-user-question/tool/format-answer.js.map +1 -1
  185. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts +3 -2
  186. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  187. package/dist/core/tools/ask-user-question/tool/response-envelope.js +15 -3
  188. package/dist/core/tools/ask-user-question/tool/response-envelope.js.map +1 -1
  189. package/dist/core/tools/ask-user-question/tool/types.d.ts +2 -1
  190. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  191. package/dist/core/tools/ask-user-question/tool/types.js.map +1 -1
  192. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts +5 -2
  193. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  194. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +2 -0
  195. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
  196. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +1 -0
  197. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  198. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +2 -1
  199. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  200. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts +3 -3
  201. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  202. package/dist/core/tools/ask-user-question/view/props-adapter.js +11 -4
  203. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  204. package/dist/core/tools/bash-policy.d.ts +62 -0
  205. package/dist/core/tools/bash-policy.d.ts.map +1 -0
  206. package/dist/core/tools/bash-policy.js +1069 -0
  207. package/dist/core/tools/bash-policy.js.map +1 -0
  208. package/dist/core/tools/bash.d.ts +5 -0
  209. package/dist/core/tools/bash.d.ts.map +1 -1
  210. package/dist/core/tools/bash.js +9 -1
  211. package/dist/core/tools/bash.js.map +1 -1
  212. package/dist/core/tools/edit.d.ts.map +1 -1
  213. package/dist/core/tools/edit.js +7 -10
  214. package/dist/core/tools/edit.js.map +1 -1
  215. package/dist/core/tools/find.d.ts.map +1 -1
  216. package/dist/core/tools/find.js +1 -1
  217. package/dist/core/tools/find.js.map +1 -1
  218. package/dist/core/tools/grep.d.ts.map +1 -1
  219. package/dist/core/tools/grep.js +1 -1
  220. package/dist/core/tools/grep.js.map +1 -1
  221. package/dist/core/tools/index.d.ts +1 -0
  222. package/dist/core/tools/index.d.ts.map +1 -1
  223. package/dist/core/tools/index.js +1 -0
  224. package/dist/core/tools/index.js.map +1 -1
  225. package/dist/core/tools/ls.d.ts.map +1 -1
  226. package/dist/core/tools/ls.js +1 -1
  227. package/dist/core/tools/ls.js.map +1 -1
  228. package/dist/core/tools/oversized-tool-result.d.ts +53 -0
  229. package/dist/core/tools/oversized-tool-result.d.ts.map +1 -0
  230. package/dist/core/tools/oversized-tool-result.js +206 -0
  231. package/dist/core/tools/oversized-tool-result.js.map +1 -0
  232. package/dist/core/tools/read.d.ts +12 -0
  233. package/dist/core/tools/read.d.ts.map +1 -1
  234. package/dist/core/tools/read.js +99 -34
  235. package/dist/core/tools/read.js.map +1 -1
  236. package/dist/core/tools/render-utils.d.ts +6 -0
  237. package/dist/core/tools/render-utils.d.ts.map +1 -1
  238. package/dist/core/tools/render-utils.js +17 -1
  239. package/dist/core/tools/render-utils.js.map +1 -1
  240. package/dist/core/tools/tool-definition-wrapper.d.ts +6 -0
  241. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  242. package/dist/core/tools/tool-definition-wrapper.js +2 -0
  243. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  244. package/dist/core/tools/tool-limits.d.ts +25 -0
  245. package/dist/core/tools/tool-limits.d.ts.map +1 -0
  246. package/dist/core/tools/tool-limits.js +25 -0
  247. package/dist/core/tools/tool-limits.js.map +1 -0
  248. package/dist/core/tools/write.d.ts.map +1 -1
  249. package/dist/core/tools/write.js +1 -1
  250. package/dist/core/tools/write.js.map +1 -1
  251. package/dist/core/trust-manager.d.ts +31 -0
  252. package/dist/core/trust-manager.d.ts.map +1 -0
  253. package/dist/core/trust-manager.js +196 -0
  254. package/dist/core/trust-manager.js.map +1 -0
  255. package/dist/index.d.ts +11 -6
  256. package/dist/index.d.ts.map +1 -1
  257. package/dist/index.js +6 -2
  258. package/dist/index.js.map +1 -1
  259. package/dist/main.d.ts.map +1 -1
  260. package/dist/main.js +142 -30
  261. package/dist/main.js.map +1 -1
  262. package/dist/migrations.d.ts +3 -1
  263. package/dist/migrations.d.ts.map +1 -1
  264. package/dist/migrations.js +325 -7
  265. package/dist/migrations.js.map +1 -1
  266. package/dist/modes/index.d.ts +1 -1
  267. package/dist/modes/index.d.ts.map +1 -1
  268. package/dist/modes/index.js.map +1 -1
  269. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  270. package/dist/modes/interactive/components/bash-execution.js +2 -2
  271. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  272. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/footer.js +6 -0
  274. package/dist/modes/interactive/components/footer.js.map +1 -1
  275. package/dist/modes/interactive/components/index.d.ts +1 -0
  276. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/index.js +1 -0
  278. package/dist/modes/interactive/components/index.js.map +1 -1
  279. package/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  280. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/login-dialog.js +9 -16
  282. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  283. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  284. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  285. package/dist/modes/interactive/components/settings-selector.js +20 -0
  286. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  287. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/tool-execution.js +22 -0
  289. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  290. package/dist/modes/interactive/components/trust-selector.d.ts +23 -0
  291. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
  292. package/dist/modes/interactive/components/trust-selector.js +85 -0
  293. package/dist/modes/interactive/components/trust-selector.js.map +1 -0
  294. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  295. package/dist/modes/interactive/components/user-message.js +1 -1
  296. package/dist/modes/interactive/components/user-message.js.map +1 -1
  297. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  298. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  299. package/dist/modes/interactive/interactive-mode.js +130 -9
  300. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  301. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  302. package/dist/modes/interactive/theme/theme.js +10 -0
  303. package/dist/modes/interactive/theme/theme.js.map +1 -1
  304. package/dist/modes/print-mode.d.ts.map +1 -1
  305. package/dist/modes/print-mode.js +1 -0
  306. package/dist/modes/print-mode.js.map +1 -1
  307. package/dist/modes/rpc/rpc-client.d.ts +3 -0
  308. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  309. package/dist/modes/rpc/rpc-client.js +50 -6
  310. package/dist/modes/rpc/rpc-client.js.map +1 -1
  311. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  312. package/dist/modes/rpc/rpc-mode.js +23 -4
  313. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  314. package/dist/modes/rpc/rpc-types.d.ts +1 -0
  315. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  316. package/dist/modes/rpc/rpc-types.js.map +1 -1
  317. package/dist/package-manager-cli.d.ts +6 -2
  318. package/dist/package-manager-cli.d.ts.map +1 -1
  319. package/dist/package-manager-cli.js +104 -10
  320. package/dist/package-manager-cli.js.map +1 -1
  321. package/dist/utils/changelog.d.ts +1 -0
  322. package/dist/utils/changelog.d.ts.map +1 -1
  323. package/dist/utils/changelog.js +72 -0
  324. package/dist/utils/changelog.js.map +1 -1
  325. package/dist/utils/deprecation.d.ts +4 -0
  326. package/dist/utils/deprecation.d.ts.map +1 -0
  327. package/dist/utils/deprecation.js +13 -0
  328. package/dist/utils/deprecation.js.map +1 -0
  329. package/dist/utils/git.d.ts.map +1 -1
  330. package/dist/utils/git.js +54 -22
  331. package/dist/utils/git.js.map +1 -1
  332. package/dist/utils/json.d.ts +3 -0
  333. package/dist/utils/json.d.ts.map +1 -0
  334. package/dist/utils/json.js +7 -0
  335. package/dist/utils/json.js.map +1 -0
  336. package/dist/utils/open-browser.d.ts +9 -0
  337. package/dist/utils/open-browser.d.ts.map +1 -0
  338. package/dist/utils/open-browser.js +22 -0
  339. package/dist/utils/open-browser.js.map +1 -0
  340. package/docs/containerization.md +111 -0
  341. package/docs/custom-provider.md +9 -9
  342. package/docs/development.md +1 -1
  343. package/docs/docs.json +2 -0
  344. package/docs/extensions.md +40 -4
  345. package/docs/index.md +2 -0
  346. package/docs/models.md +10 -10
  347. package/docs/packages.md +1 -1
  348. package/docs/prompt-templates.md +9 -2
  349. package/docs/providers.md +18 -5
  350. package/docs/quickstart.md +1 -0
  351. package/docs/rpc.md +3 -2
  352. package/docs/sdk.md +47 -0
  353. package/docs/security.md +58 -0
  354. package/docs/session-format.md +2 -2
  355. package/docs/sessions.md +8 -0
  356. package/docs/settings.md +21 -4
  357. package/docs/skills.md +1 -1
  358. package/docs/terminal-setup.md +44 -2
  359. package/docs/themes.md +1 -1
  360. package/docs/tmux.md +4 -2
  361. package/docs/tui.md +14 -5
  362. package/docs/usage.md +17 -3
  363. package/docs/workflows.md +127 -15
  364. package/examples/README.md +1 -1
  365. package/examples/extensions/README.md +8 -5
  366. package/examples/extensions/bash-spawn-hook.ts +1 -1
  367. package/examples/extensions/built-in-tool-renderer.ts +1 -1
  368. package/examples/extensions/claude-rules.ts +1 -1
  369. package/examples/extensions/commands.ts +1 -1
  370. package/examples/extensions/custom-header.ts +1 -1
  371. package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
  372. package/examples/extensions/custom-provider-anthropic/package-lock.json +4 -4
  373. package/examples/extensions/custom-provider-anthropic/package.json +6 -6
  374. package/examples/extensions/custom-provider-gitlab-duo/index.ts +55 -4
  375. package/examples/extensions/custom-provider-gitlab-duo/package.json +3 -3
  376. package/examples/extensions/doom-overlay/README.md +1 -1
  377. package/examples/extensions/doom-overlay/index.ts +2 -2
  378. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  379. package/examples/extensions/gondolin/index.ts +523 -0
  380. package/examples/extensions/gondolin/package-lock.json +185 -0
  381. package/examples/extensions/gondolin/package.json +19 -0
  382. package/examples/extensions/handoff.ts +1 -1
  383. package/examples/extensions/hidden-thinking-label.ts +1 -1
  384. package/examples/extensions/inline-bash.ts +2 -2
  385. package/examples/extensions/input-transform-streaming.ts +39 -0
  386. package/examples/extensions/input-transform.ts +3 -3
  387. package/examples/extensions/interactive-shell.ts +2 -2
  388. package/examples/extensions/mac-system-theme.ts +2 -2
  389. package/examples/extensions/minimal-mode.ts +1 -1
  390. package/examples/extensions/modal-editor.ts +1 -1
  391. package/examples/extensions/model-status.ts +1 -1
  392. package/examples/extensions/overlay-qa-tests.ts +198 -179
  393. package/examples/extensions/overlay-test.ts +1 -1
  394. package/examples/extensions/pirate.ts +1 -1
  395. package/examples/extensions/preset.ts +14 -12
  396. package/examples/extensions/project-trust.ts +64 -0
  397. package/examples/extensions/prompt-customizer.ts +1 -1
  398. package/examples/extensions/qna.ts +1 -1
  399. package/examples/extensions/question.ts +1 -1
  400. package/examples/extensions/questionnaire.ts +1 -1
  401. package/examples/extensions/rainbow-editor.ts +1 -1
  402. package/examples/extensions/sandbox/index.ts +16 -14
  403. package/examples/extensions/sandbox/package-lock.json +90 -90
  404. package/examples/extensions/sandbox/package.json +17 -17
  405. package/examples/extensions/snake.ts +1 -1
  406. package/examples/extensions/space-invaders.ts +1 -1
  407. package/examples/extensions/ssh.ts +2 -2
  408. package/examples/extensions/subagent/README.md +13 -13
  409. package/examples/extensions/subagent/agents.ts +4 -2
  410. package/examples/extensions/subagent/index.ts +6 -6
  411. package/examples/extensions/summarize.ts +1 -1
  412. package/examples/extensions/tic-tac-toe.ts +1 -1
  413. package/examples/extensions/titlebar-spinner.ts +1 -1
  414. package/examples/extensions/todo.ts +1 -1
  415. package/examples/extensions/tool-override.ts +1 -1
  416. package/examples/extensions/tools.ts +6 -1
  417. package/examples/extensions/with-deps/package-lock.json +4 -4
  418. package/examples/extensions/with-deps/package.json +7 -7
  419. package/examples/extensions/working-indicator.ts +4 -4
  420. package/examples/extensions/working-message-test.ts +1 -1
  421. package/examples/sdk/01-minimal.ts +1 -1
  422. package/examples/sdk/03-custom-prompt.ts +1 -1
  423. package/examples/sdk/04-skills.ts +1 -1
  424. package/examples/sdk/06-extensions.ts +2 -2
  425. package/examples/sdk/08-prompt-templates.ts +1 -1
  426. package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
  427. package/examples/sdk/README.md +2 -2
  428. package/package.json +4 -4
package/docs/settings.md CHANGED
@@ -7,7 +7,19 @@ Atomic uses JSON settings files with project settings overriding global settings
7
7
  | `~/.atomic/agent/settings.json` | Global (all projects) |
8
8
  | `.atomic/settings.json` | Project (current directory) |
9
9
 
10
- Edit directly or use `/settings` for common options. Atomic reads legacy `~/.pi/agent/settings.json` and `.pi/settings.json` as compatibility fallbacks, with `.atomic` paths taking precedence.
10
+ Edit directly or use `/settings` for common options. Atomic also reads legacy `~/.pi/agent/settings.json` and `.pi/settings.json` as compatibility fallbacks, with `.atomic` paths taking precedence.
11
+
12
+ ## Project Trust
13
+
14
+ On interactive startup, Atomic asks before trusting a project folder that contains trust-gated project inputs and has no saved decision for the folder or a parent folder in `~/.atomic/agent/trust.json`. Trusting a project allows Atomic to load project-local `.atomic/settings.json` and `.atomic` resources, legacy `.pi/settings.json` and `.pi` resources, project-local context files, install missing project packages, and execute project extensions.
15
+
16
+ Non-interactive modes (`-p`, `--mode json`, and `--mode rpc`) do not show a trust prompt. Without an applicable saved trust decision, they use `defaultProjectTrust` from global settings: `ask` (default) and `never` ignore trust-gated project inputs, while `always` trusts them. Pass `--approve`/`-a` or `--no-approve`/`-na` to override project trust for one run.
17
+
18
+ If no extension or saved decision applies, `defaultProjectTrust` controls the fallback behavior. Set it to `"ask"`, `"always"`, or `"never"` in `~/.atomic/agent/settings.json`, or change it with `/settings`.
19
+
20
+ `atomic config` and package commands use the same project trust flow. Pass `--approve` to trust project-local settings for one command or `--no-approve` to ignore them.
21
+
22
+ Use `/trust` in interactive mode to save a project trust decision for future sessions, including trust for the immediate parent folder. It writes `~/.atomic/agent/trust.json` only; the current session is not reloaded, so restart Atomic for changes to take effect.
11
23
 
12
24
  ## All Settings
13
25
 
@@ -58,13 +70,14 @@ Use `/fast` in interactive mode to edit these settings. Atomic applies fast mode
58
70
  |---------|------|---------|-------------|
59
71
  | `theme` | string | `"dark"` | Theme name (`"dark"`, `"light"`, a Catppuccin built-in, or custom) |
60
72
  | `quietStartup` | boolean | `false` | Hide startup header |
73
+ | `defaultProjectTrust` | string | `"ask"` | Fallback project trust behavior: `"ask"`, `"always"`, or `"never"`. Global setting only |
61
74
  | `collapseChangelog` | boolean | `false` | Show condensed changelog after updates |
62
75
  | `enableInstallTelemetry` | boolean | `true` | Send an anonymous install/update version ping after first install or changelog-detected updates. This does not control update checks |
63
76
  | `doubleEscapeAction` | string | `"tree"` | Action for double-escape: `"tree"`, `"fork"`, or `"none"` |
64
77
  | `treeFilterMode` | string | `"default"` | Default filter for `/tree`: `"default"`, `"no-tools"`, `"user-only"`, `"labeled-only"`, `"all"` |
65
78
  | `editorPaddingX` | number | `0` | Horizontal padding for input editor (0-3) |
66
79
  | `autocompleteMaxVisible` | number | `5` | Max visible items in autocomplete dropdown (3-20) |
67
- | `showHardwareCursor` | boolean | `false` | Show terminal cursor |
80
+ | `showHardwareCursor` | boolean | `false` | Show the terminal cursor while TUI positions it for IME support |
68
81
 
69
82
  ### Telemetry and update checks
70
83
 
@@ -117,11 +130,13 @@ Set `ATOMIC_SKIP_VERSION_CHECK=1` to disable the Atomic version update check. Us
117
130
  | `retry.maxRetries` | number | `3` | Maximum agent-level retry attempts |
118
131
  | `retry.baseDelayMs` | number | `2000` | Base delay for agent-level exponential backoff (2s, 4s, 8s) |
119
132
  | `retry.provider.timeoutMs` | number | SDK default | Provider/SDK request timeout in milliseconds |
120
- | `retry.provider.maxRetries` | number | SDK default | Provider/SDK retry attempts |
133
+ | `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
121
134
  | `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay before failing (60s) |
122
135
 
123
136
  When a provider requests a retry delay longer than `retry.provider.maxRetryDelayMs` (e.g., Google's "quota will reset after 5h"), the request fails immediately with an informative error instead of waiting silently. Set to `0` to disable the cap.
124
137
 
138
+ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explicitly needed. Setting it above `0` can make SDK/provider retries handle out-of-usage-limit errors before Atomic sees them, which may block the agent until the provider quota resets in some circumstances.
139
+
125
140
  ```json
126
141
  {
127
142
  "retry": {
@@ -168,7 +183,9 @@ The `/settings` picker offers these presets:
168
183
  |---------|------|---------|-------------|
169
184
  | `steeringMode` | string | `"one-at-a-time"` | How steering messages are sent: `"all"` or `"one-at-a-time"` |
170
185
  | `followUpMode` | string | `"one-at-a-time"` | How follow-up messages are sent: `"all"` or `"one-at-a-time"` |
171
- | `transport` | string | `"sse"` | Preferred transport for providers that support multiple transports: `"sse"`, `"websocket"`, or `"auto"` |
186
+ | `transport` | string | `"auto"` | Preferred transport for providers that support multiple transports: `"sse"`, `"websocket"`, `"websocket-cached"`, or `"auto"` |
187
+ | `httpIdleTimeoutMs` | number | `300000` | HTTP header/body idle timeout in milliseconds, also used by providers with explicit stream idle timeouts. Set to `0` to disable. |
188
+ | `websocketConnectTimeoutMs` | number | `15000` | WebSocket connect/open handshake timeout in milliseconds for providers that support WebSocket transports. Set to `0` to disable. |
172
189
 
173
190
  ### Terminal & Images
174
191
 
package/docs/skills.md CHANGED
@@ -26,7 +26,7 @@ Atomic loads skills from:
26
26
  - Global:
27
27
  - `~/.atomic/agent/skills/` (legacy `~/.pi/agent/skills/`)
28
28
  - `~/.agents/skills/`
29
- - Project:
29
+ - Project (only after the project is trusted):
30
30
  - `.atomic/skills/` (legacy `.pi/skills/`)
31
31
  - `.agents/skills/` in `cwd` and ancestor directories (up to git repo root, or filesystem root when not in a repo)
32
32
  - Packages: `skills/` directories, `atomic.skills`, or legacy `pi.skills` entries in `package.json`
@@ -6,6 +6,12 @@ Atomic uses the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboa
6
6
 
7
7
  Work out of the box.
8
8
 
9
+ ## Apple Terminal
10
+
11
+ Atomic enables enhanced key reporting when available. If Terminal.app still sends plain Return for `SHIFT+Enter`, Atomic uses a local macOS modifier fallback to treat that Return as `SHIFT+Enter`.
12
+
13
+ This fallback only works when Atomic runs on the same Mac as Terminal.app. It cannot detect the local keyboard over remote SSH.
14
+
9
15
  ## Ghostty
10
16
 
11
17
  Add to your Ghostty config (`~/Library/Application Support/com.mitchellh.ghostty/config` on macOS, `~/.config/ghostty/config` on Linux):
@@ -34,7 +40,7 @@ If you want `SHIFT+Enter` to keep working in tmux via that remap, add `ctrl+j` t
34
40
 
35
41
  ## WezTerm
36
42
 
37
- Create `~/.wezterm.lua`:
43
+ WezTerm usually works out of the box for `SHIFT+Enter` via xterm modifyOtherKeys. To use the Kitty keyboard protocol explicitly, create `~/.wezterm.lua`:
38
44
 
39
45
  ```lua
40
46
  local wezterm = require 'wezterm'
@@ -43,14 +49,50 @@ config.enable_kitty_keyboard = true
43
49
  return config
44
50
  ```
45
51
 
52
+ On macOS, WezTerm binds `Option+Enter` to fullscreen by default. To use `Option+Enter` for Atomic follow-up queueing, add this key override:
53
+
54
+ ```lua
55
+ local wezterm = require 'wezterm'
56
+ local config = wezterm.config_builder()
57
+ config.keys = {
58
+ {
59
+ key = 'Enter',
60
+ mods = 'ALT',
61
+ action = wezterm.action.SendString('\x1b[13;3u'),
62
+ },
63
+ }
64
+ return config
65
+ ```
66
+
67
+ If you already have a `config.keys` table, add the entry to it.
68
+
69
+ On WSL, WezTerm may require a visible hardware cursor for IME candidate window positioning. If CJK IME candidates do not follow the text cursor, set `ATOMIC_HARDWARE_CURSOR=1` before running Atomic or set `showHardwareCursor` to `true` in settings. The legacy `PI_HARDWARE_CURSOR=1` alias also works.
70
+
71
+ ## Alacritty
72
+
73
+ Alacritty usually works out of the box for `SHIFT+Enter`. On macOS, `Option+Enter` may arrive as plain `Enter`. To use `Option+Enter` for Atomic follow-up queueing, add to `~/.config/alacritty/alacritty.toml`:
74
+
75
+ ```toml
76
+ [[keyboard.bindings]]
77
+ key = "Enter"
78
+ mods = "Alt"
79
+ chars = "\u001b[13;3u"
80
+ ```
81
+
82
+ Restart Alacritty after changing the config.
83
+
46
84
  ## VS Code (Integrated Terminal)
47
85
 
86
+ VS Code 1.109.5 and newer enable Kitty keyboard protocol in the integrated terminal by default, so `SHIFT+Enter` should work out of the box.
87
+
88
+ VS Code versions older than 1.109.5 need an explicit terminal keybinding for `SHIFT+Enter`.
89
+
48
90
  `keybindings.json` locations:
49
91
  - macOS: `~/Library/Application Support/Code/User/keybindings.json`
50
92
  - Linux: `~/.config/Code/User/keybindings.json`
51
93
  - Windows: `%APPDATA%\\Code\\User\\keybindings.json`
52
94
 
53
- Add to `keybindings.json` to enable `SHIFT+Enter` for multi-line input:
95
+ Add to `keybindings.json`:
54
96
 
55
97
  ```json
56
98
  {
package/docs/themes.md CHANGED
@@ -20,7 +20,7 @@ Atomic loads themes from:
20
20
 
21
21
  - Built-in: `dark`, `light`, `catppuccin-frappe`, `catppuccin-latte`, `catppuccin-macchiato`, `catppuccin-mocha`
22
22
  - Global: `~/.atomic/agent/themes/*.json` (legacy `~/.pi/agent/themes/*.json`)
23
- - Project: `.atomic/themes/*.json` (legacy `.pi/themes/*.json`)
23
+ - Project: `.atomic/themes/*.json` (legacy `.pi/themes/*.json`, only after the project is trusted)
24
24
  - Packages: `themes/` directories, `atomic.themes`, or legacy `pi.themes` entries in `package.json`
25
25
  - Settings: `themes` array with files or directories
26
26
  - CLI: `--theme <path>` (repeatable)
package/docs/tmux.md CHANGED
@@ -18,7 +18,7 @@ tmux kill-server
18
18
  tmux
19
19
  ```
20
20
 
21
- Atomic requests extended key reporting automatically when Kitty keyboard protocol is not available. With `extended-keys-format csi-u`, tmux forwards modified keys in CSI-u format, which is the most reliable configuration.
21
+ Atomic requests extended key reporting automatically when Kitty keyboard protocol is not available. With `extended-keys-format csi-u`, tmux forwards modified keys in CSI-u format, which is the most reliable configuration. The `extended-keys-format` option requires tmux 3.5 or later.
22
22
 
23
23
  ## Why `csi-u` Is Recommended
24
24
 
@@ -57,5 +57,7 @@ This affects the default keybindings (`Enter` to submit, `SHIFT+Enter` for newli
57
57
 
58
58
  ## Requirements
59
59
 
60
- - tmux 3.2 or later (run `tmux -V` to check)
60
+ - tmux 3.5 or later for `extended-keys-format csi-u` (run `tmux -V` to check)
61
61
  - A terminal emulator that supports extended keys (Ghostty, Kitty, iTerm2, WezTerm, Windows Terminal)
62
+
63
+ With tmux 3.2 through 3.4, omit `extended-keys-format csi-u`; Atomic still supports tmux's default xterm `modifyOtherKeys` format.
package/docs/tui.md CHANGED
@@ -50,9 +50,9 @@ When a `Focusable` component has focus, TUI:
50
50
  1. Sets `focused = true` on the component
51
51
  2. Scans rendered output for `CURSOR_MARKER` (a zero-width APC escape sequence)
52
52
  3. Positions the hardware terminal cursor at that location
53
- 4. Shows the hardware cursor
53
+ 4. Shows the hardware cursor only when `showHardwareCursor` is enabled
54
54
 
55
- This enables IME candidate windows to appear at the correct position for CJK input methods. The `Editor` and `Input` built-in components already implement this interface.
55
+ The cursor remains hidden by default. This keeps the fake cursor rendering, while still positioning the hardware cursor for terminals that track IME candidate windows with hidden cursors. Some terminals require a visible hardware cursor for IME positioning; enable it with `showHardwareCursor`, `setShowHardwareCursor(true)`, or `ATOMIC_HARDWARE_CURSOR=1`. The `Editor` and `Input` built-in components already implement this interface.
56
56
 
57
57
  ### Container Components with Embedded Inputs
58
58
 
@@ -138,8 +138,11 @@ const result = await ctx.ui.custom<string | null>(
138
138
  // Responsive: hide on narrow terminals
139
139
  visible: (termWidth, termHeight) => termWidth >= 80,
140
140
  },
141
- // Get handle for programmatic visibility control
141
+ // Get handle for programmatic focus and visibility control
142
142
  onHandle: (handle) => {
143
+ // handle.focus() - focus this overlay and bring it to the visual front
144
+ // handle.unfocus() - release input to normal fallback
145
+ // handle.unfocus({ target }) - release input to a specific component or null
143
146
  // handle.setHidden(true/false) - toggle visibility
144
147
  // handle.hide() - permanently remove
145
148
  },
@@ -147,6 +150,12 @@ const result = await ctx.ui.custom<string | null>(
147
150
  );
148
151
  ```
149
152
 
153
+ ### Overlay Focus
154
+
155
+ A focused visible overlay keeps input ownership across temporary non-overlay UI. If an overlay opens another `ctx.ui.custom()` component without `{ overlay: true }`, that replacement UI receives input while it is active; when it closes, the focused overlay can reclaim input.
156
+
157
+ Use `handle.unfocus()` when a visible overlay should stop owning input and let TUI fall back to another visible capturing overlay or the previous focus target. Use `handle.unfocus({ target })` when a specific component should receive input while the overlay stays visible. Passing `{ target: null }` intentionally leaves no focused component until focus is set again.
158
+
150
159
  ### Overlay Lifecycle
151
160
 
152
161
  Overlay components are disposed when closed. Don't reuse references - create fresh instances:
@@ -433,10 +442,10 @@ interface MyTheme {
433
442
 
434
443
  ## Debug logging
435
444
 
436
- Set `PI_TUI_WRITE_LOG` to capture the raw ANSI stream written to stdout.
445
+ Set `ATOMIC_TUI_WRITE_LOG` to capture the raw ANSI stream written to stdout.
437
446
 
438
447
  ```bash
439
- PI_TUI_WRITE_LOG=/tmp/tui-ansi.log atomic
448
+ ATOMIC_TUI_WRITE_LOG=/tmp/tui-ansi.log atomic
440
449
  ```
441
450
 
442
451
  Atomic vendors TUI components through the installed `@earendil-works/pi-tui` dependency; this monorepo does not include the upstream TUI test source tree.
package/docs/usage.md CHANGED
@@ -79,7 +79,9 @@ Sessions are saved automatically to `~/.atomic/agent/sessions/`, organized by wo
79
79
  atomic -c # Continue most recent session
80
80
  atomic -r # Browse and select a session
81
81
  atomic --no-session # Ephemeral mode; do not save
82
- atomic --session <path|id> # Use a specific session file or session ID
82
+ atomic --session <path|id> # Use a specific session file or partial session ID
83
+ atomic --session-id <id> # Use/create an exact project-local session ID
84
+ atomic --name "Refactor" # Set the session display name
83
85
  atomic --fork <path|id> # Fork a session into a new session file
84
86
  ```
85
87
 
@@ -118,7 +120,7 @@ Use `/export [file]` to write a session to HTML.
118
120
 
119
121
  Use `/share` to upload a private GitHub gist with a shareable HTML link.
120
122
 
121
- If you use Atomic for open source work and want to publish sessions for model, prompt, tool, and evaluation research, see [`badlogic/pi-share-hf`](https://github.com/badlogic/pi-share-hf). It publishes sessions to Hugging Face datasets.
123
+ If you use Atomic for open source work and want to publish sessions for model, prompt, tool, and evaluation research, use an Atomic-owned workflow or your team's dataset process. Upstream Pi session-sharing utilities may still be useful for historical context, but they are not the primary Atomic publication path.
122
124
 
123
125
  ## CLI Reference
124
126
 
@@ -176,8 +178,10 @@ cat README.md | atomic -p "Summarize this text"
176
178
  | `-c`, `--continue` | Continue the most recent session |
177
179
  | `-r`, `--resume` | Browse and select a session |
178
180
  | `--session <path\|id>` | Use a specific session file or partial UUID |
181
+ | `--session-id <id>` | Use an exact project session ID, creating it if missing |
179
182
  | `--fork <path\|id>` | Fork a session file or partial UUID into a new session |
180
183
  | `--session-dir <dir>` | Custom session storage directory |
184
+ | `--name <name>`, `-n <name>` | Set the session display name |
181
185
  | `--no-session` | Ephemeral mode; do not save |
182
186
 
183
187
  ### Tool Options
@@ -185,10 +189,20 @@ cat README.md | atomic -p "Summarize this text"
185
189
  | Option | Description |
186
190
  |--------|-------------|
187
191
  | `--tools <list>`, `-t <list>` | Allowlist specific built-in, extension, and custom tools |
192
+ | `--exclude-tools <list>`, `-xt <list>` | Denylist specific built-in, extension, and custom tools |
188
193
  | `--no-builtin-tools`, `-nbt` | Disable built-in tools but keep extension/custom tools enabled |
189
194
  | `--no-tools`, `-nt` | Disable all tools |
190
195
 
191
- Default built-in tools: `read`, `bash`, `edit`, `write`, `ask_user_question`, `todo`. Additional built-in read-only tools are available through tool options: `grep`, `find`, `ls`.
196
+ Default built-in tools: `read`, `bash`, `edit`, `write`, `ask_user_question`, `todo`. Additional built-in read-only tools are available through tool options: `grep`, `find`, `ls`. Use `--exclude-tools` to disable one or more tools while leaving the rest available, for example `atomic --exclude-tools ask_user_question`.
197
+
198
+ ### Project Trust Options
199
+
200
+ | Option | Description |
201
+ |--------|-------------|
202
+ | `--approve`, `-a` | Trust project-local files/resources for this run |
203
+ | `--no-approve`, `-na` | Ignore project-local files/resources for this run |
204
+
205
+ Project trust gates `.atomic`/legacy `.pi` project resources, project package settings, project-local context files, and `.agents/skills` discovered from the project tree. Saved trust decisions can be managed with `/trust`; see [Security](/security).
192
206
 
193
207
  ### Resource Options
194
208
 
package/docs/workflows.md CHANGED
@@ -588,6 +588,41 @@ Atomic discovers workflow definitions in this order:
588
588
 
589
589
  A workflow module may export one default workflow definition and/or named workflow definitions. Discovery checks the default export first, then named exports.
590
590
 
591
+ Every runtime export of a discovered workflow file is validated as a workflow definition. A named export that is not a compiled definition — a widget factory, shared constant, or utility function — is rejected with an `INVALID_DEFINITION` discovery diagnostic (`export is not an object`), even when the module also has a valid default export (the valid workflow still loads; the diagnostic flags the extra export as skipped). Type-only exports (`export type` / `export interface`) are erased at runtime and never flagged.
592
+
593
+ To co-locate reusable helpers with your workflows — for example a `ctx.ui.custom<T>` widget factory you want to import in tests without running the workflow — put them in a subdirectory and import them from the workflow file. Discovery scans only the top level of each workflow directory, so subdirectories such as `.atomic/workflows/lib/` are never treated as workflow modules:
594
+
595
+ ```text
596
+ .atomic/workflows/
597
+ release-picker.ts # only runtime export: defineWorkflow(...).compile()
598
+ lib/
599
+ table-selector.ts # widget factory + helpers; not scanned by discovery
600
+ ```
601
+
602
+ ```ts
603
+ // .atomic/workflows/release-picker.ts
604
+ import { defineWorkflow, Type } from "@bastani/workflows";
605
+ import { tableSelectorFactory } from "./lib/table-selector.js";
606
+ ```
607
+
608
+ ```ts
609
+ // .atomic/workflows/lib/table-selector.ts
610
+ import type { WorkflowCustomUiFactory } from "@bastani/workflows";
611
+
612
+ export const tableSelectorFactory: WorkflowCustomUiFactory<{ id: string; name: string }> = (
613
+ tui,
614
+ theme,
615
+ _keybindings,
616
+ done,
617
+ ) => ({
618
+ render: (width) => ["..."],
619
+ invalidate: () => {},
620
+ handleInput: (data) => {
621
+ /* ... done({ id, name }) on Enter ... */
622
+ },
623
+ });
624
+ ```
625
+
591
626
  Workflow files are loaded via [jiti](https://github.com/unjs/jiti), so TypeScript works without compilation.
592
627
 
593
628
  ## Workflow Configuration
@@ -765,7 +800,7 @@ Input overrides are bare `key=value` tokens. Values are JSON-parsed when possibl
765
800
 
766
801
  In the TUI, `/workflow <name>` opens an input picker when the workflow declares inputs and either no arguments were supplied or required inputs are missing. Supplied values seed the picker. Pass `--no-picker` to skip that interactive flow.
767
802
 
768
- In non-interactive (`-p`, `--print`, or `--mode json`) sessions, named workflow dispatch waits for the terminal run snapshot and skips pickers. Because human input is runtime-only and workflows no longer carry a declaration-time HIL marker, headless dispatch does not reject a workflow just because its source contains `ctx.ui.*`. If you copy a HIL workflow example into a headless session, it can pass dispatch and then fail when execution reaches the prompt with an error such as `atomic-workflows: HIL ctx.ui.confirm is unavailable because Atomic runtime did not provide a UI adapter` (the primitive name varies, including `ctx.ui.custom`). Run those workflows interactively, or guard/remove runtime `ctx.ui.*` calls before using headless mode.
803
+ In non-interactive (`-p`, `--print`, or `--mode json`) sessions, named workflow dispatch waits for the terminal run snapshot and skips pickers. Because human input is runtime-only and workflows no longer carry a declaration-time HIL marker, headless dispatch does not reject a workflow just because its source contains `ctx.ui.*`. If you copy a HIL workflow example into a headless session, it can pass dispatch and then fail when execution reaches the prompt with an error such as `atomic-workflows: interactive ctx.ui.confirm is unavailable in headless (non-interactive) mode; run the workflow in interactive mode or remove the interactive prompt from this stage` (the primitive name varies, including `ctx.ui.custom`). Run those workflows interactively, or guard/remove runtime `ctx.ui.*` calls before using headless mode.
769
804
 
770
805
  <p align="center"><img src="images/workflow-input-picker.png" alt="Workflow Input Picker" width="600" /></p>
771
806
 
@@ -831,6 +866,7 @@ workflow({ action: "reload", reason: "added team workflow" })
831
866
  Control behavior:
832
867
 
833
868
  - `runId` accepts full run ids or unique prefixes for lifecycle and inspection actions. Status lists and run pickers show top-level user-launched workflows; nested child runs are implementation details of the expanded parent graph.
869
+ - `status` / `status <runId>` show terminal `ctx.exit(...)` statuses (`completed`, `skipped`, `cancelled`, or `blocked`) and the optional exit reason when one was supplied.
834
870
  - `stages` lists stage summaries, including flattened stages from nested `ctx.workflow(...)` imports and `sessionFile`/`transcriptPath` when a stage has a persisted session. Use `statusFilter: "all"` to include completed, failed, skipped, and pending stages.
835
871
  - `stage` returns details for one stage by stage id, unique prefix, or stage name, including nested child stages shown in the expanded graph and the persisted `sessionFile` when available.
836
872
  - `transcript` is reference-first with a small preview by default: it returns metadata, transcript paths, and up to 5 recent entries. For targeted lookup, quote the exact `sessionFile`/`transcriptPath` value without changing platform separators (preserve Windows backslashes), search it with `rg` or `grep`, then read only small surrounding ranges. Text results include JSON-escaped `sessionFileJson`/`transcriptPathJson` lines for copy-safe path literals. Pass explicit `tail` or `limit` to override the 5-entry preview; `tail` overrides `limit`; `includeToolOutput` includes captured snapshot tool output in snapshot transcript results.
@@ -929,7 +965,7 @@ workflow({
929
965
  })
930
966
  ```
931
967
 
932
- Direct mode supports top-level/default options and per-task options such as `context`, `forkFromSessionFile`, `model`, `fallbackModels`, `thinkingLevel`, `tools`, `noTools`, `customTools`, `mcp`, `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, and `agentDir`. Direct chains also support `chainName`, `chainDir`, and `failFast`.
968
+ Direct mode supports top-level/default options and per-task options such as `context`, `forkFromSessionFile`, `model`, `fallbackModels`, `thinkingLevel`, `tools`, `noTools`, `customTools`, `bashPolicy`, `mcp`, `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, and `agentDir`. Direct chains also support `chainName`, `chainDir`, and `failFast`.
933
969
 
934
970
  For large fan-outs, prefer `outputMode: "file-only"` so the parent result contains compact file references instead of full output. Treat intercom payloads from async direct runs as user-visible workflow output.
935
971
 
@@ -1020,7 +1056,42 @@ Builder basics:
1020
1056
 
1021
1057
  `prompt` and `task` are aliases for task text. Prefer `prompt` inside authored workflow files because it mirrors lower-level `stage.prompt(...)`; `task` remains useful in direct tool calls and chain examples.
1022
1058
 
1023
- Author workflows to create at least one tracked stage by calling `ctx.task()`, `ctx.chain()`, `ctx.parallel()`, `ctx.stage()`, or `ctx.workflow()` in the run body so each run has graph nodes to inspect, attach to, interrupt, resume, and render.
1059
+ Author workflows to create at least one tracked stage by calling `ctx.task()`, `ctx.chain()`, `ctx.parallel()`, `ctx.stage()`, or `ctx.workflow()` in the run body so each normal run has graph nodes to inspect, attach to, interrupt, resume, and render. Guard-only workflows may call `ctx.exit(...)` before creating a stage when they intentionally stop early.
1060
+
1061
+ ### Early exit with `ctx.exit()`
1062
+
1063
+ Use `ctx.exit(options?)` when workflow code intentionally stops the current run from a helper, branch, loop, or precondition guard without classifying the run as failed. `ctx.exit()` throws an executor-owned control signal and is typed as `never`, so code after it is unreachable. In async `.run()` bodies, prefer `return ctx.exit(...)` when the exit is the only path so TypeScript can see the non-returning branch.
1064
+
1065
+ ```ts
1066
+ export default defineWorkflow("guarded-import")
1067
+ .output("scanned", Type.Number())
1068
+ .run(async (ctx) => {
1069
+ const files = await findCandidateFiles(ctx.cwd);
1070
+ if (files.length === 0) {
1071
+ return ctx.exit({
1072
+ status: "skipped",
1073
+ reason: "No matching files",
1074
+ outputs: { scanned: 0 },
1075
+ });
1076
+ }
1077
+
1078
+ const review = await ctx.task("review", { prompt: `Review ${files.join(", ")}` });
1079
+ return { scanned: files.length };
1080
+ })
1081
+ .compile();
1082
+ ```
1083
+
1084
+ `ctx.exit()` accepts `status: "completed" | "skipped" | "cancelled" | "blocked"`; it never accepts `"failed"` or `"killed"` because thrown errors and external run-control keep those meanings. `status` defaults to `"completed"`. `reason` is persisted and shown in status surfaces, including the default `/workflow status` list and `/workflow status <runId>` detail, so do not put secrets in it. `outputs` may contain a partial subset of declared outputs; provided keys still must be declared with `.output(...)`, match their TypeBox schema, and be JSON-serializable. Missing required outputs are allowed only on the `ctx.exit(...)` path. Exited runs are terminal and not resumable; external `kill`, `pause`, and `interrupt` keep their existing behavior.
1085
+
1086
+ The first selected `ctx.exit({ outputs })` snapshots its output payload synchronously by value before JavaScript `finally` blocks or cleanup callbacks can mutate the caller-owned object. The snapshot preserves undeclared keys and invalid values until post-cleanup validation, so deleting an undeclared key or changing an invalid value after `ctx.exit(...)` does not change the terminal validation result. If reading `status`, `reason`, or `outputs` options, or enumerating/copying the output snapshot itself, throws, Atomic still selects the exit signal, runs workflow-exit cleanup when feasible, and then records a terminal non-resumable authoring failure (`resumable: false`) if no external terminal control won first.
1087
+
1088
+ After the first `ctx.exit(...)` wins, the executor treats that exit as a level-triggered gate. Later delayed calls to `ctx.stage`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, or graph-backed `ctx.ui.*` prompts rethrow the selected exit signal before creating stages, prompt nodes, child runs, or control handles. Retained `StageContext` handles from before the exit also become inert: `prompt`, `complete`, steering/follow-up, model/thinking controls, tree navigation, compaction, abort, and attached-pane session-realization paths refuse to touch or create an `AgentSession` after the exit is selected. `ctx.parallel` stops dequeuing queued work after exit even with `failFast: false` and limited concurrency; already-started stages and prompt nodes are finalized as `skipped` with a `workflow-exit` reason that prompt-node abort handling preserves instead of overwriting with a generic run-aborted reason.
1089
+
1090
+ Continuation replay also observes the exit gate. Replayed `ctx.stage(...).prompt(...)`, replayed `complete(...)`, graph-backed prompt-node replay, and completed child-boundary replay re-check for a selected exit after their replay microtask and before writing a current-run completed stage end. If `ctx.exit(...)` wins that gap, the pending replay finalizer is skipped/suppressed with the workflow-exit reason instead of creating a misleading completed stage in the resumed run.
1091
+
1092
+ The store is the terminal authority for all run-end races. `ctx.exit(...)` starts cleanup before validating exit outputs, and an external `/workflow kill` can still win the terminal `recordRunEnd` write while that cleanup is pending. When that happens, the SDK `RunResult`, `onRunEnd` callback, live store, and persisted `workflow.run.end` entries all report the canonical `killed` state; the losing `ctx.exit` status or validation failure is not returned and does not append a second run-end entry.
1093
+
1094
+ Control-signal probing is fail-closed. When the executor inspects an arbitrary thrown value or abort reason for internal workflow-exit markers, parent-exit markers, aggregate `errors`, `cause`, `reason`, or `scope`, throwing or inaccessible accessors are treated as “no signal for that branch.” The run then continues through ordinary failure finalization, or the ordinary killed path for external abort reasons, instead of letting author-defined getters escape the executor catch path or be misclassified as `ctx.exit(...)`.
1024
1095
 
1025
1096
  ### Guiding Principles
1026
1097
 
@@ -1061,7 +1132,7 @@ In TypeScript workflow files, `.input(...)` also narrows `ctx.inputs` for better
1061
1132
 
1062
1133
  ### Outputs
1063
1134
 
1064
- Workflow outputs are runtime contracts for completed workflow runs and for parent workflows that call a child with `ctx.workflow(childWorkflow, ...)`. A workflow returns a JSON-serializable object from `.run()`, and `.output(key, schema)` documents, validates, and exposes keys from that returned object. Primitives, arrays, `null`, functions, symbols, `undefined` properties, `NaN`, and infinite numbers fail validation.
1135
+ Workflow outputs are runtime contracts for completed workflow runs and for parent workflows that call a child with `ctx.workflow(childWorkflow, ...)`. A workflow normally returns a JSON-serializable object from `.run()`, and `.output(key, schema)` documents, validates, and exposes keys from that returned object. `ctx.exit({ outputs })` can expose a partial subset of the same declared output contract when the run intentionally stops early. Primitives, arrays, `null`, functions, symbols, `undefined` properties, `NaN`, and infinite numbers fail validation.
1065
1136
 
1066
1137
  **Return convention:** outputs are return-object keys. Atomic never infers child workflow outputs from stage names, stage order, or the final assistant message. If a parent should read `child.outputs.foo`, the child workflow's `.run()` must both declare `.output("foo", schema)` and return `{ foo: value }`. `result` is not special and is never added for you: to expose `result`, declare `.output("result", schema)` and return `{ result }` exactly like any other output. Returning a key that is not declared with `.output(...)` fails the run with `atomic-workflows: workflow "<name>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return`.
1067
1138
 
@@ -1167,7 +1238,7 @@ Tradeoff: `Type.Unsafe<T>()` does not deeply validate at runtime — it trusts t
1167
1238
 
1168
1239
  - `ctx.inputs.x` is `Static<inputSchema>` for the input you declared with `.input("x", schema)` — required and defaulted schemas are always present, and `Type.Optional(...)` adds `| undefined`.
1169
1240
  - The `.run()` return is checked against your declared outputs at **compile time** (a missing required output or a wrong value type is a TypeScript error) and at **runtime** via TypeBox `Value` (undeclared keys are rejected and the declared shape is enforced recursively).
1170
- - `ctx.workflow(child).outputs` is typed from the child's declared `.output(...)` contract, so a parent reads precisely-typed child outputs without casting.
1241
+ - `ctx.workflow(child)` returns a discriminated child result. When `child.exited === false`, `child.outputs` is the child's full declared `.output(...)` contract; when `child.exited === true`, `child.outputs` is `Partial<TOutputs>` because child `ctx.exit({ outputs })` may intentionally provide only a subset.
1171
1242
 
1172
1243
  Use `Static<typeof schema>` (both `Static` and `TSchema` are re-exported from `@bastani/workflows`) when you need the inferred TypeScript type of a schema directly — for example to type a helper that builds an output value.
1173
1244
 
@@ -1209,6 +1280,9 @@ export default defineWorkflow("research-and-synthesize")
1209
1280
  inputs: { topic: ctx.inputs.topic },
1210
1281
  stageName: "run shared research",
1211
1282
  });
1283
+ if (child.exited === true) {
1284
+ return ctx.exit({ status: child.status, reason: child.exitReason ?? "shared research stopped early" });
1285
+ }
1212
1286
 
1213
1287
  const final = await ctx.task("synthesize", {
1214
1288
  prompt: `Synthesize:\n\n${String(child.outputs.summary)}`,
@@ -1271,6 +1345,9 @@ export default defineWorkflow("research-then-implement")
1271
1345
  inputs: { prompt: topic, max_concurrency: 4 },
1272
1346
  stageName: "deep research",
1273
1347
  });
1348
+ if (research.exited === true) {
1349
+ return ctx.exit({ status: research.status, reason: research.exitReason ?? "deep research stopped early" });
1350
+ }
1274
1351
 
1275
1352
  if (String(ctx.inputs.runner) === "ralph") {
1276
1353
  const implementation = await ctx.workflow(ralph, {
@@ -1280,6 +1357,9 @@ export default defineWorkflow("research-then-implement")
1280
1357
  },
1281
1358
  stageName: "ralph implementation",
1282
1359
  });
1360
+ if (implementation.exited === true) {
1361
+ return ctx.exit({ status: implementation.status, reason: implementation.exitReason ?? "ralph stopped early" });
1362
+ }
1283
1363
 
1284
1364
  return {
1285
1365
  research_doc_path: research.outputs.research_doc_path,
@@ -1295,6 +1375,9 @@ export default defineWorkflow("research-then-implement")
1295
1375
  },
1296
1376
  stageName: "goal implementation",
1297
1377
  });
1378
+ if (implementation.exited === true) {
1379
+ return ctx.exit({ status: implementation.status, reason: implementation.exitReason ?? "goal stopped early" });
1380
+ }
1298
1381
 
1299
1382
  return {
1300
1383
  research_doc_path: research.outputs.research_doc_path,
@@ -1313,8 +1396,10 @@ Passing a compiled definition directly to `ctx.workflow(...)` uses the child wor
1313
1396
  |---|---|
1314
1397
  | `workflow` | Normalized child workflow name. |
1315
1398
  | `runId` | Nested child run id. |
1316
- | `status` | `completed` when the child workflow succeeds. Failed or interrupted children make the parent child call fail. |
1317
- | `outputs` | Declared child outputs. |
1399
+ | `status` | `completed`, or `skipped` / `cancelled` / `blocked` when the child intentionally ended with `ctx.exit(...)`. Failed or externally killed children make the parent child call fail. |
1400
+ | `exited` | `false` for normal child completion; `true` when the child used `ctx.exit(...)` (including `ctx.exit({ status: "completed" })`). |
1401
+ | `outputs` | Full declared child outputs when `exited === false`; partial declared child outputs when `exited === true`. |
1402
+ | `exitReason` | Optional child `ctx.exit({ reason })` text, present only on the `exited === true` branch. |
1318
1403
 
1319
1404
  `ctx.workflow()` options:
1320
1405
 
@@ -1327,17 +1412,23 @@ Output exposure rules:
1327
1412
 
1328
1413
  ```ts
1329
1414
  const child = await ctx.workflow(sharedResearch);
1330
- child.outputs.summary; // declared by sharedResearch.output("summary", ...)
1331
- child.outputs.sources; // declared by sharedResearch.output("sources", ...)
1415
+ if (child.exited === true) {
1416
+ child.outputs.summary; // string | undefined: ctx.exit({ outputs }) may be partial
1417
+ } else {
1418
+ child.outputs.summary; // string: normal completion returned the full declared contract
1419
+ child.outputs.sources; // string[] | undefined: optional output declared by sharedResearch
1420
+ }
1332
1421
  ```
1333
1422
 
1334
- A child exposes exactly its declared outputs — the keys it declared with `.output(...)` and returned from `.run()`. There are no implicit outputs and no raw return-object passthrough. If `.run()` returns a key that was not declared with `.output(...)`, the child run fails with `atomic-workflows: workflow "<childName>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return`, and the parent surfaces that failure through the wrapper `atomic-workflows: child workflow "<childName>" (<displayName>) failed with status failed: ...`. A child with no declared outputs therefore exposes no outputs. Missing required outputs, schema type mismatches, and non-JSON-serializable returned values fail the child workflow call before the parent continues.
1423
+ A child exposes exactly its declared outputs — the keys it declared with `.output(...)` and returned from `.run()` or supplied to `ctx.exit({ outputs })`. There are no implicit outputs and no raw return-object passthrough. If `.run()` returns a key that was not declared with `.output(...)`, the child run fails with `atomic-workflows: workflow "<childName>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return`, and the parent surfaces that failure through the wrapper `atomic-workflows: child workflow "<childName>" (<displayName>) failed with status failed: ...`. A child with no declared outputs therefore exposes no outputs. Missing required outputs, schema type mismatches, and non-JSON-serializable returned values fail normal child completion before the parent continues; child `ctx.exit({ outputs })` allows missing required outputs but still validates every provided key and sets `child.exited === true` so parent code must handle the partial shape.
1335
1424
 
1336
1425
  Only compiled workflow definitions can be passed to `ctx.workflow(...)`. Import reusable workflows with TypeScript `import` statements first; use `/workflow` names such as `goal` only for launching named runs, not as `ctx.workflow(...)` arguments. If a module is missing or does not export a compiled workflow definition, workflow discovery fails when loading that module. Nested child workflows count against `maxDepth` (default `4` total workflow levels).
1337
1426
 
1338
- The graph includes both the parent boundary node and the imported child workflow's own stages while the child is loading/running, so the user can observe progress and interrupt sub-workflows before they complete. Completed boundaries still retain the child workflow name, child run id prefix, and exposed output count for replay/debugging. Use `stageName` when the parent needs a more specific label, but keep it concise so the child summary remains readable in the graph.
1427
+ The graph includes both the parent boundary node and the imported child workflow's own stages while the child is loading/running, so the user can observe progress and interrupt sub-workflows before they complete. Completed boundaries still retain the child workflow name, child run id prefix, and exposed output count for replay/debugging. Skipped or failed boundaries do not retain child-edge metadata (`workflowChild` / `workflowChildRun`), and graph expansion ignores any stale non-completed boundary metadata from older persisted sessions instead of flattening an unrelated child run. Use `stageName` when the parent needs a more specific label, but keep it concise so the child summary remains readable in the graph.
1428
+
1429
+ If a parent workflow exits through `ctx.exit(...)` while a child workflow is in flight, the parent executor only skips the parent boundary and sends the child a typed parent-exit abort reason. The hidden child executor owns child cleanup: active child stages and prompt nodes are skipped for `workflow-exit`, live child stage handles/sessions are disposed, and the child run is finalized as terminal `cancelled` (not `killed`) and non-resumable. The child executor writes each skipped child `workflow.stage.end` exactly once before its child `workflow.run.end`, and parent exit finalization waits for that child cleanup before writing the parent `workflow.run.end`, so restored sessions do not reconstruct the child as interrupted or failed. The skipped parent boundary clears any live child-run edge before store or persistence updates, so status/graph views do not display stale child stages from a boundary that did not complete. A delayed parent branch that calls `ctx.workflow(...)` after the exit gate is selected does not create a boundary or child run.
1339
1430
 
1340
- Continuation replay treats the parent child-workflow boundary as the durable checkpoint: a previously completed child boundary replays with the original exposed outputs and without re-running the child, while a child that failed or was interrupted before completion starts again from the beginning on continuation.
1431
+ Continuation replay treats the parent child-workflow boundary as the durable checkpoint: a previously completed child boundary replays with the original exposed outputs and without re-running the child, while a child that failed or was interrupted before completion starts again from the beginning on continuation. If `ctx.exit(...)` wins while a completed boundary is being replayed but before replay finalization, the boundary is finalized as skipped and its preloaded child metadata is omitted from store, persistence, restore, and expanded graph views.
1341
1432
 
1342
1433
  ## Workflow Primitives
1343
1434
 
@@ -1381,10 +1472,31 @@ Common task/stage options include:
1381
1472
  - `previous` for small handoff context; use artifact paths plus `reads` for large outputs, logs, research bundles, or reviewer payloads
1382
1473
  - `context: "fresh" | "fork"`, `forkFromSessionFile`
1383
1474
  - `model`, `fallbackModels`, `thinkingLevel`, `scopedModels`, `modelRegistry` — `model` and each `fallbackModels` entry accept a `model_name:thinking_effort` reasoning suffix; the standalone `thinkingLevel` is deprecated (see [Reasoning levels](#reasoning-levels))
1384
- - `tools`, `noTools`, `customTools`, `mcp: { allow?: string[], deny?: string[] }`
1475
+ - `tools`, `noTools`, `customTools`, `mcp: { allow?: string[], deny?: string[] }`, `bashPolicy`
1385
1476
  - `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, `agentDir`
1386
1477
  - advanced host-supplied SDK seams: `authStorage`, `resourceLoader`, `sessionManager`, `settingsManager`, `sessionStartEvent`
1387
1478
 
1479
+ `bashPolicy` scopes the built-in `bash` tool for one stage or task. `tools` must still include `"bash"` (or leave it available by default); the policy only narrows command text after the shell tool is exposed. It supports exact strings, `{ prefix }`, command-string `{ glob }`, and `{ regex, flags? }` rules, `default: "allow" | "deny"` (default `"allow"`), `deny` precedence, and `match: "segments" | "whole"` (default `"segments"`). Omitting `bashPolicy`, passing `{}`, or passing a default-allow policy with no `allow`/`deny` rules (including empty arrays or match-only default-allow policies) preserves legacy behavior and does not parse commands; malformed policy shapes such as unknown top-level keys (`denny`, `extra`), non-array `allow`/`deny`, invalid rule objects, invalid regexes, invalid glob bracket ranges, or stateful `g`/`y` regex flags fail closed as `invalid-policy`. Segment mode checks each command in pipelines/chains/substitutions before execution, treats unquoted LF, CRLF, and bare CR as command separators, keeps non-leading Bash `>|` noclobber redirections inside the current command segment, and rejects reserved/compound shell heads, leading redirections, attached command-head redirections, and command heads that are not literal words.
1480
+
1481
+ ```ts
1482
+ await ctx.task("browser-preview", {
1483
+ tools: ["bash"],
1484
+ bashPolicy: {
1485
+ default: "deny",
1486
+ allow: [
1487
+ "which browse",
1488
+ { prefix: "browse open " },
1489
+ { prefix: "browse snapshot" },
1490
+ { prefix: "grep " },
1491
+ ],
1492
+ deny: [{ regex: "\\brm\\b" }],
1493
+ },
1494
+ prompt: "Open the preview with browse, then summarize the visible state.",
1495
+ });
1496
+ ```
1497
+
1498
+ A command such as `browse snapshot | grep title` passes only when both segments are allowed, and `browse snapshot\nrm -rf /tmp/proof` cannot be hidden behind a `{ prefix: "browse " }` rule because the newline starts a new segment. Glob rules match command strings rather than filesystem path segments: `*` and `?` may span `/`, so `{ glob: "browse *" }` matches URLs and slash-bearing paths such as `browse http://localhost:3000`, `browse docs/index.html`, and `browse ./preview/output.html` while still matching the whole target rather than `echo browse ...`; escaped bracket-class metacharacters such as `\-`, `\^`, `\]`, `\[`, and `\\` stay literal, while malformed glob ranges such as `{ glob: "echo [z-a]" }` become `invalid-policy` denials. Segment mode accepts literal heads such as `grep`, `./script`, `/usr/bin/env`, `bun`, and `browse`, and treats non-leading `>|` as redirection syntax so `echo ok >|/tmp/out` stays one segment, but conservatively rejects reserved or compound heads (`coproc`, `if`, `for`, `while`, `case`, `{`, `}`, `!`), leading redirections (`>file cmd`, `2>file cmd`, `<file cmd`, `&>file cmd`, `&>>file cmd`, `>|file cmd`, `<&0 cmd`, `>&2 cmd`), redirections attached to the command-head word (`cmd>file`, `cmd>>file`, `cmd>|file`, `cmd2>file`, `cmd>&2`, `cmd</tmp/in`), leading environment assignments (`PATH=/tmp:$PATH browse snapshot`, `LD_PRELOAD=/tmp/x browse snapshot`, `FOO=bar`), dynamic heads such as `$cmd`, `${cmd}`, `r''m`, `r\m`, `~/bin/rm`, `r*m`, `{rm,echo}`, `r$(printf m)`, or backtick-built command names. A single denied, redirection-prefixed, attached-redirection, assignment-prefixed, dynamic, or unrecognized segment blocks the whole command with a model-readable tool error and no UI prompt, so the behavior works in headless workflow runs. Use `match: "whole"` only when raw-command matching is intentional.
1499
+
1388
1500
  `gitWorktreeDir` selects a reusable Git worktree root for `ctx.stage`, `ctx.task`, `ctx.chain`, and `ctx.parallel`. If the path is missing, Atomic creates it with `git worktree add --detach <path> <baseBranch>`; if it exists, it must be a same-repository worktree root. The default stage cwd becomes the matching cwd inside the worktree and preserves the invoking repo-relative subdirectory. Explicit `cwd` still wins; relative `cwd` values resolve from the worktree cwd, while absolute `cwd` values are used as provided. `gitWorktreeDir` is mutually exclusive with `worktree: true`: use `gitWorktreeDir` for named/reusable worktrees and `worktree: true` for temporary direct-mode worktrees that are cleaned up after the run.
1389
1501
 
1390
1502
  To bind user inputs to a workflow-wide worktree default, use the builder method:
@@ -1438,7 +1550,7 @@ This applies everywhere a stage accepts a model: direct `ctx.task`/`ctx.chain`/`
1438
1550
  - `/workflow <name> key=value ...` for interactive named runs
1439
1551
  - `/workflow connect|attach|pause|interrupt|resume|status|inputs|reload` for live control, inspection, and rediscovery
1440
1552
  - the `workflow` tool for agent-initiated orchestration and direct one-off runs
1441
- Workflow definition files must export definitions produced by `defineWorkflow(...).compile()`. The former imperative object-form runner is not part of the public SDK, and authored workflow files cannot import `runWorkflow` from `@bastani/workflows`.
1553
+ Workflow definition files must export definitions produced by `defineWorkflow(...).compile()`. Keep non-workflow runtime helpers (widget factories, shared utilities) in a subdirectory the discovery scan ignores, such as `.atomic/workflows/lib/` — see [Workflow Locations](#workflow-locations). The former imperative object-form runner is not part of the public SDK, and authored workflow files cannot import `runWorkflow` from `@bastani/workflows`.
1442
1554
 
1443
1555
  Standalone TypeScript workflow packages type-check the SDK import with no hand-authored `.d.ts`, no `declare module` shim, and no `tsconfig` `paths` alias. The SDK types ship with `@bastani/atomic`, so a workflow package depends only on `@bastani/atomic` (plus a `typebox` peer):
1444
1556
 
@@ -1677,7 +1789,7 @@ Good workflows are information-flow systems, not just prompt sequences. Keep sta
1677
1789
  - Do not use legacy workflow tool fields like `agent`, `stage`, or run-control `name`.
1678
1790
  - Do not pass strings such as `"goal"` or path objects to `ctx.workflow(...)`; import the compiled workflow definition from `@bastani/workflows/builtin` or another TypeScript module first.
1679
1791
  - Do not rely on undeclared child outputs; returning a key that is not declared with `.output(...)` fails the run. Declare `.output(...)` for every child-workflow field you expose — including `result` — and return values matching those schemas from `.run()`.
1680
- - Do not expect to select or rename child outputs at the call site; parent workflows receive the child's declared output contract as `child.outputs`.
1792
+ - Do not expect to select or rename child outputs at the call site; parent workflows receive the child's declared output contract as `child.outputs` after checking `child.exited === false`, and a partial declared-output map when `child.exited === true`.
1681
1793
  - Do not expect named workflow runs to block the chat turn; they are background tasks.
1682
1794
  - Do not call `kill` when the user asks to interrupt or pause resumably.
1683
1795
  - Keep stage names readable because they appear in workflow status and UI.
@@ -1,6 +1,6 @@
1
1
  # Examples
2
2
 
3
- Example code for pi-coding-agent SDK and extensions.
3
+ Example code for the Atomic SDK and extensions.
4
4
 
5
5
  ## Directories
6
6
 
@@ -1,15 +1,15 @@
1
1
  # Extension Examples
2
2
 
3
- Example extensions for pi-coding-agent.
3
+ Example extensions for Atomic.
4
4
 
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
8
  # Load an extension with --extension flag
9
- pi --extension examples/extensions/permission-gate.ts
9
+ atomic --extension examples/extensions/permission-gate.ts
10
10
 
11
11
  # Or copy to extensions directory for auto-discovery
12
- cp permission-gate.ts ~/.pi/agent/extensions/
12
+ cp permission-gate.ts ~/.atomic/agent/extensions/
13
13
  ```
14
14
 
15
15
  ## Examples
@@ -23,6 +23,8 @@ cp permission-gate.ts ~/.pi/agent/extensions/
23
23
  | `confirm-destructive.ts` | Confirms before destructive session actions (clear, switch, fork) |
24
24
  | `dirty-repo-guard.ts` | Prevents session changes with uncommitted git changes |
25
25
  | `sandbox/` | OS-level sandboxing using `@anthropic-ai/sandbox-runtime` with per-project config |
26
+ | `project-trust.ts` | Demonstrates the `project_trust` event for user/global and CLI extensions |
27
+ | `gondolin/` | Route built-in tools and `!` commands into a Gondolin micro-VM |
26
28
 
27
29
  ### Custom Tools
28
30
 
@@ -75,6 +77,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
75
77
  | `reload-runtime.ts` | Adds `/reload-runtime` and `reload_runtime` tool showing safe reload flow |
76
78
  | `interactive-shell.ts` | Run interactive commands (vim, htop) with full terminal via `user_bash` hook |
77
79
  | `inline-bash.ts` | Expands `!{command}` patterns in prompts via `input` event transformation |
80
+ | `input-transform-streaming.ts` | Skips expensive input preprocessing for mid-stream steering via `streamingBehavior` |
78
81
 
79
82
  ### Git Integration
80
83
 
@@ -96,7 +99,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
96
99
 
97
100
  | Extension | Description |
98
101
  |-----------|-------------|
99
- | `mac-system-theme.ts` | Syncs pi theme with macOS dark/light mode |
102
+ | `mac-system-theme.ts` | Syncs Atomic theme with macOS dark/light mode |
100
103
 
101
104
  ### Resources
102
105
 
@@ -123,7 +126,7 @@ cp permission-gate.ts ~/.pi/agent/extensions/
123
126
  | Extension | Description |
124
127
  |-----------|-------------|
125
128
  | `custom-provider-anthropic/` | Custom Anthropic provider with OAuth support and custom streaming implementation |
126
- | `custom-provider-gitlab-duo/` | GitLab Duo provider using pi-ai's built-in Anthropic/OpenAI streaming via proxy |
129
+ | `custom-provider-gitlab-duo/` | GitLab Duo provider using `@earendil-works/pi-ai`'s built-in Anthropic/OpenAI streaming via proxy |
127
130
 
128
131
  ### External Dependencies
129
132