## Summary
- skip online model refresh for subagent sessions
- avoid rollout flushes during subagent startup
- keep /models refresh for non-subagent sessions
## Testing
- cargo test -p codex-core --test all
suite::models_etag_responses::refresh_models_on_models_etag_mismatch_and_avoid_duplicate_models_fetch
- cargo test -p codex-core --test all
suite::remote_models::remote_models_long_model_slug_is_sent_with_high_reasoning
- cargo test -p codex-core --test all
suite::model_switching::model_switch_to_smaller_model_updates_token_context_window
- cargo test -p codex-core --test all
suite::compact::pre_sampling_compact_runs_on_switch_to_smaller_context_model
- cargo test -p codex-core --test all
suite::compact::pre_sampling_compact_runs_after_resume_and_switch_to_smaller_model
- cargo test -p codex-core --test all
suite::personality::remote_model_friendly_personality_instructions_with_feature
---------
Co-authored-by: Codex <noreply@openai.com>
## Why
- tighten Codex memory-read behavior around stale facts and conflicting
memory
- encode the risk-of-drift vs verification-effort decision rule directly
in the read-path prompt
- make partial stale-detail updates explicit so correcting only the
answer is not treated as sufficient
## What changed
- update `codex-rs/core/templates/memories/read_path.md`
- add guidance for when to verify cheap local facts vs when to answer
from older memory with visible provenance
- strengthen same-turn `MEMORY.md` updates when stored concrete details
are stale
## Notes
- this is based on some staleness eval work
Addresses #12913
`codex exec` was not correctly defaulting to Otel metrics to enabled
`codex mcp-server` completely lacked an Otel collector
Summary:
- default to enabling analytics when `codex exec` initializes
OpenTelemetry so the CLI actually reports metrics again
- add a regression test that proves the flag remains enabled by default
- added Otel collector to `codex mcp-server`
Addresses #12967
About a month ago, I updated the Github bug report templates to
accommodate the (at the time) new Codex app. The `/feedback` code path
in the CLI was referencing one of the old templates, and I didn't
realize it at the time. This PR updates the link so users don't get an
empty bug template when using `/feedback`.
#### what
adds a `codex debug clear-memories` command to help with clearing all
memories state from disk, sqlite db, and marking threads as
`memory_mode=disabled` so they don't get resummarized when the
`memories` feature is re-enabled.
#### tests
add tests
## Why
[#12964](https://github.com/openai/codex/pull/12964) added
`host_executable()` support to `codex-execpolicy`, but the zsh-fork
interception path in `unix_escalation.rs` was still evaluating commands
with the default exact-token matcher.
That meant an intercepted absolute executable such as `/usr/bin/git
status` could still miss basename rules like `prefix_rule(pattern =
["git", "status"])`, even when the policy also defined a matching
`host_executable(name = "git", ...)` entry.
This PR adopts the new matching behavior in the zsh-fork runtime only.
That keeps the rollout intentionally narrow: zsh-fork already requires
explicit user opt-in, so it is a safer first caller to exercise the new
`host_executable()` scheme before expanding it to other execpolicy call
sites.
It also brings zsh-fork back in line with the current `prefix_rule()`
execution model. Until prefix rules can carry their own permission
profiles, a matched `prefix_rule()` is expected to rerun the intercepted
command unsandboxed on `allow`, or after the user accepts `prompt`,
instead of merely continuing inside the inherited shell sandbox.
## What Changed
- added `evaluate_intercepted_exec_policy()` in
`core/src/tools/runtimes/shell/unix_escalation.rs` to centralize
execpolicy evaluation for intercepted commands
- switched intercepted direct execs in the zsh-fork path to
`check_multiple_with_options(...)` with `MatchOptions {
resolve_host_executables: true }`
- added `commands_for_intercepted_exec_policy()` so zsh-fork policy
evaluation works from intercepted `(program, argv)` data instead of
reconstructing a synthetic command before matching
- left shell-wrapper parsing intentionally disabled by default behind
`ENABLE_INTERCEPTED_EXEC_POLICY_SHELL_WRAPPER_PARSING`, so
path-sensitive matching relies on later direct exec interception rather
than shell-script parsing
- made matched `prefix_rule()` decisions rerun intercepted commands with
`EscalationExecution::Unsandboxed`, while unmatched-command fallback
keeps the existing sandbox-preserving behavior
- extracted the zsh-fork test harness into
`core/tests/common/zsh_fork.rs` so both the skill-focused and
approval-focused integration suites can exercise the same runtime setup
- limited this change to the intercepted zsh-fork path rather than
changing every execpolicy caller at once
- added runtime coverage in
`core/src/tools/runtimes/shell/unix_escalation_tests.rs` for allowed and
disallowed `host_executable()` mappings and the wrapper-parsing modes
- added integration coverage in `core/tests/suite/approvals.rs` to
verify a saved `prefix_rule(pattern=["touch"], decision="allow")` reruns
under zsh-fork outside a restrictive `WorkspaceWrite` sandbox
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/13046).
* #13065
* __->__ #13046
Stop `thread/start` from blocking other app-server requests.
Before this change, `thread/start ran` inline on the request loop, so
slow startup paths like MCP auth checks could hold up unrelated requests
on the same connection, including `thread/loaded/list`. This moves
`thread/start` into a background task.
While doing so, it revealed an issue where we were doing nested locking
(and there were some race conditions possible that could introduce a
"phantom listener"). This PR also refactors the listener/subscription
bookkeeping - listener/subscription state is now centralized in
`ThreadStateManager` instead of being split across multiple lock
domains. That makes late auto-attach on `thread/start` race-safe and
avoids reintroducing disconnected clients as phantom subscribers.
Addresses #12566
Summary
- stop printing the final assistant message on stdout when the process
is running in a terminal so interactive users only see it once
- add a helper that gates the stdout emission and cover it with unit
tests
- override startup tooltips with model availability NUX and persist
per-model show counts in config
- stop showing each model after four exposures and fall back to normal
tooltips
Addresses https://github.com/openai/codex/issues/13007 and
https://github.com/openai/codex/issues/12170
There are situations where the ChatGPT auth backend might return a JWT
that contains no plan information. Most code paths already handle this
case well, but the internal implementation of the "account/read" app
server call was failing in this case (returning an error rather than
properly returning None for the plan).
This resulted in a situation where users needed to log in every time the
extension or app started even if they successfully logged in the last
time.
Summary
- allow ChatGPT-authenticated accounts to fall back to
`AccountPlanType::Unknown` when the token omits the plan claim
- add regression coverage in `app-server/tests/suite/v2/account.rs` to
confirm `account/read` returns `plan_type: Unknown` when the claim is
absent
- ensure the Rust auth helpers and fixtures treat missing plan claims as
Optional and default to `Unknown`