@bastani/atomic 0.8.29-alpha.4 → 0.8.30-alpha.1

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 (144) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +6 -0
  3. package/dist/builtin/cursor/package.json +2 -2
  4. package/dist/builtin/intercom/CHANGELOG.md +7 -1
  5. package/dist/builtin/intercom/package.json +2 -2
  6. package/dist/builtin/mcp/CHANGELOG.md +7 -1
  7. package/dist/builtin/mcp/package.json +3 -3
  8. package/dist/builtin/subagents/CHANGELOG.md +11 -0
  9. package/dist/builtin/subagents/README.md +10 -30
  10. package/dist/builtin/subagents/package.json +4 -4
  11. package/dist/builtin/subagents/skills/subagent/SKILL.md +5 -11
  12. package/dist/builtin/subagents/src/agents/agent-management.ts +0 -5
  13. package/dist/builtin/subagents/src/agents/agent-serializer.ts +7 -3
  14. package/dist/builtin/subagents/src/agents/agents.ts +4 -29
  15. package/dist/builtin/subagents/src/agents/chain-serializer.ts +27 -25
  16. package/dist/builtin/subagents/src/extension/schemas.ts +0 -75
  17. package/dist/builtin/subagents/src/runs/background/async-execution.ts +0 -29
  18. package/dist/builtin/subagents/src/runs/background/run-status.ts +1 -2
  19. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +134 -239
  20. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +1 -52
  21. package/dist/builtin/subagents/src/runs/foreground/execution.ts +103 -94
  22. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +0 -10
  23. package/dist/builtin/subagents/src/runs/shared/dynamic-fanout.ts +16 -8
  24. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +0 -1
  25. package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +0 -3
  26. package/dist/builtin/subagents/src/runs/shared/structured-output.ts +67 -2
  27. package/dist/builtin/subagents/src/runs/shared/subagent-control.ts +6 -20
  28. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -1
  29. package/dist/builtin/subagents/src/runs/shared/workflow-graph.ts +2 -6
  30. package/dist/builtin/subagents/src/shared/settings.ts +1 -4
  31. package/dist/builtin/subagents/src/shared/types.ts +1 -156
  32. package/dist/builtin/subagents/src/tui/render.ts +0 -1
  33. package/dist/builtin/web-access/CHANGELOG.md +7 -1
  34. package/dist/builtin/web-access/package.json +2 -2
  35. package/dist/builtin/workflows/CHANGELOG.md +13 -0
  36. package/dist/builtin/workflows/README.md +2 -2
  37. package/dist/builtin/workflows/package.json +2 -2
  38. package/dist/builtin/workflows/src/extension/index.ts +8 -1
  39. package/dist/builtin/workflows/src/extension/wiring.ts +66 -10
  40. package/dist/builtin/workflows/src/runs/foreground/executor.ts +70 -19
  41. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +98 -14
  42. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +0 -1
  43. package/dist/builtin/workflows/src/shared/persistence-restore.ts +4 -0
  44. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  45. package/dist/builtin/workflows/src/shared/store.ts +2 -0
  46. package/dist/config.d.ts.map +1 -1
  47. package/dist/config.js +18 -4
  48. package/dist/config.js.map +1 -1
  49. package/dist/core/agent-session.d.ts +2 -2
  50. package/dist/core/agent-session.d.ts.map +1 -1
  51. package/dist/core/agent-session.js +21 -9
  52. package/dist/core/agent-session.js.map +1 -1
  53. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  54. package/dist/core/compaction/branch-summarization.js +2 -2
  55. package/dist/core/compaction/branch-summarization.js.map +1 -1
  56. package/dist/core/compaction/compaction.d.ts +6 -0
  57. package/dist/core/compaction/compaction.d.ts.map +1 -1
  58. package/dist/core/compaction/compaction.js +2 -0
  59. package/dist/core/compaction/compaction.js.map +1 -1
  60. package/dist/core/compaction/context-compaction.d.ts +43 -16
  61. package/dist/core/compaction/context-compaction.d.ts.map +1 -1
  62. package/dist/core/compaction/context-compaction.js +518 -189
  63. package/dist/core/compaction/context-compaction.js.map +1 -1
  64. package/dist/core/extensions/loader.d.ts +4 -2
  65. package/dist/core/extensions/loader.d.ts.map +1 -1
  66. package/dist/core/extensions/loader.js +11 -7
  67. package/dist/core/extensions/loader.js.map +1 -1
  68. package/dist/core/extensions/types.d.ts +14 -3
  69. package/dist/core/extensions/types.d.ts.map +1 -1
  70. package/dist/core/extensions/types.js.map +1 -1
  71. package/dist/core/model-registry.d.ts.map +1 -1
  72. package/dist/core/model-registry.js +2 -1
  73. package/dist/core/model-registry.js.map +1 -1
  74. package/dist/core/package-manager.d.ts +1 -1
  75. package/dist/core/package-manager.d.ts.map +1 -1
  76. package/dist/core/package-manager.js +52 -18
  77. package/dist/core/package-manager.js.map +1 -1
  78. package/dist/core/resource-loader.d.ts +20 -0
  79. package/dist/core/resource-loader.d.ts.map +1 -1
  80. package/dist/core/resource-loader.js +89 -24
  81. package/dist/core/resource-loader.js.map +1 -1
  82. package/dist/core/session-manager.d.ts +14 -1
  83. package/dist/core/session-manager.d.ts.map +1 -1
  84. package/dist/core/session-manager.js +145 -3
  85. package/dist/core/session-manager.js.map +1 -1
  86. package/dist/core/settings-manager.d.ts +9 -0
  87. package/dist/core/settings-manager.d.ts.map +1 -1
  88. package/dist/core/settings-manager.js +16 -0
  89. package/dist/core/settings-manager.js.map +1 -1
  90. package/dist/core/thinking-blocks.d.ts +7 -0
  91. package/dist/core/thinking-blocks.d.ts.map +1 -0
  92. package/dist/core/thinking-blocks.js +16 -0
  93. package/dist/core/thinking-blocks.js.map +1 -0
  94. package/dist/core/tools/bash.d.ts.map +1 -1
  95. package/dist/core/tools/bash.js +4 -0
  96. package/dist/core/tools/bash.js.map +1 -1
  97. package/dist/index.d.ts +2 -2
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +1 -1
  100. package/dist/index.js.map +1 -1
  101. package/dist/main.d.ts.map +1 -1
  102. package/dist/main.js +30 -0
  103. package/dist/main.js.map +1 -1
  104. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  105. package/dist/modes/interactive/components/tree-selector.js +87 -12
  106. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  107. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  108. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  109. package/dist/modes/interactive/interactive-mode.js +37 -18
  110. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  111. package/dist/modes/interactive/theme/theme.d.ts +24 -1
  112. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  113. package/dist/modes/interactive/theme/theme.js +58 -13
  114. package/dist/modes/interactive/theme/theme.js.map +1 -1
  115. package/dist/utils/child-process.d.ts +9 -4
  116. package/dist/utils/child-process.d.ts.map +1 -1
  117. package/dist/utils/child-process.js +42 -10
  118. package/dist/utils/child-process.js.map +1 -1
  119. package/dist/utils/version-check.d.ts.map +1 -1
  120. package/dist/utils/version-check.js +4 -27
  121. package/dist/utils/version-check.js.map +1 -1
  122. package/docs/compaction.md +469 -51
  123. package/docs/containerization.md +37 -37
  124. package/docs/extensions.md +23 -14
  125. package/docs/models.md +6 -4
  126. package/docs/packages.md +2 -0
  127. package/docs/providers.md +1 -1
  128. package/docs/subagents.md +11 -4
  129. package/docs/workflows.md +4 -2
  130. package/examples/extensions/README.md +2 -2
  131. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  132. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  133. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  134. package/examples/extensions/gondolin/package-lock.json +2 -2
  135. package/examples/extensions/gondolin/package.json +1 -1
  136. package/examples/extensions/question.ts +39 -18
  137. package/examples/extensions/questionnaire.ts +49 -28
  138. package/examples/extensions/sandbox/package-lock.json +2 -2
  139. package/examples/extensions/sandbox/package.json +1 -1
  140. package/examples/extensions/with-deps/package-lock.json +2 -2
  141. package/examples/extensions/with-deps/package.json +1 -1
  142. package/package.json +7 -5
  143. package/dist/builtin/subagents/src/runs/shared/acceptance.ts +0 -612
  144. package/dist/builtin/subagents/src/runs/shared/completion-guard.ts +0 -147
@@ -10,46 +10,12 @@ There are two general options. You can either
10
10
 
11
11
  | Pattern | What is isolated | Best for | Notes |
12
12
  | --- | --- | --- | --- |
13
- | OpenShell | Whole `atomic` process in a policy-controlled sandbox | Local or remote managed sandbox | Requires an OpenShell gateway |
14
13
  | Gondolin extension | Built-in tools and `!` commands | Local micro-VM isolation while keeping auth on host | See [`examples/extensions/gondolin/`](https://github.com/bastani-inc/atomic/tree/main/packages/coding-agent/examples/extensions/gondolin). |
15
14
  | Plain Docker | Whole `atomic` process in a local container | Simple local isolation | Provider API keys enter the container. |
15
+ | OpenShell | Whole `atomic` process in a policy-controlled sandbox | Local or remote managed sandbox | Requires an OpenShell gateway. |
16
16
 
17
17
  Extensions run wherever the `atomic` process runs. If you run host `atomic` with a tool-routing extension, other custom extension tools still run on the host unless they also delegate their operations.
18
18
 
19
- ## OpenShell
20
-
21
- Use [NVIDIA OpenShell](https://docs.nvidia.com/openshell/about/overview) when you want a policy-controlled sandbox with filesystem, process, network, credential, and inference controls.
22
- OpenShell can run sandboxes through a local gateway backed by Docker, Podman, or a VM runtime, or through a remote Kubernetes gateway.
23
-
24
- Every sandbox requires an active gateway.
25
- Register and select one before creating a sandbox:
26
-
27
- ```bash
28
- openshell gateway add <gateway-url> --name <name>
29
- openshell gateway select <name>
30
- ```
31
-
32
- Launch `atomic` inside an OpenShell sandbox:
33
-
34
- ```bash
35
- openshell sandbox create --name atomic-sandbox --from atomic -- atomic
36
- ```
37
-
38
- In this pattern, the whole `atomic` process runs inside the sandbox.
39
- Built-in tools, `!` commands, and extension tools execute inside the OpenShell boundary.
40
-
41
- If the gateway is remote, project files are not bind-mounted from the host, meaning writes in the sandbox are not reflected on your machine.
42
- Clone the repository inside the sandbox or use OpenShell file transfer commands:
43
-
44
- ```bash
45
- openshell sandbox upload atomic-sandbox ./repo /workspace
46
- openshell sandbox download atomic-sandbox /workspace/repo ./repo-out
47
- ```
48
-
49
- OpenShell providers can keep raw model API keys outside the sandbox.
50
- When inference routing is configured, code inside the sandbox can call `https://inference.local`, and the gateway injects the configured provider credentials upstream.
51
- Configure Atomic to use the corresponding OpenAI-compatible or Anthropic-compatible endpoint if you want model traffic to use this route.
52
-
53
19
  ## Gondolin
54
20
 
55
21
  [Gondolin](https://github.com/earendil-works/gondolin) is a local Linux micro-VM.
@@ -60,7 +26,7 @@ Setup:
60
26
  ```bash
61
27
  cp -R packages/coding-agent/examples/extensions/gondolin ~/.atomic/agent/extensions/gondolin
62
28
  cd ~/.atomic/agent/extensions/gondolin
63
- npm install --ignore-scripts
29
+ bun install --ignore-scripts
64
30
  ```
65
31
 
66
32
  Run from the project you want mounted:
@@ -74,7 +40,7 @@ The extension mounts the host cwd at `/workspace` in the VM and overrides `read`
74
40
  User `!` commands are routed into the VM, as well.
75
41
  File changes under `/workspace` write through to the host.
76
42
 
77
- Requirements: Node.js >= 23.6.0 for `@earendil-works/gondolin`, plus QEMU (requires installation through your package manager).
43
+ Requirements: Bun for dependency installation, Node.js >= 23.6.0 for `@earendil-works/gondolin`, plus QEMU (requires installation through your package manager).
78
44
 
79
45
  ## Plain Docker
80
46
 
@@ -109,3 +75,37 @@ docker run --rm -it \
109
75
  The `-v "$PWD:/workspace"` mounts your current directory into the container at /workspace such that reads and writes in `/workspace` inside Docker directly affect your host files, like in the Gondolin example.
110
76
 
111
77
  Use a named volume for `/root/.atomic/agent` if you want container-local settings and sessions. Mounting your host `~/.atomic/agent` exposes host auth and session files to the container.
78
+
79
+ ## OpenShell
80
+
81
+ Use [NVIDIA OpenShell](https://docs.nvidia.com/openshell/about/overview) when you want a policy-controlled sandbox with filesystem, process, network, credential, and inference controls.
82
+ OpenShell can run sandboxes through a local gateway backed by Docker, Podman, or a VM runtime, or through a remote Kubernetes gateway.
83
+
84
+ Every sandbox requires an active gateway.
85
+ Register and select one before creating a sandbox:
86
+
87
+ ```bash
88
+ openshell gateway add <gateway-url> --name <name>
89
+ openshell gateway select <name>
90
+ ```
91
+
92
+ Launch `atomic` inside an OpenShell sandbox:
93
+
94
+ ```bash
95
+ openshell sandbox create --name atomic-sandbox --from atomic -- atomic
96
+ ```
97
+
98
+ In this pattern, the whole `atomic` process runs inside the sandbox.
99
+ Built-in tools, `!` commands, and extension tools execute inside the OpenShell boundary.
100
+
101
+ If the gateway is remote, project files are not bind-mounted from the host, meaning writes in the sandbox are not reflected on your machine.
102
+ Clone the repository inside the sandbox or use OpenShell file transfer commands:
103
+
104
+ ```bash
105
+ openshell sandbox upload atomic-sandbox ./repo /workspace
106
+ openshell sandbox download atomic-sandbox /workspace/repo ./repo-out
107
+ ```
108
+
109
+ OpenShell providers can keep raw model API keys outside the sandbox.
110
+ When inference routing is configured, code inside the sandbox can call `https://inference.local`, and the gateway injects the configured provider credentials upstream.
111
+ Configure Atomic to use the corresponding OpenAI-compatible or Anthropic-compatible endpoint if you want model traffic to use this route.
@@ -222,6 +222,12 @@ export default async function (pi: ExtensionAPI) {
222
222
 
223
223
  This pattern makes the fetched models available during normal startup and to `atomic --list-models`.
224
224
 
225
+ ### Long-lived resources and shutdown
226
+
227
+ Extension factories may run in invocations that never start a session, such as metadata commands or early configuration checks. Do not start background resources such as processes, sockets, file watchers, or timers from the factory.
228
+
229
+ Defer background resource startup until `session_start` or the command/tool/event that needs the resource. Register an idempotent `session_shutdown` handler to close any session-scoped resources you start.
230
+
225
231
  ### Extension Styles
226
232
 
227
233
  **Single file** - simplest, for small extensions:
@@ -438,15 +444,14 @@ Fired by `/compact` and auto-compaction. Compaction is deletion-only: extensions
438
444
 
439
445
  ```typescript
440
446
  pi.on("session_before_compact", async (event, ctx) => {
441
- const { preparation, branchEntries, reason, mode, signal } = event;
447
+ const { preparation, branchEntries, reason, signal } = event;
442
448
  const { transcript } = preparation;
443
449
 
444
450
  // transcript.entries - compactable entries on the active branch
445
- // transcript.protectedEntryIds - entries protected from standard compaction
451
+ // transcript.protectedEntryIds - entries validation will reject if directly deleted
446
452
  // transcript.tokensBefore - token estimate before compaction
447
453
  // branchEntries - raw session entries on the current branch
448
454
  // reason - "manual" | "threshold" | "overflow"
449
- // mode - "standard" | "critical_overflow"
450
455
 
451
456
  if (signal.aborted) return { cancel: true };
452
457
 
@@ -491,7 +496,7 @@ pi.on("session_tree", async (event, ctx) => {
491
496
 
492
497
  #### session_shutdown
493
498
 
494
- Fired before an extension runtime is torn down.
499
+ Fired before a started session runtime is torn down. Use this to clean up resources opened from `session_start` or other session-scoped hooks.
495
500
 
496
501
  ```typescript
497
502
  pi.on("session_shutdown", async (event, ctx) => {
@@ -994,10 +999,13 @@ if (usage && usage.tokens > 100_000) {
994
999
 
995
1000
  ### ctx.compact()
996
1001
 
997
- Trigger Atomic's default Verbatim Compaction without awaiting completion. This is deletion-only Context Compaction: the internal planner searches/reads transcript slices, records exact entry/content-block deletion targets with transcript-bound tools, and Atomic applies only locally validated logical deletions. Retained transcript content stays unchanged. The approach is informed by Morph's Context Compaction write-up: [Morph's Context Compaction](https://www.morphllm.com/context-compaction). Use `onComplete` and `onError` for follow-up actions.
1002
+ Trigger Atomic's default Verbatim Compaction without awaiting completion. This is deletion-only Context Compaction: the internal planner searches/reads transcript slices, records exact entry/content-block deletion targets with transcript-bound tools, and Atomic applies only locally validated logical deletions. Retained transcript content stays unchanged. The approach is informed by Morph's Context Compaction write-up: [Morph's Context Compaction](https://www.morphllm.com/context-compaction). Use `compression_ratio`, `preserve_recent`, and `query` to tune the run, and `onComplete`/`onError` for follow-up actions.
998
1003
 
999
1004
  ```typescript
1000
1005
  ctx.compact({
1006
+ compression_ratio: 0.5, // fraction to keep: 0.3 aggressive, 0.7 light
1007
+ preserve_recent: 2, // keep the last N context-eligible messages
1008
+ query: "keep active migration details",
1001
1009
  onComplete: (result) => {
1002
1010
  ctx.ui.notify(`Compaction deleted ${result.stats.objectsDeleted} objects`, "info");
1003
1011
  },
@@ -1007,7 +1015,7 @@ ctx.compact({
1007
1015
  });
1008
1016
  ```
1009
1017
 
1010
- Verbatim Compaction uses a fixed internal prompt; no custom summary text can be injected.
1018
+ Verbatim Compaction uses a fixed internal prompt; no custom summary text can be injected. The `query` parameter guides relevance-based pruning inside that fixed prompt; it is not replacement summary text.
1011
1019
 
1012
1020
  ### ctx.getSystemPrompt()
1013
1021
 
@@ -1539,24 +1547,25 @@ const result = await pi.exec("git", ["status"], { signal, timeout: 5000 });
1539
1547
 
1540
1548
  ### pi.getActiveTools() / pi.getAllTools() / pi.setActiveTools(names)
1541
1549
 
1542
- Manage active tools. This works for both built-in tools and dynamically registered tools.
1550
+ Manage active tools. This works for both built-in tools and dynamically registered tools. `pi.getActiveTools()` returns the active tool names as `string[]`; `pi.getAllTools()` returns metadata for all configured tools.
1543
1551
 
1544
1552
  ```typescript
1545
- const active = pi.getActiveTools();
1553
+ const active = pi.getActiveTools(); // ["read", "bash", ...]
1546
1554
  const all = pi.getAllTools();
1547
- // [{
1555
+ // all = [{
1548
1556
  // name: "read",
1549
1557
  // description: "Read file contents...",
1550
- // parameters: ...,
1558
+ // parameters: ...,
1559
+ // promptGuidelines: ["Use read to examine files instead of cat or sed."],
1551
1560
  // sourceInfo: { path: "<builtin:read>", source: "builtin", scope: "temporary", origin: "top-level" }
1552
1561
  // }, ...]
1553
- const names = all.map(t => t.name);
1554
1562
  const builtinTools = all.filter((t) => t.sourceInfo.source === "builtin");
1555
1563
  const extensionTools = all.filter((t) => t.sourceInfo.source !== "builtin" && t.sourceInfo.source !== "sdk");
1564
+ pi.setActiveTools([...new Set([...active, "my_custom_tool"])]); // Keep current tools and enable my_custom_tool
1556
1565
  pi.setActiveTools(["read", "bash"]); // Switch to read-only
1557
1566
  ```
1558
1567
 
1559
- `pi.getAllTools()` returns `name`, `description`, `parameters`, and `sourceInfo`.
1568
+ `pi.getAllTools()` returns `name`, `description`, `parameters`, `promptGuidelines`, and `sourceInfo`.
1560
1569
 
1561
1570
  Typical `sourceInfo.source` values:
1562
1571
  - `builtin` for built-in tools
@@ -2588,8 +2597,8 @@ All examples in [examples/extensions/](https://github.com/bastani-inc/atomic/tre
2588
2597
  |---------|-------------|----------|
2589
2598
  | **Tools** |||
2590
2599
  | `hello.ts` | Minimal tool registration | `registerTool` |
2591
- | `question.ts` | Tool with user interaction | `registerTool`, `ui.select` |
2592
- | `questionnaire.ts` | Multi-step wizard tool | `registerTool`, `ui.custom` |
2600
+ | `question.ts` | Width-wrapped single-question custom UI with option descriptions and typed answers | `registerTool`, `ui.custom` |
2601
+ | `questionnaire.ts` | Width-wrapped multi-step wizard with tab navigation and typed answers | `registerTool`, `ui.custom` |
2593
2602
  | `todo.ts` | Stateful tool with persistence | `registerTool`, `appendEntry`, `renderResult`, session events |
2594
2603
  | `dynamic-tools.ts` | Register tools after startup and during commands | `registerTool`, `session_start`, `registerCommand` |
2595
2604
  | `structured-output.ts` | Opt-in schema-specific `structured_output` tool using the canonical factory | `createStructuredOutputTool`, `registerTool`, terminating tool results |
package/docs/models.md CHANGED
@@ -156,11 +156,13 @@ The `apiKey` and `headers` fields support three formats:
156
156
  ```json
157
157
  "apiKey": "$MY_API_KEY"
158
158
  ```
159
- - **Literal value:** Used directly; bare strings are not environment variable references
159
+ - **Literal value:** Used directly when the value does not use shell-command or explicit environment-variable syntax. Use `$MY_API_KEY`/`${MY_API_KEY}` for new environment-variable references; legacy uppercase env-var-like values may be migrated as described below.
160
160
  ```json
161
161
  "apiKey": "sk-..."
162
162
  ```
163
163
 
164
+ Legacy uppercase env-var-like values in existing `models.json` provider config, such as `MY_API_KEY`, are migrated to `$MY_API_KEY` on startup only when that environment variable is present during migration; otherwise the value is preserved as a literal. New configs should use explicit `$ENV_VAR`/`${ENV_VAR}` syntax for environment variables.
165
+
164
166
  For `models.json`, shell commands are resolved at request time. Atomic intentionally does not apply built-in TTL, stale reuse, or recovery logic for arbitrary commands. Different commands need different caching and failure strategies, and Atomic cannot infer the right one.
165
167
 
166
168
  If your command is slow, expensive, rate-limited, or should keep using a previous value on transient failures, wrap it in your own script or command that implements the caching or TTL behavior you want.
@@ -191,7 +193,7 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
191
193
  | Field | Required | Default | Description |
192
194
  | ------------------ | -------- | ----------------- | ---------------------------------------------------------------------------------------------------------- |
193
195
  | `id` | Yes | — | Model identifier (passed to the API) |
194
- | `name` | No | `id` | Human-readable model label. Used for matching (`--model` patterns) and shown in model details/status text. |
196
+ | `name` | No | `id` | Human-readable model label. Used for matching (`--model` patterns) and shown as secondary model detail text. |
195
197
  | `api` | No | provider's `api` | Override provider's API for this model |
196
198
  | `reasoning` | No | `false` | Supports extended thinking |
197
199
  | `thinkingLevelMap` | No | omitted | Maps Atomic thinking levels to provider values and marks unsupported levels (see below) |
@@ -202,8 +204,8 @@ If your command is slow, expensive, rate-limited, or should keep using a previou
202
204
  | `compat` | No | provider `compat` | Provider compatibility overrides. Merged with provider-level `compat` when both are set. |
203
205
 
204
206
  Current behavior:
205
- - `/model` and `--list-models` list entries by model `id`.
206
- - The configured `name` is used for model matching and detail/status text.
207
+ - `/model`, `--list-models`, and the interactive footer display entries by model `id`.
208
+ - The configured `name` is used for model matching and secondary model detail text. It does not replace the footer/status-bar model id.
207
209
 
208
210
  ### Thinking Level Map
209
211
 
package/docs/packages.md CHANGED
@@ -54,6 +54,8 @@ atomic -e git:github.com/user/repo
54
54
 
55
55
  For local directories, `-e <dir>` also borrows project-local Atomic resources under `<dir>/.atomic`, legacy `<dir>/.pi`, and `<dir>/.agents/skills` when present. Because borrowed extensions and workflows can execute code, Atomic resolves trust for that extension source before loading those borrowed project-local resources.
56
56
 
57
+ Workflows discovered through `-e` keep that same trusted resource set when they create child stage sessions. Stage agents get fresh resource loaders seeded from the parent snapshot, so package tools/extensions, subagents and agent definitions, skills, prompt templates, themes, workflows, and trusted borrowed project-local resources remain available in workflow stages unless the stage supplies its own explicit `resourceLoader`.
58
+
57
59
  ## Package Sources
58
60
 
59
61
  Atomic accepts three source types in settings and `atomic install`.
package/docs/providers.md CHANGED
@@ -152,7 +152,7 @@ The `key` field supports command execution, environment interpolation, and liter
152
152
  { "type": "api_key", "key": "public" }
153
153
  ```
154
154
 
155
- Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup only when that environment variable is present during migration; otherwise the value is preserved as a literal. OAuth credentials are also stored here after `/login` and managed automatically.
155
+ Legacy uppercase env-var-like values such as `MY_API_KEY` are migrated to `$MY_API_KEY` on startup only when that environment variable is present during migration; otherwise the value is preserved as a literal. The same explicit `$ENV_VAR` rule and guarded legacy migration apply to custom provider `apiKey` and header values in `models.json`; see [Custom Models](/models). OAuth credentials are also stored here after `/login` and managed automatically.
156
156
 
157
157
  ## Cloud Providers
158
158
 
package/docs/subagents.md CHANGED
@@ -27,6 +27,16 @@ Research the upstream library behavior online, then compare it with our local im
27
27
 
28
28
  Atomic decides whether to call the bundled `subagent` tool, which specialist fits each part, and whether the work should run as a single child, parallel group, chain, foreground run, or background run.
29
29
 
30
+ Subagents now run and return their results directly. Atomic does not infer acceptance gates from prompt wording, inject `acceptance-report` instructions into child prompts, parse or strip `acceptance-report` blocks, or reject completed child runs because changed-file, test, or review evidence is missing. Put any evidence or validation requirements directly in the task text you give the parent or child agent.
31
+
32
+ ## Migration from acceptance gates
33
+
34
+ If you have older subagent calls, saved chains, or custom agents that used the removed gate fields:
35
+
36
+ - Remove `acceptance` properties from `subagent()` calls, `tasks` entries, `chain` steps, static parallel task items, and dynamic fanout parallel templates. Atomic no longer reads these fields; JSON chain rewrites drop legacy copies.
37
+ - Remove `completionGuard: false` from agent frontmatter and custom agent definitions. The no-mutation completion guard no longer exists, so the override has no effect and management rewrites strip it.
38
+ - Move validation, command, evidence, review, or residual-risk requirements into the natural-language task text passed to the parent or child agent.
39
+
30
40
  ## Bundled agents
31
41
 
32
42
  Atomic currently bundles these agents from `@bastani/subagents`:
@@ -136,14 +146,11 @@ tools: read, grep, bash
136
146
  model: anthropic/claude-sonnet-4
137
147
  fallbackModels: openai/gpt-5-mini
138
148
  inheritProjectContext: true
139
- completionGuard: false
140
149
  ---
141
150
 
142
151
  You are a read-only inspector. Inspect the current diff, cite evidence with file paths, and return only issues worth fixing now. Do not edit files.
143
152
  ```
144
153
 
145
- Use `completionGuard: false` sparingly. It opts a user-authored agent out of automatic completion-guard reminders and is intended for read-only agents whose prompt already prevents premature completion. Do not use it to bypass required implementation or validation work.
146
-
147
154
  If an agent or chain step uses an explicit empty `tools: []` allowlist together with `outputSchema`, Atomic starts the child with only `structured_output` enabled for the required final answer. It does not omit `--tools` and accidentally restore default tools. Path-only tool entries remain extension paths and do not create a builtin allowlist by themselves. The child prompt-runtime extension is loaded before user/tool extensions so its schema-backed `structured_output` tool is registered before explicit allowlists are applied.
148
155
 
149
156
  ## Structured output schemas
@@ -157,7 +164,7 @@ structured_output({
157
164
  })
158
165
  ```
159
166
 
160
- `outputSchema` is a plain JSON Schema descriptor object. It may describe object, array, or primitive final values, and the child should pass a JSON value that matches that schema directly. Atomic no longer adds object-root restrictions, sidecar metadata, transcript-finality checks, duplicate-call guards, or extra parent-side schema parsing. The child runtime writes the tool arguments to `output.json`; the parent reads that JSON back as `result.structuredOutput` and in named-chain references under `outputs.name.structured`.
167
+ `outputSchema` is a plain JSON Schema descriptor object. It may describe object, array, or primitive final values, and the child should pass a JSON value that matches that schema directly. Atomic no longer adds object-root restrictions, sidecar metadata, transcript-finality checks, or duplicate-call guards. The child runtime writes the tool arguments to `output.json`; the parent validates that captured JSON against the schema, reads it back as `result.structuredOutput`, and exposes it in named-chain references under `outputs.name.structured`. If the child exits without calling `structured_output`, or the captured value fails schema validation, Atomic retries up to three times with a corrective prompt that quotes the exact contract/validation error and reminds the child to call `structured_output` rather than returning plain JSON.
161
168
 
162
169
  Children without `outputSchema` do not receive `structured_output` from Atomic's default tool registry. They can still use a custom extension-provided terminating tool if you explicitly add one.
163
170
 
package/docs/workflows.md CHANGED
@@ -725,6 +725,8 @@ atomic -e npm:my-atomic-workflows
725
725
  atomic -e ./local-workflow-package
726
726
  ```
727
727
 
728
+ Workflow stage sessions inherit the same package and temporary `-e` resource discovery snapshot as the main chat. That means a workflow loaded from an external package or directory can start stages that see the package's extensions/tools, subagents and agent definitions, skills, prompt templates, themes, workflows, and trusted borrowed project-local resources without sharing the parent chat's resource-loader instance. Passing an explicit `resourceLoader` in stage options still opts that stage out of this inheritance.
729
+
728
730
  ## Settings
729
731
 
730
732
  Settings can list package sources directly:
@@ -872,7 +874,7 @@ Control behavior:
872
874
  - `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.
873
875
  - `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.
874
876
  - `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.
875
- - `send` delivery modes are `auto`, `answer`, `prompt`, `steer`, `followUp`, and `resume`. Prompt answers can include `promptId` and can carry answer content in `response`, `text`, or `message`; structured UI prompts usually prefer `response`. Arbitrary `ctx.ui.custom<T>` widget prompts require the interactive workflow graph and return a clear unsupported message when targeted through `send`.
877
+ - `send` delivery modes are `auto`, `answer`, `prompt`, `steer`, `followUp`, and `resume`. Prompt answers can include `promptId` and can carry answer content in `response`, `text`, or `message`; structured UI prompts usually prefer `response`. Follow-up messaging to completed or failed stages reuses the retained `sessionFile` when available so the conversation resumes from the archived stage transcript instead of starting empty; if no session metadata was retained, Atomic refuses the follow-up rather than silently resetting. Arbitrary `ctx.ui.custom<T>` widget prompts require the interactive workflow graph and return a clear unsupported message when targeted through `send`.
876
878
  - `delivery: "auto"` first answers a pending prompt, then resumes paused work, then steers a streaming stage, then queues a follow-up.
877
879
  - `pause`, `interrupt`, and `kill` can target one top-level run or `all: true`; `stageId` cannot be combined with `all: true`. Stage-scoped controls can target a visible nested child stage from the expanded graph; Atomic routes the operation to the owning nested run internally.
878
880
  - `interrupt` is resumable: it pauses live work when pausable stages exist and keeps the run in live history/status.
@@ -1479,7 +1481,7 @@ Common task/stage options include:
1479
1481
  - `output`, `outputMode`, `reads`, `worktree`, `gitWorktreeDir`, `baseBranch`, `maxOutput`, `artifacts`, `sessionDir`, `cwd`, `agentDir`
1480
1482
  - advanced host-supplied SDK seams: `authStorage`, `resourceLoader`, `sessionManager`, `settingsManager`, `sessionStartEvent`
1481
1483
 
1482
- `schema` is opt-in. When a `ctx.stage` call, `ctx.task` call, `ctx.chain` item, or `ctx.parallel` item includes a TypeBox schema or plain JSON Schema descriptor object, Atomic registers a schema-specific final-answer tool for that item only. The schema may describe object, array, or primitive final values; the captured value is the JSON value passed to the tool. The prompt result is the captured structured value for `ctx.stage(..., { schema }).prompt(...)`; task/chain/parallel results also include `result.structured` and keep `result.text` as formatted JSON for handoffs. Because the result contract is single-use, a schema-backed `StageContext` supports one `prompt()` call; create a new `ctx.stage(..., { schema })` for each additional structured prompt. If the item also uses an explicit `tools` allowlist, Atomic automatically adds the final-answer tool to that allowlist. Items without `schema` do not receive it from the normal tool registry.
1484
+ `schema` is opt-in. When a `ctx.stage` call, `ctx.task` call, `ctx.chain` item, or `ctx.parallel` item includes a TypeBox schema or plain JSON Schema descriptor object, Atomic registers a schema-specific final-answer tool for that item only. The schema may describe object, array, or primitive final values; the captured value is the JSON value passed to the tool. The prompt result is the captured structured value for `ctx.stage(..., { schema }).prompt(...)`; task/chain/parallel results also include `result.structured` and keep `result.text` as formatted JSON for handoffs. Because the result contract is single-use, a schema-backed `StageContext` supports one `prompt()` call; create a new `ctx.stage(..., { schema })` for each additional structured prompt. If a turn finishes without calling `structured_output`, or the tool call fails schema validation, Atomic sends up to three corrective follow-up prompts that quote the concrete contract/validation error and remind the model to call `structured_output` instead of replying with plain JSON. If the item also uses an explicit `tools` allowlist, Atomic automatically adds the final-answer tool to that allowlist. Items without `schema` do not receive it from the normal tool registry.
1483
1485
 
1484
1486
  `subagent` is available as a default workflow-stage tool, with the same default two-hop nesting budget as main chat: a workflow stage can launch a subagent, and that subagent can launch one nested subagent before the guard blocks further delegation. `tools` remains an allowlist across built-in tools and bundled extension tools; if you set `tools`, list every tool the stage should see. Explicitly listing tools such as `subagent`, `web_search`, `fetch_content`, or `intercom` exposes those tools to the stage, while `excludedTools` and `noTools: "all"` still win. The bundled subagent definitions from `@bastani/subagents` are available to the `subagent` tool in workflow stages; when a workflow is itself running inside a subagent child process, Atomic isolates stage resource discovery from the parent child-process flags so `subagent` remains available while workflow-stage nested-depth guards remain in force.
1485
1487
 
@@ -32,8 +32,8 @@ cp permission-gate.ts ~/.atomic/agent/extensions/
32
32
  |-----------|-------------|
33
33
  | `todo.ts` | Todo list tool + `/todos` command with custom rendering and state persistence |
34
34
  | `hello.ts` | Minimal custom tool example |
35
- | `question.ts` | Demonstrates `ctx.ui.select()` for asking the user questions with custom UI |
36
- | `questionnaire.ts` | Multi-question input with tab bar navigation between questions |
35
+ | `question.ts` | Width-wrapped custom UI for a single question with option descriptions and typed answers |
36
+ | `questionnaire.ts` | Width-wrapped multi-question input with tab bar navigation between questions |
37
37
  | `tool-override.ts` | Override built-in tools (e.g., add logging/access control to `read`) |
38
38
  | `dynamic-tools.ts` | Register tools after startup (`session_start`) and at runtime via command, with prompt snippets and tool-specific prompt guidelines |
39
39
  | `structured-output.ts` | Opt-in schema-specific `structured_output` tool using Atomic's canonical terminating output factory |
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "atomic-extension-custom-provider-anthropic",
3
- "version": "0.79.3",
3
+ "version": "0.79.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "atomic-extension-custom-provider-anthropic",
9
- "version": "0.79.3",
9
+ "version": "0.79.4",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "atomic-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.79.3",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "atomic-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.79.3",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "atomic-extension-gondolin",
3
- "version": "0.79.3",
3
+ "version": "0.79.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "atomic-extension-gondolin",
9
- "version": "0.79.3",
9
+ "version": "0.79.4",
10
10
  "dependencies": {
11
11
  "@earendil-works/gondolin": "0.12.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "atomic-extension-gondolin",
3
3
  "private": true,
4
- "version": "0.79.3",
4
+ "version": "0.79.4",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -5,7 +5,15 @@
5
5
  */
6
6
 
7
7
  import type { ExtensionAPI } from "@bastani/atomic";
8
- import { Editor, type EditorTheme, Key, matchesKey, Text, truncateToWidth } from "@earendil-works/pi-tui";
8
+ import {
9
+ Editor,
10
+ type EditorTheme,
11
+ Key,
12
+ matchesKey,
13
+ Text,
14
+ visibleWidth,
15
+ wrapTextWithAnsi,
16
+ } from "@earendil-works/pi-tui";
9
17
  import { Type } from "typebox";
10
18
 
11
19
  interface OptionWithDesc {
@@ -139,10 +147,27 @@ export default function question(pi: ExtensionAPI) {
139
147
  if (cachedLines) return cachedLines;
140
148
 
141
149
  const lines: string[] = [];
142
- const add = (s: string) => lines.push(truncateToWidth(s, width));
150
+ const renderWidth = Math.max(1, width);
143
151
 
144
- add(theme.fg("accent", "─".repeat(width)));
145
- add(theme.fg("text", ` ${params.question}`));
152
+ function addWrapped(text: string) {
153
+ lines.push(...wrapTextWithAnsi(text, renderWidth));
154
+ }
155
+
156
+ function addWrappedWithPrefix(prefix: string, text: string) {
157
+ const prefixWidth = visibleWidth(prefix);
158
+ if (prefixWidth >= renderWidth) {
159
+ addWrapped(prefix + text);
160
+ return;
161
+ }
162
+ const wrapped = wrapTextWithAnsi(text, renderWidth - prefixWidth);
163
+ const continuationPrefix = " ".repeat(prefixWidth);
164
+ for (let i = 0; i < wrapped.length; i++) {
165
+ lines.push(`${i === 0 ? prefix : continuationPrefix}${wrapped[i]}`);
166
+ }
167
+ }
168
+
169
+ lines.push(theme.fg("accent", "─".repeat(renderWidth)));
170
+ addWrappedWithPrefix(" ", theme.fg("text", params.question));
146
171
  lines.push("");
147
172
 
148
173
  for (let i = 0; i < allOptions.length; i++) {
@@ -150,36 +175,32 @@ export default function question(pi: ExtensionAPI) {
150
175
  const selected = i === optionIndex;
151
176
  const isOther = opt.isOther === true;
152
177
  const prefix = selected ? theme.fg("accent", "> ") : " ";
178
+ const label = `${i + 1}. ${opt.label}${isOther && editMode ? " ✎" : ""}`;
179
+ const color = selected || (isOther && editMode) ? "accent" : "text";
153
180
 
154
- if (isOther && editMode) {
155
- add(prefix + theme.fg("accent", `${i + 1}. ${opt.label} ✎`));
156
- } else if (selected) {
157
- add(prefix + theme.fg("accent", `${i + 1}. ${opt.label}`));
158
- } else {
159
- add(` ${theme.fg("text", `${i + 1}. ${opt.label}`)}`);
160
- }
181
+ addWrappedWithPrefix(prefix, theme.fg(color, label));
161
182
 
162
183
  // Show description if present
163
184
  if (opt.description) {
164
- add(` ${theme.fg("muted", opt.description)}`);
185
+ addWrappedWithPrefix(" ", theme.fg("muted", opt.description));
165
186
  }
166
187
  }
167
188
 
168
189
  if (editMode) {
169
190
  lines.push("");
170
- add(theme.fg("muted", " Your answer:"));
171
- for (const line of editor.render(width - 2)) {
172
- add(` ${line}`);
191
+ addWrappedWithPrefix(" ", theme.fg("muted", "Your answer:"));
192
+ for (const line of editor.render(Math.max(1, renderWidth - 2))) {
193
+ lines.push(` ${line}`);
173
194
  }
174
195
  }
175
196
 
176
197
  lines.push("");
177
198
  if (editMode) {
178
- add(theme.fg("dim", " Enter to submit • Esc to go back"));
199
+ addWrappedWithPrefix(" ", theme.fg("dim", "Enter to submit • Esc to go back"));
179
200
  } else {
180
- add(theme.fg("dim", " ↑↓ navigate • Enter to select • Esc to cancel"));
201
+ addWrappedWithPrefix(" ", theme.fg("dim", "↑↓ navigate • Enter to select • Esc to cancel"));
181
202
  }
182
- add(theme.fg("accent", "─".repeat(width)));
203
+ lines.push(theme.fg("accent", "─".repeat(renderWidth)));
183
204
 
184
205
  cachedLines = lines;
185
206
  return lines;