## What changed
- Set `parallel_tool_calls` for regular and remote compaction prompts without consulting model metadata.
- Remove `supports_parallel_tool_calls` from `ModelInfo`, the bundled model catalog, and related fixtures.
- Preserve the existing Responses Lite behavior that disables parallel tool calls at request construction.
GitOrigin-RevId: 49552bdf97e71fa57325abb49f8adc0c88f1401a
## What changed
- Add `TurnInputRequest` and typed submission results for atomically starting a turn, steering the active turn, or declining input with a specific reason.
- Expose `start_or_steer_turn`, `start_turn_if_idle`, and `steer_turn` on `CodexThread`, and migrate Core consumers to these APIs.
- Make app-server `turn/start` steer an active regular turn and return that turn's ID. Reject incompatible output schemas and non-steerable turns without applying settings or enqueueing input.
## Testing
- Cover concurrent start-or-steer submissions, accepted and rejected settings updates, output-schema compatibility, idle-start rejection, and app-server steering.
GitOrigin-RevId: dd9b5528d76ec650c019e97af420bc13190ea86a
## What changed
- Add fractional Unix creation times to locally authored user, developer, agent, and tool-output items when they enter durable conversation history.
- Preserve any creation time already supplied with an item across subsequent requests, remote compaction, and resumed history.
## Testing
- Cover timestamp assignment and preservation in session, client, tool, agent-spawn, and remote-compaction tests.
GitOrigin-RevId: 431d1770b9746005c808bc0a43953845166d68ad
## What changed
- Set `store` to `false` for every Responses request, including requests sent
through Azure providers.
- Remove the provider-specific storage check and simplify request construction.
- Update the Azure request test to verify that storage is disabled while
prefixed item IDs remain unchanged.
GitOrigin-RevId: 1c71cb80880058cb26789112f03269b73f0645b9
## What changed
- Wrap response items with optional harness-owned metadata while keeping the persisted response payload shape backward compatible.
- Store compacted-history metadata in an aligned sidecar and reject malformed sidecars.
- Carry metadata through history normalization, compaction, resume, fork, truncation, migration, and rollout recording without sending it to model providers.
## Testing
- Cover legacy rollout compatibility, metadata serialization and validation, compaction retention, and resume/fork reconstruction.
GitOrigin-RevId: 17a6127c9048ff984cb4df4d97c0751a38c29f5f
## What changed
- Add `codex-history` for model-history and persisted-rollout domain types, including `RolloutItem`, `RolloutLine`, `CompactedItem`, and initial/resumed history state.
- Re-export the persisted types from `codex-rollout` and update consumers to use the new crate boundary instead of `codex-protocol`.
- Preserve existing rollout serialization, including legacy numeric compacted-window IDs.
## Testing
- Add `codex-history` tests for rollout JSON round trips, compacted-history compatibility, persisted history modes, and multi-agent version selection.
GitOrigin-RevId: 944daa9297ddd231d3aebbdcb05fff4adf8b4e1b
## What changed
- Add an `x-codex-routing-hint` header containing the request model and, when selected, the service tier.
- Send the hint on Responses HTTP requests, remote compaction requests, and WebSocket handshakes, including prewarm connections.
- Limit the header to Codex-backend authentication with the OpenAI provider, omitting it for API-key and provider-specific credential paths.
## Testing
- Cover routing hints for HTTP, remote compaction, WebSocket streaming, and tiered prewarming, along with the credential paths where the header must be absent.
GitOrigin-RevId: d8d415d7fadddecf9a132d05920b856a35ac9960
## What changed
- Make the skills extension own the catalog prompt templates and rendering path.
- Remove the duplicate core fallback that injected available skills into initial context.
- Update core integration tests to install the skills extension explicitly when they expect model-visible skill context.
GitOrigin-RevId: bbe6b15c4e776a09c98b7f4166426fe4e58434ba
## What changed
- Preserve `encrypted_function_args` on function calls so an empty list can mark plaintext collaboration arguments across request replay.
- Deliver `spawn_agent`, `send_message`, and `followup_task` payloads as structured plaintext agent messages when that marker is present; retain encrypted delivery otherwise.
- Redact plaintext collaboration arguments from tool and communication logs, and omit the metadata when sending requests to non-OpenAI providers.
## Testing
- Cover serialization of empty encrypted-argument metadata and plaintext versus encrypted subagent message delivery.
- Verify plaintext tool arguments are redacted and provider-specific metadata is removed from non-OpenAI requests.
GitOrigin-RevId: 64db98ff0b61a3af2f04ed609292363f2e2362a8
## What changed
- Insert the `host_skills` world-state section before the permissions section
when permissions are present.
- Verify that skill metadata appears before `<permissions instructions>` in the
rendered developer message.
GitOrigin-RevId: e9fc62f498c384e841fc34ef679850557645f989
## What changed
- Add the `supports_standalone_web_search` model-provider setting, defaulting to `false`, and preserve it in remote thread configuration.
- Enable the standalone `web.run` tool for opted-in custom Responses providers when web search is enabled and the runtime provider supports it.
- Send standalone search requests through the custom provider's endpoint and authentication.
## Testing
- Cover opt-in, default-off, disabled-search, and unsupported-provider behavior.
- Verify custom-provider search request routing and authorization through the app server.
GitOrigin-RevId: 7c5f96b0ce924ad2b9715c45bfc635e89fc39cff
## What changed
- Add `StartThreadOptions::new` to provide the standard configuration for a new thread.
- Make `ThreadManager::start_thread` the single thread-start entry point and migrate callers from the previous convenience methods.
- Derive default environment selections when `environments` is `None`, while preserving explicit selections, including an empty list.
GitOrigin-RevId: 8977dc11aed54c5e1215a81eaed2b2cf5fc6087a
## Why
ChatGPT token refresh requests need to follow the same configured routing policy as other authentication traffic, including environments that use the system proxy.
## What changed
- Require an `AuthRouteConfig` throughout login, logout, token refresh, personal access token, and agent identity flows.
- Build auth HTTP clients directly from that configuration instead of falling back to a default HTTP client when routing configuration is absent.
## Testing
- Add an integration test that refreshes a token through a cached system-proxy route and verifies the proxy receives the expected request.
GitOrigin-RevId: 7d54ab3219939a49921c51faf08cd4c2eabca51c
## What changed
- Assign IDs to client-created response items in every session, including streamed items, forked history, compaction results, and non-OpenAI provider requests.
- Preserve server-provided IDs and stable generated IDs across persisted and resumed conversations while continuing to omit unprefixed server IDs from outbound requests.
- Retire `features.item_ids` as a configurable feature. Existing configuration and CLI toggles remain accepted as compatibility no-ops.
## Testing
- Cover default ID assignment, persistence and resume behavior, remote compaction, provider requests, WebSocket requests, and removed-feature compatibility.
GitOrigin-RevId: 7a09e2d07d2606c2ef1e8e2283d36cb555557eae
## What changed
- Add `shell_environment_policy.filters`, mapping environment-variable patterns to `include` or `exclude`, while continuing to accept the legacy `exclude` and `include_only` arrays.
- Merge filter keys case-insensitively across config layers so higher-precedence entries can override individual patterns. Switching representations replaces the other representation instead of combining them.
- Reject mixed, duplicate, or malformed filter definitions in enabled layers, config writes, and reloads, while retaining the previous session configuration when a reload is invalid.
- Make config-manager reads and writes representation-aware and preserve existing formatting when updating individual policy values.
## Testing
Add coverage for parsing, schema constraints, layered merging, validation and diagnostics, config writes, override metadata, and invalid session reloads.
GitOrigin-RevId: e15464bca53e6e7ef31fcc97537fda60ed5d670a
## What changed
- Add `audio` to the model input modalities exposed by the protocol and generated app-server schemas.
- Preserve audio in prompts for models that advertise audio input, and replace historical audio with an omission marker for models that do not.
## Testing
- Cover prompt normalization with and without audio support.
- Extend the model-switching integration test to verify that switching from a multimodal model to a text-only model strips prior image and audio content.
GitOrigin-RevId: c18e47ad602720216303ff9abb5bca4d508391ee
## What changed
- Model permission instructions as a world-state section keyed by a stable hash of their rendered developer message.
- Re-emit permission context when its model-visible contents change or the retained fragment is missing, while avoiding duplicates when matching instructions already exist in history, including bundled developer messages.
- Normalize CRLF line endings when hashing world-state fragments so equivalent content produces the same snapshot.
## Testing
- Add snapshot coverage for unchanged, changed, absent, and unknown permission state.
- Cover bundled retained permissions and restored world-state baselines during rollout reconstruction.
GitOrigin-RevId: 7d8d212460c1690ca617859f66dcd825987f1e23
## Why
Audio variants were available in the user-input protocol but were replaced with unsupported-input placeholders instead of being sent to the model.
## What changed
- Serialize audio data URLs as `input_audio` content and convert local `wav`, `mp3`, `m4a`, `webm`, and `ogg` files to labeled data URLs.
- Validate and canonicalize base64 audio before requests, enforce the 50 MiB input limit, and replace invalid, unsupported, or oversized audio with explanatory text.
- Preserve audio attachments when mapping response items back to user-message events and document the app-server input variants.
## Testing
- Add unit coverage for local-file conversion, data URL validation, size and format errors, event mapping, and attachment extraction.
- Add client tests that verify data URL and local audio payloads sent to the Responses API.
GitOrigin-RevId: f72cd6b6e43ab12757eeb47621b1d670594ac7d9
## What changed
- Allow the built-in `amazon-bedrock` provider to override `base_url`, `auth`,
and `http_headers` in addition to its AWS profile and region.
- Use command-based bearer authentication and configured endpoints without
applying AWS request signing, while retaining regional endpoint resolution
for the default Bedrock configuration.
- Replace the Bedrock account `credentialSource` enum with the
`usesCodexManagedCredentials` boolean so command-authenticated and other
externally managed configurations are reported consistently.
## Testing
- Cover configuration merging and validation, command-authenticated proxy
requests with custom headers, and account reporting for managed and external
credentials.
GitOrigin-RevId: d1acbe602060470583b5e12f8d304bee5be46f4c
## What changed
- Parse `cache_write_tokens` from response input token details and carry it through token usage aggregation.
- Expose `cache_write_input_tokens` in protocol, app-server, exec, and TypeScript SDK usage events, defaulting it to zero for compatibility with older payloads.
- Report cache-write usage in analytics, metrics, traces, and rollout data alongside existing cache-read usage.
## Testing
- Add coverage for response parsing, usage event output, aggregation, analytics, metrics, and telemetry fields.
GitOrigin-RevId: 634e4fbe3086a8923c0ee13b622573e7d9d221bd
## 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
## 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
- Default `prompt_cache_key` to the session ID from response metadata instead of the thread ID.
- Preserve explicit prompt cache key overrides.
- Verify that API-key-authenticated root and subagent requests use the same session-based cache key even though they have different thread IDs.
GitOrigin-RevId: cb1bf21a3cb8fa2c5083c5c6afd4829ff2adcf1f
## 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
## 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
- Add a `ResponseItemId` type that generates item-specific prefixes with UUIDv7 suffixes and use it across response items.
- Keep deserialization permissive for legacy histories, but omit empty or unprefixed item IDs from HTTP and WebSocket requests.
- Export the new type in the generated TypeScript protocol schema.
## Testing
- Cover prefixed ID generation, legacy deserialization, prefix recognition, and outbound request filtering.
GitOrigin-RevId: 0209fe430c826d0ae88bc4648652c4ebf390c4a4
## What changed
- Add `supports_reasoning_summary_parameter` to model metadata, defaulting to
`true` for backward compatibility.
- Omit `reasoning.summary` and its summary-delivery stream option when the
selected model does not support the parameter.
- Apply the capability of the final selected model when a spawned agent uses a
different model.
## Testing
- Cover unsupported models in regular requests and spawned-agent model
overrides.
GitOrigin-RevId: 72b783799fc0685cef1501ef2dbf62d1308ceead
## What changed
- Build a reasoning payload for every Responses request and always include `reasoning.encrypted_content`.
- Remove `supports_reasoning_summaries` from model metadata and retire the `model_supports_reasoning_summaries` configuration override.
- Use configured or model-default reasoning effort without a capability gate, including for guardian reviews and tracing.
GitOrigin-RevId: 2c9f194a5d2d4d688a2235299e6358f82ab8e1ea
## Why
`features.respect_system_proxy` already routes authentication traffic
through the OS proxy APIs, but it does not affect the primary inference
path. That leaves users behind OS-managed proxies unable to send normal
Responses API requests even after login succeeds.
This PR is the first product-path migration onto the route-aware
transport introduced in #31323 and refined in #31331. It also
establishes the construction pattern for later migrations: the effective
feature state is resolved once into a required HTTP client factory
rather than represented by an optional per-call setting.
The scope remains limited to the two HTTP Responses endpoints;
WebSockets, model discovery, memories, realtime, and file uploads remain
follow-up migrations.
## What changed
- Replace the optional proxy marker with an explicit
`OutboundProxyPolicy::{ReqwestDefault, RespectSystemProxy}` and a
required `HttpClientFactory`. The policy has no default, and the
lower-level route-aware reqwest builder is now private.
- Have `Config` construct the factory from the effective feature state
and require every `ModelClient` constructor to receive it. There is no
optional setter or implicit `None` fallback.
- Build HTTP clients for `/responses` and `/responses/compact` with
`ClientRouteClass::Api`, using the complete destination URL so PAC rules
can make URL-specific decisions.
- Layer route-aware selection onto Codex's existing default headers,
Cloudflare cookie store, custom CA handling, and sandbox no-proxy
behavior.
- Add an integration test that loads `features.respect_system_proxy`
through `config.toml`, creates a real Codex session, and verifies that
both a normal Responses turn and remote compaction reach an isolated
local proxy.
## Review guide
1. `http-client/src/outbound_proxy.rs` defines the mandatory
policy/factory boundary and keeps route resolution private.
2. `core/src/config/mod.rs`, `core/src/session/session.rs`, and
`core/src/client.rs` show the compile-time invariant: effective config
creates the factory, and `ModelClient` cannot be constructed without
one.
3. `login/src/auth/default_client.rs` preserves existing default-client
behavior while accepting the required factory for migrated routes.
4. `core/src/client.rs` switches only streaming Responses and remote
compaction HTTP transports to the API route class.
5. `core/tests/suite/responses_api_system_proxy.rs` is the behavioral
regression boundary. Its Linux subprocess deliberately sets the CGI
marker that disables reqwest's implicit environment-proxy handling, so
the test fails if session wiring or either Responses call site falls
back to the default client.
## Test plan
- `cargo check --tests -p codex-http-client -p codex-login -p
codex-core`
- `just test -p codex-login`
- `just test -p codex-core
respect_system_proxy_feature_resolves_enabled`
- Existing `compact_uses_bearer_after_agent_identity_session_fallback`
coverage passes with the new transport construction.
- New Linux integration coverage:
`responses_and_compact_use_enabled_system_proxy`
- `just bazel-lock-check`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/31335).
* #31342
* __->__ #31335
## Summary
When enabled for the OpenAI provider, Codex sends
`stream_options.reasoning_summary_delivery = "sequential_cutoff"` on
HTTP and
WebSocket requests, including prewarm, and renders completed summary
sections
from `reasoning_summary_text.done`. Flag-off and non-OpenAI behavior is
unchanged.
## Expected rollout
```text
reasoning 0 added
summary 0 done
summary 1 done
summary 2 starts
summary 2 cancelled / incomplete
reasoning 0 done <-- cancel summary 2 work and mark it incomplete
message 1 added
message 1 text streams
message 1 completed
```
Depends on
[openai/openai#1096660](https://github.com/openai/openai/pull/1096660).
## Why
The Bedrock GPT-5.6 catalog advertises `max`, but Codex treated it as an
opaque custom effort. That made the reasoning picker render it as
lowercase `max` while known efforts use productized labels.
Making `max` a known effort aligns catalog data, parsing, and UI
presentation without changing the `max` wire value or persisted
representation.
## What changed
- Add first-class `ReasoningEffort::Max` parsing and serialization.
- Use the typed effort in the Bedrock catalog and render it as `Max` in
the TUI.
- Preserve forward-compatible custom-effort coverage with a genuinely
unknown `future` value.
### Before
<img width="559" height="124" alt="Screenshot 2026-06-28 at 12 08 47 PM"
src="https://github.com/user-attachments/assets/7c43cf4f-020b-4605-9239-0a9c97eb7364"
/>
### After
<img width="558" height="107" alt="Screenshot 2026-06-28 at 12 09 10 PM"
src="https://github.com/user-attachments/assets/b9cc5ded-c940-43b4-b024-bba25abe0a17"
/>
## Why
Response item IDs represent stable conversation identity.
`ContextManager::for_prompt` repairs an unmatched call by synthesizing
an `"aborted"` output in the disposable prompt projection, but that
output previously had no ID. Assigning a fresh ID on every prompt build
would make retries and resumes change otherwise identical model context
and reduce prompt-cache reuse.
The concrete bug is that these normalization-created outputs bypass the
regular item-ID allocation path. Even with item IDs enabled, a prompt
could therefore contain an identified call paired with a synthetic
output whose `id` was missing. This change closes that gap by deriving
the output ID from the source call's item ID. For legacy calls that have
no item ID, the output remains ID-less because there is no stable source
identity to derive from.
The originating call already has a stable item ID under the item-ID
model introduced in #28814. A prompt-only output can therefore derive
stable identity from that call without mutating canonical history or
persisted rollouts. This addresses the failure exposed by #30311 while
keeping normalization read-only outside its detached prompt snapshot.
UUIDv5 is intentional here because it is the standard namespaced,
deterministic UUID construction. Using the output kind and source call
ID as the name produces the same UUID on every projection while keeping
output kinds in separate name domains. UUIDv7 would introduce randomness
and time, so keeping it stable would require persisting the synthetic
repair. UUIDv5 uses SHA-1 internally, but this is only an identity
mapping—not an authenticity or security boundary.
## What changed
- Derive a deterministic UUIDv5 ID for each synthesized call output from
the source call item ID.
- Use the Responses API prefix appropriate for function, custom-tool,
tool-search, and local-shell outputs.
- Preserve the existing insertion position immediately after the
unmatched call.
- Keep synthesized outputs prompt-only; no rollout, task-lifecycle,
compaction, or raw-response behavior changes.
## Testing
- `just test -p codex-core
for_prompt_assigns_stable_id_to_synthetic_output_without_reordering_history`
- `just test -p codex-core
synthetic_call_output_id_is_stable_across_resumes`
- `just test -p codex-core normalize_adds_missing_output`
- `just test -p codex-core response_item_ids`
## Summary
- Preserve the optional namespace on custom tool calls during response
deserialization and app-server replay.
- Use the namespaced tool identifier for streaming argument handling and
tool dispatch.
- Regenerate app-server protocol schemas.
- Add regression tests covering namespace serialization and routing.
## Testing
- Ran affected protocol and app-server test suites.
- Ran the full core test suite; two load-sensitive timing tests passed
when rerun individually.
- Ran Clippy and formatting checks.
- Verified with a local end-to-end app-server replay that the namespace
is preserved through the complete request/response flow.
## Stack
This is PR 3 of the simplified HAI single-run-task stack:
- [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
assertion and task-registration primitives, including the shared
run-task helper used by existing Agent Identity JWT auth.
- [#19049](https://github.com/openai/codex/pull/19049)
Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
Agent Identity runtime auth and its single run task.
- [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
provider auth that uses one backend-owned task id for first-party
inference and compaction requests.
[#19054](https://github.com/openai/codex/pull/19054) collapsed out of
the active stack because the simplified design no longer needs a
separate background/control-plane task helper.
## Summary
This PR moves Agent Identity usage into provider auth resolution. That
keeps `AgentAssertion` auth tied to first-party OpenAI provider requests
instead of applying a late session-wide override that could affect
local, custom, Bedrock, API-key, or external-bearer providers.
What changed:
- adds a small `ProviderAuthScope` struct carrying the run auth policy
and session source needed by provider-scoped auth resolution
- lets `Session` opt the existing `ModelClient` into `ChatGptAuth`
policy when `use_agent_identity` is enabled, without adding a second
model-client constructor
- resolves Agent Identity only for first-party OpenAI provider auth
paths
- uses the persisted run task id from the `AgentIdentityAuth` record to
build `AgentAssertion` auth for Responses requests
- routes shared request setup through scoped provider auth so unary
compact requests use the same run-task assertion path as inference turns
- keeps local/custom/Bedrock/env-key/external-bearer provider auth
unchanged
- lets missing run-task state surface through the existing model-request
error path instead of silently falling back to bearer auth
This PR intentionally does not create thread-scoped, target-scoped, or
background-scoped task identities. The run task is the only task Codex
registers in this POC shape.
## Testing
- `just test -p codex-model-provider`
- `just test -p codex-core client::tests::provider_auth_scope_uses`
- `just test -p codex-core remote_compact_uses_agent_identity_assertion`
Pick up the AgentGraphStore migration.
- Inject an explicit optional agent graph store into `ThreadManager`
- Move all calls to spawn, close, recursive resume, and
subtree/archive/delete/feedback traversal through it
- Keep using `LocalAgentGraphStore` when SQLite is available
This required some changes to the interface to deal with futures:
- The interface now matches `ThreadStore`'s object-safe pattern by
returning a boxed `AgentGraphStoreFuture` directly, allowing
`ThreadManager` to hold `Arc<dyn AgentGraphStore>`
*Slight behavior change!* Unfiltered subtree enumeration now performs a
single all-status breadth-first traversal, so a closed grandchild
beneath an open edge is included; the previous Open-then-Closed
traversals could not cross mixed-status paths and silently omitted it.
## Why
Work(TPP) threads can be launched from the Desktop app, but if they all
keep the Desktop app's default originator then downstream attribution
cannot distinguish local Work launches from cloud-backed Work launches.
`thread/start.serviceName` already carries that launch signal, while
`SessionMeta.originator` is the durable thread-level value that survives
resume and fork.
This change converts the Desktop Work service names into an effective
originator at thread creation time, persists that originator with the
thread, and keeps using it for later model requests and memory writes.
## What changed
- Map `CODEX_WORK_LOCAL` and `CODEX_WORK_CLOUD` service names to
per-thread originators, while preserving
`CODEX_INTERNAL_ORIGINATOR_OVERRIDE` as the highest-precedence override.
- Persist the effective originator in `SessionMeta.originator`, read it
back on resume/fork, and inherit the parent originator for subagent
spawns when there is no persisted session metadata.
- Handle truncated `SpawnAgentForkMode::LastNTurns` forks by falling
back to the live parent originator when the forked history no longer
includes `SessionMeta`.
- Thread the per-thread originator through Responses headers,
websocket/compaction request paths, thread-store creation, rollout
metadata, and memory stage-one telemetry.
## Verification
- `just test -p codex-core
agent::control::tests::spawn_thread_subagent_inherits_parent_originator_without_fork
agent::control::tests::spawn_thread_subagent_fork_last_n_turns_inherits_parent_originator_without_session_meta
thread_manager::tests::originator_override_precedes_service_name_remapping`
- `just test -p codex-core
agent::control::tests::resume_thread_subagent_restores_stored_metadata_and_effective_multi_agent_mode`
- `just test -p codex-memories-write`
- `just fix -p codex-core -p codex-memories-write`
- `git diff --check`
## Description
This PR is a followup to https://github.com/openai/codex/pull/28355 and
starts assigning `internal_chat_message_metadata_passthrough.turn_id` to
durable Responses API items created during a turn.
The goal is that those items keep the `turn_id` that introduced them
when Codex resends stateless HTTP context, reconstructs history for
resume/fork paths, or reuses websocket response state.
## What changed
- Set `internal_chat_message_metadata_passthrough.turn_id` when missing
as response items enter durable history, initial/replacement history,
inter-agent communication history, and local compaction summaries.
- Preserve existing item turn IDs instead of overwriting them during
persistence, resume reconstruction, compaction, forked history, and
websocket incremental reuse.
- Keep `compaction_trigger` fieldless because it is a request control,
not a durable response item.
- Update focused history/request assertions and fixtures for stateless
requests, websocket incrementals, compaction, thread injection, prompt
debug, and related CI coverage.
## Summary
Stacked on #26706.
Adds the shared auth/system-proxy contract that later platform resolver
PRs plug into. This PR moves Codex-owned auth and startup HTTP clients
through a common route-aware boundary, but does not yet add Windows or
macOS system proxy resolution.
The default path remains unchanged when `respect_system_proxy` is absent
or disabled.
## Implementation
- Adds `codex-client/src/outbound_proxy.rs` with the shared
route-selection model:
- `OutboundProxyConfig`;
- `ClientRouteClass`;
- `RouteFailureClass`;
- `build_reqwest_client_for_route`.
- Preserves the existing reqwest/default-client behavior when no route
config is supplied.
- Uses the fixed MVP routing policy when route config is supplied:
platform system/PAC/WPAD discovery, then explicit env proxy variables,
then direct connection.
- Keeps platform-specific system discovery behind the shared client
boundary. This PR provides the contract and fallback behavior; later
resolver PRs plug in Windows and macOS discovery.
- Adds `login::AuthRouteConfig` so auth call sites depend on a small
policy type instead of platform resolver details.
- Maps the resolved `Config.respect_system_proxy` boolean into
`AuthRouteConfig` for auth-owned clients.
- Wires the route config through browser login, device-code login,
access-token login, login status, logout/revoke, token refresh, API-key
exchange, app-server account login, TUI/app startup, cloud-config
bootstrap, cloud tasks, plugin auth, and exec startup config loading.
## End-user behavior
- No behavior changes by default.
- When `respect_system_proxy = true`, auth-owned clients opt into the
shared route-aware client path.
- On platforms without a resolver implementation in this PR, system
discovery is unavailable and the route-aware path falls back to explicit
env proxy handling, then direct connection.
- Custom CA handling remains separate from proxy route selection and
still runs through the shared client builder.
- No proxy URLs, PAC contents, or resolved platform details are exposed
through the public config surface introduced here.
## Tests
Adds or updates coverage for:
- preserving default auth-client fallback behavior when no route config
is provided;
- injected environment-proxy fallback without mutating process
environment;
- existing login-server E2E flows using explicit `auth_route_config:
None` to guard unchanged default behavior;
- updated auth manager, login, logout, cloud-config, startup, and
plugin-auth call sites passing route config explicitly.
## Description
This PR cuts Codex over from generic `ResponseItem.metadata` (introduced
here: https://github.com/openai/codex/pull/28355) to
`ResponseItem.internal_chat_message_metadata_passthrough`, which is the
blessed path and has strongly-typed keys.
For now we have to drop this MAv2 usage of `metadata`:
https://github.com/openai/codex/pull/28561 until we figure out where
that should live.
## What
- make Fjord's centralized response-item image preparation unconditional
for new and resumed history
- have local user images and `view_image` outputs always defer decoding
and resizing to that path
- retain `resize_all_images` as an ignored, removed compatibility key
for released clients
- delete the flag-off producer paths and obsolete policy-specific tests
## Why
Centralized preparation is now the intended image path. Keeping the
runtime feature checks also kept two image-processing implementations
alive and allowed client config to select the legacy behavior.
This is a clean replacement for #28975, rebuilt from the latest `main`.
## How
`prepare_response_items` now runs whenever items enter history and
whenever persisted history is reconstructed. Producers emit deferred
image data, so malformed images become the existing model-visible
placeholder instead of failing the session at the producer.
## Test plan
- `just fmt`
- `just fix -p codex-core -p codex-features`
- `just test -p codex-features` — 52 passed
- focused affected `codex-core` set — 20 passed
- `just test -p codex-core handle_accepts_explicit_high_detail` — 1
passed
- full `just test -p codex-core` attempt — 2,723 passed; 88 unrelated
environment failures from read-only `~/.codex` SQLite state and
unavailable integration helper binaries
## Summary
A cold-resumed subagent kept its durable thread ID but could receive a
new session ID, splitting one agent tree across multiple sessions after
a restart.
Persist the root session ID in every rollout `SessionMeta`, carry it
through thread creation, and restore it before initializing the resumed
`Session` and `AgentControl`.
## Behavior
For a nested agent tree:
```text
root session R
parent thread P
child thread C
```
The child rollout stores:
```text
session_id: R
parent_thread_id: P
id: C
```
After a cold resume, the child still belongs to root session `R` while
its immediate parent remains `P`. The integration coverage uses distinct
values for all three IDs so it catches restoring the session from
`parent_thread_id`.
## Legacy rollouts
Previous rollouts have `id` but no `session_id`. `SessionMetaLine`
deserialization treats a missing `session_id` as `id`, keeping those
files readable, listable, and resumable. When a legacy subagent is
resumed through its root, that synthesized child ID no longer overrides
the inherited root-scoped `AgentControl`. New rollouts always persist
the explicit root session ID.
## Why
Orchestrator-provided skills and Codex Apps MCP tools add model-visible
instructions, resources, and tools beyond the local workspace. Hosts
need config-level switches to disable those orchestrator-owned surfaces
independently, without disabling regular skills or regular MCP servers.
## What changed
- Adds `[orchestrator.skills].enabled` and `[orchestrator.mcp].enabled`
config entries, both defaulting to `true`.
- Includes the new settings in `config.schema.json` and in the config
lock so resolved thread configuration preserves the same orchestrator
exposure decisions.
- Threads `orchestrator.skills.enabled` through the app-server skills
extension so disabled orchestrator skills do not expose the `skills`
namespace or inject orchestrator skill context.
- Gates Codex Apps MCP exposure, app instructions, and app auth
eligibility on `orchestrator.mcp.enabled` while leaving non-Codex-Apps
MCP tools available.
- Updates the thread-manager sample config to disable both
orchestrator-owned surfaces.
## Verification
- Added config parsing, loading, defaulting, and schema coverage for the
new settings.
- Added MCP exposure coverage that `orchestrator.mcp.enabled = false`
removes Codex Apps tools while preserving regular MCP tools.
- Added app-server coverage that `orchestrator.skills.enabled = false`
prevents orchestrator skill tools, prompts, and resource reads from
reaching the model turn.
## Why
Remote v2 compaction can return replacement-history items without IDs.
Because replacement history is installed directly, those items bypass
normal history preparation and remain ID-less in later Responses
requests even when the `item_ids` feature is enabled.
## What changed
- Pass the active `TurnContext` into `replace_compacted_history`.
- When `item_ids` is enabled, assign missing IDs before installing and
persisting replacement history.
- Rebuild `CompactedItem` from the prepared history so live and
persisted replacement histories match.
- Add integration coverage requiring IDs on every ID-capable input item
in the initial, remote v2 compaction, and post-compaction requests.
## Test plan
- `just test -p codex-core response_item_ids`
- `just test -p codex-core websocket_v2_test_codex_shell_chain`
- `just test -p codex-core remote_compaction_parity_pre_turn_auto`
- `just test -p codex-app-server
thread_inject_items_adds_raw_response_items_to_thread_history`
## Why
Client-created response items enter history without IDs, so their
identity is lost across rollout persistence and resume. IDs should be
assigned once at the history-recording boundary, while IDs returned by
the server must remain unchanged.
The Responses API validates item IDs using type-specific prefixes.
Locally generated IDs therefore use the matching prefix plus a
hyphenated UUIDv7, keeping them valid while distinguishable from
server-generated IDs. Because this changes persisted history and
provider request shapes, the behavior is opt-in behind the
under-development `item_ids` feature. Compaction triggers remain request
controls whose API shape does not accept an ID.
## What changed
- Register the disabled-by-default `item_ids` feature and expose it in
`config.schema.json`.
- Make supported optional `ResponseItem` IDs serializable and expose
them in the generated app-server schemas.
- When `item_ids` is enabled, assign an ID during conversation-history
preparation if an item has no ID.
- Generate type-prefixed, hyphenated UUIDv7 IDs using the Responses API
item conventions.
- Preserve existing server IDs without rewriting them.
- Persist assigned IDs in rollouts and include them in subsequent
Responses requests.
- Remove the unsupported ID field from `CompactionTrigger` and document
why it has no ID.
- Add integration coverage for enabled ID persistence, preservation of
server IDs, and omission of generated IDs while the feature is disabled.
`prepare_conversation_items_for_history` is the single response-item ID
allocation boundary.
## Test plan
- `just test -p codex-features`
- `just test -p codex-core
response_item_ids_persist_across_resume_and_preserve_server_ids`
- `just test -p codex-core
non_openai_responses_requests_omit_item_turn_metadata`
- `just test -p codex-core
resize_all_images_prepares_failures_before_history_insertion`
- `just test -p codex-protocol`
- `just test -p codex-app-server-protocol`
- `just test -p codex-api azure_default_store_attaches_ids_and_headers`
Stacked on #28822.
## Summary
- add a host-injectable current-time provider with a built-in system
implementation
- record UTC developer reminders in history immediately before due model
requests
- keep cadence state per session and force a refresh after compaction
This does NOT include the app server client <-> server clock logic. This
PR is only for the reminder message & system clock that will be used in
prod.
## Testing
- `just test -p codex-core varlatency_`
- `just clippy -p codex-core -p codex-app-server -p codex-mcp-server -p
codex-thread-manager-sample`
- `just fmt`
## Why
`ResponseItem` variants do not have a consistent internal ID shape: some
variants carry required IDs, some carry optional IDs, and some cannot
represent an ID at all. The existing fields also use inconsistent serde,
TypeScript, and JSON-schema annotations. A single enum-level access path
is needed before history recording can assign and retain IDs.
This PR establishes that internal model only. It intentionally does not
generate or serialize IDs; allocation and wire persistence are isolated
in the stacked follow-up.
## What changed
- Give every concrete `ResponseItem` variant an `Option<String>` ID
field.
- Apply the same internal-only annotations to every ID field:
`#[serde(default, skip_serializing)]`, `#[ts(skip)]`, and
`#[schemars(skip)]`.
- Add `ResponseItem::id()` and `ResponseItem::set_id()` as the shared
accessors.
- Preserve IDs when history items are rewritten for truncation.
- Adapt consumers that previously assumed reasoning and image-generation
IDs were required.
- Regenerate app-server schemas so the hidden fields are represented
consistently.
The serde catch-all `ResponseItem::Other` remains ID-less because it
must remain a unit variant.
## Test plan
- `cargo check --tests -p codex-core -p codex-api -p codex-rollout-trace
-p codex-image-generation-extension`
- `just test -p codex-protocol`
- `just test -p codex-app-server-protocol`
- `just test -p codex-api -p codex-rollout-trace -p
codex-image-generation-extension`
- `just test -p codex-core event_mapping`
The workspace denies `clippy::expect_used` in production. Although
`clippy.toml` allows `expect` in tests, Bazel Clippy compiles
integration-test helper code in a way that does not receive that
exemption, which encouraged verbose `unwrap_or_else(... panic!(...))`
and equivalent `match`/`let else` forms.
This allows `clippy::expect_used` once at each integration-test crate
root (including aggregated suites and test-support libraries), then
replaces manual panic-based Result and Option unwraps with
`expect`/`expect_err`. Standalone `tests/*.rs` files remain their own
crate roots. Intentional assertion and unexpected-variant panics remain
unchanged, and the production `expect_used = "deny"` lint remains in
place.
The cleanup is mechanical and net-negative in line count.
## Description
This PR adds an optional `metadata` field to `ResponseItem` for
Responses API calls. Only mechanical plumbing, no actual values
populated and sent yet. Turns out just adding a new field to
`ResponseItem` has quite a large blast radius already.
This change is backwards compatible because `metadata` is optional and
omitted when absent, so existing response items and rollout history
without it still deserialize and requests that do not set it keep the
same wire shape. For provider compatibility, we strip out `metadata`
before non-OpenAI Responses requests so Azure and AWS Bedrock never see
this field.
My followup PR here will actually make use of it to start storing and
passing along `turn_id`: https://github.com/openai/codex/pull/28360
## What changed
- Added `ResponseItemMetadata` with optional `turn_id`, plus optional
`metadata` on Responses API item variants and inter-agent communication.
- Preserved item metadata through response-item rewrites such as
truncation, missing tool-output synthesis, compaction history
rebuilding, visible-history conversion, rollout/resume, and generated
app-server schemas/types.
- Strip item metadata from non-OpenAI Responses requests while
preserving it for OpenAI-shaped requests.
- Updated the mechanical fixture/test construction churn required by the
new optional field.
## Why
Windows Credential Manager limits generic credential blobs to 2,560
bytes. Large serialized ChatGPT auth payloads can exceed that limit, so
keyring-mode CLI auth needs a backend that keeps only the encryption key
in the OS keyring and stores the payload in Codex's encrypted
local-secrets file.
This is the third PR in the encrypted-auth stack:
1. #27504 — feature and config selection
2. #27535 — auth-specific local-secrets namespaces
3. This PR — CLI auth implementation and activation
4. MCP OAuth implementation and activation
## What Changed
- Added encrypted CLI-auth storage using the `CliAuth` secrets
namespace.
- Preserved direct keyring storage for platforms/configurations where it
remains selected.
- Selected the backend consistently for login, logout, refresh,
device-code login, auth loading, and login restrictions.
- Threaded resolved bootstrap/full config through CLI, exec, TUI,
app-server account handling, cloud config, and cloud tasks.
- Removed stale `auth.json` fallback data after successful encrypted
saves and removed encrypted, direct-keyring, and fallback data during
logout.
- Added storage and integration coverage for both direct and encrypted
keyring modes.
MCP OAuth persistence is intentionally left to the next PR.
## Validation
- `just test -p codex-login` — 131 passed
- `just test -p codex-cli` — 280 passed
- `just test -p codex-app-server v2::account` — 25 passed
- `just test -p codex-cloud-config service` — 21 passed, 7 skipped
- `just fix -p codex-login`
- `just fix -p codex-cli`
- `just fmt`
## Why
We already have the machinery to support multiple environments on a
single thread, but we only show the model the contents of `AGENTS.md`
files in the primary environment.
We should show the model all of the relevant project instructions when
we know there's more than one environment.
## Known Gaps
As discussed in the RFC, this implementation:
1. doesn't handle environments being added/removed to/from the thread
after its creation
2. it doesn't enforce an aggregate context budget across environments,
and instead applies the configured project maximum independently to each
environment
## Implementation
- Discover project instructions in environment order with an independent
byte budget per environment and preserve source provenance/order.
- Keep the legacy fragment byte-for-byte when exactly one environment
contributes project instructions; use environment-labeled sections when
two or more environments contribute.
- Freeze the complete rendered fragment in `LoadedAgentsMd`, insert it
directly into requests, and recognize both layouts in contextual and
memory filtering.
- Add exact rendering, independent-budget, source-order,
creation-snapshot, and consumer coverage without changing app-server
schemas.