## What changed
Select an originator-specific models manager before resolving thread model metadata. Supported originators send their own `originator` header and use separate in-memory catalogs and disk caches, so each thread receives the corresponding model instructions and tools.
Reuse managers for repeated originators and share the API-key model discovery setting across them. Unsupported originators retain the default manager and catalog.
## Testing
Add coverage for originator headers, concurrent catalog isolation, cache reuse across restarts, shared discovery policy, and the instructions and tools sent in actual turns.
GitOrigin-RevId: ae4aa51c8df2813a72fc8aed8978fd630e467069
## Why
Providers with `model_catalog_url` could advertise bundled models absent from their catalog or reuse stale models after a failed refresh. Model metadata could also match by prefix or namespaced suffix instead of the provider's exact model ID.
## What changed
- Suppress bundled model fallback and merging for explicit provider catalogs, including when discovery is disabled.
- Require unique, non-empty catalog slugs and match metadata by exact model ID.
- Serialize explicit catalog refreshes and invalidate in-memory and persisted catalog entries after fetch failures, preventing stale cache reuse.
- Report a clear configuration error when startup has no available model, while allowing an explicitly configured `model`.
## Testing
Add regression coverage for exact model matching, hidden catalog entries, cache invalidation across restart and recovery, invalid slugs, and empty-catalog startup with and without an explicit model. Update app-server model-list expectations and verify the TUI configuration error.
GitOrigin-RevId: ac323a94178e3c48df8d4a159a86fdbac20b03a2
## Why
Switching providers or authentication could reuse a previous identity's model catalog, including its default service tier. An in-flight refresh could also overwrite a newer account's catalog after cache storage completed.
## What changed
- Require a matching provider and auth identity for disk and in-memory catalog reuse. Treat legacy entries without an identity as cache misses and fall back to bundled metadata when the in-memory identity no longer matches.
- Recheck identity after fetching and storing a catalog before publishing it.
- Renew cache freshness only when the client version, identity, and ETag match the stored entry.
- Update cache fixtures to include the configured provider and auth identity.
## Testing
Add regression coverage for provider and auth switches, credential rotation, legacy and mismatched cache entries, conditional TTL renewal, and an account switch during cache storage. Verify that API-key requests do not inherit a ChatGPT catalog's default `service_tier`, while explicit tier selection still works.
GitOrigin-RevId: e15d7ffff238d951d7a1c6a68381cbb479994146
## What changed
Add a SHA-256 identity derived from provider routing, headers, and authentication scope. Return it with each model catalog response and persist it in `ModelsCacheEntry`. ChatGPT credentials with stable account and user metadata retain the same identity across token refreshes; opaque API credentials contribute to the digest.
## Testing
Add identity tests covering account, user, email, plan, auth mode, provider routing, headers, and API credential changes, plus stability across ChatGPT token refreshes. Update cache tests to include the persisted identity.
GitOrigin-RevId: 3f51c6cabcb01bc03505150a768a61dfe5d6569f
## What changed
- Add the public asynchronous `ModelsCache` contract and cache entry/error types.
- Let model providers and `OpenAiModelsManager` accept caller-provided cache implementations while retaining the existing file-backed cache by default.
- Treat cache read failures as misses, keep cache write failures non-fatal, and support refreshing an entry's TTL after ETag revalidation.
- Continue to prefer an explicitly configured static model catalog over an injected cache.
## Testing
- Cover injected cache hits, read and write failures, TTL refreshes, and end-to-end model selection.
- Verify that the file-backed implementation satisfies the new cache contract and does not serve expired entries before revalidation.
GitOrigin-RevId: eef957a317910b8502a0c20f8fc112f689d5e389
## What changed
- Remove `ModelInfo.base_instructions` as an in-memory instruction source and use `model_messages.instructions_template` consistently for bundled, remote, fallback, and overridden model metadata.
- Preserve compatibility by promoting legacy `base_instructions` values when reading model responses and caches, and by including rendered legacy instructions when serializing `ModelsResponse` for older clients.
- Treat templates without instruction variables as literal text and retain the other model-message fields when applying instruction overrides.
This completes the consolidation proposed in https://github.com/openai/codex/pull/31302.
## Testing
- Cover legacy response and cache migration, canonical-template precedence, fallback instructions, personality rendering, overrides, and model switching.
GitOrigin-RevId: 089d986ca5e30da67db2c77a1b6a046d2cff52dc
## Why
Matching model ETags can arrive on every response, but a recent models cache does
not need to be rewritten each time.
## What changed
Renew the models cache timestamp only after more than half of its configured TTL
has elapsed. Matching ETags continue to avoid refetching `/models`.
## Testing
Added integration coverage that verifies a recent cache remains unchanged and an
older cache is renewed without another `/models` request.
GitOrigin-RevId: b45da3824674c6b41d982ba4a405b419b09b7715
## 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
## Summary
- split `models-manager` out of `core` and add `ModelsManagerConfig`
plus `Config::to_models_manager_config()` so model metadata paths stop
depending on `core::Config`
- move login-owned/auth-owned code out of `core` into `codex-login`,
move model provider config into `codex-model-provider-info`, move API
bridge mapping into `codex-api`, move protocol-owned types/impls into
`codex-protocol`, and move response debug helpers into a dedicated
`response-debug-context` crate
- move feedback tag emission into `codex-feedback`, relocate tests to
the crates that now own the code, and keep broad temporary re-exports so
this PR avoids a giant import-only rewrite
## Major moves and decisions
- created `codex-models-manager` as the owner for model
cache/catalog/config/model info logic, including the new
`ModelsManagerConfig` struct
- created `codex-model-provider-info` as the owner for provider config
parsing/defaults and kept temporary `codex-login`/`codex-core`
re-exports for old import paths
- moved `api_bridge` error mapping + `CoreAuthProvider` into
`codex-api`, while `codex-login::api_bridge` temporarily re-exports
those symbols and keeps the `auth_provider_from_auth` wrapper
- moved `auth_env_telemetry` and `provider_auth` ownership to
`codex-login`
- moved `CodexErr` ownership to `codex-protocol::error`, plus
`StreamOutput`, `bytes_to_string_smart`, and network policy helpers to
protocol-owned modules
- created `codex-response-debug-context` for
`extract_response_debug_context`, `telemetry_transport_error_message`,
and related response-debug plumbing instead of leaving that behavior in
`core`
- moved `FeedbackRequestTags`, `emit_feedback_request_tags`, and
`emit_feedback_request_tags_with_auth_env` to `codex-feedback`
- deferred removal of temporary re-exports and the mechanical import
rewrites to a stacked follow-up PR so this PR stays reviewable
## Test moves
- moved auth refresh coverage from `core/tests/suite/auth_refresh.rs` to
`login/tests/suite/auth_refresh.rs`
- moved text encoding coverage from
`core/tests/suite/text_encoding_fix.rs` to
`protocol/src/exec_output_tests.rs`
- moved model info override coverage from
`core/tests/suite/model_info_overrides.rs` to
`models-manager/src/model_info_overrides_tests.rs`
---------
Co-authored-by: Codex <noreply@openai.com>