# Changelog

## [Unreleased]

## [0.9.43] - 2026-10-24

Local workflow worker pools now survive slow, busy, and crashed workers, and large workflow journals open without running out of memory.

- **Pools that recover instead of failing:** a worker that dies mid-run is replaced and its child replays from the last checkpoint instead of crashing the root with `EPIPE`, a resumed root releases children leased to workers that died with the host, unconfirmed worker exits are retried rather than failing every waiting child, and a replayed child no longer leaves an `Intercom event relay failed` error in the session ([#3891](https://github.com/bastani-inc/atomic/pull/3891), [#3893](https://github.com/bastani-inc/atomic/pull/3893), [#3895](https://github.com/bastani-inc/atomic/pull/3895))
- **Leases that follow worker liveness:** a busy root no longer fences healthy workers with `Stale work lease`, and the new `workflows.localWorkers.leaseMs` setting controls how long an unresponsive worker keeps its child (30000 ms by default) ([#3894](https://github.com/bastani-inc/atomic/pull/3894))
- **Retry decisions you can make in code:** `WorkflowInfrastructureError` and `isRetryableWorkflowError` tell retryable pool and runtime faults apart from failures of a child's own logic ([#3895](https://github.com/bastani-inc/atomic/pull/3895))
- **Faster startup on long journals:** workers become ready without reading the whole journal, the readiness timeout is configurable with `workflows.localWorkers.startupTimeoutMs`, and sessions, `/workflow status`, and the `/workflow resume` picker read each run's latest state instead of every event, so large journals no longer exhaust the heap ([#3891](https://github.com/bastani-inc/atomic/pull/3891), [#3896](https://github.com/bastani-inc/atomic/pull/3896))
- **Easier worker diagnosis:** each worker's standard error is kept under `worker-logs/` next to the journal, and startup and retry-limit errors name the log file and show the end of its output ([#3891](https://github.com/bastani-inc/atomic/pull/3891))

### Added

- Added `WorkflowInfrastructureError` and `isRetryableWorkflowError` to `@bastani/atomic/workflows`, so workflows can tell local worker pool and runtime faults (which are safe to retry) from failures of a child workflow's own logic ([#3889](https://github.com/bastani-inc/atomic/issues/3889))
- Added the `workflows.localWorkers.leaseMs` setting: how long, in milliseconds, a local workflow worker that is still running but has stopped responding keeps its child before the child is replayed on another worker (30000 by default, at least 2000) ([#3888](https://github.com/bastani-inc/atomic/issues/3888))

### Fixed

- Local workflow worker pools no longer fail with `Local worker startup timed out` when workers are slow to become ready, for example on a workflow journal with a long history. The readiness timeout is now configurable with the `workflows.localWorkers.startupTimeoutMs` setting (30000 ms by default) ([#3887](https://github.com/bastani-inc/atomic/issues/3887))

## [0.9.43-alpha.1] - 2026-10-24

Local workflow worker pools now survive slow, busy, and crashed workers, and large workflow journals open without running out of memory.

- **Pools that recover instead of failing:** a worker that dies mid-run is replaced and its child replays from the last checkpoint instead of crashing the root with `EPIPE`, a resumed root releases children leased to workers that died with the host, and unconfirmed worker exits are retried rather than failing every waiting child ([#3891](https://github.com/bastani-inc/atomic/pull/3891), [#3893](https://github.com/bastani-inc/atomic/pull/3893), [#3895](https://github.com/bastani-inc/atomic/pull/3895))
- **Leases that follow worker liveness:** a busy root no longer fences healthy workers with `Stale work lease`, and the new `workflows.localWorkers.leaseMs` setting controls how long an unresponsive worker keeps its child (30000 ms by default) ([#3894](https://github.com/bastani-inc/atomic/pull/3894))
- **Retry decisions you can make in code:** `WorkflowInfrastructureError` and `isRetryableWorkflowError` tell retryable pool and runtime faults apart from failures of a child's own logic ([#3895](https://github.com/bastani-inc/atomic/pull/3895))
- **Faster startup on long journals:** workers become ready without reading the whole journal, the readiness timeout is configurable with `workflows.localWorkers.startupTimeoutMs`, and sessions, `/workflow status`, and the `/workflow resume` picker read each run's latest state instead of every event, so large journals no longer exhaust the heap ([#3891](https://github.com/bastani-inc/atomic/pull/3891), [#3896](https://github.com/bastani-inc/atomic/pull/3896))
- **Easier worker diagnosis:** each worker's standard error is kept under `worker-logs/` next to the journal, and startup and retry-limit errors name the log file and show the end of its output ([#3891](https://github.com/bastani-inc/atomic/pull/3891))

### Added

- Added `WorkflowInfrastructureError` and `isRetryableWorkflowError` to `@bastani/atomic/workflows`, so workflows can tell local worker pool and runtime faults (which are safe to retry) from failures of a child workflow's own logic ([#3889](https://github.com/bastani-inc/atomic/issues/3889))
- Added the `workflows.localWorkers.leaseMs` setting: how long, in milliseconds, a local workflow worker that is still running but has stopped responding keeps its child before the child is replayed on another worker (30000 by default, at least 2000) ([#3888](https://github.com/bastani-inc/atomic/issues/3888))

### Fixed

- Local workflow worker pools no longer fail with `Local worker startup timed out` when workers are slow to become ready, for example on a workflow journal with a long history. The readiness timeout is now configurable with the `workflows.localWorkers.startupTimeoutMs` setting (30000 ms by default) ([#3887](https://github.com/bastani-inc/atomic/issues/3887))

## [0.9.42] - 2026-10-23

Sessions, workflow runs, and Intercom can now share one libSQL / Turso database across machines, and provider rate limits are respected across every request in a process.

- **Shared storage across machines:** install `@bastani/atomic-storage-libsql`, join a machine with `atomic storage join <url> --token-file <path>`, and check the result with `atomic storage status`. Atomic packages can ship their own storage adapters, and `createSqliteDatabase()` applies the facade rules when you write one ([#3856](https://github.com/bastani-inc/atomic/issues/3856), [#3857](https://github.com/bastani-inc/atomic/issues/3857))
- **Intercom you can inspect and restart:** `atomic intercom status` and `atomic intercom restart` show and refresh the broker's storage selection and multi-node routing, and a broker that stops answering is replaced after about a minute instead of hanging every session ([#3857](https://github.com/bastani-inc/atomic/issues/3857), [#3862](https://github.com/bastani-inc/atomic/issues/3862))
- **Rate limits handled across requests:** `configureProviderRequestBudget()` sets process-wide request budgets, 429 cooldowns are shared between model requests, and workflow model chains wait for the earliest eligible account instead of exhausting it ([#3864](https://github.com/bastani-inc/atomic/issues/3864))
- **Admission limits and local workflow pools:** SDK hosts can share session and command limits with `resourceAdmission`, and workflows can run child subtrees in local worker processes ([#3869](https://github.com/bastani-inc/atomic/issues/3869))
- **More responsive sessions and workflows:** Ctrl+C and queue clearing act without waiting for a busy engine, workflow stages reload resources four at a time, and standalone `run()` gives stages Intercom without an interactive host ([#3861](https://github.com/bastani-inc/atomic/issues/3861), [#3863](https://github.com/bastani-inc/atomic/issues/3863), [#3865](https://github.com/bastani-inc/atomic/issues/3865))
- **Login, export, and search fixes:** device-code `/login` waits for Enter before opening the browser, HTML exports show billed costs and the session's own system prompt, and `/resume` search ranks name matches first ([pi #9282](https://github.com/earendil-works/pi/issues/9282), [pi #9980](https://github.com/earendil-works/pi/issues/9980), [pi #11261](https://github.com/earendil-works/pi/issues/11261), [pi #11220](https://github.com/earendil-works/pi/issues/11220))
- **Security updates:** the standalone binaries embed Bun 1.4.4 with its upstream security fixes, and the bundled `ip-address` dependency no longer stalls on a long crafted IPv6 string ([GHSA-rxp7-6hxx-x8q5](https://github.com/advisories/GHSA-rxp7-6hxx-x8q5))

### Added

- Markdown transformers registered with `pi.registerMarkdownTransformer()` now receive `messageId` and `timestamp` in their context when a message is rendered from the saved session, so an extension can tie its output to a session entry. Both are `undefined` for live messages. See [registerMarkdownTransformer](docs/extensions/api-reference.md#pi-registermarkdowntransformer-transformer)
- Added `configureProviderRequestBudget(provider, { maxRequests, intervalMs })` for process-wide rolling request budgets shared across SDK model runtimes. Model requests also share provider cooldowns after 429 responses. Separate configured provider IDs remain independent ([#3864](https://github.com/bastani-inc/atomic/issues/3864)).
- Storage adapter modules can import `@bastani/atomic/storage` and `@bastani/atomic` without an installed or linked copy of Atomic. They get the running Atomic's own modules, as extensions do, so Atomic recognizes the storage errors they throw ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `createSqliteDatabase()` in `@bastani/atomic/storage` turns a SQLite-compatible driver's calls into a `SqliteDatabase` that applies the facade rules for you: call-order queueing, transaction handle lifetime, NUL rejection, integer and blob normalization, and typed errors with credentials redacted. `redactSecrets()` builds the credential redactor. The libSQL/Turso example in [Storage](docs/storage.md#bring-your-own-driver) now uses it and is a complete adapter ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- Atomic packages can provide storage adapters with a `storageAdapters` manifest entry, and `storage.adapter` can name one by package (`<package>` or `<package>/<name>`), so `atomic install npm:<adapter-package>` installs the adapter together with its driver. See [Adapters from Atomic packages](docs/storage.md#adapters-from-atomic-packages) ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `@bastani/atomic-storage-libsql` is a first-party libSQL / Turso storage adapter package. Install it with `atomic install npm:@bastani/atomic-storage-libsql` and select it with `"storage": { "adapter": "@bastani/atomic-storage-libsql" }` to share sessions, workflow runs, and Intercom routing across machines. See [Turso and libSQL](docs/storage.md#turso-and-libsql) ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic storage status` shows, for sessions, workflow runs, and Intercom, the selected storage and where the selection comes from (environment variable, project or global settings, or default), the outranked selections, whether the adapter loads, and whether the database answers a `SELECT 1`, without creating anything. It also shows whether the running Intercom broker uses the current selection, its multi-node routing state, and the brokers live in the shared database. `--json` prints the same report. See [Checking the selected storage](docs/storage.md#checking-the-selected-storage) ([#3856](https://github.com/bastani-inc/atomic/issues/3856), [#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic storage join <url> --token-file <path>` joins this machine to a shared libSQL / Turso database in one step: it checks the token and the connection, stores the trimmed token in the agent directory with mode 600, writes only the global `storage` setting, creates no tables, and lists the environment variables and settings that override the new selection. It refuses a URL that carries the token itself, so a credential never reaches settings or output. When `@bastani/atomic-storage-libsql` is not installed, it prints the install command. See [Joining a machine to a shared database](docs/storage.md#joining-a-machine-to-a-shared-database) ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic intercom restart` replaces the running Intercom broker so it reads a changed storage selection; connected sessions reconnect to the new broker. `atomic intercom status` shows the broker, whether it uses the current selection, and its multi-node routing state ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- Interactive and print sessions warn at startup when the shared `storage` selection names an adapter that the running Atomic cannot load, naming the adapter and the cause, instead of Intercom multi-node routing staying off silently ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- SDK hosts can share admission limits through `resourceAdmission` and `withResourceAdmission()`. Session and managed-command permits remain held until cleanup, including background commands. Delegated sessions inherit the same service and request immediate admission or rejection rather than waiting on a permit held by their parent. See [Shared resource admission](docs/sdk.md#shared-resource-admission) ([#3869](https://github.com/bastani-inc/atomic/issues/3869)).

### Changed

- The standalone `atomic` binaries are now built with Bun 1.4.4, which includes upstream security fixes in the embedded runtime
- Device-code `/login` flows (Kimi Code, GitHub Copilot, Meta, and extension providers) no longer open the browser on their own. Press Enter to open the verification URL and copy the code, or log in manually. Providers can turn off either action with `openBrowserOnConfirm: false` or `copyCodeOnConfirm: false`. See [Provider OAuth](docs/custom-provider/oauth.md#oauth-support) ([pi #9282](https://github.com/earendil-works/pi/issues/9282))
- A `{ "file": ... }` storage secret now drops one trailing line ending (`\n` or `\r\n`), so a token saved with `turso db tokens create <db> > token` works without trimming it first. Other whitespace is still kept ([#3856](https://github.com/bastani-inc/atomic/issues/3856))

### Fixed

- HTML session exports now show the recorded total cost of each response, such as OpenRouter's billed cost, instead of the sum of its catalog-estimated components. Sessions recorded before totals were stored still use the component sum ([pi #9980](https://github.com/earendil-works/pi/issues/9980))
- On macOS, cancelling a running bash command no longer lets the next command in a `a; b` sequence start while the shell is being killed: the process group is stopped before it is killed ([pi #11252](https://github.com/earendil-works/pi/issues/11252))
- HTML session exports, including `atomic --export`, now show the system prompt and available tools from the session's first system message, and later system messages as named prompt-section and tool updates. Sessions without system entries still show the prompt and tools from the agent state ([pi #11261](https://github.com/earendil-works/pi/issues/11261))
- HTML session exports of very long sessions no longer fail to render the session tree with a call-stack overflow ([pi #11225](https://github.com/earendil-works/pi/issues/11225))
- `pi.exec()` now reports why a command could not be started (for example `ENOENT` for a missing executable) in `stderr` instead of returning an empty `stderr` with exit code 1 ([pi #11085](https://github.com/earendil-works/pi/issues/11085))
- `/resume` search matches the session name, summary, id, messages and working directory separately. A name match ranks first, then text found in the id, summary, messages or directory, then characters scattered through the name, summary or directory. Matches deep in a long transcript are no longer ranked last, and scattered characters in session ids or message text no longer produce noise matches ([pi #11220](https://github.com/earendil-works/pi/issues/11220))
- Updated the bundled `ip-address` dependency to 10.7.4, which fixes a quadratic-time zone-identifier match in IPv6 address parsing that a single long string could use to stall the process ([GHSA-rxp7-6hxx-x8q5](https://github.com/advisories/GHSA-rxp7-6hxx-x8q5))
- Provider request-budget waits no longer consume OpenAI, Anthropic, Mistral, or explicit Codex SSE HTTP timeouts, and successive Anthropic federation-authenticated requests keep reusing their cached token. Cancelling a queued model request reports an aborted result without dispatching it ([#3864](https://github.com/bastani-inc/atomic/issues/3864)).
- Cancelling or clearing queued messages no longer waits for a busy interactive engine to finish draining its output before acting. Cancelled prompts and shell commands waiting to start do not run later when output resumes. If queue clearing or cancellation of chat, shell commands, compaction, or retries stalls or fails, Ctrl+C can still replace the engine even while it continues sending heartbeats, including controls sent while a replacement initializes; an earlier failed queue pause no longer prevents an abort from being sent ([#3865](https://github.com/bastani-inc/atomic/issues/3865))
- The standalone binary's Intercom broker now runs multi-node routing with a shared `storage` selection and loads storage adapters, as the npm installation does ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `atomic storage join` keeps previous credentials intact when saving settings fails, rejects relative `file:` URLs that would select different databases from different directories, and rejects credentials embedded in `file:` URLs before printing or saving them. New joins use separate private token files, preserving existing sessions' credentials. A successful join no longer reports failure when malformed project settings prevent the final override report: it warns that storage was saved and asks you to fix the settings before running status ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic storage status` reports settings load failures, opens local SQLite files (including adapter `file:` URLs) read-only without creating missing databases or changing journal mode, and bounds connection cleanup even when a query stalls ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `createSqliteDatabase()` preserves callback and rollback failures when transaction cleanup also fails ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- Package-name storage adapters now honor packages listed in legacy global settings, while explicit agent directories remain isolated ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- On macOS, pausing a managed command no longer causes it to be reported as completed and terminated ([#3869](https://github.com/bastani-inc/atomic/issues/3869)).

## [0.9.42-alpha.1] - 2026-10-23

Sessions, workflow runs, and Intercom can now share one libSQL / Turso database across machines, and provider rate limits are respected across every request in a process.

- **Shared storage across machines:** install `@bastani/atomic-storage-libsql`, join a machine with `atomic storage join <url> --token-file <path>`, and check the result with `atomic storage status`. Atomic packages can ship storage adapters, and `createSqliteDatabase()` handles the facade rules when you write your own ([#3856](https://github.com/bastani-inc/atomic/issues/3856), [#3857](https://github.com/bastani-inc/atomic/issues/3857))
- **Intercom you can inspect and restart:** `atomic intercom status` and `atomic intercom restart` show and refresh the broker's storage selection and multi-node routing, and a broker that stops answering is replaced after about a minute instead of hanging every session ([#3857](https://github.com/bastani-inc/atomic/issues/3857), [#3862](https://github.com/bastani-inc/atomic/issues/3862))
- **Rate limits handled across requests:** `configureProviderRequestBudget()` sets process-wide request budgets, 429 cooldowns are shared between model requests, and workflow model chains wait for the earliest eligible account instead of exhausting it ([#3864](https://github.com/bastani-inc/atomic/issues/3864))
- **Admission limits and local workflow pools:** SDK hosts can share session and command limits with `resourceAdmission`, and workflows can run child subtrees in local worker processes ([#3869](https://github.com/bastani-inc/atomic/issues/3869))
- **More responsive sessions and workflows:** Ctrl+C and queue clearing act without waiting for a busy engine, workflow stages reload resources four at a time, and standalone `run()` gives stages Intercom without an interactive host ([#3861](https://github.com/bastani-inc/atomic/issues/3861), [#3863](https://github.com/bastani-inc/atomic/issues/3863), [#3865](https://github.com/bastani-inc/atomic/issues/3865))
- **Login, export, and search fixes:** device-code `/login` waits for Enter before opening the browser, HTML exports show billed costs and the session's own system prompt, and `/resume` search ranks name matches first ([pi #9282](https://github.com/earendil-works/pi/issues/9282), [pi #9980](https://github.com/earendil-works/pi/issues/9980), [pi #11261](https://github.com/earendil-works/pi/issues/11261), [pi #11220](https://github.com/earendil-works/pi/issues/11220))

### Added

- Markdown transformers registered with `pi.registerMarkdownTransformer()` now receive `messageId` and `timestamp` in their context when a message is rendered from the saved session, so an extension can tie its output to a session entry. Both are `undefined` for live messages. See [registerMarkdownTransformer](docs/extensions/api-reference.md#pi-registermarkdowntransformer-transformer)
- Added `configureProviderRequestBudget(provider, { maxRequests, intervalMs })` for process-wide rolling request budgets shared across SDK model runtimes. Model requests also share provider cooldowns after 429 responses. Separate configured provider IDs remain independent ([#3864](https://github.com/bastani-inc/atomic/issues/3864)).
- Storage adapter modules can import `@bastani/atomic/storage` and `@bastani/atomic` without an installed or linked copy of Atomic. They get the running Atomic's own modules, as extensions do, so Atomic recognizes the storage errors they throw ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `createSqliteDatabase()` in `@bastani/atomic/storage` turns a SQLite-compatible driver's calls into a `SqliteDatabase` that applies the facade rules for you: call-order queueing, transaction handle lifetime, NUL rejection, integer and blob normalization, and typed errors with credentials redacted. `redactSecrets()` builds the credential redactor. The libSQL/Turso example in [Storage](docs/storage.md#bring-your-own-driver) now uses it and is a complete adapter ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- Atomic packages can provide storage adapters with a `storageAdapters` manifest entry, and `storage.adapter` can name one by package (`<package>` or `<package>/<name>`), so `atomic install npm:<adapter-package>` installs the adapter together with its driver. See [Adapters from Atomic packages](docs/storage.md#adapters-from-atomic-packages) ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `@bastani/atomic-storage-libsql` is a first-party libSQL / Turso storage adapter package. Install it with `atomic install npm:@bastani/atomic-storage-libsql` and select it with `"storage": { "adapter": "@bastani/atomic-storage-libsql" }` to share sessions, workflow runs, and Intercom routing across machines. See [Turso and libSQL](docs/storage.md#turso-and-libsql) ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic storage status` shows, for sessions, workflow runs, and Intercom, the selected storage and where the selection comes from (environment variable, project or global settings, or default), the outranked selections, whether the adapter loads, and whether the database answers a `SELECT 1`, without creating anything. It also shows whether the running Intercom broker uses the current selection, its multi-node routing state, and the brokers live in the shared database. `--json` prints the same report. See [Checking the selected storage](docs/storage.md#checking-the-selected-storage) ([#3856](https://github.com/bastani-inc/atomic/issues/3856), [#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic storage join <url> --token-file <path>` joins this machine to a shared libSQL / Turso database in one step: it checks the token and the connection, stores the trimmed token in the agent directory with mode 600, writes only the global `storage` setting, creates no tables, and lists the environment variables and settings that override the new selection. It refuses a URL that carries the token itself, so a credential never reaches settings or output. When `@bastani/atomic-storage-libsql` is not installed, it prints the install command. See [Joining a machine to a shared database](docs/storage.md#joining-a-machine-to-a-shared-database) ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic intercom restart` replaces the running Intercom broker so it reads a changed storage selection; connected sessions reconnect to the new broker. `atomic intercom status` shows the broker, whether it uses the current selection, and its multi-node routing state ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- Interactive and print sessions warn at startup when the shared `storage` selection names an adapter that the running Atomic cannot load, naming the adapter and the cause, instead of Intercom multi-node routing staying off silently ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- SDK hosts can share admission limits through `resourceAdmission` and `withResourceAdmission()`. Session and managed-command permits remain held until cleanup, including background commands. Delegated sessions inherit the same service and request immediate admission or rejection rather than waiting on a permit held by their parent. See [Shared resource admission](docs/sdk.md#shared-resource-admission) ([#3869](https://github.com/bastani-inc/atomic/issues/3869)).

### Changed

- The standalone `atomic` binaries are now built with Bun 1.4.4, which includes upstream security fixes in the embedded runtime
- Device-code `/login` flows (Kimi Code, GitHub Copilot, Meta, and extension providers) no longer open the browser on their own. Press Enter to open the verification URL and copy the code, or log in manually. Providers can turn off either action with `openBrowserOnConfirm: false` or `copyCodeOnConfirm: false`. See [Provider OAuth](docs/custom-provider/oauth.md#oauth-support) ([pi #9282](https://github.com/earendil-works/pi/issues/9282))
- A `{ "file": ... }` storage secret now drops one trailing line ending (`\n` or `\r\n`), so a token saved with `turso db tokens create <db> > token` works without trimming it first. Other whitespace is still kept ([#3856](https://github.com/bastani-inc/atomic/issues/3856))

### Fixed

- HTML session exports now show the recorded total cost of each response, such as OpenRouter's billed cost, instead of the sum of its catalog-estimated components. Sessions recorded before totals were stored still use the component sum ([pi #9980](https://github.com/earendil-works/pi/issues/9980))
- On macOS, cancelling a running bash command no longer lets the next command in a `a; b` sequence start while the shell is being killed: the process group is stopped before it is killed ([pi #11252](https://github.com/earendil-works/pi/issues/11252))
- HTML session exports, including `atomic --export`, now show the system prompt and available tools from the session's first system message, and later system messages as named prompt-section and tool updates. Sessions without system entries still show the prompt and tools from the agent state ([pi #11261](https://github.com/earendil-works/pi/issues/11261))
- HTML session exports of very long sessions no longer fail to render the session tree with a call-stack overflow ([pi #11225](https://github.com/earendil-works/pi/issues/11225))
- `pi.exec()` now reports why a command could not be started (for example `ENOENT` for a missing executable) in `stderr` instead of returning an empty `stderr` with exit code 1 ([pi #11085](https://github.com/earendil-works/pi/issues/11085))
- `/resume` search matches the session name, summary, id, messages and working directory separately. A name match ranks first, then text found in the id, summary, messages or directory, then characters scattered through the name, summary or directory. Matches deep in a long transcript are no longer ranked last, and scattered characters in session ids or message text no longer produce noise matches ([pi #11220](https://github.com/earendil-works/pi/issues/11220))
- Updated the bundled `ip-address` dependency to 10.7.4, which fixes a quadratic-time zone-identifier match in IPv6 address parsing that a single long string could use to stall the process ([GHSA-rxp7-6hxx-x8q5](https://github.com/advisories/GHSA-rxp7-6hxx-x8q5))
- Provider request-budget waits no longer consume OpenAI, Anthropic, Mistral, or explicit Codex SSE HTTP timeouts, and successive Anthropic federation-authenticated requests keep reusing their cached token. Cancelling a queued model request reports an aborted result without dispatching it ([#3864](https://github.com/bastani-inc/atomic/issues/3864)).
- Cancelling or clearing queued messages no longer waits for a busy interactive engine to finish draining its output before acting. Cancelled prompts and shell commands waiting to start do not run later when output resumes. If queue clearing or cancellation of chat, shell commands, compaction, or retries stalls or fails, Ctrl+C can still replace the engine even while it continues sending heartbeats, including controls sent while a replacement initializes; an earlier failed queue pause no longer prevents an abort from being sent ([#3865](https://github.com/bastani-inc/atomic/issues/3865))
- The standalone binary's Intercom broker now runs multi-node routing with a shared `storage` selection and loads storage adapters, as the npm installation does ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `atomic storage join` keeps previous credentials intact when saving settings fails, rejects relative `file:` URLs that would select different databases from different directories, and rejects credentials embedded in `file:` URLs before printing or saving them. New joins use separate private token files, preserving existing sessions' credentials. A successful join no longer reports failure when malformed project settings prevent the final override report: it warns that storage was saved and asks you to fix the settings before running status ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- `atomic storage status` reports settings load failures, opens local SQLite files (including adapter `file:` URLs) read-only without creating missing databases or changing journal mode, and bounds connection cleanup even when a query stalls ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- `createSqliteDatabase()` preserves callback and rollback failures when transaction cleanup also fails ([#3856](https://github.com/bastani-inc/atomic/issues/3856))
- Package-name storage adapters now honor packages listed in legacy global settings, while explicit agent directories remain isolated ([#3857](https://github.com/bastani-inc/atomic/issues/3857))
- On macOS, pausing a managed command no longer causes it to be reported as completed and terminated ([#3869](https://github.com/bastani-inc/atomic/issues/3869)).

## [0.9.41] - 2026-10-22

Workflows stay contained when they fail, classifiers read images, and usage totals say where their cost came from.

- **Workflow failures stay inside their run:** an unhandled `ctx` rejection, a stray `ctx.exit()`, or a branch that outlives a kill or quit now fails or ends only its own run instead of the whole Atomic process ([#3846](https://github.com/bastani-inc/atomic/issues/3846), [#3849](https://github.com/bastani-inc/atomic/pull/3849))
- **Image input for more classifiers:** `models.classify()` accepts `images` for Jev and Clef on Cloudflare Workers AI and OpenRouter, with corrected Workers AI pricing ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
- **Provider-billed cost flag:** `usage.cost.totalFromProvider` tells you whether a usage total is the provider's billed cost or a catalog estimate ([#3852](https://github.com/bastani-inc/atomic/pull/3852))
- **Steady sessions and installs:** SDK and RPC `steer()`/`followUp()` start a turn on an idle session, extension OAuth providers can report rejected logins, and the installers skip downloading a release that is already installed ([#3801](https://github.com/bastani-inc/atomic/issues/3801), [#3851](https://github.com/bastani-inc/atomic/pull/3851))
- **Terminal and rendering fixes:** Rex terminal support, terminal modes restored on unexpected exit, Markdown tables after unclosed `$$` blocks, CJK emphasis, and `@` autocomplete with regex characters ([#3847](https://github.com/bastani-inc/atomic/pull/3847))

**Breaking change:** SDK `steer()` and `followUp()` and the RPC `steer` and `follow_up` commands now start a turn when the agent is idle and the session already has messages.

### Breaking Changes

- SDK `steer()` and `followUp()`, and the RPC `steer` and `follow_up` commands, now start a turn when the agent is idle and the session already has messages. On a session with no messages yet, or after a run was aborted, the message still waits for the next prompt. If you queue a message on an idle session that has messages and then call `prompt()` (or send an RPC `prompt`) right away, that prompt now fails with "Agent is already processing" unless you pass `streamingBehavior` (`"steer"` or `"followUp"`) to queue it behind the running turn.

### Added

- Added `usage.cost.totalFromProvider` to usage totals. Summed usage, including nested tool-call and compaction totals, reports `true` only when every part was a provider-reported billed cost; catalog-estimated and legacy sessions without the field report `false`.
- Codemode `models.classify()` now also accepts `images` for TypeSafe's Jev and Cloudflare's Clef models on `cloudflare-workers-ai` and OpenRouter, next to Jev on `typesafe`, and lists Clef Omni and Jev 1.14 on Workers AI. See [Use classifier models](docs/models.md#use-classifier-models). Provider-level `compat` in `models.json` now also applies to classifier models, with the System One fields `imageInput` and `sendImageOptInHeader` for proxies that serve another service's classifiers. Extension classifier models accept `compat` too. See [Models Configuration](docs/models-configuration.md#typesafe-system-one).
- Added support for the Rex terminal (`TERM_PROGRAM=rex`): OSC 8 hyperlinks, truecolor, and inline images. Links previously fell back to the `text (url)` format ([#11131](https://github.com/earendil-works/pi/issues/11131), [#11150](https://github.com/earendil-works/pi/pull/11150) by [@petrroll](https://github.com/petrroll), [#11183](https://github.com/earendil-works/pi/pull/11183) by [@nihaals](https://github.com/nihaals))
- `routeExecutionModel()` accepts an optional `inference` option (exported type `RoutingInferenceBounds`) for callers that must not wait indefinitely on routing inference: `deadline` is an `AbortSignal` whose abort ends routing as a complete inference failure, so the caller can degrade to `AutoRoutingInferenceError.currentModelRoute`, and `limit` runs each routing inference under the caller's concurrency limit. Without it, routing keeps having no built-in deadline.
- RPC `get_available_models` and `refresh_models` responses now list extension providers that accept an API key in `customAuthProviders`, and report extension providers' environment or configured-key authentication in `providerAuthStatuses`.
- Extension OAuth providers can throw `OAuthRefreshRejectedError` (exported from `@bastani/atomic`, with `isOAuthRefreshRejectedError`) from `refreshToken` when the provider rejected the stored login. The user then sees the same `run /login <provider>` guidance as for a built-in provider. See [Provider OAuth](docs/custom-provider/oauth.md#rejected-logins).
- Exported `noOpUIContext`, the default no-op extension UI context, from the SDK entry point ([#3801](https://github.com/bastani-inc/atomic/issues/3801))
- `session.workflows.waitForSettled(runId)` resolves once a workflow run's ending is recorded in the workflow journal and its lease is released, so an SDK host can wait for the journal to be final after a run reports `completed`, `failed` or `killed`. It resolves at once for a run the session is not executing, and rejects with `WorkflowRunStorageError` when the journal could not record the ending. Extensions that call `registerWorkflowRunControl` must implement `waitForSettled` ([#3811](https://github.com/bastani-inc/atomic/issues/3811))
- `createStructuredOutputTool()` and `generateStructuredOutput()` accept a `timeoutMs` option: the wall-clock limit in milliseconds for one whole structured decision, covering every repair attempt, transport retry, and model fallback together. Without it, the `structured_output` tool uses the session's `httpIdleTimeoutMs` setting and other calls use that setting's 10-minute default. The calling model cannot set it. Extensions can read the setting with `ctx.getHttpIdleTimeoutMs()` ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
- The task-host `CancelCause` contract adds `pause` and `quit`, typed resumable stops for an agent that is paused and awaiting resume rather than ended. A workflow stage pausing now stops its agents with `pause` instead of `parent-handoff`; `parent-handoff` stays valid for fresh-start handoffs and for entries recorded by earlier versions.

### Changed

- `install.sh` and `install.ps1` no longer re-download Atomic when the requested release (or the latest release, when none is requested) is already installed and healthy. They print `atomic <tag> is already installed; skipping download`, keep the `Installed to` line and PATH guidance, and download nothing; on Windows a rerun also restores a missing User PATH entry. A different version, or a missing or broken launcher, `current` pointer (including one that points at a versions directory outside the install root), shim, or payload (including a missing `app.js` or other startup file), installs and repairs as before. Pass `--force` (`-Force` on Windows) or set `ATOMIC_FORCE_INSTALL=1` to reinstall anyway.
- Reopening `/resume` is faster: the session list is kept between opens and reloads only the sessions that were created, renamed, deleted, or changed since the last open, including changes made by other Atomic processes sharing the store.
- A subagent stopped with `pause` or `quit`, or by its workflow stage pausing, now reaches the parent as `Subagent <agent> quit · paused, awaiting resume` (or `paused · awaiting resume`) with `Stop reason: quit` or `pause` and the exact `subagent({ action: "resume", id: "<child-id>" })` call, as a quit workflow run is reported, instead of a `parent-handoff` stop notice saying the execution has ended and must not be restarted. Its `/tasks` row shows **Paused · awaiting resume**. Children cancelled for good keep the ended notice.
- The What's New banner shown after an update now leads with the release's highlights summary and points to `/changelog` for the full list of changes. Releases without a summary still show their full notes ([#3804](https://github.com/bastani-inc/atomic/issues/3804))
- Provider attribution headers now come from pi-ai's client identity, still sent only while `enableInstallTelemetry` is on. Chat requests to providers send Atomic's `User-Agent` (`atomic/<version> (<platform>; <runtime>; <arch>)`), including Cloudflare requests, which sent `atomic` before, and OpenCode requests send `x-opencode-client` whenever attribution is enabled, not only with a session. Extensions' `before_provider_headers` handlers no longer see the OpenRouter, NVIDIA NIM, and OpenCode attribution headers or the default `User-Agent`.
- The release lookup that runs before Atomic downloads `fd` or `ripgrep` now sends Atomic's `User-Agent` (`atomic/<version> (<platform>; <runtime>; <arch>)`) instead of `atomic`.

### Fixed

- Fixed Cloudflare Workers AI classifier pricing: Clef Flash costs $0.038 and Jev $0.042 per million input tokens instead of Jev being listed as free.
- Fixed the `grep` tool following ripgrep's `RIPGREP_CONFIG_PATH` whenever it searches with `rg`, such as with `literal: true`, where config options like `--ignore-case` silently changed what the tool returned. The tool now runs `rg --no-config` ([#11203](https://github.com/earendil-works/pi/issues/11203))
- Fixed `@` file autocomplete returning no results for queries containing regex metacharacters such as `[` or `(` ([#10972](https://github.com/earendil-works/pi/issues/10972))
- Fixed an unclosed `$$` display-math block swallowing or corrupting every Markdown table after it ([#11159](https://github.com/earendil-works/pi/issues/11159))
- Fixed terminal modes (mouse tracking, focus reporting, Kitty keyboard protocol, bracketed paste, alternate screen) staying enabled when the process exits without calling `stop()`. A process `exit` handler now restores them synchronously ([#11170](https://github.com/earendil-works/pi/issues/11170))
- Fixed Markdown bold and italic not rendering next to CJK punctuation ([#10730](https://github.com/earendil-works/pi/pull/10730) by [@theBucky](https://github.com/theBucky))
- A rejection that workflow code leaves unhandled no longer ends the whole Atomic process. This covers a rejected `ctx.tool`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` prompt (`input`, `confirm`, `select`, `editor` or `custom`) or stage method call (`prompt`, `complete`, `steer`, `sendUserMessage`, `setModel`, `compact` and the other promise-returning stage methods), including one that is started and never awaited (`void ctx.task(...)` used to leave the run `completed` with no sign of the failure), a `ctx.tool` call made after its run has stopped (`ctx.tool admission is closed for this run`), a string, number or `undefined` thrown from a `ctx.tool` callback, and an error the workflow file throws or receives from `fs` itself, for example in a rolling pool that keeps `branch.finally(...)` as a separate promise. It also covers `ctx.exit()` called inside such a branch, which ends the run as exited with the status and outputs it asked for (on Bun the process used to end right after the run reported `completed`), and a child workflow's own branch that uses `ctx` after its parent exited. Stopping a run no longer ends the process either: a branch that keeps running after `/workflow quit`, a kill, or an abort signal from the code that started the run, and then calls `ctx.stage`, `ctx.task`, `ctx.workflow` or `ctx.exit()`, or that was waiting on a `ctx.ui` prompt when the run was killed, could end it with `workflow killed` (a kill), the quit error (`/workflow quit`), an `AbortError` or `atomic-workflows: HIL aborted` right after the run reported `killed` or `paused`, and the run keeps that status. `ctx.exit()` called in such a branch while the run is paused ends the run as exited once the run resumes, and a kill of the paused run still ends it as `killed` (on Node it used to end as `failed` with `Workflow exit is waiting for explicit resume`, and on Bun the process ended). The rejection fails its own run when it surfaces before that run decides its final status, so other runs, stages, and the interactive engine keep going; a `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` or stage method rejection that the workflow awaits or catches reaches that code unchanged and does not fail the run. Once the run has ended, an error that a `ctx` call rejected with, what `ctx.exit()` threw and the reason the run was stopped with are ignored (the reason of a kill is the string `workflow killed`, which is ignored by value for the rest of the process, so a branch that wakes up long after the run ended is covered too), and so is an error thrown by the workflow file for the next 30 seconds, so the remaining branches of a failing pool cannot end the process; a later one ends it as before. On Node the owning run is identified exactly; on Bun a `ctx` call's rejection, what `ctx.exit()` threw and the reason the run was stopped with are matched by identity (a `ctx` call's string, number or `undefined` only within the event-loop turn in which the call rejected, and the string a kill uses by value), and an error thrown by the workflow file itself is matched through its stack to the run of the file that calls `workflow()`, but only when no other run of that file is running or has run at the same time (a child workflow defined in the same file does not count), so a primitive thrown outside a `ctx` call on Bun is still fatal, and an error that either of two overlapping runs of one file could have thrown ends the process as before instead of failing a run that may be healthy. This works the same in the bundled install as in a source checkout. A rejection that belongs to no workflow run, including one raised by Atomic's own code, ends the process as before, with its original error and stack, and `--unhandled-rejections=warn|none|warn-with-error-code` are honored ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
- A workflow that omits `name` is now named after the file that defines it when Atomic runs from the bundled install, as it already was in a source checkout, instead of after the bundled extension file (`index.bundle`) ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
- `/workflow status <runId>` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed run whose child `ctx.workflow` calls have started but not yet recorded a stage or tool of their own, such as a pool of concurrent children when the process was killed. The children now show as running, and a crashed run can be inspected and resumed. A run that fails while such children are still starting now cancels them first, so its status can be inspected as well ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
- Fixed `structured_output` and `generateStructuredOutput` failing the whole request with `Structured output model is unavailable or not a chat or classifier model` when one explicit `model` or `fallbackModels` ID could not be resolved, for example a display name instead of a `provider/model` ID. The unresolvable ID is now skipped and recorded in `modelAttempts`, and the remaining candidates, including the current chat model, are still tried. The request fails with the original error only when no candidate resolves ([#3814](https://github.com/bastani-inc/atomic/issues/3814))
- Fixed concurrent extension `select`/`confirm`/`input`/`editor` dialogs in interactive mode: a second dialog replaced the first and the first promise never settled, and a dialog opened from an inline `custom()` component hid that component for good. Closing the visible dialog now shows the previous one again ([#11166](https://github.com/earendil-works/pi/issues/11166))
- Fixed pasting in the shell hanging after copying text in Atomic and suspending it with Ctrl+Z on Linux: the background `wl-copy`/`xclip`/`xsel` process that keeps serving the clipboard was suspended along with Atomic ([#11179](https://github.com/earendil-works/pi/issues/11179))
- Fixed codemode scripts failing immediately when both codemode workers are busy, for example with parallel codemode calls or in-process subagents. Scripts now wait up to 60 seconds for a free worker, and `ATOMIC_CODEMODE_MAX_WORKERS` (or `PI_CODEMODE_MAX_WORKERS`) raises the limit of two workers ([#11184](https://github.com/earendil-works/pi/issues/11184))
- Fixed `mcp.json` files that start with a UTF-8 BOM failing to parse, which skipped all their MCP servers and made `atomic mcp add`/`atomic mcp remove` fail ([#11191](https://github.com/earendil-works/pi/issues/11191))
- The default GitHub Copilot model is now GPT-6.1 Sol. The previous default, GPT-5.4, was deprecated by GitHub on 2026-10-19 and dropped from the model catalog, so a Copilot login had no usable default model. GPT-6.1 Sol is GitHub's suggested replacement.
- When extensions fail to start a session, the error now names each failing extension and its own error (`Extension startup failed: <extension path>: <error>`) instead of only `Extension startup failed`. The SDK exports the thrown `ExtensionStartupError`, whose `failures` lists the extension path, event, and message of each failure, together with `isExtensionStartupFailure()` and `isTransientExtensionStartupFailure()` ([#3807](https://github.com/bastani-inc/atomic/issues/3807)).
- Stored OAuth logins, such as `openai-1` or `anthropic-2`, no longer stop working with `OAuth refresh failed ... invalid_grant` under heavy load from many sessions, workflow stages, or subagents until you run `/login`. A session that is slow or briefly unresponsive while it refreshes a token keeps its claim on that refresh, so another session no longer reuses the replaced refresh token. A slow token refresh is no longer cut off at its timeout. Its result is saved when the response arrives, and other sessions wait for it instead of reusing the replaced token. A token refresh the provider completed is also saved when another session took over meanwhile. A session whose refresh is rejected uses a login that another session has just saved. A login the provider has revoked or expired still fails. The error now tells you to run `/login <provider>` for that provider and shows only the provider's error code and description, never tokens.
- Sessions waiting for another session's OAuth token refresh no longer fail with `Lock file is already being held` before that refresh finishes under heavy load. They now wait as long as a refresh can keep its claim, then use the rotated token. Cancelling the request still stops the wait.
- Kimi Code logins no longer break with `invalid_grant` when a token refresh takes longer than 30 seconds or loses its connection mid-request. A revoked or expired OpenAI, Kimi Code, xAI, Meta or GitHub Copilot login now also tells you to run `/login <provider>`, for example on OpenAI's `refresh_token_expired` or `refresh_token_reused` errors.
- A stored OAuth login no longer breaks with `invalid_grant` after Atomic exits while a token refresh is still running, for example a subagent or `atomic -p` run ending after its request timed out, quitting an interactive session, `SIGTERM`, or `atomic auth`. Atomic now waits up to three seconds on exit for the provider's response and saves the rotated token. An `atomic -p` run no longer stays alive waiting for a token refresh that never answers. If Atomic is killed or crashes during a token refresh, run `/login <provider>`.
- An extension or `models.json` provider whose `$VAR` API key is unset now reports the missing key the way a built-in provider does wherever its models are used, for example `No API key found for <provider>` with the `/login` hint during compaction and branch summaries. Previously compaction, branch summaries, workflow stages, and subagents failed with `Failed to resolve API key ... from environment variable`.
- Extension OAuth providers whose refresh is rejected now get the built-in `run /login <provider>` guidance, and a newer login another session saved is still used first, when `refreshToken` throws an error carrying a code the built-in providers treat as a rejected login (`invalid_grant`, `refresh_token_expired`, `refresh_token_reused`, or `refresh_token_invalidated`) in its `error` or `code` property or in an OAuth error response (flat or nested under `error.code`) in `body`, `data`, `response.data`, or `response.body`, or HTTP status 401 in `status`, `statusCode`, `response.status`, or `response.statusCode`. Previously these showed the raw error message, which could include the request's tokens.
- In interactive sessions, `/login` lists extension providers that take an API key and logs in to them, `/logout` offers their stored keys, and `/login` shows an extension provider configured through an environment variable as configured instead of `not configured`.
- `/resume` search no longer keeps a session's old text after the session is rewritten with the same message count, first message, header and last-modified time, for example when an earlier message's text is replaced. Search now finds the new text and not the removed text, including when another Atomic process made the change.
- `structured_output` and `generateStructuredOutput` rejections now say what was wrong instead of the fixed `Invalid structured output: response does not match the decision schema.`. A schema mismatch lists up to five failing paths with the expected shape, such as `/files: expected array, got string` and `/summary: required`, followed by `and N more` when there are others, and never echoes the rejected values. When the provider's tool arguments were not valid JSON, the error is `Invalid structured output: tool call arguments were not valid JSON.` with the kind of syntax error and, when known, its position, without quoting the arguments, instead of a schema mismatch on `{}`. The same detail is sent to the model in the repair request and is kept in `modelAttempts` when a candidate model is exhausted ([#3802](https://github.com/bastani-inc/atomic/issues/3802))
- Shell `wait` and `kill` results now read as one status line above the task's output instead of a raw JSON envelope with internal ids: `✓ completed · exit 0`, `✗ exited 1`, `● still running · showing output so far`, or `○ cancelled`. A truncated result keeps the full-output path as a dim line, and `(no output)` is dimmed. Waiting on or killing an unknown or foreign task id now says `No background task "<id>" in this session` and points to `/tasks`, instead of `UnknownTask: UnknownTask`. The agent receives the same facts as one compact sentence (state, exit code, truncation, full-output path), the structured observation or cancellation receipt stays in the result `details`, and the failures thrown by `wait` and `kill` keep their code in a `code` property ([#3828](https://github.com/bastani-inc/atomic/issues/3828))
- Fixed a structured decision having no total time limit: a `structured_output` call, a `generateStructuredOutput()` call, or one routing inference for `model: "auto"` could run four slow generations back to back, plus retries, past every configured timeout, and held a workflow stage for over an hour. The whole decision is now stopped after the `httpIdleTimeoutMs` setting (10 minutes by default; disabled means effectively unbounded) and fails with `Structured output decision exceeded its <n>ms deadline; no decision was accepted.`, without trying another repair or fallback model ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
- The error for exhausted `structured_output` and `generateStructuredOutput` repairs, and the new deadline error, now end with how each attempt finished and how long it took, such as `Attempts: 1) stopReason=stop 25m02s; 2) stopReason=length 1m30s.`, so one slow generation reads differently from several failed ones. Each provider request, including a transport retry, is listed on its own ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
- Messages typed while the agent was busy are no longer dropped or left waiting for the end of the run. A message typed while a prompt was still being prepared, while Atomic waited to retry a failed request, just after you sent a prompt (before the screen showed that the turn had started), or while extensions were still finishing the previous run used to appear nowhere and be sent only after the run ended, or be refused with "Agent is already processing" and lost. It now shows as queued straight away and joins the run at its next turn boundary, in the order you typed it. If the agent refuses a message, the text comes back to the editor, or stays in the queue, together with the error.
- Queued messages are no longer left undelivered when the agent finishes before they are queued. The rest of a queued batch, such as the messages held during compaction, and a `steer` or `follow_up` sent to an idle agent whose session already has messages now start a turn instead of waiting for your next prompt.
- Near-neutral backgrounds such as message and selection backgrounds no longer turn too bright or blue in 256-color terminals: they now use the nearest color-cube or grayscale entry. Tool status backgrounds keep their blue, green, or red tint, so pending, successful, and failed tool blocks stay distinguishable ([#2550](https://github.com/bastani-inc/atomic/issues/2550))
- Fixed resumed sessions and `/tree` navigation leaving extension tools inactive when the branch recorded its tools before they were registered, such as tools from extensions installed after the session started. Extension tools the branch never added or removed are now active unless they are registered with `defaultActive: false` ([#11062](https://github.com/earendil-works/pi/issues/11062), [#11075](https://github.com/earendil-works/pi/issues/11075))

## [0.9.41-alpha.3] - 2026-10-22

Usage totals now say whether their cost was billed by the provider or estimated.

- **Provider-billed cost flag:** `usage.cost.totalFromProvider` is `true` only when every summed part, including nested tool-call and compaction totals, carries a provider-reported billed cost, and `false` for catalog estimates and older sessions ([#3852](https://github.com/bastani-inc/atomic/pull/3852))

### Added

- Added `usage.cost.totalFromProvider` to usage totals. Summed usage, including nested tool-call and compaction totals, reports `true` only when every part was a provider-reported billed cost; catalog-estimated and legacy sessions without the field report `false`.

### Changed

- `install.sh` and `install.ps1` no longer re-download Atomic when the requested release (or the latest release, when none is requested) is already installed and healthy. They print `atomic <tag> is already installed; skipping download`, keep the `Installed to` line and PATH guidance, and download nothing; on Windows a rerun also restores a missing User PATH entry. A different version, or a missing or broken launcher, `current` pointer (including one that points at a versions directory outside the install root), shim, or payload (including a missing `app.js` or other startup file), installs and repairs as before. Pass `--force` (`-Force` on Windows) or set `ATOMIC_FORCE_INSTALL=1` to reinstall anyway.

## [0.9.41-alpha.2] - 2026-10-22

Workflows that drop a promise stop crashing Atomic, and classifiers can read images.

- **Workflow failures stay inside their run:** an unhandled `ctx` rejection, a stray `ctx.exit()`, or a branch that outlives a kill or quit now fails or ends only its own run instead of the whole process, and `/workflow status` can inspect runs whose child workflows were still starting ([#3846](https://github.com/bastani-inc/atomic/issues/3846), [#3849](https://github.com/bastani-inc/atomic/pull/3849))
- **Image input for more classifiers:** `models.classify()` accepts `images` for Jev and Clef on Cloudflare Workers AI and OpenRouter, with corrected Workers AI pricing and `compat` settings for proxies ([#3847](https://github.com/bastani-inc/atomic/pull/3847))
- **Terminal and rendering fixes:** Rex terminal support, terminal modes restored on unexpected exit, Markdown tables after unclosed `$$` blocks, CJK emphasis, `@` autocomplete with regex characters, and a `grep` tool that ignores your ripgrep config ([#3847](https://github.com/bastani-inc/atomic/pull/3847))

### Added

- Codemode `models.classify()` now also accepts `images` for TypeSafe's Jev and Cloudflare's Clef models on `cloudflare-workers-ai` and OpenRouter, next to Jev on `typesafe`, and lists Clef Omni and Jev 1.14 on Workers AI. See [Use classifier models](docs/models.md#use-classifier-models). Provider-level `compat` in `models.json` now also applies to classifier models, with the System One fields `imageInput` and `sendImageOptInHeader` for proxies that serve another service's classifiers. Extension classifier models accept `compat` too. See [Models Configuration](docs/models-configuration.md#typesafe-system-one).
- Added support for the Rex terminal (`TERM_PROGRAM=rex`): OSC 8 hyperlinks, truecolor, and inline images. Links previously fell back to the `text (url)` format ([#11131](https://github.com/earendil-works/pi/issues/11131), [#11150](https://github.com/earendil-works/pi/pull/11150) by [@petrroll](https://github.com/petrroll), [#11183](https://github.com/earendil-works/pi/pull/11183) by [@nihaals](https://github.com/nihaals))

### Fixed

- Fixed Cloudflare Workers AI classifier pricing: Clef Flash costs $0.038 and Jev $0.042 per million input tokens instead of Jev being listed as free.
- Fixed the `grep` tool following ripgrep's `RIPGREP_CONFIG_PATH` whenever it searches with `rg`, such as with `literal: true`, where config options like `--ignore-case` silently changed what the tool returned. The tool now runs `rg --no-config` ([#11203](https://github.com/earendil-works/pi/issues/11203))
- Fixed `@` file autocomplete returning no results for queries containing regex metacharacters such as `[` or `(` ([#10972](https://github.com/earendil-works/pi/issues/10972))
- Fixed an unclosed `$$` display-math block swallowing or corrupting every Markdown table after it ([#11159](https://github.com/earendil-works/pi/issues/11159))
- Fixed terminal modes (mouse tracking, focus reporting, Kitty keyboard protocol, bracketed paste, alternate screen) staying enabled when the process exits without calling `stop()`. A process `exit` handler now restores them synchronously ([#11170](https://github.com/earendil-works/pi/issues/11170))
- Fixed Markdown bold and italic not rendering next to CJK punctuation ([#10730](https://github.com/earendil-works/pi/pull/10730) by [@theBucky](https://github.com/theBucky))
- A rejection that workflow code leaves unhandled no longer ends the whole Atomic process. This covers a rejected `ctx.tool`, `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` prompt (`input`, `confirm`, `select`, `editor` or `custom`) or stage method call (`prompt`, `complete`, `steer`, `sendUserMessage`, `setModel`, `compact` and the other promise-returning stage methods), including one that is started and never awaited (`void ctx.task(...)` used to leave the run `completed` with no sign of the failure), a `ctx.tool` call made after its run has stopped (`ctx.tool admission is closed for this run`), a string, number or `undefined` thrown from a `ctx.tool` callback, and an error the workflow file throws or receives from `fs` itself, for example in a rolling pool that keeps `branch.finally(...)` as a separate promise. It also covers `ctx.exit()` called inside such a branch, which ends the run as exited with the status and outputs it asked for (on Bun the process used to end right after the run reported `completed`), and a child workflow's own branch that uses `ctx` after its parent exited. Stopping a run no longer ends the process either: a branch that keeps running after `/workflow quit`, a kill, or an abort signal from the code that started the run, and then calls `ctx.stage`, `ctx.task`, `ctx.workflow` or `ctx.exit()`, or that was waiting on a `ctx.ui` prompt when the run was killed, could end it with `workflow killed` (a kill), the quit error (`/workflow quit`), an `AbortError` or `atomic-workflows: HIL aborted` right after the run reported `killed` or `paused`, and the run keeps that status. `ctx.exit()` called in such a branch while the run is paused ends the run as exited once the run resumes, and a kill of the paused run still ends it as `killed` (on Node it used to end as `failed` with `Workflow exit is waiting for explicit resume`, and on Bun the process ended). The rejection fails its own run when it surfaces before that run decides its final status, so other runs, stages, and the interactive engine keep going; a `ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.workflow`, `ctx.ui` or stage method rejection that the workflow awaits or catches reaches that code unchanged and does not fail the run. Once the run has ended, an error that a `ctx` call rejected with, what `ctx.exit()` threw and the reason the run was stopped with are ignored (the reason of a kill is the string `workflow killed`, which is ignored by value for the rest of the process, so a branch that wakes up long after the run ended is covered too), and so is an error thrown by the workflow file for the next 30 seconds, so the remaining branches of a failing pool cannot end the process; a later one ends it as before. On Node the owning run is identified exactly; on Bun a `ctx` call's rejection, what `ctx.exit()` threw and the reason the run was stopped with are matched by identity (a `ctx` call's string, number or `undefined` only within the event-loop turn in which the call rejected, and the string a kill uses by value), and an error thrown by the workflow file itself is matched through its stack to the run of the file that calls `workflow()`, but only when no other run of that file is running or has run at the same time (a child workflow defined in the same file does not count), so a primitive thrown outside a `ctx` call on Bun is still fatal, and an error that either of two overlapping runs of one file could have thrown ends the process as before instead of failing a run that may be healthy. This works the same in the bundled install as in a source checkout. A rejection that belongs to no workflow run, including one raised by Atomic's own code, ends the process as before, with its original error and stack, and `--unhandled-rejections=warn|none|warn-with-error-code` are honored ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
- A workflow that omits `name` is now named after the file that defines it when Atomic runs from the bundled install, as it already was in a source checkout, instead of after the bundled extension file (`index.bundle`) ([#3846](https://github.com/bastani-inc/atomic/issues/3846))
- `/workflow status <runId>` no longer reports `durable checkpoint topology is malformed or incomplete` for a running or crashed run whose child `ctx.workflow` calls have started but not yet recorded a stage or tool of their own, such as a pool of concurrent children when the process was killed. The children now show as running, and a crashed run can be inspected and resumed. A run that fails while such children are still starting now cancels them first, so its status can be inspected as well ([#3846](https://github.com/bastani-inc/atomic/issues/3846))

## [0.9.41-alpha.1] - 2026-10-21

### Breaking Changes

- SDK `steer()` and `followUp()`, and the RPC `steer` and `follow_up` commands, now start a turn when the agent is idle and the session already has messages. On a session with no messages yet, or after a run was aborted, the message still waits for the next prompt. If you queue a message on an idle session that has messages and then call `prompt()` (or send an RPC `prompt`) right away, that prompt now fails with "Agent is already processing" unless you pass `streamingBehavior` (`"steer"` or `"followUp"`) to queue it behind the running turn.

### Added

- `routeExecutionModel()` accepts an optional `inference` option (exported type `RoutingInferenceBounds`) for callers that must not wait indefinitely on routing inference: `deadline` is an `AbortSignal` whose abort ends routing as a complete inference failure, so the caller can degrade to `AutoRoutingInferenceError.currentModelRoute`, and `limit` runs each routing inference under the caller's concurrency limit. Without it, routing keeps having no built-in deadline.
- RPC `get_available_models` and `refresh_models` responses now list extension providers that accept an API key in `customAuthProviders`, and report extension providers' environment or configured-key authentication in `providerAuthStatuses`.
- Extension OAuth providers can throw `OAuthRefreshRejectedError` (exported from `@bastani/atomic`, with `isOAuthRefreshRejectedError`) from `refreshToken` when the provider rejected the stored login. The user then sees the same `run /login <provider>` guidance as for a built-in provider. See [Provider OAuth](docs/custom-provider/oauth.md#rejected-logins).
- Exported `noOpUIContext`, the default no-op extension UI context, from the SDK entry point ([#3801](https://github.com/bastani-inc/atomic/issues/3801))
- `session.workflows.waitForSettled(runId)` resolves once a workflow run's ending is recorded in the workflow journal and its lease is released, so an SDK host can wait for the journal to be final after a run reports `completed`, `failed` or `killed`. It resolves at once for a run the session is not executing, and rejects with `WorkflowRunStorageError` when the journal could not record the ending. Extensions that call `registerWorkflowRunControl` must implement `waitForSettled` ([#3811](https://github.com/bastani-inc/atomic/issues/3811))
- `createStructuredOutputTool()` and `generateStructuredOutput()` accept a `timeoutMs` option: the wall-clock limit in milliseconds for one whole structured decision, covering every repair attempt, transport retry, and model fallback together. Without it, the `structured_output` tool uses the session's `httpIdleTimeoutMs` setting and other calls use that setting's 10-minute default. The calling model cannot set it. Extensions can read the setting with `ctx.getHttpIdleTimeoutMs()` ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
- The task-host `CancelCause` contract adds `pause` and `quit`, typed resumable stops for an agent that is paused and awaiting resume rather than ended. A workflow stage pausing now stops its agents with `pause` instead of `parent-handoff`; `parent-handoff` stays valid for fresh-start handoffs and for entries recorded by earlier versions.

### Changed

- Reopening `/resume` is faster: the session list is kept between opens and reloads only the sessions that were created, renamed, deleted, or changed since the last open, including changes made by other Atomic processes sharing the store.
- A subagent stopped with `pause` or `quit`, or by its workflow stage pausing, now reaches the parent as `Subagent <agent> quit · paused, awaiting resume` (or `paused · awaiting resume`) with `Stop reason: quit` or `pause` and the exact `subagent({ action: "resume", id: "<child-id>" })` call, as a quit workflow run is reported, instead of a `parent-handoff` stop notice saying the execution has ended and must not be restarted. Its `/tasks` row shows **Paused · awaiting resume**. Children cancelled for good keep the ended notice.
- The What's New banner shown after an update now leads with the release's highlights summary and points to `/changelog` for the full list of changes. Releases without a summary still show their full notes ([#3804](https://github.com/bastani-inc/atomic/issues/3804))
- Provider attribution headers now come from pi-ai's client identity, still sent only while `enableInstallTelemetry` is on. Chat requests to providers send Atomic's `User-Agent` (`atomic/<version> (<platform>; <runtime>; <arch>)`), including Cloudflare requests, which sent `atomic` before, and OpenCode requests send `x-opencode-client` whenever attribution is enabled, not only with a session. Extensions' `before_provider_headers` handlers no longer see the OpenRouter, NVIDIA NIM, and OpenCode attribution headers or the default `User-Agent`.
- The release lookup that runs before Atomic downloads `fd` or `ripgrep` now sends Atomic's `User-Agent` (`atomic/<version> (<platform>; <runtime>; <arch>)`) instead of `atomic`.

### Fixed

- Fixed `structured_output` and `generateStructuredOutput` failing the whole request with `Structured output model is unavailable or not a chat or classifier model` when one explicit `model` or `fallbackModels` ID could not be resolved, for example a display name instead of a `provider/model` ID. The unresolvable ID is now skipped and recorded in `modelAttempts`, and the remaining candidates, including the current chat model, are still tried. The request fails with the original error only when no candidate resolves ([#3814](https://github.com/bastani-inc/atomic/issues/3814))
- Fixed concurrent extension `select`/`confirm`/`input`/`editor` dialogs in interactive mode: a second dialog replaced the first and the first promise never settled, and a dialog opened from an inline `custom()` component hid that component for good. Closing the visible dialog now shows the previous one again ([#11166](https://github.com/earendil-works/pi/issues/11166))
- Fixed pasting in the shell hanging after copying text in Atomic and suspending it with Ctrl+Z on Linux: the background `wl-copy`/`xclip`/`xsel` process that keeps serving the clipboard was suspended along with Atomic ([#11179](https://github.com/earendil-works/pi/issues/11179))
- Fixed codemode scripts failing immediately when both codemode workers are busy, for example with parallel codemode calls or in-process subagents. Scripts now wait up to 60 seconds for a free worker, and `ATOMIC_CODEMODE_MAX_WORKERS` (or `PI_CODEMODE_MAX_WORKERS`) raises the limit of two workers ([#11184](https://github.com/earendil-works/pi/issues/11184))
- Fixed `mcp.json` files that start with a UTF-8 BOM failing to parse, which skipped all their MCP servers and made `atomic mcp add`/`atomic mcp remove` fail ([#11191](https://github.com/earendil-works/pi/issues/11191))
- The default GitHub Copilot model is now GPT-6.1 Sol. The previous default, GPT-5.4, was deprecated by GitHub on 2026-10-19 and dropped from the model catalog, so a Copilot login had no usable default model. GPT-6.1 Sol is GitHub's suggested replacement.
- When extensions fail to start a session, the error now names each failing extension and its own error (`Extension startup failed: <extension path>: <error>`) instead of only `Extension startup failed`. The SDK exports the thrown `ExtensionStartupError`, whose `failures` lists the extension path, event, and message of each failure, together with `isExtensionStartupFailure()` and `isTransientExtensionStartupFailure()` ([#3807](https://github.com/bastani-inc/atomic/issues/3807)).
- Stored OAuth logins, such as `openai-1` or `anthropic-2`, no longer stop working with `OAuth refresh failed ... invalid_grant` under heavy load from many sessions, workflow stages, or subagents until you run `/login`. A session that is slow or briefly unresponsive while it refreshes a token keeps its claim on that refresh, so another session no longer reuses the replaced refresh token. A slow token refresh is no longer cut off at its timeout. Its result is saved when the response arrives, and other sessions wait for it instead of reusing the replaced token. A token refresh the provider completed is also saved when another session took over meanwhile. A session whose refresh is rejected uses a login that another session has just saved. A login the provider has revoked or expired still fails. The error now tells you to run `/login <provider>` for that provider and shows only the provider's error code and description, never tokens.
- Sessions waiting for another session's OAuth token refresh no longer fail with `Lock file is already being held` before that refresh finishes under heavy load. They now wait as long as a refresh can keep its claim, then use the rotated token. Cancelling the request still stops the wait.
- Kimi Code logins no longer break with `invalid_grant` when a token refresh takes longer than 30 seconds or loses its connection mid-request. A revoked or expired OpenAI, Kimi Code, xAI, Meta or GitHub Copilot login now also tells you to run `/login <provider>`, for example on OpenAI's `refresh_token_expired` or `refresh_token_reused` errors.
- A stored OAuth login no longer breaks with `invalid_grant` after Atomic exits while a token refresh is still running, for example a subagent or `atomic -p` run ending after its request timed out, quitting an interactive session, `SIGTERM`, or `atomic auth`. Atomic now waits up to three seconds on exit for the provider's response and saves the rotated token. An `atomic -p` run no longer stays alive waiting for a token refresh that never answers. If Atomic is killed or crashes during a token refresh, run `/login <provider>`.
- An extension or `models.json` provider whose `$VAR` API key is unset now reports the missing key the way a built-in provider does wherever its models are used, for example `No API key found for <provider>` with the `/login` hint during compaction and branch summaries. Previously compaction, branch summaries, workflow stages, and subagents failed with `Failed to resolve API key ... from environment variable`.
- Extension OAuth providers whose refresh is rejected now get the built-in `run /login <provider>` guidance, and a newer login another session saved is still used first, when `refreshToken` throws an error carrying a code the built-in providers treat as a rejected login (`invalid_grant`, `refresh_token_expired`, `refresh_token_reused`, or `refresh_token_invalidated`) in its `error` or `code` property or in an OAuth error response (flat or nested under `error.code`) in `body`, `data`, `response.data`, or `response.body`, or HTTP status 401 in `status`, `statusCode`, `response.status`, or `response.statusCode`. Previously these showed the raw error message, which could include the request's tokens.
- In interactive sessions, `/login` lists extension providers that take an API key and logs in to them, `/logout` offers their stored keys, and `/login` shows an extension provider configured through an environment variable as configured instead of `not configured`.
- `/resume` search no longer keeps a session's old text after the session is rewritten with the same message count, first message, header and last-modified time, for example when an earlier message's text is replaced. Search now finds the new text and not the removed text, including when another Atomic process made the change.
- `structured_output` and `generateStructuredOutput` rejections now say what was wrong instead of the fixed `Invalid structured output: response does not match the decision schema.`. A schema mismatch lists up to five failing paths with the expected shape, such as `/files: expected array, got string` and `/summary: required`, followed by `and N more` when there are others, and never echoes the rejected values. When the provider's tool arguments were not valid JSON, the error is `Invalid structured output: tool call arguments were not valid JSON.` with the kind of syntax error and, when known, its position, without quoting the arguments, instead of a schema mismatch on `{}`. The same detail is sent to the model in the repair request and is kept in `modelAttempts` when a candidate model is exhausted ([#3802](https://github.com/bastani-inc/atomic/issues/3802))
- Shell `wait` and `kill` results now read as one status line above the task's output instead of a raw JSON envelope with internal ids: `✓ completed · exit 0`, `✗ exited 1`, `● still running · showing output so far`, or `○ cancelled`. A truncated result keeps the full-output path as a dim line, and `(no output)` is dimmed. Waiting on or killing an unknown or foreign task id now says `No background task "<id>" in this session` and points to `/tasks`, instead of `UnknownTask: UnknownTask`. The agent receives the same facts as one compact sentence (state, exit code, truncation, full-output path), the structured observation or cancellation receipt stays in the result `details`, and the failures thrown by `wait` and `kill` keep their code in a `code` property ([#3828](https://github.com/bastani-inc/atomic/issues/3828))
- Fixed a structured decision having no total time limit: a `structured_output` call, a `generateStructuredOutput()` call, or one routing inference for `model: "auto"` could run four slow generations back to back, plus retries, past every configured timeout, and held a workflow stage for over an hour. The whole decision is now stopped after the `httpIdleTimeoutMs` setting (10 minutes by default; disabled means effectively unbounded) and fails with `Structured output decision exceeded its <n>ms deadline; no decision was accepted.`, without trying another repair or fallback model ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
- The error for exhausted `structured_output` and `generateStructuredOutput` repairs, and the new deadline error, now end with how each attempt finished and how long it took, such as `Attempts: 1) stopReason=stop 25m02s; 2) stopReason=length 1m30s.`, so one slow generation reads differently from several failed ones. Each provider request, including a transport retry, is listed on its own ([#3806](https://github.com/bastani-inc/atomic/issues/3806))
- Messages typed while the agent was busy are no longer dropped or left waiting for the end of the run. A message typed while a prompt was still being prepared, while Atomic waited to retry a failed request, just after you sent a prompt (before the screen showed that the turn had started), or while extensions were still finishing the previous run used to appear nowhere and be sent only after the run ended, or be refused with "Agent is already processing" and lost. It now shows as queued straight away and joins the run at its next turn boundary, in the order you typed it. If the agent refuses a message, the text comes back to the editor, or stays in the queue, together with the error.
- Queued messages are no longer left undelivered when the agent finishes before they are queued. The rest of a queued batch, such as the messages held during compaction, and a `steer` or `follow_up` sent to an idle agent whose session already has messages now start a turn instead of waiting for your next prompt.
- Near-neutral backgrounds such as message and selection backgrounds no longer turn too bright or blue in 256-color terminals: they now use the nearest color-cube or grayscale entry. Tool status backgrounds keep their blue, green, or red tint, so pending, successful, and failed tool blocks stay distinguishable ([#2550](https://github.com/bastani-inc/atomic/issues/2550))
- Fixed resumed sessions and `/tree` navigation leaving extension tools inactive when the branch recorded its tools before they were registered, such as tools from extensions installed after the session started. Extension tools the branch never added or removed are now active unless they are registered with `defaultActive: false` ([#11062](https://github.com/earendil-works/pi/issues/11062), [#11075](https://github.com/earendil-works/pi/issues/11075))

## [0.9.40] - 2026-10-20

### Breaking Changes

- Binding or switching a session no longer replays deliveries that a previous process left unfinished. SDK hosts call `session.startPendingRecovery()` and RPC clients send `start_pending_recovery` once they show the session. Until then, interrupted work stays pending ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Removed the bundled `subagent` tool's `kill` action. Replace `subagent({ action: "kill", id })` with `subagent({ action: "quit", id })`, which stops the child gracefully and keeps it resumable. Calls with `action: "kill"` or the earlier `action: "interrupt"` are rejected with an error that names `quit` as the replacement ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).

### Added

- Added `AgentSession.startPendingRecovery()`, the RPC `start_pending_recovery` command, and the `session_recovery_restore` and `session_recovery_replay` extension events. Extensions can now restore background work after a restart, decline to replay deliveries whose peer is gone, and defer deliveries they cannot decide on yet to the next resume without starting a turn ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Added the `typesafe/jev-1.14.0` classifier. Both direct TypeSafe classifiers, `typesafe/jev-latest` and `typesafe/jev-1.14.0`, now accept up to 262K tokens and PNG or JPEG images, for example through `models.classify()` in codemode. See [TypeSafe Jev](docs/providers.md#typesafe-jev).
- The bundled `subagent` tool now has workflow-style run control: `pause`, `resume` (with an optional `message`), and `quit`, with `all: true` for `pause` and `quit`. Pausing or quitting a parallel child still queued behind its concurrency limit keeps it from starting until you resume it. Paused, quit, interrupted, completed, and failed children resume from their own saved session under the same id and Intercom name, including children of a session you closed or switched away from (exit or crash, `/new`, `/resume` to another session, `/fork`). Subagents of a workflow stage that is paused or quit, including by Atomic exiting or a session switch, stay resumable too, and resuming the run after a restart restores them with the stage. With a `message`, a paused, quit, or interrupted child receives it once and then continues where it left off, and a completed or failed child receives it as one fresh prompt; without one, the child is asked to continue where it left off. Children cancelled for good (owner or batch cancellation, a `/tasks` stop), every subagent of a workflow stage that finished or whose run ended or was cancelled, including ones you paused or quit, and children reported gone after a restart are terminal, and `resume` on them returns an error that says why ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Background subagent launch cards show the child's latest state (running, paused, quit, interrupted, completed, failed, or terminal), also after you reopen the session (`/resume` or `atomic --session <id>`) or view a workflow stage's transcript. Expanding a card shows the end of the child's saved transcript. A resumed child continues in its original card, and replayed `pause`, `resume`, and `quit` calls render as their results ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Added per-process storage selection. `ATOMIC_STORAGE`, `ATOMIC_SESSIONS_STORAGE`, `ATOMIC_WORKFLOWS_JOURNAL`, `--storage '<json>'`, and `--storage-file <path>` take the same JSON selections as the storage settings, adapters and `secrets` included, so a host can start `atomic --mode rpc` children on their own store without editing settings. A process without them keeps using its settings, and `/resume` in each lists only its own store. Invalid or conflicting selections stop startup with a `StorageConfigError` naming the variable or flag. See [Selecting storage per process](docs/storage.md#selecting-storage-per-process) ([#3783](https://github.com/bastani-inc/atomic/issues/3783)).
- Added multi-node routing: Intercom brokers on different machines that select the same shared `storage` (in settings, `ATOMIC_STORAGE`, or `--storage`) now deliver messages to each other through that database. A message to a session on another machine arrives exactly like a local one, with the same sender identity, replies and asks, group rules, supervisor routes and save-before-delivered receipts. A send to a session that is offline, including one that quit cleanly in the last 7 days, is reported as queued for an offline session and delivered when it reconnects on any broker. Brokers coordinate only through the shared database and a polled wake-up signal; there is no broker-to-broker network connection. Without a shared `storage` selection, Intercom behaves as before.

### Fixed

- Resuming a session with `atomic --session <id>` or `/resume` now shows the transcript before any recovery work runs, instead of leaving the screen blank while hidden agent turns replay messages from before the restart. Recovery runs afterwards and is visible in the transcript. A "Replaying N interrupted deliveries" line precedes any replayed turn, and a status line summarizes the result ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Resuming no longer starts agent turns on its own for stale queued messages or background task results. Results that an earlier process never delivered appear as cards without starting a turn. Messages from peers that no longer exist are not replayed; one notice tells you and the agent which peers are gone. Messages from a background subagent that was relaunched but has not reconnected yet are deferred instead, and the notice says it is not gone ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Intercom messages sent to a busy session are no longer lost when that session is killed or crashes before it goes idle: the recipient saves each message when it arrives, the sender sees `delivered` only after that save, and resuming the session with `atomic --session <id>` or `/resume` shows each saved message once. Like interrupted deliveries, a saved message starts a turn only when its sender is still connected; one from a gone sender is added once, in full, to the session history without starting a turn, and the recovery notice says the sender is gone; one whose sender cannot be checked is deferred to the next resume ([#3761](https://github.com/bastani-inc/atomic/issues/3761)).
- Fixed Windows rejecting Bash and PowerShell executables installed as Windows Store app aliases, such as those in `%LOCALAPPDATA%\Microsoft\WindowsApps`, as missing. Shell discovery and a configured `shellPath` now accept them ([upstream commit](https://github.com/earendil-works/pi/commit/28bea006be4adb950dcb4f86f9ee5be857bf74ff)).
- Fixed Windows PowerShell discovery when PowerShell is not on `PATH`: Atomic now also checks `%ProgramFiles%\PowerShell\7\pwsh.exe` and `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`. The legacy WSL `bash.exe` launcher is now recognized under the actual Windows directory, including installs on another drive or a renamed Windows directory ([#9490](https://github.com/earendil-works/pi/issues/9490)).
- JSONL sessions no longer stall when many sessions share a directory. Each append now rereads only its own session header instead of every session header in the directory ([#3780](https://github.com/bastani-inc/atomic/pull/3780) by [@darionco](https://github.com/darionco)).
- Opening or loading a session in a directory with many JSONL sessions no longer reads every session header, including the first lookup in a process and sessions created by another process.
- A resumed session no longer splits an extension tool call into an arguments-only card and a separate result card when the extension saved its own entries between them, and tool cards in an attached workflow stage chat now redraw when their renderer updates them ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Extension tool cards, including subagent cards, now use their extension renderers and current state when a session is opened with `atomic --session <id>`, instead of showing raw arguments. They also redraw as soon as their state changes, so an expanded subagent card shows the child's saved transcript and a resumed child updates in its original card without waiting for another screen redraw ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Fixed HTML export showing literal asterisks for Markdown bold and italic next to CJK punctuation ([#3353](https://github.com/earendil-works/pi/issues/3353), [#10154](https://github.com/earendil-works/pi/issues/10154))
- Fixed HTML export omitting nested tool calls (for example from codemode scripts), and not scrolling to the tool call when a tool result is selected in the tree

## [0.9.40-alpha.2] - 2026-10-20

### Fixed

- Fixed HTML export showing literal asterisks for Markdown bold and italic next to CJK punctuation ([#3353](https://github.com/earendil-works/pi/issues/3353), [#10154](https://github.com/earendil-works/pi/issues/10154))
- Fixed HTML export omitting nested tool calls (for example from codemode scripts), and not scrolling to the tool call when a tool result is selected in the tree

## [0.9.40-alpha.1] - 2026-10-20

### Breaking Changes

- Binding or switching a session no longer replays deliveries that a previous process left unfinished. SDK hosts call `session.startPendingRecovery()` and RPC clients send `start_pending_recovery` once they show the session. Until then, interrupted work stays pending ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Removed the bundled `subagent` tool's `kill` action. Replace `subagent({ action: "kill", id })` with `subagent({ action: "quit", id })`, which stops the child gracefully and keeps it resumable. Calls with `action: "kill"` or the earlier `action: "interrupt"` are rejected with an error that names `quit` as the replacement ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).

### Added

- Added `AgentSession.startPendingRecovery()`, the RPC `start_pending_recovery` command, and the `session_recovery_restore` and `session_recovery_replay` extension events. Extensions can now restore background work after a restart, decline to replay deliveries whose peer is gone, and defer deliveries they cannot decide on yet to the next resume without starting a turn ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Added the `typesafe/jev-1.14.0` classifier. Both direct TypeSafe classifiers, `typesafe/jev-latest` and `typesafe/jev-1.14.0`, now accept up to 262K tokens and PNG or JPEG images, for example through `models.classify()` in codemode. See [TypeSafe Jev](docs/providers.md#typesafe-jev).
- The bundled `subagent` tool now has workflow-style run control: `pause`, `resume` (with an optional `message`), and `quit`, with `all: true` for `pause` and `quit`. Pausing or quitting a parallel child still queued behind its concurrency limit keeps it from starting until you resume it. Paused, quit, interrupted, completed, and failed children resume from their own saved session under the same id and Intercom name, including children of a session you closed or switched away from (exit or crash, `/new`, `/resume` to another session, `/fork`). Subagents of a workflow stage that is paused or quit, including by Atomic exiting or a session switch, stay resumable too, and resuming the run after a restart restores them with the stage. With a `message`, a paused, quit, or interrupted child receives it once and then continues where it left off, and a completed or failed child receives it as one fresh prompt; without one, the child is asked to continue where it left off. Children cancelled for good (owner or batch cancellation, a `/tasks` stop), every subagent of a workflow stage that finished or whose run ended or was cancelled, including ones you paused or quit, and children reported gone after a restart are terminal, and `resume` on them returns an error that says why ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Background subagent launch cards show the child's latest state (running, paused, quit, interrupted, completed, failed, or terminal), also after you reopen the session (`/resume` or `atomic --session <id>`) or view a workflow stage's transcript. Expanding a card shows the end of the child's saved transcript. A resumed child continues in its original card, and replayed `pause`, `resume`, and `quit` calls render as their results ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Added per-process storage selection. `ATOMIC_STORAGE`, `ATOMIC_SESSIONS_STORAGE`, `ATOMIC_WORKFLOWS_JOURNAL`, `--storage '<json>'`, and `--storage-file <path>` take the same JSON selections as the storage settings, adapters and `secrets` included, so a host can start `atomic --mode rpc` children on their own store without editing settings. A process without them keeps using its settings, and `/resume` in each lists only its own store. Invalid or conflicting selections stop startup with a `StorageConfigError` naming the variable or flag. See [Selecting storage per process](docs/storage.md#selecting-storage-per-process) ([#3783](https://github.com/bastani-inc/atomic/issues/3783)).
- Added multi-node routing: Intercom brokers on different machines that select the same shared `storage` (in settings, `ATOMIC_STORAGE`, or `--storage`) now deliver messages to each other through that database. A message to a session on another machine arrives exactly like a local one, with the same sender identity, replies and asks, group rules, supervisor routes and save-before-delivered receipts. A send to a session that is offline, including one that quit cleanly in the last 7 days, is reported as queued for an offline session and delivered when it reconnects on any broker. Brokers coordinate only through the shared database and a polled wake-up signal; there is no broker-to-broker network connection. Without a shared `storage` selection, Intercom behaves as before.

### Fixed

- Resuming a session with `atomic --session <id>` or `/resume` now shows the transcript before any recovery work runs, instead of leaving the screen blank while hidden agent turns replay messages from before the restart. Recovery runs afterwards and is visible in the transcript. A "Replaying N interrupted deliveries" line precedes any replayed turn, and a status line summarizes the result ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Resuming no longer starts agent turns on its own for stale queued messages or background task results. Results that an earlier process never delivered appear as cards without starting a turn. Messages from peers that no longer exist are not replayed; one notice tells you and the agent which peers are gone. Messages from a background subagent that was relaunched but has not reconnected yet are deferred instead, and the notice says it is not gone ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Intercom messages sent to a busy session are no longer lost when that session is killed or crashes before it goes idle: the recipient saves each message when it arrives, the sender sees `delivered` only after that save, and resuming the session with `atomic --session <id>` or `/resume` shows each saved message once. Like interrupted deliveries, a saved message starts a turn only when its sender is still connected; one from a gone sender is added once, in full, to the session history without starting a turn, and the recovery notice says the sender is gone; one whose sender cannot be checked is deferred to the next resume ([#3761](https://github.com/bastani-inc/atomic/issues/3761)).
- Fixed Windows rejecting Bash and PowerShell executables installed as Windows Store app aliases, such as those in `%LOCALAPPDATA%\Microsoft\WindowsApps`, as missing. Shell discovery and a configured `shellPath` now accept them ([upstream commit](https://github.com/earendil-works/pi/commit/28bea006be4adb950dcb4f86f9ee5be857bf74ff)).
- Fixed Windows PowerShell discovery when PowerShell is not on `PATH`: Atomic now also checks `%ProgramFiles%\PowerShell\7\pwsh.exe` and `%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe`. The legacy WSL `bash.exe` launcher is now recognized under the actual Windows directory, including installs on another drive or a renamed Windows directory ([#9490](https://github.com/earendil-works/pi/issues/9490)).
- JSONL sessions no longer stall when many sessions share a directory. Each append now rereads only its own session header instead of every session header in the directory ([#3780](https://github.com/bastani-inc/atomic/pull/3780) by [@darionco](https://github.com/darionco)).
- Opening or loading a session in a directory with many JSONL sessions no longer reads every session header, including the first lookup in a process and sessions created by another process.
- A resumed session no longer splits an extension tool call into an arguments-only card and a separate result card when the extension saved its own entries between them, and tool cards in an attached workflow stage chat now redraw when their renderer updates them ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).
- Extension tool cards, including subagent cards, now use their extension renderers and current state when a session is opened with `atomic --session <id>`, instead of showing raw arguments. They also redraw as soon as their state changes, so an expanded subagent card shows the child's saved transcript and a resumed child updates in its original card without waiting for another screen redraw ([#3760](https://github.com/bastani-inc/atomic/issues/3760)).

## [0.9.39] - 2026-10-19

### Added

- Added RPC `delete_session` (`{"type":"delete_session","sessionId":"<id>"}`, or `{"type":"delete_session","id":"<id>"}` when `sessionId` is omitted), `RpcClient.deleteSession(sessionId)`, and `SessionManager.deleteSession(sessionId)` to delete a stored session and its entries from SQLite or JSONL storage. The session currently open in the process cannot be deleted and returns an error ([#3740](https://github.com/bastani-inc/atomic/issues/3740)).
- Added `createAgentSession({ journal })` so each SDK session can choose its workflow journal (file path, caller-owned database, process-local memory, adapter, or supplied journal) without the process-wide `ATOMIC_WORKFLOW_JOURNAL_PATH` ([#3743](https://github.com/bastani-inc/atomic/issues/3743)).
- When Atomic runs on Node, the process that runs agents now gets half of system memory for its heap when that is more than Node's default limit (about 4 GiB), so large machines can run more subagents and workflow stages at once. This applies to the interactive engine and to headless runs (`-p`, `--mode json`, `--mode rpc`), which restart themselves once with the larger heap. Set `ATOMIC_HEAP_MB` to choose the size or `0` to keep Node's default; an explicit `--max-old-space-size` in `NODE_OPTIONS` still wins.

### Fixed

- `/export` and the RPC `export_html` command now export sessions stored in the default SQLite session store instead of refusing them, reading entries through the session store rather than a session file ([#3741](https://github.com/bastani-inc/atomic/issues/3741))
- When the earlier JSONL sessions folder holds several copies of one session, such as conflict copies left by a file-sync tool, the default store now lists and imports the copy with the newest message instead of the alphabetically first file.
- Fullscreen overlays that show chat messages (the workflow graph, stage chat, and the `/tasks` transcript) no longer write OSC 133 shell-integration marks to the terminal, so terminals keep the program status without Atomic re-sending an identical OSC 7501 report on every overlay frame ([#3739](https://github.com/bastani-inc/atomic/issues/3739))

## [0.9.39-alpha.2] - 2026-10-19

### Fixed

- Fullscreen overlays that show chat messages (the workflow graph, stage chat, and the `/tasks` transcript) no longer write OSC 133 shell-integration marks to the terminal, so terminals keep the program status without Atomic re-sending an identical OSC 7501 report on every overlay frame ([#3739](https://github.com/bastani-inc/atomic/issues/3739))

## [0.9.39-alpha.1] - 2026-10-19

### Added

- Added RPC `delete_session` (`{"type":"delete_session","sessionId":"<id>"}`, or `{"type":"delete_session","id":"<id>"}` when `sessionId` is omitted), `RpcClient.deleteSession(sessionId)`, and `SessionManager.deleteSession(sessionId)` to delete a stored session and its entries from SQLite or JSONL storage. The session currently open in the process cannot be deleted and returns an error ([#3740](https://github.com/bastani-inc/atomic/issues/3740)).
- Added `createAgentSession({ journal })` so each SDK session can choose its workflow journal (file path, caller-owned database, process-local memory, adapter, or supplied journal) without the process-wide `ATOMIC_WORKFLOW_JOURNAL_PATH` ([#3743](https://github.com/bastani-inc/atomic/issues/3743)).
- When Atomic runs on Node, the process that runs agents now gets half of system memory for its heap when that is more than Node's default limit (about 4 GiB), so large machines can run more subagents and workflow stages at once. This applies to the interactive engine and to headless runs (`-p`, `--mode json`, `--mode rpc`), which restart themselves once with the larger heap. Set `ATOMIC_HEAP_MB` to choose the size or `0` to keep Node's default; an explicit `--max-old-space-size` in `NODE_OPTIONS` still wins.

### Fixed

- `/export` and the RPC `export_html` command now export sessions stored in the default SQLite session store instead of refusing them, reading entries through the session store rather than a session file ([#3741](https://github.com/bastani-inc/atomic/issues/3741))
- When the earlier JSONL sessions folder holds several copies of one session, such as conflict copies left by a file-sync tool, the default store now lists and imports the copy with the newest message instead of the alphabetically first file.

## [0.9.38] - 2026-10-18

### Breaking Changes

- Workflow discovery now uses only `.atomic` default roots, and discovered modules use `@bastani/atomic/workflows` with `typebox`. Removed deprecated workflow API stubs, separate reasoning authoring fields, old stage-target and X10 mouse input formats, and replay without current topology, replay keys, or child exit tags. Use model strings such as `provider/model:high` for reasoning effort. Workflow results, inspection details and status summaries always include their tool collections. Queued stage delivery requires persisted admission order.
- Workflow heartbeat records require their original launch cadence instead of inheriting the current definition when cadence is missing. Configuration load results always include their global and project source fields, using `null` for absent or invalid sources.
- Workflow run snapshots require `toolNodes`, and inline forms require injected keybindings without obsolete submit-choice state. Removed the ignored workflow text-truncation `preserveAnsi` argument; ANSI-aware rendering is unchanged.
- Workflow runs now persist only in the local SQLite journal. Journal initialization failures throw `StorageUnavailableError` instead of continuing without durable storage. Postgres connection settings (`workflows.durability.systemDatabaseUrl` and `workflows.durability.poolSize`) and `DBOS_SYSTEM_DATABASE_URL`, `ATOMIC_POSTGRES_*`, and `PG*` environment variables no longer configure workflow storage.
- Removed automatic session-format migrations and the `migrateSessionEntries` export. Reading session files no longer appends missing newlines.
- Removed the workflow `durability` option, `WorkflowDurability`, and `WorkflowDurabilityRequiredError`. Use `RunOpts.journal` to select storage or isolate runs, and handle typed journal errors when storage is unavailable.
- Removed `InMemoryDurableBackend` and `createInMemoryTestBackend`. Use `JournalDurableBackend` over `MemoryRunJournal` for an explicitly in-memory backend. Removed the `legacyRecovery` workflow-tool argument and remaining `workflows.durability` settings.
- Removed the `@bastani/workflows` import alias; import workflow APIs from `@bastani/atomic/workflows`.
- Custom `RunJournal` implementations must provide atomic `deleteOwnedRun(lease)`, and invocation records require `invocationFingerprint`.
- `SessionManager.create`, `open`, `continueRecent`, `forkFrom`, and mutations now return promises. Await session writes, branch changes, labels, names, and model/thinking changes before reading their results or continuing dependent work. Extension `pi.appendEntry`, `pi.setSessionName`, `pi.setLabel`, and `pi.setThinkingLevel` must also be awaited. Persistent session creation writes its header immediately, and setup entries commit without waiting for the first message. A failed write rejects that call with its typed storage error without publishing the failed entry; there is no `flush` API. Explicit in-memory sessions remain available synchronously through `SessionManager.inMemory()`.
- Session headers use the format-4 fields `createdAt` and `parentSessionId`, including the header returned by `SessionManager.getHeader()`. The `timestamp` and `parentSession` header fields are removed.
- Extension provider registration and removal now return promises too: await `pi.registerProvider`, `pi.registerProviderAlias`, and `pi.unregisterProvider` before dependent model or session work.
- Workflow SQLite journals now use `SqlRunJournal` with async initialization and `wf_` table names. Use `await openSqlRunJournal(pathOrDatabase?)` or `await SqlRunJournal.open(database, { prefix })`, and await `resolveRunJournal`. Earlier unreleased bare tables are not read or migrated. Workflow `StorageUnavailableError` now provides `adapter`, `location`, and `cause` instead of `path`.
- Session selection now uses IDs instead of file paths: `--session` and `--fork` accept exact IDs or unique eight-hex UUID prefixes; use `--import <file>` for format-4 JSONL exports. Extension `switchSession` takes an ID, lifecycle events use `previousSessionId` and `targetSessionId`, and RPC `switch_session` takes `sessionId`. RPC state reports `sessionLocation` for optional display instead of `sessionFile`. Shell tools no longer expose `ATOMIC_SESSION_FILE` or `PI_SESSION_FILE`; use `ATOMIC_SESSION_ID` or `PI_SESSION_ID`.
- `RunOpts.journal.database` now takes an async `SqliteDatabase` facade, not a raw `node:sqlite` connection; use `wrapNodeSqliteDatabase` for an existing connection. Journal configuration failures now use `StorageConfigError` with `source`, `field`, and `reason` instead of `JournalConfigurationError`.
- Workflow stage forks now take `forkFromSessionId`, and stage resume, retained chat, and subagent forks resolve IDs within the selected session store. File paths are not session identities. Embedded chat hosts must use `await host.loadSession(store, sessionId)` instead of `loadSessionFile(path)`.
- Sessions now default to the local SQLite database shared with workflow runs, rather than JSONL files. JSONL must be selected explicitly. Removed the `sessionDir` setting without an alias; use `sessions.storage.dir` instead.

### Added

- Added a guide to running local models on NVIDIA RTX GPUs with llama.cpp, including recipe prerequisites, router preset conversion, and connecting Atomic.
- Changing the thinking level mid-session on OpenAI GPT-6 models with an API key now preserves the prompt cache ([#10853](https://github.com/earendil-works/pi/issues/10853)).
- Added `formatReadLineRange` to the SDK exports so extensions can render `read` line-range labels the same way Atomic does.
- Added the async SQLite facade and node adapter through `@bastani/atomic/storage`, with typed storage errors, prefix and schema-version helpers, and an adapter conformance suite in `@bastani/atomic/storage/testing`. String bindings containing NUL are rejected with `StorageInvalidTextError`; zero bytes remain supported in binary values.
- Added the `workflows.journal.path` and `workflows.journal.memory` settings, plus SDK `RunOpts.journal` for a file, caller-owned SQLite connection, explicit memory journal, or supplied journal. TUI, CLI, background, and SDK runs use the selected journal. Relative settings paths resolve against the Atomic home directory.
- Added `ctx.sessionSignal` so extension commands can close long-running resources when reload or shutdown begins, before admitted work is drained. Custom UI is now dismissed automatically at that boundary while preserving caller-signal cancellation.
- Added `wrapNodeSqliteDatabase(connection)` for caller-owned connections and facade-backed workflow journals that can share a database with the session store or use a user-supplied SQLite-compatible driver.
- Added user-installed journal adapter modules, environment/file secret references, shared `storage` defaults, and `StorageProvider` for explicit session/journal opens with shared database lifetime. `RunOpts.journal.database` accepts an async SQLite facade; invalid selections raise `StorageConfigError` without falling back to memory.
- Added `sessions.storage` and SDK `sessionStore` to select SQLite, explicit JSONL or memory, a caller-owned database, or a user-installed adapter. Shared `storage` settings now configure sessions too; SDK, CLI and environment overrides take precedence without merging selections. Store-open failures raise typed errors rather than falling back to memory.
- Added `--session-store <path>` and `--session-prefix <prefix>` to select a SQLite session database from the CLI. SQLite resume hints and Herdr commands always include `--session-store` and include `--session-prefix` when non-default.
- Added a Models Configuration reference generated from the `models.json` schema, listing provider, model, model override, and compatibility fields ([#10790](https://github.com/earendil-works/pi/issues/10790)).

### Changed

- The storage adapter conformance suite now checks call-time binary binding inside transactions.
- Node SQLite facade transactions opened on the same file now queue asynchronously within a process, including when paths use symlinks, instead of stalling the event loop and failing with a locked database.
- Changed the default NVIDIA model to `nvidia/nemotron-3.5-super-vl-120b-a12b` ([#10910](https://github.com/earendil-works/pi/pull/10910) by [@cv](https://github.com/cv)).
- Subagent parallel task limits can now be raised above 50 with `parallel.maxTasks`. Configure `parallel.concurrency` and the new `parallel.runningCapacity` together to run more than four child turns per parent; defaults remain 50 tasks, concurrency 3, and running capacity 4.

### Fixed

- Fixed resumed sessions dropping built-in tools activated during the session or reactivating tools that had been disabled. Default tool selections now restore the recorded loadout, with `--tools +name`/`-name` modifiers applied. Explicit tool selections and exclusions remain authoritative ([#10785](https://github.com/earendil-works/pi/issues/10785)).
- Fixed `read` line-range labels for numeric-string or null `offset`/`limit` arguments, such as those in sessions recorded with offset-based `read` tools, in the session tree, HTML exports, and the subagent example ([#9887](https://github.com/earendil-works/pi/issues/9887)).
- Fixed prompt templates dropping empty quoted arguments (`""` or `''`), which shifted later positional arguments and prevented positional defaults from applying ([#10867](https://github.com/earendil-works/pi/issues/10867)).
- Fixed `--model` choosing a provider by catalog order when several providers offer the match; Atomic now prefers the provider with credentials and otherwise reports the ambiguity. `--models` and `enabledModels` add a model for each provider that offers it and warn about the ambiguity. `--api-key` no longer lets stored credentials pick the provider, so a bare model ID offered by several providers needs `provider/model` alongside it, and a bare ID in `modelRouting.allowedModels` or `excludedModels` now covers every provider that offers it ([#10160](https://github.com/earendil-works/pi/issues/10160)).
- Fixed forked workflow stages and subagents losing their own tools when inheriting conversation history, and deferred startup reactivating extension tools disabled in a resumed session. Completing project-trust startup preserves the recorded selection and deliberate tool changes made by `session_start` handlers ([#3624](https://github.com/bastani-inc/atomic/pull/3624)).
- Fixed malformed tool-call arguments being executed or losing their original parse error in npm installations. Invalid calls now fail before argument preparation and schema validation, including tools whose arguments are all optional ([#3624](https://github.com/bastani-inc/atomic/pull/3624)).
- Fixed turns failing after about 14 seconds of Cloudflare 520-524 responses or connection resets. Agent-level retries now wait as long as the server asks through `Retry-After` headers or a Cloudflare `retry_after` body field, capped by `retry.provider.maxRetryDelayMs` (60s), and still stop after `retry.maxRetries`. Without a hint the 2s/4s/8s backoff is unchanged. Switching to a configured fallback model can therefore take up to about `retry.maxRetries` × 60s ([#3623](https://github.com/bastani-inc/atomic/issues/3623))
- Fixed running sessions retaining stale `auth.json` credentials and login status after another window signs in, logs out, or refreshes a token, including extension OAuth providers. Chat requests rejected for invalid credentials reread the store and retry once when newer credentials are available ([#3620](https://github.com/bastani-inc/atomic/issues/3620)).
- Fixed the OSC 7501 terminal program status showing `working` instead of `blocked` while the main chat waits on an `ask_user_question` prompt. It now reports `blocked` (`kind=question`) while the questionnaire is open and returns to `working` after you answer, matching workflow-stage prompts ([#3616](https://github.com/bastani-inc/atomic/issues/3616)).
- Fixed workflow approval gates (a run-level `ctx.ui.confirm`) reporting OSC 7501 `blocked` with `kind=question`. They now report `kind=permission`, so terminals can tell an approval apart from a typed question ([#3618](https://github.com/bastani-inc/atomic/issues/3618)).
- Fixed paused and cancelled workflow runs reporting OSC 7501 `done`. A paused or cancelled run now reports `idle`, and `done` is reserved for a successful completion ([#3617](https://github.com/bastani-inc/atomic/issues/3617)).
- Fixed the OSC 7501 status disappearing mid-run in terminals that drop it when transcript rendering emits a prompt mark. The reporter now re-sends its current status after the workflow graph and stage-chat overlay render, restoring the status during a run ([#3615](https://github.com/bastani-inc/atomic/issues/3615)).
- Fixed Xiaomi MiMo and all regional Token Plan providers defaulting to a deprecated model omitted from the generated catalog. They now select MiMo V2.6 Pro (`mimo-v2.6-pro`) by default.
- Fixed failed prompts sent after compaction dropping messages queued while the prompt was pending. Later steering, follow-up, and compaction messages now survive recovery without duplicating the original batch ([#3654](https://github.com/bastani-inc/atomic/issues/3654)).
- Fixed compaction file lists omitting hashline edits, including multi-file edits and nested tool calls. Edited files now appear under modified files rather than read-only files ([#3656](https://github.com/bastani-inc/atomic/issues/3656)).
- Fixed reordering filtered scoped models crashing or selecting a different model instead of saving the new order ([#3657](https://github.com/bastani-inc/atomic/issues/3657)).
- Fixed overlapping prompts duplicating deferred shell results in session history, and retries replaying results that were already saved.
- Fixed new and branched sessions becoming unwritable when a resumed session uses a different destination session directory.
- Fixed tree label edits appearing saved before persistence completed. Failed saves now display the error and keep the editor open for retry.
- Fixed failed custom-message writes dropping accepted pending and next-turn messages. Retries now retain the uncommitted messages in order, and overlapping drains do not duplicate saved messages.
- Fixed overlapping model, thinking-level and session-name changes losing later choices. Model and thinking cycles now advance once per request, and saved defaults retain the last choice.
- Fixed history refresh failures after a turn escaping error handling. The interactive engine now displays the failure without an unhandled rejection, and label edits await their saved history before reporting success.
- Renaming the active session in the selector writes through its active manager, so later session writes retain the correct sequence.
- Session pickers keep selection and rename bound to the selected project's store when IDs repeat across projects. Imported empty session IDs remain persistent across resume and restart instead of starting a new conversation.
- Fixed Alt+Y restoring stale text and moving the cursor before the start of the scoped-model search after clearing it with Ctrl+C ([#3668](https://github.com/bastani-inc/atomic/issues/3668)).
- Fixed MCP tools with colliding generated names overwriting another tool's owner. Generated names now avoid names already in use or reserved by other tools ([#3667](https://github.com/bastani-inc/atomic/issues/3667)).
- Fixed extension input dialogs ignoring the supplied placeholder. The hint now appears while the input is empty without becoming submitted text ([#3672](https://github.com/bastani-inc/atomic/issues/3672)).
- Session shutdown and replacement now wait for already-admitted extension session entries to finish saving before releasing their resources.
- Storage providers now preserve domain-adapter prefixes, make returned stores safe to close repeatedly, and retain typed opening errors when cleanup also fails.
- Fixed sessions written before format 4 disappearing from `/resume` and failing to open with `--continue`, `--session`, or `--fork`. Older supported sessions now upgrade automatically when opened; listing leaves their files unchanged.
- Fixed session stores closing during conversation replacement or remaining open after CLI shutdown and failed startup. Project storage settings now take effect on first trust approval, JSONL resume commands retain their directory, and SQLite sessions keep full URL-read artifacts.
- Fixed stopping a codemode script or reaching its deadline waiting for a non-interruptible built-in to return in the standalone binary ([#10748](https://github.com/earendil-works/pi/issues/10748)).
- Fixed repeated codemode timeouts or aborts accumulating surviving workers. At most two codemode workers may be alive across all sessions, and a cancelled worker counts against that limit until it actually exits ([#10748](https://github.com/earendil-works/pi/issues/10748)).
- Fixed sessions in symlinked project directories, including macOS temporary directories, being omitted from session lookup or mistaken for sessions from another project.
- Fixed compaction fallback and branch summaries failing with "generation hit the token cap" when reasoning used most of a fixed output budget. The provider now sizes summary output, and `compaction.reserveTokens` only controls when automatic compaction starts ([#9512](https://github.com/earendil-works/pi/issues/9512), [#9075](https://github.com/earendil-works/pi/issues/9075)).
- Fixed `models.schema.json` missing descriptions for provider and model fields, so editors now show hints for them ([#10790](https://github.com/earendil-works/pi/issues/10790)).
- Fixed sessions saved before the SQLite default disappearing from `/resume` and failing to open with `--continue`, `--session`, or `--fork`. With the default store, sessions in `~/.atomic/agent/sessions`, including workflow stage sessions and early 0.9.38 alpha sessions that use a `timestamp` header, are listed alongside newer ones and copied into the database only when you open one, so startup does no extra work. Original files are left untouched, deleted sessions stay deleted, and explicitly selected stores do not show them.
- Fixed slow interactive startup on machines with many sessions saved before the SQLite default, a follow-up to [#3721](https://github.com/bastani-inc/atomic/pull/3721). Opening a session by its exact ID, which the interactive engine does at launch, now looks up only that session instead of scanning `~/.atomic/agent/sessions`. `/resume`, `--resume`, and `--continue` list unconverted sessions larger than 32 KiB from their first message and most recent entries rather than parsing every file, so their message count is a lower bound and `/resume` search sees only that text until you open one, which still converts it in full.
- Fixed `/resume` and `--resume` filling up with "(no messages)" sessions. The picker now hides sessions that have no messages, name, or summary, including ones already saved by earlier versions. Opening such a session by ID still works.
- Fixed MCP Streamable HTTP servers that keep closing their GET event stream without sending event IDs causing endless reconnects. Only a new event ID or a stream that stays open resets the reconnect budget, so `reconnect.maxRetries` now bounds these reconnects ([#3729](https://github.com/bastani-inc/atomic/issues/3729)).
- Fixed queued steering and follow-up messages being reordered when an extension's async `input` hook takes longer for an earlier message than a later one. Each queue now keeps submission order. Extension commands, extension-sent messages, and messages sent from inside an `input` hook are still delivered immediately ([#3590](https://github.com/bastani-inc/atomic/issues/3590)).
- Fixed messages queued during compaction coming back out of order when the first one fails to send. The failed message stays at the front of the queue, followed by the rest of its batch and then anything you typed afterwards, both for **ALT+Up** retrieval and when they are sent again. This also applies to workflow stage chats ([#3664](https://github.com/bastani-inc/atomic/issues/3664)).

### Removed

- Removed DBOS, embedded Postgres, their bundled runtimes and native process ownership, and the non-durable storage fallback. Server-specific admission, recovery, outage handling, and deferred persistence queues are gone; workflow pause, resume, quit, answer, and checkpoint replay remain supported.

## [0.9.38-alpha.11] - 2026-10-17

### Fixed

- Fixed MCP Streamable HTTP servers that keep closing their GET event stream without sending event IDs causing endless reconnects. Only a new event ID or a stream that stays open resets the reconnect budget, so `reconnect.maxRetries` now bounds these reconnects ([#3729](https://github.com/bastani-inc/atomic/issues/3729)).
- Fixed queued steering and follow-up messages being reordered when an extension's async `input` hook takes longer for an earlier message than a later one. Each queue now keeps submission order. Extension commands, extension-sent messages, and messages sent from inside an `input` hook are still delivered immediately ([#3590](https://github.com/bastani-inc/atomic/issues/3590)).
- Fixed messages queued during compaction coming back out of order when the first one fails to send. The failed message stays at the front of the queue, followed by the rest of its batch and then anything you typed afterwards, both for **ALT+Up** retrieval and when they are sent again. This also applies to workflow stage chats ([#3664](https://github.com/bastani-inc/atomic/issues/3664)).

## [0.9.38-alpha.10] - 2026-10-17

### Fixed

- Fixed `/resume` and `--resume` filling up with "(no messages)" sessions. The picker now hides sessions that have no messages, name, or summary, including ones already saved by earlier versions. Opening such a session by ID still works.

## [0.9.38-alpha.9] - 2026-10-17

### Fixed

- Fixed slow interactive startup on machines with many sessions saved before the SQLite default, a follow-up to [#3721](https://github.com/bastani-inc/atomic/pull/3721). Opening a session by its exact ID, which the interactive engine does at launch, now looks up only that session instead of scanning `~/.atomic/agent/sessions`. `/resume`, `--resume`, and `--continue` list unconverted sessions larger than 32 KiB from their first message and most recent entries rather than parsing every file, so their message count is a lower bound and `/resume` search sees only that text until you open one, which still converts it in full.

## [0.9.38-alpha.8] - 2026-10-17

### Changed

- Subagent parallel task limits can now be raised above 50 with `parallel.maxTasks`. Configure `parallel.concurrency` and the new `parallel.runningCapacity` together to run more than four child turns per parent; defaults remain 50 tasks, concurrency 3, and running capacity 4.

### Fixed

- Fixed sessions saved before the SQLite default disappearing from `/resume` and failing to open with `--continue`, `--session`, or `--fork`. With the default store, sessions in `~/.atomic/agent/sessions`, including workflow stage sessions and early 0.9.38 alpha sessions that use a `timestamp` header, are listed alongside newer ones and copied into the database only when you open one, so startup does no extra work. Original files are left untouched, deleted sessions stay deleted, and explicitly selected stores do not show them.

## [0.9.38-alpha.7] - 2026-10-17

### Breaking Changes

- `RunOpts.journal.database` now takes an async `SqliteDatabase` facade, not a raw `node:sqlite` connection; use `wrapNodeSqliteDatabase` for an existing connection. Journal configuration failures now use `StorageConfigError` with `source`, `field`, and `reason` instead of `JournalConfigurationError`.
- Workflow stage forks now take `forkFromSessionId`, and stage resume, retained chat, and subagent forks resolve IDs within the selected session store. File paths are not session identities. Embedded chat hosts must use `await host.loadSession(store, sessionId)` instead of `loadSessionFile(path)`.
- Sessions now default to the local SQLite database shared with workflow runs, rather than JSONL files. JSONL must be selected explicitly. Removed the `sessionDir` setting without an alias; use `sessions.storage.dir` instead.

### Added

- Added user-installed journal adapter modules, environment/file secret references, shared `storage` defaults, and `StorageProvider` for explicit session/journal opens with shared database lifetime. `RunOpts.journal.database` accepts an async SQLite facade; invalid selections raise `StorageConfigError` without falling back to memory.
- Added `sessions.storage` and SDK `sessionStore` to select SQLite, explicit JSONL or memory, a caller-owned database, or a user-installed adapter. Shared `storage` settings now configure sessions too; SDK, CLI and environment overrides take precedence without merging selections. Store-open failures raise typed errors rather than falling back to memory.
- Added `--session-store <path>` and `--session-prefix <prefix>` to select a SQLite session database from the CLI. SQLite resume hints and Herdr commands always include `--session-store` and include `--session-prefix` when non-default.
- Added a Models Configuration reference generated from the `models.json` schema, listing provider, model, model override, and compatibility fields ([#10790](https://github.com/earendil-works/pi/issues/10790)).

### Changed

- Changed the default NVIDIA model to `nvidia/nemotron-3.5-super-vl-120b-a12b` ([#10910](https://github.com/earendil-works/pi/pull/10910) by [@cv](https://github.com/cv)).

### Fixed

- Session shutdown and replacement now wait for already-admitted extension session entries to finish saving before releasing their resources.
- Storage providers now preserve domain-adapter prefixes, make returned stores safe to close repeatedly, and retain typed opening errors when cleanup also fails.
- Fixed sessions written before format 4 disappearing from `/resume` and failing to open with `--continue`, `--session`, or `--fork`. Older supported sessions now upgrade automatically when opened; listing leaves their files unchanged.
- Fixed session stores closing during conversation replacement or remaining open after CLI shutdown and failed startup. Project storage settings now take effect on first trust approval, JSONL resume commands retain their directory, and SQLite sessions keep full URL-read artifacts.
- Fixed stopping a codemode script or reaching its deadline waiting for a non-interruptible built-in to return in the standalone binary ([#10748](https://github.com/earendil-works/pi/issues/10748)).
- Fixed repeated codemode timeouts or aborts accumulating surviving workers. At most two codemode workers may be alive across all sessions, and a cancelled worker counts against that limit until it actually exits ([#10748](https://github.com/earendil-works/pi/issues/10748)).
- Fixed sessions in symlinked project directories, including macOS temporary directories, being omitted from session lookup or mistaken for sessions from another project.
- Fixed compaction fallback and branch summaries failing with "generation hit the token cap" when reasoning used most of a fixed output budget. The provider now sizes summary output, and `compaction.reserveTokens` only controls when automatic compaction starts ([#9512](https://github.com/earendil-works/pi/issues/9512), [#9075](https://github.com/earendil-works/pi/issues/9075)).
- Fixed `models.schema.json` missing descriptions for provider and model fields, so editors now show hints for them ([#10790](https://github.com/earendil-works/pi/issues/10790)).

## [0.9.38-alpha.6] - 2026-10-16

### Changed

- The storage adapter conformance suite now checks call-time binary binding inside transactions.
- Node SQLite facade transactions opened on the same file now queue asynchronously within a process, including when paths use symlinks, instead of stalling the event loop and failing with a locked database.

### Breaking Changes

- Removed the workflow `durability` option, `WorkflowDurability`, and `WorkflowDurabilityRequiredError`. Use `RunOpts.journal` to select storage or isolate runs, and handle typed journal errors when storage is unavailable.
- Removed `InMemoryDurableBackend` and `createInMemoryTestBackend`. Use `JournalDurableBackend` over `MemoryRunJournal` for an explicitly in-memory backend. Removed the `legacyRecovery` workflow-tool argument and remaining `workflows.durability` settings.
- Removed the `@bastani/workflows` import alias; import workflow APIs from `@bastani/atomic/workflows`.
- Custom `RunJournal` implementations must provide atomic `deleteOwnedRun(lease)`, and invocation records require `invocationFingerprint`.
- `SessionManager.create`, `open`, `continueRecent`, `forkFrom`, and mutations now return promises. Await session writes, branch changes, labels, names, and model/thinking changes before reading their results or continuing dependent work. Extension `pi.appendEntry`, `pi.setSessionName`, `pi.setLabel`, and `pi.setThinkingLevel` must also be awaited. Persistent session creation writes its header immediately, and setup entries commit without waiting for the first message. A failed write rejects that call with its typed storage error without publishing the failed entry; there is no `flush` API. Explicit in-memory sessions remain available synchronously through `SessionManager.inMemory()`.
- Session headers use the format-4 fields `createdAt` and `parentSessionId`, including the header returned by `SessionManager.getHeader()`. The `timestamp` and `parentSession` header fields are removed.
- Extension provider registration and removal now return promises too: await `pi.registerProvider`, `pi.registerProviderAlias`, and `pi.unregisterProvider` before dependent model or session work.
- Workflow SQLite journals now use `SqlRunJournal` with async initialization and `wf_` table names. Use `await openSqlRunJournal(pathOrDatabase?)` or `await SqlRunJournal.open(database, { prefix })`, and await `resolveRunJournal`. Earlier unreleased bare tables are not read or migrated. Workflow `StorageUnavailableError` now provides `adapter`, `location`, and `cause` instead of `path`.
- Session selection now uses IDs instead of file paths: `--session` and `--fork` accept exact IDs or unique eight-hex UUID prefixes; use `--import <file>` for format-4 JSONL exports. Extension `switchSession` takes an ID, lifecycle events use `previousSessionId` and `targetSessionId`, and RPC `switch_session` takes `sessionId`. RPC state reports `sessionLocation` for optional display instead of `sessionFile`. Shell tools no longer expose `ATOMIC_SESSION_FILE` or `PI_SESSION_FILE`; use `ATOMIC_SESSION_ID` or `PI_SESSION_ID`.

### Added

- Added the `workflows.journal.path` and `workflows.journal.memory` settings, plus SDK `RunOpts.journal` for a file, caller-owned SQLite connection, explicit memory journal, or supplied journal. TUI, CLI, background, and SDK runs use the selected journal. Relative settings paths resolve against the Atomic home directory.
- Added `ctx.sessionSignal` so extension commands can close long-running resources when reload or shutdown begins, before admitted work is drained. Custom UI is now dismissed automatically at that boundary while preserving caller-signal cancellation.
- Added `wrapNodeSqliteDatabase(connection)` for caller-owned connections and facade-backed workflow journals that can share a database with the session store or use a user-supplied SQLite-compatible driver.

### Fixed

- Fixed overlapping prompts duplicating deferred shell results in session history, and retries replaying results that were already saved.
- Fixed new and branched sessions becoming unwritable when a resumed session uses a different destination session directory.
- Fixed tree label edits appearing saved before persistence completed. Failed saves now display the error and keep the editor open for retry.
- Fixed failed custom-message writes dropping accepted pending and next-turn messages. Retries now retain the uncommitted messages in order, and overlapping drains do not duplicate saved messages.
- Fixed overlapping model, thinking-level and session-name changes losing later choices. Model and thinking cycles now advance once per request, and saved defaults retain the last choice.
- Fixed history refresh failures after a turn escaping error handling. The interactive engine now displays the failure without an unhandled rejection, and label edits await their saved history before reporting success.
- Renaming the active session in the selector writes through its active manager, so later session writes retain the correct sequence.
- Session pickers keep selection and rename bound to the selected project's store when IDs repeat across projects. Imported empty session IDs remain persistent across resume and restart instead of starting a new conversation.
- Fixed Alt+Y restoring stale text and moving the cursor before the start of the scoped-model search after clearing it with Ctrl+C ([#3668](https://github.com/bastani-inc/atomic/issues/3668)).
- Fixed MCP tools with colliding generated names overwriting another tool's owner. Generated names now avoid names already in use or reserved by other tools ([#3667](https://github.com/bastani-inc/atomic/issues/3667)).
- Fixed extension input dialogs ignoring the supplied placeholder. The hint now appears while the input is empty without becoming submitted text ([#3672](https://github.com/bastani-inc/atomic/issues/3672)).

## [0.9.38-alpha.4] - 2026-10-16

### Breaking Changes

- Workflow discovery now uses only `.atomic` default roots, and discovered modules use `@bastani/atomic/workflows` with `typebox`. Removed deprecated workflow API stubs, separate reasoning authoring fields, old stage-target and X10 mouse input formats, and replay without current topology, replay keys, or child exit tags. Use model strings such as `provider/model:high` for reasoning effort. Workflow results, inspection details and status summaries always include their tool collections. Queued stage delivery requires persisted admission order.
- Workflow heartbeat records require their original launch cadence instead of inheriting the current definition when cadence is missing. Configuration load results always include their global and project source fields, using `null` for absent or invalid sources.
- Workflow run snapshots require `toolNodes`, and inline forms require injected keybindings without obsolete submit-choice state. Removed the ignored workflow text-truncation `preserveAnsi` argument; ANSI-aware rendering is unchanged.
- Workflow runs now persist only in the local SQLite journal. Journal initialization failures throw `StorageUnavailableError` instead of continuing without durable storage. Postgres connection settings (`workflows.durability.systemDatabaseUrl` and `workflows.durability.poolSize`) and `DBOS_SYSTEM_DATABASE_URL`, `ATOMIC_POSTGRES_*`, and `PG*` environment variables no longer configure workflow storage.
- Removed automatic session-format migrations and the `migrateSessionEntries` export. Reading session files no longer appends missing newlines.

### Added

- Added the async SQLite facade and node adapter through `@bastani/atomic/storage`, with typed storage errors, prefix and schema-version helpers, and an adapter conformance suite in `@bastani/atomic/storage/testing`. String bindings containing NUL are rejected with `StorageInvalidTextError`; zero bytes remain supported in binary values.
- Added the `workflows.journal.path` and `workflows.journal.memory` settings schema, plus SDK `RunOpts.journal` types for a file, caller-owned SQLite connection, explicit memory journal, or supplied journal. Journal selection is exposed through `resolveRunJournal`; runtime wiring is deferred.

### Fixed

- Fixed Xiaomi MiMo and all regional Token Plan providers defaulting to a deprecated model omitted from the generated catalog. They now select MiMo V2.6 Pro (`mimo-v2.6-pro`) by default.
- Fixed failed prompts sent after compaction dropping messages queued while the prompt was pending. Later steering, follow-up, and compaction messages now survive recovery without duplicating the original batch ([#3654](https://github.com/bastani-inc/atomic/issues/3654)).
- Fixed compaction file lists omitting hashline edits, including multi-file edits and nested tool calls. Edited files now appear under modified files rather than read-only files ([#3656](https://github.com/bastani-inc/atomic/issues/3656)).
- Fixed reordering filtered scoped models crashing or selecting a different model instead of saving the new order ([#3657](https://github.com/bastani-inc/atomic/issues/3657)).

### Removed

- Removed DBOS, embedded Postgres, their bundled runtimes and native process ownership, and the non-durable storage fallback. Server-specific admission, recovery, outage handling, and deferred persistence queues are gone; workflow pause, resume, quit, answer, and checkpoint replay remain supported.

## [0.9.38-alpha.3] - 2026-10-15

### Fixed

- Fixed the OSC 7501 terminal program status showing `working` instead of `blocked` while the main chat waits on an `ask_user_question` prompt. It now reports `blocked` (`kind=question`) while the questionnaire is open and returns to `working` after you answer, matching workflow-stage prompts ([#3616](https://github.com/bastani-inc/atomic/issues/3616)).
- Fixed workflow approval gates (a run-level `ctx.ui.confirm`) reporting OSC 7501 `blocked` with `kind=question`. They now report `kind=permission`, so terminals can tell an approval apart from a typed question ([#3618](https://github.com/bastani-inc/atomic/issues/3618)).
- Fixed paused and cancelled workflow runs reporting OSC 7501 `done`. A paused or cancelled run now reports `idle`, and `done` is reserved for a successful completion ([#3617](https://github.com/bastani-inc/atomic/issues/3617)).
- Fixed the OSC 7501 status disappearing mid-run in terminals that drop it when transcript rendering emits a prompt mark. The reporter now re-sends its current status after the workflow graph and stage-chat overlay render, restoring the status during a run ([#3615](https://github.com/bastani-inc/atomic/issues/3615)).

## [0.9.38-alpha.2] - 2026-10-15

### Fixed

- Fixed running sessions retaining stale `auth.json` credentials and login status after another window signs in, logs out, or refreshes a token, including extension OAuth providers. Chat requests rejected for invalid credentials reread the store and retry once when newer credentials are available ([#3620](https://github.com/bastani-inc/atomic/issues/3620)).

## [0.9.38-alpha.1] - 2026-10-15

### Added

- Added a guide to running local models on NVIDIA RTX GPUs with llama.cpp, including recipe prerequisites, router preset conversion, and connecting Atomic.
- Changing the thinking level mid-session on OpenAI GPT-6 models with an API key now preserves the prompt cache ([#10853](https://github.com/earendil-works/pi/issues/10853)).
- Added `formatReadLineRange` to the SDK exports so extensions can render `read` line-range labels the same way Atomic does.

### Fixed

- Fixed resumed sessions dropping built-in tools activated during the session or reactivating tools that had been disabled. Default tool selections now restore the recorded loadout, with `--tools +name`/`-name` modifiers applied. Explicit tool selections and exclusions remain authoritative ([#10785](https://github.com/earendil-works/pi/issues/10785)).
- Fixed `read` line-range labels for numeric-string or null `offset`/`limit` arguments, such as those in sessions recorded with offset-based `read` tools, in the session tree, HTML exports, and the subagent example ([#9887](https://github.com/earendil-works/pi/issues/9887)).
- Fixed prompt templates dropping empty quoted arguments (`""` or `''`), which shifted later positional arguments and prevented positional defaults from applying ([#10867](https://github.com/earendil-works/pi/issues/10867)).
- Fixed `--model` choosing a provider by catalog order when several providers offer the match; Atomic now prefers the provider with credentials and otherwise reports the ambiguity. `--models` and `enabledModels` add a model for each provider that offers it and warn about the ambiguity. `--api-key` no longer lets stored credentials pick the provider, so a bare model ID offered by several providers needs `provider/model` alongside it, and a bare ID in `modelRouting.allowedModels` or `excludedModels` now covers every provider that offers it ([#10160](https://github.com/earendil-works/pi/issues/10160)).
- Fixed forked workflow stages and subagents losing their own tools when inheriting conversation history, and deferred startup reactivating extension tools disabled in a resumed session. Completing project-trust startup preserves the recorded selection and deliberate tool changes made by `session_start` handlers ([#3624](https://github.com/bastani-inc/atomic/pull/3624)).
- Fixed malformed tool-call arguments being executed or losing their original parse error in npm installations. Invalid calls now fail before argument preparation and schema validation, including tools whose arguments are all optional ([#3624](https://github.com/bastani-inc/atomic/pull/3624)).
- Fixed turns failing after about 14 seconds of Cloudflare 520-524 responses or connection resets. Agent-level retries now wait as long as the server asks through `Retry-After` headers or a Cloudflare `retry_after` body field, capped by `retry.provider.maxRetryDelayMs` (60s), and still stop after `retry.maxRetries`. Without a hint the 2s/4s/8s backoff is unchanged. Switching to a configured fallback model can therefore take up to about `retry.maxRetries` × 60s ([#3623](https://github.com/bastani-inc/atomic/issues/3623))

## [0.9.36] - 2026-10-13

### Fixed

- Fixed Ultrafast choices disappearing when a newer remote model catalog omitted the bundled service-tier metadata.
- Fixed GPT-6.1 Sol subscription sign-in through `openai` and account aliases retaining the API's 272,000-token context. Subscription selections now use 500,000 tokens with no long-context price tier on Standard, Fast, and Ultrafast; API-key selections keep their API limits and pricing. Codex subscription limits also survive remote catalog refreshes.
- Fixed messages queued during compaction being overtaken by later messages of the same type. Held messages now join their steering or follow-up queue as soon as compaction ends, including mid-turn compaction.

## [0.9.36-alpha.1] - 2026-10-13

### Fixed

- Fixed Ultrafast choices disappearing when a newer remote model catalog omitted the bundled service-tier metadata.
- Fixed GPT-6.1 Sol subscription sign-in through `openai` and account aliases retaining the API's 272,000-token context. Subscription selections now use 500,000 tokens with no long-context price tier on Standard, Fast, and Ultrafast; API-key selections keep their API limits and pricing. Codex subscription limits also survive remote catalog refreshes.
- Fixed messages queued during compaction being overtaken by later messages of the same type. Held messages now join their steering or follow-up queue as soon as compaction ends, including mid-turn compaction.

## [0.9.35] - 2026-10-13

### Breaking Changes

- Removed the experimental `@bastani/atomic/client` entrypoint and the `@earendil-works/pi-client` and `@earendil-works/pi-protocol` dependencies, which upstream Pi no longer publishes. SDK code that imported `@bastani/atomic/client` should use `createAgentSession()` from `@bastani/atomic` in-process or RPC mode (`--mode rpc` / `@bastani/atomic/rpc-entry`), or pin the previous release. The CLI, RPC mode, extensions and workflows are unaffected.

### Added

- `session.workflows.resume(runId)` now adopts a paused run that was launched through the model-facing `workflow` tool once its owning session has been disposed, in the same process or after the owning process exits. Disposing a session quits its in-flight runs at their last checkpoint and durably records that it released its checkpointed paused runs; the adopting session becomes the owner and the run continues under the same UUID. Runs whose owner is still open, runs whose owner crashed without disposing, and finished runs are still refused ([#3566](https://github.com/bastani-inc/atomic/issues/3566)).
- Switching sessions with `/new`, `/fork`, or `/resume` now releases the previous session's paused workflow runs, the same way disposing a session does. The model in the new session can inspect them with the `workflow` tool's `status` and `stages` actions and resume them, and `session.workflows.resume(runId)` can adopt them; adoption, including the `/workflow resume <id>` slash command, transfers ownership to the resuming session. The new session lists the released runs with their `/workflow resume <id>` commands, and, in the process that released it, a run another session already adopted now reports that it is owned by another session. `/reload` still keeps runs executing and releases nothing ([#3569](https://github.com/bastani-inc/atomic/issues/3569)).
- SDK and RPC clients can read stored session summaries with their summarized-through message IDs, and list or inspect workflow history with saved summaries and run identities. SDK sessions expose `sessionSummary`; RPC adds `get_session_summary`, `list_sessions`, `list_workflow_runs`, and `get_workflow_run_summary`.
- Added `ATOMIC_CLIPBOARD_OSC52=1`, with legacy alias `PI_CLIPBOARD_OSC52=1`, to send copied text through OSC 52 after the local clipboard write. Use it when a multiplexer or remote IDE session leaves no SSH markers for Atomic to detect.
- Added elapsed time while a codemode script runs and total time when it finishes. Bash commands continued in the background keep their "Observed for" timing label.
- Codemode declarations now include input constraints such as integer bounds and defaults that TypeScript types alone cannot express.
- Added per-key OpenRouter model lists with the key's context limits, output limits and prices. Configured regional endpoints are respected, and the personalized list is saved for offline use.
- Added `workflows.durability.poolSize` to configure maximum connections per workflow database pool for managed and remote PostgreSQL (default 10, minimum 2).

### Changed

- Changed `serializeConversation()` (branch summaries, resume-picker session summaries, and the policy-refusal summary fallback) to omit thinking blocks; replayed thinking is often not in the live context, so including it could make a summarization request larger than the context it summarizes and fail. Verbatim line compaction still serializes thinking lines.
- Changed `openai-codex/gpt-6.1-sol` to a 500,000-token context window (up from 272,000) with no long-context price tier on Standard, Fast, or Ultrafast. OpenAI raised GPT-6.1 Sol's default context to 500k in Codex & Work for ChatGPT subscriptions and does not charge long-context fees when usage draws from the subscription. `openai/gpt-6.1-sol` and the other Codex models are unchanged.
- MCP tools now describe their content blocks and nested structured-content fields directly in codemode declarations, including schemas with local references.
- `showHardwareCursor` now uses only the terminal cursor at the focused input position instead of also drawing a reverse-video cursor cell. Custom TUI components can use `renderFakeCursor()` with `CURSOR_MARKER` to follow this setting.

### Fixed

- Fixed the code mode tool description claiming every failed tool call throws; a tool that declares an output schema resolves with its value on failure too (for example `bash` with a non-zero exit), so scripts should check its fields.
- Fixed `read` reporting success when it refuses a selected range as too large. The refusal is now an error result, so models see a failed read with guidance to read a smaller range, and code mode's `tools.read(...)` throws instead of resolving with the refusal text as if it were the file's contents ([#3574](https://github.com/bastani-inc/atomic/issues/3574)).
- Fixed `/resume` and `atomic -r` hiding session summaries after newer messages. The latest summary now stays visible until a branch summary retires it, and opening or resuming an interactive session refreshes missing or stale summaries in the background.
- Fixed tree navigation moving the active branch while a user prompt is waiting for `before_agent_start` handlers to finish.
- Fixed print mode returning success for responses truncated by the model output limit. Partial text remains on stdout, with a warning on stderr and exit code 1.
- Fixed home-directory (`~`) expansion in resource include and exclude patterns, including Windows environments where `HOME` differs from `USERPROFILE`.
- Fixed MCP tool calls rejecting array, null, or primitive structured content.
- Fixed cancelled MCP requests being retried after a shared authorization refresh.
- Fixed slow MCP resource listings delaying tools on connection. Resources and templates are now listed only on demand.
- Fixed code mode taking excessive time to join large numbers of text output items.
- Fixed transient Windows permission errors when acquiring the authentication lock, while keeping persistent permission failures bounded.
- Fixed runs started by `pi.sendMessage(..., { triggerTurn: true })`, including protected workflow task completions, skipping `before_agent_start` and losing handler-added system prompt sections after tool calls. The event now distinguishes user and custom messages with `messageType`; handlers reading `prompt` or `images` must first narrow to `"user"`.
- Fixed interrupt messages dropping an earlier custom message while its `before_agent_start` handler was still running.
- Fixed misplaced `// @options:` lines silently being ignored. They now fail the codemode call before the script runs; options must be on the first line.
- Fixed Kitty image detection inside Herdr terminals and delayed terminal-reply bytes leaking into keyboard input.
- Fixed shifted-symbol keybindings such as `+` using the physical key name instead of the produced symbol.
- Fixed OpenRouter account model refresh being blocked by an unavailable or invalid optional pi.dev catalog.
- Fixed running codemode and bash rows continuing to update elapsed-time displays after removal.

## [0.9.35-alpha.5] - 2026-10-12

### Added

- Added `workflows.durability.poolSize` to configure maximum connections per workflow database pool for managed and remote PostgreSQL (default 10, minimum 2).

## [0.9.35-alpha.4] - 2026-10-13

### Breaking Changes

- Removed the experimental `@bastani/atomic/client` entrypoint and the `@earendil-works/pi-client` and `@earendil-works/pi-protocol` dependencies, which upstream Pi no longer publishes. SDK code that imported `@bastani/atomic/client` should use `createAgentSession()` from `@bastani/atomic` in-process or RPC mode (`--mode rpc` / `@bastani/atomic/rpc-entry`), or pin the previous release. The CLI, RPC mode, extensions and workflows are unaffected.

### Added

- Added `ATOMIC_CLIPBOARD_OSC52=1`, with legacy alias `PI_CLIPBOARD_OSC52=1`, to send copied text through OSC 52 after the local clipboard write. Use it when a multiplexer or remote IDE session leaves no SSH markers for Atomic to detect.
- Added elapsed time while a codemode script runs and total time when it finishes. Bash commands continued in the background keep their "Observed for" timing label.
- Codemode declarations now include input constraints such as integer bounds and defaults that TypeScript types alone cannot express.
- Added per-key OpenRouter model lists with the key's context limits, output limits and prices. Configured regional endpoints are respected, and the personalized list is saved for offline use.

### Changed

- MCP tools now describe their content blocks and nested structured-content fields directly in codemode declarations, including schemas with local references.
- `showHardwareCursor` now uses only the terminal cursor at the focused input position instead of also drawing a reverse-video cursor cell. Custom TUI components can use `renderFakeCursor()` with `CURSOR_MARKER` to follow this setting.

### Fixed

- Fixed misplaced `// @options:` lines silently being ignored. They now fail the codemode call before the script runs; options must be on the first line.
- Fixed Kitty image detection inside Herdr terminals and delayed terminal-reply bytes leaking into keyboard input.
- Fixed shifted-symbol keybindings such as `+` using the physical key name instead of the produced symbol.
- Fixed OpenRouter account model refresh being blocked by an unavailable or invalid optional pi.dev catalog.
- Fixed running codemode and bash rows continuing to update elapsed-time displays after removal.

## [0.9.35-alpha.3] - 2026-10-12

### Added

- SDK and RPC clients can read stored session summaries with their summarized-through message IDs, and list or inspect workflow history with saved summaries and run identities. SDK sessions expose `sessionSummary`; RPC adds `get_session_summary`, `list_sessions`, `list_workflow_runs`, and `get_workflow_run_summary`.

### Fixed

- Fixed `/resume` and `atomic -r` hiding session summaries after newer messages. The latest summary now stays visible until a branch summary retires it, and opening or resuming an interactive session refreshes missing or stale summaries in the background.
- Fixed tree navigation moving the active branch while a user prompt is waiting for `before_agent_start` handlers to finish.
- Fixed print mode returning success for responses truncated by the model output limit. Partial text remains on stdout, with a warning on stderr and exit code 1.
- Fixed home-directory (`~`) expansion in resource include and exclude patterns, including Windows environments where `HOME` differs from `USERPROFILE`.
- Fixed MCP tool calls rejecting array, null, or primitive structured content.
- Fixed cancelled MCP requests being retried after a shared authorization refresh.
- Fixed slow MCP resource listings delaying tools on connection. Resources and templates are now listed only on demand.
- Fixed code mode taking excessive time to join large numbers of text output items.
- Fixed transient Windows permission errors when acquiring the authentication lock, while keeping persistent permission failures bounded.
- Fixed runs started by `pi.sendMessage(..., { triggerTurn: true })`, including protected workflow task completions, skipping `before_agent_start` and losing handler-added system prompt sections after tool calls. The event now distinguishes user and custom messages with `messageType`; handlers reading `prompt` or `images` must first narrow to `"user"`.
- Fixed interrupt messages dropping an earlier custom message while its `before_agent_start` handler was still running.

## [0.9.35-alpha.2] - 2026-10-12

### Fixed

- Fixed `read` reporting success when it refuses a selected range as too large. The refusal is now an error result, so models see a failed read with guidance to read a smaller range, and code mode's `tools.read(...)` throws instead of resolving with the refusal text as if it were the file's contents ([#3574](https://github.com/bastani-inc/atomic/issues/3574)).

## [0.9.35-alpha.1] - 2026-10-12

### Added

- `session.workflows.resume(runId)` now adopts a paused run that was launched through the model-facing `workflow` tool once its owning session has been disposed, in the same process or after the owning process exits. Disposing a session quits its in-flight runs at their last checkpoint and durably records that it released its checkpointed paused runs; the adopting session becomes the owner and the run continues under the same UUID. Runs whose owner is still open, runs whose owner crashed without disposing, and finished runs are still refused ([#3566](https://github.com/bastani-inc/atomic/issues/3566)).
- Switching sessions with `/new`, `/fork`, or `/resume` now releases the previous session's paused workflow runs, the same way disposing a session does. The model in the new session can inspect them with the `workflow` tool's `status` and `stages` actions and resume them, and `session.workflows.resume(runId)` can adopt them; adoption, including the `/workflow resume <id>` slash command, transfers ownership to the resuming session. The new session lists the released runs with their `/workflow resume <id>` commands, and, in the process that released it, a run another session already adopted now reports that it is owned by another session. `/reload` still keeps runs executing and releases nothing ([#3569](https://github.com/bastani-inc/atomic/issues/3569)).

### Changed

- Changed `serializeConversation()` (branch summaries, resume-picker session summaries, and the policy-refusal summary fallback) to omit thinking blocks; replayed thinking is often not in the live context, so including it could make a summarization request larger than the context it summarizes and fail. Verbatim line compaction still serializes thinking lines.
- Changed `openai-codex/gpt-6.1-sol` to a 500,000-token context window (up from 272,000) with no long-context price tier on Standard, Fast, or Ultrafast. OpenAI raised GPT-6.1 Sol's default context to 500k in Codex & Work for ChatGPT subscriptions and does not charge long-context fees when usage draws from the subscription. `openai/gpt-6.1-sol` and the other Codex models are unchanged.

### Fixed

- Fixed the code mode tool description claiming every failed tool call throws; a tool that declares an output schema resolves with its value on failure too (for example `bash` with a non-zero exit), so scripts should check its fields.

## [0.9.34] - 2026-10-11

### Fixed

- Fixed extension widgets, footer, header, and statuses set by the new session's `session_start` being cleared after switching sessions (`/resume`, new session, fork, `ctx.switchSession`). The default interactive mode cleared them after a built-in `/resume`, new session, or fork, and a switch made from an extension command cleared them when the previous session still had tracked work running. The previous session's `session_shutdown` handlers can no longer clear the new session's UI either ([#3561](https://github.com/bastani-inc/atomic/issues/3561)).

## [0.9.34-alpha.1] - 2026-10-11

### Fixed

- Fixed extension widgets, footer, header, and statuses set by the new session's `session_start` being cleared after switching sessions (`/resume`, new session, fork, `ctx.switchSession`). The default interactive mode cleared them after a built-in `/resume`, new session, or fork, and a switch made from an extension command cleared them when the previous session still had tracked work running. The previous session's `session_shutdown` handlers can no longer clear the new session's UI either ([#3561](https://github.com/bastani-inc/atomic/issues/3561)).

## [0.9.33] - 2026-10-10

### Fixed

- Fixed workflows falling back to a non-durable in-memory backend after a macOS reboot because the managed PostgreSQL cluster was refused with `Managed Postgres cluster identity mismatch` when only the data volume's device number changed. The cluster now attaches and its ownership record is updated when the data directory inode, path, cluster ID, major version, and PostgreSQL system identifier still match; genuine identity mismatches name the fields that differ ([#3555](https://github.com/bastani-inc/atomic/issues/3555)).
- Fixed terminal program status (OSC 7501) ignoring workflow runs: it now reports `working` while a workflow run executes, `blocked` while a run or stage waits for your input, `error` when a run fails (held until your next message, even after the run-end notice turn settles), and `done` once the agent and all runs finish ([#3556](https://github.com/bastani-inc/atomic/issues/3556)).

## [0.9.33-alpha.1] - 2026-10-10

### Fixed

- Fixed workflows falling back to a non-durable in-memory backend after a macOS reboot because the managed PostgreSQL cluster was refused with `Managed Postgres cluster identity mismatch` when only the data volume's device number changed. The cluster now attaches and its ownership record is updated when the data directory inode, path, cluster ID, major version, and PostgreSQL system identifier still match; genuine identity mismatches name the fields that differ ([#3555](https://github.com/bastani-inc/atomic/issues/3555)).
- Fixed terminal program status (OSC 7501) ignoring workflow runs: it now reports `working` while a workflow run executes, `blocked` while a run or stage waits for your input, `error` when a run fails (held until your next message, even after the run-end notice turn settles), and `done` once the agent and all runs finish ([#3556](https://github.com/bastani-inc/atomic/issues/3556)).

## [0.9.32] - 2026-10-10

### Fixed

- Fixed workflow stages in SDK-hosted sessions losing extensions supplied through `additionalExtensionPaths` and `extensionFactories`, and using the host process directory instead of the owning session's working directory when no stage `cwd` is set. Stage reports use that same resolved directory ([#3551](https://github.com/bastani-inc/atomic/issues/3551)).

## [0.9.32-alpha.1] - 2026-10-10

### Fixed

- Fixed workflow stages in SDK-hosted sessions losing extensions supplied through `additionalExtensionPaths` and `extensionFactories`, and using the host process directory instead of the owning session's working directory when no stage `cwd` is set. Stage reports use that same resolved directory ([#3551](https://github.com/bastani-inc/atomic/issues/3551)).

## [0.9.31] - 2026-10-09

### Fixed

- Interactive startup now selects a `defaultProvider`/`defaultModel` registered by an extension loaded from a package or the `extensions` setting, instead of warning "Configured default model is unavailable or unsupported" and starting with no model.

## [0.9.31-alpha.1] - 2026-10-09

### Fixed

- Interactive startup now selects a `defaultProvider`/`defaultModel` registered by an extension loaded from a package or the `extensions` setting, instead of warning "Configured default model is unavailable or unsupported" and starting with no model.

## [0.9.30] - 2026-10-09

### Breaking Changes

- Moved the published theme JSON Schema to `schemas/theme.schema.json` and made theme loading reject unknown top-level properties and unknown properties under `colors`, `workingIndicator`, or `export`. Update existing theme `$schema` references to `https://raw.githubusercontent.com/bastani-inc/atomic/main/packages/coding-agent/schemas/theme.schema.json`, define reusable custom colors under `vars`, and remove unsupported metadata.

### Added

- Added JSON Schemas for `settings.json`, `keybindings.json`, `models.json`, and theme files, published in the package's `schemas/` directory. Add `$schema` to a file for editor completion and validation; Atomic ignores `$schema` in `keybindings.json` instead of treating it as an action. See [Settings](docs/settings.md), [Keybindings](docs/keybindings.md), [Custom models](docs/models.md), and [Themes](docs/themes.md).

### Changed

- `models.json` now checks the type of every `compat` field, including `thinkingTokenBudgetField`, `zaiToolStream`, and `delegatesThinkingModelBinding`, which were previously accepted unchecked, and rejects non-positive `contextWindow` and `maxTokens` values in `modelOverrides`.

### Fixed

- Fixed Mistral streams being aborted mid-generation (e.g. during long thinking) after the request timeout; the timeout now applies only to waiting for response headers and to reading an error response, and cancelling a request also stops a stalled error response ([#10609](https://github.com/earendil-works/pi/issues/10609))
- Fixed compaction failing with `Invalid compactionModel: project settings may not select morph/*.` when a trusted project's `.atomic/settings.json` sets `compactionModel` to `morph/morph-compactor`. Project settings can now select Morph like any other compaction model, and the project compaction picker lists it.
- Updated the `music-metadata` dependency used for audio file conversion to 11.16.1, resolving the crafted-file denial-of-service advisories [GHSA-f94x-6692-553q](https://github.com/advisories/GHSA-f94x-6692-553q), [GHSA-5gfj-9q3v-qfp3](https://github.com/advisories/GHSA-5gfj-9q3v-qfp3), and [GHSA-8j4c-6x6g-rq3j](https://github.com/advisories/GHSA-8j4c-6x6g-rq3j).
- Updated the native module's `anyhow` (1.0.104) and `crossbeam-epoch` (0.9.21) crates, resolving [RUSTSEC-2026-0190](https://rustsec.org/advisories/RUSTSEC-2026-0190) and [RUSTSEC-2026-0204](https://rustsec.org/advisories/RUSTSEC-2026-0204).
- Fixed no project context file loading in a git worktree nested inside its main repo when the worktree's `AGENTS.md` is a symlink to the main repo's copy ([#10681](https://github.com/earendil-works/pi/issues/10681))

## [0.9.30-alpha.2] - 2026-10-09

### Fixed

- Updated the `music-metadata` dependency used for audio file conversion to 11.16.1, resolving the crafted-file denial-of-service advisories [GHSA-f94x-6692-553q](https://github.com/advisories/GHSA-f94x-6692-553q), [GHSA-5gfj-9q3v-qfp3](https://github.com/advisories/GHSA-5gfj-9q3v-qfp3), and [GHSA-8j4c-6x6g-rq3j](https://github.com/advisories/GHSA-8j4c-6x6g-rq3j).
- Updated the native module's `anyhow` (1.0.104) and `crossbeam-epoch` (0.9.21) crates, resolving [RUSTSEC-2026-0190](https://rustsec.org/advisories/RUSTSEC-2026-0190) and [RUSTSEC-2026-0204](https://rustsec.org/advisories/RUSTSEC-2026-0204).
- Fixed no project context file loading in a git worktree nested inside its main repo when the worktree's `AGENTS.md` is a symlink to the main repo's copy ([#10681](https://github.com/earendil-works/pi/issues/10681))

## [0.9.30-alpha.1] - 2026-10-09

### Breaking Changes

- Moved the published theme JSON Schema to `schemas/theme.schema.json` and made theme loading reject unknown top-level properties and unknown properties under `colors`, `workingIndicator`, or `export`. Update existing theme `$schema` references to `https://raw.githubusercontent.com/bastani-inc/atomic/main/packages/coding-agent/schemas/theme.schema.json`, define reusable custom colors under `vars`, and remove unsupported metadata.

### Added

- Added JSON Schemas for `settings.json`, `keybindings.json`, `models.json`, and theme files, published in the package's `schemas/` directory. Add `$schema` to a file for editor completion and validation; Atomic ignores `$schema` in `keybindings.json` instead of treating it as an action. See [Settings](docs/settings.md), [Keybindings](docs/keybindings.md), [Custom models](docs/models.md), and [Themes](docs/themes.md).

### Changed

- `models.json` now checks the type of every `compat` field, including `thinkingTokenBudgetField`, `zaiToolStream`, and `delegatesThinkingModelBinding`, which were previously accepted unchecked, and rejects non-positive `contextWindow` and `maxTokens` values in `modelOverrides`.

### Fixed

- Fixed Mistral streams being aborted mid-generation (e.g. during long thinking) after the request timeout; the timeout now applies only to waiting for response headers and to reading an error response, and cancelling a request also stops a stalled error response ([#10609](https://github.com/earendil-works/pi/issues/10609))
- Fixed compaction failing with `Invalid compactionModel: project settings may not select morph/*.` when a trusted project's `.atomic/settings.json` sets `compactionModel` to `morph/morph-compactor`. Project settings can now select Morph like any other compaction model, and the project compaction picker lists it.

## [0.9.29] - 2026-10-09

### Added

- Added the `/tools` command, which shows the tools the model can use, why each other tool is off and how to turn it on, selected names that an exclusion removed, and selected names that nothing registered, with a suggestion for likely typos such as `+tool-search` instead of `+tool_search`. SDK hosts get the same report from `session.getToolStatus()` and render it with `formatToolStatus()`; RPC clients use the `get_tools` command or `RpcClient.getTools()`. See [Checking tool status](docs/sdk/reference.md#checking-tool-status) and [get_tools](docs/rpc/protocol.md#get_tools).
- Added `gpt-6.1-sol-ultrafast` and `gpt-5.6-sol-ultrafast` model variants for `openai` and `openai-api`, and `gpt-6.1-sol-ultrafast` for `openai-codex`. GPT-6.1 Sol Ultrafast is priced at OpenAI's published $12 input and $60 output per million tokens; GPT-5.6 Sol Ultrafast is a limited preview without published rates, priced at six times Standard. Ultrafast access depends on your OpenAI account ([#3529](https://github.com/bastani-inc/atomic/issues/3529)).
- Added OpenAI's GPT Image models (`gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`, `gpt-image-2`, `gpt-image-1.5`, `gpt-image-1`, `gpt-image-1-mini`) as `openai-images` image models for codemode's `models.generateImages()`, extensions, and workflow `generateImages()` calls. They reuse your OpenAI API key, saved for `openai-api` or `openai` or set in `OPENAI_API_KEY`, and have no separate login; a ChatGPT sign-in cannot use them. See [Subscription and API key at the same time](docs/providers.md#subscription-and-api-key-at-the-same-time) ([#3527](https://github.com/bastani-inc/atomic/issues/3527)).
- Added the `modelRouting.allowedModels` and `modelRouting.excludedModels` settings, which keep workflow stages and subagents with `model: "auto"` from routing to models you did not allow, including through fallbacks. They take full model IDs or glob patterns, matched like `enabledModels` and `--models`. If they leave no eligible model, the stage or subagent fails before launch with an error naming the setting. `routerSelection` now also lists the `candidates` the router could choose from after filtering. See [modelRouting](docs/settings.md#modelrouting) ([#3528](https://github.com/bastani-inc/atomic/issues/3528)).

### Changed

- When the OpenAI API or the ChatGPT Codex backend rejects a Fast or Ultrafast tier, Atomic retries the request once at the default tier instead of failing the turn and shows a warning, for example `ultrafast isn't available for gpt-5.6-sol on this account; ran at default`. With an OpenAI API key, Atomic shows the same warning when the server reports a slower tier than the model you selected. Interactive sessions show it under the response, print mode writes it to stderr, and JSON mode includes it in the message's `diagnostics`. With ChatGPT sign-in (`openai` or `openai-codex`) there is no such warning, because the ChatGPT backend reports `default` for every tier it applies ([#3529](https://github.com/bastani-inc/atomic/issues/3529)).

### Fixed

- Fixed sessions created with the SDK's `createAgentSession()` or a `DefaultResourceLoader` ignoring `defaultTools: ["+codemode"]` and `tools: ["+tool_search"]`, and lacking the llama.cpp provider. Every `DefaultResourceLoader` now includes the `codemode`, `tool-search`, and `llama.cpp` built-in extensions; codemode and tool search remain opt-in. `loader.getExtensions()` now lists these three with `builtin:<name>` paths.
- Fixed `defaultTools: ["+codemode"]` and `--tools +codemode` leaving codemode inactive in interactive sessions. Opt-in tools that your tool selection names now activate when their extension finishes loading after startup, and `/tools` no longer tells you to add a tool that is already selected.

## [0.9.29-alpha.2] - 2026-10-09

### Added

- Added the `/tools` command, which shows the tools the model can use, why each other tool is off and how to turn it on, selected names that an exclusion removed, and selected names that nothing registered, with a suggestion for likely typos such as `+tool-search` instead of `+tool_search`. SDK hosts get the same report from `session.getToolStatus()` and render it with `formatToolStatus()`; RPC clients use the `get_tools` command or `RpcClient.getTools()`. See [Checking tool status](docs/sdk/reference.md#checking-tool-status) and [get_tools](docs/rpc/protocol.md#get_tools).

### Fixed

- Fixed sessions created with the SDK's `createAgentSession()` or a `DefaultResourceLoader` ignoring `defaultTools: ["+codemode"]` and `tools: ["+tool_search"]`, and lacking the llama.cpp provider. Every `DefaultResourceLoader` now includes the `codemode`, `tool-search`, and `llama.cpp` built-in extensions; codemode and tool search remain opt-in. `loader.getExtensions()` now lists these three with `builtin:<name>` paths.
- Fixed `defaultTools: ["+codemode"]` and `--tools +codemode` leaving codemode inactive in interactive sessions. Opt-in tools that your tool selection names now activate when their extension finishes loading after startup, and `/tools` no longer tells you to add a tool that is already selected.

## [0.9.29-alpha.1] - 2026-10-08

### Added

- Added `gpt-6.1-sol-ultrafast` and `gpt-5.6-sol-ultrafast` model variants for `openai` and `openai-api`, and `gpt-6.1-sol-ultrafast` for `openai-codex`. GPT-6.1 Sol Ultrafast is priced at OpenAI's published $12 input and $60 output per million tokens; GPT-5.6 Sol Ultrafast is a limited preview without published rates, priced at six times Standard. Ultrafast access depends on your OpenAI account ([#3529](https://github.com/bastani-inc/atomic/issues/3529)).
- Added OpenAI's GPT Image models (`gpt-image-2.5-flare`, `gpt-image-2.5-sunburst`, `gpt-image-2`, `gpt-image-1.5`, `gpt-image-1`, `gpt-image-1-mini`) as `openai-images` image models for codemode's `models.generateImages()`, extensions, and workflow `generateImages()` calls. They reuse your OpenAI API key, saved for `openai-api` or `openai` or set in `OPENAI_API_KEY`, and have no separate login; a ChatGPT sign-in cannot use them. See [Subscription and API key at the same time](docs/providers.md#subscription-and-api-key-at-the-same-time) ([#3527](https://github.com/bastani-inc/atomic/issues/3527)).
- Added the `modelRouting.allowedModels` and `modelRouting.excludedModels` settings, which keep workflow stages and subagents with `model: "auto"` from routing to models you did not allow, including through fallbacks. They take full model IDs or glob patterns, matched like `enabledModels` and `--models`. If they leave no eligible model, the stage or subagent fails before launch with an error naming the setting. `routerSelection` now also lists the `candidates` the router could choose from after filtering. See [modelRouting](docs/settings.md#modelrouting) ([#3528](https://github.com/bastani-inc/atomic/issues/3528)).

### Changed

- When the OpenAI API or the ChatGPT Codex backend rejects a Fast or Ultrafast tier, Atomic retries the request once at the default tier instead of failing the turn and shows a warning, for example `ultrafast isn't available for gpt-5.6-sol on this account; ran at default`. With an OpenAI API key, Atomic shows the same warning when the server reports a slower tier than the model you selected. Interactive sessions show it under the response, print mode writes it to stderr, and JSON mode includes it in the message's `diagnostics`. With ChatGPT sign-in (`openai` or `openai-codex`) there is no such warning, because the ChatGPT backend reports `default` for every tier it applies ([#3529](https://github.com/bastani-inc/atomic/issues/3529)).

## [0.9.28] - 2026-10-08

### Added

- Added `fromSignal` to the `session_shutdown` event. It is `true` when a `quit` comes from a host `SIGTERM` or `SIGHUP` instead of Ctrl+D or `/quit`, and `runtimeHost.dispose({ fromSignal: true })` sets it for SDK hosts ([#3492](https://github.com/bastani-inc/atomic/issues/3492)).
- Added the API-key-only providers `openai-api` and `anthropic-api`, so you can keep a ChatGPT or Claude subscription login on `openai`/`anthropic` and still use an API key for the same models. See [Subscription and API key at the same time](docs/providers.md#subscription-and-api-key-at-the-same-time).
- `session.compact()` and extension `ctx.compact()` accept `compactionModel` to pick the compaction model for one run without changing the `compactionModel` setting. An unknown ID rejects before the active run is interrupted. See [Compaction and tree navigation](docs/sdk.md#compaction-and-tree-navigation).
- Added terminal program-status reporting through OSC 7501 for runs, compaction, extension prompts, and sign-in. Compatible terminals can show when Atomic is working, waiting for input, done, or has failed.
- Added recorded response and tool execution durations. Bash tool results show their execution time, including after a session is resumed.
- Added OpenAI's GPT-6 Luna classifier through the Decisions API with `OPENAI_API_KEY`, and image input for codemode's `models.classify()` context. See [Classifier models](docs/models.md#use-classifier-models).
- Added native classification for llama.cpp decision models through `/v1/systemone`. Decision-only models stay out of the chat selector, while chat models keep the next-token classifier fallback. Cached native classifiers remain available after restart.
- Added exact-name `+name` and `-name` modifiers to CLI `--tools` and SDK `tools`, for example `atomic --tools +codemode,-write`. They change the resolved defaults instead of replacing them and persist across `/reload`.
- Added `compactionModel` and a **Compaction model** selector in `/settings` to choose a compactor independently of the chat model ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Added verbatim classifier compaction with registered models such as `typesafe/jev-latest`, retaining protected context and recent messages ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Added Morph verbatim compaction with `morph/morph-compactor`, authenticated through `/login morph` or `MORPH_API_KEY` ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Added `session.workflows.observe(observer)`, so SDK hosts can follow the workflow runs the agent launches without writing an extension. The observer receives a snapshot first, then changes in order. Root activity now includes an optional `graph` that lists each stage and `ctx.tool` node with its status, parents, and pending prompt, plus any run-level prompts ([#3476](https://github.com/bastani-inc/atomic/issues/3476)).
- Added `RunOpts.onStageSessionEvent` so workflow `run()` callers receive each stage session's events, such as message updates and tool executions, tagged with the run and stage ids. Events continue across fallback-model sessions and include nested workflow stages ([#3474](https://github.com/bastani-inc/atomic/issues/3474)).
- Added `createTranscript()`, which turns session events into the assistant's output as ordered, JSON-serializable text, thinking, and tool call parts, with each tool call joined to its result ([#3475](https://github.com/bastani-inc/atomic/issues/3475)).
- Added `createAgentSessionAdapter(baseOptions?)` to `@bastani/atomic/workflows`, which builds the default workflow stage-session adapter with `createAgentSession` options shared by every stage ([#3472](https://github.com/bastani-inc/atomic/issues/3472)).
- Added `ctx.isPresentationOnly` so extension lifecycle hooks can leave execution to the isolated interactive engine while retaining host-side UI ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Exported `publishExtensionContextEffect(ctx, effect, phase?)` with a synchronous commit phase for generation identity handoff, and `registerExtensionContextRetirementEffect(ctx, dispose)` for observer cleanup before authority revocation ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Added Azure Foundry Chat Completions with `azure/deepseek-v4-pro`, using the existing Azure endpoint and deployment-name settings.
- Added `*` patterns to `--tools` and `--exclude-tools`, including MCP tool names.
- Added `--no-mcp` to disable built-in MCP support for one run, including interactive sessions.
- Added `ExtensionAPI.getWorkflowHostModules()` to supply shared host module instances for the exact workflow imports `@bastani/atomic`, `@bastani/pi-ai`, and `@bastani/pi-ai/providers/all`, avoiding repeated package loading ([#3454](https://github.com/bastani-inc/atomic/issues/3454)).

### Changed

- `openai-decisions/gpt-6-luna` no longer has its own `/login` entry, so you do not need to sign in again. The classifier uses the API key saved for `openai-api`, then the one saved for `openai`, then `OPENAI_API_KEY`. A ChatGPT sign-in on `openai` cannot call the Decisions API and is not used, so the classifier stays unavailable until one of those keys exists. See [Classifier models](docs/models.md#use-classifier-models).
- Moved the GPT-6 Luna classifier from `openai/gpt-6-luna` to its own API-key-only provider, `openai-decisions/gpt-6-luna`, named "GPT-6 Luna Decisions". `openai/gpt-6-luna` now always means the chat model, and the classifier stays available while `openai` uses ChatGPT sign-in. Update `routerModel`, `compactionModel`, `structured_output` and codemode references that meant the classifier. See [Classifier models](docs/models.md#use-classifier-models).
- Codemode labels returned output items with numbered headers and places console output in a separate trailing block.
- Codemode now says that `ALL_TOOLS`, `searchTools()`, and `describeTool()` list only script-callable tools, and `searchTools()`/`describeTool()` answer an exact model-only tool name such as `subagent` with a hint to call it directly instead of returning nothing ([#3510](https://github.com/bastani-inc/atomic/issues/3510))
- Changed `atomic mcp login --timeout` to limit the whole sign-in, including requests to the authorization server, instead of only the wait for the browser.
- Compaction planners now receive structured per-message lines instead of a numbered transcript ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Compaction cards show the backend and model used, including `summary (pi fallback)` for policy-refusal summaries ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Home and End move the editor cursor to the start and end of the line. Ctrl+Home and Ctrl+End jump to the top and bottom of the fullscreen transcript.
- Renamed the Azure provider to `azure`. Existing provider references remain supported, and startup migrates credentials and configuration without replacing existing `azure` entries. Project files are migrated only when trusted.
- Codemode built-ins are frozen in pi 1.0.4. Patches to built-ins are ignored instead of crashing the host.

### Fixed

- Fixed the startup project trust prompt opening inside the fully painted interface. Atomic now shows the prompt on its own and paints the interface after you choose.
- Fixed the "This project is not trusted" warning staying in the transcript after you trusted the project at the startup prompt. The warning still appears when you decline trust or leave the project untrusted.
- Fixed Herdr restoring panes without their Atomic conversation after a Herdr restart. With Herdr 0.9.2 or newer, Atomic now reports `atomic --session <id>` (plus `--session-dir` for a custom session directory) so Herdr can reopen the same session in the same pane, and keeps it registered when Herdr or the system ends Atomic with `SIGTERM` or `SIGHUP`. Quitting with Ctrl+D or `/quit` still clears it, and older Herdr versions keep status reporting without restore ([#3492](https://github.com/bastani-inc/atomic/issues/3492)).
- Updated the sandbox example extension's lockfile to `shell-quote` 1.12.0, resolving the `quote()` command-injection advisory [GHSA-pqg4-j6r4-53mv](https://github.com/advisories/GHSA-pqg4-j6r4-53mv) in its `@anthropic-ai/sandbox-runtime` dependency.
- Fixed hyperlinks being disabled in Herdr panes, while keeping image protocols off even when outer-terminal environment variables are inherited ([#10573](https://github.com/earendil-works/pi/issues/10573)).
- Fixed `outputPad` applying inconsistently to tool results, shell output, summaries, skill invocations, and custom transcript entries. Changing it in `/settings` now updates existing blocks without rebuilding the transcript.
- Fixed fullscreen text selections surviving a transcript rebuild and selecting unrelated replacement text.
- Fixed extension-registered MCP servers silently remaining unused when an SDK host has no MCP contribution consumer. Atomic now reports each unhandled server once.
- Fixed a false `Timeout waiting for response to resume_queued_messages` error when you send a message while a paused queue is still waiting for the agent's current turn to finish, and the follow-up `Agent is already processing` error caused by the stale paused state. The resume now waits for the turn instead of giving up after 30 seconds ([#3493](https://github.com/bastani-inc/atomic/issues/3493)).
- Fixed extension `session_shutdown` handlers being cut off when you quit the interactive CLI. The engine now gets up to 5 seconds to finish extension shutdown, instead of about 750 ms, before it is force-stopped.
- Fixed slow extension loading and `/reload` on Windows. Imports such as `./helper.js` that point at TypeScript sources no longer make the loader try hundreds of missing files before finding `helper.ts`.
- Fixed `/mcp` waiting for startup connections before opening. The manager now updates live and stays usable while enabling, disabling, or reconnecting servers.
- Fixed images being dropped as unresizable under `node --watch` when Node sends its own messages on the image resize worker channel.
- Fixed codemode's tool description omitting `await` for `searchTools()`, `describeTool()`, and `describeNamespace()`, which could make scripts return unresolved promises instead of tool information.
- Fixed slash commands and shell-command drafts typed during interactive startup running before Enter, including when launching with `--model` or `--provider`. Enter-terminated input stays ordered and unfinished text remains in the editor.
- Fixed a stuck MCP server startup blocking re-enabling the server or shutting down after it was disabled.
- Fixed clipboard paste doing nothing in Termux, and failed copies there omitting the Termux:API install hint.
- Fixed `!` and RPC `bash` output keeping fragments of color codes, such as a stray `m`, when a code was split across output chunks.
- Fixed MCP OAuth sign-ins that could not be cancelled while waiting on the authorization server and kept running after the session ended. The sign-in screen now cancels with Esc at every step, session shutdown aborts a running sign-in, and each request to the authorization server times out after 15 seconds.
- Fixed shutdown waiting up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to close the server's session.
- Policy refusals from any chat compaction model, including `auto`, explicit chat-model IDs, and borrowed fallback entries, now try pi's summary compaction on the same model before continuing through fallback models. Summary requests retry recoverable errors; policy refusals are never retried. Classifier and Morph failures advance directly to fallback models ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Morph compaction failures now include the HTTP status and a bounded, credential-redacted response excerpt in error diagnostics ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Fixed Herdr publishing duplicate pane activity when startup workflow recovery refreshes an unchanged activity snapshot ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed Herdr skipping a return to an earlier pane activity after an intervening report timed out or failed ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed project `.atomic/settings.json`, extensions, skills, and other trust-gated resources being ignored without notice in untrusted projects. Interactive sessions now show a warning that points to `/trust`, matching upstream pi.
- Fixed `@bastani/atomic/workflows` type declarations disagreeing with the runtime: `StageSnapshot` now declares `parentIds`, `executionOrder`, `model`, `startedAt`, `endedAt`, and `durationMs`; `Store` now declares `snapshot()`, `graphSnapshot()`, and `subscribe()`; and an `AgentSessionAdapter.create` can return `createAgentSession(...)` directly without a type cast ([#3473](https://github.com/bastani-inc/atomic/issues/3473)).
- Fixed `run()` from `@bastani/atomic/workflows` failing at the first `ctx.task` or `ctx.stage` with "prompt adapter not configured" when `opts.adapters` was omitted. Stages now default to in-process `createAgentSession` sessions with in-memory session managers ([#3472](https://github.com/bastani-inc/atomic/issues/3472)).
- Fixed startup extension confirmations, including workflow recovery prompts, losing keyboard focus during terminal initialization ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed queued protected workflow notices producing `SessionClosed` replies during reload. Successful reload now preserves those notices for the replacement extensions without releasing an explicitly paused queue ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed idle workflow status and control commands not displaying their output in isolated interactive sessions, including after `/reload` ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed extension reload skipping remaining commit effects and predecessor shutdown when a commit effect and an SDK error observer both throw. Reload now retains both failures while keeping the replacement extensions active ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed syntax colors leaking across lines in multiline tokens and interpolation substitutions inheriting string colors.
- Invalid router and provider-list settings retain their configuration validation errors during Azure provider migration.
- Fixed codemode `tools.read()` returning text instead of an image block for image files. Pass the result to `image()` to display it ([#10251](https://github.com/earendil-works/pi/issues/10251)).
- Fixed MCP OAuth dynamic registration omitting `application_type`, which caused redirect URI rejections on OpenID Connect servers ([#10493](https://github.com/earendil-works/pi/issues/10493)).
- Fixed `--tools` removing MCP tools when selecting codemode. MCP tools remain callable unless the allowlist contains an entry starting with `mcp__`; unmatched tools are not declared directly.
- Fixed shutdown leaving MCP connections open while a server was still connecting ([#10249](https://github.com/earendil-works/pi/issues/10249)).
- Fixed hidden tools appearing in prompt rules and skill-reading hints. Codemode tool declarations now include each tool's prompt guidelines ([#10343](https://github.com/earendil-works/pi/issues/10343)).
- Fixed interactive quit waiting on pending extension commands, which could leave connecting MCP servers running.
- Fixed HTTP/2 pending stream cancellations being treated as user cancellations instead of retryable transport failures.
- Updated the bundled `proxy-addr` dependency to 2.0.8, fixing IP spoofing through IPv4-mapped IPv6 trusted subnets.

## [0.9.28-alpha.5] - 2026-10-08

### Added

- Added `fromSignal` to the `session_shutdown` event. It is `true` when a `quit` comes from a host `SIGTERM` or `SIGHUP` instead of Ctrl+D or `/quit`, and `runtimeHost.dispose({ fromSignal: true })` sets it for SDK hosts ([#3492](https://github.com/bastani-inc/atomic/issues/3492)).

### Changed

- `openai-decisions/gpt-6-luna` no longer has its own `/login` entry, so you do not need to sign in again. The classifier uses the API key saved for `openai-api`, then the one saved for `openai`, then `OPENAI_API_KEY`. A ChatGPT sign-in on `openai` cannot call the Decisions API and is not used, so the classifier stays unavailable until one of those keys exists. See [Classifier models](docs/models.md#use-classifier-models).

### Fixed

- Fixed the startup project trust prompt opening inside the fully painted interface. Atomic now shows the prompt on its own and paints the interface after you choose.
- Fixed the "This project is not trusted" warning staying in the transcript after you trusted the project at the startup prompt. The warning still appears when you decline trust or leave the project untrusted.
- Fixed Herdr restoring panes without their Atomic conversation after a Herdr restart. With Herdr 0.9.2 or newer, Atomic now reports `atomic --session <id>` (plus `--session-dir` for a custom session directory) so Herdr can reopen the same session in the same pane, and keeps it registered when Herdr or the system ends Atomic with `SIGTERM` or `SIGHUP`. Quitting with Ctrl+D or `/quit` still clears it, and older Herdr versions keep status reporting without restore ([#3492](https://github.com/bastani-inc/atomic/issues/3492)).

## [0.9.28-alpha.4] - 2026-10-07

### Added

- Added the API-key-only providers `openai-api` and `anthropic-api`, so you can keep a ChatGPT or Claude subscription login on `openai`/`anthropic` and still use an API key for the same models. See [Subscription and API key at the same time](docs/providers.md#subscription-and-api-key-at-the-same-time).
- `session.compact()` and extension `ctx.compact()` accept `compactionModel` to pick the compaction model for one run without changing the `compactionModel` setting. An unknown ID rejects before the active run is interrupted. See [Compaction and tree navigation](docs/sdk.md#compaction-and-tree-navigation).
- Added terminal program-status reporting through OSC 7501 for runs, compaction, extension prompts, and sign-in. Compatible terminals can show when Atomic is working, waiting for input, done, or has failed.
- Added recorded response and tool execution durations. Bash tool results show their execution time, including after a session is resumed.

### Changed

- Moved the GPT-6 Luna classifier from `openai/gpt-6-luna` to its own API-key-only provider, `openai-decisions/gpt-6-luna`, named "GPT-6 Luna Decisions". `openai/gpt-6-luna` now always means the chat model, and the classifier stays available while `openai` uses ChatGPT sign-in. Update `routerModel`, `compactionModel`, `structured_output` and codemode references that meant the classifier. See [Classifier models](docs/models.md#use-classifier-models).
- Codemode labels returned output items with numbered headers and places console output in a separate trailing block.
- Codemode now says that `ALL_TOOLS`, `searchTools()`, and `describeTool()` list only script-callable tools, and `searchTools()`/`describeTool()` answer an exact model-only tool name such as `subagent` with a hint to call it directly instead of returning nothing ([#3510](https://github.com/bastani-inc/atomic/issues/3510))

### Fixed

- Updated the sandbox example extension's lockfile to `shell-quote` 1.12.0, resolving the `quote()` command-injection advisory [GHSA-pqg4-j6r4-53mv](https://github.com/advisories/GHSA-pqg4-j6r4-53mv) in its `@anthropic-ai/sandbox-runtime` dependency.
- Fixed hyperlinks being disabled in Herdr panes, while keeping image protocols off even when outer-terminal environment variables are inherited ([#10573](https://github.com/earendil-works/pi/issues/10573)).
- Fixed `outputPad` applying inconsistently to tool results, shell output, summaries, skill invocations, and custom transcript entries. Changing it in `/settings` now updates existing blocks without rebuilding the transcript.
- Fixed fullscreen text selections surviving a transcript rebuild and selecting unrelated replacement text.
- Fixed extension-registered MCP servers silently remaining unused when an SDK host has no MCP contribution consumer. Atomic now reports each unhandled server once.

## [0.9.28-alpha.3] - 2026-10-07

### Added

- Added OpenAI's GPT-6 Luna classifier through the Decisions API with `OPENAI_API_KEY`, and image input for codemode's `models.classify()` context. See [Classifier models](docs/models.md#use-classifier-models).
- Added native classification for llama.cpp decision models through `/v1/systemone`. Decision-only models stay out of the chat selector, while chat models keep the next-token classifier fallback. Cached native classifiers remain available after restart.
- Added exact-name `+name` and `-name` modifiers to CLI `--tools` and SDK `tools`, for example `atomic --tools +codemode,-write`. They change the resolved defaults instead of replacing them and persist across `/reload`.

### Changed

- Changed `atomic mcp login --timeout` to limit the whole sign-in, including requests to the authorization server, instead of only the wait for the browser.

### Fixed

- Fixed a false `Timeout waiting for response to resume_queued_messages` error when you send a message while a paused queue is still waiting for the agent's current turn to finish, and the follow-up `Agent is already processing` error caused by the stale paused state. The resume now waits for the turn instead of giving up after 30 seconds ([#3493](https://github.com/bastani-inc/atomic/issues/3493)).
- Fixed extension `session_shutdown` handlers being cut off when you quit the interactive CLI. The engine now gets up to 5 seconds to finish extension shutdown, instead of about 750 ms, before it is force-stopped.
- Fixed slow extension loading and `/reload` on Windows. Imports such as `./helper.js` that point at TypeScript sources no longer make the loader try hundreds of missing files before finding `helper.ts`.
- Fixed `/mcp` waiting for startup connections before opening. The manager now updates live and stays usable while enabling, disabling, or reconnecting servers.
- Fixed images being dropped as unresizable under `node --watch` when Node sends its own messages on the image resize worker channel.
- Fixed codemode's tool description omitting `await` for `searchTools()`, `describeTool()`, and `describeNamespace()`, which could make scripts return unresolved promises instead of tool information.
- Fixed slash commands and shell-command drafts typed during interactive startup running before Enter, including when launching with `--model` or `--provider`. Enter-terminated input stays ordered and unfinished text remains in the editor.
- Fixed a stuck MCP server startup blocking re-enabling the server or shutting down after it was disabled.
- Fixed clipboard paste doing nothing in Termux, and failed copies there omitting the Termux:API install hint.
- Fixed `!` and RPC `bash` output keeping fragments of color codes, such as a stray `m`, when a code was split across output chunks.
- Fixed MCP OAuth sign-ins that could not be cancelled while waiting on the authorization server and kept running after the session ended. The sign-in screen now cancels with Esc at every step, session shutdown aborts a running sign-in, and each request to the authorization server times out after 15 seconds.
- Fixed shutdown waiting up to 15 seconds to refresh an MCP OAuth token that was about to expire, only to close the server's session.

## [0.9.28-alpha.2] - 2026-10-07

### Added

- Added `compactionModel` and a **Compaction model** selector in `/settings` to choose a compactor independently of the chat model ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Added verbatim classifier compaction with registered models such as `typesafe/jev-latest`, retaining protected context and recent messages ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Added Morph verbatim compaction with `morph/morph-compactor`, authenticated through `/login morph` or `MORPH_API_KEY` ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Added `session.workflows.observe(observer)`, so SDK hosts can follow the workflow runs the agent launches without writing an extension. The observer receives a snapshot first, then changes in order. Root activity now includes an optional `graph` that lists each stage and `ctx.tool` node with its status, parents, and pending prompt, plus any run-level prompts ([#3476](https://github.com/bastani-inc/atomic/issues/3476)).
- Added `RunOpts.onStageSessionEvent` so workflow `run()` callers receive each stage session's events, such as message updates and tool executions, tagged with the run and stage ids. Events continue across fallback-model sessions and include nested workflow stages ([#3474](https://github.com/bastani-inc/atomic/issues/3474)).
- Added `createTranscript()`, which turns session events into the assistant's output as ordered, JSON-serializable text, thinking, and tool call parts, with each tool call joined to its result ([#3475](https://github.com/bastani-inc/atomic/issues/3475)).
- Added `createAgentSessionAdapter(baseOptions?)` to `@bastani/atomic/workflows`, which builds the default workflow stage-session adapter with `createAgentSession` options shared by every stage ([#3472](https://github.com/bastani-inc/atomic/issues/3472)).
- Added `ctx.isPresentationOnly` so extension lifecycle hooks can leave execution to the isolated interactive engine while retaining host-side UI ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Exported `publishExtensionContextEffect(ctx, effect, phase?)` with a synchronous commit phase for generation identity handoff, and `registerExtensionContextRetirementEffect(ctx, dispose)` for observer cleanup before authority revocation ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).

### Changed

- Compaction planners now receive structured per-message lines instead of a numbered transcript ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Compaction cards show the backend and model used, including `summary (pi fallback)` for policy-refusal summaries ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).

### Fixed

- Policy refusals from any chat compaction model, including `auto`, explicit chat-model IDs, and borrowed fallback entries, now try pi's summary compaction on the same model before continuing through fallback models. Summary requests retry recoverable errors; policy refusals are never retried. Classifier and Morph failures advance directly to fallback models ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Morph compaction failures now include the HTTP status and a bounded, credential-redacted response excerpt in error diagnostics ([#3470](https://github.com/bastani-inc/atomic/issues/3470)).
- Fixed Herdr publishing duplicate pane activity when startup workflow recovery refreshes an unchanged activity snapshot ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed Herdr skipping a return to an earlier pane activity after an intervening report timed out or failed ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed project `.atomic/settings.json`, extensions, skills, and other trust-gated resources being ignored without notice in untrusted projects. Interactive sessions now show a warning that points to `/trust`, matching upstream pi.
- Fixed `@bastani/atomic/workflows` type declarations disagreeing with the runtime: `StageSnapshot` now declares `parentIds`, `executionOrder`, `model`, `startedAt`, `endedAt`, and `durationMs`; `Store` now declares `snapshot()`, `graphSnapshot()`, and `subscribe()`; and an `AgentSessionAdapter.create` can return `createAgentSession(...)` directly without a type cast ([#3473](https://github.com/bastani-inc/atomic/issues/3473)).
- Fixed `run()` from `@bastani/atomic/workflows` failing at the first `ctx.task` or `ctx.stage` with "prompt adapter not configured" when `opts.adapters` was omitted. Stages now default to in-process `createAgentSession` sessions with in-memory session managers ([#3472](https://github.com/bastani-inc/atomic/issues/3472)).
- Fixed startup extension confirmations, including workflow recovery prompts, losing keyboard focus during terminal initialization ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed queued protected workflow notices producing `SessionClosed` replies during reload. Successful reload now preserves those notices for the replacement extensions without releasing an explicitly paused queue ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed idle workflow status and control commands not displaying their output in isolated interactive sessions, including after `/reload` ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).
- Fixed extension reload skipping remaining commit effects and predecessor shutdown when a commit effect and an SDK error observer both throw. Reload now retains both failures while keeping the replacement extensions active ([#3468](https://github.com/bastani-inc/atomic/issues/3468)).

## [0.9.28-alpha.1] - 2026-10-05

### Added

- Added Azure Foundry Chat Completions with `azure/deepseek-v4-pro`, using the existing Azure endpoint and deployment-name settings.
- Added `*` patterns to `--tools` and `--exclude-tools`, including MCP tool names.
- Added `--no-mcp` to disable built-in MCP support for one run, including interactive sessions.
- Added `ExtensionAPI.getWorkflowHostModules()` to supply shared host module instances for the exact workflow imports `@bastani/atomic`, `@bastani/pi-ai`, and `@bastani/pi-ai/providers/all`, avoiding repeated package loading ([#3454](https://github.com/bastani-inc/atomic/issues/3454)).

### Changed

- Home and End move the editor cursor to the start and end of the line. Ctrl+Home and Ctrl+End jump to the top and bottom of the fullscreen transcript.
- Renamed the Azure provider to `azure`. Existing provider references remain supported, and startup migrates credentials and configuration without replacing existing `azure` entries. Project files are migrated only when trusted.
- Codemode built-ins are frozen in pi 1.0.4. Patches to built-ins are ignored instead of crashing the host.

### Fixed

- Fixed syntax colors leaking across lines in multiline tokens and interpolation substitutions inheriting string colors.
- Invalid router and provider-list settings retain their configuration validation errors during Azure provider migration.
- Fixed codemode `tools.read()` returning text instead of an image block for image files. Pass the result to `image()` to display it ([#10251](https://github.com/earendil-works/pi/issues/10251)).
- Fixed MCP OAuth dynamic registration omitting `application_type`, which caused redirect URI rejections on OpenID Connect servers ([#10493](https://github.com/earendil-works/pi/issues/10493)).
- Fixed `--tools` removing MCP tools when selecting codemode. MCP tools remain callable unless the allowlist contains an entry starting with `mcp__`; unmatched tools are not declared directly.
- Fixed shutdown leaving MCP connections open while a server was still connecting ([#10249](https://github.com/earendil-works/pi/issues/10249)).
- Fixed hidden tools appearing in prompt rules and skill-reading hints. Codemode tool declarations now include each tool's prompt guidelines ([#10343](https://github.com/earendil-works/pi/issues/10343)).
- Fixed interactive quit waiting on pending extension commands, which could leave connecting MCP servers running.
- Fixed HTTP/2 pending stream cancellations being treated as user cancellations instead of retryable transport failures.
- Updated the bundled `proxy-addr` dependency to 2.0.8, fixing IP spoofing through IPv4-mapped IPv6 trusted subnets.

## [0.9.27] - 2026-10-05

### Changed

- Codemode `image()` also saves each image to a private temp file and names its path before the image, so later turns can copy or move it ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Full codemode text output and binary MCP resources now use exclusively created, user-readable-only temp files ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).

### Fixed

- Fixed subscription logins such as Sign in with ChatGPT losing rotated OAuth tokens when a request or model refresh was cancelled ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed codemode failing after an update removed the running npm install, and added a restart hint when errors occur after the install changes ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed dead terminal stdin errors such as `read EIO` and `setRawMode ENOTTY` being reported as crashes ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed slow OAuth token rotation failing model refresh after the request-authentication waiting deadline ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed unrelated extension and filesystem errors being mistaken for dead-terminal exits, restoring crash reports and extension hints ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed slow OAuth rotation blocking logout and credential updates for unrelated providers. File-backed rotation now uses a provider-specific lock and preserves concurrent logout or credential replacement ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed failed `/reload` retiring cleanup leaving in-flight workflows unreachable. The replacement extensions now finish starting, pending settings take effect, joined Intercom groups and workflow-stage targets by name or ID survive, and the error names the underlying cleanup exception. Successful reloads also preserve live, pending, and future workflow-stage routing ([#3425](https://github.com/bastani-inc/atomic/issues/3425)).
- Background subagent completions now reach workflow-stage chats as visible notices that trigger a parent turn before acknowledgment, even while other notices arrive ([#3427](https://github.com/bastani-inc/atomic/issues/3427)).

## [0.9.27-alpha.1] - 2026-10-04

### Changed

- Codemode `image()` also saves each image to a private temp file and names its path before the image, so later turns can copy or move it ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Full codemode text output and binary MCP resources now use exclusively created, user-readable-only temp files ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).

### Fixed

- Fixed subscription logins such as Sign in with ChatGPT losing rotated OAuth tokens when a request or model refresh was cancelled ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed codemode failing after an update removed the running npm install, and added a restart hint when errors occur after the install changes ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed dead terminal stdin errors such as `read EIO` and `setRawMode ENOTTY` being reported as crashes ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed slow OAuth token rotation failing model refresh after the request-authentication waiting deadline ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed unrelated extension and filesystem errors being mistaken for dead-terminal exits, restoring crash reports and extension hints ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed slow OAuth rotation blocking logout and credential updates for unrelated providers. File-backed rotation now uses a provider-specific lock and preserves concurrent logout or credential replacement ([#3429](https://github.com/bastani-inc/atomic/issues/3429)).
- Fixed failed `/reload` retiring cleanup leaving in-flight workflows unreachable. The replacement extensions now finish starting, pending settings take effect, joined Intercom groups and workflow-stage targets by name or ID survive, and the error names the underlying cleanup exception. Successful reloads also preserve live, pending, and future workflow-stage routing ([#3425](https://github.com/bastani-inc/atomic/issues/3425)).
- Background subagent completions now reach workflow-stage chats as visible notices that trigger a parent turn before acknowledgment, even while other notices arrive ([#3427](https://github.com/bastani-inc/atomic/issues/3427)).

## [0.9.26] - 2026-10-04

### Breaking Changes

- MCP OAuth credentials are now stored per server name and URL, so MCP servers with the same URL can sign in with different accounts. Credentials stored by URL alone move to the first server that uses them ([#10252](https://github.com/earendil-works/pi/issues/10252)). SDK consumers of the exported `McpOAuthCredentialStore` must now pass the server name to `forServer`, `tokens`, `remove` and `removeAsync`.

### Added

- Added `samplingParamsByThinkingLevel` to `models.json` for per-thinking-level sampling parameter overrides on OpenAI-compatible APIs, including native fast routes. See [Sampling by Thinking Level](docs/models/reference.md#sampling-by-thinking-level) ([#9776](https://github.com/earendil-works/pi/pull/9776) by [@mrexodia](https://github.com/mrexodia))
- Added `pi.registerToolRenderer()` to choose renderers for any tool, including tools not yet registered ([#10285](https://github.com/earendil-works/pi/issues/10285)).
- OAuth sign-in screens now show a copy shortcut for the sign-in URL, including `/mcp login`. Use Ctrl+X or configure `app.auth.copyUrl`; existing message and workflow shortcuts are unchanged.
- Added project overrides for user-level MCP servers: a `.atomic/mcp.json` entry without `command`, `url`, or `type` sets only `enabled`, `exposure`, and `toolExposure` of the user-level server, and `/mcp` can enable or disable a server for the current project ([#10277](https://github.com/earendil-works/pi/issues/10277)).
- Added Cloudflare's Clef and Clef Flash classifier models to `cloudflare-workers-ai`, usable from codemode scripts and extensions ([#10316](https://github.com/earendil-works/pi/pull/10316) by [@ndisidore](https://github.com/ndisidore), [#10322](https://github.com/earendil-works/pi/pull/10322) by [@RealAlexandreAI](https://github.com/RealAlexandreAI)).
- Added `pi.registerProviderAlias({ id, provider, name })` for separate account logins that inherit a provider's model catalog and request behavior without copying its implementation. Existing credentials under the alias ID are preserved.
- Added `quietStartup: "header"`, which keeps the startup header but hides the model scope line. `/settings` offers it alongside `true` and `false`.
- Added `models.generateImages()` to codemode scripts. It runs image models such as OpenRouter's with the session's credentials and returns base64 image blocks that `image()` attaches to the result; usage counts toward the session cost like `models.classify()`. Extensions can call `ctx.modelRegistry.generateImages()`. See [Built-in tools](docs/tools.md#codemode).

### Changed

- Cancelling a login started from a `/login` menu now returns to that menu.
- The "Cloud Providers" section of the [Providers](docs/providers.md) docs page is now "Provider Specific Config".
- Codemode costs fewer prompt tokens. The `codemode` description points to the new [Codemode](docs/codemode.md) reference for the `models` API, which the model reads when it needs it, and declared tools say in one line how scripts call them and what the call resolves to instead of repeating their full declaration. The MCP server section of the system prompt only explains the ways of reaching tools that its servers use, and the system prompt points to the new reference.
- Codemode errors now say how to recover: reading a tool or `models` member that does not exist names the close matches (`tools.Bash` suggests `tools.bash`), `models.classify()` and `models.generateImages()` reject malformed arguments with the expected shape, an unknown model points to `models.getAvailableOfType()`, an oversized `store()` value explains what the store is for, and a script that generates images without showing them gets a note. Scripts that probed for a tool with `typeof tools.name` must use `"name" in tools`.
- `/login` and `/logout` now label providers without credentials as "not configured" instead of "unconfigured".
- OAuth browser pages now show the Atomic logo in color instead of white.

### Fixed

- Fixed fresh SDK sessions being unable to inspect or resume crashed durable workflow runs from another process. Hosts using `SessionManager.inMemory()` can recover by full run UUID in the original working directory and workflow database without reopening a session file. Database ownership fencing protects healthy connected owners, including across cloned VM identities, and rejects late durable writes after connection loss. Older unfenced runs support explicit controlled enrollment after their workers stop. Completed checkpoints are preserved; interrupted external effects still require idempotency ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
- Fixed managed workflow database monitoring timeouts unnecessarily dropping healthy run ownership. A persistent timeout now reports database unavailability without closing existing ownership connections ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
- Fixed managed workflow PostgreSQL staying down after shutdown when its old launch options were missing. Database-unavailable errors and workflow status now show the provider, credential-redacted endpoint, and retained recovery refusal or failure ([#3413](https://github.com/bastani-inc/atomic/issues/3413)).
- Fixed NVIDIA's built-in default pointing to a model missing from its current catalog. Automatic selection now prefers Nemotron 3 Ultra 550B A55B.
- Fixed MCP tool calls in resumed sessions and HTML exports rendering fully expanded before their server connects, or when it never connects ([#10285](https://github.com/earendil-works/pi/issues/10285)).
- Fixed JPEG, GIF, and WebP images rendered by extensions through `Image` not appearing on Kitty-protocol terminals ([#10292](https://github.com/earendil-works/pi/issues/10292)).
- Fixed fullscreen Kitty images collapsing after scrolling in WezTerm ([#10319](https://github.com/earendil-works/pi/issues/10319)).
- Fixed `codemode` scripts that print in a loop exhausting memory. Scripts now fail after exceeding 16 Mi characters or 100000 output items ([#10283](https://github.com/earendil-works/pi/issues/10283)).
- Fixed resumed MCP calls and HTML exports assigning the wrong server to ambiguous tool names. Calls without known renderers now retain their full identifier when the server/tool boundary cannot be recovered.
- Empty comma-separated entries in `--models` no longer add unintended models to the cycle ([#10334](https://github.com/earendil-works/pi/issues/10334)).
- Pinned `brace-expansion` to 5.0.12 so installations use the patched dependency ([#10288](https://github.com/earendil-works/pi/issues/10288)).
- MCP OAuth sign-in now rejects pasted redirect URLs whose origin or path differs from the current sign-in's redirect URI.
- Fixed `/login` hiding the redirect-URL paste field for native extension providers that declare `auth.oauth.usesCallbackServer`, including built-in OAuth flows cloned under custom provider IDs ([#3400](https://github.com/bastani-inc/atomic/issues/3400)).
- `/mcp login` now prints its sign-in URL as a clickable link in the terminal UI, even when the URL wraps ([#10186](https://github.com/earendil-works/pi/issues/10186)).
- `--provider` without `--model` now fails with an error instead of being silently ignored while the default model from another provider runs ([#10236](https://github.com/earendil-works/pi/issues/10236)).
- MCP servers that ask for more scope (`insufficient_scope`) no longer trigger repeated sign-in requests. The new sign-in now keeps the scopes granted earlier instead of requesting only the missing ones.
- User messages in the transcript now keep one copy of each rendered line instead of two full-width copies, with identical output.
- Deferred MCP tools that `tool_search` loaded are no longer dropped on resume and `/reload` when their server reconnects before the next prompt; the session restored its tools before the MCP servers reconnected.
- The `system` theme keeps pastel terminal palettes pastel instead of making their accent colors more saturated at the lightness the theme needs ([#10255](https://github.com/earendil-works/pi/issues/10255), [#10293](https://github.com/earendil-works/pi/pull/10293) by [@dgtlntv](https://github.com/dgtlntv)).
- `/login` and `/logout` no longer label every OAuth sign-in as a subscription. Only subscription-backed providers say "subscription"; other OAuth sign-ins say "account".
- The startup header logo no longer renders with gaps in Apple Terminal; it shows a text `∀ Atomic` wordmark with the version and session details instead.
- Slash command autocompletion now triggers when the input starts with whitespace ([#10218](https://github.com/earendil-works/pi/pull/10218) by [@haoqixu](https://github.com/haoqixu)).
- Color no longer bleeds past mouse selections in fullscreen mode when a styled token ends at the selection boundary ([#10169](https://github.com/earendil-works/pi/issues/10169)).
- The transcript retains less memory per rendered message; a long assistant message keeps about a fifth of the heap it kept before.

### Removed

- Removed the daxnuts easter egg shown when selecting `opencode/kimi-k2.5`.

## [0.9.26-alpha.6] - 2026-10-04

### Added

- Added `samplingParamsByThinkingLevel` to `models.json` for per-thinking-level sampling parameter overrides on OpenAI-compatible APIs, including native fast routes. See [Sampling by Thinking Level](docs/models/reference.md#sampling-by-thinking-level) ([#9776](https://github.com/earendil-works/pi/pull/9776) by [@mrexodia](https://github.com/mrexodia))

### Fixed

- Fixed fresh SDK sessions being unable to inspect or resume crashed durable workflow runs from another process. Hosts using `SessionManager.inMemory()` can recover by full run UUID in the original working directory and workflow database without reopening a session file. Database ownership fencing protects healthy connected owners, including across cloned VM identities, and rejects late durable writes after connection loss. Older unfenced runs support explicit controlled enrollment after their workers stop. Completed checkpoints are preserved; interrupted external effects still require idempotency ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).
- Fixed managed workflow database monitoring timeouts unnecessarily dropping healthy run ownership. A persistent timeout now reports database unavailability without closing existing ownership connections ([#3419](https://github.com/bastani-inc/atomic/issues/3419)).

## [0.9.26-alpha.5] - 2026-10-03

### Added

- Added `pi.registerToolRenderer()` to choose renderers for any tool, including tools not yet registered ([#10285](https://github.com/earendil-works/pi/issues/10285)).

### Fixed

- Fixed managed workflow PostgreSQL staying down after shutdown when its old launch options were missing. Database-unavailable errors and workflow status now show the provider, credential-redacted endpoint, and retained recovery refusal or failure ([#3413](https://github.com/bastani-inc/atomic/issues/3413)).
- Fixed NVIDIA's built-in default pointing to a model missing from its current catalog. Automatic selection now prefers Nemotron 3 Ultra 550B A55B.
- Fixed MCP tool calls in resumed sessions and HTML exports rendering fully expanded before their server connects, or when it never connects ([#10285](https://github.com/earendil-works/pi/issues/10285)).
- Fixed JPEG, GIF, and WebP images rendered by extensions through `Image` not appearing on Kitty-protocol terminals ([#10292](https://github.com/earendil-works/pi/issues/10292)).
- Fixed fullscreen Kitty images collapsing after scrolling in WezTerm ([#10319](https://github.com/earendil-works/pi/issues/10319)).
- Fixed `codemode` scripts that print in a loop exhausting memory. Scripts now fail after exceeding 16 Mi characters or 100000 output items ([#10283](https://github.com/earendil-works/pi/issues/10283)).
- Fixed resumed MCP calls and HTML exports assigning the wrong server to ambiguous tool names. Calls without known renderers now retain their full identifier when the server/tool boundary cannot be recovered.

## [0.9.26-alpha.3] - 2026-10-03

### Added

- OAuth sign-in screens now show a copy shortcut for the sign-in URL, including `/mcp login`. Use Ctrl+X or configure `app.auth.copyUrl`; existing message and workflow shortcuts are unchanged.
- Added project overrides for user-level MCP servers: a `.atomic/mcp.json` entry without `command`, `url`, or `type` sets only `enabled`, `exposure`, and `toolExposure` of the user-level server, and `/mcp` can enable or disable a server for the current project ([#10277](https://github.com/earendil-works/pi/issues/10277)).
- Added Cloudflare's Clef and Clef Flash classifier models to `cloudflare-workers-ai`, usable from codemode scripts and extensions ([#10316](https://github.com/earendil-works/pi/pull/10316) by [@ndisidore](https://github.com/ndisidore), [#10322](https://github.com/earendil-works/pi/pull/10322) by [@RealAlexandreAI](https://github.com/RealAlexandreAI)).

### Fixed

- Empty comma-separated entries in `--models` no longer add unintended models to the cycle ([#10334](https://github.com/earendil-works/pi/issues/10334)).
- Pinned `brace-expansion` to 5.0.12 so installations use the patched dependency ([#10288](https://github.com/earendil-works/pi/issues/10288)).
- MCP OAuth sign-in now rejects pasted redirect URLs whose origin or path differs from the current sign-in's redirect URI.

### Removed

- Removed the daxnuts easter egg shown when selecting `opencode/kimi-k2.5`.

## [0.9.26-alpha.2] - 2026-10-02

### Added

- Added `pi.registerProviderAlias({ id, provider, name })` for separate account logins that inherit a provider's model catalog and request behavior without copying its implementation. Existing credentials under the alias ID are preserved.

### Fixed

- Fixed `/login` hiding the redirect-URL paste field for native extension providers that declare `auth.oauth.usesCallbackServer`, including built-in OAuth flows cloned under custom provider IDs ([#3400](https://github.com/bastani-inc/atomic/issues/3400)).

## [0.9.26-alpha.1] - 2026-10-01

### Breaking Changes

- MCP OAuth credentials are now stored per server name and URL, so MCP servers with the same URL can sign in with different accounts. Credentials stored by URL alone move to the first server that uses them ([#10252](https://github.com/earendil-works/pi/issues/10252)). SDK consumers of the exported `McpOAuthCredentialStore` must now pass the server name to `forServer`, `tokens`, `remove` and `removeAsync`.

### Added

- Added `quietStartup: "header"`, which keeps the startup header but hides the model scope line. `/settings` offers it alongside `true` and `false`.
- Added `models.generateImages()` to codemode scripts. It runs image models such as OpenRouter's with the session's credentials and returns base64 image blocks that `image()` attaches to the result; usage counts toward the session cost like `models.classify()`. Extensions can call `ctx.modelRegistry.generateImages()`. See [Built-in tools](docs/tools.md#codemode).

### Changed

- Cancelling a login started from a `/login` menu now returns to that menu.
- The "Cloud Providers" section of the [Providers](docs/providers.md) docs page is now "Provider Specific Config".
- Codemode costs fewer prompt tokens. The `codemode` description points to the new [Codemode](docs/codemode.md) reference for the `models` API, which the model reads when it needs it, and declared tools say in one line how scripts call them and what the call resolves to instead of repeating their full declaration. The MCP server section of the system prompt only explains the ways of reaching tools that its servers use, and the system prompt points to the new reference.
- Codemode errors now say how to recover: reading a tool or `models` member that does not exist names the close matches (`tools.Bash` suggests `tools.bash`), `models.classify()` and `models.generateImages()` reject malformed arguments with the expected shape, an unknown model points to `models.getAvailableOfType()`, an oversized `store()` value explains what the store is for, and a script that generates images without showing them gets a note. Scripts that probed for a tool with `typeof tools.name` must use `"name" in tools`.
- `/login` and `/logout` now label providers without credentials as "not configured" instead of "unconfigured".
- OAuth browser pages now show the Atomic logo in color instead of white.

### Fixed

- `/mcp login` now prints its sign-in URL as a clickable link in the terminal UI, even when the URL wraps ([#10186](https://github.com/earendil-works/pi/issues/10186)).
- `--provider` without `--model` now fails with an error instead of being silently ignored while the default model from another provider runs ([#10236](https://github.com/earendil-works/pi/issues/10236)).
- MCP servers that ask for more scope (`insufficient_scope`) no longer trigger repeated sign-in requests. The new sign-in now keeps the scopes granted earlier instead of requesting only the missing ones.
- User messages in the transcript now keep one copy of each rendered line instead of two full-width copies, with identical output.
- Deferred MCP tools that `tool_search` loaded are no longer dropped on resume and `/reload` when their server reconnects before the next prompt; the session restored its tools before the MCP servers reconnected.
- The `system` theme keeps pastel terminal palettes pastel instead of making their accent colors more saturated at the lightness the theme needs ([#10255](https://github.com/earendil-works/pi/issues/10255), [#10293](https://github.com/earendil-works/pi/pull/10293) by [@dgtlntv](https://github.com/dgtlntv)).
- `/login` and `/logout` no longer label every OAuth sign-in as a subscription. Only subscription-backed providers say "subscription"; other OAuth sign-ins say "account".
- The startup header logo no longer renders with gaps in Apple Terminal; it shows a text `∀ Atomic` wordmark with the version and session details instead.
- Slash command autocompletion now triggers when the input starts with whitespace ([#10218](https://github.com/earendil-works/pi/pull/10218) by [@haoqixu](https://github.com/haoqixu)).
- Color no longer bleeds past mouse selections in fullscreen mode when a styled token ends at the selection boundary ([#10169](https://github.com/earendil-works/pi/issues/10169)).
- The transcript retains less memory per rendered message; a long assistant message keeps about a fifth of the heap it kept before.

## [0.9.25] - 2026-10-01

### Breaking Changes

- Replaced the old MCP adapter with the native client. Only `~/.atomic/agent/mcp.json` and trusted `.atomic/mcp.json` are read, using the native configuration shape. Shared config layers, client imports, and old-field translations are no longer supported; old OAuth credentials are not imported.
- MCP tools now use `mcp__<server>__<tool>` names and default to `codemode` exposure. The old gateway, configurable prefixes, lazy/idle lifecycle settings, metadata cache, SSE transport, MCP Apps rendering, and setup command are removed. Enabled servers connect in the background; `/mcp reconnect` without a name selects one server rather than reconnecting every server.

### Added

- MCP servers can set `oauth.authServerMetadataUrl` to a trusted authorization server metadata document when discovery advertises the wrong server or none. See [MCP authentication](docs/mcp-servers.md#override-oauth-authorization-server-discovery) ([#10172](https://github.com/earendil-works/pi/issues/10172)).
- Added `/mcp login [server]`, command completion, and server selection for login, logout, and reconnect. The manager offers per-server action menus for enabled state and tool exposure, and OAuth sign-in accepts a pasted redirect URL when the browser is on another machine.
- MCP servers support `codemode`, `deferred`, `direct`, and `hidden` exposure with per-tool pattern overrides, standard resource list/read tools, and per-server OAuth callback ports and URLs. See [MCP servers](docs/mcp-servers.md).
- Added shell commands `atomic mcp add`, `remove`, `list`, `login`, and `logout` to configure, check, and authenticate file-configured servers without starting a session.
- SDK hosts can now manage the workflow runs their session owns through `session.workflows`: `listRuns()`, `getRun()`, `getStages()`, `pause()`, `quit()` and `resume()` return the workflow tool's structured results, and failures reject with typed `WorkflowRunControlError` subclasses (`WorkflowRunNotFoundError`, `WorkflowRunOwnershipError`, `WorkflowRunNotResumableError`, `WorkflowRunDatabaseError`, `WorkflowStageNotFoundError`, `WorkflowStageAmbiguousError`, `WorkflowStageResumeUnsupportedError`) instead of strings. A batch `pause({ all: true })` or `quit({ all: true })` returns a `partial` outcome whose `failedRuns` names the runs still active only when that call stopped at least one run, and rejects with `WorkflowRunControlError` carrying `failedRuns` when active runs could not be stopped and the call stopped none, even if other runs were already paused. Already-paused and already-ended runs are not failures. A database outage while checking who owns a run rejects `pause()` and `quit()` with `WorkflowRunDatabaseError`. It uses the same ownership and durable-resume rules as the `workflow` tool, so runs owned by another session or live process stay read-only ([#3377](https://github.com/bastani-inc/atomic/issues/3377)).
- Codemode scripts can retrieve namespace summaries, instructions, and callable tool names with `describeNamespace(name)`. Tool search indexes namespace instructions without including them in inline tool listings, and codemode listings no longer include tool counts ([#10212](https://github.com/earendil-works/pi/issues/10212)).
- `/reload` enables tools newly added to `defaultTools`, while preserving session-disabled tools, tools removed from the setting, and explicit CLI tool restrictions ([#10245](https://github.com/earendil-works/pi/issues/10245)).
- Added Anthropic workload identity federation from the `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` environment variables, with optional `ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID`. See [Providers](docs/providers.md#anthropic-workload-identity-federation) ([#10177](https://github.com/earendil-works/pi/issues/10177), [#10242](https://github.com/earendil-works/pi/pull/10242) by [@philfreo](https://github.com/philfreo)).
- MCP servers can set `oauth.clientName` for dynamic OAuth registration, or use `"auth": { "provider": "<provider>" }` to send the current provider login token. Provider-token authentication is restricted to global configuration and extension registrations, with HTTPS required except on loopback hosts. See [MCP authentication](docs/mcp-servers.md#authentication) ([#10226](https://github.com/earendil-works/pi/issues/10226)).
- `/login anthropic` now offers **Copy code login (headless)** when the browser is on another machine and cannot reach Atomic's local callback ([#10194](https://github.com/earendil-works/pi/pull/10194) by [@lucasmeijer](https://github.com/lucasmeijer)).
- Added GPT-6.1 Sol and its first-party Fast variants for OpenAI API keys and ChatGPT Codex subscriptions.
- Added **Sign in with ChatGPT** for the OpenAI provider, independently of Codex backend login.
- Added Jev decision models on Vercel AI Gateway and OpenCode Zen.
- Added configurable fullscreen mouse-wheel scrolling with terminal-aware automatic behavior or a fixed line count.
- Extension tools can declare exposure, namespaces, annotations and output schemas, and invoke permitted nested tools through the ordinary validation and permission hooks.
- Added a selectable system theme based on the terminal's palette and contrast, and OKHSL colors for custom themes.
- Added opt-in `codemode` scripts for composing permitted tools, filtering output, branch-local JSON storage, and classifier calls, plus `tool_search` for activating deferred tools. Interactive and orchestration tools remain unavailable from scripts.
- Added `builtin:llama.cpp`, `builtin:codemode`, and `builtin:tool-search` extension resources for explicit loading and settings-based enable/disable controls, with diagnostics when an extension replaces codemode or tool search.
- Added Ultrafast for GPT-6 Astra as `openai/gpt-6-astra-ultrafast` and `openai-codex/gpt-6-astra-ultrafast`, priced at OpenAI's published Ultrafast rates. GPT-6 Astra is the only model with an Ultrafast choice. If the provider rejects an Ultrafast choice you selected, the request fails.

### Changed

- MCP now uses pi's native client, tool exposure, resources, and server manager. Calls, including codemode calls, pass through Atomic's tool permission pipeline. Native OAuth credentials are stored in `mcp-auth.json`.
- `defaultTools` now accepts `+name` and `-name` modifiers. Project modifier-only lists adjust the global selection instead of replacing it.
- Automatic model routing now includes GPT-6.1 Sol results from Artificial Analysis, FrontierCode, and OpenAI's published external benchmarks, preserving measured efforts and independent reporters. Metadata-backed Fast variants use base-model evidence without inventing separate Fast scores; independently registered Fast IDs require their own evidence.
- Completed `bash` commands now return bounded structured output for programmatic callers, including nonzero exits, without increasing model-facing output limits.
- Codex's automatic provider default is now GPT-6.1 Sol. Explicit saved model selections remain unchanged.
- Reduced transcript/footer and shell-preview rendering work in long sessions.
- Tool previews without custom renderers now show their arguments in collapsed and expanded views.
- `-fast` and `-ultrafast` choices for OpenAI and Codex models now follow the tiers each model advertises in Codex's catalog and on OpenAI's pricing page. Models without Fast, such as `gpt-5.3-codex-spark`, `openai/gpt-5-nano`, `openai/gpt-5-pro`, and `openai/o1`, no longer list a `-fast` choice. A tier the model doesn't advertise is left out of the request, which runs at standard processing.
- OpenAI and Codex Fast usage is priced at each model's published Fast rates instead of a flat 2x, for example 1.7x for GPT-4o. A Fast request that OpenAI serves at Standard is priced at Standard.

### Fixed

- `/session` now shows current message counts, token totals, cost, and the correct session ID during an interactive run instead of displaying the previous turn's statistics or zero counts on the first run ([#3383](https://github.com/bastani-inc/atomic/issues/3383)).
- MCP OAuth rejects authorization responses from another issuer, including a missing `iss` when the server promises it under RFC 9207.
- MCP OAuth sign-in and refresh now tolerate empty or null optional token and registration fields, and empty requested scopes use the next scope source ([#10266](https://github.com/earendil-works/pi/issues/10266)).
- MCP sign-out no longer lets an in-progress OAuth refresh restore deleted credentials.
- Session shutdown now closes MCP connections that are still opening instead of leaving them running after the session ends.
- Replacing an extension-contributed MCP server now retires pending connections, and reloading with active servers no longer fails during connection cleanup.
- Malformed MCP configuration errors no longer quote file contents that may contain credentials.
- MCP OAuth refreshes authorization-server discovery when a server advertises a changed metadata URL.
- On a heavily loaded machine, a workflow run could fail with a bare `Query read timeout` while Atomic verified its managed PostgreSQL connection, even though the database was healthy. Health queries now get more time (3 s) on every path, and both the check of a borrowed connection and Atomic's periodic background health check repeat once after a timeout. If the timeout persists, the run fails with a resumable database-unavailable error instead of `Query read timeout`, and other running workflows' database connections are not closed and PostgreSQL is not restarted. A refused or terminated connection, a server shutdown, or a changed database identity is still treated as an outage.
- Renderer examples now retain the built-in tools' prompt guidance and execution behavior when customizing their display ([#10072](https://github.com/earendil-works/pi/issues/10072)).
- Reduced model lookup overhead when merging large remote provider catalogs.
- Invalid extension commands now fail registration with a clear error instead of crashing slash-command completion ([#10054](https://github.com/earendil-works/pi/issues/10054)).
- Codemode and tool-search descriptions now remain unchanged when deferred tools register, preserving prompt stability while discovery continues to find those tools ([#10212](https://github.com/earendil-works/pi/issues/10212)).
- Collapsed codemode scripts and results now limit wrapped screen lines, so long single-line JSON no longer fills the transcript. Bash previews use the same cached visual-line component.
- In `codemode.mode: "only"`, the system prompt's tool list no longer advertises tools whose direct declarations are hidden ([#10192](https://github.com/earendil-works/pi/issues/10192)).
- Fixed context overflow detection for Z.AI CN endpoint `Prompt exceeds max length` errors ([#10208](https://github.com/earendil-works/pi/issues/10208)).
- Anthropic tools with strict-schema constraints the provider rejects now fall back to non-strict mode when strict sampling is preferred ([#9953](https://github.com/earendil-works/pi/issues/9953)).
- Provider retries now use exponential backoff when `Retry-After` contains an unparseable date ([#9571](https://github.com/earendil-works/pi/issues/9571)).
- MCP sign-in URLs remain clickable when they wrap, including inside the `/mcp` panel ([#10186](https://github.com/earendil-works/pi/issues/10186)).
- MCP tool names now use JavaScript-safe identifiers with deterministic suffixes for collisions and long names, consistently across direct tools, gateway discovery, and codemode ([#10239](https://github.com/earendil-works/pi/issues/10239)).
- Codemode `image()` now rejects malformed base64 and unsupported image types before saving a tool result, and detects the MIME type from the image data instead of trusting the supplied type ([#10215](https://github.com/earendil-works/pi/issues/10215)).
- OpenAI's **Sign in with ChatGPT** login now shows a redirect-URL paste input, allowing authentication from remote machines without a reachable local browser callback.
- SDK-created sessions now discover package-provided workflows from their resource loader, including after workflow reload, while preserving project trust and custom loaders without workflow resources ([#3372](https://github.com/bastani-inc/atomic/issues/3372)).
- SDK-created sessions now retain package-contributed MCP servers when adding the shipped MCP extension to a custom resource loader ([#3372](https://github.com/bastani-inc/atomic/issues/3372)).
- Structured decisions and schema-backed workflow results now accept strict-provider null placeholders for omitted optional fields instead of exhausting output repairs. Explicitly nullable fields keep their null values, and required fields still enforce the original schema.
- Theme-colored notices now refresh after theme changes and late terminal color replies.
- llama.cpp unloaded autoload presets now retain their cached effective context size instead of falling back to the model's training limit.
- Native extension providers with stored credentials become available immediately after registration without exposing unrelated unauthenticated models.
- Cancelled tools no longer start queued native or nested sequential calls after cancellation.
- The interactive footer's context percentage, token, cost and cache-hit (`CH`) segments update after every message again, instead of only when a run ends. A new session no longer shows `0.0%` with no token or cost segments for its whole first run, and steered or long runs keep the footer current. The footer totals now include cache-warming requests, which `/session` already counts, and RPC `get_session_stats` responses report the newest reply's usage as `latestAssistantUsage` ([#3328](https://github.com/bastani-inc/atomic/issues/3328); [#3364](https://github.com/bastani-inc/atomic/pull/3364) by [@dairefagan](https://github.com/dairefagan)).

## [0.9.25-alpha.5] - 2026-09-30

### Fixed

- `/session` now shows current message counts, token totals, cost, and the correct session ID during an interactive run instead of displaying the previous turn's statistics or zero counts on the first run ([#3383](https://github.com/bastani-inc/atomic/issues/3383)).

## [0.9.25-alpha.4] - 2026-09-30

### Breaking Changes

- Replaced the old MCP adapter with the native client. Only `~/.atomic/agent/mcp.json` and trusted `.atomic/mcp.json` are read, using the native configuration shape. Shared config layers, client imports, and old-field translations are no longer supported; old OAuth credentials are not imported.
- MCP tools now use `mcp__<server>__<tool>` names and default to `codemode` exposure. The old gateway, configurable prefixes, lazy/idle lifecycle settings, metadata cache, SSE transport, MCP Apps rendering, and setup command are removed. Enabled servers connect in the background; `/mcp reconnect` without a name selects one server rather than reconnecting every server.

### Added

- MCP servers can set `oauth.authServerMetadataUrl` to a trusted authorization server metadata document when discovery advertises the wrong server or none. See [MCP authentication](docs/mcp-servers.md#override-oauth-authorization-server-discovery) ([#10172](https://github.com/earendil-works/pi/issues/10172)).
- Added `/mcp login [server]`, command completion, and server selection for login, logout, and reconnect. The manager offers per-server action menus for enabled state and tool exposure, and OAuth sign-in accepts a pasted redirect URL when the browser is on another machine.
- MCP servers support `codemode`, `deferred`, `direct`, and `hidden` exposure with per-tool pattern overrides, standard resource list/read tools, and per-server OAuth callback ports and URLs. See [MCP servers](docs/mcp-servers.md).
- Added shell commands `atomic mcp add`, `remove`, `list`, `login`, and `logout` to configure, check, and authenticate file-configured servers without starting a session.

### Changed

- MCP now uses pi's native client, tool exposure, resources, and server manager. Calls, including codemode calls, pass through Atomic's tool permission pipeline. Native OAuth credentials are stored in `mcp-auth.json`.

### Fixed

- MCP OAuth rejects authorization responses from another issuer, including a missing `iss` when the server promises it under RFC 9207.
- MCP OAuth sign-in and refresh now tolerate empty or null optional token and registration fields, and empty requested scopes use the next scope source ([#10266](https://github.com/earendil-works/pi/issues/10266)).
- MCP sign-out no longer lets an in-progress OAuth refresh restore deleted credentials.
- Session shutdown now closes MCP connections that are still opening instead of leaving them running after the session ends.
- Replacing an extension-contributed MCP server now retires pending connections, and reloading with active servers no longer fails during connection cleanup.
- Malformed MCP configuration errors no longer quote file contents that may contain credentials.
- MCP OAuth refreshes authorization-server discovery when a server advertises a changed metadata URL.

## [0.9.25-alpha.3] - 2026-09-30

### Added

- SDK hosts can now manage the workflow runs their session owns through `session.workflows`: `listRuns()`, `getRun()`, `getStages()`, `pause()`, `quit()` and `resume()` return the workflow tool's structured results, and failures reject with typed `WorkflowRunControlError` subclasses (`WorkflowRunNotFoundError`, `WorkflowRunOwnershipError`, `WorkflowRunNotResumableError`, `WorkflowRunDatabaseError`, `WorkflowStageNotFoundError`, `WorkflowStageAmbiguousError`, `WorkflowStageResumeUnsupportedError`) instead of strings. A batch `pause({ all: true })` or `quit({ all: true })` returns a `partial` outcome whose `failedRuns` names the runs still active only when that call stopped at least one run, and rejects with `WorkflowRunControlError` carrying `failedRuns` when active runs could not be stopped and the call stopped none, even if other runs were already paused. Already-paused and already-ended runs are not failures. A database outage while checking who owns a run rejects `pause()` and `quit()` with `WorkflowRunDatabaseError`. It uses the same ownership and durable-resume rules as the `workflow` tool, so runs owned by another session or live process stay read-only ([#3377](https://github.com/bastani-inc/atomic/issues/3377)).
- Codemode scripts can retrieve namespace summaries, instructions, and callable tool names with `describeNamespace(name)`. Tool search indexes namespace instructions without including them in inline tool listings, and codemode listings no longer include tool counts ([#10212](https://github.com/earendil-works/pi/issues/10212)).
- `/reload` enables tools newly added to `defaultTools`, while preserving session-disabled tools, tools removed from the setting, and explicit CLI tool restrictions ([#10245](https://github.com/earendil-works/pi/issues/10245)).
- Added Anthropic workload identity federation from the `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` environment variables, with optional `ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID`. See [Providers](docs/providers.md#anthropic-workload-identity-federation) ([#10177](https://github.com/earendil-works/pi/issues/10177), [#10242](https://github.com/earendil-works/pi/pull/10242) by [@philfreo](https://github.com/philfreo)).
- MCP servers can set `oauth.clientName` for dynamic OAuth registration, or use `"auth": { "provider": "<provider>" }` to send the current provider login token. Provider-token authentication is restricted to global configuration and extension registrations, with HTTPS required except on loopback hosts. See [MCP authentication](docs/mcp-servers.md#authentication) ([#10226](https://github.com/earendil-works/pi/issues/10226)).
- `/login anthropic` now offers **Copy code login (headless)** when the browser is on another machine and cannot reach Atomic's local callback ([#10194](https://github.com/earendil-works/pi/pull/10194) by [@lucasmeijer](https://github.com/lucasmeijer)).

### Fixed

- On a heavily loaded machine, a workflow run could fail with a bare `Query read timeout` while Atomic verified its managed PostgreSQL connection, even though the database was healthy. Health queries now get more time (3 s) on every path, and both the check of a borrowed connection and Atomic's periodic background health check repeat once after a timeout. If the timeout persists, the run fails with a resumable database-unavailable error instead of `Query read timeout`, and other running workflows' database connections are not closed and PostgreSQL is not restarted. A refused or terminated connection, a server shutdown, or a changed database identity is still treated as an outage.
- Renderer examples now retain the built-in tools' prompt guidance and execution behavior when customizing their display ([#10072](https://github.com/earendil-works/pi/issues/10072)).
- Reduced model lookup overhead when merging large remote provider catalogs.
- Invalid extension commands now fail registration with a clear error instead of crashing slash-command completion ([#10054](https://github.com/earendil-works/pi/issues/10054)).
- Codemode and tool-search descriptions now remain unchanged when deferred tools register, preserving prompt stability while discovery continues to find those tools ([#10212](https://github.com/earendil-works/pi/issues/10212)).
- Collapsed codemode scripts and results now limit wrapped screen lines, so long single-line JSON no longer fills the transcript. Bash previews use the same cached visual-line component.
- In `codemode.mode: "only"`, the system prompt's tool list no longer advertises tools whose direct declarations are hidden ([#10192](https://github.com/earendil-works/pi/issues/10192)).
- Fixed context overflow detection for Z.AI CN endpoint `Prompt exceeds max length` errors ([#10208](https://github.com/earendil-works/pi/issues/10208)).
- Anthropic tools with strict-schema constraints the provider rejects now fall back to non-strict mode when strict sampling is preferred ([#9953](https://github.com/earendil-works/pi/issues/9953)).
- Provider retries now use exponential backoff when `Retry-After` contains an unparseable date ([#9571](https://github.com/earendil-works/pi/issues/9571)).
- MCP sign-in URLs remain clickable when they wrap, including inside the `/mcp` panel ([#10186](https://github.com/earendil-works/pi/issues/10186)).
- MCP tool names now use JavaScript-safe identifiers with deterministic suffixes for collisions and long names, consistently across direct tools, gateway discovery, and codemode ([#10239](https://github.com/earendil-works/pi/issues/10239)).
- Codemode `image()` now rejects malformed base64 and unsupported image types before saving a tool result, and detects the MIME type from the image data instead of trusting the supplied type ([#10215](https://github.com/earendil-works/pi/issues/10215)).

## [0.9.25-alpha.2] - 2026-09-30

### Fixed

- OpenAI's **Sign in with ChatGPT** login now shows a redirect-URL paste input, allowing authentication from remote machines without a reachable local browser callback.
- SDK-created sessions now discover package-provided workflows from their resource loader, including after workflow reload, while preserving project trust and custom loaders without workflow resources ([#3372](https://github.com/bastani-inc/atomic/issues/3372)).
- SDK-created sessions now retain package-contributed MCP servers when adding the shipped MCP extension to a custom resource loader ([#3372](https://github.com/bastani-inc/atomic/issues/3372)).
- Structured decisions and schema-backed workflow results now accept strict-provider null placeholders for omitted optional fields instead of exhausting output repairs. Explicitly nullable fields keep their null values, and required fields still enforce the original schema.

## [0.9.25-alpha.1] - 2026-09-29

### Added

- Added GPT-6.1 Sol and its first-party Fast variants for OpenAI API keys and ChatGPT Codex subscriptions.
- Added **Sign in with ChatGPT** for the OpenAI provider, independently of Codex backend login.
- Added Jev decision models on Vercel AI Gateway and OpenCode Zen.
- Added configurable fullscreen mouse-wheel scrolling with terminal-aware automatic behavior or a fixed line count.
- Extension tools can declare exposure, namespaces, annotations and output schemas, and invoke permitted nested tools through the ordinary validation and permission hooks.
- Added a selectable system theme based on the terminal's palette and contrast, and OKHSL colors for custom themes.
- Added opt-in `codemode` scripts for composing permitted tools, filtering output, branch-local JSON storage, and classifier calls, plus `tool_search` for activating deferred tools. Interactive and orchestration tools remain unavailable from scripts.
- Added `builtin:llama.cpp`, `builtin:codemode`, and `builtin:tool-search` extension resources for explicit loading and settings-based enable/disable controls, with diagnostics when an extension replaces codemode or tool search.
- Added Ultrafast for GPT-6 Astra as `openai/gpt-6-astra-ultrafast` and `openai-codex/gpt-6-astra-ultrafast`, priced at OpenAI's published Ultrafast rates. GPT-6 Astra is the only model with an Ultrafast choice. If the provider rejects an Ultrafast choice you selected, the request fails.

### Changed

- `defaultTools` now accepts `+name` and `-name` modifiers. Project modifier-only lists adjust the global selection instead of replacing it.
- Automatic model routing now includes GPT-6.1 Sol results from Artificial Analysis, FrontierCode, and OpenAI's published external benchmarks, preserving measured efforts and independent reporters. Metadata-backed Fast variants use base-model evidence without inventing separate Fast scores; independently registered Fast IDs require their own evidence.
- Completed `bash` commands now return bounded structured output for programmatic callers, including nonzero exits, without increasing model-facing output limits.
- Codex's automatic provider default is now GPT-6.1 Sol. Explicit saved model selections remain unchanged.
- Reduced transcript/footer and shell-preview rendering work in long sessions.
- Tool previews without custom renderers now show their arguments in collapsed and expanded views.
- `-fast` and `-ultrafast` choices for OpenAI and Codex models now follow the tiers each model advertises in Codex's catalog and on OpenAI's pricing page. Models without Fast, such as `gpt-5.3-codex-spark`, `openai/gpt-5-nano`, `openai/gpt-5-pro`, and `openai/o1`, no longer list a `-fast` choice. A tier the model doesn't advertise is left out of the request, which runs at standard processing.
- OpenAI and Codex Fast usage is priced at each model's published Fast rates instead of a flat 2x, for example 1.7x for GPT-4o. A Fast request that OpenAI serves at Standard is priced at Standard.

### Fixed

- Theme-colored notices now refresh after theme changes and late terminal color replies.
- llama.cpp unloaded autoload presets now retain their cached effective context size instead of falling back to the model's training limit.
- Native extension providers with stored credentials become available immediately after registration without exposing unrelated unauthenticated models.
- Cancelled tools no longer start queued native or nested sequential calls after cancellation.
- The interactive footer's context percentage, token, cost and cache-hit (`CH`) segments update after every message again, instead of only when a run ends. A new session no longer shows `0.0%` with no token or cost segments for its whole first run, and steered or long runs keep the footer current. The footer totals now include cache-warming requests, which `/session` already counts, and RPC `get_session_stats` responses report the newest reply's usage as `latestAssistantUsage` ([#3328](https://github.com/bastani-inc/atomic/issues/3328); [#3364](https://github.com/bastani-inc/atomic/pull/3364) by [@dairefagan](https://github.com/dairefagan)).

## [0.9.24] - 2026-09-29

### Added

- Added inherited Claude Sonnet 5.5 support for Anthropic with adaptive thinking, mid-conversation effort, and a 1M context window.
- llama.cpp models are now also listed as classifier models with the same ID, so a local model can make `model: "auto"` routing decisions the way TypeSafe Jev does. Because an explicit `routerModel` resolves a classifier before a chat model, `routerModel: "llama.cpp/<id>"` now routes with the classifier instead of the chat model ([#10119](https://github.com/earendil-works/pi/pull/10119)).
- Packages can contribute MCP servers with `"atomic": { "mcpServers": "./mcp.json" }` or an inline `mcpServers` object, and extensions can add runtime-computed servers with `pi.registerMcpServer(name, config)` from the factory or `session_start`. Contributed servers are the lowest-precedence MCP layer, so a same-named server in any user or project MCP config replaces them. Package filters accept `mcpServers` name patterns, project packages contribute servers only in trusted projects, and workflow stages see the same servers ([#3355](https://github.com/bastani-inc/atomic/issues/3355)).

### Changed

- Recorded Claude Sonnet 5.5 results in the evals snapshot used by automatic model routing: Artificial Analysis Intelligence Index rows for max, xhigh, high, medium and low effort, Cognition FrontierCode 1.1, and Anthropic's published OSWorld, AutomationBench, BenchCAD, Humanity's Last Exam (with tools) and Terminal-Bench-Science scores.

### Fixed

- OpenCode Go now defaults to Kimi K3 (`kimi-k3`). models.dev deprecated Kimi K2.6 for OpenCode Go, which removed it from the built-in catalog, so the previous default no longer resolved.
- Together now defaults to Kimi K3 (`moonshotai/Kimi-K3`). models.dev removed Kimi K2.6 from Together, which removed it from the built-in catalog, so the previous default no longer resolved.
- Fixed extension tool schemas leaking TypeBox `~kind`/`~optional` metadata into provider requests, which made Fireworks reject every request with `JSON Schema not supported` ([#3330](https://github.com/bastani-inc/atomic/issues/3330)).
- Resuming a session no longer appends a spurious system-prompt `tools` update when only the order of the active tools changed, so the resumed session keeps its prompt cache and the model is no longer told that unchanged tools were removed ([#3346](https://github.com/bastani-inc/atomic/issues/3346)).
- Switching sessions with `/resume` or closing a session before sending a message no longer leaves a header-only session file behind ([#3347](https://github.com/bastani-inc/atomic/issues/3347)).
- Fixed pasting files copied in Finder with `Ctrl+V` inserting the file icon as an image instead of the file paths. Copied files now paste as their paths, shell-quoted in bash mode, and clipboard read errors are reported instead of pasting the icon ([#10136](https://github.com/earendil-works/pi/pull/10136)).

## [0.9.24-alpha.1] - 2026-09-28

### Added

- Added inherited Claude Sonnet 5.5 support for Anthropic with adaptive thinking, mid-conversation effort, and a 1M context window.
- llama.cpp models are now also listed as classifier models with the same ID, so a local model can make `model: "auto"` routing decisions the way TypeSafe Jev does. Because an explicit `routerModel` resolves a classifier before a chat model, `routerModel: "llama.cpp/<id>"` now routes with the classifier instead of the chat model ([#10119](https://github.com/earendil-works/pi/pull/10119)).
- Packages can contribute MCP servers with `"atomic": { "mcpServers": "./mcp.json" }` or an inline `mcpServers` object, and extensions can add runtime-computed servers with `pi.registerMcpServer(name, config)` from the factory or `session_start`. Contributed servers are the lowest-precedence MCP layer, so a same-named server in any user or project MCP config replaces them. Package filters accept `mcpServers` name patterns, project packages contribute servers only in trusted projects, and workflow stages see the same servers ([#3355](https://github.com/bastani-inc/atomic/issues/3355)).

### Changed

- Recorded Claude Sonnet 5.5 results in the evals snapshot used by automatic model routing: Artificial Analysis Intelligence Index rows for max, xhigh, high, medium and low effort, Cognition FrontierCode 1.1, and Anthropic's published OSWorld, AutomationBench, BenchCAD, Humanity's Last Exam (with tools) and Terminal-Bench-Science scores.

### Fixed

- OpenCode Go now defaults to Kimi K3 (`kimi-k3`). models.dev deprecated Kimi K2.6 for OpenCode Go, which removed it from the built-in catalog, so the previous default no longer resolved.
- Together now defaults to Kimi K3 (`moonshotai/Kimi-K3`). models.dev removed Kimi K2.6 from Together, which removed it from the built-in catalog, so the previous default no longer resolved.
- Fixed extension tool schemas leaking TypeBox `~kind`/`~optional` metadata into provider requests, which made Fireworks reject every request with `JSON Schema not supported` ([#3330](https://github.com/bastani-inc/atomic/issues/3330)).
- Resuming a session no longer appends a spurious system-prompt `tools` update when only the order of the active tools changed, so the resumed session keeps its prompt cache and the model is no longer told that unchanged tools were removed ([#3346](https://github.com/bastani-inc/atomic/issues/3346)).
- Switching sessions with `/resume` or closing a session before sending a message no longer leaves a header-only session file behind ([#3347](https://github.com/bastani-inc/atomic/issues/3347)).
- Fixed pasting files copied in Finder with `Ctrl+V` inserting the file icon as an image instead of the file paths. Copied files now paste as their paths, shell-quoted in bash mode, and clipboard read errors are reported instead of pasting the icon ([#10136](https://github.com/earendil-works/pi/pull/10136)).

## [0.9.23] - 2026-09-27

### Fixed

- Long sessions no longer leak one open file per bash command. A finished command now releases its output file, an output file with no output is deleted, and output files left behind by Atomic processes that have exited are removed. On macOS, where a child process cannot receive a file descriptor numbered 10240 or higher, the leak eventually made every bash call and spawn fail with `Bad file descriptor`; if a spawn still fails that way, the error now names the cause ([#3313](https://github.com/bastani-inc/atomic/issues/3313)).
- Resuming a run that failed in a `ctx.tool` call now works when the call ran in parallel with other tools. Siblings that were cancelled or also threw when the run failed are run again on resume, in any order, instead of failing with `replay topology mismatch` or `unfinished or missing completed tool checkpoint`. A resume rejected by changed code no longer runs the failed tool or makes the run unresumable, including after Atomic restarts ([#3314](https://github.com/bastani-inc/atomic/issues/3314)).
- `model: "auto"` routing no longer ignores benchmark results when fewer than four models are available. Each model's results are now ranked against every model in the evals catalog rather than only the models you can route to, so with just two or three models (for example Opus and Haiku) the stronger model is no longer outranked on price alone, and a model scores the same however many models are enabled.

## [0.9.23-alpha.1] - 2026-09-27

### Fixed

- Long sessions no longer leak one open file per bash command. A finished command now releases its output file, an output file with no output is deleted, and output files left behind by Atomic processes that have exited are removed. On macOS, where a child process cannot receive a file descriptor numbered 10240 or higher, the leak eventually made every bash call and spawn fail with `Bad file descriptor`; if a spawn still fails that way, the error now names the cause ([#3313](https://github.com/bastani-inc/atomic/issues/3313)).
- Resuming a run that failed in a `ctx.tool` call now works when the call ran in parallel with other tools. Siblings that were cancelled or also threw when the run failed are run again on resume, in any order, instead of failing with `replay topology mismatch` or `unfinished or missing completed tool checkpoint`. A resume rejected by changed code no longer runs the failed tool or makes the run unresumable, including after Atomic restarts ([#3314](https://github.com/bastani-inc/atomic/issues/3314)).
- `model: "auto"` routing no longer ignores benchmark results when fewer than four models are available. Each model's results are now ranked against every model in the evals catalog rather than only the models you can route to, so with just two or three models (for example Opus and Haiku) the stronger model is no longer outranked on price alone, and a model scores the same however many models are enabled.

## [0.9.22] - 2026-09-26

### Fixed

- Schema-backed workflow stages and `structured_output` results are no longer capped at 4,096 output tokens. Large typed results such as plans, inventories and multi-item reviews can use the selected model's full output limit instead of failing with `Structured output repair exhausted` or coming back shortened ([#3309](https://github.com/bastani-inc/atomic/issues/3309)).

## [0.9.22-alpha.1] - 2026-09-26

### Fixed

- Schema-backed workflow stages and `structured_output` results are no longer capped at 4,096 output tokens. Large typed results such as plans, inventories and multi-item reviews can use the selected model's full output limit instead of failing with `Structured output repair exhausted` or coming back shortened ([#3309](https://github.com/bastani-inc/atomic/issues/3309)).

## [0.9.21] - 2026-09-26

### Added

- Subagent calls and workflow stages with `model: "auto"` accept `taskNeeds` (`work`, `difficulty`, `mistakeCost`, `needsImages`, `longContext`, `latencySensitive`) so the caller can say what it already knows about the task, and `modelConstraints.allowedModels` now sets the shortlist of models the router chooses between, as many as fit one routing request. `modelConstraints` also accepts `allowedProviders` and `excludedProviders`; setting either on a call replaces the `modelRouting` provider settings for that call, while lists in agent definitions or inherited workflow constraints only narrow them.
- Added the `modelRouting` setting with `allowedProviders` and `excludedProviders` lists, so you can keep `model: "auto"` from routing workflow stages and subagents to providers you don't want to use, for example to prefer your subscriptions over API-billed providers.
- Added a show/hide toggle (`H`) in HTML exports for custom messages marked `display: false`. They stay hidden by default and can also be revealed by selecting them in the sidebar ([#8896](https://github.com/earendil-works/pi/issues/8896)).
- Successful RPC `prompt`, `steer`, and `follow_up` responses now include `data.disposition`: `"handled"` when an extension command or input handler consumed the input, `"queued"` when it was queued, or, for `prompt`, `"started"` when it started a run, so clients know whether to wait for `agent_settled`. `AgentSession.steer()`/`followUp()` and `RpcClient.prompt()`/`steer()`/`followUp()` return the same value, and SDK `preflightResult` callbacks receive it as a second argument ([#9098](https://github.com/earendil-works/pi/issues/9098), [#9803](https://github.com/earendil-works/pi/issues/9803)).

### Changed

- Automatic model routing now has a chat model (`routerModel` when it is one, otherwise your current chat model) answer a few fixed questions about the task: kind of work, difficulty, how costly a mistake is, whether it needs images, whether it must hold a very large context, and whether speed matters. The router then chooses between a shortlist of models ranked in code from the evaluations page, each described with its own results, price tier and release date, and sees only those answers: a classifier router such as Jev never receives the task. A decision takes at most two small requests however many models you have, effort follows the task's difficulty (one level lower when speed matters), and computer-use tasks only go to models that can read images. When code cannot pick a model (a task needs images and no eligible model reads them, `allowedModels` lists more models than one routing request holds, or `evals.md` is missing), the subagent or stage runs on the current chat model, as it does when the router fails.
- Model routing no longer shortens long tasks: the chat model answering the routing questions reads the complete task, and a task too large for that model's context window is read by the cheapest eligible model whose window holds it.
- The model evaluations page and automatic model routing now include [DeepSWE](https://deepswe.datacurve.ai/) and [FrontierCode](https://cognition.com/frontiercode) leaderboards and a table of vendor- and leaderboard-published results for recent frontier models, covering computer use (OSWorld 2.0, ScreenSpot-Pro, Agents' Last Exam), browsing, CAD, science, math, cybersecurity and ARC-AGI. Each routing request carries these rows for its own candidates.
- Automatic model routing fallbacks (a classifier failing over to the chat model, or a subagent or workflow stage running on the current chat model) are no longer printed or added to the conversation. Set `ATOMIC_MODEL_ROUTING_DEBUG=1` to show them.
- Automatic model routing (`model: "auto"`) now has benchmark evidence for every model on the Artificial Analysis leaderboard instead of the top 27. Each routing request includes only the rows for your eligible models and their effort variants, matched across providers (for example `claude-opus-4.6` on Copilot and `us.anthropic.claude-opus-4-6-v1` on Bedrock).
- Automatic model routing now prefers the most recently released model among candidates in the same role tier and price range, so an older model no longer wins just because it has no benchmark row.
- Switched the build from the TypeScript native preview to TypeScript 7.0 with an ES2024 target ([#9965](https://github.com/earendil-works/pi/issues/9965)).
- Removed the `[Themes]` section from the startup banner. Custom themes remain available in `/settings`, and theme conflicts are still reported.

### Fixed

- Fireworks now defaults to Kimi K3 (`accounts/fireworks/models/kimi-k3`). Fireworks removed Kimi K2.6 from its catalog, so the previous default no longer resolved.
- Fixed pinned git extensions loaded with `-e` continuing to use the first downloaded commit after the ref changes ([#9982](https://github.com/earendil-works/pi/issues/9982)).
- Fixed `RpcClient` skipping the next event listener when a listener unsubscribes while handling an event, which could make `waitForIdle()` time out after `collectEvents()` ([#9990](https://github.com/earendil-works/pi/issues/9990)).
- Fixed new sessions being lost when Atomic exits before the first assistant response. The session file is now created when the first user message is sent, and cloning or forking a session that has not been saved yet asks you to send a message first ([#10000](https://github.com/earendil-works/pi/issues/10000)).
- Fixed custom themes ignoring the `terminal.trueColor` setting and `PI_TRUE_COLOR`; themes loaded from theme directories, packages, and theme paths now use the configured truecolor or 256-color mode, and keep it when the active theme file is edited and reloaded ([#9973](https://github.com/earendil-works/pi/issues/9973)).
- Auto model routing no longer fails over to the chat model when the routing request is too large for Jev. Large catalogs and long workflow or subagent prompts, including large `<keepContext>` spans, are now cut to fit in the router's copy only, marked `[... truncated ...]`, while the stage or subagent still receives the full prompt. When a classifier does fail, the warning now names the HTTP status and error type, for example `HTTP 400 max_tokens_exceeded`.

## [0.9.21-alpha.2] - 2026-09-26

### Added

- Subagent calls and workflow stages with `model: "auto"` accept `taskNeeds` (`work`, `difficulty`, `mistakeCost`, `needsImages`, `longContext`, `latencySensitive`) so the caller can say what it already knows about the task, and `modelConstraints.allowedModels` now sets the shortlist of models the router chooses between, as many as fit one routing request. `modelConstraints` also accepts `allowedProviders` and `excludedProviders`; setting either on a call replaces the `modelRouting` provider settings for that call, while lists in agent definitions or inherited workflow constraints only narrow them.
- Added the `modelRouting` setting with `allowedProviders` and `excludedProviders` lists, so you can keep `model: "auto"` from routing workflow stages and subagents to providers you don't want to use, for example to prefer your subscriptions over API-billed providers.
- Added a show/hide toggle (`H`) in HTML exports for custom messages marked `display: false`. They stay hidden by default and can also be revealed by selecting them in the sidebar ([#8896](https://github.com/earendil-works/pi/issues/8896)).
- Successful RPC `prompt`, `steer`, and `follow_up` responses now include `data.disposition`: `"handled"` when an extension command or input handler consumed the input, `"queued"` when it was queued, or, for `prompt`, `"started"` when it started a run, so clients know whether to wait for `agent_settled`. `AgentSession.steer()`/`followUp()` and `RpcClient.prompt()`/`steer()`/`followUp()` return the same value, and SDK `preflightResult` callbacks receive it as a second argument ([#9098](https://github.com/earendil-works/pi/issues/9098), [#9803](https://github.com/earendil-works/pi/issues/9803)).

### Changed

- Automatic model routing now has a chat model (`routerModel` when it is one, otherwise your current chat model) answer a few fixed questions about the task: kind of work, difficulty, how costly a mistake is, whether it needs images, whether it must hold a very large context, and whether speed matters. The router then chooses between a shortlist of models ranked in code from the evaluations page, each described with its own results, price tier and release date, and sees only those answers: a classifier router such as Jev never receives the task. A decision takes at most two small requests however many models you have, effort follows the task's difficulty (one level lower when speed matters), and computer-use tasks only go to models that can read images. When code cannot pick a model (a task needs images and no eligible model reads them, `allowedModels` lists more models than one routing request holds, or `evals.md` is missing), the subagent or stage runs on the current chat model, as it does when the router fails.
- Model routing no longer shortens long tasks: the chat model answering the routing questions reads the complete task, and a task too large for that model's context window is read by the cheapest eligible model whose window holds it.
- The model evaluations page and automatic model routing now include [DeepSWE](https://deepswe.datacurve.ai/) and [FrontierCode](https://cognition.com/frontiercode) leaderboards and a table of vendor- and leaderboard-published results for recent frontier models, covering computer use (OSWorld 2.0, ScreenSpot-Pro, Agents' Last Exam), browsing, CAD, science, math, cybersecurity and ARC-AGI. Each routing request carries these rows for its own candidates.
- Automatic model routing fallbacks (a classifier failing over to the chat model, or a subagent or workflow stage running on the current chat model) are no longer printed or added to the conversation. Set `ATOMIC_MODEL_ROUTING_DEBUG=1` to show them.
- Automatic model routing (`model: "auto"`) now has benchmark evidence for every model on the Artificial Analysis leaderboard instead of the top 27. Each routing request includes only the rows for your eligible models and their effort variants, matched across providers (for example `claude-opus-4.6` on Copilot and `us.anthropic.claude-opus-4-6-v1` on Bedrock).
- Automatic model routing now prefers the most recently released model among candidates in the same role tier and price range, so an older model no longer wins just because it has no benchmark row.
- Switched the build from the TypeScript native preview to TypeScript 7.0 with an ES2024 target ([#9965](https://github.com/earendil-works/pi/issues/9965)).
- Removed the `[Themes]` section from the startup banner. Custom themes remain available in `/settings`, and theme conflicts are still reported.

### Fixed

- Fixed pinned git extensions loaded with `-e` continuing to use the first downloaded commit after the ref changes ([#9982](https://github.com/earendil-works/pi/issues/9982)).
- Fixed `RpcClient` skipping the next event listener when a listener unsubscribes while handling an event, which could make `waitForIdle()` time out after `collectEvents()` ([#9990](https://github.com/earendil-works/pi/issues/9990)).
- Fixed new sessions being lost when Atomic exits before the first assistant response. The session file is now created when the first user message is sent, and cloning or forking a session that has not been saved yet asks you to send a message first ([#10000](https://github.com/earendil-works/pi/issues/10000)).
- Fixed custom themes ignoring the `terminal.trueColor` setting and `PI_TRUE_COLOR`; themes loaded from theme directories, packages, and theme paths now use the configured truecolor or 256-color mode, and keep it when the active theme file is edited and reloaded ([#9973](https://github.com/earendil-works/pi/issues/9973)).

## [0.9.21-alpha.1] - 2026-09-25

### Fixed

- Auto model routing no longer fails over to the chat model when the routing request is too large for Jev. Large catalogs and long workflow or subagent prompts, including large `<keepContext>` spans, are now cut to fit in the router's copy only, marked `[... truncated ...]`, while the stage or subagent still receives the full prompt. When a classifier does fail, the warning now names the HTTP status and error type, for example `HTTP 400 max_tokens_exceeded`.

## [0.9.20] - 2026-09-24

### Breaking Changes

- Direct TypeSafe decisions now use the canonical `typesafe/jev-latest` classifier model and `typesafe` provider authentication. Replace `typesafe-ai/jev-latest` or `typesafe-ai/jev` in router settings and SDK requests, and re-save keys stored under `typesafe-ai` with `/login typesafe`; old direct IDs, `/login typesafe-ai`, and automatic migration of those saved keys are not supported. `TYPESAFE_API_KEY` and Atomic's `-fast` model behavior are unchanged.
- Removed `getStructuredOutputProviders()`, `JEV_STRUCTURED_OUTPUT_PROVIDER`, and `JevStructuredOutputProvider`. Classifier selection uses only models registered in the current model registry. Previously bundled gateway IDs such as `vercel-ai-gateway/typesafe-ai/jev` and `opencode/jev-1.13` no longer resolve by default. Replace them with a registered classifier such as `typesafe/jev-latest`, `openrouter/~typesafe/jev-latest`, or `cloudflare-workers-ai/typesafe/jev`, a chat model, or explicitly register the classifier and its operation under that ID.
- `structured_output` now infers a schema-validated result from `instructions` and nonempty named `state` instead of treating its arguments as the result. The tool and `generateStructuredOutput()` accept optional exact `model` and ordered `fallbackModels` IDs resolved through the current model registry. A classifier ID uses the generic classify operation. Omit `model` to use the current stage or chat model, which is also the terminal fallback. A classifier runs only for a compatible finite Choice schema and is otherwise skipped. Runtime failures, including provider refusals, advance the fallback chain; cancellation does not. Ordinary text without a result gets up to three repairs before advancing, without checking for refusal wording. General requests derive choice questions from the schema. An explicit `routerModel` still overrides automatic routing; unset or `auto` uses the current chat model and does not select a classifier from saved credentials.
- Renamed the public SDK function `inferStructuredOutput()` to `generateStructuredOutput()`; update imports and calls. `createStructuredOutputTool()` still constructs the session tool. Removed the public `inferRouterDecision()`, `resolveRouterModel()`, and routing-only types; `model: "auto"` routes internally with no separate SDK call.
- Removed `/workflow dependency`, the workflow tool's `dependency` action, and the `workflowDependency()` SDK export and its report/operation types. Use workflow status to inspect an affected run; Atomic handles managed PostgreSQL health and recovery internally.
- The model-facing workflow tool no longer has a route action or a routing classifier step: the agent decides whether a workflow fits and runs a registered workflow by name. The router model setting now applies only to automatic workflow-stage and subagent model selection.
- Removed the `timeoutMs` option from `StructuredOutputRequest` and `RouterDecisionRequest`, and the `DEFAULT_STRUCTURED_OUTPUT_TIMEOUT_MS` export. SDK integrations can pass an `AbortSignal` for cancellation or a caller-owned deadline.
- Removed the inherited `shouldStopAfterTurn` agent option (pi-agent-core 0.87.0). Use `finishTurn` and return `{ action: "end" }` instead. `finishTurn` runs before `turn_end` but applies the decision afterward, and it also receives error and aborted responses; migrate normal-response predicates by returning `undefined` for those hard exits. See the `@earendil-works/pi-agent-core` changelog for a complete before-and-after example.
- Added `ContextEditEntry` to the exported `SessionEntry` union. TypeScript consumers with exhaustive entry switches must handle `context_edit`; use `replacement: null` for omission and a content replacement otherwise.
- Made `SessionManager` canonical for `AgentSession` provider context. Assigning `session.agent.state.messages` no longer replaces future request history; restore with `SessionManager.inMemory(cwd, { id }, entries)`, navigate with `session.navigateTree()`, or append through `session.sessionManager` and call `session.refreshContext()`. A caller-supplied `prepareNextTurnWithContext` replacement context is still honored for the request it prepared.
- Expanded `TurnEndEvent` with required boundary fields (`messageEntryId`, `toolResultEntryIds`, `outcome`, `entries`, `continue`, `context`) and added `AgentBeforeSettleEvent` to the exported `ExtensionEvent` union. Consumers constructing events or exhaustively switching on `ExtensionEvent` must handle the new shapes. `ExtensionRunner.emit()` no longer accepts `turn_end`; host integrations dispatch actionable boundaries with `emitBoundary(baseEvent, buildContext)`.
- Deferred runs requested from `agent_settled` handlers until all settled handlers finish. Handlers still observe `ctx.isIdle() === true`, but no longer see a reentrant `agent_start` during the same notification dispatch.
- Every provider request now waits for the session's queued agent events (including `agent_start`, `turn_start`, `message_start`, `message_end`, and `tool_execution_*` extension handlers) before it is sent, because the request is built from the persisted canonical projection and `message_end` handlers may replace the message that gets persisted. Per-event listener dispatch stays nonblocking, but an extension handler that awaits a later part of the same run (for example a provider response) now stalls that run instead of merely running late; move such waits to `agent_end` or `agent_settled`.
- Provider implementations now receive transcript-only contexts. Read instructions and tools with `getCurrentSystemPrompt(context.messages)` and `getCurrentTools(context.messages)`. Extension prompt options are mutable; return a forced system prompt only for an unrecorded, complete per-run replacement.
- Intercom now respects tool allowlists and exclusions. `tools: []` and `noTools: "all"` expose no tools; `noTools: "all"` also overrides nonempty allowlists. Select `"intercom"` explicitly when using an allowlist. The builtin selection flags in `CreateAgentSessionOptions` can disable individual shipped packages and resources without reload restoring them ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Extension dialogs without human-input support now reject with `code: "HumanInputUnavailable"` instead of returning empty defaults. Check `ctx.hasHumanInput` for questions; `ctx.hasUI` remains a presentation capability. Pending dialogs are cancelled on adapter withdrawal, abort, reload and disposal ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Headless workflow launches now return an accepted run identity instead of waiting for completion. Inspect workflow status for results. Required durable gates remain pending even when no human host was initially bound; absent UI is no longer inferred as an execution restriction. Explicit runtime policies remain enforced ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- `AgentSession.dispose()` now returns a promise. Await it to cancel and drain owned work, including admitted prompt hooks and reload preparation, flush persistence and release builtin resources. Repeated calls share the same outcome; partial cleanup rejects with `code: "ShutdownFailed"` rather than reporting success. Runtime replacement uses the same shutdown and preserves host bindings. Suspended callbacks must settle before cleanup completes; retired preflight cannot start a provider or publish a reload candidate ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Terminal shutdown now seals compaction and user/custom queues and drains initial extension binding, steering/follow-up hooks and pending runtime replacement factories/startup before resolving. Independent sessions borrowing the same resource loader or session storage no longer share workflow/native-task shutdown ownership. Custom forwarding loaders, subclasses and copied/edited extension registrations retain caller policy and session-local API bindings. Failed factories unwind owned acquisitions; failed outgoing retirement still finalizes retained workflow resources before rejecting ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Factory rollback now covers pre-constructor setup and preserves all cleanup causes. Concurrent runtime replacements retire displaced successors and preserve surviving workflow ownership after candidate failure. Disposal cancels all shell calls with repeated caller IDs and reports real, session-attributed settings write failures without consuming shared manager errors ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Closing preserves queued conversation events and persistence, drains current and superseded background summaries, and rolls back candidate acquisitions when reload discovery fails before startup. Reload cleanup failures retain every original and cleanup cause ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Model-tool workflow launches now use `workflow route` with actual request/message/document text, then `workflow run` with the registered execution ID and inputs. Reservations retain one identity through admission and supported lifecycle operations; invalid inputs can be corrected without rerouting. Old run-as-router calls and duration values are rejected. Estimates use canonical quarter-hour labels through `1d`, plus `>1d`; budgets and approval gates remain unchanged ([#3106](https://github.com/bastani-inc/atomic/issues/3106)).
- Renamed the TypeSafe Jev environment variable from `TYPESAFE_AI_API_KEY` to `TYPESAFE_API_KEY`. Update shell and deployment configuration; the old name is no longer recognized. Saved `/login typesafe-ai` credentials are unchanged.
- Renamed the direct Jev decision model from `typesafe-ai/jev` to `typesafe-ai/jev-latest`. Update `routerModel` settings and explicit SDK selections; saved TypeSafe credentials are unchanged.
- Workflow duration estimates no longer accept the string value "unknown"; routers give their best estimate using the existing quarter-hour buckets or `>1d`.

### Added

- Added the `provider_stream_event` extension event for observing each parsed provider stream event before normalization, including provider-specific fields that assistant messages drop, plus an opt-in `/debug-provider` example viewer ([#9784](https://github.com/earendil-works/pi/issues/9784)).
- Added image generation and classification to the model runtime with provider authentication, including built-in OpenRouter image models and TypeSafe `jev-latest`. Extensions can register chat, image, and classifier models together, with operation implementations keyed by API; a declared model list replaces all operations for that provider.
- Remote model-catalog refresh now requests chat, image, and classifier entries, while preserving chat-only responses from older catalog servers.
- Added fast variants for first-party xAI Grok models, such as `xai/grok-4.7-fast`. They send the base Grok model with xAI Priority Processing (`service_tier: priority`) for lower latency, billed at twice the standard token rates only when xAI confirms the priority tier. `usesXaiFastServiceTier` reports which models qualify.
- Added inherited Claude Opus 5.5, GPT-6 Sol, and GPT-6 Luna support for GitHub Copilot.
- Added inherited GPT-6 Sol and GPT-6 Luna support for OpenAI API keys and OpenAI Codex subscriptions, including the `openai/gpt-6-sol-fast`, `openai/gpt-6-luna-fast`, `openai-codex/gpt-6-sol-fast`, and `openai-codex/gpt-6-luna-fast` Fast-mode choices.
- Recorded Artificial Analysis Intelligence Index and Cognition FrontierCode 1.1 results for GPT-6 Sol and GPT-6 Luna in the model evals used by automatic routing.
- Added Vercel AI Gateway and OpenCode Zen as Jev structured-decision providers (`vercel-ai-gateway/typesafe-ai/jev`, `opencode/jev-1.13`, `opencode/jev-1.13-free`) for the router model setting and SDK decisions, reusing each gateway's existing login or its AI_GATEWAY_API_KEY / OPENCODE_API_KEY environment key. Gateway model IDs, limits, and prices come from the models.dev decision catalog, and `getStructuredOutputProviders()` entries now carry the published context window and price.
- Added inherited Claude Opus 5.5 support for Anthropic with adaptive thinking and a 1M context window.
- Added Anthropic fast mode as selectable fast models: `anthropic/claude-opus-5-5-fast`, `anthropic/claude-opus-5-fast`, and `anthropic/claude-opus-4-8-fast`. Each sends the base model with Anthropic's `speed: "fast"` request field and fast-mode beta, for up to 2.5x faster output at twice the standard token rates, and works with both API keys and Claude subscription logins.
- Subagents can now run background `bash` and `powershell` commands instead of being refused with "Background bash observation requires a supported task owner". These shells appear in the parent's `/tasks` (the main chat's, or the current workflow stage's) and keep running after the subagent finishes. The subagent can wait on and `kill` only the shells it launched. Completion notices go to the subagent while it runs, then to the parent. Subagents still cannot launch other subagents.
- Added Meta (Muse subscription) login via `/login meta` with automatic Model API key refresh, plus `META_API_KEY` support.
- Added append-only model-context edits. For example, `sessionManager.appendContextEdit(entryId, null)` omits one message from future provider context without changing raw history, usage, or UI history. See [ContextEditEntry](docs/session-format.md#contexteditentry).
- Added actionable `turn_end` and `agent_before_settle` extension boundaries. Return `{ entries: [...event.entries, draft], continue: true }` to persist structural entries (`custom`, `custom_message`, `context_edit`, or a verbatim `compaction` handoff) in order and ensure one next provider request without changing steering or follow-up scheduling. See [extension events](docs/extensions/events.md#turn_start-/-turn_end).
- Added the `context_with_system` extension event, which runs after `context` handlers on the full transcript including system messages and sends its result verbatim. See [`context_with_system`](docs/extensions/events.md#context_with_system).
- Added per-model image resize profiles through `inputLimits.images.resize` in `models.json` (deep-merged by `modelOverrides`), applied to `@file` attachments, `read` tool images, and tool-result images. Attachments are resized once the run's model is known, so a `before_agent_start` model switch selects the profile. See [Image Input Limits](docs/models/reference.md#image-input-limits) ([#9631](https://github.com/earendil-works/pi/issues/9631)).
- Added crash diagnostics that identify loaded extensions whose source files appear in an uncaught-exception or fatal-error stack trace, with a hint to disable them with `atomic config` or confirm with `atomic -ne`.
- The SDK exports `spawnProcess` next to `createChildProcessEnvironment`: the child-process spawner that stamps Atomic's agent attribution into the environment and, on Windows, runs `.cmd`/`.bat` launchers through `cross-spawn` with correct argument quoting. The bundled `impeccable` skill's engine launcher and the `open-claude-design` live loop use it.
- System instructions and tool changes are recorded as chronological transcript messages, with structured prompt patches and replay across resume, branches, and verbatim compaction. Supported providers preserve the cached prefix for mid-conversation updates.
- Optional prompt-cache warming (`streaming`, `idle`, or `off`) refreshes replayable caches before TTL expiry, with owner/session cancellation and a `/settings` control. Default mode is streaming; prompt-cache retention still defaults to long where the model supports it.
- Added workflow dependency status, doctor and safe managed-cluster recovery through `/workflow dependency`, the workflow tool and `workflowDependency()`, reporting verified identity, actual port, runtime versions, consumers, retained latest failure and recovery guidance without resetting data ([#3074](https://github.com/bastani-inc/atomic/issues/3074)).
- Session, Workflow, and Intercom UUID selectors accept unique 8-character hexadecimal prefixes while preserving exact custom IDs and visibility boundaries ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).
- Attached stage chats show `[stage: name]` on the composer top rule, a mounted question's top rule, and awaiting-input prompt borders ([#2886](https://github.com/bastani-inc/atomic/issues/2886), [#3013](https://github.com/bastani-inc/atomic/pull/3013) by [@sumitvairagar](https://github.com/sumitvairagar)).
- Added bounded structured-decision SDK APIs and direct TypeSafe Jev Choice integration. The shared `routerModel` setting selects prerequisite routing inference; general `inferStructuredOutput()` calls select their inference model explicitly and remain one-shot. Decisions validate exact schema values without agent loops. The selected chat model and `structured_output` tool are unchanged ([#3089](https://github.com/bastani-inc/atomic/issues/3089), [#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- Model-tool workflow launches now require task/conversation/documentation state and one bounded routing decision before admission. Tool results show the validated workflow and exact budget as formatted JSON; `none` means continue inline without launching, and stale registry decisions are refused. Routing context containing known configured credentials is rejected before either provider receives it. User `/workflow` commands and authored `ctx.workflow(...)` calls bypass routing ([#3089](https://github.com/bastani-inc/atomic/issues/3089)).
- Added opt-in subagent `model: "auto"` routing for single tasks, parallel tasks and agent defaults, using available model/effort pairs and shipped evaluation guidance. The shared `routerModel` setting selects the decision provider; invalid or stale decisions prevent child launch, and selected versus fallback execution models remain distinct in task metadata ([#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- TypeSafe Jev supports API-key login and logout through the normal credential store, with environment-key fallback. Saved Jev credentials can select the default router while Jev remains absent from chat-model selection.
- Added a searchable Router model entry in `/settings`, saving automatic routing, Jev, or an available provider/model to `settings.json` without changing the chat model.
- Added workflow-stage `model: "auto"` selection from actual prompts and evaluation guidance, with constrained model/effort pairs and separate selected versus execution metadata.
- SDK hosts can provide typed `HostInput` callbacks for confirmations, selection, text and questionnaires without a terminal. Replies are validated and carry session/request identity and cancellation; the onDiagnostic callback receives session-attributed operational messages ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- SDK human-input callbacks now answer durable workflow approvals and stage questionnaires, including nested workflows, with workflow/run identity. Withdrawing or replacing a host keeps unresolved approvals pending and rejects stale replies; the same definition can resume under a different host without repeating checkpointed tool effects ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Added `openrouter/~typesafe/jev-latest` for routing and SDK structured decisions through OpenRouter's Decisions API, reusing existing OpenRouter sign-in, saved API keys, and environment fallback without a TypeSafe key.
- Added `compat.allowedFallbackModels` configuration for Anthropic-compatible models, including complete target pricing and `[]` to disable inherited fallbacks.

### Changed

- Agents and workflow stages now try agent-browser first wherever it can reach the target, because its text snapshots use far fewer tokens than screenshot-driven computer use. That now includes Mobile Safari in the iOS Simulator (`agent-browser -p ios`) and apps that expose a Chrome DevTools Protocol port. Cua Driver handles everything else: native desktop apps, native iOS apps in the Simulator, Android emulators, OS dialogs, and desktop Safari or other non-Chromium browsers. The [Computer use](docs/computer-use.md#what-agent-browser-covers) guide has a new table showing what agent-browser covers.
- The agent no longer treats missing, sparse, or mostly inline workflow history as a reason to run every task inline. It judges from the task itself whether a workflow would help, then refines that choice with comparable execution history and your stated or recorded preferences.
- When a shell tool is available, the default system prompt now asks the agent to record how each task ran in the commits, PRs, issues, and comments it writes: an `Assistant-workflow` trailer naming the workflow and run (or `Assistant-workflow: inline` when no workflow ran), an `Assistant-duration` trailer with measured time to converge against the estimate, and a `User-preference` trailer for durable preferences you express, tied to you by a `Co-authored-by` trailer. Before choosing between a workflow and inline work, estimating duration, or resolving ambiguity, the agent mines these records for comparable tasks with your version control system and its hosting CLI (for example `git log` and the GitHub CLI), and reports sample size, median, and range. Your own recorded preferences take priority; other contributors' preferences apply only as repository conventions when yours do not cover the situation. The history is used as a guide, not as the decision. Ask in your request, a context file, or `APPEND_SYSTEM.md` to turn off any of these records.
- The default system prompt now also asks the agent to record each verification it ran or attempted as an `Assistant-verification` line (for example unit or E2E tests, agent-browser, Cua Driver, Herdr terminal automation, or the qlty CLI, with `passed`, `failed`, or `unavailable`). Before choosing how to verify a change, the agent looks up these records for comparable changes and reuses the checks that worked, without replacing the project's required checks.
- `FILE_MUTATION_CONFLICT` errors from `edit` and `write` now use one labeled line each for the problem, the next step, the first divergence, the live file, and the requester identity, instead of a single wrapped paragraph. The next step names the file to read. A rejected snapshot tag now explains that shell output and tags quoted in errors do not count as reads, and the current tag is marked as for comparison only, so agents stop retrying with it.
- Cua Driver setup guidance now distinguishes installing the executable from installing an agent skill. Atomic already bundles the skill, so it directs agents to skip upstream's optional skill-install commands and leave existing user-level copies alone.
- Decision selection rejects image-generation models; execution `auto` remains restricted to chat language models, including those with multimodal input.
- `/workflow resume` picker rows now display the full workflow ID first, with the workflow name in the adjacent description.
- Tool calls in one assistant message are now ordered by each tool's new `concurrency` setting (`"shared"` by default, or `"exclusive"`). Built-in `edit`, `write`, `todo`, and `ask_user_question` run exclusively, and `bash` does when it uses `pty: true`, so a later `read` in the same message sees an earlier `edit` or `write`, rather than possibly reading the file first. Extension tools can declare `concurrency` as a mode or a per-call function of their arguments.
- `/tasks` now orders each Agents and Shells section by status, with running tasks first (those needing attention at the top), then stopping, queued, failed, cancelled, and completed tasks, newest first within each status. The selected task stays selected when its status changes and it moves ([#3252](https://github.com/bastani-inc/atomic/issues/3252)).
- Structured-decision requests (router and generic) now retry transient provider failures — connection errors and HTTP 408, 429 and 5xx — up to three times with exponential backoff, for both Jev and chat decision models, matching ordinary chat-provider retry defaults. Cancellation is never retried ([#3206](https://github.com/bastani-inc/atomic/issues/3206)).
- Any Jev routing failure now falls back to the current chat model when one exists, including explicitly pinned Jev router selections, missing or unresolvable Jev credentials, context-budget rejections, and oversized response bodies. Cancellation still never falls back ([#3206](https://github.com/bastani-inc/atomic/issues/3206)).
- Jev structured-decision responses are now accepted whenever every question names a known Choice option; reported model, usage, probabilities, confidence and answer types are advisory instead of grounds for rejection ([#3206](https://github.com/bastani-inc/atomic/issues/3206)).
- Recorded Artificial Analysis Intelligence Index and Cognition FrontierCode 1.1 results for Claude Opus 5.5 in the evals snapshot used by automatic model routing.
- Updated the inherited Pi runtime dependencies (`pi-agent-core`, `pi-client`, `pi-protocol`, `pi-tui`, `pi-telemetry`, and transitive `chord`) to 0.87.1.
- The Windows installer extracts the release archive with the native .NET `ZipFile` API instead of `Expand-Archive`, which processed the archive's ~19,000 entries at script speed under Windows PowerShell 5.1. Extraction is now several times faster; `Expand-Archive` remains only as a fallback when the compression assembly cannot load.
- Desktop, simulator, and emulator computer use now routes to [Cua Driver](https://github.com/trycua/cua/tree/main/libs/cua-driver) instead of PyAutoGUI. The default system prompt, the Goal/Ralph end-to-end verification guidance, and the workflow-authoring guidance state one face rule: the `cua-driver` CLI through the bundled `cua-driver` skill when a model chooses each action, and the `@trycua/cua-driver` TypeScript SDK inside `ctx.tool` when workflow code owns the scenario. Guidance installs a missing `cua-driver` executable with upstream's one-line installer in one bounded attempt, runs every invocation with `CUA_DRIVER_RS_TELEMETRY_ENABLED=false` plus `cua-driver telemetry disable` after an install, reports missing macOS grants or a non-interactive session as `blocked`/`needs_human` with exact remediation, and never runs `cua-driver skills install`. The Computer use, Verification, Workflow authoring, and Skills guides document installation, readiness, telemetry and update-check switches, a `cua-driver call` example, and a runnable `ctx.tool` verification example ([#3181](https://github.com/bastani-inc/atomic/issues/3181)).
- A custom message sent with `triggerTurn: false` during an active run is still appended after the current turn's tool results rather than steered into it, and, because provider requests now use the canonical session projection, it is visible to that run's next request instead of only a later turn.
- Changed the default xAI model to Grok 4.7.
- Replaced the routing evals snapshot with one Artificial Analysis table of the top 26 catalog models by Intelligence Index, accessed 2026-09-21, with each published benchmark as a column. Grok 4.7 is a row in that table. Auto routing sends that snapshot in full, with the model-selection guide, inside Jev's 32k-token state-plus-longest-question limit.
- Automatic subagent and workflow-stage model selection now sends a fixed `model_selection_guide` state field next to `evals`: the "Benchmarks are evidence, not policy" and "Role-based thinking effort" sections from [Model Selection](/models/model-selection), so the router applies the role defaults and effort rules rather than only the dated benchmark tables. To keep every Jev request within its input budget, the model-selection task excerpt cap is now 10,000 JSON-encoded UTF-8 bytes (was 12,000); execution tasks are still sent in full.
- The `model_selection_guide` sent with automatic model routing, and the [Model Selection](/models/model-selection) guide it mirrors, now pair each stage role with a model cost tier as well as a thinking level: cheap, fast models for codebase exploration and routine implementation, frontier models for code review, verification, and final approval, and mid-priced models for orchestration and reporting. The router is told to match the role tier first, so high-volume tool-driven work stops defaulting to the most expensive candidate. The larger guide is paid for by lowering the model-selection task excerpt cap to 9,000 JSON-encoded UTF-8 bytes (was 10,000); execution tasks are still sent in full.
- The default system prompt's Guidelines section now tells the agent to write self-describing code and not add comments unless the user explicitly asks for them or the task instructions call for them; code that needs a comment to be understood is treated as a smell to restructure with clearer names, smaller units, or explicit types.
- The default system prompt and the computer-use, skills, first-session, and verification guides now direct browser automation to the `agent-browser` skill and CLI ([vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser) v0.38.1) instead of the removed `playwright-cli` skill; install with `npm i -g agent-browser && agent-browser install`.
- The bundled `impeccable` skill is upstream `skill-v4.3.1` with its self-contained engine (0.1.5). Its commands run through `scripts/impeccable` (`impeccable.cmd` on Windows), which uses an engine binary beside the launcher or downloads and checksum-verifies the pinned release for your platform on first use; set `IMPECCABLE_BIN` to a preinstalled engine on machines without network access. Atomic's launcher keeps the engine's anonymous ping and `npx impeccable update` prompt off. See the [first session](/getting-started/first-session) guide.
- Install/update telemetry pings now go to the Atomic version-adoption endpoint instead of pi.dev. First-interactive-launch triggers, opt-outs, and the independent update check are unchanged ([#2498](https://github.com/bastani-inc/atomic/issues/2498)).
- Pi runtime dependencies (`pi-agent-core`, `pi-client`, `pi-protocol`, `pi-tui`, `pi-telemetry`, and transitive `chord`) are pinned to 0.86.1. Workspace package versions remain `0.0.0`.
- Automatic subagent and workflow-stage model selection no longer includes system prompts as agent metadata, reducing routing input. Execution prompts are unchanged; self-contained subagents still use their system prompt as the task when no task is supplied.
- SDK session creation now includes shipped Atomic builtin extensions and resources, shares CLI defaults, and completes extension startup before returning. Supply startup host bindings through the extensionBindings option; rebinding no longer repeats startup. Failed startup rolls back the partial session, and missing shipped packages report `code: "BuiltinUnavailable"` ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Main chat, workflow stages, and subagents now default to long prompt-cache retention where supported. Unset retention uses ordinary caching for OpenAI models without known extended-retention support, such as GPT-4o, and five-minute caching for older Bedrock Claude models, such as Claude 3.7. Explicit retention choices remain unchanged; use `PI_CACHE_RETENTION=short` to opt back into shorter caching. Anthropic one-hour cache writes cost more than five-minute writes, so savings depend on reuse.
- All bundled workflows and subagents now default to `model: "auto"`, selecting task-specific models and efforts instead of fixed fallback chains. Explicit model/effort selections remain supported; main-chat defaults and user-authored resources are unchanged.
- Automatic model selection now ranks up to three distinct eligible models with individual reasoning efforts. Subagents and workflow stages try them in order before remaining configured fallbacks; workflow resume preserves the ranking.
- Automatic subagent and workflow-stage model selection now uses bounded excerpts of long tasks, preserving their beginning, end and protected `<keepContext>` spans. Execution prompts and hard model constraints remain unchanged; oversized protected content retains the existing router fallback behavior.
- Automatic subagent and workflow-stage model selection now sends the factual `evals.md` benchmark records as the router's evals state field instead of sending the general model-selection guide. The guide stays human-readable and links to the dated eval records, which use markdown tables.
- Exact `--session-id` lookup and fork-target collision checks read session headers instead of scanning transcripts, while keeping internal workflow sessions hidden.

### Fixed

- Sessions no longer break when a model reuses a tool call id in a later turn or returns an empty one, as Kimi models do through NVIDIA NIM and other providers that pass Kimi's per-response ids (`bash:0`) through unchanged. Every request after the repeated call used to fail with "tool call id bash:0 appears in more than one assistant message". Sessions already in that state continue on the next message ([#3243](https://github.com/bastani-inc/atomic/issues/3243))
- The `curl | sh` and `irm | iex` installers can install older releases again. They no longer run `--internal-validate-postgres-runtime` or the bundled PostgreSQL `--version` checks before promotion. Installing a release built before that option existed, or one without a bundled PostgreSQL runtime, could fail before promotion. The installers still verify the SHA256 checksum and run the staged `atomic --version`.
- Durable workflows work again on Ubuntu and Debian with the default umask 002. Atomic had refused its own group-writable `~/.atomic` as an ancestor of the PostgreSQL runtime cache and ran workflows in memory, so runs couldn't be resumed after closing Atomic. A group-writable folder is now accepted when its group is your private group: no other account in `/etc/passwd` uses it as its primary group, and `/etc/group` lists no member other than you. World-writable folders and shared groups are still rejected.
- Workflow stage model fallback now runs the next model after a candidate fails. Previously, retiring the failed candidate paused the replacement session's message queue, so every later fallback, including the current chat model, failed immediately with "Model turn ended without an assistant message after the prompt (empty completion)", and messages sent from the attached stage chat stayed queued.
- Prompt cache-miss notices now attribute a miss to TTL expiry only when the idle time exceeds the cache lifetime of the model and retention that served the previous request, instead of a fixed 5 minutes. With 1-hour retention, a miss after 6–59 minutes is no longer mislabeled ([#3259](https://github.com/bastani-inc/atomic/issues/3259)).
- Fixed workflow stages with `model: "auto"` (and other stage sessions) crashing with `Cannot read properties of undefined (reading 'baseDir')` when Atomic was launched with `--theme <path>` or other CLI-provided resource paths ([#3229](https://github.com/bastani-inc/atomic/issues/3229)).
- Managed git packages no longer install Atomic's host-provided peer dependencies (`@bastani/atomic`, `@bastani/pi-ai`, `@earendil-works/pi-agent-core`, `@earendil-works/pi-tui`, and TypeBox) when their dependencies are installed with npm, pnpm, or Bun, and package-manager wrappers in the npm command setting, such as `corepack pnpm`, are now recognized. Extension packages that list one of those packages under dependencies instead of peer dependencies now get an extension warning, because an installed copy can load a duplicate runtime ([#9863](https://github.com/earendil-works/pi/issues/9863)).
- Atomic no longer warns at startup that its bundled workflows, subagents, MCP, and intercom extensions list `typebox` under dependencies. They now declare it as a `*` peer dependency, so they use the host's TypeBox.
- Fixed an assistant reply occasionally disappearing from the live chat when a workflow completion notice arrived just as the reply finished streaming. The reply was still saved to the session, but the conversation shown and sent to the model could omit it until the session was reloaded.
- Fixed a model switch made with Ctrl+P or `/model` just as Atomic finished loading startup resources sometimes being undone on screen: the footer went back to the previous model even though the new model was already in use.
- Fixed X11 clipboard text being pasted as an image when the clipboard owner accepts image targets it does not advertise. Atomic now reads only an advertised image type ([#9786](https://github.com/earendil-works/pi/issues/9786)).
- Managed PostgreSQL now runs from a retained runtime separate from workflow data and project checkouts. Removing a source worktree no longer breaks its server; reinstalling a complete healthy package lets automatic recovery replace a damaged runtime, including the same version, even when new database connections are impossible or the server has already exited. A running server's process identity must verify before shutdown; identity mismatches or corruption still require investigation, and Atomic preserves the database.
- Fixed `structured_output` and workflow stages with a result schema failing on Claude models with `Structured output failed across N model candidate(s)` when the schema bounds a number or string length, such as the builtin `classify-and-act` classifier's `confidence` between 0 and 1.
- Subagent completion notices and `/tasks` details now take the result excerpt and prompt from the child's own messages. A forked child no longer shows parent text it inherited as its result ([#3251](https://github.com/bastani-inc/atomic/issues/3251)).
- Fixed `model: "auto"` subagents and workflow stages always falling back to the current chat model with `Subagent auto routing failed` when a Claude model made the routing decision. Anthropic rejected the routing schema's reasoning-effort field, so no automatic selection ever ran on Claude.
- The bundled `bash` and `powershell` tools work again with OpenAI-compatible gateways that enforce structured-outputs schema rules, including models configured with `compat.supportsStrictMode: false`. Their tool schemas no longer declare top-level JSON Schema combinators. Inputs that mix a command with a task wait are still rejected before execution ([#3220](https://github.com/bastani-inc/atomic/issues/3220)).
- Fixed interactive sessions stalling on Windows while agent `bash` and `powershell` commands ran. Each running command polled `tasklist.exe` synchronously 20 times a second, blocking the session for roughly 45 ms per poll; the process-exit check now asks the OS directly without starting a process. With several workflow stages running commands at once, the engine could previously freeze for seconds at a time.
- Fixed a Windows stall when workflow stages or new sessions start: PowerShell availability was looked up with a synchronous `where` process up to three times per session and on every PowerShell tool call. Lookups are now remembered for the current `PATH`.
- Reduced interactive CPU use while the workflow graph or another full-screen overlay is open: covered rows are no longer re-scanned behind the overlay on every frame, and the settled startup banner is no longer rewrapped on every frame.
- Skill catalogs resolve each skill path once per build instead of several times, cutting filesystem work when workflow stages start on Windows.
- Fixed an API key saved with `/login` in an interactive session not reaching the running engine, so workflow routing and other engine-side work kept reporting `requires an API key` until Atomic was restarted. API-key logins are now persisted by the engine, like OAuth logins and `/logout`, and both sides report the provider consistently ([#3193](https://github.com/bastani-inc/atomic/issues/3193)).
- Fixed the RPC extension UI example to disable extensions with the supported `--no-extensions` flag and safely frame split UTF-8 output.
- The Windows installer now retries removing the previous version backup with a bounded backoff when a file inside it is briefly locked (for example by Defender or the search indexer), instead of leaving a `.backup-*` directory behind in the install root.
- Fixed a subagent launched from a workflow stage taking over the stage's task owner when it inspected its tasks, which could send the stage's background task completions to that subagent.
- Fixed string context-edit replacements producing invalid assistant and tool-result message content instead of text blocks.
- Fixed edited-context accounting both discarding valid assistant usage captured after the latest context edit and reusing that usage after a later compaction made it stale.
- Fixed selected error retries, model fallbacks, and final length/overflow recovery retaining abandoned model attempts in future provider context; post-run recovery omissions are now persisted as `context_edit` entries without hiding raw transcript history or changing queue scheduling.
- Fixed `context` handlers that filter or slice messages dropping the prompt and tool declarations, which after extension-driven compaction left requests without built-in tools or made Codex emit raw tool-call text. Handlers no longer see system messages; Atomic restores the prompt and tool state after they run. See [`context`](docs/extensions/events.md#context) ([#9789](https://github.com/earendil-works/pi/issues/9789), [#9822](https://github.com/earendil-works/pi/issues/9822)).
- Fixed text files beginning with `GIF` being misclassified as images and omitted from `read` and CLI `@file` input ([#9755](https://github.com/earendil-works/pi/issues/9755)).
- Fixed malformed prompt template frontmatter being silently ignored instead of reported as a resource warning naming the file and parse position ([#9830](https://github.com/earendil-works/pi/pull/9830) by [@christianklotz](https://github.com/christianklotz)).
- Fixed inherited unknown OpenAI-compatible Chat Completions endpoints receiving strict tool schemas unless they explicitly advertise support ([#9816](https://github.com/earendil-works/pi/issues/9816)).
- Structured decisions and automatic model selection no longer fail at a fixed 30-second deadline, including during model ranking, output repairs and Jev-to-chat fallback. Explicit cancellation and independent provider and tool-request limits still apply.
- Workflow reviewers and other stages no longer fail extension startup with a misleading `Live workflow-stage route is owned by another active session` when their workflow owner is re-registering with the Intercom broker, when the previous attempt's session is still being torn down, or when a later review round reuses a stage name whose earlier occurrence is still connected. Refusals now name the actual condition, transient ones recover through the bounded stage warm-up retry, and genuine duplicate owners are still refused. See [Intercom operations](/intercom/operations#workflow-stage-route-refusals) ([#3163](https://github.com/bastani-inc/atomic/issues/3163)).
- Fixed missing or invalid `--mode` values being silently ignored instead of reporting an error and exiting with a nonzero status ([#9045](https://github.com/earendil-works/pi/issues/9045)).
- Workflow stage chats and other transcript views built from session messages no longer print a bare dim "system" line for the session's system-prompt message.
- Auto model routing no longer prints `Text was truncated to fit the input budget. Continuing with the shortened text.` to the console when it shortens a long task for its own routing request; the execution prompt was never affected.
- Automatic compaction cancelled from a `compaction_start` listener now aborts the auto controller before `session_before_compact` or planner auth.
- Anthropic thinking-drop notices are short, omit path details, and stay silent when the cumulative drop count is unchanged.
- Isolated-engine prompts wait for the remote prompt RPC to settle, and stale kitty image conversions no longer replace a later tool result.
- Fixed idle prompt-cache warming rebuilding expired caches when its timer or an extension decision is delayed.
- Fixed clipboard copy failing in containers and WSL without WSLg by restoring the OSC 52 fallback when no display is available, and added a verified Windows clipboard backend for WSL ([earendil-works/pi#9688](https://github.com/earendil-works/pi/issues/9688)).
- Clipboard copy from an SSH or Mosh session into WSL now emits only OSC 52 for the connected client instead of also writing the remote host's Windows clipboard.
- Fixed inherited z.ai `Prompt too long` errors not being recognized as context overflow ([earendil-works/pi#9805](https://github.com/earendil-works/pi/issues/9805)).
- Fixed inherited Cerebras models advertising unsupported strict tool schemas, which caused HTTP 400 errors when strict and non-strict tools were mixed ([earendil-works/pi#9804](https://github.com/earendil-works/pi/pull/9804) by [@EdenGottlieb](https://github.com/EdenGottlieb)).
- Skill invocation summaries expand and collapse on left-click, matching branch summaries and compaction boundaries.
- Fireworks deferred-tool docs now describe chronological system-message tool additions and removals instead of the removed added-tool-names field from tool results.
- Reload now releases unpublished preparation acquisitions, drains failed candidate callbacks before shutdown, and retains self-reload command continuations through final disposal, including cleanup failures ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Reload now retains retiring-generation shutdown and invalidation when postcommit runtime reconstruction fails, preserving both setup and cleanup causes and keeping the candidate owned for disposal ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Custom messages received during tool execution now enter conversation history after the tool results and before the next assistant response, including when extension callbacks finish asynchronously ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Entry renderer discovery preserves the registered function identity while transcript rendering uses the owning session and rejects retired runners ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Session disposal now reports event-listener failures occurring during shutdown through its aggregate rejection while still flushing consumed workflow reconciliation and attempting remaining cleanup ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Stage-scoped durable workflow resume now refuses before dispatch instead of resuming the whole root, including restored local shadows of paused durable runs, and prefix resume no longer treats nested children as root candidates ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).
- Explicit Intercom reply names and full session IDs keep their original identity through broker collision checks, unique UUID prefixes still canonicalize to the stored session ID, and hidden same-name collisions refuse regardless of letter case ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).
- Canonicalized Intercom UUID-prefix sends revalidate the original selector against the broker's current authorized sessions, so a newly visible same-prefix UUID, exact name, or custom ID refuses instead of delivering to the previously unique identity ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).
- Prefix ambiguity refusals no longer include unauthorized or hidden session names when a canonicalized UUID-prefix send is rejected ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).
- Running Atomic inside a Herdr pane no longer leaves the pane unreported or labelled as another agent when Herdr's installed Pi integration is present. Herdr installs `herdr-agent-state.ts` into the legacy `~/.pi/agent/extensions` directory that Atomic also loads from, and Atomic used to stand its built-in reporter down as soon as that file loaded — but the installed asset reports itself as `pi`, so the pane ended up mislabelled or never updated at all. Inside a Herdr pane Atomic now skips that installed integration when loading extensions and reports the pane itself; outside a Herdr pane the file loads exactly as before. Nothing to configure, and the extra extension no longer needs to be disabled by hand ([#2416](https://github.com/bastani-inc/atomic/pull/2416) by [@makgunay](https://github.com/makgunay))
- Request authentication now fails within 15 seconds of one credential-preparation attempt when refresh or derivation does not settle, including a fallback whose OAuth login has expired. Agent sessions reuse that request's resolved auth for transport setup instead of starting a second deadline. Timeout errors tell you to check the provider's credential source rather than to log in. Operator cancellation still aborts without fallback or an auth-block notice, and the bound does not apply to human-input waits, tool execution, or an already-opened model stream ([#3085](https://github.com/bastani-inc/atomic/issues/3085), [#3087](https://github.com/bastani-inc/atomic/pull/3087)).
- `/reload` now remounts a still-live extension panel after the previous generation is cleared, instead of leaving the panel gone. Replacing one panel with another still does not count as a clear ([#2529](https://github.com/bastani-inc/atomic/issues/2529)).
- MCP server URLs now support `${VAR}` and `$env:VAR` environment interpolation, including deployment-specific origins in shared configuration. Empty, invalid, or unsupported resolved endpoints report a configuration error before connecting. Diagnostics redact query-key credentials, while manual OAuth instructions show the complete authorization URL, including all paths, parameters, and any credentials ([#3088](https://github.com/bastani-inc/atomic/issues/3088)).
- After DBOS initialization, workflow root database registration now has a 10-second deadline instead of waiting for the request timeout. Unavailable admission skips database cleanup, preserving the failure diagnostic and run identity even when PostgreSQL stops answering. Cancelled admission cannot start workflow code later, and database loss invalidates admission readiness. First-time provisioning and initialization are outside this bound ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Malformed saved workflow topology is non-resumable. Rejection during root admission returns a failed run result without executing workflow code or hiding database write failures ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Timed-out or cancelled workflow resume admission no longer publishes stale ownership metadata when its database operation finishes late ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Database-rejected workflow admission preserves PostgreSQL authentication and permission diagnostics and removes a never-persisted local run without reporting it as still running ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Resume continuations skip cleanup when database admission is unavailable, leaving the source resumable instead of waiting on the failed database ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Cancellation during admission now retires a possibly committed database record without waiting on the abandoned admission write, including a checkpointed continuation whose resume has not finished. Failed cancellation persistence retains the local cancelled outcome and warns that database state is unknown ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Graceful quit during startup admission acknowledges the local stop immediately without waiting for registration. Registration settlement retains its admission deadline, including when a local kill arrives while registration finishes. Settlement failures appear in run status while preserving the local pause and resumability for continuations with saved durable progress. Runs without usable durable progress remain nonresumable ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Pausing during startup acknowledges immediately and retains the live executor without waiting for database admission. Later admission failures retain their original diagnostic in run status, and resume releases the failed attempt instead of leaving it stuck behind a rejected pause ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Workflow status distinguishes pending database admission from execution and reports dependency failures, phase age, and last progress. Live executor pause/resume reports local versus confirmed database persistence, stays responsive during outages, and preserves the paused owner for same-ID recovery ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Live executor resume allows up to 10 seconds for database confirmation instead of using the 500ms pause acknowledgement budget, avoiding false timeouts on healthy remote PostgreSQL connections ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Live executor pause acknowledges within 500ms while database confirmation continues for up to 10 seconds. Healthy slower connections no longer leave a false dependency failure; status updates to durable when confirmation succeeds ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Keep ready shared PostgreSQL running across Atomic client exit and reload, with installation-independent cluster ownership records and consumer leases ([#3074](https://github.com/bastani-inc/atomic/issues/3074)).
- Managed PostgreSQL verifies server identity and SQL readiness, supports `ATOMIC_POSTGRES_PORT`, and shares an available actual port across sessions without adopting foreign listeners or unregistered data ([#3074](https://github.com/bastani-inc/atomic/issues/3074)).
- Custom models declared in `models.json` no longer generate automatic `-fast` siblings, preventing invalid choices such as `gpt-6-astra-slow-fast`. Built-in fast variants and `modelOverrides` remain supported.
- Bedrock requests honor the configured provider retry count, including zero, so bounded router decisions do not make hidden SDK retry attempts ([#3089](https://github.com/bastani-inc/atomic/issues/3089), [#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- Jev structured decisions now handle choices above 255 options through bounded multi-request tournaments, including every candidate and comparing finalists without cross-batch probability ranking. Subagent auto routing supports large model/effort catalogs; workflow routing retains `none` and exact budgets. One shared deadline and fail-closed validation cover all rounds ([#3089](https://github.com/bastani-inc/atomic/issues/3089), [#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- Workflow routing preserves omitted budget fields with strict-schema providers instead of rejecting their required null placeholders. Explicit zero and fractional limits retain their exact meaning.
- Subagent auto routing uses the routed thinking level when checking unsuffixed fallbacks and cancels sibling routing requests when a parallel routing decision fails ([#3103](https://github.com/bastani-inc/atomic/pull/3103)).
- The Router model picker updates an existing project override instead of silently changing a global default that the project ignores, and displays the save scope ([#3103](https://github.com/bastani-inc/atomic/pull/3103)).
- Saved Jev credentials become available to automatic routing immediately after login, even during catalog refresh; stale refreshes no longer restore credentials after logout.
- Workflow and automatic model routing now repair malformed or schema-invalid answers up to three times after the initial attempt, including Jev, within one shared deadline. Authentication/provider failures, cancellation and stale decisions still stop without retries or launch.
- Workflow and subagent children retain the invoking SDK session's model/auth configuration, host callbacks and tool restrictions through fallback. Child requests and diagnostics keep their own session identity; disabled Intercom cannot grant supervisor access, and workflow children no longer share their parent stage's pending asks ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Workflow stage questionnaires respect explicit child human-input adapters and withdrawal without losing durable pending/rebind behavior. Stage fallback replacements enforce inherited model restrictions instead of treating fallback candidates as unrestricted primaries ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Explicitly rebinding a workflow child to its originally inherited adapter now keeps questions with that selected host after parent rebinding, including across child reload ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Child sessions honor a supplied session manager's working directory when the cwd option is omitted, and optional `undefined` child configuration no longer discards inherited models, settings or host callbacks ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Failed file-loaded extension factories now run registered rollback cleanup; cleanup failures reject instead of becoming discovery-only diagnostics. Reload seals retiring input before successor startup. Disposal drains asynchronous extension notifications, bus handlers, shortcuts and workflow observers before shutdown, including notifications from synchronous thinking/session-name setters ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Command-context session replacement no longer waits on its own command. It seals retiring admission and drains peers before handoff, while final disposal still drains the command continuation and cleanup. Closing preserves completed tool results and hooks, and nontransactional reload failures release owned acquisitions without closing borrowed discovery ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Concurrent command replacements no longer circularly drain one another. Reload activation and commit failures clean their started candidates, and unpublished MCP initialization cleanup failures remain explicit during disposal ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- SDK creation now cleans owned factory acquisitions omitted by extension selection without disabling selected extensions. Self-reload revokes captured old APIs before successor admission while retaining continuation cleanup and deferred failures ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Filtered creation and reload now retire omitted factories' APIs and subscriptions independently, preserving selected extensions. Captured extension actions refuse new work immediately during close/reload, while admitted subprocesses drain. Deferred shutdown after self-reload or command replacement can no longer mutate, acquire task hosts from, or send events into the successor ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Reload acquisition rollback no longer depends on a working extension getter. Shutdown and factory rollback now wait for tracked cleanup subprocesses after handlers return, while keeping fresh admission closed. Released event subscriptions and workflow publishers no longer retain completed release callbacks or their captured data after manual release or generation invalidation ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Factory rollback now refuses fresh work across all closing factories before awaiting cleanup and lets already-admitted callbacks finish before shutdown. Omitted factories drain independently of selected extensions sharing their runtime, preventing late resource leaks without blocking surviving extensions ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Workflow resource refreshes started through the extension API now finish before disposal, reload retirement or failed-factory cleanup, preventing late acquisitions from surviving successful shutdown ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Installed Node SDK consumers can now check the complete public declarations with strict NodeNext settings and `skipLibCheck: false`, including model catalogs and the find tool's path interface ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Prepared SDK services no longer reconstruct safe extensions or repeat their startup when project trust completes. Additional sessions borrowing those services keep independent extension lifetimes ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Jev structured decisions no longer reject responses solely because their probabilities do not sum to one.
- Jev SDK decisions retain their original model, provider and credentials across tournament rounds when callers mutate their model selection during inference.
- `/logout` now autocompletes providers with stored API-key or subscription credentials and opens a filtered selector when a provider is supplied, instead of sending the command as a prompt.
- Built-in Cloudflare AI Gateway, GitHub Copilot, and OpenCode routes now respect OpenAI model limits when choosing default cache retention, avoiding unsupported extended retention on models such as GPT-4o. Explicit retention overrides remain unchanged.
- Jev failures now report SDK error classes, recognized context-limit diagnostics and safe request IDs instead of generic provider-availability advice. Requests use the TypeSafe SDK without hidden retries or body logging.
- Automatic routing switches from Jev to the current chat model after an HTTP/connection failure or exhausted output repairs. By default each provider gets an initial attempt plus three corrective retries for invalid output, with a reported switch and one shared context, validation contract and deadline. Explicit router selections and general structured-output calls remain pinned.
- Reduced routing context with a concise model-selection guide and per-candidate contracts instead of repeated catalogs. Jev checks every request against conservative context budgets, splits verbose choices even below 255 options, and avoids sending oversized inputs.
- Extension imports of provider environment helpers now resolve correctly during workflow loading and reload instead of failing with `compat.js/utils/provider-env` module errors.
- Automatic subagent and workflow-stage model selection now receives the shipped model-selection guide directly, restoring benchmark guidance that the previous provider-identity filter excluded.
- Fixed compaction statistics mixing authoritative provider token counts with heuristic region estimates, which could produce incorrect/negative percentReduction. Statistics now use symmetric heuristic estimates (region + explicit tail) while preserving the authoritative tokensBefore for budgeting and display ([#2052](https://github.com/bastani-inc/atomic/issues/2052)).
- Standalone installations include PostgreSQL library aliases and reject unusable database runtimes before installing or upgrading. macOS and Linux packaging validates the transitive dependencies required for PostgreSQL startup and workflow database operation. A server left running by an older installation no longer hides an incomplete runtime. npm hydration preserves safe ordered alias chains ([#3073](https://github.com/bastani-inc/atomic/issues/3073)).
- Local clipboard failures now report platform-specific remedies instead of falling back to an unverified OSC 52 write. Fullscreen selection displays the diagnostic for five seconds; SSH and Mosh retain OSC 52 support.
- Exported missing extension hook event and result types from the public package entrypoint.
- Bundled integration documentation is discoverable under the same `docs/` root in npm and binary installations, including the MCP setup guide at `docs/mcp.md`.
- Workflow startup now reports its phase and age, and explicit cancellation releases waiting callers while retaining unfinished creation ownership. Cancelled queued reloads cannot start later; late SDK/binding results cannot attach or dispatch ([#3040](https://github.com/bastani-inc/atomic/issues/3040)).

### Removed

- Removed the Benchmark Sources and Pareto Efficiency documentation pages. Dated measurements and one-paragraph benchmark explainers live in Evals; general selection guidance stays in Model Selection.

## [0.9.20-alpha.12] - 2026-09-24

### Changed

- Agents and workflow stages now try agent-browser first wherever it can reach the target, because its text snapshots use far fewer tokens than screenshot-driven computer use. That now includes Mobile Safari in the iOS Simulator (`agent-browser -p ios`) and apps that expose a Chrome DevTools Protocol port. Cua Driver handles everything else: native desktop apps, native iOS apps in the Simulator, Android emulators, OS dialogs, and desktop Safari or other non-Chromium browsers. The [Computer use](docs/computer-use.md#what-agent-browser-covers) guide has a new table showing what agent-browser covers.

### Fixed

- Sessions no longer break when a model reuses a tool call id in a later turn or returns an empty one, as Kimi models do through NVIDIA NIM and other providers that pass Kimi's per-response ids (`bash:0`) through unchanged. Every request after the repeated call used to fail with "tool call id bash:0 appears in more than one assistant message". Sessions already in that state continue on the next message ([#3243](https://github.com/bastani-inc/atomic/issues/3243))

## [0.9.20-alpha.11] - 2026-09-24

### Changed

- The agent no longer treats missing, sparse, or mostly inline workflow history as a reason to run every task inline. It judges from the task itself whether a workflow would help, then refines that choice with comparable execution history and your stated or recorded preferences.

### Fixed

- The `curl | sh` and `irm | iex` installers can install older releases again. They no longer run `--internal-validate-postgres-runtime` or the bundled PostgreSQL `--version` checks before promotion. Installing a release built before that option existed, or one without a bundled PostgreSQL runtime, could fail before promotion. The installers still verify the SHA256 checksum and run the staged `atomic --version`.
- Durable workflows work again on Ubuntu and Debian with the default umask 002. Atomic had refused its own group-writable `~/.atomic` as an ancestor of the PostgreSQL runtime cache and ran workflows in memory, so runs couldn't be resumed after closing Atomic. A group-writable folder is now accepted when its group is your private group: no other account in `/etc/passwd` uses it as its primary group, and `/etc/group` lists no member other than you. World-writable folders and shared groups are still rejected.

## [0.9.20-alpha.10] - 2026-09-24

### Fixed

- Workflow stage model fallback now runs the next model after a candidate fails. Previously, retiring the failed candidate paused the replacement session's message queue, so every later fallback, including the current chat model, failed immediately with "Model turn ended without an assistant message after the prompt (empty completion)", and messages sent from the attached stage chat stayed queued.

## [0.9.20-alpha.9] - 2026-09-24

### Breaking Changes

- Direct TypeSafe decisions now use the canonical `typesafe/jev-latest` classifier model and `typesafe` provider authentication. Replace `typesafe-ai/jev-latest` or `typesafe-ai/jev` in router settings and SDK requests, and re-save keys stored under `typesafe-ai` with `/login typesafe`; old direct IDs, `/login typesafe-ai`, and automatic migration of those saved keys are not supported. `TYPESAFE_API_KEY` and Atomic's `-fast` model behavior are unchanged.
- Removed `getStructuredOutputProviders()`, `JEV_STRUCTURED_OUTPUT_PROVIDER`, and `JevStructuredOutputProvider`. Classifier selection uses only models registered in the current model registry. Previously bundled gateway IDs such as `vercel-ai-gateway/typesafe-ai/jev` and `opencode/jev-1.13` no longer resolve by default. Replace them with a registered classifier such as `typesafe/jev-latest`, `openrouter/~typesafe/jev-latest`, or `cloudflare-workers-ai/typesafe/jev`, a chat model, or explicitly register the classifier and its operation under that ID.
- `structured_output` now infers a schema-validated result from `instructions` and nonempty named `state` instead of treating its arguments as the result. The tool and `generateStructuredOutput()` accept optional exact `model` and ordered `fallbackModels` IDs resolved through the current model registry. A classifier ID uses the generic classify operation. Omit `model` to use the current stage or chat model, which is also the terminal fallback. A classifier runs only for a compatible finite Choice schema and is otherwise skipped. Runtime failures, including provider refusals, advance the fallback chain; cancellation does not. Ordinary text without a result gets up to three repairs before advancing, without checking for refusal wording. General requests derive choice questions from the schema. An explicit `routerModel` still overrides automatic routing; unset or `auto` uses the current chat model and does not select a classifier from saved credentials.
- Renamed the public SDK function `inferStructuredOutput()` to `generateStructuredOutput()`; update imports and calls. `createStructuredOutputTool()` still constructs the session tool. Removed the public `inferRouterDecision()`, `resolveRouterModel()`, and routing-only types; `model: "auto"` routes internally with no separate SDK call.
- Removed `/workflow dependency`, the workflow tool's `dependency` action, and the `workflowDependency()` SDK export and its report/operation types. Use workflow status to inspect an affected run; Atomic handles managed PostgreSQL health and recovery internally.

### Added

- Added the `provider_stream_event` extension event for observing each parsed provider stream event before normalization, including provider-specific fields that assistant messages drop, plus an opt-in `/debug-provider` example viewer ([#9784](https://github.com/earendil-works/pi/issues/9784)).
- Added image generation and classification to the model runtime with provider authentication, including built-in OpenRouter image models and TypeSafe `jev-latest`. Extensions can register chat, image, and classifier models together, with operation implementations keyed by API; a declared model list replaces all operations for that provider.
- Remote model-catalog refresh now requests chat, image, and classifier entries, while preserving chat-only responses from older catalog servers.

### Changed

- When a shell tool is available, the default system prompt now asks the agent to record how each task ran in the commits, PRs, issues, and comments it writes: an `Assistant-workflow` trailer naming the workflow and run (or `Assistant-workflow: inline` when no workflow ran), an `Assistant-duration` trailer with measured time to converge against the estimate, and a `User-preference` trailer for durable preferences you express, tied to you by a `Co-authored-by` trailer. Before choosing between a workflow and inline work, estimating duration, or resolving ambiguity, the agent mines these records for comparable tasks with your version control system and its hosting CLI (for example `git log` and the GitHub CLI), and reports sample size, median, and range. Your own recorded preferences take priority; other contributors' preferences apply only as repository conventions when yours do not cover the situation. The history is used as a guide, not as the decision. Ask in your request, a context file, or `APPEND_SYSTEM.md` to turn off any of these records.
- The default system prompt now also asks the agent to record each verification it ran or attempted as an `Assistant-verification` line (for example unit or E2E tests, agent-browser, Cua Driver, Herdr terminal automation, or the qlty CLI, with `passed`, `failed`, or `unavailable`). Before choosing how to verify a change, the agent looks up these records for comparable changes and reuses the checks that worked, without replacing the project's required checks.
- `FILE_MUTATION_CONFLICT` errors from `edit` and `write` now use one labeled line each for the problem, the next step, the first divergence, the live file, and the requester identity, instead of a single wrapped paragraph. The next step names the file to read. A rejected snapshot tag now explains that shell output and tags quoted in errors do not count as reads, and the current tag is marked as for comparison only, so agents stop retrying with it.
- Cua Driver setup guidance now distinguishes installing the executable from installing an agent skill. Atomic already bundles the skill, so it directs agents to skip upstream's optional skill-install commands and leave existing user-level copies alone.
- Decision selection rejects image-generation models; execution `auto` remains restricted to chat language models, including those with multimodal input.
- `/workflow resume` picker rows now display the full workflow ID first, with the workflow name in the adjacent description.
- Tool calls in one assistant message are now ordered by each tool's new `concurrency` setting (`"shared"` by default, or `"exclusive"`). Built-in `edit`, `write`, `todo`, and `ask_user_question` run exclusively, and `bash` does when it uses `pty: true`, so a later `read` in the same message sees an earlier `edit` or `write`, rather than possibly reading the file first. Extension tools can declare `concurrency` as a mode or a per-call function of their arguments.
- `/tasks` now orders each Agents and Shells section by status, with running tasks first (those needing attention at the top), then stopping, queued, failed, cancelled, and completed tasks, newest first within each status. The selected task stays selected when its status changes and it moves ([#3252](https://github.com/bastani-inc/atomic/issues/3252)).

### Fixed

- Prompt cache-miss notices now attribute a miss to TTL expiry only when the idle time exceeds the cache lifetime of the model and retention that served the previous request, instead of a fixed 5 minutes. With 1-hour retention, a miss after 6–59 minutes is no longer mislabeled ([#3259](https://github.com/bastani-inc/atomic/issues/3259)).
- Fixed workflow stages with `model: "auto"` (and other stage sessions) crashing with `Cannot read properties of undefined (reading 'baseDir')` when Atomic was launched with `--theme <path>` or other CLI-provided resource paths ([#3229](https://github.com/bastani-inc/atomic/issues/3229)).
- Managed git packages no longer install Atomic's host-provided peer dependencies (`@bastani/atomic`, `@bastani/pi-ai`, `@earendil-works/pi-agent-core`, `@earendil-works/pi-tui`, and TypeBox) when their dependencies are installed with npm, pnpm, or Bun, and package-manager wrappers in the npm command setting, such as `corepack pnpm`, are now recognized. Extension packages that list one of those packages under dependencies instead of peer dependencies now get an extension warning, because an installed copy can load a duplicate runtime ([#9863](https://github.com/earendil-works/pi/issues/9863)).
- Atomic no longer warns at startup that its bundled workflows, subagents, MCP, and intercom extensions list `typebox` under dependencies. They now declare it as a `*` peer dependency, so they use the host's TypeBox.
- Fixed an assistant reply occasionally disappearing from the live chat when a workflow completion notice arrived just as the reply finished streaming. The reply was still saved to the session, but the conversation shown and sent to the model could omit it until the session was reloaded.
- Fixed a model switch made with Ctrl+P or `/model` just as Atomic finished loading startup resources sometimes being undone on screen: the footer went back to the previous model even though the new model was already in use.
- Fixed X11 clipboard text being pasted as an image when the clipboard owner accepts image targets it does not advertise. Atomic now reads only an advertised image type ([#9786](https://github.com/earendil-works/pi/issues/9786)).
- Managed PostgreSQL now runs from a retained runtime separate from workflow data and project checkouts. Removing a source worktree no longer breaks its server; reinstalling a complete healthy package lets automatic recovery replace a damaged runtime, including the same version, even when new database connections are impossible or the server has already exited. A running server's process identity must verify before shutdown; identity mismatches or corruption still require investigation, and Atomic preserves the database.
- Fixed `structured_output` and workflow stages with a result schema failing on Claude models with `Structured output failed across N model candidate(s)` when the schema bounds a number or string length, such as the builtin `classify-and-act` classifier's `confidence` between 0 and 1.
- Subagent completion notices and `/tasks` details now take the result excerpt and prompt from the child's own messages. A forked child no longer shows parent text it inherited as its result ([#3251](https://github.com/bastani-inc/atomic/issues/3251)).
- Fixed `model: "auto"` subagents and workflow stages always falling back to the current chat model with `Subagent auto routing failed` when a Claude model made the routing decision. Anthropic rejected the routing schema's reasoning-effort field, so no automatic selection ever ran on Claude.

## [0.9.20-alpha.8] - 2026-09-22

### Added

- Added fast variants for first-party xAI Grok models, such as `xai/grok-4.7-fast`. They send the base Grok model with xAI Priority Processing (`service_tier: priority`) for lower latency, billed at twice the standard token rates only when xAI confirms the priority tier. `usesXaiFastServiceTier` reports which models qualify.

### Fixed

- The bundled `bash` and `powershell` tools work again with OpenAI-compatible gateways that enforce structured-outputs schema rules, including models configured with `compat.supportsStrictMode: false`. Their tool schemas no longer declare top-level JSON Schema combinators. Inputs that mix a command with a task wait are still rejected before execution ([#3220](https://github.com/bastani-inc/atomic/issues/3220)).
- Fixed interactive sessions stalling on Windows while agent `bash` and `powershell` commands ran. Each running command polled `tasklist.exe` synchronously 20 times a second, blocking the session for roughly 45 ms per poll; the process-exit check now asks the OS directly without starting a process. With several workflow stages running commands at once, the engine could previously freeze for seconds at a time.
- Fixed a Windows stall when workflow stages or new sessions start: PowerShell availability was looked up with a synchronous `where` process up to three times per session and on every PowerShell tool call. Lookups are now remembered for the current `PATH`.
- Reduced interactive CPU use while the workflow graph or another full-screen overlay is open: covered rows are no longer re-scanned behind the overlay on every frame, and the settled startup banner is no longer rewrapped on every frame.
- Skill catalogs resolve each skill path once per build instead of several times, cutting filesystem work when workflow stages start on Windows.

## [0.9.20-alpha.7] - 2026-09-22

### Breaking Changes

- The model-facing workflow tool no longer has a route action or a routing classifier step: the agent decides whether a workflow fits and runs a registered workflow by name. The router model setting now applies only to automatic workflow-stage and subagent model selection.

### Added

- Added inherited Claude Opus 5.5, GPT-6 Sol, and GPT-6 Luna support for GitHub Copilot.
- Added inherited GPT-6 Sol and GPT-6 Luna support for OpenAI API keys and OpenAI Codex subscriptions, including the `openai/gpt-6-sol-fast`, `openai/gpt-6-luna-fast`, `openai-codex/gpt-6-sol-fast`, and `openai-codex/gpt-6-luna-fast` Fast-mode choices.
- Recorded Artificial Analysis Intelligence Index and Cognition FrontierCode 1.1 results for GPT-6 Sol and GPT-6 Luna in the model evals used by automatic routing.
- Added Vercel AI Gateway and OpenCode Zen as Jev structured-decision providers (`vercel-ai-gateway/typesafe-ai/jev`, `opencode/jev-1.13`, `opencode/jev-1.13-free`) for the router model setting and SDK decisions, reusing each gateway's existing login or its AI_GATEWAY_API_KEY / OPENCODE_API_KEY environment key. Gateway model IDs, limits, and prices come from the models.dev decision catalog, and `getStructuredOutputProviders()` entries now carry the published context window and price.
- Added inherited Claude Opus 5.5 support for Anthropic with adaptive thinking and a 1M context window.
- Added Anthropic fast mode as selectable fast models: `anthropic/claude-opus-5-5-fast`, `anthropic/claude-opus-5-fast`, and `anthropic/claude-opus-4-8-fast`. Each sends the base model with Anthropic's `speed: "fast"` request field and fast-mode beta, for up to 2.5x faster output at twice the standard token rates, and works with both API keys and Claude subscription logins.
- Subagents can now run background `bash` and `powershell` commands instead of being refused with "Background bash observation requires a supported task owner". These shells appear in the parent's `/tasks` (the main chat's, or the current workflow stage's) and keep running after the subagent finishes. The subagent can wait on and `kill` only the shells it launched. Completion notices go to the subagent while it runs, then to the parent. Subagents still cannot launch other subagents.

### Changed

- Structured-decision requests (router and generic) now retry transient provider failures — connection errors and HTTP 408, 429 and 5xx — up to three times with exponential backoff, for both Jev and chat decision models, matching ordinary chat-provider retry defaults. Cancellation is never retried ([#3206](https://github.com/bastani-inc/atomic/issues/3206)).
- Any Jev routing failure now falls back to the current chat model when one exists, including explicitly pinned Jev router selections, missing or unresolvable Jev credentials, context-budget rejections, and oversized response bodies. Cancellation still never falls back ([#3206](https://github.com/bastani-inc/atomic/issues/3206)).
- Jev structured-decision responses are now accepted whenever every question names a known Choice option; reported model, usage, probabilities, confidence and answer types are advisory instead of grounds for rejection ([#3206](https://github.com/bastani-inc/atomic/issues/3206)).
- Recorded Artificial Analysis Intelligence Index and Cognition FrontierCode 1.1 results for Claude Opus 5.5 in the evals snapshot used by automatic model routing.
- Updated the inherited Pi runtime dependencies (`pi-agent-core`, `pi-client`, `pi-protocol`, `pi-tui`, `pi-telemetry`, and transitive `chord`) to 0.87.1.
- The Windows installer extracts the release archive with the native .NET `ZipFile` API instead of `Expand-Archive`, which processed the archive's ~19,000 entries at script speed under Windows PowerShell 5.1. Extraction is now several times faster; `Expand-Archive` remains only as a fallback when the compression assembly cannot load.

### Fixed

- Fixed an API key saved with `/login` in an interactive session not reaching the running engine, so workflow routing and other engine-side work kept reporting `requires an API key` until Atomic was restarted. API-key logins are now persisted by the engine, like OAuth logins and `/logout`, and both sides report the provider consistently ([#3193](https://github.com/bastani-inc/atomic/issues/3193)).
- Fixed the RPC extension UI example to disable extensions with the supported `--no-extensions` flag and safely frame split UTF-8 output.
- The Windows installer now retries removing the previous version backup with a bounded backoff when a file inside it is briefly locked (for example by Defender or the search indexer), instead of leaving a `.backup-*` directory behind in the install root.
- Fixed a subagent launched from a workflow stage taking over the stage's task owner when it inspected its tasks, which could send the stage's background task completions to that subagent.

## [0.9.20-alpha.6] - 2026-09-22

### Breaking Changes

- Removed the `timeoutMs` option from `StructuredOutputRequest` and `RouterDecisionRequest`, and the `DEFAULT_STRUCTURED_OUTPUT_TIMEOUT_MS` export. SDK integrations can pass an `AbortSignal` for cancellation or a caller-owned deadline.
- Removed the inherited `shouldStopAfterTurn` agent option (pi-agent-core 0.87.0). Use `finishTurn` and return `{ action: "end" }` instead. `finishTurn` runs before `turn_end` but applies the decision afterward, and it also receives error and aborted responses; migrate normal-response predicates by returning `undefined` for those hard exits. See the `@earendil-works/pi-agent-core` changelog for a complete before-and-after example.
- Added `ContextEditEntry` to the exported `SessionEntry` union. TypeScript consumers with exhaustive entry switches must handle `context_edit`; use `replacement: null` for omission and a content replacement otherwise.
- Made `SessionManager` canonical for `AgentSession` provider context. Assigning `session.agent.state.messages` no longer replaces future request history; restore with `SessionManager.inMemory(cwd, { id }, entries)`, navigate with `session.navigateTree()`, or append through `session.sessionManager` and call `session.refreshContext()`. A caller-supplied `prepareNextTurnWithContext` replacement context is still honored for the request it prepared.
- Expanded `TurnEndEvent` with required boundary fields (`messageEntryId`, `toolResultEntryIds`, `outcome`, `entries`, `continue`, `context`) and added `AgentBeforeSettleEvent` to the exported `ExtensionEvent` union. Consumers constructing events or exhaustively switching on `ExtensionEvent` must handle the new shapes. `ExtensionRunner.emit()` no longer accepts `turn_end`; host integrations dispatch actionable boundaries with `emitBoundary(baseEvent, buildContext)`.
- Deferred runs requested from `agent_settled` handlers until all settled handlers finish. Handlers still observe `ctx.isIdle() === true`, but no longer see a reentrant `agent_start` during the same notification dispatch.
- Every provider request now waits for the session's queued agent events (including `agent_start`, `turn_start`, `message_start`, `message_end`, and `tool_execution_*` extension handlers) before it is sent, because the request is built from the persisted canonical projection and `message_end` handlers may replace the message that gets persisted. Per-event listener dispatch stays nonblocking, but an extension handler that awaits a later part of the same run (for example a provider response) now stalls that run instead of merely running late; move such waits to `agent_end` or `agent_settled`.

### Added

- Added Meta (Muse subscription) login via `/login meta` with automatic Model API key refresh, plus `META_API_KEY` support.
- Added append-only model-context edits. For example, `sessionManager.appendContextEdit(entryId, null)` omits one message from future provider context without changing raw history, usage, or UI history. See [ContextEditEntry](docs/session-format.md#contexteditentry).
- Added actionable `turn_end` and `agent_before_settle` extension boundaries. Return `{ entries: [...event.entries, draft], continue: true }` to persist structural entries (`custom`, `custom_message`, `context_edit`, or a verbatim `compaction` handoff) in order and ensure one next provider request without changing steering or follow-up scheduling. See [extension events](docs/extensions/events.md#turn_start-/-turn_end).
- Added the `context_with_system` extension event, which runs after `context` handlers on the full transcript including system messages and sends its result verbatim. See [`context_with_system`](docs/extensions/events.md#context_with_system).
- Added per-model image resize profiles through `inputLimits.images.resize` in `models.json` (deep-merged by `modelOverrides`), applied to `@file` attachments, `read` tool images, and tool-result images. Attachments are resized once the run's model is known, so a `before_agent_start` model switch selects the profile. See [Image Input Limits](docs/models/reference.md#image-input-limits) ([#9631](https://github.com/earendil-works/pi/issues/9631)).
- Added crash diagnostics that identify loaded extensions whose source files appear in an uncaught-exception or fatal-error stack trace, with a hint to disable them with `atomic config` or confirm with `atomic -ne`.

### Changed

- Desktop, simulator, and emulator computer use now routes to [Cua Driver](https://github.com/trycua/cua/tree/main/libs/cua-driver) instead of PyAutoGUI. The default system prompt, the Goal/Ralph end-to-end verification guidance, and the workflow-authoring guidance state one face rule: the `cua-driver` CLI through the bundled `cua-driver` skill when a model chooses each action, and the `@trycua/cua-driver` TypeScript SDK inside `ctx.tool` when workflow code owns the scenario. Guidance installs a missing `cua-driver` executable with upstream's one-line installer in one bounded attempt, runs every invocation with `CUA_DRIVER_RS_TELEMETRY_ENABLED=false` plus `cua-driver telemetry disable` after an install, reports missing macOS grants or a non-interactive session as `blocked`/`needs_human` with exact remediation, and never runs `cua-driver skills install`. The Computer use, Verification, Workflow authoring, and Skills guides document installation, readiness, telemetry and update-check switches, a `cua-driver call` example, and a runnable `ctx.tool` verification example ([#3181](https://github.com/bastani-inc/atomic/issues/3181)).
- A custom message sent with `triggerTurn: false` during an active run is still appended after the current turn's tool results rather than steered into it, and, because provider requests now use the canonical session projection, it is visible to that run's next request instead of only a later turn.
- Changed the default xAI model to Grok 4.7.
- Replaced the routing evals snapshot with one Artificial Analysis table of the top 26 catalog models by Intelligence Index, accessed 2026-09-21, with each published benchmark as a column. Grok 4.7 is a row in that table. Auto routing sends that snapshot in full, with the model-selection guide, inside Jev's 32k-token state-plus-longest-question limit.

### Fixed

- Fixed string context-edit replacements producing invalid assistant and tool-result message content instead of text blocks.
- Fixed edited-context accounting both discarding valid assistant usage captured after the latest context edit and reusing that usage after a later compaction made it stale.
- Fixed selected error retries, model fallbacks, and final length/overflow recovery retaining abandoned model attempts in future provider context; post-run recovery omissions are now persisted as `context_edit` entries without hiding raw transcript history or changing queue scheduling.
- Fixed `context` handlers that filter or slice messages dropping the prompt and tool declarations, which after extension-driven compaction left requests without built-in tools or made Codex emit raw tool-call text. Handlers no longer see system messages; Atomic restores the prompt and tool state after they run. See [`context`](docs/extensions/events.md#context) ([#9789](https://github.com/earendil-works/pi/issues/9789), [#9822](https://github.com/earendil-works/pi/issues/9822)).
- Fixed text files beginning with `GIF` being misclassified as images and omitted from `read` and CLI `@file` input ([#9755](https://github.com/earendil-works/pi/issues/9755)).
- Fixed malformed prompt template frontmatter being silently ignored instead of reported as a resource warning naming the file and parse position ([#9830](https://github.com/earendil-works/pi/pull/9830) by [@christianklotz](https://github.com/christianklotz)).
- Fixed inherited unknown OpenAI-compatible Chat Completions endpoints receiving strict tool schemas unless they explicitly advertise support ([#9816](https://github.com/earendil-works/pi/issues/9816)).
- Structured decisions and automatic model selection no longer fail at a fixed 30-second deadline, including during model ranking, output repairs and Jev-to-chat fallback. Explicit cancellation and independent provider and tool-request limits still apply.
- Workflow reviewers and other stages no longer fail extension startup with a misleading `Live workflow-stage route is owned by another active session` when their workflow owner is re-registering with the Intercom broker, when the previous attempt's session is still being torn down, or when a later review round reuses a stage name whose earlier occurrence is still connected. Refusals now name the actual condition, transient ones recover through the bounded stage warm-up retry, and genuine duplicate owners are still refused. See [Intercom operations](/intercom/operations#workflow-stage-route-refusals) ([#3163](https://github.com/bastani-inc/atomic/issues/3163)).
- Fixed missing or invalid `--mode` values being silently ignored instead of reporting an error and exiting with a nonzero status ([#9045](https://github.com/earendil-works/pi/issues/9045)).

## [0.9.20-alpha.5] - 2026-09-20

### Added

- The SDK exports `spawnProcess` next to `createChildProcessEnvironment`: the child-process spawner that stamps Atomic's agent attribution into the environment and, on Windows, runs `.cmd`/`.bat` launchers through `cross-spawn` with correct argument quoting. The bundled `impeccable` skill's engine launcher and the `open-claude-design` live loop use it.

### Changed

- Automatic subagent and workflow-stage model selection now sends a fixed `model_selection_guide` state field next to `evals`: the "Benchmarks are evidence, not policy" and "Role-based thinking effort" sections from [Model Selection](/models/model-selection), so the router applies the role defaults and effort rules rather than only the dated benchmark tables. To keep every Jev request within its input budget, the model-selection task excerpt cap is now 10,000 JSON-encoded UTF-8 bytes (was 12,000); execution tasks are still sent in full.
- The `model_selection_guide` sent with automatic model routing, and the [Model Selection](/models/model-selection) guide it mirrors, now pair each stage role with a model cost tier as well as a thinking level: cheap, fast models for codebase exploration and routine implementation, frontier models for code review, verification, and final approval, and mid-priced models for orchestration and reporting. The router is told to match the role tier first, so high-volume tool-driven work stops defaulting to the most expensive candidate. The larger guide is paid for by lowering the model-selection task excerpt cap to 9,000 JSON-encoded UTF-8 bytes (was 10,000); execution tasks are still sent in full.
- The default system prompt's Guidelines section now tells the agent to write self-describing code and not add comments unless the user explicitly asks for them or the task instructions call for them; code that needs a comment to be understood is treated as a smell to restructure with clearer names, smaller units, or explicit types.
- The default system prompt and the computer-use, skills, first-session, and verification guides now direct browser automation to the `agent-browser` skill and CLI ([vercel-labs/agent-browser](https://github.com/vercel-labs/agent-browser) v0.38.1) instead of the removed `playwright-cli` skill; install with `npm i -g agent-browser && agent-browser install`.
- The bundled `impeccable` skill is upstream `skill-v4.3.1` with its self-contained engine (0.1.5). Its commands run through `scripts/impeccable` (`impeccable.cmd` on Windows), which uses an engine binary beside the launcher or downloads and checksum-verifies the pinned release for your platform on first use; set `IMPECCABLE_BIN` to a preinstalled engine on machines without network access. Atomic's launcher keeps the engine's anonymous ping and `npx impeccable update` prompt off. See the [first session](/getting-started/first-session) guide.

### Fixed

- Workflow stage chats and other transcript views built from session messages no longer print a bare dim "system" line for the session's system-prompt message.
- Auto model routing no longer prints `Text was truncated to fit the input budget. Continuing with the shortened text.` to the console when it shortens a long task for its own routing request; the execution prompt was never affected.

## [0.9.20-alpha.4] - 2026-09-20

### Breaking Changes

- Provider implementations now receive transcript-only contexts. Read instructions and tools with `getCurrentSystemPrompt(context.messages)` and `getCurrentTools(context.messages)`. Extension prompt options are mutable; return a forced system prompt only for an unrecorded, complete per-run replacement.

- Intercom now respects tool allowlists and exclusions. `tools: []` and `noTools: "all"` expose no tools; `noTools: "all"` also overrides nonempty allowlists. Select `"intercom"` explicitly when using an allowlist. The builtin selection flags in `CreateAgentSessionOptions` can disable individual shipped packages and resources without reload restoring them ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Extension dialogs without human-input support now reject with `code: "HumanInputUnavailable"` instead of returning empty defaults. Check `ctx.hasHumanInput` for questions; `ctx.hasUI` remains a presentation capability. Pending dialogs are cancelled on adapter withdrawal, abort, reload and disposal ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Headless workflow launches now return an accepted run identity instead of waiting for completion. Inspect workflow status for results. Required durable gates remain pending even when no human host was initially bound; absent UI is no longer inferred as an execution restriction. Explicit runtime policies remain enforced ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- `AgentSession.dispose()` now returns a promise. Await it to cancel and drain owned work, including admitted prompt hooks and reload preparation, flush persistence and release builtin resources. Repeated calls share the same outcome; partial cleanup rejects with `code: "ShutdownFailed"` rather than reporting success. Runtime replacement uses the same shutdown and preserves host bindings. Suspended callbacks must settle before cleanup completes; retired preflight cannot start a provider or publish a reload candidate ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Terminal shutdown now seals compaction and user/custom queues and drains initial extension binding, steering/follow-up hooks and pending runtime replacement factories/startup before resolving. Independent sessions borrowing the same resource loader or session storage no longer share workflow/native-task shutdown ownership. Custom forwarding loaders, subclasses and copied/edited extension registrations retain caller policy and session-local API bindings. Failed factories unwind owned acquisitions; failed outgoing retirement still finalizes retained workflow resources before rejecting ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Factory rollback now covers pre-constructor setup and preserves all cleanup causes. Concurrent runtime replacements retire displaced successors and preserve surviving workflow ownership after candidate failure. Disposal cancels all shell calls with repeated caller IDs and reports real, session-attributed settings write failures without consuming shared manager errors ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Closing preserves queued conversation events and persistence, drains current and superseded background summaries, and rolls back candidate acquisitions when reload discovery fails before startup. Reload cleanup failures retain every original and cleanup cause ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Model-tool workflow launches now use `workflow route` with actual request/message/document text, then `workflow run` with the registered execution ID and inputs. Reservations retain one identity through admission and supported lifecycle operations; invalid inputs can be corrected without rerouting. Old run-as-router calls and duration values are rejected. Estimates use canonical quarter-hour labels through `1d`, plus `>1d`; budgets and approval gates remain unchanged ([#3106](https://github.com/bastani-inc/atomic/issues/3106)).
- Renamed the TypeSafe Jev environment variable from `TYPESAFE_AI_API_KEY` to `TYPESAFE_API_KEY`. Update shell and deployment configuration; the old name is no longer recognized. Saved `/login typesafe-ai` credentials are unchanged.
- Renamed the direct Jev decision model from `typesafe-ai/jev` to `typesafe-ai/jev-latest`. Update `routerModel` settings and explicit SDK selections; saved TypeSafe credentials are unchanged.
- Workflow duration estimates no longer accept the string value "unknown"; routers give their best estimate using the existing quarter-hour buckets or `>1d`.

### Added

- System instructions and tool changes are recorded as chronological transcript messages, with structured prompt patches and replay across resume, branches, and verbatim compaction. Supported providers preserve the cached prefix for mid-conversation updates.

- Optional prompt-cache warming (`streaming`, `idle`, or `off`) refreshes replayable caches before TTL expiry, with owner/session cancellation and a `/settings` control. Default mode is streaming; prompt-cache retention still defaults to long where the model supports it.

- Added workflow dependency status, doctor and safe managed-cluster recovery through `/workflow dependency`, the workflow tool and `workflowDependency()`, reporting verified identity, actual port, runtime versions, consumers, retained latest failure and recovery guidance without resetting data ([#3074](https://github.com/bastani-inc/atomic/issues/3074)).
- Session, Workflow, and Intercom UUID selectors accept unique 8-character hexadecimal prefixes while preserving exact custom IDs and visibility boundaries ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).
- Attached stage chats show `[stage: name]` on the composer top rule, a mounted question's top rule, and awaiting-input prompt borders ([#2886](https://github.com/bastani-inc/atomic/issues/2886), [#3013](https://github.com/bastani-inc/atomic/pull/3013) by [@sumitvairagar](https://github.com/sumitvairagar)).
- Added bounded structured-decision SDK APIs and direct TypeSafe Jev Choice integration. The shared `routerModel` setting selects prerequisite routing inference; general `inferStructuredOutput()` calls select their inference model explicitly and remain one-shot. Decisions validate exact schema values without agent loops. The selected chat model and `structured_output` tool are unchanged ([#3089](https://github.com/bastani-inc/atomic/issues/3089), [#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- Model-tool workflow launches now require task/conversation/documentation state and one bounded routing decision before admission. Tool results show the validated workflow and exact budget as formatted JSON; `none` means continue inline without launching, and stale registry decisions are refused. Routing context containing known configured credentials is rejected before either provider receives it. User `/workflow` commands and authored `ctx.workflow(...)` calls bypass routing ([#3089](https://github.com/bastani-inc/atomic/issues/3089)).
- Added opt-in subagent `model: "auto"` routing for single tasks, parallel tasks and agent defaults, using available model/effort pairs and shipped evaluation guidance. The shared `routerModel` setting selects the decision provider; invalid or stale decisions prevent child launch, and selected versus fallback execution models remain distinct in task metadata ([#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- TypeSafe Jev supports API-key login and logout through the normal credential store, with environment-key fallback. Saved Jev credentials can select the default router while Jev remains absent from chat-model selection.
- Added a searchable Router model entry in `/settings`, saving automatic routing, Jev, or an available provider/model to `settings.json` without changing the chat model.
- Added workflow-stage `model: "auto"` selection from actual prompts and evaluation guidance, with constrained model/effort pairs and separate selected versus execution metadata.

- SDK hosts can provide typed `HostInput` callbacks for confirmations, selection, text and questionnaires without a terminal. Replies are validated and carry session/request identity and cancellation; the onDiagnostic callback receives session-attributed operational messages ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- SDK human-input callbacks now answer durable workflow approvals and stage questionnaires, including nested workflows, with workflow/run identity. Withdrawing or replacing a host keeps unresolved approvals pending and rejects stale replies; the same definition can resume under a different host without repeating checkpointed tool effects ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Added `openrouter/~typesafe/jev-latest` for routing and SDK structured decisions through OpenRouter's Decisions API, reusing existing OpenRouter sign-in, saved API keys, and environment fallback without a TypeSafe key.

### Changed

- Install/update telemetry pings now go to the Atomic version-adoption endpoint instead of pi.dev. First-interactive-launch triggers, opt-outs, and the independent update check are unchanged ([#2498](https://github.com/bastani-inc/atomic/issues/2498)).
- Pi runtime dependencies (`pi-agent-core`, `pi-client`, `pi-protocol`, `pi-tui`, `pi-telemetry`, and transitive `chord`) are pinned to 0.86.1. Workspace package versions remain `0.0.0`.
- Automatic subagent and workflow-stage model selection no longer includes system prompts as agent metadata, reducing routing input. Execution prompts are unchanged; self-contained subagents still use their system prompt as the task when no task is supplied.
- SDK session creation now includes shipped Atomic builtin extensions and resources, shares CLI defaults, and completes extension startup before returning. Supply startup host bindings through the extensionBindings option; rebinding no longer repeats startup. Failed startup rolls back the partial session, and missing shipped packages report `code: "BuiltinUnavailable"` ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Main chat, workflow stages, and subagents now default to long prompt-cache retention where supported. Unset retention uses ordinary caching for OpenAI models without known extended-retention support, such as GPT-4o, and five-minute caching for older Bedrock Claude models, such as Claude 3.7. Explicit retention choices remain unchanged; use `PI_CACHE_RETENTION=short` to opt back into shorter caching. Anthropic one-hour cache writes cost more than five-minute writes, so savings depend on reuse.
- All bundled workflows and subagents now default to `model: "auto"`, selecting task-specific models and efforts instead of fixed fallback chains. Explicit model/effort selections remain supported; main-chat defaults and user-authored resources are unchanged.
- Automatic model selection now ranks up to three distinct eligible models with individual reasoning efforts. Subagents and workflow stages try them in order before remaining configured fallbacks; workflow resume preserves the ranking.
- Automatic subagent and workflow-stage model selection now uses bounded excerpts of long tasks, preserving their beginning, end and protected `<keepContext>` spans. Execution prompts and hard model constraints remain unchanged; oversized protected content retains the existing router fallback behavior.
- Automatic subagent and workflow-stage model selection now sends the factual `evals.md` benchmark records as the router's evals state field instead of sending the general model-selection guide. The guide stays human-readable and links to the dated eval records, which use markdown tables.

### Removed

- Removed the Benchmark Sources and Pareto Efficiency documentation pages. Dated measurements and one-paragraph benchmark explainers live in Evals; general selection guidance stays in Model Selection.

### Fixed

- Automatic compaction cancelled from a `compaction_start` listener now aborts the auto controller before `session_before_compact` or planner auth.
- Anthropic thinking-drop notices are short, omit path details, and stay silent when the cumulative drop count is unchanged.
- Isolated-engine prompts wait for the remote prompt RPC to settle, and stale kitty image conversions no longer replace a later tool result.
- Fixed idle prompt-cache warming rebuilding expired caches when its timer or an extension decision is delayed.
- Fixed clipboard copy failing in containers and WSL without WSLg by restoring the OSC 52 fallback when no display is available, and added a verified Windows clipboard backend for WSL ([earendil-works/pi#9688](https://github.com/earendil-works/pi/issues/9688)).
- Clipboard copy from an SSH or Mosh session into WSL now emits only OSC 52 for the connected client instead of also writing the remote host's Windows clipboard.
- Fixed inherited z.ai `Prompt too long` errors not being recognized as context overflow ([earendil-works/pi#9805](https://github.com/earendil-works/pi/issues/9805)).
- Fixed inherited Cerebras models advertising unsupported strict tool schemas, which caused HTTP 400 errors when strict and non-strict tools were mixed ([earendil-works/pi#9804](https://github.com/earendil-works/pi/pull/9804) by [@EdenGottlieb](https://github.com/EdenGottlieb)).

- Skill invocation summaries expand and collapse on left-click, matching branch summaries and compaction boundaries.
- Fireworks deferred-tool docs now describe chronological system-message tool additions and removals instead of the removed added-tool-names field from tool results.

- Reload now releases unpublished preparation acquisitions, drains failed candidate callbacks before shutdown, and retains self-reload command continuations through final disposal, including cleanup failures ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Reload now retains retiring-generation shutdown and invalidation when postcommit runtime reconstruction fails, preserving both setup and cleanup causes and keeping the candidate owned for disposal ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Custom messages received during tool execution now enter conversation history after the tool results and before the next assistant response, including when extension callbacks finish asynchronously ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Entry renderer discovery preserves the registered function identity while transcript rendering uses the owning session and rejects retired runners ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Session disposal now reports event-listener failures occurring during shutdown through its aggregate rejection while still flushing consumed workflow reconciliation and attempting remaining cleanup ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).

- Stage-scoped durable workflow resume now refuses before dispatch instead of resuming the whole root, including restored local shadows of paused durable runs, and prefix resume no longer treats nested children as root candidates ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).

- Explicit Intercom reply names and full session IDs keep their original identity through broker collision checks, unique UUID prefixes still canonicalize to the stored session ID, and hidden same-name collisions refuse regardless of letter case ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).

- Canonicalized Intercom UUID-prefix sends revalidate the original selector against the broker's current authorized sessions, so a newly visible same-prefix UUID, exact name, or custom ID refuses instead of delivering to the previously unique identity ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).

- Prefix ambiguity refusals no longer include unauthorized or hidden session names when a canonicalized UUID-prefix send is rejected ([#2603](https://github.com/bastani-inc/atomic/issues/2603)).

- Running Atomic inside a Herdr pane no longer leaves the pane unreported or labelled as another agent when Herdr's installed Pi integration is present. Herdr installs `herdr-agent-state.ts` into the legacy `~/.pi/agent/extensions` directory that Atomic also loads from, and Atomic used to stand its built-in reporter down as soon as that file loaded — but the installed asset reports itself as `pi`, so the pane ended up mislabelled or never updated at all. Inside a Herdr pane Atomic now skips that installed integration when loading extensions and reports the pane itself; outside a Herdr pane the file loads exactly as before. Nothing to configure, and the extra extension no longer needs to be disabled by hand ([#2416](https://github.com/bastani-inc/atomic/pull/2416) by [@makgunay](https://github.com/makgunay))
- Request authentication now fails within 15 seconds of one credential-preparation attempt when refresh or derivation does not settle, including a fallback whose OAuth login has expired. Agent sessions reuse that request's resolved auth for transport setup instead of starting a second deadline. Timeout errors tell you to check the provider's credential source rather than to log in. Operator cancellation still aborts without fallback or an auth-block notice, and the bound does not apply to human-input waits, tool execution, or an already-opened model stream ([#3085](https://github.com/bastani-inc/atomic/issues/3085), [#3087](https://github.com/bastani-inc/atomic/pull/3087)).
- `/reload` now remounts a still-live extension panel after the previous generation is cleared, instead of leaving the panel gone. Replacing one panel with another still does not count as a clear ([#2529](https://github.com/bastani-inc/atomic/issues/2529)).
- MCP server URLs now support `${VAR}` and `$env:VAR` environment interpolation, including deployment-specific origins in shared configuration. Empty, invalid, or unsupported resolved endpoints report a configuration error before connecting. Diagnostics redact query-key credentials, while manual OAuth instructions show the complete authorization URL, including all paths, parameters, and any credentials ([#3088](https://github.com/bastani-inc/atomic/issues/3088)).
- After DBOS initialization, workflow root database registration now has a 10-second deadline instead of waiting for the request timeout. Unavailable admission skips database cleanup, preserving the failure diagnostic and run identity even when PostgreSQL stops answering. Cancelled admission cannot start workflow code later, and database loss invalidates admission readiness. First-time provisioning and initialization are outside this bound ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Malformed saved workflow topology is non-resumable. Rejection during root admission returns a failed run result without executing workflow code or hiding database write failures ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Timed-out or cancelled workflow resume admission no longer publishes stale ownership metadata when its database operation finishes late ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Database-rejected workflow admission preserves PostgreSQL authentication and permission diagnostics and removes a never-persisted local run without reporting it as still running ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Resume continuations skip cleanup when database admission is unavailable, leaving the source resumable instead of waiting on the failed database ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Cancellation during admission now retires a possibly committed database record without waiting on the abandoned admission write, including a checkpointed continuation whose resume has not finished. Failed cancellation persistence retains the local cancelled outcome and warns that database state is unknown ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Graceful quit during startup admission acknowledges the local stop immediately without waiting for registration. Registration settlement retains its admission deadline, including when a local kill arrives while registration finishes. Settlement failures appear in run status while preserving the local pause and resumability for continuations with saved durable progress. Runs without usable durable progress remain nonresumable ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Pausing during startup acknowledges immediately and retains the live executor without waiting for database admission. Later admission failures retain their original diagnostic in run status, and resume releases the failed attempt instead of leaving it stuck behind a rejected pause ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Workflow status distinguishes pending database admission from execution and reports dependency failures, phase age, and last progress. Live executor pause/resume reports local versus confirmed database persistence, stays responsive during outages, and preserves the paused owner for same-ID recovery ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Live executor resume allows up to 10 seconds for database confirmation instead of using the 500ms pause acknowledgement budget, avoiding false timeouts on healthy remote PostgreSQL connections ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Live executor pause acknowledges within 500ms while database confirmation continues for up to 10 seconds. Healthy slower connections no longer leave a false dependency failure; status updates to durable when confirmation succeeds ([#3072](https://github.com/bastani-inc/atomic/issues/3072)).
- Keep ready shared PostgreSQL running across Atomic client exit and reload, with installation-independent cluster ownership records and consumer leases ([#3074](https://github.com/bastani-inc/atomic/issues/3074)).
- Managed PostgreSQL verifies server identity and SQL readiness, supports `ATOMIC_POSTGRES_PORT`, and shares an available actual port across sessions without adopting foreign listeners or unregistered data ([#3074](https://github.com/bastani-inc/atomic/issues/3074)).
- Custom models declared in `models.json` no longer generate automatic `-fast` siblings, preventing invalid choices such as `gpt-6-astra-slow-fast`. Built-in fast variants and `modelOverrides` remain supported.
- Bedrock requests honor the configured provider retry count, including zero, so bounded router decisions do not make hidden SDK retry attempts ([#3089](https://github.com/bastani-inc/atomic/issues/3089), [#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- Jev structured decisions now handle choices above 255 options through bounded multi-request tournaments, including every candidate and comparing finalists without cross-batch probability ranking. Subagent auto routing supports large model/effort catalogs; workflow routing retains `none` and exact budgets. One shared deadline and fail-closed validation cover all rounds ([#3089](https://github.com/bastani-inc/atomic/issues/3089), [#3090](https://github.com/bastani-inc/atomic/issues/3090)).
- Workflow routing preserves omitted budget fields with strict-schema providers instead of rejecting their required null placeholders. Explicit zero and fractional limits retain their exact meaning.
- Subagent auto routing uses the routed thinking level when checking unsuffixed fallbacks and cancels sibling routing requests when a parallel routing decision fails ([#3103](https://github.com/bastani-inc/atomic/pull/3103)).
- The Router model picker updates an existing project override instead of silently changing a global default that the project ignores, and displays the save scope ([#3103](https://github.com/bastani-inc/atomic/pull/3103)).
- Saved Jev credentials become available to automatic routing immediately after login, even during catalog refresh; stale refreshes no longer restore credentials after logout.
- Workflow and automatic model routing now repair malformed or schema-invalid answers up to three times after the initial attempt, including Jev, within one shared deadline. Authentication/provider failures, cancellation and stale decisions still stop without retries or launch.
- Workflow and subagent children retain the invoking SDK session's model/auth configuration, host callbacks and tool restrictions through fallback. Child requests and diagnostics keep their own session identity; disabled Intercom cannot grant supervisor access, and workflow children no longer share their parent stage's pending asks ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Workflow stage questionnaires respect explicit child human-input adapters and withdrawal without losing durable pending/rebind behavior. Stage fallback replacements enforce inherited model restrictions instead of treating fallback candidates as unrestricted primaries ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Explicitly rebinding a workflow child to its originally inherited adapter now keeps questions with that selected host after parent rebinding, including across child reload ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Child sessions honor a supplied session manager's working directory when the cwd option is omitted, and optional `undefined` child configuration no longer discards inherited models, settings or host callbacks ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Failed file-loaded extension factories now run registered rollback cleanup; cleanup failures reject instead of becoming discovery-only diagnostics. Reload seals retiring input before successor startup. Disposal drains asynchronous extension notifications, bus handlers, shortcuts and workflow observers before shutdown, including notifications from synchronous thinking/session-name setters ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Command-context session replacement no longer waits on its own command. It seals retiring admission and drains peers before handoff, while final disposal still drains the command continuation and cleanup. Closing preserves completed tool results and hooks, and nontransactional reload failures release owned acquisitions without closing borrowed discovery ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Concurrent command replacements no longer circularly drain one another. Reload activation and commit failures clean their started candidates, and unpublished MCP initialization cleanup failures remain explicit during disposal ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- SDK creation now cleans owned factory acquisitions omitted by extension selection without disabling selected extensions. Self-reload revokes captured old APIs before successor admission while retaining continuation cleanup and deferred failures ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Filtered creation and reload now retire omitted factories' APIs and subscriptions independently, preserving selected extensions. Captured extension actions refuse new work immediately during close/reload, while admitted subprocesses drain. Deferred shutdown after self-reload or command replacement can no longer mutate, acquire task hosts from, or send events into the successor ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Reload acquisition rollback no longer depends on a working extension getter. Shutdown and factory rollback now wait for tracked cleanup subprocesses after handlers return, while keeping fresh admission closed. Released event subscriptions and workflow publishers no longer retain completed release callbacks or their captured data after manual release or generation invalidation ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Factory rollback now refuses fresh work across all closing factories before awaiting cleanup and lets already-admitted callbacks finish before shutdown. Omitted factories drain independently of selected extensions sharing their runtime, preventing late resource leaks without blocking surviving extensions ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Workflow resource refreshes started through the extension API now finish before disposal, reload retirement or failed-factory cleanup, preventing late acquisitions from surviving successful shutdown ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Installed Node SDK consumers can now check the complete public declarations with strict NodeNext settings and `skipLibCheck: false`, including model catalogs and the find tool's path interface ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Prepared SDK services no longer reconstruct safe extensions or repeat their startup when project trust completes. Additional sessions borrowing those services keep independent extension lifetimes ([#3105](https://github.com/bastani-inc/atomic/issues/3105)).
- Jev structured decisions no longer reject responses solely because their probabilities do not sum to one.
- Jev SDK decisions retain their original model, provider and credentials across tournament rounds when callers mutate their model selection during inference.
- `/logout` now autocompletes providers with stored API-key or subscription credentials and opens a filtered selector when a provider is supplied, instead of sending the command as a prompt.
- Built-in Cloudflare AI Gateway, GitHub Copilot, and OpenCode routes now respect OpenAI model limits when choosing default cache retention, avoiding unsupported extended retention on models such as GPT-4o. Explicit retention overrides remain unchanged.
- Jev failures now report SDK error classes, recognized context-limit diagnostics and safe request IDs instead of generic provider-availability advice. Requests use the TypeSafe SDK without hidden retries or body logging.
- Automatic routing switches from Jev to the current chat model after an HTTP/connection failure or exhausted output repairs. By default each provider gets an initial attempt plus three corrective retries for invalid output, with a reported switch and one shared context, validation contract and deadline. Explicit router selections and general structured-output calls remain pinned.
- Reduced routing context with a concise model-selection guide and per-candidate contracts instead of repeated catalogs. Jev checks every request against conservative context budgets, splits verbose choices even below 255 options, and avoids sending oversized inputs.
- Extension imports of provider environment helpers now resolve correctly during workflow loading and reload instead of failing with `compat.js/utils/provider-env` module errors.
- Automatic subagent and workflow-stage model selection now receives the shipped model-selection guide directly, restoring benchmark guidance that the previous provider-identity filter excluded.

## [0.9.20-alpha.3] - 2026-09-16

### Fixed

- Fixed compaction statistics mixing authoritative provider token counts with heuristic region estimates, which could produce incorrect/negative percentReduction. Statistics now use symmetric heuristic estimates (region + explicit tail) while preserving the authoritative tokensBefore for budgeting and display ([#2052](https://github.com/bastani-inc/atomic/issues/2052)).
- Standalone installations include PostgreSQL library aliases and reject unusable database runtimes before installing or upgrading. macOS and Linux packaging validates the transitive dependencies required for PostgreSQL startup and workflow database operation. A server left running by an older installation no longer hides an incomplete runtime. npm hydration preserves safe ordered alias chains ([#3073](https://github.com/bastani-inc/atomic/issues/3073)).
- Local clipboard failures now report platform-specific remedies instead of falling back to an unverified OSC 52 write. Fullscreen selection displays the diagnostic for five seconds; SSH and Mosh retain OSC 52 support.
- Exported missing extension hook event and result types from the public package entrypoint.

### Added

- Added `compat.allowedFallbackModels` configuration for Anthropic-compatible models, including complete target pricing and `[]` to disable inherited fallbacks.

### Changed

- Exact `--session-id` lookup and fork-target collision checks read session headers instead of scanning transcripts, while keeping internal workflow sessions hidden.

## [0.9.20-alpha.2] - 2026-09-15

### Fixed

- Bundled integration documentation is discoverable under the same `docs/` root in npm and binary installations, including the MCP setup guide at `docs/mcp.md`.

## [0.9.20-alpha.1] - 2026-09-14

### Fixed

- Workflow startup now reports its phase and age, and explicit cancellation releases waiting callers while retaining unfinished creation ownership. Cancelled queued reloads cannot start later; late SDK/binding results cannot attach or dispatch ([#3040](https://github.com/bastani-inc/atomic/issues/3040)).

## [0.9.19] - 2026-09-13

Cumulative release of the `0.9.19-alpha.1` through `0.9.19-alpha.12` prereleases. Per-change details remain in the unchanged prerelease sections below.

### Breaking Changes

- Overwriting an existing file with `write` requires this session to have observed the exact content being replaced. Missing or stale observations return typed `FILE_MUTATION_CONFLICT` errors with requester identity and recovery guidance. Creating files and overwriting files the session has read, written or edited remain supported ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- `WriteOperations` requires `readFile`, returning `undefined` only for absence, and `writeFile` accepts optional `WriteFileOptions`. Custom/remote implementations supply reads for generated-file and observation checks; existing writers may ignore the options but lose exclusive-create semantics ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- Workflow controls use `/workflow pause`, `/workflow quit` and `/workflow resume`; lifecycle pause events use `action: "pause"`. The workflow tool supports run, stage and durable-tool pause targets.
- Bundled subagent terminal control is now `kill`, not `interrupt`, including calls using `runId`. Killed children cannot resume; launch a fresh child for follow-up work.
- Bundled `code_search` requires `repoName` in `owner/repo` format and uses DeepWiki MCP without an Exa fallback. `fetch_content` requires a nonempty `urls` array instead of singular `url`; update existing calls. `web_search` is unchanged.
- Intercom removed model-facing `retryToken` fields and owns bounded reconnect retries. Explicit `replyTo` selectors reject invalid or stale threads rather than falling back; use `action: "pending"` to select an unresolved question.

### Added

- Added owner-bound background shell and agent tasks with one execution per admitted attempt, observation-only yielding, retained output/transcripts, owner-scoped cancellation and independent cleanup. SDK contexts expose trusted task adapters, snapshot subscriptions and replay-safe completion delivery ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added per-call Bash/PowerShell foreground-first or background observation, `{ action: "wait", id, budgetMs }` for existing tasks, and `kill({ id })` for owned background shell tasks. Observation does not extend execution timeout or owner lifetime; unsupported background requests fail before execution.
- Added shared `/tasks` inspection with a compact inline picker, fullscreen detail/transcript/input pages, paging, confirmed cancellation and stable selection. Main and attached stage chats show background counts below the composer and readable completion notifications.
- Added a searchable `/agents` catalog with source grouping and configuration details.
- Added supervised Windows ConPTY execution with Job Object containment before resume and confirmed cleanup, without an unsupervised fallback. The command SDK accepts direct-executable `shell: { program, args }` and `inheritEnv` controls.
- Added workflow activity observers and typed lifecycle, activity, stage-completion and heartbeat hooks with ordered snapshots, bounded queues and reload-safe leases. The built-in Herdr reporter combines agent/task activity, workflow execution and approval waits, supports `herdr.enabled` opt-out, and reports parent session identity ([#2891](https://github.com/bastani-inc/atomic/issues/2891)).
- Added `reason: "project_trust"` to UI prompt lifecycle events, including paired isolated-engine notifications, so integrations can observe trust decisions ([#2873](https://github.com/bastani-inc/atomic/issues/2873)).
- Editable workflow-stage chats discover source-qualified skills and expand submissions once through stage admission. Graph cards show effective model/thinking identity, including fast suffixes and live fallbacks ([#1859](https://github.com/bastani-inc/atomic/pull/1859) by [@sina85](https://github.com/sina85)).
- Added per-model compaction overrides for `reserveTokens` and `preserve_recent`, with non-negative safe-integer validation and fallback to ordinary settings/defaults across manual, automatic and post-tool compaction.
- Extension widgets can opt into height-capped fullscreen viewports with local wheel scrolling, overflow-only scrollbars and scroll-position feedback. The workflow list uses local scrolling with configurable Alt+K/J shortcuts, labeled Option on macOS, while retaining Alt+PageUp/PageDown aliases and editor-binding precedence.

### Changed

- Built-in read, edit, write and shell tools prefer strict JSON-schema sampling where supported; other experimental hints remain opt-in.
- Task receipts and details display resolved model/reasoning, grouped counts, themed status, recent activity, bounded previews and narrow-terminal layouts. Only active background work contributes to the footer; retained results remain in `/tasks`.
- Native Windows owner-bound shell calls automatically yield after the owner's observation budget, normally ten seconds. PowerShell uses encoded transport; execution timeouts remain separate, and unsupported owned WSL bash stdin transport remains refused. Suspend opens a PowerShell subshell without freezing background tasks.
- Guidance routes user questions through structured question tools when available, favors normal shell observation budgets over repeated short polls, and keeps blocking work local while overlapping independent delegation. Requests to work "quickly" select inline execution without hidden workflows or skipped validation.
- Model-choice guidance consults eval documentation and Artificial Analysis; automation guidance uses PyAutoGUI for desktop, playwright-cli for browsers, and Herdr with supported fallbacks for terminals.
- Open Claude Design starts with Fable 5.1 at medium, followed by Copilot Fable 5.1 and Astra at medium, with Fable-first OpenRouter fallbacks. Debugger uses Astra/Fable at medium and Sol at high; Goal/Ralph reviewers use Astra/Sol at high, and orchestration/research/design fallbacks use Fable at medium and Sol at high.
- Intercom lists lead with exact session IDs and workflow paths. Sends and asks interrupt working recipients within the same task/generation, retaining ordered messages and completed side effects rather than restarting work.

### Fixed

- Restored offline embedded PostgreSQL discovery in compiled archives and all eight target-selected archive/npm runtimes, retaining scriptless installs and existing v18 clusters. Windows ARM64 still requires Windows 11 x64 emulation.
- Embedded Postgres starts on Windows administrative accounts using restricted tokens while retaining exact-process shutdown. Early startup exit reports actual logs; Unicode environments, executable lookup, batch/explicit-command launchers and safe verbatim paths work without handle leaks or cross-process stream inheritance.
- Rejected edits no longer authorize themselves on retry by recording a new snapshot tag. Drift, deletion, unreadable targets and permission changes return typed conflicts with exact requester identity and recovery guidance. Writes exclusively claim new paths, preserve observations after cancellation that follows a completed write, and retain standalone `local://` observations ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- Bounded noisy engine diagnostic history and routed stderr/MuPDF diagnostics through deferred, terminal-safe status output, preserving split Unicode and failure details without corrupting fullscreen or RPC JSON output ([#2964](https://github.com/bastani-inc/atomic/issues/2964)).
- Fixed npm-installed task supervisor class initialization and missing upstream AI imports. First-load and Windows-reloaded TypeScript extensions share live host classes/singletons, including the upstream-compatible import, without losing dependency edits.
- Workflow fallback releases failed-attempt Intercom ownership before replacement. Repeated nested workflow calls no longer collide during fallback ([#3020](https://github.com/bastani-inc/atomic/issues/3020)).
- Foreground subagent waits yield to parent steering and Intercom messages without cancelling children. Parallel coordination preserves active/queued siblings, same-child messages precede completion, and queued-child cancellation cleans up unused worktrees and branches.
- Task completion retries retain delivery identity without relaunching work, including restored outboxes, native shell settlement and `/tasks` cancellations. Foreground-only shells do not create duplicate background notifications or model turns.
- Fixed yielded shell waits replaying the first output page, honored owner wait policies, disclosed retained output gaps, and bounded output pages to 1 MiB under a shared disk cap ([#2972](https://github.com/bastani-inc/atomic/pull/2972), [#2905](https://github.com/bastani-inc/atomic/pull/2905)).
- Live transcripts stream updates without reopening, preserve historical paging and configured shortcuts, and reject stale task-detail results after selection changes. Long Windows transcripts reuse unchanged rendering to reduce CPU without dropping history.
- Isolated sessions use the engine's task owner and inspector, with `/tasks` autocomplete and live metrics. Task rows reattach after transcript replacement, old owners clear on session switches, and inspection failures remain retryable instead of rejecting unhandled.
- Stage pause cancels owned admitted/active tasks and waits for cleanup while preserving messages and sibling work; ordinary chat interruption leaves background tasks running. Explicit subagent kill and grouped cancellation outcomes remain consistent through parent-cancellation races.
- Corrected SDK task Results, cursors, opaque leases, wait policies and cleanup races. Subscription disposal/overflow and throwing callbacks are contained; exact strings/numbers, bounded activity replay and protected settlement IDs survive native conversion, and NaN waits no longer panic ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Fixed macOS cleanup failing immediately on temporary unreaped process-group zombies while retaining bounded, confirmed cleanup.
- Herdr registration survives reload and session replacement without stale runners or child shutdown clearing parent ownership. Failed identity reports retry, background tasks prevent false idle, output-cap continuations remain working, and settled workflow failures retain attention without a false active wait.
- Navigation through `/tasks`, `/agents` and workflow graphs no longer creates false Herdr approval blocks. Answered prompts and acknowledged workflow blocks clear correctly even with slow observers; genuine pending approvals remain blocked.
- Concurrent inline approvals retain the surviving prompt and input focus. Task navigation does not cancel queued questionnaires, isolated cancellation removes loading placeholders, and background counts coexist with questions and MCP context on narrow terminals.
- Filled theme backgrounds survive nested resets and truncation, including completion cards. Workflow lists retain row caps and viewed-run anchors through clipping, live changes and resize.
- Resume trust is prepared before disposing the outgoing session and retained on failed preflight. Startup and isolated trust waits reach eligible lifecycle observers without loading untrusted extensions or replaying dialogs ([#2873](https://github.com/bastani-inc/atomic/issues/2873)).
- Internal-URL expansion rejects unsafe shell syntax while safely quoting bare resolved URLs; PowerShell apostrophes remain literal. Commands without resolved URLs are unchanged.
- Intercom excludes non-agent routing/prompt/tool connections without hiding busy agents or invalidating connected aliases. Roster updates synchronize with broker acceptance; targeted replies keep exact correlation, and delivery retries preserve FIFO order without duplicate cards ([#2895](https://github.com/bastani-inc/atomic/pull/2895)).
- Capped retry backoff with `retry.maxAgentDelayMs`, defaulting to 60 seconds, while preserving provider limits. Retry countdowns use Atomic's themed indicator ([#8826](https://github.com/earendil-works/pi/issues/8826)).
- Fixed Anthropic object-union schemas and Grok acceptance of the bundled bash command/wait schema while local validation still rejects mixed inputs. Extension registration rejects missing or nonobject schema containers ([#2190](https://github.com/bastani-inc/atomic/pull/2190) by [@elefthei](https://github.com/elefthei), [#3031](https://github.com/bastani-inc/atomic/issues/3031), [#9300](https://github.com/earendil-works/pi/issues/9300)).
- Updated patched Hono/js-yaml dependencies and provider SDKs; Google's consecutive tool-call limit returns an error. Extension model registries expose authenticated custom-provider streams, and Radius login chooses an available discovered model.
- Settings writes preserve inherited `.pi` provenance, concurrent primary edits and explicit Atomic overrides instead of copying inherited values into `.atomic` ([#2299](https://github.com/bastani-inc/atomic/issues/2299)).
- Direct steering/follow-up calls run extension input handlers before admission, preserving transformed text/images and RPC attribution. Tree navigation refuses active compaction/summarization without disturbing the current branch.
- Stage skill autocomplete reuses shared attachment without repeated checkpoints, ended recoverable blocks resume rather than return snapshot-only success, and cancelled stage-chat pause avoids unhandled rejection.
- Released unused alternate-folder Git watchers and timers without disturbing other chat footers ([#2926](https://github.com/bastani-inc/atomic/pull/2926)).
- Restored the Gondolin example after legacy grep API removal, keeping search on the host and guest-only searches in the routed shell ([#2482](https://github.com/bastani-inc/atomic/pull/2482)).

## [0.9.19-alpha.12] - 2026-09-13

### Fixed

- Grok accepts the bundled bash tool schema for command execution and task waits, while local validation still rejects mixed inputs ([#3031](https://github.com/bastani-inc/atomic/issues/3031)).

## [0.9.19-alpha.11] - 2026-09-13

### Fixed

- Repeated nested `ctx.workflow()` invocations no longer collide on Intercom route ownership during model fallback.

## [0.9.19-alpha.10] - 2026-09-13

### Added

- Extension widgets can opt into a height-capped fullscreen viewport with widget-local wheel scrolling, an overflow-only scrollbar, and scroll-position feedback in both in-process and isolated-engine sessions. Existing widgets retain their default behavior.
- The main-chat workflow list supports local wheel scrolling with an overflow-only slim scrollbar and configurable Alt+K/J shortcuts, labeled Option on macOS. Alt+PageUp/PageDown remain aliases, configured editor bindings take precedence, and the list keeps its row cap and run anchors through clipping and resize.

### Fixed

- Workflow model fallback cleans up the failed attempt's Intercom ownership before registering its replacement, preventing duplicate live-stage ownership warnings without weakening route protections ([#3020](https://github.com/bastani-inc/atomic/issues/3020)).

## [0.9.19-alpha.9] - 2026-09-12

### Breaking Changes

- Explicit Intercom `action: "reply"` selectors reject stale, unknown, empty, or sender-mismatched threads instead of falling back. Use `action: "pending"` and `replyTo: "<pending-ask-id>"` for unresolved questions.

### Changed

- Intercom agent lists put exact copyable session IDs and workflow paths first, with useful metadata and meaningful names secondary rather than redundant generated aliases.

### Fixed

- Intercom targeted replies now complete the requested ask when an unrelated message is active, rather than delivering a response with the wrong thread ID or refusing it.

## [0.9.19-alpha.7] - 2026-09-12

### Fixed

- Reduced CPU work in long `/tasks` live transcripts on Windows by reusing unchanged message rendering during streaming, without dropping history or delaying live updates.

## [0.9.19-alpha.6] - 2026-09-11

### Breaking Changes

- `write` no longer silently overwrites a file this session has never seen. Replacing an existing file now requires the session to have already observed exactly the content being replaced, checked under the same per-file mutation queue as the write. A session with no version of its own is refused with `no_prior_observation`; one whose recorded version no longer matches the file on disk is refused with `changed_since_observation` and told which line diverged, what it assumed was there, and what the file holds instead. Both carry the same `FILE_MUTATION_CONFLICT` code and requester identity as an `edit` conflict. Creating a new file, and overwriting one this session read, wrote, or edited, are unaffected ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- `WriteOperations` now requires a `readFile` member, and `writeFile` receives an optional third `WriteFileOptions` argument. `write` reads before every write to refuse generated files, to check that the session has observed what it is replacing, and to decide between creating and overwriting; routing that read through `WriteOperations` is what makes those checks see the filesystem a custom or remote implementation actually writes to, rather than local disk. `readFile` must report absence as `undefined` and reject for anything else, since a path that exists but cannot be read is not a free path. Existing `writeFile` implementations continue to typecheck and may ignore the new options argument, losing only exclusive-create semantics ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- Bundled `code_search` now requires `repoName` in `owner/repo` format alongside `query` and uses DeepWiki MCP for public-repository questions instead of Exa. Add the repository to existing calls. There is no Exa fallback; `web_search` is unchanged, and `maxTokens` remains a best-effort local output bound.

### Changed

- Shell guidance now favors the owner's normal observation budget over routine one-second yields and repeated short polls. Bash and PowerShell share this guidance; explicit budgets, background execution, and execution timeouts are unchanged.
- Bundled delegation guidance now favors keeping immediately blocking work local and overlapping independent tasks, while preserving specialist and explicitly requested delegation.

### Fixed

- Fixed continuously noisy engines growing interactive diagnostic history without limit. Recent diagnostics retain their order and duplicates without removing normal chat or status messages.
- Fixed damaged-PDF reads corrupting the fullscreen terminal with MuPDF diagnostics. Bounded diagnostics now appear in the TUI when interactive and use `console.log` otherwise; conversion failures also retain their diagnostic suffix ([#2964](https://github.com/bastani-inc/atomic/issues/2964)).
- Routed RPC engine stderr through bounded, deferred status messages instead of synchronous filesystem writes or raw terminal output, preserving Unicode characters split across output chunks. Interactive MuPDF and engine diagnostic display neutralizes terminal controls without changing stored diagnostic text. RPC stdout remains JSON-only, and child diagnostics reach the interactive host without changing engine health ([#2964](https://github.com/bastani-inc/atomic/issues/2964)).
- A rejected `edit` no longer hands back a snapshot tag that authorizes the same edit on retry. Building the rejection recorded the file's current content in the session's snapshot store and reported that tag in the error, so re-sending the identical edit with the reported tag was accepted even though the model had never read the changed file, silently overwriting whatever the other writer had just put there. The rejection now reports the tag without recording it, so the retry is rejected again and the model must re-read first. Drift recovery for tags the session did record is unchanged ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- An `edit` rejected because the file changed after the patch was prepared now reports which line diverged, what the edit assumed was there, what the file holds instead, and how large the target is, instead of a bare "content changed before write". A file deleted in that same window is reported as a missing target rather than a raw filesystem error, and no longer tells the model to re-read a file that is gone. A path that survives but stops being readable, because it was replaced by a directory, locked, or made inaccessible, is now reported as an unreadable target carrying the filesystem error code, instead of escaping as an untyped error. Rejections carry a stable `FILE_MUTATION_CONFLICT` code and the identity of the session, workflow stage, or subagent whose call was refused, so parallel workers editing one file are distinguishable ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- A `write` or hashline `edit` cancelled after its bytes had already reached disk no longer left the change unrecorded, which made the session's own file look like another agent's work on the next overwrite ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- `write` creating a file now claims the path exclusively (`O_EXCL`). A file that appears between `write` observing an absent path and its own write landing is reported as `target_exists`, describing what is there now, instead of being silently truncated ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- Standalone write tools retain their implicit observation store across `local://` writes, so overwriting a file the same tool just created no longer incorrectly fails with `no_prior_observation`.
- Preserved typed `edit` conflicts when parent-directory permissions change after patch preparation. The rejection retains the original target identity and filesystem error code instead of escaping as a raw permission error ([#2329](https://github.com/bastani-inc/atomic/issues/2329)).
- Fixed the Gondolin example failing to load after removal of the legacy grep tool API. It now registers only its supported file and shell overrides; the search tool remains on the host, and guest-only content searches use the routed shell ([#2482](https://github.com/bastani-inc/atomic/pull/2482)).

## [0.9.19-alpha.5] - 2026-09-11

### Changed

- Open Claude Design now starts with Claude Fable 5.1 at medium effort, followed by Copilot Fable 5.1 and Astra at medium effort, with the same Fable-first order on OpenRouter.
- Tuned bundled debugger, Goal, and Ralph reasoning efforts: debugger uses Astra/Fable at medium and Sol at high; Goal and Ralph reviewers use Astra/Sol at high; orchestration, Ralph research, and design use Fable at medium and Sol at high in their fallbacks. Ralph prompt refinement and other bundled agents retain their existing configurations.

## [0.9.19-alpha.4] - 2026-09-10

### Breaking Changes

- Workflow controls, completion, help, and status hints use `/workflow pause`, `/workflow quit`, and `/workflow resume`. The workflow tool supports run, stage, and individual durable-tool pause targets; `/workflow pause [run-id|--all]` controls runs. Workflow lifecycle control events report `action: "pause"` for pause requests.
- Renamed the bundled subagent `interrupt` action to `kill`. Migrate `subagent({ action: "interrupt", id })` to `subagent({ action: "kill", id })`, including calls using `runId`. The old action is no longer accepted. Kill terminally stops the child and cannot be resumed; follow-up work requires a fresh launch. Workflow controls use `/workflow pause`; parent cancellation behavior is unchanged.

### Added

- Added a model row to the `/workflow connect` graph node cards showing each stage's effective model and thinking level, including canonical fast model identity, with model-name truncation preserving the suffixes. Cards retain duration, status and dependencies in a six-row layout, reflect live fallbacks, and restore identity through durable resume ([#1859](https://github.com/bastani-inc/atomic/pull/1859) by [@sina85](https://github.com/sina85)).
- Added `{ action: "wait", id, budgetMs }` to Bash and PowerShell for observing existing asynchronous tasks without rerunning commands. The observation budget is optional. Waits retain output and terminal metadata, follow owner observation policy, and release on cancellation or incoming messages without stopping execution or extending task lifetime.
- Added the agent-callable `kill({ id })` tool for owned background bash and PowerShell tasks in main and workflow-stage chat. It cancels by task ID, preserves retained output and original outcomes, reports current cleanup state, and rejects other owners' tasks and subagents.
- Added exact per-model overrides through `compaction.modelOverrides` for `compaction.reserveTokens` and Atomic's `compaction.preserve_recent` message count. Each field falls back to ordinary settings and then built-in defaults, with non-negative safe-integer validation. Manual, automatic, and post-tool compaction use the active model's budgets while retaining verbatim line compaction.

### Fixed

- First-loaded TypeScript extensions share the live host's classes and singletons when native import falls back to transformation, avoiding duplicate host evaluation and slow startup.
- Fixed repeated yielded shell waits replaying the first output page instead of progressing through retained output while the task is still running ([#2972](https://github.com/bastani-inc/atomic/pull/2972)).
- Preserved an explicit subagent kill when parent cancellation arrives during execution-capacity waiting, and kept grouped Intercom cancellation status consistent when a killed child has parent-cancelled siblings.
- Embedded Postgres now starts on Windows administrative accounts. PostgreSQL refuses to run for a member of the Administrators or Power Users groups, so Atomic launches the retained server process with the same restricted access token `pg_ctl` uses, keeping exact-process shutdown semantics; non-administrative Windows accounts are unchanged. Postgres processes that exit during startup (including that administrator refusal on older builds) now fail fast with the actual server log instead of a readiness timeout.
- Preserved embedded Postgres startup logs on regular Windows accounts, honored Unicode environment overrides on administrative launches, and prevented Windows handle leaks across repeated launches.
- Preserved `PATH` lookup and relative executable paths for administrative Windows Postgres launches. Invalid launch inputs containing embedded NUL characters now fail before starting a process rather than using truncated paths, arguments, or environment values.
- Fixed custom Windows Postgres `.cmd` and `.bat` launchers failing with arguments on administrative accounts, including launcher paths containing spaces. Batch arguments retain their existing quoting and line-break rejection.
- Fixed explicit `cmd.exe` Postgres launchers and safe verbatim working directories on Windows administrative accounts. Concurrent Postgres launches no longer keep one another's log files open or expose them to unrelated commands starting at the same time.
- Incoming Intercom send and ask messages now act as a priority interrupt queue for working subagents and live workflow stages: the receiver's current model call or cancellable tool is cancelled immediately and the message is processed within the same task, session, and stage generation. Admission survives consumed preflight input and overlapping SDK interrupt turns. Completed tool side effects are never replayed, the original task prompt is not repeated, multiple arrivals stay in arrival order with duplicate suppression, and exact ask/reply correlation survives the cancelled turn. Explicit user abort, host stop, and terminal/closed receivers still win: late input never restarts finished work.
- Fixed a deadlock where an extension event hook awaiting an ordinary context-only message could wait behind an inbound Intercom delivery that was itself waiting for that event hook.
- Inbound delivery retries after a transient persistence failure keep their original arrival position and remain part of both child and workflow-stage settlement, and a card appended before a failed flush is completed on retry instead of being appended a second time.

## [0.9.19-alpha.3] - 2026-09-09

### Fixed

- Fixed `Failed to initialize class constructor` when npm-installed Node sessions create multiple task supervisors, preventing shell commands and subagent launches from failing during task-host initialization.
- Fixed foreground subagent launches and explicit waits blocking parent user steering and incoming Intercom asks/sends. Admitted messages now release the waiting parent's observations in main and workflow-stage chat without cancelling children or affecting other owners.
- Fixed PowerShell internal-URL path quoting so apostrophes and smart single quotes in resolved paths remain literal instead of allowing injected commands. Bash quoting, deliberate shell commands, and balanced command prefixes are unchanged.
- Capped agent retry backoff with `retry.maxAgentDelayMs` (60 seconds by default), preserving independent provider retry limits ([#8826](https://github.com/earendil-works/pi/issues/8826)).
- Rejected extension tools with missing or non-object parameter schema containers during registration instead of breaking provider requests ([#9300](https://github.com/earendil-works/pi/issues/9300)).
- Workflow-stage pause now blocks new task launches, cancels active and admitted queued agents and commands, and waits for in-flight command admission and resource cleanup. Main-chat and sibling tasks stay unaffected; queued user and Intercom messages survive, and resume permits fresh work without restarting cancelled executions.
- Fixed npm-installed Node startup failing with a missing upstream AI package after the retry-policy update. Core retry imports now use Atomic's declared AI dependency.
- Fixed an unhandled rejection when session cancellation interrupts an in-flight stage-chat pause, while preserving pause error reporting and rejection for callers awaiting completion.
- Fixed Windows extension reloads re-evaluating Atomic's host modules, avoiding duplicate host classes and long reload delays while preserving edits to extension dependencies. The supported `@earendil-works/pi-coding-agent` import also shares host identity with `@bastani/atomic` after reload.

## [0.9.19-alpha.2] - 2026-09-08

### Added

- Added host-side workflow activity observation with ordered snapshots, publisher epochs, bounded observer queues and diagnostics, plus workflow_lifecycle, workflow_activity_changed, workflow_stage_completed, and workflow_heartbeat extension hooks typed as `WorkflowLifecycleEvent`, `WorkflowActivityChangedEvent`, `WorkflowStageCompletedEvent`, and `WorkflowHeartbeatEvent`. Initialization-time publications are retained until runner binding. Publisher and observer leases are fenced on reload, including pending hook handlers when an earlier handler is awaiting. Workflow runtime publication is a separate integration ([#2891](https://github.com/bastani-inc/atomic/issues/2891)).
- Added the built-in Herdr reporter for eligible interactive panes, combining settled agent activity, approval prompts, and observed workflow roots under custom:atomic. Pane ownership and ordered sequences survive in-process reload/replacement; direct CLI delivery is serialized, coalesced, and bounded by a five-second timeout. Supports herdr.enabled opt-out, loaded reporter conflict deferral, and one-time parent session identity reporting. The full activity path is integration-tested against a fake Herdr CLI that records argv, proving the ordered working, blocked, working, idle reports and strictly increasing sequence numbers for a real workflow with a tool-only execution and a human-input prompt. The Herdr documentation gains a compatibility matrix for the tested minimum Herdr 0.8.2 / protocol 20: custom-source authority, equal/older sequence handling, the sequence high-water mark surviving release, release requiring a sequence, idle-after-working surfacing as done, unretained message and session identity for custom sources, and the fail-safe behaviour for missing environment variables, a stopped server, or an older CLI ([#2891](https://github.com/bastani-inc/atomic/issues/2891)).
- Editable workflow stage chats now discover source-qualified skills from their own session catalog and expand `/skill:` submissions once through stage admission, preserving Enter/Ctrl+F delivery and literal human-input answers. Skill diagnostics remain in the stage chat; blocked, archived, and replayed stages stay read-only.
- Added a searchable `/agents` catalog with source grouping and agent configuration details.
- Added per-call `bash.wait` selection for immediate background or foreground-first observation with an optional millisecond budget. Omitted waits keep owner-configured automatic backgrounding, and execution timeouts remain independent. Unsupported background requests fail before launching a command.
- Added the SDK-only owner-bound task foundation: opaque host/task/wait capabilities, one execution per admitted attempt, observation-only yields, independent cleanup acknowledgement, and snapshot-reconciled subscriptions. Existing CLI, workflow and subagent runners are unchanged; output storage and real-runner integration remain later work ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added trusted command start/input/output doors and an owner-aware bash/PTY execution seam. Commands remain alive after the 10000 ms foreground observation budget and are reaped with their owner; stdin distinguishes empty bytes from EOF and refuses backpressure before admission ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added a narrow trusted-host agent task adapter with per-launch runner factories, owner-scoped observation and cancellation, and workflow generation ownership that survives fallback session replacement. Real subagent producers and completion delivery remain unintegrated ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added owner-bound agent task hosts to runtime-created session contexts, single-subagent observation waits, and terminal completion intents delivered nonvisually through existing model admission with stable retry identity ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added an owner-bound task projection and shared compact task rows for main and attached stage chats, with stable live anchors after tool/turn completion, snapshot reattachment, bounded activity previews and a compact background-task footer. Host adapters opt in by binding their session's task store; producer migration remains separate ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added SDK task transcript references bound by the admitted runner, with task-scoped paging, original message/tool-call identities, and explicit unavailable history. Hidden reasoning is excluded; production subagent runners bind child history for the shared host inspector ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added a shared focused task inspector for owner-store hosts: stable grouped selection, retained transcript rendering, foreground waits, confirmed cancellation, and explicit stdin focus without replacing composer drafts ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Added `reason: "project_trust"` to `UIPromptStartEvent` and `UIPromptEndEvent` for the built-in `/trust` selector in both interactive modes, so status integrations can observe the wait. Isolated mode retains paired notifications until the current engine binds, keeping separate completed dialogs distinct without delaying the trust decision. ([#2873](https://github.com/bastani-inc/atomic/issues/2873))
- Added supervised native Windows ConPTY shell execution with Job Object containment before resume, confirmed cleanup, and no unsupervised spawn fallback.
- Added optional direct-executable `shell: { program, args }` and `inheritEnv` controls to the command SDK, including operation replay identity.

### Changed

- Built-in read, edit, write and shell tools now prefer strict JSON-schema sampling by default, falling back on providers without strict support. Atomic's other experimental tool hints retain their existing opt-in gate.
- `/tasks` now opens as a compact inline picker like `/workflow connect`, with fullscreen detail, transcript, input, and confirmation pages. Returning from detail preserves selection.
- Foreground and background subagent rows, task lists, and completion cards now display resolved model and reasoning settings, retaining them after completion.
- `/tasks` in an attached stage chat is now a local inspection command, never a model message, including while an Escape interruption settles. Hosts without a task inspector report that it is unavailable.
- Task lists and subagent detail views now use grouped counts, semantic status styling, pinned status/metrics/actions, recent tool activity, bounded response and shell-output previews, and narrow-terminal layouts. Finished tasks remain discoverable through `/tasks`; background bash receipts distinguish observation time from execution completion.
- Default guidance now routes all user questions, including confirmations and approvals, through `ask_user_question` or an equivalent available question tool rather than prose-only prompts. Sessions without a usable question tool continue autonomously using best judgment.
- Model-choice guidance now consults the eval docs and Artificial Analysis benchmark charts. Automation guidance uses PyAutoGUI for desktop CUA, playwright-cli for browsers, and prefers Herdr across macOS, Linux and Windows with installation guidance and tmux/psmux fallback.
- Native Windows owner-bound `bash` and `powershell` calls now automatically yield after the owner's command observation budget (normally 10 seconds). Explicit per-call budgets override it; execution timeouts and the 30-second explicit foreground subagent default remain separate. PowerShell uses encoded command transport while retaining original command descriptions. Owned legacy Windows WSL `bash.exe` stdin transport remains refused; Atomic inside WSL stays on POSIX/Bash.
- On native Windows, Suspend opens a PowerShell subshell and restores the same session on exit instead of freezing the process; background tasks continue running.

### Fixed

- Fixed Herdr registration disappearing during resource reload or in-process session replacement while workflow activity is recovering or unavailable. Reporter handoffs now preserve registration and release it only on quit, without requiring another prompt.
- Fixed Herdr turning red again after every response for an already-observed workflow block. Interactive messages now acknowledge existing pane attention without resuming workflows; new blocks and open approval prompts still report blocked.
- Prevented internal-URL shell expansion from turning quoted paths into executable syntax. Expansion now rejects commands containing quotes, substitutions, escapes, heredocs or other non-plain syntax; bare URLs remain safely quoted, and commands without resolved URLs are unchanged.
- Fixed Herdr reporting idle while standalone subagents or background shell tasks are running. The parent reporter now observes owner-scoped task activity and reattaches on reload, retaining working status until all tasks settle without granting children pane ownership.
- Open subagent transcripts now refresh from live session events, including streaming text and partial/final tool results. Shell transcript reads coalesce pending state updates instead of requiring the view to be reopened.
- Discarded stale command detail results and read errors after task selection, focus, or inspector lifetime changes, preventing another task's output from appearing in the current view ([#2908](https://github.com/bastani-inc/atomic/pull/2908)).
- Connected isolated interactive sessions to the engine's compact background-task indicator below the prompt box and command-opened `/tasks` inspector instead of opening an empty host-local task owner. Added `/tasks` autocomplete and live subagent activity, tool/token counts, and response previews inside task details.
- Connected top-level POSIX model bash executions to their task owner so long-running shells appear in task inspection after their observation yields. Preserved cleared session environment variables in the supervised shell path; native Windows and child-agent bash retain their existing execution path.
- Replaced raw JSON task-completion content with readable names, outcomes, errors, and available response excerpts. Background completions now show a visible notification in main and owning workflow-stage chat, using the existing persisted delivery identity rather than relying on a model reply.
- Connected native shell settlement to completion delivery using retained receipts and snapshot reconciliation. Background shell cards show bounded output and exit status; foreground-only shells do not trigger duplicate notifications or model turns. Initial bash observation now designates the host so yielded commands update background counts.
- Preserved pending transcript navigation and historical pages across background updates, and honored configured task keys before fallback navigation and stop shortcuts.
- Restricted `/tasks` to background work and its retained results, and below-prompt counts to active background work. Workflow-stage chat now uses the same footer-only status and completion notifications as main chat. Transcript inspection has a separate bounded viewport with position and paging controls instead of nested chat boxes.
- Kept active task status below MCP and above workflow background cards, including isolated sessions regardless of widget arrival order. Workflow-stage `/tasks` now appears in slash suggestions; selected skill suggestions use main-chat-style terminal-default backgrounds and accent text. Active counts and `/tasks` remain visible at narrow widths.
- Pausing main or workflow-stage chat keeps background agents and shells running with the same identities. Closing a stage generation cancels only that owner's remaining work.
- Remounted active task rows after session transcript replacement and cleared prior-session task rows and footer state before a replacement owner store binds ([#2907](https://github.com/bastani-inc/atomic/pull/2907)).
- Honored owner command wait budgets and `until-settled` in supervised bash/PTY execution, disclosed retained output gaps, and bounded output pages to 1 MiB. Supervised file-spool writes now share a hard disk cap rather than overshooting between polls ([#2905](https://github.com/bastani-inc/atomic/pull/2905)).
- Stopped task subscription event delivery when a reconciliation callback disposes observation, including events retained by the active drain ([#2902](https://github.com/bastani-inc/atomic/pull/2902)).
- Failed `/tasks` inspection now displays a diagnostic and preserves the input for retry instead of leaving an unhandled editor submission rejection.
- Workflow skill autocomplete no longer reattaches and checkpoints the stage for every completion request. Concurrent lazy discovery requests share one attachment.
- Prepare resume trust before disposing the outgoing session, preserve it on failed preflight, and allow pending prompt observers up to 1,000 ms to settle without delaying dialog display or answers. Isolated resume uses child-local trust UI and forwards missing-directory overrides without serializing callbacks. ([#2873](https://github.com/bastani-inc/atomic/issues/2873))
- Startup trust waits now reach existing prompt lifecycle handlers with a live session context, including isolated-engine and borrowed-source dialogs. Only trust-safe extensions load before authorization; approval retains their session and factories, starts newly authorized extensions once, and does not replay earlier waits. ([#2873](https://github.com/bastani-inc/atomic/issues/2873))
- Intercom now hides internal workflow routing/control connections and refuses messages to non-agent recipients, including model-less `ctx.ui` prompts and `ctx.tool` nodes, without hiding agents busy in tools or awaiting human input. Connected agent aliases remain reachable after pending capability changes or stage completion, even with same-name non-agent nodes.
- Preserved duplicate-agent routing beside same-name prompt/tool nodes, rejected malformed Intercom recipient purposes without breaking older hosts, and synchronized workflow roster-update completion with broker processing ([#2895](https://github.com/bastani-inc/atomic/pull/2895)).
- Fixed Herdr parent session identity being dropped after a failed first report. Later activity includes the identity until CLI delivery succeeds, while shutdown still attempts release after failed delivery ([#2891](https://github.com/bastani-inc/atomic/issues/2891)).
- Fixed child shutdown clearing the parent's Herdr indicator when a resource loader shares the reporter across sessions. Only the bound session can change activity or release the pane, including while acquisition waits for a predecessor. Owning shutdown permits an eligible successor to reuse the already-loaded reporter, with serialized retirement before the new identity reports and stale predecessor events fenced; genuine quit still releases the pane.
- Fixed retiring reload runners releasing the new Herdr reporter when both share one `SessionManager`. Transaction commit retains the started runner, and stale shutdown, activity, approval, and start events cannot mutate or reclaim its binding. Candidate reporting is staged until preparation succeeds, so a rejected reload preserves the live reporter's approval state, continued work, and final quit cleanup ([#2925](https://github.com/bastani-inc/atomic/pull/2925)).
- Corrected S1 SDK waits, foregrounding and cancellation to return promised Results, restored watch cursors and opaque subscription leases, applied configured agent wait budgets, and fixed cleanup/disposal races including external owner closure of settled tasks ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Preserved S1 cancellation attention consistently with native snapshots, made subscription overflow/reset observable through iterator completion without a required callback, and safely recorded non-Error runner/setup/cleanup rejections without blocking confirmed cleanup ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Contained arbitrary S1 reconciliation callback exceptions, including unprintable values, as safe `subscription.failure` diagnostics without interrupting native wake delivery or fallback polling ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Preserved exact JavaScript strings in S1 snapshots, titles and report replay, including isolated UTF-16 surrogates and embedded NUL, without changing the SDK's ordinary string types ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Bounded S1 activity replay to the latest 256 accepted report identities per task without retaining their full payload history. Identical retained reports remain duplicates, conflicts remain refused, and evicted IDs are fresh reports; terminal receipts never expire while the task record exists ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Prevented accepted S1 activity IDs such as `runner-outcome` from blocking runner settlement, independent cleanup and owner closure. Internal terminal IDs are allocated atomically without reserving caller IDs or changing report conflict/replay and cancellation rules ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Prevented accepted NaN S1 wait budgets, including owner-configured agent budgets, from panicking native scheduling. Observations remain releasable by yield, settlement, disposal and owner closure without changing other budgets or per-call precedence ([#2884](https://github.com/bastani-inc/atomic/pull/2884)).
- Restored task completion outboxes now retry unacknowledged terminal intents on initialization instead of waiting for another task to settle, retaining completion identity and current admission checks ([#2906](https://github.com/bastani-inc/atomic/pull/2906)).
- Fixed unrelated settings changes copying inherited `.pi` fields into primary `.atomic` settings and changing their resource provenance. Scoped writes preserve newer primary-file edits, explicit Atomic overrides (including empty arrays), and inherited builtin-resource precedence ([#2299](https://github.com/bastani-inc/atomic/issues/2299)).
- Automatic and summary retry countdowns now use Atomic's theme-aware `∀` indicator instead of a braille spinner, preserving cancellation and countdown updates.
- Settled tasks now leave the compact footer immediately while remaining inspectable in `/tasks` and completion cards, so retained failures do not keep the live indicator red.
- Opening `/tasks` in isolated mode no longer emits an approval prompt or incorrectly changes Herdr to blocked. Genuine approval reporting remains unchanged.
- Filled theme backgrounds now survive nested and truncation resets, keeping long task-completion titles, previews, and expand hints shaded through the last column.
- Owner-bound task notifications now order queued same-child Intercom messages before completion through the delivery outbox. Failed delivery keeps the terminal intent retryable without changing the execution outcome, replaying successful earlier messages, or relaunching the task.
- Fixed `/workflow connect` falsely marking Herdr blocked, including after hiding the graph. Custom UI supports `purpose: "navigation"` without suppressing genuine approval events.
- Parallel subagent communication now preserves active and queued siblings while only a blocking requester waits for its correlated reply. Foreground observation yielding no longer discards queued work or changes execution concurrency; cancellation and owner cleanup remain explicit.
- Fixed leaked parallel subagent worktrees and branches when a queued child is cancelled before starting, including session/workflow-stage owner closure after observation yields. Live siblings retain their worktrees until execution finishes.
- Fixed Anthropic Messages tool requests advertising root object-union parameter schemas without fields by projecting their branch fields into Anthropic-compatible object schemas ([#2190](https://github.com/bastani-inc/atomic/pull/2190) by [@elefthei](https://github.com/elefthei)).
- Matched main and workflow-stage `/tasks` compact pickers, retaining model, reasoning, session cwd/branch, and MCP context even while the owner streams. Task navigation no longer produces a false main-chat input notice; genuine pending prompts keep their routing and clear the notice when answered or the graph is hidden.
- Fixed concurrent inline approvals hiding one another when completed or canceled out of order. The surviving prompt or task picker stays visible and regains input after leaving the workflow graph, in both local and isolated interactive modes. Canceled isolated approvals no longer leave a dead loading component behind.
- Released alternate-folder Git branch watchers, polling and retry timers when their last chat footer closes or changes folders, while preserving concurrent main/stage updates and ignoring stale refreshes after replacement ([#2926](https://github.com/bastani-inc/atomic/pull/2926)).
- Finished workflow failures now let Herdr return to idle while retaining attention and the failed outcome. Pending decisions, budget approvals, and live execution keep their existing states.
- Fixed resume requests for ended recoverable workflow blocks falling through to a snapshot-only response. Non-resumable targets and unchanged blocked snapshots no longer report success.
- Fixed macOS background-command cleanup failing immediately when a terminated process group temporarily contains an unreaped zombie. Cleanup remains bounded and still requires confirmed group disappearance.
- Updated the locked Hono and js-yaml dependencies to patched releases and refreshed provider SDKs against upstream Pi. Google's consecutive tool-call limit now returns an error stop reason.
- Extension model registries now expose authenticated streaming calls through their configured custom providers, preserving runtime provider ownership ([#9272](https://github.com/earendil-works/pi/pull/9272)).
- Tree navigation rejects active compaction or branch summarization before replacing the current operation's UI, leaving the active branch intact ([#9179](https://github.com/earendil-works/pi/pull/9179)).
- Radius login now prefers the discovered balanced model and falls back to the account's first available model after catalog refresh.
- Direct steering and follow-up calls now run extension input handlers before queue admission, preserving transformed text/images and RPC input-source attribution while consuming handled inputs.
- Fixed subagents stopped with `x` in `/tasks` failing to notify the parent chat. Running and queued cancellations now deliver their confirmed terminal receipt once, without requiring a final child response, duplicating completion notices, or overwriting an outcome that already settled.
- Fixed premature Herdr idle reports between repeated output-cap continuations by keeping the original prompt active until the complete continuation chain settles, including quiet provider waits. No heartbeat or inactivity timer is required.
- Fixed false Herdr blocked status after an answered prompt when an earlier notification observer is slow, and while browsing the read-only `/agents` catalog. Genuine user decisions still report blocked.
- Fixed Escape in `/tasks` and subagent transcript navigation cancelling a pending `ask_user_question`. Questionnaires now wait behind navigation and resume with their selection intact; ordinary questionnaire cancellation is unchanged.

## [0.9.19-alpha.1] - 2026-09-06

### Fixed

- Restored offline embedded PostgreSQL discovery in compiled archives and bundled target-selected runtimes for all eight supported archive/npm platforms, preserving scriptless installs and existing v18 clusters. Windows ARM64 continues to require Windows 11 x64 emulation.

## [0.9.18] - 2026-09-05

Cumulative release of the `0.9.18-alpha.1` through `0.9.18-alpha.7` prereleases. Per-change details remain in the unchanged prerelease sections below.

### Breaking Changes

- Replaced `/fast` with selectable canonical `-fast` model IDs. Removed its selector, `codexFastMode.chat`/`codexFastMode.workflow` settings, `ATOMIC_CODEX_FAST_MODE`, scope inheritance, toggle-state exports, settings-manager methods, and the environment-sourced settings-override layer without a compatibility shim. Select a fast model explicitly; effective settings now merge global then project settings.
- Fast-routing APIs now live in `core/fast-model-routing.ts` and `core/fast-model-routing-transport.ts`, with package-root exports retained for the replacement APIs. `usesChatGptCodexTransport` and `usesFirstPartyCodexRouting` remain; `withCodexFastRouteHeaders` derives identity from route metadata rather than an enabled flag.
- Cloudflare AI Gateway binding transport requires `binding.fetch()` and no longer falls back to `gateway(id).run(...)`.
- `WorkflowPendingStageDelivery` now requires `fail(reason: Error): void` so delivery owners can settle exhausted recovery instead of leaving stages parked forever.
- Removed the experimental remote-session lease API, transcript projection helpers, and harness factory. `@bastani/atomic/client` now re-exports the service-addressed `@earendil-works/pi-client` API.

### Added

- Added explicit fast model variants to `/model`, `--list-models`, workflow catalogs, scoped lists, session restore, fallback candidates, usage, and attempt records. Thinking suffixes remain supported. OpenAI and Codex variants send the base upstream ID with priority tier; Copilot exposes only account-advertised fast IDs and sends them without an OpenAI tier.
- Added fast-specific `modelOverrides`, derivation APIs, `ModelRuntime.getFastModelVariantDiagnostics()`, and `getWarning()`. Existing provider, custom, or extension-owned IDs win collisions and produce actionable startup/catalog warnings. Route metadata, not a name suffix, grants fast behavior.
- Added GPT-6-Astra across OpenAI, Codex, Bedrock, OpenRouter, and Vercel, including derived first-party fast variants, provider-specific pricing, long-context tiers, and provisional account-gated Copilot support. Added Gemini 3.8 Flash, Claude Fable 5.1, Copilot Claude Fable 5/5.1, and Baseten GLM-5.3-Fast with provider-owned metadata and availability.
- Embedded PostgreSQL now supports offline durable storage on Linux musl x64/ARM64 and Windows ARM64. Windows ARM64 uses PostgreSQL x64 through Windows 11 emulation; installs and standalone archives carry only the matching runtime.
- Added `supportsMidConvoEffort` and `compat.vllmPriority` custom-model settings, and transcript notices for dropped Anthropic thinking when cache-miss notices are enabled.
- Added a clickable fullscreen `Jump to latest message` overlay with the configured shortcut. Holding Alt makes fullscreen mouse-wheel scrolling move five times as far ([upstream #9166](https://github.com/earendil-works/pi/pull/9166)).
- Documented Zed terminal keybindings for Kitty-protocol modified keys ([#8828](https://github.com/earendil-works/pi/pull/8828)).

### Changed

- Updated Pi runtime dependencies to 0.85.1, preserving Atomic startup, client API, and footer watchers. Fullscreen search uses the cached index and visible-match highlighting introduced in Pi 0.85.0.
- Startup paints the themed identity and focused editor before isolated-engine readiness. First submissions wait for optional resources, Escape cancels a waiting submission, and `/reload` retries extension failures without publishing failed candidates' host-managed state.
- Compiled/bundled builds reuse native-imported builtin extension factories across reloads while editable extensions and workflows retain content-hash invalidation. Standalone builds use syntax-minified shared sidecars and bytecode launchers, including Windows.
- Interactive model, thinking, cycling, and scoped-list selections save as startup defaults immediately. Selectors keep active choices marked while browsing; scoped lists use consistent toggles and strike through unavailable models. Removed Ctrl+S save-default UI.
- The working indicator reads `Working`, preserving Atomic's animated status row. The centered jump overlay no longer consumes a transcript row, and workflow stage chat uses matching copy and shortcuts.
- `ModelRuntime` enforces first-party Codex routing for standalone stream/complete calls too. Fast derivation excludes unsupported adapters and extension-owned transports, Copilot restore requires account entitlement and a base catalog model, and all provider catalog accessors agree on derived variants. Labels show the canonical model ID without a redundant fast badge.
- Claude on Anthropic and Bedrock accepts PDF input through the bundled AI library, with visible placeholders elsewhere. Interactive sessions do not yet produce document blocks; Bedrock requires citations for full visual understanding.
- Updated hashline edit guidance with worked examples, rejected-shape warnings, and a specification of native block resolution. Model-selection references record the August 26 DeepSWE snapshot, corrected prices and Pareto choices, and Fable 5.1's unmeasured status. Compaction docs explain why `preserve_recent` preserves text and tool exchanges but resets signed reasoning.
- Capable Responses models use `prompt_cache_options.ttl: "30m"` for long retention; older models retain the 24-hour legacy control.

### Fixed

- Fixed Windows x64 and ARM64 archives crashing before startup in `0.9.18-alpha.1` ([#2781](https://github.com/bastani-inc/atomic/pull/2781)).
- Restored startup resource listings, source-path expansion, slash-prefixed prompt labels, isolated-child extension inventory, deterministic collision labels, overlap warnings, and consistent hidden-resource handling across platforms.
- Fixed in-memory forks during running tools, lost compaction boundaries after forks, session import filename collisions, RPC aborts not cancelling manual compaction, and concurrent `/share` exports overwriting temporary files.
- Builtin tools resolve relative paths against an extension's `ctx.cwd`, including matching search hashline tags. Write confirmations no longer mislabel UTF-16 counts as bytes or discard user prose merely because it begins with `Successfully wrote to`.
- Fixed Claude Fable thinking-level routing on Copilot, Fireworks GLM endpoint routing, Fable 5.1 prefix/model-switch recovery, exact reasoning-byte restoration, unsupported sampling fields and forced tool choices, fallback replay and serving-model billing, and persisted thinking-drop notices on resume. Custom Responses gateways can disable `max_output_tokens`.
- Fixed managed fd/rg downloads behind GitHub API quotas, selected static musl archives on Linux x64/ARM64, and pinned darwin/x64 fd to 10.3.0. Fixed proxied plain-HTTP requests hanging after tool calls and signal-killed subprocesses appearing successful.
- Fixed JPEG EXIF orientation detection, skills disappearing when bash exists without read, and model-refresh errors missing their status spacing. Jump-overlay clicks preserve other mouse reports in the same input chunk.
- Raised branch-summary output to 4096 tokens, clamped to model limits, to avoid reasoning exhausting the previous cap ([#8845](https://github.com/earendil-works/pi/issues/8845)).
- Resumed workflows cannot report completion or start replacement work while skipping their exact unfinished durable tool. Completed work remains cached and cancellation retains precedence.

### Removed

- Removed `/atomic`. Use `/workflow list`, `/changelog`, and `/hotkeys` instead.
- Removed Ctrl+X copy-message/selection to avoid workflow-navigation conflicts. `/copy` and automatic mouse-selection copying remain.

## [0.9.18-alpha.7] - 2026-09-05

### Added

- Fullscreen mouse-wheel scrolling moves five times as far while Alt is held, through the Pi TUI 0.85.1 update ([upstream #9166](https://github.com/earendil-works/pi/pull/9166)).

### Changed

- Updated Pi runtime dependencies to 0.85.1, preserving Atomic's CLI startup, published client API, and footer watcher behavior.

## [0.9.18-alpha.6] - 2026-09-04

### Breaking Changes

- Replaced the `/fast` toggle with explicit selectable fast model identity. `/fast`, its selector UI, the `codexFastMode.chat`/`codexFastMode.workflow` settings, the `ATOMIC_CODEX_FAST_MODE` environment variable, chat-versus-workflow scope inheritance, and every public export tied to that toggle state are removed with no compatibility shim or migration. Select fast inference by choosing a fast model.
- Removed the `ENV_CODEX_FAST_MODE`, `CODEX_FAST_MODE_SERVICE_TIER`, `CodexFastModeResolvedSettings`, `CodexFastModeScope`, `formatCodexFastModeModelLabel`, `getCodexFastModeScope`, `hasSupportedCodexFastModeModel`, `isCodexFastModeCandidateModelId`, `isCodexFastModeEnabledForScope`, `isCodexFastModeSupportedModel`, `isCodexFastModeSupportedProvider`, `shouldApplyCodexFastMode`, `shouldApplyCodexFastModeForScope`, `withCodexFastModeHeaders`, `CODEX_FAST_MODE_ORIGINATOR`, and `CODEX_FAST_MODE_ROUTING_HEADER` package exports, and the `getCodexFastModeSettings`/`setCodexFastModeSettings` settings-manager methods. `usesChatGptCodexTransport` and `usesFirstPartyCodexRouting` remain, now exported from `core/fast-model-routing.ts` alongside `getModelFastRoute`, `resolveUpstreamModelId`, `withFastRouteStreamOptions`, and `withCodexFastRouteHeaders`; `CODEX_FAST_ROUTE_ORIGINATOR` and `CODEX_FAST_ROUTE_HEADER` come from `core/fast-model-routing-transport.ts`. All of them remain importable from the package root.
- Removed the environment-sourced runtime settings-override layer, which existed only to inherit the fast-mode toggle into child sessions. Effective settings are now the global-then-project merge.
- Cloudflare AI Gateway binding transport no longer falls back to `gateway(id).run(...)`. `createGatewayBindingFetch` requires `binding.fetch()`.
- `WorkflowPendingStageDelivery`, exported from the package root, now requires a `fail(reason: Error): void` member. A delivery owner that has run out of recovery calls it to settle the stage's pending delivery terminally; `ready()` has no timeout, so a delivery nobody can settle leaves its stage parked forever. Implementers of the interface must add `fail(reason)`.
- Removed the experimental remote-session lease API, transcript projection helpers, and harness factory after Pi 0.85 replaced their underlying contracts. `@bastani/atomic/client` now re-exports the service-addressed `@earendil-works/pi-client` API.

### Added

- Embedded PostgreSQL now provisions durable DBOS storage offline at runtime on Linux musl x64/ARM64 and Windows ARM64. npm installs receive only the matching runtime through the platform-specific native package, standalone archives carry only their target runtime, and Windows ARM64 deliberately runs PostgreSQL x64 through Windows 11 x64 emulation rather than claiming native ARM64 support.
- Added `supportsMidConvoEffort` to custom Anthropic Messages model compatibility settings.
- Added transcript notices for Anthropic thinking blocks dropped during provider recovery when cache miss notices are enabled.
- `models.json` now accepts `compat.vllmPriority` on `openai-completions` models. Atomic sends it as the top-level `priority` request field, which vLLM uses to order queued requests when it runs with `--scheduling-policy priority`; lower values are handled earlier and the server default is `0`. Set it on a background or batch model so its long prefills queue behind interactive sessions ([#9004](https://github.com/earendil-works/pi/pull/9004)).
- Eligible providers now publish a second selectable model whose canonical ID is the base model ID plus `-fast`, for example `openai-codex/gpt-5.6-sol-fast`. It appears in `/model`, `atomic --list-models`, workflow model catalogs, and scoped-model lists next to its normal sibling, is persisted and restored by that exact ID, and accepts a thinking suffix (`openai-codex/gpt-5.6-sol-fast:medium`). Normal and fast IDs stay distinct fallback candidates, usage rows, and model-attempt records.
- First-party OpenAI and OpenAI Codex models route fast requests by sending the **base** upstream model ID plus the fixed `service_tier: priority`, preserving the existing first-party Codex `originator: codex_cli_rs` and `x-codex-routing-hint` HTTP/SSE and WebSocket contract. GitHub Copilot exposes only the fast sibling IDs its OAuth account catalog advertises when the corresponding base catalog model exists, and sends those real suffixed IDs with no OpenAI service-tier field. Renamed providers, proxies, Azure OpenAI, OpenRouter, and generic OpenAI-compatible providers publish no synthetic fast variants.
- A `models.json` `modelOverrides` entry keyed on a derived `-fast` model ID now applies to that derived entry, after derivation. Overriding the base model still flows through by inheritance; a fast-specific override wins over the inherited value, and the entry's routing metadata survives the override.
- New `core/fast-model-variants.ts` exports (`deriveFastModelVariants`, `withFastModelVariants`, `fastModelId`, `usesOpenAIFastServiceTier`, `isNativeFastRouteApi`, `copilotAdvertisedFastModelIds`, `FAST_MODEL_ID_SUFFIX`, `FAST_MODEL_SERVICE_TIER`, and the `FastModelVariantDiagnostic`/`FastModelVariantDerivation`/`FastModelVariantsOptions` types), plus `ModelRuntime.getFastModelVariantDiagnostics()` and `ModelRuntime.getWarning()`.
- A provider, `models.json` custom model, or extension that already owns an exact `-fast` model ID wins: Atomic keeps that model exactly as declared, suppresses the derived duplicate, and reports an actionable warning in the interactive startup notices and from `--list-models`. The interactive notice renders on every launch, and is re-read after deferred extension loading so a collision an extension provider introduces is reported too. Fast behavior comes only from explicit route metadata, never from the `-fast` suffix.
- Added a clickable "Jump to latest message" label with the `tui.altScreen.bottom` shortcut to the fullscreen transcript while it is scrolled up ([#9080](https://github.com/earendil-works/pi/pull/9080) by [@rwachtler](https://github.com/rwachtler)).
- Gemini 3.8 Flash is now selectable in Atomic on Google (`google/gemini-3.8-flash`), Google Vertex, GitHub Copilot, opencode zen, OpenRouter (`openrouter/google/gemini-3.8-flash` and its `:batch` variant), and the Vercel AI Gateway. Provider metadata follows the upstream catalogs: Google, Google Vertex, opencode zen, and OpenRouter currently advertise a 1,048,576-token context window and 65,536 maximum output tokens; models.dev advertises 1,000,000 and 64,000 for GitHub Copilot; the Vercel AI Gateway advertises 1,000,000 and 65,536. Google, Google Vertex, opencode zen, OpenRouter, and GitHub Copilot offer low, medium, and high; the Vercel AI Gateway publishes no per-model thinking levels, so its entry also offers off and minimal. Run `--list-models` for the current catalog and the models your account can access ([#9076](https://github.com/earendil-works/pi/issues/9076)).
- Claude Fable 5 and Claude Fable 5.1 are now present in the generated GitHub Copilot catalog with models.dev limits, pricing, and reasoning metadata. Copilot account availability remains gated by the authenticated model picker.
- Baseten's models.dev-backed catalog now includes `zai-org/GLM-5.3-Fast` with the limits, pricing, inputs, and reasoning levels models.dev advertises.
- GPT-6-Astra is now selectable through OpenAI, OpenAI Codex, Amazon Bedrock, OpenRouter, and the Vercel AI Gateway. Atomic derives distinct `openai/gpt-6-astra-fast` and `openai-codex/gpt-6-astra-fast` identities through the existing fast-model catalog overlay. The Codex fast identity sends upstream model `gpt-6-astra` with `service_tier: priority` while preserving the `codex_cli_rs` originator, the `model=gpt-6-astra;tier=priority` routing hint, session restore identity, usage attribution, and 2× request-time price accounting. Bedrock publishes the direct, global, and US IDs unchanged and sends `low` through `max` reasoning effort. The current dynamic OpenRouter catalog contributes `openai/gpt-6-astra` and `openai/gpt-6-astra-pro`; Vercel contributes `openai/gpt-6-astra` and its own route-less `openai/gpt-6-astra-fast`. Astra accepts text and image input, supports 128,000 output tokens, and retains each provider's long-context pricing above 272,000 aggregate input tokens.
- Added provisional GitHub Copilot Astra support. `github-copilot/gpt-6-astra-fast` is derived only when the OAuth account catalog advertises that exact ID, and sends the suffixed ID without an OpenAI priority tier. The fallback base entry uses known Astra capabilities and zero costs for unknown Copilot pricing; it does not establish service availability.

### Changed

- Updated all upstream Pi runtime dependencies to 0.85.0.
- Reduced fullscreen transcript search latency on large transcripts through pi-tui 0.85.0's cached search index and visible-match highlighting.
- Changed the default interactive working copy from `Working...` to `Working` (and `Working (esc Interrupt)` while resetting extension UI) while preserving Atomic's animated `∀` luminance ramp in its standalone status row. Custom editors may opt into placing the indicator in their top border.
- Replaced the fullscreen transcript's dock-reserved `Jump to bottom` row with the centered `Jump to latest message` overlay, so scrolling up no longer shrinks the transcript viewport by one row.
- Aligned the shared workflow-stage-chat jump indicator with that overlay: `↓ Jump to latest message · <tui.altScreen.bottom>`, matching upstream pi's shortcut display.
- `ModelRuntime` now attaches the first-party ChatGPT Codex routing identity itself, so a standalone `stream()`/`complete()`/`streamSimple()`/`completeSimple()` request on a route-bearing fast Codex model sends `originator: codex_cli_rs` and `x-codex-routing-hint: model=<base-upstream-model>;tier=priority` exactly as an agent-session turn does, and installs the WebSocket handshake identity that only CLI entrypoints used to install. It is applied after auth headers are merged, because the routing wrapper mutates a header object that header merging would otherwise copy. A normal model still sends `originator: pi` and no hint even when a caller requests the priority tier, and an extension that supplies its own stream function for a model's API keeps full ownership of its transport.
- `ModelRuntime.canRestoreUnknownModel()` takes an optional second `modelId` argument, and session restore now passes it. A GitHub Copilot `-fast` model ID is refused when the stored credential does not advertise it or when an advertised fast sibling has no corresponding base model in the catalog, so the existing `Could not restore model …` warning fires rather than silently constructing a route-less identity. It consults the same per-account lists the Copilot provider's own model filter uses, so a Copilot model the account advertises as ordinary still restores even when its name ends in `-fast`. Other providers keep the provider-scoped answer.
- `withCodexFastRouteHeaders` no longer takes an `enabled` flag, and the runtime transport wrapper no longer derives identity from a caller flag or the final payload tier. Both paths derive the first-party Codex identity from the model's own `fastRoute` — added only when that route declares the priority tier and the model uses first-party Codex routing — and the routing hint names the route's upstream model ID instead of the canonical `-fast` ID. A caller or payload hook therefore cannot grant fast identity to a normal model.
- No fast variant is derived for a model whose API is served by a registered extension's own stream function, including a natively registered provider. Atomic cannot enforce the route through a transport it does not serialize, so publishing a `-fast` choice the extension may not honor is the same hazard as publishing one for an adapter that cannot carry the tier. Such a provider keeps its normal models and its transport untouched.
- Fast variants are derived only for models on an adapter that can actually carry `service_tier` (`openai-responses` and `openai-codex-responses`). An `openai`/`openai-codex` model on `openai-completions` no longer offers a `-fast` choice that would have silently sent an ordinary request.
- `ModelRuntime.getProvider()` now returns the same published provider as `getProviders()`, so every catalog accessor agrees about derived `-fast` models. It is the registered provider behind a `{ ...provider, getModels }` overlay, so `auth`, `login`, `stream`, and `streamSimple` remain the same function references; use `getRegisteredNativeProvider()` when you need the exact registered object.
- The chat footer, startup banner, workflow stage labels, and subagent result labels no longer append a separate `fast` marker. The selected model ID carries the `-fast` suffix itself, so a subagent renders as `codebase-analyzer (openai-codex/gpt-5.6-sol-fast · thinking medium)`.
- The `edit` tool's model-facing hashline guidance now includes compact worked examples and anti-patterns for commonly rejected patch shapes, and correctly identifies native Rust tree-sitter block resolution as primary with the brace/indent heuristic as its fallback. The hashline reference documentation is now a specification covering inputs, verified tolerated shapes, outputs, worked examples, limits, literal error messages, and warnings.
- OpenAI Responses models that advertise explicit prompt-cache support, including GPT-5.6 and GPT-6 Astra, now use `prompt_cache_options.ttl: "30m"` for long cache retention. Earlier Responses models retain `prompt_cache_retention: "24h"`; no-cache and short-cache requests continue to omit unsupported fields.

- Interactive model and thinking selections, including cycling shortcuts, now automatically become startup defaults. Scoped-model cycle-list changes also save immediately. Removed the Ctrl+S save-default shortcuts and UI.

### Fixed

- Fixed configurable save keybindings in the model and thinking selectors ([#8797](https://github.com/earendil-works/pi/issues/8797)).
- Fixed RPC abort requests reporting success without cancelling an in-progress manual compaction ([#8920](https://github.com/earendil-works/pi/issues/8920)).
- Fixed forked sessions losing a compaction boundary that referenced a removed label ([#8989](https://github.com/earendil-works/pi/issues/8989)).
- Fixed session imports overwriting an existing stored session with the same filename ([#8985](https://github.com/earendil-works/pi/issues/8985)).
- Fixed fd and rg auto-downloads failing behind exhausted shared GitHub API quotas by resolving release versions from GitHub's web redirect ([#8708](https://github.com/earendil-works/pi/issues/8708)).
- Fixed JPEG orientation detection stopping at a non-EXIF APP1 segment before the EXIF segment ([#8616](https://github.com/earendil-works/pi/issues/8616)).
- Fixed model-invocable skills disappearing when the bash tool is available but the read tool is not ([#8552](https://github.com/earendil-works/pi/issues/8552)).
- Fixed concurrent `/share` commands overwriting one another's temporary session export ([#8613](https://github.com/earendil-works/pi/issues/8613)).
- Fixed resumed sessions omitting persisted notices for Anthropic thinking blocks dropped during provider recovery.
- Fixed proxied plain-HTTP provider requests hanging after a tool call by tunneling them with CONNECT ([#8134](https://github.com/earendil-works/pi/issues/8134)).
- Fixed GitHub Copilot Claude Fable models to run through the Anthropic Messages adapter, so a selected thinking level reaches the provider instead of being dropped ([#8961](https://github.com/earendil-works/pi/issues/8961)).
- Fixed every Fireworks GLM model to use the OpenAI-compatible completions endpoint. Previously only the `glm-5p2` family did, so newer entries such as `glm-5p3` were served over the Anthropic-compatible endpoint ([#8978](https://github.com/earendil-works/pi/issues/8978)).
- Fixed the `write` tool reporting UTF-16 code-unit counts as byte counts by removing the misleading count. Every write confirmation now reads `Successfully wrote to <path>` ([#8979](https://github.com/earendil-works/pi/issues/8979)).
- Fixed the `write` tool mistaking user-authored lines beginning with `Successfully wrote to` for copied tool confirmation text and silently discarding them. A copied confirmation is now recognized only when it names the complete path the write was asked for, its resolved or cwd-relative form, or the copied snapshot's own path, so prose that merely shares a prefix or a basename is written through unchanged.
- Restricted synthetic fast aliases and first-party Codex routing identity to the explicit OpenAI/OpenAI Codex model route, suppressed aliases over native extension transports, and prevented Copilot from restoring an advertised fast ID without its base catalog model.
- Fixed signal-killed child processes reporting a null exit code that callers could mistake for success; Unix signals now use the shell convention `128 + signal`, with unknown signal names falling back to the non-zero status 128 ([#8994](https://github.com/earendil-works/pi/pull/8994)).
- Fixed Linux managed-tool downloads to use statically linked musl archives for fd and ripgrep on both x64 and ARM64 ([#9070](https://github.com/earendil-works/pi/pull/9070) by [@charlesisworkinghard](https://github.com/charlesisworkinghard)).
- Fixed a jump-to-latest click discarding other mouse reports delivered in the same terminal input chunk.
- Fixed branch summaries failing when reasoning consumes a 2048-token output cap by raising the cap to 4096 tokens, clamped to the model's `maxTokens` ([#8845](https://github.com/earendil-works/pi/issues/8845)).
- Pinned managed fd downloads on darwin/x64 to 10.3.0, matching upstream pi's known-good archive for that host ([#8708](https://github.com/earendil-works/pi/issues/8708)).
- Fixed model catalog refresh errors in the `/model` selector rendering without the blank line used by the corresponding success status.

- Resumed workflows can no longer report completion after changed control flow omits their unfinished durable tool. Normal returns and explicit completed exits require exact frontier consumption. New model/task and child-workflow execution, including stage/task worktree setup, is rejected while that frontier is pending; completed work stays cached and cancellation controls retain precedence.

### Removed

- Removed the Ctrl+X copy-message/selection action to avoid confusion with workflow navigation. `/copy` and automatic mouse-selection copying remain available.

## [0.9.18-alpha.5] - 2026-09-01

### Added

- Documented the Zed integrated-terminal `keymap.json` bindings that make Zed emit the Kitty-protocol sequences Atomic reads for `Shift+Enter` and friends ([#8828](https://github.com/earendil-works/pi/pull/8828)).
- Claude Fable 5.1 (`claude-fable-5-1`) is now selectable in Atomic on the providers it has a Claude runtime integration for — currently Anthropic, Amazon Bedrock, OpenRouter, and the Vercel AI Gateway, with a provider "latest" alias such as OpenRouter's `~anthropic/claude-fable-latest` also routing to it. It offers a 1M-token context window, 128K maximum output, always-on adaptive thinking at exactly `low`, `medium`, `high`, `xhigh`, or `max`, and a $0.25 per million cache read against Claude Fable 5's $1.00. Published catalogs also list the model on Google Vertex and Azure, which Atomic does not generate. Run `--list-models` for the current set; provider catalogs change independently of Atomic releases.

### Changed

- Changed selectors in `/thinking`, `/model`, `/scoped-models`, `/trust`, per-model thinking settings, and theme settings to keep active options marked while browsing. `/scoped-models` now uses consistent per-item toggles and strikes through unavailable models ([#8900](https://github.com/earendil-works/pi/pull/8900), [#8950](https://github.com/earendil-works/pi/pull/8950)).
- The bundled model-selection and Pareto-efficiency reference pages now state that Claude Fable 5.1 is unmeasured in Atomic's August 26, 2026 DeepSWE snapshot, because the model was released after it. They record the model's source-backed limits, effort levels, and pricing, and state explicitly that Claude Fable 5's benchmark row must not be read as a Fable 5.1 result. Builtin agent and workflow model defaults are unchanged.
- Claude models on Anthropic and Amazon Bedrock now accept PDF input. Anthropic documents PDF as a platform capability handled through the same vision path as images, so Atomic enables it wherever it has a runtime that can serialize a document block — the Anthropic Messages and Bedrock Converse paths — and leaves every other Claude mirror at text and images. A PDF sent to a model without support is replaced by a visible placeholder instead of being dropped. Bedrock needs citations enabled for full visual understanding; without them it falls back to text extraction. Note that no interactive Atomic session produces a document block yet, so this is currently reachable through the bundled `@bastani/pi-ai` library.
- The compaction documentation now states that client-side `preserve_recent` compaction intentionally resets the signed reasoning chain: because the protected tail is serialized into the boundary message rather than replayed as structured messages, no thinking block crosses a boundary, while the tail's text, tool calls, and tool results cross losslessly.

### Fixed

- Fixed forking an in-memory (`--no-session`) session while a tool call was still running appending the aborted turn to the replacement session. The active turn is now settled before the fork ([#8937](https://github.com/earendil-works/pi/pull/8937)).
- The built-in `read`, `write`, `edit`, `search`, `find`, `ls`, `bash`, and `powershell` tools now resolve relative paths against `ctx.cwd` when an extension supplies one, instead of the cwd captured when the tool was created ([#8627](https://github.com/earendil-works/pi/pull/8627)). Atomic's `search` wrapper has no upstream counterpart; without this it returned content from the extension cwd while stamping hashline tags from the tool's original cwd, so a search result could not be edited.
- Custom provider model configs accept a `supportsMaxOutputTokens` compat flag, so openai-responses gateways that reject `max_output_tokens` can opt out ([#8941](https://github.com/earendil-works/pi/pull/8941)).
- Fixed Claude Fable 5.1 sessions breaking on a mid-conversation model switch or a mid-session prefix change. Anthropic ties each thinking block to the model that produced it and, on Claude Fable 5.1, to the conversation it was produced in, and enforces the second check by default for organizations created on or after August 31, 2026. Atomic now sends the block-binding beta with `prefix_mismatch_behavior: "drop_block"` for that model on every request, including turns with no reasoning level, so a changed system prompt, a tool-set change, or a model switch drops the affected reasoning and still answers the turn instead of failing with a 400. Switching between first-party Anthropic models also keeps reasoning the target model is allowed to read — switching up to Claude Fable 5.1 preserves it, switching down has the API drop it — while assistant text, tool calls, and tool results survive the switch intact. Claude on Amazon Bedrock, Google Vertex, and Anthropic-compatible proxies is unchanged, including opencode zen and the Vercel AI Gateway; see [Preserved thinking and model switches](https://atomic.bastani.dev/models#preserved-thinking-and-model-switches).
- Fixed forced tool use reaching Claude Fable 5.1, which rejects it on every request whichever platform serves it. Atomic's agent loop only ever asks for `auto` or `none`, so no interactive session was affected, but the bundled `@bastani/pi-ai` library's Anthropic, Amazon Bedrock, and OpenAI-completions entry points all accept the wider tool-choice shape. Such a request now fails with an error naming the model and the remedy, rather than being sent or silently rewritten to `auto`. Through a gateway the old behavior was quieter and worse: OpenRouter drops parameters a model does not support, so the forced choice disappeared and the answer looked normal. On the OpenAI-completions path the check covers all four of OpenAI's forcing shapes, including a named `custom` tool and `allowed_tools` with `mode: "required"`; `allowed_tools` with `mode: "auto"` narrows the candidate set rather than forcing a call and is left alone. The guard matches the versioned Claude Fable 5.1 model id, so it covers whichever mirrors a provider currently publishes. Claude Fable 5 accepts forced tool use and is untouched, and an unversioned "latest" alias is not covered because its target cannot be known from its id.
- Fixed Claude Fable 5 and Claude Fable 5.1 sending `temperature` when used through Amazon Bedrock, OpenRouter, or GitHub Copilot. The Anthropic path already omitted it, but the Bedrock Converse and OpenAI-completions request builders emitted the field for every model, so every Claude Fable mirror on those providers sent a parameter the model rejects — including the OpenRouter "latest" alias. All Claude Fable entries on those APIs now omit it, matching what the Anthropic path already did. Models outside that family are unchanged.
- Fixed Claude Fable 5.1 offering a `minimal` thinking level in `/model` and `--list-models` that Anthropic does not publish for it. The model now offers exactly the five documented efforts.
- Fixed a model's `samplingParams` reopening sampling fields it rejects. Custom sampling keys are merged last so they can override Atomic's named fields, which meant `temperature`, `top_p`, or `top_k` set that way still reached a model that returns an error for them. Those three keys are now dropped for such models, including when they come from a model-level default. Every other custom key still overrides as before.
- Fixed a server-side fallback losing track of which model answered, and undercounting its cost. When Claude Fable 5.1 declines partway through a response and Anthropic retries on Claude Opus 4.8 or Opus 5 within the same stream, the handoff marker was discarded, the reply stayed labelled as Fable 5.1, and the turn was costed at Fable 5.1's rates rather than at those of the model that produced it. Atomic now records the marker, reports the serving model, prices the turn correctly, and also bills the earlier attempt when it produced output before declining — Anthropic charges that separately and Atomic was reporting it as free. Continuing the conversation follows Anthropic's replay rules, so a follow-up turn is not rejected for echoing reasoning from both sides of the handoff.
- Fixed a forced tool choice being ignored rather than rejected on Amazon Bedrock when the request carried no tools. The validation ran after an early return, so the instruction was discarded in silence; it now runs first.
- Fixed Atomic silently giving up on restoring exact Anthropic reasoning bytes for two kinds of turn it had not been taught about. Atomic re-inserts a replayed thinking block's original bytes after the provider payload is built, and verifies the message shape first so it can never splice reasoning into the wrong turn. That verification did not know that a model which delegates thinking-block binding replays another Claude model's signed reasoning even when its text is empty — Claude Fable 5.1's default, since its thinking display is `"omitted"` — nor that a server-side fallback marker is now sent on the wire. Either one made the check abandon repair for the whole request. No incorrect bytes were ever sent; the repair simply stopped running. Both shapes are now recognized, reasoning and unexecuted tool calls from before a fallback boundary stay dropped as Anthropic requires, and a genuinely mismatched turn is still left untouched.


## [0.9.18-alpha.4] - 2026-09-01

### Changed

- The bundled model-selection reference documentation now tracks the August 26, 2026 DeepSWE v1.1 snapshot across the model-selection, Pareto-efficiency, and benchmark-source pages. Displayed pricing is corrected for GPT-5.6 Sol ($8.39 to $6.46 after OpenAI's promotional cut), DeepSeek V4 Pro ($1.67), and DeepSeek V4 Flash ($0.46). The recomputed Pareto frontier adds GLM-5.3 Flash as its cheapest point, moves both DeepSeek V4 configurations to provider-diversity options, and retains seven configurations no longer displayed on the live leaderboard as clearly labeled history. These static reference changes do not alter runtime routing or model defaults ([#2798](https://github.com/bastani-inc/atomic/pull/2798)).

## [0.9.18-alpha.2] - 2026-08-31

### Fixed

- Fixed the `0.9.18-alpha.1` Windows x64 and ARM64 release archives crashing before startup because Bun 1.4.0 bytecode launchers were cross-compiled on Linux. Tagged releases now build both Windows archives on a Windows host, smoke-test the x64 archive, and stage both architectures into the release payload ([#2781](https://github.com/bastani-inc/atomic/pull/2781)).

## [0.9.18-alpha.1] - 2026-08-30

### Changed

- Interactive startup now paints the themed identity and focused editor before isolated-engine binding completes, while first submissions wait for the complete optional resource set. Escape cancels a submission still blocked on readiness, and `/reload` can retry after an extension failure without restarting Atomic. Failed transactional candidates do not publish host-managed settings, providers, tools, resources, event subscriptions, or system-prompt state. Compiled builds syntax-minify the shared application sidecar without identifier minification and now use Bun bytecode launchers on Windows x64 and ARM64, matching Linux and macOS. No measured Windows speedup is claimed yet.
- Bun compiled and bundled single-file builds now native-import the five fixed Atomic builtin extension bundles once and reuse their evaluated factories across reloads, avoiding jiti source reads, transforms, hashing, and graph-manifest work for immutable shipped code. Editable user, project, and package extensions and user workflows continue to use content-hash invalidation and re-evaluate after direct or transitive source edits.

### Fixed

- Restored the interactive startup resource listing so loaded context files, prompts, skills, extensions, and themes are named on screen by default, with source paths available in the expanded view. Prompt commands now appear once with their slash-prefixed labels, and Ctrl+O expands resource sections nested in the startup disclosure. The Extensions section includes isolated engine-child extensions without loading them again in the terminal host, labels bundled extensions by package name in the compact view, and disambiguates local labels that collide with those builtin names. It prefers compact parent segments, falls back to the local display path when no parent is available, and uses deterministic numeric suffixes only for remaining duplicates. The listing preserves overlap warnings when it refreshes and keeps an extension hidden when either inventory marks its identity hidden. Windows expanded listings also omit trailing extension `index.ts` and `index.js` entry names consistently with other platforms.

### Removed

- Removed the `/atomic` onboarding and help guide command. Use `/workflow list` to explore built-in workflows, `/changelog` for release notes, and `/hotkeys` for keyboard shortcuts.

## [0.9.17] - 2026-08-29

Cumulative release of the `0.9.17-alpha.1` prerelease. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease section below.

### Fixed

- Idle intercom brokers now exit after the 5-second shutdown window even when no session ever registered, including sockets that close before register. An evicted incumbent does not delete a successor's socket or pid file ([#2765](https://github.com/bastani-inc/atomic/issues/2765)).

## [0.9.17-alpha.1] - 2026-08-29

### Fixed

- Idle intercom brokers now exit after the 5-second shutdown window even when no session ever registered, including sockets that close before register. An evicted incumbent does not delete a successor's socket or pid file ([#2765](https://github.com/bastani-inc/atomic/issues/2765)).

## [0.9.16] - 2026-08-29

Cumulative release of the `0.9.16-alpha.1` – `0.9.16-alpha.11` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.

### Breaking Changes

- `AgentSession` now invokes a caller-supplied `Agent.shouldStopAfterTurn` callback before `prepareNextTurn` or `prepareNextTurnWithContext`. Preparation runs only after the loop or a non-empty steering/follow-up queue establishes that another assistant turn will start; it no longer runs after final responses, terminating tool batches without queued work, or a stop callback that returns `true`. For a post-tool threshold crossing, Atomic compacts first, passes that compacted context to preparation, and uses the caller's complete returned replacement for the next provider request. Callers that used preparation as an after-every-turn notification must move that work to `shouldStopAfterTurn` or session events ([#6879](https://github.com/earendil-works/pi/issues/6879)).
- Removed the public `workflow send` action. Use `workflow answer` for pending workflow prompts and ordinary Intercom for workflow-stage communication.
- Workflow source that typechecks against the legacy bare `@bastani/workflows` specifier must migrate to `@bastani/atomic/workflows`; removing the broken ambient declaration bridge intentionally removes TypeScript resolution for the legacy name. Runtime loading remains backward compatible because Atomic continues to alias `@bastani/workflows` to the in-memory SDK. ([#2716](https://github.com/bastani-inc/atomic/issues/2716))

### Added

- Added JSON-only terminal capability overrides for hyperlinks, Kitty/iTerm2 images, and truecolor, plus a live `fullscreenCopyOnSelect` setting. With automatic selection copying disabled, Ctrl+X copies the retained fullscreen selection before falling back to the last assistant message, while `/copy` keeps its last-message behavior and workflow/scoped-selector hierarchy transitions retain precedence.
- Added `ui_prompt_start` and `ui_prompt_end` extension events around blocking `ctx.ui` prompts so integrations can distinguish active agent work from waiting for user input. Nested and overlapping prompts form one balanced outer span, and prompt failures or UI-context replacement still emit the matching end notification without delaying the prompt UI. ([#5329](https://github.com/earendil-works/pi/issues/5329))
- Added fast-mode support for eligible GitHub Copilot models advertised by the signed-in account, alongside the existing OpenAI priority service tier. Chat and workflow scopes remain independently configurable with `/fast`.
- Added `/thinking [level]` plus searchable model and per-model thinking selectors. Selectors show saved defaults, search by model/provider/default state, order the current and default models first, and use `Ctrl+S` to persist a startup default while Enter changes only the current session ([#8399](https://github.com/earendil-works/pi/issues/8399)).
- Added a Windows PowerShell tool that prefers PowerShell 7 and falls back to Windows PowerShell when `pwsh.exe` or `powershell.exe` is on `PATH`. The tool is omitted when neither executable is available and is exposed through the SDK with typed extension events and result guards. Bash remains the shell for `!`/`!!`, plus Windows/WSL-friendly default keybindings ([#8512](https://github.com/earendil-works/pi/issues/8512)).
- Added path-aware, deduplicated settings diagnostics and routed startup diagnostics into the fullscreen transcript so alternate-screen startup cannot hide configuration errors.
- Added transcript billing notices for compaction and branch summaries when cache-miss notices are enabled. Verbatim Compaction aggregates usage from every completed planner response, including retries, into the persisted boundary, session statistics, footer totals, and cost breakdowns.
- Added export-only `atomic.share` context records containing the system prompt and tool schemas. Session sharing keeps private-Gist transport and Atomic viewer URLs; no persisted session is mutated and no Radius upload is introduced.
- Added RPC `clear_queue` to retrieve and remove queued steering and follow-up messages ([#8432](https://github.com/earendil-works/pi/issues/8432)).
- Exported `detectSupportedImageMimeTypeFromFile` from the public coding-agent API ([#8600](https://github.com/earendil-works/pi/pull/8600)).

### Changed

- Updated the external Pi runtime dependencies to 0.84.4, inheriting nested autocomplete ordering, surrogate-safe chunked terminal writes above 1 MiB, slash/kebab double-click selection, the 0.84.3 agent/client/protocol updates, and TUI fixes for narrow-width padding and wrapped Markdown-table link colors ([#8252](https://github.com/earendil-works/pi/issues/8252), [#8335](https://github.com/earendil-works/pi/issues/8335)).
- Ordinary bundled `intercom` is now a mandatory runtime tool in main chat and SDK-created model sessions. Tool and extension allowlists, exclusions, no-tool and deferred-resource modes, supplied loaders, overrides, same-name custom/project collisions, runtime active-tool mutation, and reload preserve its trusted definition without changing restrictions on any other tool. Fresh SDK sessions do not load optional bundled workflow, subagent, MCP, or web-access extensions unless configured.
- Updated Baseten's default model from `zai-org/GLM-5.2` to the directly selectable `zai-org/GLM-5.3`; GLM-5.2 remains available when fully disabled reasoning is required, while GLM-5.3 Flash provides multimodal input.
- Installed builtin extensions (`workflows`, `subagents`, `mcp`, `web-access`, `intercom`) now ship as prebundled ESM entry files. Resource trees (`skills/`, `agents/`, workflow builtins) stay on disk; leftover extension TypeScript is pruned from the npm/binary payload. Source maps stay in the published `dist/`, matching upstream pi.
- Main-chat model fallback is now session-sticky: after failover, later turns keep using the selected fallback model and thinking level until an explicit `/model` selection or model cycle changes it.
- The default system prompt now teaches repository-intent inference when a shell tool is available: review recent commits, open and merged PRs, issues and their comments, and project/board status to learn maintainer conventions, and identify the requesting user (`git config user.name`/`user.email`, `gh api user`) to interpret ambiguous requests through their own patterns.
- The ask_user_question fallback guideline now instructs the agent, when no human input channel exists, to choose the interpretation best supported by the repository and stated objective — mining git history, commits, PRs, issues, and the user's own comments to infer how they would decide — state the assumption, and continue fully autonomously instead of stalling.
- Model and thinking-level changes are session-scoped by default; saved per-model thinking overrides determine startup behavior, and persistence is now explicit through `Ctrl+S` ([#8356](https://github.com/earendil-works/pi/issues/8356)).
- Skill discovery silently ignores ordinary root Markdown files without skill frontmatter, continues diagnosing malformed declared skills, and loads nested Markdown skills from packages ([#8012](https://github.com/earendil-works/pi/issues/8012), [#8255](https://github.com/earendil-works/pi/issues/8255)).
- llama.cpp catalogs now expose sleeping models, refresh local-router catalogs even in offline mode, detect autoload support and offer presets, and explain when `/llama` is required after login ([#8235](https://github.com/earendil-works/pi/issues/8235), [#8236](https://github.com/earendil-works/pi/issues/8236), [#8238](https://github.com/earendil-works/pi/issues/8238), [#8558](https://github.com/earendil-works/pi/issues/8558)).
- Deferred uncommon syntax grammars until after first render to reduce interactive startup work while keeping common languages available immediately.
- Extension examples whose intent is final settlement now listen for `agent_settled`; JSON mode exposes tool-call ID and tool name at stream start; session-share links use canonical terminal hyperlinks and perform GitHub authentication preflight before export ([#8242](https://github.com/earendil-works/pi/issues/8242), [#7953](https://github.com/earendil-works/pi/issues/7953)).
- Compaction now reports truthful aggregate planner usage notices, including multi-attempt Verbatim planning, when cache-miss notices are enabled.
- Documented `/thinking`, the Ctrl+S startup-default shortcut in the model and thinking pickers, and the `modelThinkingLevels` setting.

### Fixed

- Fixed workflow discovery helpers from Git-installed production-only packages by making the workflow module loader resolve the same supported TypeBox runtime aliases as extension loading, including `typebox/compile`, `typebox/value`, and legacy `@sinclair/typebox` subpaths.
- Fixed independent workflow launches appearing indefinitely as empty `running` runs when another workflow's DBOS write stalled. Durable write ordering and errors are isolated per root workflow without weakening the global shutdown drain.
- Fixed `/fast` command and selector copy that still described fast mode as OpenAI/Codex-only after GitHub Copilot fast variants became eligible.
- Fixed large tool results crossing the auto-compaction threshold being sent to the provider before compaction. Atomic compacts between tool execution and the next assistant response in the same run, and restores interactive progress when that run resumes ([#6879](https://github.com/earendil-works/pi/issues/6879)).
- Prevented steering or follow-up messages queued while `shouldStopAfterTurn` resolves `true` from automatically starting another provider request. The stopped run now leaves that work pending for a later explicit prompt or continuation.
- Fixed `/model` and `/thinking` Ctrl+S failing to persist startup defaults in isolated interactive mode. Isolated mode now refreshes the host settings view and remote scoped-model catalog after the engine acknowledges persist, and `set_thinking_level` can include the engine's clamped level plus the provider/model the engine persisted against so a later `model_changed` cannot re-key the saved thinking override ([#2727](https://github.com/bastani-inc/atomic/issues/2727)).
- Fixed installed packages where the intercom broker failed to start on every launch, leaving both the `intercom` and `subagent` tools entirely non-functional.
- Fixed Bun-compiled binaries crashing when a transformed extension performed a second `proper-lockfile` operation. Compiled extension imports now reuse the host's lockfile module instead of transforming and proxy-wrapping its mutable internals.
- Fixed the published workflow SDK so importing `@bastani/atomic` no longer injects the workflows graph into every consumer's TypeScript program, consumers can typecheck against the package again, and workflow authoring types no longer silently collapse to `any`. ([#2716](https://github.com/bastani-inc/atomic/issues/2716))
- Workflow stages on installed/prebundled builds now resolve credentials from `~/.atomic/agent` again instead of creating an empty `~/.workflows/agent` and reporting `No API key found` for every provider.
- Coalesced same-turn sibling `subagent` execution calls into one indexed parallel run instead of rejecting every call after the first ([#2588](https://github.com/bastani-inc/atomic/issues/2588)).
- Fixed `/thinking` being absent from slash-command autocomplete and added completions for the active model's available thinking levels.
- Fixed `/tree` ranking below `/thinking` in slash-command autocomplete, so typing `/t` again offers branch navigation before the thinking-level command.
- Fixed the thinking-level selector so a capability-clamped default still receives the default badge when the saved setting is higher than the active model supports.
- Fixed the per-model thinking-level settings row opening an empty, unexplained picker when no models are available; it now states that a provider login or API key is required.
- Failed extension loads now roll back provider registrations applied during that load instead of leaving an earlier provider in the runtime.
- PowerShell cancellation now terminates the Windows process tree and returns without waiting on descendant-held stdout or stderr.
- Fixed PowerShell tool calls rendering with the Bash `$` transcript prompt. PowerShell calls now render as `PS> <command>`, and truncated PowerShell output is saved under its own temp-file prefix.
- Fixed Windows compiled binaries crashing with `ERR_INVALID_FILE_URL_PATH` when Return reached native modifier detection, including from the `/model` selector.
- Fixed `bundle:dev` and `start:fast` omitting the statically registered OAuth adapters. Development bundles now use the Bun entrypoint shared with compiled builds, so OpenAI Codex and xAI OAuth derivation and refresh no longer fail on unresolved runtime imports.
- Fixed duplicate fullscreen right-click paste in VS Code-based terminals on Windows ([#8186](https://github.com/earendil-works/pi/issues/8186)).
- Fixed padded text exceeding narrow terminal widths ([#8252](https://github.com/earendil-works/pi/issues/8252)).
- Fixed wrapped Markdown table links leaking color into borders and neighboring cells, including tables inside blockquotes ([#8335](https://github.com/earendil-works/pi/issues/8335)).
- Compaction failures and cancellations now emit `session_compact_failed` to extensions and expose public failure details with their trigger, error, abort/retry state, and whether an extension supplied the compacted text ([#8241](https://github.com/earendil-works/pi/issues/8241)).
- Automatic compaction now estimates message size when providers report all-zero usage instead of silently skipping the threshold check ([#8328](https://github.com/earendil-works/pi/issues/8328)).
- Branch and session summaries reject tool calls, and their requests explicitly disable tools; planner requests do the same while retaining validated truncated-range recovery.
- Branch and session summaries reject token-cap-truncated prose instead of persisting incomplete checkpoints ([#7048](https://github.com/earendil-works/pi/issues/7048)).
- Branch summaries record the source leaf rather than the navigation destination.
- Fixed package updates comparing versions for inequality and potentially downgrading a newer installed package; upgrades now use semantic-version ordering ([#8239](https://github.com/earendil-works/pi/issues/8239)).
- Fixed extension flags accepting runtime defaults whose value did not match the declared boolean/string type, and failed extension factories leaking partially registered commands, tools, flags, and inherited resources into later loads ([#8123](https://github.com/earendil-works/pi/issues/8123), [#8424](https://github.com/earendil-works/pi/issues/8424)).
- Fixed hung authenticated model-catalog attempts consuming the whole request budget without retrying by applying a per-attempt timeout before retrying.
- Fixed UTF-8 BOMs breaking auth, model, keybinding, frontmatter, package, resource, theme, CLI, and external-editor text inputs while preserving BOM-aware hashline edits.
- Fixed atomic managed-state rewrites resetting existing file permissions. `auth.json` and `models-store.json` are created owner-only (`0600`) but keep an administrator-managed mode across later rewrites.
- Fixed explicitly persisted default models disappearing from non-empty model scopes; persisted defaults are now added to both the active and saved scope.
- Fixed extension messages sent with `triggerTurn: false` while the agent is running being inserted between a tool call and its result; they are now appended after the turn's tool results ([#8537](https://github.com/earendil-works/pi/issues/8537)).
- Fixed compaction range planning and branch/session summaries forcing `toolChoice: "none"`, which sent `tool_choice` with no tools and could be rejected by gateways ([#8649](https://github.com/earendil-works/pi/issues/8649), [#8638](https://github.com/earendil-works/pi/issues/8638)).
- Fixed Windows shell aborts crashing Atomic when `taskkill.exe` is not on `PATH`. Both `killProcessTree` and the detached-child guardian worker now spawn the System32 executable and consume the asynchronous spawn error ([#6596](https://github.com/earendil-works/pi/issues/6596)).
- Fixed Windows `taskkill` spawning without `windowsHide`. Because the spawn is `detached`, Windows allocated and briefly flashed a console window on every process-tree kill.
- Fixed session files whose final record has no trailing newline not being repaired on load, so a later append no longer concatenates onto that record.
- Fixed toggling thinking-block visibility discarding partial Bash output. Rendered assistant messages are updated in place instead of rebuilding the chat and its live tool components ([#8611](https://github.com/earendil-works/pi/issues/8611)).

### Removed

- Removed the bundled writing-rule set from the default system prompt's `Guidelines` section. The unrelated "Be concise in your responses" and "Show file paths clearly when working with files" guidelines remain.
- Removed the bundled `/subagents-doctor`, `/run`, and `/parallel` entries from the slash-command catalog, and dropped `/parallel-review` and `/parallel-cleanup` suggestions from the `/atomic` guide. Launch children with the `subagent` tool.

## [0.9.16-alpha.11] - 2026-08-28

### Added

- Added JSON-only terminal capability overrides for hyperlinks, Kitty/iTerm2 images, and truecolor, plus a live `fullscreenCopyOnSelect` setting. With automatic selection copying disabled, Ctrl+X copies the retained fullscreen selection before falling back to the last assistant message, while `/copy` keeps its last-message behavior and workflow/scoped-selector hierarchy transitions retain precedence.

### Changed

- Updated the external Pi runtime dependencies to 0.84.4, inheriting nested autocomplete ordering, surrogate-safe chunked terminal writes above 1 MiB, and slash/kebab double-click selection.
- Ordinary bundled `intercom` is now a mandatory runtime tool in main chat and SDK-created model sessions. Tool and extension allowlists, exclusions, no-tool and deferred-resource modes, supplied loaders, overrides, same-name custom/project collisions, runtime active-tool mutation, and reload preserve its trusted definition without changing restrictions on any other tool. Fresh SDK sessions do not load optional bundled workflow, subagent, MCP, or web-access extensions unless configured.

### Fixed

- Fixed workflow discovery helpers from Git-installed production-only packages by making the workflow module loader resolve the same supported TypeBox runtime aliases as extension loading, including `typebox/compile`, `typebox/value`, and legacy `@sinclair/typebox` subpaths.
- Fixed independent workflow launches appearing indefinitely as empty `running` runs when another workflow's DBOS write stalled. Durable write ordering and errors are isolated per root workflow without weakening the global shutdown drain.
- Fixed `/fast` command and selector copy that still described fast mode as OpenAI/Codex-only after GitHub Copilot fast variants became eligible.

## [0.9.16-alpha.10] - 2026-08-28

### Breaking Changes

- `AgentSession` now invokes a caller-supplied `Agent.shouldStopAfterTurn` callback before `prepareNextTurn` or `prepareNextTurnWithContext`. Preparation runs only after the loop or a non-empty steering/follow-up queue establishes that another assistant turn will start; it no longer runs after final responses, terminating tool batches without queued work, or a stop callback that returns `true`. For a post-tool threshold crossing, Atomic compacts first, passes that compacted context to preparation, and uses the caller's complete returned replacement for the next provider request. Callers that used preparation as an after-every-turn notification must move that work to `shouldStopAfterTurn` or session events ([#6879](https://github.com/earendil-works/pi/issues/6879)).

### Changed

- Updated Baseten's default model from `zai-org/GLM-5.2` to the directly selectable `zai-org/GLM-5.3`; GLM-5.2 remains available when fully disabled reasoning is required, while GLM-5.3 Flash provides multimodal input.

### Fixed

- Fixed large tool results crossing the auto-compaction threshold being sent to the provider before compaction. Atomic compacts between tool execution and the next assistant response in the same run, and restores interactive progress when that run resumes ([#6879](https://github.com/earendil-works/pi/issues/6879)).
- Prevented steering or follow-up messages queued while `shouldStopAfterTurn` resolves `true` from automatically starting another provider request. The stopped run now leaves that work pending for a later explicit prompt or continuation.

## [0.9.16-alpha.9] - 2026-08-27

### Added

- Added `ui_prompt_start` and `ui_prompt_end` extension events around blocking `ctx.ui` prompts so integrations can distinguish active agent work from waiting for user input. Nested and overlapping prompts form one balanced outer span, and prompt failures or UI-context replacement still emit the matching end notification without delaying the prompt UI. ([#5329](https://github.com/earendil-works/pi/issues/5329))

## [0.9.16-alpha.8] - 2026-08-27

### Breaking Changes

- Removed the public `workflow send` action. Use `workflow answer` for pending workflow prompts and ordinary Intercom for workflow-stage communication.
- Workflow source that typechecks against the legacy bare `@bastani/workflows` specifier must migrate to `@bastani/atomic/workflows`; removing the broken ambient declaration bridge intentionally removes TypeScript resolution for the legacy name. Runtime loading remains backward compatible because Atomic continues to alias `@bastani/workflows` to the in-memory SDK. ([#2716](https://github.com/bastani-inc/atomic/issues/2716))

### Added

- Added fast-mode support for eligible GitHub Copilot models advertised by the signed-in account, alongside the existing OpenAI priority service tier. Chat and workflow scopes remain independently configurable with `/fast`.

### Fixed

- Fixed `/model` and `/thinking` Ctrl+S failing to persist startup defaults in isolated interactive mode. The engine RPC dropped the persist flag, so `settings.json` never received `defaultProvider`/`defaultModel` or the thinking default. Isolated mode now refreshes the host settings view and remote scoped-model catalog after the engine acknowledges persist, and `set_thinking_level` can include the engine's clamped level plus the provider/model the engine persisted against so a later `model_changed` cannot re-key the saved thinking override. Isolated `/thinking` set and cycle update the host session immediately so rapid cycles and UI refresh do not wait for ACK, while a later `thinking_level_changed` still wins over an older ACK. An unsupported requested level is clamped with the same nearest-supported scan as the engine, so hiding `minimal` via `thinkingLevelMap` becomes `low` immediately rather than `off`. `RpcClient.setThinkingLevel(level)` remains one-argument `Promise<void>`; isolated persist reads that ACK through an internal client path, and data-less `set_thinking_level` success responses remain valid. `set_model`, `cycle_model`, and `set_thinking_level` accept optional `persist`. ([#2727](https://github.com/bastani-inc/atomic/issues/2727))
- Fixed installed packages where the intercom broker failed to start on every launch, leaving both the `intercom` and `subagent` tools entirely non-functional.
- Fixed Bun-compiled binaries crashing when a transformed extension performed a second `proper-lockfile` operation. Compiled extension imports now reuse the host's lockfile module instead of transforming and proxy-wrapping its mutable internals.

- Fixed the published workflow SDK so importing `@bastani/atomic` no longer injects the workflows graph into every consumer's TypeScript program, consumers can typecheck against the package again, and workflow authoring types no longer silently collapse to `any`. ([#2716](https://github.com/bastani-inc/atomic/issues/2716))

## [0.9.16-alpha.6] - 2026-08-26

### Changed

- The default system prompt now teaches repository-intent inference when a shell tool is available: review recent commits, open and merged PRs, issues and their comments, and project/board status to learn maintainer conventions, and identify the requesting user (`git config user.name`/`user.email`, `gh api user`) to interpret ambiguous requests through their own patterns.
- The ask_user_question fallback guideline now instructs the agent, when no human input channel exists, to choose the interpretation best supported by the repository and stated objective — mining git history, commits, PRs, issues, and the user's own comments to infer how they would decide — state the assumption, and continue fully autonomously instead of stalling.

## [0.9.16-alpha.5] - 2026-08-26

### Added

- Added `/thinking [level]` plus searchable model and per-model thinking selectors. Selectors show saved defaults, search by model/provider/default state, order the current and default models first, and use `Ctrl+S` to persist a startup default while Enter changes only the current session ([#8399](https://github.com/earendil-works/pi/issues/8399)).
- Added a Windows PowerShell tool that prefers PowerShell 7 and falls back to Windows PowerShell when `pwsh.exe` or `powershell.exe` is on `PATH`. The tool is omitted when neither executable is available and is exposed through the SDK with typed extension events and result guards. Bash remains the shell for `!`/`!!`, plus Windows/WSL-friendly default keybindings ([#8512](https://github.com/earendil-works/pi/issues/8512)).
- Added path-aware, deduplicated settings diagnostics and routed startup diagnostics into the fullscreen transcript so alternate-screen startup cannot hide configuration errors.
- Added transcript billing notices for compaction and branch summaries when cache-miss notices are enabled. Verbatim Compaction aggregates usage from every completed planner response, including retries, into the persisted boundary, session statistics, footer totals, and cost breakdowns.
- Added export-only `atomic.share` context records containing the system prompt and tool schemas. Session sharing keeps private-Gist transport and Atomic viewer URLs; no persisted session is mutated and no Radius upload is introduced.
- Added RPC `clear_queue` to retrieve and remove queued steering and follow-up messages ([#8432](https://github.com/earendil-works/pi/issues/8432)).
- Exported `detectSupportedImageMimeTypeFromFile` from the public coding-agent API ([#8600](https://github.com/earendil-works/pi/pull/8600)).

### Changed

- Updated the external pi runtime dependencies to 0.84.3, inheriting the matching agent/client/protocol updates and TUI fixes for narrow-width padding and wrapped Markdown-table link colors ([#8252](https://github.com/earendil-works/pi/issues/8252), [#8335](https://github.com/earendil-works/pi/issues/8335)).
- Model and thinking-level changes are session-scoped by default; saved per-model thinking overrides determine startup behavior, and persistence is now explicit through `Ctrl+S` ([#8356](https://github.com/earendil-works/pi/issues/8356)).
- Skill discovery silently ignores ordinary root Markdown files without skill frontmatter, continues diagnosing malformed declared skills, and loads nested Markdown skills from packages ([#8012](https://github.com/earendil-works/pi/issues/8012), [#8255](https://github.com/earendil-works/pi/issues/8255)).
- llama.cpp catalogs now expose sleeping models, refresh local-router catalogs even in offline mode, detect autoload support and offer presets, and explain when `/llama` is required after login ([#8235](https://github.com/earendil-works/pi/issues/8235), [#8236](https://github.com/earendil-works/pi/issues/8236), [#8238](https://github.com/earendil-works/pi/issues/8238), [#8558](https://github.com/earendil-works/pi/issues/8558)).
- Deferred uncommon syntax grammars until after first render to reduce interactive startup work while keeping common languages available immediately.
- Extension examples whose intent is final settlement now listen for `agent_settled`; JSON mode exposes tool-call ID and tool name at stream start; session-share links use canonical terminal hyperlinks and perform GitHub authentication preflight before export ([#8242](https://github.com/earendil-works/pi/issues/8242), [#7953](https://github.com/earendil-works/pi/issues/7953)).
- Compaction now reports truthful aggregate planner usage notices, including multi-attempt Verbatim planning, when cache-miss notices are enabled.
- Documented `/thinking`, the Ctrl+S startup-default shortcut in the model and thinking pickers, and the `modelThinkingLevels` setting.

### Fixed

- Fixed `/thinking` being absent from slash-command autocomplete and added completions for the active model's available thinking levels.
- Fixed the thinking-level selector so a capability-clamped default still receives the default badge when the saved setting is higher than the active model supports.
- Failed extension loads now roll back provider registrations applied during that load instead of leaving an earlier provider in the runtime.
- PowerShell cancellation now terminates the Windows process tree and returns without waiting on descendant-held stdout or stderr.
- Fixed `/tree` ranking below `/thinking` in slash-command autocomplete, so typing `/t` again offers branch navigation before the thinking-level command.
- Fixed PowerShell tool calls rendering with the Bash `$` transcript prompt. PowerShell calls now render as `PS> <command>`, and truncated PowerShell output is saved under its own temp-file prefix.
- Fixed the per-model thinking-level settings row opening an empty, unexplained picker when no models are available; it now states that a provider login or API key is required.
- Fixed Windows compiled binaries crashing with `ERR_INVALID_FILE_URL_PATH` when Return reached native modifier detection, including from the `/model` selector. Release app bundles keep only `pi-tui`'s native modifier loader runtime-relative, avoiding both the Linux build host's frozen module URL and Bun's inability to resolve a fully external `pi-tui` from a compiled split launcher, while retaining the staged Windows native helper for Shift+Enter handling.
- Fixed `bundle:dev` and `start:fast` omitting the statically registered OAuth adapters. Development bundles now use the Bun entrypoint shared with compiled builds, so OpenAI Codex and xAI OAuth derivation and refresh no longer fail on unresolved runtime imports.
- Fixed duplicate fullscreen right-click paste in VS Code-based terminals on Windows ([#8186](https://github.com/earendil-works/pi/issues/8186)).
- Fixed padded text exceeding narrow terminal widths ([#8252](https://github.com/earendil-works/pi/issues/8252)).
- Fixed wrapped Markdown table links leaking color into borders and neighboring cells, including tables inside blockquotes ([#8335](https://github.com/earendil-works/pi/issues/8335)).
- Compaction failures and cancellations now emit `session_compact_failed` to extensions and expose public failure details with their trigger, error, abort/retry state, and whether an extension supplied the compacted text ([#8241](https://github.com/earendil-works/pi/issues/8241)).
- Automatic compaction now estimates message size when providers report all-zero usage instead of silently skipping the threshold check ([#8328](https://github.com/earendil-works/pi/issues/8328)).
- Branch and session summaries reject tool calls, and their requests explicitly disable tools; planner requests do the same while retaining validated truncated-range recovery.
- Branch and session summaries reject token-cap-truncated prose instead of persisting incomplete checkpoints ([#7048](https://github.com/earendil-works/pi/issues/7048)).
- Branch summaries record the source leaf rather than the navigation destination.
- Fixed package updates comparing versions for inequality and potentially downgrading a newer installed package; upgrades now use semantic-version ordering ([#8239](https://github.com/earendil-works/pi/issues/8239)).
- Fixed extension flags accepting runtime defaults whose value did not match the declared boolean/string type, and failed extension factories leaking partially registered commands, tools, flags, and inherited resources into later loads ([#8123](https://github.com/earendil-works/pi/issues/8123), [#8424](https://github.com/earendil-works/pi/issues/8424)).
- Fixed hung authenticated model-catalog attempts consuming the whole request budget without retrying by applying a per-attempt timeout before retrying.
- Fixed UTF-8 BOMs breaking auth, model, keybinding, frontmatter, package, resource, theme, CLI, and external-editor text inputs while preserving BOM-aware hashline edits.
- Fixed atomic managed-state rewrites resetting existing file permissions. `auth.json` and `models-store.json` are created owner-only (`0600`) but keep an administrator-managed mode across later rewrites instead of having it reset by the atomic replacement.
- Fixed explicitly persisted default models disappearing from non-empty model scopes; persisted defaults are now added to both the active and saved scope.
- Fixed extension messages sent with `triggerTurn: false` while the agent is running being inserted between a tool call and its result; they are now appended after the turn's tool results ([#8537](https://github.com/earendil-works/pi/issues/8537)).
- Fixed compaction range planning and branch/session summaries forcing `toolChoice: "none"`, which sent `tool_choice` with no tools and could be rejected by gateways ([#8649](https://github.com/earendil-works/pi/issues/8649), [#8638](https://github.com/earendil-works/pi/issues/8638)).
- Fixed Windows shell aborts crashing Atomic when `taskkill.exe` is not on `PATH`. Both `killProcessTree` and the detached-child guardian worker now spawn the System32 executable and consume the asynchronous spawn error ([#6596](https://github.com/earendil-works/pi/issues/6596)).
- Fixed Windows `taskkill` spawning without `windowsHide`. Because the spawn is `detached`, Windows allocated and briefly flashed a console window on every process-tree kill.
- Fixed session files whose final record has no trailing newline not being repaired on load, so a later append no longer concatenates onto that record.
- Fixed toggling thinking-block visibility discarding partial Bash output. Rendered assistant messages are updated in place instead of rebuilding the chat and its live tool components ([#8611](https://github.com/earendil-works/pi/issues/8611)).

## [0.9.16-alpha.4] - 2026-08-23

### Fixed

- Workflow stages on installed/prebundled builds now resolve credentials from `~/.atomic/agent` again. The companion extension bundle was treating `@bastani/workflows` as the app package, so stages created an empty `~/.workflows/agent` and reported `No API key found` for every provider.


## [0.9.16-alpha.3] - 2026-08-23

### Changed

- Installed builtin extensions (`workflows`, `subagents`, `mcp`, `web-access`, `intercom`) now ship as prebundled ESM entry files. Resource trees (`skills/`, `agents/`, workflow builtins) stay on disk; leftover extension TypeScript is pruned from the npm/binary payload. Source maps stay in the published `dist/`, matching upstream pi.


## [0.9.16-alpha.2] - 2026-08-23

### Changed

- Main-chat model fallback is now session-sticky: after failover, later turns keep using the selected fallback model and thinking level until an explicit `/model` selection or model cycle changes it.

### Removed

- Removed the bundled writing-rule set from the default system prompt's `Guidelines` section. The unrelated "Be concise in your responses" and "Show file paths clearly when working with files" guidelines remain.
- Removed the bundled `/subagents-doctor`, `/run`, and `/parallel` entries from the slash-command catalog, and dropped `/parallel-review` and `/parallel-cleanup` suggestions from the `/atomic` guide. Launch children with the `subagent` tool.


## [0.9.16-alpha.1] - 2026-08-23

### Fixed

- Foreground child decisions, structured interviews, and `intercom.ask` calls resolved to their launching parent now pause the retained child and return the question plus ordered attachments through the parent `subagent` call instead of deadlocking behind Intercom reply delivery. Real typed foreground children receive exact broker authorization for `contact_supervisor`, including successful non-blocking progress delivery. Bare run-ID resume preserves each paused child's session, cwd, Intercom group, execution settings, canonical index, worktree, and dirty changes while rebuilding control and detach callbacks; a sibling completed at the ask boundary stays terminal without blocking paused siblings. Queued work remains unlaunched and unauthorized, worktree diff capture and cleanup wait for terminal resume, and completed children remain non-resumable ([#2589](https://github.com/bastani-inc/atomic/issues/2589)).
- Coalesced same-turn sibling `subagent` execution calls into one indexed parallel run instead of rejecting every call after the first. Live result, progress, control, and artifact updates and final results stay route-local without sibling data, while the TUI redraws the shared run as one aggregate parallel widget. Solitary, sequential, management, and true-overlap behavior remains unchanged ([#2588](https://github.com/bastani-inc/atomic/issues/2588)).

## [0.9.15] - 2026-08-21

Cumulative release of the `0.9.15-alpha.1` prerelease. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease section below.

### Breaking Changes

- Subagent delegation is now fixed at one level. `WorkflowStageOrchestrationContext.constraints` no longer has the required nesting-depth field and is now `{ disableWorkflowTool: true }`; `SubagentChildPolicy` no longer has the optional inherited delegation limit and keeps `depth` alone. SDK callers that construct stage orchestration contexts must drop the removed field.

### Changed

- Vendored `@earendil-works/pi-ai` into the Atomic monorepo as `@bastani/pi-ai` and switched first-party imports, loader aliases, shrinkwrap, and tagged release publishing to the workspace package. Legacy extensions that import `@earendil-works/pi-ai` still resolve through the loader.
- `@bastani/pi-ai` now refreshes its models.dev catalog at package build instead of shipping the frozen v0.84.2 snapshot.
- Moved `COPILOT_GITHUB_TOKEN` env-token host routing into `@bastani/pi-ai/providers/github-copilot-env`, preserving `models.json` endpoint overrides ([#2522](https://github.com/bastani-inc/atomic/issues/2522)).
- SQLite resource selectors now use `node:sqlite` only. The `bun:sqlite` fallback was removed as Atomic's Bun floor moved to 1.4.0; Node ≥ 22.13 and Bun ≥ 1.4.0 provide `node:sqlite` without a flag.

### Fixed

- Fixed fine-grained PATs supplied through `COPILOT_GITHUB_TOKEN` failing with `Personal Access Tokens are not supported for this endpoint`. Raw tokens now send `Copilot-Integration-Id: copilot-developer-cli`, exchanged OAuth tokens retain their behavior, and explicit provider/model header overrides keep precedence ([#2522](https://github.com/bastani-inc/atomic/issues/2522)).
- Fixed workflow stages hanging after GitHub Copilot stream decompression failures. Provider stream stalls now settle as transient transport errors and can retry or fail over; the new `streamDeadlineMs` setting bounds idle gaps and supports duration strings or disabling the deadline ([#2553](https://github.com/bastani-inc/atomic/issues/2553)).
- Extension widgets now remount once after the interactive host clears or disposes them, preserve in-place updates, and discard stale isolated-engine mounts before reopening ([#2556](https://github.com/bastani-inc/atomic/issues/2556)).
- Ported unreleased upstream pi-ai fixes for Copilot throttling, provider usage and reasoning metadata, Bedrock headers and reasoning, Anthropic fallback pricing, Azure tool choice, model catalogs, xAI Responses routing, stream request options, User-Agent headers, and OpenAI reasoning replay. Cerebras now defaults to `gpt-oss-120b`, and the unused OpenTelemetry dependency was removed.

## [0.9.15-alpha.1] - 2026-08-21

### Breaking Changes

- Two exported SDK types each lose a field, because subagent delegation is now a fixed one-level rule rather than a configurable depth. `WorkflowStageOrchestrationContext.constraints` no longer has the required nesting-depth number and is now `{ disableWorkflowTool: true }`; `SubagentChildPolicy` no longer has the optional inherited delegation limit and keeps `depth` alone. Code that constructs a stage orchestration context must drop the removed required field.

### Changed

- `@bastani/pi-ai` now refreshes its models.dev catalog at package build, matching upstream pi-ai, instead of shipping the frozen v0.84.2 snapshot.

- Vendored `@earendil-works/pi-ai` into `packages/ai` as `@bastani/pi-ai` and switched Atomic onto the workspace package. First-party imports, extension loader aliases, shrinkwrap, and the release publisher now use `@bastani/pi-ai`. Extensions that still import `@earendil-works/pi-ai` keep resolving through the loader. The first npm version of `@bastani/pi-ai` must be published by hand before a tagged Atomic release can publish it via trusted publishing.

- Moved `COPILOT_GITHUB_TOKEN` env-token host routing into `@bastani/pi-ai/providers/github-copilot-env`, preserving `models.json` endpoint overrides ([#2522](https://github.com/bastani-inc/atomic/issues/2522)).

- SQLite resource selectors now load `node:sqlite` exclusively; the `bun:sqlite` fallback for Bun releases before 1.4.0 was removed. `node:sqlite` ships unflagged in Node ≥ 22.13 and Bun ≥ 1.4.0, which is now the repository's Bun floor (engines, CI pins, and release binaries all moved to Bun 1.4.0).

### Fixed

- Fixed the `400 checking third-party user token: bad request: Personal Access Tokens are not supported for this endpoint` failure for fine-grained PATs supplied through `COPILOT_GITHUB_TOKEN`. Raw tokens now send `Copilot-Integration-Id: copilot-developer-cli`; exchanged OAuth tokens containing a `tid=` segment remain unchanged, and per-request or `models.json` provider/model header overrides win. Catalog default headers are no longer promoted into the per-request override layer, while remaining visible to `before_provider_headers` extension hooks ([#2522](https://github.com/bastani-inc/atomic/issues/2522)).
- Fixed workflow stages staying `running` forever when GitHub Copilot on the default `transport: "auto"` hit a repeated `Library error: zlib error: incorrect header check`. The stalled provider stream never settled, so retry and model fallback never advanced and the stage transcript kept only its user prompt. Such a failure now settles as a transient transport error and fails over, and a new `streamDeadlineMs` setting (default `300000`; accepts durations such as `30s` and `5m`; `0`/`"disabled"` disables it) bounds the idle gap between provider stream events below the HTTP layer ([#2553](https://github.com/bastani-inc/atomic/issues/2553)).
- Extension widgets now recover after the interactive host clears or disposes a live widget: reactive controllers receive a release signal, remount exactly once on the next refresh, and preserve in-place updates without flicker. Isolated-engine widgets also discard stale remote mounts before reopening ([#2556](https://github.com/bastani-inc/atomic/issues/2556)).
- Ported unreleased `packages/ai` commits from `earendil-works/pi` main after the vendored v0.84.2 snapshot into `@bastani/pi-ai`: Copilot login rate-limit handling, Kimi cached-token accounting, Google thinking-level maps, Bedrock response headers and redacted reasoning, Anthropic fallback pricing, Azure Responses `toolChoice`, OpenAI Completions reasoning-details replay, catalog fixes for Xiaomi, China ZAI Coding Plan, Qwen Token Plan Individual, Baseten GLM-5.2, and DeepSeek V4 Flash `low` thinking, plus xAI Responses routing, simple `toolChoice`, generalized thinking-token budgets, adapter User-Agent headers, and dropping the unused OpenTelemetry dependency. Cerebras now defaults to `gpt-oss-120b`.

## [0.9.14] - 2026-08-19

Cumulative release of the `0.9.14-alpha.1` – `0.9.14-alpha.5` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.

### Breaking Changes

- **The `workflow` parameter on bundled `web_search` is gone.** Models cannot select the curator per call. Persist `"workflow": "summary-review"` in web-search config or run `/curator on` to opt in.

### Added

- Two new unbound viewport actions, `tui.altScreen.lineUp` and `tui.altScreen.lineDown`, scroll the transcript by a single line ([#2385](https://github.com/bastani-inc/atomic/issues/2385)).
- Added the `fullscreenExitOutput` setting with a `/settings` row beside **Fullscreen scrollbar**. `"transcript"` (the default) prints the final transcript and session resume hint when an interactive session exits; `"resume-hint"` restores the terminal's previous screen and prints only the resume hint.
- Added the `defaultTools` setting, which selects the built-in tools a session starts with — an empty array starts with none — while extension and SDK custom tools stay registered and active, so `workflow`, `subagent`, `intercom`, `mcp`, and `web_search` survive a narrow selection. Explicit `--tools`, `--no-tools`, `--no-builtin-tools`, and `--exclude-tools` keep their precedence, a project array replaces the global array, and an unreadable value reads as unset.
- Added `--use-theme <name[/name]>`, which selects the interactive theme for one run without writing settings. `atomic --use-theme light/dark` follows terminal appearance; choosing another theme later in `/settings` applies immediately and saves normally, and `/export`/`/share` render with the run's theme.
- Added `expandPromptTemplates` to `sendUserMessage()`. The default stays `false`, so extension-authored messages keep their literal-send meaning; `true` dispatches registered extension commands and expands skill commands and prompt templates, and an unknown command falls through to a literal send.
- Added opt-in strict JSON-schema constrained sampling for built-in tool definitions behind the existing experimental gate (`ATOMIC_EXPERIMENTAL=1` or legacy `PI_EXPERIMENTAL=1`). `read`, `bash`, `edit`, `write`, `find`, `search`, `ls`, `ask_user_question`, `todo`, and the harness tool wrapper declare `{ type: "json_schema", strict: "prefer" }` only while the gate is on.
- Added the Cloudflare AI Gateway Workers AI binding transport to the public surface: `createGatewayBindingFetch`, the `CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL` placeholder credential, and the binding types are re-exported from `@bastani/atomic`, and [Providers](docs/providers.md) documents the no-API-token path.
- Added the `SessionNameState` type and `getSessionNameState()` to the public SDK surface. `getSessionNameState()` reports whether a session was never named, named and then cleared, or currently holds a name — the distinction the session file already records — where `getLatestSessionName` returns only the name.
- Same-name skills stay independently selectable. `/skill:name` still uses the current project/user-over-package winner, and Atomic now keeps every distinct file as a source-qualified command such as `/skill:tdd@user` and `/skill:tdd@builtin`. Autocomplete, `pi.getCommands()`, RPC `get_commands`, the model-visible skill list, collision diagnostics, and in-process child `skills` selectors share those identities ([#2328](https://github.com/bastani-inc/atomic/issues/2328)).
- Added `sessionScopedExtensionState()` to the public surface: a host primitive for extension state that must survive `/reload`. Reloading file extensions re-evaluates their module graph, so module-scoped singletons are recreated while the session keeps running; the primitive returns the state already registered for that session's event bus under a caller-chosen key ([#2462](https://github.com/bastani-inc/atomic/issues/2462) by [@makgunay](https://github.com/makgunay)).
- `ctx.ui.custom()` accepts `reserveTranscriptRows` for bottom-anchored overlay mounts. A blocking dialog sets it to be height-bounded against the terminal and to have its covered transcript-bottom suffix added to the scroll extent. Reserving overlays require a bottom anchor and reject `row` or a nonzero `offsetY`.
- `OVERLAY_ACTIVE_ROW_MARKER` is exported for components mounted that way. Embed it in the line you most need kept — the selected row of a list — and the bound places what it keeps around that row instead of taking a fixed head. It is a zero-width APC sequence the host strips before painting.
- Added HumanLayer's bundled `show-me` skill for visual explanations, diagrams, code-shape sketches, and focused HTML artifacts. Sourced from https://github.com/humanlayer/skills and distributed under the MIT License.

### Changed

- Adopted the pi 0.84.2 runtime across all six `@earendil-works/pi-*` dependencies ([#2385](https://github.com/bastani-inc/atomic/issues/2385)). Provider fixes arrive with `pi-ai`: OpenAI Responses namespace handling, DeepSeek `max_tokens` and case-insensitive base URLs, Bedrock empty tool-argument keys, Google length stops alongside tool calls, Codex `end_turn`, upstream buffer retry, Cloudflare strict-tools gating, and DeepSeek V4 Flash low effort. Renderer fixes arrive with `pi-tui`: full-width row painting, repaint after an idle focus loss, LaTeX control-space handling across line endings, focused-overlay wheel/viewport-key deferral, and generic SGR mouse release. Mistral models now stream over a native HTTP transport — the Mistral SDK is no longer installed — and `PI_TUI_ESC_TIMEOUT` (default `100` ms over SSH, `10` ms otherwise) tunes how long the renderer waits after a lone `ESC` before treating it as Escape.
- Bundled `web_search` no longer opens the curator confirmation UI by default. Persist `"workflow": "summary-review"` in web-search config or run `/curator on` to opt in.
- The exported `Theme` constructor's color maps now accept the optional `scrollbarThumb` token, with the same fallback encoded in the typebox and JSON theme schemas, so every user-authored theme keeps validating and rendering. Unused leftover `searchMatchBg` / `searchMatchText` keys remain accepted so older theme files still load.
- Automatic theme detection starts its colour-scheme and background probes concurrently instead of sequentially, halving the worst-case per-startup detection delay from 200 ms to 100 ms.
- Concurrent interactive model-catalog refreshes share one in-flight pass per runtime and effective network policy — keyed also on credential state and catalog content — so startup's refresh and the `/model` selector's refresh no longer start overlapping passes for the same catalog.
- Changed the built-in xAI default and the shipped xAI and OpenRouter workflow and subagent fallback chains from Grok 4.5 to Grok 4.6, documented those fallbacks as `xai/grok-4.6:xhigh` and `github-copilot/grok-4.6:xhigh`, and refreshed model-selection guidance from the latest DeepSWE v1.1 and Artificial Analysis v4.1.1 results.
- Optional DBOS durable execution now makes `/workflow resume` recovery idempotent, re-stamps `executor_id` on a successful step checkpoint, and keeps the SDK's internal admin server off by default.
- MCP OAuth browser launch now succeeds from WSL when the Linux working directory is not reachable from Windows.
- `ask_user_question` no longer submits an empty or whitespace-only **Type something.** answer. The readiness-gate tool can treat **Chat about this** as a plain option via `createAskUserQuestionToolDefinition({ chatAsOption: true })`.

### Fixed

- **The whole transcript stays readable while the `ask_user_question` dialog is open** ([#2378](https://github.com/bastani-inc/atomic/issues/2378)). The dialog is now pinned to the bottom as an overlay, so the transcript keeps its full viewport height and full page step. The overlay is bounded so at least six transcript rows always survive on a short terminal, and the rows it covers are added back to the transcript's scroll extent. Transcript page, Home/End, and wheel input remain available while the questionnaire's Notes editor is active.
- **The active questionnaire row stays visible on a short terminal.** The dialog now keeps a window around every active row, including single- and multi-select choices, Next, Submit, Cancel, inline sentinels, and focused text inputs.
- The `ask_user_question` dialog no longer draws one preview row at the wrong horizontal offset. The active-row mark now terminates its APC string with ST as ECMA-48 requires instead of BEL, and both interactive renderers strip the mark centrally before painting.
- Interactive startup now exits quietly when Ctrl+C stops the engine during first-paint binding, without leaking expected in-flight RPC transport failures.
- Bare `/` and slash-command drafts typed while explicitly loaded extension and workflow packages finish loading now remain in the interactive editor until Enter is pressed.
- Queue-control RPC frames now remain reachable during long-running prompts. When all overlapping remote `clear_queue` calls fail, Atomic restores the pre-clear host queue only if no later `queue_update` has supplied engine truth, including an empty queue ([#2516](https://github.com/bastani-inc/atomic/issues/2516)).
- Parallel `edit` calls that share a `[path#TAG]` are now applied as one snapshot-anchored batch, so later siblings no longer fail with `file content changed before write` after the first write mints a new tag.
- PDF extraction no longer throws `TypeError: Math.sumPrecise is not a function` on Node runtimes without that builtin.
- Updated the Z.AI and Z.AI Coding Plan defaults to the catalog-valid `glm-5.3` model; direct GLM-5.3 chains use `:high`, while OpenRouter and Baseten remain documented provider-specific exceptions because their catalogs do not expose GLM-5.3 ([#2459](https://github.com/bastani-inc/atomic/issues/2459)).
- Hardened hashline edits against unsafe integer anchors and oversized numeric ranges (inclusive ranges are capped at 100,000 lines before expansion), preserved hunk-looking `+TEXT` rows as literal payloads with a parser warning, omitted the synthetic terminal-newline row consistently across whole-file, truncated, and selector reads for LF and CRLF text while retaining genuine blank lines, and kept copied read output's original terminal-newline state when writing it back.
- Codex fast mode now sends the real ChatGPT routing contract only when the final request body uses `service_tier: priority`. Final non-priority requests keep `originator: pi` with no routing hint, and routing changes close cached sockets before reuse.
- `message_update` events on the JSON and RPC wire now carry the cumulative provider-reported `usage` on every streaming update (upstream #7982) and — only when the provider reports one — `endTurn`. A non-assistant message on this event throws instead of emitting an invented zeroed usage.
- `triggerTurn: false` no longer steers an active run (upstream #8022). A custom message queued with the turn trigger disabled is appended and reaches the model on a later turn instead of being delivered into the running one.
- The system prompt ends with a newline after the working-directory line, in both the default and custom-prompt branches (upstream #7887).
- Extension tool output with no registered renderer collapses to a ten-line preview with Atomic's expand hint instead of printing the whole result (upstream #7979).
- Fullscreen drag-selection copy reports honest failure (upstream #8110). The copy routes through Atomic's clipboard path, so a host clipboard that never received the text flashes "Copy failed" instead of a false "Copied!".
- A mouse release reported with the generic SGR button code — the form some terminals send (upstream #7963) — ends a fullscreen drag selection.
- Fullscreen viewport keys keep their Atomic routing on pi-tui 0.84.2. A focused overlay is still offered viewport keys first and the transcript still receives what it declines — including behind the `ask_user_question` dialog.
- Managed-tool startup status (`fd`/`rg` readiness) reports through the transcript instead of writing to the console, whose output previously landed in and corrupted the fullscreen alternate screen.
- The `-e`/`--extension` source trust prompt names the product through its branding constant, so a rebranded distribution asks users to trust its own name rather than a hardcoded one.
- A session that was named and then cleared now reads as cleared rather than never-named. `getLatestSessionName` and the session-list reducer preserve the distinction the session file already records.
- Corrected the [custom provider](docs/custom-provider.md) reference list and documented the previously undocumented `ModelRuntime` model-catalog options in the [SDK](docs/sdk.md) guide.

### Removed

- Removed the `workflow` parameter on bundled `web_search`. Models cannot select the curator per call.
- Removed fullscreen transcript search. `Ctrl+Shift+F` no longer opens a find box over the main transcript or an attached workflow stage chat; the four `tui.altScreen.search*` actions stay unbound and do nothing.
- Removed the bundled `@bastani/i-have-adhd` package and its default-on ADHD-friendly response mode. The `/i-have-adhd` command, `--no-adhd` flag, `.i-have-adhd-off` marker, the "ADHD Mode" status badge, and the `stop adhd mode` / `normal mode` control phrases no longer exist; responses now use Atomic's default style.

## [0.9.14-alpha.5] - 2026-08-19

### Added

- Added HumanLayer's bundled `show-me` skill for visual explanations, diagrams, code-shape sketches, and focused HTML artifacts. Sourced from https://github.com/humanlayer/skills and distributed under the MIT License.

### Fixed

- Interactive startup now exits quietly when Ctrl+C stops the engine during first-paint binding, without leaking expected in-flight RPC transport failures.
- Bare `/` and slash-command drafts typed while explicitly loaded extension and workflow packages finish loading now remain in the interactive editor until Enter is pressed.
- Queue-control RPC frames now remain reachable during long-running prompts. When all overlapping remote `clear_queue` calls fail, Atomic restores the pre-clear host queue only if no later `queue_update` has supplied engine truth, including an empty queue ([#2516](https://github.com/bastani-inc/atomic/issues/2516)).
- The `ask_user_question` dialog no longer draws one preview row at the wrong horizontal offset. In side-by-side layout the preview box border landed 11-13 columns early on the row carrying the `❯` cursor, cutting into the option label, and the damage followed the cursor between options. The active-row mark that caused it now terminates its APC string with ST as ECMA-48 requires instead of BEL, and both interactive renderers strip the mark centrally before painting, so it no longer reaches the terminal from any host — including workflow stage chats and other custom-UI mounts that do not go through a reserving bottom overlay.

## [0.9.14-alpha.4] - 2026-08-18

### Changed

- Bundled `web_search` no longer opens the curator confirmation UI by default. Persist `"workflow": "summary-review"` in web-search config or run `/curator on` to opt in.

### Removed

- The `workflow` parameter on bundled `web_search`. Models cannot select the curator per call.

## [0.9.14-alpha.3] - 2026-08-17

### Changed

- Documented built-in workflow and subagent Grok 4.6 fallbacks as `xai/grok-4.6:xhigh` and `github-copilot/grok-4.6:xhigh`.
- Optional DBOS durable execution now makes `/workflow resume` recovery idempotent, re-stamps `executor_id` on a successful step checkpoint, and keeps the SDK's internal admin server off by default.
- MCP OAuth browser launch now succeeds from WSL when the Linux working directory is not reachable from Windows.
- `ask_user_question` no longer submits an empty or whitespace-only **Type something.** answer. The readiness-gate tool can treat **Chat about this** as a plain option via `createAskUserQuestionToolDefinition({ chatAsOption: true })`.

### Fixed

- Parallel `edit` calls that share a `[path#TAG]` are now applied as one snapshot-anchored batch, so later siblings no longer fail with `file content changed before write` after the first write mints a new tag.
- PDF extraction no longer throws `TypeError: Math.sumPrecise is not a function` on Node runtimes without that builtin.

## [0.9.14-alpha.2] - 2026-08-17

### Added

- Two new unbound viewport actions, `tui.altScreen.lineUp` and `tui.altScreen.lineDown`, scroll the transcript by a single line ([#2385](https://github.com/bastani-inc/atomic/issues/2385)).
- Added the `fullscreenExitOutput` setting with a `/settings` row beside **Fullscreen scrollbar**. `"transcript"` (the default) prints the final transcript and session resume hint when an interactive session exits; `"resume-hint"` restores the terminal's previous screen and prints only the resume hint.
- Added the `defaultTools` setting, which selects the built-in tools a session starts with — an empty array starts with none — while extension and SDK custom tools stay registered and active, so `workflow`, `subagent`, `intercom`, `mcp`, and `web_search` survive a narrow selection. Explicit `--tools`, `--no-tools`, `--no-builtin-tools`, and `--exclude-tools` keep their precedence, a project array replaces the global array, and an unreadable value reads as unset.
- Added `--use-theme <name[/name]>`, which selects the interactive theme for one run without writing settings. `atomic --use-theme light/dark` follows terminal appearance; choosing another theme later in `/settings` applies immediately and saves normally, and `/export`/`/share` render with the run's theme. A missing or flag-shaped value reports `--use-theme requires a theme name`, and an unknown theme surfaces through the ordinary theme error.
- Added `expandPromptTemplates` to `sendUserMessage()`. The default stays `false`, so extension-authored messages keep their literal-send meaning; `true` dispatches registered extension commands and expands skill commands and prompt templates, and an unknown command falls through to a literal send.
- Added opt-in strict JSON-schema constrained sampling for built-in tool definitions behind the existing experimental gate (`ATOMIC_EXPERIMENTAL=1` or legacy `PI_EXPERIMENTAL=1`). `read`, `bash`, `edit`, `write`, `find`, `search`, `ls`, `ask_user_question`, `todo`, and the harness tool wrapper declare `{ type: "json_schema", strict: "prefer" }` only while the gate is on, and a definition carrying its own constraint — including `structured-output`'s caller-supplied schema or an explicit `false` opt-out — keeps it rather than being re-wrapped.
- Added the Cloudflare AI Gateway Workers AI binding transport to the public surface: `createGatewayBindingFetch`, the `CLOUDFLARE_GATEWAY_BINDING_AUTH_SENTINEL` placeholder credential, and the binding types are re-exported from `@bastani/atomic`, and [Providers](docs/providers.md) documents the no-API-token path with a working provider-extension example.
- Added the `SessionNameState` type and `getSessionNameState()` to the public SDK surface. `getSessionNameState()` reports whether a session was never named, named and then cleared, or currently holds a name — the distinction the session file already records — where `getLatestSessionName` returns only the name.
- Same-name skills stay independently selectable. `/skill:name` still uses the current project/user-over-package winner, and Atomic now keeps every distinct file as a source-qualified command such as `/skill:tdd@user` and `/skill:tdd@builtin`. Autocomplete, `pi.getCommands()`, RPC `get_commands`, the model-visible skill list, collision diagnostics, and in-process child `skills` selectors share those identities. A qualified selector is exact: unknown or ambiguous aliases error instead of falling back to the winner ([#2328](https://github.com/bastani-inc/atomic/issues/2328)).
- Added `sessionScopedExtensionState()` to the public surface: a host primitive for extension state that must survive `/reload`. Reloading file extensions re-evaluates their module graph, so module-scoped singletons are recreated while the session keeps running; the primitive resolves an extension's `events` facade back to the canonical session event bus it forwards to and returns the state already registered for that bus under a caller-chosen key, creating it on first use. Every load generation of one session re-binds to the same state object while two in-process sessions with distinct event buses stay isolated, and entries live exactly as long as the bus they serve. The key namespace is session-wide: callers must prefix keys with a stable extension identity and a version suffix (e.g. `"my-extension:state:v1"`) so two extensions cannot collide and an extension whose state shape changes can decline an incompatible predecessor. [Extensions](docs/extensions.md#session-scoped-in-memory-state) documents the required scope, versioning, reload behavior, and namespace. No host or extension code path calls it yet ([#2462](https://github.com/bastani-inc/atomic/issues/2462) by [@makgunay](https://github.com/makgunay)).

### Changed

- Adopted the pi 0.84.2 runtime across all six `@earendil-works/pi-*` dependencies ([#2385](https://github.com/bastani-inc/atomic/issues/2385)). Provider fixes arrive with `pi-ai`: OpenAI Responses namespace handling, DeepSeek `max_tokens` and case-insensitive base URLs, Bedrock empty tool-argument keys, Google length stops alongside tool calls, Codex `end_turn`, upstream buffer retry, Cloudflare strict-tools gating, and DeepSeek V4 Flash low effort. Renderer fixes arrive with `pi-tui`: full-width row painting, repaint after an idle focus loss, LaTeX control-space handling across line endings, focused-overlay wheel/viewport-key deferral, and generic SGR mouse release. Mistral models now stream over a native HTTP transport — the Mistral SDK is no longer installed — and `PI_TUI_ESC_TIMEOUT` (default `100` ms over SSH, `10` ms otherwise) tunes how long the renderer waits after a lone `ESC` before treating it as Escape.
- The exported `Theme` constructor's color maps now accept the optional `scrollbarThumb` token, with the same fallback encoded in the typebox and JSON theme schemas, so every user-authored theme keeps validating and rendering. Unused leftover `searchMatchBg` / `searchMatchText` keys remain accepted so older theme files still load.
- Automatic theme detection starts its colour-scheme and background probes concurrently instead of sequentially, halving the worst-case per-startup detection delay from 200 ms to 100 ms.
- Concurrent interactive model-catalog refreshes share one in-flight pass per runtime and effective network policy — keyed also on credential state and catalog content — so startup's refresh and the `/model` selector's refresh no longer start overlapping passes for the same catalog, and cancellation stays synchronous.
- Changed the built-in xAI default and the shipped xAI and OpenRouter workflow and subagent fallback chains from Grok 4.5 to Grok 4.6, and refreshed model-selection guidance from the latest DeepSWE v1.1 and Artificial Analysis v4.1.1 results.

### Fixed

- Updated the Z.AI and Z.AI Coding Plan defaults to the catalog-valid `glm-5.3` model; direct GLM-5.3 chains use `:high`, while OpenRouter and Baseten remain documented provider-specific exceptions because their catalogs do not expose GLM-5.3 ([#2459](https://github.com/bastani-inc/atomic/issues/2459)).

- Hardened hashline edits against unsafe integer anchors and oversized numeric ranges (inclusive ranges are capped at 100,000 lines before expansion), preserved hunk-looking `+TEXT` rows as literal payloads with a parser warning, omitted the synthetic terminal-newline row consistently across whole-file, truncated, and selector reads for LF and CRLF text while retaining genuine blank lines, and kept copied read output's original terminal-newline state when writing it back. Bare CR line-ending compatibility behavior is unchanged.
- Codex fast mode now sends the real ChatGPT routing contract only when the final request body uses `service_tier: priority`. The shared Codex transport applies `originator: codex_cli_rs` and `x-codex-routing-hint: model=<model>;tier=priority` across HTTP/SSE, WebSocket, fallback, reconnect, retry, compaction-planner, and branch-summary requests. Transport detection also supports renamed providers, credential-resolved endpoints, and monitoring proxies that retain `api: "openai-codex-responses"`. Final non-priority requests keep `originator: pi` with no routing hint, and routing changes close cached sockets before reuse.
- `message_update` events on the JSON and RPC wire now carry the cumulative provider-reported `usage` on every streaming update (upstream #7982) and — only when the provider reports one — `endTurn`, the provider's explicit end-of-turn signal such as OpenAI Codex `end_turn`. A non-assistant message on this event throws instead of emitting an invented zeroed usage, and an unreported `endTurn` leaves the key absent rather than present-but-`undefined`.
- `triggerTurn: false` no longer steers an active run (upstream #8022). A custom message queued with the turn trigger disabled is appended and reaches the model on a later turn instead of being delivered into the running one.
- The system prompt ends with a newline after the working-directory line, in both the default and custom-prompt branches (upstream #7887).
- Extension tool output with no registered renderer collapses to a ten-line preview with Atomic's expand hint instead of printing the whole result (upstream #7979).
- Fullscreen drag-selection copy reports honest failure (upstream #8110). The copy routes through Atomic's clipboard path, so a host clipboard that never received the text flashes "Copy failed" instead of a false "Copied!".
- A mouse release reported with the generic SGR button code — the form some terminals send (upstream #7963) — ends a fullscreen drag selection. Previously the release was dropped, which left the drag open, copied nothing, and never activated the OSC 8 link under the press, including Atomic's jump-to-end link.
- Fullscreen viewport keys keep their Atomic routing on pi-tui 0.84.2. A focused overlay is still offered viewport keys first and the transcript still receives what it declines — including behind the `ask_user_question` dialog — and every settled overlay answer other than `true` — an async handler resolving `undefined`, a rejection, or an overlay with no input handler at all — is treated as a decline and replayed to the transcript.
- Managed-tool startup status (`fd`/`rg` readiness) reports through the transcript instead of writing to the console, whose output previously landed in and corrupted the fullscreen alternate screen; readiness from a superseded session is ignored after a transcript rebuild so it cannot appear in the replacement session.
- The `-e`/`--extension` source trust prompt names the product through its branding constant, so a rebranded distribution asks users to trust its own name rather than a hardcoded one.
- A session that was named and then cleared now reads as cleared rather than never-named. `getLatestSessionName` and the session-list reducer preserve the distinction the session file already records.
- Corrected the [custom provider](docs/custom-provider.md) reference list: the streaming implementations live in `dist/api/` (two of the six named files did not exist at the documented location), `dist/providers/` holds per-vendor configuration, and `mistral-conversations` is documented as native Mistral Chat Completions streaming now that the Mistral SDK is gone. Documented the previously undocumented `ModelRuntime` model-catalog options — `allowModelNetwork`, `modelRefreshTimeoutMs`, `modelsStorePath`, `modelsStore`, and `refresh({ allowNetwork, force, signal })` — with their implementation-verified defaults in the [SDK](docs/sdk.md) guide.

### Removed

- Removed fullscreen transcript search. `Ctrl+Shift+F` no longer opens a find box over the main transcript or an attached workflow stage chat; the four `tui.altScreen.search*` actions stay unbound and do nothing.
- Removed the bundled `@bastani/i-have-adhd` package and its default-on ADHD-friendly response mode. The `/i-have-adhd` command, `--no-adhd` flag, `.i-have-adhd-off` marker, the "ADHD Mode" status badge, and the `stop adhd mode` / `normal mode` control phrases no longer exist; responses now use Atomic's default style.

## [0.9.14-alpha.1] - 2026-08-14

### Fixed

- **The whole transcript stays readable while the `ask_user_question` dialog is open** ([#2378](https://github.com/bastani-inc/atomic/issues/2378)). The dialog used to mount inline in the fullscreen dock, where it is a flex sibling of the transcript viewport, so a tall side-by-side questionnaire took the transcript's rows: on a 200×40 terminal the viewport collapsed from 34 rows to 6, and because the page step is derived from that viewport, `PageUp` crawled two lines at a time through a six-line window. The dialog is now pinned to the bottom as an overlay, which is not measured into the layout, so the transcript keeps its full viewport height and full page step — a single `PageUp` moves a full page and `Home` reaches the top of the scrollback. The overlay is also bounded so at least six transcript rows always survive on a short terminal, and the rows it does cover are added back to the transcript's scroll extent, so scrolling to the end raises the newest output into the visible strip instead of leaving it stranded behind the dialog. Transcript page, Home/End, and wheel input remain available while the questionnaire's Notes editor is active. Notes keeps text and edit keys, and the dialog still owns arrows, `enter`, `tab`, `space`, `esc`, clicks, and selection.
- **The active questionnaire row stays visible on a short terminal.** Bounding a tall dialog has to drop rows, and dropping them from a fixed head cropped away the control you were using: at 80 columns a single-select option disappeared as the arrow keys moved down, multi-select choices plus their Next and Submit rows vanished, and the focused Notes input was hidden. The dialog now keeps a window around every active row, including single- and multi-select choices, Next, Submit, Cancel, inline sentinels, and focused text inputs.

### Added

- `ctx.ui.custom()` accepts `reserveTranscriptRows` for bottom-anchored overlay mounts. A blocking dialog sets it to be height-bounded against the terminal and to have its covered transcript-bottom suffix added to the scroll extent. Numeric and percentage `maxHeight` limits are applied before active-row windowing, so the focused control is not removed by a second fixed-head crop. Top and bottom margins are included in the overlay geometry, and concurrent overlays share the connected covered suffix instead of adding duplicate padding. Mount and resize height changes settle through one automatic repaint. A hidden overlay reserves nothing, and the reserve returns when it becomes visible again; handle-based, closed, or raw host removal releases the exact registration. Reserving overlays require a bottom anchor and reject `row` or a nonzero `offsetY`; leave the option unset for an overlay meant to take the screen, such as the workflow graph.
- `OVERLAY_ACTIVE_ROW_MARKER` is exported for components mounted that way. Embed it in the line you most need kept — the selected row of a list — and the bound places what it keeps around that row instead of taking a fixed head. It is a zero-width APC sequence the host strips before painting, so it never reaches the terminal.

## [0.9.13] - 2026-08-13

Cumulative release of the `0.9.13-alpha.1` – `0.9.13-alpha.4` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.

### Breaking Changes

- **Interactive TTY sessions always use the fullscreen renderer.** The `--tui-mode` flag, the `tuiMode` JSON setting, the `/settings` TUI-mode row, the settings-manager accessors, and the exported `TuiMode` alias are removed, with no replacement for `--tui-mode regular`. `fullscreenScrollbar` remains configurable, non-TTY output continues through print mode, and internal `TERM=dumb` and CI guards keep the main-screen renderer as a non-user-selectable fallback.
- **The `bash` tool no longer accepts its `async` parameter,** and the `__atomic_bash_job` and `__atomic_bash_job_cancel` polling and cancellation commands are gone. Drop `async` from bash calls, and run a long-lived command in a separate shell or process manager.
- **Streaming `message_update` events are delta-only across the public API.** JSON, RPC, SDK, and extension subscribers receive only `type` plus the raw `assistantMessageEvent`; the cumulative `message` and wire-only `assistantMessageEvent.partial` fields are gone. `message_start` seeds the message, ordered deltas build it without deduplication, and `message_end` supplies the authoritative final message, so a consumer that read `message` or `partial` must accumulate deltas itself. This also removes quadratic wire amplification: an 8000-character answer wrote 8,428,280 bytes across 501 frames and now writes 65,588.
- **`ctx.ui.custom()` components must report input consumption.** `handleInput` returns `true` when it consumes a key and `false` or `undefined` when it does not; a handler returning `void` must migrate. In fullscreen mode an unhandled viewport key may reach the transcript, and a remote component disposed before its reply is deliberately dropped rather than replayed.
- **Custom extension runtime and UI implementations must adopt lifecycle fields.** An exported `ExtensionRuntime` must provide `trackEventBusSubscription(unsubscribe)`, and `ExtensionUIContext.getChatRenderSettings()` must return `markdownTransformers`. `pi.events` is bound to an extension instance: subscriptions auto-unsubscribe on reload or disposal, and calling `on()` or `emit()` through a captured stale handle throws.
- **Extension provider OAuth `login` and `refreshToken` take abort signals.** The contracts are `login(callbacks, signal)` and `refreshToken(credentials, signal)` with a required concrete `AbortSignal`, so cancelling authentication or credential refresh abandons the extension's in-flight network work. Implementations must accept the parameter and forward it into their requests.
- **`ModelRegistry.refresh()` returns `ModelsRefreshResult`** — `{ aborted, errors }`, a per-provider `ReadonlyMap<string, Error>` plus a cancellation flag — instead of `Promise<void>`, so a refresh in which some providers failed, or which was aborted, is no longer indistinguishable from a clean success. A caller that awaited it only for completion needs no change.
- **`ModelRuntime.setRuntimeApiKey()` no longer refreshes the model catalog.** It takes auth-cancellation options and does only what its name says: record the runtime key and mark the provider configured, aborting early if the signal is already aborted. A caller that needs a fresh catalog must now call `refresh({ providers: [providerId], signal })`.

### Added

- Added the fullscreen interactive TUI layout. The transcript scrolls above a sticky dock holding the editor, status line, usage meter, extension widgets, and footer, and terminal resizes preserve that layout. Wheel and trackpad gestures reach focused workflow overlays before the transcript viewport, and events those overlays do not consume fall through ([#2269](https://github.com/bastani-inc/atomic/pull/2269), [#2274](https://github.com/bastani-inc/atomic/pull/2274)).
- Added a draggable fullscreen transcript scrollbar configured as `auto`, `always`, or `hidden` through `/settings`. Themes may set `scrollbarThumb`, which falls back to `selectedBg` for existing custom themes ([#2275](https://github.com/bastani-inc/atomic/pull/2275)).
- Added fullscreen transcript navigation for page, opt-in half-page, marked-message, `home`, and `end` actions, while `ctrl+home`, `ctrl+end`, `ctrl+pageUp`, and `ctrl+pageDown` remain available to the editor. Matching viewport bindings pass through to focused overlays and inline custom components. Prompt history can be enabled with explicit `tui.editor.historyPrevious`/`historyNext` bindings such as `ctrl+p`/`ctrl+n`, which take precedence over ordinary action handlers while the editor is focused ([#2281](https://github.com/bastani-inc/atomic/pull/2281), [#2296](https://github.com/bastani-inc/atomic/pull/2296), [#2300](https://github.com/bastani-inc/atomic/pull/2300)).
- Added a clickable, centered jump-to-bottom indicator above the fullscreen input dock when the transcript is scrolled away from the live end. It displays the live `tui.altScreen.bottom` key binding and supports mouse activation ([#2361](https://github.com/bastani-inc/atomic/issues/2361)).
- Windows fullscreen sessions paste system clipboard text on a secondary-button press, with focus checks and bracketed-paste framing preserved for host and engine-owned editors.
- Added generated session summaries to the resume picker, so `/resume` and `atomic -r` show what a conversation was about in a dedicated summary column beside the session name or first message. Atomic writes a one-line summary once the agent goes idle, reusing the session's existing model, credentials, stream function, and retry policy, and persists it as a `session_summary` entry anchored to the message it describes; a later message or branch summary retires it. Generation is best-effort and invisible: it is skipped for very short sessions, workflow stage sessions, and `--print`/JSON modes, never blocks a turn, is cancelled when the next prompt starts, and a slow request that outlives the conversation discards its own result. Set `sessionSummary.enabled` to `false` to disable it ([#1033](https://github.com/bastani-inc/atomic/issues/1033)).
- Added the bundled, default-on ADHD-friendly response mode. Its hidden rules message persists across turns, resumes, branches, and compaction; `/i-have-adhd on|off`, `--no-adhd`, `.i-have-adhd-off`, and the `stop adhd mode` / `normal mode` phrases control it.
- Added `atomic auth check` to verify a provider or model's effective authentication before a session starts. It reports `ready`, `not_ready`, or `invalid`, supports JSON output and a no-refresh read-only mode, and emits no credential material unless the explicit `--credentials` opt-in is present.
- Added an **experimental** `@bastani/atomic/client` entrypoint with `RemoteSession` lifecycle management and transcript projection helpers for remote protocol sessions.
- Added the configurable and package-exported `createCodingAgentHarness()` factory, with Atomic's hashline coding tools, live prompt metadata, Atomic and `PI_*` bash session variables, and injectable pi-agent-core execution environments for read (including directory trees), bash, edit, write, and find, rejecting at runtime an environment that omits `renameFile()`. The default `search` tool remains local until its filesystem and matching pipeline gains a complete remote operations seam; read and edit retain local path-variant probes and notebook projection, read also retains local archive, SQLite, and internal-resource selectors, write retains local generated-file, shebang, conflict, and resource helpers, and bash validates its cwd locally.
- Added `pi.registerMarkdownTransformer()` for ordered, synchronous, display-only Markdown transforms in user, assistant, and assistant-thinking output. Each transformer receives the prior output plus message type, streaming state, and available width; an error leaves the current display text intact and never alters stored messages or model context ([#7231](https://github.com/earendil-works/pi/pull/7231) by [@xl0](https://github.com/xl0)).
- Added themed Unicode Mermaid diagrams in interactive transcripts, with optional rendering while responses stream ([#7624](https://github.com/earendil-works/pi/pull/7624) by [@xl0](https://github.com/xl0)), and configurable terminal-friendly Unicode LaTeX math via the `markdown.latex` setting. See [Markdown settings](docs/settings.md#markdown).
- Added per-directory `AGENTS.override.md` context files. An override replaces that directory's `AGENTS.md` or `CLAUDE.md` while global and ancestor context still layers normally; the same canonical filename works through Atomic's legacy `.pi` and `PI_*` agent-directory aliases ([#7681](https://github.com/earendil-works/pi/pull/7681) by [@m3](https://github.com/m3)).
- Added `AI_AGENT=atomic` attribution at the CLI, RPC, and compiled-binary entry points and across Atomic-owned child-process environments, including bash and tool commands, isolated RPC children, subagent and workflow runners, MCP and web-access subprocesses, and the intercom broker. Child environments follow upstream's overwrite policy while cloning rather than mutating caller-supplied environment objects.
- Added arbitrary OpenAI-compatible `samplingParams` for custom model definitions, per-model overrides, extension providers, and per-request stream options. Parameters merge by key, preserve provider-specific names, and apply to OpenAI-compatible request bodies; a malformed configuration shape now fails during `models.json` loading. Added opt-in vLLM `compat.supportsThinkingTokenBudget` handling and `compat.supportsFinishReason` stop inference for finishless OpenAI-compatible streams.
- Added built-in Baseten support with `BASETEN_API_KEY` and the `zai-org/GLM-5.2` default, and Qwen Token Plan Individual with the documented subscription catalog, the shared international `QWEN_TOKEN_PLAN_API_KEY`, and `qwen3.8-max` as its default model.
- Tool output that spills to disk is now stored per session and reaped on age, so the system temp directory can no longer fill up with orphaned files. `bash` overflow logs, streamed spill files, and the in-memory-session tool-result fallback move into one owner- and session-scoped tree, `<tmpdir>/atomic-<uid>/<session-id>/`, created `0700` with `0600` files where the platform supports it; on Windows the domain-qualified account name plus a short digest replaces the uid, so two domain principals sharing a bare account name cannot land on one tree. Every component below the system temp directory is created and checked one level at a time: a symlink is refused, a foreign-owned directory is refused, and one of this account's left too permissive is tightened and re-checked. Refusal fails closed — the tool runs with no spill file rather than writing command output through a link someone else planted. On Windows that ownership check reads the security descriptor through PowerShell `Get-Acl` and decides from SIDs alone, caching a successful verification per path per process keyed to the directory's metadata identity ([#2245](https://github.com/bastani-inc/atomic/issues/2245)).
- Persisted tool-output files are capped at 64 MB each, cut on a character boundary even when a multi-byte character spans two chunks, with bytes that are not valid UTF-8 written through unchanged. A spill file that fails to write is no longer advertised, and a storage fault never changes the outcome of the command that produced the output. A deferred, unref'd sweep started shortly after session startup removes session temp trees and `tool-results` directories whose newest file is more than 30 days old, throttled once a day per target and guarded by an exclusive lock; session transcripts and `.jsonl` session files are outside its scope, and a tree belonging to a running session is never reaped.
- Added `terminate` to blocked extension `tool_call` results. A terminating blocked call suppresses the follow-up model request only when every finalized call in its batch also terminates.
- Added `CredentialSynchronizationError` for a credential mutation that commits successfully but fails to update local model state, so credential-save failures stay distinct from post-commit synchronization failures in interactive auth surfaces.
- `CreateAgentSessionOptions` and `ExtensionContext` carry an immutable typed subagent child capability policy for in-process tool registration, and `CreateAgentSessionOptions` supports construction-time system-prompt and inherited-context transforms, so an in-process child session applies typed policy without extension injection ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
- Added immutable system-prompt contribution constants for Atomic's builtin coding tools, keeping custom harnesses and tool registries aligned with the prompt metadata the builtin factories use.

### Changed

- Adopted the pi 0.84.1 coding-agent, agent, AI, client, protocol, telemetry, and TUI runtime surfaces, which supply the renderer behavior described above. Atomic keeps its isolated interactive engine, provider, path, and legacy `PI_*`/`.pi` compatibility surfaces, and uses the dependency's renderer and layout implementations rather than carrying forked copies.
- Bundled subagent launches use in-process `AgentSession` children with typed admission, canonical identities, and live continuation, and typed child policy now carries MCP direct-tool selection and admission-issued Intercom identity and capability, so an in-process extension consumes child policy without inheritable environment state ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
- Live tool-call argument previews no longer update while a call's arguments are still streaming. The assistant message is rebuilt from `message_update` deltas, and `toolcall_start`/`toolcall_delta` carry an unparsable fragment of JSON rather than a usable value, so the accumulator ignores them and installs the complete parsed call at `toolcall_end`. The tool card still appears as soon as the call starts, and the arguments it shows are correct and complete; the progressively filled preview is gone, which is the deliberate trade for removing the quadratic amplification above.
- Footer and workflow stage-chat extension statuses are joined with a dim `•` separator instead of a bare space, so adjacent statuses read as distinct items.

### Fixed

- Fixed a stall where a message queued during a turn was shown in the transcript but never answered, leaving the session idle until you typed another message. Escape and Ctrl+C hold queued work by pausing the agent's own steering and follow-up queues, but a message the agent loop has already polled is in neither queue: it is written straight into the transcript, so the interrupt cancelled its reply and nothing scheduled a new one. This was most visible after answering an `ask_user_question` questionnaire. An admitted message whose reply was cancelled before it produced any output now receives that reply, and a message sent while that reply is still streaming is delivered as soon as it finishes. Messages that are still held are unchanged, and a reply that had already streamed some output is never restarted ([#2362](https://github.com/bastani-inc/atomic/issues/2362)).
- Fixed fullscreen mouse routing for focused workflow graphs and stage chats. SGR/X10 wheel and click sequences reach the focused overlay through the bounded custom-input reply path before the alternate-screen viewport, so graph scrolling and click-to-attach work without changing keyboard viewport fallback ([#2303](https://github.com/bastani-inc/atomic/issues/2303)). OSC-8 jump-to-bottom activation lets the focused overlay claim its own transcript jump before falling back to the host transcript ([#2370](https://github.com/bastani-inc/atomic/issues/2370)).
- Fixed fullscreen viewport shortcuts stealing matching keys from a focused overlay or inline custom component: a focused handler receives the key exactly once and can return `false` to let a no-op fall through to transcript navigation, input replies correlate with each forwarded key, and transcript scrolling proceeds on an explicit `false`, a failed reply, or a bounded timeout. Fullscreen shutdown avoids restarting the replacement renderer.
- Fixed isolated overlay components such as workflow stage chat failing to re-render after a terminal height change, which could clip the composer and footer after a vertical resize, and fixed isolated fullscreen tool renderers rebuilding Kitty image components on every engine render, which retransmitted visible payloads and rendered the transcript twice per frame.
- Fixed fullscreen overlay text selection when one input read contains multiple SGR mouse reports. Reports are replayed one at a time, while Shift/Option/Ctrl-modified presses and motion stay out of application selection; modifier-bearing SGR releases still close an active selection, and legacy X10 release remains excluded because pi-tui cannot identify it for selection.
- Fixed main-chat model fallback to classify provider failures consistently with workflows, advance rejected credentials and incompatible or unavailable models to the next candidate, and restore the user-selected model at the next turn without overriding an explicit `/model` choice. A failure that requesting the same model again cannot repair leaves that model out of the chain for the rest of the turn at every reasoning level, while transient rate-limit and transport failures keep those reasoning variants. A context overflow compaction cannot resolve now advances the chain instead of ending the turn, a reasoning-level change no longer strands the session on the fallback model, and gRPC `ResourceExhausted` errors are retried like other transient failures ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
- Fixed a GitHub Copilot `openai-responses` session hard-stopping with `400 invalid_request_body` once a replayed tool call entered its context. The outbound sanitizer treated an *absent* `function_call.id` the same as a malformed one and synthesized a replacement from `call_id`; across a model-identity mismatch that value was always exactly 64 characters, the server rejected it, and because the value is a pure function of the tool call every resend rebuilt the same rejected identifier. An absent id is now left absent, only a present malformed id is repaired, and the synthesized-length budget and validator agree on a maximum of 63 characters ([#2204](https://github.com/bastani-inc/atomic/issues/2204)).
- Fixed a `null` request-header suppression marker being discarded instead of suppressing the provider default header. pi documents a `null` value in `ProviderHeaders` as suppressing the provider or API default header of the same name, but Atomic narrowed the type and enforced the narrowing at runtime in two places, so the default was sent anyway and a header map of only suppression markers arrived as `{}`. Both filters are gone. This was never reachable from `models.json`, whose schema rejects `null`; the marker has to come from pi's catalog headers, credential-derived request auth, or the `before_provider_headers` hook. The agent-session filter also affected compaction, auto-compaction, post-tool compaction, and branch-summary requests.
- Fixed the model list widening to a provider's whole built-in catalog after an extension registered an additive provider override, so a GitHub Copilot account entitled to one model could be offered every catalog model. Republishing the model list now carries the previous availability projection across by model ID, including an empty one, and a registration exposes a provider's catalog provisionally only when it is what newly configures the provider.
- Fixed registration-triggered offline catalog refreshes racing newer credential-resolved refreshes, so configured `$ENV` and `!command` credentials remain authoritative ([#2318](https://github.com/bastani-inc/atomic/issues/2318)), and fixed `atomic auth check --no-refresh` resolving command-backed API keys as configured values rather than treating `!command` as a literal credential. `--json` no longer labels an unresolved model as its provider, and `print-api-key` consistently rejects `--min-expiry` before or after a `--` terminator.
- Fixed compaction across the board: manual `/compact` keeps agent events live, cancels unfinished automatic compaction safely, and then runs the requested compaction instead of rejecting it or returning an automatic boundary with different parameters; input queued during manual compaction sends after completion; a response truncated before its requested output limit gets one compact-and-retry attempt while output-cap stops retain bounded direct continuation; a stale automatic post-compaction retry no longer resumes after a manual `/compact`; a short transcript with repeated below-cap truncations no longer retries compaction more than once; and `ChatSessionHost` no longer leaves queued prompts stuck when compaction is cancelled or fails.
- Fixed a chat surface built on `ChatSessionHost` — the workflow stage chat, and any extension pane using it — showing no assistant text when opened onto a session that was already streaming. A host mounted part-way through a turn saw neither the `message_start` nor the fragments that had already gone past. A host now adopts the session's in-flight assistant message when it mounts and continues it with later deltas, and streaming consumers assemble into their own copy of the message rather than mutating the provider's live partial. `ChatSessionHost` also no longer tail-windows settled assistant rows while a later answer streams.
- Fixed in-process child sessions losing their admission-issued nesting depth and delegation limit; `SubagentChildPolicy` carries both the admitted `depth` and the effective `maxSubagentDepth` ([#2220](https://github.com/bastani-inc/atomic/pull/2220), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
- Reduced redundant repaints of a Ctrl+O-expanded live subagent widget, which were scrolling the chat window to the bottom and clearing terminal scrollback during a run. Progress is now published only at milestones that change what the widget shows. A genuine above-fold milestone change can still require that redraw when the dock plus the live widget exceed the terminal height; that path is upstream (earendil-works/pi#4785, #7194) ([#2213](https://github.com/bastani-inc/atomic/pull/2213), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
- Fixed the scroll anchor in `ScrollableComponentViewport` drifting when its content changes height. The offset is now re-derived from the anchored row rather than compensated only on growth, and a windowed component can report a row map (`rowSegments`) identifying each run of rows by the entry that produced it, so a compaction that deletes entries above the viewer still leaves them on the entry they were reading; if that entry was compacted away, the viewer lands on the nearest surviving entry above it ([#2214](https://github.com/bastani-inc/atomic/pull/2214), regression source [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
- Fixed workflow runs awaiting human input to use the blue `？` indicator in the BACKGROUND panel, `/workflow connect` picker, and `/workflow status` listing, including prompts raised by hidden nested workflow children; the indicator returns to the run's current state when the prompt resolves.
- Release archives no longer ship packages built for a different architecture. Every archive was produced on one Linux x64 runner whose `node_modules` was copied verbatim, so 0.9.12's arm64 archives both contained `@esbuild/linux-x64`. The build now prunes the `@embedded-postgres` leaves that cannot run on the archive being staged, then fails if any staged package's own `os`/`cpu`/`libc` declaration is incompatible ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
- Fixed extension event-bus subscriptions surviving session reload or disposal ([#7193](https://github.com/earendil-works/pi/issues/7193), [#7656](https://github.com/earendil-works/pi/pull/7656) by [@tudoroancea](https://github.com/tudoroancea)), and moved the public `isStaleExtensionContextError()` predicate and its marker to the host beside the error path, so session replacement and reload guards cannot drift from host wording.
- Fixed image blocks returned by builtin and extension tools entering history at their original size; they now honor `images.autoResize` after `tool_result` handlers run, and a failed conversion leaves the original intact. Fixed same-state `ctx.ui.setToolsExpanded()` calls still traversing the transcript, extension-facing `ModelRegistry.complete()` bypassing the active model runtime, and custom editors installed through `ctx.ui.setEditorComponent()` not inheriting the default autocomplete dropdown limit.
- Fixed bare exact `--model` IDs shared by providers choosing catalog order; Atomic uses the sole authenticated matching provider or reports an ambiguity with fully qualified choices ([#7327](https://github.com/earendil-works/pi/issues/7327)). Fixed `/model <name>` and `/scoped-models` waiting for remote catalog refreshes before using cached models ([#7153](https://github.com/earendil-works/pi/issues/7153), [#7443](https://github.com/earendil-works/pi/issues/7443)), and project `retry.provider` overrides discarding unmodified global provider retry fields ([#7572](https://github.com/earendil-works/pi/issues/7572)).
- Fixed credential-resolved model endpoints — including GitHub Copilot Business and Enterprise hosts — falling back to the catalog endpoint for Verbatim Compaction, branch summaries, MCP sampling, and direct web summaries ([#6768](https://github.com/earendil-works/pi/issues/6768), [#7579](https://github.com/earendil-works/pi/issues/7579)).
- Fixed Windows fallback `find` glob patterns with paths returning no results when `fd` uses native separators ([#6817](https://github.com/earendil-works/pi/issues/6817)), `find` results at filesystem roots losing their first segment ([#6104](https://github.com/earendil-works/pi/issues/6104), [#7569](https://github.com/earendil-works/pi/pull/7569)), Git Bash, MSYS, Cygwin, and WSL drive paths resolving against the current Windows drive ([#7064](https://github.com/earendil-works/pi/issues/7064), [#7547](https://github.com/earendil-works/pi/issues/7547)), and linked project session directories being omitted from the session picker ([#7552](https://github.com/earendil-works/pi/issues/7552)).
- Fixed standalone x64 binaries failing to start on pre-Haswell CPUs that lack AVX2/BMI2 by using Bun's baseline target, including musl archives ([#7390](https://github.com/earendil-works/pi/issues/7390)), malformed package-manifest resource fields blocking valid resources or crashing startup ([#7187](https://github.com/earendil-works/pi/issues/7187)), Git package updates left incomplete after `git clean` fails ([#7570](https://github.com/earendil-works/pi/issues/7570)), and transient management requests not being retried ([#7632](https://github.com/earendil-works/pi/issues/7632)).
- Fixed native macOS `Cmd+V` so clipboard images paste into the editor without duplicate image inserts from terminal key repeats ([#2186](https://github.com/bastani-inc/atomic/issues/2186)), and reduced automatic terminal-theme detection latency by starting color-scheme and background probes together.

### Removed

- Removed background bash jobs and the `async` parameter from the `bash` tool. Bash commands always run in the foreground; use a separate shell or process manager when a command must outlive the tool call. Polling and cancellation commands and automatic completion follow-ups are gone.
- Removed the Ctrl+X shortcut for copying the last agent message; `/copy` still copies it to the clipboard and confirms in the status line.
- Removed the `tsx` dependency from the shipped runtime. The Intercom broker's Node path runs on Atomic's bundled `jiti` loader, which is dependency-free pure JavaScript. `tsx` was the only thing pulling `esbuild`, so this removes `esbuild`, its 26 platform packages, and `fsevents` from `npm-shrinkwrap.json` and from every release archive — about 11.5 MB across 72 files. Worth noting for extension authors: TypeScript reached through the Node broker path is now transpiled by jiti (Babel-based) rather than esbuild, and jiti defaults JSX support and `tsconfig` path aliases to off while enabling legacy decorators. An extension that resolved `tsx` from the bundled `node_modules` was relying on an undocumented dependency and will no longer find it ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
- Removed the Atomic-only workflow custom-terminal mouse-scroll control from the isolated engine protocol and host terminal-mode arbiter; remote overlays retain the typed autowrap control only. Focused workflow overlays leave `ctrl+t` to the host `app.thinking.toggle` action, while the tree selector still receives the same key through `app.tree.filter.noTools`. Fullscreen pi-tui owns application selection over overlays; copy still uses OSC 52, and terminals that refuse OSC 52 writes require the modifier-drag bypass (Shift/Option, as provided by the terminal).

## [0.9.13-alpha.4] - 2026-08-13

### Added

- Added a clickable, centered jump-to-bottom indicator above the fullscreen input dock when the transcript is scrolled away from the live end; it displays the live `tui.altScreen.bottom` key binding and supports mouse activation ([#2361](https://github.com/bastani-inc/atomic/issues/2361)).

### Removed

- Removed the Ctrl+X shortcut for copying the last agent message; the `/copy` slash command still copies the last agent message to the clipboard and confirms in the status line.

### Fixed

- Fixed a stall where a message queued during a turn was shown in the transcript but never answered, leaving the session idle until you typed another message. Escape and Ctrl+C hold queued work by pausing the agent's own steering and follow-up queues, but a message the agent loop has already polled is no longer in either queue: it is written straight into the transcript, so the interrupt cancelled its reply and nothing scheduled a new one. This was most visible after answering an `ask_user_question` questionnaire, which admits the queued message and immediately opens the request the interrupt then cancels. An admitted message whose reply was cancelled before it produced any output now receives that reply, and a message you send while that reply is still streaming is delivered as soon as it finishes. Queued messages that are still held are unchanged: Escape still restores them to the editor and leaves the session paused, and a reply that had already streamed some output is never restarted ([#2362](https://github.com/bastani-inc/atomic/issues/2362)).
- Fixed isolated overlay components such as workflow stage chat failing to re-render after a terminal height change, which could clip the composer and footer after a vertical resize.
- Fixed OSC-8 jump-to-bottom activation to let the focused workflow overlay claim its own transcript jump before falling back to the host transcript ([#2370](https://github.com/bastani-inc/atomic/issues/2370)).

## [0.9.13-alpha.3] - 2026-08-13

### Breaking Changes

- The `bash` tool no longer accepts its `async` parameter, and the `__atomic_bash_job` and `__atomic_bash_job_cancel` polling and cancellation commands have been removed. Drop `async` from bash calls; run long-lived commands in a separate shell or process manager instead.

### Added

- Added generated session summaries to the resume picker, so `/resume` and `atomic -r` show what a conversation was about in a dedicated summary column beside the session name or first message. Atomic writes a one-line summary once the agent goes idle, reusing the session's existing model, credentials, stream function, and retry policy with a dedicated one-line prompt and no reasoning request; the stored line has its whitespace collapsed and length clamped so a long response cannot break picker layout. Each summary is persisted as a `session_summary` session entry anchored to the last user/assistant message it describes, and the picker shows it only while that message is still the newest one — a later message, or a later branch summary, retires it and the summary column shows "No summary available." instead, exactly as it does when a summary has not been generated, has failed, or is still in flight; the session name and first message are never displaced. Summaries are matched by picker search, and their token usage is counted toward session usage totals and the footer. Generation is best-effort and invisible: it is skipped for very short sessions, workflow stage sessions, and `--print`/JSON modes, it never blocks or delays a turn, it is cancelled when the next prompt starts or the session shuts down, and a slow request that outlives the conversation discards its own result rather than persisting a stale summary. Set `sessionSummary.enabled` to `false` to disable it ([#1033](https://github.com/bastani-inc/atomic/issues/1033)).

### Removed

- Removed background bash jobs and the `async` parameter from the `bash` tool. Bash commands now always run in the foreground; use a separate shell or process manager when a command must outlive the tool call. Polling and cancellation commands and automatic completion follow-ups are no longer available.

## [0.9.13-alpha.2] - 2026-08-12

### Breaking Changes

- **Streaming `message_update` events are now delta-only across the public API.** JSON, RPC, SDK, and extension subscribers receive only `type` plus the raw `assistantMessageEvent`; the cumulative `message` and wire-only `assistantMessageEvent.partial` fields are gone. `message_start` seeds the message, ordered deltas build it without deduplication, and `message_end` supplies the authoritative final message. Consumers that read `message` or `partial` from `message_update` must accumulate deltas themselves. This also removes quadratic wire amplification: an 8000-character answer wrote 8,428,280 bytes across 501 frames and now writes 65,588, with bytes growing linearly rather than quadratically.
- **Extension provider OAuth `login` and `refreshToken` now take abort signals.** The fork-owned contracts are `login(callbacks, signal)` and `refreshToken(credentials, signal)`, both with a required concrete `AbortSignal`. Atomic forwards cancellation through both operations, so cancelling authentication or credential refresh abandons the extension's in-flight network work. Provider implementations must accept the new parameter and forward it into their network requests.
- **`ModelRegistry.refresh()` returns `ModelsRefreshResult` instead of `Promise<void>`.** Callers now receive pi's real result — `{ aborted, errors }`, a per-provider `ReadonlyMap<string, Error>` plus a cancellation flag — so a refresh in which some providers failed, or which was aborted, is no longer indistinguishable from a clean success. Atomic previously discarded the result behind a defensive cast and an `?? {}` fallback; both are gone and the value pi returns is passed through. Callers that awaited `refresh()` purely for its completion need no change; callers that want to report provider failures or honor cancellation can now read them.
- **`ModelRuntime.setRuntimeApiKey()` no longer refreshes the model catalog.** It now takes auth-cancellation options and does only what its name says: record the runtime key and mark the provider as configured, aborting early if the supplied signal is already aborted. Previously it awaited a full catalog refresh internally, so setting a key paid for a network refresh whether or not the caller wanted one and could not be cancelled. Callers that need a fresh catalog after setting a key must now call `refresh({ providers: [providerId], signal })` themselves.
- **`ctx.ui.custom()` components now report input consumption.** Their `handleInput` method must return `true` when they consume a key and `false` or `undefined` when they do not; extensions whose handlers return `void` must migrate. In fullscreen mode, unhandled viewport keys may reach the transcript. A remote component that is disposed before its reply is deliberately dropped rather than replayed.
- **Custom extension runtime and UI implementations must adopt lifecycle fields.** Exported `ExtensionRuntime` implementations must provide `trackEventBusSubscription(unsubscribe)`, and `ExtensionUIContext.getChatRenderSettings()` must return `markdownTransformers`. `pi.events` is now bound to an extension instance: subscriptions auto-unsubscribe on reload or disposal, and calling `on()` or `emit()` through a captured stale handle throws.
- **`ctx.ui.custom()` components now report input consumption.** A `handleInput` method must return `true` when it consumes a key and `false` or `undefined` when it does not; extensions whose handlers return `void` must migrate. In fullscreen mode, unhandled viewport keys can reach the transcript, while a disposed remote component's late reply is dropped.
- **Interactive TTY sessions now always use the fullscreen renderer.** The `--tui-mode` flag, `tuiMode` JSON setting, `/settings` TUI-mode row, settings-manager accessors, and exported `TuiMode` alias are removed. There is no replacement for `--tui-mode regular`; fullscreen is the interactive mode. `fullscreenScrollbar` remains configurable. Non-TTY output continues through print mode, while internal `TERM=dumb` and CI guards retain the main-screen renderer as a non-user-selectable fallback.

### Added

- Added an **experimental** `@bastani/atomic/client` entrypoint with `RemoteSession` lifecycle management and transcript projection helpers for remote protocol sessions.
- Added the fullscreen interactive TUI layout. Wheel and trackpad gestures reach focused workflow overlays before the transcript viewport, and events those overlays do not consume fall through to the fullscreen transcript.
- Fullscreen sessions keep the editor, status line, extension widgets, usage meter, and footer in a sticky dock while the transcript scrolls independently; terminal resizes preserve that layout.
- Fullscreen mode now supports a draggable transcript scrollbar configured as `auto`, `always`, or `hidden` through `/settings`; themes can set `scrollbarThumb`, which falls back to `selectedBg` for existing custom themes.
- Fullscreen transcript navigation now supports page, opt-in half-page, marked-message, and `home`/`end` shortcuts, while `ctrl+home`, `ctrl+end`, `ctrl+pageUp`, and `ctrl+pageDown` remain available to the editor. Matching viewport bindings pass through to focused fullscreen overlays and inline custom components, so host selectors and workflow stage-chat paging stay local. Prompt history can be enabled with explicit `tui.editor.historyPrevious`/`historyNext` bindings such as `ctrl+p`/`ctrl+n`; those bindings take precedence over ordinary application action handlers while the editor is focused.
- Windows fullscreen sessions now paste system clipboard text on a secondary-button press, with focus checks and bracketed-paste framing preserved for host and engine-owned editors.
- Added the bundled, default-on ADHD-friendly response mode. Its hidden rules message persists across turns, resumes, branches, and compaction; `/i-have-adhd on|off`, `--no-adhd`, `.i-have-adhd-off`, and the `stop adhd mode` / `normal mode` phrases control it.

- Added `atomic auth check` to verify a provider or model's effective authentication before a session starts. It reports `ready`, `not_ready`, or `invalid`, supports JSON output and a no-refresh read-only mode, and does not emit credential material unless `--credentials` explicitly requests it.

- Added the explicit `--credentials` opt-in to `atomic auth check` for scripts that need the resolved credential. Checks remain status-only unless that flag is present.
- Added immutable system-prompt contribution constants for Atomic's builtin coding tools, with alignment coverage that keeps each factory's prompt metadata in sync without duplicating prompt assembly.

- Added `CredentialSynchronizationError` for credential mutations that commit successfully but fail to update local model state. Credential-save failures remain distinct from post-commit synchronization failures, and interactive auth surfaces report the distinction.

- Added `terminate` to blocked extension `tool_call` results. A terminating blocked call suppresses the follow-up model request only when every finalized call in its batch also terminates.

- Added the configurable and package-exported `createCodingAgentHarness()` factory, with Atomic's hashline coding tools, live prompt metadata, Atomic and `PI_*` bash session variables, injectable pi-agent-core execution environments for read (including directory trees), bash, edit, write, and find, and runtime rejection of environments that omit `renameFile()`. URL reads now use the session id for cache scope, avoid unsupported session-manager access, and do not persist host-local artifacts in factory contexts. The default `search` tool remains local until its filesystem and matching pipeline gains a complete remote operations seam; read and edit retain local path-variant probes and notebook projection, read also retains local archive, SQLite, and internal-resource selectors, write retains local generated-file, shebang, conflict, and resource helpers, and bash validates its cwd locally and uses Atomic's local temp storage for overflow output.
- Added immutable system-prompt contribution constants for Atomic's built-in coding tools, keeping custom harnesses and tool registries aligned with the prompt metadata used by the built-in factories.
- Added `terminate` to blocked extension `tool_call` results. A terminating blocked call suppresses the follow-up model request only when every finalized call in its batch also terminates.

- Added `pi.registerMarkdownTransformer()` to the Atomic extension API for ordered, synchronous, display-only Markdown transforms in user, assistant, and assistant-thinking transcript output. Each transformer receives the prior transformer's output plus message type, streaming state, and available width; errors leave the current display text intact and do not alter stored messages or model context ([#7231](https://github.com/earendil-works/pi/pull/7231) by [@xl0](https://github.com/xl0)).
- Added themed Unicode Mermaid diagrams in interactive transcripts, with optional rendering while responses stream ([#7624](https://github.com/earendil-works/pi/pull/7624) by [@xl0](https://github.com/xl0)).
- Added configurable terminal-friendly Unicode LaTeX math in interactive transcripts via the `markdown.latex` setting. See [Markdown settings](docs/settings.md#markdown).
- Added per-directory `AGENTS.override.md` context files. An override replaces that directory's `AGENTS.md` or `CLAUDE.md` while global and ancestor context still layers normally; the same canonical filename works through Atomic's legacy `.pi` and `PI_*` agent-directory aliases ([#7681](https://github.com/earendil-works/pi/pull/7681) by [@m3](https://github.com/m3)).
- Added `AI_AGENT=atomic` attribution at the CLI, RPC, and compiled-binary entry points and across Atomic-owned child-process environments, including bash/tool commands, isolated RPC children, subagent/workflow runners, MCP and web-access subprocesses, and the intercom broker. Child environments follow upstream's overwrite policy while cloning rather than mutating caller-supplied environment objects.

- Added arbitrary OpenAI-compatible `samplingParams` for custom model definitions, per-model overrides, extension providers, and per-request stream options. Parameters merge by key, preserve provider-specific names, and apply to OpenAI-compatible request bodies; malformed configuration shapes now fail during `models.json` loading. Added opt-in vLLM `compat.supportsThinkingTokenBudget` handling and `compat.supportsFinishReason` stop inference for finishless OpenAI-compatible streams.
- Added built-in Baseten support with `BASETEN_API_KEY`, the `zai-org/GLM-5.2` default, and the catalog's provider-specific thinking levels.
- Added Qwen Token Plan Individual with the documented subscription catalog, shared international `QWEN_TOKEN_PLAN_API_KEY`, and `qwen3.8-max` as its default model.

- Added the fullscreen interactive TUI layout. The transcript scrolls above a sticky dock containing the editor, status line, usage meter, extension widgets, and footer, while wheel and trackpad input routes first to focused engine-hosted overlays such as workflow graphs ([#2269](https://github.com/bastani-inc/atomic/pull/2269), [#2274](https://github.com/bastani-inc/atomic/pull/2274)).
- Added a draggable fullscreen transcript scrollbar with `auto`, `always`, and `hidden` settings. Custom themes may define the optional `scrollbarThumb` color; existing themes fall back to `selectedBg` ([#2275](https://github.com/bastani-inc/atomic/pull/2275)).
- Added fullscreen transcript navigation for page, half-page, marked-message, `home`, and `end` actions, explicit `tui.editor.historyPrevious`/`historyNext` prompt-history bindings, and Windows secondary-button clipboard paste. Focused overlays and custom components receive matching viewport keys first and can return `false` to let transcript scrolling continue ([#2281](https://github.com/bastani-inc/atomic/pull/2281), [#2296](https://github.com/bastani-inc/atomic/pull/2296), [#2300](https://github.com/bastani-inc/atomic/pull/2300)).

- Tool output that spills to disk is now stored per session and reaped on age, so the system temp directory can no longer fill up with orphaned files. `bash` overflow logs, background-job logs, streamed spill files, and the in-memory-session tool-result fallback all move from the bare temp directory into one owner- and session-scoped tree, `<tmpdir>/atomic-<uid>/<session-id>/`, created `0700` with `0600` files where the platform supports it. Where there is no uid, as on Windows, the domain-qualified account name plus a short digest of it replaces the uid, so neither two names that reduce to the same safe path component nor two domain principals sharing a bare account name (`CONTOSO\Alice` and `FABRIKAM\Alice`) can land on one tree. A session id containing path separators or `..` is reduced to a single safe component and cannot place files outside that tree. A session directory deleted out of band — by a system temp reaper, or by another process's sweep — is recreated on the next write instead of handing back a dead path. Sessions that persist to disk keep writing tool results to `<sessionDir>/tool-results/` exactly as before.
- That temp path is predictable, so Atomic validates it instead of trusting it. Every component below the system temp directory is created one level at a time and checked: a symlink is refused, a directory owned by another account is refused, and one of this account's left too permissive is tightened to `0700` and re-checked. Refusal fails closed — the tool runs with no spill file and advertises no path, rather than writing command output through a link someone else planted at the predictable owner root. Only the final session directory is replaceable: a stale file or link exactly where a session directory belongs is removed (a link by itself, never its target) and recreated.
- On Windows, that ownership check is now real rather than skipped ([#2245](https://github.com/bastani-inc/atomic/issues/2245)). Before adopting an existing temp component, Atomic reads its security descriptor through PowerShell `Get-Acl` and decides from SIDs alone: the owner must be the current user, SYSTEM, or Administrators, and every access-allowed DACL entry must grant only those principals (plus inherit-only `CREATOR OWNER`/`OWNER RIGHTS` plumbing). A foreign-owned root, a root readable or writable by other accounts (a machine-wide redirected `TMP`), and a descriptor that cannot be read or fully parsed are all refused — failing closed to "no spill file", as on POSIX. A successful verification is cached per path per process, keyed to the directory's metadata identity (creation, change, and last-write times): a directory swapped underneath the path and an in-place DACL edit — which bumps the NTFS change time — both force a fresh descriptor read, and only a directory whose metadata is unchanged skips the subprocess; the symlink and directory checks still run on every use.
- Persisted tool-output files are capped at 64 MB each. Output that lands exactly on the cap is kept whole; output that passes it is cut off there with `[Output truncated: persisted-output cap of 67108864 bytes reached]` in place of the rest. The cut lands on a character boundary even when a multi-byte character is split across two chunks of streamed output, and bytes that are not valid UTF-8 — binary command output — are written through unchanged rather than decoded. The tool result and its `fullOutputPath` are unchanged, except that a spill file which fails to write is no longer advertised: `bash` reports no path rather than one pointing at a missing file, and the underlying stream error can no longer surface as an uncaught exception. A storage fault never changes the outcome of the command that produced the output: a background job whose process exited `0` but whose spill file could not be flushed is still reported completed with its real exit code rather than failed, since reporting a storage fault as command failure would invite a caller to re-run a command that already had its effect.
- A deferred, unref'd sweep started shortly after session startup removes session temp trees and `tool-results` directories whose newest file is more than 30 days old. It covers the default project-nested session roots and a custom session directory selected with `--session-dir`, `ATOMIC_CODING_AGENT_SESSION_DIR`, or the `sessionDir` setting, whose tool results have no project nesting to walk. The newest entry decides the whole directory — one fresh file keeps every stale sibling beside it, so a path a tool result recorded weeks ago stays readable while the tree is in use. Each target is throttled to once a day by a `.last-cleanup` marker and guarded by a `.cleanup.lock` exclusive lock, so concurrent sessions never race and a lock left by a crashed process is broken only once stale; session-storage targets keep that pair in a control directory under the temp root, out of the directory being scanned. Session transcripts and `.jsonl` session files are outside the sweep's scope entirely, symlinked scan roots and symlinked `tool-results` directories are skipped rather than followed, and a temp tree belonging to a session the running process is using is never reaped — a `Full output saved to:` path stays valid for the life of the session that produced it.

### Changed

- Footer and workflow stage-chat extension statuses are now joined with a dim `•` separator instead of a bare space, so adjacent statuses read as distinct items.
- Live tool-call argument previews no longer update while a tool call's arguments are still streaming. On the default interactive path the assistant message is now rebuilt from `message_update` deltas, and `toolcall_start`/`toolcall_delta` carry an unparsable fragment of JSON rather than a usable value, so the accumulator deliberately ignores them and installs the complete parsed call at `toolcall_end`. The tool card itself still appears as soon as the call starts — it is created by `tool_execution_start`, not by the argument stream — and the arguments it finally shows are correct and complete; what is gone is the partially-filled argument text that used to fill in progressively before the call was ready. This is a deliberate trade for correctness: the previous incremental preview was only possible because every streaming frame re-sent the entire message, which is exactly the quadratic amplification removed above.

- Adopted the pi 0.84.1 coding-agent, agent, AI, client, protocol, telemetry, and TUI runtime surfaces. The TUI adoption supplies the renderer behavior described above; Atomic keeps its isolated interactive engine, provider, path, and legacy `PI_*`/`.pi` compatibility surfaces.

### Fixed

- Fixed fullscreen mouse routing for focused workflow graphs and stage chats. SGR/X10 wheel and click sequences now reach the focused overlay through the existing bounded custom-input reply path before the alternate-screen viewport, so graph scrolling and click-to-attach work without changing keyboard viewport fallback behavior ([#2303](https://github.com/bastani-inc/atomic/issues/2303)).
- Fixed registration-triggered offline model catalog refreshes racing newer credential-resolved refreshes, so configured `$ENV` and `!command` credentials remain authoritative ([#2318](https://github.com/bastani-inc/atomic/issues/2318)).
- Fixed fullscreen overlay text selection when one input read contains multiple SGR mouse reports. Reports are replayed one at a time, while Shift/Option/Ctrl-modified presses and motion stay out of application selection; modifier-bearing SGR releases still close an active selection, and legacy X10 release remains excluded because pi-tui cannot identify it for selection.
- Fixed `atomic auth check --no-refresh` resolving command-backed API keys as configured values rather than treating `!command` as a literal credential.

- Fixed `atomic auth check --json` labelling an unresolved model as its provider. Invalid results now omit `provider` until a provider has actually resolved, and `print-api-key` consistently rejects `--min-expiry` before or after a `--` terminator.
- Fixed fullscreen viewport shortcuts stealing matching keys from a focused overlay or inline custom component. A focused handler now receives the key exactly once and can return `false` to let a no-op fall through to transcript navigation. Fullscreen shutdown also avoids restarting the replacement renderer, and the `ctrl+x` copy shortcut uses a transient `Copied!` flash instead of growing the fixed status dock.
- Fullscreen rendering now inherits pi-tui 0.84.1's stacked flashes, corrected multi-click selection and focus reset, reduced multiplexer mouse tracking, nested stack minimum sizing, and Kitty image clipping at dock/layout boundaries, single-pass redraw/retransmission, and offscreen caching fixes. Atomic deliberately uses the dependency implementations rather than carrying forked renderer or layout copies.
- Fixed fullscreen viewport shortcuts stealing matching keys from a focused overlay or inline custom component. A focused handler now receives the key exactly once; returning `true` keeps a consumed key local, while an unhandled result falls through to transcript navigation. The `ctrl+x` copy shortcut uses a transient `Copied!` flash instead of growing the fixed status dock.
- Verified the inherited pi-tui 0.84.1 fullscreen fixes against the upstream commits `17090d4` (`AltScreenFlashContainer` stacked flashes), `58fc043` and `171c6b5` (`TuiAltScreen` multi-click selection and focus reset), `fc3554e` (multiplexer mouse tracking), `f24ab6e` (`VStack` nested minimum sizing), `af187ee` (layout Kitty clipping), `73414d0` (layout/terminal-image redraw reuse), and `a8ee03b` (terminal-image offscreen caching). Atomic uses the dependency implementations rather than carrying forked renderer or layout copies.
- Fixed fullscreen viewport shortcuts stealing matching keys from a focused overlay or inline custom component. When host focus policy defers transcript navigation, the matching input stays with the focused component. Fullscreen shutdown avoids restarting the replacement renderer, and `ctrl+x` shows a transient `Copied!` flash instead of growing the fixed status dock.
- Fixed isolated fullscreen custom components swallowing viewport navigation when their child handler did not consume the key. Input replies now correlate with each forwarded key, repaint remote components after input, and let transcript scrolling proceed on an explicit `false`, a failed reply, or a bounded timeout while preserving focus and key-release semantics.
- Fixed isolated fullscreen tool renderers rebuilding Kitty image components on every engine render, which retransmitted visible payloads and rendered the transcript twice per frame during layout changes.
- Verified the remaining fullscreen polish against pi-tui 0.84.1: stacked flashes, phantom-selection prevention, nested stack minimum sizing, Kitty clipping at dock boundaries, redraw reuse, and offscreen caching are supplied by the dependency. Its multi-click text-selection and tmux/Zellij/Screen mouse-tracking reductions also ship there, so Atomic deliberately does not port them. The fullscreen shutdown query-leak fix is already present in Atomic's `7a5a2674e` core layer.
- Fixed extension event-bus subscriptions surviving session reload or disposal ([#7193](https://github.com/earendil-works/pi/issues/7193)).
- Fixed extension event-bus subscriptions surviving session reload or disposal ([#7656](https://github.com/earendil-works/pi/pull/7656) by [@tudoroancea](https://github.com/tudoroancea)).
- Fixed stale extension-context detection for bundled extensions. The public `isStaleExtensionContextError()` predicate and its marker now belong to the host beside the error path, so session replacement and reload guards cannot silently drift from host wording.
- Fixed `ChatSessionHost` tail-windowing settled assistant rows while a later answer streamed; only the live assistant row is now trimmed.
- Fixed bare exact `--model` IDs shared by providers choosing catalog order. Atomic now uses the sole authenticated matching provider or reports an ambiguity with fully qualified choices ([#7327](https://github.com/earendil-works/pi/issues/7327)).
- Fixed `/model <name>` and `/scoped-models` waiting for remote catalog refreshes before using cached models. Exact cached matches now resolve immediately, and the scoped selector renders its cache first, refreshes in the background, and cancels that work when it closes ([#7153](https://github.com/earendil-works/pi/issues/7153), [#7443](https://github.com/earendil-works/pi/issues/7443)).
- Fixed project `retry.provider` overrides discarding unmodified global provider retry fields by recursively merging nested settings ([#7572](https://github.com/earendil-works/pi/issues/7572)).
- Reduced automatic terminal-theme detection latency by starting color-scheme and background probes together.
- Softened the bash session-environment prompt while retaining Atomic and legacy `PI_*` environment support.

- Verified the remaining pi-tui 0.84.1 renderer fixes used by fullscreen and extension UI: stacked flashes, phantom-selection prevention, nested-stack minimum sizing, Kitty clipping at dock boundaries, redraw reuse, offscreen caching, multi-click word/line selection, and reduced mouse tracking in tmux, Zellij, and GNU Screen. The dependency also supplies the LaTeX whitespace and matrix fixes; Atomic does not vendor a second TUI implementation.

- Fixed same-state `ctx.ui.setToolsExpanded()` calls still traversing the transcript or requesting a custom-UI render. Repeated bundled-extension requests to keep tool output collapsed now leave an already-collapsed interactive or RPC UI untouched.
- Fixed extension-facing `ModelRegistry.complete()` requests to dispatch through the active model runtime, so registered providers and resolved request authentication remain available to extension code.
- Fixed image blocks returned by builtin and extension tools entering history at their original size. They now honor `images.autoResize` after `tool_result` handlers run, including images injected by an extension; a failed conversion leaves the original image intact.
- Fixed custom editors installed through `ctx.ui.setEditorComponent()` that expose `setAutocompleteMaxVisible()` not inheriting the default editor's active autocomplete dropdown limit.

- Fixed in-process child sessions losing their admission-issued nesting depth and delegation limit. `SubagentChildPolicy` now carries both the admitted `depth` and the effective `maxSubagentDepth`, so the subagent executor can enforce the configured and inherited limits without relying on the removed process-environment bridge ([#2220](https://github.com/bastani-inc/atomic/pull/2220), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
- Release archives no longer ship packages built for a different architecture. Every archive is produced on one Linux x64 runner, and that runner's `node_modules` was copied verbatim into all of them, so 0.9.12's `atomic-linux-arm64.tar.gz` and `atomic-darwin-arm64.tar.gz` both contained `@esbuild/linux-x64`. The build now prunes the `@embedded-postgres` leaves that cannot run on the archive being staged and then fails the build if any staged package's own `os`/`cpu`/`libc` declaration is incompatible with that archive ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
- Reduced redundant repaints of a Ctrl+O-expanded live subagent widget, which were scrolling the chat window to the bottom and clearing terminal scrollback during a run. The widget was republished on every child session event, including assistant streaming deltas, so it repainted continuously instead of when its contents changed; each repaint of a row above the terminal fold costs a pi-tui full redraw that clears scrollback. Progress is now published only at milestones that change what the widget shows. A genuine above-fold milestone change can still require that redraw when the editor/footer region plus the live widget exceed the terminal height — that path is upstream (earendil-works/pi#4785, #7194) and is not fixed here ([#2213](https://github.com/bastani-inc/atomic/pull/2213), regression from [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
- Fixed the scroll anchor in `ScrollableComponentViewport` drifting when its content changes height. The offset is stored as a distance from the bottom and was compensated only when content grew, so a component losing rows — such as a live subagent widget dropping its current-tool row at the end of each tool call — slid the rows a scrolled-up viewer was reading. The offset is now re-derived from the anchored row, which also keeps the anchor still when rows change *above* it; a plain height-delta adjustment moved the viewer in that case. Because the chat host renders the entire transcript as a single component, the anchored row is normally *inside* one component, where per-component row counts cannot say whether rows changed above or below it; a windowed component can now report a row map (`rowSegments`) identifying each run of rows by the entry that produced it, so a compaction that deletes entries above the viewer — renumbering every positional cache key — still leaves the viewer on the entry they were reading. If the anchored entry itself was compacted away, the viewer lands on the nearest surviving entry above it. A viewer already at the bottom still sticks to the bottom. This affects the workflow stage chat view and any extension surface built on `ChatSessionHost`; it is not the path the normal interactive chat uses, so it does not by itself preserve main-chat terminal scrollback ([#2214](https://github.com/bastani-inc/atomic/pull/2214), regression source [#2205](https://github.com/bastani-inc/atomic/pull/2205)).
- Fixed a GitHub Copilot `openai-responses` session hard-stopping with `400 invalid_request_body` once a replayed tool call entered its context. The outbound payload sanitizer treated an *absent* `function_call.id` the same as a malformed one and synthesized a replacement from `call_id`; on the Copilot path across a model-identity mismatch (a mid-session model switch, a session resumed under another model, or an aliased model id) that synthesized value was always exactly 64 characters, and the server rejected it. Because the value is a pure function of the tool call, every resend rebuilt the identical rejected identifier and nothing retries HTTP 400, so the session could not recover without hand-editing its context. An absent id is now left absent, and only an id that is actually present and malformed is repaired. The synthesized-length budget and the id validator were also tightened to agree on a single maximum of 63 characters, below the 64-character length the server was observed to reject rather than at it, so the sanitizer can no longer bless a value the server refuses ([#2204](https://github.com/bastani-inc/atomic/issues/2204)).
- Fixed the model list widening to a provider's whole built-in catalog after an extension registered an additive provider override, so a GitHub Copilot account entitled to one model could be offered every catalog model. Availability is a per-model fact — a credential can entitle an account to a subset of a provider's catalog — but the model-list refresh and registration projections rebuilt it from the provider's *configured* status, which reads as "every model this provider has". Registering an override recomposes the provider and republishes, so the credential-filtered result was replaced by the full catalog, and it stayed replaced whenever the background refresh that the registration schedules superseded the availability pass a caller was awaiting. Republishing the model list now carries the previous availability projection across by model ID, including an empty one, and a registration exposes a provider's catalog provisionally only when that registration is what newly configures the provider.


- Fixed a `null` request-header suppression marker being discarded instead of suppressing the provider default header. pi types request headers as `ProviderHeaders` (`Record<string, string | null>`) and documents a `null` value as suppressing the provider or API default header of the same name. Atomic narrowed that to `Record<string, string>` and enforced the narrowing at runtime in **two** places — `getApiKeyAndHeaders` on `ModelRegistry`, and `_getRequiredRequestAuth` on the agent session — each filtering every `null` entry out before the headers reached the request layer. The provider default was then sent anyway, and a header map consisting only of suppression markers arrived as `{}`, which reads as nothing configured. Both filters are gone and markers now pass through untouched. Note that this is **not** reachable from `models.json`: that schema declares headers as `Record<string, string>` and rejects a `null` value outright, so a marker has to come from a source that speaks `ProviderHeaders` — pi's provider-level catalog headers, the credential-derived request auth pi returns from `getAuth()`, or the `before_provider_headers` extension hook. The agent-session filter additionally affected every request that resolves auth through that path rather than through the registry: compaction, auto-compaction, post-tool-compaction, and branch-summary requests each discarded the marker on every call.
- Fixed a chat surface built on `ChatSessionHost` — the workflow stage chat, and any extension pane using it — showing no assistant text at all when it was opened onto a session that was already streaming. Now that `message_update` carries only the next delta, a host mounted part-way through a turn sees neither the `message_start` that opened the assistant message nor the fragments that already went past, and nothing later restates them; the message itself reaches the transcript only when the turn ends, so a stage held mid-turn stayed blank for as long as it streamed. A host now adopts the session's in-flight assistant message when it mounts, and the deltas that arrive afterwards continue that message instead of opening a second one. Streaming consumers also assemble into their own copy of the message: an in-process subscriber is handed the provider's live partial, whose `content` array pi-ai keeps appending to, so applying deltas in place wrote each fragment on top of the accumulation the provider had already done and doubled the visible text (regression from the delta-only `message_update` change above).
- Manual `/compact` now keeps agent events live, cancels unfinished automatic compaction safely, and then executes the requested manual compaction instead of rejecting it or returning an automatic boundary with different parameters.
- Input queued during manual compaction now sends after completion because the session exposes its idle state before `compaction_end` listeners run.
- A response truncated before its requested output limit now gets one compact-and-retry attempt; output-cap stops retain bounded direct continuation, and the TUI reports neutral truncation text.
- Fixed a stale automatic post-compaction retry resuming after a manual `/compact` finished during the continuation grace period.
- Fixed a short transcript with repeated below-cap truncations retrying compaction more than once when no compaction boundary could be made, while preserving load-bearing recovery for a later actual context overflow.
- Fixed `ChatSessionHost` leaving queued prompts stuck when compaction was cancelled or failed; queues now drain after every non-mid-turn compaction completion.
- Fixed malformed package-manifest resource fields that could block valid resources or crash startup ([#7187](https://github.com/earendil-works/pi/issues/7187)).
- Fixed Git package updates left incomplete after `git clean` fails, repairing missing dependencies and retrying cleanup on the next update ([#7570](https://github.com/earendil-works/pi/issues/7570)).
- Retried transient management requests for explicit version checks, remote catalogs, managed-tool downloads, and self updates without multiplying the overall timeout budget ([#7632](https://github.com/earendil-works/pi/issues/7632)).
- Fixed Windows fallback `find` glob patterns with paths returning no results when `fd` uses native separators ([#6817](https://github.com/earendil-works/pi/issues/6817)).
- Fixed `find` results at filesystem roots, including POSIX `/` and Windows drive roots, so paths keep their first segment, directory matches use one trailing slash, and POSIX filenames ending in a backslash remain literal ([#6104](https://github.com/earendil-works/pi/issues/6104), [#7569](https://github.com/earendil-works/pi/pull/7569)).
- Fixed Git Bash, MSYS, Cygwin, and WSL drive paths resolving against the current Windows drive instead of their native drive ([#7064](https://github.com/earendil-works/pi/issues/7064), [#7547](https://github.com/earendil-works/pi/issues/7547)).
- Fixed linked project session directories being omitted from the session picker ([#7552](https://github.com/earendil-works/pi/issues/7552)).
- Fixed standalone x64 binaries failing to start on pre-Haswell CPUs that lack AVX2/BMI2 by using Bun's baseline target, including musl archives ([#7390](https://github.com/earendil-works/pi/issues/7390)).
- Fixed credential-resolved model endpoints — including GitHub Copilot Business and Enterprise hosts — falling back to the catalog endpoint for Verbatim Compaction, branch summaries, MCP sampling, and direct web summaries. Atomic now retains the resolved `baseUrl` alongside request auth, so request-header `null` suppression markers still pass through unchanged ([#6768](https://github.com/earendil-works/pi/issues/6768), [#7579](https://github.com/earendil-works/pi/issues/7579)).
- Fixed native macOS `Cmd+V` so clipboard images paste into the editor without duplicate image inserts from terminal key repeats ([#2186](https://github.com/bastani-inc/atomic/issues/2186)).

### Removed

- Removed the `tsx` dependency from the shipped runtime. The Intercom broker's Node path now runs on Atomic's bundled `jiti` loader, which is dependency-free pure JavaScript, instead of resolving `tsx` first. `tsx` was the only thing pulling `esbuild`, so this removes `esbuild`, its 26 platform packages, and `fsevents` from `npm-shrinkwrap.json` and from every release archive — about 11.5 MB across 72 files. The `npx --no-install tsx` config pair remains a recognized compatibility sentinel, and explicit custom broker commands are unaffected ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).
- Behavior change worth noting for extension authors: TypeScript reached through the Node broker path is now transpiled by jiti (Babel-based) rather than esbuild. Both erase types without type-checking, but they are not identical — jiti defaults JSX support and `tsconfig` path aliases to off, and enables legacy decorators. A third-party extension that resolved `tsx` from the bundled `node_modules` was relying on an undocumented dependency and will no longer find it ([#2208](https://github.com/bastani-inc/atomic/issues/2208)).

- Removed the Atomic-only workflow custom-terminal mouse-scroll control from the isolated engine protocol and host terminal-mode arbiter; remote overlays retain the typed autowrap control only. Focused workflow overlays now leave `ctrl+t` to the host `app.thinking.toggle` action, while the tree selector continues to receive the same key through `app.tree.filter.noTools`. Fullscreen pi-tui owns application selection over overlays; copy still uses OSC 52, and terminals that refuse OSC 52 writes require the modifier-drag bypass (Shift/Option, as provided by the terminal).

## [0.9.13-alpha.1] - 2026-08-05

### Added

- `CreateAgentSessionOptions` and `ExtensionContext` now carry an immutable typed subagent child capability policy for in-process tool registration ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
- `CreateAgentSessionOptions` now supports construction-time system-prompt and inherited-context transforms, allowing in-process child sessions to apply typed policy without extension injection ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).

### Changed

- Typed child policy now includes MCP direct-tool selection and admission-issued Intercom identity/capability, allowing in-process extensions to consume child policy without inheritable environment state ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).
- Bundled subagent launches now use in-process `AgentSession` children with typed admission, canonical identities, and live async continuation; `async: true` work is owned by the parent process and does not survive parent exit ([#2188](https://github.com/bastani-inc/atomic/issues/2188)).

### Fixed

- Workflow runs awaiting human input now use the blue `？` indicator in the BACKGROUND panel, `/workflow connect` picker, and `/workflow status` listing, including prompts raised by hidden nested workflow children; the indicator returns to the run's current state when the prompt resolves.
- Fixed main-chat model fallback to classify provider failures consistently with workflows, advance rejected credentials and incompatible or unavailable models to the next candidate, and restore the user-selected model at the next turn without overriding an explicit `/model` choice. A failure that requesting the same model again cannot repair now takes that model out of the chain for the rest of the turn at every reasoning level, so a fallback entry differing only by its `:low`/`:high` suffix is skipped instead of retrying the same dead credential; transient rate-limit and transport failures keep those reasoning variants ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
- Fixed a context overflow that compaction cannot resolve to advance the configured `fallbackModels` chain instead of ending the turn, so a larger-context candidate can answer. Compaction still runs first, and a compactable overflow spends no fallback candidate ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
- Fixed a reasoning-level change during a model fallback stranding the session on the fallback model. Changing reasoning effort is not a model choice, so it no longer cancels the pending restore; the next turn returns to the user-selected primary and keeps the reasoning level that was chosen. Only an explicit `/model` selection or model cycle cancels the restore ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).
- Fixed gRPC `ResourceExhausted` provider errors, seen from providers such as NVIDIA NIM, bypassing the same-model auto-retry budget. They are now retried like other transient provider failures, matching upstream pi-ai, and still advance a configured fallback chain when the retries are exhausted ([#2170](https://github.com/bastani-inc/atomic/issues/2170)).

## [0.9.12] - 2026-08-04

Cumulative release of the `0.9.12-alpha.1` prerelease. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease section below.

### Added

- Compaction honors `<keepContext>` / `</keepContext>` tags. Wrap any part of a prompt you never want compressed, and every line of the span — tag lines included — is protected verbatim regardless of the compression ratio. The guarantee is mechanical rather than advisory: deletion ranges are split around protected lines after the planner responds. Because the tag lines are protected too, a span is re-detected on each subsequent boundary and stays protected for the life of the session. Detection is bounded on three sides, because the transcript it scans is untrusted: a tag must be the whole line after any role header, a span is scoped to one message so an unclosed tag stops at the end of its own message rather than running to the end of the region, and tags inside tool results are inert — user and assistant messages may both protect, so prompts, steering, and an agent's own pinned information all work, but file, page, or command output an agent reads cannot mark itself unreclaimable or persist through compaction ahead of real content. Protected lines count against the keep target rather than raising it, so the compression ratio stays a real bound on output size and the unprotected remainder compresses harder. Results report force-preserved ranges as `keptRanges` ([#2172](https://github.com/bastani-inc/atomic/issues/2172)).

### Fixed

- The compaction relevance query is no longer truncated to 1,000 characters. Truncation made prompt section order the retention policy: for a long structured prompt only the leading section reached the planner, so a constraint stated later could not influence what was kept, while the objective it qualified survived and was acted on. Long queries are safe — an oversized planner request surfaces as an explicit provider-overflow failure rather than silent truncation ([#2172](https://github.com/bastani-inc/atomic/issues/2172)).
- Cleared three advisories in the shipped dependency tree. `undici` moves 8.5.0 → 8.9.0, which covers five advisories against 8.0.0–8.8.0 — response desynchronization via the retry interceptor, two cross-user cache-directive disclosures, CRLF injection through a blob body `type`, and cookie-attribute injection — and it is the dispatcher behind `fetch_url` and every agent HTTP request. Transitively under `@modelcontextprotocol/sdk`, `ip-address` is pinned to 10.3.1 for three SSRF and trust-boundary bypasses (octal-decoded leading-zero octets, CIDR suffixes suppressing special-use classification, and misclassified IPv4-mapped/NAT64 addresses), and `hono` to 4.12.34 for a CORS-middleware ReDoS. `npm audit` is clean.
- Fixed bundled builtin extensions failing to load on Windows when shared modules were evaluated twice, so bundled workflows and tools initialize correctly.

## [0.9.12-alpha.1] - 2026-08-04

### Added

- Compaction honors `<keepContext>` / `</keepContext>` tags. Wrap any part of a prompt you never want compressed, and every line of the span — tag lines included — is protected verbatim regardless of the compression ratio. The guarantee is mechanical rather than advisory: deletion ranges are split around protected lines after the planner responds. Because the tag lines are protected too, a span is re-detected on each subsequent boundary and stays protected for the life of the session. Detection is bounded on three sides, because the transcript it scans is untrusted: a tag must be the whole line after any role header, a span is scoped to one message so an unclosed tag stops at the end of its own message rather than running to the end of the region, and tags inside tool results are inert — user and assistant messages may both protect, so prompts, steering, and an agent's own pinned information all work, but file, page, or command output an agent reads cannot mark itself unreclaimable or persist through compaction ahead of real content. Protected lines count against the keep target rather than raising it, so the compression ratio stays a real bound on output size and the unprotected remainder compresses harder. Results report force-preserved ranges as `keptRanges` ([#2172](https://github.com/bastani-inc/atomic/issues/2172)).

### Fixed

- The compaction relevance query is no longer truncated to 1,000 characters. Truncation made prompt section order the retention policy: for a long structured prompt only the leading section reached the planner, so a constraint stated later could not influence what was kept, while the objective it qualified survived and was acted on. Long queries are safe — an oversized planner request surfaces as an explicit provider-overflow failure rather than silent truncation ([#2172](https://github.com/bastani-inc/atomic/issues/2172)).
- Cleared three advisories in the shipped dependency tree. `undici` moves 8.5.0 → 8.9.0, which covers five advisories against 8.0.0–8.8.0 — response desynchronization via the retry interceptor, two cross-user cache-directive disclosures, CRLF injection through a blob body `type`, and cookie-attribute injection — and it is the dispatcher behind `fetch_url` and every agent HTTP request. Transitively under `@modelcontextprotocol/sdk`, `ip-address` is pinned to 10.3.1 for three SSRF and trust-boundary bypasses (octal-decoded leading-zero octets, CIDR suffixes suppressing special-use classification, and misclassified IPv4-mapped/NAT64 addresses), and `hono` to 4.12.34 for a CORS-middleware ReDoS. `npm audit` is clean.

- Fixed bundled builtin extensions failing to load on Windows when shared modules were evaluated twice, so bundled workflows and tools initialize correctly.

## [0.9.11] - 2026-08-03

Cumulative release of the `0.9.11-alpha.1` – `0.9.11-alpha.12` prereleases. The summary below covers the user-visible outcome of that work; the per-change detail remains in the prerelease sections below.

### Breaking Changes

- Adopted pi 0.83.0 (`@earendil-works/pi-agent-core`, `pi-ai`, `pi-tui` at `^0.83.0`), which moves the bundled TypeBox to 1.3.7 and **removes seven deprecated APIs**. Atomic inherits the removal exactly as upstream shipped it with no compatibility shim, so an extension, skill, workflow, or MCP schema still calling one of these fails to compile and to run. Atomic's own sources use none of them.

  | Removed API | Replacement |
  | --- | --- |
  | `Type.Options(type, options)` | `Type.With(type, options)` — straight rename |
  | `Type.Base` | `Type.Refine()` + `Type.Unsafe()` |
  | `Type.Promise(item)` | none — model the resolved value and validate after `await` |
  | `Type.Awaited(type)` | none — use the resolved type itself |
  | `Type.Iterator(items)` / `Type.AsyncIterator(items)` | none — drain first, then `Type.Array(items)` |
  | `Value.Mutate(current, next)` | `Value.Patch(current, Value.Diff(current, next))` — returns a **new** value rather than assigning in place |

- Replaced Atomic's legacy extension OAuth registration bridge with provider-owned authentication from `@earendil-works/pi-ai`, and adopted pi's provider-owned `ModelRuntime` architecture for model composition, credentials, streaming, and catalog refresh. The bridge functions (`registerOAuthProvider`, `getOAuthApiKey`, and the rest) and their credential/status types are gone from the package root; `AuthStorage` now implements pi's asynchronous `CredentialStore` contract, so its synchronous compatibility methods and provider-auth helpers were removed in favour of `read`, `list`, `modify`, `delete`, and `ModelRuntime`; `ModelRegistry` is now the thin synchronous extension-compatibility facade constructed as `new ModelRegistry(runtime)`; and `CreateAgentSessionOptions` takes `modelRuntime` instead of `authStorage`/`modelRegistry`. Extensions must declare OAuth or API-key authentication on their provider registration, and `streamSimple` implementations are now scoped to their registered provider.
- Model configuration follows pi's single-file `ModelConfig` contract: Atomic reads one `models.json` from the active agent directory and no longer reads project-scoped `.atomic/models.json`, falls back to `~/.pi/agent/models.json`, or merges layered files. Move project-scoped custom providers and models into the agent-directory file. Catalog refreshes also use pi's exact timeout semantics — `modelRefreshTimeoutMs` applies only to the initial runtime creation refresh, and `/model` owns its own 15-second timeout.
- Removed all Atomic-specific GitHub Copilot handling; `github-copilot` is now an ordinary upstream provider sourced entirely from `pi-ai`. The live CAPI catalog fetch and disk cache, enterprise/GHE base-URL routing, the Gemini tool-schema sanitizer, the Anthropic `message_stop` SSE repair, and Copilot prompt-limit guidance are all gone. Because pi's bundled context windows can exceed what Copilot's API enforces, long sessions on affected models may overrun the server-side cap instead of compacting first ([#1608](https://github.com/bastani-inc/atomic/issues/1608)).
- Removed the short/long context-window selection feature, which upstream pi does not have: the `contextWindowOptions` / `defaultContextWindow` / `maxInputTokens` model fields, the `/model` picker, `--context-window`, the `defaultContextWindow(s)` settings, the `set_context_window` and `get_available_context_windows` RPC commands, the `context_window_changed` event, the `contextWindow` / `contextWindowStrict` SDK and workflow-stage options, the related root exports, and the `(1m)` / `(long)` token in subagent and workflow model strings. A model's `contextWindow` is now a single catalog value.
- Isolated interactive `ctx.ui.custom()` components that consume Ctrl+C must declare `{ handlesCtrlC: true }`. Without it the first Ctrl+C is the host escape and closes the focused component, resolving its promise with `undefined`. Bundled workflow and MCP surfaces were migrated; third-party extensions binding Ctrl+C need the one-line addition.
- Changed the published `runVerbatimCompaction` SDK export to a request object: `runVerbatimCompaction(preparation, model, { resolveAuth, signal, thinkingLevel, urgency, ...options })`. Pre-resolved credentials become a `resolveAuth(model)` callback because a borrowed fallback model needs its own credentials, and `urgency` is required with no default so only a `load_bearing` caller can reach the context-destroying fresh rung. The persisted `CompactionEntry` shape, resume, and the compaction hooks are preserved.
- Workflow run targets now require the full 36-character run UUID; typed prefixes are no longer resolved by any command or `runId` action, and a malformed target is rejected with a diagnostic distinct from `Run not found:` so a truncated paste is diagnosable. Stage targets are matched exactly, so `build` will not select `build-check`. Every user-facing surface already prints the full id.
- The RPC `login_provider` response no longer carries the API key it just stored. `RpcLoginProviderResult` dropped its `credential` member; `type` confirms which kind of credential was saved.

### Added

- Added first-class Alpine/musl Linux support for x64 and arm64, including native search and PTY bindings and the `atomic-linux-x64-musl.tar.gz` and `atomic-linux-arm64-musl.tar.gz` archives. The musl archives omit the clipboard binding and glibc-linked `@embedded-postgres/*` binaries, so Atomic falls back to Linux clipboard commands/OSC52 and durable Alpine workflows require external Postgres.
- Added `atomic auth print-api-key` and `atomic auth print-bearer-token`, which print exactly one named credential to stdout while every warning and diagnostic goes to stderr, so `KEY=$(atomic auth print-api-key --model gpt-5.5)` cannot capture noise. `--model` is required, `--provider` and `--model` are the only accepted options, there is no file or clipboard sink, and `print-bearer-token` refreshes a token with less than `--min-expiry` (default `30m`) remaining. Failures are distinguishable by exit code 1–9, including a partial-write exit that tells you to discard the output. Documented in `docs/usage.md` and `docs/security.md`.
- Added a compaction rung ladder so a compaction that must succeed always does. The range planner returns typed outcomes instead of ranges-or-throw; a terminal attempt borrows the next `settings.fallbackModels` entry for one planner request resolved with that candidate's own credentials, without changing the session model or history; an overflowing planner request withholds the oldest region lines and retries; and when every model is exhausted, a load-bearing compaction starts a fresh context window — a loud (`✻ Context cleared (compaction degraded)`) and durable (`details.rung: "fresh"`) model-free rung. Manual `/compact` and threshold auto-compaction may borrow a model but can never clear context. **A configured fallback model may now receive the compaction transcript.**
- Added a built-in, hidden llama.cpp router provider with `/login llama.cpp`, `LLAMA_BASE_URL`/`LLAMA_API_KEY` configuration, loaded-model discovery, and the `/llama` manager for load, unload, reconnect, and Hugging Face GGUF download flows.
- Added provider-metadata-driven `/login` with direct `/login <provider>` routing and autocomplete, OpenRouter PKCE and Kimi Code device subscription login, API-key login for Qwen Token Plan, Radius, and GitHub Copilot, and immediate post-login model refresh without transporting credentials to the frontend.
- Added Claude Opus 5 to the Anthropic and Amazon Bedrock catalog paths with adaptive thinking/`xhigh`, Bedrock inference profiles, prompt caching, and strict-tool metadata.
- Added end-to-end constrained tool sampling to extension/SDK tool definitions and model catalogs: strict JSON Schema `prefer`/`require`, OpenAI grammar tools with Lark/regex variants, and `supportsStrictMode` / `supportsStrictTools` / `supportsOpenAIGrammarTools` flags exposed through additive optional `ModelInfo.compat` ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Added execution-time session metadata to every bash execution path through `ATOMIC_SESSION_ID`, `ATOMIC_SESSION_FILE`, `ATOMIC_PROVIDER`, `ATOMIC_MODEL`, and `ATOMIC_REASONING_LEVEL` with exact `PI_*` aliases, plus request-correlated `bash_execution_update` RPC events that separate stdout from stderr and support concurrent requests and per-request cancellation ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Added `ctx.scopedModels` to the extension context: the read-only list of models scoped to the current session, resolving at access time and handing back a fresh copy on every read so an extension cannot widen its own scope. Documented in `docs/extensions.md`.
- Added durable, searchable companion transcripts for every workflow stage that declares `output:`, run-scoped durable artifact directories under `~/.atomic/workflows/runs/<runId>/` (overridable with `ATOMIC_WORKFLOW_ARTIFACT_DIR`) with state-aware retention, explicit warnings for degenerate stage artifacts, and a real artifact-integrity probe in the shared resume predicate.
- Added extension lifecycle hooks `before_provider_headers` and `agent_settled`, durable `entry_appended` events with `registerEntryRenderer()`, named/hidden `InlineExtension` descriptors, and the public `ModelRuntime` facade, `readStoredCredential`, and session-context conversion helpers on the SDK surface.
- Added global `httpProxy`, duration-string and `"disabled"` HTTP idle timeouts, package `autoload: false` project deltas, `oauth: "radius"` custom gateways, global/project scope switching in `atomic config`, and the `get_available_thinking_levels` RPC command.
- Added Ctrl+X copy-last-message, clipboard text fallback when image paste finds no image, durable cache-miss attribution with waste totals, per-model session usage and cost breakdowns including tool and summary work, and Atomic's first-run theme preview with explicit analytics opt-in (no analytics data is transmitted by this change).

### Changed

- Reworked interactive startup and working motion around Atomic's revision-3 identity: the ∀ banner assembles inward before its identity beat, and main and workflow-stage surfaces keep the one-cell `∀` visible on a theme-aware dark → accent → bright → accent → dark luminance ramp at 88 ms while retaining all 453 randomized working verbs. `NO_COLOR` keeps regular/bold activity without color escapes, and reduced motion keeps a static accent `∀` ([#1883](https://github.com/bastani-inc/atomic/issues/1883)).
- Made interactive startup dramatically faster on Windows: installed builds ship the builtin workflows extension as a single prebundled ESM file instead of transpiling a ~300-file module graph per launch (~7 s → ~1.1 s on a test VM), and the CLI enables Node's persistent on-disk V8 compile cache so host and engine child reuse compiled bytecode across runs ([#1962](https://github.com/bastani-inc/atomic/issues/1962)).
- Removed the fixed 1 MiB size limit from RPC and isolated interactive-engine JSONL transport; commands, responses, events, and render frames now pass through in full ([#1985](https://github.com/bastani-inc/atomic/issues/1985)).
- A stored OAuth token is now refreshed once fewer than five minutes of validity remain, rather than at expiry, so a long turn is not started on a credential that dies mid-request. The refresh runs inside the `auth.json` lock, so concurrent sessions refresh it once between them.
- The OpenRouter login now accepts a pasted final redirect URL, or the authorization code alone, for remote and headless machines where the browser cannot reach the loopback callback.
- A provider that ends a turn with an unrecognised terminal reason now surfaces a provider error naming the raw reason instead of reporting an ordinary successful stop, and assistant messages carry a new `pending` stop reason while streaming. Documented in `docs/providers.md`, `docs/custom-provider.md`, `docs/rpc.md`, and `docs/session-format.md`.
- Workflow run identifiers are shown as full UUIDs across the BACKGROUND widget, status views, run pickers, and control messages; the four-workflow BACKGROUND display cap is gone; and `workflow({ action: "status", format: "json" })` no longer includes `runIdPrefix` — read `runId`.
- Added Orwell's six writing rules from "Politics and the English Language" to the core default system prompt's `Guidelines` section, and the startup `RESOURCES` disclosure now lists `SYSTEM.md` and `APPEND_SYSTEM.md` alongside the `AGENTS.md`/`CLAUDE.md` context files.
- Synchronized the bundled `impeccable` skill tree with upstream 4.0.3 (four surface modes, new routing and craft-floor guidance, a `doctor` drift check, documenter and finish-reviewer agents), and verified the `playwright-cli` and `liteparse` trees against their upstream baselines.
- Updated the bundled user documentation for the current subagent, workflow-stage, reviewer, and `prompt-engineer` prompt contracts, removing response prefilling and private-reasoning requests for GPT-5.6, Claude Opus 5, and Claude Fable 5 compatibility.

### Fixed

- Fixed the isolated interactive engine losing or duplicating your typed input. A submission the engine never accepted now returns to the editor exactly as typed — through every submit route, including `/atomic`, deferred slash commands, streaming steer sends, `!bash`, `/compact`, and Alt+Enter — with its own draft carried end to end so two similar submissions cannot restore each other's text, and without a misleading red transport error beside it. Work the engine *had* started is no longer offered back as unsent: the child announces ownership of every correlated request before its handler can touch the shell, so a silent `!touch marker && sleep 400` is not re-invited for a second run. The interactive-engine protocol version is now `2`.
- Fixed a total interactive-TUI lockup when the engine generation behind a mounted remote custom UI died, which previously left components mounted forever with input trapped and required killing the process from another terminal. Engine death is now a host-local lifecycle event that closes every remote component from the dead generation, settles its promises, releases widget keys, resets terminal modes, and refocuses the editor without waiting for a replacement.
- Fixed interrupting a tool call terminating the interactive engine and reporting `Engine terminated; … result unknown`. Escape now simply requests cooperative cancellation and waits for it — interrupting never terminates or restarts the engine and never discards in-flight tool state — and every cooperative-cancellation command (`abort`, `abort_bash`, `abort_compaction`, `abort_retry`, `cancel_login_provider`) is exempt from the generic 30-second RPC deadline.
- Added recovery from an unexpected interactive-engine exit — one automatic replacement attempt with the calm status `Interactive engine stopped unexpectedly; restarting.` — and kept an explicit Ctrl+C escape hatch for a provably wedged engine. Reopening an inactive session after such an exit now derives explicit error results for unanswered `toolUse` calls without rewriting its append-only JSONL, so the session is usable again instead of permanently pending.
- Fixed the interactive engine exporting its private control values (`ATOMIC_INTERACTIVE_ENGINE_CHILD`, `_HOST_PID`, `_GUARD_FILE`, `_API_KEY`) to every process it spawned, which made nested `atomic` runs start in engine mode, misdirected process guardians, disabled subagent idle watchdogs, and leaked an `--api-key` credential to every child. Those values now travel in an owner-only bootstrap file read once and unlinked by the child.
- Fixed OAuth logins being destroyed by an unrelated model-catalog refresh. A timed-out or partially failed post-login refresh used to roll freshly acquired tokens back to the previous credential and — because providers rotate refresh tokens — could permanently strand the account behind repeating `invalid_grant` failures. Freshly persisted credentials now always survive the refresh.
- Fixed `/model` and `/logout` hanging. The selector's 15-second abort now crosses the engine RPC boundary and covers credential reload, model refreshes run in a concurrent correlated lane so selection stays responsive, and `/logout` publishes credential removal immediately instead of timing out after it had already deleted the credential. Newly authenticated providers also appear without a restart ([#1918](https://github.com/bastani-inc/atomic/issues/1918)).
- Fixed Anthropic subscription login on Bun-hosted Linux sessions remaining at `Exchanging authorization code for tokens...`, caused by Bun not exposing Undici's CommonJS `install()` helper through its ESM namespace, so the configured transport was silently never used.
- Fixed RPC `save_provider_credential` writes disappearing after restart (they were stored as non-persistent runtime overrides), `ANTHROPIC_AUTH_TOKEN` header-only bearer authentication being rejected across catalog and request paths, and GitHub Copilot requests failing with `421 Misdirected Request` for `COPILOT_GITHUB_TOKEN` users on business, enterprise, and GHE tenants ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Fixed a mid-turn compaction leaving the turn unsendable, which the provider rejected with an unrecoverable `400 ... each tool_use must have a single result`. The post-tool preflight returned the agent's live internal message array, so every later turn duplicated assistant messages and tool results; it now returns its own copy, with provider-side deduplication and a descriptive pre-conversion guard as defence in depth ([#2051](https://github.com/bastani-inc/atomic/issues/2051)).
- Fixed reasoning tokens being able to fail a compaction outright. The planner capped its own request at `0.8 × reserveTokens` — an input-side reserve reused as an output cap — so a high-reasoning session could spend the whole budget thinking and return no deletion records, killing the active turn during a mid-turn preflight. The planner now sends no `max_tokens` at all; branch summarization received the same fix.
- Fixed compaction rebuilding the retained recent messages as structured blocks after the boundary, which could produce out-of-order provider blocks and a rejected request. The `preserve_recent` tail is now serialized with the same transcript grammar and concatenated onto the boundary, losslessly: retained tool results keep their full text and retained images stay as image blocks.
- Fixed repeated `/compact` presses starting several overlapping compactions. `AgentSession.compact()` is now single-flight — a second manual request joins the run in flight rather than emitting a duplicate boundary — and messages queued before an automatic compaction no longer stay stuck until Escape ([#1984](https://github.com/bastani-inc/atomic/issues/1984)).
- Fixed `/reload` re-transpiling and re-evaluating the entire transitive TypeScript graph of every extension on Windows and in compiled builds even when nothing changed. A per-extension SHA256 graph manifest now lets an unchanged graph reuse its evaluated factory; reloading the five builtin packages drops from ~25 s cold / ~16 s warm to ~90 ms ([#2069](https://github.com/bastani-inc/atomic/issues/2069)).
- Fixed interactive startup crashing with `Interactive engine did not become ready within 5000 ms` on machines with slow cold starts; the fixed deadline is removed while a dead process or broken transport still fails fast ([#1962](https://github.com/bastani-inc/atomic/issues/1962)).
- Fixed the external editor failing on Windows whenever the editor path, its arguments, or the temporary prompt path contained a space, and fixed the compiled package CLI crashing before TUI startup when invoked from another working directory ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Fixed physical Escape and Ctrl+C depending on the configurable `app.clear` action, which could send Escape to the engine stop branch and strip Ctrl+C of its host route. Both are now matched by physical key identity, and a single Ctrl+C again dismisses the `/login` popups instead of clearing the hidden editor and risking a double-press exit.
- Fixed session-tree navigation being accepted while a response was still streaming, which stranded the in-flight turn's tool results on the abandoned branch, and fixed a startup session switch rendering every agent event twice.
- Fixed inherited `.pi` extensions conflicting with Atomic-bundled resources, extension-contributed skills, prompts, and themes losing their package source metadata after a resource reload, and a nested linked git worktree loading the repository's `AGENTS.md`/`CLAUDE.md` twice ([#1955](https://github.com/bastani-inc/atomic/issues/1955)).
- Fixed workflow reliability from the parent-session side: stage `output:` no longer lost to an admitted external turn, `reads:` references fail loudly when an artifact is missing instead of handing the model an empty file, the resume picker stops offering stale or non-resumable runs, fast named workflows no longer leave a stale "still running" claim, queued work no longer escapes a controlled pause ([#1983](https://github.com/bastani-inc/atomic/issues/1983)), and tool-only workflows are no longer reported failed after their durable side effects completed ([#1991](https://github.com/bastani-inc/atomic/issues/1991)).
- Fixed the RPC `bash` command bypassing extension `user_bash` handlers, so sandbox and remote-shell extensions see an RPC request exactly as they see `user_bash`.
- Fixed a failed `git` package install leaving a half-written checkout that silently suppressed the next repair, and hardened the archive and SQLite path-selector parsers against polynomial-time regex backtracking (CodeQL `js/polynomial-redos`) with no change to accepted selector syntax.
- Fixed PDF extraction dropping text and streaming `TypeError: Math.sumPrecise is not a function` on Node runtimes without `Math.sumPrecise` by exact-pinning `unpdf` to `1.7.0` instead of letting it float to 1.8.0 ([#2141](https://github.com/bastani-inc/atomic/issues/2141)).

## [0.9.11-alpha.12] - 2026-08-03

### Fixed

- Fixed PDF extraction dropping text and streaming `TypeError: Math.sumPrecise is not a function` warnings on Node runtimes that do not implement `Math.sumPrecise`. The `unpdf` dependency was declared as the floating range `^1.6.2`, which resolves to 1.8.0 in a published or global install; that release's bundled PDF.js v6.1 dropped the guarded fallback and calls the builtin unconditionally, so extraction threw per glyph, emitted font-substitution warnings, and silently returned fewer text items. `unpdf` is now exact-pinned to `1.7.0`, the newest release that still ships the fallback, so no install can float to 1.8.0 ([#2141](https://github.com/bastani-inc/atomic/issues/2141)).
- Upgraded the pinned `unpdf` from 1.6.2 to 1.7.0, which adds `cMapUrl` CJK font support under Node, destroys internally created document proxies and canvas resources, and preserves line breaks in `extractText` with `mergePages`.

## [0.9.11-alpha.11] - 2026-08-03

### Breaking Changes

- Workflow run targets now require the full 36-character run UUID. Typed prefixes are no longer resolved by any command or workflow-tool action that accepts `runId`, including `status`, `stages`, `stage`, `transcript`, `send`, `pause`, `resume`, `interrupt`, and `quit`, and by `/workflow connect`, `/workflow attach`, and `/workflow resume`. A target that is not a well-formed 8-4-4-4-12 hex UUID — a prefix, a 32-character dashless id, or a same-length non-hex string — is rejected with `Run id must be a full 36-character UUID; got "339e05a4" (8 chars).`, which is deliberately distinct from `Run not found:` so a truncated paste is diagnosable as truncated rather than looking like a stale run. Since every user-facing surface already prints the full id, copy it back verbatim. Because ids are unique and now matched exactly, run-target ambiguity is unreachable and the "Ambiguous run prefix" diagnostic is gone.
- Stage targets are now matched exactly. A `stageId` resolves by exact stage id — a bare UUID at the root, the full `runId:stageId` composite for a stage inside a nested `ctx.workflow(...)` import, or `tool:<argsHash>` for a `ctx.tool` node — or by exact stage or tool name. Prefixes and partial names no longer resolve, so `build` will not select `build-check`. Stage ids are deliberately **not** held to the 36-character rule, because nested and tool identifiers are legitimately longer or differently shaped; nested-stage and tool-node targeting are unchanged. Two stages sharing an exact name still return the existing ambiguity diagnostic.

### Changed

- Removed the four-workflow display cap from the BACKGROUND widget so every qualifying top-level run is rendered.
- Moved durable workflow run artifacts—including goal ledgers, Ralph implementation notes, QA evidence video paths, and worktree task outputs—from per-invocation OS temp directories to the run-scoped durable root under the Atomic config directory (`~/.atomic/workflows/runs/<runId>/`, overridable with `ATOMIC_WORKFLOW_ARTIFACT_DIR`), so they survive OS temp purges and follow state-aware retention.
- Toggling tool-output expansion (`ctrl+o`) no longer prints a `Tool output: expanded` / `Tool output: collapsed` status line. The chat re-renders in the new state, which is the same information without the extra line.

- Workflow run identifiers are now shown as full UUIDs across the BACKGROUND widget, status and detail views, run pickers, control messages, and awaiting-input attribution banners. BACKGROUND cards use a two-line identity layout at 80 columns and wider, while narrow chat surfaces wrap full ids without cutting them or breaking their borders. The public `workflow({ action: "status", format: "json" })` payload no longer includes `runIdPrefix`; read `runId` instead, which now carries the full id. The `/workflow connect` run picker now shows five runs at a time and scrolls to additional retained runs with the arrow keys or mouse wheel. The picker's type-to-filter box still narrows by name substring or id prefix, since that selects a row from a list rather than resolving a typed id.

### Fixed

- Fixed Escape aborts restoring queued steering and follow-up messages to the editor in the main interactive chat and attached workflow-stage chats instead of leaving them stuck in the pending-message queue.
- Fixed `/model <model_name>` waiting indefinitely when a model-catalog refresh never settles; it now stops after the interactive 15-second deadline and searches cached models.
- Quit workflow cards now expire from the BACKGROUND widget after the same recent-run window as finished cards while remaining resumable and discoverable through workflow status; the header count now matches the rendered cards after expiry.
- Fixed detached foreground subagent completion notifications rendering as an unstyled dump of the full child output. They now use the same structured, collapsed notification UI as background completions while preserving the distinct detached-task wording, and persisted notifications with custom labels remain parseable.
- Fixed workflow-stage re-attach status falling back to `Working...` while context compaction is still active; the active compaction reason now survives host remounts so manual compaction, threshold auto-compaction, and overflow recovery retain their factual status labels.
- Fixed workflow stage file output being lost when an admitted external turn (for example an async subagent completion) arrived while the stage was still running: the deterministic `output:` write re-derived its content from session state at close time, so a trailing acknowledgement could overwrite the stage's real deliverable. Every stage that declares `output:` now also gets a durable, searchable companion transcript outside the repository tree, and the receipt names both paths with the intended access pattern, so the work remains recoverable regardless of which turn was persisted. The runner does not attempt to infer which turn was the intended deliverable; it writes the stage's final assistant message, and the receipt reports one fact only — an empty artifact.
- Fixed the workflow resume picker offering stale or non-resumable runs. Picker and command paths now share one resumability predicate and revalidate durable state before displaying a target, while explicit ids retain their explanatory rejection.
- Fixed workflow `reads:` references to fail loudly when an artifact path no longer exists instead of handing the model an empty, silently missing context file.
- Fixed relayed terminal subagent results retaining their stable stage-admission identity when delivered through the intercom ordering barrier, so an acknowledgement turn cannot arrive without the admission marker that identifies it in a running stage's companion transcript.
- Fixed a failed companion-transcript write reporting a bogus `Output file error` for an output artifact that was written correctly, and returning the full stage text under `outputMode: "file-only"` instead of the compact reference. A transcript failure now degrades to a warning naming the transcript path while the saved-output reference is unchanged, and artifact-retention pruning failures no longer propagate into the stage receipt.
- Fixed `createWorkflowArtifactDirectory()` attributing artifacts to the ambient most-recently-started run rather than the workflow that created them, and returning one shared directory whose fixed artifact filenames could overwrite each other. Every builtin call site now passes its owning run id, and each call receives a unique directory beneath that run.
- Fixed the workflow resume picker re-offering a live run as a durable row after the shared resumability predicate rejected it for missing artifacts or checkpoints. Rejected non-running snapshots are now suppressed from live and durable resumable rows, while completed inspection rows remain available; active-running snapshots retain their existing durable fallback.

### Added
- Added first-class Alpine/musl Linux support for x64 and arm64, including native search and PTY bindings and the `atomic-linux-x64-musl.tar.gz` and `atomic-linux-arm64-musl.tar.gz` release archives. The musl archives omit the clipboard native binding and glibc-linked `@embedded-postgres/*` binaries: Atomic falls back to Linux clipboard commands/OSC52, while durable Alpine workflows require external Postgres via `DBOS_SYSTEM_DATABASE_URL` or Docker; otherwise the existing loud non-durable in-memory fallback applies.
- Added state-aware retention for durable workflow artifact directories: only terminal or unowned runs older than `WORKFLOW_ARTIFACT_RETENTION_MS` are pruned, while running, paused, quit, blocked, and awaiting-input runs keep live resume dependencies. Aging out a terminal run now deletes its durable entry first and removes the artifact directory only when that deletion succeeds, so a surviving durable record can never reference deleted files.
- Added durable, searchable companion transcripts for every workflow stage that declares `output:`. The caller's receipt names both the artifact and transcript paths and says to search with `rg`, read narrow line ranges, and never read the transcript whole; under `outputMode: "file-only"`, that receipt is the entire handoff.
- Added explicit warnings for degenerate stage artifacts instead of silently persisting them: an empty write or a write that only points to its own output path directs the reader to the companion transcript.
- Added a real artifact-integrity probe to the shared resume predicate. A run whose snapshot references a run-scoped artifact path is offered for resume only while that run directory still exists, and durable-checkpoint eligibility is supplied from the authoritative loadable check, so `/workflow resume` matches the documented behaviour instead of relying on flags no caller set.

## [0.9.11-alpha.10] - 2026-08-01

### Fixed

- Fixed an isolated interactive-engine exit after an admitted tool call leaving that session permanently pending and unusable on resume. Reopening an inactive session now derives explicit error results for unanswered `toolUse` calls without rewriting its append-only JSONL, so the transcript can render, compact, and accept another prompt while preserving that the interrupted command's side effects are unknown. Unexpected-restart notices now also retain the bounded exit kind, code, or signal that triggered recovery while excluding child stderr.
- Fixed Anthropic subscription login on Bun-hosted Linux sessions remaining at `Exchanging authorization code for tokens...`. Atomic's HTTP setup relied on Undici's CommonJS `install()` helper, but Bun does not expose that helper through its ESM namespace, so source-mode and compiled interactive-engine processes silently kept Bun's native `fetch` instead of the configured Undici transport. Atomic now installs the exported Undici web globals explicitly under Bun. Login completion also ends when the provider credential is persisted and publishes that provider against the already-loaded model snapshot immediately; cache restoration, ambient availability checks, and catalog discovery no longer run inside the successful credential transaction, so none can keep the login dialog open after the provider appears in `auth.json`.
- Fixed existing authenticated users being unable to select a model, or seeing `Refreshing model catalogs…` indefinitely, after an isolated or direct `/model` refresh stalled. The frontend's 15-second abort previously never crossed the engine RPC boundary, the engine started its deadline only after an unbounded credential/availability reload, and the selector assumed refresh work would honor cancellation. The shared deadline now covers credential reload and catalog work inside the engine, the selector races that deadline itself and replaces the progress state on both timeout and rejection, and model refreshes use a concurrent correlated lane so selection remains responsive while catalogs update. Existing `auth.json` credentials remain compatible and no `~/.atomic` reset is required.
- Fixed `/logout` timing out after it had already deleted the credential, leaving the provider in `/model` until restart and preventing the selector from reopening. Logout now publishes credential removal immediately, skips post-logout catalog refreshes, bounds the local probe for authentication that remains in the environment, and prevents a model refresh started before the mutation from overwriting the newer login or logout snapshot when it eventually settles.

## [0.9.11-alpha.9] - 2026-08-01

### Breaking Changes

- Adopted pi 0.83.0 (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, `@earendil-works/pi-tui` at `^0.83.0`), which moves the bundled TypeBox to 1.3.7. TypeBox 1.3.7 **removes seven deprecated APIs**. Atomic inherits the removal exactly as upstream shipped it and does not provide a compatibility shim, so an extension, skill, workflow, or MCP schema that still calls one of these will now fail to compile and to run. Atomic's own sources use none of them; the migration below is for package authors. TypeBox 1.3.7 also fixes compiled validation of nullable array tool arguments, and — because `typebox` is pinned to a single hoisted `1.3.7` node — an Atomic-defined tool schema and a pi-defined one now validate identically in the same process.

  | Removed API | Replacement | Migration |
  | --- | --- | --- |
  | `Type.Options(type, options)` | `Type.With(type, options)` | Straight rename; same arguments, same result. `Options` was already an alias of `With`. |
  | `Type.Base` | `Type.Refine()` + `Type.Unsafe()` | `class DateType extends Type.Base<Date> { Check(value) { return value instanceof Date } }` becomes `const DateType = Type.Refine(Type.Unsafe<Date>({}), (value) => value instanceof Date)`. Class-instance schemas lost their prototype methods through TypeBox's clone/spread compositor, which is why the class form is gone. |
  | `Type.Promise(item)` | none | Model the resolved value directly and validate after `await`, or hold the slot with `Type.Unsafe<Promise<T>>({})` when a schema entry is required. A promise was never checkable without settling it. |
  | `Type.Awaited(type)` | none | Nothing is left to unwrap once `Type.Promise` is gone: use the resolved type itself where `Type.Awaited(Type.Promise(T))` appeared. |
  | `Type.Iterator(items)` | none | Validate the materialized sequence with `Type.Array(items)` after draining the iterator, or hold the slot with `Type.Unsafe<Iterable<T>>({})`. |
  | `Type.AsyncIterator(items)` | none | Same as `Type.Iterator`: drain first and validate with `Type.Array(items)`, or use `Type.Unsafe<AsyncIterable<T>>({})`. |
  | `Value.Mutate(current, next)` | `Value.Patch(current, Value.Diff(current, next))` | `Patch` returns a **new** value rather than assigning into `current` in place, so code that relied on `Mutate` retaining internal references as a redraw signal must now assign the result and re-read the reference. |

- The RPC `login_provider` response no longer carries the API key it just stored. `RpcLoginProviderResult` dropped its `credential` member and now reports `{ provider, cancelled: false, type: "api_key", …catalog }`. The key travelled as plaintext inside a JSON line on the same stdout the credential-export door treats as the single egress, and the host had itself supplied that key through the login input form, so nothing needed it echoed back. A host that read `data.credential` should use the value it submitted; `type` confirms which kind of credential was saved.

### Added

- Added `ctx.scopedModels` to the extension context: the read-only list of models scoped to the current session, resolved from the `--models` flag and the `enabledModels` setting — the same set `/scoped-models` shows. It is empty when no scoping is configured. The value resolves at access time, so it follows session replacement, and under the isolated interactive engine it reflects the engine's catalogue rather than a stale host snapshot. It reports the scope and cannot widen it: `readonly` is a compile-time claim only, so both the extension context and the hand-built context that shortcut handlers receive hand back a fresh copy on every read, and an extension that mutates what it was given changes nothing. Its types are exported too — `ScopedModel` for one entry and `ExtensionScopedModels` for the accessor — from `core/extensions/types.ts` and from the package root, so an extension can name what it reads without importing an internal module. Documented in `docs/extensions.md`.
- Added `atomic auth print-api-key` and `atomic auth print-bearer-token`, which print one configured credential for an external client. The credential goes to stdout and nothing else — warnings, provider selection, refresh notices, and help all go to stderr — so `KEY=$(atomic auth print-api-key --model gpt-5.5)` cannot capture a diagnostic. `--model` is required, so no ambient "current model" can emit a credential you did not name. `--provider` and `--model` are the only options either subcommand accepts, enforced as an allowlist over the parsed arguments rather than a list of refusals, so a path-naming flag such as `--export` or `--session-dir` is a usage error instead of a flag this path happens to ignore; there is no file or clipboard sink. When `--provider` is omitted, eligibility comes from the runtime's resolved auth type for each provider offering that model, so a credential supplied by an environment variable or a custom resolver — which `auth.json` never sees — is exported like any other, and a provider offering both an API key and OAuth is selected by whichever one a request would actually use rather than by what it supports. `print-bearer-token` refreshes a token with less than `--min-expiry` (default `30m`, accepting `ms`/`s`/`m`/`h`, in both `--min-expiry 30m` and `--min-expiry=30m` spellings) remaining; `--min-expiry` with `print-api-key` is a usage error in either spelling rather than a silent no-op, because an API key has no expiry. Failures are distinguishable by exit code: `1` usage, `2` no credential configured, `3` provider ambiguous, `4` credential kind unsupported for that provider, `5` the OAuth refresh itself failed (the only exit that promises the stored credential is untouched, because that failure happens before anything is persisted), `6` the provider cannot mint a token that lives long enough, `7` any other OAuth failure, which makes no claim about the stored credential, `8` the credential could not be written and nothing was emitted, and `9` only part of it was written and the output should be discarded. Which of those two a failed write gets is read from `process.stdout.bytesWritten` rather than guessed: nothing flushed is `8` with stdout provably empty, the whole payload flushed is exit `0` with the failure on stderr — the caller holds what it asked for — and a partial flush is `9`, the one exit whose stdout is not empty, because those bytes cannot be recalled and reporting success over a truncated secret would be worse than saying so. One function in `src/cli/credential-print.ts` is the only place in the source tree that can read the credential wrapper and write it to stdout; a test enumerates every call in `packages/coding-agent/src` that puts a non-literal value on the real stdout, and carries a negative control that fails if the scanner stops reporting a planted egress. Documented in `docs/usage.md` and `docs/security.md`.

### Changed

- `ChatSessionHostCommands.prompt` now receives the submitting key's mode as a second argument, and the new `ChatSessionSubmitMode` (`"auto" | "followUp"`) type is exported alongside the other chat-session host types. A host whose idle prompt command chooses a delivery queue can now read the mode of the invocation it is serving instead of shared state a concurrent submission may still own. Existing single-argument `prompt` implementations keep working unchanged ([#2074](https://github.com/bastani-inc/atomic/issues/2074)).
- The `/model` selector now highlights the top row whenever a filter query is typed, so the best match is the one Enter selects. Clearing the query restores the previous position, clamped to the restored list.
- Toggling tool-output expansion now reports the new state (`Tool output: expanded` / `Tool output: collapsed`) on the status line instead of re-rendering silently.
- The startup RESOURCES disclosure now lists `SYSTEM.md` and `APPEND_SYSTEM.md` alongside the `AGENTS.md`/`CLAUDE.md` context files, in the order they reach the prompt. Those files shape the system prompt exactly like a context file, so leaving them out of the listing hid them.
- llama.cpp models now declare streaming usage support, so token usage is reported during a streamed response instead of only at the end.
- A provider that ends a turn with a terminal reason the mapping does not recognise now surfaces a provider error naming the raw reason, instead of reporting an ordinary successful stop. A truncation or safety signal a provider adds after this release is therefore visible rather than arriving as a turn that looks like it finished normally; every raw reason that already mapped to a successful stop still does. Documented in `docs/providers.md`.
- Assistant messages carry the new `pending` stop reason while they are still streaming. It is replaced by the terminal reason before the message completes, so it never reaches session JSONL, but an RPC or SDK client that switches on the stop reason of a partial message needs the case, and a custom `streamSimple` provider should start its partial message at `pending` and raise if the stream ends still holding it. Documented in `docs/providers.md`, `docs/custom-provider.md`, `docs/rpc.md`, and `docs/session-format.md`.
- A stored OAuth token is now refreshed once fewer than five minutes of validity remain, rather than at expiry, so a long turn is no longer started on a credential that dies mid-request. The refresh runs inside the `auth.json` lock and re-checks the stored expiry after taking it, so concurrent sessions sharing one credential file refresh it once between them rather than once each. Documented in `docs/providers.md`.
- The OpenRouter login now accepts a pasted final redirect URL, or the authorization code on its own, for remote and headless machines where the browser cannot reach the loopback callback. pi 0.83.0 added that fallback to the OpenRouter OAuth flow, which races the callback against a manual-entry prompt; Atomic's terminal only offers the input for providers whose metadata declares a callback server, so inheriting the flow alone left the prompt waiting on an input that was never shown and the login ended at the callback timeout. Documented in `docs/providers.md`.

### Fixed

- Fixed `/reload` re-transpiling and re-evaluating the entire transitive TypeScript graph of every extension on Windows and in single-file (compiled) builds even when nothing changed. Transformed extension evaluations now record a per-extension graph manifest mapping every file jiti reads to a SHA256 content hash, persisted under the versioned transpile cache directory. On reload, an unchanged graph — every recorded file rehashes identically and the stored manifest matches — reuses the previously evaluated factory instead of re-importing; any hash mismatch, missing file, missing or corrupt manifest, or graph-shape change falls back to a full fresh re-evaluation, which rewrites the manifest, so edited extension source is always picked up. Measured on Windows, reloading the five builtin packages drops from ~25 s cold / ~16 s warm (jiti fsCache) to ~90 ms — the cost of rehashing the recorded graph ([#2069](https://github.com/bastani-inc/atomic/issues/2069)).
- Hardened the archive and SQLite path-selector parsers used by the read, search, and write tools against polynomial-time regex backtracking (CodeQL `js/polynomial-redos`). The ambiguous greedy `(.+\.ext)` prefixes were replaced with linear right-to-left scans; accepted selector syntax and parsing results are unchanged.
- Fixed a failed `git` package install leaving a half-written checkout behind. Because the next install treats an existing target directory as installed, that partial clone silently suppressed the repair. A clone, checkout, or dependency install that fails now removes the target directory and prunes the host/owner directories it created.
- Fixed the RPC `bash` command bypassing extension `user_bash` handlers. A sandbox or remote-shell extension that intercepts user shell commands now sees an RPC `bash` request exactly as it sees `user_bash`, with request-scoped ownership, streaming correlation, and result recording unchanged.
- Fixed a startup session switch rendering every agent event twice. A session replacement that began while the startup rebind was still binding extensions produced two live agent subscriptions; the superseded rebind now returns without subscribing.
- Fixed session-tree navigation being accepted while a response was still streaming, which stranded the in-flight turn's tool results on the abandoned branch. Navigation now rejects while streaming, the interactive tree selector stops the active response (restoring queued messages to the editor) before navigating, and a session replacement settles its active response before the outgoing session is torn down. The isolated engine's host facade deliberately does not re-enter its cooperative-abort round trip during teardown, so an unresponsive or dead engine still cannot hang a session switch.
- Fixed a nested linked git worktree loading the repository's `AGENTS.md`/`CLAUDE.md` twice. When a worktree created inside its own main repository has its own copy of the same tracked context file, the main repository's copy is now skipped. Ordinary repositories, sibling worktrees, bare layouts, and submodules keep normal ancestor inheritance.
- Fixed extension-contributed skills, prompt templates, and themes losing their package source metadata when they were registered after a resource reload. The reload's resolved metadata is now retained on the loader so later `extendResources` passes still resolve it.

## [0.9.11-alpha.8] - 2026-07-29

### Breaking Changes

- Replaced Atomic's legacy extension OAuth registration bridge with provider-owned authentication from `@earendil-works/pi-ai`. Extensions must declare OAuth or API-key authentication on their provider registration. The package root no longer exports the bridge functions `registerOAuthProvider`, `resetOAuthProviders`, `getOAuthApiKey`, `getOAuthProvider`, or `getOAuthProviders`, nor the bridge and credential/status types `LegacyOAuthProvider`, `OAuthProviderDescriptor`, `ApiKeyCredential`, `AuthCredential`, `AuthStatus`, or `OAuthCredential`; import current credential types from `@earendil-works/pi-ai` and use provider-owned authentication instead. The internal legacy registration/refresh machinery and custom API-key login hooks beyond pi's provider contract were also removed.
- Model configuration now follows pi's single-file `ModelConfig` contract. Atomic reads one `models.json` from the active Atomic agent directory (`~/.atomic/agent/models.json`, or the directory selected by `ATOMIC_CODING_AGENT_DIR`/`PI_CODING_AGENT_DIR`); it no longer reads project-scoped `.atomic/models.json`, falls back to `~/.pi/agent/models.json`, or layers and merges `.pi` and `.atomic` model configuration files. Move project-scoped custom providers and models into the active agent-directory file. The legacy `.pi` read fallback remains only for configuration surfaces that explicitly use layered paths, such as `auth.json`.
- Model-catalog refreshes now use pi's exact timeout semantics. `modelRefreshTimeoutMs` applies only to the initial runtime creation refresh, and post-login catalog refreshes are unbounded; the interactive `/model` selector owns its 15-second refresh timeout like pi, rendering cached models immediately, aborting a slow refresh, and reporting "Model refresh timed out; showing cached models." Callers of `ModelRuntime.refresh()` that require cancellation must provide their own abort signal.
- Extension `streamSimple` implementations are now scoped to their registered provider and `ModelRuntime`, matching pi's provider composer. Unregistering a newer provider no longer restores an older global API-owner registration; extensions that replace a provider stream must keep that provider registered for as long as the stream should remain active.
- Remote model catalogs now publish refreshed models in memory before persisting the catalog, matching pi's ordering. If the catalog-store write fails, refresh reports the storage error but the newly refreshed in-memory catalog remains active for the current process.
- `AuthStorage` now implements pi's asynchronous `CredentialStore` contract directly. Its synchronous compatibility methods (`get`, `set`, `remove`, `has`, `hasAuth`, `getAll`, `getAuthStatus`, `getLoadError`, `drainErrors`, and synchronous `list`), `asCredentialStore()` adapter, fallback/runtime-key helpers (`setFallbackResolver`, `getRuntimeApiKey`, `setRuntimeApiKey`, and `removeRuntimeApiKey`), and provider-auth methods (`login`, `logout`, `logoutAsync`, `getModelAuth`, `getApiKey`, and `getOAuthProviders`) were removed. Use and `await` `read`, `list`, `modify`, and `delete` for persisted credentials, call `reload` when an existing instance must reread its backing store, and use `ModelRuntime` for authentication resolution, login/logout, and runtime API-key overrides.
- `ModelRegistry` is now constructed from a `ModelRuntime` and exposes only pi's thin compatibility-facade surface. The `create` and `inMemory` factories, public `authStorage` member, and the `canRestoreUnknownModel`, `checkAuth`, `getAuth`, `getCustomApiKeyAuth`, `getCustomApiKeyAuthProviders`, `getProviders`, `hasProvider`, `hasRegisteredStreamSimpleForApi`, `login`, and `logoutProvider` methods were removed. Instantiate and consume `ModelRuntime` for runtime-owned model/provider discovery and authentication, and pass it to `new ModelRegistry(runtime)` only where the synchronous extension-facing facade is required.
- `CreateAgentSessionOptions` no longer accepts the public `authStorage` and `modelRegistry` overrides. Construct or supply a `ModelRuntime` through `modelRuntime` instead; when omitted, `createAgentSession()` creates the runtime from the active agent directory's `auth.json` and `models.json`.
- Isolated interactive `ctx.ui.custom()` components that consume Ctrl+C must now pass `{ handlesCtrlC: true }`. Without that declaration the first Ctrl+C is the host escape and closes the focused component, resolving its promise with `undefined`; a declared component receives the first press and stays host-closable on the next one. This keeps a component that never resolves from trapping the keyboard. The bundled workflow and MCP surfaces were migrated; third-party extensions that bind Ctrl+C for Skip, Close, or cancel need the one-line addition.

### Added

- Added a `handlesCtrlC` option to `ctx.ui.custom()` for components that bind the key themselves. See the migration note under Breaking Changes for the behavior an existing component must declare to keep.

### Changed

- Adopted pi's provider-owned `ModelRuntime` architecture for model composition, credentials, streaming, and catalog refresh. `ModelRegistry` is now the thin synchronous extension-compatibility facade used by pi, while coding-agent, SDK, RPC, isolated-engine, workflow, and MCP internals consume `ModelRuntime` directly.

### Fixed
- Fixed physical Escape and Ctrl+C depending on the configurable `app.clear` action. With `app.clear` bound to Escape, Escape could reach the engine stop/restart branch while Ctrl+C lost the host route entirely — the opposite of the intended behavior on both keys. Both are now matched by physical key identity through pi-tui's parser (so complete CSI/OSC sequences and bracketed paste are never mistaken for a bare Escape, and Kitty key-release events never act twice), and the configured `app.clear` still governs ordinary editor clearing on whatever key it is bound to. The editor applies the same fixed Escape guard before its configurable action handlers, while keeping Escape as autocomplete cancel when autocomplete is open.
- Fixed the remote-UI escape hatch replacing a healthy engine, and made it act on the first press. Ctrl+C behind a remote `ctx.ui.custom()` component used to need two presses and then stopped and replaced the whole engine child, discarding everything else that generation was doing. Ownership is now declared per mount: an unresponsive engine is still terminated on the first press (a wedged child cannot run a local handler either), a component that declared `handlesCtrlC` receives the press and keeps working — and is closed if it is still holding input on the next one — and an undeclared component is closed immediately through the ordinary close path, resolving its `ctx.ui.custom()` promise with `undefined` while the engine, and every other component it has mounted, keeps running.
- Fixed an engine child that exits during the startup window leaving a live TUI bound to a dead engine. A child could report ready, let startup return, and exit before the host finished attaching its listeners; that death had no listener and recovery never ran. The terminal event is now retained and replayed to a listener that attaches afterwards, while a death belonging to an already-replaced generation is never replayed.
- Fixed frames buffered by a dead engine generation being delivered to the host afterwards. A `ctx.ui.custom()` mount frame queued before its controller subscribed could arrive after that generation died and after teardown ran, remounting exactly the UI the teardown had just closed — and colliding with the replacement child, whose component IDs restart at the same values. Buffered engine messages and extension UI requests are now tagged with their generation, dropped when it dies, and never observed from a replaced generation; frames belonging to the live generation still replay as before.
- Fixed two submissions whose text differs only in whitespace being able to restore each other's draft. The exact typed buffer travelled in a single shared slot that every Enter overwrote and every finished turn cleared, so a failure could put back the wrong text — or nothing. Each submission now carries its own draft end to end, through the input queue and the prompt turn, and submissions still queued behind a failed one come back with it in the order they were entered rather than being restored later in reverse.
- Fixed a raw stream write failure still discarding submitted text. `write EPIPE`, `Cannot call write after a stream was destroyed`, and `write after end` are exactly what a dying engine produces, and none of them matched the message fragments the host used to recognize a send that never landed — so those submissions showed a red `Error: write EPIPE` and the typed text was lost. Transport failures are now classified where they happen, with an additive non-enumerable marker that leaves the error's identity and its `code`, `errno`, and `syscall` fields intact, and the queued writer, the child exit/error/stdin handlers, an explicit stop, a malformed transport, a missing writer, and a not-started client all carry it. Request timeouts after a successful write, RPC error responses, provider failures, and anything after `agent_start` are deliberately not classified and still surface as errors.
- Fixed an idle Alt+Enter submission losing its surrounding whitespace when the send failed. Alt+Enter is an app action, so the editor never takes its own pre-trim snapshot for it and the submit handler's only draft is the value the Alt+Enter path hands over — which was the already-trimmed text. The raw expanded buffer is passed instead, so a failed idle follow-up now returns exactly what was typed while the agent still receives the same normalized prompt.
- Fixed quiet work that the engine had already started being offered back to the editor as unsent. Ownership was inferred from the first byte of output, so `!touch marker && sleep 400` — which changes the working tree and prints nothing — looked identical to a command the child never received: killing the engine put that `!` line back in the editor, inviting a second run. The child now announces ownership of every correlated request, flushed before its handler can touch the shell, an extension, the queue, or compaction, and the host restores a draft only for a transport failure that arrived without an announcement. An accepted failure is reported as an ordinary failure instead. The interactive-engine protocol version is now `2`; a mismatched host and child do not bind rather than falling back to the old policy.
- Fixed automatic recovery discarding the ownership of the work it was recovering from. An announcement can still be unparsed in the pipe when the child dies, and recovery — which starts in the same turn as the death event — detached the reader, retired the generation, and re-failed those requests with `Agent process stopped`, so already-running work was reported as never sent and offered back for a second run. A dead generation now gets a bounded settling window: its UI is torn down immediately as before, but its stdout keeps being read, only its ownership frames are honoured, its requests are classified exactly once with the error of whatever ended it, and the replacement starts only afterwards. Every terminal cause — exit, spawn error, stdin error, malformed transport, and explicit stop — goes through that one path, and a descendant that inherited stdout cannot delay recovery beyond the bound. The JSONL reader also reports completion for a stream that ends with nothing buffered, which previously left that wait to time out.
- Fixed session teardown racing an engine replacement. A recovery attempt sits between its own stop and its spawn with no child attached, so disposal's stop found nothing to do and returned — and the attempt then started a fresh engine, plus its host initialization, after teardown had finished. Disposal now voids the in-flight restart permit and joins the attempt before returning, and a superseded restart fails as cancelled instead of quietly succeeding. Automatic single-shot recovery, the Ctrl+C rescue, and the overdue-replacement rules are unchanged; a cancelled attempt reports no failure and arms nothing.
- Exempted every cooperative-cancellation command from the generic 30-second RPC deadline, not just `abort`. Cancelling a running `!sleep` still produced `Timeout waiting for response to abort_bash` — a red error about the host's own impatience while the engine was still working. `abort_bash`, `abort_compaction`, `abort_retry`, and `cancel_login_provider` now wait like `abort` does.
- Fixed engine loss during an active replacement leaving the host with no engine and no way to ask for one. A newer child dying while the automatic attempt was still running was silently ignored, and once that attempt settled nothing armed Ctrl+C. The nested death now latches the escape hatch without starting a second automatic restart, so recovery stays single-shot and user-driven.
- Fixed engine-owned line widgets surviving the generation that published them. `setWidget` writes plain host state with no component to close, so a dead generation left its lines on screen. Widget keys are now tracked against their latest writing generation and released only when that generation dies; a newer generation that rewrites the same key takes ownership and its content survives stale cleanup.
- Fixed the restored draft being the already-trimmed submission rather than what was typed. pi-tui expands pastes, trims, and clears the editor before it calls `onSubmit`, so the callback argument could never carry the user's leading or trailing whitespace. The editor now snapshots its expanded buffer for the same input dispatch that submits it, and restoration uses that.
- Stopped showing a red transport error next to a restored draft. A submission the engine never accepted already reports itself: the draft is back in the editor and the calm recovery status explains why. Errors that were not restored — provider failures, model errors, and any failure after the agent turn started — still surface.
- Fixed a stream write that failed in its callback leaving its caller's promise pending forever. An EPIPE to a dying engine could hang a prompt or an abort instead of failing it; the in-flight frame is now rejected along with everything still queued.
- Fixed the heartbeat observer consuming the buffered startup custom-UI messages. Watching heartbeats through the generic engine-message channel drained messages queued before the real controllers subscribed, so a custom UI opened during startup could be lost. Heartbeats now arrive on a dedicated callback that does not participate in that buffering.

- Fixed interrupting a tool call terminating the interactive engine and reporting `Engine terminated; … result unknown; inspect side effects before retrying`. Escape raced the engine's cooperative abort against a 250 ms deadline and killed plus restarted the engine child whenever it lost that race, which any ordinary event-loop stall in the child could cause — a synchronous extension or tool callback, GC, or a large tool-output flush. Escape now simply requests the engine's cooperative cancellation and waits for it: interrupting never terminates the engine, never restarts it, never discards in-flight tool state, and the removed message no longer exists anywhere in Atomic. The wait is genuinely unbounded — `abort` is exempt from the generic 30-second RPC deadline, so a stopped or blocked child no longer turns Escape into a red `Timeout waiting for response to abort` while the engine is still working.
- Fixed a total interactive-TUI lockup when the engine generation behind a mounted remote custom UI died. A crashed, killed, or replaced engine left the host's `ctx.ui.custom()` components mounted forever: their promises never settled, an inline mount kept the editor detached from its container, an overlay kept input focus, widget keys and terminal modes stayed claimed, and the blocking inline custom-UI depth stayed above zero, so typing, Escape, Ctrl+C, and even Ctrl+C Ctrl+C all did nothing and the process had to be killed from another terminal. Engine death is now a host-local lifecycle event that immediately closes every remote component from the dead generation, settles its promises, releases widget keys, resets terminal modes, remounts and refocuses the editor, and unwinds the inline depth to zero — without waiting for a replacement `engine_ready`, so a crash with no restart, or a hung or failed restart, recovers too. Teardown unwinds newest-first and hides each overlay through its own handle, so a nested overlay never restores focus to a layer that is already gone and never closes an unrelated native overlay, and the editor is refocused only when no surviving native modal owns input. Host `select`, `confirm`, `input`, and `editor` dialogs opened by the dead generation are now owned by it too: they used to stay on screen forever with their promise unsettled and could answer through the replacement child's writer.
- Added recovery from an unexpected interactive-engine exit. A crashed engine previously left a live TUI bound to a dead child with no way back; Atomic now makes one automatic replacement attempt and shows the calm status `Interactive engine stopped unexpectedly; restarting.`, reports `Interactive engine restart failed: …` when the replacement cannot start, and leaves the editor mounted, focused, and usable in either case.
- Kept an explicit escape hatch for a wedged engine, matching the watchdog's own `Esc interrupt · Ctrl+C terminate` copy, and made it reachable in every case where the key would otherwise vanish. Ctrl+C is now always handled by the host while an engine-owned `ctx.ui.custom()` component or overlay holds input — even with a healthy engine, because such a component forwards every key to the child and a component that never resolves would trap the escape key too; with a healthy engine the host closes that one component rather than touching the engine. Ctrl+C also still replaces an engine that is provably not answering: watchdog-confirmed unresponsive, a cooperative abort unanswered past the same one-second threshold, a replacement waiting for readiness past it (previously unrecoverable, since such a child emits no heartbeat and no diagnostic), or a replacement that failed outright, which now keeps Ctrl+C armed indefinitely instead of leaving the host permanently engine-less. A repeat press can stop an overdue replacement and start another; a fresh replacement is never stopped by a stray press; and host-native selectors, dialogs, input forms, and session pickers keep Ctrl+C-as-cancel whenever the engine is healthy.
- Fixed the interactive engine exporting its private control values to every process it spawned. `ATOMIC_INTERACTIVE_ENGINE_CHILD`, `_HOST_PID`, `_GUARD_FILE`, and `_API_KEY` were set on the engine child's environment, so bash tool commands, subagent children, MCP servers, hooks, and nested Atomic runs all inherited them: a nested `atomic` started in engine mode and flooded stdout with heartbeats instead of opening a TUI, parent-process guardians watched an unrelated PID, subagent idle watchdogs never fired, and an `--api-key` credential reached every child. Deleting the variables at startup cannot fix this under Bun, where a child spawned without an explicit `env` inherits the runtime's launch-time environment; the engine is therefore now launched with an environment that never contains the values. They travel in an owner-only bootstrap file whose path is a private CLI argument, read once and unlinked by the child, and every reader uses that frozen snapshot. Legacy variables are still scrubbed from `process.env` and from subagent foreground, background-streaming, and detached-runner spawn environments as defense in depth, and extension `ctx.exec` passes an explicit environment. Bootstrap cleanup is ownership-scoped: the child unlinks exactly the file it was told to read, while recursive removal of the containing directory requires the handle the host received when it created it, so a hostile `--internal-engine-bootstrap` path cannot delete an unrelated directory. A failed publication removes its own temporary file and directory before rethrowing, so a `0600` credential file is never left behind.
- Fixed typed input being discarded when a submission could not be sent. A prompt that failed because the engine was gone, stopping, or restarting showed only `Error: Agent process stopped` and lost the text. The submission now returns to the editor exactly as the editor handed it over, placed ahead of anything typed while the send was still pending and separated by a blank line, so neither part is lost and neither is trimmed; a pending large paste is expanded rather than reduced to a dead `[paste #1 …]` marker, which `setText` would otherwise discard. Focus returns to the editor unless a modal owns input, and the restored text is treated as a draft rather than replayed as a submission. This covers every submit route, not just Enter: `/atomic`, a deferred slash command, an extension command during compaction, a streaming steer send, `!bash`, `/compact`, and Alt+Enter all dispatch straight to the engine and previously dropped the text on their own. Text that reached a started agent turn stays in the transcript as before.
- Fixed OAuth logins being destroyed by an unrelated model-catalog refresh, matching upstream pi's behavior. After a successful `/login`, a timed-out (aborted) or partially failed catalog refresh threw `Model refresh aborted after OAuth login` and rolled the freshly acquired tokens back to the previous credential. Because providers rotate refresh tokens, that rollback could permanently strand a server-side-invalidated credential — every send then failed with `invalid_grant` ("Refresh token not found or invalid") and every re-login was rolled back again, typically on machines with slow routes to catalog endpoints. Freshly persisted OAuth credentials now always survive the post-login refresh: per-provider refresh errors and refresh timeouts no longer fail the login in either the direct interactive or isolated-engine path, and models fall back to the cached snapshot.
- Fixed the `/model` selector reporting `Could not refresh llama.cpp; showing cached models.` for users who never configured a llama.cpp server. The bundled llama.cpp extension now uses pi's provider-owned registration: the provider stays dormant — no refresh attempt, no error — until a server is configured through `LLAMA_BASE_URL` or a stored login, and `/login` prompts for the server URL plus optional API key exactly like pi.
- Fixed RPC `save_provider_credential` writes disappearing after process restart. Saved API-key and OAuth credentials now persist to `auth.json` through `ModelRuntime.saveCredential()` instead of being stored as non-persistent runtime API-key overrides; the RPC command again accepts the full credential union, awaits a model-catalog refresh, and returns the refreshed catalog.
- Fixed GitHub Copilot requests failing with `421 Misdirected Request` for users who authenticate with `COPILOT_GITHUB_TOKEN`. Upstream pi pins the `github-copilot` provider to `https://api.individual.githubcopilot.com` and only derives a per-tenant host inside its OAuth loader, so business, enterprise, and GHE tokens supplied through the environment were sent to the individual CAPI host. Atomic now resolves the endpoint for env-token auth again, highest precedence first: `COPILOT_API_TARGET` / `GITHUB_COPILOT_BASE_URL`, the token's embedded `proxy-ep` segment, `GITHUB_SERVER_URL` (`<tenant>.ghe.com` routes to `copilot-api.<tenant>.ghe.com`, other non-`github.com` hosts to `https://api.enterprise.githubcopilot.com`), and finally the public routing hub `https://api.githubcopilot.com`. A `models.json` provider `baseUrl` still overrides all of it, and the OAuth path remains exactly upstream.

## [0.9.11-alpha.7] - 2026-07-28

### Changed

- Synchronized the complete bundled `impeccable` skill tree with pbakaus/impeccable at `14c27e43af190cbed3793d0b76e33feea4a8859a` (previously `630fc2682a5bd39b25a8e61f74b6b3f14f2b1e21`). Impeccable 4.0.3 replaces the brand/product register split with four surface modes (Persuade, Operate, Read, Experience), routes work through new `routing.md`, `new-work.md`, and mandatory `craft-floor.md` guidance in place of the deprecated `craft` ownership model, adds a `doctor` drift check with repair steps, adds documenter and finish-reviewer agents with degraded-mode reference fallbacks, adds surface briefs plus concept/composition catalogs, staleness checks, and image generation, and expands live review with generation preflight, source search and locking, poll lanes, and TanStack support alongside updated detector rules, static-HTML analysis, hooks, and platform guidance. Atomic's divergences are preserved: scanner-based HTML/script/style/comment filtering, argument-separated `git check-ignore`, backslash-safe live-preview selector escaping, the CodeQL CSS-property fix, and no removed-editor hook/provider compatibility.
- Verified the complete bundled `playwright-cli` skill tree against microsoft/playwright-cli at `eee5a185c98e6b04d88f580d45a854e9692ab50b` (previously `793cfb32572733cbcb401e6f28d05a7a914ce408`). Every bundled file is already byte-identical to that commit, so its browser, session, tracing, and test-generation guidance is unchanged.
- Verified the complete bundled `liteparse` skill tree against run-llama/llamaparse-agent-skills `main` at `2dcef7c62417bd2ec4671fce4621bb1e8cce48d0`, which is still the previously recorded baseline. Its `SKILL.md` and `scripts/search.py` helper are the only upstream files, the helper is byte-identical, and LiteParse's parse-once workflow, bounded search windows, BM25 fallback search, OCR, and page-screenshot guidance are unchanged. Atomic keeps two deliberate divergences: the public `LiteParse` heading and a package-relative `scripts/search.py` invocation instead of upstream's stale `Effective LiteParse` heading and `.claude/skills/effective-liteparse/` path. The skill still resolves only as `liteparse`.

### Fixed

- Stopped a broad Python packaging ignore rule from excluding `scripts/lib/` files inside bundled upstream skill trees, so a synchronized skill ships its complete set of modules instead of silently losing part of the tree.
- Fixed the main and workflow-stage chats losing the automatic-compaction activity indicator after a tool result. When a tool call (for example a subagent) expanded the context enough to trigger post-tool compaction, the follow-up turn that Pi opens before compaction finishes replaced the factual `Auto-compacting...` status with a generic Working spinner, and the compaction's own cleanup then removed that spinner too — leaving the main chat with no activity at all for the rest of the continuing stream until the next user message. Compaction now keeps the status surface until its own completion event, ordinary Working resumes automatically when the same stream continues (including a successful compaction that had nothing to compact), and the attached workflow-stage chat paints its compaction status from the start event instead of waiting for an animation frame. Cancelled and failed compactions still stop all activity; the main chat keeps its existing cancellation receipt, while failures retain event-provided error text.
- Fixed attached workflow-stage chats missing Working during workflow-authored idle follow-ups, including while a saved stage session restores or a chat attaches mid-startup. Working persists through prompt startup and pre-turn compaction; live-turn queueing and factual status precedence remain intact, and late events cannot restart it after the final retained delivery settles.

## [0.9.11-alpha.6] - 2026-07-28

### Added

- Added execution-time session metadata to built-in, SDK/factory, direct, workflow-stage, and isolated bash execution through `ATOMIC_SESSION_ID`, `ATOMIC_SESSION_FILE`, `ATOMIC_PROVIDER`, `ATOMIC_MODEL`, and `ATOMIC_REASONING_LEVEL`, with exact `PI_*` compatibility aliases and safe omission of unsaved session files. Direct RPC bash now emits request-correlated, ordered `bash_execution_update` events that distinguish stdout from stderr, supports concurrent requests and per-request cancellation, and keeps streaming, cancellation, one terminal response, and exactly-once starting-session persistence intact across new/switch/import/fork/clone session replacement and RPC shutdown ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Added end-to-end constrained tool sampling to public extension/SDK tool definitions, wrappers, inspection, isolated transport, and model catalogs: strict JSON Schema supports `prefer`/`require`, OpenAI grammar tools support Lark/regex variants, omitted versus explicitly present optional properties remain distinguishable, and `supportsStrictMode`, `supportsStrictTools`, canonical `supportsOpenAIGrammarTools`, plus Atomic's `supportsGrammarTools` alias prevent false enforcement claims. Typed RPC model listings expose these flags through additive optional `ModelInfo.compat` ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Added Claude Opus 5 to Anthropic and Amazon Bedrock catalog paths with catalog-advertised adaptive thinking/`xhigh`, Bedrock inference profiles, prompt caching, strict-tool metadata, and preserved AWS validation errors. Custom message renderers now receive `outputPad` in normal and isolated-engine paths ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Added a compaction rung ladder so a compaction that must succeed always does. The range planner now returns typed outcomes (`ranked`, `recovered`, `rateLimited`, `unusable`, `overflowed`, `providerError`) instead of ranges-or-throw, and reasoning starvation is a distinct `starved` diagnostic category rather than an untyped empty plan. When a planner attempt ends terminally, Atomic borrows the next entry from `settings.fallbackModels` for one planner request, resolved with that candidate's own API key, headers, and base URL; borrowing is planner-only and never changes the session model, thinking level, model history, or main-chat fallback bookkeeping, and never calls `agent.continue()`. A planner request that itself overflows now withholds the oldest region lines and retries the same model until no strictly smaller view remains, with the withheld head recorded as a deterministic deletion and each trimmed view deriving its own keep target from `compression_ratio`. Silent overflows that arrive as an ordinary `stop` or `length` completion are classified before the response text is parsed, so valid-looking records cannot mask them. When every configured model is exhausted, a load-bearing compaction (overflow recovery, post-tool preflight) starts a fresh context window: a total, model-free rung that discards the compactable region and prior summary, keeps explicit protected spans, and keeps the `preserve_recent` tail unless retaining it would still exceed the provider hard input limit. That rung is loud (`✻ Context cleared (compaction degraded)`) and durable (`details.rung: "fresh"`), and a borrowed planner is recorded in `details.plannerModel` and on `compaction_end`. Manual `/compact` and threshold auto-compaction remain recoverable: they may borrow a model but can never clear context. Diagnostics gain `starved`, `rate_limited`, and `context_overflow` categories plus `rateLimitExhausted`, which separates spent retry backoff from quota/billing exhaustion, and a borrowed planned success writes a `compaction-success` sidecar naming the model that ranked the lines. Every sidecar filename now carries a per-attempt identifier and is created exclusively, so two attempts inside one millisecond keep separate durable evidence instead of overwriting each other. Generic usage-limit wording is classified as quota exhaustion rather than transient throttling, because pi-ai never schedules a backoff for it. Unavailable credentials advance the ladder instead of ending the compaction, so a fallback with its own credentials can still rank the lines and a load-bearing caller still reaches the fresh rung. A compactable region below the planner minimum reaches the fresh rung only when the context is already known not to fit — overflow recovery, or a post-tool preflight over the provider hard input limit — so one oversized tool result can no longer strand a mid-turn preflight while a threshold crossing that still fits stays a safe no-op instead of clearing context. The fallback walk is one monotonic pass over the configured list, so no position is inspected twice and the order cannot rewind. Quota exhaustion is now typed and recorded separately from transient throttling (`PlannerOutcome.rateLimited.category`, and a `quota` diagnostic category), independently of whether a retry was actually scheduled. The fresh rung keeps the `preserve_recent` tail unless the tail alone exceeds the hard input limit. `rateLimited.exhausted` and the `rateLimitExhausted` sidecar field are now derived from observed retry activity rather than assumed, and the whole HTTP `5xx` class is classified as rate limiting. A `fresh` boundary is no longer swallowed by an earlier `planned` boundary with identical text, because rung identity is part of boundary deduplication. **A configured fallback model may now receive the compaction transcript.**

### Breaking Changes

- Removed all Atomic-specific GitHub Copilot handling. `github-copilot` is now an ordinary upstream provider sourced entirely from `@earendil-works/pi-ai`'s `githubCopilotProvider()`, matching pi exactly. Dropped: the live CAPI `GET /models` catalog fetch and its 30-minute disk cache, the bundled static CAPI limit snapshot, dynamic Copilot model synthesis, `COPILOT_API_TARGET` / `GITHUB_COPILOT_BASE_URL` / `GITHUB_SERVER_URL` enterprise and GHE base-URL routing, the `X-GitHub-Api-Version: 2026-06-01` header, the Copilot Gemini tool-schema sanitizer, the Copilot Anthropic `message_stop` SSE repair, the Copilot Gemini reasoning-details interceptor, flattened Copilot Gemini tool-argument normalization, and Copilot prompt-limit error guidance. Model metadata now comes verbatim from the bundled pi catalog, and `COPILOT_GITHUB_TOKEN` is read by upstream's own env-key auth. Because pi's bundled context windows can exceed what Copilot's API enforces, long sessions on affected models may overrun the server-side cap instead of compacting first ([#1608](https://github.com/bastani-inc/atomic/issues/1608)).
- Removed the short/long context-window selection feature, which upstream pi does not have. Dropped: the `contextWindowOptions` / `defaultContextWindow` / `maxInputTokens` model fields and their `Model<Api>` augmentation, the `/model` context-window picker, the `--context-window` CLI flag, the `defaultContextWindow` and `defaultContextWindows` settings, the `set_context_window` and `get_available_context_windows` RPC commands, the `context_window_changed` event, the `context_window_change` session-journal entry, the `contextWindow` / `contextWindowStrict` SDK and workflow-stage options with their `contextWindowWarning` / `contextWindowError` results, `AgentSession.setContextWindow()` / `getAvailableContextWindows()` / `supportsContextWindowSelection()`, the `parseContextWindowValue` / `formatContextWindow` / `selectContextWindow` / `getSupportedContextWindows` / `getModelDefaultContextWindow` / `withContextWindowOptions` / `normalizeContextWindowOptions` / `validateContextWindowValue` root exports, and the `(1m)` / `(long)` context token in subagent and workflow model strings. A model's `contextWindow` is now a single value taken from its catalog entry.
- Changed the published `runVerbatimCompaction` SDK export to a request object. Positional `apiKey`, `headers`, `signal`, `thinkingLevel`, and `options` collapse into `runVerbatimCompaction(preparation, model, { resolveAuth, signal, thinkingLevel, urgency, ...options })`. Pre-resolved credentials become a `resolveAuth(model)` callback because a borrowed fallback model needs its own credentials, and `urgency` is required with no default so only a `load_bearing` caller can reach the context-destroying fresh rung. Migrate with `await runVerbatimCompaction(prep, model, { resolveAuth: options.resolvePlannerAuth, signal, urgency: "recoverable", ...options })`. The internal `planDeletedLineRanges` signature and return type also changed; it is not exported from the package root. Compaction settings, `reserveTokens` semantics and defaults, the auto-compaction threshold, `settings.retry` behavior, the persisted `CompactionEntry` shape, resume, and the `session_before_compact`/`session_compact` hooks are all preserved; `details.rung` widens additively to a third value and `details` gains one optional field.

### Changed

- Reworked interactive startup and ordinary working motion around Atomic's revision-3 identity system: the ∀ banner assembles inward in whole-column steps before its identity and manifesto beat land; main and workflow-stage work surfaces keep the exact one-cell `∀` visible while following a pronounced, theme-aware dark → accent → bright/bold → accent → dark luminance ramp at an 88ms cadence and retaining all 453 original randomized whimsical working verbs. Optional partial theme palettes can specify any phase exactly—including the approved Catppuccin Mocha prototype and terminal indices 0–255—while omitted tones derive from semantic selected-surface, accent, and text roles. `NO_COLOR` retains regular/bold activity without foreground-color escapes; reduced motion keeps a static regular accent `∀`; extension indicator frame and interval values remain verbatim while their phase/cadence restarts each turn; and factual status/prompt/receipt surfaces keep precedence. The inline icon and longest message fit tested standard/64-column layouts ([#1883](https://github.com/bastani-inc/atomic/issues/1883)).
- Removed the fixed 1 MiB size limit from RPC and isolated interactive-engine JSONL transport. Commands, responses, events, queued writes, and render frames now pass through in full instead of being rejected, dropped, or truncated; clients remain responsible for the memory and latency cost of large records ([#1985](https://github.com/bastani-inc/atomic/issues/1985)).
- Added OpenRouter PKCE and Kimi Code device subscription login to Atomic's provider-metadata-driven `/login` flow, including direct provider routing, isolated engine-owned extension OAuth discovery/acquisition, correlated UI callbacks and cancellation, transactional persistence/rollback, automatic Kimi refresh, logout, and immediate post-login model refresh without transporting credentials or executable provider functions to the frontend ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Updated all active Pi runtime declarations and resolved artifacts to 0.82.1. Dynamic pi.dev catalogs now revalidate with ETags and retain cached data on empty `304` responses; newer bundled catalogs beat stale overlays; llama.cpp catalogs persist across restarts, remain selectable while a failed first online refresh reports its original error, and use loaded context as their output limit; and `/model` reloads layered `.pi` then `.atomic` model configuration on every open ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Updated the bundled user documentation for the current subagent, workflow-stage, reviewer, and `prompt-engineer` prompt contracts: outcome-first agent and stage structure, evidence-grounded reporting, query-last artifact ordering, selective delegation, deterministic `stop_review_loop` derivation, explicit output/stop rules, and the removal of response prefilling and private-reasoning requests for current GPT-5.6, Claude Opus 5, and Claude Fable 5 compatibility.

### Fixed

- Fixed a mid-turn compaction leaving the turn unsendable, which the provider rejected with an unrecoverable `400 ... each tool_use must have a single result. Found multiple tool_result blocks with id ...`. The post-tool compaction preflight returned the agent's live internal message array, so the agent loop adopted it as the working context for the rest of the run; from the next turn on, both the loop and the message reducer appended to that one array and duplicated every assistant message and tool result. The preflight now returns its own copy. As defence in depth, provider conversion additionally keeps only the first tool result for each announced `tool_use` id, alongside the existing orphan-result repair, and a structural guard runs immediately before conversion and fails with a descriptive Atomic-side error naming any `tool_use` id that appears in more than one assistant message rather than letting the request 400 ([#2051](https://github.com/bastani-inc/atomic/issues/2051)).
- Fixed compaction repairs from the rung-ladder review: manual `/compact` now projects only compaction parameters, so no runtime property on a caller-supplied object can raise its urgency to `load_bearing` and reach the context-destroying rung; a committed `fresh` boundary stays visible in main and attached workflow stage chat even when the following provider hard-input-limit gate reports its own error, because `compaction_end` carries `result` and `errorMessage` independently; and a post-tool threshold crossing whose context still fits ends as a successful no-op rather than raising "no compactable transcript entries were available" and killing the active turn.
- Fixed compaction rebuilding the retained recent messages as structured message blocks after the boundary. The `preserve_recent` tail is now serialized with the same transcript grammar as the compacted region and concatenated onto the end of the boundary string, so a compacted context reaches the provider as one message. A kept tail that started or ended mid-turn — an unpaired tool result, or a trailing tool call whose result was compacted away — can no longer produce out-of-order provider blocks and a rejected request. Tail serialization is lossless: retained tool results keep their full text rather than being truncated at 16k characters, and retained images stay as image blocks on the boundary message instead of collapsing to an `[image]` marker. Applies to manual, automatic, mid-turn post-tool, and extension-supplied compaction, and to sessions resumed from existing boundaries.
- Fixed shared, main, and workflow-stage Atomic working animation lifecycle safety. Every agent/turn starts from phase zero on a fresh cadence, restoring the ordinary indicator after an extension override resets its phase/cadence, and turn, interrupt/pause, terminal, error, replacement, and disposal cleanup fence both built-in and delegated timer callbacks without leaks or stale repaint. Reduced motion remains a static regular accent `∀` without a timer ([#1883](https://github.com/bastani-inc/atomic/issues/1883)).
- Fixed Windows and limited-color branding fidelity: caller-supplied workflow palettes now honor the detected terminal color mode, partial ANSI-index palettes derive omitted tones from indices 0–15, and Catppuccin Mocha starts its pulse at readable truecolor and 256-color contrast. Startup now honors `NO_COLOR` across the mark, metadata, and manifesto, remains nonblank below the mark width, and settles through the real input chain before Ctrl+C is consumed. Bundled themes declare and validate against Atomic's rebranded local schema, standalone binaries link to Atomic releases, and reachable startup/crash diagnostics use Atomic branding while legacy `.pi` paths, `PI_*` aliases, migrations, and service integrations remain compatible ([#1883](https://github.com/bastani-inc/atomic/issues/1883)).
- Fixed inherited `.pi` extensions conflicting with Atomic-bundled resources: exact-name tool, command, prompt, flag, and shortcut overlaps now keep Atomic's bundled registration without disabling unrelated extension features, interactive startup consolidates overlaps into one warning, and isolated TUI discovery again matches print/RPC while preserving explicit `.atomic` overrides and agent-directory isolation ([#1955](https://github.com/bastani-inc/atomic/issues/1955)).
- Fixed fast named workflows leaving a stale “still running” parent-chat claim when a terminal lifecycle notice arrives before the launch result or during final text. The opt-in lifecycle path now commits one context-excluded display card before send admission, preserves the assistant call → `status=running` result order, and delivers a separate hidden reconciliation at a native safe boundary; queue clearing, ordinary aborts, stage-session transfer, persistence retries, and fail-closed session teardown retain that reconciliation without interrupting unrelated active text, invalidating a session too early, or duplicating the lifecycle card.
- Fixed the bundled workflow UI and control surfaces after fresh-process nested resume: input-bound child boundaries now retain stable graph identity, ordering, edges, lifecycle state, and true child-owner targets even when parallel dispatch order reverses; malformed or aliased topology and non-atomic boundary histories fail closed before routing. Completed inspection stays available without a retained chat, retained child follow-up remains detached from execution, and colliding child-local ids or names are reported as ambiguous instead of routing to the first stage.
- Fixed queued steering, follow-up, trigger-turn custom/batched/interrupt messages, async deliveries, and user prompts escaping an interactive or workflow pause. Escape/Ctrl+C and controlled workflow pause now synchronously hold every raw queued item before abort, preserve content, optional data, duplicates, message type, display state, and per-queue order across fallback session transfer, gate late arrivals and continuation, serialize pause/resume with abort settlement, and release exactly once through the existing explicit resume boundary. `AgentSession` now publicly exposes `queuedMessagesPaused`, `pauseQueuedMessages()`, and `resumeQueuedMessages(): Promise<boolean>`; resume reports whether raw held work was released but never starts a turn. Clearing or restoring the final visible item leaves the pause gate closed, rejected abort boundaries block one release attempt before an explicit retry, and transferred interrupt turns serialize on the live owner. Paused protected reconciliation is persisted directly during shutdown without a model turn, while non-paused protocol-sensitive disposal remains fail-closed. Idle Ctrl+C clear/double-press exit and explicit final dequeue remain reachable without silently releasing held work; completed post-mortem stage chat releases its conversation-only hold on the next ordinary submission without changing terminal workflow state ([#1983](https://github.com/bastani-inc/atomic/issues/1983)).
- Fixed tool-only workflows being reported failed after durable `ctx.tool()` side effects had completed. Root finalization now waits for admitted tools, promotes failures left by a fulfilled body with an explicit selected tool-node origin, and preserves ordinary body rejections without guessing a tool origin from indistinguishable native-promise values; the workflow graph and completed-run inspector show non-attachable tool nodes including cached replay state, and reopening completed history remains lifecycle-silent while preserving live declared outputs and preventing duplicate side effects. Retained `ctx.tool` calls made after any terminal executor path now reject safely before side effects, transient legacy child-topology migration failures no longer fail cached replay or later DBOS flush, nested child tools appear in no-target status with owning-run metadata, and completed retained-session stage chat is again reachable from graph keyboard, mouse, switcher, and hint surfaces without making tools attachable ([#1991](https://github.com/bastani-inc/atomic/issues/1991)).
- Fixed reusable extension chat hosts to show and animate Atomic's Working indicator immediately when an idle prompt is submitted, preserving it across prompt attachment and agent startup while still stopping cleanly when no agent turn begins. This keeps retained terminal workflow-stage chats visibly active throughout follow-up processing ([#1982](https://github.com/bastani-inc/atomic/issues/1982)).
- Fixed messages queued before automatic compaction remaining stuck afterward until Escape was pressed; successful compaction now resumes queued input automatically once active work settles ([#1984](https://github.com/bastani-inc/atomic/issues/1984)).
- Fixed a workflow lifecycle card surviving a crash or a failed prompt start while its hidden reconciliation turn was lost. The durable card now carries its recovery marker in the same atomic session append, a rejected prompt start requeues only the hidden turn, and the next session start replays exactly one unresolved correction without duplicating the visible card. Physical session appends also roll back a partial single-entry or initial-batch write to the exact prior file length, so a later retry cannot inherit a malformed JSONL tail, duplicate id, or phantom parent ([#2021](https://github.com/bastani-inc/atomic/issues/2021)).
- Fixed compaction status surfaces using the generic braille spinner instead of Atomic's `∀` identity. Main-session auto/manual compaction, summarization retry attempts, and workflow-stage compaction now pulse the one-cell `∀` on the shared 88ms luminance ramp, and the workflow-stage message carries the same `(esc Cancel)` hint as the main session.
- Fixed Anthropic `ANTHROPIC_AUTH_TOKEN` header-only bearer authentication across catalog availability, direct/default/restored model selection, cycling, RPC/isolated model listings, ordinary requests, custom headers, branch summaries, and Verbatim Compaction without synthesizing an API key or `x-api-key`; also inherited Radius gateway OAuth routing and underlying model-auth cause details ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Fixed intentional OAuth cancellation surfacing as login failure. Escape/Ctrl+C, native `AbortError`, exact signal reasons, nested abort causes, and the legacy exact cancellation error now restore the interactive editor quietly for built-in and extension OAuth in direct and isolated sessions, while denial, timeout, network/protocol, token exchange, persistence, and refresh failures remain visible ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Fixed the remaining applicable Pi 0.82.1 coding-agent parity issues: complete literal model IDs win before thinking-suffix or glob parsing, unavailable scoped models remain removable, TUI logs honor the configured Atomic/Pi agent directory, the shared async Ctrl+G/extension editor uses private exact-file storage without temp scans and honors configured/environment/platform editor precedence, sibling package resources display relative paths, explicit self-update bypasses startup skip flags, summaries use fresh routing IDs without prompt-cache writes, Wayland copy falls back after `wl-copy` failure, and context discovery skips directories named like context files ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Fixed the compiled Atomic package CLI crashing before TUI startup when invoked from another working directory after standalone assets had placed `package.json` in `dist/`. Built-in themes, interactive images, and HTML-export templates now resolve from the actual `src/` or `dist/` module layout without producing a duplicated `dist/dist` path, while package-directory overrides and standalone adjacent assets remain supported ([#1995](https://github.com/bastani-inc/atomic/issues/1995)).
- Fixed the external editor failing on Windows whenever the editor path, its arguments, or the temporary prompt path contained a space. Node does not quote arguments when spawning through a shell, so `cmd.exe` split values such as `C:\Program Files\Microsoft VS Code\Code.exe` into separate arguments; each token is now quoted with the Windows command-line rules before launch.
- Fixed repeated `/compact` presses starting several overlapping compactions and the resumed-session prompt showing no progress before the compaction spinner appeared. `AgentSession.compact()` is now single-flight: a second manual request joins the run already in flight and receives its result instead of starting another model call, emitting a duplicate `compaction_start`/`compaction_end` pair, or appending a second compaction boundary; a manual request made while an automatic compaction is running fails fast with a clear message rather than racing it; and only the owning call clears the abort controller and reconnects agent events, so `esc` still cancels the live run for every waiter. In the interactive TUI, `/compact` typed while a compaction is already running now warns instead of starting or queueing another one, a repeated `compaction_start` releases the previous spinner and keeps the original Escape handler, and the working indicator stays visible continuously from prompt submit through deferred extension startup and prompt preflight until the agent turn or compaction indicator takes over.
- Fixed interactive startup output ordering so the `RESOURCES` disclosure remains above extension notifications and every other startup warning or notice on eager, deferred, and immediate-prompt paths.
- Fixed reasoning tokens being able to fail a compaction outright. The planner used to cap its own request at `0.8 × reserveTokens` — an input-side reserve reused as an output cap — so a high-reasoning session could spend the entire budget thinking and return no deletion records, producing `Compaction range planning produced no usable deleted ranges`; during a mid-turn post-tool preflight that killed the active turn. The planner now sends no `max_tokens` at all, leaving the provider's own context clamp as the only bound, and `PlannerBudget.maxTokens` is typed `undefined` so the cap cannot be reintroduced without changing the type. The session reasoning level is still inherited and is never modified between attempts. A starved or rate-limited planner response no longer ends compaction: it advances the rung ladder to a configured fallback model and, for a load-bearing compaction, to a fresh context window. Branch summarization received the same budget fix — its `2048` output cap is removed and it now inherits the session thinking level, while its input-side `reserveTokens` behavior is unchanged. Fallback borrowing is now a single ordered pass: a candidate whose credentials are unavailable is consumed rather than re-inspected, so no candidate has its auth resolved twice and the configured order cannot rewind. `startNewContextWindow` returns exactly a `CompactedTranscript`; the tail decision it used to expose is runner bookkeeping and travels on `CompactionRungResult.keptTail`. Zero-usable-ranges output is still rejected: no unvalidated model output ever becomes a compaction boundary, and every retained line stays byte-identical on every rung.

## [0.9.11-alpha.5] - 2026-07-23

### Changed

- Added Orwell's six writing rules from “Politics and the English Language” to the core default system prompt's `Guidelines` section so every standard Atomic session receives them, while removing the bundled workflow guidance's requirement to announce an inline-or-workflow mode before the first tool call.

## [0.9.11-alpha.4] - 2026-07-23

### Changed

- Installed builds now ship the builtin workflows extension as a single prebundled ESM file. Loading it previously resolved and transpiled a ~300-file TypeScript module graph per launch, which dominated interactive-engine startup on Windows (~7 s on a test VM, now ~1.1 s, ~6x faster) ([#1962](https://github.com/bastani-inc/atomic/issues/1962)). Source checkouts still load the raw TypeScript sources.
- Interactive TUI startup on Windows is dramatically faster: the CLI now enables Node's persistent on-disk V8 compile cache (Node >= 22.8) and flushes it before spawning the isolated interactive engine, so both the host and the engine child reuse compiled module bytecode across runs and within the same launch ([#1962](https://github.com/bastani-inc/atomic/issues/1962)).

### Fixed

- Fixed interactive startup crashing with `Interactive engine did not become ready within 5000 ms` on machines with slow cold starts (commonly Windows PowerShell with npm-global installs). The fixed readiness deadline is removed; the host now waits until the engine reports ready and still fails fast if the engine process exits or the transport breaks ([#1962](https://github.com/bastani-inc/atomic/issues/1962)).
- Fixed positional prompts beginning with `-`, `--`, or `@` being parsed as options or file arguments by supporting the conventional `--` end-of-options terminator ([#1950](https://github.com/bastani-inc/atomic/issues/1950)).
- Fixed embedded extension UIs in isolated interactive mode receiving an empty placeholder from `ctx.ui.getFooterDataProvider()`. The engine session now exposes its live extension statuses and cached, watched Git branch so synchronous renderers such as workflow stage chat can match the main footer without RPC calls or per-render Git processes.

## [0.9.11-alpha.3] - 2026-07-21

### Added

- Added a built-in, hidden llama.cpp router provider with `/login llama.cpp`, `LLAMA_BASE_URL`/`LLAMA_API_KEY` configuration, loaded-model discovery, and the `/llama` manager for load, unload, cancellation, reconnect, and Hugging Face GGUF download workflows.
- Added provider-metadata-driven `/login` choices, direct `/login <provider>` routing and autocomplete, provider-owned login information links, API-key login for Qwen Token Plan, Radius, and GitHub Copilot, plus preferred defaults for Radius, NVIDIA, and ZAI Coding Plan China.
- Added global `httpProxy`, duration-string and `"disabled"` HTTP idle timeouts, package `autoload: false` project deltas, Kimi deferred-tool compatibility, `oauth: "radius"` custom gateways, and global/project scope switching in `atomic config` (`-l` starts in project scope).
- Added extension lifecycle hooks `before_provider_headers` and `agent_settled`, durable `entry_appended` events and `registerEntryRenderer()`, complete native `Provider` registration, and named/hidden `InlineExtension` descriptors.
- Added the public `ModelRuntime` facade and associated option types, `readStoredCredential`, `InlineExtension`, entry-renderer/event types, and session context conversion helpers to the SDK/package-root surface. Atomic intentionally continues to expose Search rather than adding obsolete grep compatibility aliases.
- Added Ctrl+X copy-last-message, clipboard text fallback when image paste finds no image, restored footer branch/session/experimental/extension-status segments, Output padding and Cache miss notices settings, and clear-on-shrink idle placeholders.
- Added durable cache-miss attribution and waste totals, complete per-model session usage/cost breakdowns including tool and summary work, and optional live/resumed cache-miss transcript notices.
- Added Atomic's first-run theme preview and explicit analytics opt-in. Analytics settings and a local tracking identifier are persisted only when selected; this change does not transmit analytics data.

### Changed

- Login provider discovery now follows registered provider authentication capabilities instead of using display names as an allowlist, so newly installed native providers automatically expose their supported OAuth and API-key methods.
- Dynamic provider catalogs, including Radius `pi-messages`, are restored and refreshed through the provider-owned model store; documentation now distinguishes generic custom-provider APIs from the broader installed native API surface.
- Package self-update now honors advisory release notes, pins the exact advertised package and version, reports version transitions, and cleans stale Windows native-dependency quarantine data during ordinary startup.
- Interactive session diagnostics now show cache hit rate, wasted cache cost, and provider/model usage costs; the footer includes usage spent by tool results and summaries.

### Fixed

- Fixed Qwen Token Plan global/China, Radius API-key, and GitHub Copilot token methods being hidden from first-time `/login`, which kept otherwise installed models out of `/model` and `--list-models`.
- Fixed package-manager self-update helpers rejecting the legacy string target form while retaining exact-version install targets.
- Fixed `/login` omitting extension-registered custom-auth providers in isolated interactive mode. Provider metadata now synchronizes from the engine child, and provider-owned login prompts, credential persistence, cancellation, and post-login model refresh execute across the existing isolated-engine bridge.
- Fixed project package overrides being unable to selectively subtract or include resources from a matching global package without replacing the complete declaration.
- Fixed Ctrl+V reporting an image error when the clipboard contained usable text, and made Ctrl+X invoke the same copy-last-assistant behavior as `/copy`.
- Fixed a single Ctrl+C not dismissing the `/login` popups (auth-method selector, provider selector, and login dialogs), even though Escape worked and the on-screen hint promised `esc/ctrl+c cancel`. The global Ctrl+C clear/exit handler consumed the key before the focused popup could see it, so the first press silently cleared the hidden editor and a second press within the double-press window exited the entire CLI. The global handler now defers whenever an inline popup owns input (and its overlay/custom-UI guards are evaluated live instead of once at startup), so Ctrl+C reaches the focused component and cancels exactly like Escape while editor clearing and double-press exit continue to work when the editor has focus.

## [0.9.11-alpha.2] - 2026-07-21

### Added

- Added Qwen Token Plan and Qwen Token Plan China to Atomic's built-in provider defaults and credential guidance, inherited from `pi-ai` 0.81.1 ([#6858](https://github.com/earendil-works/pi/pull/6858)).
- Added the `get_available_thinking_levels` RPC command and `RpcClient.getAvailableThinkingLevels()` so headless clients can discover the active model's exact supported cycle order ([#6865](https://github.com/earendil-works/pi/pull/6865)).
- Exported message and tool-execution lifecycle event types from the Atomic package root for extension and SDK consumers ([#6772](https://github.com/earendil-works/pi/pull/6772)).

### Changed

- Updated `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` to 0.81.1, inheriting provider, transport, model-catalog, Kimi K3 thinking, OpenAI Responses retry, Codex request-ID, terminal rendering, and queue fixes from Pi 0.81.0–0.81.1 while retaining Atomic's branding, bundled packages, Verbatim Compaction, and versionless release flow.
- Restored persisted model catalogs without network access during service creation, then moved automatic network refresh into running interactive and RPC modes. Interactive startup computes its first footer provider count from the restored snapshot and starts network refresh only after TUI initialization.

### Fixed

- Fixed transient provider failures aborting Verbatim Compaction planning or branch summarization. Both now honor the configured retry policy and publish `summarization_retry_scheduled`, `summarization_retry_attempt_start`, and `summarization_retry_finished` lifecycle events to interactive, JSON, RPC, and SDK consumers ([#6901](https://github.com/earendil-works/pi/pull/6901)).
- Fixed persisted remote model catalogs overriding newer bundled catalogs after an upgrade by comparing catalog modification times and treating legacy undated overlays as stale ([upstream commit](https://github.com/earendil-works/pi/commit/54fad505b9d8cbc8922ff55d7e2938f70cbf6a3d)).
- Fixed steering and follow-up delivery modes being lost when queued messages were flushed as compaction finished ([#6730](https://github.com/earendil-works/pi/pull/6730)).
- Fixed read-tool errors being syntax-highlighted as though they were successful file contents ([#6731](https://github.com/earendil-works/pi/pull/6731)).
- Fixed `${@:-default}` and `${ARGUMENTS:-default}` prompt-template defaults, while retaining Atomic's bounded default-value parser ([#6695](https://github.com/earendil-works/pi/issues/6695)).
- Fixed persisted sessions being read and parsed twice when opened, and adopted the shared UUIDv7 generator for new session IDs ([#6793](https://github.com/earendil-works/pi/pull/6793), [#6834](https://github.com/earendil-works/pi/pull/6834)).
- Fixed Kimi Coding usage costs being shown without the subscription estimate indicator.

## [0.9.11-alpha.1] - 2026-07-20

### Changed

- Clarified the bundled `deep-research-codebase` discovery guidance so agents reserve the heavy workflow for tasks requiring comprehensive whole-repository context ([#1925](https://github.com/bastani-inc/atomic/issues/1925)).

### Fixed

- Fixed newly authenticated providers remaining unavailable in the model selector until Atomic restarted. API-key and OAuth login now reload the isolated engine's credentials and refresh its authoritative model catalog immediately, including credential-dependent dynamic discovery ([#1918](https://github.com/bastani-inc/atomic/issues/1918)).
- Added actionable `/logout` and `/login` recovery guidance when OpenAI Codex invalidates or revokes a locally unexpired OAuth token, and prevented an earlier WebSocket-to-SSE transport diagnostic from making the definitive authentication rejection retryable ([#1922](https://github.com/bastani-inc/atomic/issues/1922)).


## [0.9.10] - 2026-07-20

### Breaking Changes

- The bundled `workflow` tool no longer accepts direct one-off `task`, `tasks`, or `chain` shapes (or their direct-only top-level options), and the bundled workflow SDK no longer exports `runTask`, `runParallel`, or `runChain`. Launch named workflows with `workflow` + `inputs`, and author custom definitions with the unchanged `ctx.task`, `ctx.chain`, and `ctx.parallel` primitives.
- Bundled workflow durability now requires DBOS/Postgres and no longer provides local JSON/SQLite persistence, runtime opt-out/backend selection, session-transcript discovery, or conversion of prior durable data.
- Added a host-native session picker capability, `ctx.ui.hostSessionPicker(request)`, implemented first-class by every interactive host with one identical API: non-isolated interactive mode mounts the real built-in session selector directly in the terminal process (no IPC), and the isolated interactive engine routes the same capability over a new engine session-picker protocol channel (`engine_session_picker_open/update/error/close` messages and `engine_session_picker_select/cancel/delete` commands). The extension ships JSON-safe rows (`HostSessionPickerRow` — `SessionInfo` with `createdAt`/`modifiedAt` epoch millis) and only semantic events ever cross a process boundary; arrow-key navigation and search are zero-IPC and stay responsive even while the extension's event loop is busy, unlike remote-rendered `ctx.ui.custom()` components, which pay one round trip per keypress. Deletion is extension-owned: the host keeps the row until the extension replies with an update (row removed) or an error. Pickers are disposed cleanly on extension-initiated close and on engine crash/restart. The bundled `/workflow resume` picker now REQUIRES this capability — its remote-rendered `ctx.ui.custom()` selector path was removed with no fallback, and hosts without the capability fail the resume command with one actionable error. The member is absent only on non-interactive surfaces (headless RPC, print). See `docs/tui.md` and `docs/extensions.md`.

### Added

- Added private compaction-planner diagnostic sidecars for failed persisted-session planner calls. Malformed output, unusable ranges, provider errors, and stream failures now save the full response text plus stop reason, usage, request output budget, and non-secret model metadata beside the session JSONL with `0600` permissions where supported; the compaction error reports that path. Credentials, headers, and request prompts are excluded, while in-memory sessions and sidecar write failures preserve the original `RangePlanError` unchanged.
- Replaced the compaction planner output format from JSON `{"d":[[start,end],...]}` with bare `start,end` records (one per line, no brackets/fences/prose). The new format is ~1% more token-efficient at equivalent range counts and enables trivial newline-based length-truncation recovery.
- Added silent recovery of complete deletion records from length-truncated compaction planner responses. When the model output is cut by `max_tokens` (`stopReason: "length"`), a deterministic line parser recovers newline-terminated `start,end` records, discards the final unterminated fragment (never guesses multi-digit integers), and passes results through normal validation. Recovery is silent: no warning, banner, or special status—UI shows the normal `✻ Context compacted` message. A private `0600` recovery-diagnostic sidecar is written beside persisted sessions for operational observability (never surfaced in UI). The planner prompt now instructs the model to emit ranges in descending deletion confidence so the highest-priority deletions survive truncation.
- Added six bundled, composable pattern workflow definitions — `classify-and-act`, `fan-out-and-synthesize`, `adversarial-verification`, `generate-and-filter`, `tournament`, and `loop-until-done` — with typed/defaulted inputs, dedicated artifact-aware stage prompts, declared outputs, named discovery, and `@bastani/workflows/builtin` exports for nested `ctx.workflow(...)` composition.
- Added `ctx.ui.hostInputForm(request)`, a JSON-safe host-native structured-form capability implemented identically by isolated and non-isolated interactive hosts. The terminal host owns inline rendering, focus, validation, configured keybindings, and editing state; under engine isolation only open and submit/cancel semantic events cross the protocol boundary. Headless RPC and print surfaces omit the optional member.
- Added `/workflows [run-id]` as a retained workflow-run alias for `/workflow resume`, with confirmed backend-aware deletion for inactive durable/completed rows that protects in-flight runs and preserves independent session transcripts.
- Added supervised interactive-engine isolation: extension tools, hooks, commands, workflow code, and custom UI rendering now run in a child process so a synchronous busy loop cannot freeze terminal input, spinners, or rendering. A 50 ms heartbeat watchdog identifies blocked callbacks after 250 ms, exposes interrupt/termination guidance after one second, and reports terminated results as unknown without automatic retry. JSON-safe `ctx.ui.custom()` components are remotely rendered with asynchronous input forwarding; unsupported synchronous host callback APIs warn instead of falling back in-process.
- Added a typed, allowlisted host-terminal control channel for isolated `ctx.ui.custom()` components. An overlay factory's `tui.terminal` now exposes `setMouseScrollTracking(enabled)` and `setAutowrap(enabled)` setters that the terminal host applies to the real host TTY over the engine protocol — the engine child (whose stdout is the JSONL transport, not a TTY) can enable SGR mouse-scroll reporting and Windows autowrap without ever forwarding raw child bytes to the terminal. Controls are component/generation scoped and every enabled mode is reset when the overlay hides, closes, is disposed, or the engine child crashes/restarts. See `docs/tui.md`.
- Added live-updating, semantically colored rows to the bundled `/workflow resume` picker: completed rows render green, paused yellow, and failed/blocked/crashed red (`SessionInfo.messageColor` now also accepts `"error"`). Rows re-list on run-store changes and a bounded cross-session poll while the picker is open, and running workflows are never offered as resume targets — a fresh-heartbeat running row is hidden from every session, and only stale (crashed) ones surface, labeled `crashed`.
- Added `atomic update --models` to force-refresh authenticated dynamic provider catalogs with bounded, provider-scoped persistence and actionable failures ([#1875](https://github.com/bastani-inc/atomic/issues/1875)).

### Changed

- Promoted the DBOS SDK and `embedded-postgres` to mandatory dependencies. Atomic configures and launches DBOS lazily on the first workflow action, awaits readiness before every workflow lifecycle path, and surfaces initialization or persistence failures for that action instead of continuing on another backend. Without `DBOS_SYSTEM_DATABASE_URL`, workflow durability runs on an embedded Postgres from npm-distributed binaries (detached daemon under `~/.atomic/postgres`, shared across sessions; Docker `dbos-db` container only as a platform fallback). Concurrent Atomic sessions sharing one database use per-process executor identities, owner/heartbeat metadata, and first-writer-wins resume claims so one session's live workflow cannot be double-dispatched from another.
- Documented and enforced `/resume`-equivalent workflow-history retention: eligible runs are not filtered or garbage-collected by age/count, and the selector viewport does not limit search/navigation.
- Kept the interactive-engine watchdog's early 250 ms blocking signal internal instead of rendering repetitive "has not yielded" warnings during ordinary slow extension/tool loading. One-second unresponsive heartbeat-watchdog diagnostics are likewise always kept internal, whether or not they attribute a callback (for example, `extension.hook tool_execution_end`); the isolated host remains responsive and still provides Escape/Ctrl+C recovery controls. Concrete engine failures such as termination and RPC errors continue to surface as chat errors.
- Changed verbatim compaction to classify the complete active transcript except for exactly the newest `preserve_recent` context-visible messages (default `2`). The protected tail is no longer widened to a user-turn boundary, `preserve_recent: 0` now retains no ordinary message, and repeated compaction includes the prior durable verbatim summary while preserving resumable session boundaries.
- Redesigned the `/workflow <name>` input form (shown when a named workflow is launched with required inputs missing). The form now renders inside a rounded, theme-aware panel titled `WORKFLOW INPUTS` with the workflow name and field count, matching the workflow catalogue's visual language: each field shows a focus chevron, an accent field name when active, and a right-aligned `required`/`optional` badge (required in the warning color); descriptions use the readable muted tone, validation errors render as a `✗ reason` row, the submit control is a `[ Run workflow ]` button pill (filled accent when focused), and the footer uses accent key chips. Multi-line text inputs blend their inset rules into the panel border, the panel scales to the full terminal width like the other chat surfaces, and the embedded editor cursor stays correctly positioned.
- Migrated model authentication and refresh to Pi's provider-owned APIs while preserving Atomic's public `AuthStorage`, `ModelRegistry`, SDK options, legacy extension OAuth registration, complete request headers, and credential-specific enterprise endpoints. The model picker now shows cached models immediately and reports concise success, partial-error, and timeout status ([#1875](https://github.com/bastani-inc/atomic/issues/1875)).

### Fixed

- Fixed `/logout` leaving GitHub Copilot and other providers authenticated in the isolated interactive engine until restart. Logout now invalidates the authoritative child immediately, refreshes the host model projection, prevents provider transport with the removed credential, and deletes effective legacy auth-file entries so credentials do not reappear after restart ([#1919](https://github.com/bastani-inc/atomic/issues/1919)).
- Fixed custom project, user, and package-provided workflow names being omitted from `/workflow <name>` and `/workflow inputs <name>` argument completion under the isolated interactive engine. The host now evaluates completion callbacks in the engine child against the live workflow registry, so `/workflow reload` updates the completion popup immediately while retaining bundled administrative-command fallback completion ([#1921](https://github.com/bastani-inc/atomic/issues/1921)).

- Fixed the repository `publish-release` workflow to reconcile an exact release PR merged externally while required checks are pending. It preserves identity/refs/SHA; correlates workflow-qualified Actions reruns by name plus workflow; and supports empty-workflow `StatusContext` and GitHub App `CheckRun` evidence. Linked reruns group by inferred kind/name across URL changes; linkless rows inspect both external kinds, accept all-passing candidates, block any pending/failure, and exclude nonempty-workflow Actions. Duplicate aliases reuse exact passing evidence. It rechecks after merge and validates merge/branch evidence. Tag recovery proves `verified merge → tag parent → current base`; exhaustive history avoids GitHub's 1,000-result ceiling; protected coordination retains its lock through ambiguous dispatch visibility; and recovered success requires exact-SHA integrity evidence.
- Fixed shared extension chat compaction rendering so manual, threshold, and overflow compaction use the animated working spinner with reason-aware copy instead of a duplicate plain status row plus generic `Working...`; successful compaction now falls back to the existing typed `✻ Context compacted` message when a refreshed live session snapshot is unavailable, while preserving durable session reconstruction and avoiding duplicate boundaries.
- Fixed workflow stage-chat `/compact` cancellation and planner/provider failures from escaping their fire-and-forget editor submission promise and terminating the CLI. The authoritative `compaction_end` event now owns the visible status, animation cleanup, and diagnostic path while the same stage remains usable for retry or follow-up.
- Fixed OpenAI Responses, Codex Responses, and OpenAI Completions context accounting to sum their normalized uncached and cached input partitions even when the values are nearly equal. Anthropic Messages retains its mirrored-cache guard, preventing missed auto-compaction thresholds, understated footer usage, and negative persisted reduction percentages on Codex sessions.
- Fixed workflow-stage message admission races by adding a linearizable AgentSession generation boundary. Intercom traffic and async bash/subagent completions received before close now use native queued steering/follow-up delivery and drain before terminal stage publication; close-winning detached results route once to the main chat without reopening or mutating the completed stage. Detached producers remain non-blocking, stable producer identities prevent duplicate delivery, and explicit `StageContext.sendUserMessage` plus post-mortem stage chat remain available.
- Fixed asynchronous extension custom-message admission to return its delivery promise through `pi.sendMessage()` and `pi.sendMessages()`, allowing workflow late-message routers to propagate main-chat failures and release stable producer keys for retry instead of treating rejected routes as successful.
- Fixed bundled workflow stages freezing when an active foreground subagent used Intercom or blocking `contact_supervisor`. Busy stages give the exact child owner's acknowledged detach handshake first refusal before model-visible stage-session queue insertion; owner loss between probe/commit falls back instead of dropping the broker message. In parallel foreground groups, one accepted commit releases aggregate supervision for all active siblings while their child processes and eventual result/status recovery remain owned; run-owned worktrees are retained until every detached child closes and their diffs are captured.
- Fixed bundled workflow-stage `intercom.ask` delivery when the target sibling is mid-turn in an in-flight tool call. The open generation now owns the ask before asynchronous foreground-owner coordination begins, waits for its native queue insertion, and drains it before terminal publication; genuinely failed destination admission returns a correlated actionable error rather than consuming the full reply timeout.
- Fixed `/workflow <name>` input forms becoming non-responsive under interactive-engine isolation. The RPC extension surface advertised `setEditorComponent` as a callable compatibility stub even though it could not install a host editor, so the workflow command waited on an editor that was never mounted while Tab and Escape stayed routed to the ordinary prompt. Named workflow forms now prefer the host-native input-form channel, keeping Tab/Shift+Tab, arrows, editing, configured keybindings, Enter, Escape, and Ctrl+C local and responsive in both interactive modes; cancellation does not dispatch the workflow, inline bottom-editor geometry is preserved, and legacy custom-editor/`ctx.ui.custom()` fallbacks remain available for older hosts.
- Fixed Ctrl+O (`app.tools.expand`) being consumed without expanding tool and workflow-node detail in attached workflow stage chats under interactive-engine isolation. The child RPC UI facade retains one expansion state shared by its getter and chat render settings, and the remote custom-UI bridge now filters Kitty key-release events unless the child component explicitly opts into them, preventing one physical Ctrl+O press from immediately toggling expansion back off. Repeated expand/collapse toggles match main chat across active and completed runs, single/parallel/chain views, and narrow terminals without changing prompt, custom-UI, or unrelated key ownership.
- Fixed bundled workflow-stage `intercom.ask` calls hanging when the addressed sibling stage had already completed. Atomic now schedules a post-mortem turn in the completed target's retained conversation, preserves the exact child-to-child reply thread, prevents parent or unrelated sessions from satisfying it, and returns bounded actionable errors for unavailable or non-resumable retained targets. Production workflow and Intercom late-message listeners preserve the first owner's completion promise regardless of registration order, so later or duplicate listeners cannot turn revival failures into silent no-ops ([#1854](https://github.com/bastani-inc/atomic/issues/1854)).
- Fixed tool results being able to push the active Pi loop past Atomic's buffered compaction threshold before its next provider request. Atomic now performs one ordinary verbatim compaction preflight after tool results enter the prospective next-turn context, returns the rebuilt protocol-valid context to the same loop without scheduling `agent.continue()`, leaves below-threshold turns unchanged, surfaces compaction cancellation/failure, and blocks a follow-up request that remains known to exceed the provider hard input limit.
- Fixed the interactive TUI dropping its working spinner after successful post-tool autocompaction while the same agent stream continued. The normal working indicator is now restored immediately at the mid-turn compaction boundary, without waiting for the user's next interaction.
- Fixed bundled workflow stage messaging to distinguish idle sessions from active streams: idle `followUp`/eligible `auto` sends now start a real prompt and report `prompt`, streaming follow-ups queue and steering stays in-turn, paused idle-chat `resume(message)` starts exactly one recorded turn, and interrupted-turn resume, abort/exit guards, and at-most-once continuation behavior remain intact ([#1850](https://github.com/bastani-inc/atomic/issues/1850)).
- Fixed Escape during isolated-engine compaction timing out with `Timeout waiting for response to abort_compaction`. RPC input now keeps ordinary commands FIFO while validated cancellation and host-response control frames use an independent lane, so `abort_compaction` reaches the active engine session before the pending `compact` request settles; duplicate and late aborts remain harmless, and ordinary command ordering is preserved.
- Fixed interactive non-schema workflow stages treating typed/freeform `ask_user_question` chat answers as an invisible implicit “stay”. The conversational acknowledgement now completes first, then a real brokered readiness gate exposes `awaiting_input`/`inputRequest.kind: "readiness_gate"`; in that chat flow, Not ready permits another stage-chat turn and re-prompts, while Ready releases dependents. Structured-option behavior remains unchanged, and successful schema finalization stays terminal. ([#1849](https://github.com/bastani-inc/atomic/issues/1849))
- Fixed the bundled workflow `BACKGROUND` panel's elapsed timer remaining at `0s` until the user switched to the orchestrator. Visible running cards now repaint their existing mounted component on exact one-second boundaries from workflow start, without panel navigation or widget remounting; paused timers remain frozen and ended cards retain their one-shot expiry lifecycle.
- Fixed the interactive TUI crashing with `Rendered line N exceeds terminal width` (uncaught exception in pi-tui `doRender`) when the terminal was resized — most visibly when shrinking an active session with chat history (e.g. 112 → 83 columns) or fuzz-resizing mid-turn. Under the isolated interactive engine, tool cards and custom messages render out-of-process and asynchronously: after a resize they kept replaying the previous frame wrapped for the old width until the engine child delivered a re-wrapped one, so pi-tui's differential renderer could see lines wider than the terminal and abort the whole session. Remote frames are now defensively clamped to the current render width while the engine catches up, so grow/shrink resizing (including rapid adversarial flapping) never emits an overflowing line; properly re-wrapped frames still replace the clamped ones as soon as they arrive.
- Fixed the startup banner logo wrapping and shredding into misaligned art on narrow terminals: the identity header is now width-aware, keeping the side-by-side logo/meta layout only when every combined row fits, stacking the identity text (version, provider/model, cwd) under the logo when the meta column would wrap, and dropping the logo art entirely when the terminal is narrower than the logo itself. The layout re-adapts on every terminal resize.
- Fixed adjacent assistant thinking blocks rendering as separate sections, Windows package checks leaving npm's terminal title behind, standalone Bun binaries omitting interactive OAuth adapters, and clone/fork attempts before the first assistant response returning an unhelpful failure ([#1875](https://github.com/bastani-inc/atomic/issues/1875)).
- Stopped the isolated interactive engine's extension-level `requestRender()` from invalidating hidden remote overlay components ([#1856](https://github.com/bastani-inc/atomic/issues/1856)). The broadcast now skips components whose remote OverlayHandle is hidden (`setHidden(true)`/`hide()`), so widget-local updates do not trigger host render work for hidden graph/stage frames; shown-again components rejoin the broadcast immediately.
- Reduced keypress latency for all remotely rendered extension custom UI (workflow inputs forms, graph overlays, and other `ctx.ui.custom()` components) under the isolated interactive engine. The host now pipelines a fresh frame request directly behind every forwarded keypress — engine commands are delivered in order, so the returned frame always reflects the applied input. This cuts the previous input → child-invalidate → render-request → frame path to a single round trip and repaints components that change state on input without self-invalidating.
- Fixed the mouse wheel scrolling native/main-chat scrollback instead of the workflow graph when a durable workflow overlay was resumed under the isolated interactive engine. The overlay adapter runs inside the engine child, whose stdout is the JSONL transport rather than a TTY, so its `process.stdout` mouse-tracking escape sequences never put the real host terminal into mouse-reporting mode. The overlay now enables host mouse-scroll reporting (and Windows autowrap) through the new typed host-terminal control channel, so wheel gestures reach the remote `GraphView` and change graph scroll offsets while a visible overlay captures the mouse; keyboard navigation, stage-chat wheel capture, and Ctrl+T copy mode are unchanged. Resume selection deterministically disposes the inline picker before the fullscreen overlay mounts and takes focus, so a late picker cleanup can no longer steal focus or leave the graph as an inline/bottom component. Non-isolated hosts keep their local `process.stdout` behavior.
- Fixed `/resume` and `/workflow resume` intermittently failing with `Timeout waiting for response to prompt` when the picker (or any long-lived interactive prompt) stayed open past 30 seconds. The isolated-engine RPC client now exempts long-lived interactive commands — prompts and custom-UI pickers, queued `steer`/`follow-up` sends, long shell and compaction work, `fork`/`clone`, session `switch`/`new`/`import`, tree navigation, and shortcut invocation — from the generic 30-second request deadline. Process exit, transport violations, abort, and generation replacement still reject pending requests immediately, and bounded metadata/control requests keep a (now injectable) deadline, so real failure detection is unchanged.
- Made the `/resume` session selector mount and paint its header, search, and loading state on the very first frame, then discover and parse sessions off the host UI loop. Directory scans run in bounded cooperative batches and a single very large transcript is parsed in yielding chunks, so input, search, and cancel stay responsive and one large session can no longer visibly freeze the picker. Closing the selector now cancels in-flight loads and ignores stale results, preventing late list updates after close, scope switch, or a newer load.
- Fixed a startup input race where typing a command-like draft such as a bare `/` before the header finished loading was reclassified as a submitted cooked-mode command and sent automatically. Raw startup capture now remains authoritative: only Enter-terminated submissions replay, while unfinished slash/bash drafts stay in the editor.
- Fixed startup changelog and first-run onboarding notices being gated behind the deferred extension reload — and, when a prompt was typed immediately at launch, behind the entire first agent turn. They now render right after the input handler is ready (milliseconds after first paint), matching pi's behavior; the RESOURCES disclosure still waits for the actual extension load it reports on.
- Reduced deferred extension-load stalls by yielding to the event loop between extension loads only when the current turn has actually run long (≥16 ms) instead of unconditionally — the previous unconditional yields cost a full macrotask turn (~100 ms each while the TUI is live) per bundled extension (~0.5 s of the deferred load).
- Hardened the isolated interactive engine's runtime and transport. Best-effort RPC rejections are now centrally contained so a fire-and-forget path can no longer crash the host; Escape/cancel recovery is generation-fenced so a cancelled turn survives, the host stays alive, and the engine child is cleanly restarted. The child is the sole authoritative writer of transcript/settings state (host snapshots are side-effect-free, and model/thinking/name operations persist exactly once), and a guardian tracks the full process tree so forced host death leaves no orphaned engine or detached grandchild.
- Hardened the interactive-engine JSONL transport against unbounded growth and frame loss. Framing is UTF-8 byte-bounded (not UTF-16 char counts), writers in both directions are byte-accounted with backpressure, update coalescing happens before serialization, and terminal/correlated frames are either delivered or fail immediately with an explicit protocol error and same-id rejection instead of a 30-second timeout.
- Fixed the isolated interactive engine omitting extension slash commands (`/workflow`, its `/workflows` alias, `/run`, `/mcp`, and others) from autocomplete. Because the host session loads no extensions in the interactive-engine child model, those commands live only in the engine child; the host now fetches the child's command catalog asynchronously after engine bind (never delaying first paint or input), merges it into autocomplete with built-in names reserved and locally-present prompts/skills deduped, and re-fetches after engine restart, reload, and new/resume/fork. Command execution continues to route through the child with no duplicate host handling.
- Fixed expandable skill, resource, built-in tool, MCP, and subagent headers losing their effective keybinding in the isolated interactive engine (for example, `[skill] tmux ( Expand)`). Child rendering and injected custom UI now share one reloadable Atomic keybinding manager, so the default displays `ctrl+o`, remaps display the configured key, and intentionally unbound expansion omits the unavailable shortcut affordance without malformed punctuation.
- Fixed the TUI flickering with rapid full repaints (~58 Hz, plus idle CPU burn on both processes) whenever a tool card rendered under the isolated interactive engine during a streaming turn — most visibly when the `workflow` tool's `quit`/`pause` action ran while a workflow stage was streaming, though any tool call (e.g. a plain `bash` echo) reproduced it. The engine child's remote tool-card renderer disposed and recreated the `ToolExecutionComponent` on every `engine_tool_render` request, and re-seeding the fresh component unconditionally scheduled a render on its off-screen TUI whose terminal write emitted `engine_custom_invalidate` back to the host, which marked the card dirty and re-sent the render request — forever. The engine render service now reuses the cached component per remote component id (applying args/result/expansion/image-option updates in place) and the seeding setters (`markExecutionStarted`/`setArgsComplete`) are idempotent, so the loop converges after at most one invalidate round trip while legitimate async invalidations (such as image format conversion) still repaint.

## [0.9.10-alpha.1] - 2026-07-19

### Breaking Changes

- The bundled `workflow` tool no longer accepts direct one-off `task`, `tasks`, or `chain` shapes (or their direct-only top-level options), and the bundled workflow SDK no longer exports `runTask`, `runParallel`, or `runChain`. Launch named workflows with `workflow` + `inputs`, and author custom definitions with the unchanged `ctx.task`, `ctx.chain`, and `ctx.parallel` primitives.
- Bundled workflow durability now requires DBOS/Postgres and no longer provides local JSON/SQLite persistence, runtime opt-out/backend selection, session-transcript discovery, or conversion of prior durable data.
- Added a host-native session picker capability, `ctx.ui.hostSessionPicker(request)`, implemented first-class by every interactive host with one identical API: non-isolated interactive mode mounts the real built-in session selector directly in the terminal process (no IPC), and the isolated interactive engine routes the same capability over a new engine session-picker protocol channel (`engine_session_picker_open/update/error/close` messages and `engine_session_picker_select/cancel/delete` commands). The extension ships JSON-safe rows (`HostSessionPickerRow` — `SessionInfo` with `createdAt`/`modifiedAt` epoch millis) and only semantic events ever cross a process boundary; arrow-key navigation and search are zero-IPC and stay responsive even while the extension's event loop is busy, unlike remote-rendered `ctx.ui.custom()` components, which pay one round trip per keypress. Deletion is extension-owned: the host keeps the row until the extension replies with an update (row removed) or an error. Pickers are disposed cleanly on extension-initiated close and on engine crash/restart. The bundled `/workflow resume` picker now REQUIRES this capability — its remote-rendered `ctx.ui.custom()` selector path was removed with no fallback, and hosts without the capability fail the resume command with one actionable error. The member is absent only on non-interactive surfaces (headless RPC, print). See `docs/tui.md` and `docs/extensions.md`.

### Added

- Added private compaction-planner diagnostic sidecars for failed persisted-session planner calls. Malformed output, unusable ranges, provider errors, and stream failures now save the full response text plus stop reason, usage, request output budget, and non-secret model metadata beside the session JSONL with `0600` permissions where supported; the compaction error reports that path. Credentials, headers, and request prompts are excluded, while in-memory sessions and sidecar write failures preserve the original `RangePlanError` unchanged.
- Replaced the compaction planner output format from JSON `{"d":[[start,end],...]}` with bare `start,end` records (one per line, no brackets/fences/prose). The new format is ~1% more token-efficient at equivalent range counts and enables trivial newline-based length-truncation recovery.
- Added silent recovery of complete deletion records from length-truncated compaction planner responses. When the model output is cut by `max_tokens` (`stopReason: "length"`), a deterministic line parser recovers newline-terminated `start,end` records, discards the final unterminated fragment (never guesses multi-digit integers), and passes results through normal validation. Recovery is silent: no warning, banner, or special status—UI shows the normal `✻ Context compacted` message. A private `0600` recovery-diagnostic sidecar is written beside persisted sessions for operational observability (never surfaced in UI). The planner prompt now instructs the model to emit ranges in descending deletion confidence so the highest-priority deletions survive truncation.
- Added six bundled, composable pattern workflow definitions — `classify-and-act`, `fan-out-and-synthesize`, `adversarial-verification`, `generate-and-filter`, `tournament`, and `loop-until-done` — with typed/defaulted inputs, dedicated artifact-aware stage prompts, declared outputs, named discovery, and `@bastani/workflows/builtin` exports for nested `ctx.workflow(...)` composition.
- Added `ctx.ui.hostInputForm(request)`, a JSON-safe host-native structured-form capability implemented identically by isolated and non-isolated interactive hosts. The terminal host owns inline rendering, focus, validation, configured keybindings, and editing state; under engine isolation only open and submit/cancel semantic events cross the protocol boundary. Headless RPC and print surfaces omit the optional member.
- Added `/workflows [run-id]` as a retained workflow-run alias for `/workflow resume`, with confirmed backend-aware deletion for inactive durable/completed rows that protects in-flight runs and preserves independent session transcripts.
- Added supervised interactive-engine isolation: extension tools, hooks, commands, workflow code, and custom UI rendering now run in a child process so a synchronous busy loop cannot freeze terminal input, spinners, or rendering. A 50 ms heartbeat watchdog identifies blocked callbacks after 250 ms, exposes interrupt/termination guidance after one second, and reports terminated results as unknown without automatic retry. JSON-safe `ctx.ui.custom()` components are remotely rendered with asynchronous input forwarding; unsupported synchronous host callback APIs warn instead of falling back in-process.
- Added a typed, allowlisted host-terminal control channel for isolated `ctx.ui.custom()` components. An overlay factory's `tui.terminal` now exposes `setMouseScrollTracking(enabled)` and `setAutowrap(enabled)` setters that the terminal host applies to the real host TTY over the engine protocol — the engine child (whose stdout is the JSONL transport, not a TTY) can enable SGR mouse-scroll reporting and Windows autowrap without ever forwarding raw child bytes to the terminal. Controls are component/generation scoped and every enabled mode is reset when the overlay hides, closes, is disposed, or the engine child crashes/restarts. See `docs/tui.md`.
- Added live-updating, semantically colored rows to the bundled `/workflow resume` picker: completed rows render green, paused yellow, and failed/blocked/crashed red (`SessionInfo.messageColor` now also accepts `"error"`). Rows re-list on run-store changes and a bounded cross-session poll while the picker is open, and running workflows are never offered as resume targets — a fresh-heartbeat running row is hidden from every session, and only stale (crashed) ones surface, labeled `crashed`.
- Added `atomic update --models` to force-refresh authenticated dynamic provider catalogs with bounded, provider-scoped persistence and actionable failures ([#1875](https://github.com/bastani-inc/atomic/issues/1875)).

### Changed

- Promoted the DBOS SDK and `embedded-postgres` to mandatory dependencies. Atomic configures and launches DBOS lazily on the first workflow action, awaits readiness before every workflow lifecycle path, and surfaces initialization or persistence failures for that action instead of continuing on another backend. Without `DBOS_SYSTEM_DATABASE_URL`, workflow durability runs on an embedded Postgres from npm-distributed binaries (detached daemon under `~/.atomic/postgres`, shared across sessions; Docker `dbos-db` container only as a platform fallback). Concurrent Atomic sessions sharing one database use per-process executor identities, owner/heartbeat metadata, and first-writer-wins resume claims so one session's live workflow cannot be double-dispatched from another.
- Documented and enforced `/resume`-equivalent workflow-history retention: eligible runs are not filtered or garbage-collected by age/count, and the selector viewport does not limit search/navigation.
- Kept the interactive-engine watchdog's early 250 ms blocking signal internal instead of rendering repetitive "has not yielded" warnings during ordinary slow extension/tool loading. One-second unresponsive heartbeat-watchdog diagnostics are likewise always kept internal, whether or not they attribute a callback (for example, `extension.hook tool_execution_end`); the isolated host remains responsive and still provides Escape/Ctrl+C recovery controls. Concrete engine failures such as termination and RPC errors continue to surface as chat errors.
- Changed verbatim compaction to classify the complete active transcript except for exactly the newest `preserve_recent` context-visible messages (default `2`). The protected tail is no longer widened to a user-turn boundary, `preserve_recent: 0` now retains no ordinary message, and repeated compaction includes the prior durable verbatim summary while preserving resumable session boundaries.
- Redesigned the `/workflow <name>` input form (shown when a named workflow is launched with required inputs missing). The form now renders inside a rounded, theme-aware panel titled `WORKFLOW INPUTS` with the workflow name and field count, matching the workflow catalogue's visual language: each field shows a focus chevron, an accent field name when active, and a right-aligned `required`/`optional` badge (required in the warning color); descriptions use the readable muted tone, validation errors render as a `✗ reason` row, the submit control is a `[ Run workflow ]` button pill (filled accent when focused), and the footer uses accent key chips. Multi-line text inputs blend their inset rules into the panel border, the panel scales to the full terminal width like the other chat surfaces, and the embedded editor cursor stays correctly positioned.
- Migrated model authentication and refresh to Pi's provider-owned APIs while preserving Atomic's public `AuthStorage`, `ModelRegistry`, SDK options, legacy extension OAuth registration, complete request headers, and credential-specific enterprise endpoints. The model picker now shows cached models immediately and reports concise success, partial-error, and timeout status ([#1875](https://github.com/bastani-inc/atomic/issues/1875)).

### Fixed

- Fixed the repository `publish-release` workflow to reconcile an exact release PR merged externally while required checks are pending. It preserves identity/refs/SHA; correlates workflow-qualified Actions reruns by name plus workflow; and supports empty-workflow `StatusContext` and GitHub App `CheckRun` evidence. Linked reruns group by inferred kind/name across URL changes; linkless rows inspect both external kinds, accept all-passing candidates, block any pending/failure, and exclude nonempty-workflow Actions. Duplicate aliases reuse exact passing evidence. It rechecks after merge and validates merge/branch evidence. Tag recovery proves `verified merge → tag parent → current base`; exhaustive history avoids GitHub's 1,000-result ceiling; protected coordination retains its lock through ambiguous dispatch visibility; and recovered success requires exact-SHA integrity evidence.
- Fixed shared extension chat compaction rendering so manual, threshold, and overflow compaction use the animated working spinner with reason-aware copy instead of a duplicate plain status row plus generic `Working...`; successful compaction now falls back to the existing typed `✻ Context compacted` message when a refreshed live session snapshot is unavailable, while preserving durable session reconstruction and avoiding duplicate boundaries.
- Fixed workflow stage-chat `/compact` cancellation and planner/provider failures from escaping their fire-and-forget editor submission promise and terminating the CLI. The authoritative `compaction_end` event now owns the visible status, animation cleanup, and diagnostic path while the same stage remains usable for retry or follow-up.
- Fixed OpenAI Responses, Codex Responses, and OpenAI Completions context accounting to sum their normalized uncached and cached input partitions even when the values are nearly equal. Anthropic Messages retains its mirrored-cache guard, preventing missed auto-compaction thresholds, understated footer usage, and negative persisted reduction percentages on Codex sessions.
- Fixed workflow-stage message admission races by adding a linearizable AgentSession generation boundary. Intercom traffic and async bash/subagent completions received before close now use native queued steering/follow-up delivery and drain before terminal stage publication; close-winning detached results route once to the main chat without reopening or mutating the completed stage. Detached producers remain non-blocking, stable producer identities prevent duplicate delivery, and explicit `StageContext.sendUserMessage` plus post-mortem stage chat remain available.
- Fixed asynchronous extension custom-message admission to return its delivery promise through `pi.sendMessage()` and `pi.sendMessages()`, allowing workflow late-message routers to propagate main-chat failures and release stable producer keys for retry instead of treating rejected routes as successful.
- Fixed bundled workflow stages freezing when an active foreground subagent used Intercom or blocking `contact_supervisor`. Busy stages give the exact child owner's acknowledged detach handshake first refusal before model-visible stage-session queue insertion; owner loss between probe/commit falls back instead of dropping the broker message. In parallel foreground groups, one accepted commit releases aggregate supervision for all active siblings while their child processes and eventual result/status recovery remain owned; run-owned worktrees are retained until every detached child closes and their diffs are captured.
- Fixed bundled workflow-stage `intercom.ask` delivery when the target sibling is mid-turn in an in-flight tool call. The open generation now owns the ask before asynchronous foreground-owner coordination begins, waits for its native queue insertion, and drains it before terminal publication; genuinely failed destination admission returns a correlated actionable error rather than consuming the full reply timeout.
- Fixed `/workflow <name>` input forms becoming non-responsive under interactive-engine isolation. The RPC extension surface advertised `setEditorComponent` as a callable compatibility stub even though it could not install a host editor, so the workflow command waited on an editor that was never mounted while Tab and Escape stayed routed to the ordinary prompt. Named workflow forms now prefer the host-native input-form channel, keeping Tab/Shift+Tab, arrows, editing, configured keybindings, Enter, Escape, and Ctrl+C local and responsive in both interactive modes; cancellation does not dispatch the workflow, inline bottom-editor geometry is preserved, and legacy custom-editor/`ctx.ui.custom()` fallbacks remain available for older hosts.
- Fixed Ctrl+O (`app.tools.expand`) being consumed without expanding tool and workflow-node detail in attached workflow stage chats under interactive-engine isolation. The child RPC UI facade retains one expansion state shared by its getter and chat render settings, and the remote custom-UI bridge now filters Kitty key-release events unless the child component explicitly opts into them, preventing one physical Ctrl+O press from immediately toggling expansion back off. Repeated expand/collapse toggles match main chat across active and completed runs, single/parallel/chain views, and narrow terminals without changing prompt, custom-UI, or unrelated key ownership.
- Fixed bundled workflow-stage `intercom.ask` calls hanging when the addressed sibling stage had already completed. Atomic now schedules a post-mortem turn in the completed target's retained conversation, preserves the exact child-to-child reply thread, prevents parent or unrelated sessions from satisfying it, and returns bounded actionable errors for unavailable or non-resumable retained targets. Production workflow and Intercom late-message listeners preserve the first owner's completion promise regardless of registration order, so later or duplicate listeners cannot turn revival failures into silent no-ops ([#1854](https://github.com/bastani-inc/atomic/issues/1854)).
- Fixed tool results being able to push the active Pi loop past Atomic's buffered compaction threshold before its next provider request. Atomic now performs one ordinary verbatim compaction preflight after tool results enter the prospective next-turn context, returns the rebuilt protocol-valid context to the same loop without scheduling `agent.continue()`, leaves below-threshold turns unchanged, surfaces compaction cancellation/failure, and blocks a follow-up request that remains known to exceed the provider hard input limit.
- Fixed the interactive TUI dropping its working spinner after successful post-tool autocompaction while the same agent stream continued. The normal working indicator is now restored immediately at the mid-turn compaction boundary, without waiting for the user's next interaction.
- Fixed bundled workflow stage messaging to distinguish idle sessions from active streams: idle `followUp`/eligible `auto` sends now start a real prompt and report `prompt`, streaming follow-ups queue and steering stays in-turn, paused idle-chat `resume(message)` starts exactly one recorded turn, and interrupted-turn resume, abort/exit guards, and at-most-once continuation behavior remain intact ([#1850](https://github.com/bastani-inc/atomic/issues/1850)).
- Fixed Escape during isolated-engine compaction timing out with `Timeout waiting for response to abort_compaction`. RPC input now keeps ordinary commands FIFO while validated cancellation and host-response control frames use an independent lane, so `abort_compaction` reaches the active engine session before the pending `compact` request settles; duplicate and late aborts remain harmless, and ordinary command ordering is preserved.
- Fixed interactive non-schema workflow stages treating typed/freeform `ask_user_question` chat answers as an invisible implicit “stay”. The conversational acknowledgement now completes first, then a real brokered readiness gate exposes `awaiting_input`/`inputRequest.kind: "readiness_gate"`; in that chat flow, Not ready permits another stage-chat turn and re-prompts, while Ready releases dependents. Structured-option behavior remains unchanged, and successful schema finalization stays terminal. ([#1849](https://github.com/bastani-inc/atomic/issues/1849))
- Fixed the bundled workflow `BACKGROUND` panel's elapsed timer remaining at `0s` until the user switched to the orchestrator. Visible running cards now repaint their existing mounted component on exact one-second boundaries from workflow start, without panel navigation or widget remounting; paused timers remain frozen and ended cards retain their one-shot expiry lifecycle.
- Fixed the interactive TUI crashing with `Rendered line N exceeds terminal width` (uncaught exception in pi-tui `doRender`) when the terminal was resized — most visibly when shrinking an active session with chat history (e.g. 112 → 83 columns) or fuzz-resizing mid-turn. Under the isolated interactive engine, tool cards and custom messages render out-of-process and asynchronously: after a resize they kept replaying the previous frame wrapped for the old width until the engine child delivered a re-wrapped one, so pi-tui's differential renderer could see lines wider than the terminal and abort the whole session. Remote frames are now defensively clamped to the current render width while the engine catches up, so grow/shrink resizing (including rapid adversarial flapping) never emits an overflowing line; properly re-wrapped frames still replace the clamped ones as soon as they arrive.
- Fixed the startup banner logo wrapping and shredding into misaligned art on narrow terminals: the identity header is now width-aware, keeping the side-by-side logo/meta layout only when every combined row fits, stacking the identity text (version, provider/model, cwd) under the logo when the meta column would wrap, and dropping the logo art entirely when the terminal is narrower than the logo itself. The layout re-adapts on every terminal resize.
- Fixed adjacent assistant thinking blocks rendering as separate sections, Windows package checks leaving npm's terminal title behind, standalone Bun binaries omitting interactive OAuth adapters, and clone/fork attempts before the first assistant response returning an unhelpful failure ([#1875](https://github.com/bastani-inc/atomic/issues/1875)).
- Stopped the isolated interactive engine's extension-level `requestRender()` from invalidating hidden remote overlay components ([#1856](https://github.com/bastani-inc/atomic/issues/1856)). The broadcast now skips components whose remote OverlayHandle is hidden (`setHidden(true)`/`hide()`), so widget-local updates do not trigger host render work for hidden graph/stage frames; shown-again components rejoin the broadcast immediately.
- Reduced keypress latency for all remotely rendered extension custom UI (workflow inputs forms, graph overlays, and other `ctx.ui.custom()` components) under the isolated interactive engine. The host now pipelines a fresh frame request directly behind every forwarded keypress — engine commands are delivered in order, so the returned frame always reflects the applied input. This cuts the previous input → child-invalidate → render-request → frame path to a single round trip and repaints components that change state on input without self-invalidating.
- Fixed the mouse wheel scrolling native/main-chat scrollback instead of the workflow graph when a durable workflow overlay was resumed under the isolated interactive engine. The overlay adapter runs inside the engine child, whose stdout is the JSONL transport rather than a TTY, so its `process.stdout` mouse-tracking escape sequences never put the real host terminal into mouse-reporting mode. The overlay now enables host mouse-scroll reporting (and Windows autowrap) through the new typed host-terminal control channel, so wheel gestures reach the remote `GraphView` and change graph scroll offsets while a visible overlay captures the mouse; keyboard navigation, stage-chat wheel capture, and Ctrl+T copy mode are unchanged. Resume selection deterministically disposes the inline picker before the fullscreen overlay mounts and takes focus, so a late picker cleanup can no longer steal focus or leave the graph as an inline/bottom component. Non-isolated hosts keep their local `process.stdout` behavior.
- Fixed `/resume` and `/workflow resume` intermittently failing with `Timeout waiting for response to prompt` when the picker (or any long-lived interactive prompt) stayed open past 30 seconds. The isolated-engine RPC client now exempts long-lived interactive commands — prompts and custom-UI pickers, queued `steer`/`follow-up` sends, long shell and compaction work, `fork`/`clone`, session `switch`/`new`/`import`, tree navigation, and shortcut invocation — from the generic 30-second request deadline. Process exit, transport violations, abort, and generation replacement still reject pending requests immediately, and bounded metadata/control requests keep a (now injectable) deadline, so real failure detection is unchanged.
- Made the `/resume` session selector mount and paint its header, search, and loading state on the very first frame, then discover and parse sessions off the host UI loop. Directory scans run in bounded cooperative batches and a single very large transcript is parsed in yielding chunks, so input, search, and cancel stay responsive and one large session can no longer visibly freeze the picker. Closing the selector now cancels in-flight loads and ignores stale results, preventing late list updates after close, scope switch, or a newer load.
- Fixed a startup input race where typing a command-like draft such as a bare `/` before the header finished loading was reclassified as a submitted cooked-mode command and sent automatically. Raw startup capture now remains authoritative: only Enter-terminated submissions replay, while unfinished slash/bash drafts stay in the editor.
- Fixed startup changelog and first-run onboarding notices being gated behind the deferred extension reload — and, when a prompt was typed immediately at launch, behind the entire first agent turn. They now render right after the input handler is ready (milliseconds after first paint), matching pi's behavior; the RESOURCES disclosure still waits for the actual extension load it reports on.
- Reduced deferred extension-load stalls by yielding to the event loop between extension loads only when the current turn has actually run long (≥16 ms) instead of unconditionally — the previous unconditional yields cost a full macrotask turn (~100 ms each while the TUI is live) per bundled extension (~0.5 s of the deferred load).
- Hardened the isolated interactive engine's runtime and transport. Best-effort RPC rejections are now centrally contained so a fire-and-forget path can no longer crash the host; Escape/cancel recovery is generation-fenced so a cancelled turn survives, the host stays alive, and the engine child is cleanly restarted. The child is the sole authoritative writer of transcript/settings state (host snapshots are side-effect-free, and model/thinking/name operations persist exactly once), and a guardian tracks the full process tree so forced host death leaves no orphaned engine or detached grandchild.
- Hardened the interactive-engine JSONL transport against unbounded growth and frame loss. Framing is UTF-8 byte-bounded (not UTF-16 char counts), writers in both directions are byte-accounted with backpressure, update coalescing happens before serialization, and terminal/correlated frames are either delivered or fail immediately with an explicit protocol error and same-id rejection instead of a 30-second timeout.
- Fixed the isolated interactive engine omitting extension slash commands (`/workflow`, its `/workflows` alias, `/run`, `/mcp`, and others) from autocomplete. Because the host session loads no extensions in the interactive-engine child model, those commands live only in the engine child; the host now fetches the child's command catalog asynchronously after engine bind (never delaying first paint or input), merges it into autocomplete with built-in names reserved and locally-present prompts/skills deduped, and re-fetches after engine restart, reload, and new/resume/fork. Command execution continues to route through the child with no duplicate host handling.
- Fixed expandable skill, resource, built-in tool, MCP, and subagent headers losing their effective keybinding in the isolated interactive engine (for example, `[skill] tmux ( Expand)`). Child rendering and injected custom UI now share one reloadable Atomic keybinding manager, so the default displays `ctrl+o`, remaps display the configured key, and intentionally unbound expansion omits the unavailable shortcut affordance without malformed punctuation.
- Fixed the TUI flickering with rapid full repaints (~58 Hz, plus idle CPU burn on both processes) whenever a tool card rendered under the isolated interactive engine during a streaming turn — most visibly when the `workflow` tool's `quit`/`pause` action ran while a workflow stage was streaming, though any tool call (e.g. a plain `bash` echo) reproduced it. The engine child's remote tool-card renderer disposed and recreated the `ToolExecutionComponent` on every `engine_tool_render` request, and re-seeding the fresh component unconditionally scheduled a render on its off-screen TUI whose terminal write emitted `engine_custom_invalidate` back to the host, which marked the card dirty and re-sent the render request — forever. The engine render service now reuses the cached component per remote component id (applying args/result/expansion/image-option updates in place) and the seeding setters (`markExecutionStarted`/`setArgsComplete`) are idempotent, so the loop converges after at most one invalidate round trip while legitimate async invalidations (such as image format conversion) still repaint.

## [0.9.9] - 2026-07-15

### Breaking Changes

- Replaced deletion-target context compaction with durable verbatim line compaction. Legacy `context_compaction` entries are now inert archival records, so content they previously hid may re-enter context when old sessions resume; start a new conversation or compact again to establish a new boundary.
- Removed the deprecated `contextCompact()` SDK method, `context_compact` RPC command, and `context_compaction_start`/`context_compaction_end` events. Use `compact()` and `compaction_start`/`compaction_end`.
- Changed extension compaction hooks: `session_before_compact` now accepts `cancel` or a complete non-empty `compactedText` override instead of `deletionRequest`; `session_compact` exposes `VerbatimCompactionResult` and the saved `compactionEntry`.

### Added

- Added one-pass contextual line-ranking compaction: Atomic asks the active session model at its active reasoning level, through the normal session stream/provider wrapper, for one whole-region compact `{"d":[[start,end],...]}` deletion plan, then safety-normalizes ranges and mechanically reconstructs verbatim text with cumulative `(filtered N lines)` markers.
- Added strict pi-style compaction failure semantics: oversized prompts, provider/API errors or overflow, aborts, malformed JSON, and empty/unusable ranges fail without persistence or continuation and without semantic retries, chunking, critical replans, deterministic fallback, or deterministic target correction.
- Added durable pi-style `CompactionEntry` boundaries discriminated by `details.strategy: "verbatim-lines"`, plus resumable custom-role boundary messages and a collapsible TUI card showing retained-line and token-reduction statistics.
- Added `pi.sendMessages()` for atomic, array-ordered custom-message admission without waiting for the resulting model turn, allowing companion extensions to keep related preludes and terminal notices contiguous without globally serializing unrelated work.

### Changed

- Changed `compression_ratio` to explicitly represent the fraction of compactable lines to keep. `preserve_recent` is enforced client-side, widens the tail to a user-turn start, and always protects the final logical turn. Role headers are now ordinary ranked lines; only explicit protected spans are split out of model-selected deletion ranges.
- Simplified resume reconstruction to load the persisted compacted string followed by original messages from `firstKeptEntryId`; no deletion filters, signed-thinking repair pass, or tool dependency repair is re-derived.
- Retuned the one-pass compaction classifier's retention guidance to surgically thin long tool results, preserve compact diagnostic/code/state anchors, prune repeated retry and superseded bodies, and treat formatting, stack position, markers, and query matches as contextual rather than hard categories.
- Updated the Pi runtime dependency set (`@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui`) from `^0.80.6` to `^0.80.7` across Atomic and its bundled packages, with matching Bun and npm lock metadata. This inherits the upstream provider authentication, session-affinity, reasoning replay, tool-choice, model-catalog, terminal input, and prompt-cache fixes from [Pi v0.80.7](https://github.com/earendil-works/pi/releases/tag/v0.80.7) while preserving versionless `0.0.0` workspace manifests.
- Consolidated ten closed Dependabot updates into the Pi v0.80.7 branch: `linkedom` 0.18.13, npm `ignore` 7.0.6, `@typescript/native-preview` 7.0.0-dev.20260707.2, `typebox` 1.3.6, TypeScript 7.0.2, and the native dependency refreshes documented in `@bastani/atomic-natives`; regenerated the Bun, npm, and publish shrinkwrap dependency graphs without changing workspace package versions.
- Added an Atomic `StringEnum` export that preserves Pi's Google-compatible enum schema while bridging Pi 0.80.7's TypeBox identity to the direct TypeBox 1.3.6 types; updated bundled extension examples and documentation to use the portable export, and updated TypeScript fixture configs for TypeScript 7's removal of `baseUrl`.

### Fixed

- Fixed resumed sessions resurrecting records hidden by earlier Verbatim Compaction entries after later compactions changed signed-thinking turn or tool-call/result dependencies. Persisted logical deletions are now an authoritative lower bound during every context rebuild: Atomic closes unsafe replay structures by adding transient omissions instead of restoring compacted calls/results, pairs each result with its concrete call occurrence so independent exchanges may reuse an opaque call ID, leaves unrelated retained history unchanged, and keeps the append-only session file intact.
- Fixed successful manual and automatic compaction UI refreshes to render the compacted transcript with exactly one visible `✻ Context compacted` boundary, while aborted and failed compactions remain unchanged.
- Fixed the bundled workflows `/workflow resume` experience to mix successful completed workflows into the existing globally newest-first, deduplicated picker with green completed styling, resolve full IDs and prefixes across live, durable, and completed targets, and reopen retained stage chats for follow-up without re-running workflow code or replaying side effects. Completed durable state is retained for authoritative inspection; rows need checkpoints and at least one strictly valid retained conversation, invalid per-stage transcript paths cannot open chat, repeated inspection refreshes changed authoritative chat handles, and selector mount failures close safely. ([#1532](https://github.com/bastani-inc/atomic/issues/1532))
- Fixed workflow-owned transcripts leaking into normal `/resume`, `-r`, `-c`, and `--continue` history by requiring complete workflow ownership markers, persisting classification in initial fork headers, inheriting it across branches, and keeping malformed legacy markers and ordinary user forks visible.
- Preserved accepted async-child Intercom chronology across lazily activated companion extensions by atomically admitting same-child ordinary messages before pause, completion, and failure notices; unrelated children remain independent and ask/reply behavior is unchanged ([#1802](https://github.com/bastani-inc/atomic/issues/1802)).
- Fixed in-process workflow reload to atomically publish freshly rescanned project, user, legacy, configured, and package workflow resources across list/get/inputs/help/completion/invocation surfaces. Overlapping requests are serialized and coalesced, stale session generations cannot overwrite current state, fatal refresh failures retain the prior registry, and reload no longer blocks or changes workflows already in flight. `/workflow reload` and the workflow tool now include actionable per-resource diagnostics instead of reporting bare success when malformed or missing resources were skipped.

## [0.9.9-alpha.4] - 2026-07-15

### Fixed

- Fixed in-process workflow reload to atomically publish freshly rescanned project, user, legacy, configured, and package workflow resources across list/get/inputs/help/completion/invocation surfaces. Overlapping requests are serialized and coalesced, stale session generations cannot overwrite current state, fatal refresh failures retain the prior registry, and reload no longer blocks or changes workflows already in flight. `/workflow reload` and the workflow tool now include actionable per-resource diagnostics instead of reporting bare success when malformed or missing resources were skipped.

## [0.9.9-alpha.3] - 2026-07-14

### Added

- Added `pi.sendMessages()` for atomic, array-ordered custom-message admission without waiting for the resulting model turn, allowing companion extensions to keep related preludes and terminal notices contiguous without globally serializing unrelated work.

### Fixed

- Preserved accepted async-child Intercom chronology across lazily activated companion extensions by atomically admitting same-child ordinary messages before pause, completion, and failure notices; unrelated children remain independent and ask/reply behavior is unchanged ([#1802](https://github.com/bastani-inc/atomic/issues/1802)).

## [0.9.9-alpha.2] - 2026-07-14

### Changed

- Updated the Pi runtime dependency set (`@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui`) from `^0.80.6` to `^0.80.7` across Atomic and its bundled packages, with matching Bun and npm lock metadata. This inherits the upstream provider authentication, session-affinity, reasoning replay, tool-choice, model-catalog, terminal input, and prompt-cache fixes from [Pi v0.80.7](https://github.com/earendil-works/pi/releases/tag/v0.80.7) while preserving versionless `0.0.0` workspace manifests.
- Consolidated ten closed Dependabot updates into the Pi v0.80.7 branch: `linkedom` 0.18.13, npm `ignore` 7.0.6, `@typescript/native-preview` 7.0.0-dev.20260707.2, `typebox` 1.3.6, TypeScript 7.0.2, and the native dependency refreshes documented in `@bastani/atomic-natives`; regenerated the Bun, npm, and publish shrinkwrap dependency graphs without changing workspace package versions.
- Added an Atomic `StringEnum` export that preserves Pi's Google-compatible enum schema while bridging Pi 0.80.7's TypeBox identity to the direct TypeBox 1.3.6 types; updated bundled extension examples and documentation to use the portable export, and updated TypeScript fixture configs for TypeScript 7's removal of `baseUrl`.

## [0.9.9-alpha.1] - 2026-07-14

### Breaking Changes

- Replaced deletion-target context compaction with durable verbatim line compaction. Legacy `context_compaction` entries are now inert archival records, so content they previously hid may re-enter context when old sessions resume; start a new conversation or compact again to establish a new boundary.
- Removed the deprecated `contextCompact()` SDK method, `context_compact` RPC command, and `context_compaction_start`/`context_compaction_end` events. Use `compact()` and `compaction_start`/`compaction_end`.
- Changed extension compaction hooks: `session_before_compact` now accepts `cancel` or a complete non-empty `compactedText` override instead of `deletionRequest`; `session_compact` exposes `VerbatimCompactionResult` and the saved `compactionEntry`.

### Added

- Added one-pass contextual line-ranking compaction: Atomic asks the active session model at its active reasoning level, through the normal session stream/provider wrapper, for one whole-region compact `{"d":[[start,end],...]}` deletion plan, then safety-normalizes ranges and mechanically reconstructs verbatim text with cumulative `(filtered N lines)` markers.
- Added strict pi-style compaction failure semantics: oversized prompts, provider/API errors or overflow, aborts, malformed JSON, and empty/unusable ranges fail without persistence or continuation and without semantic retries, chunking, critical replans, deterministic fallback, or deterministic target correction.
- Added durable pi-style `CompactionEntry` boundaries discriminated by `details.strategy: "verbatim-lines"`, plus resumable custom-role boundary messages and a collapsible TUI card showing retained-line and token-reduction statistics.

### Changed

- Changed `compression_ratio` to explicitly represent the fraction of compactable lines to keep. `preserve_recent` is enforced client-side, widens the tail to a user-turn start, and always protects the final logical turn. Role headers are now ordinary ranked lines; only explicit protected spans are split out of model-selected deletion ranges.
- Simplified resume reconstruction to load the persisted compacted string followed by original messages from `firstKeptEntryId`; no deletion filters, signed-thinking repair pass, or tool dependency repair is re-derived.
- Retuned the one-pass compaction classifier's retention guidance to surgically thin long tool results, preserve compact diagnostic/code/state anchors, prune repeated retry and superseded bodies, and treat formatting, stack position, markers, and query matches as contextual rather than hard categories.


### Fixed

- Fixed resumed sessions resurrecting records hidden by earlier Verbatim Compaction entries after later compactions changed signed-thinking turn or tool-call/result dependencies. Persisted logical deletions are now an authoritative lower bound during every context rebuild: Atomic closes unsafe replay structures by adding transient omissions instead of restoring compacted calls/results, pairs each result with its concrete call occurrence so independent exchanges may reuse an opaque call ID, leaves unrelated retained history unchanged, and keeps the append-only session file intact.
- Fixed successful manual and automatic compaction UI refreshes to render the compacted transcript with exactly one visible `✻ Context compacted` boundary, while aborted and failed compactions remain unchanged.
- Fixed the bundled workflows `/workflow resume` experience to mix successful completed workflows into the existing globally newest-first, deduplicated picker with green completed styling, resolve full IDs and prefixes across live, durable, and completed targets, and reopen retained stage chats for follow-up without re-running workflow code or replaying side effects. Completed durable state is retained for authoritative inspection; rows need checkpoints and at least one strictly valid retained conversation, invalid per-stage transcript paths cannot open chat, repeated inspection refreshes changed authoritative chat handles, and selector mount failures close safely. ([#1532](https://github.com/bastani-inc/atomic/issues/1532))
- Fixed workflow-owned transcripts leaking into normal `/resume`, `-r`, `-c`, and `--continue` history by requiring complete workflow ownership markers, persisting classification in initial fork headers, inheriting it across branches, and keeping malformed legacy markers and ordinary user forks visible.

## [0.9.8] - 2026-07-12

### Changed

- Changed builtin `goal` and `ralph` reviewer approval to be deterministic on the reviewer's self-reported `stop_review_loop` boolean: a reviewer approves exactly when it returns `stop_review_loop=true` with no `reviewer_error`, parse failures still count as non-approval, and Goal's reducer completes on quorum of those booleans without recomputing approval from findings arrays, priorities, or `requirements_traceability` statuses. Recomputing approval from those arrays could deadlock runs whose acceptance criteria referenced the review process itself (for example "three reviewers approve" or "an unmerged PR is created"): no individual reviewer can prove such clauses, so traceability never became fully `proven` even when every reviewer explicitly approved, and the loop burned worker/review turns until `needs_human`. Reviewer prompts now state that the boolean is the single authoritative convergence signal, spell out how to derive it (blocking P0/P1/P2 findings and `required_by_objective` findings at any priority mean `false`; in-scope P3 nice-to-haves, `beyond_objective`/`contradicts_objective` observations, the reviewer-quorum process itself, and the authorized post-approval PR/MR/review final action must never hold it at `false`), and keep findings/traceability as required audit evidence.
- Synchronized the builtin `playwright-cli` skill with microsoft/playwright-cli at `793cfb32572733cbcb401e6f28d05a7a914ce408`, including current installation, snapshot search, mobile emulation, and test generation guidance.
- Renamed the builtin `effective-liteparse` skill to `liteparse` and synchronized it with run-llama/llamaparse-agent-skills at `2dcef7c62417bd2ec4671fce4621bb1e8cce48d0`; existing `/skill:effective-liteparse` references must migrate to `/skill:liteparse`.
- Synchronized the complete builtin `impeccable` skill tree with pbakaus/impeccable at `630fc2682a5bd39b25a8e61f74b6b3f14f2b1e21`, including its latest references, detector libraries, live-review scripts, and provider integrations.

### Fixed

- Disabled terminal autowrap while the fullscreen workflow graph overlay is visible on Windows and restored the previous terminal mode when the overlay closes, preventing wrapped graph rows and stale terminal state ([#1760](https://github.com/bastani-inc/atomic/issues/1760)).
- Hardened synced Impeccable HTML filtering and preview selector escaping against nested sanitizer inputs, permissive script/style closing tags, HTML comment end-bang syntax, and backslash-containing session identifiers; also removed an ineffective CSS property replacement flagged by CodeQL.

## [0.9.8-alpha.1] - 2026-07-12

### Changed

- Changed builtin `goal` and `ralph` reviewer approval to be deterministic on the reviewer's self-reported `stop_review_loop` boolean: a reviewer approves exactly when it returns `stop_review_loop=true` with no `reviewer_error`, parse failures still count as non-approval, and Goal's reducer completes on quorum of those booleans without recomputing approval from findings arrays, priorities, or `requirements_traceability` statuses. Recomputing approval from those arrays could deadlock runs whose acceptance criteria referenced the review process itself (for example "three reviewers approve" or "an unmerged PR is created"): no individual reviewer can prove such clauses, so traceability never became fully `proven` even when every reviewer explicitly approved, and the loop burned worker/review turns until `needs_human`. Reviewer prompts now state that the boolean is the single authoritative convergence signal, spell out how to derive it (blocking P0/P1/P2 findings and `required_by_objective` findings at any priority mean `false`; in-scope P3 nice-to-haves, `beyond_objective`/`contradicts_objective` observations, the reviewer-quorum process itself, and the authorized post-approval PR/MR/review final action must never hold it at `false`), and keep findings/traceability as required audit evidence.
- Synchronized the builtin `playwright-cli` skill with microsoft/playwright-cli at `793cfb32572733cbcb401e6f28d05a7a914ce408`, including current installation, snapshot search, mobile emulation, and test generation guidance.
- Renamed the builtin `effective-liteparse` skill to `liteparse` and synchronized it with run-llama/llamaparse-agent-skills at `2dcef7c62417bd2ec4671fce4621bb1e8cce48d0`; existing `/skill:effective-liteparse` references must migrate to `/skill:liteparse`.
- Synchronized the complete builtin `impeccable` skill tree with pbakaus/impeccable at `630fc2682a5bd39b25a8e61f74b6b3f14f2b1e21`, including its latest references, detector libraries, live-review scripts, and provider integrations.

### Fixed

- Disabled terminal autowrap while the fullscreen workflow graph overlay is visible on Windows and restored the previous terminal mode when the overlay closes, preventing wrapped graph rows and stale terminal state ([#1760](https://github.com/bastani-inc/atomic/issues/1760)).
- Hardened synced Impeccable HTML filtering and preview selector escaping against nested sanitizer inputs, permissive script/style closing tags, HTML comment end-bang syntax, and backslash-containing session identifiers; also removed an ineffective CSS property replacement flagged by CodeQL.

## [0.9.7] - 2026-07-12

### Added

- Added a shared convergence contract to the bundled `goal` and `ralph` workflows: implementation starts from an observable acceptance/contract matrix derived from the literal objective/acceptance criteria (with explicit state/transition/invariant modeling for stateful work), reviewers independently derive adversarial checks from the literal contract before relying on worker receipts or worker-authored tests, reproduced findings require durable regression evidence before they count as resolved, and each review round persists a deduplicated cross-reviewer `consolidated_findings` batch that the next worker turn repairs together instead of one finding per turn. Literal-contract scope controls are preserved throughout, so nothing beyond the user's requirements is forced.
- Added a "Choosing an Execution Shape" section to `docs/workflows.md`: an agent-facing decision ladder covering inline work, inline subagent delegation, direct one-off `task`/`tasks`/`chain` shapes, named/builtin workflows, custom TypeScript workflows, and composed/nested workflows; a six-dimension scoring rubric (structure, verifiability, iteration, risk, duration, isolation) with hard-signal overrides; a "Task queues and software factories" playbook for fire-and-forget requests like "address all open issues" (enumerate and dependency-classify first, fan out independent items as separate per-item workflow runs in bounded waves with per-item worktrees/PRs, compose dependent items into one parent graph that nests proven children, and mix both for clustered queues); and a "Prompting the choice" guide listing the user prompt levers (naming the shape, acceptance criteria, loop wording, evidence requests, scope boundaries, and queue policy) that steer the agent's execution-shape decision.
- Added a "Context-Mode-Aware Prompt Text" section to `docs/workflows.md` documenting that stage prompts must not describe their own context mode, fresh stages must not reference invisible context (prior conversation, sibling stages, graph topology), and forked continuation prompts should send only the delta with a pointer back to guidance already established in the forked history.

### Changed

- Accelerated PR/main CI by running platform-independent validation once on Linux while retaining installed-package Node integration and release-archive smoke coverage on Linux and Windows, and by reusing caller-installed dependencies and package builds during binary assembly. Test suites now have one bounded, observable flake-recovery attempt with preserved logs, environment/resource diagnostics, CI annotations, and no retries for deterministic workflow/release/package/publish gates. Release publication no longer reruns the full PR suite: a protected-default-branch integrity gate proves the release commit is generated from a parent already integrated into `main`, contains exactly the expected version and shrinkwrap material, and pins that immutable SHA across release jobs before preserving all release-specific metadata, docs, native, binary, package, and npm provenance checks.
- Changed bundled `goal` completion to evidence closure rather than reviewer agreement alone: reviewer quorum can only complete the run when no objective-relevant blocking finding from any reviewer in the current round remains unresolved, unresolved findings are recorded in the inspectable reducer decision reason, and the bounded loop still stops at `max_turns` as `needs_human`. Severity labels alone no longer dismiss objective-relevant findings in Goal or Ralph: `required_by_objective` findings block at any priority (P3 included), while `consistent_with_objective` P3 nice-to-haves stay non-blocking.

### Fixed

- Fixed retryable-failure classification across main-chat retry/fallback, workflow stage fallback, and subagent fallback to treat provider usage-limit exhaustion (for example `Codex error: The usage limit has been reached`, plus `usage_limit_reached`/`insufficient_quota`-style codes) as a retryable quota/rate-limit failure, so configured `fallbackModels` advance to the next candidate provider/model instead of dead-ending the turn, stage, or run. Provider messages that flatten the token into free text (for example `usage_limit_reached` or `usage-limit`, matched with space/underscore/hyphen/joined separators) classify the same as the structured codes across all three paths. Nested cause/diagnostic and session-shaped error payloads classify the same way; cancellations, safety refusals, task/tool failures, and unrelated errors remain non-retryable.

## [0.9.7-alpha.1] - 2026-07-12

### Added

- Added a shared convergence contract to the bundled `goal` and `ralph` workflows: implementation starts from an observable acceptance/contract matrix derived from the literal objective/acceptance criteria (with explicit state/transition/invariant modeling for stateful work), reviewers independently derive adversarial checks from the literal contract before relying on worker receipts or worker-authored tests, reproduced findings require durable regression evidence before they count as resolved, and each review round persists a deduplicated cross-reviewer `consolidated_findings` batch that the next worker turn repairs together instead of one finding per turn. Literal-contract scope controls are preserved throughout, so nothing beyond the user's requirements is forced.
- Added a "Choosing an Execution Shape" section to `docs/workflows.md`: an agent-facing decision ladder covering inline work, inline subagent delegation, direct one-off `task`/`tasks`/`chain` shapes, named/builtin workflows, custom TypeScript workflows, and composed/nested workflows; a six-dimension scoring rubric (structure, verifiability, iteration, risk, duration, isolation) with hard-signal overrides; a "Task queues and software factories" playbook for fire-and-forget requests like "address all open issues" (enumerate and dependency-classify first, fan out independent items as separate per-item workflow runs in bounded waves with per-item worktrees/PRs, compose dependent items into one parent graph that nests proven children, and mix both for clustered queues); and a "Prompting the choice" guide listing the user prompt levers (naming the shape, acceptance criteria, loop wording, evidence requests, scope boundaries, and queue policy) that steer the agent's execution-shape decision.
- Added a "Context-Mode-Aware Prompt Text" section to `docs/workflows.md` documenting that stage prompts must not describe their own context mode, fresh stages must not reference invisible context (prior conversation, sibling stages, graph topology), and forked continuation prompts should send only the delta with a pointer back to guidance already established in the forked history.

### Changed

- Changed bundled `goal` completion to evidence closure rather than reviewer agreement alone: reviewer quorum can only complete the run when no objective-relevant blocking finding from any reviewer in the current round remains unresolved, unresolved findings are recorded in the inspectable reducer decision reason, and the bounded loop still stops at `max_turns` as `needs_human`. Severity labels alone no longer dismiss objective-relevant findings in Goal or Ralph: `required_by_objective` findings block at any priority (P3 included), while `consistent_with_objective` P3 nice-to-haves stay non-blocking.

### Fixed

- Fixed retryable-failure classification across main-chat retry/fallback, workflow stage fallback, and subagent fallback to treat provider usage-limit exhaustion (for example `Codex error: The usage limit has been reached`, plus `usage_limit_reached`/`insufficient_quota`-style codes) as a retryable quota/rate-limit failure, so configured `fallbackModels` advance to the next candidate provider/model instead of dead-ending the turn, stage, or run. Provider messages that flatten the token into free text (for example `usage_limit_reached` or `usage-limit`, matched with space/underscore/hyphen/joined separators) classify the same as the structured codes across all three paths. Nested cause/diagnostic and session-shaped error payloads classify the same way; cancellations, safety refusals, task/tool failures, and unrelated errors remain non-retryable.

## [0.9.6] - 2026-07-12

### Changed

- Restored workflow-first Atomic guidance for non-trivial work with verifiable objectives and synchronized help/docs around rich inline TypeScript workflow authoring, including dynamic branching, fan-out, verification, candidate-selection, human-gate, child-workflow, and bounded-loop patterns.
- Documented compositional workflow authoring in model prompts and onboarding/help surfaces, including importing bundled workflows from `@bastani/workflows/builtin`, nesting definitions with `ctx.workflow(...)`, and building deeper reusable workflow graphs within `maxDepth`.
- Restored tool-driven bundled Intercom startup so foreground subagent launches and bridged child session startup no longer connect either session automatically; the model or user must invoke Intercom when coordination is needed.

## [0.9.6-alpha.1] - 2026-07-12

### Changed

- Restored workflow-first Atomic guidance for non-trivial work with verifiable objectives and synchronized help/docs around rich inline TypeScript workflow authoring, including dynamic branching, fan-out, verification, candidate-selection, human-gate, child-workflow, and bounded-loop patterns.
- Documented compositional workflow authoring in model prompts and onboarding/help surfaces, including importing bundled workflows from `@bastani/workflows/builtin`, nesting definitions with `ctx.workflow(...)`, and building deeper reusable workflow graphs within `maxDepth`.
- Restored tool-driven bundled Intercom startup so foreground subagent launches and bridged child session startup no longer connect either session automatically; the model or user must invoke Intercom when coordination is needed.

## [0.9.5] - 2026-07-11

### Breaking Changes

- Hardened the builtin `goal` and `ralph` review contracts against objective-drift failures: review findings now require `objective_alignment`, Goal and Ralph review decisions require `requirements_traceability`, and reviewer approval rejects empty or non-proven traceability. Consumers that parse or synthesize these structured reviewer outputs must emit the new required fields.

### Added

- Added model-capability-aware `max` thinking support across the CLI, settings, SDK/RPC/extension surfaces, Cursor model mapping, workflow stages, and bundled subagents; models still expose only the levels in their own capability maps ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Added request-wide `cost.tiers` support for custom `models.json` models, partial `modelOverrides`, and extension-registered providers, including complete tier validation, aggregate-input threshold selection, inherited-tier preservation for scalar overrides, and explicit tier-array replacement/clearing ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Added main-chat `fallbackModels` support for retryable provider/model failures ([#1418](https://github.com/bastani-inc/atomic/issues/1418)). Users can configure an ordered fallback chain in settings or SDK session options with per-candidate reasoning suffixes such as `:high` and `:xhigh`; normal same-model retry remains first when enabled, fallback switches are recorded as session model changes, and the UI reports fallback progress.
- Added an internal tiered fallback ladder for automatic Verbatim Compaction when the strict `compression_ratio` target is not achievable: threshold and overflow auto-compaction now keep the standard strict planner pass as tier 1, tier 2 can accept a validated below-target result with at least one deletion when projected `tokensAfter` clears the relevant budget (`effectiveInputBudget - reserveTokens` for threshold, effective input budget for overflow), overflow commits from Atomic's internal ladder are gated on fitting the effective input budget even when the strict target is met, overflow-only tier 3 reruns the planner with critical LRU-style protected-entry eligibility for stale task-bearing user/custom/branch-summary context and an effective recent floor of `max(preserve_recent, 5)` across all compactable entries, and overflow-only tier 4 performs deterministic code-level LRU eviction without a model call or API credentials while enforcing the same effective last-5 recent floor until the effective input budget fits or no more safe deletion remains. The fallback tiers remain internal: extension hooks keep their existing shapes, extension-provided deletion requests still bypass the ladder including the overflow budget gate, and no public compaction mode API is exposed.
- Added hard iteration caps to compaction recovery loops: planner provider turns are capped at 50 per planner run (including tool-call turns), planner nudge follow-ups are capped at 50 per planner run, and deterministic overflow eviction is capped at 50 passes, so compaction cannot spin indefinitely and terminal failures report the achieved reduction, deletion count, projected `tokensAfter`, and budget.
- Added immutable `acceptance_criteria` to the builtin `goal` and `ralph` workflows. Goal persists it in the ledger/model-visible projection and final reports; Ralph threads it through research, orchestrator, and reviewer prompts next to the literal objective contract. Orchestrators should pass the original task text when launching follow-up Goal or Ralph runs from reviewer findings.
- Added literal-contract prompt language shared by `goal` and `ralph`, objective-alignment arbitration for reviewer findings, non-blocking treatment for `beyond_objective`/`contradicts_objective` findings, and clause-by-clause requirements traceability so reviewer evidence must map directly back to the objective/acceptance criteria.
- Added attempt-first E2E guidance for `goal` and `ralph`: workers/reviewers must not skip playwright-cli/tmux validation because credentials or auth are merely assumed missing, and skipped E2E must cite the exact attempted commands and observed failure output.

### Changed

- Synced the upstream Pi runtime dependencies (`@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui`) through `^0.80.6` across Atomic and bundled extensions, retained the same consolidated refresh's `lru-cache`, optional `@dbos-inc/dbos-sdk`, `napi`, `napi-derive`, and `tree-sitter` updates, regenerated Bun/Cargo/npm shrinkwrap metadata with mutually consistent 0.80.6 registry integrity, and kept versionless `0.0.0` Atomic manifests. This inherits signed empty Anthropic thinking preservation, request-wide GPT-5.4/5.5 long-context pricing, corrected GPT-5.6 catalogs/backend windows, and upstream retry/provider fixes while preserving Atomic's Copilot/Gemini behavior ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Replaced universal workflow/subagent routing with an intent-first least-orchestration policy across model guidance, help, and documentation. Interactive exploration stays inline, bounded specialist work may use single/chain/parallel subagents, and clearly delegated long-running autonomous work uses named, direct-shape, or custom inline TypeScript workflows when durable execution features add value. Explicit loop and stop-condition requests remain key workflow signals when the user delegates execution, so retries, evidence, and convergence are tracked.
- Explicit bash timeouts now fail before execution when non-finite, non-positive, or greater than Atomic's 3600-second ceiling; omitting the field still uses 300 seconds, and valid fractional values retain Atomic's floor behavior instead of being silently clamped into range ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Changed the bundled `ralph` workflow review fan-out from three reviewers to two (`reviewer-a` and `reviewer-b`), removing `reviewer-c` and its GLM-led model chain while keeping unanimous approval across the remaining reviewers.
- Improved interactive startup responsiveness by keeping footer git watcher setup out of first paint and deferring it until the first input handler is ready, while preserving lazy footer branch reads and post-frame branch-change re-rendering. Deferred extension/resource loading no longer starts on a blind post-paint timer that can freeze live typing; instead, Atomic starts it in the background once input is ready and uses a readiness gate before the first normal prompt/model turn if the background load has not settled. This keeps first-paint/typeahead responsive while ensuring the first interactive prompt sees extension tools, skills, prompt templates, resources, and extension-registered provider/model updates. Built-in slash commands stay available immediately, and bundled extension slash commands expose lightweight metadata for autocomplete before their heavy implementations load; explicitly submitted extension slash commands load the implementation on demand and then route the same command. Explicit provider/model selections stay on the synchronous startup path because they must resolve against extension-registered providers before the session is created. Changelog and first-run notice materialization now happen after the first frame, and startup/input timing probes cover raw-mode enablement, first captured raw key, first frame, input-handler callback installation, and first submit for benchmark/probe runs.
- Documented the built-in extension lazy-startup model: lightweight tools/commands register immediately while MCP connection warmup, workflow discovery, subagent maintenance, and web-access heavy provider loading run in the background or on explicit use.
- Changed interactive Ctrl+C to interrupt the agent when it is busy: a single Ctrl+C now aborts the running agent turn (restoring queued messages to the editor), a running bash command, an active context compaction, or an auto-retry countdown — matching Escape and common CLI muscle memory. When idle, Ctrl+C keeps its previous behavior (first press clears the editor, a quick double-press exits), and the Ctrl+C immediately following an interrupt clears rather than exits. Escape remains the primary interrupt key.
- Simplified first-run onboarding to a one-time verifiable-coding-agent-runtime explanation shown after any What's New notes and directly above the normal input box; Atomic no longer intercepts pasted tasks, saves pre-login seeds, routes first-run input to `goal`/`ralph`, raises reasoning for onboarding, or requires `/chat` to continue normally. The notice now reminds unauthenticated users to run `/login` first, and starting a new session with `/new` clears any rendered first-run notice state from the previous session canvas.
- Hardened the bundled workflows extension's workflow-tool prompt guidance against inline analysis-paralysis drift: the agent must now decide and state the inline-vs-workflow execution mode before its first tool call (reconnaissance explicitly counts as inline execution), budget pre-workflow scoping to a few quick reads that only sharpen the objective and validation criteria, course-correct after roughly ten deliverable-free exploration tool calls (or repeated "let me verify one more thing" loops) by writing findings to a context file and handing off to the best-fit workflow via `reads` (named or user-defined workflows discovered with `action: "list"` first, builtin `goal`/`ralph` as fallbacks when nothing more specific fits), and treat sunk inline research as transferable via files rather than a reason to stay inline. The same "Decide before you explore" and "Course-correct instead of drifting" guidance is mirrored in `docs/workflows.md` under "When to Use Workflows".
- Refreshed the July 2026 builtin workflow and subagent frontier model rosters bundled with Atomic: high-capacity synthesis, planning, debugging, and review paths now lead or fall back through Claude Fable 5 `:xhigh`, GPT-5.5 `:xhigh`, Opus 4.8 long-context `:xhigh`, GLM-5.2, and the valid OpenRouter Fugu Ultra mirror while keeping dominated or unsupported model IDs out of shipped chains.

### Fixed

- Fixed CLI resolution of unknown/custom model IDs with a recognized `:<thinking>` suffix so the suffix is applied as the thinking level instead of leaking into the synthesized model ID, while preserving registered and unrecognized colon-bearing model IDs.
- Fixed bundled MCP readiness so failed background initialization is retryable and single-flight within the active session, stale retries cannot publish after shutdown, replacement startup waits for retired initializer/state/OAuth cleanup, and proxy/direct readiness plus lazy-connection and OAuth waits use caller-local cancellation without stopping shared producers. Direct executors reject old-state work on success and failure paths after replacement and close stale newly opened Apps views, SDK resource/tool requests receive the invocation signal, and UI-backed MCP Apps calls emit one terminal cancellation before teardown while preserving the exact host reason over SDK/notification failures.
- Fixed bundled web-access and Intercom lazy wrappers to retire session-scoped candidates on shutdown, reject calls spanning teardown, and initialize fresh state after restart; host cancellation after web provider/curator execution now preserves its exact abort reason while explicit curator user cancellation remains a normal result.
- Fixed bundled web-access lazy tools failing during native Bun heavy-module loading by resolving compatibility helpers from the canonical installed `@earendil-works/pi-ai/compat` package ([#1728](https://github.com/bastani-inc/atomic/issues/1728)).
- Fixed stale pre-compaction usage estimates after a newer inserted prefix, normalized lax null or omitted content at extension, custom-message, and restored-session boundaries, and kept Atomic's Verbatim Compaction custom-message budgeting semantics intact ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed `models.json` `modelOverrides` so matching extension-registered provider models receive configured names, thinking maps, compatibility fields, context settings, and merged headers without replacing Atomic's dynamic Copilot/context-window behavior; normal CLI startup now loads layered legacy `.pi` and primary `.atomic` files, disjoint provider/model entries survive, and an exact primary entry replaces the complete legacy override with source-correct header precedence and `{}` restoration of built-in values ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed project context traversal at Windows drive roots, surfaced credential persistence failures without mutating in-memory auth first, skipped unauthenticated saved defaults, preserved missing saved IDs for authenticated custom OpenAI-compatible providers, prevented removed static catalog model IDs from being synthesized during session restore, and cleared cached label timestamps when starting a new session ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed missing exact `--session-id` handling to warn before creating the requested session, ignored rapid duplicate fork-menu selections, and kept visible custom messages emitted during streaming ordered before the live assistant row ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed standalone clipboard image support by falling back to xclip after an empty native result and packaging each matching 0.3.9 native binding beside the wrapper in Atomic's split binary archives; caller-relative `TMPDIR` values are canonicalized before directory changes, and `--skip-deps` tolerates an absent optional clipboard wrapper while strict release builds remain loud ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed Anthropic/GitHub Copilot signed-thinking replay after Verbatim Compaction by validating provider-visible logical assistant tool-use turns instead of isolated messages: the current final turn retains every `thinking`/`redacted_thinking` entry, completed historical turns retain all or omit all signed entries, complete grep batches and deterministic overflow eviction use the same invariant, and unsafe persisted plans heal in memory with exact retained blocks, signatures, and paired tool results restored without rewriting session JSONL. Provider-visible user/custom inputs, included bash executions, and non-empty branch summaries establish boundaries, task anchors, recent windows, and finite transcript token totals; provider-omitted empty or whitespace-only user/custom inputs and empty or malformed branch summaries do not, while whitespace-only branch summaries remain visible through their wrapper. Raw block visibility is retained separately from transcript display projections, future unknown typed blocks fail visible with a conservative token estimate, malformed siblings are transiently filtered before provider calls, and stale user-image deletion cannot remove the final provider-visible task. Raw `redacted_thinking` blocks are normalized in the transient, non-mutating LLM-compatible messages returned by `convertToLlm`, so retained opaque data serializes exactly without mutating durable history.
- Fixed bundled foreground subagent coordination so `intercom.ask` and `contact_supervisor need_decision` surface while the parent tool is active, detach only their exact child, resume through threaded replies, and expose the retained detached-to-terminal child status and actual output through public status queries; progress/send and background execution remain nonblocking and unchanged ([#1727](https://github.com/bastani-inc/atomic/issues/1727)).
- Register bridged foreground/background children with bundled Intercom before agent work and lazily gate interactive foreground launches on parent broker readiness, while disabled, unavailable, unused-parent, and management-only paths remain lazy and broker-free.
- Fixed Windows native filesystem watchers to canonicalize watched paths before calling `fs.watch`, reject unresolved 8.3 short-name paths, and fall back to polling for unsafe watcher paths. This prevents libuv fs-event assertion crashes when temp, session, footer, or theme paths contain short-name components such as `USERNA~1`.
- Fixed responses truncated at the output-token cap (`stopReason: "length"`) dead-ending the model's work with a "maximum output token limit" error when the context was still below the auto-compaction threshold. Previously auto-continuation only happened as a side effect of threshold compaction, so a length truncation with input room to spare left the task half-finished. Length-truncated turns now continue directly without compacting: the incomplete assistant is removed from retry context and the generation resumes automatically so the model finishes where it left off, bounded by a small consecutive-continuation cap so a turn that keeps exceeding the per-turn output cap still terminates. Compaction-driven `willRetry: true` continuations are now consistently tracked through the normal prompt lifecycle, so `AgentSession.prompt()` waits for threshold length-stop continuation before resolving instead of treating it as fire-and-forget.
- Fixed auto-compaction after OpenAI Responses output-budget underflow errors such as `Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 1 instead.` Atomic now classifies that specific context-pressure failure as retry-worthy when the live context crosses the compaction threshold, removes the empty error assistant from retry context after compaction, and automatically continues from the preceding work anchor instead of waiting for the user to type `Continue`. Generic `invalid_request_body` errors such as malformed tool schemas are still not auto-retried, OpenAI Responses payload sanitization now prevents `max_output_tokens` values below the provider minimum of 16 from being sent, and repeated output-budget underflow continuation is capped at one compact-and-retry attempt so an unrecoverable context terminates visibly instead of looping or stalling.
- Fixed bundled workflow durable resume for reusable `git_worktree_dir` worktrees so resumed runs reuse the original invocation repository/cwd and report slow Git subprocess timeouts as Git timeouts instead of repository-detection failures.
- Removed the queued-message restore success status from the interactive UI so restoring queued messages silently returns them to the editor without showing transient restore copy.
- Fixed bundled MCP proxy metadata paths so explicit cold-cache `search`, `describe`, and server-list requests hydrate lazy server metadata on demand without reconnecting all lazy servers during startup.
- Fixed provider context-window overflow recovery so an exhausted overflow auto-compaction attempt emits an explicit unresolved-overflow signal instead of silently leaving callers to retry the same model, and planner calls that themselves overflow now degrade through the deterministic overflow-eviction ladder rather than throwing before non-model reduction can run. Planner overflow is now recognized whether it is returned as an assistant error message or thrown by the provider stream before `agent.prompt()` completes, so overflow recovery skips the critical planner retry and goes directly to deterministic non-model reduction in both forms.
- Fixed overflow auto-compaction retry reliability so `AgentSession.prompt()` now waits for the `willRetry: true` post-compaction continuation before resolving. Late unresolved-overflow signals from that continuation are observed by workflow callers before they can mark the original prompt successful.
- Fixed normal interactive TTY startup so typing before the prompt box is fully mounted is captured instead of being invisibly blocked by deferred resource initialization. Atomic now starts a short-lived raw input buffer only on the existing deferred-startup fast path, keeps it active until the TUI input handler is mounted, replays draft text into the editor, queues only ordinary Enter-submitted prompts for the prompt loop, and preserves command-like early submissions such as `/settings` or `!pwd` as standalone editor submissions so normal command routing handles them after mount. Once a command-like startup submission is captured, later captured submissions wait behind it and replay in original input order after that command is routed, preventing a later ordinary prompt from leapfrogging the earlier command without merging commands and following prompts into one draft. The capture now restores terminal raw mode across early startup exits/signals and ignores split escape sequences so partial arrow-key/protocol bytes never become draft text. Trust prompts, explicit resource flags, metadata commands, non-TTY modes, and explicit provider/model selection stay on the synchronous startup path.
- Fixed the bundled intercom extension to keep broker runtime files under the active Atomic agent directory, including custom `ATOMIC_CODING_AGENT_DIR` values and the legacy `PI_CODING_AGENT_DIR` alias, while documenting the `~/.atomic/agent/intercom/` primary path and `~/.pi/agent/intercom/` fallback. The default pi-compatible `npx --no-install tsx` broker sentinel is now hardened to launch through the current runtime (`process.execPath`): Node-based installs use a resolved `tsx` CLI with a bundled `jiti` fallback, Bun source-checkout runs use the current Bun executable directly, and standalone Atomic Bun binaries use a narrow internal split-launcher broker handoff, so default startup does not depend on `npx`, `tsx`, or `bun` being on `PATH`; explicit custom broker configs remain supported.
- Fixed GitHub Copilot Claude/Anthropic Messages streams that cleanly report a terminal stop reason but omit the required `message_stop` SSE event. Atomic now adds that single terminal event only for closed, non-error Copilot `/v1/messages` event streams before provider parsing, including complete final SSE frames that reach EOF without a trailing blank-line separator and GitHub Enterprise/GHE tenant routing hosts such as `copilot-api.<enterprise>.ghe.com`, while leaving malformed, truncated, already well-formed, non-Copilot, look-alike host, non-SSE, Gemini, and OpenAI-style streams to the normal parser/retry behavior.
- Fixed bundled workflow durable resume hydration so replayed parallel reviewer fanout preserves branch structure and completed workflow stages restore persisted summaries, durations, session/model metadata, and checkpoint contents in status and graph views.
- Fixed a noticeable delay before the working spinner appeared after submitting a prompt: the interactive input loop now mounts the spinner immediately on submit (respecting extension `workingVisible` suppression) and yields once so it paints before prompt preflight — extension input hooks, template/skill expansion, auth and compaction checks, and deferred startup completion — runs. Previously the spinner was created only when the agent emitted `agent_start`, so the status row stayed empty during preflight, making Ctrl+C feel unresponsive until the spinner finally appeared. Submissions that resolve without starting an agent turn (e.g. extension slash-commands) clear the pre-shown spinner when idle so it never lingers.
- Fixed post-context-compaction provider requests so retained pre-compaction assistant usage is scrubbed from the provider-bound context clone. This keeps durable billing history intact while preventing stale high token counts from shrinking `max_output_tokens` to an invalid one-token budget on the first turn after compaction.
- Fixed auto-compaction continuation for OpenAI Responses providers by normalizing replayed `function_call.id` values to valid `fc_*` item identifiers while preserving `call_id` pairing. This prevents the opaque `400 {"code":"invalid_request_body"}` failure that could appear immediately after compaction even though a manual `Continue` message succeeded.
- Fixed auto-compaction stalling after a response reaches the maximum output-token limit: length-stopped assistant turns now compact as incomplete work, remove the truncated assistant from retry context, and automatically continue without requiring the user to type `Continue` ([#1662](https://github.com/bastani-inc/atomic/issues/1662)).
- Reduced Windows compiled-binary interactive cold-start latency by shipping release archives with a small launcher plus a sidecar app bundle instead of one monolithic executable, and by extending the deferred TUI fast path beyond extension module imports. Normal interactive TTY startup now paints and installs the input loop before bundled package/resource discovery scans skills, prompts, themes, context files, and system-prompt files. Deferred loading now uses async filesystem discovery and async file reads for package resources, skills, prompts, themes, and extension-discovered resources, with cooperative yields so Enter, Ctrl+C, rendering, and the normal prompt spinner remain responsive when a submitted prompt needs resources. Startup no longer shows a resource-loading spinner before the user submits a prompt; explicit resource flags, system-prompt inputs, unknown extension flags, model/provider selection, metadata commands, non-TTY modes, and unresolved project-trust prompts stay on the synchronous path.
- Fixed Windows compiled-binary keyboard responsiveness by avoiding invisible typeahead buffering in normal interactive startup, keeping terminal input attached to the visible editor, handling Ctrl+C globally with a SIGINT fallback, bounding Ctrl+C shutdown input draining to 250ms, and packaging the Windows console-mode helper used by the TUI for virtual-terminal input.
- Fixed the Anthropic subscription extra-usage warning racing ahead of the startup `RESOURCES` disclosure on the deferred TUI fast path: the warning is now held until after the resource summary line renders (at deferred-startup completion, agent-end disclosure flushes, or the post-prompt flush), so the `RESOURCES` line always appears first.
- Fixed deferred-startup TUI chat ordering so `RESOURCES`, changelog, update/package/tmux/subscription startup notices, and queued user prompts render in request order instead of completion order. Startup notices now use a stable block above session messages, queued inputs wait for their later `message_start`, and failed pre-prompt echoes are removed from the chat.
- Fixed Windows release archives to avoid Bun's `--bytecode` standalone executable startup crash and every runtime path that decodes the split-launcher sidecar's macOS build-machine `import.meta.url` on Windows. Windows binaries remain compiled standalone launchers but now ship the source payload instead of embedded bytecode until Bun's Windows bytecode-alignment fix is available. A centralized split-launcher helper (`ATOMIC_CODING_AGENT` + executable path detection) now resolves package assets, built-in package discovery, and — critically — the jiti resolution base next to `atomic.exe`, so the bundled built-in extensions (`mcp`, `web-access`, `intercom`, `cursor`, `workflows`, `subagents`) all load on Windows instead of failing with `File URL path must be an absolute path`.
- Fixed deferred TUI extension loading so the editor keeps accepting and echoing keystrokes after first paint: startup now yields cooperatively between extension/resource-loading chunks, preserves text typed during completion, and safely queues Enter submissions made before the main prompt loop is ready.
- Fixed a warm-start TUI first-paint regression where persisted `enabledModels` or `--models` patterns forced all extensions to load synchronously before the first frame. Interactive startup now keeps the deferred-extension fast path and reapplies the model scope after extensions finish loading, preserving extension-registered provider matches and unmatched-pattern warnings without blocking paint.
- Fixed project trust prompts for bare projects that only create inert `.atomic/` or `.pi/` state: state-only directories such as `todos/` and `sessions/` no longer count as trust inputs or disable deferred extension startup, while trust-requiring config continues to prompt until the user makes an explicit persistent decision and implicit trust is persisted only by the `/reload` path.
- Fixed a security regression that could silently persist project trust at shutdown or deferred-startup completion after trust-requiring config appeared mid-session; Atomic now preserves the startup trust prompt unless the user explicitly saves a decision or the existing `/reload` implicit-trust flow applies.
- Fixed BOM-prefixed JSON state files being treated as parse failures: settings and project trust JSON readers now strip a leading UTF-8 BOM before parsing, so Windows-authored `settings.json` and `trust.json` files load the same as BOM-less files instead of silently ignoring user settings.
- Fixed the bundled workflows documentation to reflect that attached workflow stage chats render live `subagent` widgets for single, parallel, and chain calls, keep them live across attach/re-attach cycles, and let Ctrl+O expand live detail for every child. ([#1643](https://github.com/bastani-inc/atomic/issues/1643))
- Fixed a Windows `waitForChildProcess` exit-code race where the process-alive poll could report a fast-dying child as gone before Node emitted the real `exit` event, causing bash commands such as `exit 1` to be reported as successful with a fabricated exit code 0 ([#1647](https://github.com/bastani-inc/atomic/issues/1647)).
- Fixed overflow auto-compaction silently no-oping when planner authentication was unavailable or when the current branch has no preparable compactable transcript: overflow recovery now either skips model tiers and runs deterministic no-auth LRU eviction directly through the existing validation pipeline, or surfaces a terminal error that nothing more was safely deletable.
- Fixed feasible partial compaction results being discarded solely because they missed the strict ratio target: automatic threshold and overflow compaction now commit validated below-target deletions when their projected `tokensAfter` clears the trigger/budget boundary, including partial deletion state salvaged after a provider context-overflow error.

## [0.9.5-alpha.10] - 2026-07-11

### Added

- Added model-capability-aware `max` thinking support across the CLI, settings, SDK/RPC/extension surfaces, Cursor model mapping, workflow stages, and bundled subagents; models still expose only the levels in their own capability maps ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Added request-wide `cost.tiers` support for custom `models.json` models, partial `modelOverrides`, and extension-registered providers, including complete tier validation, aggregate-input threshold selection, inherited-tier preservation for scalar overrides, and explicit tier-array replacement/clearing ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).

### Changed

- Synced the upstream Pi runtime dependencies (`@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui`) through `^0.80.6` across Atomic and bundled extensions, retained the same consolidated refresh's `lru-cache`, optional `@dbos-inc/dbos-sdk`, `napi`, `napi-derive`, and `tree-sitter` updates, regenerated Bun/Cargo/npm shrinkwrap metadata with mutually consistent 0.80.6 registry integrity, and kept versionless `0.0.0` Atomic manifests. This inherits signed empty Anthropic thinking preservation, request-wide GPT-5.4/5.5 long-context pricing, corrected GPT-5.6 catalogs/backend windows, and upstream retry/provider fixes while preserving Atomic's Copilot/Gemini behavior ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Replaced universal workflow/subagent routing with an intent-first least-orchestration policy across model guidance, help, and documentation. Interactive exploration stays inline, bounded specialist work may use single/chain/parallel subagents, and clearly delegated long-running autonomous work uses named, direct-shape, or custom inline TypeScript workflows when durable execution features add value. Explicit loop and stop-condition requests remain key workflow signals when the user delegates execution, so retries, evidence, and convergence are tracked.

- Explicit bash timeouts now fail before execution when non-finite, non-positive, or greater than Atomic's 3600-second ceiling; omitting the field still uses 300 seconds, and valid fractional values retain Atomic's floor behavior instead of being silently clamped into range ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).

- Changed the bundled `ralph` workflow review fan-out from three reviewers to two (`reviewer-a` and `reviewer-b`), removing `reviewer-c` and its GLM-led model chain while keeping unanimous approval across the remaining reviewers.

### Fixed

- Fixed CLI resolution of unknown/custom model IDs with a recognized `:<thinking>` suffix so the suffix is applied as the thinking level instead of leaking into the synthesized model ID, while preserving registered and unrecognized colon-bearing model IDs.
- Fixed bundled MCP readiness so failed background initialization is retryable and single-flight within the active session, stale retries cannot publish after shutdown, replacement startup waits for retired initializer/state/OAuth cleanup, and proxy/direct readiness plus lazy-connection and OAuth waits use caller-local cancellation without stopping shared producers. Direct executors reject old-state work on success and failure paths after replacement and close stale newly opened Apps views, SDK resource/tool requests receive the invocation signal, and UI-backed MCP Apps calls emit one terminal cancellation before teardown while preserving the exact host reason over SDK/notification failures.
- Fixed bundled web-access and Intercom lazy wrappers to retire session-scoped candidates on shutdown, reject calls spanning teardown, and initialize fresh state after restart; host cancellation after web provider/curator execution now preserves its exact abort reason while explicit curator user cancellation remains a normal result.
- Fixed bundled web-access lazy tools failing during native Bun heavy-module loading by resolving compatibility helpers from the canonical installed `@earendil-works/pi-ai/compat` package ([#1728](https://github.com/bastani-inc/atomic/issues/1728)).
- Fixed stale pre-compaction usage estimates after a newer inserted prefix, normalized lax null or omitted content at extension, custom-message, and restored-session boundaries, and kept Atomic's Verbatim Compaction custom-message budgeting semantics intact ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed `models.json` `modelOverrides` so matching extension-registered provider models receive configured names, thinking maps, compatibility fields, context settings, and merged headers without replacing Atomic's dynamic Copilot/context-window behavior; normal CLI startup now loads layered legacy `.pi` and primary `.atomic` files, disjoint provider/model entries survive, and an exact primary entry replaces the complete legacy override with source-correct header precedence and `{}` restoration of built-in values ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed project context traversal at Windows drive roots, surfaced credential persistence failures without mutating in-memory auth first, skipped unauthenticated saved defaults, preserved missing saved IDs for authenticated custom OpenAI-compatible providers, prevented removed static catalog model IDs from being synthesized during session restore, and cleared cached label timestamps when starting a new session ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed missing exact `--session-id` handling to warn before creating the requested session, ignored rapid duplicate fork-menu selections, and kept visible custom messages emitted during streaming ordered before the live assistant row ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed standalone clipboard image support by falling back to xclip after an empty native result and packaging each matching 0.3.9 native binding beside the wrapper in Atomic's split binary archives; caller-relative `TMPDIR` values are canonicalized before directory changes, and `--skip-deps` tolerates an absent optional clipboard wrapper while strict release builds remain loud ([#1703](https://github.com/bastani-inc/atomic/issues/1703)).
- Fixed Anthropic/GitHub Copilot signed-thinking replay after Verbatim Compaction by validating provider-visible logical assistant tool-use turns instead of isolated messages: the current final turn retains every `thinking`/`redacted_thinking` entry, completed historical turns retain all or omit all signed entries, complete grep batches and deterministic overflow eviction use the same invariant, and unsafe persisted plans heal in memory with exact retained blocks, signatures, and paired tool results restored without rewriting session JSONL. Provider-visible user/custom inputs, included bash executions, and non-empty branch summaries establish boundaries, task anchors, recent windows, and finite transcript token totals; provider-omitted empty or whitespace-only user/custom inputs and empty or malformed branch summaries do not, while whitespace-only branch summaries remain visible through their wrapper. Raw block visibility is retained separately from transcript display projections, future unknown typed blocks fail visible with a conservative token estimate, malformed siblings are transiently filtered before provider calls, and stale user-image deletion cannot remove the final provider-visible task. Raw `redacted_thinking` blocks are normalized in the transient, non-mutating LLM-compatible messages returned by `convertToLlm`, so retained opaque data serializes exactly without mutating durable history.
- Fixed bundled foreground subagent coordination so `intercom.ask` and `contact_supervisor need_decision` surface while the parent tool is active, detach only their exact child, resume through threaded replies, and expose the retained detached-to-terminal child status and actual output through public status queries; progress/send and background execution remain nonblocking and unchanged ([#1727](https://github.com/bastani-inc/atomic/issues/1727)).
- Register bridged foreground/background children with bundled Intercom before agent work and lazily gate interactive foreground launches on parent broker readiness, while disabled, unavailable, unused-parent, and management-only paths remain lazy and broker-free.

## [0.9.5-alpha.9] - 2026-07-09

### Changed

- Improved interactive startup responsiveness by keeping footer git watcher setup out of first paint and deferring it until the first input handler is ready, while preserving lazy footer branch reads and post-frame branch-change re-rendering. Deferred extension/resource loading no longer starts on a blind post-paint timer that can freeze live typing; instead, Atomic starts it in the background once input is ready and uses a readiness gate before the first normal prompt/model turn if the background load has not settled. This keeps first-paint/typeahead responsive while ensuring the first interactive prompt sees extension tools, skills, prompt templates, resources, and extension-registered provider/model updates. Built-in slash commands stay available immediately, and bundled extension slash commands expose lightweight metadata for autocomplete before their heavy implementations load; explicitly submitted extension slash commands load the implementation on demand and then route the same command. Explicit provider/model selections stay on the synchronous startup path because they must resolve against extension-registered providers before the session is created. Changelog and first-run notice materialization now happen after the first frame, and startup/input timing probes cover raw-mode enablement, first captured raw key, first frame, input-handler callback installation, and first submit for benchmark/probe runs.
- Documented the built-in extension lazy-startup model: lightweight tools/commands register immediately while MCP connection warmup, workflow discovery, subagent maintenance, and web-access heavy provider loading run in the background or on explicit use.

### Fixed

- Fixed Windows native filesystem watchers to canonicalize watched paths before calling `fs.watch`, reject unresolved 8.3 short-name paths, and fall back to polling for unsafe watcher paths. This prevents libuv fs-event assertion crashes when temp, session, footer, or theme paths contain short-name components such as `USERNA~1`.
- Fixed responses truncated at the output-token cap (`stopReason: "length"`) dead-ending the model's work with a "maximum output token limit" error when the context was still below the auto-compaction threshold. Previously auto-continuation only happened as a side effect of threshold compaction, so a length truncation with input room to spare left the task half-finished. Length-truncated turns now continue directly without compacting: the incomplete assistant is removed from retry context and the generation resumes automatically so the model finishes where it left off, bounded by a small consecutive-continuation cap so a turn that keeps exceeding the per-turn output cap still terminates. Compaction-driven `willRetry: true` continuations are now consistently tracked through the normal prompt lifecycle, so `AgentSession.prompt()` waits for threshold length-stop continuation before resolving instead of treating it as fire-and-forget.
- Fixed auto-compaction after OpenAI Responses output-budget underflow errors such as `Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 1 instead.` Atomic now classifies that specific context-pressure failure as retry-worthy when the live context crosses the compaction threshold, removes the empty error assistant from retry context after compaction, and automatically continues from the preceding work anchor instead of waiting for the user to type `Continue`. Generic `invalid_request_body` errors such as malformed tool schemas are still not auto-retried, OpenAI Responses payload sanitization now prevents `max_output_tokens` values below the provider minimum of 16 from being sent, and repeated output-budget underflow continuation is capped at one compact-and-retry attempt so an unrecoverable context terminates visibly instead of looping or stalling.
- Fixed bundled workflow durable resume for reusable `git_worktree_dir` worktrees so resumed runs reuse the original invocation repository/cwd and report slow Git subprocess timeouts as Git timeouts instead of repository-detection failures.
- Removed the queued-message restore success status from the interactive UI so restoring queued messages silently returns them to the editor without showing transient restore copy.
- Fixed bundled MCP proxy metadata paths so explicit cold-cache `search`, `describe`, and server-list requests hydrate lazy server metadata on demand without reconnecting all lazy servers during startup.

## [0.9.5-alpha.8] - 2026-07-08

### Added

- Added main-chat `fallbackModels` support for retryable provider/model failures ([#1418](https://github.com/bastani-inc/atomic/issues/1418)). Users can configure an ordered fallback chain in settings or SDK session options with per-candidate reasoning suffixes such as `:high` and `:xhigh`; normal same-model retry remains first when enabled, fallback switches are recorded as session model changes, and the UI reports fallback progress.

### Fixed

- Fixed provider context-window overflow recovery so an exhausted overflow auto-compaction attempt emits an explicit unresolved-overflow signal instead of silently leaving callers to retry the same model, and planner calls that themselves overflow now degrade through the deterministic overflow-eviction ladder rather than throwing before non-model reduction can run. Planner overflow is now recognized whether it is returned as an assistant error message or thrown by the provider stream before `agent.prompt()` completes, so overflow recovery skips the critical planner retry and goes directly to deterministic non-model reduction in both forms.
- Fixed overflow auto-compaction retry reliability so `AgentSession.prompt()` now waits for the `willRetry: true` post-compaction continuation before resolving. Late unresolved-overflow signals from that continuation are observed by workflow callers before they can mark the original prompt successful.
- Fixed normal interactive TTY startup so typing before the prompt box is fully mounted is captured instead of being invisibly blocked by deferred resource initialization. Atomic now starts a short-lived raw input buffer only on the existing deferred-startup fast path, keeps it active until the TUI input handler is mounted, replays draft text into the editor, queues only ordinary Enter-submitted prompts for the prompt loop, and preserves command-like early submissions such as `/settings` or `!pwd` as standalone editor submissions so normal command routing handles them after mount. Once a command-like startup submission is captured, later captured submissions wait behind it and replay in original input order after that command is routed, preventing a later ordinary prompt from leapfrogging the earlier command without merging commands and following prompts into one draft. The capture now restores terminal raw mode across early startup exits/signals and ignores split escape sequences so partial arrow-key/protocol bytes never become draft text. Trust prompts, explicit resource flags, metadata commands, non-TTY modes, and explicit provider/model selection stay on the synchronous startup path.
- Fixed the bundled intercom extension to keep broker runtime files under the active Atomic agent directory, including custom `ATOMIC_CODING_AGENT_DIR` values and the legacy `PI_CODING_AGENT_DIR` alias, while documenting the `~/.atomic/agent/intercom/` primary path and `~/.pi/agent/intercom/` fallback. The default pi-compatible `npx --no-install tsx` broker sentinel is now hardened to launch through the current runtime (`process.execPath`): Node-based installs use a resolved `tsx` CLI with a bundled `jiti` fallback, Bun source-checkout runs use the current Bun executable directly, and standalone Atomic Bun binaries use a narrow internal split-launcher broker handoff, so default startup does not depend on `npx`, `tsx`, or `bun` being on `PATH`; explicit custom broker configs remain supported.
- Fixed GitHub Copilot Claude/Anthropic Messages streams that cleanly report a terminal stop reason but omit the required `message_stop` SSE event. Atomic now adds that single terminal event only for closed, non-error Copilot `/v1/messages` event streams before provider parsing, including complete final SSE frames that reach EOF without a trailing blank-line separator and GitHub Enterprise/GHE tenant routing hosts such as `copilot-api.<enterprise>.ghe.com`, while leaving malformed, truncated, already well-formed, non-Copilot, look-alike host, non-SSE, Gemini, and OpenAI-style streams to the normal parser/retry behavior.
- Fixed bundled workflow durable resume hydration so replayed parallel reviewer fanout preserves branch structure and completed workflow stages restore persisted summaries, durations, session/model metadata, and checkpoint contents in status and graph views.

## [0.9.5-alpha.7] - 2026-07-07

### Changed

- Changed interactive Ctrl+C to interrupt the agent when it is busy: a single Ctrl+C now aborts the running agent turn (restoring queued messages to the editor), a running bash command, an active context compaction, or an auto-retry countdown — matching Escape and common CLI muscle memory. When idle, Ctrl+C keeps its previous behavior (first press clears the editor, a quick double-press exits), and the Ctrl+C immediately following an interrupt clears rather than exits. Escape remains the primary interrupt key.

### Fixed

- Fixed a noticeable delay before the working spinner appeared after submitting a prompt: the interactive input loop now mounts the spinner immediately on submit (respecting extension `workingVisible` suppression) and yields once so it paints before prompt preflight — extension input hooks, template/skill expansion, auth and compaction checks, and deferred startup completion — runs. Previously the spinner was created only when the agent emitted `agent_start`, so the status row stayed empty during preflight, making Ctrl+C feel unresponsive until the spinner finally appeared. Submissions that resolve without starting an agent turn (e.g. extension slash-commands) clear the pre-shown spinner when idle so it never lingers.
- Fixed post-context-compaction provider requests so retained pre-compaction assistant usage is scrubbed from the provider-bound context clone. This keeps durable billing history intact while preventing stale high token counts from shrinking `max_output_tokens` to an invalid one-token budget on the first turn after compaction.
- Fixed auto-compaction continuation for OpenAI Responses providers by normalizing replayed `function_call.id` values to valid `fc_*` item identifiers while preserving `call_id` pairing. This prevents the opaque `400 {"code":"invalid_request_body"}` failure that could appear immediately after compaction even though a manual `Continue` message succeeded.
- Fixed auto-compaction stalling after a response reaches the maximum output-token limit: length-stopped assistant turns now compact as incomplete work, remove the truncated assistant from retry context, and automatically continue without requiring the user to type `Continue` ([#1662](https://github.com/bastani-inc/atomic/issues/1662)).
- Reduced Windows compiled-binary interactive cold-start latency by shipping release archives with a small launcher plus a sidecar app bundle instead of one monolithic executable, and by extending the deferred TUI fast path beyond extension module imports. Normal interactive TTY startup now paints and installs the input loop before bundled package/resource discovery scans skills, prompts, themes, context files, and system-prompt files. Deferred loading now uses async filesystem discovery and async file reads for package resources, skills, prompts, themes, and extension-discovered resources, with cooperative yields so Enter, Ctrl+C, rendering, and the normal prompt spinner remain responsive when a submitted prompt needs resources. Startup no longer shows a resource-loading spinner before the user submits a prompt; explicit resource flags, system-prompt inputs, unknown extension flags, model/provider selection, metadata commands, non-TTY modes, and unresolved project-trust prompts stay on the synchronous path.
- Fixed Windows compiled-binary keyboard responsiveness by avoiding invisible typeahead buffering in normal interactive startup, keeping terminal input attached to the visible editor, handling Ctrl+C globally with a SIGINT fallback, bounding Ctrl+C shutdown input draining to 250ms, and packaging the Windows console-mode helper used by the TUI for virtual-terminal input.
- Fixed the Anthropic subscription extra-usage warning racing ahead of the startup `RESOURCES` disclosure on the deferred TUI fast path: the warning is now held until after the resource summary line renders (at deferred-startup completion, agent-end disclosure flushes, or the post-prompt flush), so the `RESOURCES` line always appears first.
- Fixed deferred-startup TUI chat ordering so `RESOURCES`, changelog, update/package/tmux/subscription startup notices, and queued user prompts render in request order instead of completion order. Startup notices now use a stable block above session messages, queued inputs wait for their later `message_start`, and failed pre-prompt echoes are removed from the chat.
- Fixed Windows release archives to avoid Bun's `--bytecode` standalone executable startup crash and every runtime path that decodes the split-launcher sidecar's macOS build-machine `import.meta.url` on Windows. Windows binaries remain compiled standalone launchers but now ship the source payload instead of embedded bytecode until Bun's Windows bytecode-alignment fix is available. A centralized split-launcher helper (`ATOMIC_CODING_AGENT` + executable path detection) now resolves package assets, built-in package discovery, and — critically — the jiti resolution base next to `atomic.exe`, so the bundled built-in extensions (`mcp`, `web-access`, `intercom`, `cursor`, `workflows`, `subagents`) all load on Windows instead of failing with `File URL path must be an absolute path`.

## [0.9.5-alpha.6] - 2026-07-06

### Fixed

- Fixed deferred TUI extension loading so the editor keeps accepting and echoing keystrokes after first paint: startup now yields cooperatively between extension/resource-loading chunks, preserves text typed during completion, and safely queues Enter submissions made before the main prompt loop is ready.
- Fixed a warm-start TUI first-paint regression where persisted `enabledModels` or `--models` patterns forced all extensions to load synchronously before the first frame. Interactive startup now keeps the deferred-extension fast path and reapplies the model scope after extensions finish loading, preserving extension-registered provider matches and unmatched-pattern warnings without blocking paint.
- Fixed project trust prompts for bare projects that only create inert `.atomic/` or `.pi/` state: state-only directories such as `todos/` and `sessions/` no longer count as trust inputs or disable deferred extension startup, while trust-requiring config continues to prompt until the user makes an explicit persistent decision and implicit trust is persisted only by the `/reload` path.
- Fixed a security regression that could silently persist project trust at shutdown or deferred-startup completion after trust-requiring config appeared mid-session; Atomic now preserves the startup trust prompt unless the user explicitly saves a decision or the existing `/reload` implicit-trust flow applies.
- Fixed BOM-prefixed JSON state files being treated as parse failures: settings and project trust JSON readers now strip a leading UTF-8 BOM before parsing, so Windows-authored `settings.json` and `trust.json` files load the same as BOM-less files instead of silently ignoring user settings.
- Fixed the bundled workflows documentation to reflect that attached workflow stage chats render live `subagent` widgets for single, parallel, and chain calls, keep them live across attach/re-attach cycles, and let Ctrl+O expand live detail for every child. ([#1643](https://github.com/bastani-inc/atomic/issues/1643))
- Fixed a Windows `waitForChildProcess` exit-code race where the process-alive poll could report a fast-dying child as gone before Node emitted the real `exit` event, causing bash commands such as `exit 1` to be reported as successful with a fabricated exit code 0 ([#1647](https://github.com/bastani-inc/atomic/issues/1647)).

## [0.9.5-alpha.5] - 2026-07-06

### Added

- Added an internal tiered fallback ladder for automatic Verbatim Compaction when the strict `compression_ratio` target is not achievable: threshold and overflow auto-compaction now keep the standard strict planner pass as tier 1, tier 2 can accept a validated below-target result with at least one deletion when projected `tokensAfter` clears the relevant budget (`effectiveInputBudget - reserveTokens` for threshold, effective input budget for overflow), overflow commits from Atomic's internal ladder are gated on fitting the effective input budget even when the strict target is met, overflow-only tier 3 reruns the planner with critical LRU-style protected-entry eligibility for stale task-bearing user/custom/branch-summary context and an effective recent floor of `max(preserve_recent, 5)` across all compactable entries, and overflow-only tier 4 performs deterministic code-level LRU eviction without a model call or API credentials while enforcing the same effective last-5 recent floor until the effective input budget fits or no more safe deletion remains. The fallback tiers remain internal: extension hooks keep their existing shapes, extension-provided deletion requests still bypass the ladder including the overflow budget gate, and no public compaction mode API is exposed.
- Added hard iteration caps to compaction recovery loops: planner provider turns are capped at 50 per planner run (including tool-call turns), planner nudge follow-ups are capped at 50 per planner run, and deterministic overflow eviction is capped at 50 passes, so compaction cannot spin indefinitely and terminal failures report the achieved reduction, deletion count, projected `tokensAfter`, and budget.

### Fixed

- Fixed overflow auto-compaction silently no-oping when planner authentication was unavailable or when the current branch has no preparable compactable transcript: overflow recovery now either skips model tiers and runs deterministic no-auth LRU eviction directly through the existing validation pipeline, or surfaces a terminal error that nothing more was safely deletable.
- Fixed feasible partial compaction results being discarded solely because they missed the strict ratio target: automatic threshold and overflow compaction now commit validated below-target deletions when their projected `tokensAfter` clears the trigger/budget boundary, including partial deletion state salvaged after a provider context-overflow error.

## [0.9.5-alpha.4] - 2026-07-05

### Changed

- Simplified first-run onboarding to a one-time verifiable-coding-agent-runtime explanation shown after any What's New notes and directly above the normal input box; Atomic no longer intercepts pasted tasks, saves pre-login seeds, routes first-run input to `goal`/`ralph`, raises reasoning for onboarding, or requires `/chat` to continue normally. The notice now reminds unauthenticated users to run `/login` first, and starting a new session with `/new` clears any rendered first-run notice state from the previous session canvas.

## [0.9.5-alpha.3] - 2026-07-04

### Changed

- Hardened the bundled workflows extension's workflow-tool prompt guidance against inline analysis-paralysis drift: the agent must now decide and state the inline-vs-workflow execution mode before its first tool call (reconnaissance explicitly counts as inline execution), budget pre-workflow scoping to a few quick reads that only sharpen the objective and validation criteria, course-correct after roughly ten deliverable-free exploration tool calls (or repeated "let me verify one more thing" loops) by writing findings to a context file and handing off to the best-fit workflow via `reads` (named or user-defined workflows discovered with `action: "list"` first, builtin `goal`/`ralph` as fallbacks when nothing more specific fits), and treat sunk inline research as transferable via files rather than a reason to stay inline. The same "Decide before you explore" and "Course-correct instead of drifting" guidance is mirrored in `docs/workflows.md` under "When to Use Workflows".

## [0.9.5-alpha.2] - 2026-07-04

### Changed

- Refreshed the July 2026 builtin workflow and subagent frontier model rosters bundled with Atomic: high-capacity synthesis, planning, debugging, and review paths now lead or fall back through Claude Fable 5 `:xhigh`, GPT-5.5 `:xhigh`, Opus 4.8 long-context `:xhigh`, GLM-5.2, and the valid OpenRouter Fugu Ultra mirror while keeping dominated or unsupported model IDs out of shipped chains.

## [0.9.5-alpha.1] - 2026-07-04

### Breaking Changes

- Hardened the builtin `goal` and `ralph` review contracts against objective-drift failures: review findings now require `objective_alignment`, Goal and Ralph review decisions require `requirements_traceability`, and reviewer approval rejects empty or non-proven traceability. Consumers that parse or synthesize these structured reviewer outputs must emit the new required fields.

### Added

- Added immutable `acceptance_criteria` to the builtin `goal` and `ralph` workflows. Goal persists it in the ledger/model-visible projection and final reports; Ralph threads it through research, orchestrator, and reviewer prompts next to the literal objective contract. Orchestrators should pass the original task text when launching follow-up Goal or Ralph runs from reviewer findings.
- Added literal-contract prompt language shared by `goal` and `ralph`, objective-alignment arbitration for reviewer findings, non-blocking treatment for `beyond_objective`/`contradicts_objective` findings, and clause-by-clause requirements traceability so reviewer evidence must map directly back to the objective/acceptance criteria.
- Added attempt-first E2E guidance for `goal` and `ralph`: workers/reviewers must not skip playwright-cli/tmux validation because credentials or auth are merely assumed missing, and skipped E2E must cite the exact attempted commands and observed failure output.

## [0.9.4] - 2026-07-03

### Added

- Added dynamic GitHub Copilot model population from the live CAPI `/models` catalog — picker-enabled chat ids are synthesized from catalog metadata while built-in `pi-ai` definitions still win — plus catalog-driven thinking-level gating so synthesized and bundled Copilot models only offer the reasoning levels CAPI's `capabilities.supports.reasoning_effort` arrays advertise.
- Added `get_entries`/`get_tree` RPC commands with `RpcClient.getEntries`/`getTree` helpers, a package `./rpc-entry` export for launching Atomic directly in RPC mode, a `session_info_changed` extension event, an `externalEditor` settings.json override for Ctrl+G, an `outputPad` setting controlling message padding, BMP image detection with PNG normalization, and separate extension-load timing namespaces (inherited from upstream Pi 0.80.3).
- Added subagent watchdog escape hatches: setting `ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS` or `ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS` to `0` (or negative) disables the corresponding per-attempt timeout, while the `ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS` SIGTERM→SIGKILL grace period always stays bounded ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).

### Changed

- Sped up startup substantially: compiled binaries now use Bun bytecode compilation (`--bytecode --format=cjs`, TUI first paint ~450ms → ~200ms), builtin extensions transpile through a persistent on-disk jiti cache at `~/.atomic/agent/cache/jiti/<version>` (~2.8x faster warm TUI starts), the interactive TUI shell paints immediately while extensions load in the background, `--version`/`-v` is a fast path (~380ms → ~25ms), and the HTML session export module loads lazily on first use.
- Synced `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` runtime dependencies from `^0.80.2` to `^0.80.3` across `@bastani/atomic` and all bundled first-party extensions, and changed the default OpenAI model to `gpt-5.5` (the Atomic-specific `github-copilot` default remains `gpt-5.4`).
- Restored upstream Pi's provider retry default by leaving `retry.provider.maxRetries` unset/zero unless users configure it explicitly, so Atomic's agent-level retry observes provider transport failures directly instead of the SDK retrying them first.

### Fixed

- Fixed async `bash({ async: true })` jobs finishing silently: completed or failed session-managed background jobs now enqueue an `async-job-result` follow-up message into the originating session with duplicate suppression, retention-bounded delivery bookkeeping, full-output-path persistence for truncated previews, and lifecycle tracking that survives owner-session disposal; also fixed a leak where a failed async-manager registration left a never-executed job permanently reported as `running`.
- Fixed every builtin extension (workflows, subagents, mcp, web-access, intercom, cursor) failing to load with `Package subpath './package.json' is not defined by "exports"` on npm/bun package installs (0.9.4-alpha.9 regression): the loader now locates package roots by scanning the `node_modules` resolution chain instead of exports-encapsulated `require.resolve`, guarded by installed-layout CI smoke tests under Node on Linux and Windows.
- Fixed intercepted provider safety refusals dead-ending agent turns: canned zero-usage refusals are detected and auto-retried, structured safety-trigger errors (Anthropic `refusal` stops, OpenAI-style `finish_reason: content_filter`) are now retryable for all providers, and a static CAPI-derived limit snapshot (2026-07-02) keeps bundled GitHub Copilot models within real server-enforced context/output limits — including branded long-context tiers with hard input caps — whenever the live catalog is unavailable ([#1608](https://github.com/bastani-inc/atomic/issues/1608)).
- Fixed GitHub Copilot metadata handling: models use the live `max_output_tokens` from the catalog ([#1582](https://github.com/bastani-inc/atomic/issues/1582)), active sessions adopt live catalog metadata without a restart, and `COPILOT_GITHUB_TOKEN` env auth routes through an endpoint resolver honoring explicit base URL overrides, GitHub Enterprise server URLs, and the public Copilot routing hub, avoiding `421 Misdirected Request` failures ([#1569](https://github.com/bastani-inc/atomic/issues/1569)).
- Fixed workflow and subagent model fallback chains so request/context incompatibility failures (HTTP 400/413/422, unsupported tool/parameter, context-window overflow, `invalid_request`/`bad_request`/`too_large`) advance to the next candidate and ultimately fall back to the current user-selected model; bounded foreground and background subagent attempts with an idle watchdog and wall-clock cap where in-flight tool executions count as activity; skipped known unauthenticated providers before spawning; aligned the subagents and workflows model-failure classifiers with a cross-package conformance suite; and made the background runner spawn one default-model attempt when no candidates were ever configured ([#1580](https://github.com/bastani-inc/atomic/issues/1580), [#1581](https://github.com/bastani-inc/atomic/pull/1581)).
- Fixed the `read` tool to parse colon-delimited `file:START:END` (and grep-style `file:LINE:COL`) path selectors as line ranges instead of producing a bogus `ENOENT` ([#1585](https://github.com/bastani-inc/atomic/issues/1585)).
- Fixed post-compaction queued work to resume through the full agent continuation lifecycle with surfaced continuation failures ([#1570](https://github.com/bastani-inc/atomic/issues/1570)), removed the fixed 10-second connect-phase timeout from the global proxy-aware HTTP dispatcher so slow provider CONNECT establishment behind policy proxies no longer fails spuriously, and made headless JSON print mode exit nonzero when the final assistant turn ends with `stopReason: "error"` or `"aborted"`.
- Fixed release packaging determinism: declared `lru-cache` as a direct runtime dependency, added publish-time `npm-shrinkwrap.json` generation matching upstream Pi, and made shrinkwrap preparation hermetic by deriving `@bastani/atomic-natives` and generated platform optional package entries from local stamped metadata with deterministic registry tarball URLs.
- Fixed issues inherited from upstream Pi 0.80.3, including mid-run extension `setActiveTools` changes applying before the next provider request, `before_agent_start` system prompt overrides surviving mid-run tool changes, clean red errors (not stack traces) for invalid session files without overwriting them, `outputPad`-aware transcript rendering, visible backslashes in Markdown escape sequences, a visible incomplete-response error on output-length stops, `--no-session --session-id` for deterministic provider cache affinity, disk BMP attachment support, and a crash when undici emits an internal client error mid-stream.

## [0.9.4-alpha.11] - 2026-07-03

### Fixed

- Fixed async `bash({ async: true })` jobs finishing silently: completed or failed session-managed background bash jobs now enqueue an `async-job-result` follow-up message into the originating chat session automatically, while explicit completed-job polling, explicit cancellation, and parent aborts acknowledge the job and suppress duplicate or unwanted idle turns. Suppression is checked again at the streaming boundary so a completed job polled while its automatic follow-up is staged does not later deliver a duplicate. Async delivery bookkeeping is now bounded by the existing background-job retention defaults, suppressions stay tied to retained jobs so disposed-session running jobs cannot later fall back into the owner session, 12KB–50KB follow-up outputs persist their full output path before inline preview truncation, just-under-threshold raw outputs remain fully inline instead of being preview-truncated without a `fullOutputPath`, shared-manager lifecycle tracking prevents owner-session disposal from dropping later-session jobs while cleaning stale handlers from disposed fork/subagent sessions, and non-blocking delivery attempts keep one live streaming session from delaying unrelated async job completions.
- Fixed a background bash job leak where a failed async-manager registration (disposed manager/session or a capacity race mid-flight) left a never-executed job permanently reported as `running` to `__atomic_bash_job` polls; the managed job entry is now discarded when registration fails and the tool call error is surfaced unchanged.

## [0.9.4-alpha.10] - 2026-07-03

### Fixed

- Fixed every builtin extension (workflows, subagents, mcp, web-access, intercom, cursor) failing to load with `Package subpath './package.json' is not defined by "exports"` for npm/bun package installs of Atomic (regression in 0.9.4-alpha.9): the extension loader's installed-package alias fallback resolved host packages via `require.resolve("<pkg>/package.json")`, which Node's strict exports-map encapsulation rejects for packages like `@earendil-works/pi-ai` that do not export `./package.json` (the compiled binary and Bun-run dev paths were unaffected, which is why it slipped through). The loader now locates package roots by scanning the `node_modules` resolution chain directly, bypassing exports maps entirely while staying `import.meta.resolve()`-free to keep bytecode compilation intact. CI now guards this path with an installed-layout smoke test that runs the built package under the Node runtime on both Linux and Windows.

## [0.9.4-alpha.9] - 2026-07-02

### Changed

- Sped up TUI startup for the compiled Bun binary (and Windows) by ~2.8x on warm starts: builtin extensions transpiled at runtime through jiti now use a persistent on-disk cache at `~/.atomic/agent/cache/jiti/<version>` instead of re-transpiling ~280 TypeScript files on every launch (extension loading drops from ~4.1s to ~1.4s on Linux). Cache entries self-invalidate via jiti's source-content hashing, and stale version directories are pruned in the background.
- Made `--version`/`-v` a fast path that prints the version before loading the full CLI module graph, dropping `atomic --version` from ~380ms to ~25ms. The CLI entrypoint now loads the main module graph dynamically so metadata fast paths skip it entirely.
- Decoupled the TUI first paint from extension loading: in interactive mode the shell (header, editor, footer) now renders immediately with a "Loading extensions, skills, prompts, themes..." indicator while extension code loads in the background, dropping perceived compiled-binary startup from ~2s (warm) / ~5s (cold) to ~0.5s regardless of extension count. Deferred loading only engages when nothing before first paint needs extensions (no pending trust prompt, `-e` paths, extension flags, or CLI/settings model selection); resources, startup notices, and any saved-model restore that depends on extension-registered providers are applied once the background load completes.
- Deferred loading of the HTML session export module (including its large generated template) until `--export` or `/export` is actually used.
- Compiled binaries are now built with Bun bytecode compilation (`--bytecode --format=cjs`), skipping JavaScript parsing at launch: TUI first paint drops from ~450ms to ~200ms and `--version` from ~285ms to ~85ms. To support the CJS bundle (where `import.meta.url` no longer points into Bun's virtual filesystem), binary detection also checks `process.argv[1]`, native-module `require`s are anchored to the executable path, and the extension loader's dev-mode alias fallback resolves packages via their `package.json` export instead of `import.meta.resolve()`.

### Fixed

- Fixed intercepted provider safety refusals dead-ending agent turns: a canned "I'm sorry, but I cannot assist with that request." completion arriving with zero token usage and a spurious `stopReason` of `length` (or `stop`) — observed on `github-copilot` GPT models under very large contexts — was accepted as the final answer, silently ending the turn (and, inside workflows, poisoning worker receipts). Such canned refusals are now detected (single short refusal text, no tool calls or thinking, zero billed output) and auto-retried like other transient failures, bounded by `maxRetries`; genuine model-authored refusals that bill output tokens are never retried ([#1608](https://github.com/bastani-inc/atomic/issues/1608)).
- Fixed the root cause of GitHub Copilot sessions overrunning server-enforced limits when the live CAPI `/models` catalog is unavailable (cold start without cache, catalog fetch failure, or network-restricted environments): several bundled `pi-ai` Copilot model definitions disagree with what CAPI actually enforces — the `gpt-5.x` family claims a 400k context window vs CAPI's 272k default tier, `claude-opus-4.6`/`claude-sonnet-4.6` claim the branded 1M window as their base tier vs CAPI's 200k, and `claude-opus-4.6`/`4.7` ship a 32k output cap vs CAPI's real 64k. Because auto-compaction thresholds are driven by `model.contextWindow`, an overstated window meant sessions never compacted, sailed past the real server cap, and were intercepted by CAPI with the canned refusals above; understated output caps truncated long responses at half the real limit. Atomic now applies a static CAPI-derived limit snapshot (snapshotted 2026-07-02) for every bundled Copilot model present in the CAPI catalog whenever the active catalog has no entry for it, carrying the full tier structure — default-tier context window, `max_output_tokens`, and the branded long-context tier with its hard input cap where CAPI advertises one (e.g. 272k/1.05M with a 922k input cap for `gpt-5.5`, 200k/1M with a 936k input cap for `claude-opus-4.8`) — so persisted long-context selections stay valid offline; the live catalog (or its disk cache) always wins when available ([#1608](https://github.com/bastani-inc/atomic/issues/1608)).
- Changed structured safety-trigger errors to be auto-retried for all providers, not just GitHub Copilot Gemini: pi-ai maps Anthropic `refusal` stops to a canned "The model refused to complete the request" error and OpenAI-style APIs surface `finish_reason: content_filter`; both are now classified as retryable (bounded by `maxRetries`) so spurious safety triggers re-request the model call instead of dead-ending the task ([#1608](https://github.com/bastani-inc/atomic/issues/1608)).

## [0.9.4-alpha.7] - 2026-07-02

### Added

- Added dynamic GitHub Copilot model population from the live CAPI `/models` catalog: picker-enabled, non-disabled plain chat ids are synthesized from catalog metadata (endpoints, capabilities, limits, and display names) while built-in `pi-ai` definitions still win, namespaced enterprise deployments such as `org/deployment/model` are skipped, and cached catalog metadata enables the same models on cold start.
- Added catalog-driven thinking-level gating for GitHub Copilot models so dynamically synthesized entries and bundled `pi-ai` Copilot models only offer the reasoning levels advertised by CAPI's `capabilities.supports.reasoning_effort` arrays, while models without an effort array keep their existing thinking behavior.
- Added a subagent watchdog escape hatch: setting `ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS` or `ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS` to `0` (or a negative value) now disables the corresponding per-attempt timeout entirely; non-numeric values are ignored and the defaults apply. The `ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS` SIGTERM→SIGKILL grace period intentionally cannot be disabled — `0`, negative, or non-numeric values fall back to its default so escalation always stays bounded ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).

### Fixed

- Reverted the `@types/node` devDependency from `26.0.1` back to `24.12.4` to match the deliberate root `overrides` pin that aligns with upstream Pi's resolved lockfile (see [#1489](https://github.com/bastani-inc/atomic/pull/1489)); the dependabot bump in [#1548](https://github.com/bastani-inc/atomic/pull/1548) was a semantic no-op because the override forced resolution to `24.12.4`. Dependabot now ignores `@types/node` so it stops proposing inert bumps.
- Fixed the `read` tool to parse colon-delimited `file:START:END` (and grep-style `file:LINE:COL`) path selectors as a line range instead of leaving the leading number glued to the path (`file:395`), which produced a bogus `ENOENT` and pushed models (e.g. Opus) to fall back to `sed` ([#1585](https://github.com/bastani-inc/atomic/issues/1585)).
- Fixed GitHub Copilot models to use the live `max_output_tokens` value from the Copilot model catalog, preventing `github-copilot/claude-opus-4.8` from being capped by Atomic's stale built-in output-token limit after compaction ([#1582](https://github.com/bastani-inc/atomic/issues/1582)).
- Fixed active GitHub Copilot sessions to adopt live catalog model metadata as soon as the catalog loads, so fallback models refresh their supported reasoning levels without requiring a restart.
- Fixed workflow and subagent model fallback chains so request/context incompatibility failures (HTTP 400/413/422 bad/unprocessable/payload-too-large request, unsupported tool/parameter, context-length/context-window overflow, `invalid_request`/`bad_request`/`too_large` errors) advance to the next candidate instead of stopping. This ensures that when none of the configured fallback candidates can serve the current request, Atomic falls back to the currently selected user model rather than failing outright. Refusals, content-filter/safety blocks, cancellations, and task failures still stop the chain and are never retried on another model ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
- Fixed foreground and background subagent model attempts that produced no child activity from hanging indefinitely. Atomic now bounds each candidate with an idle watchdog and wall-clock cap, records retryable timeout attempts so fallback continues, and skips known unauthenticated providers before spawning while preserving unknown/custom providers and the current-model last resort ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
- Fixed the subagent per-attempt idle watchdog so an in-flight tool execution counts as activity: a slow, quiet tool call (long build or test run with no interim output) is no longer killed as a stalled attempt; the wall-clock cap still bounds such attempts ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
- Aligned the subagents and workflows model-failure classifiers' direct-message precedence and added a cross-package conformance test suite so the two classifier copies cannot silently drift ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).
- Fixed the background subagent runner to spawn one default-model attempt when no model candidates were ever configured (no primary model, no fallbacks, and no current model), mirroring the foreground path instead of silently exiting 1 with no error and no spawn; an explicitly empty candidate list produced by pre-spawn auth filtering is still respected and surfaced as an error ([#1581](https://github.com/bastani-inc/atomic/pull/1581)).

## [0.9.4-alpha.6] - 2026-07-01

### Added

- Added `get_entries` and `get_tree` RPC commands for reading session entries and tree snapshots over RPC, with corresponding `RpcClient.getEntries`/`getTree` helpers and response types (inherited from upstream Pi [#6078](https://github.com/earendil-works/pi/pull/6078)).
- Added a package `./rpc-entry` export and `src/rpc-entry.ts` entrypoint for launching Atomic directly in RPC mode (inherited from upstream Pi).
- Added `session_info_changed` as an extension event so extensions can observe session name changes, wired through `AgentSession.setSessionName` to the extension runner (inherited from upstream Pi [#6175](https://github.com/earendil-works/pi/pull/6175)).
- Added an `externalEditor` settings.json override for Ctrl+G external editor commands, with default fallbacks to Notepad on Windows and `nano` elsewhere (inherited from upstream Pi [#6122](https://github.com/earendil-works/pi/issues/6122)).
- Added an `outputPad` setting (`0 | 1`, default `1`) controlling horizontal padding for user messages, assistant messages, and thinking blocks (inherited from upstream Pi [#6168](https://github.com/earendil-works/pi/issues/6168)).
- Added BMP image detection and shared `processImage`/`convertImageBytesToPng` helpers so unsupported image formats are normalized (converted to PNG) before inlining (inherited from upstream Pi [#6047](https://github.com/earendil-works/pi/issues/6047)).
- Added `resetTimings`/`time` timing namespaces so extension load timings are recorded and reset separately from main startup timings (inherited from upstream Pi [#6030](https://github.com/earendil-works/pi/pull/6030), [#6063](https://github.com/earendil-works/pi/pull/6063)).

### Changed

- Synced Atomic's `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` runtime dependencies from `^0.80.2` to `^0.80.3` and ported the relevant coding-agent changes while preserving Atomic branding, package names, the versionless-`main` release convention, bundled first-party extensions, and user-facing Atomic docs.
- Bumped `@earendil-works/pi-agent-core`, `pi-ai`, and `pi-tui` from `^0.80.2` to `^0.80.3` across `@bastani/atomic` and all bundled first-party extensions, and regenerated `bun.lock`, `package-lock.json`, and `npm-shrinkwrap.json`.
- Changed the default OpenAI model to `gpt-5.5` (inherited from upstream Pi). The Atomic-specific `github-copilot` default remains `gpt-5.4`.
- Deferred the shared `isRetryableAssistantError` retry classifier because Atomic's `_isRetryableError` carries Atomic-specific Copilot Gemini handling (`content_filter`/`finish_reason: error` for CAPI-mapped MALFORMED_FUNCTION_CALL); adopting it safely requires merging Atomic's extra patterns back in during a follow-up sync.
- Deferred the interactive status-indicator stabilization refactor (Loader → StatusIndicator classes) because it is a large TUI refactor that needs visual QA.

### Fixed

- Fixed extension `setActiveTools` changes to apply before the next provider request in the same run, by installing a `prepareNextTurnWithContext` hook that refreshes system prompt and tools (inherited from upstream Pi [#6162](https://github.com/earendil-works/pi/issues/6162)).
- Fixed `before_agent_start` system prompt overrides to survive when extension tool changes occur mid-run (inherited from upstream Pi [#6162](https://github.com/earendil-works/pi/issues/6162)).
- Fixed invalid session file errors at CLI entry to print a clean red error message instead of a stack trace, via `openSessionOrExit` wrappers around `SessionManager.open` call sites (inherited from upstream Pi [#6002](https://github.com/earendil-works/pi/issues/6002)).
- Fixed resumed/transcript chat rendering to respect the `outputPad` setting for user and assistant messages rendered through `renderChatMessageEntry` and the chat-session-host render path (inherited from upstream Pi [#6168](https://github.com/earendil-works/pi/issues/6168)).
- Fixed the dedicated `rpc-entry` to use Atomic's APP_NAME-based env marker (`ATOMIC_CODING_AGENT`) instead of the upstream `PI_CODING_AGENT`, and to force `--mode rpc` as the last CLI argument so a caller-supplied `--mode` cannot override it.
- Fixed pre-prompt compaction to stop after compaction instead of continuing immediately (already present in Atomic; documented for parity with upstream Pi [#6074](https://github.com/earendil-works/pi/pull/6074)).
- Fixed `--session` and `SessionManager` to reject non-empty invalid session files without overwriting them (inherited from upstream Pi [#6002](https://github.com/earendil-works/pi/issues/6002)).
- Fixed user-message transcript rendering to keep visible backslashes in Markdown escape sequences such as `\"` (inherited from upstream Pi [#6105](https://github.com/earendil-works/pi/issues/6105)).
- Fixed assistant messages stopped by output length to show a visible incomplete-response error (inherited from upstream Pi [#4290](https://github.com/earendil-works/pi/issues/4290)).
- Fixed `--no-session --session-id` so ephemeral CLI runs can use deterministic session IDs for provider cache affinity (inherited from upstream Pi [#6070](https://github.com/earendil-works/pi/issues/6070)).
- Fixed disk BMP image files to be detected, converted to PNG, and attached through `read` and CLI `@file` inputs (inherited from upstream Pi [#6047](https://github.com/earendil-works/pi/issues/6047)).
- Fixed a crash when undici emits an internal client error while terminating a mid-stream HTTP response, by attaching error-suppressing listeners to the global dispatcher and per-origin pools (inherited from upstream Pi [#6133](https://github.com/earendil-works/pi/issues/6133)).

## [0.9.4-alpha.5] - 2026-07-01

### Fixed

- Routed `COPILOT_GITHUB_TOKEN` env auth through an endpoint resolver that honors explicit Copilot base URL overrides, GitHub Enterprise server URLs, and the public Copilot routing hub so env-token users avoid account-specific endpoint `421 Misdirected Request` failures ([#1569](https://github.com/bastani-inc/atomic/issues/1569)).

## [0.9.4-alpha.4] - 2026-06-30

### Fixed

- Disabled Undici's default 10-second connect timeout in Atomic's global proxy-aware HTTP dispatcher so headless or sandboxed runs behind policy proxies can wait for slow provider CONNECT establishment instead of surfacing spurious `Connection error.` failures.
- Resumed post-compaction queued work through the full agent continuation lifecycle and surfaced continuation failures, preventing sessions from appearing dead after auto-compaction or failed tool-call recovery ([#1570](https://github.com/bastani-inc/atomic/issues/1570)).

## [0.9.4-alpha.3] - 2026-06-30

### Fixed

- Made release shrinkwrap preparation hermetic by deriving `@bastani/atomic-natives` and generated platform optional package entries from local stamped package metadata with deterministic registry tarball URLs, removing post-native-publish npm metadata lookups that could race registry propagation.

## [0.9.4-alpha.2] - 2026-06-29

### Fixed

- Hardened the coding-agent npm shrinkwrap generator so release publishes resolve the already-published `@bastani/atomic-natives` registry metadata and generated platform optional packages before packing `@bastani/atomic`, while versionless main keeps deterministic placeholder native package entries for shrinkwrap checks.

## [0.9.4-alpha.1] - 2026-06-29

### Changed

- Restored upstream Pi's provider retry default by leaving `retry.provider.maxRetries` unset/zero unless users configure it explicitly, so Atomic's agent-level retry observes provider transport failures directly instead of the SDK retrying them first.

### Fixed

- Aligned Atomic's global HTTP dispatcher with upstream Pi by removing the fixed 10-second connect-phase timeout and installing undici's fetch implementation alongside the configured proxy dispatcher, avoiding spurious provider `Connection error` failures behind policy/proxy egress layers.
- Made headless JSON print mode return a nonzero exit code when the final assistant turn ends with `stopReason: "error"` or `"aborted"`, so Pier and other harnesses can mark provider failures as agent errors.
- Declared `lru-cache` as a direct runtime dependency and added publish-time `npm-shrinkwrap.json` generation, matching upstream Pi's deterministic npm installs and fixing strict pnpm global installs that could not resolve Atomic's hashline and fetch-tool cache imports.
- Updated user-facing workflow documentation for the blocked lifecycle notice state, the default `workflowNotifications.notifyOn` set, and the reserved top-level workflow output `status` convention for returned `failed`/`blocked` run statuses.

## [0.9.3] - 2026-06-29

### Breaking Changes

- Replaced the legacy exact-replacement `edit` tool API with the hashline-only `input` script schema; callers must use snapshot tags from `read`/`search`/`write`/`edit` instead of `path` + `edits[]` or `oldText`/`newText`.
- Tightened the model-facing `read`, `find`, and `search` schemas to the new builtin contracts (`read` path selectors, required `find.paths`, and the narrowed `search` option set).

### Added

- Added first-run onboarding that routes pasted tickets, specs, and tasks into normal Atomic sessions with scope-estimation guidance and workflow handoff next steps.
- Added internal workflow-stage session marking so workflow-created sessions stay out of ordinary `/resume`, `atomic -r`, and `--continue` history while remaining accessible through workflow resume/status surfaces.
- Added first-class `find`/`search` builtins, hashline snapshot anchors, hashline edit scripts with stale-tag safety, a disabled-by-default Bash Interceptor toggle, Rust-backed `pty:true` bash execution, and native glob/grep/search bindings for oh-my-pi parity.

### Changed

- Synced bundled upstream Pi runtime packages to `^0.80.2` and routed legacy pi-ai imports through the temporary `/compat` entrypoint, including virtual-module and Jiti aliases for first-party and user-installed extensions.
- Enabled provider/SDK retries by default, raised the standard HTTP idle timeout to 10 minutes, and added a 10-second connect-phase timeout so transient socket drops retry while unreachable hosts fail fast.
- Accounted for image content blocks in context-window estimates and compaction budgets, raised delegated workflow/subagent nesting to the shared five-level maximum, and exported the canonical schema-aware flattened-argument helper used by host and MCP tool execution.
- Updated workflow and user-facing documentation so structured, validation-heavy, implementation, debugging, migration, and loop-shaped requests are routed to workflows by default.

### Fixed

- Fixed custom tool renderer cleanup, persisted-context replay after context compaction, and multiple first-run onboarding edge cases around placeholders, file references, isolated config discovery, saved tasks, `/import`, `/model`, and `/new`.
- Completed the oh-my-pi builtin parity pass across `read`, `write`, `find`, `search`, `edit`, `bash`, archive, document, URL, internal-resource, conflict, and SQLite selectors, including pagination, truncation metadata, native cache invalidation, copied-hashline stripping, async/PTY behavior, and cross-platform path handling.
- Hardened generated-file, URL, `local://`, SQLite, archive, native PTY/search, and bash-interceptor paths against traversal, SSRF, unsafe raw SQL/archive cases, stale cache writes, native panics, leaked async output, and provider-hostile argument shapes.
- Fixed compiled release binary packaging and cross-platform package tests by externalizing `mupdf`, preparing native bindings and fixtures in CI, running the coding-agent Vitest suite under Bun, and hardening Windows path, color, and process-spawn coverage.

## [0.9.3-alpha.6] - 2026-06-29

### Changed

- Enabled provider/SDK retries by default (`retry.provider.maxRetries` `0` → `5`) so transient socket drops retry instead of surfacing as fatal `"Connection error"`. Connection failures (status-undefined) back off briefly while the existing `retry.provider.maxRetryDelayMs` cap still fails fast on long quota/rate-limit waits.
- Removed the hard-coded `httpIdleTimeoutMs:0` override from the eval harness adapters (`atomic_pier.py`, `atomic_harbor.py`); they now inherit the standard 10-minute idle timeout default.

## [0.9.3-alpha.5] - 2026-06-28

### Changed

- Synced bundled upstream Pi runtime packages to `^0.80.2` (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui`) across the coding-agent direct/peer dependency pins, aligning Atomic with upstream Pi's 0.80.x "Models runtime migration" where the `@earendil-works/pi-ai` root entrypoint became core-only and the legacy global provider/model API moved to the temporary `@earendil-works/pi-ai/compat` entrypoint.
- Retargeted Atomic's first-party `@earendil-works/pi-ai` imports to the `/compat` entrypoint across the coding-agent runtime, tools, tests, examples, and SDK (both value and type imports), preserving the public extension and SDK surface downstream users depend on. The `/oauth` and `/bedrock-provider` subpaths were left untouched.
- Updated the extension loader's virtual-module and jiti alias paths to alias the `@earendil-works/pi-ai` root specifiers to the `/compat` entrypoint, so first-party bundled extensions and user-installed extensions that import from the root keep resolving transparently with no source changes required.
- Widened `ProviderHeaders` handling in `provider-attribution.ts` to the upstream 0.80.2 `Record<string, string | null>` shape, preserving `null` values verbatim through the merged attribution output so they reach pi-ai as the documented suppression signal for default provider/API headers (rather than collapsing them to `undefined`, which would let pi-ai re-add its own defaults).
- Consolidated open Dependabot updates onto this branch: bumped `actions/checkout` to 7.0.0 (release workflow), `@j178/prek` to 0.4.5 (root devDependency), `@napi-rs/cli` to 3.7.2 (natives devDependency), and refreshed transitive Cargo crates `rustls` to 0.23.41, `napi` to 3.9.4, `bytes` to 1.12.0, and `webpki-roots` to 1.0.8 (the latter removing the Mozilla-deprecated `SecureSign Root CA12` root).

- Raised the default HTTP idle timeout from 5 to 10 minutes (`httpIdleTimeoutMs` 300000 → 600000) and added a fixed 10-second connect-phase timeout, so slow long-context turns finish instead of failing as "Connection error." while unreachable or firewall-blocked hosts fail fast instead of hanging.

### Fixed

- Debounced duplicate reftable watcher notifications by de-duplicating unchanged `.git/reftable/tables.list` states, avoiding extra Windows footer branch refreshes while preserving real reftable branch updates.

## [0.9.3-alpha.4] - 2026-06-28

### Changed

- Updated workflow, quickstart, Atomic guide, and workflow-playbook documentation to present workflows as the default path for non-trivial or structured requests with verifiable objectives, explicitly including implementation, build, debugging, bug-fix, migration, new-feature, scoped multi-file, and validation-heavy docs/code prompts alongside loop-shaped prompts such as `do X until Y`, `repeat until`, `iterate until`, and review/fix/test-until-passing.

## [0.9.3-alpha.3] - 2026-06-27

### Changed

- Published the Atomic 0.9.3-alpha.3 prerelease from the same code as 0.9.3-alpha.2; no coding-agent changes were made after the previous prerelease.

## [0.9.3-alpha.2] - 2026-06-27

### Fixed

- Fixed custom tool renderer disposal to honor renderer-owned cleanup callbacks, preventing stale animation registry entries after terminal workflow tool rows are finalized ([#1518](https://github.com/bastani-inc/atomic/issues/1518)).
- Hardened session replay and LLM conversion so persisted context-compaction filters cannot leave orphaned `toolResult` messages after deleting their paired assistant `toolCall`, preventing GitHub Copilot Claude/Anthropic replay failures after repeated subagent runs ([#1527](https://github.com/bastani-inc/atomic/issues/1527)).

## [0.9.3-alpha.1] - 2026-06-25

### Breaking Changes

- Replaced the previous exact-replacement `edit` input shape with the hashline-only `input` script schema; `path` + `edits[]` and top-level `oldText`/`newText` edit calls are no longer accepted ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Tightened the model-facing `read`, `find`, and `search` schemas to the new builtin contracts: `read` uses path selectors instead of `offset`/`limit`, `find` requires `paths`, and `search` accepts only `pattern`, `paths`, `i`, `case`, `gitignore`, and `skip` ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).

### Added

- Added one-time first-run onboarding that explains Atomic workflows, uses an onboarding editor placeholder, lets users opt into normal chat with `/chat`, preserves other slash commands, saves a pre-login pasted task in memory only, and hands the first ready ticket/spec/task to the normal coding-agent session with `goal`/`ralph` workflow-routing guidance.
- Added first-run onboarding routing guidance that raises the parent session to high reasoning when supported, asks the coding agent to first make a text-only scope estimate from tickets/GitHub issues/specs, routes directly when the task is clearly tiny or small with high confidence, and only uses targeted read-only `codebase-locator`/`codebase-analyzer`/`codebase-pattern-finder` probing when referenced context must be read or scope is medium, large, unclear, risky, or not obviously tiny before choosing `goal` or `ralph`.
- Excluded workflow-created (internal) sessions from the standard `/resume`, `atomic -r`, and `--continue` history by marking their `SessionHeader` with `internal: true` and optional `workflow: { runId, stageId, stageName }` linkage, while keeping them resumable via `/workflow resume`/`workflow({ action: "resume" })` and direct `--session <path>` access; added `includeInternal` opt-ins to `SessionManager.list`/`listAll`/`continueRecent`, a `markSessionInternal` method, robust full-line `readSessionHeader`, and regression tests ([#1504](https://github.com/bastani-inc/atomic/issues/1504)).
- Added a first-class `search` built-in and exposed `find`/`search` in normal coding sessions ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Added hashline snapshot anchors across `read`, `search`, `write`, and successful `edit` results, plus hashline line-range/block/multi-section edit scripts with stale-tag safety checks and snapshot-based recovery for non-overlapping file drift, empty-replace validation, and fresh post-mutation tags for follow-up edits ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Added disabled-by-default `bashInterceptor.enabled` settings support with built-in shell anti-pattern rules, a `/settings` **Bash Interceptor** toggle, and optional `user_bash` extension routing, without changing the default local-execution behavior ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Added Rust-backed native PTY execution for `bash({ "pty": true })`, so local PTY calls run through `@bastani/atomic-natives` `PtySession` with real terminal semantics, streaming output, timeout, abort/kill, cwd, shell, and environment support ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Added native `glob`/`grep` bindings copied from oh-my-pi and wired `find`/`search` to use them when available, matching upstream hidden-file, `.gitignore`, node_modules, result-limit, context, truncation, and timeout behavior without shelling out to host tools ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).

### Changed

- Updated first-run `goal`/`ralph` workflow handoff guidance so new users see `/workflow status <id>` and `/workflow connect <id>` next steps with the run id, understand connect is where they can watch, attach, and steer, and know they can ask the current chat for status or steering at any point.
- Removed the unused first-run onboarding scope-probe/routing-assessment subsystem and dead probe-only tests after normal-session prompt handoff became the active onboarding path.
- Accounted for image content blocks in context-window token accounting and compaction thresholds: image tokens are now estimated through a single shared conservative estimate (1200 tokens per image) used consistently by both the heuristic context-estimate path and the transcript planner, so image-heavy conversations trigger compaction at the correct time. The deletion planner now reports image token share (`remainingImageTokens`, `imageBlockCount`, `imageTokenPercent`) via `context_compaction_budget` and is instructed to prefer deleting stale, superseded, or unrelated image content blocks when images dominate, while preserving task-relevant recent images and user text. Because Verbatim Compaction is deletion-only, compaction never reintroduces image payloads or generates image-bearing summaries ([#1500](https://github.com/bastani-inc/atomic/issues/1500)).
- Made `context_compaction_budget` image statistics deletion-aware: the `remainingImageTokens`/`imageBlockCount`/`imageTokenPercent` fields are now recomputed from the live deletion-target set on every budget tool call, so they immediately reflect image blocks already deleted within the current compaction run instead of reporting frozen pre-deletion totals ([#1500](https://github.com/bastani-inc/atomic/issues/1500)).
- Raised the bundled subagent and workflow-stage nesting budget to a hard maximum of five delegated levels, and documented the `0`-to-`5` recursion-guard range.
- Extracted the schema-aware flattened-argument disambiguation into a shared canonical `unflattenArgumentsWithSchema` helper in `core/flattened-tool-arguments.ts` (exported from `@bastani/atomic`), now reused by both the GitHub Copilot Gemini per-tool normalization and the MCP `callTool` boundary so literal dotted argument keys are preserved unless the tool schema proves they are nested paths. A literal dotted top-level property (e.g. `filter.name`) is preserved verbatim even when the schema also defines a same-head container property (e.g. `filter`) (issue [#1496](https://github.com/bastani-inc/atomic/issues/1496)).

### Fixed

- Fixed extension loading in Windows package tests by lazy-initializing compiled-binary virtual modules, avoiding eager source-barrel resolution before Jiti aliases are installed.
- Fixed the first-run onboarding input placeholder so it uses muted TUI text and still renders a visible cursor while empty, making the startup composer read as an editable field instead of static copy.
- Fixed `@` file-reference autocomplete in the first-run onboarding editor before the asynchronous `fd` readiness check completes by falling back to the built-in synchronous path completer while preserving `@` prefixes and quoted paths.
- Fixed workflow config/discovery isolation so `ATOMIC_CODING_AGENT_DIR` prevents home-global workflows from shadowing the bundled first-run onboarding `goal` and `ralph` targets.
- Fixed first-run onboarding returning-user detection so existing Atomic users with prior changelog state are marked onboarded and do not see the first-run CTA/placeholder when upgrading to a build that includes onboarding, while auth-only fresh installs and unfinished onboarding sessions still see the first-run flow.
- Fixed first-run onboarding so multiline absolute path seeds with `:line[:column]` plus notes are saved or handed off with the full original text instead of being mistaken for slash commands.
- Fixed successful `/import <jsonl>` during first-run onboarding so the imported session exits onboarding UI/interception state instead of treating the next normal message as a fresh onboarding seed.
- Fixed first-run onboarding so a task saved before the session is ready resumes after successful `/model` selection, including the context-window follow-up step when required.
- Fixed `/new` during first-run onboarding so the replacement session remains in onboarding but drops any previously saved in-memory task seed instead of resuming stale work later.
- Fixed `context_compaction_budget` `imageTokenPercent` denominator so image share is computed against the remaining (post-deletion) context total rather than the original pre-deletion total. The budget text said “of remaining context” but the value used the original transcript size; deleting non-image text now correctly increases the reported image share while deleting image blocks decreases it ([#1500](https://github.com/bastani-inc/atomic/issues/1500)).
- Fixed delete-context pruning for old user-pasted image attachments: stale, non-recent user `image` content blocks can now be deleted while preserving user text, old image-only user entries can be deleted when another task-bearing entry remains, multi-image-only user matches are canonicalized to a safe entry deletion during `[image]` grep-delete batches, and recent user images remain protected ([#1500](https://github.com/bastani-inc/atomic/issues/1500)).
- Fixed `find` and `search` glob entries in `paths`, restored `search.skip` file-page pagination for filesystem and resource-backed matches (including SQLite text primary keys with spaces), surfaced skip pagination hints across multiple pages when filesystem, archive, SQLite, internal, explicit-path, and ranged-selector search pages are full, avoided false continuation hints when later explicit targets do not match, kept `search` line-selector context inside the requested ranges for native and non-native fallback search, searched ranged single-file selectors without dropping matches beyond the internal raw grep cap while preserving backend regex semantics such as inline `(?i)`/`(?m)`/`(?x)`, normalized copied quoted/empty path inputs including `paths: []`, split delimiter-joined glob search/find/resource paths only after preserving exact filesystem paths with spaces or delimiter characters, awaited async internal-resource find resolvers and continued to fallback resolvers when earlier async resolvers returned `undefined`, resolved `local://`/router-backed find paths before filesystem normalization, preserved trailing slashes for directory find matches, stopped exact-file and exact-limit glob find hits from reporting false limit truncation while reporting real truncation when exact files fill the page before later targets, enforced custom find backend result limits without fabricating empty directory matches, made `find.timeout` return a partial timed-out result instead of accepting an ignored option, ensured custom find backends receive/enforce `hidden:false`, and ensured `gitignore:false` plus explicit `node_modules` globs include `node_modules` in native find/search scans ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Fixed copied hashline output passed to `write` by stripping `[PATH#TAG]` headers, directory banners, continuation/truncation footers, and `LINE:` prefixes only for known current-session snapshots, including bounded/truncated read and search snippets, while preserving literal hashline-looking user content and rejecting unknown/stale tags from other snapshot stores; successful hashline edits now return compact refreshed-anchor metadata instead of the full post-edit file, `insert tail` now appends exactly once for trailing-newline, no-final-newline, and empty files, line-anchored edits on empty hashline snapshots no longer silently no-op, and multi-file hashline edits preflight all stale tags before writing any file ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Fixed additional builtin parity gaps by removing the archive selector dependency on host `python3`, bounding oversized archive/internal/URL/SQLite/local-document reads like file reads while preserving oversized-read details for collapsed renderers, truncating resource-backed and SQLite search lines, aligning archive/internal/SQLite resource search regex semantics with filesystem search, avoiding inflation of unrelated zip members for selected reads/searches/writes, routing internal-resource selectors through a session router when available, preserving custom read/find backends that do not map to local files, filtering direct MCP tool allowlists that collide with the new builtin `search`, and expanding supported internal URLs in bash command/cwd/env inputs before execution ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Fixed read/write parity for URL/internal-resource line selectors, `:raw` URL bypasses, archive members named `raw`, `conflicts`, numeric/L-prefixed names, suffix-looking paths such as `raw:notes.txt`, and archive member `:raw`/`:conflicts` suffixes with or without line ranges, reference-context bounded ranges, raw multi-range sorting/merging, invalid open-ended `+` selectors, no-conflict sentinel output, read-registered `conflict://<id>` plus `conflict://*` splicing with `@ours`/`@theirs`/`@base` and fresh resolved-file snapshot headers, read-only conflict side scopes, generated-file overwrite protection, archive directory-write rejection, reader-style HTML/notebook extraction plus `markit-ai` document conversion for PDF/Office/RTF/EPUB formats, SQLite row/search/pagination/schema selectors, SQLite table `{}` default-value inserts, SQLite JSON5 object validation and query-param rejection for writes, JSON5 writes that preserve quoted hex-like strings, non-SQLite `.db` fallback, and conflict-only hashline line numbers; async bash job polling now surfaces stored execution errors, async and headless `pty:true` requests get a real PTY, PTY execution preserves configured shell argv/stdin transport and live writes instead of forcing a login shell, the bash interceptor now checks commands before configured prefixes are prepended, exact absolute `find` targets outside the workspace render as valid relative paths, native grep file searches emit callbacks, and native `find` bypasses stale scan caches after shell/external mutations ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Completed remaining oh-my-pi builtin parity for source-backed `local://` reads/searches/writes with filesystem hashline labels, richer `find`/`search` result details, streaming `find` progress updates, oh-my-pi-shaped `bash` async metadata, write `resolvedPath`/`madeExecutable`/SQLite source metadata, native in-memory resource search usage, expanded direct `grep` native options, reference-shaped edit success headers, `bash` leading-`cd` rewrite/interceptor ordering, and implicit `find` `**/*` directory patterns ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Aligned oh-my-pi SQLite/notebook/metadata parity: SQLite query default limit is 20 with a 500 cap, raw `?q=` rows cap at 1000, table lists cap at 500 and exclude `sqlite_%` system tables; `.ipynb` cells use 0-based `cell:N` IDs; resource-backed search honors `search.contextBefore`/`search.contextAfter` settings; and successful `read` results across filesystem, URL, SQLite, archive, internal-resource, document, and directory paths consistently return `details.meta.source`/`sourcePath` (plus truncation/limits where applicable), matching the referenced `details.meta` contract ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Tightened the final oh-my-pi builtin parity pass by restoring upstream output caps (3,000 shared read lines, 512-character search lines, 64 MiB archive members), preserving plain URL-read truncation metadata from the fetch pipeline, and aligning the oversized URL test expectation with oh-my-pi's truncating URL behavior ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Hardened oh-my-pi parity code paths flagged by GitHub code-quality and advanced-security review by replacing ambiguous regex parsing in `bash`, `edit`, `find`, read selectors, and HTML document extraction with linear parsers and by removing stale local analysis warnings ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Fixed compiled release binary builds by externalizing `mupdf` during Bun compilation so the `markit-ai` document reader can keep its runtime dependency with top-level await in copied `node_modules` instead of being inlined into the executable ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Hardened URL and `local://` resource reads/writes by blocking private/metadata URL fetch targets by default, revalidating manual redirects, and rejecting `local://` paths that escape the workspace root ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Addressed the PR review hardening pass for oh-my-pi parity: built-in bash interception now checks raw, expanded, prefix, and leading-`cd`-normalized commands; hashline edit batches route writes through the vendored patcher with duplicate canonical-path rejection and partial-write reporting; URL reads pin DNS-validated addresses and cap streamed bodies; async bash jobs support cancellation/eviction; native PTY cleanup joins reader threads; native worker panics and poisoned locks are converted to errors; and find/search/native cache paths received additional caps and nonblocking safeguards ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Closed the remaining oh-my-pi parity and PR review gaps for builtin tools: directory reads now use recency-sorted capped trees, comma/semicolon find/search path lists split when at least one path resolves, search pagination exposes `fileLimitReached` details, line-range search renders context around in-range matches, plain writes return compact fresh headers and note copied-hashline stripping, generated-file guards scan a larger header window, notebooks preserve unknown top-level fields, SQLite raw reads match oh-my-pi's readonly raw-query behavior, `bash` leading-`cd` handles `~`, async bash job timeouts render human-readable errors, native/fallback grep count and multiline behavior are aligned, macOS variant-resolved read snapshots can be edited, native Rust grep tests compile through an `rlib`, and selector/URL security hardening now rejects numeric private-IP URL forms plus archive/SQLite/skill/local symlink escapes ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Followed up on the post-push oh-my-pi audit by matching additional SQLite and discovery semantics: SQLite table listings now avoid full table-count scans with bounded row-count probes, raw `?q=` reads stop after 1000 rows while iterating, structured `where=` validation ignores quoted keywords but rejects control clauses such as `INTERSECT`, SQLite writes validate table columns and scalar values before binding, directory reads prune `.git`/`node_modules`, and broad `find` scans keep `node_modules` pruned even with `gitignore:false` unless explicitly requested ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Addressed the follow-up PR review by validating raw SQLite `?q=` selectors as single safe `SELECT` statements, rejecting raw access to `sqlite_%` internals and dangerous statements such as `ATTACH`, bounds-checking zip central-directory/local-entry offsets before reads or inflates, extending URL SSRF guards to NAT64/6to4 private-address forms and documenting `ATOMIC_ALLOW_PRIVATE_URL_READS` as dev-only, wrapping native block/PTY/Cursor HTTP2 entrypoints in panic guards, ensuring PTY error paths still clean up readers/children, documenting generated Rust split wrappers, bounding uncached native grep streaming accumulation, and correcting the direct-`grep` native-cache docs to match the fresh-by-default implementation ([#1483](https://github.com/bastani-inc/atomic/issues/1483)).
- Fixed cross-platform package test execution by preparing native bindings and LFS fixtures in CI, running the coding-agent Vitest suite under Bun, making self-update/home-directory detection respect live Bun environments, and hardening Windows path/color/process-spawn test coverage ([#1490](https://github.com/bastani-inc/atomic/pull/1490)).
- Addressed follow-up PR review findings by making hashline snapshot collision handling compare full snapshot text before treating 4-hex tags as identities, extending URL SSRF detection to the full IPv6 link-local `fe80::/10` range plus IPv4-compatible IPv6 forms, re-checking bash interceptor rules after `spawnHook` rewrites, surfacing search pagination collection caps without duplicate continuation banners, preserving truncated async bash output in a recoverable `fullOutputPath`, and passing per-path native search-cache invalidations through to the native binding ([#1490](https://github.com/bastani-inc/atomic/pull/1490)).
- Addressed the latest PR review hardening pass by counting multi-file search per-file caps by match lines instead of context lines, making native filesystem scan cache insertion generation-aware so in-flight scans cannot repopulate after invalidation, rejecting SQLite raw-query `pragma_*` table-valued functions and double-quoted internal-name splices, and bounds-checking zip central-directory offsets during selective archive writes ([#1490](https://github.com/bastani-inc/atomic/pull/1490)).
- Addressed the final PR review hardening pass by restoring header-only copied-hashline writes to their snapshot content instead of emptying files, decoding and sanitizing async bash output with a streaming UTF-8 decoder, cleaning up async bash temp output files on eviction/TTL, invalidating native search caches after bash commands, keeping URL protocol validation outside the private-read escape hatch, documenting single-file search skip handling, preserving CR-only hashline edit line endings, rejecting selective zip writes that would drop data descriptors, and exposing `search` in extension `tool_call`/`tool_result` type guards like other builtins ([#1490](https://github.com/bastani-inc/atomic/pull/1490)).

## [0.9.2] - 2026-06-23

### Changed

- Removed the initial `prompt-refinement` stage and shared prompt-refinement helper from the bundled `goal` and `ralph` workflows so both now use the raw objective/prompt as the operative task text for their first downstream stages; the now-obsolete refined/original trace outputs were also removed.
- Updated bundled `goal` and `ralph` reviewer prompts to inspect referenced QA end-to-end video evidence before treating it as proof of user-visible behavior.
- Synced bundled upstream Pi package dependencies to `^0.79.10` across Atomic's CLI and extension peer manifests, and aligned shared coding-agent direct runtime/dev dependency pins with upstream Pi v0.79.10.
- Raised the published Node.js engine floor to `>=22.19.0` to match direct runtime dependency requirements, including `undici@8.5.0`.

### Fixed

- Fixed GitHub Copilot Gemini tool-call normalization to synthesize omitted required empty array properties before validation, preventing Ralph reviewer structured output such as `findings: []` from failing when CAPI drops the empty array from the tool call.

## [0.9.2-alpha.1] - 2026-06-23

### Changed

- Removed the initial `prompt-refinement` stage and shared prompt-refinement helper from the bundled `goal` and `ralph` workflows so both now use the raw objective/prompt as the operative task text for their first downstream stages; the now-obsolete refined/original trace outputs were also removed.
- Updated bundled `goal` and `ralph` reviewer prompts to inspect referenced QA end-to-end video evidence before treating it as proof of user-visible behavior.
- Synced bundled upstream Pi package dependencies to `^0.79.10` across Atomic's CLI and extension peer manifests, and aligned shared coding-agent direct runtime/dev dependency pins with upstream Pi v0.79.10.
- Raised the published Node.js engine floor to `>=22.19.0` to match direct runtime dependency requirements, including `undici@8.5.0`.

### Fixed

- Fixed GitHub Copilot Gemini tool-call normalization to synthesize omitted required empty array properties before validation, preventing Ralph reviewer structured output such as `findings: []` from failing when CAPI drops the empty array from the tool call.

## [0.9.1] - 2026-06-23

### Changed

- Changed the bundled `goal`/`ralph` workflow prompt-refinement stage to use a workflow-neutral, model-only rubric prompt that returns only the refined objective instead of invoking the `prompt-engineer` skill directly.

### Fixed

- Fixed the bundled `ralph` workflow reviewer-c model configuration to use Gemini 3.1 Pro as the third reviewer with Gemini 3.1 provider fallbacks, removing Gemini 3.5 Flash from that slot's fallback chain ([#1484](https://github.com/bastani-inc/atomic/issues/1484)).

## [0.9.1-alpha.1] - 2026-06-22

### Changed

- Changed the bundled `goal`/`ralph` workflow prompt-refinement stage to use a workflow-neutral, model-only rubric prompt that returns only the refined objective instead of invoking the `prompt-engineer` skill directly.

### Fixed

- Fixed the bundled `ralph` workflow reviewer-c model configuration to use Gemini 3.1 Pro as the third reviewer with Gemini 3.1 provider fallbacks, removing Gemini 3.5 Flash from that slot's fallback chain ([#1484](https://github.com/bastani-inc/atomic/issues/1484)).

## [0.9.0] - 2026-06-22

### Breaking Changes

- Changed the bundled `open-claude-design` workflow input contract by removing `reference`, `output_type`, and `design_system`; discovery now gathers output type, design-system context, and references during the run.
- Removed the Atomic-specific `bashPolicy` command allow/deny API from the SDK/session surface and built-in `bash` tool; use `tools`/`excludedTools`/`noTools`, custom tools, or an external sandbox for command scoping.

### Added

- Added configurable context-window selection for models with multiple context tiers, including GitHub Copilot live-catalog support, CLI/settings/RPC controls, SDK helpers and exported types, per-session persistence, model picker UI, and long-context workflow model tokens.
- Added upstream Pi 0.79.7/0.79.9 capabilities including automatic light/dark themes, chat-template thinking compatibility for custom OpenAI-compatible providers, expanded SDK exports, updated model/provider metadata, and Markdown streaming stability.
- Replaced browser automation with the bundled `playwright-cli` skill/command and added the `effective-liteparse` document-extraction skill for local PDF/DOCX/PPTX/XLSX/image extraction.
- Added the builtin `goal` workflow's safe-by-default `create_pr` toggle and final PR/MR/review handoff stage.
- Restructured the builtin `open-claude-design` workflow around `impeccable` discovery/init, design-system/reference gathering, forked generation plus user-feedback loops, and a minimal export/display phase.

### Changed

- Aligned Atomic's bundled upstream Pi runtime packages through `^0.79.9`, carrying provider catalog updates, GitHub Copilot model filtering, GLM-5.2 metadata, Mistral prompt-cache accounting, OpenRouter Fusion, and shared TUI/runtime fixes.
- Changed GitHub Copilot context-window handling to derive tiers from the live CAPI model catalog, display full long-context windows while tracking effective prompt budgets internally, seed cached tiers at startup, and keep per-model persisted selections isolated.
- Synced the Atomic CLI with upstream behavior such as self-only `atomic update` by default, improved model search/autocomplete, generated model catalog updates, theme mode support, and extension documentation/API clarifications.
- Updated bundled workflows, subagents, docs, and verification guidance to use `playwright-cli` instead of the removed `browser` skill / `browse` CLI.
- Changed repository validation to include the monorepo-wide Bun/prek/CI file-length gate for tracked TS/JS/Rust files with only documented generated/vendored exclusions.
- Switched the repository to versionless `main`: package manifests stay at `0.0.0`, release notes land as CHANGELOG-only PRs, and real versions are stamped only on off-main tag commits via `scripts/cut-release.ts`.

### Fixed

- Fixed GitHub Copilot Gemini request/stream failures by sanitizing Gemini-incompatible tool schemas, reconstructing flattened and dotted tool arguments for execution and replay, preserving Gemini thought signatures through `reasoning_opaque`, retrying degenerate empty/error completions appropriately, and hardening reconstruction against prototype pollution.
- Fixed context-window startup, session-switch, settings, RPC, picker, SDK, and GitHub Copilot restart edge cases so selected windows are validated and persisted consistently without leaking unsupported defaults across providers.
- Fixed credential-store load failures and concurrent session creation by surfacing real auth-store read errors, avoiding throwaway `AuthStorage` instances when a registry is supplied, and making credential reads lock-free while retaining atomic locked writes.
- Fixed workflow and extension resource/session behavior, including default session-dir visibility for extensions, same-directory resource reload reuse, workflow stage inheritance from non-default session dirs, and custom resource snapshots for `atomic -e` workflows.
- Fixed model and runtime polish including RPC unknown-command ids, `/model` query ranking, WSL bash stdin execution, fuzzy `edit` patch generation, overflow compaction after oversized successful responses, source-CLI Bun subprocess tests, and stale update banners on `0.0.0` dev builds.
- Fixed release and workflow reliability issues including the `publish-release` Publish-run verifier, `open-claude-design` feedback threading/artifact safety/browser handling, and successful overflow-sized assistant response compaction.

## [0.9.0-alpha.4] - 2026-06-22

### Breaking Changes

- Changed the bundled builtin `open-claude-design` workflow's inputs: removed `reference`, `output_type`, and `design_system` in favor of a new `discovery` interview stage that asks for the output type and references. Inputs are now `prompt`, `discover_references`, and `max_refinements`. Drop those removed arguments from existing invocations and let discovery ask (or describe them in `prompt`).

### Added

- Restructured the bundled builtin `open-claude-design` workflow around the accessible `impeccable` skill. It now opens with one combined `discovery` stage that runs `/skill:impeccable shape` and `/skill:impeccable init` so PRODUCT.md/DESIGN.md are detected, created, or reconciled without a separate init stage, gathers context with `ds-locator` / `ds-analyzer` / `ds-patterns` (which directly capture/parse user-provided URL/file references), then runs optional gallery `reference-discovery` using that ds-* context and asks which curated direction the user prefers (or asks for a reference image/screenshot/URL/path if none fit), then runs a forked `generate-*` / `user-feedback-*` loop. `generate-1` is the first loop iteration, later generate stages fork from the previous generate session, and user-feedback stages fork from the previous feedback session while driving `/skill:impeccable live`. Export is now deliberately only `exporter` plus `final-display`; the separate `web-capture-*`, `file-parser-*`, `design-system-builder`, `pre-export-scan`, and `forced-fix` stages were removed. Updated the bundled workflow docs to describe the trimmed input set, combined discovery/init stage, context/reference phase, reference precedence, forked generate/user-feedback loop, and minimal export phase.
- Added the bundled builtin `goal` workflow's safe-by-default `create_pr` toggle and final `pull-request` stage. Goal now mirrors Ralph's PR handoff behavior: omitted or `false` skips PR/MR/review creation and omits `pr_report`, while strict `create_pr=true` authorizes only the final stage after reviewer quorum plus reducer approval to inspect the goal ledger, worker receipts, reviewer artifacts, final report, repository state, and provider credentials before attempting a provider-appropriate PR/MR/review request. Intermediate Goal worker/reviewer prompts now tell stages to ignore PR-creation requests so PR handoff stays confined to the final stage. Updated the bundled workflow docs, quickstart, README, and `/atomic` guide copy to describe the opt-in behavior.

### Fixed

- Fixed the bundled builtin `open-claude-design` feedback path so live annotations from `user-feedback-*` are parsed, persisted, and required to thread into the next `generate-*` prompt before another revision runs. Also kept final display read-only, constrained annotation snapshot artifact copies to the project/artifact dir, and preserved the browser-centric clean early exit when `playwright-cli` is unavailable (skipped under the test harness).

## [0.9.0-alpha.3] - 2026-06-21

### Fixed

- Fixed the repository `publish-release` workflow's final Publish-run verifier to poll GitHub Actions run JSON until a terminal state instead of treating a still-`in_progress` publish run as a failed prerelease.

## [0.9.0-alpha.2] - 2026-06-21

### Breaking Changes

- Removed the Atomic-specific `bashPolicy` command-level allow/deny API from `createAgentSession()`, `AgentSession` internals, SDK exports, and the built-in `bash` tool so shell execution matches upstream pi behavior. Use `tools`/`excludedTools`/`noTools` to expose or hide `bash`, and use a sandbox or custom tool for command allowlisting.

### Added

- Added upstream Pi v0.79.9 chat-template thinking compatibility to Atomic custom-model/provider configuration, including `compat.thinkingFormat: "chat-template"` and `compat.chatTemplateKwargs` schema/merge support for OpenAI-compatible vLLM/Hugging Face chat templates.

### Changed

- Aligned Atomic's bundled upstream Pi runtime libraries (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui`) with `^0.79.9`, picking up dependency-covered GLM-5.2 provider metadata, GitHub Copilot model-availability filtering, Mistral prompt caching, selective base entry points, OpenRouter Fusion, Markdown streaming code-fence stability, and runtime dependency fixes.

### Fixed

- Fixed same-directory session/resource reloads to reuse imported extension modules while still creating fresh extension instances and lifecycle events, reducing avoidable reload work without making extension contexts stale.
- Fixed deep session branch path/context traversal to build paths linearly instead of using repeated front-insertion on long branches.
- Fixed fuzzy `edit` replacements to preserve untouched original line blocks and produce patches that apply to the original file instead of rewriting the entire file through normalized text.
- Fixed legacy WSL `C:\Windows\System32\bash.exe`/`Sysnative\bash.exe` command execution to send scripts over stdin with `bash -s`, preserving shell-variable expansion in the target bash.
- Fixed `/model` selector search ranking so exact provider-prefixed queries (for example `openai/gpt`) rank ahead of proxy-provider model IDs such as `openrouter/openai/gpt-*`, while autocomplete keeps the broader inherited search behavior.
- Fixed successful overflow-sized assistant responses to trigger compaction without retrying or dropping the completed assistant message.

## [0.9.0-alpha.1] - 2026-06-20

### Added

- Added configurable context-window support for models that declare `contextWindowOptions`, including explicit `--context-window` CLI/settings control, a GitHub Copilot CLI-style `/model`-flow picker (numbered `Default`/`Long context` tiers with token counts), session replay, SDK/runtime/RPC APIs, and docs while preserving each model's scalar default context window. For GitHub Copilot, context windows are measured in **input (prompt) tokens** (consistent with every other provider) and derived **dynamically from GitHub's live CAPI model catalog** (`GET /models`) instead of a hardcoded model list: Atomic resolves each model's input budget as `max_prompt_tokens || max_context_window_tokens || 128_000` and, for tiered models, exposes a selectable default window (`token_prices.default.context_max`) plus a long window set to the model's full `max_context_window_tokens` (retaining `max_prompt_tokens` as the internal effective compaction/overflow budget) — gated on the user actually having the GitHub Copilot provider and cached on disk for 30 minutes (for example `github-copilot/gpt-5.5` exposes `272k` default / `1.05m` long, and the Claude/Gemini long-context models `200k` default / `1m` long). Atomic raises the local budget and sends `X-GitHub-Api-Version: 2026-06-01`, while GitHub applies the long-context billing tier server-side by prompt token count. Long-context Copilot requests consume more AI credits and require Copilot long-context/usage-based billing entitlement; offline, unauthenticated, or non-Copilot sessions leave the built-in window untouched and show no picker; custom providers and explicit model overrides can still expose their own selectable windows ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Exported context-window helper functions and types from the package root, including parser/formatter/normalizer/selection utilities and the `Model<Api>` augmentation for `contextWindowOptions`/`defaultContextWindow`, so SDK consumers can use the public API without importing internal source paths ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Added RPC mode runtime context-window commands so headless clients can read supported token budgets with `get_available_context_windows` and select the active runtime budget with `set_context_window` without persisting context-window settings ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Added upstream pi v0.79.7 automatic theme mode support so `/settings` can choose separate light and dark themes and follow terminal color-scheme changes.
- Exported the upstream `CONFIG_DIR_NAME` constant and edit diff helpers (`generateDiffString`, `generateUnifiedPatch`, and `EditDiffResult`) from the public SDK entrypoint so extensions can avoid hardcoded project config paths and reuse edit-style diff rendering.

### Changed

- Changed the GitHub Copilot **long-context tier to advertise the model's full context window** (`max_context_window_tokens`, for example `github-copilot/gpt-5.5` `1.05m`, and `github-copilot/claude-opus-4.8`/`github-copilot/gemini-3.1-pro-preview` `1m`) instead of GitHub's prompt-token cap, so Copilot models report and display the same window as the native `openai/*` and `anthropic/*` providers (the chat footer denominator now shows the full window). GitHub's lower server-side input cap (`max_prompt_tokens`, e.g. `922k`/`936k`, which equals `max_context_window_tokens − max_output_tokens`) is now parsed and carried as an internal effective input budget (`Model.maxInputTokens`, exposed via the new `getEffectiveInputBudget()` helper): auto-compaction thresholds and the Copilot overflow-recovery guard run against that budget while the picker/footer show the full window. As a result, a prompt that reaches the real prompt cap is now compacted-and-retried automatically (previously the long window equalled the cap), and the friendly “enable long-context/usage-based billing / server-cap” hint fires only when GitHub rejects a prompt *below* the cap (a genuine entitlement/tier drop) rather than at the cap. Sparse catalog payloads without `max_context_window_tokens` still fall back to the long-context prompt threshold, and the on-disk Copilot catalog cache schema version was bumped so existing caches refetch the new windows ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Changed built-in GitHub Copilot context windows to be measured in **input (prompt) tokens** (matching every other provider) and derived from GitHub's live CAPI model catalog (`GET /models`, cached 30 minutes, gated on the Copilot provider) instead of a hardcoded long-context model list, so newly added/removed Copilot models and retiered windows are reflected automatically without shipping a stale snapshot. Each model's window now resolves to `max_prompt_tokens || max_context_window_tokens || 128_000`, and tiered models expose a selectable default window (`token_prices.default.context_max`) plus a long window set to the model's full `max_context_window_tokens` (e.g. `gpt-5.5` 272k/1.05m, Claude/Gemini 200k/1m), with `max_prompt_tokens` retained as the internal effective compaction/overflow budget — while preserving custom provider entries and explicit `models.json` overrides and relying on GitHub's API-version header and server-side tier selection rather than payload fields or model-id variants ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Bumped the bundled upstream pi runtime libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.79.4` to `^0.79.6` so Atomic's installed pi runtime packages pick up upstream v0.79.5/v0.79.6 provider, model, thinking-payload, and shared TUI compatibility fixes; no Atomic coding-agent source changes were made for upstream coding-agent-only marked export or fetch-override behavior in this dependency sync ([#1413](https://github.com/bastani-inc/atomic/issues/1413)).
- Synced Atomic's coding-agent fork with upstream pi v0.79.7, including the new self-only default for bare `atomic update` (`atomic update --all` restores the previous all-packages behavior), automatic light/dark theme settings, configured project config directory labels, extension example updates, model-search parity, tree navigator horizontal panning, and the latest user-facing docs.
- Bumped the bundled upstream pi runtime libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.79.6` to `^0.79.7` so Atomic inherits upstream v0.79.7 TUI color-scheme, Warp image, generated model catalog, and agent-core fixes.
- Reserved `/` in theme names for automatic light/dark theme settings.
- Replaced the bundled `browser` skill / `browse` CLI with the `playwright-cli` skill and `playwright-cli` command across `@bastani/atomic`, and bundled the new `effective-liteparse` document-extraction skill. The builtin `ralph`, `goal`, and `open-claude-design` workflows and the `debugger`/`codebase-online-researcher` subagents now drive browsers via `playwright-cli`; `open-claude-design`'s deterministic setup step ensures `playwright-cli` (`npm install -g @playwright/cli@latest`) and renames its `browse_cli_status` output to `playwright_cli_status`; and `ralph` now records a `playwright-cli` QA end-to-end proof video (`qa_video_path`) for UI-applicable/full-stack changes, references it in the implementation notes, and attaches or links it to the final pull request when `create_pr=true`. Updated the user-facing docs (workflows, SDK bash-policy examples, quickstart skills, README) to match.
- Changed contributor validation to include a monorepo-wide file-length gate in Bun scripts, local `prek` hooks, and PR CI, covering tracked TS/JS/Rust files with the documented generated/vendored exclusions and no grandfathered baseline allowlist ([#1445](https://github.com/bastani-inc/atomic/issues/1445)).
- Switched the repository to a **versionless `main`** release model (modeled on openai/codex): every `packages/*/package.json` on `main` now stays at the `0.0.0` placeholder, and the real version is materialized only on a throwaway, off-`main` `Release <version>` commit created and tagged by the new top-level `scripts/cut-release.ts` (which stamps the version inside a detached git worktree via `scripts/bump-version.ts` and pushes only the tag — `main` is never bumped). This lets a stable release and an ahead-of-stable prerelease line be cut from the same trunk without release branches, mirroring how the npm `@latest`/`@next` dist-tags are derived from the tag shape. `publish.yml` still builds and publishes from the tagged (real-version) commit and now additionally refuses to publish the `0.0.0` placeholder, and the `publish-release` Atomic workflow now lands a CHANGELOG-only release-notes PR on `main` and then stamps/tags the release off-`main` via `cut-release.ts` instead of merging a version bump into `main`, accepts an optional `base_ref` input (default `main`) so a release can be cut from a maintenance/integration branch, and accepts an optional `from_ref` input that cuts an ephemeral release from any commit/tag/branch (auto-creating a CI-gated `release/<version>`/`prerelease/<version>` branch, cutting the tag off it, then deleting it — the changelog lives on the tag only). CI `test.yml` now also runs on `release/**`/`prerelease/**` pushes so those ephemeral branches are gated. End users installing from npm are unaffected; only local/`main` dev builds report `0.0.0` for `--version`.

### Fixed

- Exposed `SessionManager.usesDefaultSessionDir()` through the read-only extension session-manager surface so bundled extensions can distinguish default global session storage from non-default `--session-dir`, `ATOMIC_CODING_AGENT_SESSION_DIR`, or settings-backed session directories without path guessing ([#1444](https://github.com/bastani-inc/atomic/issues/1444)).
- Fixed `github-copilot/*` Gemini models (for example `github-copilot/gemini-3.1-pro-preview` and `github-copilot/gemini-3.5-flash`) failing **every** chat turn with `Error: 400 invalid request body`. These models are served through GitHub's Copilot API (CAPI), which translates the OpenAI chat-completions request into a Google GenAI `GenerateContent` request and forwards tool/function JSON Schema `anyOf`/`oneOf` verbatim into Gemini's `FunctionDeclaration` schema. Gemini rejects a union whose branch is a complex **object** schema, so Google returned HTTP 400 and CAPI relabelled it `{"error":{"code":"invalid_request_body"}}`. Because Atomic's bundled `workflow` tool — and any tool using the TypeBox `Type.Union([Type.Object(...), Type.String()])` pattern for fields such as `task`, `chain`, and `parallel` — is present in normal chat turns, the request failed before the model ever ran (it was previously masked only when a fallback model existed). Atomic now sanitizes outbound tool JSON Schemas for GitHub Copilot Gemini models into the subset CAPI/Gemini honors: it resolves object/array-bearing `anyOf`/`oneOf` to their most expressive branch, converts `const`/literal unions to `enum`, collapses nullable unions to `nullable`, prunes `required` to existing properties, and drops non-portable keywords (`additionalProperties`, `patternProperties`, `$schema`, `format`, `pattern`, numeric/length bounds, `default`, `title`, etc.). The transform is gated to `github-copilot` Gemini `openai-completions` models and runs last in the provider-payload pipeline (so it also covers extension/SDK-injected tools), leaving every other provider/model payload unchanged.
- Fixed `github-copilot/*` Gemini models getting stuck in an infinite tool-call retry loop (most visibly on the workflow `structured_output` tool). Capturing the raw CAPI stream confirmed that Gemini serializes array/object function-call arguments as **flattened indexed keys** on the wire — for example `{ keywords: ["a", "b"] }` arrives as `{ "keywords[0]": "a", "keywords[1]": "b" }` — so schema validation failed (`keywords: must have required properties keywords` and `root: must not have additional properties`) and the model re-emitted the same shape forever. Atomic now reconstructs flattened tool-call arguments (`name[i]`, `name[i].sub`, `parent.child`) back into proper arrays/objects in each tool's `prepareArguments` step, before validation runs. Gated to GitHub Copilot Gemini models at call time and a no-op for well-formed arguments, so it covers built-in, extension, SDK, and MCP tools without affecting any other provider/model.
- Fixed `github-copilot/*` Gemini models (for example `github-copilot/gemini-3.1-pro-preview`) silently dying mid-task instead of continuing the turn. Inspecting the affected sessions and confirming against GitHub's Copilot API (CAPI) source showed two distinct degenerate stream endings that Atomic was not recovering from: (1) CAPI's `getAzureFinishReason` maps several Gemini finish reasons — `MALFORMED_FUNCTION_CALL`, `OTHER`, `LANGUAGE`, and `UNEXPECTED_TOOL_CALL` — to a bare OpenAI `finish_reason: "error"`, which `pi-ai` surfaces as `"Provider finish_reason: error"`; the auto-retry classifier's regex did not match it, so the turn ended with an empty assistant message and no retry; and (2) Gemini intermittently ends the stream with `finish_reason: "stop"`, an **empty content array**, and **0 output tokens**, which Atomic treated as a successful (if empty) turn and stopped. Atomic now treats bare `finish_reason: error`/`content_filter` as retryable and detects degenerate empty completions (no text/tool-call/thinking content **and** zero output tokens on a `stop`/`toolUse` turn) as retryable, re-issuing the request with the existing exponential-backoff path. Empty `stop` completions also no longer reset the auto-retry counter, so repeated empties stay bounded by `maxRetries` instead of retrying forever.
- Fixed the **root cause** behind `github-copilot/*` Gemini (for example `github-copilot/gemini-3.1-pro-preview`) returning repeated empty completions and "stopping to respond" after its first tool call. Gemini is a thinking model: each function/tool call it emits comes with an opaque **thought signature** that must be replayed, verbatim, on the next request or Gemini refuses to continue the reasoning chain. Confirmed against GitHub's Copilot API (CAPI) source, CAPI carries that signature in a non-standard `reasoning_opaque` field on the assistant message / streamed delta and reads the same `reasoning_opaque` back off the assistant message on replay to re-attach the signature to each Gemini function-call part (keyed by `tool_call.id`). The bundled `pi-ai` OpenAI-completions client never captured or replayed `reasoning_opaque` (it only round-trips the OpenRouter-style `reasoning_details: [{ type: "reasoning.encrypted", id, data }]` shape, which CAPI does not emit), so the real Gemini thought signature was dropped inbound and never sent back. With it missing, CAPI substitutes the sentinel `skip_thought_signature_validator` on the first replayed function call and Gemini responds with an empty candidate / `finish_reason: "stop"` and zero output tokens — which the empty-completion retry above then re-issued against the same signature-less history until `maxRetries` was exhausted. Atomic now bridges `reasoning_opaque` to the mechanism the client already round-trips: a `globalThis.fetch` interceptor scoped to `*.githubcopilot.com` event streams rewrites each CAPI Gemini SSE delta that carries both `reasoning_opaque` and a `tool_calls[].id` to add a matching `reasoning_details` entry (captured by the client as the tool call's `thoughtSignature`), and a provider-payload (`onPayload`) transform converts the `reasoning_details` the client re-emits on replayed assistant messages back into the single `reasoning_opaque` field CAPI reads. Both transforms are gated to GitHub Copilot Gemini `openai-completions` models and are no-ops for every other provider/model and for Gemini turns that carry no thought signature; the thinking text round-trips inside the same opaque blob, so combined think-then-tool-call turns keep their signatures across session save/load.
- Fixed a second `github-copilot/*` Gemini multi-turn failure that surfaced once thought signatures were preserved: a turn after any **array/object tool call** (most visibly `edit`) ended with a bare `finish_reason: "error"` and then retried to exhaustion. CAPI delivers Gemini's array/object function-call arguments as **flattened indexed keys** (for example an `edit` call arrives as `{ "edits[0].newText": "...", "edits[0].oldText": "...", "path": "..." }`), and Atomic only reconstructed them at tool **execution** time — the persisted assistant message kept the raw flattened keys. On the next turn that message was replayed verbatim, CAPI parsed those literal keys straight into the Gemini `FunctionCall.Args`, and the resulting call no longer matched the tool's declared schema (nor the structure Gemini originally signed), so Gemini ended the turn with `MALFORMED_FUNCTION_CALL` / `UNEXPECTED_TOOL_CALL` / `OTHER` — all of which CAPI maps to a bare OpenAI `finish_reason: "error"`. Atomic now also reconstructs flattened tool-call arguments on the **outbound replay payload** for GitHub Copilot Gemini: each replayed assistant `tool_calls[].function.arguments` is unflattened (reusing the same `unflattenGeminiToolArguments` logic with the tool's own parameter schema, looked up from the request `tools`) back into the nested arrays/objects Gemini produced, before the request reaches CAPI. This runs in the provider-payload pipeline after schema sanitization and alongside the `reasoning_opaque` restore, is gated to GitHub Copilot Gemini `openai-completions` models, fails open on non-JSON arguments, and is a no-op for already well-formed arguments — healing both new sessions and already-persisted transcripts that contain flattened Gemini tool calls.
- Reduced `github-copilot/*` Gemini `MALFORMED_FUNCTION_CALL` failures (surfaced as `finish_reason: "error"`) by emitting tool/function JSON Schemas in the shape Gemini resolves most reliably. The Gemini schema sanitizer now infers an explicit `type` on container nodes that omit one (`properties`/`required` ⇒ `object`, `items` ⇒ `array`) and collapses a tuple-form `items` array — which Gemini's single-`items` function-declaration schema rejects — into a single (most expressive object/array) schema. Gated to `github-copilot` Gemini `openai-completions` models and applied last in the provider-payload pipeline, so every other provider/model payload is unchanged.
- Fixed `github-copilot/*` Gemini tool calls with **nested object arguments but no arrays** still failing validation and looping. CAPI flattens such arguments to purely dotted keys (for example `{ "metadata.confidence": 0.5 }` with no bracket index anywhere), which the previous reconstruction — gated on the presence of a `name[<digit>]` bracket key — skipped, so the nested-object call never validated. Atomic now also reconstructs purely dotted keys, disambiguated by the tool's own parameter schema: a dotted key is split into a nested path only when its head segment names an object/array container property (including container branches of an `anyOf`/`oneOf` union), so legitimate argument keys that happen to contain a dot are left intact. Bracket-indexed reconstruction is unchanged, and the transform remains gated to GitHub Copilot Gemini models and a no-op for well-formed arguments.
- Hardened the GitHub Copilot Gemini tool-argument reconstruction against prototype pollution. `unflattenGeminiToolArguments` previously walked model-emitted key paths into a fresh object without guarding `__proto__`/`constructor`/`prototype`, so a steered Gemini tool call mixing a bracket key with e.g. `__proto__.polluted` could reach and mutate `Object.prototype` process-wide. Reconstruction now drops any key whose parsed path contains one of those segments (at any position, including the final segment and a literal plain key). The parse/assign/compact reconstruction (and this single guard) lives in one canonical module shared with the `@bastani/mcp` `callTool` normalizer, so the two implementations can no longer diverge on the fix.
- Scoped the GitHub Copilot Gemini `content_filter` retry. The earlier finish-reason retry change treated `finish_reason: "content_filter"` as retryable for **every** provider/model; a genuine `content_filter` safety block on a non-Gemini provider would therefore be re-issued up to `maxRetries` times before its inevitable failure. `content_filter` is now retried only for GitHub Copilot Gemini models (where CAPI maps spurious Gemini RECITATION/safety blocks to it); a bare `finish_reason: "error"` remains retryable for all providers as a generic transient failure.
- Fixed RPC unknown-command errors to include the request id so RPC clients do not hang waiting for a response.
- Fixed `/model` autocomplete and model-selection searches to match provider/model queries regardless of whether the provider or model token is typed first.
- Fixed the tree navigator to horizontally pan deep entries so the selected item remains readable.
- Fixed long-context selection for GitHub Copilot's rounded 1M model names: requesting `1m` now selects the advertised full context window when the catalog exposes it, and otherwise resolves to the largest advertised long-context window at or below the request (for example `936k` for sparse catalog payloads) instead of falling back to the short `200k` tier. Interactive/context-picker persistence now writes the effective selected budget to per-model `defaultContextWindows["provider/modelId"]` settings instead of the global `defaultContextWindow` fallback, so Copilot-specific prompt caps such as `936k`/`922k` do not leak into Anthropic, Cursor, or other providers on restart. Legacy/stale global `defaultContextWindow` values from earlier builds are now treated as optional fallbacks and ignored without warning when unsupported by the active model.
- Suppressed the stale-update nag on versionless dev/source-tree builds. Because `main` (and direct runs like `bun packages/coding-agent/src/cli.ts`) reads the `0.0.0` placeholder from `package.json`, the startup version check always saw `0.0.0` as older than every published release and popped an "update available" banner on every dev run. The automatic startup check now treats `0.0.0` as a dev build and skips both the comparison and the npm registry call; the explicit `atomic update` command is unchanged.
- Fixed a GitHub Copilot context-window warning on restart: after selecting a long-context window (e.g. `claude-opus-4.8` → `936k`) and reopening Atomic, startup validated the persisted selection before the (async, auth-gated) Copilot catalog loaded, so the model still looked limited to its default window and Atomic warned “Context window 936k is not supported… Supported values: 200k” and reset the choice. The model registry now seeds the Copilot context-window catalog synchronously from its on-disk cache at construction (ignoring the refresh TTL, gated on a `github-copilot` credential), so a returning user's selection is recognized immediately while the live refresh still runs in the background ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Fixed context-window startup, session-switch, settings, and RPC edge cases: unknown provider fallback models no longer inherit selectable context-window options from provider defaults, fatal startup diagnostics no longer persist context-window settings, `AgentSession.setModel()` preserves an incoming target model's explicit selected context window, model-switch paths that change effective context windows now notify listeners via `context_window_changed`, the interactive context-window picker keys selection on raw token counts so colliding formatted labels never change which window is selected, RPC `set_model` returns the effective post-switch session model, and explicit startup `contextWindow` selections are journaled even when they equal the model scalar default ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Fixed `AgentSession.setContextWindow()` so bare SDK/runtime calls update the active session, append `context_window_change`, and emit `context_window_changed` without persisting settings; callers must pass `{ persistDefault: true }` to update the active model's `defaultContextWindows["provider/modelId"]` setting ([#1409](https://github.com/bastani-inc/atomic/issues/1409)).
- Fixed `packages/coding-agent` source-CLI subprocess tests (`session-id-readonly`, `startup-session-name`, `stdout-cleanliness`) crashing with `ERR_MODULE_NOT_FOUND` (for example `src/core/tools/oversized-tool-result.js`) when the Vitest worker pool runs under Node. They now launch the TypeScript source CLI with Bun explicitly via a `bunExecutable()` helper (matching `context-window-cli`/`rpc-context-window`) instead of assuming `process.execPath` is Bun, so the package test suite is portable across environments. The repo-wide `.js`->`.ts` source-import convention and shipped `dist/` are unchanged ([#1419](https://github.com/bastani-inc/atomic/issues/1419)).
- Fixed a credential-store **load failure being misreported as `No API key found`**. When a fresh `AuthStorage` could not read `auth.json` (for example it was briefly locked by a concurrent process, surfacing an `ELOCKED` error), `reload()` recorded the error but left an empty in-memory credential set, and the prompt preflight then threw `No API key found for <provider>` — even though the credentials existed on disk. `AuthStorage` now exposes `getLoadError()`, and the prompt preflight surfaces the real load failure (`Could not load stored credentials for <provider>: …`, with the original error attached as `cause`) instead of claiming the key is absent, so a transient store-read failure is no longer indistinguishable from genuinely missing credentials. The message intentionally still reads as a recoverable auth failure so model fallback keeps retrying ([#1431](https://github.com/bastani-inc/atomic/issues/1431)).
- Fixed `createAgentSession()` constructing a throwaway `AuthStorage` even when a `modelRegistry` was supplied. Because `AuthStorage` eagerly calls `reload()` in its constructor — taking the `auth.json` file lock — building one only to discard it added redundant lock contention on every session creation. `createAgentSession()` now only creates an `AuthStorage` when neither a `modelRegistry` nor an `authStorage` is provided, so callers that reuse one registry across sessions (such as workflow stage model fallback) no longer trigger an extra contended credential reload per session ([#1431](https://github.com/bastani-inc/atomic/issues/1431)).
- Fixed the remaining `auth.json` **lock-contention hard failure** under many concurrent sessions (for example a workflow that fans out parallel stages through model fallback). `AuthStorage.reload()` previously acquired the exclusive `proper-lockfile` write lock just to *read* `auth.json`, and its sync acquisition (`acquireLockSyncWithRetry`) used a 200 ms **event-loop-blocking busy-wait**; when one stage held the lock across an async OAuth token refresh, sibling stages busy-waited (starving the very event loop the holder needed to release), gave up with `ELOCKED`, and recorded a credential load failure. With the #1431 message fix in place this no longer misreported as `No API key found`, but it could still burn a stage's configured fallback candidates (each skipped as a recoverable auth error) until the chain exhausted and the stage hard-failed. Pure reads are now **lock-free**: `AuthStorageBackend` gains an optional `read()` method (built-in backends implement it; custom backends that omit it fall back to the previous locked read, so the released interface stays compatible) and `reload()` uses it without taking any lock, while writers persist `auth.json` **atomically** (sibling temp file + `rename`) so a lock-free reader always observes a complete previous-or-next snapshot, never a torn one. The exclusive lock is retained only for read-modify-write paths (credential `set`/`remove` and locked OAuth refresh), and file permissions stay `0600`. Concurrent session creation no longer contends on or is starved by the credential store ([#1431](https://github.com/bastani-inc/atomic/issues/1431)).

## [0.8.30] - 2026-06-17

### Changed

- Bumped the bundled upstream pi runtime libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.79.3` to `^0.79.4`, added `semver` as an explicit runtime dependency for package/version checks, and aligned companion extension peer ranges so Atomic inherits upstream provider/model metadata updates, agent-core fixes, the new terminal background-color query used by first-run theme detection, and shared TUI wrapping/keyboard compatibility fixes.
- Clarified `models.json` value-resolution docs for Atomic's intentional divergence from upstream pi v0.79.4: legacy uppercase env-var-like provider values are still migrated to explicit `$VAR` syntax on startup when the same-named environment variable is present, while new configs should use `$VAR`/`${VAR}` explicitly and non-migrated values remain literals.
- Changed package install/update handling to respect npm semver ranges instead of forcing ranged package specs to `latest`, preserve configured npm specs during batch updates, locate pnpm global package roots when `pnpm root -g` is unavailable, and exit immediately after package commands complete.
- Updated extension docs and examples for the upstream v0.79.4 API clarifications: `pi.getActiveTools()` returns active tool names while `pi.getAllTools()` returns metadata including `promptGuidelines`, long-lived resources should be opened from session-scoped hooks and cleaned up in `session_shutdown`, and question/questionnaire example UIs wrap long prompts and option descriptions without truncation.
- Removed the bundled subagent acceptance and no-mutation completion-gate behavior from the CLI-visible subagent extension: no acceptance field, inferred gate, acceptance-report prompt injection/parsing, completionGuard config, or acceptance/completion-guard status display remains. Migration guidance now directs users to remove stale acceptance fields from subagent calls/chains/parallel items and move validation requirements into task text; JSON chain rewrites drop legacy acceptance entries ([#1398](https://github.com/bastani-inc/atomic/issues/1398)).
- Replaced the verbatim context-compaction planner's fixed turn cap and mode split with a deterministic strict reduction loop driven by configurable compaction parameters: `compression_ratio` (fraction to keep, default `0.5`), `preserve_recent` (default `2`), and `query` (explicit or auto-detected). The prompt substitutes the effective ratio-derived target as a hard completion requirement, hooks receive the parameters through `event.parameters`/`preparation.parameters`/`result.parameters`, extension `ctx.compact()` can override them per run, the runtime exits only once validated deletion stats meet the target, and premature plain-text exits receive an automatic nudge to keep removing message entries/content blocks. The planner still balances early search/read exploration with quick exploitation of high-confidence low-value deletion targets, the `context_compaction_budget` progress tool reports context-window fullness and remaining reduction work while inheriting the session's current model thinking level, and legacy parsing of deletion JSON from final assistant prose has been removed so only validated tool state can drive compaction.

### Fixed

- Fixed Anthropic/GitHub Copilot extended-thinking replay by repairing provider payloads so same-model `thinking` and `redacted_thinking` blocks are restored byte-for-byte after provider conversion and extension `before_provider_request` hooks, and by making compaction treat retained thinking-bearing assistant messages as all-or-nothing so sibling tool calls/text cannot be partially removed. This covers signed empty thinking blocks, sanitized thinking text, and compacted sessions that previously triggered 400 `thinking blocks ... cannot be modified` failures.
- Fixed post-compaction context usage accounting so the footer no longer trusts provider `totalTokens` when normalized usage components are available and avoids double-counting Anthropic-compatible cache buckets that mirror `input` tokens, preventing compacted sessions from displaying roughly doubled context-window percentages such as ~117% when the active prompt is closer to ~58%.
- Fixed package commands to drain stdout/stderr before the forced post-command exit so piped `atomic list`, help, progress, and self-update fallback output is not truncated under Node while still terminating leaked extension handles.
- Fixed custom `models.json` providers whose `apiKey` references an unset explicit environment variable so their models are omitted from `/model`, `--list-models`, available-model RPC responses, and automatic fallback candidates until the environment variable is configured.
- Fixed bash/child-process output draining so late stdout/stderr arriving after process exit continues draining while active, quiet inherited pipes release promptly, endlessly noisy detached descendants are bounded by a longer active-drain cap, and the built-in `bash` tool ignores output after its result accumulator has been finalized.
- Fixed npm package source resolution to re-resolve and re-validate the managed install path after installing a configured semver range, preventing stale legacy global package copies from being loaded.
- Fixed explicit npm dist-tag package sources such as `npm:pkg@beta` and `npm:pkg@latest` so online resolution verifies installed copies against the resolved registry target before accepting them, while offline resolution keeps already-installed tag-based resources usable without attempting registry or install work.
- Fixed interactive shutdown ordering so SIGTERM/SIGHUP-triggered exits keep signal handlers installed until terminal cleanup and extension `session_shutdown` disposal finish, preventing terminal restoration from being skipped during signal-exit handling.
- Fixed first-run theme selection to query the terminal OSC 11 background color before falling back to `COLORFGBG`, persist high-confidence auto-detected dark/light themes, and wrap `/tree` help rows by semantic chunks instead of truncating keybinding hints.
- Fixed release publishing to generate and upload a `SHA256SUMS` asset alongside the six Atomic binary archives.
- Fixed workflow stages launched from workflows discovered through `atomic -e` to inherit the parent chat's custom resource-loading snapshot (extensions/tools, subagents and agent definitions, skills, prompt templates, themes, workflows, packages, and trusted borrowed project-local resources) without sharing the parent resource-loader instance.
- Fixed context compaction recent-entry and id-only deletion guards so `context_delete` attempts against disallowed context entries now return explicit non-terminating correction errors, exact deletion payloads with transcript text or replacement content are rejected instead of ignored, `context_grep_delete` silently ignores rejected matches while deleting allowed matches without counting rejected blocks as removals, and `context_grep_delete` keeps `maxMatches` scoped to one tool call without adding any cumulative deletion cap.
- Fixed bundled workflow and subagent structured-output gates to recover from missing or invalid `structured_output` final answers by issuing up to three corrective retries that echo the actual contract or schema-validation error before failing.
- Fixed bundled workflow failed-stage metadata so error-stage transcripts remain discoverable and follow-up messaging resumes from the failed conversation instead of resetting to an empty session.
- Fixed context compaction so older assistant `thinking` and `redacted_thinking` blocks can be removed like other stale blocks, while `thinking` or `redacted_thinking` blocks in the latest assistant message remain rejected by validation and paired tool-result restoration still preserves active context integrity ([#1386](https://github.com/bastani-inc/atomic/issues/1386)).
- Fixed bundled workflow graph rendering/runtime state for limited-concurrency `ctx.parallel` fan-outs so queued branches now keep sibling parentage after earlier branch failures.
- Fixed context compaction to universally protect every content block in the latest retained assistant message when that message contains `thinking` or `redacted_thinking`, so `context_delete` and `context_grep_delete` cannot remove visible sibling blocks or make an older partially-filtered thinking-bearing assistant become the latest retained assistant ([#1405](https://github.com/bastani-inc/atomic/issues/1405)).

## [0.8.29] - 2026-06-15

### Added

- Added support for local `-e <dir>` extension sources to borrow project-local Atomic resources from `<dir>/.atomic`, legacy `<dir>/.pi`, and `<dir>/.agents/skills` after resolving trust for that extension source, preserving package-manager provenance and explicit-path workflow forwarding while avoiding untrusted borrowed resources ([#1354](https://github.com/bastani-inc/atomic/issues/1354)).
- Added a prototype Rust/N-API Cursor HTTP/2 native transport binding through the generated `@bastani/atomic-natives` NAPI-RS package, so Atomic can use an in-process native HTTP/2 client without requiring Node.js on `PATH`.
- Added the experimental bundled `@bastani/cursor` provider scaffold so `/login` can offer Cursor OAuth, estimated fallback exposes `cursor/composer-2`, and Cursor model mapping/streaming hooks are available behind an isolated HTTP/2 Connect transport boundary with production-default protobuf decoding, buffered Connect frames, write-before-headers Run streaming, stable Cursor conversation ids, schema-correct Cursor MCP tool advertisement, Cursor MCP tool-call decoding with protobuf `Value` or raw UTF-8/JSON arguments and exec-id metadata, same-stream MCP tool-result resume, abort/idle cleanup for paused tool streams, Connect end-stream error classification, exact live model id fidelity without static default injection, fast/thinking catalog grouping, and usage-delta accumulation.
- Added the opt-in `createStructuredOutputTool({ schema, capture, output, name })` factory for terminating machine-readable final answers with direct schema-as-parameters capture, flat `details`, in-process capture, configurable private file capture via `output.outputPath`, and the concise two-line `structured_output` prompt guidance from `pi-dynamic-workflows` without registering `structured_output` in normal agent sessions by default ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).

### Changed

- Changed the bundled builtin `ralph` workflow to run `/skill:prompt-engineer` prompt-engineering and `/skill:research-codebase` research before orchestration instead of starting with an RFC/planner stage, pass the research artifact as primary implementation context, reuse prior research session data on follow-up loops, and feed unresolved reviewer findings into later research passes ([#1371](https://github.com/bastani-inc/atomic/issues/1371)).
- Changed bundled `goal`, `ralph`, and `open-claude-design` decision gates to use schema-backed workflow `structured_output` stages instead of registering bespoke terminating custom tools.
- Changed bundled `goal` worker/reviewer prompts and `ralph` orchestrator/reviewer prompts to request end-to-end verification when practical, using browser-skilled subagents for web/frontend flows that may depend on backend/API behavior and tmux-skilled subagents for TUI or terminal-app scenarios.
- Bumped the bundled upstream pi runtime libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.79.1` to `^0.79.3`, bringing the latest upstream provider, model, agent-core, and TUI compatibility fixes into `@bastani/atomic`.
- Updated the structured-output extension example and SDK/workflow/extension docs to use the canonical factory instead of hand-rolled `terminate: true` wrappers, and documented that Atomic passes the supplied schema directly to the tool without additional structured-output parsing, object-root restrictions, or sidecar validation ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Made custom-named factory tools advertise the configured name in concise prompt metadata, and documented that text print mode recognizes factory-created custom structured-output tools without treating every terminating tool as printable ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Changed factory-created structured-output tool descriptions and prompt guidance to use context-neutral final-result wording for SDK, extension, workflow, and subagent registrations ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Clarified SDK, extension, and workflow guidance that structured-output tools are opt-in custom tools, with workflow stages/tasks/chains/parallel items receiving `structured_output` only when they declare a `schema` and subagent children receiving it only when `outputSchema` is enabled ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).

### Fixed

- Fixed the root `@bastani/atomic` package export to include a `default` condition alongside the ESM import target, improving compatibility with loaders that select default export conditions.
- Fixed extension custom UI focus deferral so full-screen overlays can keep keyboard focus while a parent/main-chat inline custom UI is pending, then focus that pending UI when the overlay is hidden; already-aborted custom UI calls no longer invoke factories or emit host custom-UI state changes ([#1353](https://github.com/bastani-inc/atomic/issues/1353)).
- Fixed text print mode to emit trailing terminating JSON from factory-created structured-output tools, including custom tool names such as `final_decision`, instead of only recognizing the canonical `structured_output` name; the same structured value remains available through `details`, capture sinks, and workflow/subagent structured result fields ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Fixed terminating `structured_output` results to opt out of oversized-result persistence ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Fixed cross-process structured-output file capture to preserve flat tool arguments in `output.json` without sidecar metadata or transcript-finality parsing ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Fixed bundled subagent handling so explicit empty `tools: []` plus `outputSchema` grants only the schema-backed `structured_output` runtime tool instead of restoring default tools ([#1350](https://github.com/bastani-inc/atomic/issues/1350)).
- Fixed prerelease publishing for native Atomic artifacts by allowing the `@bastani/atomic-natives` package metadata in release-preparation verification, running native artifact builds on architecture-matched Blacksmith and macOS runners, and documenting the two-package publish flow while keeping npm provenance publishing on GitHub-hosted Ubuntu.
- Fixed the bundled experimental Cursor provider to honor per-request stream deadlines across open/read/resume writes, reset timed-out or aborted streams, clean up replaced paused turns safely, catch cleanup cancellation failures, tolerate non-MCP Cursor exec protocol messages without ending assistant turns, and align Run requests with Cursor's private CLI protocol by using blob/KV conversation state plus request-context tool-definition responses without the unsupported custom system-prompt field ([#1286](https://github.com/bastani-inc/atomic/issues/1286)).
- Fixed release archive startup for the bundled experimental Cursor provider by declaring `@bufbuild/protobuf` as an `@bastani/atomic` runtime dependency, covering Cursor in the bundled-package dependency metadata guard, and smoke-checking Cursor/protobuf assets in native archives ([#1286](https://github.com/bastani-inc/atomic/issues/1286)).
- Fixed bundled `ralph` skill-prompt stages to invoke bundled skills through `/skill:<name>` expansion so prompt engineering and research stages receive the intended skill instructions.
- Fixed concurrent bundled workflow stage resource reloads to serialize temporary subagent child environment isolation so parallel stage startup cannot leave parent process child flags accidentally cleared.
- Fixed bundled workflow stage sessions to keep workflow package skills (`create-spec`, `impeccable`, `prompt-engineer`, `research-codebase`, and `skill-creator`) available while disabling only the recursive workflows extension in child sessions.
- Fixed bundled workflow stage resource discovery so bundled subagent definitions stay available, `subagent` is active by default with the same two-hop nesting budget as main chat, and explicitly allowlisted bundled extension tools such as `subagent`, `web_search`, `fetch_content`, and `intercom` remain visible even when a workflow is launched from a subagent child process.

### Security

- Kept Cursor credentials OAuth-only with token/header and PKCE poll-secret redaction and no localhost proxy, while moving Cursor HTTP/2 to a bundled Rust/N-API native binding and no longer sending the current working directory as `previousWorkspaceUris` by default.

## [0.8.28] - 2026-06-11

### Added

- Added optional inline free-form text entry to the `ask_user_question` TUI's **Chat about this** footer row. Non-empty typed chat text now returns as a `kind: "chat"` answer surfaced to the agent without the legacy stop/wait termination envelope, while empty submissions keep the existing sentinel behavior.
- Added session-scoped `bashPolicy` support for the built-in `bash` tool, with exact/prefix/command-string-glob/regex rules, deny-over-allow precedence, segment-aware parsing by default, fail-closed validation of invalid policies, and conservative rejection of compound heads, redirections, assignments, and non-literal command heads before shell execution.
- Ported the upstream project-trust store and resolver foundation: project trust decisions are remembered, `--approve`/`--no-approve` affect runtime trust state, untrusted sessions skip project-local extensions/resources/context/system-prompt discovery and refuse project-setting writes, startup migrations and project config reads are trust-gated, and a new `/trust` slash command with the upstream `TrustSelectorComponent` lets saved project-trust decisions be reviewed and changed in-session.
- Added upstream pi 0.76.0-0.79.1 coding-agent compatibility exports for package asset path helpers, CLI argument parsing (`Args`, `parseArgs`), `SettingsManagerCreateOptions`, image conversion (`convertToPng`), and RPC extension UI request/response types, plus the shared JSON comment/trailing-comma stripping utility used by model configuration migrations.
- Added the upstream `project-trust`, `git-merge-and-resolve`, `input-transform-streaming`, and Gondolin tool-routing example extensions adapted to Atomic package identity, shared `warnDeprecation`/`openBrowser` utilities, upstream `docs/security.md` and `docs/containerization.md` rebranded for Atomic, and extensive upstream regression coverage.

### Changed

- Changed Atomic compaction to be verbatim-only across manual `/compact`, automatic threshold/overflow compaction, SDK/RPC compaction, and extension-triggered compaction. All compaction now records validated `context_compaction` deletion targets and rebuilds active context with retained transcript content verbatim and unchanged; retained file paths, exact commands, error strings, and line numbers are never paraphrased.
- Changed compaction extension hooks (`session_before_compact`, `session_compact`) to receive verbatim context-compaction preparations/results and allow cancellation or locally validated deletion requests instead of custom generated summaries.
- Changed the verbatim compaction critical-overflow recovery prompt to evict in an explicit priority order (removable reasoning traces first, then removable user/custom/summary context) while preserving existing safety/retention rules ([#1308](https://github.com/bastani-inc/atomic/issues/1308)).
- Changed the bundled builtin `deep-research-codebase`, `goal`, `ralph`, and `open-claude-design` workflows to use `anthropic/claude-fable-5:xhigh` as the primary planner/reviewer/design model, demoting each previous primary to the head of the fallback chain ([#1345](https://github.com/bastani-inc/atomic/pull/1345)).
- Bumped the bundled upstream pi libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.78.1` to `^0.79.1`, bringing in Claude Fable 5 and Azure metadata updates, GPT-5 token/context metadata fixes, provider thinking-payload compatibility updates, autocomplete/CJK prompt rendering fixes, and keyboard-protocol fallback improvements.
- Ported upstream prompt-template argument default handling and added `${N:-default}` positional default support in prompt templates, matching upstream slash-template substitution behavior without recursively expanding argument/default values.

### Fixed

- Fixed oversized tool-call results flooding model context by persisting large results to disk (`<sessionDir>/tool-results/<toolCallId>.txt`) and returning a compact `<persisted-output>` message with the file path and a 2KB head preview when a result exceeds the 50,000-character system cap or a lower per-tool cap; tools can opt out via `maxResultSizeChars: Infinity`, and persistence degrades gracefully for images or write failures ([#1322](https://github.com/bastani-inc/atomic/issues/1322)).
- Fixed the Read tool to block text file-read results above 50,000 characters and return incremental-read guidance, including byte-slice guidance for oversized single-line selections ([#1323](https://github.com/bastani-inc/atomic/issues/1323)).
- Fixed `AgentSession.prompt` surfacing the confusing `No API key found for undefined` error when a model never resolved to a real provider; the prompt path now fails fast with a clear `Unknown model: "<id>" did not resolve to an available provider` message.
- Hardened prompt-template argument substitution against polynomial-time regex backtracking (ReDoS) by length-bounding the `${N:-default}` default-value capture.
- Fixed provider auth-status reporting for explicit `$ENV_VAR` config values, preserved uppercase literal credentials during config-value migrations (including legacy `~/.pi/agent` roots), preserved `models.json` JSONC comments/formatting during migration, and accepted the upstream `supportsDeveloperRole` flag for custom OpenAI Responses models.
- Fixed RPC client requests to reject promptly when the child agent process exits or its stdio fails, completed the RPC-mode output/backpressure and `excludeFromContext` bash-command port, and preserved steering/follow-up queue modes across extension-triggered RPC session reloads.
- Fixed SDK provider stream options so HTTP idle timeouts and WebSocket connect timeouts from settings are forwarded to provider streams while preserving per-request overrides.
- Fixed interactive startup input handling so prompts submitted before the main input loop is installed are queued instead of dropped, captured command-like and ordinary startup submissions replay in original input order without later prompts leapfrogging earlier commands, prompts typed behind an active startup command stay behind that command even while a prior prompt is streaming, and pre-session selectors or confirmations such as `--resume` and cross-project `--session` no longer leak typed text into chat input.
- Fixed the initial `--resume` session picker and all-sessions pane to honor a custom `--session-dir`, and fixed signal-triggered shutdown ordering so extension `session_shutdown` cleanup runs before terminal restore writes.
- Fixed plain metadata commands (`--version`, `--help`, `--list-models`) to keep their output on stdout for scripts/completions while keeping auto-install/startup chatter off stdout.
- Fixed OAuth login dialog prompt/manual input rendering so submitted values remain stable, auth storage writes to consistently use `0600` file mode, and self-update command generation to bypass package-manager minimum-release-age delays.
- Fixed changelog link normalization to produce Atomic repository/tag-pinned links from local package links and legacy pi-mono URLs, wired into startup and `/changelog` output.
- Fixed WSL repositories on Windows-mounted paths to poll Git `HEAD` changes so the footer branch display updates reliably, plus footer cache-hit-rate display, settings selector default project-trust editing, tool self-render image rendering, and collapsed tool-output hint styling.
- Rebranded provider attribution headers (OpenRouter, NVIDIA NIM) and the Gondolin VM session label to Atomic identity, matched OpenRouter-compatible custom endpoints by exact hostname, and corrected README/RPC/session-format/SDK/example docs to use `atomic`, `ATOMIC_*`, and `.atomic` as primary with legacy `PI_*`/`.pi` labeled as such.
- Fixed extension command contexts to expose live base system-prompt options, hid `streamingBehavior` from idle input handlers, continued agent turns for follow-ups queued during `agent_end` handlers, and ported upstream tool path rendering with terminal hyperlink support for edit tool output.

### Removed

- Removed the legacy summary-compaction runtime path, summary prompts, `CompactionEntry` active-context injection, `CompactionSummaryMessage` active message type, custom compaction instructions (`CompactOptions.customInstructions`, RPC `compact.customInstructions`, `/compact [instructions]`), `compaction.keepRecentTokens` setting, summary-compaction public exports, and summary-compaction docs and examples. Historical `type:"compaction"` JSONL lines on disk are inert and are not injected into active LLM context.

### Security

- Bumped the transitive `shell-quote` dependency from `1.8.3` to `1.8.4` in the `examples/extensions/sandbox` lockfile, resolving the critical advisory [GHSA-w7jw-789q-3m8p](https://github.com/advisories/GHSA-w7jw-789q-3m8p).

## [0.8.28-alpha.4] - 2026-06-11

### Changed

- Changed the bundled builtin `deep-research-codebase`, `goal`, `ralph`, and `open-claude-design` workflows to use `anthropic/claude-fable-5:xhigh` as the primary planner/reviewer/design model, demoting each previous primary (`openai-codex/gpt-5.5:xhigh` or `github-copilot/claude-opus-4.8:xhigh`) to the head of the fallback chain ([#1345](https://github.com/bastani-inc/atomic/pull/1345)).

## [0.8.28-alpha.3] - 2026-06-11

### Added

- Added optional inline free-form text entry to the `ask_user_question` TUI's **Chat about this** footer row. Non-empty typed chat text now returns as a `kind: "chat"` answer and is surfaced to the agent without the legacy stop/wait termination envelope, while empty submissions keep the existing sentinel behavior.
- Added session-scoped `bashPolicy` support for the built-in `bash` tool, with exact/prefix/command-string-glob/regex rules, deny-over-allow precedence, segment-aware parsing by default, newline command separators, conservative reserved/compound-head, leading-redirection, attached command-head redirection, assignment, and non-literal command-head rejection, escaped glob bracket-class literal preservation, invalid glob range handling through `invalid-policy`, non-leading `>|` noclobber redirection handling, unknown top-level policy key rejection, runtime invalid-policy fail-closed validation, default-allow no-rule compatibility, and enforcement before shell execution.

## [0.8.28-alpha.2] - 2026-06-10

### Security

- Bumped the transitive `shell-quote` dependency from `1.8.3` to `1.8.4` in the `examples/extensions/sandbox` lockfile, resolving the critical advisory [GHSA-w7jw-789q-3m8p](https://github.com/advisories/GHSA-w7jw-789q-3m8p) (`shell-quote` `quote()` does not escape newlines in object `.op` values). The bump stays within `@anthropic-ai/sandbox-runtime`'s existing `^1.8.3` range.

### Added

- Added upstream pi 0.76.0-0.79.1 coding-agent compatibility exports for package asset path helpers (`getPackageDir`, bundled asset/documentation/example paths), CLI argument parsing (`Args`, `parseArgs`), `SettingsManagerCreateOptions`, image conversion (`convertToPng`), and RPC extension UI request/response types so extensions can reference the same public APIs as upstream while retaining Atomic package identity.
- Added the shared JSON comment/trailing-comma stripping utility used by model configuration migrations, aligning Atomic with upstream's safer config-value migration path.
- Added the upstream project-trust selector UI (`/trust` slash command and `TrustSelectorComponent`) so saved project-trust decisions can be reviewed and changed inside an interactive session, plus the corresponding upstream component test.
- Added the upstream `project-trust`, `git-merge-and-resolve`, and `input-transform-streaming` example extensions, adapted to Atomic package identity and config directory conventions.
- Added upstream `docs/security.md` and `docs/containerization.md`, rebranded to Atomic naming, config directories, environment variables, and package identity, and wired them into the docs navigation.
- Added the upstream Gondolin tool-routing example extension under `examples/extensions/gondolin`, rebranded for Atomic usage/imports so the new containerization guide points at a shipped example, and kept host shell environment secrets out of guest VM tool execution.
- Added shared `warnDeprecation` and `openBrowser` utilities from upstream and routed `registerProvider` legacy env-var migration and OAuth browser-launch through them.
- Added upstream regression coverage for experimental feature gating, config-value env-var syntax migration (`auth.json`/`models.json` and `registerProvider`), the ported example extensions, startup input buffering, resume-command formatting, changelog link normalization, RPC child-process exits, SDK stream options, read-only session-id handling, startup session naming, exclude-tools behavior, signal shutdown cleanup, and extension OAuth prompt input stability.

### Changed

- Bumped the bundled upstream pi libraries `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` from `^0.78.1` to `^0.79.1`, bringing in upstream AI/model and TUI fixes such as Claude Fable 5 and Azure metadata updates, GPT-5 token/context metadata fixes, provider thinking-payload compatibility updates, autocomplete/CJK prompt rendering fixes, and keyboard-protocol fallback improvements.
- Ported upstream prompt-template argument default handling so programmatic callers can omit explicit prompt paths and default-inclusion options while existing Atomic resource-loading behavior remains explicit and unchanged.
- Added `${N:-default}` positional default support in prompt templates, matching upstream slash-template substitution behavior without recursively expanding argument/default values.

### Fixed

- Fixed oversized tool-call results flooding model context by persisting large results to disk instead of returning them inline. Following the Claude Code tool-result storage convention, when a tool result's text content exceeds the system-wide cap of 50,000 characters (`DEFAULT_MAX_RESULT_SIZE_CHARS`) or a lower per-tool character cap, the full output is written to a session-scoped file (`<sessionDir>/tool-results/<toolCallId>.txt`) and the model receives a compact `<persisted-output>` message containing the file path plus a 2KB preview of the head of the output instead of the full payload. Tools can declare `maxResultSizeChars: Infinity` to opt out when they already self-bound model-visible output and provide their own full-output file; the built-in `bash` and `read` tools use that opt-out. Writes use the `wx` flag so repeated/replayed calls reuse the existing file (idempotent), size labels are reported in UTF-8 bytes while the threshold remains character-based, and persistence degrades gracefully — image results and any result that cannot be written to disk are returned unchanged. Tool `details` metadata is passed through untouched ([#1322](https://github.com/bastani-inc/atomic/issues/1322)).
- Fixed the Read tool to block text file-read results above 50,000 characters (matching the mehmoodosman/claude-code `DEFAULT_MAX_RESULT_SIZE_CHARS` limit) and return incremental-read guidance, including byte-slice guidance for oversized single-line selections, instead of inserting oversized file contents into model context ([#1323](https://github.com/bastani-inc/atomic/issues/1323)).
- Hardened the prompt-template argument substitution against polynomial-time regex backtracking (ReDoS) by length-bounding the `${N:-default}` default-value capture when scanning template content read from disk (an Atomic divergence from upstream; the bound far exceeds any realistic default value).
- Fixed the containerization guide to link the Gondolin example extension via an absolute repository URL instead of a relative `../examples/...` path so docs link validation passes.
- Fixed custom OpenAI Responses model validation to accept the upstream `supportsDeveloperRole` compatibility flag in `models.json`.
- Fixed provider auth-status reporting for explicit `$ENV_VAR` config values so missing environment variables are reported as unconfigured instead of as literal `models.json` API keys.
- Rebranded provider attribution headers for OpenRouter and NVIDIA NIM so Atomic sends Atomic identity rather than upstream Pi identity when install telemetry is enabled, matched OpenRouter-compatible custom endpoints by exact hostname rather than substring, and rebranded the Gondolin VM session label.
- Fixed config-value migrations to cover legacy `~/.pi/agent` config roots that Atomic still reads as compatibility fallbacks while preserving uppercase literal credentials when no matching environment variable exists and preserving `models.json` JSONC comments/formatting while rewriting only the intended `apiKey`/header fields.
- Fixed the initial `--resume` session picker so its all-sessions pane honors a custom `--session-dir`.
- Fixed interactive startup input handling so prompts submitted before the main input loop is installed are queued instead of dropped.
- Fixed RPC client requests to reject promptly when the child agent process exits or its stdio fails, and completed the RPC-mode output/backpressure and `excludeFromContext` bash-command port so high-volume JSONL output is flushed before continuation/shutdown.
- Fixed SDK provider stream options so HTTP idle timeouts and WebSocket connect timeouts from settings are forwarded to provider streams, while preserving per-request overrides.
- Fixed self-update command generation to bypass npm/pnpm/Bun minimum-release-age delays when reinstalling Atomic globally and documented the newly ported session, tool exclusion, and project-trust CLI flags in public usage docs/README.
- Fixed auth storage writes to consistently create/update `auth.json` with `0600` file mode.
- Fixed WSL repositories on Windows-mounted paths to poll Git `HEAD` changes so the footer branch display updates reliably.
- Fixed branch summarization to use the session stream function for provider request behavior parity and to count image content during token estimation.
- Fixed footer cache-hit-rate display, settings selector default project-trust editing, tool self-render image rendering, and collapsed tool-output hint styling.
- Fixed extension command contexts to expose live base system-prompt options, hid `streamingBehavior` from idle input handlers, and continued agent turns for follow-ups queued during `agent_end` extension handlers.
- Fixed signal-triggered interactive shutdown ordering so extension `session_shutdown` cleanup runs before terminal restore writes, and interactive quits print a resume command for persisted sessions.
- Fixed OAuth login dialog prompt/manual input rendering so submitted values remain stable after later prompts reuse the input component.
- Fixed changelog link normalization to produce Atomic repository/tag-pinned links from local package links and legacy pi-mono repository URLs using Atomic's unprefixed release tags, and wired normalization into startup and `/changelog` display output.
- Fixed custom model/provider/extension docs and API comments to use explicit `$ENV_VAR` config-value syntax now that bare uppercase strings are treated as literals, clarified that legacy uppercase env-like auth values are migrated only when the referenced environment variable is present during migration, restored real provider authentication environment variable names such as `ANTHROPIC_API_KEY`/`AZURE_OPENAI_API_VERSION`, corrected README/RPC/session-format/SDK/example path branding to use `atomic`, `ATOMIC_*`, and `.atomic` as primary with `PI_*`/`.pi` labeled legacy, removed the nonexistent `ATOMIC_CACHE_RETENTION` README entry in favor of the real provider-specific `PI_CACHE_RETENTION` knob, fixed README Atomic package anchors and package CLI examples, updated preset/sandbox/subagent examples to load project config from `.atomic` before legacy `.pi` and only after `ctx.isProjectTrusted()` approves project-local resources where applicable, documented context files as project-trust inputs in the security guide, documented the new `project_trust` extension event, `ctx.isProjectTrusted()`, and shipped project-trust/Gondolin/streaming-input examples, and updated README philosophy/example comments so Atomic's bundled workflows, subagents, MCP, web access, intercom, and todo capabilities are documented as shipped first-party features rather than absent upstream-Pi capabilities.
- Fixed extension-triggered RPC session reloads to preserve the current steering and follow-up queue modes across reload.
- Fixed plain metadata commands (`--version`, `--help`, and `--list-models`) to keep their output on stdout for scripts/completions, while package auto-install/startup chatter is kept off stdout and explicit non-interactive modes such as `-p --help` and `--mode json --help` continue routing all output away from stdout.
- Fixed the resume session picker so all-session listing respects a custom `--session-dir` instead of falling back to the default global session directory.
- Ported upstream tool path rendering support for edit tool output, including terminal hyperlink support when available, and accompanying upstream regression coverage for argument parsing, extension input events, session file operations, syntax highlighting, and related helpers.
- Fixed the ported coding-agent package tests by adding upstream CLI parsing and runtime wiring for named sessions, exact session ids, project trust approval overrides, and tool exclusion; extension runner context mode/project-trust APIs and input streaming behavior; prompt-guideline exposure in tool metadata; safer export-HTML markdown URL sanitization; Ant Ling default model selection; syntax highlight mappings for regex/diff scopes; custom session-id validation and propagation; cwd-scoped flat session listing; and streaming session-file reads for very large transcripts.
- Ported the upstream project-trust store and resolver foundation so project trust decisions can be remembered, `--approve`/`--no-approve` affects runtime trust state, extension `ctx.isProjectTrusted()` reflects the active decision instead of always reporting trusted, startup settings and project-local migrations are gated before project config is read, first-run interactive or extension project-trust approval immediately reruns trusted project migrations for the current startup, `defaultProjectTrust: "always"` is honored before startup project migrations/session lookup, untrusted sessions bootstrap without loading project-local extensions/resources, project-setting writes are refused while untrusted, trust-store lock acquisition retries transient contention, untrusted sessions skip project-local context/system prompt discovery, context-only projects with `AGENTS.md`/`CLAUDE.md` are trust-gated instead of implicitly trusted, reload-created project config can persist implicit startup trust for future sessions, `/trust` saves decisions through the active runtime agent directory, legacy `.pi` project config roots are gated behind trust alongside `.atomic`, user-global `~/.agents/skills` no longer makes every child project require project approval, read-only help/model-list commands avoid interactive trust prompts, session-only trust decisions persist across runtime reloads in the current process, stored trust decisions can still be reviewed by pre-trust extension handlers, pre-trust extensions are reused for the final extension set instead of initialized twice, interactive session switches use the active TUI context for trust prompts, package/config commands now resolve project trust before reading or writing project package settings, and manager-level project package install/remove operations refuse to touch project package storage while untrusted.
## [0.8.28-alpha.1] - 2026-06-09

### Changed

- Changed Atomic compaction to be verbatim-only across manual `/compact`, automatic threshold/overflow compaction, SDK/RPC compaction, and extension-triggered compaction. All compaction now records validated `context_compaction` deletion targets and rebuilds active context with retained transcript content verbatim and unchanged. Retained file paths, exact commands, error strings, and line numbers are never paraphrased or rewritten.
- Changed compaction extension hooks (`session_before_compact`, `session_compact`) to receive verbatim context-compaction preparations/results and allow cancellation or locally validated deletion requests instead of custom generated summaries. The before-compact hook now yields `ContextCompactionPreparation` and accepts `{ cancel: true }` or `{ deletionRequest }` returns; the after-compact hook now receives `ContextCompactionResult` and `contextCompactionEntry`.
- Changed the verbatim compaction critical-overflow recovery prompt to evict in an explicit priority order when context still exceeds the token budget after compaction: removable reasoning traces are evicted first, then removable user/custom/summary context. Existing safety/retention rules (recent entries, unresolved errors, failed commands, and at least one task-bearing entry) are preserved ([#1308](https://github.com/bastani-inc/atomic/issues/1308)).

### Fixed

- Fixed `AgentSession.prompt` surfacing the confusing `No API key found for undefined` error when a model never resolved to a real provider (for example an unknown/unresolved model id reaching the prompt path as a bare string). The prompt path now fails fast with a clear `Unknown model: "<id>" did not resolve to an available provider` message, and `No API key found` guidance no longer renders a literal `undefined` provider.

### Removed

- Removed the legacy summary-compaction runtime path, summary prompts, `CompactionEntry` active-context injection, `CompactionSummaryMessage` active message type, custom compaction instructions (`CompactOptions.customInstructions`, RPC `compact.customInstructions`, `/compact [instructions]`), `compaction.keepRecentTokens` setting, summary-compaction public exports (`CompactionResult`, `CompactionPreparation`, `appendCompaction()`, `prepareCompaction()`, `generateSummary()`, summary `compact()`), and summary-compaction docs and examples. Historical `type:"compaction"` JSONL lines on disk are inert and are not injected into active LLM context.

## [0.8.27] - 2026-06-08

### Fixed

- Fixed `/compact` and auto-compaction regressions by removing the native `better-sqlite3` dependency from transcript-bound deletion tools and preserving the currently selected reasoning level for the compaction planner ([#1310](https://github.com/bastani-inc/atomic/issues/1310)).

## [0.8.27-alpha.1] - 2026-06-08

### Fixed

- Fixed `/compact` and auto-compaction regressions by removing the native `better-sqlite3` dependency from transcript-bound deletion tools and preserving the currently selected reasoning level for the compaction planner ([#1310](https://github.com/bastani-inc/atomic/issues/1310)).

## [0.8.26] - 2026-06-08

### Added

- Added deletion-only transcript compaction as the default `/compact` behavior, preserving retained transcript content verbatim while validating model-proposed logical deletion targets.
- Added session entries and expandable summary-card rendering for completed context compaction results.

### Changed

- Improved Windows cold startup by lazily loading bundled web-access, intercom, and MCP implementation modules, deferring readiness checks until after the first interactive frame, and adding detailed startup timing spans ([#1223](https://github.com/bastani-inc/atomic/issues/1223)).
- Updated automatic compaction and manual `/compact` documentation to describe transcript-bound Verbatim Compaction, validated logical deletion targets, critical overflow behavior, and legacy summary-compaction settings.
- Documented npm and pnpm installation options in Atomic docs and limited Mintlify validation to pull requests ([#1294](https://github.com/bastani-inc/atomic/pull/1294)).
- Updated builtin `ralph` workflow docs to describe the safe default for PR creation, `create_pr=true` opt-in examples, omitted disabled `pr_report`, and final-stage-only provider-aware PR/MR/review creation instructions ([#1255](https://github.com/bastani-inc/atomic/issues/1255)).
- Updated maintainer release guidance so prerelease and stable changelog entries summarize concrete user-facing changes instead of placeholder version-bump notes.
- Bumped the `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` dependencies to 0.78.1.

### Fixed

- Fixed an uncaught `TypeError: child.render is not a function` crash on `/resume` when an extension custom-message renderer returned a non-`Component` value, and allowed custom-message renderers to return `null` to render nothing ([#1236](https://github.com/bastani-inc/atomic/issues/1236)).
- Fixed auto-compaction so queued in-progress work resumes without requiring a manual follow-up prompt ([#1280](https://github.com/bastani-inc/atomic/issues/1280)).
- Clarified overflow auto-compaction warnings in the TUI footer so automatic transcript compaction is reported distinctly from user-triggered compaction ([#1250](https://github.com/bastani-inc/atomic/issues/1250)).
- Fixed internal Git subprocesses to strip ambient repository-local Git environment variables before package-manager and footer branch lookups inspect a targeted working tree.
- Fixed Mintlify MDX autolinks in package docs so documentation validation passes ([#1293](https://github.com/bastani-inc/atomic/pull/1293)).

### Removed

- Removed the `/context-compact` interactive and workflow-stage slash command; use `/compact` instead.
- Removed the temporary manual `@earendil-works/pi-tui` patch, patched-dependency configuration, and bundled patched TUI packaging fallback.

## [0.8.26-alpha.11] - 2026-06-08

### Changed

- Updated maintainer release guidance to require prerelease and stable changelog entries to summarize concrete user-facing changes instead of placeholder version-bump notes.

## [0.8.26-alpha.10] - 2026-06-08

### Changed

- Updated compaction documentation to explain transcript-bound Verbatim Compaction, validated logical deletion targets, critical overflow behavior, and legacy summary-compaction settings.

## [0.8.26-alpha.9] - 2026-06-07

### Changed

- Documented npm/pnpm installation options in Atomic docs and limited Mintlify validation to pull requests ([#1294](https://github.com/bastani-inc/atomic/pull/1294)).
- Fixed Mintlify MDX autolinks in package docs so documentation validation passes ([#1293](https://github.com/bastani-inc/atomic/pull/1293)).

## [0.8.26-alpha.8] - 2026-06-07

### Changed

- Changed manual `/compact` and auto-compaction to use deletion-only context compaction by default, preserving retained transcript content verbatim.
- Restyled completed context compaction results with the same expandable summary-card treatment as summary compaction.

### Removed

- Removed the `/context-compact` interactive and workflow-stage slash command; use `/compact` instead.

## [0.8.26-alpha.7] - 2026-06-07

### Added

- Added `/context-compact`, a fixed no-argument deletion-only compaction command that validates model-proposed logical deletion targets, preserves retained transcript content verbatim, and records `context_compaction` session entries.

### Changed

- Bumped the `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, and `@earendil-works/pi-tui` dependencies to 0.78.1.

### Fixed

- Fixed auto-compaction so queued in-progress work resumes without requiring a manual follow-up prompt ([#1280](https://github.com/bastani-inc/atomic/issues/1280)).

### Removed

- Removed the temporary manual `@earendil-works/pi-tui` patch, patched-dependency configuration, and bundled patched TUI packaging fallback.

## [0.8.26-alpha.6] - 2026-06-06

### Changed

- Bumped package version for the Atomic 0.8.26-alpha.6 prerelease.

## [0.8.26-alpha.5] - 2026-06-06

### Changed

- Bumped package version for the Atomic 0.8.26-alpha.5 prerelease.

## [0.8.26-alpha.4] - 2026-06-05

### Changed

- Bumped package version for the Atomic 0.8.26-alpha.4 prerelease.

## [0.8.26-alpha.3] - 2026-06-05

### Changed

- Documented the builtin `ralph` workflow's safe default for PR creation, `create_pr=true` opt-in examples, omitted disabled `pr_report`, and final-stage-only provider-aware PR/MR/review creation instructions ([#1255](https://github.com/bastani-inc/atomic/issues/1255)).

## [0.8.26-alpha.2] - 2026-06-05

### Fixed

- Clarified overflow auto-compaction warnings in the TUI footer so automatic transcript compaction is reported distinctly from user-triggered compaction ([#1250](https://github.com/bastani-inc/atomic/issues/1250)).
- Fixed internal Git subprocesses to strip ambient repository-local Git environment variables before package-manager and footer branch lookups inspect a targeted working tree.

## [0.8.26-alpha.1] - 2026-06-05

### Fixed

- Fixed an uncaught `TypeError: child.render is not a function` that crashed the TUI on `/resume` when an extension's custom-message renderer returned a non-`Component` value (such as a string). `CustomMessageComponent` now validates the renderer result exposes a `render()` method and falls back to the default boxed rendering otherwise ([#1236](https://github.com/bastani-inc/atomic/issues/1236)).
- Custom-message renderers can now return `null` to render nothing: `CustomMessageComponent` skips its default box and the leading spacer for a `null` result so the entry occupies zero rows. This lets extensions suppress a rehydrated entry whose backing state is gone (e.g. the workflows input form on `/resume`) without leaving a blank line.

### Changed

- Improved Windows cold startup by lazily loading heavy bundled web-access/intercom/MCP implementation modules, moving fd/rg readiness checks after the first interactive frame, and adding detailed startup timing spans ([#1223](https://github.com/bastani-inc/atomic/issues/1223)).

## [0.8.25] - 2026-06-04

### Changed

- Promoted the 0.8.25 prerelease package version to a stable release.

## [0.8.25-alpha.1] - 2026-06-04

### Changed

- Bumped package version for the Atomic 0.8.25-alpha.1 prerelease.

## [0.8.24] - 2026-06-04

### Changed

- Promoted the 0.8.24 prerelease package version to a stable release.

## [0.8.24-alpha.4] - 2026-06-04

### Fixed

- Fixed TUI flicker and scrollback wipes while scrolling during active streaming in short terminals by adding a strict no-write off-viewport renderer skip and preserving scrollback for content-driven full redraws ([#1222](https://github.com/bastani-inc/atomic/issues/1222)).

## [0.8.24-alpha.3] - 2026-06-03

### Added

- Added `/exit` as a built-in interactive slash command alias for graceful app shutdown from the main chat.

## [0.8.24-alpha.2] - 2026-06-03

### Added

- Shipped externally-resolvable TypeScript types for the `@bastani/workflows` SDK through `@bastani/atomic`. New `./workflows`, `./workflows/builtin`, `./workflows/builtin/*`, and `./workflows/ambient` package exports resolve to declarations emitted from the lean authoring surface during the build, and a generated ambient bridge maps the documented bare `@bastani/workflows` specifier (and its `builtin/*` submodules) onto those exports. Installed packages now type-check `import { defineWorkflow, Type } from "@bastani/workflows"` and `@bastani/workflows/builtin/*` composition imports under `tsc` (`moduleResolution: NodeNext`) with no hand-authored `.d.ts`, no `declare module` shim, and no `paths` alias: packages that import `@bastani/atomic` pick the types up automatically, while pure workflow-only packages add one `compilerOptions.types: ["@bastani/atomic/workflows/ambient"]` (or `/// <reference types="@bastani/atomic/workflows/ambient" />`) opt-in. The runtime workflow loader, jiti virtual modules, and `atomic.workflows` discovery are unchanged ([#1208](https://github.com/bastani-inc/atomic/issues/1208)).
- Added a `verify:workflow-types` script that packs `@bastani/atomic` and type-checks throwaway external consumer fixtures (workflow-only opt-in, reference directive, auto-include, and a negative control) so the issue #1208 acceptance test is repeatable ([#1208](https://github.com/bastani-inc/atomic/issues/1208)).

### Changed

- Documented the workflow SDK typing model in `docs/packages.md` and `docs/workflows.md`: the single ambient opt-in for pure workflow-only packages, automatic pickup for packages that import `@bastani/atomic`, and the requirement to list `@bastani/atomic` and `typebox` as peer dependencies ([#1208](https://github.com/bastani-inc/atomic/issues/1208)).

## [0.8.24-alpha.1] - 2026-06-02

### Breaking Changes

- Removed the bundled workflows package's imperative `runWorkflow` object-form API; workflow packages must export branded `defineWorkflow(...).compile()` definitions while direct `workflow` tool task/tasks/chain modes remain available.

### Changed

- Adopted the new `-alpha.N` prerelease version convention (revision starting at 1), replacing the legacy numeric `-N` prerelease suffix in the release tooling (bump script, CI publish validation, and changelog parsing).
- Dropped the leading `v` from release git tags and `release/`/`prerelease/` branch names; the Publish CI now triggers on and validates bare version tags such as `0.8.24` or `0.8.24-alpha.1`.

### Fixed

- Fixed workflow node chat rendering a bare tool-name marker (e.g. `read …`) instead of tool output for parallel tool calls; `LiveChatEntriesController` now pairs concurrent same-named tool calls strictly by `toolCallId` ([#1198](https://github.com/bastani-inc/atomic/issues/1198)).

## [0.8.23] - 2026-06-02

### Changed

- Promoted the 0.8.23 prerelease package version to a stable release.

## [0.8.23-0] - 2026-06-02

### Changed

- Documented workflow artifact-path handoffs and `Read the file at <path>...` downstream prompts as the preferred alternative to injecting large `previous` payloads, review histories, or session tails.
- Updated the Ralph workflow docs to reflect the simplified plan/orchestrate/simplify/review loop without separate `infra-*` discovery stages.
- Updated the default workflow system-prompt guidance to prefer file/artifact handoffs with explicit downstream read instructions for large stage-to-stage context.

### Fixed

- Fixed severe flickering in the `ask_user_question` dialog on short terminals by suspending the animated working loader while the blocking question UI is open; the inline dialog no longer pushes the ticking loader above the viewport, which had forced a full-screen clear+replay on every spinner frame.

## [0.8.22] - 2026-06-01

### Breaking Changes

- Migrated bundled workflow authoring to explicit TypeBox input/output schemas and explicit workflow outputs, so composed workflows now validate contracts with `Type`/`Static` and no longer support legacy descriptors, implicit `result`, `rawOutput`, `.import(...)`, or declaration-time `.humanInTheLoop(...)`.

### Added

- Added Codex `/fast` mode toggles for chat and workflow-stage sessions with visible `fast` model markers on eligible OpenAI/OpenAI Codex models.
- Added reactive extension UI rendering via `ExtensionUIContext.requestRender()` so long-lived widgets can repaint without remount flicker.
- Added interrupt-delivered extension custom messages with optional abort messages, letting workflow and other first-party extensions surface urgent events immediately.

### Changed

- Expanded bundled workflow authoring docs and agent guidance for direct `ctx.workflow(compiledWorkflow, options)` composition, reusable builtin workflow modules, explicit child outputs, and safer long-running workflow monitoring.
- Refined the `/fast` selector copy, layout, toggle states, and keyboard support for clearer chat/workflow scoping.
- Improved workflow graph/status rendering for nested child workflows, compact lifecycle/HIL cards, and reference-first transcript inspection.

### Fixed

- Fixed workflow reloads so package-manifest workflow entries refresh in-process without a full Atomic restart.
- Fixed Codex fast-mode propagation, persistence, request payloads, workflow footer markers, subagent launch metadata, and fallback marker synchronization across chat, workflow, and subagent surfaces.
- Fixed headless `/workflow` automation and print-mode output so successful commands emit displayable summaries, terminal failures surface correctly, completed stage handles dispose on exit, and command-originated extension errors are the only non-zero extension-error exits.
- Hardened workflow human-in-the-loop prompts and answers so brokered prompts remain focusable/scrollable, avoid stale Enter submissions, stay out of model context where appropriate, and resolve duplicate or raced tool answers deterministically.
- Stabilized long-lived workflow and subagent widgets with coalesced repaint paths, durable async-run hydration, and spinner ticks that avoid remount or scrollback flicker.

## [0.8.22-0] - 2026-06-01

### Added

- Added `ExtensionUIContext.requestRender()` and a shared reactive widget installer for extensions to mount widgets once, repaint via coalesced render requests, and own timer-based refreshes without remount flicker ([#1150](https://github.com/bastani-inc/atomic/issues/1150)).
- Added `/fast` Codex fast mode toggles for chat and workflow-stage sessions, applying OpenAI priority service tier to supported `openai/*` and `openai-codex/*` models only; active supported models now show a visible `fast` indicator after the model name ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Added extension custom-message `deliverAs: "interrupt"` delivery so first-party extensions can abort a stale streaming turn and start an immediate custom-message turn ([#1137](https://github.com/bastani-inc/atomic/issues/1137)).
- Added an `interruptAbortMessage` option for interrupt-delivered extension messages so meaningful external events can replace generic `Operation aborted` tool output ([#1137](https://github.com/bastani-inc/atomic/issues/1137)).

### Changed

- Documented direct `ctx.workflow(compiledWorkflow, options)` composition, TypeScript module-style child workflow calls, reusable builtin workflow modules, and child workflow output contracts in the bundled workflow authoring guide.
- Refined the `/fast` selector into a conventional toggle UI with on/off states, clearer scope descriptions, and space/enter toggle support ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Compressed the `/fast` selector copy, row layout, and per-change status message so the summary, toggles, scopes, and keyboard hints stay readable without duplicate off/standard-tier messaging ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Clarified workflow-creation guidance so Atomic asks clarifying questions and writes first-time workflows directly, reserving `/goal` for explicitly chosen long-running reviewer-gated implementation.
- Tightened workflow tool guidance so Atomic monitors long-running workflow runs periodically without micro-managing stages, steers only when appropriate, and inspects transcript paths surgically instead of reading whole session logs.
- Expanded the workflow authoring docs for composing user-defined workflows and builtin child workflows such as `deep-research-codebase`, `goal`, and `ralph`, including explicit output contracts and the `.run()` return-object convention for the implicit string `result` output.
- Documented the current workflow tool action surface, lifecycle notices, human-in-the-loop answer notifications, workflow notification config, `/workflow` slash-command discovery, and workflow Codex fast-mode behavior ([#1151](https://github.com/bastani-inc/atomic/issues/1151)).

### Fixed

- Added a host workflow-resource refresh path so workflow reloads can re-read package manifests without a full Atomic reload ([#1155](https://github.com/bastani-inc/atomic/issues/1155)).
- Preserved custom registered provider streamers when Codex fast mode is enabled for native OpenAI response APIs ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Fixed `/fast` changes so the banner/footer and current session update immediately, and inherited chat fast-mode state now reaches subagent child sessions without waiting for a restart ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Fixed `/fast` persistence so existing project-level fast-mode overrides are updated alongside global settings for the changed scope without clobbering untouched global chat or workflow fast-mode preferences ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Made Codex fast-mode request helpers require an explicit enabled flag and treat `service_tier: undefined` as unset when preparing OpenAI payloads ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Fixed attached workflow-stage chat footers to resolve the `fast` model indicator against workflow fast-mode settings instead of chat settings ([#1134](https://github.com/bastani-inc/atomic/issues/1134)).
- Scoped print-mode non-zero extension-error exits to command-originated failures so non-fatal lifecycle extension errors do not fail otherwise successful headless output ([#1123](https://github.com/bastani-inc/atomic/issues/1123)).
- Fixed `ask_user_question` custom UI abort handling so interrupt-delivered workflow HiL answer notices are not stuck behind a blocking question modal ([#1137](https://github.com/bastani-inc/atomic/issues/1137)).
- Fixed print-mode slash-command output for headless `/workflow` automation by printing final displayable custom messages and treating command-originated extension errors as non-zero while suppressing stale final output ([#1156](https://github.com/bastani-inc/atomic/issues/1156)).

## [0.8.21] - 2026-05-30

### Changed

- Promoted the 0.8.21 prerelease changes to a stable release.

## [0.8.21-0] - 2026-05-30

### Changed

- Upgraded the pi runtime packages (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, `@earendil-works/pi-tui`) to 0.78.0 and aligned skill name validation with upstream pi so frontmatter names no longer need to match parent directory names ([#1124](https://github.com/bastani-inc/atomic/issues/1124)).

## [0.8.20] - 2026-05-29

### Changed

- Promoted the 0.8.20 prerelease changes to a stable release.

## [0.8.20-0] - 2026-05-29

### Added

- Added session-scoped `orchestrationContext` support to SDK agent sessions and extension contexts for workflow-stage policy enforcement.
- Added support for the Claude Opus 4.8 model across model configuration, selection, and validation via the `@earendil-works/pi-ai` 0.77.0 upgrade ([#1097](https://github.com/bastani-inc/atomic/issues/1097)).

### Changed

- Upgraded the pi runtime packages (`@earendil-works/pi-agent-core`, `@earendil-works/pi-ai`, `@earendil-works/pi-tui`) from 0.75.5 to 0.77.0 and bumped `@modelcontextprotocol/ext-apps` to 1.7.2, `highlight.js` to 11.x, `linkedom` to 0.18.x, `undici` to 8.x, and `vitest` (dev) to 4.x.
- Switched the `highlight.js` import to the package-root default export and replaced the Node stream pipeline in the tools downloader with `Bun.write()` to align with the upgraded dependencies.
- Pinned the footer (model + cwd identity) directly under the editor and moved below-editor widgets beneath it (separated by a blank line), so transient run status such as the workflow companion counter renders at the very bottom instead of separating the footer from the input. Stacked below-editor widgets (e.g. the async-subagent widget and the workflow run counter) are also separated from each other by a blank line. Rendering below-editor widgets last keeps a live widget at the bottom of the buffer (within the viewport), preserving the widget resize-flicker fix. Extension-provided custom footers are now swapped in place (rather than appended), so installing a custom footer keeps the below-editor widget container as the last UI child and does not regress this ordering ([#1109](https://github.com/bastani-inc/atomic/issues/1109)).

## [0.8.19] - 2026-05-27

### Changed

- Renamed the SDK tool exclusion option from `excludeTools` to `excludedTools` for consistency with internal system prompt terminology, while preserving backward-compatible handling for existing SDK callers.

## [0.8.19-0] - 2026-05-27

### Changed

- Renamed the SDK tool exclusion option from `excludeTools` to `excludedTools` for consistency with internal system prompt terminology, while preserving backward-compatible handling for existing SDK callers.

## [0.8.18] - 2026-05-27

### Changed

- Promoted the 0.8.18 prerelease changes to a stable release.

## [0.8.18-0] - 2026-05-27

### Added

- Added SDK `excludeTools` support for omitting named built-in, extension, and custom tools from `createAgentSession()` sessions while preserving existing `tools` and `noTools` behavior ([#1070](https://github.com/bastani-inc/atomic/issues/1070)).

### Changed

- Clarified bundled workflow docs and `/atomic` onboarding copy for using `goal` on smaller scoped changes with explicit outcomes, testing instructions, and done criteria, while positioning `ralph` for larger migrations, broad refactors, and PR-prep workflows.

## [0.8.17] - 2026-05-26

### Changed

- Promoted the 0.8.17 prerelease changes to a stable release.

## [0.8.17-0] - 2026-05-26

### Breaking Changes

- Removed bundled Ralph's configurable `review_quorum` and `blocker_threshold` inputs; `max_turns` remains configurable alongside `objective` and optional `base_branch` ([#1061](https://github.com/bastani-inc/atomic/issues/1061)).

### Changed

- Updated bundled Ralph docs and prompts to more closely match Codex `/goal` continuation guidance while retaining deterministic reviewer-gated completion ([#1061](https://github.com/bastani-inc/atomic/issues/1061)).
- Restored bundled Ralph's stronger historical review gate prompt and `review_decision` schema with findings, oracle satisfaction, receipt assessment, verification remaining, and reviewer-error guard fields.

## [0.8.16] - 2026-05-26

### Changed

- Promoted the 0.8.16 prerelease changes to a stable release.

## [0.8.16-0] - 2026-05-26

### Breaking Changes

- Removed Ralph's `prompt` and `max_loops` compatibility inputs from the bundled workflow; use `objective` and `max_turns` instead ([#1061](https://github.com/bastani-inc/atomic/issues/1061)).

### Changed

- Updated bundled Ralph docs and guide examples for the Goal Runner workflow with goal-ledger receipts, reviewer quorum, repeated-blocker gating, and `max_turns`/`objective` inputs ([#1061](https://github.com/bastani-inc/atomic/issues/1061)).

### Fixed

- Clarified bundled Ralph's bounded `blocker_threshold` behavior ([#1061](https://github.com/bastani-inc/atomic/issues/1061)).

## [0.8.15] - 2026-05-26

### Changed

- Promoted the 0.8.15 prerelease changes to a stable release.

## [0.8.15-0] - 2026-05-25

### Breaking Changes

- Changed workflow `ctx.ui.*` prompts to render as synthetic graph stage nodes with `awaiting_input` status, including new `StageSnapshot.promptAnswerState` metadata.
- Changed `ctx.ui.select(..., [])` to throw before creating a prompt node instead of returning an empty string.

### Added

- Added live-memory prompt answer replay for workflow continuations.
- Added workflow control and subagents documentation updates, plus bundled docs link validation.

### Changed

- Updated Ralph around an autonomous goal contract so stages infer verifiable criteria, require receipts, and judge completion against the verification oracle.
- Updated Ralph to discover project initialization needs from repository evidence before implementation work proceeds.

### Fixed

- Returned the workflow overlay to the graph orchestrator after answering or skipping a `ctx.ui` prompt node.
- Stopped eagerly starting MCP OAuth callback handling during session startup.
- Carried forward bundled subagent fixes for nested fanout state, child allowlists, async runner config permissions, fallback models, read-only guard overrides, Windows child process spawning, stable running glyphs, and recovered intermediate child errors.

## [0.8.14] - 2026-05-25

### Changed

- Promoted the 0.8.14 prerelease changes to a stable release.
- Synced Atomic's coding-agent fork with upstream Pi patches since v0.75.4 and updated bundled Pi libraries to 0.75.5.

### Fixed

- Carried forward upstream fixes for managed extension installs, git package ref reconciliation, async file tools, export HTML escaping, OpenCode session headers, OAuth device-code login, footer path abbreviation, clipboard native loading, and collapsed read output rendering.
- Refreshed the built-in header model label whenever the active model changes, matching the footer below the chat box.

## [0.8.14-0] - 2026-05-25

### Changed

- Synced Atomic's coding-agent fork with upstream Pi patches since v0.75.4 and updated bundled Pi libraries to 0.75.5.

### Fixed

- Carried forward upstream fixes for managed extension installs, git package ref reconciliation, async file tools, export HTML escaping, OpenCode session headers, OAuth device-code login, footer path abbreviation, clipboard native loading, and collapsed read output rendering.
- Refreshed the built-in header model label whenever the active model changes, matching the footer below the chat box.

## [0.8.13] - 2026-05-21

### Changed

- Promoted the 0.8.13 prerelease changes to a stable release.
- Updated Atomic agent guidance.

### Fixed

- Fixed packaged workflow discovery so package-authored workflow resources load through `jiti` with the same `@bastani/workflows` SDK imports used by project and user workflows.
- Preserved workflow discovery diagnostics for invalid default exports and supported SDK imports when running from the Bun binary.
- Preserved spacing for async subagent widgets before the prompt box.

## [0.8.13-0] - 2026-05-21

### Fixed

- Fixed packaged workflow discovery so package-authored workflow resources load through `jiti` with the same `@bastani/workflows` SDK imports used by project and user workflows.
- Preserved workflow discovery diagnostics for invalid default exports and supported SDK imports when running from the Bun binary.
- Preserved spacing for async subagent widgets before the prompt box.

## [0.8.12] - 2026-05-20

### Added

- Added a configurable HTTP idle timeout for long-running model and web requests, with selectable presets from 30 seconds through 30 minutes and an option to disable the timeout.
- Added the Atomic workflows documentation page and navigation so users can discover workflow authoring and execution guidance from the Atomic docs.

### Changed

- Promoted the 0.8.12 prerelease changes to a stable release.
- Updated the bundled Pi libraries to 0.75.4.
- Migrated internal runtime, tool, TUI, tests, and extension example imports to explicit `.ts` specifiers to improve raw-TypeScript extension compatibility.
- Refreshed Atomic documentation for package usage, customization, workflows, and release guidance.
- Included selected model reasoning level metadata in generated system prompts and project context markup.

### Fixed

- Improved Windows self-update behavior for package-manager installs, including native dependency cleanup, quarantine handling, and clearer messaging when updates are unavailable.
- Stabilized subagent live result rendering, async render updates, transient child-error recovery, and live widget animations.
- Detached settled workflow stages from run-control tracking and centered the empty workflow graph waiting state.
- Normalized negative and fractional duration displays across bash, subagent, and workflow UI renderers.

## [0.8.12-0] - 2026-05-20

### Added

- Added a configurable HTTP idle timeout for long-running model and web requests, with presets from 30 seconds through 30 minutes plus a disabled option.
- Added the Atomic workflows documentation page and navigation so `/atomic` and project guidance can point users to workflow authoring and execution details.

### Changed

- Updated the bundled Pi libraries to 0.75.4.
- Migrated internal runtime, tool, TUI, tests, and extension example imports to explicit `.ts` specifiers for better raw-TypeScript extension compatibility.
- Rebranded and refreshed the coding-agent documentation for Atomic, including package, customization, and workflow guidance.
- Included the selected model reasoning level in generated system prompts and project context markup.

### Fixed

- Improved Windows self-update handling for package-manager installs, including native dependency cleanup/quarantine and clearer unavailable-update messaging.
- Stabilized subagent live result rendering, async render updates, transient child-error recovery, and live widget animations.
- Detached settled workflow stages from run-control tracking and centered the empty graph waiting state.
- Normalized negative and fractional duration displays across bash, subagent, and workflow UI renderers.

## [0.8.11] - 2026-05-20

### Changed

- Updated the What's New release notes to highlight the new `/atomic` onboarding guide, including overview, examples, workflows, and release notes from inside the CLI.

## [0.8.10] - 2026-05-20

### Added

- Added the `/atomic` onboarding guide, with built-in overview, examples, workflows, and release notes to help users discover Atomic from inside the CLI.

## [0.8.10-0] - 2026-05-20

### Added

- Reintroduced `/atomic` as an interactive guide with options for overview, examples, workflows, and release notes.

### Changed

- Prepared the 0.8.10-0 prerelease.

## [0.8.9] - 2026-05-19

### Changed

- Prepared the 0.8.9 release.

## [0.8.9-0] - 2026-05-19

### Changed

- Prepared the 0.8.9-0 prerelease.

## [0.8.8] - 2026-05-19

### Changed

- Prepared the 0.8.8 release.

## [0.8.8-0] - 2026-05-19

### Changed

- Prepared the 0.8.8-0 prerelease.

## [0.8.7] - 2026-05-19

### Changed

- Prepared the 0.8.7 release.

## [0.8.7-0] - 2026-05-19

### Changed

- Prepared the 0.8.7-0 prerelease.

## [0.8.6] - 2026-05-18

### Changed

- Prepared the 0.8.6 release.

## [0.8.6-0] - 2026-05-18

### Changed

- Prepared the 0.8.6-0 prerelease.

## [0.8.5] - 2026-05-18

### Changed

- Prepared the 0.8.5 release.

## [0.8.5-0] - 2026-05-18

### Changed

- Prepared the 0.8.5-0 prerelease.

## [0.8.4] - 2026-05-17

### Added

- Added selected model details to the system prompt for assistant model attribution.

### Changed

- Aligned package references with Atomic branding.

## [0.8.4-0] - 2026-05-17

### Added

- Added selected model details to the system prompt for assistant model attribution.

### Changed

- Aligned package references with Atomic branding.

## [0.8.3] - 2026-05-17

### Changed

- Pinned `@j178/prek` to 0.3.13 to keep hook installation stable.

## [0.8.3-0] - 2026-05-17

### Changed

- Pinned `@j178/prek` to 0.3.13 to keep hook installation stable.

## [0.8.2] - 2026-05-16

### Changed

- Reduced the Atomic startup banner to a compact three-line mark.

## [0.8.2-0] - 2026-05-16

### Changed

- Reduced the Atomic startup banner to a compact three-line mark.

## [0.8.1] - 2026-05-15

### Fixed

- Fixed the Atomic changelog viewer to show only the current release notes instead of including older sections.
- Fixed the published `@bastani/atomic` package manifest so Bun can install it outside the monorepo without resolving private workspace-only bundled packages.

## [0.8.1-1] - 2026-05-15

### Fixed

- Fixed the Atomic changelog viewer to show only the current release notes instead of including older sections.

## [0.8.1-0] - 2026-05-15

### Fixed

- Fixed the published `@bastani/atomic` package manifest so Bun can install it outside the monorepo without resolving private workspace-only bundled packages.

## [0.8.0] - 2026-05-15

### Added

- Added Together AI to built-in provider setup, `/login` API-key auth, and default model resolution ([#3624](https://github.com/earendil-works/pi-mono/pull/3624) by [@Nutlope](https://github.com/Nutlope)).
- Added Windows ARM64 standalone binary release artifacts ([#4458](https://github.com/earendil-works/pi/pull/4458) by [@brianmichel](https://github.com/brianmichel)).

### Fixed

- Fixed interactive error messages to render with trailing spacing so reload errors do not run into resource listings ([#4510](https://github.com/earendil-works/pi/issues/4510)).
- Fixed nested code fences in the Termux setup documentation so the example AGENTS.md renders correctly ([#4503](https://github.com/earendil-works/pi/issues/4503)).
- Fixed tool output expansion while extension confirmation dialogs are focused ([#4429](https://github.com/earendil-works/pi/issues/4429)).
- Fixed auto-retry for Anthropic streams that end before `message_stop` ([#4433](https://github.com/earendil-works/pi/issues/4433)).
- Fixed theme sharing across package scopes so extensions do not crash with `Theme not initialized` ([#4333](https://github.com/earendil-works/pi/issues/4333)).
- Fixed keybinding hints to show Option instead of Alt on macOS ([#4289](https://github.com/earendil-works/pi/issues/4289)).
- Fixed the interactive update notification to render the changelog as an OSC 8 hyperlink when the terminal supports hyperlinks ([#4280](https://github.com/earendil-works/pi/issues/4280)).

## [0.8.0-0] - 2026-05-15

### Added

- Added Together AI to built-in provider setup, `/login` API-key auth, and default model resolution ([#3624](https://github.com/earendil-works/pi-mono/pull/3624) by [@Nutlope](https://github.com/Nutlope)).
- Added Windows ARM64 standalone binary release artifacts ([#4458](https://github.com/earendil-works/pi/pull/4458) by [@brianmichel](https://github.com/brianmichel)).

### Fixed

- Fixed interactive error messages to render with trailing spacing so reload errors do not run into resource listings ([#4510](https://github.com/earendil-works/pi/issues/4510)).
- Fixed nested code fences in the Termux setup documentation so the example AGENTS.md renders correctly ([#4503](https://github.com/earendil-works/pi/issues/4503)).
- Fixed tool output expansion while extension confirmation dialogs are focused ([#4429](https://github.com/earendil-works/pi/issues/4429)).
- Fixed auto-retry for Anthropic streams that end before `message_stop` ([#4433](https://github.com/earendil-works/pi/issues/4433)).
- Fixed theme sharing across package scopes so extensions do not crash with `Theme not initialized` ([#4333](https://github.com/earendil-works/pi/issues/4333)).
- Fixed keybinding hints to show Option instead of Alt on macOS ([#4289](https://github.com/earendil-works/pi/issues/4289)).
- Fixed the interactive update notification to render the changelog as an OSC 8 hyperlink when the terminal supports hyperlinks ([#4280](https://github.com/earendil-works/pi/issues/4280)).

## [0.74.0] - 2026-05-07

### Changed

- Updated repository links and package references for the move to `earendil-works/pi-mono` and `@earendil-works/*` package scopes.

## [0.73.1] - 2026-05-07

### New Features

- **Self-update support for the npm scope migration**: `pi update --self` now supports the upcoming package rename from `@bastani/atomic` to `@bastani/atomic`. After the new package is published, existing global installs can update through the normal self-update flow; pi will uninstall the old global package and install the package name returned by the version check endpoint.
- **Interactive OAuth login selection**: OAuth providers can now present multiple login choices in `/login`, enabling provider-specific interactive authentication flows. See [Providers](docs/providers.md).
- **JSONC-style `models.json` parsing**: `models.json` now allows comments and trailing commas, making custom provider and model configuration easier to maintain. See [Providers](docs/providers.md) and [Custom Providers](docs/custom-provider.md).

### Added

- Added interactive login selection support so OAuth providers can present multiple login choices ([#4190](https://github.com/earendil-works/pi-mono/pull/4190) by [@mitsuhiko](https://github.com/mitsuhiko)).

### Changed

- Changed `pi update --self` to honor the active package name returned by the Pi version check endpoint, defaulting to the current package when omitted and uninstalling the old global package before installing a renamed package.
- Changed extension loading to use upstream `jiti` 2.7 instead of the `@mariozechner/jiti` fork ([#4244](https://github.com/earendil-works/pi-mono/pull/4244) by [@pi0](https://github.com/pi0)).
- Changed `models.json` parsing to allow comments and trailing commas ([#4162](https://github.com/earendil-works/pi-mono/pull/4162) by [@julien-c](https://github.com/julien-c)).

### Fixed

- Fixed `pi -p` treating prompts that start with YAML frontmatter as extension flags instead of user messages ([#4163](https://github.com/badlogic/pi-mono/issues/4163)).
- Fixed pending tool results not updating in the live TUI after toggling thinking block visibility while the tool is running ([#4167](https://github.com/badlogic/pi-mono/issues/4167)).
- Fixed `/copy` reporting success on Linux without writing the clipboard on Wayland-only compositors (Hyprland, Niri, ...) by skipping the X11-only native addon on Linux and routing through `wl-copy`/`xclip`/`xsel` instead ([#4177](https://github.com/badlogic/pi-mono/issues/4177)).
- Fixed HTML session exports to strip skill wrapper XML from rendered user messages ([#4234](https://github.com/earendil-works/pi-mono/pull/4234) by [@aliou](https://github.com/aliou)).
- Fixed OpenAI-compatible chat completion streams that interleave content and tool-call deltas in the same choice.
- Fixed OpenAI Codex OAuth refresh failures writing directly to stderr while the TUI is active ([#4141](https://github.com/badlogic/pi-mono/issues/4141)).
- Fixed OpenAI Codex Responses requests to send a non-empty system prompt ([#4184](https://github.com/earendil-works/pi-mono/issues/4184)).
- Fixed Kimi For Coding model resolution for the Kimi K2 P6 alias ([#4218](https://github.com/earendil-works/pi-mono/issues/4218)).
- Fixed Kitty inline image redraws to stay within TUI-owned terminal regions and avoid writing below the active viewport.
- Fixed Kitty inline image rendering by letting the terminal allocate image ids and bounding parsed image ids to valid values.
- Fixed inline image capability detection to disable inline images in cmux terminals.

## [0.73.0] - 2026-05-04

### New Features

- **Xiaomi MiMo API billing and regional Token Plan providers** - `xiaomi` now uses API billing, with separate `xiaomi-token-plan-{cn,ams,sgp}` providers. See [docs/providers.md#api-keys](docs/providers.md#api-keys) and [README.md#providers--models](README.md#providers--models). ([#4112](https://github.com/badlogic/pi-mono/pull/4112) by [@Phoen1xCode](https://github.com/Phoen1xCode))
- **Incremental bash output streaming** - Bash tool output now appears while commands run instead of only after completion. ([#4145](https://github.com/badlogic/pi-mono/issues/4145))
- **Compact read rendering** - Interactive `read` output for Pi docs, context files, and skills is collapsed by default and shows selected line ranges.

### Breaking Changes

- Switched the built-in `xiaomi` provider from Token Plan AMS to Xiaomi's API billing endpoint, and renamed its `/login` display from "Xiaomi MiMo Token Plan" to "Xiaomi MiMo". `XIAOMI_API_KEY` now refers to the API billing key from [platform.xiaomimimo.com](https://platform.xiaomimimo.com). Users on Token Plan should switch to the appropriate `xiaomi-token-plan-*` provider and set the corresponding env var ([#4112](https://github.com/badlogic/pi-mono/pull/4112) by [@Phoen1xCode](https://github.com/Phoen1xCode)).

### Added

- Added three Xiaomi MiMo Token Plan regional providers visible in `/login`: `xiaomi-token-plan-cn` (`XIAOMI_TOKEN_PLAN_CN_API_KEY`), `xiaomi-token-plan-ams` (`XIAOMI_TOKEN_PLAN_AMS_API_KEY`), `xiaomi-token-plan-sgp` (`XIAOMI_TOKEN_PLAN_SGP_API_KEY`). Each defaults to `mimo-v2.5-pro` ([#4112](https://github.com/badlogic/pi-mono/pull/4112) by [@Phoen1xCode](https://github.com/Phoen1xCode)).

### Changed

- Changed `read` tool rendering to collapse Pi documentation, AGENTS/CLAUDE context files, and `SKILL.md` contents by default in interactive output.

### Fixed

- Fixed generated OpenAI-compatible model metadata for Qwen 3.5/3.6 and MiniMax M2.7, so those models work through the built-in provider catalog ([#4110](https://github.com/badlogic/pi-mono/pull/4110) by [@jsynowiec](https://github.com/jsynowiec)).
- Fixed Bedrock Claude Opus 4.7 `xhigh` thinking requests by preserving the provider's native effort value.
- Fixed OpenAI Codex WebSocket transport to fall back to SSE when setup fails before streaming starts, and surface transport diagnostics in the assistant message ([#4133](https://github.com/badlogic/pi-mono/issues/4133)).
- Fixed OpenAI Codex WebSocket transport keeping `--print` and JSON mode processes alive after the response by closing cached WebSocket sessions during session shutdown ([#4103](https://github.com/badlogic/pi-mono/issues/4103)).
- Fixed compact `read` tool calls to render directly and include selected line ranges in interactive output.
- Fixed interactive sessions to exit when terminal input is lost instead of continuing in a broken state.
- Fixed bash tool output to stream incrementally while commands run instead of waiting for command completion ([#4145](https://github.com/badlogic/pi-mono/issues/4145)).
- Fixed selector and autocomplete fuzzy ranking to prioritize exact matches.

## [0.72.1] - 2026-05-02

## [0.72.0] - 2026-05-01

### New Features

- **Xiaomi MiMo Token Plan provider** - New Anthropic-compatible provider with `XIAOMI_API_KEY` auth, default model (`mimo-v2.5-pro`), and `/login` display. See [docs/providers.md](docs/providers.md). ([#4005](https://github.com/badlogic/pi-mono/pull/4005) by [@Phoen1xCode](https://github.com/Phoen1xCode)).
- **Model thinking level metadata** - Models can now declare which thinking levels they support via `thinkingLevelMap`, replacing the old `reasoningEffortMap`. See [docs/models.md#thinking-level-map](docs/models.md#thinking-level-map) and [docs/custom-provider.md](docs/custom-provider.md). ([#3208](https://github.com/badlogic/pi-mono/issues/3208)).
- **Custom provider base URL overrides** - `pi.registerProvider()` now respects per-model `baseUrl` settings. See [docs/custom-provider.md](docs/custom-provider.md). ([#4063](https://github.com/badlogic/pi-mono/issues/4063)).
- **Post-turn stop callback** - Agent loop can now exit gracefully after a completed turn via `shouldStopAfterTurn`. See [`packages/agent/README.md`](https://github.com/badlogic/pi-mono/blob/main/packages/agent/README.md).
- **Self-update detection fix** - `pi` now correctly identifies and applies available updates. ([#3942](https://github.com/badlogic/pi-mono/issues/3942), [#3980](https://github.com/badlogic/pi-mono/issues/3980), [#3922](https://github.com/badlogic/pi-mono/issues/3922)).

### Breaking Changes

- Replaced `compat.reasoningEffortMap` in `models.json` and `pi.registerProvider()` model definitions with model-level `thinkingLevelMap` ([#3208](https://github.com/badlogic/pi-mono/issues/3208)). Migration: move old mappings from `compat.reasoningEffortMap` to `thinkingLevelMap`. Use string values for provider-specific thinking values and `null` for unsupported pi levels that should be hidden and skipped by cycling. See `docs/models.md#thinking-level-map` and `docs/custom-provider.md`.

### Added

- Added Xiaomi MiMo Token Plan provider support with `XIAOMI_API_KEY`, default model resolution, `/login` display support, and provider documentation ([#4005](https://github.com/badlogic/pi-mono/pull/4005) by [@Phoen1xCode](https://github.com/Phoen1xCode)).
- Added model-level `thinkingLevelMap` support in `models.json` and `pi.registerProvider()`, allowing models to expose only the thinking levels they actually support ([#3208](https://github.com/badlogic/pi-mono/issues/3208)).
- Added `shouldStopAfterTurn` agent loop callback for post-turn stop control, inherited from `@mariozechner/pi-agent-core`. See [`packages/agent/README.md`](https://github.com/badlogic/pi-mono/blob/main/packages/agent/README.md).

### Fixed

- Fixed the default transport setting to use `auto`, allowing OpenAI Codex to use cached WebSocket context when available ([#4083](https://github.com/badlogic/pi-mono/issues/4083)).
- Fixed `pi.registerProvider()` to honor per-model `baseUrl` overrides ([#4063](https://github.com/badlogic/pi-mono/issues/4063)).
- Fixed self-update detection so `pi` correctly identifies when a newer version is available and applies updates ([#3942](https://github.com/badlogic/pi-mono/issues/3942), [#3980](https://github.com/badlogic/pi-mono/issues/3980), [#3922](https://github.com/badlogic/pi-mono/issues/3922)).

## [0.71.1] - 2026-05-01

### Added

- Added `websocket-cached` to the transport setting options for the OpenAI Codex provider used with ChatGPT subscription auth. This keeps the same WebSocket open for a session and, after the first request, sends only the new conversation items instead of resending the full chat history when possible.

## [0.71.0] - 2026-04-30

### Breaking Changes

- Removed built-in Google Gemini CLI and Google Antigravity support. Existing configurations using those providers must switch to another supported provider.

### New Features

- Cloudflare AI Gateway provider support with `CLOUDFLARE_API_KEY`/`CLOUDFLARE_ACCOUNT_ID`/`CLOUDFLARE_GATEWAY_ID`, default model resolution, and `/login` display. See [docs/providers.md#cloudflare-ai-gateway](docs/providers.md#cloudflare-ai-gateway). ([#3856](https://github.com/badlogic/pi-mono/pull/3856) by [@mchenco](https://github.com/mchenco)).
- Moonshot AI provider support with `MOONSHOT_API_KEY`, default model resolution, and `/login` display.
- Mistral Medium 3.5 built-in model support. See [docs/providers.md#api-keys](docs/providers.md#api-keys). ([#4009](https://github.com/badlogic/pi-mono/pull/4009) by [@technocidal](https://github.com/technocidal)).
- Extension APIs can replace finalized `message_end` messages, wrap custom editor factories via `ctx.ui.getEditorComponent()`, and observe thinking level changes. See [docs/extensions.md#message_start--message_update--message_end](docs/extensions.md#message_start--message_update--message_end), [docs/extensions.md#widgets-status-and-footer](docs/extensions.md#widgets-status-and-footer), and [docs/extensions.md#thinking_level_select](docs/extensions.md#thinking_level_select).
- `PI_CODING_AGENT_SESSION_DIR` configures session storage from the environment. See [docs/usage.md#environment-variables](docs/usage.md#environment-variables).

### Added

- Added Cloudflare AI Gateway as a built-in provider with `CLOUDFLARE_API_KEY`/`CLOUDFLARE_ACCOUNT_ID`/`CLOUDFLARE_GATEWAY_ID` setup, default model resolution, `/login` display support, and provider documentation ([#3856](https://github.com/badlogic/pi-mono/pull/3856) by [@mchenco](https://github.com/mchenco)).
- Added Moonshot AI as a built-in provider with `MOONSHOT_API_KEY` setup, default model resolution, and `/login` display support.
- Added Mistral Medium 3.5 built-in model support via `@mariozechner/pi-ai` ([#4009](https://github.com/badlogic/pi-mono/pull/4009) by [@technocidal](https://github.com/technocidal)).
- Added routed OpenAI-compatible response model metadata in assistant messages, so providers such as OpenRouter can expose the concrete model used ([#3968](https://github.com/badlogic/pi-mono/pull/3968) by [@purrgrammer](https://github.com/purrgrammer)).
- Added `PI_CODING_AGENT_SESSION_DIR` as an environment equivalent to `--session-dir` ([#4027](https://github.com/badlogic/pi-mono/issues/4027)).
- Added `message_end` extension result support for replacing finalized messages, enabling extensions to override assistant usage cost ([#3982](https://github.com/badlogic/pi-mono/issues/3982)).
- Added top-level `name` support to `pi.registerProvider()` so extension-registered providers can show a friendly name in `/login` ([#3956](https://github.com/badlogic/pi-mono/issues/3956)).
- Added `ctx.ui.getEditorComponent()` so extensions can wrap the currently configured custom editor factory ([#3935](https://github.com/badlogic/pi-mono/issues/3935)).
- Added a `thinking_level_select` extension event for observing thinking level changes ([#3888](https://github.com/badlogic/pi-mono/issues/3888)).

### Fixed

- Fixed WSL clipboard image paste by passing the PowerShell save path directly instead of through a custom environment variable ([#2469](https://github.com/badlogic/pi-mono/issues/2469)).
- Fixed Google Vertex Gemini 3 tool call replay for unsigned tool calls ([#4032](https://github.com/badlogic/pi-mono/issues/4032)).
- Fixed blocked `edit` tool results rendering the rejection reason twice after interactive extension confirmation ([#3830](https://github.com/badlogic/pi-mono/issues/3830)).
- Fixed extension-triggered thinking level changes refreshing the interactive editor border immediately ([#3888](https://github.com/badlogic/pi-mono/issues/3888)).
- Fixed the coding-agent README See Also link to point at `@mariozechner/pi-agent-core` ([#4023](https://github.com/badlogic/pi-mono/issues/4023)).
- Fixed `grep` and `find` tool argument injection for flag-like search patterns ([#4018](https://github.com/badlogic/pi-mono/issues/4018)).
- Fixed PowerShell shell command output on Windows by only spawning detached processes on Unix ([#4013](https://github.com/badlogic/pi-mono/pull/4013) by [@picasso250](https://github.com/picasso250)).
- Fixed Bun package manager `node_modules` discovery when `npmCommand` is configured to use Bun ([#3998](https://github.com/badlogic/pi-mono/pull/3998) by [@thirtythreeforty](https://github.com/thirtythreeforty)).
- Fixed edit and edit-preview access failures to report filesystem errors correctly ([#3955](https://github.com/badlogic/pi-mono/pull/3955) by [@rwachtler](https://github.com/rwachtler)).
- Fixed `ProcessTerminal` sizing to use `COLUMNS` and `LINES` before falling back to 80x24 ([#4004](https://github.com/badlogic/pi-mono/issues/4004)).
- Updated `@anthropic-ai/sdk` to clear GHSA-p7fg-763f-g4gf audit findings ([#3992](https://github.com/badlogic/pi-mono/issues/3992)).
- Updated `@mariozechner/clipboard` to an attested release so package managers with trust policies do not reject installs ([#3946](https://github.com/badlogic/pi-mono/issues/3946)).
- Fixed project context discovery to load `AGENTS.MD` files in addition to `AGENTS.md` ([#3949](https://github.com/badlogic/pi-mono/issues/3949)).
- Fixed `/handoff` to use compacted session context instead of pre-compaction raw messages ([#3945](https://github.com/badlogic/pi-mono/issues/3945)).
- Fixed DeepSeek V4 Flash `xhigh` thinking support so requests map to DeepSeek's `max` reasoning effort ([#3944](https://github.com/badlogic/pi-mono/issues/3944)).
- Fixed Anthropic streams that end before `message_stop` to be treated as errors instead of successful partial responses ([#3936](https://github.com/badlogic/pi-mono/issues/3936)).
- Fixed generated OpenAI-compatible DeepSeek V4 reasoning compatibility outside the direct DeepSeek provider ([#3940](https://github.com/badlogic/pi-mono/issues/3940)).
- Fixed idle follow-up submission to clear the editor like normal message submission ([#3926](https://github.com/badlogic/pi-mono/issues/3926)).
- Fixed editor rendering artifacts for Thai Sara Am and Lao AM vowel characters ([#3904](https://github.com/badlogic/pi-mono/issues/3904)).
- Fixed DeepSeek V4 Flash and V4 Pro pricing metadata to match current official rates ([#3910](https://github.com/badlogic/pi-mono/issues/3910)).
- Updated the sandbox extension example lockfile to resolve the vulnerable `lodash-es` transitive dependency ([#3901](https://github.com/badlogic/pi-mono/issues/3901)).
- Fixed DeepSeek prompt cache hits to be tracked from OpenAI-compatible usage responses ([#3880](https://github.com/badlogic/pi-mono/issues/3880)).

### Removed

- Removed the discontinued Qwen CLI OAuth custom provider extension example ([#3832](https://github.com/badlogic/pi-mono/pull/3832) by [@4h9fbZ](https://github.com/4h9fbZ)).
- Removed Google Gemini CLI and Google Antigravity built-in login, default model, documentation, and example extension support.

## [0.70.6] - 2026-04-28

### New Features

- Cloudflare Workers AI provider support with `CLOUDFLARE_API_KEY`/`CLOUDFLARE_ACCOUNT_ID` setup. See [docs/providers.md#api-keys](docs/providers.md#api-keys). ([#3851](https://github.com/badlogic/pi-mono/pull/3851) by [@mchenco](https://github.com/mchenco))
- Pi update checks now use `pi.dev` and identify Pi with a `pi/<version>` user agent. See [docs/packages.md](docs/packages.md). ([#3877](https://github.com/badlogic/pi-mono/pull/3877) by [@mitsuhiko](https://github.com/mitsuhiko))

### Added

- Added Cloudflare Workers AI as a built-in provider with `CLOUDFLARE_API_KEY`/`CLOUDFLARE_ACCOUNT_ID` setup, default model resolution, `/login` support, and provider documentation ([#3851](https://github.com/badlogic/pi-mono/pull/3851) by [@mchenco](https://github.com/mchenco)).

### Changed

- Changed Pi version checks to identify Pi with a `pi/<version>` user agent ([#3877](https://github.com/badlogic/pi-mono/pull/3877) by [@mitsuhiko](https://github.com/mitsuhiko)).

### Fixed

- Fixed config selector scroll indicators to show item counts instead of line counts ([#3820](https://github.com/badlogic/pi-mono/pull/3820) by [@aliou](https://github.com/aliou)).
- Fixed exported HTML to escape embedded image data and session metadata, preventing crafted session content from injecting markup ([#3819](https://github.com/badlogic/pi-mono/pull/3819) by [@justinpbarnett](https://github.com/justinpbarnett), [#3883](https://github.com/badlogic/pi-mono/pull/3883) by [@justinpbarnett](https://github.com/justinpbarnett)).
- Fixed Bun-based package manager startup by locating global `node_modules` relative to Bun's install layout ([#3861](https://github.com/badlogic/pi-mono/pull/3861) by [@thirtythreeforty](https://github.com/thirtythreeforty)).
- Fixed Bedrock inference profile capability checks by normalizing profile ARNs to the underlying model name.
- Fixed file discovery to fall back to `fdfind` when `fd` is unavailable.
- Fixed `pi update` to skip self-update reinstalls when the installed version is already current ([#3853](https://github.com/badlogic/pi-mono/issues/3853)).
- Fixed Cloudflare Workers AI attribution headers to honor the install telemetry setting.
- Fixed `pi update --self` detection and execution for Windows package-manager shim installs, including symlinked global package roots, and print the manual fallback command when self-update fails ([#3857](https://github.com/badlogic/pi-mono/issues/3857)).

## [0.70.5] - 2026-04-27

### Fixed

- Fixed HTML export preserving ANSI-renderer trailing padding as extra blank wrapped lines.

## [0.70.4] - 2026-04-27

### Fixed

- Fixed packaged `pi` startup failing because the session selector imported a source-only utility path.

## [0.70.3] - 2026-04-27

### New Features

- `pi update` can now update pi itself in addition to installed pi packages. See [docs/packages.md](docs/packages.md). ([#3680](https://github.com/badlogic/pi-mono/pull/3680) by [@mitsuhiko](https://github.com/mitsuhiko))
- Azure Cognitive Services endpoint support for Azure OpenAI Responses deployments. See [docs/providers.md#api-keys](docs/providers.md#api-keys). ([#3799](https://github.com/badlogic/pi-mono/pull/3799) by [@marcbloech](https://github.com/marcbloech))
- Suppressible Anthropic extra-usage billing warning via `warnings.anthropicExtraUsage` in `/settings`. See [docs/settings.md](docs/settings.md). ([#3808](https://github.com/badlogic/pi-mono/issues/3808))
- Extension-controlled working row visibility via `ctx.ui.setWorkingVisible()`, allowing extensions to hide the built-in loader row and render custom working state. See [docs/extensions.md](docs/extensions.md) and [examples/extensions/border-status-editor.ts](examples/extensions/border-status-editor.ts). ([#3674](https://github.com/badlogic/pi-mono/issues/3674))

### Added

- Added `pi update` support for updating pi itself in addition to installed pi packages ([#3680](https://github.com/badlogic/pi-mono/pull/3680) by [@mitsuhiko](https://github.com/mitsuhiko)).
- Added Azure Cognitive Services endpoint support for Azure OpenAI Responses base URLs ([#3799](https://github.com/badlogic/pi-mono/pull/3799) by [@marcbloech](https://github.com/marcbloech)).
- Added `warnings.anthropicExtraUsage` and a `/settings` warnings submenu to suppress the Anthropic extra usage billing warning ([#3808](https://github.com/badlogic/pi-mono/issues/3808))
- Added `ctx.ui.setWorkingVisible()` so extensions can hide the built-in interactive working loader row without reserving layout space, plus a border-status editor example that moves working state into a custom editor border ([#3674](https://github.com/badlogic/pi-mono/issues/3674))

### Fixed

- Fixed duplicate printable characters from Kitty keyboard protocol CSI-u plus raw character input on layouts such as Italian ([#3780](https://github.com/badlogic/pi-mono/issues/3780)).
- Fixed API-key environment discovery and Bun startup to fall back to `/proc/self/environ` when Bun's sandbox leaves `process.env` empty ([#3801](https://github.com/badlogic/pi-mono/pull/3801) by [@mdsjip](https://github.com/mdsjip)).
- Fixed Bun sandboxed package-manager commands when `process.env` is empty ([#3807](https://github.com/badlogic/pi-mono/pull/3807) by [@mdsjip](https://github.com/mdsjip)).
- Fixed symlinked packages, resources, skills, and sessions being duplicated in selectors and loaders ([#3818](https://github.com/badlogic/pi-mono/pull/3818) by [@aliou](https://github.com/aliou)).
- Fixed Bedrock prompt-caching and adaptive-thinking capability checks for inference profile ARNs ([#3527](https://github.com/badlogic/pi-mono/pull/3527) by [@anirudhmarc](https://github.com/anirudhmarc)).
- Fixed OpenAI Codex Responses default verbosity to `low` when no verbosity is specified.
- Stopped sending empty `tools` arrays to providers that reject them when tools are disabled ([#3650](https://github.com/badlogic/pi-mono/pull/3650) by [@HQidea](https://github.com/HQidea)).
- Fixed Anthropic SSE parsing to ignore unknown proxy events such as OpenAI-style `done` terminators ([#3708](https://github.com/badlogic/pi-mono/issues/3708)).
- Fixed provider registration with override-only `models.json` entries to preserve built-in model lists ([#3651](https://github.com/badlogic/pi-mono/issues/3651)).
- Fixed `/login` to show auth supplied by `models.json` provider definitions.
- Fixed HTML export whitespace around extension-rendered tool output and expandable output hints.
- Fixed bash executor temp output streams leaking file descriptors when output was truncated by line count ([#3786](https://github.com/badlogic/pi-mono/issues/3786))
- Fixed extension `pi.setSessionName()` updates to refresh the interactive terminal title immediately ([#3686](https://github.com/badlogic/pi-mono/issues/3686))
- Fixed `/tree` cancellation via `session_before_tree` leaving the session stuck in compaction state ([#3688](https://github.com/badlogic/pi-mono/issues/3688))
- Fixed Escape interrupt handling when extensions hide the built-in working loader row ([#3674](https://github.com/badlogic/pi-mono/issues/3674))
- Fixed coding-agent test expectations for current default models and missing-auth guidance.
- Fixed long local-LLM SSE streams aborting at 5 minutes with `UND_ERR_BODY_TIMEOUT` by disabling undici `bodyTimeout`/`headersTimeout` on the global dispatcher; provider SDKs continue to enforce their own deadlines via `retry.provider.timeoutMs` ([#3715](https://github.com/badlogic/pi-mono/issues/3715))

## [0.70.2] - 2026-04-24

### Fixed

- Fixed provider retry/timeout forwarding to omit undefined provider request controls, avoiding downstream SDK validation errors such as `timeout must be an integer` when `retry.provider.timeoutMs` is not configured ([#3627](https://github.com/badlogic/pi-mono/issues/3627))

## [0.70.1] - 2026-04-24

### New Features

- DeepSeek provider support with V4 Flash/Pro models and `DEEPSEEK_API_KEY` authentication. See [README.md#providers--models](README.md#providers--models) and [docs/providers.md#api-keys](docs/providers.md#api-keys).
- Provider request timeout/retry controls via `retry.provider.{timeoutMs,maxRetries,maxRetryDelayMs}`, useful for long-running local inference and provider SDK retry behavior. See [docs/settings.md#retry](docs/settings.md#retry). ([#3627](https://github.com/badlogic/pi-mono/issues/3627))

### Added

- Added DeepSeek to built-in provider setup, default model resolution, and provider documentation.

### Fixed

- Fixed `/copy` to avoid unbounded OSC 52 writes and clipboard races that could break terminal rendering or panic the native clipboard addon ([#3639](https://github.com/badlogic/pi-mono/issues/3639))
- Fixed extension flag docs to show `pi.getFlag()` using registered flag names without the CLI `--` prefix ([#3614](https://github.com/badlogic/pi-mono/issues/3614))
- Fixed provider retry/timeout settings wiring by adding `retry.provider.{timeoutMs,maxRetries,maxRetryDelayMs}`, migrating legacy `retry.maxDelayMs`, and forwarding provider controls into `streamSimple` request options ([#3627](https://github.com/badlogic/pi-mono/issues/3627))
- Fixed Windows git package installs to bypass `cmd.exe` for native git commands, so install paths containing spaces no longer break `pi install git:...` with `fatal: Too many arguments` ([#3642](https://github.com/badlogic/pi-mono/issues/3642))
- Fixed DeepSeek V4 session replay 400 errors by sending DeepSeek-compatible thinking controls and replayed assistant `reasoning_content` fields ([#3636](https://github.com/badlogic/pi-mono/issues/3636))
- Fixed GPT-5.5 generated context window metadata to use the observed 272k limit.
- Fixed CSI-u Ctrl+letter decoding inside bracketed paste, so pasted modified-key escape sequences no longer become literal editor text ([#3623](https://github.com/badlogic/pi-mono/pull/3623) by [@Exrun94](https://github.com/Exrun94))

## [0.70.0] - 2026-04-23

### New Features

- Searchable auth provider login flow: the `/login` provider selector now supports fuzzy search/filtering, making it faster to find providers when many are configured. See [docs/providers.md](docs/providers.md). ([#3572](https://github.com/badlogic/pi-mono/pull/3572) by [@mitsuhiko](https://github.com/mitsuhiko))
- GPT-5.5 Codex support: `openai-codex/gpt-5.5` is available as a model option, including `xhigh` reasoning support and corrected priority-tier pricing.
- Terminal progress indicators are now opt-in: OSC 9;4 progress reporting during streaming/compaction is off by default and can be toggled via `terminal.showTerminalProgress` in `/settings` ([#3588](https://github.com/badlogic/pi-mono/issues/3588))
- `--no-builtin-tools` / `createAgentSession({ noTools: "builtin" })` now correctly disables only built-in tools while keeping extension tools active. See [docs/extensions.md](docs/extensions.md) and [README.md](README.md) ([#3592](https://github.com/badlogic/pi-mono/issues/3592))

### Breaking Changes

- Disabled OSC 9;4 terminal progress indicators by default. Set `terminal.showTerminalProgress` to `true` in `/settings` to re-enable ([#3588](https://github.com/badlogic/pi-mono/issues/3588))

### Added

- Added searchable auth provider login flow with fuzzy filtering in the provider selector ([#3572](https://github.com/badlogic/pi-mono/pull/3572) by [@mitsuhiko](https://github.com/mitsuhiko))
- Added GPT-5.5 Codex model
- Added auth source labels in `/login` so provider entries can show when auth comes from `--api-key`, an environment variable, or custom provider fallback without exposing secrets.

### Changed

- Updated default model selection across providers to current recommended models.
- Improved stale extension context errors after session replacement or reload to tell extension authors to avoid captured `pi`/command `ctx` and use `withSession` for post-replacement work.

### Fixed

- Fixed `/model` selector cancellation to request render instead of incorrectly triggering login selector.
- Changed login, OAuth, and extension selectors for more consistent styling.
- Added Amazon Bedrock setup guidance to `/login` and updated `/model` copy to refer to configured providers instead of only API keys.
- Improved no-model and missing-auth warnings to point users to `/login` for OAuth or API key setup.
- Fixed `/quit` shutdown ordering to stop the TUI before extension UI teardown can repaint, preserving the final rendered frame while still emitting `session_shutdown` before process exit.
- Fixed `SettingsManager.inMemory()` initial settings being lost after reloads triggered by SDK resource loading ([#3616](https://github.com/badlogic/pi-mono/issues/3616))
- Fixed `models.json` provider compatibility to accept `compat.supportsLongCacheRetention`, allowing proxies to opt out of long-retention cache fields when needed while long retention is enabled by default when requested ([#3543](https://github.com/badlogic/pi-mono/issues/3543))
- Fixed `--thinking xhigh` for `openai-codex` `gpt-5.5` so it is no longer downgraded to `high`.
- Fixed git package installs with custom `npmCommand` values such as `pnpm` by avoiding npm-specific production flags in that compatibility path ([#3604](https://github.com/badlogic/pi-mono/issues/3604))
- Fixed first user messages rendering without spacing after existing notices such as compaction summaries or status messages ([#3613](https://github.com/badlogic/pi-mono/issues/3613))
- Fixed the handoff extension example to use the replacement-session context after creating a new session, avoiding stale `ctx` errors when it installs the generated prompt ([#3606](https://github.com/badlogic/pi-mono/issues/3606))
- Fixed session replacement and `/quit` teardown ordering to run host-owned extension UI cleanup synchronously after `session_shutdown` handlers complete but before invalidating the old extension context, preventing stale extension UI from rendering against a disposed session ([#3597](https://github.com/badlogic/pi-mono/pull/3597) by [@vegarsti](https://github.com/vegarsti))
- Fixed crash on `/quit` when an extension registers a custom footer whose `render()` accesses `ctx`, by tearing down extension-provided UI before invalidating the extension runner during shutdown ([#3595](https://github.com/badlogic/pi-mono/issues/3595))
- Fixed auto-retry to treat Bedrock/Smithy HTTP/2 transport failures like `http2 request did not get a response` as transient errors, so the agent retries automatically instead of waiting for a manual nudge ([#3594](https://github.com/badlogic/pi-mono/issues/3594))
- Fixed the CLI/SDK tool-selection split so `--no-builtin-tools` and `createAgentSession({ noTools: "builtin" })` disable only built-in default tools while keeping extension/custom tools enabled, instead of falling through to the same "disable everything" path as `--no-tools` ([#3592](https://github.com/badlogic/pi-mono/issues/3592))
- Fixed remaining hardcoded `pi` / `.pi` branding to route through `APP_NAME` and `CONFIG_DIR_NAME` extension points, so SDK rebrands get consistent naming in `/quit` description, `process.title`, and the project-local extensions directory ([#3583](https://github.com/badlogic/pi-mono/pull/3583) by [@jlaneve](https://github.com/jlaneve))
- Fixed `pi-coding-agent` shipping `uuid@11`, which triggered `npm audit` moderate vulnerability reports for downstream installs; the package now depends on `uuid@14` ([#3577](https://github.com/badlogic/pi-mono/issues/3577))
- Fixed `openai-completions` streamed tool-call assembly to coalesce deltas by stable tool index when OpenAI-compatible gateways mutate tool call IDs mid-stream, preventing malformed Kimi K2.6/OpenCode tool streams from splitting one call into multiple bogus tool calls ([#3576](https://github.com/badlogic/pi-mono/issues/3576))
- Fixed `ctx.ui.setWorkingMessage()` to persist across loader recreation, matching the behavior of `ctx.ui.setWorkingIndicator()` ([#3566](https://github.com/badlogic/pi-mono/issues/3566))
- Fixed coding-agent `fs.watch` error handling for theme and git-footer watchers to retry after transient watcher failures such as `EMFILE`, avoiding startup crashes in large repos ([#3564](https://github.com/badlogic/pi-mono/issues/3564))
- Fixed built-in `kimi-coding` model generation to attach the expected `User-Agent` header so direct Kimi Coding requests use the provider's expected client identity ([#3586](https://github.com/badlogic/pi-mono/issues/3586))
- Fixed extension shortcut conflict diagnostics to display at startup instead of only on reload, so extension authors discover reserved keybinding conflicts immediately rather than discovering them later through user feedback ([#3617](https://github.com/badlogic/pi-mono/issues/3617))
- Fixed `models.json` Anthropic-compatible provider configuration to accept `compat.supportsEagerToolInputStreaming`, allowing proxies that reject per-tool `eager_input_streaming` to use the legacy fine-grained tool streaming beta header instead ([#3575](https://github.com/badlogic/pi-mono/issues/3575))
- Fixed startup banner extension labels to strip trailing `index.js`/`index.ts` suffixes ([#3596](https://github.com/badlogic/pi-mono/pull/3596) by [@aliou](https://github.com/aliou))
- Fixed OSC 9;4 terminal progress updates to stay alive in terminals such as Ghostty during long-running agent work ([#3610](https://github.com/badlogic/pi-mono/issues/3610))
- Fixed OpenAI-compatible completion usage parsing to avoid double-counting reasoning tokens already included in `completion_tokens` ([#3581](https://github.com/badlogic/pi-mono/issues/3581))
- Fixed `openai-responses` compatibility for strict OpenAI-compatible proxies by allowing `models.json` to disable the underscore-containing `session_id` header with `compat.sendSessionIdHeader: false` ([#3579](https://github.com/badlogic/pi-mono/issues/3579))
- Fixed GPT-5.5 Codex capability handling to clamp unsupported minimal reasoning to `low` and apply the model's 2.5x priority service-tier pricing multiplier ([#3618](https://github.com/badlogic/pi-mono/pull/3618) by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.69.0] - 2026-04-22

### New Features

- TypeBox 1.x migration for extensions and SDK integrations, including TypeBox-native tool argument validation that now works in eval-restricted runtimes such as Cloudflare Workers. See [docs/extensions.md](docs/extensions.md) and [docs/sdk.md](docs/sdk.md).
- Stacked extension autocomplete providers via `ctx.ui.addAutocompleteProvider(...)`, allowing extensions to layer custom completion logic on top of built-in slash and path completion. See [docs/extensions.md#autocomplete-providers](docs/extensions.md#autocomplete-providers) and [examples/extensions/github-issue-autocomplete.ts](examples/extensions/github-issue-autocomplete.ts).
- Terminating tool results via `terminate: true`, allowing custom tools to end on a final tool call without paying for an automatic follow-up LLM turn. See [docs/extensions.md](docs/extensions.md) and [examples/extensions/structured-output.ts](examples/extensions/structured-output.ts).
- OSC 9;4 terminal progress indicators during agent streaming and compaction for supporting terminals.

### Breaking Changes

- Migrated first-party coding-agent code, SDK/examples/docs, and package metadata from `@sinclair/typebox` 0.34.x to `typebox` 1.x. New extensions, SDK integrations, and pi packages should depend on and import from `typebox`. Legacy extension loading still aliases the root `@sinclair/typebox` package, but `@sinclair/typebox/compiler` is no longer shimmed. This migration also picks up the new `@mariozechner/pi-ai` TypeBox-native validator path, so tool argument validation now works in eval-restricted runtimes such as Cloudflare Workers instead of being skipped ([#3112](https://github.com/badlogic/pi-mono/issues/3112))
- Session-replacement commands now invalidate captured pre-replacement session-bound extension objects after `ctx.newSession()`, `ctx.fork()`, and `ctx.switchSession()`. Old `pi` and command `ctx` references now throw instead of silently targeting the replaced session. Migration: if code needs to keep working in the replacement session after one of those calls, pass `withSession` to that same method and do the post-switch work there. In practice, move post-switch `pi.sendUserMessage()`, `pi.sendMessage()`, and command-ctx/session-manager access into `withSession`, and use only the `ReplacedSessionContext` passed to that callback for session-bound operations. Footguns: `withSession` runs after the old extension instance has already received `session_shutdown`, old cleanup may already have invalidated captured state, captured old `pi` / old command `ctx` are stale, and previously extracted raw objects such as `const sm = ctx.sessionManager` remain the caller's responsibility and must not be reused after the switch.

### Added

- Added support for terminating tool results via `terminate: true`, allowing custom tools to end the current tool batch without an automatic follow-up LLM call, plus a `structured-output.ts` extension example and extension docs showing the pattern ([#3525](https://github.com/badlogic/pi-mono/issues/3525))
- Added OSC 9;4 terminal progress indicators during agent streaming and compaction, so terminals like iTerm2, WezTerm, Windows Terminal, and Kitty show activity in their tab bar
- Added `ctx.ui.addAutocompleteProvider(...)` for stacking extension autocomplete providers on top of the built-in slash/path provider, plus a `github-issue-autocomplete.ts` example and extension docs ([#2983](https://github.com/badlogic/pi-mono/issues/2983))

### Fixed

- Fixed exported session HTML to sanitize markdown link URLs before rendering them into anchor tags, blocking `javascript:`-style payloads while preserving safe links in shared/exported sessions ([#3532](https://github.com/badlogic/pi-mono/issues/3532))
- Fixed `ctx.getSystemPrompt()` inside `before_agent_start` to reflect chained system-prompt changes made by earlier `before_agent_start` handlers, and clarified the extension docs around provider-payload rewrites and what `ctx.getSystemPrompt()` does and does not report ([#3539](https://github.com/badlogic/pi-mono/issues/3539))
- Fixed built-in `google-gemini-cli` model lists and selector entries to include `gemini-3.1-flash-lite-preview`, so Cloud Code Assist users no longer need manual `--model` fallback selection to use it ([#3545](https://github.com/badlogic/pi-mono/issues/3545))
- Fixed extension session-replacement flows so `ctx.newSession()`, `ctx.fork()`, `ctx.switchSession()`, and imported-session replacements fully rebind before post-switch work runs, added `withSession` replacement callbacks with fresh `ReplacedSessionContext` helpers, and make stale pre-replacement `pi` / `ctx` session-bound accesses throw instead of silently targeting the wrong session ([#2860](https://github.com/badlogic/pi-mono/issues/2860))
- Fixed `models.json` built-in provider overrides to accept `headers` without requiring `baseUrl`, so request-header-only overrides now load and apply correctly ([#3538](https://github.com/badlogic/pi-mono/issues/3538))

## [0.68.1] - 2026-04-22

### New Features

- Fireworks provider support with built-in models and `FIREWORKS_API_KEY` auth. See [README.md#providers--models](README.md#providers--models) and [docs/providers.md](docs/providers.md).
- Configurable inline tool image width via `terminal.imageWidthCells` in `/settings`. See [docs/settings.md#terminal--images](docs/settings.md#terminal--images).

### Added

- Added built-in Fireworks provider support, including `FIREWORKS_API_KEY` setup/docs and the default Fireworks model `accounts/fireworks/models/kimi-k2p6` ([#3519](https://github.com/badlogic/pi-mono/issues/3519))

### Fixed

- Fixed interactive inline tool images to honor configurable `terminal.imageWidthCells` via `/settings`, so tool-output images are no longer hard-capped to 60 terminal cells ([#3508](https://github.com/badlogic/pi-mono/issues/3508))
- Fixed `sessionDir` in `settings.json` to expand `~`, so portable session-directory settings no longer require a shell wrapper ([#3514](https://github.com/badlogic/pi-mono/issues/3514))
- Fixed parallel tool-call rows to leave the pending state as soon as each tool is finalized, while still appending persisted tool results in assistant source order ([#3503](https://github.com/badlogic/pi-mono/issues/3503))
- Fixed exported session markdown to render Markdown while showing HTML-like message content such as `<file name="...">...</file>` verbatim, so shared sessions match the TUI instead of letting the browser interpret message text ([#3484](https://github.com/badlogic/pi-mono/issues/3484))
- Fixed exported session HTML to render `grep` and `find` output through their existing TUI renderers and `ls` output through a native template renderer, avoiding missing formatting and spacing artifacts in shared sessions ([#3491](https://github.com/badlogic/pi-mono/pull/3491) by [@aliou](https://github.com/aliou))
- Fixed `@` autocomplete fuzzy search to follow symlinked directories and include symlinked paths in results ([#3507](https://github.com/badlogic/pi-mono/issues/3507))
- Fixed proxied agent streams to preserve the proxy-safe serializable subset of stream options, including session, transport, retry-delay, metadata, header, cache-retention, and thinking-budget settings ([#3512](https://github.com/badlogic/pi-mono/issues/3512))
- Hardened Anthropic streaming against malformed tool-call JSON by owning SSE parsing with defensive JSON repair, replacing the deprecated `fine-grained-tool-streaming` beta header with per-tool `eager_input_streaming`, and updating stale test model references ([#3175](https://github.com/badlogic/pi-mono/issues/3175))
- Fixed Bedrock runtime endpoint resolution to stop pinning built-in regional endpoints over `AWS_REGION` / `AWS_PROFILE`, restoring `us.*` and `eu.*` inference profile support after v0.68.0 while preserving custom VPC/proxy endpoint overrides ([#3481](https://github.com/badlogic/pi-mono/issues/3481), [#3485](https://github.com/badlogic/pi-mono/issues/3485), [#3486](https://github.com/badlogic/pi-mono/issues/3486), [#3487](https://github.com/badlogic/pi-mono/issues/3487), [#3488](https://github.com/badlogic/pi-mono/issues/3488))

## [0.68.0] - 2026-04-20

### New Features

- Configurable streaming working indicator for extensions via `ctx.ui.setWorkingIndicator()`, including animated, static, and hidden indicators. See [docs/tui.md#working-indicator](docs/tui.md#working-indicator), [docs/extensions.md](docs/extensions.md), and [examples/extensions/working-indicator.ts](examples/extensions/working-indicator.ts).
- `before_agent_start` now exposes `systemPromptOptions` (`BuildSystemPromptOptions`) so extensions can inspect the structured system-prompt inputs without re-discovering resources. See [docs/extensions.md#before_agent_start](docs/extensions.md#before_agent_start) and [examples/extensions/prompt-customizer.ts](examples/extensions/prompt-customizer.ts).
- Configurable keybindings for scoped model selector actions and session-tree filter actions. See [docs/keybindings.md](docs/keybindings.md).
- `/clone` duplicates the current active branch into a new session, while extensions can choose whether to fork `before` or `at` an entry via `ctx.fork(..., { position })`. See [README.md](README.md), [docs/extensions.md](docs/extensions.md), and [docs/session.md](docs/session.md).

### Breaking Changes

- Changed SDK and CLI tool selection from cwd-bound built-in tool instances to tool-name allowlists. `createAgentSession({ tools })` now expects `string[]` names such as `"read"` and `"bash"` instead of `Tool[]`, `--tools` now allowlists built-in, extension, and custom tools by name, and `--no-tools` now disables all tools by default rather than only built-ins. Migrate SDK code from `tools: [readTool, bashTool]` to `tools: ["read", "bash"]` ([#2835](https://github.com/badlogic/pi-mono/issues/2835), [#3452](https://github.com/badlogic/pi-mono/issues/3452))
- Removed prebuilt cwd-bound tool and tool-definition exports from `@bastani/atomic`, including `readTool`, `bashTool`, `editTool`, `writeTool`, `grepTool`, `findTool`, `lsTool`, `readOnlyTools`, `codingTools`, and the corresponding `*ToolDefinition` values. Use the explicit factory exports instead, for example `createReadTool(cwd)`, `createBashTool(cwd)`, `createCodingTools(cwd)`, and `createReadToolDefinition(cwd)` ([#3452](https://github.com/badlogic/pi-mono/issues/3452))
- Removed ambient `process.cwd()` / default agent-dir fallback behavior from public resource helpers. `DefaultResourceLoader`, `loadProjectContextFiles()`, and `loadSkills()` now require explicit cwd/agent-dir style inputs, and exported system-prompt option types now require an explicit `cwd`. Pass the session or project cwd explicitly instead of relying on process-global defaults ([#3452](https://github.com/badlogic/pi-mono/issues/3452))

### Added

- Added extension support for customizing the interactive streaming working indicator via `ctx.ui.setWorkingIndicator()`, including custom animated frames, static indicators, hidden indicators, a new `working-indicator.ts` example extension, and updated extension/TUI/RPC docs ([#3413](https://github.com/badlogic/pi-mono/issues/3413))
- Added `systemPromptOptions` (`BuildSystemPromptOptions`) to `before_agent_start` extension events, so extensions can inspect the structured inputs used to build the current system prompt ([#3473](https://github.com/badlogic/pi-mono/pull/3473) by [@dljsjr](https://github.com/dljsjr))
- Added `/clone` to duplicate the current active branch into a new session, while keeping `/fork` focused on forking from a previous user message ([#2962](https://github.com/badlogic/pi-mono/issues/2962))
- Added `ctx.fork()` support for `position: "before" | "at"` so extensions and integrations can branch before a user message or duplicate the current point in the conversation; the interactive clone/fork UX builds on that runtime support ([#3431](https://github.com/badlogic/pi-mono/pull/3431) by [@mitsuhiko](https://github.com/mitsuhiko))
- Added configurable keybinding ids for scoped model selector actions and tree filter actions, so those interactive shortcuts can be remapped in `keybindings.json` ([#3343](https://github.com/badlogic/pi-mono/pull/3343) by [@mpazik](https://github.com/mpazik))
- Added `PI_OAUTH_CALLBACK_HOST` support for built-in OAuth login flows, allowing local callback servers used by `pi auth` to bind to a custom interface instead of hardcoded `127.0.0.1` ([#3409](https://github.com/badlogic/pi-mono/pull/3409) by [@Michaelliv](https://github.com/Michaelliv))
- Added `reason` and `targetSessionFile` metadata to `session_shutdown` extension events, so extensions can distinguish quit, reload, new-session, resume, and fork teardown paths ([#2863](https://github.com/badlogic/pi-mono/issues/2863))

### Changed

- Changed `pi update` to batch npm package updates per scope and run git package updates with bounded parallelism, reducing multi-package update time while preserving skip behavior for pinned and already-current packages ([#2980](https://github.com/badlogic/pi-mono/issues/2980))
- Changed Bedrock session requests to omit `maxTokens` when model token limits are unknown and to omit `temperature` when unset, letting Bedrock use provider defaults and avoid unnecessary TPM quota reservation ([#3400](https://github.com/badlogic/pi-mono/pull/3400) by [@wirjo](https://github.com/wirjo))

### Fixed

- Fixed `AgentSession` system-prompt option initialization to avoid constructing an invalid empty `BuildSystemPromptOptions`, so `npm run check` passes after `cwd` became mandatory.
- Fixed shell-path resolution to stop consulting ambient `process.cwd()` state during bash execution, so session/project-specific `shellPath` settings now follow the active coding-agent session cwd instead of the launcher cwd ([#3452](https://github.com/badlogic/pi-mono/issues/3452))
- Fixed `ctx.ui.setWorkingIndicator()` custom frames to render verbatim instead of forcing the theme accent color, so extensions now own working-indicator coloring when they customize it ([#3467](https://github.com/badlogic/pi-mono/issues/3467))
- Fixed `pi update` reinstalling npm packages that are already at the latest published version by checking the installed package version before running `npm install <pkg>@latest` ([#3000](https://github.com/badlogic/pi-mono/issues/3000))
- Fixed `@` autocomplete plain queries to stop matching against the full cwd/base path, so path fragments in worktree names no longer crowd out intended results such as `@plan` ([#2778](https://github.com/badlogic/pi-mono/issues/2778))
- Fixed built-in tool wrapping to use the same extension-runner context path as extension tools, so built-in tools receive execution context and `read` can warn when the current model does not support images ([#3429](https://github.com/badlogic/pi-mono/issues/3429))
- Fixed `openai-completions` assistant replay to preserve `compat.requiresThinkingAsText` text-part serialization, avoiding same-model follow-up crashes when previous assistant messages mix thinking and text ([#3387](https://github.com/badlogic/pi-mono/issues/3387))
- Fixed direct OpenAI Chat Completions sessions to map `sessionId` and `cacheRetention` to prompt caching fields, sending `prompt_cache_key` when caching is enabled and `prompt_cache_retention: "24h"` for direct `api.openai.com` requests with long retention ([#3426](https://github.com/badlogic/pi-mono/issues/3426))
- Fixed OpenAI-compatible Chat Completions sessions to optionally send aligned `session_id`, `x-client-request-id`, and `x-session-affinity` headers from `sessionId` via `compat.sendSessionAffinityHeaders`, improving cache-affinity routing for backends such as Fireworks ([#3430](https://github.com/badlogic/pi-mono/issues/3430))
- Fixed threaded `/resume` session relationships and current-session detection to canonicalize symlinked session paths during selector comparisons, so shared session directories no longer break parent-child matching or active-session delete protection ([#3364](https://github.com/badlogic/pi-mono/issues/3364))
- Fixed `/session`, Sessions docs, and CLI help to consistently document that session reuse supports both file paths and session IDs, and that `/session` shows the current session ID ([#3390](https://github.com/badlogic/pi-mono/issues/3390))
- Fixed Windows pnpm global install detection to recognize `\\.pnpm\\` store paths, so update notices now suggest `pnpm install -g @bastani/atomic` instead of falling back to npm ([#3378](https://github.com/badlogic/pi-mono/issues/3378))
- Fixed missing `@sinclair/typebox` runtime dependency in `@bastani/atomic`, so strict pnpm installs no longer fail with `ERR_MODULE_NOT_FOUND` when starting `pi` ([#3434](https://github.com/badlogic/pi-mono/issues/3434))
- Fixed xterm uppercase typing in the interactive editor by decoding printable `modifyOtherKeys` input and normalizing shifted letter matching, so `Shift+letter` no longer disappears in `pi` ([#3436](https://github.com/badlogic/pi-mono/issues/3436))
- Fixed `/compact` to reuse the session thinking level for compaction summaries instead of forcing `high`, avoiding invalid reasoning-effort errors on `github-copilot/claude-opus-4.7` sessions configured for `medium` thinking ([#3438](https://github.com/badlogic/pi-mono/issues/3438))
- Fixed shared/exported plain-text tool output to preserve indentation instead of collapsing leading whitespace in the web share page ([#3440](https://github.com/badlogic/pi-mono/issues/3440))
- Fixed exported share pages to use browser-safe `T` and `O` shortcuts with clickable header toggles for thinking and tool visibility instead of browser-reserved `Ctrl+T` / `Ctrl+O` bindings ([#3374](https://github.com/badlogic/pi-mono/pull/3374) by [@vekexasia](https://github.com/vekexasia))
- Fixed skill resolution to dedupe symlinked aliases by canonical path, so `pi config` no longer shows duplicate skill entries when `~/.pi/agent/skills` points to `~/.agents/skills` ([#3417](https://github.com/badlogic/pi-mono/pull/3417) by [@rwachtler](https://github.com/rwachtler))
- Fixed OpenRouter request attribution to include Pi app headers (`HTTP-Referer: https://pi.dev`, `X-OpenRouter-Title: pi`, `X-OpenRouter-Categories: cli-agent`) when sessions are created through the coding-agent SDK and install telemetry is enabled ([#3414](https://github.com/badlogic/pi-mono/issues/3414))
- Fixed custom-model `compat` schema/docs to support `cacheControlFormat: "anthropic"` for OpenAI-compatible providers that expose Anthropic-style prompt caching via `cache_control` markers ([#3392](https://github.com/badlogic/pi-mono/issues/3392))
- Fixed Cloud Code Assist tool schemas to strip JSON Schema meta-declaration keys before provider translation, avoiding validation failures for tool-enabled sessions that use `$schema`, `$defs`, and related metadata ([#3412](https://github.com/badlogic/pi-mono/pull/3412) by [@vladlearns](https://github.com/vladlearns))
- Fixed direct Bedrock sessions to honor `model.baseUrl` as the runtime client endpoint, restoring support for custom Bedrock VPC or proxy routes ([#3402](https://github.com/badlogic/pi-mono/pull/3402) by [@wirjo](https://github.com/wirjo))
- Fixed the `edit` tool to coerce stringified `edits` JSON before validation, so models that send the array payload as a JSON string no longer fall back to ad-hoc shell edits ([#3370](https://github.com/badlogic/pi-mono/pull/3370) by [@dannote](https://github.com/dannote))
- Fixed package manifest positive glob entries to expand before loading packaged resources, restoring manifest patterns such as `skills/**/*.md` ([#3350](https://github.com/badlogic/pi-mono/pull/3350) by [@neonspectra](https://github.com/neonspectra))

## [0.67.68] - 2026-04-17

## [0.67.67] - 2026-04-17

### New Features

- Bedrock sessions can now authenticate with `AWS_BEARER_TOKEN_BEDROCK`, enabling Converse API access without local SigV4 credentials. See [docs/providers.md#amazon-bedrock](docs/providers.md#amazon-bedrock).

### Added

- Added Bedrock bearer-token authentication support via `AWS_BEARER_TOKEN_BEDROCK`, enabling coding-agent sessions to use Bedrock Converse without local SigV4 credentials ([#3125](https://github.com/badlogic/pi-mono/pull/3125) by [@wirjo](https://github.com/wirjo))

### Fixed

- Fixed `/scoped-models` Alt+Up/Down to stay a no-op in the implicit `all enabled` state instead of materializing a full explicit enabled-model list and marking the selector dirty ([#3331](https://github.com/badlogic/pi-mono/issues/3331))
- Fixed Mistral Small 4 default thinking requests to use the model's supported reasoning control, avoiding `400` errors when starting sessions on `mistral-small-2603` and `mistral-small-latest` ([#3338](https://github.com/badlogic/pi-mono/issues/3338))
- Fixed Qwen chat-template thinking replay to preserve prior thinking across turns, so affected OpenAI-compatible models keep multi-turn tool-call arguments instead of degrading to empty `{}` payloads ([#3325](https://github.com/badlogic/pi-mono/issues/3325))
- Fixed exported HTML transcripts so text selection no longer triggers click-based expand/collapse toggles ([#3332](https://github.com/badlogic/pi-mono/pull/3332) by [@xu0o0](https://github.com/xu0o0))
- Fixed flaky git package update notifications by waiting for captured git command stdio to fully drain before comparing local and remote commit SHAs ([#3027](https://github.com/badlogic/pi-mono/issues/3027))
- Fixed system prompt dates to use a stable `YYYY-MM-DD` format instead of locale-dependent output, keeping prompts deterministic across runtimes and locales ([#2814](https://github.com/badlogic/pi-mono/issues/2814))
- Fixed auto-retry transient error detection to treat `Network connection lost.` as retryable, so dropped provider connections retry instead of terminating the agent ([#3317](https://github.com/badlogic/pi-mono/issues/3317))
- Fixed compact interactive extension startup summaries to disambiguate package extensions and repeated local `index.ts` entries by using package-aware labels and the minimal parent path needed to make local entries unique ([#3308](https://github.com/badlogic/pi-mono/issues/3308))
- Fixed git package dependency installation to use production installs (`npm install --omit=dev`) during both install and update flows, so extension runtime dependencies must come from `dependencies` and not `devDependencies` ([#3009](https://github.com/badlogic/pi-mono/issues/3009))
- Fixed `tool_result` / `afterToolCall` extension handling for error results by forwarding `details` and `isError` overrides through `AgentSession` instead of dropping them when `isError` was already true ([#3051](https://github.com/badlogic/pi-mono/issues/3051))
- Fixed missing root exports for `RpcClient` and RPC protocol types from `@bastani/atomic`, so ESM consumers can import them from the main package entrypoint ([#3275](https://github.com/badlogic/pi-mono/issues/3275))
- Fixed OpenAI Codex service-tier cost accounting to trust the explicitly requested tier when the API echoes the default tier in responses, keeping session cost displays aligned with the selected tier ([#3307](https://github.com/badlogic/pi-mono/pull/3307) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- Fixed parallel tool-call finalization to convert `afterToolCall` hook throws into error tool results instead of aborting the remaining tool batch ([#3084](https://github.com/badlogic/pi-mono/issues/3084))
- Fixed Bun binary asset path resolution to honor `PI_PACKAGE_DIR` for built-in themes, HTML export templates, and interactive bundled assets ([#3074](https://github.com/badlogic/pi-mono/issues/3074))
- Fixed user-message turn spacing in interactive mode by restoring an inter-message spacer before user turns (except the first user message), preventing assistant and user blocks from rendering flush together.
- Fixed interactive `/import` handling to support quoted JSONL paths with spaces, route missing JSONL files through the non-fatal `SessionImportFileNotFoundError` path, and document the `importFromJsonl()` exceptions (`SessionImportFileNotFoundError`, `MissingSessionCwdError`).

## [0.67.6] - 2026-04-16

### New Features

- Prompt templates support an `argument-hint` frontmatter field that renders before the description in the `/` autocomplete dropdown, using `<angle>` for required and `[square]` for optional arguments. See [docs/prompt-templates.md#argument-hints](docs/prompt-templates.md#argument-hints).
- New `after_provider_response` extension hook lets extensions inspect provider HTTP status codes and headers immediately after each response is received and before stream consumption begins. See [docs/extensions.md](docs/extensions.md).
- Compact interactive startup header with a comma-separated view of loaded AGENTS.md files, prompt templates, skills, and extensions. Press `Ctrl+O` to toggle the expanded listing.
- Markdown links in assistant output now render as OSC 8 hyperlinks on terminals that advertise support; unknown terminals and tmux/screen default to plain text so URLs are never silently dropped.

### Added

- Added `argument-hint` frontmatter field for prompt templates, displayed before the description in the autocomplete dropdown ([#2780](https://github.com/badlogic/pi-mono/pull/2780) by [@andresvi94](https://github.com/andresvi94))
- Added `after_provider_response` extension hook so extensions can inspect provider HTTP status codes and headers after each provider response is received and before stream consumption begins ([#3128](https://github.com/badlogic/pi-mono/issues/3128))
- Added OSC 8 hyperlink rendering for markdown links when the terminal advertises support ([#3248](https://github.com/badlogic/pi-mono/pull/3248) by [@ofa1](https://github.com/ofa1))

### Changed

- Changed interactive startup header to a compact, comma-separated view of loaded AGENTS.md files, prompt templates, skills, and extensions, with `Ctrl+O` to toggle the expanded listing ([#3267](https://github.com/badlogic/pi-mono/pull/3267))
- Tightened hyperlink capability detection to default `hyperlinks: false` for unknown terminals and force it off under tmux/screen (including nested sessions), preventing markdown link URLs from disappearing on terminals that silently swallow OSC 8 sequences ([#3248](https://github.com/badlogic/pi-mono/pull/3248))

### Fixed

- Fixed interactive user message rendering to keep bottom padding visible in terminals affected by OSC 133 prompt markers without adding an extra blank line before the following assistant message ([#3090](https://github.com/badlogic/pi-mono/issues/3090))
- Fixed `--verbose` startup output to begin with expanded startup help and loaded resource listings after the compact startup header change ([#3147](https://github.com/badlogic/pi-mono/issues/3147))
- Fixed `find` tool returning no results for path-based glob patterns such as `src/**/*.spec.ts` or `some/parent/child/**` by switching fd into full-path mode and normalizing the pattern when it contains a `/` ([#3302](https://github.com/badlogic/pi-mono/issues/3302))
- Fixed `find` tool applying nested `.gitignore` rules across sibling directories (e.g. rules from `a/.gitignore` hiding matching files under `b/`) by dropping the manual `--ignore-file` collection and delegating to fd's hierarchical `.gitignore` handling via `--no-require-git` ([#3303](https://github.com/badlogic/pi-mono/issues/3303))
- Fixed OpenAI Responses prompt caching for non-`api.openai.com` base URLs (OpenAI-compatible proxies such as litellm, theclawbay) by sending the `session_id` and `x-client-request-id` cache-affinity headers unconditionally when a `sessionId` is provided, matching the official Codex CLI behavior ([#3264](https://github.com/badlogic/pi-mono/pull/3264) by [@vegarsti](https://github.com/vegarsti))
- Fixed the `preset` example extension to snapshot the active model, thinking level, and tool set on the first preset application and restore that state when cycling back to `(none)`, instead of falling back to a hardcoded default tool list ([#3272](https://github.com/badlogic/pi-mono/pull/3272) by [@stembi](https://github.com/stembi))

## [0.67.5] - 2026-04-16

### Fixed

- Fixed Opus 4.7 adaptive thinking configuration across Anthropic and Bedrock providers by recognizing Opus 4.7 adaptive-thinking support and mapping `xhigh` reasoning to provider-supported effort values ([#3286](https://github.com/badlogic/pi-mono/pull/3286) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- Fixed Zellij `Shift+Enter` regressions by reverting the Zellij-specific Kitty keyboard query bypass and restoring the previous keyboard negotiation behavior ([#3259](https://github.com/badlogic/pi-mono/issues/3259))

## [0.67.4] - 2026-04-16

### New Features

- `--no-context-files` (`-nc`) disables automatic `AGENTS.md` / `CLAUDE.md` discovery when you need a clean run without project context injection. See [README.md#context-files](README.md#context-files).
- `loadProjectContextFiles()` is now exported as a standalone utility for extensions and SDK-style integrations that need to inspect the same context-file resolution order used by the CLI. See [README.md#context-files](README.md#context-files).
- New `after_provider_response` extension hook lets extensions inspect provider HTTP status codes and headers immediately after response creation and before stream consumption. See [docs/extensions.md](docs/extensions.md).

### Added

- Added `--no-context-files` (`-nc`) to disable `AGENTS.md` and `CLAUDE.md` context file discovery and loading ([#3253](https://github.com/badlogic/pi-mono/issues/3253))
- Exported `loadProjectContextFiles()` as a standalone utility so extensions can discover project context files without instantiating a full `DefaultResourceLoader` ([#3142](https://github.com/badlogic/pi-mono/issues/3142))
- Added `after_provider_response` extension hook so extensions can inspect provider HTTP status codes and headers after each provider response is received and before stream consumption begins ([#3128](https://github.com/badlogic/pi-mono/issues/3128))

### Changed

- Added `claude-opus-4-7` model for Anthropic.
- Changed Anthropic prompt caching to add a `cache_control` breakpoint on the last tool definition, so tool schemas can be cached independently from transcript updates while preserving existing cache retention behavior ([#3260](https://github.com/badlogic/pi-mono/issues/3260))

### Fixed

- Fixed markdown strikethrough parsing in interactive rendering and HTML export to require strict double-tilde delimiters (`~~text~~`) with non-whitespace boundaries.
- Fixed shutdown handling to kill tracked detached `bash` tool child processes on exit signals, preventing orphaned background processes.
- Fixed flaky `edit-tool-no-full-redraw` TUI tests by waiting for asynchronous preview and preflight error rendering instead of relying on fixed render ticks.
- Fixed `kimi-coding` default model selection to use `kimi-for-coding` instead of `kimi-k2-thinking` ([#3242](https://github.com/badlogic/pi-mono/issues/3242))
- Fixed `ctrl+z` on native Windows to avoid crashing interactive mode, disable the default suspend binding there, and show a status message when suspend is invoked manually ([#3191](https://github.com/badlogic/pi-mono/issues/3191))
- Fixed `find` tool cancellation and responsiveness on broad searches by making `.gitignore` discovery and `fd` execution fully abort-aware and non-blocking ([#3148](https://github.com/badlogic/pi-mono/issues/3148))
- Fixed `grep` broad-search stalls when `context=0` by formatting match lines from ripgrep JSON output instead of doing synchronous per-match file reads ([#3205](https://github.com/badlogic/pi-mono/issues/3205))

## [0.67.3] - 2026-04-15

### New Features

- `renderShell: "self"` for custom and built-in tool renderers so tools can own their outer shell instead of the default boxed shell. Useful for stable large previews such as edit diffs. See [docs/extensions.md#custom-rendering](docs/extensions.md#custom-rendering).
- Interactive auto-retry status now shows a live countdown during backoff instead of a static retry delay message.

### Added

- Added `renderShell: "self"` for custom and built-in tool renderers so tools can own their outer shell instead of using the default boxed shell. This is useful for stable large previews such as edit diffs ([#3134](https://github.com/badlogic/pi-mono/issues/3134))

### Fixed

- Fixed edit diff previews to stay visible during edit permission dialogs and session replay without reintroducing large-result redraw flicker ([#3134](https://github.com/badlogic/pi-mono/issues/3134))
- Fixed `/reload` to render a static reload status box instead of an animated spinner, avoiding redraw instability during interactive reloads.
- Fixed the `plan-mode` example extension to allow `eza` in the read-only bash allowlist instead of the deprecated `exa` command ([#3240](https://github.com/badlogic/pi-mono/pull/3240) by [@rwachtler](https://github.com/rwachtler))
- Fixed `google-vertex` API key resolution to treat `gcp-vertex-credentials` as an Application Default Credentials marker instead of a literal API key, so marker-based setups correctly fall back to ADC ([#3221](https://github.com/badlogic/pi-mono/pull/3221) by [@deepkilo](https://github.com/deepkilo))
- Fixed RPC `prompt` to wait for prompt preflight success before emitting its single authoritative response, while still treating handled and queued prompts as success ([#3049](https://github.com/badlogic/pi-mono/issues/3049))
- Fixed `/scoped-models` reordering to propagate into the `/model` scoped tab, preserving the user-defined scoped model order instead of re-sorting it ([#3217](https://github.com/badlogic/pi-mono/issues/3217))
- Fixed `session_shutdown` to fire on `SIGHUP` and `SIGTERM` in interactive, print, and RPC modes so extensions can run shutdown cleanup on those signal-driven exits ([#3212](https://github.com/badlogic/pi-mono/issues/3212))
- Fixed screenshot path parsing to handle lower case am/pm in macOS screenshot filenames ([#3194](https://github.com/badlogic/pi-mono/pull/3194) by [@jay-aye-see-kay](https://github.com/jay-aye-see-kay))
- Fixed interactive auto-retry status updates to show a live countdown during backoff instead of a static retry delay message ([#3187](https://github.com/badlogic/pi-mono/issues/3187))

## [0.67.2] - 2026-04-14

### New Features

- Support for multiple `--append-system-prompt` flags, each value is appended to the system prompt separated by double newlines. See [README.md#other-options](README.md#other-options).
- Support for passing inline extension factories to `main()` for embedded integrations and custom entrypoints.
- Interactive keybinding support for Kitty `super`-modified shortcuts such as `super+k`, `super+enter`, and `ctrl+super+k`. See [docs/keybindings.md](docs/keybindings.md).

### Added

- Added support for multiple `--append-system-prompt` flags, each value is appended to the system prompt separated by double newlines ([#3171](https://github.com/badlogic/pi-mono/pull/3171) by [@aliou](https://github.com/aliou))
- Added interactive keybinding support for Kitty `super`-modified shortcuts such as `super+k`, `super+enter`, and `ctrl+super+k` ([#3111](https://github.com/badlogic/pi-mono/pull/3111) by [@sudosubin](https://github.com/sudosubin))
- Added support for passing inline extension factories to `main()` for embedded integrations and custom entrypoints ([#3099](https://github.com/badlogic/pi-mono/pull/3099) by [@pmateusz](https://github.com/pmateusz))

### Fixed

- Fixed direct OpenAI Responses and Codex SSE requests to align `prompt_cache_key`, `session_id`, and `x-client-request-id` values with the same session-derived identifier, improving prompt cache affinity for append-only sessions ([#3018](https://github.com/badlogic/pi-mono/pull/3018) by [@steipete](https://github.com/steipete))
- Fixed streaming-only `partialJson` scratch buffers leaking into persisted OpenAI Responses tool calls, which could corrupt follow-up payloads on resumed conversations.
- Fixed Ctrl+Alt letter key matching in tmux by falling through from legacy ESC-prefixed handling to CSI-u and xterm `modifyOtherKeys` parsing when the legacy form does not match ([#2989](https://github.com/badlogic/pi-mono/pull/2989) by [@kaofelix](https://github.com/kaofelix))
- Fixed the shipped `subagent` example to avoid leaking Bun virtual filesystem script paths into subagent prompts ([#3002](https://github.com/badlogic/pi-mono/pull/3002) by [@nathyong](https://github.com/nathyong))
- Fixed bordered loaders to stop their animation timer when disposed, preventing stale loader updates after teardown.

## [0.67.1] - 2026-04-13

### Telemetry

Interactive mode now sends a lightweight anonymous install/update telemetry ping to `https://pi.dev/install?version=x.y.z` after it writes `lastChangelogVersion` in `settings.json`.

Why this exists:
- Pi needs a reliable per-version usage signal to understand whether releases are being adopted and to help justify funding continued development.
- npm download counts are not a reliable proxy for actual Pi usage.

How it works:
- It only runs in interactive mode.
- It does not run in RPC mode, print mode, JSON mode, or SDK mode.
- On a fresh interactive install, Pi writes `lastChangelogVersion`, then sends the ping.
- On later interactive startups, if the local changelog contains entries newer than the previously stored `lastChangelogVersion`, Pi writes the new `lastChangelogVersion`, then sends the ping.
- The request is fire-and-forget. Startup does not wait for it, and any errors are ignored.

What data is collected:
- Only the Pi version in the request path, for example `https://pi.dev/install?version=0.67.1`.
- The server stores only aggregate per-version counters such as `{ "0.67.1": 3 }`.
- It does not store IP addresses, client identifiers, prompts, paths, models, auth state, or any other per-user data. It literally only increments a counter for that version.

How to disable it:
- `/settings` → disable `Install telemetry`
- `settings.json` → set `enableInstallTelemetry` to `false`
- `PI_OFFLINE=1`
- `PI_TELEMETRY=0`

### New Features

- Full `openRouterRouting` support in `models.json`, including fallbacks, parameter requirements, data collection, ZDR, ignore lists, quantizations, provider sorting, max price, and preferred throughput and latency constraints. See [docs/models.md](docs/models.md).
- `PI_CODING_AGENT=true` environment variable set at startup so subprocesses can detect they are running inside the coding agent.
- Updated `antigravity-image-gen.ts` example extension to use User-Agent version `1.21.9` ([#2901](https://github.com/badlogic/pi-mono/pull/2901) by [@aadishv](https://github.com/aadishv))
- Fixed `--list-models` silently swallowing `models.json` load errors; errors are now printed to stderr ([#3072](https://github.com/badlogic/pi-mono/issues/3072))
- Fixed custom models for built-in providers (e.g. `openrouter`) being silently dropped from `--list-models` by inheriting `api`/`baseUrl` from built-in model definitions and no longer requiring `apiKey` for providers with existing auth ([#2921](https://github.com/badlogic/pi-mono/issues/2921) and [#3072](https://github.com/badlogic/pi-mono/issues/3072))
### Added

- Added full `openRouterRouting` field support in `models.json`, including fallbacks, parameter requirements, data collection, ZDR, ignore lists, quantizations, provider sorting, max price, and preferred throughput and latency constraints ([#2904](https://github.com/badlogic/pi-mono/pull/2904) by [@zmberber](https://github.com/zmberber))
- Set `PI_CODING_AGENT=true` environment variable at startup so sub-processes can detect they are running inside the coding agent ([#2868](https://github.com/badlogic/pi-mono/issues/2868))

### Fixed

- Fixed interactive changelog rendering for the telemetry notes by moving the section under a `### Telemetry` heading, so startup shows the full release notes instead of only the version header.
- Updated `antigravity-image-gen.ts` example extension to use User-Agent version `1.21.9` ([#2901](https://github.com/badlogic/pi-mono/pull/2901) by [@aadishv](https://github.com/aadishv))
- Bumped default Antigravity User-Agent version to `1.21.9` ([#2901](https://github.com/badlogic/pi-mono/pull/2901) by [@aadishv](https://github.com/aadishv))
- Fixed Gemma 4 thinking level mapping to route between `MINIMAL` and `HIGH`, and map Pi reasoning levels to the model's supported thinking levels ([#2903](https://github.com/badlogic/pi-mono/pull/2903) by [@aadishv](https://github.com/aadishv))
- Fixed Gemini 2.5 Flash Lite minimal thinking budget to use the model's supported 512-token minimum instead of the regular Flash 128-token minimum, avoiding invalid thinking budget errors ([#2861](https://github.com/badlogic/pi-mono/pull/2861) by [@JasonOA888](https://github.com/JasonOA888))
- Fixed OpenAI Codex Responses requests to forward configured `serviceTier` values, restoring service-tier selection for Codex sessions ([#2996](https://github.com/badlogic/pi-mono/pull/2996) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- Fixed newly generated session IDs to use UUIDv7, improving time locality for session-based request routing ([#3018](https://github.com/badlogic/pi-mono/pull/3018) by [@steipete](https://github.com/steipete))
- Fixed `Container.render()` stack overflow on long sessions by replacing `Array.push(...spread)` with a loop-based push, preventing `RangeError: Maximum call stack size exceeded` when child output exceeds the V8 call stack argument limit ([#2651](https://github.com/badlogic/pi-mono/issues/2651))
- Fixed editor sticky-column tracking around paste markers so vertical cursor navigation restores the column from before the cursor entered a paste marker instead of jumping inside or past pasted content ([#3092](https://github.com/badlogic/pi-mono/pull/3092) by [@Perlence](https://github.com/Perlence))
- Fixed queued messages typed during `/tree` branch summarization to flush automatically after navigation completes, so they no longer remain stuck in the steering queue ([#3091](https://github.com/badlogic/pi-mono/pull/3091) by [@Perlence](https://github.com/Perlence))
- Fixed npm package update check to work with packages on non-default registries by using `npm view` instead of hardcoded `registry.npmjs.org` fetch ([#3164](https://github.com/badlogic/pi-mono/pull/3164) by [@aliou](https://github.com/aliou))

## [0.67.0] - 2026-04-13

See [0.67.1]. Version 0.67.0 shipped with a changelog formatting error that caused interactive startup to show only the version header instead of the full release notes.

## [0.66.1] - 2026-04-08

### Changed

- Changed the Earendil announcement from an automatic startup notice to the hidden `/dementedelves` slash command.

## [0.66.0] - 2026-04-08

### New Features

- Earendil startup announcement with bundled inline image rendering and a linked blog post for April 8 and 9, 2026.
- Interactive Anthropic subscription auth warning when Anthropic subscription auth is active, clarifying that Anthropic third-party usage draws from extra usage and is billed per token.

### Fixed

- Fixed bare `readline` import to use `node:readline` prefix for Deno compatibility ([#2885](https://github.com/badlogic/pi-mono/issues/2885) by [@milosv-vtool](https://github.com/milosv-vtool))
- Fixed auto-retry to treat stream failures like `request ended without sending any chunks` as transient errors ([#2892](https://github.com/badlogic/pi-mono/issues/2892))
- Fixed interactive startup notices to render after the initial resource listing, and added a bundled Earendil startup announcement with inline image rendering for April 8 and 9, 2026. Moved the blog link above the image to avoid overlap with terminal image rendering.
- Fixed interactive mode to warn when Anthropic subscription auth is active, so users know Anthropic third-party usage draws from extra usage and is billed per token.

## [0.65.2] - 2026-04-06

## [0.65.1] - 2026-04-05

### Fixed

- Fixed bash output truncation by line count to always persist full output to a temp file, preventing data loss when output exceeds 2000 lines but stays under the byte threshold ([#2852](https://github.com/badlogic/pi-mono/issues/2852))
- RpcClient now forwards subprocess stderr to parent process in real-time ([#2805](https://github.com/badlogic/pi-mono/issues/2805))
- Theme file watcher now handles async `fs.watch` error events instead of crashing the process ([#2791](https://github.com/badlogic/pi-mono/issues/2791))
- Fixed stored session cwd handling so resuming or importing a session whose original working directory no longer exists now prompts interactive users to continue in the current cwd, while non-interactive modes fail with a clear error.
- Fixed resource collision precedence so project and user skills, prompt templates, and themes override package resources consistently, and CLI-provided paths take precedence over discovered resources ([#2781](https://github.com/badlogic/pi-mono/issues/2781))
- Fixed OpenAI-compatible completions streaming usage accounting to preserve `prompt_tokens_details.cache_write_tokens` and normalize OpenRouter `cached_tokens`, preventing incorrect cache read/write token and cost reporting in pi ([#2802](https://github.com/badlogic/pi-mono/issues/2802))
- Fixed CLI extension paths like `git:gist.github.com/...` being incorrectly resolved against cwd instead of being passed through to the package manager ([#2845](https://github.com/badlogic/pi-mono/pull/2845) by [@aliou](https://github.com/aliou))
- Fixed piped stdin runs with `--mode json` to preserve JSONL output instead of falling back to plain text ([#2848](https://github.com/badlogic/pi-mono/pull/2848) by [@aliou](https://github.com/aliou))
- Fixed interactive command docs to stop listing removed `/exit` as a supported quit command ([#2850](https://github.com/badlogic/pi-mono/issues/2850))

## [0.65.0] - 2026-04-03

### New Features

- **Session runtime API**: `createAgentSessionRuntime()` and `AgentSessionRuntime` provide a closure-based runtime that recreates cwd-bound services and session config on every session switch. Startup, `/new`, `/resume`, `/fork`, and import all use the same creation path. See [docs/sdk.md](docs/sdk.md) and [examples/sdk/13-session-runtime.ts](examples/sdk/13-session-runtime.ts).
- **Label timestamps in `/tree`**: Toggle timestamps on tree entries with `Shift+T`, with smart date formatting and timestamp preservation through branching ([#2691](https://github.com/badlogic/pi-mono/pull/2691) by [@w-winter](https://github.com/w-winter))
- **`defineTool()` helper**: Create standalone custom tool definitions with full TypeScript parameter type inference, no manual casts needed ([#2746](https://github.com/badlogic/pi-mono/issues/2746)). See [docs/extensions.md](docs/extensions.md).
- **Unified diagnostics**: Arg parsing, service creation, session option resolution, and resource loading all return structured diagnostics (`info`/`warning`/`error`) instead of logging or exiting. The app layer decides presentation and exit behavior.

### Breaking Changes

- Removed extension post-transition events `session_switch` and `session_fork`. Use `session_start` with `event.reason` (`"startup" | "reload" | "new" | "resume" | "fork"`). For `"new"`, `"resume"`, and `"fork"`, `session_start` includes `previousSessionFile`.
- Removed session-replacement methods from `AgentSession`. Use `AgentSessionRuntime` for `newSession()`, `switchSession()`, `fork()`, and `importFromJsonl()`. Cross-cwd session replacement rebuilds all cwd-bound runtime state and replaces the live `AgentSession` instance.
- Removed `session_directory` from extension and settings APIs.
- Unknown single-dash CLI flags (e.g. `-s`) now produce an error instead of being silently ignored.

#### Migration: Extensions

Before:

```ts
pi.on("session_switch", async (event, ctx) => { ... });
pi.on("session_fork", async (_event, ctx) => { ... });
```

After:

```ts
pi.on("session_start", async (event, ctx) => {
  // event.reason: "startup" | "reload" | "new" | "resume" | "fork"
  // event.previousSessionFile: set for "new", "resume", "fork"
});
```

#### Migration: SDK session replacement

Before:

```ts
await session.newSession();
await session.switchSession("/path/to/session.jsonl");
```

After:

```ts
import {
  type CreateAgentSessionRuntimeFactory,
  createAgentSessionFromServices,
  createAgentSessionRuntime,
  createAgentSessionServices,
  getAgentDir,
  SessionManager,
} from "@bastani/atomic";

const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
  const services = await createAgentSessionServices({ cwd });
  return {
    ...(await createAgentSessionFromServices({ services, sessionManager, sessionStartEvent })),
    services,
    diagnostics: services.diagnostics,
  };
};

const runtime = await createAgentSessionRuntime(createRuntime, {
  cwd: process.cwd(),
  agentDir: getAgentDir(),
  sessionManager: SessionManager.create(process.cwd()),
});

await runtime.newSession();
await runtime.switchSession("/path/to/session.jsonl");
await runtime.fork("entry-id");

// After replacement, runtime.session is the new live session.
// Rebind any session-local subscriptions or extension bindings.
```

### Added

- Added `createAgentSessionRuntime()` and `AgentSessionRuntime` for runtime-backed session replacement. The runtime takes a `CreateAgentSessionRuntimeFactory` closure that closes over process-global fixed inputs and recreates cwd-bound services and session config for each effective cwd. Startup and later `/new`, `/resume`, `/fork`, import all use the same factory.
- Added unified diagnostics model (`info`/`warning`/`error`) for arg parsing, service creation, session option resolution, and resource loading. Creation logic no longer logs or exits. The app layer decides presentation and exit behavior.
- Added error diagnostics for missing explicit CLI resource paths (`-e`, `--skill`, `--prompt-template`, `--theme`)

- Added `defineTool()` so standalone and array-based custom tool definitions keep inferred parameter types without manual casts ([#2746](https://github.com/badlogic/pi-mono/issues/2746))

- Added label timestamps to the session tree with a `Shift+T` toggle in `/tree`, smart date formatting, and timestamp preservation through branching ([#2691](https://github.com/badlogic/pi-mono/pull/2691) by [@w-winter](https://github.com/w-winter))

### Fixed

- Fixed startup resource loading to reuse the initial `ResourceLoader` for the first runtime, so extensions are not loaded twice before session startup and `session_start` handlers still fire for singleton-style extensions ([#2766](https://github.com/badlogic/pi-mono/issues/2766))
- Fixed retry settlement so retried agent runs wait for the full retry cycle to complete before declaring idle, preventing stale state after transient errors
- Fixed theme `export` colors to resolve theme variables the same way as `colors`, so `/export` HTML backgrounds now honor entries like `pageBg: "base"` instead of requiring inline hex values ([#2707](https://github.com/badlogic/pi-mono/issues/2707))
- Fixed Bedrock throttling errors being misidentified as context overflow, causing unnecessary compaction instead of retry ([#2699](https://github.com/badlogic/pi-mono/pull/2699) by [@xu0o0](https://github.com/xu0o0))
- Added tool streaming support for newer Z.ai models ([#2732](https://github.com/badlogic/pi-mono/pull/2732) by [@kaofelix](https://github.com/kaofelix))

## [0.64.0] - 2026-03-29

### New Features

- Extensions and SDK callers can attach a `prepareArguments` hook to any tool definition, letting them normalize or migrate raw model arguments before schema validation. The built-in `edit` tool uses this to transparently support sessions created with the old single-edit schema. See [docs/extensions.md](docs/extensions.md)
- Extensions can customize the collapsed thinking block label via `ctx.ui.setHiddenThinkingLabel()`. See [examples/extensions/hidden-thinking-label.ts](examples/extensions/hidden-thinking-label.ts) ([#2673](https://github.com/badlogic/pi-mono/issues/2673))

### Breaking Changes

- `ModelRegistry` no longer has a public constructor. SDK callers and tests must use `ModelRegistry.create(authStorage, modelsJsonPath?)` for file-backed registries or `ModelRegistry.inMemory(authStorage)` for built-in-only registries. Direct `new ModelRegistry(...)` calls no longer compile.

### Added

- Added `ToolDefinition.prepareArguments` hook to prepare raw tool call arguments before schema validation, enabling compatibility shims for resumed sessions with outdated tool schemas
- Built-in `edit` tool now uses `prepareArguments` to silently fold legacy top-level `oldText`/`newText` into `edits[]` when resuming old sessions
- Added `ctx.ui.setHiddenThinkingLabel()` so extensions can customize the collapsed thinking label in interactive mode, with a no-op in RPC mode and a runnable example extension in `examples/extensions/hidden-thinking-label.ts` ([#2673](https://github.com/badlogic/pi-mono/issues/2673))

### Fixed

- Fixed extension-queued user messages to refresh the interactive pending-message list so messages submitted while a turn is active are no longer silently dropped ([#2674](https://github.com/badlogic/pi-mono/pull/2674) by [@mrexodia](https://github.com/mrexodia))
- Fixed monorepo `tsconfig.json` path mappings to resolve `@mariozechner/pi-ai` subpath exports to source files in development checkouts ([#2625](https://github.com/badlogic/pi-mono/pull/2625) by [@ferologics](https://github.com/ferologics))
- Fixed TUI cell size response handling to consume only exact `CSI 6 ; height ; width t` replies, so bare `Escape` is no longer swallowed while waiting for terminal image metadata ([#2661](https://github.com/badlogic/pi-mono/issues/2661))
- Fixed Kitty keyboard protocol keypad functional keys to normalize to logical digits, symbols, and navigation keys, so numpad input in terminals such as iTerm2 no longer inserts Private Use Area gibberish or gets ignored ([#2650](https://github.com/badlogic/pi-mono/issues/2650))

## [0.63.2] - 2026-03-29

### New Features

- Extension handlers can now use `ctx.signal` to forward cancellation into nested model calls, `fetch()`, and other abort-aware work. See [docs/extensions.md#ctxsignal](docs/extensions.md#ctxsignal) ([#2660](https://github.com/badlogic/pi-mono/issues/2660))
- Built-in `edit` tool input now uses `edits[]` as the only replacement shape, reducing invalid tool calls caused by mixed single-edit and multi-edit schemas ([#2639](https://github.com/badlogic/pi-mono/issues/2639))
- Large multi-edit results no longer trigger full-screen redraws in the interactive TUI when the final diff is rendered ([#2664](https://github.com/badlogic/pi-mono/issues/2664))

### Added

- Added `ctx.signal` to `ExtensionContext` and wired it to the active agent turn so extension handlers can forward cancellation into nested model calls, `fetch()`, and other abort-aware work ([#2660](https://github.com/badlogic/pi-mono/issues/2660))

### Fixed

- Fixed built-in `edit` tool input to use `edits[]` as the only replacement shape, eliminating the mixed single-edit and multi-edit modes that caused repeated invalid tool calls and retries ([#2639](https://github.com/badlogic/pi-mono/issues/2639))
- Fixed edit tool TUI rendering to defer large multi-edit diffs to the settled result, avoiding full-screen redraws when the tool completes ([#2664](https://github.com/badlogic/pi-mono/issues/2664))

## [0.63.1] - 2026-03-27

### Added

- Added `gemini-3.1-pro-preview-customtools` model availability for the `google-vertex` provider ([#2610](https://github.com/badlogic/pi-mono/pull/2610) by [@gordonhwc](https://github.com/gordonhwc))

### Fixed

- Documented `tool_call` input mutation as supported extension API behavior, clarified that post-mutation inputs are not re-validated, and added regression coverage for executing mutated tool arguments ([#2611](https://github.com/badlogic/pi-mono/issues/2611))
- Fixed repeated compactions dropping messages that were kept by an earlier compaction by re-summarizing from the previous kept boundary and recalculating `tokensBefore` from the rebuilt session context ([#2608](https://github.com/badlogic/pi-mono/issues/2608))
- Fixed interactive compaction UI updates so `ctx.compact()` rebuilds the chat through unified compaction events, manual compaction no longer duplicates the summary block, and the `trigger-compact` example only fires when context usage crosses its threshold ([#2617](https://github.com/badlogic/pi-mono/issues/2617))
- Fixed interactive compaction completion to append a synthetic compaction summary after rebuilding the chat so the latest compaction remains visible at the bottom
- Fixed skill discovery to stop recursing once a directory contains `SKILL.md`, and to ignore root `*.md` files in `.agents/skills` while keeping root markdown skill files supported in `~/.pi/agent/skills`, `.pi/skills`, and package `skills/` directories ([#2603](https://github.com/badlogic/pi-mono/issues/2603))
- Fixed edit tool diff rendering for multi-edit operations with large unchanged gaps so distant edits collapse intermediate context instead of dumping the full unchanged middle block
- Fixed edit tool error rendering to avoid repeating the same exact-match failure in both the preview and result blocks
- Fixed auto-compaction overflow recovery for Ollama models when the backend returns explicit `prompt too long; exceeded max context length ...` errors instead of silently truncating input ([#2626](https://github.com/badlogic/pi-mono/issues/2626))
- Fixed built-in tool overrides that reuse built-in parameter schemas to still honor custom `renderCall` and `renderResult` renderers in the interactive TUI, restoring the `minimal-mode` example ([#2595](https://github.com/badlogic/pi-mono/issues/2595))

## [0.63.0] - 2026-03-27

### Breaking Changes

- `ModelRegistry.getApiKey(model)` has been replaced by `getApiKeyAndHeaders(model)` because `models.json` auth and header values can now resolve dynamically on every request. Extensions and SDK integrations that previously fetched only an API key must now fetch request auth per call and forward both `apiKey` and `headers`. Use `getApiKeyForProvider(provider)` only when you explicitly want provider-level API key lookup without model headers or `authHeader` handling ([#1835](https://github.com/badlogic/pi-mono/issues/1835))
- Removed deprecated direct `minimax` and `minimax-cn` model IDs, keeping only `MiniMax-M2.7` and `MiniMax-M2.7-highspeed`. Update pinned model IDs to one of those supported direct MiniMax models, or use another provider route that still exposes the older IDs ([#2596](https://github.com/badlogic/pi-mono/pull/2596) by [@liyuan97](https://github.com/liyuan97))

#### Migration Notes

Before:

```ts
const apiKey = await ctx.modelRegistry.getApiKey(model);
return streamSimple(model, messages, { apiKey });
```

After:

```ts
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
if (!auth.ok) throw new Error(auth.error);
return streamSimple(model, messages, {
  apiKey: auth.apiKey,
  headers: auth.headers,
});
```

### Added

- Added `sessionDir` setting support in global and project `settings.json` so session storage can be configured without passing `--session-dir` on every invocation ([#2598](https://github.com/badlogic/pi-mono/pull/2598) by [@smcllns](https://github.com/smcllns))
- Added a startup onboarding hint in the interactive header telling users pi can explain its own features and documentation ([#2620](https://github.com/badlogic/pi-mono/pull/2620) by [@ferologics](https://github.com/ferologics))
- Added `edit` tool multi-edit support so one call can update multiple separate, disjoint regions in the same file while matching all replacements against the original file content
- Added support for `PI_TUI_WRITE_LOG` directory paths, creating a unique log file (`tui-<timestamp>-<pid>.log`) per instance for easier debugging of multiple pi sessions ([#2508](https://github.com/badlogic/pi-mono/pull/2508) by [@mrexodia](https://github.com/mrexodia))

### Changed

### Fixed

- Fixed file mutation queue ordering so concurrent `edit` and `write` operations targeting the same file stay serialized in request order instead of being reordered during queue-key resolution
- Fixed `models.json` shell-command auth and headers to resolve at request time instead of being cached into long-lived model state. pi now leaves TTL, caching, and recovery policy to user-provided wrapper commands because arbitrary shell commands need provider-specific strategies ([#1835](https://github.com/badlogic/pi-mono/issues/1835))
- Fixed Google and Vertex cost calculation to subtract cached prompt tokens from billable input tokens instead of double-counting them when providers report `cachedContentTokenCount` ([#2588](https://github.com/badlogic/pi-mono/pull/2588) by [@sparkleMing](https://github.com/sparkleMing))
- Added missing `ajv` direct dependency; previously relied on transitive install via `@mariozechner/pi-ai` which broke standalone installs ([#2252](https://github.com/badlogic/pi-mono/issues/2252))
- Fixed `/export` HTML backgrounds to honor `theme.export.pageBg`, `cardBg`, and `infoBg` instead of always deriving them from `userMessageBg` ([#2565](https://github.com/badlogic/pi-mono/issues/2565))
- Fixed interactive bash execution collapsed previews to recompute visual line wrapping at render time, so previews respect the current terminal width after resizes and split-pane width changes ([#2569](https://github.com/badlogic/pi-mono/issues/2569))
- Fixed RPC `get_session_stats` to expose `contextUsage`, so headless clients can read actual current context-window usage instead of deriving it from token totals ([#2550](https://github.com/badlogic/pi-mono/issues/2550))
- Fixed `pi update` for git packages to fetch only the tracked target branch with `--no-tags`, reducing unrelated branch and tag noise while preserving force-push-safe updates ([#2548](https://github.com/badlogic/pi-mono/issues/2548))
- Fixed print and JSON modes to emit `session_shutdown` before exit, so extensions can release long-lived resources and non-interactive runs terminate cleanly ([#2576](https://github.com/badlogic/pi-mono/issues/2576))
- Fixed GitHub Copilot OpenAI Responses requests to omit the `reasoning` field entirely when no reasoning effort is requested, avoiding `400` errors from Copilot `gpt-5-mini` rejecting `reasoning: { effort: "none" }` during internal summary calls ([#2567](https://github.com/badlogic/pi-mono/issues/2567))
- Fixed blockquote text color breaking after inline links (and other inline elements) due to missing style restoration prefix
- Fixed slash-command Tab completion from immediately chaining into argument autocomplete after completing the command name, restoring flows like `/model` that submit into a selector dialog ([#2577](https://github.com/badlogic/pi-mono/issues/2577))
- Fixed stale content and incorrect viewport tracking after TUI content shrinks or transient components inflate the working area ([#2126](https://github.com/badlogic/pi-mono/pull/2126) by [@Perlence](https://github.com/Perlence))
- Fixed `@` autocomplete to debounce editor-triggered searches, cancel in-flight `fd` lookups cleanly, and keep suggestions visible while results refresh ([#1278](https://github.com/badlogic/pi-mono/issues/1278))

## [0.62.0] - 2026-03-23

### New Features

- Built-in tools as extensible ToolDefinitions. Extension authors can now override rendering of built-in read/write/edit/bash/grep/find/ls tools with custom `renderCall`/`renderResult` components. See [docs/extensions.md](docs/extensions.md).
- Unified source provenance via `sourceInfo`. All resources, commands, tools, skills, and prompt templates now carry structured `sourceInfo` with path, scope, and source metadata. Visible in autocomplete, RPC discovery, and SDK introspection. See [docs/extensions.md](docs/extensions.md).
- AWS Bedrock cost allocation tagging. New `requestMetadata` option on `BedrockOptions` forwards key-value pairs to the Bedrock Converse API for AWS Cost Explorer split cost allocation.

### Breaking Changes

- Changed `ToolDefinition.renderCall` and `renderResult` semantics. Fallback rendering now happens only when a renderer is not defined for that slot. If `renderCall` or `renderResult` is defined, it must return a `Component`.
- Changed slash command provenance to use `sourceInfo` consistently. RPC `get_commands`, `RpcSlashCommand`, and SDK `SlashCommandInfo` no longer expose `location` or `path`. Use `sourceInfo` instead ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
- Removed legacy `source` fields from `Skill` and `PromptTemplate`. Use `sourceInfo.source` for provenance instead ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
- Removed `ResourceLoader.getPathMetadata()`. Resource provenance is now attached directly to loaded resources via `sourceInfo` ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
- Removed `extensionPath` from `RegisteredCommand` and `RegisteredTool`. Use `sourceInfo.path` for provenance instead ([#1734](https://github.com/badlogic/pi-mono/issues/1734))

#### Migration Notes

Resource, command, and tool provenance now use `sourceInfo` consistently.

Common updates:
- RPC `get_commands`: replace `path` and `location` with `sourceInfo.path`, `sourceInfo.scope`, and `sourceInfo.source`
- `SlashCommandInfo`: replace `command.path` and `command.location` with `command.sourceInfo`
- `Skill` and `PromptTemplate`: replace `.source` with `.sourceInfo.source`
- `RegisteredCommand` and `RegisteredTool`: replace `.extensionPath` with `.sourceInfo.path`
- Custom `ResourceLoader` implementations: remove `getPathMetadata()` and read provenance from loaded resources directly

Examples:
- `command.path` -> `command.sourceInfo.path`
- `command.location === "user"` -> `command.sourceInfo.scope === "user"`
- `skill.source` -> `skill.sourceInfo.source`
- `tool.extensionPath` -> `tool.sourceInfo.path`

### Changed

- Built-in tools now work like custom tools in extensions. To get built-in tool definitions, import `readToolDefinition` / `createReadToolDefinition()` and the equivalent `bash`, `edit`, `write`, `grep`, `find`, and `ls` exports from `@bastani/atomic`.
- Cleaned up `buildSystemPrompt()` so built-in tool snippets and tool-local guidelines come from built-in `ToolDefinition` metadata, while cross-tool and global prompt rules stay in system prompt construction.
- Added structured `sourceInfo` to `pi.getAllTools()` results for built-in, SDK, and extension tools ([#1734](https://github.com/badlogic/pi-mono/issues/1734))

### Fixed

- Fixed extension command name conflicts so extensions with duplicate command names can load together. Conflicting extension commands now get numeric invocation suffixes in load order, for example `/review:1` and `/review:2` ([#1061](https://github.com/badlogic/pi-mono/issues/1061))
- Fixed slash command source attribution for extension commands, prompt templates, and skills in autocomplete and command discovery ([#1734](https://github.com/badlogic/pi-mono/issues/1734))
- Fixed auto-resized image handling to enforce the inline image size limit on the final base64 payload, return text-only fallbacks when resizing cannot produce a safe image, and avoid falling back to the original image in `read` and `@file` auto-resize paths ([#2055](https://github.com/badlogic/pi-mono/issues/2055))
- Fixed `pi update` for git packages to skip destructive reset, clean, and reinstall steps when the fetched target already matches the local checkout ([#2503](https://github.com/badlogic/pi-mono/issues/2503))
- Fixed print and JSON mode to take over stdout during non-interactive startup, keeping package-manager and other incidental chatter off protocol/output stdout ([#2482](https://github.com/badlogic/pi-mono/issues/2482))
- Fixed cli-highlight auto-detection for languageless code blocks that misidentified prose as programming languages and colored random English words as keywords
- Fixed Anthropic thinking disable handling to send `thinking: { type: "disabled" }` for reasoning-capable models when thinking is explicitly off ([#2022](https://github.com/badlogic/pi-mono/issues/2022))
- Fixed explicit thinking disable handling across Google, Google Vertex, Gemini CLI, OpenAI Responses, Azure OpenAI Responses, and OpenRouter-backed OpenAI-compatible completions ([#2490](https://github.com/badlogic/pi-mono/issues/2490))
- Fixed OpenAI Responses replay for foreign tool-call item IDs by hashing foreign IDs into bounded `fc_<hash>` IDs
- Fixed OpenAI-compatible completions streams to ignore null chunks instead of crashing ([#2466](https://github.com/badlogic/pi-mono/pull/2466) by [@Cheng-Zi-Qing](https://github.com/Cheng-Zi-Qing))
- Fixed `truncateToWidth()` performance for very large strings by streaming truncation ([#2447](https://github.com/badlogic/pi-mono/issues/2447))
- Fixed markdown heading styling being lost after inline code spans within headings

## [0.61.1] - 2026-03-20

### New Features

- Typed `tool_call` handler return values via `ToolCallEventResult` exports from the top-level package and core extension entry. See [docs/extensions.md](docs/extensions.md).
- Updated default models for `zai`, `cerebras`, `minimax`, and `minimax-cn`, and aligned MiniMax catalog coverage and limits with the current provider lineup. See [docs/models.md](docs/models.md) and [docs/providers.md](docs/providers.md).

### Added

- Added `ToolCallEventResult` to the `@bastani/atomic` top-level and core extension exports so extension authors can type explicit `tool_call` handler return values ([#2458](https://github.com/badlogic/pi-mono/issues/2458))

### Changed

- Changed the default models for `zai`, `cerebras`, `minimax`, and `minimax-cn` to match the current provider lineup, and added missing `MiniMax-M2.1-highspeed` model entries with normalized MiniMax context limits ([#2445](https://github.com/badlogic/pi-mono/pull/2445) by [@1500256797](https://github.com/1500256797))

### Fixed

- Fixed `ctrl+z` suspend and `fg` resume reliability by keeping the process alive until the `SIGCONT` handler restores the TUI, avoiding immediate process exit in environments with no other live event-loop handles ([#2454](https://github.com/badlogic/pi-mono/issues/2454))
- Fixed `createAgentSession({ agentDir })` to derive the default persisted session path from the provided `agentDir`, keeping session storage aligned with settings, auth, models, and resource loading ([#2457](https://github.com/badlogic/pi-mono/issues/2457))
- Fixed shared keybinding resolution to stop user overrides from evicting unrelated default shortcuts such as selector confirm and editor cursor keys ([#2455](https://github.com/badlogic/pi-mono/issues/2455))
- Fixed Termux software keyboard height changes from forcing full-screen redraws and replaying TUI history on every toggle ([#2467](https://github.com/badlogic/pi-mono/issues/2467))
- Fixed project-local npm package updates to install npm `latest` instead of reusing stale saved dependency ranges, and added `Did you mean ...?` suggestions when `pi update <source>` omits the configured npm or git source prefix ([#2459](https://github.com/badlogic/pi-mono/issues/2459))

## [0.61.0] - 2026-03-20

### New Features

- Namespaced keybinding ids and a unified keybinding manager across the app and TUI. See [docs/keybindings.md](docs/keybindings.md) and [docs/extensions.md](docs/extensions.md).
- JSONL session export and import via `/export <path.jsonl>` and `/import <path.jsonl>`. See [README.md](README.md) and [docs/session.md](docs/session.md).
- Resizable sidebar in HTML share and export views. See [README.md](README.md).

### Breaking Changes

- Interactive keybinding ids are now namespaced, and `keybindings.json` now uses those same canonical namespaced ids. Older config files are migrated automatically on startup. Custom editors and extension UI components still receive an injected `keybindings: KeybindingsManager`. They do not call `getKeybindings()` or `setKeybindings()` themselves. Declaration merging applies to that injected type ([#2391](https://github.com/badlogic/pi-mono/issues/2391))
- Extension author migration: update `keyHint()`, `keyText()`, and injected `keybindings.matches(...)` calls from old built-in names like `"expandTools"`, `"selectConfirm"`, and `"interrupt"` to namespaced ids like `"app.tools.expand"`, `"tui.select.confirm"`, and `"app.interrupt"`. See [docs/keybindings.md](docs/keybindings.md) for the full list. `pi.registerShortcut("ctrl+shift+p", ...)` is unchanged because extension shortcuts still use raw key combos, not keybinding ids.

### Added

- Added `gpt-5.4-mini` to the `openai-codex` model catalog ([#2334](https://github.com/badlogic/pi-mono/pull/2334) by [@justram](https://github.com/justram))
- Added JSONL session export and import via `/export <path.jsonl>` and `/import <path.jsonl>` ([#2356](https://github.com/badlogic/pi-mono/pull/2356) by [@hjanuschka](https://github.com/hjanuschka))
- Added a resizable sidebar to HTML share and export views ([#2435](https://github.com/badlogic/pi-mono/pull/2435) by [@dmmulroy](https://github.com/dmmulroy))

### Fixed

- Tests for session-selector-rename and tree-selector are now keybinding-agnostic, resetting editor keybindings to defaults before each test so user `keybindings.json` cannot cause failures ([#2360](https://github.com/badlogic/pi-mono/issues/2360))
- Fixed custom `keybindings.json` overrides to shadow conflicting default shortcuts globally, so bindings such as `cursorUp: ["up", "ctrl+p"]` no longer leave default actions like model cycling active ([#2391](https://github.com/badlogic/pi-mono/issues/2391))
- Fixed concurrent `edit` and `write` mutations targeting the same file to run serially, preventing interleaved file writes from overwriting each other ([#2327](https://github.com/badlogic/pi-mono/issues/2327))
- Fixed RPC mode to redirect unexpected stdout writes to stderr so JSONL responses remain parseable ([#2388](https://github.com/badlogic/pi-mono/issues/2388))
- Fixed auto-retry with tool-using retry responses so `session.prompt()` waits for the full retry loop, including tool execution, before returning ([#2440](https://github.com/badlogic/pi-mono/pull/2440) by [@pasky](https://github.com/pasky))
- Fixed `/model` to refresh scoped model lists after `models.json` changes, avoiding stale selector contents ([#2408](https://github.com/badlogic/pi-mono/pull/2408) by [@Perlence](https://github.com/Perlence))
- Fixed `validateToolArguments()` to fall back gracefully when AJV schema compilation is blocked in restricted runtimes such as Cloudflare Workers, allowing tool execution to proceed without schema validation ([#2395](https://github.com/badlogic/pi-mono/issues/2395))
- Fixed CLI startup to suppress process warnings from leaking into terminal, print, and RPC output ([#2404](https://github.com/badlogic/pi-mono/issues/2404))
- Fixed bash tool rendering to show elapsed time at the bottom of the tool block ([#2406](https://github.com/badlogic/pi-mono/issues/2406))
- Fixed custom theme file watching to reload updated theme contents from disk instead of keeping stale cached theme data ([#2417](https://github.com/badlogic/pi-mono/issues/2417), [#2003](https://github.com/badlogic/pi-mono/issues/2003))
- Fixed footer Git branch refreshes to run asynchronously so branch watcher updates do not block the UI ([#2418](https://github.com/badlogic/pi-mono/issues/2418))
- Fixed invalid extension provider registrations to surface an extension error without preventing other providers from loading ([#2431](https://github.com/badlogic/pi-mono/issues/2431))
- Fixed Windows bash execution hanging for commands that spawn detached descendants inheriting stdout/stderr handles, which caused `agent-browser` and similar commands to spin forever ([#2389](https://github.com/badlogic/pi-mono/pull/2389) by [@mrexodia](https://github.com/mrexodia))
- Fixed `google-vertex` API key resolution to ignore placeholder auth markers like `<authenticated>` and fall back to ADC instead of sending them as literal API keys ([#2335](https://github.com/badlogic/pi-mono/issues/2335))
- Fixed desktop clipboard text copy to prefer native OS clipboard integration before shell fallbacks, improving reliability on macOS and Windows ([#2347](https://github.com/badlogic/pi-mono/issues/2347))
- Fixed Bun Bedrock provider registration to survive provider resets and session reloads in compiled binaries ([#2350](https://github.com/badlogic/pi-mono/pull/2350) by [@unexge](https://github.com/unexge))
- Fixed OpenRouter reasoning requests to use the provider's nested reasoning payload, restoring thinking level support for OpenRouter models and custom compat settings ([#2298](https://github.com/badlogic/pi-mono/pull/2298) by [@PriNova](https://github.com/PriNova))
- Fixed Bedrock application inference profiles to support prompt caching when `AWS_BEDROCK_FORCE_CACHE=1` is set, covering profile ARNs that do not expose the underlying Claude model name ([#2346](https://github.com/badlogic/pi-mono/pull/2346) by [@haoqixu](https://github.com/haoqixu))

## [0.60.0] - 2026-03-18

### New Features

- Fork existing sessions directly from the CLI with `--fork <path|id>`, which copies a source session into a new session in the current project. See [README.md](README.md).
- Extensions and SDK callers can reuse pi's built-in local bash backend via `createLocalBashOperations()` for `user_bash` interception and custom bash integrations. See [docs/extensions.md#user_bash](docs/extensions.md#user_bash).
- Startup no longer updates unpinned npm and git packages automatically. Use `pi update` explicitly, while interactive mode checks for updates in the background and notifies you when newer packages are available. See [README.md](README.md).

### Breaking Changes

- Changed package startup behavior so installed unpinned packages are no longer checked or updated during startup. Use `pi update` to apply npm/git package updates, while interactive mode now checks for available package updates in the background and notifies you when updates are available ([#1963](https://github.com/badlogic/pi-mono/issues/1963))

### Added

- Added `--fork <path|id>` CLI flag to fork an existing session file or partial session UUID directly into a new session ([#2290](https://github.com/badlogic/pi-mono/issues/2290))
- Added `createLocalBashOperations()` export so extensions and SDK callers can wrap pi's built-in local bash backend for `user_bash` handling and other custom bash integrations ([#2299](https://github.com/badlogic/pi-mono/issues/2299))

### Fixed

- Fixed active model selection to refresh immediately after dynamic provider registrations or updates change the available model set ([#2291](https://github.com/badlogic/pi-mono/issues/2291))
- Fixed tmux xterm `modifyOtherKeys` matching for `Backspace`, `Escape`, and `Space`, and resolved raw `\x08` backspace ambiguity by treating Windows Terminal sessions differently from legacy terminals ([#2293](https://github.com/badlogic/pi-mono/issues/2293))
- Fixed Gemini 3 and Antigravity image tool results to stay inline as multimodal tool responses instead of being rerouted through separate follow-up messages ([#2052](https://github.com/badlogic/pi-mono/issues/2052))
- Fixed bundled Bedrock Claude 4.6 model metadata to use the correct 200K context window instead of 1M ([#2305](https://github.com/badlogic/pi-mono/issues/2305))
- Fixed `/reload` to reload keybindings from disk so changes in `keybindings.json` apply immediately ([#2309](https://github.com/badlogic/pi-mono/issues/2309))
- Fixed lazy built-in provider registration so compiled Bun binaries can still load providers on first use without eagerly bundling provider SDKs ([#2314](https://github.com/badlogic/pi-mono/issues/2314))
- Fixed built-in OAuth login flows to use aligned callback handling across Anthropic, Gemini CLI, Antigravity, and OpenAI Codex, and fixed OpenAI Codex login to complete immediately once the browser callback succeeds ([#2316](https://github.com/badlogic/pi-mono/issues/2316))
- Fixed OpenAI-compatible z.ai `network_error` responses to trigger error handling and retries instead of being treated as successful assistant output ([#2313](https://github.com/badlogic/pi-mono/issues/2313))
- Fixed print mode to merge piped stdin into the initial prompt when both stdin and an explicit prompt are provided ([#2315](https://github.com/badlogic/pi-mono/issues/2315))
- Fixed OpenAI Responses replay in coding-agent to normalize oversized resumed tool call IDs before sending them back to OpenAI Codex and other Responses-compatible targets ([#2328](https://github.com/badlogic/pi-mono/issues/2328))
- Fixed tmux extended-keys warning to stay hidden when the tmux server is unreachable, avoiding false startup warnings in sandboxed environments ([#2311](https://github.com/badlogic/pi-mono/pull/2311) by [@kaffarell](https://github.com/kaffarell))

## [0.59.0] - 2026-03-17

### New Features

- Faster startup by lazy-loading `@mariozechner/pi-ai` provider SDKs on first use instead of import time ([#2297](https://github.com/badlogic/pi-mono/issues/2297))
- Better provider retry behavior when providers return error messages as responses ([#2264](https://github.com/badlogic/pi-mono/issues/2264))
- Better terminal integration via OSC 133 command-executed markers ([#2242](https://github.com/badlogic/pi-mono/issues/2242))
- Better Git footer branch detection for repositories using reftable storage ([#2300](https://github.com/badlogic/pi-mono/issues/2300))

### Breaking Changes

- Changed custom tool system prompt behavior so extension and SDK tools are included in the default `Available tools` section only when they provide `promptSnippet`. Omitting `promptSnippet` now leaves the tool out of that section instead of falling back to `description` ([#2285](https://github.com/badlogic/pi-mono/issues/2285))

### Changed

- Lazy-load built-in `@mariozechner/pi-ai` provider modules and root provider wrappers so coding-agent startup no longer eagerly loads provider SDKs before first use ([#2297](https://github.com/badlogic/pi-mono/issues/2297))

### Fixed

- Fixed session title handling in `/tree`, compaction, and branch summarization so empty title clears render correctly and `session_info` entries stay out of summaries ([#2304](https://github.com/badlogic/pi-mono/pull/2304) by [@aliou](https://github.com/aliou))
- Fixed footer branch detection for Git repositories using reftable storage so branch names still appear correctly in the footer ([#2300](https://github.com/badlogic/pi-mono/issues/2300))
- Fixed rendered user messages to emit an OSC 133 command-executed marker after command output, improving terminal prompt integration ([#2242](https://github.com/badlogic/pi-mono/issues/2242))
- Fixed provider retry handling to treat provider-returned error messages as retryable failures instead of successful responses ([#2264](https://github.com/badlogic/pi-mono/issues/2264))
- Fixed Claude 4.6 context window overrides in bundled model metadata so coding-agent sees the intended model limits after generated catalogs are rebuilt ([#2286](https://github.com/badlogic/pi-mono/issues/2286))

## [0.58.4] - 2026-03-16

### Fixed

- Fixed steering messages to wait until the current assistant message's tool-call batch fully finishes instead of skipping pending tool calls.

## [0.58.3] - 2026-03-15

## [0.58.2] - 2026-03-15

### Added

- Improved settings, theme, thinking, and show-images selector layouts by using configurable select-list primary column sizing ([#2154](https://github.com/badlogic/pi-mono/pull/2154) by [@markusylisiurunen](https://github.com/markusylisiurunen))

### Fixed

- Fixed fuzzy `edit` matching to normalize Unicode compatibility variants before comparison, reducing false "oldText not found" failures for text such as CJK and full-width characters ([#2044](https://github.com/badlogic/pi-mono/issues/2044))
- Fixed `/model <ref>` exact matching and picker search to recognize canonical `provider/model` references when model IDs themselves contain `/`, such as LM Studio models like `unsloth/qwen3.5-35b-a3b` ([#2174](https://github.com/badlogic/pi-mono/issues/2174))
- Fixed Anthropic OAuth manual login and token refresh by using the localhost callback URI for pasted redirect/code flows and omitting `scope` from refresh-token requests ([#2169](https://github.com/badlogic/pi-mono/issues/2169))
- Fixed stale scrollback remaining after session switches by clearing the screen before wiping scrollback ([#2155](https://github.com/badlogic/pi-mono/pull/2155) by [@Perlence](https://github.com/Perlence))
- Fixed extra blank lines after markdown block elements in rendered output ([#2152](https://github.com/badlogic/pi-mono/pull/2152) by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.58.1] - 2026-03-14

### Added

- Added `pi uninstall` alias for `pi install --uninstall` convenience

### Fixed

- Fixed OpenAI Codex websocket protocol to include required headers and properly terminate SSE streams on connection close ([#1961](https://github.com/badlogic/pi-mono/issues/1961))
- Fixed WSL clipboard image fallback to properly handle missing clipboard utilities and permission errors ([#1722](https://github.com/badlogic/pi-mono/issues/1722))
- Fixed extension `session_start` hook firing before TUI was ready, causing UI operations in `session_start` handlers to fail ([#2035](https://github.com/badlogic/pi-mono/issues/2035))
- Fixed Windows shell and path handling for package manager operations and autocomplete to properly handle drive letters and mixed path separators
- Fixed Bedrock prompt caching being enabled for non-Claude models, causing API errors ([#2053](https://github.com/badlogic/pi-mono/issues/2053))
- Fixed Qwen models via OpenAI-compatible providers by adding `qwen-chat-template` compat mode that uses Qwen's native chat template format ([#2020](https://github.com/badlogic/pi-mono/issues/2020))
- Fixed Bedrock unsigned thinking replay to handle edge cases with empty or malformed thinking blocks ([#2063](https://github.com/badlogic/pi-mono/issues/2063))
- Fixed headless clipboard fallback logging spurious errors in non-interactive environments ([#2056](https://github.com/badlogic/pi-mono/issues/2056))
- Fixed `models.json` provider compat flags not being honored when loading custom model definitions ([#2062](https://github.com/badlogic/pi-mono/issues/2062))
- Fixed xhigh reasoning effort detection for Claude Opus 4.6 to match by model ID instead of requiring explicit capability flag ([#2040](https://github.com/badlogic/pi-mono/issues/2040))
- Fixed prompt cwd containing Windows backslashes breaking bash tool execution by normalizing to forward slashes ([#2080](https://github.com/badlogic/pi-mono/issues/2080))
- Fixed editor paste to preserve literal content instead of normalizing newlines, preventing content corruption for text with embedded escape sequences ([#2064](https://github.com/badlogic/pi-mono/issues/2064))
- Fixed skill discovery recursing past skill root directories when nested SKILL.md files exist ([#2075](https://github.com/badlogic/pi-mono/issues/2075))
- Fixed tab completion to preserve `./` prefix when completing relative paths ([#2087](https://github.com/badlogic/pi-mono/issues/2087))
- Fixed npm package installs and lookups being tied to the active repository Node version by adding `npmCommand` as an argv-style settings override for package manager operations ([#2072](https://github.com/badlogic/pi-mono/issues/2072))
- Fixed `ctx.ui.getEditorText()` in the extension API returning paste markers (e.g., `[paste #1 +24 lines]`) instead of the actual pasted content ([#2084](https://github.com/badlogic/pi-mono/issues/2084))
- Fixed startup crash when downloading `fd`/`ripgrep` on first run by using `pipeline()` instead of `finished(readable.pipe(writable))` so stream errors from timeouts are caught properly, and increased the download timeout from 10s to 120s ([#2066](https://github.com/badlogic/pi-mono/issues/2066))

## [0.58.0] - 2026-03-14

### New Features

- Claude Opus 4.6, Sonnet 4.6, and related Bedrock models now use a 1M token context window (up from 200K) ([#2135](https://github.com/badlogic/pi-mono/pull/2135) by [@mitsuhiko](https://github.com/mitsuhiko)).
- Extension tool calls now execute in parallel by default, with sequential `tool_call` preflight preserved for extension interception.
- `GOOGLE_CLOUD_API_KEY` environment variable support for the `google-vertex` provider as an alternative to Application Default Credentials ([#1976](https://github.com/badlogic/pi-mono/pull/1976) by [@gordonhwc](https://github.com/gordonhwc)).
- Extensions can supply deterministic session IDs via `newSession()` ([#2130](https://github.com/badlogic/pi-mono/pull/2130) by [@zhahaoyu](https://github.com/zhahaoyu)).

### Added

- Added `GOOGLE_CLOUD_API_KEY` environment variable support for the `google-vertex` provider as an alternative to Application Default Credentials ([#1976](https://github.com/badlogic/pi-mono/pull/1976) by [@gordonhwc](https://github.com/gordonhwc))
- Added custom session ID support in `newSession()` for extensions that need deterministic session paths ([#2130](https://github.com/badlogic/pi-mono/pull/2130) by [@zhahaoyu](https://github.com/zhahaoyu))

### Changed

- Changed extension tool interception to use agent-core `beforeToolCall` and `afterToolCall` hooks instead of wrapper-based interception. Tool calls now execute in parallel by default, extension `tool_call` preflight still runs sequentially, and final tool results are emitted in assistant source order.
- Raised Claude Opus 4.6, Sonnet 4.6, and related Bedrock model context windows from 200K to 1M tokens ([#2135](https://github.com/badlogic/pi-mono/pull/2135) by [@mitsuhiko](https://github.com/mitsuhiko))

### Fixed

- Fixed `tool_call` extension handlers observing stale `sessionManager` state during multi-tool turns by draining queued agent events before each `tool_call` preflight. In parallel tool mode this guarantees state through the current assistant tool-calling message, but not sibling tool results from the same assistant message.
- Fixed interactive input fields backed by the TUI `Input` component to scroll by visual column width for wide Unicode text (CJK, fullwidth characters), preventing rendered line overflow and TUI crashes in places like search and filter inputs ([#1982](https://github.com/badlogic/pi-mono/issues/1982))
- Fixed `shift+tab` and other modified Tab bindings in tmux when `extended-keys-format` is left at the default `xterm`
- Fixed EXIF orientation not being applied during image convert and resize, causing JPEG and WebP images from phone cameras to display rotated or mirrored ([#2105](https://github.com/badlogic/pi-mono/pull/2105) by [@melihmucuk](https://github.com/melihmucuk))
- Fixed the default coding-agent system prompt to include only the current date in ISO format, not the current time, so prompt prefixes stay cacheable across reloads and resumed sessions ([#2131](https://github.com/badlogic/pi-mono/issues/2131))
- Fixed retry regex to match `server_error` and `internal_error` error types from providers, improving automatic retry coverage ([#2117](https://github.com/badlogic/pi-mono/pull/2117) by [@MadKangYu](https://github.com/MadKangYu))
- Fixed example extensions to support `PI_CODING_AGENT_DIR` environment variable for custom agent directory paths ([#2009](https://github.com/badlogic/pi-mono/pull/2009) by [@smithbm2316](https://github.com/smithbm2316))
- Fixed tool result images not being sent in `function_call_output` items for OpenAI Responses API providers, causing image data to be silently dropped in tool results ([#2104](https://github.com/badlogic/pi-mono/issues/2104))
- Fixed assistant content being sent as structured content blocks instead of plain strings in the `openai-completions` provider, causing errors with some OpenAI-compatible backends ([#2008](https://github.com/badlogic/pi-mono/pull/2008) by [@geraldoaax](https://github.com/geraldoaax))
- Fixed error details in OpenAI Responses `response.failed` handler to include status code, error code, and message instead of a generic failure ([#1956](https://github.com/badlogic/pi-mono/pull/1956) by [@drewburr](https://github.com/drewburr))
- Fixed GitHub Copilot device-code login polling to respect OAuth slow-down intervals, wait before the first token poll, and include a clearer clock-drift hint in WSL/VM environments when repeated slow-downs lead to timeout
- Fixed usage statistics not being captured for OpenAI-compatible providers that return usage in `choice.usage` instead of the standard `chunk.usage` (e.g., Moonshot/Kimi) ([#2017](https://github.com/badlogic/pi-mono/issues/2017))
- Fixed editor scroll indicator rendering crash in narrow terminal widths ([#2103](https://github.com/badlogic/pi-mono/pull/2103) by [@haoqixu](https://github.com/haoqixu))
- Fixed tab characters in editor and input paste not being normalized to spaces ([#2027](https://github.com/badlogic/pi-mono/pull/2027), [#1975](https://github.com/badlogic/pi-mono/pull/1975) by [@haoqixu](https://github.com/haoqixu))
- Fixed `wordWrapLine` overflow when wide characters (CJK, fullwidth) fall exactly at the wrap boundary ([#2082](https://github.com/badlogic/pi-mono/pull/2082) by [@haoqixu](https://github.com/haoqixu))
- Fixed paste markers not being treated as atomic segments in editor word wrapping and cursor navigation ([#2111](https://github.com/badlogic/pi-mono/pull/2111) by [@haoqixu](https://github.com/haoqixu))

## [0.57.1] - 2026-03-07

### New Features
- Tree branch folding and segment-jump navigation in `/tree`, with `Ctrl+←`/`Ctrl+→` and `Alt+←`/`Alt+→` shortcuts while `←`/`→` and `Page Up`/`Page Down` remain available for paging. See [docs/tree.md](docs/tree.md) and [docs/keybindings.md](docs/keybindings.md).
- `session_directory` extension event for customizing session directory paths before session manager creation. See [docs/extensions.md](docs/extensions.md).
- Digit keybindings (`0-9`) in the TUI keybinding system, including modified combos like `ctrl+1`. See [docs/keybindings.md](docs/keybindings.md).

### Added
- Added `/tree` branch folding and segment-jump navigation with `Ctrl+←`/`Ctrl+→` and `Alt+←`/`Alt+→`, while keeping `←`/`→` and `Page Up`/`Page Down` for paging ([#1724](https://github.com/badlogic/pi-mono/pull/1724) by [@Perlence](https://github.com/Perlence))
- Added `session_directory` extension event that fires before session manager creation, allowing extensions to customize the session directory path based on cwd and other factors. CLI `--session-dir` flag takes precedence over extension-provided paths ([#1730](https://github.com/badlogic/pi-mono/pull/1730) by [@hjanuschka](https://github.com/hjanuschka)).
- Added digit keys (`0-9`) to the keybinding system, including Kitty CSI-u and xterm `modifyOtherKeys` support for bindings like `ctrl+1` ([#1905](https://github.com/badlogic/pi-mono/issues/1905))

### Fixed
- Fixed custom tool collapsed/expanded rendering in HTML exports. Custom tools that define different collapsed vs expanded displays now render correctly in exported HTML, with expandable sections when both states differ and direct display when only expanded exists ([#1934](https://github.com/badlogic/pi-mono/pull/1934) by [@aliou](https://github.com/aliou))
- Fixed tmux startup guidance and keyboard setup warnings for modified key handling, including Ghostty `shift+enter=text:\n` remap guidance and tmux `extended-keys-format` detection ([#1872](https://github.com/badlogic/pi-mono/issues/1872))
- Fixed z.ai context overflow recovery so `model_context_window_exceeded` errors trigger auto-compaction instead of surfacing as unhandled stop reason failures ([#1937](https://github.com/badlogic/pi-mono/issues/1937))
- Fixed autocomplete selection ignoring typed text: highlight now follows the first prefix match as the user types, and exact matches are always selected on Enter ([#1931](https://github.com/badlogic/pi-mono/pull/1931) by [@aliou](https://github.com/aliou))
- Fixed slash-command Tab completion to immediately open argument completions when available ([#1481](https://github.com/badlogic/pi-mono/pull/1481) by [@barapa](https://github.com/barapa))
- Fixed explicit `pi -e <path>` extensions losing command and tool conflicts to discovered extensions by giving CLI-loaded extensions higher precedence ([#1896](https://github.com/badlogic/pi-mono/issues/1896))
- Fixed Windows external editor launch for `Ctrl+G` and `ctx.ui.editor()` so shell-based commands like `EDITOR="code --wait"` work correctly ([#1925](https://github.com/badlogic/pi-mono/issues/1925))

## [0.57.0] - 2026-03-07

### New Features

- Extensions can intercept and modify provider request payloads via `before_provider_request`. See [docs/extensions.md#before_provider_request](docs/extensions.md#before_provider_request).
- Extension UIs can use non-capturing overlays with explicit focus control via `OverlayOptions.nonCapturing` and `OverlayHandle.focus()` / `unfocus()` / `isFocused()`. See [docs/extensions.md](docs/extensions.md) and [../tui/README.md](../tui/README.md).
- RPC mode now uses strict LF-only JSONL framing for robust payload handling. See [docs/rpc.md](docs/rpc.md).

### Breaking Changes

- RPC mode now uses strict LF-delimited JSONL framing. Clients must split records on `\n` only instead of using generic line readers such as Node `readline`, which also split on Unicode separators inside JSON payloads ([#1911](https://github.com/badlogic/pi-mono/issues/1911))

### Added

- Added `before_provider_request` extension hook so extensions can inspect or replace provider payloads before requests are sent, with an example in `examples/extensions/provider-payload.ts`
- Added non-capturing overlay focus control for extension UIs via `OverlayOptions.nonCapturing` and `OverlayHandle.focus()` / `unfocus()` / `isFocused()` ([#1916](https://github.com/badlogic/pi-mono/pull/1916) by [@nicobailon](https://github.com/nicobailon))

### Changed

- Overlay compositing in extension UIs now uses focus order so focused overlays render on top while preserving stack semantics for show/hide behavior ([#1916](https://github.com/badlogic/pi-mono/pull/1916) by [@nicobailon](https://github.com/nicobailon))

### Fixed

- Fixed RPC mode stdin/stdout framing to use strict LF-delimited JSONL instead of `readline`, so payloads containing `U+2028` or `U+2029` no longer corrupt command or event streams ([#1911](https://github.com/badlogic/pi-mono/issues/1911))
- Fixed automatic overlay focus restoration in extension UIs to skip non-capturing overlays, and fixed overlay hide behavior to only reassign focus when the hidden overlay had focus ([#1916](https://github.com/badlogic/pi-mono/pull/1916) by [@nicobailon](https://github.com/nicobailon))
- Fixed `pi config` misclassifying `~/.agents/skills` as project-scoped in non-git directories under `$HOME`, so toggling those skills no longer writes project overrides to `.pi/settings.json` ([#1915](https://github.com/badlogic/pi-mono/issues/1915))

## [0.56.3] - 2026-03-06

### New Features

- `claude-sonnet-4-6` model available via the `google-antigravity` provider ([#1859](https://github.com/badlogic/pi-mono/issues/1859))
- Custom editors can now define their own `onEscape`/`onCtrlD` handlers without being overwritten by app defaults, enabling vim-mode extensions ([#1838](https://github.com/badlogic/pi-mono/issues/1838))
- Shift+Enter and Ctrl+Enter now work inside tmux via xterm modifyOtherKeys fallback ([docs/tmux.md](docs/tmux.md), [#1872](https://github.com/badlogic/pi-mono/issues/1872))
- Auto-compaction is now resilient to persistent API errors (e.g. 529 overloaded) and no longer retriggers spuriously after compaction ([#1834](https://github.com/badlogic/pi-mono/issues/1834), [#1860](https://github.com/badlogic/pi-mono/issues/1860))

### Added

- Added `claude-sonnet-4-6` model for the `google-antigravity` provider ([#1859](https://github.com/badlogic/pi-mono/issues/1859)).
- Added [tmux setup documentation](docs/tmux.md) for modified enter key support ([#1872](https://github.com/badlogic/pi-mono/issues/1872))

### Fixed

- Fixed custom editors having their `onEscape`/`onCtrlD` handlers unconditionally overwritten by app-level defaults, making vim-style escape handling impossible ([#1838](https://github.com/badlogic/pi-mono/issues/1838))
- Fixed auto-compaction retriggering on the first prompt after compaction due to stale pre-compaction assistant usage ([#1860](https://github.com/badlogic/pi-mono/issues/1860) by [@joelhooks](https://github.com/joelhooks))
- Fixed sessions never auto-compacting when hitting persistent API errors (e.g. 529 overloaded) by estimating context size from the last successful response ([#1834](https://github.com/badlogic/pi-mono/issues/1834))
- Fixed compaction summarization requests exceeding context limits by truncating tool results to 2k chars ([#1796](https://github.com/badlogic/pi-mono/issues/1796))
- Fixed `/new` leaving startup header content, including the changelog, visible after starting a fresh session ([#1880](https://github.com/badlogic/pi-mono/issues/1880))
- Fixed misleading docs and example implying that returning `{ isError: true }` from a tool's `execute` function marks the execution as failed; errors must be signaled by throwing ([#1881](https://github.com/badlogic/pi-mono/issues/1881))
- Fixed model switches through non-reasoning models to preserve the saved default thinking level instead of persisting a capability-forced `off` clamp ([#1864](https://github.com/badlogic/pi-mono/issues/1864))
- Fixed parallel pi processes failing with false "No API key found" errors due to immediate lockfile contention on `auth.json` and `settings.json` ([#1871](https://github.com/badlogic/pi-mono/issues/1871))
- Fixed OpenAI Responses reasoning replay regression that broke multi-turn reasoning continuity ([#1878](https://github.com/badlogic/pi-mono/issues/1878))

## [0.56.2] - 2026-03-05

### New Features

- GPT-5.4 support across `openai`, `openai-codex`, `azure-openai-responses`, and `opencode`, with `gpt-5.4` now the default for `openai` and `openai-codex` ([README.md](README.md), [docs/providers.md](docs/providers.md)).
- `treeFilterMode` setting to choose the default `/tree` filter mode (`default`, `no-tools`, `user-only`, `labeled-only`, `all`) ([docs/settings.md](docs/settings.md), [#1852](https://github.com/badlogic/pi-mono/pull/1852) by [@lajarre](https://github.com/lajarre)).
- Mistral native conversations integration with SDK-backed provider behavior, preserving Mistral-specific thinking and replay semantics ([README.md](README.md), [docs/providers.md](docs/providers.md), [#1716](https://github.com/badlogic/pi-mono/issues/1716)).

### Added

- Added `gpt-5.4` model availability for `openai`, `openai-codex`, `azure-openai-responses`, and `opencode` providers.
- Added `gpt-5.3-codex` fallback model availability for `github-copilot` until upstream model catalogs include it ([#1853](https://github.com/badlogic/pi-mono/issues/1853)).
- Added `treeFilterMode` setting to choose the default `/tree` filter mode (`default`, `no-tools`, `user-only`, `labeled-only`, `all`) ([#1852](https://github.com/badlogic/pi-mono/pull/1852) by [@lajarre](https://github.com/lajarre)).

### Changed

- Updated the default models for the `openai` and `openai-codex` providers to `gpt-5.4`.

### Fixed

- Fixed GPT-5.3 Codex follow-up turns dropping OpenAI Responses assistant `phase` metadata by preserving replayable signatures in session history and forwarding `phase` back to the Responses API ([#1819](https://github.com/badlogic/pi-mono/issues/1819)).
- Fixed OpenAI Responses replay to omit empty thinking blocks, avoiding invalid no-op reasoning items in follow-up turns.
- Updated Mistral integration to use the native SDK-backed provider and conversations API, including coding-agent model/provider wiring and Mistral setup documentation ([#1716](https://github.com/badlogic/pi-mono/issues/1716)).
- Fixed Antigravity reliability: endpoint cascade on 403/404, added autopush sandbox fallback, removed extra fingerprint headers ([#1830](https://github.com/badlogic/pi-mono/issues/1830)).
- Fixed `@mariozechner/pi-ai/oauth` extension imports in published installs by resolving the subpath directly from built `dist` files instead of package-root wrapper shims ([#1856](https://github.com/badlogic/pi-mono/issues/1856)).
- Fixed Gemini 3 multi-turn tool use losing structured context by using `skip_thought_signature_validator` sentinel for unsigned function calls instead of text fallback ([#1829](https://github.com/badlogic/pi-mono/issues/1829)).
- Fixed model selector filter not accepting typed characters in VS Code 1.110+ due to missing Kitty CSI-u printable decoding in the `Input` component ([#1857](https://github.com/badlogic/pi-mono/issues/1857))
- Fixed editor/footer visibility drift during terminal resize by forcing full redraws when terminal width or height changes ([#1844](https://github.com/badlogic/pi-mono/pull/1844) by [@ghoulr](https://github.com/ghoulr)).
- Fixed footer width truncation for wide Unicode text (session name, model, provider) to prevent TUI crashes from rendered lines exceeding terminal width ([#1833](https://github.com/badlogic/pi-mono/issues/1833)).
- Fixed Windows write preview background artifacts by normalizing CRLF content (`\r\n`) to LF for display rendering in tool output previews ([#1854](https://github.com/badlogic/pi-mono/issues/1854)).

## [0.56.1] - 2026-03-05

### Fixed

- Fixed extension alias fallback resolution to use ESM-aware resolution for `jiti` aliases in global installs ([#1821](https://github.com/badlogic/pi-mono/pull/1821) by [@Perlence](https://github.com/Perlence))
- Fixed markdown blockquote rendering to isolate blockquote styling from default text style, preventing style leakage.

## [0.56.0] - 2026-03-04

### New Features

- Added OpenCode Go provider support with `opencode-go` model defaults and `OPENCODE_API_KEY` environment variable support ([docs/providers.md](docs/providers.md), [#1757](https://github.com/badlogic/pi-mono/issues/1757)).
- Added `branchSummary.skipPrompt` setting to skip branch summarization prompts during tree navigation ([docs/settings.md](docs/settings.md), [#1792](https://github.com/badlogic/pi-mono/issues/1792)).
- Added `gemini-3.1-flash-lite-preview` fallback model availability for Google provider catalogs when upstream model metadata lags ([README.md](README.md), [#1785](https://github.com/badlogic/pi-mono/issues/1785)).

### Breaking Changes

- Changed scoped model thinking semantics. Scoped entries without an explicit `:<thinking>` suffix now inherit the current session thinking level when selected, instead of applying a startup-captured default.
- Moved Node OAuth runtime exports off the top-level `@mariozechner/pi-ai` entry. OAuth login and refresh must be imported from `@mariozechner/pi-ai/oauth` ([#1814](https://github.com/badlogic/pi-mono/issues/1814)).

### Added

- Added `branchSummary.skipPrompt` setting to skip the summary prompt when navigating branches ([#1792](https://github.com/badlogic/pi-mono/issues/1792)).
- Added OpenCode Go provider support with `opencode-go` model defaults and `OPENCODE_API_KEY` environment variable support ([#1757](https://github.com/badlogic/pi-mono/issues/1757)).
- Added `gemini-3.1-flash-lite-preview` fallback model availability in provider catalogs when upstream catalogs lag ([#1785](https://github.com/badlogic/pi-mono/issues/1785)).

### Changed

- Updated Antigravity Gemini 3.1 model metadata and request headers to match upstream behavior.

### Fixed

- Fixed IME hardware cursor positioning in the custom extension editor (`ctx.ui.editor()` / extension editor dialog) by propagating focus to the internal `Editor`, preventing the terminal cursor from getting stuck at the bottom-right during composition.
- Added OSC 133 semantic zone markers around rendered user messages to support terminal navigation between prompts in iTerm2, WezTerm, Kitty, Ghostty, and other compatible terminals ([#1805](https://github.com/badlogic/pi-mono/issues/1805)).
- Fixed markdown blockquotes dropping nested list content in the TUI renderer ([#1787](https://github.com/badlogic/pi-mono/issues/1787)).
- Fixed TUI width handling for regional indicator symbols to prevent wrap drift and stale characters during streaming ([#1783](https://github.com/badlogic/pi-mono/issues/1783)).
- Fixed Kitty CSI-u handling to ignore unsupported modifiers so modifier-only events do not insert printable characters ([#1807](https://github.com/badlogic/pi-mono/issues/1807)).
- Fixed single-line paste handling to insert text atomically and avoid repeated `@` autocomplete scans on large pastes ([#1812](https://github.com/badlogic/pi-mono/issues/1812)).
- Fixed extension loading with the new `@mariozechner/pi-ai/oauth` export path by aliasing the oauth subpath in the extension loader and development path mapping ([#1814](https://github.com/badlogic/pi-mono/issues/1814)).
- Fixed browser-safe provider loading regressions by preloading the Bedrock provider module in compiled Bun binaries and rebuilding binaries against fresh workspace dependencies ([#1814](https://github.com/badlogic/pi-mono/issues/1814)).
- Fixed GNU screen terminal detection by downgrading theme output to 256-color mode for `screen*` TERM values ([#1809](https://github.com/badlogic/pi-mono/issues/1809)).
- Fixed branch summarization queue handling so messages typed while summaries are generated are processed correctly ([#1803](https://github.com/badlogic/pi-mono/issues/1803)).
- Fixed compaction summary requests to avoid reasoning output for non-reasoning models ([#1793](https://github.com/badlogic/pi-mono/issues/1793)).
- Fixed overflow auto-compaction cascades so a single overflow does not trigger repeated compaction loops.
- Fixed `models.json` to allow provider-scoped custom model ids and model-level `baseUrl` overrides ([#1759](https://github.com/badlogic/pi-mono/issues/1759), [#1777](https://github.com/badlogic/pi-mono/issues/1777)).
- Fixed session selector display sanitization by stripping control characters from session display text ([#1747](https://github.com/badlogic/pi-mono/issues/1747)).
- Fixed Groq Qwen3 reasoning effort mapping for OpenAI-compatible models ([#1745](https://github.com/badlogic/pi-mono/issues/1745)).
- Fixed Bedrock `AWS_PROFILE` region resolution by honoring profile `region` values ([#1800](https://github.com/badlogic/pi-mono/issues/1800)).
- Fixed Gemini 3.1 thinking-level detection for `google` and `google-vertex` providers ([#1785](https://github.com/badlogic/pi-mono/issues/1785)).
- Fixed browser bundling compatibility for `@mariozechner/pi-ai` by removing Node-only side effects from default browser import paths ([#1814](https://github.com/badlogic/pi-mono/issues/1814)).
## [0.55.4] - 2026-03-02

### New Features

- Runtime tool registration now applies immediately in active sessions. Tools registered via `pi.registerTool()` after startup are available to `pi.getAllTools()` and the LLM without `/reload` ([docs/extensions.md](docs/extensions.md), [examples/extensions/dynamic-tools.ts](examples/extensions/dynamic-tools.ts), [#1720](https://github.com/badlogic/pi-mono/issues/1720)).
- Tool definitions can customize the default system prompt with `promptSnippet` (`Available tools`) and `promptGuidelines` (`Guidelines`) while the tool is active ([docs/extensions.md](docs/extensions.md), [#1720](https://github.com/badlogic/pi-mono/issues/1720)).
- Custom tool renderers can suppress transcript output without leaving extra spacing or empty transcript footprint in interactive rendering ([docs/extensions.md](docs/extensions.md), [#1719](https://github.com/badlogic/pi-mono/pull/1719)).

### Added

- Added optional `promptSnippet` to `ToolDefinition` for one-line entries in the default system prompt's `Available tools` section. Active extension tools appear there when registered and active ([#1237](https://github.com/badlogic/pi-mono/pull/1237) by [@semtexzv](https://github.com/semtexzv)).
- Added optional `promptGuidelines` to `ToolDefinition` so active tools can append tool-specific bullets to the default system prompt `Guidelines` section ([#1720](https://github.com/badlogic/pi-mono/issues/1720)).

### Fixed

- Fixed `pi.registerTool()` dynamic registration after session initialization. Tools registered in `session_start` and later handlers now refresh immediately, become active, and are visible to the LLM without `/reload` ([#1720](https://github.com/badlogic/pi-mono/issues/1720))
- Fixed session message persistence ordering by serializing `AgentSession` event processing, preventing `toolResult` entries from being written before their corresponding assistant tool-call messages when extension handlers are asynchronous ([#1717](https://github.com/badlogic/pi-mono/issues/1717))
- Fixed spacing artifacts when custom tool renderers intentionally suppress per-call transcript output, including extra blank rows in interactive streaming and non-zero transcript footprint for empty custom renders ([#1719](https://github.com/badlogic/pi-mono/pull/1719) by [@alasano](https://github.com/alasano))
- Fixed `session.prompt()` returning before retry completion by creating the retry promise synchronously at `agent_end` dispatch, which closes a race when earlier queued event handlers are async ([#1726](https://github.com/badlogic/pi-mono/pull/1726) by [@pasky](https://github.com/pasky))

## [0.55.3] - 2026-02-27

### Fixed

- Changed the default image paste keybinding on Windows to `alt+v` to avoid `ctrl+v` conflicts with terminal paste behavior ([#1682](https://github.com/badlogic/pi-mono/pull/1682) by [@mrexodia](https://github.com/mrexodia)).

## [0.55.2] - 2026-02-27

### New Features

- Extensions can dynamically remove custom providers via `pi.unregisterProvider(name)`, restoring any built-in models that were overridden, without requiring `/reload` ([docs](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/custom-provider.md)).
- `pi.registerProvider()` now takes effect immediately when called outside the initial extension load phase (e.g. from a command handler), removing the need for `/reload` after late registrations.

### Added

- `pi.unregisterProvider(name)` removes a dynamically registered provider and its models from the registry without requiring `/reload`. Built-in models that were overridden by the provider are restored ([#1669](https://github.com/badlogic/pi-mono/pull/1669) by [@aliou](https://github.com/aliou)).

### Fixed

- `pi.registerProvider()` now takes effect immediately when called after the initial extension load phase (e.g. from a command handler). Previously the registration sat in a pending queue that was never flushed until the next `/reload` ([#1669](https://github.com/badlogic/pi-mono/pull/1669) by [@aliou](https://github.com/aliou)).
- Fixed duplicate session headers when forking from a point before any assistant message. `createBranchedSession` now defers file creation to `_persist()` when the branched path has no assistant message, matching the `newSession()` contract ([#1672](https://github.com/badlogic/pi-mono/pull/1672) by [@w-winter](https://github.com/w-winter)).
- Fixed SIGINT being delivered to pi while the process is suspended (e.g. via `ctrl+z`), which could corrupt terminal state on resume ([#1668](https://github.com/badlogic/pi-mono/pull/1668) by [@aliou](https://github.com/aliou)).
- Fixed Z.ai thinking control using wrong parameter name, causing thinking to always be enabled and wasting tokens/latency ([#1674](https://github.com/badlogic/pi-mono/pull/1674) by [@okuyam2y](https://github.com/okuyam2y))
- Fixed `redacted_thinking` blocks being silently dropped during Anthropic streaming, and related issues with interleaved-thinking beta headers and temperature being sent alongside extended thinking ([#1665](https://github.com/badlogic/pi-mono/pull/1665) by [@tctev](https://github.com/tctev))
- Fixed `(external, cli)` user-agent flag causing 401 errors on Anthropic setup-token endpoint ([#1677](https://github.com/badlogic/pi-mono/pull/1677) by [@LazerLance777](https://github.com/LazerLance777))
- Fixed crash when OpenAI-compatible provider returns a chunk with no `choices` array ([#1671](https://github.com/badlogic/pi-mono/issues/1671))

## [0.55.1] - 2026-02-26

### New Features

- Added offline startup mode via `--offline` (or `PI_OFFLINE`) to disable startup network operations, with startup network timeouts to avoid hangs in restricted or offline environments.
- Added `gemini-3.1-pro-preview` model support to the `google-gemini-cli` provider ([#1599](https://github.com/badlogic/pi-mono/pull/1599) by [@audichuang](https://github.com/audichuang)).

### Fixed

- Fixed offline startup hangs by adding offline startup behavior and network timeouts during managed tool setup ([#1631](https://github.com/badlogic/pi-mono/pull/1631) by [@mcollina](https://github.com/mcollina))
- Fixed Windows VT input initialization in ESM by loading koffi via createRequire, avoiding runtime and bundling issues in end-user environments ([#1627](https://github.com/badlogic/pi-mono/pull/1627) by [@kaste](https://github.com/kaste))
- Fixed managed `fd`/`rg` bootstrap on Windows in Git Bash by using `extract-zip` for `.zip` archives, searching extracted layouts more robustly, and isolating extraction temp directories to avoid concurrent download races ([#1348](https://github.com/badlogic/pi-mono/issues/1348))
- Fixed extension loading on Windows when resolving `@sinclair/typebox` aliases so subpath imports like `@sinclair/typebox/compiler` resolve correctly.
- Fixed adaptive thinking for Claude Sonnet 4.6 in Anthropic and Bedrock providers, and clamped unsupported `xhigh` effort values to supported levels ([#1548](https://github.com/badlogic/pi-mono/pull/1548) by [@tctev](https://github.com/tctev))
- Fixed Vertex ADC credential detection race by avoiding caching a false negative during async import initialization ([#1550](https://github.com/badlogic/pi-mono/pull/1550) by [@jeremiahgaylord-web](https://github.com/jeremiahgaylord-web))
- Fixed subagent extension example to resolve user agents from the configured agent directory instead of hardcoded paths ([#1559](https://github.com/badlogic/pi-mono/pull/1559) by [@tianshuwang](https://github.com/tianshuwang))

## [0.55.0] - 2026-02-24

### Breaking Changes

- Resource precedence for extensions, skills, prompts, themes, and slash-command name collisions is now project-first (`cwd/.pi`) before user-global (`~/.pi/agent`). If you relied on global resources overriding project resources with the same names, rename or reorder your resources.
- Extension registration conflicts no longer unload the entire later extension. All extensions stay loaded, and conflicting command/tool/flag names are resolved by first registration in load order.

## [0.54.2] - 2026-02-23

### Fixed

- Fixed `.pi` folder being created unnecessarily when only reading settings. The folder is now only created when writing project-specific settings.
- Fixed extension-driven runtime theme changes to persist in settings so `/settings` reflects the active `currentTheme` after `ctx.ui.setTheme(...)` ([#1483](https://github.com/badlogic/pi-mono/pull/1483) by [@ferologics](https://github.com/ferologics))
- Fixed interactive mode freezes during large streaming `write` tool calls by using incremental syntax highlighting while partial arguments stream, with a final full re-highlight after tool-call arguments complete.

## [0.54.1] - 2026-02-22

### Fixed

- Externalized koffi from bun binary builds, reducing archive sizes by ~15MB per platform (e.g. darwin-arm64: 43MB -> 28MB). Koffi's Windows-only `.node` file is now shipped alongside the Windows binary only.

## [0.54.0] - 2026-02-19

### Added

- Added default skill auto-discovery for `.agents/skills` locations. Pi now discovers project skills from `.agents/skills` in `cwd` and ancestor directories (up to git repo root, or filesystem root when not in a repo), and global skills from `~/.agents/skills`, in addition to existing `.pi` skill paths.

## [0.53.1] - 2026-02-19

### Changed

- Added Gemini 3.1 model catalog entries for all built-in providers that currently expose it: `google`, `google-vertex`, `opencode`, `openrouter`, and `vercel-ai-gateway`.
- Added Claude Opus 4.6 Thinking to the `google-antigravity` model catalog.

## [0.53.0] - 2026-02-17

### Breaking Changes

- `SettingsManager` persistence semantics changed for SDK consumers. Setters now update in-memory state immediately and queue disk writes. Code that requires durable on-disk settings must call `await settingsManager.flush()`.
- `AuthStorage` constructor is no longer public. Use static factories (`AuthStorage.create(...)`, `AuthStorage.fromStorage(...)`, `AuthStorage.inMemory(...)`). This breaks code that used `new AuthStorage(...)` directly.

### Added

- Added `SettingsManager.drainErrors()` for caller-controlled settings I/O error handling without manager-side console output.
- Added auth storage backends (`FileAuthStorageBackend`, `InMemoryAuthStorageBackend`) and `AuthStorage.fromStorage(...)` for storage-first auth persistence wiring.
- Added Anthropic `claude-sonnet-4-6` model fallback entry to generated model definitions.

### Changed

- `SettingsManager` now uses scoped storage abstraction with per-scope locked read/merge/write persistence for global and project settings.

### Fixed

- Fixed project settings persistence to preserve unrelated external edits via merge-on-write, while still applying in-memory changes for modified keys.
- Fixed auth credential persistence to preserve unrelated external edits to `auth.json` via locked read/merge/write updates.
- Fixed auth load/persist error surfacing by buffering errors and exposing them via `AuthStorage.drainErrors()`.

## [0.52.12] - 2026-02-13

### Added

- Added `transport` setting (`"sse"`, `"websocket"`, `"auto"`) to `/settings` and `settings.json` for providers that support multiple transports (currently `openai-codex` via OpenAI Codex Responses).

### Changed

- Interactive mode now applies transport changes immediately to the active agent session.
- Settings migration now maps legacy `websockets: boolean` to the new `transport` setting.

## [0.52.11] - 2026-02-13

### Added

- Added MiniMax M2.5 model entries for `minimax`, `minimax-cn`, `openrouter`, and `vercel-ai-gateway` providers, plus `minimax-m2.5-free` for `opencode`.

## [0.52.10] - 2026-02-12

### New Features

- Extension terminal input interception via `terminal_input`, allowing extensions to consume or transform raw input before normal TUI handling. See [docs/extensions.md](docs/extensions.md).
- Expanded CLI model selection: `--model` now supports `provider/id`, fuzzy matching, and `:<thinking>` suffixes. See [README.md](README.md) and [docs/models.md](docs/models.md).
- Safer package source handling with stricter git source parsing and improved local path normalization. See [docs/packages.md](docs/packages.md).
- New built-in model definition `gpt-5.3-codex-spark` for OpenAI and OpenAI Codex providers.
- Improved OpenAI stream robustness for malformed trailing tool-call JSON in partial chunks.
- Added built-in GLM-5 model support via z.ai and OpenRouter provider catalogs.

### Breaking Changes

- `ContextUsage.tokens` and `ContextUsage.percent` are now `number | null`. After compaction, context token count is unknown until the next LLM response, so these fields return `null`. Extensions that read `ContextUsage` must handle the `null` case. Removed `usageTokens`, `trailingTokens`, and `lastUsageIndex` fields from `ContextUsage` (implementation details that should not have been public) ([#1382](https://github.com/badlogic/pi-mono/pull/1382) by [@ferologics](https://github.com/ferologics))
- Git source parsing is now strict without `git:` prefix: only protocol URLs are treated as git (`https://`, `http://`, `ssh://`, `git://`). Shorthand sources like `github.com/org/repo` and `git@github.com:org/repo` now require the `git:` prefix. ([#1426](https://github.com/badlogic/pi-mono/issues/1426))

### Added

- Added extension event forwarding for message and tool execution lifecycles (`message_start`, `message_update`, `message_end`, `tool_execution_start`, `tool_execution_update`, `tool_execution_end`) ([#1375](https://github.com/badlogic/pi-mono/pull/1375) by [@sumeet](https://github.com/sumeet))
- Added `terminal_input` extension event to intercept, consume, or transform raw terminal input before normal TUI handling.
- Added `gpt-5.3-codex-spark` model definition for OpenAI and OpenAI Codex providers (research preview).

### Changed

- Routed GitHub Copilot Claude 4.x models through Anthropic Messages API, with updated Copilot header handling for Claude model requests.

### Fixed

- Fixed context usage percentage in footer showing stale pre-compaction values. After compaction the footer now shows `?/200k` until the next LLM response provides accurate usage ([#1382](https://github.com/badlogic/pi-mono/pull/1382) by [@ferologics](https://github.com/ferologics))
- Fixed `_checkCompaction()` using the first compaction entry instead of the latest, which could cause incorrect overflow detection with multiple compactions ([#1382](https://github.com/badlogic/pi-mono/pull/1382) by [@ferologics](https://github.com/ferologics))
- `--model` now works without `--provider`, supports `provider/id` syntax, fuzzy matching, and `:<thinking>` suffix (e.g., `--model sonnet:high`, `--model openai/gpt-4o`) ([#1350](https://github.com/badlogic/pi-mono/pull/1350) by [@mitsuhiko](https://github.com/mitsuhiko))
- Fixed local package path normalization for extension sources while tightening git source parsing rules ([#1426](https://github.com/badlogic/pi-mono/issues/1426))
- Fixed extension terminal input listeners not being cleared during session resets, which could leave stale handlers active.
- Fixed Termux bootstrap package name for `fd` installation ([#1433](https://github.com/badlogic/pi-mono/pull/1433))
- Fixed `@` file autocomplete fuzzy matching to prioritize path-prefix and segment matches for nested paths ([#1423](https://github.com/badlogic/pi-mono/issues/1423))
- Fixed OpenAI streaming tool-call parsing to tolerate malformed trailing JSON in partial chunks ([#1424](https://github.com/badlogic/pi-mono/issues/1424))

## [0.52.9] - 2026-02-08

### New Features

- Extensions can trigger a full runtime reload via `ctx.reload()`, useful for hot-reloading configuration or restarting the agent. See [docs/extensions.md](docs/extensions.md) and the [`reload-runtime` example](examples/extensions/reload-runtime.ts) ([#1371](https://github.com/badlogic/pi-mono/issues/1371))
- Short CLI disable aliases: `-ne` (`--no-extensions`), `-ns` (`--no-skills`), and `-np` (`--no-prompt-templates`) for faster interactive usage and scripting.
- `/export` HTML now includes collapsible tool input schemas (parameter names, types, and descriptions), improving session review and sharing workflows ([#1416](https://github.com/badlogic/pi-mono/pull/1416) by [@marchellodev](https://github.com/marchellodev)).
- `pi.getAllTools()` now exposes tool parameters in addition to name and description, enabling richer extension integrations ([#1416](https://github.com/badlogic/pi-mono/pull/1416) by [@marchellodev](https://github.com/marchellodev)).

### Added

- Added `ctx.reload()` to the extension API for programmatic runtime reload ([#1371](https://github.com/badlogic/pi-mono/issues/1371))
- Added short aliases for disable flags: `-ne` for `--no-extensions`, `-ns` for `--no-skills`, `-np` for `--no-prompt-templates`
- `/export` HTML now includes tool input schema (parameter names, types, descriptions) in a collapsible section under each tool ([#1416](https://github.com/badlogic/pi-mono/pull/1416) by [@marchellodev](https://github.com/marchellodev))
- `pi.getAllTools()` now returns tool parameters in addition to name and description ([#1416](https://github.com/badlogic/pi-mono/pull/1416) by [@marchellodev](https://github.com/marchellodev))

### Fixed

- Fixed extension source parsing so dot-prefixed local paths (for example `.pi/extensions/foo.ts`) are treated as local paths instead of git URLs
- Fixed fd/rg download failing on Windows due to `unzip` not being available; now uses `tar` for both `.tar.gz` and `.zip` extraction, with proper error reporting ([#1348](https://github.com/badlogic/pi-mono/issues/1348))
- Fixed RPC mode documentation incorrectly stating `ctx.hasUI` is `false`; it is `true` because dialog and fire-and-forget UI methods work via the RPC sub-protocol. Also documented missing unsupported/degraded methods (`pasteToEditor`, `getAllThemes`, `getTheme`, `setTheme`) ([#1411](https://github.com/badlogic/pi-mono/pull/1411) by [@aliou](https://github.com/aliou))
- Fixed `rg` not available in bash tool by downloading it at startup alongside `fd` ([#1348](https://github.com/badlogic/pi-mono/issues/1348))
- Fixed `custom-compaction` example to use `ModelRegistry` ([#1387](https://github.com/badlogic/pi-mono/issues/1387))
- Google providers now support full JSON Schema in tool declarations (anyOf, oneOf, const, etc.) ([#1398](https://github.com/badlogic/pi-mono/issues/1398) by [@jarib](https://github.com/jarib))
- Reverted incorrect Antigravity model change: `claude-opus-4-6-thinking` back to `claude-opus-4-5-thinking` (model does not exist on Antigravity endpoint)
- Updated the Antigravity system instruction to a more compact version for Google Gemini CLI compatibility
- Corrected opencode context windows for Claude Sonnet 4 and 4.5 ([#1383](https://github.com/badlogic/pi-mono/issues/1383))
- Fixed subagent example unknown-agent errors to include available agent names ([#1414](https://github.com/badlogic/pi-mono/pull/1414) by [@dnouri](https://github.com/dnouri))

## [0.52.8] - 2026-02-07

### New Features

- Emacs-style kill ring (`ctrl+k`/`ctrl+y`/`alt+y`) and undo (`ctrl+z`) in the editor input ([#1373](https://github.com/badlogic/pi-mono/pull/1373) by [@Perlence](https://github.com/Perlence))
- OpenRouter `auto` model alias (`openrouter:auto`) for automatic model routing ([#1361](https://github.com/badlogic/pi-mono/pull/1361) by [@yogasanas](https://github.com/yogasanas))
- Extensions can programmatically paste content into the editor via `pasteToEditor` in the extension UI context. See [docs/extensions.md](docs/extensions.md) ([#1351](https://github.com/badlogic/pi-mono/pull/1351) by [@kaofelix](https://github.com/kaofelix))
- `pi <package> --help` and invalid subcommands now show helpful output instead of failing silently ([#1347](https://github.com/badlogic/pi-mono/pull/1347) by [@ferologics](https://github.com/ferologics))

### Added

- Added `pasteToEditor` to extension UI context for programmatic editor paste ([#1351](https://github.com/badlogic/pi-mono/pull/1351) by [@kaofelix](https://github.com/kaofelix))
- Added package subcommand help and friendly error messages for invalid commands ([#1347](https://github.com/badlogic/pi-mono/pull/1347) by [@ferologics](https://github.com/ferologics))
- Added OpenRouter `auto` model alias for automatic model routing ([#1361](https://github.com/badlogic/pi-mono/pull/1361) by [@yogasanas](https://github.com/yogasanas))
- Added kill ring (ctrl+k/ctrl+y/alt+y) and undo (ctrl+z) support to the editor input ([#1373](https://github.com/badlogic/pi-mono/pull/1373) by [@Perlence](https://github.com/Perlence))

### Changed

- Replaced Claude Opus 4.5 with Opus 4.6 as default model ([#1345](https://github.com/badlogic/pi-mono/pull/1345) by [@calvin-hpnet](https://github.com/calvin-hpnet))

### Fixed

- Fixed temporary git package caches (`-e <git-url>`) to refresh on cache hits for unpinned sources, including detached/no-upstream checkouts
- Fixed aborting retries when an extension customizes the editor ([#1364](https://github.com/badlogic/pi-mono/pull/1364) by [@Perlence](https://github.com/Perlence))
- Fixed autocomplete not propagating to custom editors created by extensions ([#1372](https://github.com/badlogic/pi-mono/pull/1372) by [@Perlence](https://github.com/Perlence))
- Fixed extension shutdown to use clean TUI shutdown path, preventing orphaned processes

## [0.52.7] - 2026-02-06

### New Features

- Per-model overrides in `models.json` via `modelOverrides`, allowing customization of built-in provider models without replacing provider model lists. See [docs/models.md#per-model-overrides](docs/models.md#per-model-overrides).
- `models.json` provider `models` now merge with built-in models by `id`, so custom models can be added or replace matching built-ins without full provider replacement. See [docs/models.md#overriding-built-in-providers](docs/models.md#overriding-built-in-providers).
- Bedrock proxy support for unauthenticated endpoints via `AWS_BEDROCK_SKIP_AUTH` and `AWS_BEDROCK_FORCE_HTTP1`. See [docs/providers.md](docs/providers.md).

### Breaking Changes

- Changed `models.json` provider `models` behavior from full replacement to merge-by-id with built-in models. Built-in models are now kept by default, and custom models upsert by `id`.

### Added

- Added `modelOverrides` in `models.json` to customize individual built-in models per provider without full provider replacement ([#1332](https://github.com/badlogic/pi-mono/pull/1332) by [@charles-cooper](https://github.com/charles-cooper))
- Added `AWS_BEDROCK_SKIP_AUTH` and `AWS_BEDROCK_FORCE_HTTP1` environment variables for connecting to unauthenticated Bedrock proxies ([#1320](https://github.com/badlogic/pi-mono/pull/1320) by [@virtuald](https://github.com/virtuald))

### Fixed

- Fixed extra spacing between thinking-only assistant content and subsequent tool execution blocks when assistant messages contain no text
- Fixed queued steering/follow-up/custom messages remaining stuck after threshold auto-compaction by resuming the agent loop when Agent-level queues still contain pending messages ([#1312](https://github.com/badlogic/pi-mono/pull/1312) by [@ferologics](https://github.com/ferologics))
- Fixed `tool_result` extension handlers to chain result patches across handlers instead of last-handler-wins behavior ([#1280](https://github.com/badlogic/pi-mono/issues/1280))
- Fixed compromised auth lock files being handled gracefully instead of crashing auth storage initialization ([#1322](https://github.com/badlogic/pi-mono/issues/1322))
- Fixed Bedrock adaptive thinking handling for Claude Opus 4.6 with interleaved thinking beta responses ([#1323](https://github.com/badlogic/pi-mono/pull/1323) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- Fixed OpenAI Responses API requests to use `store: false` by default to avoid server-side history logging ([#1308](https://github.com/badlogic/pi-mono/issues/1308))
- Fixed interactive mode startup by initializing autocomplete after resources are loaded ([#1328](https://github.com/badlogic/pi-mono/issues/1328))
- Fixed `modelOverrides` merge behavior for nested objects and documented usage details ([#1062](https://github.com/badlogic/pi-mono/issues/1062))

## [0.52.6] - 2026-02-05

### Breaking Changes

- Removed `/exit` command handling. Use `/quit` to exit ([#1303](https://github.com/badlogic/pi-mono/issues/1303))

### Fixed

- Fixed `/quit` being shadowed by fuzzy slash command autocomplete matches from skills by adding `/quit` to built-in command autocomplete ([#1303](https://github.com/badlogic/pi-mono/issues/1303))
- Fixed local package source parsing and settings normalization regression that misclassified relative paths as git URLs and prevented globally installed local packages from loading after restart ([#1304](https://github.com/badlogic/pi-mono/issues/1304))

## [0.52.5] - 2026-02-05

### Fixed

- Fixed thinking level capability detection so Anthropic Opus 4.6 models expose `xhigh` in selectors and cycling

## [0.52.4] - 2026-02-05

### Fixed

- Fixed extensions setting not respecting `package.json` `pi.extensions` manifest when directory is specified directly ([#1302](https://github.com/badlogic/pi-mono/pull/1302) by [@hjanuschka](https://github.com/hjanuschka))

## [0.52.3] - 2026-02-05

### Fixed

- Fixed git package parsing fallback for unknown hosts so enterprise git sources like `git:github.tools.sap/org/repo` are treated as git packages instead of local paths
- Fixed git package `@ref` parsing for shorthand, HTTPS, and SSH source formats, including branch refs with slashes
- Fixed Bedrock default model ID from `us.anthropic.claude-opus-4-6-v1:0` to `us.anthropic.claude-opus-4-6-v1`
- Fixed Bedrock Opus 4.6 model metadata (IDs, cache pricing) and added missing EU profile
- Fixed Claude Opus 4.6 context window metadata to 200000 for Anthropic and OpenCode providers

## [0.52.2] - 2026-02-05

### Changed

- Updated default model for `anthropic` provider to `claude-opus-4-6`
- Updated default model for `openai-codex` provider to `gpt-5.3-codex`
- Updated default model for `amazon-bedrock` provider to `us.anthropic.claude-opus-4-6-v1:0`
- Updated default model for `vercel-ai-gateway` provider to `anthropic/claude-opus-4-6`
- Updated default model for `opencode` provider to `claude-opus-4-6`

## [0.52.1] - 2026-02-05

## [0.52.0] - 2026-02-05

### New Features

- Claude Opus 4.6 model support.
- GPT-5.3 Codex model support (OpenAI Codex provider only).
- SSH URL support for git packages. See [docs/packages.md](docs/packages.md).
- `auth.json` API keys now support shell command resolution (`!command`) and environment variable lookup. See [docs/providers.md](docs/providers.md).
- Model selectors now display the selected model name.

### Added

- API keys in `auth.json` now support shell command resolution (`!command`) and environment variable lookup, matching the behavior in `models.json`
- Added `minimal-mode.ts` example extension demonstrating how to override built-in tool rendering for a minimal display mode
- Added Claude Opus 4.6 model to the model catalog
- Added GPT-5.3 Codex model to the model catalog (OpenAI Codex provider only)
- Added SSH URL support for git packages ([#1287](https://github.com/badlogic/pi-mono/pull/1287) by [@markusn](https://github.com/markusn))
- Model selectors now display the selected model name ([#1275](https://github.com/badlogic/pi-mono/pull/1275) by [@haoqixu](https://github.com/haoqixu))

### Fixed

- Fixed HTML export losing indentation in ANSI-rendered tool output (e.g. JSON code blocks in custom tool results) ([#1269](https://github.com/badlogic/pi-mono/pull/1269) by [@aliou](https://github.com/aliou))
- Fixed images being silently dropped when `prompt()` is called with both `images` and `streamingBehavior` during streaming. `steer()`, `followUp()`, and the corresponding RPC commands now accept optional images. ([#1271](https://github.com/badlogic/pi-mono/pull/1271) by [@aliou](https://github.com/aliou))
- CLI `--help`, `--version`, `--list-models`, and `--export` now exit even if extensions keep the event loop alive ([#1285](https://github.com/badlogic/pi-mono/pull/1285) by [@ferologics](https://github.com/ferologics))
- Fixed crash when models send malformed tool arguments (objects instead of strings) ([#1259](https://github.com/badlogic/pi-mono/issues/1259))
- Fixed custom message expand state not being respected ([#1258](https://github.com/badlogic/pi-mono/pull/1258) by [@Gurpartap](https://github.com/Gurpartap))
- Fixed skill loader to respect .gitignore, .ignore, and .fdignore when scanning directories

## [0.51.6] - 2026-02-04

### New Features

- Configurable resume keybinding action for opening the session resume selector. See [docs/keybindings.md](docs/keybindings.md). ([#1249](https://github.com/badlogic/pi-mono/pull/1249) by [@juanibiapina](https://github.com/juanibiapina))

### Added

- Added `resume` as a configurable keybinding action, allowing users to bind a key to open the session resume selector (like `newSession`, `tree`, and `fork`) ([#1249](https://github.com/badlogic/pi-mono/pull/1249) by [@juanibiapina](https://github.com/juanibiapina))

### Changed

- Slash command menu now triggers on the first line even when other lines have content, allowing commands to be prepended to existing text ([#1227](https://github.com/badlogic/pi-mono/pull/1227) by [@aliou](https://github.com/aliou))

### Fixed

- Ignored unknown skill frontmatter fields when loading skills
- Fixed `/reload` not picking up changes in global settings.json ([#1241](https://github.com/badlogic/pi-mono/issues/1241))
- Fixed forked sessions to persist the user message after forking
- Fixed forked sessions to write to new session files instead of the parent ([#1242](https://github.com/badlogic/pi-mono/issues/1242))
- Fixed local package removal to normalize paths before comparison ([#1243](https://github.com/badlogic/pi-mono/issues/1243))
- Fixed OpenAI Codex Responses provider to respect configured baseUrl ([#1244](https://github.com/badlogic/pi-mono/issues/1244))
- Fixed `/settings` crashing in narrow terminals by handling small widths in the settings list ([#1246](https://github.com/badlogic/pi-mono/pull/1246) by [@haoqixu](https://github.com/haoqixu))
- Fixed Unix bash detection to fall back to PATH lookup when `/bin/bash` is unavailable, including Termux setups ([#1230](https://github.com/badlogic/pi-mono/pull/1230) by [@VaclavSynacek](https://github.com/VaclavSynacek))

## [0.51.5] - 2026-02-04

### Changed

- Changed Bedrock model generation to drop legacy workarounds now handled upstream ([#1239](https://github.com/badlogic/pi-mono/pull/1239) by [@unexge](https://github.com/unexge))

### Fixed

- Fixed Windows package installs regression by using shell execution instead of `.cmd` resolution ([#1220](https://github.com/badlogic/pi-mono/issues/1220))

## [0.51.4] - 2026-02-03

### New Features

- Share URLs now default to pi.dev, graciously donated by exe.dev.

### Changed

- Share URLs now use pi.dev by default while pi.dev and buildwithpi.ai continue to work.

### Fixed

- Fixed input scrolling to avoid splitting emoji sequences ([#1228](https://github.com/badlogic/pi-mono/pull/1228) by [@haoqixu](https://github.com/haoqixu))

## [0.51.3] - 2026-02-03

### New Features

- Command discovery for extensions via `ExtensionAPI.getCommands()`, with `commands.ts` example for invocation patterns. See [docs/extensions.md#pigetcommands](docs/extensions.md#pigetcommands) and [examples/extensions/commands.ts](examples/extensions/commands.ts).
- Local path support for `pi install` and `pi remove`, with relative path resolution against the settings file. See [docs/packages.md#local-paths](docs/packages.md#local-paths).

### Breaking Changes

- RPC `get_commands` response and `SlashCommandSource` type: renamed `"template"` to `"prompt"` for consistency with the rest of the codebase

### Added

- Added `ExtensionAPI.getCommands()` to let extensions list available slash commands (extensions, prompt templates, skills) for invocation via `prompt` ([#1210](https://github.com/badlogic/pi-mono/pull/1210) by [@w-winter](https://github.com/w-winter))
- Added `commands.ts` example extension and exported `SlashCommandInfo` types for command discovery integrations ([#1210](https://github.com/badlogic/pi-mono/pull/1210) by [@w-winter](https://github.com/w-winter))
- Added local path support for `pi install` and `pi remove` with relative paths stored against the target settings file ([#1216](https://github.com/badlogic/pi-mono/issues/1216))

### Fixed

- Fixed default thinking level persistence so settings-derived defaults are saved and restored correctly
- Fixed Windows package installs by resolving `npm.cmd` when `npm` is not directly executable ([#1220](https://github.com/badlogic/pi-mono/issues/1220))
- Fixed xhigh thinking level support check to accept gpt-5.2 model IDs ([#1209](https://github.com/badlogic/pi-mono/issues/1209))

## [0.51.2] - 2026-02-03

### New Features

- Extension tool output expansion controls via ExtensionUIContext getToolsExpanded and setToolsExpanded. See [docs/extensions.md](docs/extensions.md) and [docs/rpc.md](docs/rpc.md).

### Added

- Added ExtensionUIContext getToolsExpanded and setToolsExpanded for controlling tool output expansion ([#1199](https://github.com/badlogic/pi-mono/pull/1199) by [@academo](https://github.com/academo))
- Added install method detection to show package manager specific update instructions ([#1203](https://github.com/badlogic/pi-mono/pull/1203) by [@Itsnotaka](https://github.com/Itsnotaka))

### Fixed

- Fixed Kitty key release events leaking to parent shell over slow SSH connections by draining stdin for up to 1s on exit ([#1204](https://github.com/badlogic/pi-mono/issues/1204))
- Fixed legacy newline handling in the editor to preserve previous newline behavior
- Fixed @ autocomplete to include hidden paths
- Fixed submit fallback to honor configured keybindings
- Fixed extension commands conflicting with built-in commands by skipping them ([#1196](https://github.com/badlogic/pi-mono/pull/1196) by [@haoqixu](https://github.com/haoqixu))
- Fixed @-prefixed tool paths failing to resolve by stripping the prefix ([#1206](https://github.com/badlogic/pi-mono/issues/1206))
- Fixed install method detection to avoid stale cached results

## [0.51.1] - 2026-02-02

### New Features

- **Extension API switchSession**: Extensions can now programmatically switch sessions via `ctx.switchSession(sessionPath)`. See [docs/extensions.md](docs/extensions.md). ([#1187](https://github.com/badlogic/pi-mono/issues/1187))
- **Clear on shrink setting**: New `terminal.clearOnShrink` setting keeps the editor and footer pinned to the bottom of the terminal when content shrinks. May cause some flicker due to redraws. Disabled by default. Enable via `/settings` or `PI_CLEAR_ON_SHRINK=1` env var.

### Fixed

- Fixed scoped models not finding valid credentials after logout ([#1194](https://github.com/badlogic/pi-mono/pull/1194) by [@terrorobe](https://github.com/terrorobe))
- Fixed Ctrl+D exit closing the parent SSH session due to stdin buffer race condition ([#1185](https://github.com/badlogic/pi-mono/issues/1185))
- Fixed emoji cursor positioning in editor input ([#1183](https://github.com/badlogic/pi-mono/pull/1183) by [@haoqixu](https://github.com/haoqixu))

## [0.51.0] - 2026-02-01

### Breaking Changes

- **Extension tool signature change**: `ToolDefinition.execute` now uses `(toolCallId, params, signal, onUpdate, ctx)` parameter order to match `AgentTool.execute`. Previously it was `(toolCallId, params, onUpdate, ctx, signal)`. This makes wrapping built-in tools trivial since the first four parameters now align. Update your extensions by swapping the `signal` and `onUpdate` parameters:
  ```ts
  // Before
  async execute(toolCallId, params, onUpdate, ctx, signal) { ... }

  // After
  async execute(toolCallId, params, signal, onUpdate, ctx) { ... }
  ```

### New Features

- **Android/Termux support**: Pi now runs on Android via Termux. Install with:
  ```bash
  pkg install nodejs termux-api git
  npm install -g @bastani/atomic
  mkdir -p ~/.pi/agent
  echo "You are running on Android in Termux." > ~/.pi/agent/AGENTS.md
  ```
  Clipboard operations fall back gracefully when `termux-api` is unavailable. ([#1164](https://github.com/badlogic/pi-mono/issues/1164))
- **Bash spawn hook**: Extensions can now intercept and modify bash commands before execution via `pi.setBashSpawnHook()`. Adjust the command string, working directory, or environment variables. See [docs/extensions.md](docs/extensions.md). ([#1160](https://github.com/badlogic/pi-mono/pull/1160) by [@mitsuhiko](https://github.com/mitsuhiko))
- **Linux ARM64 musl support**: Pi now runs on Alpine Linux ARM64 (linux-arm64-musl) via updated clipboard dependency.
- **Nix/Guix support**: `PI_PACKAGE_DIR` environment variable overrides the package path for content-addressed package managers where store paths tokenize poorly. See [README.md#environment-variables](README.md#environment-variables). ([#1153](https://github.com/badlogic/pi-mono/pull/1153) by [@odysseus0](https://github.com/odysseus0))
- **Named session filter**: `/resume` picker now supports filtering to show only named sessions via Ctrl+N. Configurable via `toggleSessionNamedFilter` keybinding. See [docs/keybindings.md](docs/keybindings.md). ([#1128](https://github.com/badlogic/pi-mono/pull/1128) by [@w-winter](https://github.com/w-winter))
- **Typed tool call events**: Extension developers can narrow `ToolCallEvent` types using `isToolCallEventType()` for better TypeScript support. See [docs/extensions.md#tool-call-events](docs/extensions.md#tool-call-events). ([#1147](https://github.com/badlogic/pi-mono/pull/1147) by [@giuseppeg](https://github.com/giuseppeg))
- **Extension UI Protocol**: Full RPC documentation and examples for extension dialogs and notifications, enabling headless clients to support interactive extensions. See [docs/rpc.md#extension-ui-protocol](docs/rpc.md#extension-ui-protocol). ([#1144](https://github.com/badlogic/pi-mono/pull/1144) by [@aliou](https://github.com/aliou))

### Added

- Added Linux ARM64 musl (Alpine Linux) support via clipboard dependency update
- Added Android/Termux support with graceful clipboard fallback ([#1164](https://github.com/badlogic/pi-mono/issues/1164))
- Added bash tool spawn hook support for adjusting command, cwd, and env before execution ([#1160](https://github.com/badlogic/pi-mono/pull/1160) by [@mitsuhiko](https://github.com/mitsuhiko))
- Added typed `ToolCallEvent.input` per tool with `isToolCallEventType()` type guard for narrowing built-in tool events ([#1147](https://github.com/badlogic/pi-mono/pull/1147) by [@giuseppeg](https://github.com/giuseppeg))
- Exported `discoverAndLoadExtensions` from package to enable extension testing without a local repo clone ([#1148](https://github.com/badlogic/pi-mono/issues/1148))
- Added Extension UI Protocol documentation to RPC docs covering all request/response types for extension dialogs and notifications ([#1144](https://github.com/badlogic/pi-mono/pull/1144) by [@aliou](https://github.com/aliou))
- Added `rpc-demo.ts` example extension exercising all RPC-supported extension UI methods ([#1144](https://github.com/badlogic/pi-mono/pull/1144) by [@aliou](https://github.com/aliou))
- Added `rpc-extension-ui.ts` TUI example client demonstrating the extension UI protocol with interactive dialogs ([#1144](https://github.com/badlogic/pi-mono/pull/1144) by [@aliou](https://github.com/aliou))
- Added `PI_PACKAGE_DIR` environment variable to override package path for content-addressed package managers (Nix, Guix) where store paths tokenize poorly ([#1153](https://github.com/badlogic/pi-mono/pull/1153) by [@odysseus0](https://github.com/odysseus0))
- `/resume` session picker now supports named-only filter toggle (default Ctrl+N, configurable via `toggleSessionNamedFilter`) to show only named sessions ([#1128](https://github.com/badlogic/pi-mono/pull/1128) by [@w-winter](https://github.com/w-winter))

### Fixed

- Fixed `pi update` not updating npm/git packages when called without arguments ([#1151](https://github.com/badlogic/pi-mono/issues/1151))
- Fixed `models.json` validation requiring fields documented as optional. Model definitions now only require `id`; all other fields (`name`, `reasoning`, `input`, `cost`, `contextWindow`, `maxTokens`) have sensible defaults. ([#1146](https://github.com/badlogic/pi-mono/issues/1146))
- Fixed models resolving relative paths in skill files from cwd instead of skill directory by adding explicit guidance to skills preamble ([#1136](https://github.com/badlogic/pi-mono/issues/1136))
- Fixed tree selector losing focus state when navigating entries ([#1142](https://github.com/badlogic/pi-mono/pull/1142) by [@Perlence](https://github.com/Perlence))
- Fixed `cacheRetention` option not being passed through in `buildBaseOptions` ([#1154](https://github.com/badlogic/pi-mono/issues/1154))
- Fixed OAuth login/refresh not using HTTP proxy settings (`HTTP_PROXY`, `HTTPS_PROXY` env vars) ([#1132](https://github.com/badlogic/pi-mono/issues/1132))
- Fixed `pi update <source>` installing packages locally when the source is only registered globally ([#1163](https://github.com/badlogic/pi-mono/pull/1163) by [@aliou](https://github.com/aliou))
- Fixed tree navigation with summarization overwriting editor content typed during the summarization wait ([#1169](https://github.com/badlogic/pi-mono/pull/1169) by [@aliou](https://github.com/aliou))

## [0.50.9] - 2026-02-01

### Added

- Added `titlebar-spinner.ts` example extension that shows a braille spinner animation in the terminal title while the agent is working.
- Added `PI_AI_ANTIGRAVITY_VERSION` environment variable documentation to help text ([#1129](https://github.com/badlogic/pi-mono/issues/1129))
- Added `cacheRetention` stream option with provider-specific mappings for prompt cache controls, defaulting to short retention ([#1134](https://github.com/badlogic/pi-mono/issues/1134))

## [0.50.8] - 2026-02-01

### Added

- Added `newSession`, `tree`, and `fork` keybinding actions for `/new`, `/tree`, and `/fork` commands. All unbound by default. ([#1114](https://github.com/badlogic/pi-mono/pull/1114) by [@juanibiapina](https://github.com/juanibiapina))
- Added `retry.maxDelayMs` setting to cap maximum server-requested retry delay. When a provider requests a longer delay (e.g., Google's "quota will reset after 5h"), the request fails immediately with an informative error instead of waiting silently. Default: 60000ms (60 seconds). ([#1123](https://github.com/badlogic/pi-mono/issues/1123))
- `/resume` session picker: new "Threaded" sort mode (now default) displays sessions in a tree structure based on fork relationships. Compact one-line format with message count and age on the right. ([#1124](https://github.com/badlogic/pi-mono/pull/1124) by [@pasky](https://github.com/pasky))
- Added Qwen CLI OAuth provider extension example. ([#940](https://github.com/badlogic/pi-mono/pull/940) by [@4h9fbZ](https://github.com/4h9fbZ))
- Added OAuth `modifyModels` hook support for extension-registered providers at registration time. ([#940](https://github.com/badlogic/pi-mono/pull/940) by [@4h9fbZ](https://github.com/4h9fbZ))
- Added Qwen thinking format support for OpenAI-compatible completions via `enable_thinking`. ([#940](https://github.com/badlogic/pi-mono/pull/940) by [@4h9fbZ](https://github.com/4h9fbZ))
- Added sticky column tracking for vertical cursor navigation so the editor restores the preferred column when moving across short lines. ([#1120](https://github.com/badlogic/pi-mono/pull/1120) by [@Perlence](https://github.com/Perlence))
- Added `resources_discover` extension hook to supply additional skills, prompts, and themes on startup and reload.

### Fixed

- Fixed `switchSession()` appending spurious `thinking_level_change` entry to session log on resume. `setThinkingLevel()` is now idempotent. ([#1118](https://github.com/badlogic/pi-mono/issues/1118))
- Fixed clipboard image paste on WSL2/WSLg writing invalid PNG files when clipboard provides `image/bmp` format. BMP images are now converted to PNG before saving. ([#1112](https://github.com/badlogic/pi-mono/pull/1112) by [@lightningRalf](https://github.com/lightningRalf))
- Fixed Kitty keyboard protocol base layout fallback so non-QWERTY layouts do not trigger wrong shortcuts ([#1096](https://github.com/badlogic/pi-mono/pull/1096) by [@rytswd](https://github.com/rytswd))

## [0.50.7] - 2026-01-31

### Fixed

- Multi-file extensions in packages now work correctly. Package resolution now uses the same discovery logic as local extensions: only `index.ts` (or manifest-declared entries) are loaded from subdirectories, not helper modules. ([#1102](https://github.com/badlogic/pi-mono/issues/1102))

## [0.50.6] - 2026-01-30

### Added

- Added `ctx.getSystemPrompt()` to extension context for accessing the current effective system prompt ([#1098](https://github.com/badlogic/pi-mono/pull/1098) by [@kaofelix](https://github.com/kaofelix))

### Fixed

- Fixed empty rows appearing below footer when content shrinks (e.g., closing `/tree`, clearing multi-line editor) ([#1095](https://github.com/badlogic/pi-mono/pull/1095) by [@marckrenn](https://github.com/marckrenn))
- Fixed terminal cursor remaining hidden after exiting TUI via `stop()` when a render was pending ([#1099](https://github.com/badlogic/pi-mono/pull/1099) by [@haoqixu](https://github.com/haoqixu))

## [0.50.5] - 2026-01-30

## [0.50.4] - 2026-01-30

### New Features

- **OSC 52 clipboard support for SSH/mosh** - The `/copy` command now works over remote connections using the OSC 52 terminal escape sequence. No more clipboard frustration when using pi over SSH. ([#1069](https://github.com/badlogic/pi-mono/issues/1069) by [@gturkoglu](https://github.com/gturkoglu))
- **Vercel AI Gateway routing** - Route requests through Vercel's AI Gateway with provider failover and load balancing. Configure via `vercelGatewayRouting` in models.json. ([#1051](https://github.com/badlogic/pi-mono/pull/1051) by [@ben-vargas](https://github.com/ben-vargas))
- **Character jump navigation** - Bash/Readline-style character search: Ctrl+] jumps forward to the next occurrence of a character, Ctrl+Alt+] jumps backward. ([#1074](https://github.com/badlogic/pi-mono/pull/1074) by [@Perlence](https://github.com/Perlence))
- **Emacs-style Ctrl+B/Ctrl+F navigation** - Alternative keybindings for word navigation (cursor word left/right) in the editor. ([#1053](https://github.com/badlogic/pi-mono/pull/1053) by [@ninlds](https://github.com/ninlds))
- **Line boundary navigation** - Editor jumps to line start when pressing Up at first visual line, and line end when pressing Down at last visual line. ([#1050](https://github.com/badlogic/pi-mono/pull/1050) by [@4h9fbZ](https://github.com/4h9fbZ))
- **Performance improvements** - Optimized image line detection and box rendering cache in the TUI for better rendering performance. ([#1084](https://github.com/badlogic/pi-mono/pull/1084) by [@can1357](https://github.com/can1357))
- **`set_session_name` RPC command** - Headless clients can now set the session display name programmatically. ([#1075](https://github.com/badlogic/pi-mono/pull/1075) by [@dnouri](https://github.com/dnouri))
- **Disable double-escape behavior** - New `"none"` option for `doubleEscapeAction` setting completely disables the double-escape shortcut. ([#973](https://github.com/badlogic/pi-mono/issues/973) by [@juanibiapina](https://github.com/juanibiapina))

### Added

- Added "none" option to `doubleEscapeAction` setting to disable double-escape behavior entirely ([#973](https://github.com/badlogic/pi-mono/issues/973) by [@juanibiapina](https://github.com/juanibiapina))
- Added OSC 52 clipboard support for SSH/mosh sessions. `/copy` now works over remote connections. ([#1069](https://github.com/badlogic/pi-mono/issues/1069) by [@gturkoglu](https://github.com/gturkoglu))
- Added Vercel AI Gateway routing support via `vercelGatewayRouting` in models.json ([#1051](https://github.com/badlogic/pi-mono/pull/1051) by [@ben-vargas](https://github.com/ben-vargas))
- Added Ctrl+B and Ctrl+F keybindings for cursor word left/right navigation in the editor ([#1053](https://github.com/badlogic/pi-mono/pull/1053) by [@ninlds](https://github.com/ninlds))
- Added character jump navigation: Ctrl+] jumps forward to next character, Ctrl+Alt+] jumps backward ([#1074](https://github.com/badlogic/pi-mono/pull/1074) by [@Perlence](https://github.com/Perlence))
- Editor now jumps to line start when pressing Up at first visual line, and line end when pressing Down at last visual line ([#1050](https://github.com/badlogic/pi-mono/pull/1050) by [@4h9fbZ](https://github.com/4h9fbZ))
- Optimized image line detection and box rendering cache for better TUI performance ([#1084](https://github.com/badlogic/pi-mono/pull/1084) by [@can1357](https://github.com/can1357))
- Added `set_session_name` RPC command for headless clients to set session display name ([#1075](https://github.com/badlogic/pi-mono/pull/1075) by [@dnouri](https://github.com/dnouri))

### Fixed

- Read tool now handles macOS filenames with curly quotes (U+2019) and NFD Unicode normalization ([#1078](https://github.com/badlogic/pi-mono/issues/1078))
- Respect .gitignore, .ignore, and .fdignore files when scanning package resources for skills, prompts, themes, and extensions ([#1072](https://github.com/badlogic/pi-mono/issues/1072))
- Fixed tool call argument defaults when providers omit inputs ([#1065](https://github.com/badlogic/pi-mono/issues/1065))
- Invalid JSON in settings.json no longer causes the file to be overwritten with empty settings ([#1054](https://github.com/badlogic/pi-mono/issues/1054))
- Config selector now shows folder name for extensions with duplicate display names ([#1064](https://github.com/badlogic/pi-mono/pull/1064) by [@Graffioh](https://github.com/Graffioh))

## [0.50.3] - 2026-01-29

### New Features

- **Kimi For Coding provider**: Access Moonshot AI's Anthropic-compatible coding API. Set `KIMI_API_KEY` environment variable. See [README.md#kimi-for-coding](README.md#kimi-for-coding).

### Added

- Added Kimi For Coding provider support (Moonshot AI's Anthropic-compatible coding API). Set `KIMI_API_KEY` environment variable. See [README.md#kimi-for-coding](README.md#kimi-for-coding).

### Fixed

- Resources now appear before messages when resuming a session, preventing loaded context from appearing at the bottom of the chat.

## [0.50.2] - 2026-01-29

### New Features

- **Hugging Face provider**: Access Hugging Face models via OpenAI-compatible Inference Router. Set `HF_TOKEN` environment variable. See [README.md#hugging-face](README.md#hugging-face).
- **Extended prompt caching**: `PI_CACHE_RETENTION=long` enables 1-hour caching for Anthropic (vs 5min default) and 24-hour for OpenAI (vs in-memory default). Only applies to direct API calls. See [README.md#prompt-caching](README.md#prompt-caching).
- **Configurable autocomplete height**: `autocompleteMaxVisible` setting (3-20 items, default 5) controls dropdown size. Adjust via `/settings` or `settings.json`.
- **Shell-style keybindings**: `alt+b`/`alt+f` for word navigation, `ctrl+d` for delete character forward. See [docs/keybindings.md](docs/keybindings.md).
- **RPC `get_commands`**: Headless clients can now list available commands programmatically. See [docs/rpc.md](docs/rpc.md).

### Added

- Added Hugging Face provider support via OpenAI-compatible Inference Router ([#994](https://github.com/badlogic/pi-mono/issues/994))
- Added `PI_CACHE_RETENTION` environment variable to control cache TTL for Anthropic (5m vs 1h) and OpenAI (in-memory vs 24h). Set to `long` for extended retention. ([#967](https://github.com/badlogic/pi-mono/issues/967))
- Added `autocompleteMaxVisible` setting for configurable autocomplete dropdown height (3-20 items, default 5) ([#972](https://github.com/badlogic/pi-mono/pull/972) by [@masonc15](https://github.com/masonc15))
- Added `/files` command to list all file operations (read, write, edit) in the current session
- Added shell-style keybindings: `alt+b`/`alt+f` for word navigation, `ctrl+d` for delete character forward (when editor has text) ([#1043](https://github.com/badlogic/pi-mono/issues/1043) by [@jasonish](https://github.com/jasonish))
- Added `get_commands` RPC method for headless clients to list available commands ([#995](https://github.com/badlogic/pi-mono/pull/995) by [@dnouri](https://github.com/dnouri))

### Changed

- Improved `extractCursorPosition` performance in TUI: scans lines in reverse order, early-outs when cursor is above viewport ([#1004](https://github.com/badlogic/pi-mono/pull/1004) by [@can1357](https://github.com/can1357))
- Autocomplete improvements: better handling of partial matches and edge cases ([#1024](https://github.com/badlogic/pi-mono/pull/1024) by [@Perlence](https://github.com/Perlence))

### Fixed

- External edits to `settings.json` are now preserved when pi reloads or saves unrelated settings. Previously, editing settings.json directly (e.g., removing a package from `packages` array) would be silently reverted on next pi startup when automatic setters like `setLastChangelogVersion()` triggered a save.
- Fixed custom header not displaying correctly with `quietStartup` enabled ([#1039](https://github.com/badlogic/pi-mono/pull/1039) by [@tudoroancea](https://github.com/tudoroancea))
- Empty array in package filter now disables all resources instead of falling back to manifest defaults ([#1044](https://github.com/badlogic/pi-mono/issues/1044))
- Auto-retry counter now resets after each successful LLM response instead of accumulating across tool-use turns ([#1019](https://github.com/badlogic/pi-mono/issues/1019))
- Fixed incorrect `.md` file names in warning messages ([#1041](https://github.com/badlogic/pi-mono/issues/1041) by [@llimllib](https://github.com/llimllib))
- Fixed provider name hidden in footer when terminal is narrow ([#981](https://github.com/badlogic/pi-mono/pull/981) by [@Perlence](https://github.com/Perlence))
- Fixed backslash input buffering causing delayed character display in editor ([#1037](https://github.com/badlogic/pi-mono/pull/1037) by [@Perlence](https://github.com/Perlence))
- Fixed markdown table rendering with proper row dividers and minimum column width ([#997](https://github.com/badlogic/pi-mono/pull/997) by [@tmustier](https://github.com/tmustier))
- Fixed OpenAI completions `toolChoice` handling ([#998](https://github.com/badlogic/pi-mono/pull/998) by [@williamtwomey](https://github.com/williamtwomey))
- Fixed cross-provider handoff failing when switching from OpenAI Responses API providers due to pipe-separated tool call IDs ([#1022](https://github.com/badlogic/pi-mono/issues/1022))
- Fixed 429 rate limit errors incorrectly triggering auto-compaction instead of retry with backoff ([#1038](https://github.com/badlogic/pi-mono/issues/1038))
- Fixed Anthropic provider to handle `sensitive` stop_reason returned by API ([#978](https://github.com/badlogic/pi-mono/issues/978))
- Fixed DeepSeek API compatibility by detecting `deepseek.com` URLs and disabling unsupported `developer` role ([#1048](https://github.com/badlogic/pi-mono/issues/1048))
- Fixed Anthropic provider to preserve input token counts when proxies omit them in `message_delta` events ([#1045](https://github.com/badlogic/pi-mono/issues/1045))
- Fixed `autocompleteMaxVisible` setting not persisting to `settings.json`

## [0.50.1] - 2026-01-26

### Fixed

- Git extension updates now handle force-pushed remotes gracefully instead of failing ([#961](https://github.com/badlogic/pi-mono/pull/961) by [@aliou](https://github.com/aliou))
- Extension `ctx.newSession({ setup })` now properly syncs agent state and renders messages after setup callback runs ([#968](https://github.com/badlogic/pi-mono/issues/968))
- Fixed extension UI bindings not initializing when starting with no extensions, which broke UI methods after `/reload`
- Fixed `/hotkeys` output to title-case extension hotkeys ([#969](https://github.com/badlogic/pi-mono/pull/969) by [@Perlence](https://github.com/Perlence))
- Fixed model catalog generation to exclude deprecated OpenCode Zen models ([#970](https://github.com/badlogic/pi-mono/pull/970) by [@DanielTatarkin](https://github.com/DanielTatarkin))
- Fixed git extension removal to prune empty directories

## [0.50.0] - 2026-01-26

### New Features

- Pi packages for bundling and installing extensions, skills, prompts, and themes. See [docs/packages.md](docs/packages.md).
- Hot reload (`/reload`) of resources including AGENTS.md, SYSTEM.md, APPEND_SYSTEM.md, prompt templates, skills, themes, and extensions. See [README.md#commands](README.md#commands) and [README.md#context-files](README.md#context-files).
- Custom providers via `pi.registerProvider()` for proxies, custom endpoints, OAuth or SSO flows, and non-standard streaming APIs. See [docs/custom-provider.md](docs/custom-provider.md).
- Azure OpenAI Responses provider support with deployment-aware model mapping. See [docs/providers.md#azure-openai](docs/providers.md#azure-openai).
- OpenRouter routing support for custom models via `openRouterRouting`. See [docs/providers.md#api-keys](docs/providers.md#api-keys) and [docs/models.md](docs/models.md).
- Skill invocation messages are now collapsible and skills can opt out of model invocation via `disable-model-invocation`. See [docs/skills.md#frontmatter](docs/skills.md#frontmatter).
- Session selector renaming and configurable keybindings. See [README.md#commands](README.md#commands) and [docs/keybindings.md](docs/keybindings.md).
- `models.json` headers can resolve environment variables and shell commands. See [docs/models.md#value-resolution](docs/models.md#value-resolution).
- `--verbose` CLI flag to override quiet startup. See [README.md#cli-reference](README.md#cli-reference).

Read the fully revamped docs in `README.md`, or have your clanker read them for you.

### SDK Migration Guide

There are multiple SDK breaking changes since v0.49.3. For the quickest migration, point your agent at `packages/coding-agent/docs/sdk.md`, the SDK examples in `packages/coding-agent/examples/sdk`, and the SDK source in `packages/coding-agent/src/core/sdk.ts` and related modules.

### Breaking Changes

- Header values in `models.json` now resolve environment variables (if a header value matches an env var name, the env var value is used). This may change behavior if a literal header value accidentally matches an env var name. ([#909](https://github.com/badlogic/pi-mono/issues/909))
- External packages (npm/git) are now configured via `packages` array in settings.json instead of `extensions`. Existing npm:/git: entries in `extensions` are auto-migrated. ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Resource loading now uses `ResourceLoader` only and settings.json uses arrays for extensions, skills, prompts, and themes ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Removed `discoverAuthStorage` and `discoverModels` from the SDK. `AuthStorage` and `ModelRegistry` now default to `~/.pi/agent` paths unless you pass an `agentDir` ([#645](https://github.com/badlogic/pi-mono/issues/645))

### Added

- Session renaming in `/resume` picker via `Ctrl+R` without opening the session ([#863](https://github.com/badlogic/pi-mono/pull/863) by [@svkozak](https://github.com/svkozak))
- Session selector keybindings are now configurable ([#948](https://github.com/badlogic/pi-mono/pull/948) by [@aos](https://github.com/aos))
- `disable-model-invocation` frontmatter field for skills to prevent agentic invocation while still allowing explicit `/skill:name` commands ([#927](https://github.com/badlogic/pi-mono/issues/927))
- Exposed `copyToClipboard` utility for extensions ([#926](https://github.com/badlogic/pi-mono/issues/926) by [@mitsuhiko](https://github.com/mitsuhiko))
- Skill invocation messages are now collapsible in chat output, showing collapsed by default with skill name and expand hint ([#894](https://github.com/badlogic/pi-mono/issues/894))
- Header values in `models.json` now support environment variables and shell commands, matching `apiKey` resolution ([#909](https://github.com/badlogic/pi-mono/issues/909))
- Added HTTP proxy environment variable support for API requests ([#942](https://github.com/badlogic/pi-mono/pull/942) by [@haoqixu](https://github.com/haoqixu))
- Added OpenRouter provider routing support for custom models via `openRouterRouting` compat field ([#859](https://github.com/badlogic/pi-mono/pull/859) by [@v01dpr1mr0s3](https://github.com/v01dpr1mr0s3))
- Added `azure-openai-responses` provider support for Azure OpenAI Responses API. ([#890](https://github.com/badlogic/pi-mono/pull/890) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- Added changelog link to update notifications ([#925](https://github.com/badlogic/pi-mono/pull/925) by [@dannote](https://github.com/dannote))
- Added `--verbose` CLI flag to override quietStartup setting ([#906](https://github.com/badlogic/pi-mono/pull/906) by [@Perlence](https://github.com/Perlence))
- `markdown.codeBlockIndent` setting to customize code block indentation in rendered output
- Extension package management with `pi install`, `pi remove`, `pi update`, and `pi list` commands ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Package filtering: selectively load resources from packages using object form in `packages` array ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Glob pattern support with minimatch in package filters, top-level settings arrays, and pi manifest (e.g., `"!funky.json"`, `"*.ts"`) ([#645](https://github.com/badlogic/pi-mono/issues/645))
- `/reload` command to reload extensions, skills, prompts, and themes ([#645](https://github.com/badlogic/pi-mono/issues/645))
- `pi config` command with TUI to enable/disable package and top-level resources via patterns ([#938](https://github.com/badlogic/pi-mono/issues/938))
- CLI flags for `--skill`, `--prompt-template`, `--theme`, `--no-prompt-templates`, and `--no-themes` ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Package deduplication: if same package appears in global and project settings, project wins ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Unified collision reporting with `ResourceDiagnostic` type for all resource types ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Show provider alongside the model in the footer if multiple providers are available
- Custom provider support via `pi.registerProvider()` with `streamSimple` for custom API implementations
- Added `custom-provider.ts` example extension demonstrating custom Anthropic provider with OAuth

### Changed

- `/resume` picker sort toggle moved to `Ctrl+S` to free `Ctrl+R` for rename ([#863](https://github.com/badlogic/pi-mono/pull/863) by [@svkozak](https://github.com/svkozak))
- HTML export: clicking a sidebar message now navigates to its newest leaf and scrolls to it, instead of truncating the branch ([#853](https://github.com/badlogic/pi-mono/pull/853) by [@mitsuhiko](https://github.com/mitsuhiko))
- HTML export: active path is now visually highlighted with dimmed off-path nodes ([#929](https://github.com/badlogic/pi-mono/pull/929) by [@hewliyang](https://github.com/hewliyang))
- Azure OpenAI Responses provider now uses base URL configuration with deployment-aware model mapping and no longer includes service tier handling
- `/reload` now re-renders the entire scrollback so updated extension components are visible immediately ([#928](https://github.com/badlogic/pi-mono/pull/928) by [@ferologics](https://github.com/ferologics))
- Skill, prompt template, and theme discovery now use settings and CLI path arrays instead of legacy filters ([#645](https://github.com/badlogic/pi-mono/issues/645))

### Fixed

- Extension `setWorkingMessage()` calls in `agent_start` handlers now work correctly; previously the message was silently ignored because the loading animation didn't exist yet ([#935](https://github.com/badlogic/pi-mono/issues/935))
- Fixed package auto-discovery to respect loader rules, config overrides, and force-exclude patterns
- Fixed /reload restoring the correct editor after reload ([#949](https://github.com/badlogic/pi-mono/pull/949) by [@Perlence](https://github.com/Perlence))
- Fixed distributed themes breaking `/export` ([#946](https://github.com/badlogic/pi-mono/pull/946) by [@mitsuhiko](https://github.com/mitsuhiko))
- Fixed startup hints to clarify thinking level selection and expanded thinking guidance
- Fixed SDK initial model resolution to use `findInitialModel` and default to Claude Opus 4.5 for Anthropic models
- Fixed no-models warning to include the `/model` instruction
- Fixed authentication error messages to point to the authentication documentation
- Fixed bash output hint lines to truncate to terminal width
- Fixed custom editors to honor the `paddingX` setting ([#936](https://github.com/badlogic/pi-mono/pull/936) by [@Perlence](https://github.com/Perlence))
- Fixed system prompt tool list to show only built-in tools
- Fixed package manager to check npm package versions before using cached copies
- Fixed package manager to run `npm install` after cloning git repositories with a package.json
- Fixed extension provider registrations to apply before model resolution
- Fixed editor multi-line insertion handling and lastAction tracking ([#945](https://github.com/badlogic/pi-mono/pull/945) by [@Perlence](https://github.com/Perlence))
- Fixed editor word wrapping to reserve a cursor column ([#934](https://github.com/badlogic/pi-mono/pull/934) by [@Perlence](https://github.com/Perlence))
- Fixed editor word wrapping to use single-pass backtracking for whitespace handling ([#924](https://github.com/badlogic/pi-mono/pull/924) by [@Perlence](https://github.com/Perlence))
- Fixed Kitty image ID allocation and cleanup to prevent image ID collisions
- Fixed overlays staying centered after terminal resizes ([#950](https://github.com/badlogic/pi-mono/pull/950) by [@nicobailon](https://github.com/nicobailon))
- Fixed streaming dispatch to use the model api type instead of hardcoded API defaults
- Fixed Google providers to default tool call arguments to an empty object when omitted
- Fixed OpenAI Responses streaming to handle `arguments.done` events on OpenAI-compatible endpoints ([#917](https://github.com/badlogic/pi-mono/pull/917) by [@williballenthin](https://github.com/williballenthin))
- Fixed OpenAI Codex Responses tool strictness handling after the shared responses refactor
- Fixed Azure OpenAI Responses streaming to guard deltas before content parts and correct metadata and handoff gating
- Fixed OpenAI completions tool-result image batching after consecutive tool results ([#902](https://github.com/badlogic/pi-mono/pull/902) by [@terrorobe](https://github.com/terrorobe))
- Off-by-one error in bash output "earlier lines" count caused by counting spacing newline as hidden content ([#921](https://github.com/badlogic/pi-mono/issues/921))
- User package filters now layer on top of manifest filters instead of replacing them ([#645](https://github.com/badlogic/pi-mono/issues/645))
- Auto-retry now handles "terminated" errors from Codex API mid-stream failures
- Follow-up queue (Alt+Enter) now sends full paste content instead of `[paste #N ...]` markers ([#912](https://github.com/badlogic/pi-mono/issues/912))
- Fixed Alt-Up not restoring messages queued during compaction ([#923](https://github.com/badlogic/pi-mono/pull/923) by [@aliou](https://github.com/aliou))
- Fixed session corruption when loading empty or invalid session files via `--session` flag ([#932](https://github.com/badlogic/pi-mono/issues/932) by [@armanddp](https://github.com/armanddp))
- Fixed extension shortcuts not firing when extension also uses `setEditorComponent()` ([#947](https://github.com/badlogic/pi-mono/pull/947) by [@Perlence](https://github.com/Perlence))
- Session "modified" time now uses last message timestamp instead of file mtime, so renaming doesn't reorder the recent list ([#863](https://github.com/badlogic/pi-mono/pull/863) by [@svkozak](https://github.com/svkozak))

## [0.49.3] - 2026-01-22

### Added

- `markdown.codeBlockIndent` setting to customize code block indentation in rendered output ([#855](https://github.com/badlogic/pi-mono/pull/855) by [@terrorobe](https://github.com/terrorobe))
- Added `inline-bash.ts` example extension for expanding `!{command}` patterns in prompts ([#881](https://github.com/badlogic/pi-mono/pull/881) by [@scutifer](https://github.com/scutifer))
- Added `antigravity-image-gen.ts` example extension for AI image generation via Google Antigravity ([#893](https://github.com/badlogic/pi-mono/pull/893) by [@ben-vargas](https://github.com/ben-vargas))
- Added `PI_SHARE_VIEWER_URL` environment variable for custom share viewer URLs ([#889](https://github.com/badlogic/pi-mono/pull/889) by [@andresaraujo](https://github.com/andresaraujo))
- Added Alt+Delete as hotkey for delete word forwards ([#878](https://github.com/badlogic/pi-mono/pull/878) by [@Perlence](https://github.com/Perlence))

### Changed

- Tree selector: changed label filter shortcut from `l` to `Shift+L` so users can search for entries containing "l" ([#861](https://github.com/badlogic/pi-mono/pull/861) by [@mitsuhiko](https://github.com/mitsuhiko))
- Fuzzy matching now scores consecutive matches higher for better search relevance ([#860](https://github.com/badlogic/pi-mono/pull/860) by [@mitsuhiko](https://github.com/mitsuhiko))

### Fixed

- Fixed error messages showing hardcoded `~/.pi/agent/` paths instead of respecting `PI_CODING_AGENT_DIR` ([#887](https://github.com/badlogic/pi-mono/pull/887) by [@aliou](https://github.com/aliou))
- Fixed `write` tool not displaying errors in the UI when execution fails ([#856](https://github.com/badlogic/pi-mono/issues/856))
- Fixed HTML export using default theme instead of user's active theme ([#870](https://github.com/badlogic/pi-mono/pull/870) by [@scutifer](https://github.com/scutifer))
- Show session name in the footer and terminal / tab title ([#876](https://github.com/badlogic/pi-mono/pull/876) by [@scutifer](https://github.com/scutifer))
- Fixed 256color fallback in Terminal.app to prevent color rendering issues ([#869](https://github.com/badlogic/pi-mono/pull/869) by [@Perlence](https://github.com/Perlence))
- Fixed viewport tracking and cursor positioning for overlays and content shrink scenarios
- Fixed autocomplete to allow searches with `/` characters (e.g., `folder1/folder2`) ([#882](https://github.com/badlogic/pi-mono/pull/882) by [@richardgill](https://github.com/richardgill))
- Fixed autolinked emails displaying redundant `(mailto:...)` suffix ([#888](https://github.com/badlogic/pi-mono/pull/888) by [@terrorobe](https://github.com/terrorobe))
- Fixed `@` file autocomplete adding space after directories, breaking continued autocomplete into subdirectories

## [0.49.2] - 2026-01-19

### Added

- Added widget placement option for extension widgets via `widgetPlacement` in `pi.addWidget()` ([#850](https://github.com/badlogic/pi-mono/pull/850) by [@marckrenn](https://github.com/marckrenn))
- Added AWS credential detection for ECS/Kubernetes environments: `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`, `AWS_CONTAINER_CREDENTIALS_FULL_URI`, `AWS_WEB_IDENTITY_TOKEN_FILE` ([#848](https://github.com/badlogic/pi-mono/issues/848))
- Add "quiet startup" setting to `/settings` ([#847](https://github.com/badlogic/pi-mono/pull/847) by [@unexge](https://github.com/unexge))

### Changed

- HTML export now includes JSONL download button, jump-to-last-message on click, and fixed missing labels ([#853](https://github.com/badlogic/pi-mono/pull/853) by [@mitsuhiko](https://github.com/mitsuhiko))
- Improved error message for OAuth authentication failures (expired credentials, offline) instead of generic 'No API key found' ([#849](https://github.com/badlogic/pi-mono/pull/849) by [@zedrdave](https://github.com/zedrdave))

### Fixed
- Fixed `/model` selector scope toggle so you can switch between all and scoped models when scoped models are saved ([#844](https://github.com/badlogic/pi-mono/issues/844))
- Fixed OpenAI Responses 400 error "reasoning without following item" when replaying aborted turns ([#838](https://github.com/badlogic/pi-mono/pull/838))
- Fixed pi exiting with code 0 when cancelling resume session selection

### Removed

- Removed `strictResponsesPairing` compat option from models.json schema (no longer needed)

## [0.49.1] - 2026-01-18

### Added

- Added `strictResponsesPairing` compat option for custom OpenAI Responses models on Azure ([#768](https://github.com/badlogic/pi-mono/pull/768) by [@prateekmedia](https://github.com/prateekmedia))
- Session selector (`/resume`) now supports path display toggle (`Ctrl+P`) and session deletion (`Ctrl+D`) with inline confirmation ([#816](https://github.com/badlogic/pi-mono/pull/816) by [@w-winter](https://github.com/w-winter))
- Added undo support in interactive mode with Ctrl+- hotkey. ([#831](https://github.com/badlogic/pi-mono/pull/831) by [@Perlence](https://github.com/Perlence))

### Changed

- Share URLs now use hash fragments (`#`) instead of query strings (`?`) to prevent session IDs from being sent to buildwithpi.ai ([#829](https://github.com/badlogic/pi-mono/pull/829) by [@terrorobe](https://github.com/terrorobe))
- API keys in `models.json` can now be retrieved via shell command using `!` prefix (e.g., `"apiKey": "!security find-generic-password -ws 'anthropic'"` for macOS Keychain) ([#762](https://github.com/badlogic/pi-mono/pull/762) by [@cv](https://github.com/cv))

### Fixed

- Fixed IME candidate window appearing in wrong position when filtering menus with Input Method Editor (e.g., Chinese IME). Components with search inputs now properly propagate focus state for cursor positioning. ([#827](https://github.com/badlogic/pi-mono/issues/827))
- Fixed extension shortcut conflicts to respect user keybindings when built-in actions are remapped. ([#826](https://github.com/badlogic/pi-mono/pull/826) by [@richardgill](https://github.com/richardgill))
- Fixed photon WASM loading in standalone compiled binaries.
- Fixed tool call ID normalization for cross-provider handoffs (e.g., Codex to Antigravity Claude) ([#821](https://github.com/badlogic/pi-mono/issues/821))

## [0.49.0] - 2026-01-17

### Added

- `pi.setLabel(entryId, label)` in ExtensionAPI for setting per-entry labels from extensions ([#806](https://github.com/badlogic/pi-mono/issues/806))
- Export `keyHint`, `appKeyHint`, `editorKey`, `appKey`, `rawKeyHint` for extensions to format keybinding hints consistently ([#802](https://github.com/badlogic/pi-mono/pull/802) by [@dannote](https://github.com/dannote))
- Exported `VERSION` from the package index and updated the custom-header example. ([#798](https://github.com/badlogic/pi-mono/pull/798) by [@tallshort](https://github.com/tallshort))
- Added `showHardwareCursor` setting to control cursor visibility while still positioning it for IME support. ([#800](https://github.com/badlogic/pi-mono/pull/800) by [@ghoulr](https://github.com/ghoulr))
- Added Emacs-style kill ring editing with yank and yank-pop keybindings, plus legacy Alt+letter handling and Alt+D delete word forward support in the interactive editor. ([#810](https://github.com/badlogic/pi-mono/pull/810) by [@Perlence](https://github.com/Perlence))
- Added `ctx.compact()` and `ctx.getContextUsage()` to extension contexts for programmatic compaction and context usage checks.
- Added documentation for delete word forward and kill ring keybindings in interactive mode. ([#810](https://github.com/badlogic/pi-mono/pull/810) by [@Perlence](https://github.com/Perlence))

### Changed

- Updated the default system prompt wording to clarify the pi harness and documentation scope.
- Simplified Codex system prompt handling to use the default system prompt directly for Codex instructions.

### Fixed

- Fixed photon module failing to load in ESM context with "require is not defined" error ([#795](https://github.com/badlogic/pi-mono/pull/795) by [@dannote](https://github.com/dannote))
- Fixed compaction UI not showing when extensions trigger compaction.
- Fixed orphaned tool results after errored assistant messages causing Codex API errors. When an assistant message has `stopReason: "error"`, its tool calls are now excluded from pending tool tracking, preventing synthetic tool results from being generated for calls that will be dropped by provider-specific converters. ([#812](https://github.com/badlogic/pi-mono/issues/812))
- Fixed Bedrock Claude max_tokens handling to always exceed thinking budget tokens, preventing compaction failures. ([#797](https://github.com/badlogic/pi-mono/pull/797) by [@pjtf93](https://github.com/pjtf93))
- Fixed Claude Code tool name normalization to match the Claude Code tool list case-insensitively and remove invalid mappings.

### Removed

- Removed `pi-internal://` path resolution from the read tool.

## [0.48.0] - 2026-01-16

### Added

- Added `quietStartup` setting to silence startup output (version header, loaded context info, model scope line). Changelog notifications are still shown. ([#777](https://github.com/badlogic/pi-mono/pull/777) by [@ribelo](https://github.com/ribelo))
- Added `editorPaddingX` setting for horizontal padding in input editor (0-3, default: 0)
- Added `shellCommandPrefix` setting to prepend commands to every bash execution, enabling alias expansion in non-interactive shells (e.g., `"shellCommandPrefix": "shopt -s expand_aliases"`) ([#790](https://github.com/badlogic/pi-mono/pull/790) by [@richardgill](https://github.com/richardgill))
- Added bash-style argument slicing for prompt templates ([#770](https://github.com/badlogic/pi-mono/pull/770) by [@airtonix](https://github.com/airtonix))
- Extension commands can provide argument auto-completions via `getArgumentCompletions` in `pi.registerCommand()` ([#775](https://github.com/badlogic/pi-mono/pull/775) by [@ribelo](https://github.com/ribelo))
- Bash tool now displays the timeout value in the UI when a timeout is set ([#780](https://github.com/badlogic/pi-mono/pull/780) by [@dannote](https://github.com/dannote))
- Export `getShellConfig` for extensions to detect user's shell environment ([#766](https://github.com/badlogic/pi-mono/pull/766) by [@dannote](https://github.com/dannote))
- Added `thinkingText` and `selectedBg` to theme schema ([#763](https://github.com/badlogic/pi-mono/pull/763) by [@scutifer](https://github.com/scutifer))
- `navigateTree()` now supports `replaceInstructions` option to replace the default summarization prompt entirely, and `label` option to attach a label to the branch summary entry ([#787](https://github.com/badlogic/pi-mono/pull/787) by [@mitsuhiko](https://github.com/mitsuhiko))

### Fixed

- Fixed crash during auto-compaction when summarization fails (e.g., quota exceeded). Now displays error message instead of crashing ([#792](https://github.com/badlogic/pi-mono/issues/792))
- Fixed `--session <UUID>` to search globally across projects if not found locally, with option to fork sessions from other projects ([#785](https://github.com/badlogic/pi-mono/pull/785) by [@ribelo](https://github.com/ribelo))
- Fixed standalone binary WASM loading on Linux ([#784](https://github.com/badlogic/pi-mono/issues/784))
- Fixed string numbers in tool arguments not being coerced to numbers during validation ([#786](https://github.com/badlogic/pi-mono/pull/786) by [@dannote](https://github.com/dannote))
- Fixed `--no-extensions` flag not preventing extension discovery ([#776](https://github.com/badlogic/pi-mono/issues/776))
- Fixed extension messages rendering twice on startup when `pi.sendMessage({ display: true })` is called during `session_start` ([#765](https://github.com/badlogic/pi-mono/pull/765) by [@dannote](https://github.com/dannote))
- Fixed `PI_CODING_AGENT_DIR` env var not expanding tilde (`~`) to home directory ([#778](https://github.com/badlogic/pi-mono/pull/778) by [@aliou](https://github.com/aliou))
- Fixed session picker hint text overflow ([#764](https://github.com/badlogic/pi-mono/issues/764))
- Fixed Kitty keyboard protocol shifted symbol keys (e.g., `@`, `?`) not working in editor ([#779](https://github.com/badlogic/pi-mono/pull/779) by [@iamd3vil](https://github.com/iamd3vil))
- Fixed Bedrock tool call IDs causing API errors from invalid characters ([#781](https://github.com/badlogic/pi-mono/pull/781) by [@pjtf93](https://github.com/pjtf93))

### Changed

- Hardware cursor is now disabled by default for better terminal compatibility. Set `PI_HARDWARE_CURSOR=1` to enable (replaces `PI_NO_HARDWARE_CURSOR=1` which disabled it).

## [0.47.0] - 2026-01-16

### Breaking Changes

- Extensions using `Editor` directly must now pass `TUI` as the first constructor argument: `new Editor(tui, theme)`. The `tui` parameter is available in extension factory functions. ([#732](https://github.com/badlogic/pi-mono/issues/732))

### Added

- **OpenAI Codex official support**: Full compatibility with OpenAI's Codex CLI models (`gpt-5.1`, `gpt-5.2`, `gpt-5.1-codex-mini`, `gpt-5.2-codex`). Features include static system prompt for OpenAI allowlisting, prompt caching via session ID, and reasoning signature retention across turns. Set `OPENAI_API_KEY` and use `--provider openai-codex` or select a Codex model. ([#737](https://github.com/badlogic/pi-mono/pull/737))
- `pi-internal://` URL scheme in read tool for accessing internal documentation. The model can read files from the coding-agent package (README, docs, examples) to learn about extending pi.
- New `input` event in extension system for intercepting, transforming, or handling user input before the agent processes it. Supports three result types: `continue` (pass through), `transform` (modify text/images), `handled` (respond without LLM). Handlers chain transforms and short-circuit on handled. ([#761](https://github.com/badlogic/pi-mono/pull/761) by [@nicobailon](https://github.com/nicobailon))
- Extension example: `input-transform.ts` demonstrating input interception patterns (quick mode, instant commands, source routing) ([#761](https://github.com/badlogic/pi-mono/pull/761) by [@nicobailon](https://github.com/nicobailon))
- Custom tool HTML export: extensions with `renderCall`/`renderResult` now render in `/share` and `/export` output with ANSI-to-HTML color conversion ([#702](https://github.com/badlogic/pi-mono/pull/702) by [@aliou](https://github.com/aliou))
- Direct filter shortcuts in Tree mode: Ctrl+D (default), Ctrl+T (no-tools), Ctrl+U (user-only), Ctrl+L (labeled-only), Ctrl+A (all) ([#747](https://github.com/badlogic/pi-mono/pull/747) by [@kaofelix](https://github.com/kaofelix))

### Changed

- Skill commands (`/skill:name`) are now expanded in AgentSession instead of interactive mode. This enables skill commands in RPC and print modes, and allows the `input` event to intercept `/skill:name` before expansion.

### Fixed

- Editor no longer corrupts terminal display when loading large prompts via `setEditorText`. Content now scrolls vertically with indicators showing lines above/below the viewport. ([#732](https://github.com/badlogic/pi-mono/issues/732))
- Piped stdin now works correctly: `echo foo | pi` is equivalent to `pi -p foo`. When stdin is piped, print mode is automatically enabled since interactive mode requires a TTY ([#708](https://github.com/badlogic/pi-mono/issues/708))
- Session tree now preserves branch connectors and indentation when filters hide intermediate entries so descendants attach to the nearest visible ancestor and sibling branches align. Fixed in both TUI and HTML export ([#739](https://github.com/badlogic/pi-mono/pull/739) by [@w-winter](https://github.com/w-winter))
- Added `upstream connect`, `connection refused`, and `reset before headers` patterns to auto-retry error detection ([#733](https://github.com/badlogic/pi-mono/issues/733))
- Multi-line YAML frontmatter in skills and prompt templates now parses correctly. Centralized frontmatter parsing using the `yaml` library. ([#728](https://github.com/badlogic/pi-mono/pull/728) by [@richardgill](https://github.com/richardgill))
- `ctx.shutdown()` now waits for pending UI renders to complete before exiting, ensuring notifications and final output are visible ([#756](https://github.com/badlogic/pi-mono/issues/756))
- OpenAI Codex provider now retries on transient errors (429, 5xx, connection failures) with exponential backoff ([#733](https://github.com/badlogic/pi-mono/issues/733))

## [0.46.0] - 2026-01-15

### Fixed

- Scoped models (`--models` or `enabledModels`) now remember the last selected model across sessions instead of always starting with the first model in the scope ([#736](https://github.com/badlogic/pi-mono/pull/736) by [@ogulcancelik](https://github.com/ogulcancelik))
- Show `bun install` instead of `npm install` in update notification when running under Bun ([#714](https://github.com/badlogic/pi-mono/pull/714) by [@dannote](https://github.com/dannote))
- `/skill` prompts now include the skill path ([#711](https://github.com/badlogic/pi-mono/pull/711) by [@jblwilliams](https://github.com/jblwilliams))
- Use configurable `expandTools` keybinding instead of hardcoded Ctrl+O ([#717](https://github.com/badlogic/pi-mono/pull/717) by [@dannote](https://github.com/dannote))
- Compaction turn prefix summaries now merge correctly ([#738](https://github.com/badlogic/pi-mono/pull/738) by [@vsabavat](https://github.com/vsabavat))
- Avoid unsigned Gemini 3 tool calls ([#741](https://github.com/badlogic/pi-mono/pull/741) by [@roshanasingh4](https://github.com/roshanasingh4))
- Fixed signature support for non-Anthropic models in Amazon Bedrock provider ([#727](https://github.com/badlogic/pi-mono/pull/727) by [@unexge](https://github.com/unexge))
- Keyboard shortcuts (Ctrl+C, Ctrl+D, etc.) now work on non-Latin keyboard layouts (Russian, Ukrainian, Bulgarian, etc.) in terminals supporting Kitty keyboard protocol with alternate key reporting ([#718](https://github.com/badlogic/pi-mono/pull/718) by [@dannote](https://github.com/dannote))

### Added

- Edit tool now uses fuzzy matching as fallback when exact match fails, tolerating trailing whitespace, smart quotes, Unicode dashes, and special spaces ([#713](https://github.com/badlogic/pi-mono/pull/713) by [@dannote](https://github.com/dannote))
- Support `APPEND_SYSTEM.md` to append instructions to the system prompt ([#716](https://github.com/badlogic/pi-mono/pull/716) by [@tallshort](https://github.com/tallshort))
- Session picker search: Ctrl+R toggles sorting between fuzzy match (default) and most recent; supports quoted phrase matching and `re:` regex mode ([#731](https://github.com/badlogic/pi-mono/pull/731) by [@ogulcancelik](https://github.com/ogulcancelik))
- Export `getAgentDir` for extensions ([#749](https://github.com/badlogic/pi-mono/pull/749) by [@dannote](https://github.com/dannote))
- Show loaded prompt templates on startup ([#743](https://github.com/badlogic/pi-mono/pull/743) by [@tallshort](https://github.com/tallshort))
- MiniMax China (`minimax-cn`) provider support ([#725](https://github.com/badlogic/pi-mono/pull/725) by [@tallshort](https://github.com/tallshort))
- `gpt-5.2-codex` models for GitHub Copilot and OpenCode Zen providers ([#734](https://github.com/badlogic/pi-mono/pull/734) by [@aadishv](https://github.com/aadishv))

### Changed

- Replaced `wasm-vips` with `@silvia-odwyer/photon-node` for image processing ([#710](https://github.com/badlogic/pi-mono/pull/710) by [@can1357](https://github.com/can1357))
- Extension example: `plan-mode/` shortcut changed from Shift+P to Ctrl+Alt+P to avoid conflict with typing capital P ([#746](https://github.com/badlogic/pi-mono/pull/746) by [@ferologics](https://github.com/ferologics))
- UI keybinding hints now respect configured keybindings across components ([#724](https://github.com/badlogic/pi-mono/pull/724) by [@dannote](https://github.com/dannote))
- CLI process title is now set to `pi` for easier process identification ([#742](https://github.com/badlogic/pi-mono/pull/742) by [@richardgill](https://github.com/richardgill))

## [0.45.7] - 2026-01-13

### Added

- Exported `highlightCode` and `getLanguageFromPath` for extensions ([#703](https://github.com/badlogic/pi-mono/pull/703) by [@dannote](https://github.com/dannote))

## [0.45.6] - 2026-01-13

### Added

- `ctx.ui.custom()` now accepts `overlayOptions` for overlay positioning and sizing (anchor, margins, offsets, percentages, absolute positioning) ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
- `ctx.ui.custom()` now accepts `onHandle` callback to receive the `OverlayHandle` for controlling overlay visibility ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
- Extension example: `overlay-qa-tests.ts` with 10 commands for testing overlay positioning, animation, and toggle scenarios ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
- Extension example: `doom-overlay/` - DOOM game running as an overlay at 35 FPS (auto-downloads WAD on first run) ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))

## [0.45.5] - 2026-01-13

### Fixed

- Skip changelog display on fresh install (only show on upgrades)

## [0.45.4] - 2026-01-13

### Changed

- Light theme colors adjusted for WCAG AA compliance (4.5:1 contrast ratio against white backgrounds)
- Replaced `sharp` with `wasm-vips` for image processing (resize, PNG conversion). Eliminates native build requirements that caused installation failures on some systems. ([#696](https://github.com/badlogic/pi-mono/issues/696))

### Added

- Extension example: `summarize.ts` for summarizing conversations using custom UI and an external model ([#684](https://github.com/badlogic/pi-mono/pull/684) by [@scutifer](https://github.com/scutifer))
- Extension example: `question.ts` enhanced with custom UI for asking user questions ([#693](https://github.com/badlogic/pi-mono/pull/693) by [@ferologics](https://github.com/ferologics))
- Extension example: `plan-mode/` enhanced with explicit step tracking and progress widget ([#694](https://github.com/badlogic/pi-mono/pull/694) by [@ferologics](https://github.com/ferologics))
- Extension example: `questionnaire.ts` for multi-question input with tab bar navigation ([#695](https://github.com/badlogic/pi-mono/pull/695) by [@ferologics](https://github.com/ferologics))
- Experimental Vercel AI Gateway provider support: set `AI_GATEWAY_API_KEY` and use `--provider vercel-ai-gateway`. Token usage is currently reported incorrectly by Anthropic Messages compatible endpoint. ([#689](https://github.com/badlogic/pi-mono/pull/689) by [@timolins](https://github.com/timolins))

### Fixed

- Fix API key resolution after model switches by using provider argument ([#691](https://github.com/badlogic/pi-mono/pull/691) by [@joshp123](https://github.com/joshp123))
- Fixed z.ai thinking/reasoning: thinking toggle now correctly enables/disables thinking for z.ai models ([#688](https://github.com/badlogic/pi-mono/issues/688))
- Fixed extension loading in compiled Bun binary: extensions with local file imports now work correctly. Updated `@mariozechner/jiti` to v2.6.5 which bundles babel for Bun binary compatibility. ([#681](https://github.com/badlogic/pi-mono/issues/681))
- Fixed theme loading when installed via mise: use wrapper directory in release tarballs for compatibility with mise's `strip_components=1` extraction. ([#681](https://github.com/badlogic/pi-mono/issues/681))

## [0.45.3] - 2026-01-13

## [0.45.2] - 2026-01-13

### Fixed

- Extensions now load correctly in compiled Bun binary using `@mariozechner/jiti` fork with `virtualModules` support. Bundled packages (`@sinclair/typebox`, `@mariozechner/pi-tui`, `@mariozechner/pi-ai`, `@bastani/atomic`) are accessible to extensions without filesystem node_modules.

## [0.45.1] - 2026-01-13

### Changed

- `/share` now outputs `buildwithpi.ai` session preview URLs instead of `pi.dev`

## [0.45.0] - 2026-01-13

### Added

- MiniMax provider support: set `MINIMAX_API_KEY` and use `minimax/MiniMax-M2.1` ([#656](https://github.com/badlogic/pi-mono/pull/656) by [@dannote](https://github.com/dannote))
- `/scoped-models`: Alt+Up/Down to reorder enabled models. Order is preserved when saving with Ctrl+S and determines Ctrl+P cycling order. ([#676](https://github.com/badlogic/pi-mono/pull/676) by [@thomasmhr](https://github.com/thomasmhr))
- Amazon Bedrock provider support (experimental, tested with Anthropic Claude models only) ([#494](https://github.com/badlogic/pi-mono/pull/494) by [@unexge](https://github.com/unexge))
- Extension example: `sandbox/` for OS-level bash sandboxing using `@anthropic-ai/sandbox-runtime` with per-project config ([#673](https://github.com/badlogic/pi-mono/pull/673) by [@dannote](https://github.com/dannote))
- Print mode JSON output now emits the session header as the first line.

## [0.44.0] - 2026-01-12

### Breaking Changes

- `pi.getAllTools()` now returns `ToolInfo[]` (with `name` and `description`) instead of `string[]`. Extensions that only need names can use `.map(t => t.name)`. ([#648](https://github.com/badlogic/pi-mono/pull/648) by [@carsonfarmer](https://github.com/carsonfarmer))

### Added

- Session naming: `/name <name>` command sets a display name shown in the session selector instead of the first message. Useful for distinguishing forked sessions. Extensions can use `pi.setSessionName()` and `pi.getSessionName()`. ([#650](https://github.com/badlogic/pi-mono/pull/650) by [@scutifer](https://github.com/scutifer))
- Extension example: `notify.ts` for desktop notifications via OSC 777 escape sequence ([#658](https://github.com/badlogic/pi-mono/pull/658) by [@ferologics](https://github.com/ferologics))
- Inline hint for queued messages showing the `Alt+Up` restore shortcut ([#657](https://github.com/badlogic/pi-mono/pull/657) by [@tmustier](https://github.com/tmustier))
- Page-up/down navigation in `/resume` session selector to jump by 5 items ([#662](https://github.com/badlogic/pi-mono/pull/662) by [@aliou](https://github.com/aliou))
- Fuzzy search in `/settings` menu: type to filter settings by label ([#643](https://github.com/badlogic/pi-mono/pull/643) by [@ninlds](https://github.com/ninlds))

### Fixed

- Session selector now stays open when current folder has no sessions, allowing Tab to switch to "all" scope ([#661](https://github.com/badlogic/pi-mono/pull/661) by [@aliou](https://github.com/aliou))
- Extensions using theme utilities like `getSettingsListTheme()` now work in dev mode with tsx

## [0.43.0] - 2026-01-11

### Breaking Changes

- Extension editor (`ctx.ui.editor()`) now uses Enter to submit and Shift+Enter for newlines, matching the main editor. Previously used Ctrl+Enter to submit. Extensions with hardcoded "ctrl+enter" hints need updating. ([#642](https://github.com/badlogic/pi-mono/pull/642) by [@mitsuhiko](https://github.com/mitsuhiko))
- Renamed `/branch` command to `/fork` ([#641](https://github.com/badlogic/pi-mono/issues/641))
  - RPC: `branch` → `fork`, `get_branch_messages` → `get_fork_messages`
  - SDK: `branch()` → `fork()`, `getBranchMessages()` → `getForkMessages()`
  - AgentSession: `branch()` → `fork()`, `getUserMessagesForBranching()` → `getUserMessagesForForking()`
  - Extension events: `session_before_branch` → `session_before_fork`, `session_branch` → `session_fork`
  - Settings: `doubleEscapeAction: "branch" | "tree"` → `"fork" | "tree"`
- `SessionManager.list()` and `SessionManager.listAll()` are now async, returning `Promise<SessionInfo[]>`. Callers must await them. ([#620](https://github.com/badlogic/pi-mono/pull/620) by [@tmustier](https://github.com/tmustier))

### Added
- `/resume` selector now toggles between current-folder and all sessions with Tab, showing the session cwd in the All view and loading progress. ([#620](https://github.com/badlogic/pi-mono/pull/620) by [@tmustier](https://github.com/tmustier))
- `SessionManager.list()` and `SessionManager.listAll()` accept optional `onProgress` callback for progress updates
- `SessionInfo.cwd` field containing the session's working directory (empty string for old sessions)
- `SessionListProgress` type export for progress callbacks
- `/scoped-models` command to enable/disable models for Ctrl+P cycling. Changes are session-only by default; press Ctrl+S to persist to settings.json. ([#626](https://github.com/badlogic/pi-mono/pull/626) by [@CarlosGtrz](https://github.com/CarlosGtrz))
- `model_select` extension hook fires when model changes via `/model`, model cycling, or session restore with `source` field and `previousModel` ([#628](https://github.com/badlogic/pi-mono/pull/628) by [@marckrenn](https://github.com/marckrenn))
- `ctx.ui.setWorkingMessage()` extension API to customize the "Working..." message during streaming ([#625](https://github.com/badlogic/pi-mono/pull/625) by [@nicobailon](https://github.com/nicobailon))
- Skill slash commands: loaded skills are registered as `/skill:name` commands for quick access. Toggle via `/settings` or `skills.enableSkillCommands` in settings.json. ([#630](https://github.com/badlogic/pi-mono/pull/630) by [@Dwsy](https://github.com/Dwsy))
- Slash command autocomplete now uses fuzzy matching (type `/skbra` to match `/skill:brave-search`)
- `/tree` branch summarization now offers three options: "No summary", "Summarize", and "Summarize with custom prompt". Custom prompts are appended as additional focus to the default summarization instructions. ([#642](https://github.com/badlogic/pi-mono/pull/642) by [@mitsuhiko](https://github.com/mitsuhiko))

### Fixed

- Missing spacer between assistant message and text editor ([#655](https://github.com/badlogic/pi-mono/issues/655))
- Session picker respects custom keybindings when using `--resume` ([#633](https://github.com/badlogic/pi-mono/pull/633) by [@aos](https://github.com/aos))
- Custom footer extensions now see model changes: `ctx.model` is now a getter that returns the current model instead of a snapshot from when the context was created ([#634](https://github.com/badlogic/pi-mono/pull/634) by [@ogulcancelik](https://github.com/ogulcancelik))
- Footer git branch not updating after external branch switches. Git uses atomic writes (temp file + rename), which changes the inode and breaks `fs.watch` on the file. Now watches the directory instead.
- Extension loading errors are now displayed to the user instead of being silently ignored ([#639](https://github.com/badlogic/pi-mono/pull/639) by [@aliou](https://github.com/aliou))

## [0.42.5] - 2026-01-11

### Fixed

- Reduced flicker by only re-rendering changed lines ([#617](https://github.com/badlogic/pi-mono/pull/617) by [@ogulcancelik](https://github.com/ogulcancelik)). No worries tho, there's still a little flicker in the VS Code Terminal. Praise the flicker.
- Cursor position tracking when content shrinks with unchanged remaining lines
- TUI renders with wrong dimensions after suspend/resume if terminal was resized while suspended ([#599](https://github.com/badlogic/pi-mono/issues/599))
- Pasted content containing Kitty key release patterns (e.g., `:3F` in MAC addresses) was incorrectly filtered out ([#623](https://github.com/badlogic/pi-mono/pull/623) by [@ogulcancelik](https://github.com/ogulcancelik))

## [0.42.4] - 2026-01-10

### Fixed

- Bash output expanded hint now says "(ctrl+o to collapse)" ([#610](https://github.com/badlogic/pi-mono/pull/610) by [@tallshort](https://github.com/tallshort))
- Fixed UTF-8 text corruption in remote bash execution (SSH, containers) by using streaming TextDecoder ([#608](https://github.com/badlogic/pi-mono/issues/608))

## [0.42.3] - 2026-01-10

### Changed

- OpenAI Codex: updated to use bundled system prompt from upstream

## [0.42.2] - 2026-01-10

### Added

- `/model <search>` now pre-filters the model selector or auto-selects on exact match. Use `provider/model` syntax to disambiguate (e.g., `/model openai/gpt-4`). ([#587](https://github.com/badlogic/pi-mono/pull/587) by [@zedrdave](https://github.com/zedrdave))
- `FooterDataProvider` for custom footers: `ctx.ui.setFooter()` now receives a third `footerData` parameter providing `getGitBranch()`, `getExtensionStatuses()`, and `onBranchChange()` for reactive updates ([#600](https://github.com/badlogic/pi-mono/pull/600) by [@nicobailon](https://github.com/nicobailon))
- `Alt+Up` hotkey to restore queued steering/follow-up messages back into the editor without aborting the current run ([#604](https://github.com/badlogic/pi-mono/pull/604) by [@tmustier](https://github.com/tmustier))

### Fixed

- Fixed LM Studio compatibility for OpenAI Responses tool strict mapping in the ai provider ([#598](https://github.com/badlogic/pi-mono/pull/598) by [@gnattu](https://github.com/gnattu))

## [0.42.1] - 2026-01-09

### Fixed

- Symlinked directories in `prompts/` folders are now followed when loading prompt templates ([#601](https://github.com/badlogic/pi-mono/pull/601) by [@aliou](https://github.com/aliou))

## [0.42.0] - 2026-01-09

### Added

- Added OpenCode Zen provider support. Set `OPENCODE_API_KEY` env var and use `opencode/<model-id>` (e.g., `opencode/claude-opus-4-5`).

## [0.41.0] - 2026-01-09

### Added

- Anthropic OAuth support is back! Use `/login` to authenticate with your Claude Pro/Max subscription.

## [0.40.1] - 2026-01-09

### Removed

- Anthropic OAuth support (`/login`). Use API keys instead.

## [0.40.0] - 2026-01-08

### Added

- Documentation on component invalidation and theme changes in `docs/tui.md`

### Fixed

- Components now properly rebuild their content on theme change (tool executions, assistant messages, bash executions, custom messages, branch/compaction summaries)

## [0.39.1] - 2026-01-08

### Fixed

- `setTheme()` now triggers a full rerender so previously rendered components update with the new theme colors
- `mac-system-theme.ts` example now polls every 2 seconds and uses `osascript` for real-time macOS appearance detection

## [0.39.0] - 2026-01-08

### Breaking Changes

- `before_agent_start` event now receives `systemPrompt` in the event object and returns `systemPrompt` (full replacement) instead of `systemPromptAppend`. Extensions that were appending must now use `event.systemPrompt + extra` pattern. ([#575](https://github.com/badlogic/pi-mono/issues/575))
- `discoverSkills()` now returns `{ skills: Skill[], warnings: SkillWarning[] }` instead of `Skill[]`. This allows callers to handle skill loading warnings. ([#577](https://github.com/badlogic/pi-mono/pull/577) by [@cv](https://github.com/cv))

### Added

- `ctx.ui.getAllThemes()`, `ctx.ui.getTheme(name)`, and `ctx.ui.setTheme(name | Theme)` methods for extensions to list, load, and switch themes at runtime ([#576](https://github.com/badlogic/pi-mono/pull/576))
- `--no-tools` flag to disable all built-in tools, allowing extension-only tool setups ([#557](https://github.com/badlogic/pi-mono/pull/557) by [@cv](https://github.com/cv))
- Pluggable operations for built-in tools enabling remote execution via SSH or other transports ([#564](https://github.com/badlogic/pi-mono/issues/564)). Interfaces: `ReadOperations`, `WriteOperations`, `EditOperations`, `BashOperations`, `LsOperations`, `GrepOperations`, `FindOperations`
- `user_bash` event for intercepting user `!`/`!!` commands, allowing extensions to redirect to remote systems ([#528](https://github.com/badlogic/pi-mono/issues/528))
- `setActiveTools()` in ExtensionAPI for dynamic tool management
- Built-in renderers used automatically for tool overrides without custom `renderCall`/`renderResult`
- `ssh.ts` example: remote tool execution via `--ssh user@host:/path`
- `interactive-shell.ts` example: run interactive commands (vim, git rebase, htop) with full terminal access via `!i` prefix or auto-detection
- Wayland clipboard support for `/copy` command using wl-copy with xclip/xsel fallback ([#570](https://github.com/badlogic/pi-mono/pull/570) by [@OgulcanCelik](https://github.com/OgulcanCelik))
- **Experimental:** `ctx.ui.custom()` now accepts `{ overlay: true }` option for floating modal components that composite over existing content without clearing the screen ([#558](https://github.com/badlogic/pi-mono/pull/558) by [@nicobailon](https://github.com/nicobailon))
- `AgentSession.skills` and `AgentSession.skillWarnings` properties to access loaded skills without rediscovery ([#577](https://github.com/badlogic/pi-mono/pull/577) by [@cv](https://github.com/cv))

### Fixed

- String `systemPrompt` in `createAgentSession()` now works as a full replacement instead of having context files and skills appended, matching documented behavior ([#543](https://github.com/badlogic/pi-mono/issues/543))
- Update notification for bun binary installs now shows release download URL instead of npm command ([#567](https://github.com/badlogic/pi-mono/pull/567) by [@ferologics](https://github.com/ferologics))
- ESC key now works during "Working..." state after auto-retry ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
- Abort messages now show correct retry attempt count (e.g., "Aborted after 2 retry attempts") ([#568](https://github.com/badlogic/pi-mono/pull/568) by [@tmustier](https://github.com/tmustier))
- Fixed Antigravity provider returning 429 errors despite available quota ([#571](https://github.com/badlogic/pi-mono/pull/571) by [@ben-vargas](https://github.com/ben-vargas))
- Fixed malformed thinking text in Gemini/Antigravity responses where thinking content appeared as regular text or vice versa. Cross-model conversations now properly convert thinking blocks to plain text. ([#561](https://github.com/badlogic/pi-mono/issues/561))
- `--no-skills` flag now correctly prevents skills from loading in interactive mode ([#577](https://github.com/badlogic/pi-mono/pull/577) by [@cv](https://github.com/cv))

## [0.38.0] - 2026-01-08

### Breaking Changes

- `ctx.ui.custom()` factory signature changed from `(tui, theme, done)` to `(tui, theme, keybindings, done)` for keybinding access in custom components
- `LoadedExtension` type renamed to `Extension`
- `LoadExtensionsResult.setUIContext()` removed, replaced with `runtime: ExtensionRuntime`
- `ExtensionRunner` constructor now requires `runtime: ExtensionRuntime` as second parameter
- `ExtensionRunner.initialize()` signature changed from options object to positional params `(actions, contextActions, commandContextActions?, uiContext?)`
- `ExtensionRunner.getHasUI()` renamed to `hasUI()`
- OpenAI Codex model aliases removed (`gpt-5`, `gpt-5-mini`, `gpt-5-nano`, `codex-mini-latest`). Use canonical IDs: `gpt-5.1`, `gpt-5.1-codex-mini`, `gpt-5.2`, `gpt-5.2-codex`. ([#536](https://github.com/badlogic/pi-mono/pull/536) by [@ghoulr](https://github.com/ghoulr))

### Added

- `--no-extensions` flag to disable extension discovery while still allowing explicit `-e` paths ([#524](https://github.com/badlogic/pi-mono/pull/524) by [@cv](https://github.com/cv))
- SDK: `InteractiveMode`, `runPrintMode()`, `runRpcMode()` exported for building custom run modes. See `docs/sdk.md`.
- `PI_SKIP_VERSION_CHECK` environment variable to disable new version notifications at startup ([#549](https://github.com/badlogic/pi-mono/pull/549) by [@aos](https://github.com/aos))
- `thinkingBudgets` setting to customize token budgets per thinking level for token-based providers ([#529](https://github.com/badlogic/pi-mono/pull/529) by [@melihmucuk](https://github.com/melihmucuk))
- Extension UI dialogs (`ctx.ui.select()`, `ctx.ui.confirm()`, `ctx.ui.input()`) now support a `timeout` option with live countdown display ([#522](https://github.com/badlogic/pi-mono/pull/522) by [@nicobailon](https://github.com/nicobailon))
- Extensions can now provide custom editor components via `ctx.ui.setEditorComponent()`. See `examples/extensions/modal-editor.ts` and `docs/tui.md` Pattern 7.
- Extension factories can now be async, enabling dynamic imports and lazy-loaded dependencies ([#513](https://github.com/badlogic/pi-mono/pull/513) by [@austinm911](https://github.com/austinm911))
- `ctx.shutdown()` is now available in extension contexts for requesting a graceful shutdown. In interactive mode, shutdown is deferred until the agent becomes idle (after processing all queued steering and follow-up messages). In RPC mode, shutdown is deferred until after completing the current command response. In print mode, shutdown is a no-op as the process exits automatically when prompts complete. ([#542](https://github.com/badlogic/pi-mono/pull/542) by [@kaofelix](https://github.com/kaofelix))

### Fixed

- Default thinking level from settings now applies correctly when `enabledModels` is configured ([#540](https://github.com/badlogic/pi-mono/pull/540) by [@ferologics](https://github.com/ferologics))
- External edits to `settings.json` while pi is running are now preserved when pi saves settings ([#527](https://github.com/badlogic/pi-mono/pull/527) by [@ferologics](https://github.com/ferologics))
- Overflow-based compaction now skips if error came from a different model or was already handled by a previous compaction ([#535](https://github.com/badlogic/pi-mono/pull/535) by [@mitsuhiko](https://github.com/mitsuhiko))
- OpenAI Codex context window reduced from 400k to 272k tokens to match Codex CLI defaults and prevent 400 errors ([#536](https://github.com/badlogic/pi-mono/pull/536) by [@ghoulr](https://github.com/ghoulr))
- Context overflow detection now recognizes `context_length_exceeded` errors.
- Key presses no longer dropped when input is batched over SSH ([#538](https://github.com/badlogic/pi-mono/issues/538))
- Clipboard image support now works on Alpine Linux and other musl-based distros ([#533](https://github.com/badlogic/pi-mono/issues/533))

## [0.37.8] - 2026-01-07

## [0.37.7] - 2026-01-07

## [0.37.6] - 2026-01-06

### Added

- Extension UI dialogs (`ctx.ui.select()`, `ctx.ui.confirm()`, `ctx.ui.input()`) now accept an optional `AbortSignal` to programmatically dismiss dialogs. Useful for implementing timeouts. See `examples/extensions/timed-confirm.ts`. ([#474](https://github.com/badlogic/pi-mono/issues/474))
- HTML export now shows bridge prompts in model change messages for Codex sessions ([#510](https://github.com/badlogic/pi-mono/pull/510) by [@mitsuhiko](https://github.com/mitsuhiko))

## [0.37.5] - 2026-01-06

### Added

- ExtensionAPI: `setModel()`, `getThinkingLevel()`, `setThinkingLevel()` methods for extensions to change model and thinking level at runtime ([#509](https://github.com/badlogic/pi-mono/issues/509))
- Exported truncation utilities for custom tools: `truncateHead`, `truncateTail`, `truncateLine`, `formatSize`, `DEFAULT_MAX_BYTES`, `DEFAULT_MAX_LINES`, `TruncationOptions`, `TruncationResult`
- New example `truncated-tool.ts` demonstrating proper output truncation with custom rendering for extensions
- New example `preset.ts` demonstrating preset configurations with model/thinking/tools switching ([#347](https://github.com/badlogic/pi-mono/issues/347))
- Documentation for output truncation best practices in `docs/extensions.md`
- Exported all UI components for extensions: `ArminComponent`, `AssistantMessageComponent`, `BashExecutionComponent`, `BorderedLoader`, `BranchSummaryMessageComponent`, `CompactionSummaryMessageComponent`, `CustomEditor`, `CustomMessageComponent`, `DynamicBorder`, `ExtensionEditorComponent`, `ExtensionInputComponent`, `ExtensionSelectorComponent`, `FooterComponent`, `LoginDialogComponent`, `ModelSelectorComponent`, `OAuthSelectorComponent`, `SessionSelectorComponent`, `SettingsSelectorComponent`, `ShowImagesSelectorComponent`, `ThemeSelectorComponent`, `ThinkingSelectorComponent`, `ToolExecutionComponent`, `TreeSelectorComponent`, `UserMessageComponent`, `UserMessageSelectorComponent`, plus utilities `renderDiff`, `truncateToVisualLines`
- `docs/tui.md`: Common Patterns section with copy-paste code for SelectList, BorderedLoader, SettingsList, setStatus, setWidget, setFooter
- `docs/tui.md`: Key Rules section documenting critical patterns for extension UI development
- `docs/extensions.md`: Exhaustive example links for all ExtensionAPI methods and events
- System prompt now references `docs/tui.md` for TUI component development

## [0.37.4] - 2026-01-06

### Added

- Session picker (`pi -r`) and `--session` flag now support searching/resuming by session ID (UUID prefix) ([#495](https://github.com/badlogic/pi-mono/issues/495) by [@arunsathiya](https://github.com/arunsathiya))
- Extensions can now replace the startup header with `ctx.ui.setHeader()`, see `examples/extensions/custom-header.ts` ([#500](https://github.com/badlogic/pi-mono/pull/500) by [@tudoroancea](https://github.com/tudoroancea))

### Changed

- Startup help text: fixed misleading "ctrl+k to delete line" to "ctrl+k to delete to end"
- Startup help text and `/hotkeys`: added `!!` shortcut for running bash without adding output to context

### Fixed

- Queued steering/follow-up messages no longer wipe unsent editor input ([#503](https://github.com/badlogic/pi-mono/pull/503) by [@tmustier](https://github.com/tmustier))
- OAuth token refresh failure no longer crashes app at startup, allowing user to `/login` to re-authenticate ([#498](https://github.com/badlogic/pi-mono/issues/498))

## [0.37.3] - 2026-01-06

### Added

- Extensions can now replace the footer with `ctx.ui.setFooter()`, see `examples/extensions/custom-footer.ts` ([#481](https://github.com/badlogic/pi-mono/issues/481))
- Session ID is now forwarded to LLM providers for session-based caching (used by OpenAI Codex for prompt caching).
- Added `blockImages` setting to prevent images from being sent to LLM providers ([#492](https://github.com/badlogic/pi-mono/pull/492) by [@jsinge97](https://github.com/jsinge97))
- Extensions can now send user messages via `pi.sendUserMessage()` ([#483](https://github.com/badlogic/pi-mono/issues/483))

### Fixed

- Add `minimatch` as a direct dependency for explicit imports.
- Status bar now shows correct git branch when running in a git worktree ([#490](https://github.com/badlogic/pi-mono/pull/490) by [@kcosr](https://github.com/kcosr))
- Interactive mode: Ctrl+V clipboard image paste now works on Wayland sessions by using `wl-paste` with `xclip` fallback ([#488](https://github.com/badlogic/pi-mono/pull/488) by [@ghoulr](https://github.com/ghoulr))

## [0.37.2] - 2026-01-05

### Fixed

- Extension directories in `settings.json` now respect `package.json` manifests, matching global extension behavior ([#480](https://github.com/badlogic/pi-mono/pull/480) by [@prateekmedia](https://github.com/prateekmedia))
- Share viewer: deep links now scroll to the target message when opened via `/share`
- Bash tool now handles spawn errors gracefully instead of crashing the agent (missing cwd, invalid shell path) ([#479](https://github.com/badlogic/pi-mono/pull/479) by [@robinwander](https://github.com/robinwander))

## [0.37.1] - 2026-01-05

### Fixed

- Share viewer: copy-link buttons now generate correct URLs when session is viewed via `/share` (iframe context)

## [0.37.0] - 2026-01-05

### Added

- Share viewer: copy-link button on messages to share URLs that navigate directly to a specific message ([#477](https://github.com/badlogic/pi-mono/pull/477) by [@lockmeister](https://github.com/lockmeister))
- Extension example: add `claude-rules` to load `.claude/rules/` entries into the system prompt ([#461](https://github.com/badlogic/pi-mono/pull/461) by [@vaayne](https://github.com/vaayne))
- Headless OAuth login: all providers now show paste input for manual URL/code entry, works over SSH without DISPLAY ([#428](https://github.com/badlogic/pi-mono/pull/428) by [@ben-vargas](https://github.com/ben-vargas), [#468](https://github.com/badlogic/pi-mono/pull/468) by [@crcatala](https://github.com/crcatala))

### Changed

- OAuth login UI now uses dedicated dialog component with consistent borders
- Assume truecolor support for all terminals except `dumb`, empty, or `linux` (fixes colors over SSH)
- OpenAI Codex clean-up: removed per-thinking-level model variants, thinking level is now set separately and the provider clamps to what each model supports internally (initial implementation in [#472](https://github.com/badlogic/pi-mono/pull/472) by [@ben-vargas](https://github.com/ben-vargas))

### Fixed

- Messages submitted during compaction are queued and delivered after compaction completes, preserving steering and follow-up behavior. Extension commands execute immediately during compaction. ([#476](https://github.com/badlogic/pi-mono/pull/476) by [@tmustier](https://github.com/tmustier))
- Managed binaries (`fd`, `rg`) now stored in `~/.pi/agent/bin/` instead of `tools/`, eliminating false deprecation warnings ([#470](https://github.com/badlogic/pi-mono/pull/470) by [@mcinteerj](https://github.com/mcinteerj))
- Extensions defined in `settings.json` were not loaded ([#463](https://github.com/badlogic/pi-mono/pull/463) by [@melihmucuk](https://github.com/melihmucuk))
- OAuth refresh no longer logs users out when multiple pi instances are running ([#466](https://github.com/badlogic/pi-mono/pull/466) by [@Cursivez](https://github.com/Cursivez))
- Migration warnings now ignore `fd.exe` and `rg.exe` in `tools/` on Windows ([#458](https://github.com/badlogic/pi-mono/pull/458) by [@carlosgtrz](https://github.com/carlosgtrz))
- CI: add `examples/extensions/with-deps` to workspaces to fix typecheck ([#467](https://github.com/badlogic/pi-mono/pull/467) by [@aliou](https://github.com/aliou))
- SDK: passing `extensions: []` now disables extension discovery as documented ([#465](https://github.com/badlogic/pi-mono/pull/465) by [@aliou](https://github.com/aliou))

## [0.36.0] - 2026-01-05

### Added

- Experimental: OpenAI Codex OAuth provider support: access Codex models via ChatGPT Plus/Pro subscription using `/login openai-codex` ([#451](https://github.com/badlogic/pi-mono/pull/451) by [@kim0](https://github.com/kim0))

## [0.35.0] - 2026-01-05

This release unifies hooks and custom tools into a single "extensions" system and renames "slash commands" to "prompt templates". ([#454](https://github.com/badlogic/pi-mono/issues/454))

**Before migrating, read:**

- [docs/extensions.md](docs/extensions.md) - Full API reference
- [README.md](README.md) - Extensions section with examples
- [examples/extensions/](examples/extensions/) - Working examples

### Extensions Migration

Hooks and custom tools are now unified as **extensions**. Both were TypeScript modules exporting a factory function that receives an API object. Now there's one concept, one discovery location, one CLI flag, one settings.json entry.

**Automatic migration:**

- `commands/` directories are automatically renamed to `prompts/` on startup (both `~/.pi/agent/commands/` and `.pi/commands/`)

**Manual migration required:**

1. Move files from `hooks/` and `tools/` directories to `extensions/` (deprecation warnings shown on startup)
2. Update imports and type names in your extension code
3. Update `settings.json` if you have explicit hook and custom tool paths configured

**Directory changes:**

```
# Before
~/.pi/agent/hooks/*.ts       →  ~/.pi/agent/extensions/*.ts
~/.pi/agent/tools/*.ts       →  ~/.pi/agent/extensions/*.ts
.pi/hooks/*.ts               →  .pi/extensions/*.ts
.pi/tools/*.ts               →  .pi/extensions/*.ts
```

**Extension discovery rules** (in `extensions/` directories):

1. **Direct files:** `extensions/*.ts` or `*.js` → loaded directly
2. **Subdirectory with index:** `extensions/myext/index.ts` → loaded as single extension
3. **Subdirectory with package.json:** `extensions/myext/package.json` with `"pi"` field → loads declared paths

```json
// extensions/my-package/package.json
{
  "name": "my-extension-package",
  "dependencies": { "zod": "^3.0.0" },
  "pi": {
    "extensions": ["./src/main.ts", "./src/tools.ts"]
  }
}
```

No recursion beyond one level. Complex packages must use the `package.json` manifest. Dependencies are resolved via jiti, and extensions can be published to and installed from npm.

**Type renames:**

- `HookAPI` → `ExtensionAPI`
- `HookContext` → `ExtensionContext`
- `HookCommandContext` → `ExtensionCommandContext`
- `HookUIContext` → `ExtensionUIContext`
- `CustomToolAPI` → `ExtensionAPI` (merged)
- `CustomToolContext` → `ExtensionContext` (merged)
- `CustomToolUIContext` → `ExtensionUIContext`
- `CustomTool` → `ToolDefinition`
- `CustomToolFactory` → `ExtensionFactory`
- `HookMessage` → `CustomMessage`

**Import changes:**

```typescript
// Before (hook)
import type { HookAPI, HookContext } from "@bastani/atomic";
export default function (pi: HookAPI) { ... }

// Before (custom tool)
import type { CustomToolFactory } from "@bastani/atomic";
const factory: CustomToolFactory = (pi) => ({ name: "my_tool", ... });
export default factory;

// After (both are now extensions)
import type { ExtensionAPI } from "@bastani/atomic";
export default function (pi: ExtensionAPI) {
  pi.on("tool_call", async (event, ctx) => { ... });
  pi.registerTool({ name: "my_tool", ... });
}
```

**Custom tools now have full context access.** Tools registered via `pi.registerTool()` now receive the same `ctx` object that event handlers receive. Previously, custom tools had limited context. Now all extension code shares the same capabilities:

- `pi.registerTool()` - Register tools the LLM can call
- `pi.registerCommand()` - Register commands like `/mycommand`
- `pi.registerShortcut()` - Register keyboard shortcuts (shown in `/hotkeys`)
- `pi.registerFlag()` - Register CLI flags (shown in `--help`)
- `pi.registerMessageRenderer()` - Custom TUI rendering for message types
- `pi.on()` - Subscribe to lifecycle events (tool_call, session_start, etc.)
- `pi.sendMessage()` - Inject messages into the conversation
- `pi.appendEntry()` - Persist custom data in session (survives restart/branch)
- `pi.exec()` - Run shell commands
- `pi.getActiveTools()` / `pi.setActiveTools()` - Dynamic tool enable/disable
- `pi.getAllTools()` - List all available tools
- `pi.events` - Event bus for cross-extension communication
- `ctx.ui.confirm()` / `select()` / `input()` - User prompts
- `ctx.ui.notify()` - Toast notifications
- `ctx.ui.setStatus()` - Persistent status in footer (multiple extensions can set their own)
- `ctx.ui.setWidget()` - Widget display above editor
- `ctx.ui.setTitle()` - Set terminal window title
- `ctx.ui.custom()` - Full TUI component with keyboard handling
- `ctx.ui.editor()` - Multi-line text editor with external editor support
- `ctx.sessionManager` - Read session entries, get branch history

**Settings changes:**

```json
// Before
{
  "hooks": ["./my-hook.ts"],
  "customTools": ["./my-tool.ts"]
}

// After
{
  "extensions": ["./my-extension.ts"]
}
```

**CLI changes:**

```bash
# Before
pi --hook ./safety.ts --tool ./todo.ts

# After
pi --extension ./safety.ts -e ./todo.ts
```

### Prompt Templates Migration

"Slash commands" (markdown files defining reusable prompts invoked via `/name`) are renamed to "prompt templates" to avoid confusion with extension-registered commands.

**Automatic migration:** The `commands/` directory is automatically renamed to `prompts/` on startup (if `prompts/` doesn't exist). Works for both regular directories and symlinks.

**Directory changes:**

```
~/.pi/agent/commands/*.md    →  ~/.pi/agent/prompts/*.md
.pi/commands/*.md            →  .pi/prompts/*.md
```

**SDK type renames:**

- `FileSlashCommand` → `PromptTemplate`
- `LoadSlashCommandsOptions` → `LoadPromptTemplatesOptions`

**SDK function renames:**

- `discoverSlashCommands()` → `discoverPromptTemplates()`
- `loadSlashCommands()` → `loadPromptTemplates()`
- `expandSlashCommand()` → `expandPromptTemplate()`
- `getCommandsDir()` → `getPromptsDir()`

**SDK option renames:**

- `CreateAgentSessionOptions.slashCommands` → `.promptTemplates`
- `AgentSession.fileCommands` → `.promptTemplates`
- `PromptOptions.expandSlashCommands` → `.expandPromptTemplates`

### SDK Migration

**Discovery functions:**

- `discoverAndLoadHooks()` → `discoverAndLoadExtensions()`
- `discoverAndLoadCustomTools()` → merged into `discoverAndLoadExtensions()`
- `loadHooks()` → `loadExtensions()`
- `loadCustomTools()` → merged into `loadExtensions()`

**Runner and wrapper:**

- `HookRunner` → `ExtensionRunner`
- `wrapToolsWithHooks()` → `wrapToolsWithExtensions()`
- `wrapToolWithHooks()` → `wrapToolWithExtensions()`

**CreateAgentSessionOptions:**

- `.hooks` → removed (use `.additionalExtensionPaths` for paths)
- `.additionalHookPaths` → `.additionalExtensionPaths`
- `.preloadedHooks` → `.preloadedExtensions`
- `.customTools` type changed: `Array<{ path?; tool: CustomTool }>` → `ToolDefinition[]`
- `.additionalCustomToolPaths` → merged into `.additionalExtensionPaths`
- `.slashCommands` → `.promptTemplates`

**AgentSession:**

- `.hookRunner` → `.extensionRunner`
- `.fileCommands` → `.promptTemplates`
- `.sendHookMessage()` → `.sendCustomMessage()`

### Session Migration

**Automatic.** Session version bumped from 2 to 3. Existing sessions are migrated on first load:

- Message role `"hookMessage"` → `"custom"`

### Breaking Changes

- **Settings:** `hooks` and `customTools` arrays replaced with single `extensions` array
- **CLI:** `--hook` and `--tool` flags replaced with `--extension` / `-e`
- **Directories:** `hooks/`, `tools/` → `extensions/`; `commands/` → `prompts/`
- **Types:** See type renames above
- **SDK:** See SDK migration above

### Changed

- Extensions can have their own `package.json` with dependencies (resolved via jiti)
- Documentation: `docs/hooks.md` and `docs/custom-tools.md` merged into `docs/extensions.md`
- Examples: `examples/hooks/` and `examples/custom-tools/` merged into `examples/extensions/`
- README: Extensions section expanded with custom tools, commands, events, state persistence, shortcuts, flags, and UI examples
- SDK: `customTools` option now accepts `ToolDefinition[]` directly (simplified from `Array<{ path?, tool }>`)
- SDK: `extensions` option accepts `ExtensionFactory[]` for inline extensions
- SDK: `additionalExtensionPaths` replaces both `additionalHookPaths` and `additionalCustomToolPaths`

## [0.34.2] - 2026-01-04

## [0.34.1] - 2026-01-04

### Added

- Hook API: `ctx.ui.setTitle(title)` allows hooks to set the terminal window/tab title ([#446](https://github.com/badlogic/pi-mono/pull/446) by [@aliou](https://github.com/aliou))

### Changed

- Expanded keybinding documentation to list all 32 supported symbol keys with notes on ctrl+symbol behavior ([#450](https://github.com/badlogic/pi-mono/pull/450) by [@kaofelix](https://github.com/kaofelix))

## [0.34.0] - 2026-01-04

### Added

- Hook API: `pi.getActiveTools()` and `pi.setActiveTools(toolNames)` for dynamically enabling/disabling tools from hooks
- Hook API: `pi.getAllTools()` to enumerate all configured tools (built-in via --tools or default, plus custom tools)
- Hook API: `pi.registerFlag(name, options)` and `pi.getFlag(name)` for hooks to register custom CLI flags (parsed automatically)
- Hook API: `pi.registerShortcut(shortcut, options)` for hooks to register custom keyboard shortcuts using `KeyId` (e.g., `Key.shift("p")`). Conflicts with built-in shortcuts are skipped, conflicts between hooks logged as warnings.
- Hook API: `ctx.ui.setWidget(key, content)` for status displays above the editor. Accepts either a string array or a component factory function.
- Hook API: `theme.strikethrough(text)` for strikethrough text styling
- Hook API: `before_agent_start` handlers can now return `systemPromptAppend` to dynamically append text to the system prompt for that turn. Multiple hooks' appends are concatenated.
- Hook API: `before_agent_start` handlers can now return multiple messages (all are injected, not just the first)
- `/hotkeys` command now shows hook-registered shortcuts in a separate "Hooks" section
- New example hook: `plan-mode.ts` - Claude Code-style read-only exploration mode:
  - Toggle via `/plan` command, `Shift+P` shortcut, or `--plan` CLI flag
  - Read-only tools: `read`, `bash`, `grep`, `find`, `ls` (no `edit`/`write`)
  - Bash commands restricted to non-destructive operations (blocks `rm`, `mv`, `git commit`, `npm install`, etc.)
  - Interactive prompt after each response: execute plan, stay in plan mode, or refine
  - Todo list widget showing progress with checkboxes and strikethrough for completed items
  - Each todo has a unique ID; agent marks items done by outputting `[DONE:id]`
  - Progress updates via `agent_end` hook (parses completed items from final message)
  - `/todos` command to view current plan progress
  - Shows `⏸ plan` indicator in footer when in plan mode, `📋 2/5` when executing
  - State persists across sessions (including todo progress)
- New example hook: `tools.ts` - Interactive `/tools` command to enable/disable tools with session persistence
- New example hook: `pirate.ts` - Demonstrates `systemPromptAppend` to make the agent speak like a pirate
- Tool registry now contains all built-in tools (read, bash, edit, write, grep, find, ls) even when `--tools` limits the initially active set. Hooks can enable any tool from the registry via `pi.setActiveTools()`.
- System prompt now automatically rebuilds when tools change via `setActiveTools()`, updating tool descriptions and guidelines to match the new tool set
- Hook errors now display full stack traces for easier debugging
- Event bus (`pi.events`) for tool/hook communication: shared pub/sub between custom tools and hooks
- Custom tools now have `pi.sendMessage()` to send messages directly to the agent session without needing the event bus
- `sendMessage()` supports `deliverAs: "nextTurn"` to queue messages for the next user prompt

### Changed

- Removed image placeholders after copy & paste, replaced with inserting image file paths directly. ([#442](https://github.com/badlogic/pi-mono/pull/442) by [@mitsuhiko](https://github.com/mitsuhiko))

### Fixed

- Fixed potential text decoding issues in bash executor by using streaming TextDecoder instead of Buffer.toString()
- External editor (Ctrl-G) now shows full pasted content instead of `[paste #N ...]` placeholders ([#444](https://github.com/badlogic/pi-mono/pull/444) by [@aliou](https://github.com/aliou))

## [0.33.0] - 2026-01-04

### Breaking Changes

- **Key detection functions removed from `@mariozechner/pi-tui`**: All `isXxx()` key detection functions (`isEnter()`, `isEscape()`, `isCtrlC()`, etc.) have been removed. Use `matchesKey(data, keyId)` instead (e.g., `matchesKey(data, "enter")`, `matchesKey(data, "ctrl+c")`). This affects hooks and custom tools that use `ctx.ui.custom()` with keyboard input handling. ([#405](https://github.com/badlogic/pi-mono/pull/405))

### Added

- Clipboard image paste support via `Ctrl+V`. Images are saved to a temp file and attached to the message. Works on macOS, Windows, and Linux (X11). ([#419](https://github.com/badlogic/pi-mono/issues/419))
- Configurable keybindings via `~/.pi/agent/keybindings.json`. All keyboard shortcuts (editor navigation, deletion, app actions like model cycling, etc.) can now be customized. Supports multiple bindings per action. ([#405](https://github.com/badlogic/pi-mono/pull/405) by [@hjanuschka](https://github.com/hjanuschka))
- `/quit` and `/exit` slash commands to gracefully exit the application. Unlike double Ctrl+C, these properly await hook and custom tool cleanup handlers before exiting. ([#426](https://github.com/badlogic/pi-mono/pull/426) by [@ben-vargas](https://github.com/ben-vargas))

### Fixed

- Subagent example README referenced incorrect filename `subagent.ts` instead of `index.ts` ([#427](https://github.com/badlogic/pi-mono/pull/427) by [@Whamp](https://github.com/Whamp))

## [0.32.3] - 2026-01-03

### Fixed

- `--list-models` no longer shows Google Vertex AI models without explicit authentication configured
- JPEG/GIF/WebP images not displaying in terminals using Kitty graphics protocol (Kitty, Ghostty, WezTerm). The protocol requires PNG format, so non-PNG images are now converted before display.
- Version check URL typo preventing update notifications from working ([#423](https://github.com/badlogic/pi-mono/pull/423) by [@skuridin](https://github.com/skuridin))
- Large images exceeding Anthropic's 5MB limit now retry with progressive quality/size reduction ([#424](https://github.com/badlogic/pi-mono/pull/424) by [@mitsuhiko](https://github.com/mitsuhiko))

## [0.32.2] - 2026-01-03

### Added

- `$ARGUMENTS` syntax for custom slash commands as alternative to `$@` for all arguments joined. Aligns with patterns used by Claude, Codex, and OpenCode. Both syntaxes remain fully supported. ([#418](https://github.com/badlogic/pi-mono/pull/418) by [@skuridin](https://github.com/skuridin))

### Changed

- **Slash commands and hook commands now work during streaming**: Previously, using a slash command or hook command while the agent was streaming would crash with "Agent is already processing". Now:
  - Hook commands execute immediately (they manage their own LLM interaction via `pi.sendMessage()`)
  - File-based slash commands are expanded and queued via steer/followUp
  - `steer()` and `followUp()` now expand file-based slash commands and error on hook commands (hook commands cannot be queued)
  - `prompt()` accepts new `streamingBehavior` option (`"steer"` or `"followUp"`) to specify queueing behavior during streaming
  - RPC `prompt` command now accepts optional `streamingBehavior` field
    ([#420](https://github.com/badlogic/pi-mono/issues/420))

### Fixed

- Slash command argument substitution now processes positional arguments (`$1`, `$2`, etc.) before all-arguments (`$@`, `$ARGUMENTS`) to prevent recursive substitution when argument values contain dollar-digit patterns like `$100`. ([#418](https://github.com/badlogic/pi-mono/pull/418) by [@skuridin](https://github.com/skuridin))

## [0.32.1] - 2026-01-03

### Added

- Shell commands without context contribution: use `!!command` to execute a bash command that is shown in the TUI and saved to session history but excluded from LLM context. Useful for running commands you don't want the AI to see. ([#414](https://github.com/badlogic/pi-mono/issues/414))

### Fixed

- Edit tool diff not displaying in TUI due to race condition between async preview computation and tool execution

## [0.32.0] - 2026-01-03

### Breaking Changes

- **Queue API replaced with steer/followUp**: The `queueMessage()` method has been split into two methods with different delivery semantics ([#403](https://github.com/badlogic/pi-mono/issues/403)):
  - `steer(text)`: Interrupts the agent mid-run (Enter while streaming). Delivered after current tool execution.
  - `followUp(text)`: Waits until the agent finishes (Alt+Enter while streaming). Delivered only when agent stops.
- **Settings renamed**: `queueMode` setting renamed to `steeringMode`. Added new `followUpMode` setting. Old settings.json files are migrated automatically.
- **AgentSession methods renamed**:
  - `queueMessage()` → `steer()` and `followUp()`
  - `queueMode` getter → `steeringMode` and `followUpMode` getters
  - `setQueueMode()` → `setSteeringMode()` and `setFollowUpMode()`
  - `queuedMessageCount` → `pendingMessageCount`
  - `getQueuedMessages()` → `getSteeringMessages()` and `getFollowUpMessages()`
  - `clearQueue()` now returns `{ steering: string[], followUp: string[] }`
  - `hasQueuedMessages()` → `hasPendingMessages()`
- **Hook API signature changed**: `pi.sendMessage()` second parameter changed from `triggerTurn?: boolean` to `options?: { triggerTurn?, deliverAs? }`. Use `deliverAs: "followUp"` for follow-up delivery. Affects both hooks and internal `sendHookMessage()` method.
- **RPC API changes**:
  - `queue_message` command → `steer` and `follow_up` commands
  - `set_queue_mode` command → `set_steering_mode` and `set_follow_up_mode` commands
  - `RpcSessionState.queueMode` → `steeringMode` and `followUpMode`
- **Settings UI**: "Queue mode" setting split into "Steering mode" and "Follow-up mode"

### Added

- Configurable double-escape action: choose whether double-escape with empty editor opens `/tree` (default) or `/branch`. Configure via `/settings` or `doubleEscapeAction` in settings.json ([#404](https://github.com/badlogic/pi-mono/issues/404))
- Vertex AI provider (`google-vertex`): access Gemini models via Google Cloud Vertex AI using Application Default Credentials ([#300](https://github.com/badlogic/pi-mono/pull/300) by [@default-anton](https://github.com/default-anton))
- Built-in provider overrides in `models.json`: override just `baseUrl` to route a built-in provider through a proxy while keeping all its models, or define `models` to fully replace the provider ([#406](https://github.com/badlogic/pi-mono/pull/406) by [@yevhen](https://github.com/yevhen))
- Automatic image resizing: images larger than 2000x2000 are resized for better model compatibility. Original dimensions are injected into the prompt. Controlled via `/settings` or `images.autoResize` in settings.json. ([#402](https://github.com/badlogic/pi-mono/pull/402) by [@mitsuhiko](https://github.com/mitsuhiko))
- Alt+Enter keybind to queue follow-up messages while agent is streaming
- `Theme` and `ThemeColor` types now exported for hooks using `ctx.ui.custom()`
- Terminal window title now displays "pi - dirname" to identify which project session you're in ([#407](https://github.com/badlogic/pi-mono/pull/407) by [@kaofelix](https://github.com/kaofelix))

### Changed

- Editor component now uses word wrapping instead of character-level wrapping for better readability ([#382](https://github.com/badlogic/pi-mono/pull/382) by [@nickseelert](https://github.com/nickseelert))

### Fixed

- `/model` selector now opens instantly instead of waiting for OAuth token refresh. Token refresh is deferred until a model is actually used.
- Shift+Space, Shift+Backspace, and Shift+Delete now work correctly in Kitty-protocol terminals (Kitty, WezTerm, etc.) instead of being silently ignored ([#411](https://github.com/badlogic/pi-mono/pull/411) by [@nathyong](https://github.com/nathyong))
- `AgentSession.prompt()` now throws if called while the agent is already streaming, preventing race conditions. Use `steer()` or `followUp()` to queue messages during streaming.
- Ctrl+C now works like Escape in selector components, so mashing Ctrl+C will eventually close the program ([#400](https://github.com/badlogic/pi-mono/pull/400) by [@mitsuhiko](https://github.com/mitsuhiko))

## [0.31.1] - 2026-01-02

### Fixed

- Model selector no longer allows negative index when pressing arrow keys before models finish loading ([#398](https://github.com/badlogic/pi-mono/pull/398) by [@mitsuhiko](https://github.com/mitsuhiko))
- Type guard functions (`isBashToolResult`, etc.) now exported at runtime, not just in type declarations ([#397](https://github.com/badlogic/pi-mono/issues/397))

## [0.31.0] - 2026-01-02

This release introduces session trees for in-place branching, major API changes to hooks and custom tools, and structured compaction with file tracking.

### Session Tree

Sessions now use a tree structure with `id`/`parentId` fields. This enables in-place branching: navigate to any previous point with `/tree`, continue from there, and switch between branches while preserving all history in a single file.

**Existing sessions are automatically migrated** (v1 → v2) on first load. No manual action required.

New entry types: `BranchSummaryEntry` (context from abandoned branches), `CustomEntry` (hook state), `CustomMessageEntry` (hook-injected messages), `LabelEntry` (bookmarks).

See [docs/session.md](docs/session.md) for the file format and `SessionManager` API.

### Hooks Migration

The hooks API has been restructured with more granular events and better session access.

**Type renames:**

- `HookEventContext` → `HookContext`
- `HookCommandContext` is now a new interface extending `HookContext` with session control methods

**Event changes:**

- The monolithic `session` event is now split into granular events: `session_start`, `session_before_switch`, `session_switch`, `session_before_branch`, `session_branch`, `session_before_compact`, `session_compact`, `session_shutdown`
- `session_before_switch` and `session_switch` events now include `reason: "new" | "resume"` to distinguish between `/new` and `/resume`
- New `session_before_tree` and `session_tree` events for `/tree` navigation (hook can provide custom branch summary)
- New `before_agent_start` event: inject messages before the agent loop starts
- New `context` event: modify messages non-destructively before each LLM call
- Session entries are no longer passed in events. Use `ctx.sessionManager.getEntries()` or `ctx.sessionManager.getBranch()` instead

**API changes:**

- `pi.send(text, attachments?)` → `pi.sendMessage(message, triggerTurn?)` (creates `CustomMessageEntry`)
- New `pi.appendEntry(customType, data?)` for hook state persistence (not in LLM context)
- New `pi.registerCommand(name, options)` for custom slash commands (handler receives `HookCommandContext`)
- New `pi.registerMessageRenderer(customType, renderer)` for custom TUI rendering
- New `ctx.isIdle()`, `ctx.abort()`, `ctx.hasQueuedMessages()` for agent state (available in all events)
- New `ctx.ui.editor(title, prefill?)` for multi-line text editing with Ctrl+G external editor support
- New `ctx.ui.custom(component)` for full TUI component rendering with keyboard focus
- New `ctx.ui.setStatus(key, text)` for persistent status text in footer (multiple hooks can set their own)
- New `ctx.ui.theme` getter for styling text with theme colors
- `ctx.exec()` moved to `pi.exec()`
- `ctx.sessionFile` → `ctx.sessionManager.getSessionFile()`
- New `ctx.modelRegistry` and `ctx.model` for API key resolution

**HookCommandContext (slash commands only):**

- `ctx.waitForIdle()` - wait for agent to finish streaming
- `ctx.newSession(options?)` - create new sessions with optional setup callback
- `ctx.fork(entryId) - fork from a specific entry, creating a new session file
- `ctx.navigateTree(targetId, options?)` - navigate the session tree

These methods are only on `HookCommandContext` (not `HookContext`) because they can deadlock if called from event handlers that run inside the agent loop.

**Removed:**

- `hookTimeout` setting (hooks no longer have timeouts; use Ctrl+C to abort)
- `resolveApiKey` parameter (use `ctx.modelRegistry.getApiKey(model)`)

See [docs/hooks.md](docs/hooks.md) and [examples/hooks/](examples/hooks/) for the current API.

### Custom Tools Migration

The custom tools API has been restructured to mirror the hooks pattern with a context object.

**Type renames:**

- `CustomAgentTool` → `CustomTool`
- `ToolAPI` → `CustomToolAPI`
- `ToolContext` → `CustomToolContext`
- `ToolSessionEvent` → `CustomToolSessionEvent`

**Execute signature changed:**

```typescript
// Before (v0.30.2)
execute(toolCallId, params, signal, onUpdate)

// After
execute(toolCallId, params, onUpdate, ctx, signal?)
```

The new `ctx: CustomToolContext` provides `sessionManager`, `modelRegistry`, `model`, and agent state methods:

- `ctx.isIdle()` - check if agent is streaming
- `ctx.hasQueuedMessages()` - check if user has queued messages (skip interactive prompts)
- `ctx.abort()` - abort current operation (fire-and-forget)

**Session event changes:**

- `CustomToolSessionEvent` now only has `reason` and `previousSessionFile`
- Session entries are no longer in the event. Use `ctx.sessionManager.getBranch()` or `ctx.sessionManager.getEntries()` to reconstruct state
- Reasons: `"start" | "switch" | "branch" | "tree" | "shutdown"` (no separate `"new"` reason; `/new` triggers `"switch"`)
- `dispose()` method removed. Use `onSession` with `reason: "shutdown"` for cleanup

See [docs/custom-tools.md](docs/custom-tools.md) and [examples/custom-tools/](examples/custom-tools/) for the current API.

### SDK Migration

**Type changes:**

- `CustomAgentTool` → `CustomTool`
- `AppMessage` → `AgentMessage`
- `sessionFile` returns `string | undefined` (was `string | null`)
- `model` returns `Model | undefined` (was `Model | null`)
- `Attachment` type removed. Use `ImageContent` from `@mariozechner/pi-ai` instead. Add images directly to message content arrays.

**AgentSession API:**

- `branch(entryIndex: number)` → `branch(entryId: string)`
- `getUserMessagesForBranching()` returns `{ entryId, text }` instead of `{ entryIndex, text }`
- `reset()` → `newSession(options?)` where options has optional `parentSession` for lineage tracking
- `newSession()` and `switchSession()` now return `Promise<boolean>` (false if cancelled by hook)
- New `navigateTree(targetId, options?)` for in-place tree navigation

**Hook integration:**

- New `sendHookMessage(message, triggerTurn?)` for hook message injection

**SessionManager API:**

- Method renames: `saveXXX()` → `appendXXX()` (e.g., `appendMessage`, `appendCompaction`)
- `branchInPlace()` → `branch()`
- `reset()` → `newSession(options?)` with optional `parentSession` for lineage tracking
- `createBranchedSessionFromEntries(entries, index)` → `createBranchedSession(leafId)`
- `SessionHeader.branchedFrom` → `SessionHeader.parentSession`
- `saveCompaction(entry)` → `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?)`
- `getEntries()` now excludes the session header (use `getHeader()` separately)
- `getSessionFile()` returns `string | undefined` (undefined for in-memory sessions)
- New tree methods: `getTree()`, `getBranch()`, `getLeafId()`, `getLeafEntry()`, `getEntry()`, `getChildren()`, `getLabel()`
- New append methods: `appendCustomEntry()`, `appendCustomMessageEntry()`, `appendLabelChange()`
- New branch methods: `branch(entryId)`, `branchWithSummary()`

**ModelRegistry (new):**

`ModelRegistry` is a new class that manages model discovery and API key resolution. It combines built-in models with custom models from `models.json` and resolves API keys via `AuthStorage`.

```typescript
import {
  discoverAuthStorage,
  discoverModels,
} from "@bastani/atomic";

const authStorage = discoverAuthStorage(); // ~/.pi/agent/auth.json
const modelRegistry = discoverModels(authStorage); // + ~/.pi/agent/models.json

// Get all models (built-in + custom)
const allModels = modelRegistry.getAll();

// Get only models with valid API keys
const available = await modelRegistry.getAvailable();

// Find specific model
const model = modelRegistry.find("anthropic", "claude-sonnet-4-20250514");

// Get API key for a model
const apiKey = await modelRegistry.getApiKey(model);
```

This replaces the old `resolveApiKey` callback pattern. Hooks and custom tools access it via `ctx.modelRegistry`.

**Renamed exports:**

- `messageTransformer` → `convertToLlm`
- `SessionContext` alias `LoadedSession` removed

See [docs/sdk.md](docs/sdk.md) and [examples/sdk/](examples/sdk/) for the current API.

### RPC Migration

**Session commands:**

- `reset` command → `new_session` command with optional `parentSession` field

**Branching commands:**

- `branch` command: `entryIndex` → `entryId`
- `get_branch_messages` response: `entryIndex` → `entryId`

**Type changes:**

- Messages are now `AgentMessage` (was `AppMessage`)
- `prompt` command: `attachments` field replaced with `images` field using `ImageContent` format

**Compaction events:**

- `auto_compaction_start` now includes `reason` field (`"threshold"` or `"overflow"`)
- `auto_compaction_end` now includes `willRetry` field
- `compact` response includes full `CompactionResult` (`summary`, `firstKeptEntryId`, `tokensBefore`, `details`)

See [docs/rpc.md](docs/rpc.md) for the current protocol.

### Structured Compaction

Compaction and branch summarization now use a structured output format:

- Clear sections: Goal, Progress, Key Information, File Operations
- File tracking: `readFiles` and `modifiedFiles` arrays in `details`, accumulated across compactions
- Conversations are serialized to text before summarization to prevent the model from "continuing" them

The `before_compact` and `before_tree` hook events allow custom compaction implementations. See [docs/compaction.md](docs/compaction.md).

### Interactive Mode

**`/tree` command:**

- Navigate the full session tree in-place
- Search by typing, page with ←/→
- Filter modes (Ctrl+O): default → no-tools → user-only → labeled-only → all
- Press `l` to label entries as bookmarks
- Selecting a branch switches context and optionally injects a summary of the abandoned branch

**Entry labels:**

- Bookmark any entry via `/tree` → select → `l`
- Labels appear in tree view and persist as `LabelEntry`

**Theme changes (breaking for custom themes):**

Custom themes must add these new color tokens or they will fail to load:

- `selectedBg`: background for selected/highlighted items in tree selector and other components
- `customMessageBg`: background for hook-injected messages (`CustomMessageEntry`)
- `customMessageText`: text color for hook messages
- `customMessageLabel`: label color for hook messages (the `[customType]` prefix)

Total color count increased from 46 to 50. See [docs/themes.md](docs/themes.md) for the full color list and copy values from the built-in dark/light themes.

**Settings:**

- `enabledModels`: allowlist models in `settings.json` (same format as `--models` CLI)

### Added

- `ctx.ui.setStatus(key, text)` for hooks to display persistent status text in the footer ([#385](https://github.com/badlogic/pi-mono/pull/385) by [@prateekmedia](https://github.com/prateekmedia))
- `ctx.ui.theme` getter for styling status text and other output with theme colors
- `/share` command to upload session as a secret GitHub gist and get a shareable URL via pi.dev ([#380](https://github.com/badlogic/pi-mono/issues/380))
- HTML export now includes a tree visualization sidebar for navigating session branches ([#375](https://github.com/badlogic/pi-mono/issues/375))
- HTML export supports keyboard shortcuts: Ctrl+T to toggle thinking blocks, Ctrl+O to toggle tool outputs
- HTML export supports theme-configurable background colors via optional `export` section in theme JSON ([#387](https://github.com/badlogic/pi-mono/pull/387) by [@mitsuhiko](https://github.com/mitsuhiko))
- HTML export syntax highlighting now uses theme colors and matches TUI rendering
- **Snake game example hook**: Demonstrates `ui.custom()`, `registerCommand()`, and session persistence. See [examples/hooks/snake.ts](examples/hooks/snake.ts).
- **`thinkingText` theme token**: Configurable color for thinking block text. ([#366](https://github.com/badlogic/pi-mono/pull/366) by [@paulbettner](https://github.com/paulbettner))

### Changed

- **Entry IDs**: Session entries now use short 8-character hex IDs instead of full UUIDs
- **API key priority**: `ANTHROPIC_OAUTH_TOKEN` now takes precedence over `ANTHROPIC_API_KEY`
- HTML export template split into separate files (template.html, template.css, template.js) for easier maintenance

### Fixed

- HTML export now properly sanitizes user messages containing HTML tags like `<style>` that could break DOM rendering
- Crash when displaying bash output containing Unicode format characters like U+0600-U+0604 ([#372](https://github.com/badlogic/pi-mono/pull/372) by [@HACKE-RC](https://github.com/HACKE-RC))
- **Footer shows full session stats**: Token usage and cost now include all messages, not just those after compaction. ([#322](https://github.com/badlogic/pi-mono/issues/322))
- **Status messages spam chat log**: Rapidly changing settings (e.g., thinking level via Shift+Tab) would add multiple status lines. Sequential status updates now coalesce into a single line. ([#365](https://github.com/badlogic/pi-mono/pull/365) by [@paulbettner](https://github.com/paulbettner))
- **Toggling thinking blocks during streaming shows nothing**: Pressing Ctrl+T while streaming would hide the current message until streaming completed.
- **Resuming session resets thinking level to off**: Initial model and thinking level were not saved to session file, causing `--resume`/`--continue` to default to `off`. ([#342](https://github.com/badlogic/pi-mono/issues/342) by [@aliou](https://github.com/aliou))
- **Hook `tool_result` event ignores errors from custom tools**: The `tool_result` hook event was never emitted when tools threw errors, and always had `isError: false` for successful executions. Now emits the event with correct `isError` value in both success and error cases. ([#374](https://github.com/badlogic/pi-mono/issues/374) by [@nicobailon](https://github.com/nicobailon))
- **Edit tool fails on Windows due to CRLF line endings**: Files with CRLF line endings now match correctly when LLMs send LF-only text. Line endings are normalized before matching and restored to original style on write. ([#355](https://github.com/badlogic/pi-mono/issues/355) by [@Pratham-Dubey](https://github.com/Pratham-Dubey))
- **Edit tool fails on files with UTF-8 BOM**: Files with UTF-8 BOM marker could cause "text not found" errors since the LLM doesn't include the invisible BOM character. BOM is now stripped before matching and restored on write. ([#394](https://github.com/badlogic/pi-mono/pull/394) by [@prathamdby](https://github.com/prathamdby))
- **Use bash instead of sh on Unix**: Fixed shell commands using `/bin/sh` instead of `/bin/bash` on Unix systems. ([#328](https://github.com/badlogic/pi-mono/pull/328) by [@dnouri](https://github.com/dnouri))
- **OAuth login URL clickable**: Made OAuth login URLs clickable in terminal. ([#349](https://github.com/badlogic/pi-mono/pull/349) by [@Cursivez](https://github.com/Cursivez))
- **Improved error messages**: Better error messages when `apiKey` or `model` are missing. ([#346](https://github.com/badlogic/pi-mono/pull/346) by [@ronyrus](https://github.com/ronyrus))
- **Session file validation**: `findMostRecentSession()` now validates session headers before returning, preventing non-session JSONL files from being loaded
- **Compaction error handling**: `generateSummary()` and `generateTurnPrefixSummary()` now throw on LLM errors instead of returning empty strings
- **Compaction with branched sessions**: Fixed compaction incorrectly including entries from abandoned branches, causing token overflow errors. Compaction now uses `sessionManager.getPath()` to work only on the current branch path, eliminating 80+ lines of duplicate entry collection logic between `prepareCompaction()` and `compact()`
- **enabledModels glob patterns**: `--models` and `enabledModels` now support glob patterns like `github-copilot/*` or `*sonnet*`. Previously, patterns were only matched literally or via substring search. ([#337](https://github.com/badlogic/pi-mono/issues/337))

## [0.30.2] - 2025-12-26

### Changed

- **Consolidated migrations**: Moved auth migration from `AuthStorage.migrateLegacy()` to new `migrations.ts` module.

## [0.30.1] - 2025-12-26

### Fixed

- **Sessions saved to wrong directory**: In v0.30.0, sessions were being saved to `~/.pi/agent/` instead of `~/.pi/agent/sessions/<encoded-cwd>/`, breaking `--resume` and `/resume`. Misplaced sessions are automatically migrated on startup. ([#320](https://github.com/badlogic/pi-mono/issues/320) by [@aliou](https://github.com/aliou))
- **Custom system prompts missing context**: When using a custom system prompt string, project context files (AGENTS.md), skills, date/time, and working directory were not appended. ([#321](https://github.com/badlogic/pi-mono/issues/321))

## [0.30.0] - 2025-12-25

### Breaking Changes

- **SessionManager API**: The second parameter of `create()`, `continueRecent()`, and `list()` changed from `agentDir` to `sessionDir`. When provided, it specifies the session directory directly (no cwd encoding). When omitted, uses default (`~/.pi/agent/sessions/<encoded-cwd>/`). `open()` no longer takes `agentDir`. ([#313](https://github.com/badlogic/pi-mono/pull/313))

### Added

- **`--session-dir` flag**: Use a custom directory for sessions instead of the default `~/.pi/agent/sessions/<encoded-cwd>/`. Works with `-c` (continue) and `-r` (resume) flags. ([#313](https://github.com/badlogic/pi-mono/pull/313) by [@scutifer](https://github.com/scutifer))
- **Reverse model cycling and model selector**: Shift+Ctrl+P cycles models backward, Ctrl+L opens model selector (retaining text in editor). ([#315](https://github.com/badlogic/pi-mono/pull/315) by [@mitsuhiko](https://github.com/mitsuhiko))

## [0.29.1] - 2025-12-25

### Added

- **Automatic custom system prompt loading**: Pi now auto-loads `SYSTEM.md` files to replace the default system prompt. Project-local `.pi/SYSTEM.md` takes precedence over global `~/.pi/agent/SYSTEM.md`. CLI `--system-prompt` flag overrides both. ([#309](https://github.com/badlogic/pi-mono/issues/309))
- **Unified `/settings` command**: New settings menu consolidating thinking level, theme, queue mode, auto-compact, show images, hide thinking, and collapse changelog. Replaces individual `/thinking`, `/queue`, `/theme`, `/autocompact`, and `/show-images` commands. ([#310](https://github.com/badlogic/pi-mono/issues/310))

### Fixed

- **Custom tools/hooks with typebox subpath imports**: Fixed jiti alias for `@sinclair/typebox` to point to package root instead of entry file, allowing imports like `@sinclair/typebox/compiler` to resolve correctly. ([#311](https://github.com/badlogic/pi-mono/issues/311) by [@kim0](https://github.com/kim0))

## [0.29.0] - 2025-12-25

### Breaking Changes

- **Renamed `/clear` to `/new`**: The command to start a fresh session is now `/new`. Hook event reasons `before_clear`/`clear` are now `before_new`/`new`. Merry Christmas [@mitsuhiko](https://github.com/mitsuhiko)! ([#305](https://github.com/badlogic/pi-mono/pull/305))

### Added

- **Auto-space before pasted file paths**: When pasting a file path (starting with `/`, `~`, or `.`) after a word character, a space is automatically prepended. ([#307](https://github.com/badlogic/pi-mono/pull/307) by [@mitsuhiko](https://github.com/mitsuhiko))
- **Word navigation in input fields**: Added Ctrl+Left/Right and Alt+Left/Right for word-by-word cursor movement. ([#306](https://github.com/badlogic/pi-mono/pull/306) by [@kim0](https://github.com/kim0))
- **Full Unicode input**: Input fields now accept Unicode characters beyond ASCII. ([#306](https://github.com/badlogic/pi-mono/pull/306) by [@kim0](https://github.com/kim0))

### Fixed

- **Readline-style Ctrl+W**: Now skips trailing whitespace before deleting the preceding word, matching standard readline behavior. ([#306](https://github.com/badlogic/pi-mono/pull/306) by [@kim0](https://github.com/kim0))

## [0.28.0] - 2025-12-25

### Changed

- **Credential storage refactored**: API keys and OAuth tokens are now stored in `~/.pi/agent/auth.json` instead of `oauth.json` and `settings.json`. Existing credentials are automatically migrated on first run. ([#296](https://github.com/badlogic/pi-mono/issues/296))

- **SDK API changes** ([#296](https://github.com/badlogic/pi-mono/issues/296)):

  - Added `AuthStorage` class for credential management (API keys and OAuth tokens)
  - Added `ModelRegistry` class for model discovery and API key resolution
  - Added `discoverAuthStorage()` and `discoverModels()` discovery functions
  - `createAgentSession()` now accepts `authStorage` and `modelRegistry` options
  - Removed `configureOAuthStorage()`, `defaultGetApiKey()`, `findModel()`, `discoverAvailableModels()`
  - Removed `getApiKey` callback option (use `AuthStorage.setRuntimeApiKey()` for runtime overrides)
  - Use `getModel()` from `@mariozechner/pi-ai` for built-in models, `modelRegistry.find()` for custom models + built-in models
  - See updated [SDK documentation](docs/sdk.md) and [README](README.md)

- **Settings changes**: Removed `apiKeys` from `settings.json`. Use `auth.json` instead. ([#296](https://github.com/badlogic/pi-mono/issues/296))

### Fixed

- **Duplicate skill warnings for symlinks**: Skills loaded via symlinks pointing to the same file are now silently deduplicated instead of showing name collision warnings. ([#304](https://github.com/badlogic/pi-mono/pull/304) by [@mitsuhiko](https://github.com/mitsuhiko))

## [0.27.9] - 2025-12-24

### Fixed

- **Model selector and --list-models with settings.json API keys**: Models with API keys configured in settings.json (but not in environment variables) now properly appear in the /model selector and `--list-models` output. ([#295](https://github.com/badlogic/pi-mono/issues/295))

## [0.27.8] - 2025-12-24

### Fixed

- **API key priority**: OAuth tokens now take priority over settings.json API keys. Previously, an API key in settings.json would trump OAuth, causing users logged in with a plan (unlimited tokens) to be billed via PAYG instead.

## [0.27.7] - 2025-12-24

### Fixed

- **Thinking tag leakage**: Fixed Claude mimicking literal `</thinking>` tags in responses. Unsigned thinking blocks (from aborted streams) are now converted to plain text without `<thinking>` tags. The TUI still displays them as thinking blocks. ([#302](https://github.com/badlogic/pi-mono/pull/302) by [@nicobailon](https://github.com/nicobailon))

## [0.27.6] - 2025-12-24

### Added

- **Compaction hook improvements**: The `before_compact` session event now includes:

  - `previousSummary`: Summary from the last compaction (if any), so hooks can preserve accumulated context
  - `messagesToKeep`: Messages that will be kept after the summary (recent turns), in addition to `messagesToSummarize`
  - `resolveApiKey`: Function to resolve API keys for any model (checks settings, OAuth, env vars)
  - Removed `apiKey` string in favor of `resolveApiKey` for more flexibility

- **SessionManager API cleanup**:
  - Renamed `loadSessionFromEntries()` to `buildSessionContext()` (builds LLM context from entries, handling compaction)
  - Renamed `loadEntries()` to `getEntries()` (returns defensive copy of all session entries)
  - Added `buildSessionContext()` method to SessionManager

## [0.27.5] - 2025-12-24

### Added

- **HTML export syntax highlighting**: Code blocks in markdown and tool outputs (read, write) now have syntax highlighting using highlight.js with theme-aware colors matching the TUI.
- **HTML export improvements**: Render markdown server-side using marked (tables, headings, code blocks, etc.), honor user's chosen theme (light/dark), add image rendering for user messages, and style code blocks with TUI-like language markers. ([@scutifer](https://github.com/scutifer))

### Fixed

- **Ghostty inline images in tmux**: Fixed terminal detection for Ghostty when running inside tmux by checking `GHOSTTY_RESOURCES_DIR` env var. ([#299](https://github.com/badlogic/pi-mono/pull/299) by [@nicobailon](https://github.com/nicobailon))

## [0.27.4] - 2025-12-24

### Fixed

- **Symlinked skill directories**: Skills in symlinked directories (e.g., `~/.pi/agent/skills/my-skills -> /path/to/skills`) are now correctly discovered and loaded.

## [0.27.3] - 2025-12-24

### Added

- **API keys in settings.json**: Store API keys in `~/.pi/agent/settings.json` under the `apiKeys` field (e.g., `{ "apiKeys": { "anthropic": "sk-..." } }`). Settings keys take priority over environment variables. ([#295](https://github.com/badlogic/pi-mono/issues/295))

### Fixed

- **Allow startup without API keys**: Interactive mode no longer throws when no API keys are configured. Users can now start the agent and use `/login` to authenticate. ([#288](https://github.com/badlogic/pi-mono/issues/288))
- **`--system-prompt` file path support**: The `--system-prompt` argument now correctly resolves file paths (like `--append-system-prompt` already did). ([#287](https://github.com/badlogic/pi-mono/pull/287) by [@scutifer](https://github.com/scutifer))

## [0.27.2] - 2025-12-23

### Added

- **Skip conversation restore on branch**: Hooks can return `{ skipConversationRestore: true }` from `before_branch` to create the branched session file without restoring conversation messages. Useful for checkpoint hooks that restore files separately. ([#286](https://github.com/badlogic/pi-mono/pull/286) by [@nicobarray](https://github.com/nicobarray))

## [0.27.1] - 2025-12-22

### Fixed

- **Skill discovery performance**: Skip `node_modules` directories when recursively scanning for skills. Fixes ~60ms startup delay when skill directories contain npm dependencies.

### Added

- **Startup timing instrumentation**: Set `PI_TIMING=1` to see startup performance breakdown (interactive mode only).

## [0.27.0] - 2025-12-22

### Breaking

- **Session hooks API redesign**: Merged `branch` event into `session` event. `BranchEvent`, `BranchEventResult` types and `pi.on("branch", ...)` removed. Use `pi.on("session", ...)` with `reason: "before_branch" | "branch"` instead. `AgentSession.branch()` returns `{ cancelled }` instead of `{ skipped }`. `AgentSession.reset()` and `switchSession()` now return `boolean` (false if cancelled by hook). RPC commands `reset`, `switch_session`, and `branch` now include `cancelled` in response data. ([#278](https://github.com/badlogic/pi-mono/issues/278))

### Added

- **Session lifecycle hooks**: Added `before_*` variants (`before_switch`, `before_clear`, `before_branch`) that fire before actions and can be cancelled with `{ cancel: true }`. Added `shutdown` reason for graceful exit handling. ([#278](https://github.com/badlogic/pi-mono/issues/278))

### Fixed

- **File tab completion display**: File paths no longer get cut off early. Folders now show trailing `/` and removed redundant "directory"/"file" labels to maximize horizontal space. ([#280](https://github.com/badlogic/pi-mono/issues/280))

- **Bash tool visual line truncation**: Fixed bash tool output in collapsed mode to use visual line counting (accounting for line wrapping) instead of logical line counting. Now consistent with bash-execution.ts behavior. Extracted shared `truncateToVisualLines` utility. ([#275](https://github.com/badlogic/pi-mono/issues/275))

## [0.26.1] - 2025-12-22

### Fixed

- **SDK tools respect cwd**: Core tools (bash, read, edit, write, grep, find, ls) now properly use the `cwd` option from `createAgentSession()`. Added tool factory functions (`createBashTool`, `createReadTool`, etc.) for SDK users who specify custom `cwd` with explicit tools. ([#279](https://github.com/badlogic/pi-mono/issues/279))

## [0.26.0] - 2025-12-22

### Added

- **SDK for programmatic usage**: New `createAgentSession()` factory with full control over model, tools, hooks, skills, session persistence, and settings. Philosophy: "omit to discover, provide to override". Includes 12 examples and comprehensive documentation. ([#272](https://github.com/badlogic/pi-mono/issues/272))

- **Project-specific settings**: Settings now load from both `~/.pi/agent/settings.json` (global) and `<cwd>/.pi/settings.json` (project). Project settings override global with deep merge for nested objects. Project settings are read-only (for version control). ([#276](https://github.com/badlogic/pi-mono/pull/276))

- **SettingsManager static factories**: `SettingsManager.create(cwd?, agentDir?)` for file-based settings, `SettingsManager.inMemory(settings?)` for testing. Added `applyOverrides()` for programmatic overrides.

- **SessionManager static factories**: `SessionManager.create()`, `SessionManager.open()`, `SessionManager.continueRecent()`, `SessionManager.inMemory()`, `SessionManager.list()` for flexible session management.

## [0.25.4] - 2025-12-22

### Fixed

- **Syntax highlighting stderr spam**: Fixed cli-highlight logging errors to stderr when markdown contains malformed code fences (e.g., missing newlines around closing backticks). Now validates language identifiers before highlighting and falls back silently to plain text. ([#274](https://github.com/badlogic/pi-mono/issues/274))

## [0.25.3] - 2025-12-21

### Added

- **Gemini 3 preview models**: Added `gemini-3-pro-preview` and `gemini-3-flash-preview` to the google-gemini-cli provider. ([#264](https://github.com/badlogic/pi-mono/pull/264) by [@LukeFost](https://github.com/LukeFost))

- **External editor support**: Press `Ctrl+G` to edit your message in an external editor. Uses `$VISUAL` or `$EDITOR` environment variable. On successful save, the message is replaced; on cancel, the original is kept. ([#266](https://github.com/badlogic/pi-mono/pull/266) by [@aliou](https://github.com/aliou))

- **Process suspension**: Press `Ctrl+Z` to suspend pi and return to the shell. Resume with `fg` as usual. ([#267](https://github.com/badlogic/pi-mono/pull/267) by [@aliou](https://github.com/aliou))

- **Configurable skills directories**: Added granular control over skill sources with `enableCodexUser`, `enableClaudeUser`, `enableClaudeProject`, `enablePiUser`, `enablePiProject` toggles, plus `customDirectories` and `ignoredSkills` settings. ([#269](https://github.com/badlogic/pi-mono/pull/269) by [@nicobailon](https://github.com/nicobailon))

- **Skills CLI filtering**: Added `--skills <patterns>` flag for filtering skills with glob patterns. Also added `includeSkills` setting and glob pattern support for `ignoredSkills`. ([#268](https://github.com/badlogic/pi-mono/issues/268))

## [0.25.2] - 2025-12-21

### Fixed

- **Image shifting in tool output**: Fixed an issue where images in tool output would shift down (due to accumulating spacers) each time the tool output was expanded or collapsed via Ctrl+O.

## [0.25.1] - 2025-12-21

### Fixed

- **Gemini image reading broken**: Fixed the `read` tool returning images causing flaky/broken responses with Gemini models. Images in tool results are now properly formatted per the Gemini API spec.

- **Tab completion for absolute paths**: Fixed tab completion producing `//tmp` instead of `/tmp/`. Also fixed symlinks to directories (like `/tmp`) not getting a trailing slash, which prevented continuing to tab through subdirectories.

## [0.25.0] - 2025-12-20

### Added

- **Interruptible tool execution**: Queuing a message while tools are executing now interrupts the current tool batch. Remaining tools are skipped with an error result, and your queued message is processed immediately. Useful for redirecting the agent mid-task. ([#259](https://github.com/badlogic/pi-mono/pull/259) by [@steipete](https://github.com/steipete))

- **Google Gemini CLI OAuth provider**: Access Gemini 2.0/2.5 models for free via Google Cloud Code Assist. Login with `/login` and select "Google Gemini CLI". Uses your Google account with rate limits.

- **Google Antigravity OAuth provider**: Access Gemini 3, Claude (sonnet/opus thinking models), and GPT-OSS models for free via Google's Antigravity sandbox. Login with `/login` and select "Antigravity". Uses your Google account with rate limits.

### Changed

- **Model selector respects --models scope**: The `/model` command now only shows models specified via `--models` flag when that flag is used, instead of showing all available models. This prevents accidentally selecting models from unintended providers. ([#255](https://github.com/badlogic/pi-mono/issues/255))

### Fixed

- **Connection errors not retried**: Added "connection error" to the list of retryable errors so Anthropic connection drops trigger auto-retry instead of silently failing. ([#252](https://github.com/badlogic/pi-mono/issues/252))

- **Thinking level not clamped on model switch**: Fixed TUI showing xhigh thinking level after switching to a model that doesn't support it. Thinking level is now automatically clamped to model capabilities. ([#253](https://github.com/badlogic/pi-mono/issues/253))

- **Cross-model thinking handoff**: Fixed error when switching between models with different thinking signature formats (e.g., GPT-OSS to Claude thinking models via Antigravity). Thinking blocks without signatures are now converted to text with `<thinking>` delimiters.

## [0.24.5] - 2025-12-20

### Fixed

- **Input buffering in iTerm2**: Fixed Ctrl+C, Ctrl+D, and other keys requiring multiple presses in iTerm2. The cell size query response parser was incorrectly holding back keyboard input.

## [0.24.4] - 2025-12-20

### Fixed

- **Arrow keys and Enter in selector components**: Fixed arrow keys and Enter not working in model selector, session selector, OAuth selector, and other selector components when Caps Lock or Num Lock is enabled. ([#243](https://github.com/badlogic/pi-mono/issues/243))

## [0.24.3] - 2025-12-19

### Fixed

- **Footer overflow on narrow terminals**: Fixed footer path display exceeding terminal width when resizing to very narrow widths, causing rendering crashes. /arminsayshi

## [0.24.2] - 2025-12-20

### Fixed

- **More Kitty keyboard protocol fixes**: Fixed Backspace, Enter, Home, End, and Delete keys not working with Caps Lock enabled. The initial fix in 0.24.1 missed several key handlers that were still using raw byte detection. Now all key handlers use the helper functions that properly mask out lock key bits. ([#243](https://github.com/badlogic/pi-mono/issues/243))

## [0.24.1] - 2025-12-19

### Added

- **OAuth and model config exports**: Scripts using `AgentSession` directly can now import `getAvailableModels`, `getApiKeyForModel`, `findModel`, `login`, `logout`, and `getOAuthProviders` from `@bastani/atomic` to reuse OAuth token storage and model resolution. ([#245](https://github.com/badlogic/pi-mono/issues/245))

- **xhigh thinking level for gpt-5.2 models**: The thinking level selector and shift+tab cycling now show xhigh option for gpt-5.2 and gpt-5.2-codex models (in addition to gpt-5.1-codex-max). ([#236](https://github.com/badlogic/pi-mono/pull/236) by [@theBucky](https://github.com/theBucky))

### Fixed

- **Hooks wrap custom tools**: Custom tools are now executed through the hook wrapper, so `tool_call`/`tool_result` hooks can observe, block, and modify custom tool executions (consistent with hook type docs). ([#248](https://github.com/badlogic/pi-mono/pull/248) by [@nicobailon](https://github.com/nicobailon))

- **Hook onUpdate callback forwarding**: The `onUpdate` callback is now correctly forwarded through the hook wrapper, fixing custom tool progress updates. ([#238](https://github.com/badlogic/pi-mono/pull/238) by [@nicobailon](https://github.com/nicobailon))

- **Terminal cleanup on Ctrl+C in session selector**: Fixed terminal not being properly restored when pressing Ctrl+C in the session selector. ([#247](https://github.com/badlogic/pi-mono/pull/247) by [@aliou](https://github.com/aliou))

- **OpenRouter models with colons in IDs**: Fixed parsing of OpenRouter model IDs that contain colons (e.g., `openrouter:meta-llama/llama-4-scout:free`). ([#242](https://github.com/badlogic/pi-mono/pull/242) by [@aliou](https://github.com/aliou))

- **Global AGENTS.md loaded twice**: Fixed global AGENTS.md being loaded twice when present in both `~/.pi/agent/` and the current directory. ([#239](https://github.com/badlogic/pi-mono/pull/239) by [@aliou](https://github.com/aliou))

- **Kitty keyboard protocol on Linux**: Fixed keyboard input not working in Ghostty on Linux when Num Lock is enabled. The Kitty protocol includes Caps Lock and Num Lock state in modifier values, which broke key detection. Now correctly masks out lock key bits when matching keyboard shortcuts. ([#243](https://github.com/badlogic/pi-mono/issues/243))

- **Emoji deletion and cursor movement**: Backspace, Delete, and arrow keys now correctly handle multi-codepoint characters like emojis. Previously, deleting an emoji would leave partial bytes, corrupting the editor state. ([#240](https://github.com/badlogic/pi-mono/issues/240))

## [0.24.0] - 2025-12-19

### Added

- **Subagent orchestration example**: Added comprehensive custom tool example for spawning and orchestrating sub-agents with isolated context windows. Includes scout/planner/reviewer/worker agents and workflow commands for multi-agent pipelines. ([#215](https://github.com/badlogic/pi-mono/pull/215) by [@nicobailon](https://github.com/nicobailon))

- **`getMarkdownTheme()` export**: Custom tools can now import `getMarkdownTheme()` from `@bastani/atomic` to use the same markdown styling as the main UI.

- **`pi.exec()` signal and timeout support**: Custom tools and hooks can now pass `{ signal, timeout }` options to `pi.exec()` for cancellation and timeout handling. The result includes a `killed` flag when the process was terminated.

- **Kitty keyboard protocol support**: Shift+Enter, Alt+Enter, Shift+Tab, Ctrl+D, and all Ctrl+key combinations now work in Ghostty, Kitty, WezTerm, and other modern terminals. ([#225](https://github.com/badlogic/pi-mono/pull/225) by [@kim0](https://github.com/kim0))

- **Dynamic API key refresh**: OAuth tokens (GitHub Copilot, Anthropic OAuth) are now refreshed before each LLM call, preventing failures in long-running agent loops where tokens expire mid-session. ([#223](https://github.com/badlogic/pi-mono/pull/223) by [@kim0](https://github.com/kim0))

- **`/hotkeys` command**: Shows all keyboard shortcuts in a formatted table.

- **Markdown table borders**: Tables now render with proper top and bottom borders.

### Changed

- **Subagent example improvements**: Parallel mode now streams updates from all tasks. Chain mode shows all completed steps during streaming. Expanded view uses proper markdown rendering with syntax highlighting. Usage footer shows turn count.

- **Skills standard compliance**: Skills now adhere to the [Agent Skills standard](https://agentskills.io/specification). Validates name (must match parent directory, lowercase, max 64 chars), description (required, max 1024 chars), and frontmatter fields. Warns on violations but remains lenient. Prompt format changed to XML structure. Removed `{baseDir}` placeholder in favor of relative paths. ([#231](https://github.com/badlogic/pi-mono/issues/231))

### Fixed

- **JSON mode stdout flush**: Fixed race condition where `pi --mode json` could exit before all output was written to stdout, causing consumers to miss final events.

- **Symlinked tools, hooks, and slash commands**: Discovery now correctly follows symlinks when scanning for custom tools, hooks, and slash commands. ([#219](https://github.com/badlogic/pi-mono/pull/219), [#232](https://github.com/badlogic/pi-mono/pull/232) by [@aliou](https://github.com/aliou))

### Breaking Changes

- **Custom tools now require `index.ts` entry point**: Auto-discovered custom tools must be in a subdirectory with an `index.ts` file. The old pattern `~/.pi/agent/tools/mytool.ts` must become `~/.pi/agent/tools/mytool/index.ts`. This allows multi-file tools to import helper modules. Explicit paths via `--tool` or `settings.json` still work with any `.ts` file.

- **Hook `tool_result` event restructured**: The `ToolResultEvent` now exposes full tool result data instead of just text. ([#233](https://github.com/badlogic/pi-mono/pull/233))
  - Removed: `result: string` field
  - Added: `content: (TextContent | ImageContent)[]` - full content array
  - Added: `details: unknown` - tool-specific details (typed per tool via discriminated union on `toolName`)
  - `ToolResultEventResult.result` renamed to `ToolResultEventResult.text` (removed), use `content` instead
  - Hook handlers returning `{ result: "..." }` must change to `{ content: [{ type: "text", text: "..." }] }`
  - Built-in tool details types exported: `BashToolDetails`, `ReadToolDetails`, `GrepToolDetails`, `FindToolDetails`, `LsToolDetails`, `TruncationResult`
  - Type guards exported for narrowing: `isBashToolResult`, `isReadToolResult`, `isEditToolResult`, `isWriteToolResult`, `isGrepToolResult`, `isFindToolResult`, `isLsToolResult`

## [0.23.4] - 2025-12-18

### Added

- **Syntax highlighting**: Added syntax highlighting for markdown code blocks, read tool output, and write tool content. Uses cli-highlight with theme-aware color mapping and VS Code-style syntax colors. ([#214](https://github.com/badlogic/pi-mono/pull/214) by [@svkozak](https://github.com/svkozak))

- **Intra-line diff highlighting**: Edit tool now shows word-level changes with inverse highlighting when a single line is modified. Multi-line changes show all removed lines first, then all added lines.

### Fixed

- **Gemini tool result format**: Fixed tool result format for Gemini 3 Flash Preview which strictly requires `{ output: value }` for success and `{ error: value }` for errors. Previous format using `{ result, isError }` was rejected by newer Gemini models. ([#213](https://github.com/badlogic/pi-mono/issues/213), [#220](https://github.com/badlogic/pi-mono/pull/220))

- **Google baseUrl configuration**: Google provider now respects `baseUrl` configuration for custom endpoints or API proxies. ([#216](https://github.com/badlogic/pi-mono/issues/216), [#221](https://github.com/badlogic/pi-mono/pull/221) by [@theBucky](https://github.com/theBucky))

- **Google provider FinishReason**: Added handling for new `IMAGE_RECITATION` and `IMAGE_OTHER` finish reasons. Upgraded @google/genai to 1.34.0.

## [0.23.3] - 2025-12-17

### Fixed

- Check for compaction before submitting user prompt, not just after agent turn ends. This catches cases where user aborts mid-response and context is already near the limit.

### Changed

- Improved system prompt documentation section with clearer pointers to specific doc files for custom models, themes, skills, hooks, custom tools, and RPC.

- Cleaned up documentation:

  - `theme.md`: Added missing color tokens (`thinkingXhigh`, `bashMode`)
  - `skills.md`: Rewrote with better framing and examples
  - `hooks.md`: Fixed timeout/error handling docs, added import aliases section
  - `custom-tools.md`: Added intro with use cases and comparison table
  - `rpc.md`: Added missing `hook_error` event documentation
  - `README.md`: Complete settings table, condensed philosophy section, standardized OAuth docs

- Hooks loader now supports same import aliases as custom tools (`@sinclair/typebox`, `@mariozechner/pi-ai`, `@mariozechner/pi-tui`, `@bastani/atomic`).

### Breaking Changes

- **Hooks**: `turn_end` event's `toolResults` type changed from `AppMessage[]` to `ToolResultMessage[]`. If you have hooks that handle `turn_end` events and explicitly type the results, update your type annotations.

## [0.23.2] - 2025-12-17

### Fixed

- Fixed Claude models via GitHub Copilot re-answering all previous prompts in multi-turn conversations. The issue was that assistant message content was sent as an array instead of a string, which Copilot's Claude adapter misinterpreted. Also added missing `Openai-Intent: conversation-edits` header and fixed `X-Initiator` logic to check for any assistant/tool message in history. ([#209](https://github.com/badlogic/pi-mono/issues/209))

- Detect image MIME type via file magic (read tool and `@file` attachments), not filename extension.

- Fixed markdown tables overflowing terminal width. Tables now wrap cell contents to fit available width instead of breaking borders mid-row. ([#206](https://github.com/badlogic/pi-mono/pull/206) by [@kim0](https://github.com/kim0))

## [0.23.1] - 2025-12-17

### Fixed

- Fixed TUI performance regression caused by Box component lacking render caching. Built-in tools now use Text directly (like v0.22.5), and Box has proper caching for custom tool rendering.

- Fixed custom tools failing to load from `~/.pi/agent/tools/` when pi is installed globally. Module imports (`@sinclair/typebox`, `@mariozechner/pi-tui`, `@mariozechner/pi-ai`) are now resolved via aliases.

## [0.23.0] - 2025-12-17

### Added

- **Custom tools**: Extend pi with custom tools written in TypeScript. Tools can provide custom TUI rendering, interact with users via `pi.ui` (select, confirm, input, notify), and maintain state across sessions via `onSession` callback. See [docs/custom-tools.md](docs/custom-tools.md) and [examples/custom-tools/](examples/custom-tools/). ([#190](https://github.com/badlogic/pi-mono/issues/190))

- **Hook and tool examples**: Added `examples/hooks/` and `examples/custom-tools/` with working examples. Examples are now bundled in npm and binary releases.

### Breaking Changes

- **Hooks**: Replaced `session_start` and `session_switch` events with unified `session` event. Use `event.reason` (`"start" | "switch" | "clear"`) to distinguish. Event now includes `entries` array for state reconstruction.

## [0.22.5] - 2025-12-17

### Fixed

- Fixed `--session` flag not saving sessions in print mode (`-p`). The session manager was never receiving events because no subscriber was attached.

## [0.22.4] - 2025-12-17

### Added

- `--list-models [search]` CLI flag to list available models with optional fuzzy search. Shows provider, model ID, context window, max output, thinking support, and image support. Only lists models with configured API keys. ([#203](https://github.com/badlogic/pi-mono/issues/203))

### Fixed

- Fixed tool execution showing green (success) background while still running. Now correctly shows gray (pending) background until the tool completes.

## [0.22.3] - 2025-12-16

### Added

- **Streaming bash output**: Bash tool now streams output in real-time during execution. The TUI displays live progress with the last 5 lines visible (expandable with ctrl+o). ([#44](https://github.com/badlogic/pi-mono/issues/44))

### Changed

- **Tool output display**: When collapsed, tool output now shows the last N lines instead of the first N lines, making streaming output more useful.

- Updated `@mariozechner/pi-ai` with X-Initiator header support for GitHub Copilot, ensuring agent calls are not deducted from quota. ([#200](https://github.com/badlogic/pi-mono/pull/200) by [@kim0](https://github.com/kim0))

### Fixed

- Fixed editor text being cleared during compaction. Text typed while compaction is running is now preserved. ([#179](https://github.com/badlogic/pi-mono/issues/179))
- Improved RGB to 256-color mapping for terminals without truecolor support. Now correctly uses grayscale ramp for neutral colors and preserves semantic tints (green for success, red for error, blue for pending) instead of mapping everything to wrong cube colors.
- `/think off` now actually disables thinking for all providers. Previously, providers like Gemini with "dynamic thinking" enabled by default would still use thinking even when turned off. ([#180](https://github.com/badlogic/pi-mono/pull/180) by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.22.2] - 2025-12-15

### Changed

- Updated `@mariozechner/pi-ai` with interleaved thinking enabled by default for Anthropic Claude 4 models.

## [0.22.1] - 2025-12-15

_Dedicated to Peter's shoulder ([@steipete](https://twitter.com/steipete))_

### Changed

- Updated `@mariozechner/pi-ai` with interleaved thinking support for Anthropic models.

## [0.22.0] - 2025-12-15

### Added

- **GitHub Copilot support**: Use GitHub Copilot models via OAuth login (`/login` -> "GitHub Copilot"). Supports both github.com and GitHub Enterprise. Models are sourced from models.dev and include Claude, GPT, Gemini, Grok, and more. All models are automatically enabled after login. ([#191](https://github.com/badlogic/pi-mono/pull/191) by [@cau1k](https://github.com/cau1k))

### Fixed

- Model selector fuzzy search now matches against provider name (not just model ID) and supports space-separated tokens where all tokens must match

## [0.21.0] - 2025-12-14

### Added

- **Inline image rendering**: Terminals supporting Kitty graphics protocol (Kitty, Ghostty, WezTerm) or iTerm2 inline images now render images inline in tool output. Aspect ratio is preserved by querying terminal cell dimensions on startup. Toggle with `/show-images` command or `terminal.showImages` setting. Falls back to text placeholder on unsupported terminals or when disabled. ([#177](https://github.com/badlogic/pi-mono/pull/177) by [@nicobailon](https://github.com/nicobailon))

- **Gemini 3 Pro thinking levels**: Thinking level selector now works with Gemini 3 Pro models. Minimal/low map to Google's LOW, medium/high map to Google's HIGH. ([#176](https://github.com/badlogic/pi-mono/pull/176) by [@markusylisiurunen](https://github.com/markusylisiurunen))

### Fixed

- Fixed read tool failing on macOS screenshot filenames due to Unicode Narrow No-Break Space (U+202F) in timestamp. Added fallback to try macOS variant paths and consolidated duplicate expandPath functions into shared path-utils.ts. ([#181](https://github.com/badlogic/pi-mono/pull/181) by [@nicobailon](https://github.com/nicobailon))

- Fixed double blank lines rendering after markdown code blocks ([#173](https://github.com/badlogic/pi-mono/pull/173) by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.20.1] - 2025-12-13

### Added

- **Exported skills API**: `loadSkillsFromDir`, `formatSkillsForPrompt`, and related types are now exported for use by other packages (e.g., mom).

## [0.20.0] - 2025-12-13

### Breaking Changes

- **Pi skills now use `SKILL.md` convention**: Pi skills must now be named `SKILL.md` inside a directory, matching Codex CLI format. Previously any `*.md` file was treated as a skill. Migrate by renaming `~/.pi/agent/skills/foo.md` to `~/.pi/agent/skills/foo/SKILL.md`.

### Added

- Display loaded skills on startup in interactive mode

## [0.19.1] - 2025-12-12

### Fixed

- Documentation: Added skills system documentation to README (setup, usage, CLI flags, settings)

## [0.19.0] - 2025-12-12

### Added

- **Skills system**: Auto-discover and load instruction files on-demand. Supports Claude Code (`~/.claude/skills/*/SKILL.md`), Codex CLI (`~/.codex/skills/`), and Pi-native formats (`~/.pi/agent/skills/`, `.pi/skills/`). Skills are listed in system prompt with descriptions, agent loads them via read tool when needed. Supports `{baseDir}` placeholder. Disable with `--no-skills` or `skills.enabled: false` in settings. ([#169](https://github.com/badlogic/pi-mono/issues/169))

- **Version flag**: Added `--version` / `-v` flag to display the current version and exit. ([#170](https://github.com/badlogic/pi-mono/pull/170))

## [0.18.2] - 2025-12-11

### Added

- **Auto-retry on transient errors**: Automatically retries requests when providers return overloaded, rate limit, or server errors (429, 500, 502, 503, 504). Uses exponential backoff (2s, 4s, 8s). Shows retry status in TUI with option to cancel via Escape. Configurable in `settings.json` via `retry.enabled`, `retry.maxRetries`, `retry.baseDelayMs`. RPC mode emits `auto_retry_start` and `auto_retry_end` events. ([#157](https://github.com/badlogic/pi-mono/issues/157))

- **HTML export line numbers**: Read tool calls in HTML exports now display line number ranges (e.g., `file.txt:10-20`) when offset/limit parameters are used, matching the TUI display format. Line numbers appear in yellow color for better visibility. ([#166](https://github.com/badlogic/pi-mono/issues/166))

### Fixed

- **Branch selector now works with single message**: Previously the branch selector would not open when there was only one user message. Now it correctly allows branching from any message, including the first one. This is needed for checkpoint hooks to restore state from before the first message. ([#163](https://github.com/badlogic/pi-mono/issues/163))

- **In-memory branching for `--no-session` mode**: Branching now works correctly in `--no-session` mode without creating any session files. The conversation is truncated in memory.

- **Git branch indicator now works in subdirectories**: The footer's git branch detection now walks up the directory hierarchy to find the git root, so it works when running pi from a subdirectory of a repository. ([#156](https://github.com/badlogic/pi-mono/issues/156))

## [0.18.1] - 2025-12-10

### Added

- **Mistral provider**: Added support for Mistral AI models. Set `MISTRAL_API_KEY` environment variable to use.

### Fixed

- Fixed print mode (`-p`) not exiting after output when custom themes are present (theme watcher now properly stops in print mode) ([#161](https://github.com/badlogic/pi-mono/issues/161))

## [0.18.0] - 2025-12-10

### Added

- **Hooks system**: TypeScript modules that extend agent behavior by subscribing to lifecycle events. Hooks can intercept tool calls, prompt for confirmation, modify results, and inject messages from external sources. Auto-discovered from `~/.pi/agent/hooks/*.ts` and `.pi/hooks/*.ts`. Thanks to [@nicobailon](https://github.com/nicobailon) for the collaboration on the design and implementation. ([#145](https://github.com/badlogic/pi-mono/issues/145), supersedes [#158](https://github.com/badlogic/pi-mono/pull/158))

- **`pi.send()` API**: Hooks can inject messages into the agent session from external sources (file watchers, webhooks, CI systems). If streaming, messages are queued; otherwise a new agent loop starts immediately.

- **`--hook <path>` CLI flag**: Load hook files directly for testing without modifying settings.

- **Hook events**: `session_start`, `session_switch`, `agent_start`, `agent_end`, `turn_start`, `turn_end`, `tool_call` (can block), `tool_result` (can modify), `branch`.

- **Hook UI primitives**: `ctx.ui.select()`, `ctx.ui.confirm()`, `ctx.ui.input()`, `ctx.ui.notify()` for interactive prompts from hooks.

- **Hooks documentation**: Full API reference at `docs/hooks.md`, shipped with npm package.

## [0.17.0] - 2025-12-09

### Changed

- **Simplified compaction flow**: Removed proactive compaction (aborting mid-turn when threshold approached). Compaction now triggers in two cases only: (1) overflow error from LLM, which compacts and auto-retries, or (2) threshold crossed after a successful turn, which compacts without retry.

- **Compaction retry uses `Agent.continue()`**: Auto-retry after overflow now uses the new `continue()` API instead of re-sending the user message, preserving exact context state.

- **Merged turn prefix summary**: When a turn is split during compaction, the turn prefix summary is now merged into the main history summary instead of being stored separately.

### Added

- **`isCompacting` property on AgentSession**: Check if auto-compaction is currently running.

- **Session compaction indicator**: When resuming a compacted session, displays "Session compacted N times" status message.

### Fixed

- **Block input during compaction**: User input is now blocked while auto-compaction is running to prevent race conditions.

- **Skip error messages in usage calculation**: Context size estimation now skips both aborted and error messages, as neither have valid usage data.

## [0.16.0] - 2025-12-09

### Breaking Changes

- **New RPC protocol**: The RPC mode (`--mode rpc`) has been completely redesigned with a new JSON protocol. The old protocol is no longer supported. See [`docs/rpc.md`](docs/rpc.md) for the new protocol documentation and [`test/rpc-example.ts`](test/rpc-example.ts) for a working example. Includes `RpcClient` TypeScript class for easy integration. ([#91](https://github.com/badlogic/pi-mono/issues/91))

### Changed

- **README restructured**: Reorganized documentation from 30+ flat sections into 10 logical groups. Converted verbose subsections to scannable tables. Consolidated philosophy sections. Reduced size by ~60% while preserving all information.

## [0.15.0] - 2025-12-09

### Changed

- **Major code refactoring**: Restructured codebase for better maintainability and separation of concerns. Moved files into organized directories (`core/`, `modes/`, `utils/`, `cli/`). Extracted `AgentSession` class as central session management abstraction. Split `main.ts` and `tui-renderer.ts` into focused modules. See `DEVELOPMENT.md` for the new code map. ([#153](https://github.com/badlogic/pi-mono/issues/153))

## [0.14.2] - 2025-12-08

### Added

- `/debug` command now includes agent messages as JSONL in the output

### Fixed

- Fix crash when bash command outputs binary data (e.g., `curl` downloading a video file)

## [0.14.1] - 2025-12-08

### Fixed

- Fix build errors with tsgo 7.0.0-dev.20251208.1 by properly importing `ReasoningEffort` type

## [0.14.0] - 2025-12-08

### Breaking Changes

- **Custom themes require new color tokens**: Themes must now include `thinkingXhigh` and `bashMode` color tokens. The theme loader provides helpful error messages listing missing tokens. See built-in themes (dark.json, light.json) for reference values.

### Added

- **OpenAI compatibility overrides in models.json**: Custom models using `openai-completions` API can now specify a `compat` object to override provider quirks (`supportsStore`, `supportsDeveloperRole`, `supportsReasoningEffort`, `maxTokensField`). Useful for LiteLLM, custom proxies, and other non-standard endpoints. ([#133](https://github.com/badlogic/pi-mono/issues/133), thanks @fink-andreas for the initial idea and PR)

- **xhigh thinking level**: Added `xhigh` thinking level for OpenAI codex-max models. Cycle through thinking levels with Shift+Tab; `xhigh` appears only when using a codex-max model. ([#143](https://github.com/badlogic/pi-mono/issues/143))

- **Collapse changelog setting**: Add `"collapseChangelog": true` to `~/.pi/agent/settings.json` to show a condensed "Updated to vX.Y.Z" message instead of the full changelog after updates. Use `/changelog` to view the full changelog. ([#148](https://github.com/badlogic/pi-mono/issues/148))

- **Bash mode**: Execute shell commands directly from the editor by prefixing with `!` (e.g., `!ls -la`). Output streams in real-time, is added to the LLM context, and persists in session history. Supports multiline commands, cancellation (Escape), truncation for large outputs, and preview/expand toggle (Ctrl+O). Also available in RPC mode via `{"type":"bash","command":"..."}`. ([#112](https://github.com/badlogic/pi-mono/pull/112), original implementation by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.13.2] - 2025-12-07

### Changed

- **Tool output truncation**: All tools now enforce consistent truncation limits with actionable notices for the LLM. ([#134](https://github.com/badlogic/pi-mono/issues/134))
  - **Limits**: 2000 lines OR 50KB (whichever hits first), never partial lines
  - **read**: Shows `[Showing lines X-Y of Z. Use offset=N to continue]`. If first line exceeds 50KB, suggests bash command
  - **bash**: Tail truncation with temp file. Shows `[Showing lines X-Y of Z. Full output: /tmp/...]`
  - **grep**: Pre-truncates match lines to 500 chars. Shows match limit and line truncation notices
  - **find/ls**: Shows result/entry limit notices
  - TUI displays truncation warnings in yellow at bottom of tool output (visible even when collapsed)

## [0.13.1] - 2025-12-06

### Added

- **Flexible Windows shell configuration**: The bash tool now supports multiple shell sources beyond Git Bash. Resolution order: (1) custom `shellPath` in settings.json, (2) Git Bash in standard locations, (3) any bash.exe on PATH. This enables Cygwin, MSYS2, and other bash environments. Configure with `~/.pi/agent/settings.json`: `{"shellPath": "C:\\cygwin64\\bin\\bash.exe"}`.

### Fixed

- **Windows binary detection**: Fixed Bun compiled binary detection on Windows by checking for URL-encoded `%7EBUN` in addition to `$bunfs` and `~BUN` in `import.meta.url`. This ensures the binary correctly locates supporting files (package.json, themes, etc.) next to the executable.

## [0.12.15] - 2025-12-06

### Fixed

- **Editor crash with emojis/CJK characters**: Fixed crash when pasting or typing text containing wide characters (emojis like ✅, CJK characters) that caused line width to exceed terminal width. The editor now uses grapheme-aware text wrapping with proper visible width calculation.

## [0.12.14] - 2025-12-06

### Added

- **Double-Escape Branch Shortcut**: Press Escape twice with an empty editor to quickly open the `/branch` selector for conversation branching.

## [0.12.13] - 2025-12-05

### Changed

- **Faster startup**: Version check now runs in parallel with TUI initialization instead of blocking startup for up to 1 second. Update notifications appear in chat when the check completes.

## [0.12.12] - 2025-12-05

### Changed

- **Footer display**: Token counts now use M suffix for millions (e.g., `10.2M` instead of `10184k`). Context display shortened from `61.3% of 200k` to `61.3%/200k`.

### Fixed

- **Multi-key sequences in inputs**: Inputs like model search now handle multi-key sequences identically to the main prompt editor. ([#122](https://github.com/badlogic/pi-mono/pull/122) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- **Line wrapping escape codes**: Fixed underline style bleeding into padding when wrapping long URLs. ANSI codes now attach to the correct content, and line-end resets only turn off underline (preserving background colors). ([#109](https://github.com/badlogic/pi-mono/issues/109))

### Added

- **Fuzzy search models and sessions**: Implemented a simple fuzzy search for models and sessions (e.g., `codexmax` now finds `gpt-5.1-codex-max`). ([#122](https://github.com/badlogic/pi-mono/pull/122) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- **Prompt History Navigation**: Browse previously submitted prompts using Up/Down arrow keys when the editor is empty. Press Up to cycle through older prompts, Down to return to newer ones or clear the editor. Similar to shell history and Claude Code's prompt history feature. History is session-scoped and stores up to 100 entries. ([#121](https://github.com/badlogic/pi-mono/pull/121) by [@nicobailon](https://github.com/nicobailon))
- **`/resume` Command**: Switch to a different session mid-conversation. Opens an interactive selector showing all available sessions. Equivalent to the `--resume` CLI flag but can be used without restarting the agent. ([#117](https://github.com/badlogic/pi-mono/pull/117) by [@hewliyang](https://github.com/hewliyang))

## [0.12.11] - 2025-12-05

### Changed

- **Compaction UI**: Simplified collapsed compaction indicator to show warning-colored text with token count instead of styled banner. Removed redundant success message after compaction. ([#108](https://github.com/badlogic/pi-mono/issues/108))

### Fixed

- **Print mode error handling**: `-p` flag now outputs error messages and exits with code 1 when requests fail, instead of silently producing no output.
- **Branch selector crash**: Fixed TUI crash when user messages contained Unicode characters (like `✔` or `›`) that caused line width to exceed terminal width. Now uses proper `truncateToWidth` instead of `substring`.
- **Bash output escape sequences**: Fixed incomplete stripping of terminal escape sequences in bash tool output. `stripAnsi` misses some sequences like standalone String Terminator (`ESC \`), which could cause rendering issues when displaying captured TUI output.
- **Footer overflow crash**: Fixed TUI crash when terminal width is too narrow for the footer stats line. The footer now truncates gracefully instead of overflowing.

### Added

- **`authHeader` option in models.json**: Custom providers can set `"authHeader": true` to automatically add `Authorization: Bearer <apiKey>` header. Useful for providers that require explicit auth headers. ([#81](https://github.com/badlogic/pi-mono/issues/81))
- **`--append-system-prompt` Flag**: Append additional text or file contents to the system prompt. Supports both inline text and file paths. Complements `--system-prompt` for layering custom instructions without replacing the base system prompt. ([#114](https://github.com/badlogic/pi-mono/pull/114) by [@markusylisiurunen](https://github.com/markusylisiurunen))
- **Thinking Block Toggle**: Added `Ctrl+T` shortcut to toggle visibility of LLM thinking blocks. When toggled off, shows a static "Thinking..." label instead of full content. Useful for reducing visual clutter during long conversations. ([#113](https://github.com/badlogic/pi-mono/pull/113) by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.12.10] - 2025-12-04

### Added

- Added `gpt-5.1-codex-max` model support

## [0.12.9] - 2025-12-04

### Added

- **`/copy` Command**: Copy the last agent message to clipboard. Works cross-platform (macOS, Windows, Linux). Useful for extracting text from rendered Markdown output. ([#105](https://github.com/badlogic/pi-mono/pull/105) by [@markusylisiurunen](https://github.com/markusylisiurunen))

## [0.12.8] - 2025-12-04

- Fix: Use CTRL+O consistently for compaction expand shortcut (not CMD+O on Mac)

## [0.12.7] - 2025-12-04

### Added

- **Context Compaction**: Long sessions can now be compacted to reduce context usage while preserving recent conversation history. ([#92](https://github.com/badlogic/pi-mono/issues/92), [docs](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/README.md#context-compaction))
  - `/compact [instructions]`: Manually compact context with optional custom instructions for the summary
  - `/autocompact`: Toggle automatic compaction when context exceeds threshold
  - Compaction summarizes older messages while keeping recent messages (default 20k tokens) verbatim
  - Auto-compaction triggers when context reaches `contextWindow - reserveTokens` (default 16k reserve)
  - Compacted sessions show a collapsible summary in the TUI (toggle with `o` key)
  - HTML exports include compaction summaries as collapsible sections
  - RPC mode supports `{"type":"compact"}` command and auto-compaction (emits compaction events)
- **Branch Source Tracking**: Branched sessions now store `branchedFrom` in the session header, containing the path to the original session file. Useful for tracing session lineage.

## [0.12.5] - 2025-12-03

### Added

- **Forking/Rebranding Support**: All branding (app name, config directory, environment variable names) is now configurable via `piConfig` in `package.json`. Forks can change `piConfig.name` and `piConfig.configDir` to rebrand the CLI without code changes. Affects CLI banner, help text, config paths, and error messages. ([#95](https://github.com/badlogic/pi-mono/pull/95))

### Fixed

- **Bun Binary Detection**: Fixed Bun compiled binary failing to start after Bun updated its virtual filesystem path format from `%7EBUN` to `$bunfs`. ([#95](https://github.com/badlogic/pi-mono/pull/95))

## [0.12.4] - 2025-12-02

### Added

- **RPC Termination Safeguard**: When running as an RPC worker (stdin pipe detected), the CLI now exits immediately if the parent process terminates unexpectedly. Prevents orphaned RPC workers from persisting indefinitely and consuming system resources.

## [0.12.3] - 2025-12-02

### Fixed

- **Rate limit handling**: Anthropic rate limit errors now trigger automatic retry with exponential backoff (base 10s, max 5 retries). Previously these errors would abort the request immediately.
- **Usage tracking during retries**: Retried requests now correctly accumulate token usage from all attempts, not just the final successful one. Fixes artificially low token counts when requests were retried.

## [0.12.2] - 2025-12-02

### Changed

- Removed support for gpt-4.5-preview and o3 models (not yet available)

## [0.12.1] - 2025-12-02

### Added

- **Models**: Added support for OpenAI's new models:
  - `gpt-4.1` (128K context)
  - `gpt-4.1-mini` (128K context)
  - `gpt-4.1-nano` (128K context)
  - `o3` (200K context, reasoning model)
  - `o4-mini` (200K context, reasoning model)

## [0.12.0] - 2025-12-02

### Added

- **`-p, --print` Flag**: Run in non-interactive batch mode. Processes input message or piped stdin without TUI, prints agent response directly to stdout. Ideal for scripting, piping, and CI/CD integration. Exits after first response.
- **`-P, --print-streaming` Flag**: Like `-p`, but streams response tokens as they arrive. Use `--print-streaming --no-markdown` for raw unformatted output.
- **`--print-turn` Flag**: Continue processing tool calls and agent turns until the agent naturally finishes or requires user input. Combine with `-p` for complete multi-turn conversations.
- **`--no-markdown` Flag**: Output raw text without Markdown formatting. Useful when piping output to tools that expect plain text.
- **Streaming Print Mode**: Added internal `printStreaming` option for streaming output in non-TUI mode.
- **RPC Mode `print` Command**: Send `{"type":"print","content":"text"}` to get formatted print output via `print_output` events.
- **Auto-Save in Print Mode**: Print mode conversations are automatically saved to the session directory, allowing later resumption with `--continue`.
- **Thinking level options**: Added `--thinking-off`, `--thinking-minimal`, `--thinking-low`, `--thinking-medium`, `--thinking-high` flags for directly specifying thinking level without the selector UI.

### Changed

- **Simplified RPC Protocol**: Replaced the `prompt` wrapper command with direct message objects. Send `{"role":"user","content":"text"}` instead of `{"type":"prompt","message":"text"}`. Better aligns with message format throughout the codebase.
- **RPC Message Handling**: Agent now processes raw message objects directly, with `timestamp` auto-populated if missing.

## [0.11.9] - 2025-12-02

### Changed

- Change Ctrl+I to Ctrl+P for model cycling shortcut to avoid collision with Tab key in some terminals

## [0.11.8] - 2025-12-01

### Fixed

- Absolute glob patterns (e.g., `/Users/foo/**/*.ts`) are now handled correctly. Previously the leading `/` was being stripped, causing the pattern to be interpreted relative to the current directory.

## [0.11.7] - 2025-12-01

### Fixed

- Fix read path traversal vulnerability. Paths are now validated to prevent reading outside the working directory or its parents. The `read` tool can read from `cwd`, its ancestors (for config files), and all descendants. Symlinks are resolved before validation.

## [0.11.6] - 2025-12-01

### Fixed

- Fix `--system-prompt <path>` allowing the path argument to be captured by the message collection, causing "file not found" errors.

## [0.11.5] - 2025-11-30

### Fixed

- Fixed fatal error "Cannot set properties of undefined (setting '0')" when editing empty files in the `edit` tool.
- Simplified `edit` tool output: Shows only "Edited file.txt" for successful edits instead of verbose search/replace details.
- Fixed fatal error in footer rendering when token counts contain NaN values due to missing usage data.

## [0.11.4] - 2025-11-30

### Fixed

- Fixed chat rendering crash when messages contain preformatted/styled text (e.g., thinking traces with gray italic styling). The markdown renderer now preserves existing ANSI escape codes when they appear before inline elements.

## [0.11.3] - 2025-11-29

### Fixed

- Fix file drop functionality for absolute paths

## [0.11.2] - 2025-11-29

### Fixed

- Fixed TUI crash when pasting content containing tab characters. Tabs are now converted to 4 spaces before insertion.
- Fixed terminal corruption after exit when shell integration sequences (OSC 133) appeared in bash output. These sequences are now stripped along with other ANSI codes.

## [0.11.1] - 2025-11-29

### Added

- Added `fd` integration for file path autocompletion. Now uses `fd` for faster fuzzy file search

### Fixed

- Fixed keyboard shortcuts Ctrl+A, Ctrl+E, Ctrl+K, Ctrl+U, Ctrl+W, and word navigation (Option+Arrow) not working in VS Code integrated terminal and some other terminal emulators

## [0.11.0] - 2025-11-29

### Added

- **File-based Slash Commands**: Create custom reusable prompts as `.txt` files in `~/.pi/slash-commands/`. Files become `/filename` commands with first-line descriptions. Supports `{{selection}}` placeholder for referencing selected/attached content.
- **`/branch` Command**: Create conversation branches from any previous user message. Opens a selector to pick a message, then creates a new session file starting from that point. Original message text is placed in the editor for modification.
- **Unified Content References**: Both `@path` in messages and `--file path` CLI arguments now use the same attachment system with consistent MIME type detection.
- **Drag & Drop Files**: Drop files onto the terminal to attach them to your message. Supports multiple files and both text and image content.

### Changed

- **Model Selector with Search**: The `/model` command now opens a searchable list. Type to filter models by name, use arrows to navigate, Enter to select.
- **Improved File Autocomplete**: File path completion after `@` now supports fuzzy matching and shows file/directory indicators.
- **Session Selector with Search**: The `--resume` and `--session` flags now open a searchable session list with fuzzy filtering.
- **Attachment Display**: Files added via `@path` are now shown as "Attached: filename" in the user message, separate from the prompt text.
- **Tab Completion**: Tab key now triggers file path autocompletion anywhere in the editor, not just after `@` symbol.

### Fixed

- Fixed autocomplete z-order issue where dropdown could appear behind chat messages
- Fixed cursor position when navigating through wrapped lines in the editor
- Fixed attachment handling for continued sessions to preserve file references

## [0.10.6] - 2025-11-28

### Changed

- Show base64-truncated indicator for large images in tool output

### Fixed

- Fixed image dimensions not being read correctly from PNG/JPEG/GIF files
- Fixed PDF images being incorrectly base64-truncated in display
- Allow reading files from ancestor directories (needed for monorepo configs)

## [0.10.5] - 2025-11-28

### Added

- Full multimodal support: attach images (PNG, JPEG, GIF, WebP) and PDFs to prompts using `@path` syntax or `--file` flag

### Fixed

- `@`-references now handle special characters in file names (spaces, quotes, unicode)
- Fixed cursor positioning issues with multi-byte unicode characters in editor

## [0.10.4] - 2025-11-28

### Fixed

- Removed padding on first user message in TUI to improve visual consistency.

## [0.10.3] - 2025-11-28

### Added

- Added RPC mode (`--rpc`) for programmatic integration. Accepts JSON commands on stdin, emits JSON events on stdout. See [RPC mode documentation](https://github.com/nicobailon/pi-mono/blob/main/packages/coding-agent/README.md#rpc-mode) for protocol details.

### Changed

- Refactored internal architecture to support multiple frontends (TUI, RPC) with shared agent logic.

## [0.10.2] - 2025-11-26

### Added

- Added thinking level persistence. Default level stored in `~/.pi/settings.json`, restored on startup. Per-session overrides saved in session files.
- Added model cycling shortcut: `Ctrl+I` cycles through available models (or scoped models with `-m` flag).
- Added automatic retry with exponential backoff for transient API errors (network issues, 500s, overload).
- Cumulative token usage now shown in footer (total tokens used across all messages in session).
- Added `--system-prompt` flag to override default system prompt with custom text or file contents.
- Footer now shows estimated total cost in USD based on model pricing.

### Changed

- Replaced `--models` flag with `-m/--model` supporting multiple values. Specify models as `provider/model@thinking` (e.g., `anthropic/claude-sonnet-4-20250514@high`). Multiple `-m` flags scope available models for the session.
- Thinking level border now persists visually after selector closes.
- Improved tool result display with collapsible output (default collapsed, expand with `Ctrl+O`).

## [0.10.1] - 2025-11-25

### Added

- Add custom model configuration via `~/.pi/models.json`

## [0.10.0] - 2025-11-25

Initial public release.

### Added

- Interactive TUI with streaming responses
- Conversation session management with `--continue`, `--resume`, and `--session` flags
- Multi-line input support (Shift+Enter or Option+Enter for new lines)
- Tool execution: `read`, `write`, `edit`, `bash`, `glob`, `grep`, `think`
- Thinking mode support for Claude with visual indicator and `/thinking` selector
- File path autocompletion with `@` prefix
- Slash command autocompletion
- `/export` command for HTML session export
- `/model` command for runtime model switching
- `/session` command for session statistics
- Model provider support: Anthropic (Claude), OpenAI, Google (Gemini)
- Git branch display in footer
- Message queueing during streaming responses
- OAuth integration for Gmail and Google Calendar access
- HTML export with syntax highlighting and collapsible sections
