@bastani/atomic 0.9.28 → 0.9.29-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 (108) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  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 +6 -0
  8. package/dist/builtin/workflows/package.json +1 -1
  9. package/dist/builtin/workflows/src/extension/index.bundle.mjs +2 -2
  10. package/dist/builtin/workflows/src/index.js +2 -2
  11. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  12. package/dist/core/agent-session-extension-bindings.js +2 -4
  13. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  14. package/dist/core/agent-session-methods.d.ts +4 -1
  15. package/dist/core/agent-session-methods.d.ts.map +1 -1
  16. package/dist/core/agent-session-methods.js.map +1 -1
  17. package/dist/core/agent-session-state.d.ts +7 -0
  18. package/dist/core/agent-session-state.d.ts.map +1 -1
  19. package/dist/core/agent-session-state.js +31 -0
  20. package/dist/core/agent-session-state.js.map +1 -1
  21. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  22. package/dist/core/agent-session-tool-registry.js +2 -6
  23. package/dist/core/agent-session-tool-registry.js.map +1 -1
  24. package/dist/core/builtin-resource-loader.d.ts.map +1 -1
  25. package/dist/core/builtin-resource-loader.js +8 -0
  26. package/dist/core/builtin-resource-loader.js.map +1 -1
  27. package/dist/core/execution-model-router.d.ts +1 -1
  28. package/dist/core/execution-model-router.d.ts.map +1 -1
  29. package/dist/core/execution-model-router.js +37 -15
  30. package/dist/core/execution-model-router.js.map +1 -1
  31. package/dist/core/model-routing-constraints.d.ts +2 -0
  32. package/dist/core/model-routing-constraints.d.ts.map +1 -1
  33. package/dist/core/model-routing-constraints.js.map +1 -1
  34. package/dist/core/resource-loader-core.d.ts.map +1 -1
  35. package/dist/core/resource-loader-core.js +3 -2
  36. package/dist/core/resource-loader-core.js.map +1 -1
  37. package/dist/core/service-tier-warnings.d.ts +4 -0
  38. package/dist/core/service-tier-warnings.d.ts.map +1 -0
  39. package/dist/core/service-tier-warnings.js +7 -0
  40. package/dist/core/service-tier-warnings.js.map +1 -0
  41. package/dist/core/settings-manager-basic-accessors.js +8 -4
  42. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  43. package/dist/core/settings-types.d.ts +5 -1
  44. package/dist/core/settings-types.d.ts.map +1 -1
  45. package/dist/core/settings-types.js.map +1 -1
  46. package/dist/core/slash-commands.d.ts.map +1 -1
  47. package/dist/core/slash-commands.js +1 -0
  48. package/dist/core/slash-commands.js.map +1 -1
  49. package/dist/core/tool-selection.d.ts +18 -0
  50. package/dist/core/tool-selection.d.ts.map +1 -1
  51. package/dist/core/tool-selection.js +20 -0
  52. package/dist/core/tool-selection.js.map +1 -1
  53. package/dist/core/tool-status.d.ts +61 -0
  54. package/dist/core/tool-status.d.ts.map +1 -0
  55. package/dist/core/tool-status.js +156 -0
  56. package/dist/core/tool-status.js.map +1 -0
  57. package/dist/extensions/default-builtins.d.ts +4 -0
  58. package/dist/extensions/default-builtins.d.ts.map +1 -0
  59. package/dist/extensions/default-builtins.js +14 -0
  60. package/dist/extensions/default-builtins.js.map +1 -0
  61. package/dist/extensions/index.d.ts.map +1 -1
  62. package/dist/extensions/index.js +2 -6
  63. package/dist/extensions/index.js.map +1 -1
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +1 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/modes/interactive/interactive-input-handling.js +5 -0
  69. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  70. package/dist/modes/interactive/interactive-mode-surface.d.ts +1 -0
  71. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  72. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  73. package/dist/modes/interactive/interactive-render-chat.js +10 -0
  74. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  75. package/dist/modes/interactive/interactive-slash-commands.js +22 -0
  76. package/dist/modes/interactive/interactive-slash-commands.js.map +1 -1
  77. package/dist/modes/interactive-engine/isolated-runtime.d.ts +3 -0
  78. package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
  79. package/dist/modes/interactive-engine/isolated-runtime.js +4 -0
  80. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  81. package/dist/modes/print-mode.d.ts.map +1 -1
  82. package/dist/modes/print-mode.js +5 -0
  83. package/dist/modes/print-mode.js.map +1 -1
  84. package/dist/modes/rpc/rpc-client-api.d.ts +3 -0
  85. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  86. package/dist/modes/rpc/rpc-client-api.js +4 -0
  87. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  88. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  89. package/dist/modes/rpc/rpc-command-handler.js +3 -0
  90. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  91. package/dist/modes/rpc/rpc-types.d.ts +10 -0
  92. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  93. package/dist/modes/rpc/rpc-types.js.map +1 -1
  94. package/docs/codemode.md +1 -1
  95. package/docs/models/model-selection.md +1 -1
  96. package/docs/models/reference.md +2 -2
  97. package/docs/providers.md +6 -4
  98. package/docs/rpc/protocol.md +61 -0
  99. package/docs/sdk/reference.md +45 -0
  100. package/docs/settings.md +18 -6
  101. package/docs/subagents/reference.md +4 -4
  102. package/docs/tools.md +4 -0
  103. package/docs/usage.md +1 -0
  104. package/docs/workflows/api-reference.md +4 -4
  105. package/docs/workflows/authoring.md +4 -0
  106. package/docs/workflows/operations.md +1 -1
  107. package/npm-shrinkwrap.json +32 -32
  108. package/package.json +3 -3
@@ -138,7 +138,7 @@ Rates are per million tokens. `openai/gpt-6-astra-fast` and `openai-codex/gpt-6-
138
138
  | Up to 272,000 | $60 | $6 | $75 | $300 |
139
139
  | Above 272,000 | $120 | $12 | $150 | $450 |
140
140
 
141
- GPT-6 Astra is the only model with an Ultrafast choice. Auto routing requires an Ultrafast choice's exact ID in `modelConstraints.allowedModels`. Base-model evaluations are reused through explicit route metadata, with no separate Ultrafast benchmark or latency score. See [Fast and Ultrafast tiers](/providers#fast-and-ultrafast-tiers).
141
+ GPT-6 Astra and GPT-6.1 Sol have an Ultrafast choice on Codex and the OpenAI API; GPT-5.6 Sol has one on the OpenAI API only. Auto routing requires an Ultrafast choice's exact ID in `modelConstraints.allowedModels`. Base-model evaluations are reused through explicit route metadata, with no separate Ultrafast benchmark or latency score. See [Fast and Ultrafast tiers](/providers#fast-and-ultrafast-tiers).
142
142
 
143
143
  Amazon Bedrock exposes `openai.gpt-6-astra`, `global.openai.gpt-6-astra`, and `us.openai.gpt-6-astra` through the `amazon-bedrock` provider. These entries keep the same 272,000 input and 128,000 output limits, text and image input, and five reasoning levels; Atomic sends the selected effort as Bedrock's OpenAI `reasoning_effort` field. They do not get Fast or OpenAI tool-search metadata. Atomic sends each Bedrock ID unchanged and records all four price fields as zero because AWS had not published Astra pricing. Zero means unknown here, not free.
144
144
 
@@ -161,7 +161,7 @@ Atomic uses a 272,000-token default context and a 128,000-token output limit. In
161
161
 
162
162
  Rates are per million tokens. Derived `openai/gpt-6.1-sol-fast` and `openai-codex/gpt-6.1-sol-fast` choices send the same upstream model with priority routing. Fast costs twice the applicable rates; do not pre-multiply catalog costs. [Published evaluations](/models/evals) describe base-model results and measured efforts, not separate Fast measurements.
163
163
 
164
- GPT-6.1 Sol supports Standard and Fast only; it has no `-ultrafast` choice. See [provider tier caveats](/providers#fast-models).
164
+ `openai/gpt-6.1-sol-ultrafast` (also `openai-api/` and `openai-codex/`) sends `service_tier: "ultrafast"`. It uses OpenAI's published Ultrafast rates of $12 input and $60 output per million tokens up to 272,000 input tokens. OpenAI has not listed Sol Ultrafast cache rates, so Atomic derives them at the same six-times-Standard ratio: $0.60 cached input and $15 cache write. Above 272,000 input tokens, the usual long-context multipliers apply. See [provider tier caveats](/providers#fast-models).
165
165
 
166
166
  ### Image Input Limits
167
167
 
package/docs/providers.md CHANGED
@@ -75,6 +75,8 @@ For example, sign in with ChatGPT under `openai` and select `openai/gpt-6-sol` f
75
75
 
76
76
  The GPT-6 Luna Decisions classifier (`openai-decisions`) has no login of its own. It uses the key saved for `openai-api`, then a key saved for `openai`, then `OPENAI_API_KEY`, and ignores a ChatGPT sign-in. See [Classifier models](/models#use-classifier-models).
77
77
 
78
+ OpenAI's image models (`openai-images`, such as `openai-images/gpt-image-2.5-flare`) work the same way: no login of their own, the key saved for `openai-api`, then a key saved for `openai`, then `OPENAI_API_KEY`. A ChatGPT sign-in cannot call OpenAI's Images API and is skipped, so with only that sign-in the image models stay unavailable until you save an API key with `/login openai-api` or set `OPENAI_API_KEY`. Generate images with them from [codemode](/codemode#generate-images) or a [workflow tool step](/workflows/authoring#generate-an-image-artifact-in-a-durable-tool-step).
79
+
78
80
  ### OpenAI Codex
79
81
 
80
82
  - Requires ChatGPT Plus or Pro subscription
@@ -90,20 +92,20 @@ GPT-6 Sol and GPT-6 Luna are selectable as `openai/gpt-6-sol`, `openai/gpt-6-lun
90
92
 
91
93
  GPT-6.1 Sol is selectable as `openai/gpt-6.1-sol` and `openai-codex/gpt-6.1-sol`, with derived `-fast` siblings. For example, `atomic --model openai-codex/gpt-6.1-sol-fast:high` selects priority routing at high effort. Sol 6.1 supports `low`, `medium`, `high`, `xhigh`, and `max`; Codex's `minimal` UI alias also sends `low`. `off` is unavailable. See [Sol 6.1 limits and pricing](/models/reference#gpt-6-1-sol).
92
94
 
93
- GPT-6.1 Sol supports Standard and Fast only, so it has no `-ultrafast` choice yet.
95
+ GPT-6.1 Sol also has an `-ultrafast` choice on `openai/*`, `openai-api/*` and `openai-codex/*`, for example `openai/gpt-6.1-sol-ultrafast`.
94
96
 
95
97
  #### Fast and Ultrafast tiers
96
98
 
97
99
  Atomic offers a tier only for the models that advertise it, following Codex's per-model catalog and OpenAI's published API pricing:
98
100
 
99
101
  - **Fast** (`service_tier: priority`): every Codex model except `gpt-5.3-codex-spark`, and the `openai/*` models listed on OpenAI's [Fast pricing](https://developers.openai.com/api/docs/pricing) tab. For example, `openai/gpt-5-nano`, `openai/gpt-5-pro`, and `openai/o1` have no `-fast` choice.
100
- - **Ultrafast** (`service_tier: "ultrafast"`): GPT-6 Astra only, as `openai/gpt-6-astra-ultrafast` and `openai-codex/gpt-6-astra-ultrafast`. For example, `atomic --model openai-codex/gpt-6-astra-ultrafast:high`. Each keeps the base upstream model, following [Codex's tier-switching protocol](https://github.com/openai/codex/blob/17a9df60e420b58e3edc55efb1bb052e39492bbc/codex-rs/core/tests/suite/agent_websocket.rs#L518-L605). GPT-5.6 Sol Ultrafast is an API-only limited preview without published rates, so Atomic does not offer it.
102
+ - **Ultrafast** (`service_tier: "ultrafast"`): GPT-6 Astra and GPT-6.1 Sol, as `openai/gpt-6-astra-ultrafast` and `openai/gpt-6.1-sol-ultrafast` (also under `openai-api/` and `openai-codex/`), plus GPT-5.6 Sol on the OpenAI API, as `openai/gpt-5.6-sol-ultrafast` (also under `openai-api/`). For example, `atomic --model openai-codex/gpt-6-astra-ultrafast:high`. Each keeps the base upstream model, following [Codex's tier-switching protocol](https://github.com/openai/codex/blob/17a9df60e420b58e3edc55efb1bb052e39492bbc/codex-rs/core/tests/suite/agent_websocket.rs#L518-L605). GPT-5.6 Sol Ultrafast is a limited preview, so most accounts get the fallback below.
101
103
 
102
- Listing a choice does not establish that your account has access. Ultrafast needs an eligible plan: Pro $500 or an eligible Enterprise or Edu workspace with ChatGPT sign-in. With an API key, it is available at low default rate limits. If the provider rejects a Fast or Ultrafast choice you selected, the request fails; Atomic does not retry it at a lower tier.
104
+ Listing a choice does not establish that your account has access. Ultrafast needs an eligible plan: Pro $500 or an eligible Enterprise or Edu workspace with ChatGPT sign-in. With an API key, it is available at low default rate limits. If the OpenAI API or the Codex backend rejects the tier of a Fast or Ultrafast choice you selected (for example `400 Invalid service_tier argument`), Atomic retries once at the default tier and warns that the choice ran at default instead of failing the turn. Other providers still fail the request. With an OpenAI API key, Atomic also warns when the response reports a slower tier than the one you chose. With ChatGPT sign-in, on `openai/*` or `openai-codex/*`, it does not, because the ChatGPT backend reports `default` for every tier, including Fast and Ultrafast requests it ran at that tier. The warning, for example `ultrafast isn't available for gpt-5.6-sol on this account; ran at default`, appears under the response in interactive sessions, on stderr in print mode, and in the message `diagnostics` in JSON mode.
103
105
 
104
106
  A tier the model does not advertise is left out of the request, which then runs at standard processing instead of failing. `flex` is always passed through. With Codex, an explicit `default` sends no tier, as in Codex. With the OpenAI API, `default` is sent as requested.
105
107
 
106
- Usage is priced at the tier that served the request, using OpenAI's published per-model rates. Fast is usually twice Standard but varies by model: GPT-5.5 is 2.5x and GPT-4o is 1.7x. GPT-6 Astra Ultrafast is six times Standard: $60 input, $6 cached input, $75 cache write, and $300 output per million tokens up to 272,000 input tokens, and $120, $12, $150, and $450 above that ([OpenAI API pricing](https://developers.openai.com/api/docs/pricing?latest-pricing=ultrafast)). If OpenAI serves a Fast request at Standard, the usage is priced at Standard. With ChatGPT sign-in, [Codex](https://developers.openai.com/codex/speed) counts Fast against included limits at 2.5x and Astra Ultrafast at 8x, and bills purchased credits at 2x and 6x. Atomic's displayed cost uses the API rates.
108
+ Usage is priced at the tier that served the request, using OpenAI's published per-model rates. Fast is usually twice Standard but varies by model: GPT-5.5 is 2.5x and GPT-4o is 1.7x. GPT-6 Astra Ultrafast is six times Standard: $60 input, $6 cached input, $75 cache write, and $300 output per million tokens up to 272,000 input tokens, and $120, $12, $150, and $450 above that ([OpenAI API pricing](https://developers.openai.com/api/docs/pricing?latest-pricing=ultrafast)). GPT-6.1 Sol Ultrafast uses OpenAI's published $12 input and $60 output; its cache rates are not listed, so Atomic derives them at six times Standard. GPT-5.6 Sol Ultrafast has no published rates, so Atomic prices it at six times Standard ($24 input, $120 output). With an API key, if OpenAI reports a Fast request as served at Standard, the usage is priced at Standard; with ChatGPT sign-in it is priced at the tier you chose. With ChatGPT sign-in, [Codex](https://developers.openai.com/codex/speed) counts Fast against included limits at 2.5x and Astra Ultrafast at 8x, and bills purchased credits at 2x and 6x. Atomic's displayed cost uses the API rates.
107
109
 
108
110
  Auto routing excludes Ultrafast unless `modelConstraints.allowedModels` explicitly lists its exact provider/model ID. When explicitly allowed, it uses base-model benchmark evidence without an additional latency bonus. Normal and Fast choices keep their existing automatic selection behavior. Pick a fast identity only when the latency reduction is worth the higher usage and price.
109
111
 
@@ -643,6 +643,67 @@ Response:
643
643
 
644
644
  `latestAssistantUsage` is the usage reported by the newest assistant message, including an aborted or failed one; the interactive footer reads its cache-hit rate from it. It is omitted until the session has an assistant message.
645
645
 
646
+ #### get_tools
647
+
648
+ Explain which tools are active, which are not and why, and which selected tool names nothing registered.
649
+
650
+ ```json
651
+ {"type": "get_tools"}
652
+ ```
653
+
654
+ Response, shortened to three tools, for `"defaultTools": ["+codemode", "+tool-search"]`:
655
+ ```json
656
+ {
657
+ "type": "response",
658
+ "command": "get_tools",
659
+ "success": true,
660
+ "data": {
661
+ "selection": {
662
+ "source": "default-tools-setting",
663
+ "description": "the defaultTools setting",
664
+ "names": ["read", "bash", "kill", "edit", "write", "find", "search", "ask_user_question", "todo", "codemode", "tool-search"],
665
+ "modifiers": []
666
+ },
667
+ "tools": [
668
+ {
669
+ "name": "read",
670
+ "active": true,
671
+ "exposure": "direct",
672
+ "source": "built-in",
673
+ "sourcePath": "<builtin:read>",
674
+ "summary": "Read files, directories, archives, SQLite databases, internal resources, images, documents, and URLs through one path string."
675
+ },
676
+ {
677
+ "name": "codemode",
678
+ "active": true,
679
+ "exposure": "model-only",
680
+ "source": "built-in extension \"codemode\"",
681
+ "sourcePath": "builtin:codemode",
682
+ "summary": "Run JavaScript that composes tool calls in a fresh QuickJS worker sandbox."
683
+ },
684
+ {
685
+ "name": "tool_search",
686
+ "active": false,
687
+ "exposure": "model-only",
688
+ "source": "built-in extension \"tool-search\"",
689
+ "sourcePath": "builtin:tool-search",
690
+ "summary": "Search deferred tool metadata with BM25 and load matching tools for the next model call. Some tools may not have been provided upfront; use tool_search to discover currently registered tools.",
691
+ "inactiveReason": "opt-in; add \"+tool_search\" to defaultTools or --tools to enable it"
692
+ }
693
+ ],
694
+ "missing": [
695
+ {
696
+ "name": "tool-search",
697
+ "reason": "selected by the defaultTools setting, but no built-in, extension, SDK custom tool, or MCP server has registered a tool named \"tool-search\"; did you mean \"tool_search\"?"
698
+ }
699
+ ],
700
+ "excluded": []
701
+ }
702
+ }
703
+ ```
704
+
705
+ `selection.source` is one of `default-tools-setting`, `built-in-defaults`, `tools-allowlist`, `explicit-selection`, `no-built-in-tools`, or `no-tools`; `modifiers` lists `+name`/`-name` entries from `--tools`. `tools` lists active tools first. `excluded` lists selected names that `--exclude-tools` or a mode without user input removed. The command waits until extensions finish loading, so the report includes their tools. `RpcClient.getTools()` returns the same `data`.
706
+
646
707
  #### export_html
647
708
 
648
709
  Export session to an HTML file.
@@ -213,6 +213,49 @@ const { session } = await createAgentSession({
213
213
  });
214
214
  ```
215
215
 
216
+ #### Checking tool status
217
+
218
+ `session.getToolStatus()` explains a session's tools: where the tool selection came from, every registered tool with its exposure, owner, and active state, why each inactive tool is off and how to turn it on, selected names that an exclusion removed, and selected names that nothing registered, with a suggestion when a name looks like a typo. `formatToolStatus()` renders the report as plain text.
219
+
220
+ ```typescript
221
+ import { createAgentSession, formatToolStatus } from "@bastani/atomic";
222
+
223
+ const { session } = await createAgentSession({ tools: ["+codemode"] });
224
+ console.log(formatToolStatus(session.getToolStatus()));
225
+ ```
226
+
227
+ ```text
228
+ Tool selection: Atomic's default tool set with +codemode
229
+ read, bash, kill, edit, write, find, search, ask_user_question, todo, codemode
230
+
231
+ Active (17)
232
+ read direct built-in
233
+ bash direct built-in
234
+ kill direct built-in
235
+ edit direct built-in
236
+ write direct built-in
237
+ find direct built-in
238
+ search direct built-in
239
+ ask_user_question model-only built-in
240
+ todo direct built-in
241
+ workflow model-only bundled package "workflows"
242
+ subagent model-only bundled package "subagents"
243
+ web_search direct bundled package "web-access"
244
+ code_search direct bundled package "web-access"
245
+ fetch_content direct bundled package "web-access"
246
+ get_search_content direct bundled package "web-access"
247
+ intercom model-only bundled package "intercom"
248
+ codemode model-only built-in extension "codemode"
249
+
250
+ Inactive (2)
251
+ ls direct built-in
252
+ not selected; add "+ls" to defaultTools or --tools to enable it
253
+ tool_search model-only built-in extension "tool-search"
254
+ opt-in; add "+tool_search" to defaultTools or --tools to enable it
255
+ ```
256
+
257
+ Each entry in `tools` has `name`, `active`, `exposure`, `source`, `sourcePath`, `summary`, and, for inactive tools, `inactiveReason`. `missing` and `excluded` list `{ name, reason }` entries; a `missing` name usually means a typo or an extension that failed to load. Pass `{ heading, dim, warning }` functions as the second argument to `formatToolStatus()` to add terminal colors. Interactive mode shows the same report with `/tools`, and RPC clients request it with [`get_tools`](/rpc/protocol#get_tools).
258
+
216
259
  #### Bash tool behavior
217
260
 
218
261
  Atomic's built-in `bash` tool matches upstream pi: when `bash` is enabled, commands execute through the configured shell with the Atomic process permissions. Use `tools`, `excludedTools`, or `noTools` to decide whether a session exposes the `bash` tool at all. Atomic no longer provides a command-level allow/deny option for `bash`; use an operating-system/container sandbox or a custom tool/extension when you need command allowlisting or stronger isolation.
@@ -379,6 +422,8 @@ const { session } = await createAgentSession({ resourceLoader: loader });
379
422
 
380
423
  Named `extensionFactories` descriptors may set `builtin: true` to expose the factory as a synthetic `builtin:<name>` resource, and `replaceable: true` to omit it when another extension claims the same tool, command, or flag. Use `additionalExtensionPaths: ["builtin:codemode"]` for explicit built-in loading, including with `noExtensions: true`. These inline resource controls are separate from the companion-package `builtins` map. See [built-in extension resources](/extensions#built-in-extension-resources).
381
424
 
425
+ Every `DefaultResourceLoader` includes Atomic's inline built-in extensions, `codemode`, `tool-search`, and `llama.cpp`, so `defaultTools: ["+codemode"]` or `tools: ["+tool_search"]` works in SDK sessions without extra factories. `loader.getExtensions()` lists them with `builtin:<name>` paths. Registering them does not activate `codemode` or `tool_search`; both stay opt-in. A descriptor you pass with the same name replaces the default one, and `noExtensions: true`, `disabledBuiltinExtensions: ["codemode"]`, or an `extensions` setting such as `"-builtin:codemode"` turns one off.
426
+
382
427
  Pass `extensionBindings` to `createAgentSession()` to install `uiContext`, `mode`, `commandContextActions`, `shutdownHandler` or `onError` before startup hooks run. Creation awaits startup and resource discovery. A failed startup shuts down the partially created session before rejecting. Rebinding updates the host without replaying `session_start`; reload emits one start for its new generation. Remove manual post-creation startup calls from integrations that only used them to initialize extensions.
383
428
 
384
429
  Strict reloads (`failOnExtensionErrors: true`) require the loader's transactional `prepareReload()` support so a failed candidate cannot mutate live state before validation. `DefaultResourceLoader` provides that support. Custom loaders without it remain compatible with ordinary reloads, but strict reload fails before calling their mutating `reload()` method.
package/docs/settings.md CHANGED
@@ -56,7 +56,7 @@ See [Herdr](/herdr) for state aggregation, reporter conflicts, privacy, and Herd
56
56
  | `defaultProvider` | string | - | Startup provider, saved automatically when you switch models interactively |
57
57
  | `defaultModel` | string | - | Startup model ID, saved automatically when you switch models interactively |
58
58
  | `routerModel` | string | `""` | Inference model for workflow-stage and subagent `model: "auto"` selection only. An exact `provider/model` selects a registered chat or classifier model. `auto` and empty use the current chat model. Does not change chat or `structured_output` tool inference. |
59
- | `modelRouting` | object | `{}` | Provider filters for the models `model: "auto"` may select: `allowedProviders` and `excludedProviders` (provider ID arrays). Does not change `routerModel`. See [modelRouting](#modelrouting). |
59
+ | `modelRouting` | object | `{}` | Provider and model filters for the models `model: "auto"` may select: `allowedProviders` and `excludedProviders` (provider ID arrays), `allowedModels` and `excludedModels` (model ID or glob pattern arrays). Does not change `routerModel`. See [modelRouting](#modelrouting). |
60
60
  | `defaultThinkingLevel` | string | - | Startup thinking level, saved automatically on interactive model/thinking changes: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`; clamped to the active model's supported levels |
61
61
  | `modelThinkingLevels` | object | - | Per-model startup thinking levels keyed by `"provider/modelId"`; updated automatically on interactive model/thinking changes, or configured from `/settings` → Default thinking level per model |
62
62
  | `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
@@ -100,19 +100,31 @@ Remove secrets from routing tasks, inputs, and workflow descriptions/contracts b
100
100
  {
101
101
  "modelRouting": {
102
102
  "allowedProviders": ["github-copilot", "openai-codex", "anthropic"],
103
- "excludedProviders": ["openrouter"]
103
+ "excludedProviders": ["openrouter"],
104
+ "allowedModels": ["github-copilot/*", "anthropic/claude-sonnet-*", "openai-codex/gpt-5.5"],
105
+ "excludedModels": ["*-fast"]
104
106
  }
105
107
  }
106
108
  ```
107
109
 
108
- Limits which providers' models workflow stages and subagents with `model: "auto"` can be routed to. It filters the candidates only; the model that makes the decision is still `routerModel`.
110
+ Limits which models workflow stages and subagents with `model: "auto"` can be routed to, including builtin workflow stages that do not name a model. It filters the candidates only; the model that makes the decision is still `routerModel`.
109
111
 
110
112
  - `allowedProviders`: when nonempty, only models from these providers are candidates.
111
113
  - `excludedProviders`: models from these providers are never candidates, even if they are also allowed.
114
+ - `allowedModels`: when nonempty, only models matching one of these patterns are candidates.
115
+ - `excludedModels`: models matching any of these patterns are never candidates, even if they are also allowed.
112
116
 
113
- Use provider IDs as shown by `/model` or `workflow({ action: "models" })`, such as `github-copilot`, `openai-codex`, `anthropic`, or `openrouter`. For example, to route only to your subscriptions, exclude the API-billed providers you have configured. The Claude subscription and the Anthropic API both use the `anthropic` provider, so this setting cannot separate them.
117
+ Use provider IDs as shown by `/model` or `workflow({ action: "models" })`, such as `github-copilot`, `openai-codex`, `anthropic`, or `openrouter`. For example, to route only to your subscriptions, exclude the API-billed providers you have configured. The Claude subscription and the Anthropic API both use the `anthropic` provider, so the provider lists cannot separate them.
114
118
 
115
- A project list replaces the global list of the same name; the other list is kept. A subagent call or workflow stage that sets `modelConstraints.allowedProviders` or `excludedProviders`, for example because you asked for a provider, uses its own lists instead of these for that call. Provider lists in an agent definition or inherited from a parent workflow only narrow these settings; they never lift an exclusion. If the filters leave no eligible model, the stage or subagent fails before launch with an error that names this setting. A run resumed after you exclude a provider rejects a recorded selection from that provider instead of using it.
119
+ Model patterns use the same format as [`enabledModels`](#models) and `--models`: a full `provider/model` ID, a bare model ID, or a glob such as `anthropic/claude-*` or `*sonnet*`. Matching is case-insensitive. A pattern without glob characters selects a single model, so use a glob to cover a family. A thinking suffix such as `anthropic/claude-opus-4-8:high` selects the model only; it does not limit the effort `auto` chooses. A candidate must pass every list. Patterns that match no available model are ignored, so an allowlist whose patterns match nothing leaves no candidate.
120
+
121
+ A project list replaces the global list of the same name; the other lists are kept. A subagent call or workflow stage that sets `modelConstraints.allowedProviders` or `excludedProviders`, for example because you asked for a provider, uses its own lists instead of the provider lists here for that call. `allowedModels` and `excludedModels` always apply, and the call's own `modelConstraints.allowedModels` can only narrow them. Provider lists in an agent definition or inherited from a parent workflow only narrow these settings; they never lift an exclusion. The filters apply to the routed model, its ranked fallbacks, any other fallback models the stage or subagent would try, and the current chat model that `auto` falls back to when routing fails, so none of them can run a filtered-out model.
122
+
123
+ If the filters leave no eligible model, the stage or subagent fails before launch with an error that names the settings involved, such as `modelRouting.allowedModels`; Atomic never falls back to the unfiltered catalog. A run resumed after you exclude a provider or model rejects a recorded selection that is no longer allowed instead of using it.
124
+
125
+ These settings apply only to `model: "auto"`. A stage, task, or agent that names a concrete model, and its explicit fallback models, run as written.
126
+
127
+ To audit a routing decision, the stage's `routerSelection` in `workflow({ action: "status", runId })` lists the chosen model and fallbacks, and `candidates` lists every model the router could choose from after these filters and the task's image and long-context needs. Subagent results carry the same `routerSelection`.
116
128
 
117
129
  #### thinkingBudgets
118
130
 
@@ -161,7 +173,7 @@ Fallback entries should be fully qualified `provider/model` ids. Add a reasoning
161
173
 
162
174
  Fallback attempts are visible as model changes in the session transcript and as a fallback status in the UI. Switching providers can change latency, billing, data-handling terms, and subscription/credit usage. Configure only providers you are comfortable sending the current conversation and tool context to.
163
175
 
164
- `enabledModels` is separate: it only controls the interactive Ctrl+P model cycle list and is not used as an implicit fallback chain.
176
+ `enabledModels` is separate: it only controls the interactive Ctrl+P model cycle list and is not used as an implicit fallback chain. It does not limit `model: "auto"` routing; use [`modelRouting.allowedModels`](#modelrouting) for that.
165
177
 
166
178
  ### Fast models
167
179
 
@@ -38,7 +38,7 @@ subagent({
38
38
  })
39
39
  ```
40
40
 
41
- `work` is one of `computer_use`, `coding`, `code_review`, `codebase_lookup`, `research`, `business_workflow`, `math_science` or `writing`; `difficulty` is `trivial`, `easy`, `moderate`, `hard` or `very_hard`; `mistakeCost` is `negligible`, `low`, `moderate`, `high` or `severe`; `needsImages`, `longContext` (must hold a very large codebase or document set) and `latencySensitive` (a fast answer matters more than extra reasoning) are true or false. When listing candidates, prefer the user's subscription models over pay-per-token API models unless the user asked for API models or has none. To keep providers out of routing entirely, use the [`modelRouting`](/settings#modelrouting) setting. You do not need to attach eval records yourself.
41
+ `work` is one of `computer_use`, `coding`, `code_review`, `codebase_lookup`, `research`, `business_workflow`, `math_science` or `writing`; `difficulty` is `trivial`, `easy`, `moderate`, `hard` or `very_hard`; `mistakeCost` is `negligible`, `low`, `moderate`, `high` or `severe`; `needsImages`, `longContext` (must hold a very large codebase or document set) and `latencySensitive` (a fast answer matters more than extra reasoning) are true or false. When listing candidates, prefer the user's subscription models over pay-per-token API models unless the user asked for API models or has none. To keep providers or models out of routing entirely, use the [`modelRouting`](/settings#modelrouting) setting. You do not need to attach eval records yourself.
42
42
 
43
43
  The agent's system prompt is not routing metadata. For a self-contained agent with no task, it remains the task fallback. The router weighs task-relevant evidence, cost, and latency rather than always choosing a benchmark winner or maximum effort. Benchmark measurement effort does not prescribe execution effort.
44
44
 
@@ -53,7 +53,7 @@ Saved classifier credentials do not change this order. Neither routing nor child
53
53
 
54
54
  Routing has no built-in wall-clock deadline. Slow decisions can finish; cancel the request to stop waiting. Independent provider and credential-preparation limits still apply. Chat routing gets an initial attempt plus three corrective retries for malformed or schema-invalid answers. A classifier routing failure switches to the current chat model instead of repeating the classifier request. A valid answer stops repairs.
55
55
 
56
- The result records a primary `{ model, effort }` and up to two ordered `fallbacks`, each with its own model and effort. Atomic ranks three distinct eligible provider/model IDs, or all available IDs when fewer than three qualify. It selects each rank from the remaining models, excluding all efforts of earlier choices. A supported `"off"` is distinct from `null`, which means no configurable reasoning. The catalog reflects configured authentication, not proof of valid credentials, quota, or entitlement.
56
+ The result records a primary `{ model, effort }`, up to two ordered `fallbacks`, each with its own model and effort, and `candidates`, every model the router could choose from after the routing settings, constraints, and the task's image and long-context needs. Atomic ranks three distinct eligible provider/model IDs, or all available IDs when fewer than three qualify. It selects each rank from the remaining models, excluding all efforts of earlier choices. A supported `"off"` is distinct from `null`, which means no configurable reasoning. The catalog reflects configured authentication, not proof of valid credentials, quota, or entitlement.
57
57
 
58
58
  The child does not start if no candidates are eligible, availability changes, or cancellation occurs. A classifier provider failure, missing credentials, unsupported classify operation, size rejection, or malformed answer switches to the current chat model. Transient provider failures are retried up to three times first. The chat model gets its own output-repair allowance. If routing inference fails completely, or Atomic cannot pick a model itself (the task needs images and no eligible model reads them, `allowedModels` lists more models than one routing request holds (roughly 100), or `evals.md` is missing), the child runs on the current chat model instead of failing, but only when that model is available and satisfies every routing constraint (such as `allowedModels` and effort, cost, context and input limits). Otherwise the launch fails. Invalid inputs, conflicting constraints, cancellation and stale-catalog failures never trigger fallback. No partial decision can launch a child. These switches are silent unless `ATOMIC_MODEL_ROUTING_DEBUG=1` is set.
59
59
 
@@ -65,11 +65,11 @@ Choose a router provider permitted to receive the task and agent name/descriptio
65
65
 
66
66
  ### Hard model constraints
67
67
 
68
- For automatic routing, optional `modelConstraints` on a call, parallel task, or agent definition restricts eligible choices and execution fallbacks. All applicable restrictions must hold; a call cannot widen an agent's restrictions. Omit this object to use the full available catalog.
68
+ For automatic routing, optional `modelConstraints` on a call, parallel task, or agent definition restricts eligible choices and execution fallbacks. All applicable restrictions must hold; a call cannot widen an agent's restrictions. Omit this object to use the full available catalog that your [`modelRouting`](/settings#modelrouting) settings allow.
69
69
 
70
70
  | Field | Meaning |
71
71
  | --- | --- |
72
- | `allowedModels` | Exact provider/model IDs permitted to receive the task |
72
+ | `allowedModels` | Exact provider/model IDs permitted to receive the task. The [`modelRouting`](/settings#modelrouting) `allowedModels` and `excludedModels` settings still apply on top of this list |
73
73
  | `allowedProviders`, `excludedProviders` | Provider IDs whose models may or may never be used. On a call, setting either replaces the [`modelRouting`](/settings#modelrouting) provider settings for that call, so set them only when the user asks; in an agent definition they only narrow those settings |
74
74
  | `maxInputCost`, `maxOutputCost` | Maximum catalog price in USD per million input or output tokens, not a total spending cap |
75
75
  | `minContextWindow` | Minimum advertised context window in tokens |
package/docs/tools.md CHANGED
@@ -14,6 +14,8 @@ Atomic enables these coding tools in normal sessions by default: `read`, `write`
14
14
 
15
15
  Bundled integrations also provide [public repository search](#code_search), [web fetching](/web-access), and [MCP tools](/mcp-servers).
16
16
 
17
+ Run `/tools` to see which tools are active, why the others are off and how to turn them on, and which selected names nothing registered, such as a misspelled `defaultTools` entry. SDK and RPC clients get the same report from [`session.getToolStatus()`](/sdk/reference#checking-tool-status) and [`get_tools`](/rpc/protocol#get_tools).
18
+
17
19
  ## `codemode`
18
20
 
19
21
  Codemode lets the model compose permitted tool calls in JavaScript and return only the useful output. It is shipped but inactive by default. Enable it alongside Atomic's defaults in `~/.atomic/agent/settings.json` or project `.atomic/settings.json`:
@@ -32,6 +34,8 @@ const files = await Promise.all([
32
34
  return files.map((text) => text.slice(0, 1000));
33
35
  ```
34
36
 
37
+ Codemode works with every provider. On models that support OpenAI grammar tools, Atomic also constrains its input to JavaScript source; other models receive it as an ordinary tool. Run `/tools` to confirm it is active.
38
+
35
39
  Scripts run in a fresh QuickJS worker with a 256 MiB memory limit. They have no direct Node, filesystem, network, modules, timers, or credential globals. Tool calls still run with the session's permissions and can have real side effects; a failed script does **not** undo them. Workflow, subagent, Intercom/supervisor, and user-question tools are model-only and cannot be called from scripts. Allowlists, exclusions, and deactivated direct MCP tools remain authoritative.
36
40
 
37
41
  Use `text(value)`, `image(dataUrlOrImageContent)`, `console.log(...)`, or a top-level return for output; `exit()` ends early. An optional first line configures output and a caller-owned deadline:
package/docs/usage.md CHANGED
@@ -76,6 +76,7 @@ Type `/` in the editor to open command completion. Extensions can register custo
76
76
  | `/new` | Start a new session |
77
77
  | `/name <name>` | Set session display name |
78
78
  | `/session` | Show session file, ID, messages, tokens, and cost |
79
+ | `/tools` | Show active tools, why other tools are off, and selected tool names that nothing registered |
79
80
  | `/tree` | Jump to any point in the session and continue from there |
80
81
  | `/fork` | Create a new session from a previous user message |
81
82
  | `/clone` | Duplicate the current active branch into a new session |
@@ -546,7 +546,7 @@ Selects the primary stage model. String values can carry the reasoning suffix de
546
546
 
547
547
  Use `model: "auto"` for prompt-based selection of an execution model and supported effort before session admission. Omission and concrete models keep prior behavior. Shared chain/parallel defaults accept `auto`; each stage receives one decision. The router sees the final supplied prompt, including interpolated inputs and supplied task context, rather than only a stage name.
548
548
 
549
- `modelConstraints?: ModelConstraints` applies hard restrictions to auto selection and execution/compaction fallbacks. Fields are `allowedModels?: string[]`, `allowedProviders?: string[]`, `excludedProviders?: string[]`, `allowedEfforts?: (string | null)[]`, `maxInputCost?: number`, `maxOutputCost?: number`, `minContextWindow?: number`, and `requiredInputs?: ("text" | "image")[]`. Model IDs are exact provider/model IDs. Costs are catalog USD per million tokens including pricing tiers, not task budgets. Efforts are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, or `null` for nonreasoning models. Empty allowlists admit nothing. Inherited and stage restrictions all hold; a task override cannot widen shared restrictions. An authored `thinkingLevel` restricts auto selection to that supported effort.
549
+ `modelConstraints?: ModelConstraints` applies hard restrictions to auto selection and execution/compaction fallbacks. Fields are `allowedModels?: string[]`, `allowedProviders?: string[]`, `excludedProviders?: string[]`, `allowedEfforts?: (string | null)[]`, `maxInputCost?: number`, `maxOutputCost?: number`, `minContextWindow?: number`, and `requiredInputs?: ("text" | "image")[]`. Model IDs are exact provider/model IDs. Costs are catalog USD per million tokens including pricing tiers, not task budgets. Efforts are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, or `null` for nonreasoning models. Empty allowlists admit nothing. Inherited and stage restrictions all hold; a task override cannot widen shared restrictions. An authored `thinkingLevel` restricts auto selection to that supported effort. The user's [`modelRouting.allowedModels` and `excludedModels`](/settings#modelrouting) settings also apply to every auto stage, including builtin stages that omit a model; `modelConstraints` cannot widen them, and a stage whose filters leave no eligible model fails before launch.
550
550
 
551
551
  An auto stage has no session until it receives prompt text. Model-dependent operations such as `compact()` or `cycleModel()` before that point report an error; call `prompt()` first or deliberately select a concrete model with `setModel()`. Session operations after admission keep their normal behavior.
552
552
 
@@ -569,7 +569,7 @@ Rejected credentials, unavailable models, and incompatible requests advance imme
569
569
 
570
570
  A stage that sets `model` or `fallbackModels` uses only the chain it declares. The `fallbackModels` list in `settings.json` and the chain of the session that launched the workflow never apply to it, so a provider failure cannot move the stage to a model or reasoning level outside the chain you declared. A stage that sets neither runs on the session's current model and keeps that session's fallback behavior.
571
571
 
572
- Explicit stage models can use numbered account providers such as `openai-1/gpt-5-mini:low` with credentials saved under `openai-1` in `auth.json`. They use the source provider's catalog and endpoint, but the account's own credentials; no alias entry in `models.json` or provider-registration extension is needed. The source provider and requested model must exist. `modelRouting.allowedProviders`, `modelRouting.excludedProviders`, and `routerModel` apply to `model: "auto"`, not to these pinned primary or fallback models.
572
+ Explicit stage models can use numbered account providers such as `openai-1/gpt-5-mini:low` with credentials saved under `openai-1` in `auth.json`. They use the source provider's catalog and endpoint, but the account's own credentials; no alias entry in `models.json` or provider-registration extension is needed. The source provider and requested model must exist. `modelRouting` (`allowedProviders`, `excludedProviders`, `allowedModels`, `excludedModels`) and `routerModel` apply to `model: "auto"`, not to these pinned primary or fallback models.
573
573
 
574
574
  A same-candidate retry resumes the existing turn when the transcript ends in a message the agent can continue from. Otherwise, it re-sends the stage prompt. In either case, Atomic removes the failed provider error from the live transcript and delivers the prompt exactly once.
575
575
 
@@ -1023,7 +1023,7 @@ interface WorkflowTaskResult extends WorkflowTaskContext {
1023
1023
  readonly artifacts?: readonly WorkflowArtifact[];
1024
1024
  readonly model?: string;
1025
1025
  readonly thinkingLevel?: string;
1026
- readonly routerSelection?: { readonly model: string; readonly effort: string | null };
1026
+ readonly routerSelection?: ModelRouterOutput;
1027
1027
  readonly attemptedModels?: readonly string[];
1028
1028
  readonly modelAttempts?: readonly WorkflowModelAttempt[];
1029
1029
  readonly warnings?: readonly string[];
@@ -1177,7 +1177,7 @@ interface StageSnapshot extends WorkflowSerializableObject {
1177
1177
 
1178
1178
  Programmatic `run(...)` returns this type. `exited` identifies `ctx.exit(...)` termination, and `stages` contains the final stage snapshots.
1179
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.
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: `model` and `effort`, ranked `fallbacks`, and `candidates`, the full IDs of every model the router could choose from after [`modelRouting`](/settings#modelrouting) and `modelConstraints` filtering. Selections recorded before `candidates` existed omit it. 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
1181
 
1182
1182
  ## Programmatic usage
1183
1183
 
@@ -826,6 +826,10 @@ export default workflow({
826
826
 
827
827
  Hand the path to a later chat stage in its prompt. Review generated assets before publishing them or citing them as evidence: a generated image is not proof of a real-world state.
828
828
 
829
+ To call OpenAI's Images API with your own OpenAI API key instead of OpenRouter, use an `openai-images` model, for example `models.getModelOfType("image", "openai-images", "gpt-image-2.5-flare")`. The provider also lists `gpt-image-2.5-sunburst`, `gpt-image-2`, `gpt-image-1.5`, `gpt-image-1`, and `gpt-image-1-mini`. It has no login of its own: it uses the key saved for `openai-api`, then a key saved for `openai`, then `OPENAI_API_KEY`. A ChatGPT sign-in cannot call the Images API, so with only that sign-in the call returns `stopReason: "error"` with `Provider is not configured: openai-images`. Text blocks become the prompt; add image blocks to `input` to edit them or use them as references. Pass `size`, `quality`, `n`, or `background` with the other options when you need them, for example `{ signal, size: "1024x1024", quality: "low" }`; each generated image is a separate image block in `result.output`.
830
+
831
+ `builtinModels()` covers Atomic's built-in image providers. For another image backend, register its implementation for a new `api` ID with `registerImagesApiProvider({ api, generateImages })` from `@bastani/pi-ai/compat`, then call `generateImages(model, context, { apiKey, signal })` from the same entry point with a model whose `api` matches. That call dispatches on `model.api` and does not look up credentials, so take `apiKey` from your own configuration, such as an environment variable, and never put it in workflow state or outputs. `@bastani/pi-ai/compat` is not one of the host-provided imports, so install `@bastani/pi-ai` next to the workflow. To make an image backend available to codemode and extensions through the session's model registry instead, register a provider with `images`; see [Custom providers](/custom-provider#quick-reference).
832
+
829
833
  ### Workflow Composition
830
834
 
831
835
  Use workflow composition when a workflow calls a reusable user-defined workflow from the project or package, or a bundled builtin workflow, and consumes its outputs as a tracked boundary stage. Import the child definition with a normal TypeScript import, then pass it directly to `ctx.workflow(workflowDefinition, options)`. `ctx.workflow(...)` does not accept registry names, path objects, or string aliases.
@@ -4,7 +4,7 @@ Run and operate workflows from interactive chat or the workflow tool: identify r
4
4
 
5
5
  ## Automatic stage models
6
6
 
7
- For stages authored with `model: "auto"`, Atomic ranks up to three distinct eligible models before starting execution. It tries them in order, retaining each selected effort, before remaining configured fallbacks and the current chat model. Duplicate model IDs are skipped. The primary model/effort and ordered `fallbacks` are retained as `routerSelection`, independently of the actual `model`, `thinkingLevel`, and attempts. Remaining unsuffixed fallbacks inherit the primary effort; explicit suffixes retain their own effort. Every automatic fallback must satisfy the stage's hard constraints.
7
+ For stages authored with `model: "auto"`, Atomic ranks up to three distinct eligible models before starting execution. It tries them in order, retaining each selected effort, before remaining configured fallbacks and the current chat model. Duplicate model IDs are skipped. The primary model/effort and ordered `fallbacks` are retained as `routerSelection`, independently of the actual `model`, `thinkingLevel`, and attempts, together with `candidates`, every model the router could choose from after filtering. Remaining unsuffixed fallbacks inherit the primary effort; explicit suffixes retain their own effort. Every automatic fallback must satisfy the stage's hard constraints and the [`modelRouting`](/settings#modelrouting) settings.
8
8
 
9
9
  Model selection uses the actual stage prompt, stage name, hard constraints, eligible model catalog, and compact dated evaluation records, not full human guides. Stage system and appended system prompts are not sent to the model router. Put requirements that should influence model selection in the stage prompt; the execution session still receives its configured system prompts.
10
10
 
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.28",
3
+ "version": "0.9.29-alpha.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bastani/atomic",
9
- "version": "0.9.28",
9
+ "version": "0.9.29-alpha.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@bastani/atomic-natives": "0.9.28",
13
- "@bastani/pi-ai": "0.9.28",
12
+ "@bastani/atomic-natives": "0.9.29-alpha.2",
13
+ "@bastani/pi-ai": "0.9.29-alpha.2",
14
14
  "@dbos-inc/dbos-sdk": "4.25.14",
15
15
  "@earendil-works/pi-agent-core": "1.1.0",
16
16
  "@earendil-works/pi-client": "1.1.0",
@@ -432,18 +432,18 @@
432
432
  }
433
433
  },
434
434
  "node_modules/@bastani/atomic-natives": {
435
- "version": "0.9.28",
436
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.28.tgz",
435
+ "version": "0.9.29-alpha.2",
436
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives/-/atomic-natives-0.9.29-alpha.2.tgz",
437
437
  "license": "MIT",
438
438
  "optionalDependencies": {
439
- "@bastani/atomic-natives-darwin-arm64": "0.9.28",
440
- "@bastani/atomic-natives-darwin-x64": "0.9.28",
441
- "@bastani/atomic-natives-linux-arm64-gnu": "0.9.28",
442
- "@bastani/atomic-natives-linux-arm64-musl": "0.9.28",
443
- "@bastani/atomic-natives-linux-x64-gnu": "0.9.28",
444
- "@bastani/atomic-natives-linux-x64-musl": "0.9.28",
445
- "@bastani/atomic-natives-win32-arm64-msvc": "0.9.28",
446
- "@bastani/atomic-natives-win32-x64-msvc": "0.9.28"
439
+ "@bastani/atomic-natives-darwin-arm64": "0.9.29-alpha.2",
440
+ "@bastani/atomic-natives-darwin-x64": "0.9.29-alpha.2",
441
+ "@bastani/atomic-natives-linux-arm64-gnu": "0.9.29-alpha.2",
442
+ "@bastani/atomic-natives-linux-arm64-musl": "0.9.29-alpha.2",
443
+ "@bastani/atomic-natives-linux-x64-gnu": "0.9.29-alpha.2",
444
+ "@bastani/atomic-natives-linux-x64-musl": "0.9.29-alpha.2",
445
+ "@bastani/atomic-natives-win32-arm64-msvc": "0.9.29-alpha.2",
446
+ "@bastani/atomic-natives-win32-x64-msvc": "0.9.29-alpha.2"
447
447
  },
448
448
  "engines": {
449
449
  "bun": ">=1.4.2",
@@ -451,8 +451,8 @@
451
451
  }
452
452
  },
453
453
  "node_modules/@bastani/atomic-natives-darwin-arm64": {
454
- "version": "0.9.28",
455
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.28.tgz",
454
+ "version": "0.9.29-alpha.2",
455
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-arm64/-/atomic-natives-darwin-arm64-0.9.29-alpha.2.tgz",
456
456
  "license": "MIT",
457
457
  "os": [
458
458
  "darwin"
@@ -463,8 +463,8 @@
463
463
  "optional": true
464
464
  },
465
465
  "node_modules/@bastani/atomic-natives-darwin-x64": {
466
- "version": "0.9.28",
467
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.28.tgz",
466
+ "version": "0.9.29-alpha.2",
467
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-darwin-x64/-/atomic-natives-darwin-x64-0.9.29-alpha.2.tgz",
468
468
  "license": "MIT",
469
469
  "os": [
470
470
  "darwin"
@@ -475,8 +475,8 @@
475
475
  "optional": true
476
476
  },
477
477
  "node_modules/@bastani/atomic-natives-linux-arm64-gnu": {
478
- "version": "0.9.28",
479
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.28.tgz",
478
+ "version": "0.9.29-alpha.2",
479
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-gnu/-/atomic-natives-linux-arm64-gnu-0.9.29-alpha.2.tgz",
480
480
  "license": "MIT",
481
481
  "os": [
482
482
  "linux"
@@ -490,8 +490,8 @@
490
490
  "optional": true
491
491
  },
492
492
  "node_modules/@bastani/atomic-natives-linux-arm64-musl": {
493
- "version": "0.9.28",
494
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.28.tgz",
493
+ "version": "0.9.29-alpha.2",
494
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-arm64-musl/-/atomic-natives-linux-arm64-musl-0.9.29-alpha.2.tgz",
495
495
  "license": "MIT",
496
496
  "os": [
497
497
  "linux"
@@ -505,8 +505,8 @@
505
505
  "optional": true
506
506
  },
507
507
  "node_modules/@bastani/atomic-natives-linux-x64-gnu": {
508
- "version": "0.9.28",
509
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.28.tgz",
508
+ "version": "0.9.29-alpha.2",
509
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-gnu/-/atomic-natives-linux-x64-gnu-0.9.29-alpha.2.tgz",
510
510
  "license": "MIT",
511
511
  "os": [
512
512
  "linux"
@@ -520,8 +520,8 @@
520
520
  "optional": true
521
521
  },
522
522
  "node_modules/@bastani/atomic-natives-linux-x64-musl": {
523
- "version": "0.9.28",
524
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.28.tgz",
523
+ "version": "0.9.29-alpha.2",
524
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-linux-x64-musl/-/atomic-natives-linux-x64-musl-0.9.29-alpha.2.tgz",
525
525
  "license": "MIT",
526
526
  "os": [
527
527
  "linux"
@@ -535,8 +535,8 @@
535
535
  "optional": true
536
536
  },
537
537
  "node_modules/@bastani/atomic-natives-win32-arm64-msvc": {
538
- "version": "0.9.28",
539
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.28.tgz",
538
+ "version": "0.9.29-alpha.2",
539
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-arm64-msvc/-/atomic-natives-win32-arm64-msvc-0.9.29-alpha.2.tgz",
540
540
  "license": "MIT",
541
541
  "os": [
542
542
  "win32"
@@ -547,8 +547,8 @@
547
547
  "optional": true
548
548
  },
549
549
  "node_modules/@bastani/atomic-natives-win32-x64-msvc": {
550
- "version": "0.9.28",
551
- "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.28.tgz",
550
+ "version": "0.9.29-alpha.2",
551
+ "resolved": "https://registry.npmjs.org/@bastani/atomic-natives-win32-x64-msvc/-/atomic-natives-win32-x64-msvc-0.9.29-alpha.2.tgz",
552
552
  "license": "MIT",
553
553
  "os": [
554
554
  "win32"
@@ -559,8 +559,8 @@
559
559
  "optional": true
560
560
  },
561
561
  "node_modules/@bastani/pi-ai": {
562
- "version": "0.9.28",
563
- "resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.28.tgz",
562
+ "version": "0.9.29-alpha.2",
563
+ "resolved": "https://registry.npmjs.org/@bastani/pi-ai/-/pi-ai-0.9.29-alpha.2.tgz",
564
564
  "license": "MIT",
565
565
  "dependencies": {
566
566
  "@anthropic-ai/sdk": "0.129.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.9.28",
3
+ "version": "0.9.29-alpha.2",
4
4
  "description": "Atomic coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "atomicConfig": {
@@ -80,8 +80,8 @@
80
80
  "prepublishOnly": "bun run clean && bun run build && bun run shrinkwrap"
81
81
  },
82
82
  "dependencies": {
83
- "@bastani/atomic-natives": "0.9.28",
84
- "@bastani/pi-ai": "0.9.28",
83
+ "@bastani/atomic-natives": "0.9.29-alpha.2",
84
+ "@bastani/pi-ai": "0.9.29-alpha.2",
85
85
  "@dbos-inc/dbos-sdk": "4.25.14",
86
86
  "@earendil-works/pi-agent-core": "1.1.0",
87
87
  "@earendil-works/pi-client": "1.1.0",