## Why
Host skills can change after a thread starts. Subsequent turns need the updated
catalog without reinjecting an unchanged catalog on every turn.
## What changed
- Register the host skill provider in app-server and project its catalog through
a dedicated World State section.
- Render host skills with the existing metadata budget and usage instructions,
including skill-root aliases when available.
- Suppress the legacy host catalog and contextual fragment when World State owns
the catalog, while preserving host skills for explicit and shadow selection.
## Testing
Added an app-server test that installs a host skill at runtime and verifies the
catalog updates once and remains stable on the following turn.
GitOrigin-RevId: 4cacc6b2258fc3cab8499fbdf9aa6ab0324ec10d
## What changed
- Correlate MCP server attribution in imported session records with connector
metadata, and persist normalized connector names in the session import ledger.
- Extend `externalAgentConfig/import/readHistories` with connector candidates,
including each connector's display name, imported-session count, and detection
source.
## Testing
- Cover connector detection through the session import and history-read flow.
- Verify candidate aggregation uses the latest import for each source session.
GitOrigin-RevId: 7f1f7f596925b2e6efcd17afd4490148e1ac9ff1
## What changed
- Convert supported plugin command Markdown into generated skills when a plugin is installed.
- Read command sources from the manifest's `commands` field, falling back to the plugin's `commands/` directory.
- Load generated command skills alongside native plugin skills while preferring native skills with the same name.
- Skip commands with unsupported templates, missing descriptions, name collisions, or generated skills larger than 4 KB, and keep migration failures from blocking plugin installation.
## Testing
- Cover default and manifest-configured command paths, stale generated skill cleanup, size limits, native-skill precedence, invalid manifest fields, and non-fatal migration errors.
GitOrigin-RevId: 67d910048544b160b191066569bca69b2b951ebc
## What changed
- Allow `thread/start` to select experimental `historyMode: "paginated"` when the thread store supports turn and item pagination.
- Resume paginated threads with bounded model context and require `excludeTurns: true` instead of loading full history.
- Return `turnsBackwardsCursor` and `itemsBackwardsCursor` from `thread/resume` so clients can hydrate durable history through `thread/turns/list` and `thread/items/list` while newer records arrive through live notifications.
- Reject operations that still require full history, including `initialTurnsPage`, `thread/read` with `includeTurns: true`, `thread/rollback`, and detached review.
- Make item cursors independent of the optional `turnId` filter.
## Testing
- Cover paginated thread start, cold and live resume, backwards cursor hydration, metadata reads, unsupported operations, and stores without pagination support.
GitOrigin-RevId: c42f9b268e29d049843330f474fa3ecde12f5b8a
## What changed
- Track connected and disconnected states across initial remote exec-server connections and reconnection attempts.
- Emit experimental `thread/environment/connected` and `thread/environment/disconnected` app-server notifications for each thread selecting the environment. Each payload identifies the thread and environment; current state is not replayed when a thread starts.
- Stop forwarding connection events when an environment selection is removed or replaced.
## Testing
- Cover connection, disconnection, reconnection, shared-environment notifications, and replacement of a selected environment.
GitOrigin-RevId: 5dd767372363c4a2a8319fc16164be117d5bd20c
## What changed
- Add experimental `beforeTurnId` support to `thread/fork` for copying history strictly before a turn, including an in-progress turn.
- Use before-turn forks for TUI backtracking and safety retries so the replacement prompt retains thread context instead of recreating the branch from the preceding turn or a new thread.
- Add experimental `deferGoalContinuation` support so safety-retry forks can inherit an active goal while waiting for the replacement turn before automatic continuation resumes.
- Hide duplicated nested-review prompts when replaying history or selecting a prompt to edit.
## Testing
- Cover before-turn truncation, fork validation, active-goal inheritance, TUI safety retries, backtracking, and nested-review replay.
GitOrigin-RevId: 4915d85ae1929a0b8c40f3ed7dca29c7a5bdcdf3
## Why
Sparse and out-of-order rate-limit updates can omit account metadata or let an
older full read overwrite a newer workspace hard stop. Credit availability also
does not always include a displayable balance.
## What changed
- Propagate the backend's `spendControlReached` state through rate-limit
snapshots and the app-server protocol, preserving it across sparse updates.
- Keep workspace hard-stop classifications for error routing and ignore stale
full-read results after a newer hard-stop notification.
- Base TUI warnings and model-switch prompts on credit availability and hard-stop
state, and show `Credits: Available` when a finite balance is hidden.
## Testing
Added coverage for spend-control mapping, snapshot merging, out-of-order reads,
workspace error routing, warning and prompt behavior, and credit status display.
GitOrigin-RevId: 18b09d493d5a620a7c079fb721ec50b38dd5b04c
## Why
Starting a new session should not have to wait for an unchanged stdio MCP
server to initialize before its tools can be presented to the model.
## What changed
- Cache recent tool catalogs for matching stdio MCP server configurations and
use them while a new live connection starts in the background.
- Bound the process-wide cache to 32 entries with a 30-minute TTL, and key
entries by the server configuration, environment, working directory, and
elicitation capabilities that affect the catalog.
- Keep connection-specific instructions and tool annotations out of cached
catalogs. Resolve tool calls against the live connection so tools that are
unavailable or hidden in the new session cannot be invoked from stale data.
- Publish only the newest completed catalog fetch and do not cache HTTP servers
or stdio configurations that depend on remotely sourced environment values.
## Testing
- Add coverage for cache expiration, environment changes, catalog sanitization,
and out-of-order refreshes.
- Add an end-to-end test that starts inference from cached definitions while a
replacement MCP process initializes, then verifies calls and visibility
against the live process.
GitOrigin-RevId: 2ed0f29ba53ace12532730f951b175c588aa3ae7
## What changed
- Add `subErrorType` to external agent config import failures and carry it through progress and completion notifications, persisted import histories, and analytics.
- Expose plugin store failure subtypes from `PluginInstallError` so manual installs and imported plugins report details such as `failed_to_copy_plugin_file` while failures without a subtype remain `null`.
- Keep existing persisted import records compatible by defaulting the new field when it is absent.
## Testing
- Cover store I/O subtype extraction, analytics serialization, persisted import histories, and migration notification fixtures.
GitOrigin-RevId: ec1a85623fc826cbd7cfe9f55c2dd2bc86665937
## Why
Amazon Bedrock can use either a Codex-managed API key or credentials managed by
AWS, so logout must not remove or misrepresent credentials that Codex does not
control.
## What changed
- Remove the managed Bedrock key on logout and clear `model_provider` only when
its user-config value is still `amazon-bedrock`.
- Reject logout for AWS-managed Bedrock credentials without changing existing
authentication or configuration.
- Add a `test-logout` app-server test-client command that waits for the
resulting `account/updated` notification.
## Testing
Add coverage for managed and AWS-managed Bedrock logout, concurrent provider
changes, config reload failures, and conditional user-config cleanup.
GitOrigin-RevId: a003c0ec27ad7b36d499fe7b63ab4a0dd6369b4f
## What changed
- Handle experimental `account/login/start` requests with `type: "amazonBedrock"`.
- Validate the API key and Mantle region, persist the managed credential, select the `amazon-bedrock` model provider, and emit the standard login and account-update notifications.
- Reload configuration for account and authentication status reads so the newly selected provider is visible immediately, with the startup configuration as a fallback.
- Extend the app-server test client with an Amazon Bedrock login mode that redacts API keys from request logs.
## Testing
- Add app-server coverage for successful login, credential and provider persistence, notifications, refreshed account state, invalid inputs, conflicting configuration, forced ChatGPT login, external authentication, and configuration reload failure.
GitOrigin-RevId: bf3ae98375caee41e78d2cdc677127138184c17a
## Why
Detached reviews should behave like ordinary forked turns so clients get the normal steering, tool, permission, and item-stream behavior.
## What changed
- Add a bundled `$review-agent` skill with read-only, defect-first review guidance.
- Start detached reviews through `AgentRunner` with a target-specific prompt that explicitly references the bundled skill. Keep inline reviews on the existing review-mode flow.
- Continue emitting the detached review thread and turn identifiers through the app-server API.
## Testing
- Update the detached review integration test to verify the forked turn prompt, ordinary turn completion, and selection of the bundled skill when a user skill has the same name.
GitOrigin-RevId: 979664cd80800fb0d51eabdc4e3211da90c12db9
## Why
Clients could discover threads with paginated history, but could not page their
turns through `thread/turns/list`.
## What changed
- Route `thread/turns/list` for paginated threads through the thread store,
preserving cursors, sort direction, turn status, timing, errors, and the
requested `summary` or `notLoaded` item view.
- Reject the `full` turn item view for paginated threads and direct clients to
`thread/items/list` for complete item pagination.
- Return each item page entry with both its containing `turnId` and `item`, so
clients can regroup thread-wide pages into turns.
- Expose local thread-store turn and item pagination through the `ThreadStore`
implementation.
## Testing
Added app-server coverage for paging projected turns and items, item views,
cursors, turn metadata, and rejection of the unsupported `full` view.
GitOrigin-RevId: 9c4a127a9b4fa91313f0e87494c62fe2e3a06f19
## What changed
- Accept a caller-provided `CodexAppsToolsCache` when constructing a `ThreadManager` and pass it through to the MCP manager.
- Add `ConnectorRuntimeManager::new_without_cache()` for an in-memory runtime that neither loads nor persists connector state on disk.
- Re-export `CodexAppsToolsCache` from `codex-core` and `codex-core-api` for embedders.
## Testing
- Verify that a cache-disabled connector runtime ignores existing disk state and publishes live tools without creating cache files.
GitOrigin-RevId: f52f4f27c425a8d84ad0d0987b30a6fc3c14d702
## What changed
- Check the source thread's `history_mode` before loading its history for
`thread/fork`.
- Return the existing `paginated_threads is not supported yet` method-not-found
error for paginated source threads.
## Testing
- Add an app-server integration test covering rejection of a paginated source
thread.
GitOrigin-RevId: 49b4b8aef7c9488eec93c24e831cea6382f8c995
## Why
Clients that account for individual upstream Responses API calls need the exact
usage for each completion rather than accumulated or estimated thread usage.
## What changed
- Add the opt-in `rawResponse/completed` app-server notification with the
`threadId`, `turnId`, `responseId`, and upstream `usage` payload.
- Emit the transient event for normal turns and compaction requests when
`experimentalRawEvents` is enabled. The event is not persisted or replayed,
and `usage` is `null` when the upstream response omits it.
- Export the notification in the app-server schemas and document its behavior.
## Testing
- Cover exact token breakdowns for turn and compaction completions.
GitOrigin-RevId: 49040a060bf1784c1ff1dd62112efb4a778428c4
## Why
Multi-environment turns can run against different filesystems, so a single
thread-wide workspace-root list cannot accurately describe every selected
environment.
## What changed
- Add optional `runtimeWorkspaceRoots` to each app-server environment selection.
Omitted roots default to that environment's `cwd`, while an empty list selects
no roots.
- Carry environment-native roots through turn selection, model-visible context,
permission materialization, and tool sandboxing.
- Keep the top-level `runtimeWorkspaceRoots` field as a compatibility input when
constructing default environment selections; explicit selections use their
own roots.
## Testing
- Cover thread and turn API defaults, overrides, empty roots, and schema
round-tripping.
- Verify foreign roots remain model-visible and remote execution applies the
target environment's roots before sandbox selection.
GitOrigin-RevId: c6e7a62883c45c676c153055f46a55ee15a323a4
## What changed
- Project durable paginated rollout records into rebuildable SQLite tables for turns, items, and projection progress while keeping JSONL as the source of truth.
- Add cursor-based local reads for turn summaries and thread or per-turn items, including archived-thread and history-mode validation.
- Serialize writes, shutdown, and deletion per thread so projection updates cannot race cleanup, and remove projected rows when a thread is deleted.
- Preserve stable item ordering while updating repeated snapshots and expose structured turn error details.
## Testing
- Cover projection, snapshot updates, summary recovery, pagination, failure handling, shutdown flushing, legacy-mode rejection, and deletion races.
GitOrigin-RevId: 34533185ece7f1f5690004c7f12b0f1f34dc2640
## What changed
- Add the experimental `environment/status` request for inspecting a configured environment without starting or reconnecting it.
- Report `ready`, `pending`, `disconnected`, or `unknown`, including error details for disconnected and unknown environments.
- Probe ready remote environments over their existing exec-server connection.
## Testing
- Add an app-server integration test covering local and remote ready, pending, disconnected, and unknown environments.
GitOrigin-RevId: 397bba603aa9e0b59008ee6cb7cbde46e357652c
## Why
Embedding callers need to control whether model catalogs are persisted to disk.
## What changed
- Accept a shared models manager when constructing `ThreadManager` instead of always creating a cached manager internally.
- Add provider APIs for creating uncached model managers. OpenAI-compatible providers fetch on each `OnlineIfUncached` refresh without reading or writing `models_cache.json`.
- Keep existing app server, MCP server, sample, and test callers on the standard cached manager.
## Testing
- Verify an uncached manager fetches on every refresh.
- Verify an injected uncached manager controls thread refresh behavior and does not create `models_cache.json`.
GitOrigin-RevId: 8bb646054ecec4ccb865b2bf56249384916ea9db
## What changed
- Add an optional `emittedAtMs` field to server notification envelopes and expose the envelope in the generated JSON and TypeScript schemas.
- Populate the Unix timestamp when app-server emits a notification, before transport routing, so every connection in a fan-out receives the same value.
- Keep the field optional for clients decoding notifications from older app-server versions.
## Testing
- Verify notification serialization includes `emittedAtMs`.
- Verify targeted fan-out reuses one timestamp across connections.
GitOrigin-RevId: b379cb11e8e91c558bcd3d46ce459311a6e81e54
## What changed
- Include the active Codex Apps tools cache and connector directory cache, when available, in diagnostic uploads that include logs.
- Resolve cache paths from the current authentication context and use stable attachment filenames.
- List both optional cache files in the upload consent dialog.
## Testing
- Add coverage for selecting existing active cache files, including a directory cache without an account ID.
- Update consent dialog snapshots to include the optional attachments.
GitOrigin-RevId: 1d4ffbf2bcb96653f03dc25f91d80dd65038d585
## What changed
- Add an optional `source` selector to `externalAgentConfig/detect`, while preserving the existing behavior for omitted, `null`, and unrecognized values.
- Pass `claude-code` as the source for TUI detection and import requests.
- Move source-specific migration behavior behind an adapter boundary and consolidate session migration into `codex-external-agent-migration`.
## Testing
- Cover explicit, omitted, and unknown detection source values with app-server protocol tests.
- Add unit coverage for the extracted migration and hook helpers.
GitOrigin-RevId: 63bd3dcb485f3ad7bd996022db4e351506aeea77
## Why
Network proxy enforcement on Windows requires the elevated sandbox backend. Silently selecting that backend for an unelevated configuration makes the effective sandbox differ from the configured mode.
## What changed
- Reject enabled network proxy configurations on Windows unless managed requirements allow only the elevated sandbox and `windows.sandbox` resolves to `"elevated"`.
- Preserve the configured sandbox backend during execution and validate proxy compatibility at config loading, sandbox setup, and process launch boundaries.
- Defer this validation during cloud-config bootstrap so authoritative managed requirements can be loaded first.
- Do not start or expose disabled Windows proxies, and render transitions to disabled networking as `<network enabled="false"></network>`.
## Testing
- Cover Windows requirement and sandbox compatibility matrices, bootstrap behavior, backend selection, disabled proxy handling, and environment-context updates.
GitOrigin-RevId: bfa1dda98594e0db61883f8e7f65bd560e3453e6
## What changed
- Add an opt-in `skill_search` feature that ranks prompt-visible skills against each turn's user input with a bounded weighted lexical selector.
- Keep the ranked selection out of model-visible context and record metrics for selection cost, catalog reduction, and whether later implicit or `skills.read` invocations matched the ranked candidates.
- Include host-provided skills in the experiment catalog without changing the rendered skill catalog.
## Testing
- Add selector unit tests covering ranking, limits, truncation, stop words, and deterministic tie-breaking.
- Add extension tests covering turn-local invocation recording and host-skill shadow selection.
GitOrigin-RevId: 4d00a1c805ea8b391d6c6ac6a8450afa88ca3e25
## Why
`Max` and `Ultra` consume usage limits faster than standard reasoning levels, so they should not be selected accidentally while navigating the normal effort scale.
## What changed
- Move `Max` and `Ultra` behind a `More reasoning…` entry with a dedicated warning and descriptions.
- Keep the reasoning shortcuts from silently increasing into advanced efforts.
- Apply `Ultra` to the active conversation without changing defaults for new threads, while preserving it across mode switches and thread resumes.
- Record applied thread settings in thread metadata, including explicit clearing of reasoning effort, so resumed threads restore their latest model settings unless the user supplied an override.
## Testing
- Add TUI coverage for the advanced picker, shortcuts, Plan mode, configuration defaults, and resumed conversations.
- Add app-server and thread-store coverage for persisting, clearing, and restoring model and reasoning settings.
GitOrigin-RevId: 6708c7c2e8d38f491bf63000ea34f83476afdfe6
## What changed
- Move the Codex Apps tool cache into a reusable `codex-connectors` runtime manager keyed by account and workspace.
- Represent cached tools as atomically published snapshots with refresh timestamps while preserving the newest accepted fetch generation.
- Harden disk persistence with bounded reads, atomic file replacement, and serialized writes so an older fetch cannot overwrite newer state.
## Testing
Add coverage for identity isolation, snapshot timestamps, oversized cache files, atomic replacement, and concurrent persistence ordering.
GitOrigin-RevId: 5ea2234469daae3abf54b030244c3251de62ca5a
## What changed
- Pass the parent turn's effective permission profile to the memory consolidation agent, including thread-level permission and legacy sandbox overrides.
- Preserve disabled and externally enforced permission profiles instead of replacing them with a managed sandbox.
- Continue restricting consolidation to the memory root without network access when the parent uses Codex-managed permissions.
## Testing
- Add coverage for disabled, external, and managed parent permission profiles.
GitOrigin-RevId: 9ca3be0e41dc14d858053f6f70e33f4ae7c578e1
## Why
Paginated thread history needs durable ordering so consumers can process a rollout suffix without rebuilding all earlier history.
## What changed
- Add optional, zero-based ordinals to `RolloutLine` records in paginated rollouts while leaving legacy rollout serialization unchanged.
- Continue ordinals from the last valid record when appending or resuming, including after gaps or an incomplete tail, and reject overflow without appending.
- Add a stateless `project_rollout_line` helper that maps canonical turn lifecycle and completed-item records into thread-history change sets.
## Testing
- Cover ordinal assignment, legacy compatibility, resume and tail recovery, overflow handling, and thread-history projection for completed, failed, and interrupted turns.
GitOrigin-RevId: 3a9bb6cd2a1f674a9f154342e96e9aa3d8330781
## What changed
- Carry newly installed or updated remote plugin metadata through effective-plugin refresh callbacks, including coalesced refreshes.
- After a successful refresh, record the current hook hashes for listed workspace plugins associated with the active account while preserving existing hook settings and unrelated state.
- Serialize the background trust write with config mutations, and leave hooks untrusted if the write fails or the active account changes.
## Testing
- Cover eligible plugin selection and escaped hook config keys.
- Verify end-to-end trust for newly materialized plugin hooks, preservation of existing config, and fail-closed behavior when config cannot be written.
GitOrigin-RevId: 0b150766415be6fccc117f7856a241e10ad456f2
## What changed
- Allow the local thread store to create paginated threads while keeping them unsupported through the app-server API.
- Filter live append items using the history mode recorded with the thread's live recorder, so paginated threads persist canonical `ItemCompleted` events instead of legacy history events.
## Testing
- Add a local-store test that creates a paginated thread and verifies that a paginated item is persisted while a legacy user-message event is omitted.
GitOrigin-RevId: 83d93660afd5f0217213d4f21b5bf2a97745ff19
## What changed
- Add an optional `error` payload to `TurnCompleteEvent` and omit it when a turn completes without an error.
- Preserve the full terminal `ErrorEvent` through the turn lifecycle so the completion event includes the same error details emitted separately by `EventMsg::Error`.
## Testing
- Extend the stream-error integration test to verify that `TurnCompleteEvent` contains the emitted error and that the next turn can still proceed.
GitOrigin-RevId: 8d32942a44132763076130102d8b89ab356d59d0
## What changed
- Stop the TUI and app server from inspecting existing sessions at startup to
set `personality = "pragmatic"` automatically.
- Remove the migration marker, helper APIs, and associated tests.
GitOrigin-RevId: 9e963bf113aff21651696bbc463f2749ac0f510a
## What changed
- Add optional `scheduledTasks` metadata to `plugin/read` responses for remote
plugins, including hourly, daily, weekday, and weekly schedules.
- Preserve the distinction between unavailable metadata (`null`) and a catalog
response containing no scheduled tasks (`[]`). Local plugin details return
`null`.
- Export the corresponding Rust, JSON Schema, and TypeScript protocol types.
## Testing
- Cover scheduled-task deserialization and propagation through `plugin/read`.
- Verify missing and empty remote metadata remain distinct.
GitOrigin-RevId: 7af223ed0a1bf6d8f054293084a73f34c6d0332d
## What changed
- Add an optional `started_at` Unix timestamp to `TurnCompleteEvent` and
`TurnAbortedEvent`.
- Populate the timestamp from turn timing state when a turn completes or is
aborted.
- Preserve the timestamp when synthesizing interrupted fork history and when
importing external sessions.
## Testing
- Verify that interrupting a turn emits a `TurnAbortedEvent` with a start
timestamp.
GitOrigin-RevId: 75b3911b95c060e5fc1843a5e293d8fe47377186
## Why
App-server clients can report whether Amazon Bedrock is using
AWS-managed credentials or a Codex-managed API key, but they do not have
a matching API for creating the managed login. This PR defines that
experimental wire contract independently from its implementation.
Managed Bedrock API keys are already a primary `CodexAuth` mode. The API
therefore describes a normal Codex login that replaces the current
stored auth rather than introducing provider-scoped credential storage.
## What changed
- Add the experimental `amazonBedrock` variant to `account/login/start`.
- Accept an API key and AWS region and return a matching discriminated
response.
- Gate the request behind the app-server `experimentalApi` capability.
- Regenerate the JSON and TypeScript protocol schemas.
- Document the login contract, notifications, primary-auth replacement
semantics, restart boundary, and non-transactional durable writes.
## Impact
This PR defines the API shape but does not implement login behavior. The
next PR adds validation, persistence through the existing Codex auth
lifecycle, provider selection, and notifications.
## Validation
- `just test -p codex-app-server-protocol`
## Stack
1. **#31327 Managed Bedrock experimental API** — base: `main`
2. #31326 Managed Bedrock login — base: `codex/managed-bedrock-api`
3. #31325 Managed Bedrock logout — base:
`codex/managed-bedrock-login-v2`
## Summary
- discover enabled-plugin marketplace sources from the user-level known
marketplace registry
- preserve scoped settings and supported Git/directory declarations,
falling back to the matching registry `installLocation` for file, URL,
npm-hosted, and inline marketplace sources
- resolve relative registry materialization paths against the
external-agent home
- reuse the existing marketplace add and plugin install flow during
import
- emit `plugin_not_found` tracing and analytics with plugin and
marketplace identity
## Why
Enabled plugins can remain in settings after their marketplace source
has moved into the user-level registry. Import detection previously
skipped those plugins because it only consulted settings-defined
marketplace sources.
## Impact
`/import` can now restore those marketplaces and install their enabled
plugins through the normal plugin installer, including npm, Git, and
local plugin sources. Missing plugins remain visible in import results
and the plugin-install failure analytics stream.
## Description
This PR makes new threads with `history_mode = "paginated"` persist
`ItemCompleted(item: <turn_item>)` in their rollout JSONL file.
Legacy threads keep persisting the existing legacy events. Because the
format is selected per thread, a rollout is either legacy or paginated;
we do not need to support mixed rollouts containing both
representations.
This PR depends on [#31473](https://github.com/openai/codex/pull/31473).
## Why
Paginated thread history needs stable turn/item IDs and completed item
snapshots so the later SQLite projector can materialize appended rollout
JSONL without rebuilding the whole thread.
Keeping the legacy persistence policy unchanged avoids changing
historical rollouts or the readers that still consume them.
## What changed
- Made rollout filtering history-mode aware. Paginated threads keep
completed canonical `ItemCompleted` events and drop their redundant
legacy projections; legacy threads keep the existing event set.
- Made forks inherit the source thread history mode, so copied legacy
history is never filtered as paginated.
- Made paginated threads assign IDs to locally-created response items
even when `Feature::ItemIds` is off, and reject streamed output items
that arrive without server IDs.
- Updated legacy turn replay, rollout list/search, and SQLite metadata
extraction to understand completed canonical user-message items.
## Why
A thread resumed without an explicit reviewer could pick up the reviewer
from the current config instead of preserving the reviewer already in
use by the thread. After an app restart, this meant a thread running
with auto review could silently switch back to user review, and the next
turn could continue under the wrong reviewer.
## What changed
Persist the effective reviewer with each turn and restore the latest
persisted value when the thread resumes. If the resume request
explicitly provides a reviewer, that value still takes precedence.
## Test plan
- Added a regression test that starts a thread with auto review, records
a turn, restarts with user review in config, resumes without an
override, and verifies that auto review is preserved.
- `just test -p codex-protocol`
- `just test -p codex-state`
- `just test -p codex-rollout`
- `just test -p codex-app-server
thread_resume_preserves_persisted_approvals_reviewer`
- Clippy for the affected crates
## Why
Macrobenchmarks benefit from having a way to exercise remote-executor
latency without depending on Docker.
This is a very minimal first cut, if we find that simulating network
conditions is useful we can always expand this scope or switch to a more
robust network shaping approach.
## What
- add a package-local exec-server binary for Cargo and Bazel test
fixtures
- add a host-local WebSocket exec-server fixture and fixed-delay
interposer
- let TestAppServer route its auto environment through that delayed
WebSocket transport
- cover the delayed thread/start path through the public app-server API
## Stack
1. [#31425 test: add TestAppServer
builder](https://github.com/openai/codex/pull/31425)
2. [#31427 test: add delayed exec-server
transport](https://github.com/openai/codex/pull/31427)
3. [#31295 bench: add cold skill load
macrobenchmark](https://github.com/openai/codex/pull/31295)
4. [#31428 bench: add e2e benchmark
entrypoints](https://github.com/openai/codex/pull/31428)
5. [#31429 ci: smoke Bazel e2e
benchmarks](https://github.com/openai/codex/pull/31429)
## Description
This PR moves hook prompts onto the canonical `TurnItem` lifecycle in
core.
Stop hooks now record their `ResponseItem` through the existing
lifecycle path, which emits `ItemStarted` and `ItemCompleted`.
App-server consumes those events directly instead of deriving a hook
prompt from `RawResponseItem`.
## Why
Hook prompts were the only `ThreadItem` app-server synthesized from
`RawResponseItem`. This brings them in line with other core-owned turn
items while preserving legacy rollout replay.
## What changed
- Route stop-hook prompts through
`record_response_item_and_emit_turn_item`.
- Materialize canonical hook prompts in `ThreadHistoryBuilder`.
- Remove `RawResponseItem` to `ThreadItem` synthesis while preserving
legacy rollout replay.
- Add focused coverage for lifecycle emission and canonical and legacy
history materialization.
## Description
This PR moves review-mode markers onto canonical `TurnItem` lifecycle:
- `TurnItem::EnteredReviewMode`
- `TurnItem::ExitedReviewMode`
Core now emits `ItemStarted` / `ItemCompleted` for both. The completed
items map back into the existing `EnteredReviewMode` /
`ExitedReviewMode` events, so raw core event consumers and legacy
rollout persistence keep seeing the old events.
This is the compatibility layer needed before paginated rollouts persist
review markers as `ItemCompleted(TurnItem)`.
## Why
Review markers were one of the remaining app-server thread items created
directly from legacy events. Giving them canonical items lets paginated
history persist stable turn/item IDs without changing legacy rollouts.
## What changed
- Added canonical review-mode `TurnItem`s and switched review flow to
emit their lifecycle.
- Added completed-item → legacy review event mappings with stable
turn/item IDs.
- Switched app-server live notifications to the generic canonical item
path and kept legacy replay compatible with old payloads.
- Updated `ThreadHistoryBuilder` to replay canonical review items even
though review turns still do not emit `TurnStarted`.
## Summary
- classify plugin store and remote bundle I/O failures by privacy-safe
operation context
- include the normalized `sub_error_type` in structured plugin-install
failure warnings
- leave analytics events and schemas unchanged
## Why
Top-level error types such as `store_io` collapse multiple filesystem
operations, making logs harder to diagnose. The subtype identifies the
failed operation without logging paths or new exception details.
## Impact
Plugin-install warning logs gain an optional normalized subtype.
Analytics payloads, app-server APIs, and stored data are unchanged.
## Validation
- `just fmt`
- `just test -p codex-core-plugins -p codex-app-server` (1,235 passed;
14 failed and 6 timed out because local `sandbox-exec` was denied,
`test_stdio_server` was unavailable, or user-level skill fixtures were
loaded)
## Description
This PR adds a `codex-extension-items` crate for extension-owned
`TurnItem` schemas, and updates standalone image generation to start
using it via `TurnItem::Extension`.
This gives us a way to prevent Core from having to be aware of all
extension items. App-server still exposes the existing public
`ThreadItem::ImageGeneration` shape, now by wrapping the same shared
`image_generation::ImageGenerationItem` type.
The new `codex-extension-items` crate is necessary because the image gen
extension item is used by:
- `codex-image-generation-extension`, which produces it.
- `codex-tools / core`, which carry it generically.
- `codex-protocol`, which serializes it into lifecycle events and
rollouts.
- `app-server protocol`, which wraps it in public
`ThreadItem::ImageGeneration`
```
extension implementation
↓
codex-extension-items
↓
protocol / tools / app-server
```
We keep the hosted Responses API image generation as
`TurnItem::ImageGeneration` because core still owns its persistence and
legacy fanout.
### Before
Standalone image generation is implemented as an extension, but its item
representation previously lived in the core protocol. This sets the
precedent that core is aware of all extension items, which would be good
to avoid.
```
image-gen extension
→ constructs codex_protocol::ImageGenerationItem
→ emits ExtensionTurnItem::ImageGeneration
→ core matches ImageGeneration specially
→ protocol stores TurnItem::ImageGeneration
```
### After
```
image-gen extension
→ constructs extension-owned ImageGenerationItem
→ emits generic ExtensionItem
→ core transports/persists it generically
→ app-server wraps ImageGenerationItem as ThreadItem::ImageGeneration
```
Future extension items can have typed app-server APIs without adding a
new `TurnItem` variant, `ExtensionTurnItem` variant, or core emitter
match arm.
## What changed
- Added `codex-extension-items` with the closed `ExtensionItem` enum and
shared `image_generation::ImageGenerationItem` schema.
- Added generic `TurnItem::Extension(ExtensionItem)` and
`ExtensionTurnItem::Extension { item, legacy_events }` paths.
- Updated standalone image generation to emit a typed extension item and
provide its existing legacy `ImageGenerationBegin` /
`ImageGenerationEnd` events explicitly.
- Kept canonical lifecycle ordering: core emits `ItemStarted` /
`ItemCompleted` before extension-provided legacy events.
## Follow-up
Standalone web search still uses its typed special-case path. Migrating
it later would let `ExtensionTurnItem` collapse into a single
extension-item struct.
## Summary
External auth had two paths: provider-command credentials were resolved
through `ExternalAuth`, while app-provided ChatGPT credentials were
installed separately and only used the provider for refresh.
`ExternalAuth` also declared an auth mode independently from the
`CodexAuth` value it returned, so the declaration and credentials could
disagree.
This change makes the provider-owned `CodexAuth` authoritative for
initial resolution, credential kind, and refresh.
- remove `ExternalAuth::auth_mode` and require providers to return their
current auth from `resolve`
- route external auth registration, resolution, and unauthorized refresh
through `AuthManager::set_external_auth`
- keep the last resolved credential with its provider so synchronous
consumers and unauthorized recovery observe the credential's actual mode
- make the app-server bridge own both initial and refreshed ChatGPT
credentials, and detach it on logout
- keep model listing free of auth-refresh side effects, including in
offline mode
Provider-command auth still follows its configured cache interval.
App-provided ChatGPT auth still asks the parent app once after a `401`
and retries the request once.
Stacked on #31355.
## Testing
- `just test -p codex-login`
- `just test -p codex-models-manager`
- focused `codex-app-server` tests for external login/logout,
unauthorized refresh, and workspace mismatch
This PR depends on [#31296](https://github.com/openai/codex/pull/31296)
for the canonical-to-legacy event mappings.
## Description
This PR makes the v1 and v2 wait paths emit canonical
`TurnItem::CollabAgentToolCall` lifecycle instead of
`CollabWaitingBegin` / `CollabWaitingEnd` directly.
Both paths already used the same legacy waiting events before this PR.
The v1 item carries receiver metadata and final agent statuses for its
target agents; v2 waits for mailbox activity rather than specific
agents, so it keeps those fields empty, matching the existing v2 legacy
payload.
App-server v2 consumes the canonical item directly and ignores the
mapped legacy wait events.
## Why
Wait is separate from the other collab tools because it is multi-target
and has distinct timeout/status behavior. Keeping it last also lets this
PR remove the old helper that only existed to shape legacy wait status
entries in core.
## What changed
- Emit canonical collab wait items from both v1 and v2 wait handlers.
- Preserve receiver metadata and agent status snapshots on completed
wait items.
- Remove the old core helper for building legacy wait status entries.
## Follow-up
The next stack PR, [#30188](https://github.com/openai/codex/pull/30188),
writes canonical `TurnItem` values to paginated rollout files.
This PR depends on [#31296](https://github.com/openai/codex/pull/31296)
for the canonical-to-legacy event mappings.
## Description
This PR makes the non-wait v1 collaboration tools—spawn, send input,
resume, and close—emit canonical `TurnItem::CollabAgentToolCall`
lifecycle instead of their legacy begin/end events directly.
App-server v2 consumes the canonical collab items directly, ignores the
mapped legacy events, and applies close-agent thread-watch cleanup from
the completed item.
## Why
These four tools share the same single-target lifecycle shape. Wait
stays separate because it carries multi-target status snapshots and has
its own status-shaping cleanup.
## What changed
- Add shared helpers for emitting canonical collab tool-call lifecycle.
- Migrate spawn, send input, resume, and close handlers.
- Move close-agent watcher cleanup onto canonical completed collab
items.
This PR depends on [#31296](https://github.com/openai/codex/pull/31296)
for the canonical-to-legacy event mappings.
## Description
This PR makes the MultiAgentV2 spawn, message/follow-up, and interrupt
paths emit completed canonical `TurnItem::SubAgentActivity` items
instead of `SubAgentActivityEvent` directly.
App-server v2 now applies interrupted-agent thread-watch cleanup from
the canonical completed item and ignores the mapped legacy activity
event.
## Why
Sub-agent activity is separate from the v1 collab tool begin/end
lifecycle. Keeping it separate makes the v2 watcher side effect
reviewable without mixing in the larger collab tool-call migration.
## What changed
- Emit canonical sub-agent activity items from v2 spawn,
message/follow-up, and interrupt paths.
- Move missing-thread watcher cleanup onto canonical completed activity
items.
- Update focused app-server coverage to exercise canonical interrupted
activity.