Commit Graph

116 Commits

  • [codex-rs] auto-review model override (#23767)
    ## Why
    
    Guardian auto-review normally uses the provider-preferred review model
    when one is available. Some parent models need model-catalog metadata to
    select a different review model while keeping older `/models` payloads
    compatible when that metadata is absent.
    
    ## What changed
    
    - Added optional `ModelInfo::auto_review_model_override` metadata to the
    public model payload as a review-model slug.
    - Updated Guardian review model selection to prefer the catalog override
    when present, while preserving the existing provider preferred-model
    path and parent-model fallback when it is omitted.
    - Added focused Guardian coverage for override and no-override model
    selection.
    - Added an `auto_review` core integration suite test that loads override
    metadata from a remote model catalog path and asserts the strict
    auto-review `/responses` request uses the catalog-selected review model.
    - Updated existing `ModelInfo` fixtures and local catalog constructors
    for the new optional field.
    
    ## Validation
    
    - `cargo test -p codex-protocol
    model_info_defaults_availability_nux_to_none_when_omitted`
    - `cargo test -p codex-core guardian_review_uses_`
    - `cargo test -p codex-core
    remote_model_override_uses_catalog_model_for_strict_auto_review --test
    all`
    - `just fix -p codex-protocol`
    - `just fix -p codex-core`
    - `just fmt`
    - `git diff --check`
  • [codex] Add model tool mode selector (#25031)
    ## Why
    Some models need to select their code-execution behavior through model
    catalog metadata. Models without that metadata must continue to follow
    the existing `CodeMode` and `CodeModeOnly` feature flags, including when
    a newer server sends an enum value this client does not recognize.
    
    ## What changed
    - add optional `ModelInfo.tool_mode` metadata with `direct`,
    `code_mode`, and `code_mode_only`
    - treat omitted and unknown wire values as `None`
    - resolve `None` from the existing feature flags
    - carry the resolved `ToolMode` directly on `TurnContext`, outside
    `Config`
    - use the resolved value for turn creation, model switches, review
    turns, tool planning, and code execution
    
    ## Coverage
    - add protocol coverage for omitted, known, and unknown enum values
    - add focused coverage for flag fallback and explicit metadata
    overriding feature flags
    - add core integration coverage that fetches remote model metadata
    through `/v1/models` and verifies the outbound `/responses` tools for
    explicit `direct` and `code_mode_only` selectors
    
    ## Stack
    - followed by #25032
  • fix(auto-review) skip legacy notify for auto review threads (#24714)
    ## Summary
    Clear inherited legacy `notify` from Guardian review session config,
    since we should not be passing auto review threads into `notify`
    targets. Keeps legacy notify payload and hook runtime behavior unchanged
    for normal user turns.
    
    ## Testing
    - [x] add a Guardian config regression and dedicated Guardian
    integration test so review sessions cannot inherit parent notify hooks
  • Add experimental turn additional context (#24154)
    ## Summary
    
    Adds experimental `additionalContext` support to `turn/start` and
    `turn/steer` so clients can provide ephemeral external context, such as
    browser or automation state, without turning that plumbing into a
    visible user prompt or triggering user-prompt lifecycle behavior.
    
    ## API Shape
    
    The parameter shape is:
    
    ```ts
    additionalContext?: Record<string, {
      value: string
      kind: "untrusted" | "application"
    }> | null
    ```
    
    Example:
    
    ```json
    {
      "additionalContext": {
        "browser_info": {
          "value": "Active tab is CI failures.",
          "kind": "untrusted"
        },
        "automation_info": {
          "value": "CI rerun is in progress.",
          "kind": "application"
        }
      }
    }
    ```
    
    The keys are opaque and caller-defined.
    
    ## Context Injection
    
    When provided, accepted entries are inserted into model context as
    hidden contextual message items, not as visible thread user-message
    items.
    
    `kind: "untrusted"` entries are inserted with role `user`:
    
    ```text
    <external_${key}>${value}</external_${key}>
    ```
    
    `kind: "application"` entries are inserted with role `developer`:
    
    ```text
    <${key}>${value}</${key}>
    ```
    
    Values are not escaped. Each value is truncated to 1k approximate tokens
    before wrapping.
    
    For `turn/start`, accepted additional context is inserted before normal
    user input. For `turn/steer`, additional context is merged only when the
    steer includes non-empty user input; context-only steers still reject as
    empty input.
    
    ## Dedupe Strategy
    
    `AdditionalContextStore` lives on session state and stores the latest
    complete additional-context map.
    
    Each `turn/start` or non-empty `turn/steer` treats its
    `additionalContext` as the current complete set of values. Entries are
    injected only when the key is new or the exact entry for that key
    changed, including `value` or `kind`. After merging, the store is
    replaced with the provided map, so omitted keys are removed from the
    retained set and can be injected again later if reintroduced.
    
    Omitting `additionalContext`, passing `null`, or passing an empty object
    resets the store to empty and injects nothing.
    
    ## What Changed
    
    - Threads experimental v2 `additionalContext` through app-server into
    core turn start and steer handling.
    - Adds separate contextual fragment types for untrusted user-role
    context and application developer-role context.
    - Uses pending response input items so additional context can be
    combined with normal user input without treating it as prompt text.
    - Adds integration coverage for start/steer flow, role routing,
    dedupe/reset behavior, deletion/re-add behavior, hook-blocked input
    behavior, empty context-only steer rejection, external-fragment marker
    matching, and truncation.
  • Run compact hooks for remote compaction v2 (#22828)
    ## Why
    
    Remote compaction v2 is the `/responses` implementation of
    session-history compaction, but it still needs to preserve the
    observable contract of the legacy `/responses/compact` path. In
    particular, users and integrations that rely on `PreCompact` and
    `PostCompact` hooks should not see different behavior when
    `remote_compaction_v2` is enabled.
    
    ## What Changed
    
    - Runs `PreCompact` before issuing the remote compaction v2 request,
    including `Interrupted` analytics when a pre-hook stops execution.
    - Runs `PostCompact` after a successful v2 compaction and aborts the
    turn if the post-hook stops execution.
    - Adds `compact_remote_parity` coverage that compares legacy and v2
    compaction across manual transcript shapes, automatic pre-turn
    compaction, automatic mid-turn compaction, hook payloads, replacement
    history, follow-up request payloads, and API-key `service_tier=fast`
    behavior.
    - Registers the new parity suite under `core/tests/suite`.
    
    Relevant code:
    
    -
    [`compact_remote_v2.rs`](https://github.com/openai/codex/blob/af63745cb502183a6fc447d0240f8150934d70b7/codex-rs/core/src/compact_remote_v2.rs)
    -
    [`compact_remote_parity.rs`](https://github.com/openai/codex/blob/af63745cb502183a6fc447d0240f8150934d70b7/codex-rs/core/tests/suite/compact_remote_parity.rs)
    
    ## Verification
    
    - Added `core/tests/suite/compact_remote_parity.rs` to assert parity
    between legacy remote compaction and remote compaction v2 for the
    affected request, hook, rollout-history, and follow-up paths.
    - Existing `compact_remote_v2` unit coverage still exercises v2
    replacement-history retention and compaction-output collection.
  • Add user_input_requested_during_turn to MCP turn metadata (#22237)
    ## Why
    - Similar change as https://github.com/openai/codex/pull/21219
    - Without change: MCP tool calls receive
    `_meta["x-codex-turn-metadata"]` with various key values.
    - Issue: MCP servers currently do not know if user input was requested
    during the turn (Ex: Model decides to prompt the user for approval
    mid-turn before making a possibly risky tool call). MCP servers may want
    to know this when tracking latency metrics because these instances are
    inflated.
    
    ## What Changed
    - With change: MCP turn metadata now includes
    `user_input_requested_during_turn` when a model-visible
    `request_user_input` call happened earlier in the turn, propagated in
    `_meta["x-codex-turn-metadata"]`.
    - `mark_turn_user_input_requested()` is called when user input is
    requested through either MCP elicitation (`mcp.rs`) or the
    `request_user_input` tool (`mod.rs`).
    - MCP tool call `_meta` is now built immediately before execution
    (`mcp_tool_call.rs`) so user input requested earlier in the same turn,
    including within the same tool call via elicitation, is reflected in the
    metadata.
    - Normal `/responses` turn metadata headers are unchanged.
    
    ## Verification
    - `codex-rs/core/src/session/mcp_tests.rs`
    - `codex-rs/core/src/tools/handlers/request_user_input_tests.rs`
    - `codex-rs/core/src/turn_metadata_tests.rs`
    - `codex-rs/core/tests/suite/search_tool.rs`
  • feat(sandbox): add Windows deny-read parity (#18202)
    ## Why
    
    The split filesystem policy stack already supports exact and glob
    `access = none` read restrictions on macOS and Linux. Windows still
    needed subprocess handling for those deny-read policies without claiming
    enforcement from a backend that cannot provide it.
    
    ## Key finding
    
    The unelevated restricted-token backend cannot safely enforce deny-read
    overlays. Its `WRITE_RESTRICTED` token model is authoritative for write
    checks, not read denials, so this PR intentionally fails that backend
    closed when deny-read overrides are present instead of claiming
    unsupported enforcement.
    
    ## What changed
    
    This PR adds the Windows deny-read enforcement layer and makes the
    backend split explicit:
    
    - Resolves Windows deny-read filesystem policy entries into concrete ACL
    targets.
    - Preserves exact missing paths so they can be materialized and denied
    before an enforceable sandboxed process starts.
    - Snapshot-expands existing glob matches into ACL targets for Windows
    subprocess enforcement.
    - Honors `glob_scan_max_depth` when expanding Windows deny-read globs.
    - Plans both the configured lexical path and the canonical target for
    existing paths so reparse-point aliases are covered.
    - Threads deny-read overrides through the elevated/logon-user Windows
    sandbox backend and unified exec.
    - Applies elevated deny-read ACLs synchronously before command launch
    rather than delegating them to the background read-grant helper.
    - Reconciles persistent deny-read ACEs per sandbox principal so policy
    changes do not leave stale deny-read ACLs behind.
    - Fails closed on the unelevated restricted-token backend when deny-read
    overrides are present, because its `WRITE_RESTRICTED` token model is not
    authoritative for read denials.
    
    ## Landed prerequisites
    
    These prerequisite PRs are already on `main`:
    
    1. #15979 `feat(permissions): add glob deny-read policy support`
    2. #18096 `feat(sandbox): add glob deny-read platform enforcement`
    3. #17740 `feat(config): support managed deny-read requirements`
    
    This PR targets `main` directly and contains only the Windows deny-read
    enforcement layer.
    
    ## Implementation notes
    
    - Exact deny-read paths remain enforceable on the elevated path even
    when they do not exist yet: Windows materializes the missing path before
    applying the deny ACE, so the sandboxed command cannot create and read
    it during the same run.
    - Existing exact deny paths are preserved lexically until the ACL
    planner, which then adds the canonical target as a second ACL target
    when needed. That keeps both the configured alias and the resolved
    object covered.
    - Windows ACLs do not consume Codex glob syntax directly, so glob
    deny-read entries are expanded to the concrete matches that exist before
    process launch.
    - Glob traversal deduplicates directory visits within each pattern walk
    to avoid cycles, without collapsing distinct lexical roots that happen
    to resolve to the same target.
    - Persistent deny-read ACL state is keyed by sandbox principal SID, so
    cleanup only removes ACEs owned by the same backend principal.
    - Deny-read ACEs are fail-closed on the elevated path: setup aborts if
    mandatory deny-read ACL application fails.
    - Unelevated restricted-token sessions reject deny-read overrides early
    instead of running with a silently unenforceable read policy.
    
    ## Verification
    
    - `cargo test -p codex-core
    windows_restricted_token_rejects_unreadable_split_carveouts`
    - `just fmt`
    - `just fix -p codex-core`
    - `just fix -p codex-windows-sandbox`
    - GitHub Actions rerun is in progress on the pushed head.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Reapply "Move skills watcher to app-server" (#21652)
    ## Why
    
    PR #21460 reverted the earlier move of skills change watching from
    `codex-core` into app-server. This reapplies that boundary change so
    app-server owns client-facing `skills/changed` notifications and core no
    longer carries the watcher.
    
    ## What
    
    - Restore the app-server `SkillsWatcher` and register it from thread
    listener setup.
    - Remove the core-owned skills watcher and its core live-reload
    integration surface.
    - Restore app-server coverage for `skills/changed` notifications after a
    watched skill file changes.
    
    ## Validation
    
    - `cargo test -p codex-app-server --test all
    suite::v2::skills_list::skills_changed_notification_is_emitted_after_skill_change
    -- --exact --nocapture`
    - `cargo test -p codex-core --lib --no-run`
  • Enable --deny-warnings for cargo shear (#21616)
    ## Summary
    
    In https://github.com/openai/codex/pull/21584, we disabled doctests for
    crates that lack any doctests. We can enforce that property via `cargo
    shear --deny-warnings`: crates that lack doctests will be flagged if
    doctests are enabled, and crates with doctests will be flagged if
    doctests are disabled.
    
    A few additional notes:
    
    - By adding `--deny-warnings`, `cargo shear` also flagged a number of
    modules that were not reachable at all. Some of those have been removed.
    - This PR removes a usage of `windows_modules!` (since `cargo shear` and
    `rustfmt` couldn't see through it) in favor of simple `#[cfg(target_os =
    "windows")]` macros. As a consequence, many of these files exhibit churn
    in this PR, since they weren't being formatted by `rustfmt` at all on
    main.
    - Again, to make the code more analyzable, this PR also removes some
    usages of `#[path = "cwd_junction.rs"]` in favor of a more standard
    module structure. The bin sidecar structure is still retained, but,
    e.g., `windows-sandbox-rs/src/bin/command_runner.rs‎` was moved to
    `windows-sandbox-rs/src/bin/command_runner/main.rs`, and so on.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Move skills watcher to app-server (#21287)
    ## Why
    
    Skills update notifications are app-server API behavior, but the watcher
    lived in `codex-core` and surfaced through
    `EventMsg::SkillsUpdateAvailable`. Moving the watcher out keeps core
    focused on thread execution and lets app-server own both cache
    invalidation and the `skills/changed` notification.
    
    ## What changed
    
    - Added an app-server-owned skills watcher that watches local skill
    roots, clears the shared skills cache, and emits `skills/changed`
    directly.
    - Registers skill watches from the common app-server thread listener
    attach path, including direct starts, resumes, and app-server-observed
    child or forked threads.
    - Stores the `WatchRegistration` on `ThreadState`, so listener
    replacement, thread teardown, idle unload, and app-server shutdown
    deregister by dropping the RAII guard.
    - Removed `EventMsg::SkillsUpdateAvailable`, the core watcher, and the
    old core live-reload test.
    - Extended the app-server skills change test to verify a cached skills
    list is refreshed after a filesystem change without forcing reload.
    
    ## Validation
    
    - `cargo check -p codex-core -p codex-app-server -p codex-mcp-server -p
    codex-rollout -p codex-rollout-trace`
    - `cargo test -p codex-app-server
    skills_changed_notification_is_emitted_after_skill_change`
  • core: fix apply_patch request permissions test (#21060)
    ## Why
    
    The Bazel test coverage change exposed
    `approved_folder_write_request_permissions_unblocks_later_apply_patch`,
    and `rust-ci-full.yml` showed the same test failing on `main` on macOS.
    There were two separate classes of problems here.
    
    ### Clean CI failure
    
    The test emits an `apply_patch` tool call, but its config did not enable
    the `apply_patch` tool, so the mocked response completed without an
    `apply-patch-call` output. After enabling the tool, the same path also
    needs the aggregate `codex-core` test binary to dispatch
    `--codex-run-as-fs-helper`; sandboxed `apply_patch` uses that helper
    under macOS Seatbelt.
    
    The test now also canonicalizes the temporary patch target before
    building the patch payload so the path matches normalized grants on
    macOS, where `/var` paths often normalize to `/private/var`.
    
    ### Local/enterprise config isolation
    
    The core test harness now builds its default test config with managed
    config disabled, so host-managed enterprise config cannot alter these
    tests. The request-permissions turns in this test also explicitly use
    the user reviewer path, keeping the assertions focused on
    `request_permissions` behavior rather than reviewer defaults from the
    host.
    
    ## What Changed
    
    - Enable `apply_patch` in
    `approved_folder_write_request_permissions_unblocks_later_apply_patch`.
    - Teach the core integration test binary to dispatch
    `CODEX_FS_HELPER_ARG1`, matching the existing apply-patch and
    linux-sandbox dispatch paths.
    - Canonicalize the tempdir-backed patch target before creating the
    patch.
    - Ignore managed config in default core test configs and explicitly pin
    this test to `ApprovalsReviewer::User`.
    
    ## Verification
    
    Run outside the Codex app sandbox because these macOS tests
    intentionally spawn Seatbelt:
    
    - `cargo test -p codex-core
    approved_folder_write_request_permissions_unblocks_later_apply_patch`
    - `cargo test -p codex-core
    approved_folder_write_request_permissions_unblocks_later_exec_without_sandbox_args`
  • [tool_suggest] More prompt polishes. (#20566)
    Tool suggest still misfires when model needs tool_search, updating the
    prompts to further disambiguate it:
    
    - [x] rename it from `tool_suggest` to `request_plugin_install`
    - [x] rephrase "suggestion" to "install" in the tool descriptions.
    - [x] disambiguate "the tool" vs "the plugin/connector". 
    
    Tested with the Codex App and verified it still works.
  • [codex] Remove unused event messages (#20511)
    ## Why
    
    Several legacy `EventMsg` variants were still emitted or mapped even
    though clients either ignored them or had moved to item/lifecycle
    events. `Op::Undo` had also degraded to an unavailable shim, so this
    removes that dead task path instead of preserving a command that cannot
    do useful work.
    
    `McpStartupComplete`, `WebSearchBegin`, and `ImageGenerationBegin` are
    intentionally kept because useful consumers still depend on them: MCP
    startup completion drives readiness behavior, and the begin events let
    app-server/core consumers surface in-progress web-search and
    image-generation items before the final payload arrives.
    
    ## What Changed
    
    - Removed weak legacy event variants and payloads from `codex-protocol`,
    including legacy agent deltas, background events, and undo lifecycle
    events.
    - Kept/restored `EventMsg::McpStartupComplete`,
    `EventMsg::WebSearchBegin`, and `EventMsg::ImageGenerationBegin` with
    serializer and emission coverage.
    - Updated core, rollout, MCP server, app-server thread history,
    review/delegate filtering, and tests to rely on the useful replacement
    events that remain.
    - Removed `Op::Undo`, `UndoTask`, the undo test module, and stale TUI
    slash-command comments.
    - Stopped agent job/background progress and compaction retry notices
    from emitting `BackgroundEvent` payloads.
    
    ## Verification
    
    - `cargo check -p codex-protocol -p codex-app-server-protocol -p
    codex-core -p codex-rollout -p codex-rollout-trace -p codex-mcp-server`
    - `cargo test -p codex-protocol -p codex-app-server-protocol -p
    codex-rollout -p codex-rollout-trace -p codex-mcp-server`
    - `cargo test -p codex-core --test all suite::items`
    - `just fix -p codex-protocol -p codex-app-server-protocol -p codex-core
    -p codex-rollout -p codex-rollout-trace -p codex-mcp-server`
    - Earlier coverage on this PR also included `codex-mcp`, `codex-tui`,
    core library tests, MCP/plugin/delegate/review/agent job tests, and MCP
    startup TUI tests.
  • feat: split memories part 2 (#19860)
    Keep extracting memories out of core and moving the write trigger in the
    app-server
    This is temporary and it should move at the client level as a follow-up
    This makes core fully independant from `codex-memories-write`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Support MCP tools in hooks (#18385)
    ## Summary
    
    Lifecycle hooks currently treat `PreToolUse`, `PostToolUse`, and
    `PermissionRequest` as Bash-only flows
    - hook schema constrains `tool_name` to `Bash`
    - hook input assumes a command-shaped `tool_input`
    - core hook dispatch path passes only shell command strings
    
    That means hooks cannot target MCP tools even though MCP tool names are
    model-visible and stable
    
    This change generalizes those hook paths so they can match and receive
    payloads for MCP tools while preserving the existing Bash behavior.
    
    ## Reviewer Notes
    
    I think these are the key files
    - `codex-rs/core/src/tools/handlers/mcp.rs`
    - `codex-rs/core/src/mcp_tool_call.rs`
    
    Otherwise the changes across apply_patch, shell, and unified_exec are
    mainly to rewire everything to be `tool_input` based instead of just
    `command` so that it'll make sense for MCP tools.
    
    ## Changes
    
    - Allow `PreToolUse`, `PostToolUse`, and `PermissionRequest` hook inputs
    to carry arbitrary `tool_name` and `tool_input` values instead of
    hard-coding `Bash` and command-only payloads.
    - Add MCP hook payload support through `McpHandler`, using the
    model-visible tool name from `ToolInvocation` and the raw MCP arguments
    as `tool_input`.
    - Include MCP tool responses in `PostToolUse` by serializing
    `McpToolOutput` into the hook response payload.
    - Run `PermissionRequest` hooks for MCP approval requests after
    remembered approval checks and before falling back to user-facing MCP
    elicitation.
    - Preserve exact matching for literal hook matchers like `Bash` and
    `mcp__memory__create_entities`, while keeping regex matcher support for
    patterns like `mcp__memory__.*` and `mcp__.*__write.*`.
    
    ---------
    
    Co-authored-by: Andrei Eternal <eternal@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • test(core): move prompt debug coverage to integration suite (#18916)
    ## Why
    
    `build_prompt_input` now initializes `ExecServerRuntimePaths`, which
    requires a configured Codex executable path. The previous inline unit
    test in `core/src/prompt_debug.rs` built a bare `test_config()` and then
    failed before it could assert anything useful:
    
    ```text
    Codex executable path is not configured
    ```
    
    This coverage is also integration-shaped: it drives the public
    `build_prompt_input` entry point through config, thread, and session
    setup rather than testing a small internal helper in isolation.
    
    Bazel CI did not catch this earlier because the affected test was behind
    the same wrapped Rust unit-test path fixed by #18913. Before that
    launcher/sharding fix, the outer `workspace_root_test` changed the
    working directory for Insta compatibility while the inner `rules_rust`
    sharding wrapper still expected its runfiles working directory. In
    practice, Bazel could report success without executing the Rust test
    cases in that shard. Once #18913 makes the wrapper run the Rust test
    binary directly and shard with libtest arguments, this stale unit test
    actually runs and exposes the missing `codex_self_exe` setup.
    
    ## What Changed
    
    - Moved `build_prompt_input_includes_context_and_user_message` out of
    `core/src/prompt_debug.rs`.
    - Added `core/tests/suite/prompt_debug_tests.rs` and registered it from
    `core/tests/suite/mod.rs`.
    - Builds the test config with `ConfigBuilder` and provides
    `codex_self_exe` using the current test executable, matching the
    runtime-path invariant required by prompt debug setup.
    - Preserves the existing assertions that the generated prompt input
    includes both the debug user message and project-specific user
    instructions.
    
    ## Verification
    
    - `cargo test -p codex-core --test all
    prompt_debug_tests::build_prompt_input_includes_context_and_user_message`
    - `bazel test //codex-rs/core:core-all-test
    --test_arg=prompt_debug_tests::build_prompt_input_includes_context_and_user_message
    --test_output=errors`
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18916).
    * #18913
    * __->__ #18916
  • sandbox: remove dead seatbelt helper and update tests (#17859)
    ## Why
    
    `spawn_command_under_seatbelt()` in `codex-rs/core/src/seatbelt.rs` had
    fallen out of production use and was only referenced by test-only
    wrappers. That left us with sandbox tests that could stay green even if
    the actual seatbelt exec path regressed, because production shell
    execution now flows through `SandboxManager::transform()` and
    `ExecRequest::from_sandbox_exec_request()` instead of that helper.
    
    Removing the dead helper also exposed one downstream `codex-exec`
    integration test that still imported it, which broke `just clippy`.
    
    ## What Changed
    
    - Removed `codex-rs/core/src/seatbelt.rs` and stopped exporting
    `codex_core::seatbelt`.
    - Removed the redundant `codex-rs/core/tests/suite/seatbelt.rs` coverage
    that only exercised the dead helper.
    - Kept the `openpty` regression check, but moved it into
    `codex-rs/core/tests/suite/exec.rs` so it now runs through
    `process_exec_tool_call()`.
    - Fixed the seatbelt denial test in `codex-rs/core/tests/suite/exec.rs`
    to use `/usr/bin/touch`, so it actually exercises the sandbox instead of
    a nonexistent path.
    - Updated `codex-rs/exec/tests/suite/sandbox.rs` on macOS to build the
    sandboxed command through `build_exec_request()` and spawn the
    transformed command, instead of importing the removed helper.
    - Left the lower-level seatbelt policy coverage in
    `codex-rs/sandboxing/src/seatbelt_tests.rs`, where the policy generator
    is still covered directly.
    
    ## Verification
    
    - `cargo test -p codex-core suite::exec::`
    - `cargo test -p codex-exec`
    - `cargo clippy -p codex-exec --tests -- -D warnings`
  • Stabilize exec-server filesystem tests in CI (#17671)
    ## Summary\n- add an exec-server package-local test helper binary that
    can run exec-server and fs-helper flows\n- route exec-server filesystem
    tests through that helper instead of cross-crate codex helper
    binaries\n- stop relying on Bazel-only extra binary wiring for these
    tests\n\n## Testing\n- not run (per repo guidance for codex changes)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Run exec-server fs operations through sandbox helper (#17294)
    ## Summary
    - run exec-server filesystem RPCs requiring sandboxing through a
    `codex-fs` arg0 helper over stdin/stdout
    - keep direct local filesystem execution for `DangerFullAccess` and
    external sandbox policies
    - remove the standalone exec-server binary path in favor of top-level
    arg0 dispatch/runtime paths
    - add sandbox escape regression coverage for local and remote filesystem
    paths
    
    ## Validation
    - `just fmt`
    - `git diff --check`
    - remote devbox: `cd codex-rs && bazel test --bes_backend=
    --bes_results_url= //codex-rs/exec-server:all` (6/6 passed)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: add Codex Apps sediment file remapping (#15197)
    ## Summary
    - bridge Codex Apps tools that declare `_meta["openai/fileParams"]`
    through the OpenAI file upload flow
    - mask those file params in model-visible tool schemas so the model
    provides absolute local file paths instead of raw file payload objects
    - rewrite those local file path arguments client-side into
    `ProvidedFilePayload`-shaped objects before the normal MCP tool call
    
    ## Details
    - applies to scalar and array file params declared in
    `openai/fileParams`
    - Codex uploads local files directly to the backend and uses the
    uploaded file metadata to build the MCP tool arguments locally
    - this PR is input-only
    
    ## Verification
    - `just fmt`
    - `cargo test -p codex-core mcp_tool_call -- --nocapture`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Make AGENTS.md discovery FS-aware (#15826)
    ## Summary
    - make AGENTS.md discovery and loading fully FS-aware and remove the
    non-FS discover helper
    - migrate remote-aware codex-core tests to use TestEnv workspace setup
    instead of syncing a local workspace copy
    - add AGENTS.md corner-case coverage, including directory fallbacks and
    remote-aware integration coverage
    
    ## Testing
    - cargo test -p codex-core project_doc -- --nocapture
    - cargo test -p codex-core hierarchical_agents -- --nocapture
    - cargo test -p codex-core agents_md -- --nocapture
    - cargo test -p codex-tui status -- --nocapture
    - cargo test -p codex-tui-app-server status -- --nocapture
    - just fix
    - just fmt
    - just bazel-lock-update
    - just bazel-lock-check
    - just argument-comment-lint
    - remote Linux executor tests in progress via scripts/test-remote-env.sh
  • [codex] add response proxy subagent header test (#16876)
    This adds end-to-end coverage for `responses-api-proxy` request dumps
    when Codex spawns a subagent and validates that the `x-codex-window-id`
    and `x-openai-subagent` are properly set.
  • [codex] add context-window lineage headers (#16758)
    This change adds client-owned context-window and parent thread id
    headers to all requests to responses api.
  • extract models manager and related ownership from core (#16508)
    ## 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>
  • fix: fix old system bubblewrap compatibility without falling back to vendored bwrap (#15693)
    Fixes #15283.
    
    ## Summary
    Older system bubblewrap builds reject `--argv0`, which makes our Linux
    sandbox fail before the helper can re-exec. This PR keeps using system
    `/usr/bin/bwrap` whenever it exists and only falls back to vendored
    bwrap when the system binary is missing. That matters on stricter
    AppArmor hosts, where the distro bwrap package also provides the policy
    setup needed for user namespaces.
    
    For old system bwrap, we avoid `--argv0` instead of switching binaries:
    - pass the sandbox helper a full-path `argv0`,
    - keep the existing `current_exe() + --argv0` path when the selected
    launcher supports it,
    - otherwise omit `--argv0` and re-exec through the helper's own
    `argv[0]` path, whose basename still dispatches as
    `codex-linux-sandbox`.
    
    Also updates the launcher/warning tests and docs so they match the new
    behavior: present-but-old system bwrap uses the compatibility path, and
    only absent system bwrap falls back to vendored.
    
    ### Validation
    
    1. Install Ubuntu 20.04 in a VM
    2. Compile codex and run without bubblewrap installed - see a warning
    about falling back to the vendored bwrap
    3. Install bwrap and verify version is 0.4.0 without `argv0` support
    4. run codex and use apply_patch tool without errors
    
    <img width="802" height="631" alt="Screenshot 2026-03-25 at 11 48 36 PM"
    src="https://github.com/user-attachments/assets/77248a29-aa38-4d7c-9833-496ec6a458b8"
    />
    <img width="807" height="634" alt="Screenshot 2026-03-25 at 11 47 32 PM"
    src="https://github.com/user-attachments/assets/5af8b850-a466-489b-95a6-455b76b5050f"
    />
    <img width="812" height="635" alt="Screenshot 2026-03-25 at 11 45 45 PM"
    src="https://github.com/user-attachments/assets/438074f0-8435-4274-a667-332efdd5cb57"
    />
    <img width="801" height="623" alt="Screenshot 2026-03-25 at 11 43 56 PM"
    src="https://github.com/user-attachments/assets/0dc8d3f5-e8cf-4218-b4b4-a4f7d9bf02e3"
    />
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • [apps][tool_suggest] Remove tool_suggest's dependency on tool search. (#14856)
    - [x] Remove tool_suggest's dependency on tool search.
  • chore: remove grep_files handler (#15775)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add remote env CI matrix and integration test (#14869)
    `CODEX_TEST_REMOTE_ENV` will make `test_codex` start the executor
    "remotely" (inside a docker container) turning any integration test into
    remote test.
  • [hooks] stop continuation & stop_hook_active mechanics (#14532)
    Stop hooks now receive `stop_hook_active` and enable stop hooks to loop
    forever if they'd like to. In the initial hooks PR, we implemented a
    simpler mechanic that the stop-blocking could only happen once in a row
    
    - support stop hook adding a continuation prompt to add a further task
    - if multiple stop-blocks happen that have continuation prompts, they
    are concatenated
    
    example run:
    ```
    › hey :)
    
    
    • Running SessionStart hook: lighting the observatory
    
    SessionStart hook (completed)
      warning: Hi, I'm a session start hook for wizard-tower (startup).
      hook context: Startup context: A wimboltine stonpet is an exotic cuisine from hyperspace
    
    • Aloha :) Happy to jam with you. What are we building today?
    
    • Running Stop hook: updating the guards
    
    Stop hook (blocked)
      warning: Wizard Tower Stop hook continuing conversation
      feedback: cook the stonpet
    
    • Aloha, here’s the hyperspace move for cooking a wimboltine stonpet:
    
      1. Sear the stonpet in a hot pan with moon-oil until the edges shimmer.
      2. Add star-lime, black salt, and a little fermented nebula paste.
      3. Lower the heat and let it braise for 8 cosmic minutes with a splash of comet broth.
      4. Finish with sky herbs and serve over warm asteroid rice.
    
      The vibe: crispy outside, tender center, deep interdimensional savor.
    
    • Running Stop hook: updating the guards
    
    Stop hook (completed)
      warning: Wizard Tower Stop hook saw a second pass and stayed calm to avoid a loop.
    ```
    
    .codex/config.toml
    ```
    [features]
    codex_hooks = true
    ```
    
    .codex/hooks.json
    ```
    {
      "hooks": {
        "SessionStart": [
          {
            "matcher": "startup|resume",
            "hooks": [
              {
                "type": "command",
                "command": "/usr/bin/python3 .codex/hooks/session_start_demo.py",
                "timeoutSec": 10,
                "statusMessage": "lighting the observatory"
              }
            ]
          }
        ],
        "Stop": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "/usr/bin/python3 .codex/hooks/stop_demo_block.py",
                "timeoutSec": 10,
                "statusMessage": "updating the guards"
              }
            ]
          }
        ]
      }
    }
    ```
    
    .codex/hooks/session_start_demo.py
    ```
    #!/usr/bin/env python3
    
    import json
    import sys
    from pathlib import Path
    
    
    def main() -> int:
        payload = json.load(sys.stdin)
        cwd = Path(payload.get("cwd", ".")).name or "wizard-tower"
        source = payload.get("source", "startup")
        source_label = "resume" if source == "resume" else "startup"
        source_prefix = (
            "Resume context:"
            if source == "resume"
            else "Startup context:"
        )
    
        output = {
            "systemMessage": (
                f"Hi, I'm a session start hook for {cwd} ({source_label})."
            ),
            "hookSpecificOutput": {
                "hookEventName": "SessionStart",
                "additionalContext": (
                    f"{source_prefix} A wimboltine stonpet is an exotic cuisine from hyperspace"
                ),
            },
        }
        print(json.dumps(output))
        return 0
    
    
    if __name__ == "__main__":
        raise SystemExit(main())
    ```
    
    .codex/hooks/stop_demo_block.py
    ```
    #!/usr/bin/env python3
    
    import json
    import sys
    
    
    def main() -> int:
        payload = json.load(sys.stdin)
        stop_hook_active = payload.get("stop_hook_active", False)
        last_assistant_message = payload.get("last_assistant_message") or ""
        char_count = len(last_assistant_message.strip())
    
        if stop_hook_active:
            system_message = (
                "Wizard Tower Stop hook saw a second pass and stayed calm to avoid a loop."
            )
            print(json.dumps({"systemMessage": system_message}))
        else:
            system_message = (
                f"Wizard Tower Stop hook continuing conversation"
            )
            print(json.dumps({"systemMessage": system_message, "decision": "block", "reason": "cook the stonpet"}))
    
        return 0
    
    
    if __name__ == "__main__":
        raise SystemExit(main())
    ```
  • spawn prompt (#14362)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
    
    Include a link to a bug report or enhancement request.
  • Add code_mode experimental feature (#13418)
    A much narrower and more isolated (no node features) version of js_repl
  • feat(core) Persist request_permission data across turns (#14009)
    ## Summary
    request_permissions flows should support persisting results for the
    session.
    
    Open Question: Still deciding if we need within-turn approvals - this
    adds complexity but I could see it being useful
    
    ## Testing
    - [x] Updated unit tests
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: load from plugins (#12864)
    Support loading plugins.
    
    Plugins can now be enabled via [plugins.<name>] in config.toml. They are
    loaded as first-class entities through PluginsManager, and their default
    skills/ and .mcp.json contributions are integrated into the existing
    skills and MCP flows.
  • feat: include sandbox config with escalation request (#12839)
    ## Why
    
    Before this change, an escalation approval could say that a command
    should be rerun, but it could not carry the sandbox configuration that
    should still apply when the escalated command is actually spawned.
    
    That left an unsafe gap in the `zsh-fork` skill path: skill scripts
    under `scripts/` that did not declare permissions could be escalated
    without a sandbox, and scripts that did declare permissions could lose
    their bounded sandbox on rerun or cached session approval.
    
    This PR extends the escalation protocol so approvals can optionally
    carry sandbox configuration all the way through execution. That lets the
    shell runtime preserve the intended sandbox instead of silently widening
    access.
    
    We likely want a single permissions type for this codepath eventually,
    probably centered on `Permissions`. For now, the protocol needs to
    represent both the existing `PermissionProfile` form and the fuller
    `Permissions` form, so this introduces a temporary disjoint union,
    `EscalationPermissions`, to carry either one.
    
    Further, this means that today, a skill either:
    
    - does not declare any permissions, in which case it is run using the
    default sandbox for the turn
    - specifies permissions, in which case the skill is run using that exact
    sandbox, which might be more restrictive than the default sandbox for
    the turn
    
    We will likely change the skill's permissions to be additive to the
    existing permissions for the turn.
    
    ## What Changed
    
    - Added `EscalationPermissions` to `codex-protocol` so escalation
    requests can carry either a `PermissionProfile` or a full `Permissions`
    payload.
    - Added an explicit `EscalationExecution` mode to the shell escalation
    protocol so reruns distinguish between `Unsandboxed`, `TurnDefault`, and
    `Permissions(...)` instead of overloading `None`.
    - Updated `zsh-fork` shell reruns to resolve `TurnDefault` at execution
    time, which keeps ordinary `UseDefault` commands on the turn sandbox and
    preserves turn-level macOS seatbelt profile extensions.
    - Updated the `zsh-fork` skill path so a skill with no declared
    permissions inherits the conversation's effective sandbox instead of
    escalating unsandboxed.
    - Updated the `zsh-fork` skill path so a skill with declared permissions
    reruns with exactly those permissions, including when a cached session
    approval is reused.
    
    ## Testing
    
    - Added unit coverage in
    `core/src/tools/runtimes/shell/unix_escalation.rs` for the explicit
    `UseDefault` / `RequireEscalated` / `WithAdditionalPermissions`
    execution mapping.
    - Added unit coverage in
    `core/src/tools/runtimes/shell/unix_escalation.rs` for macOS seatbelt
    extension preservation in both the `TurnDefault` and
    explicit-permissions rerun paths.
    - Added integration coverage in `core/tests/suite/skill_approval.rs` for
    permissionless skills inheriting the turn sandbox and explicit skill
    permissions remaining bounded across cached approval reuse.
  • feat: memories forgetting (#12900)
    Add diff based memory forgetting
  • Revert "Add skill approval event/response (#12633)" (#12811)
    This reverts commit https://github.com/openai/codex/pull/12633. We no
    longer need this PR, because we favor sending normal exec command
    approval server request with `additional_permissions` of skill
    permissions instead
  • feat: add service name to app-server (#12319)
    Add service name to the app-server so that the app can use it's own
    service name
    
    This is on thread level because later we might plan the app-server to
    become a singleton on the computer
  • tests(js_repl): stabilize CI runtime test execution (#12407)
    ## Summary
    
    Stabilize `js_repl` runtime test setup in CI and move tool-facing
    `js_repl` behavior coverage into integration tests.
    
    This is a test/CI change only. No production `js_repl` behavior change
    is intended.
    
    ## Why
    
    - Bazel test sandboxes (especially on macOS) could resolve a different
    `node` than the one installed by `actions/setup-node`, which caused
    `js_repl` runtime/version failures.
    - `js_repl` runtime tests depend on platform-specific
    sandbox/test-harness behavior, so they need explicit gating in a
    base-stability commit.
    - Several tests in the `js_repl` unit test module were actually
    black-box/tool-level behavior tests and fit better in the integration
    suite.
    
    ## Changes
    
    - Add `actions/setup-node` to the Bazel and Rust `Tests` workflows,
    using the exact version pinned in the repo’s Node version file.
    - In Bazel (non-Windows), pass `CODEX_JS_REPL_NODE_PATH=$(which node)`
    into test env so `js_repl` uses the `actions/setup-node` runtime inside
    Bazel tests.
    - Add a new integration test suite for `js_repl` tool behavior and
    register it in the core integration test suite module.
    - Move black-box `js_repl` behavior tests into the integration suite
    (persistence/TLA, builtin tool invocation, recursive self-call
    rejection, `process` isolation, blocked builtin imports).
    - Keep white-box manager/kernel tests in the `js_repl` unit test module.
    - Gate `js_repl` runtime tests to run only on macOS and only when a
    usable Node runtime is available (skip on other platforms / missing Node
    in this commit).
    
    ## Impact
    
    - Reduces `js_repl` CI failures caused by Node resolution drift in
    Bazel.
    - Improves test organization by separating tool-facing behavior tests
    from white-box manager/kernel tests.
    - Keeps the base commit stable while expanding `js_repl` runtime
    coverage.
    
    
    #### [git stack](https://github.com/magus/git-stack-cli)
    -  `1` https://github.com/openai/codex/pull/12372
    - 👉 `2` https://github.com/openai/codex/pull/12407
    -  `3` https://github.com/openai/codex/pull/12185
    -  `4` https://github.com/openai/codex/pull/10673
  • Agent jobs (spawn_agents_on_csv) + progress UI (#10935)
    ## Summary
    - Add agent job support: spawn a batch of sub-agents from CSV, auto-run,
    auto-export, and store results in SQLite.
    - Simplify workflow: remove run/resume/get-status/export tools; spawn is
    deterministic and completes in one call.
    - Improve exec UX: stable, single-line progress bar with ETA; suppress
    sub-agent chatter in exec.
    
    ## Why
    Enables map-reduce style workflows over arbitrarily large repos using
    the existing Codex orchestrator. This addresses review feedback about
    overly complex job controls and non-deterministic monitoring.
    
    ## Demo (progress bar)
    ```
    ./codex-rs/target/debug/codex exec \
      --enable collab \
      --enable sqlite \
      --full-auto \
      --progress-cursor \
      -c agents.max_threads=16 \
      -C /Users/daveaitel/code/codex \
      - <<'PROMPT'
    Create /tmp/agent_job_progress_demo.csv with columns: path,area and 30 rows:
    path = item-01..item-30, area = test.
    
    Then call spawn_agents_on_csv with:
    - csv_path: /tmp/agent_job_progress_demo.csv
    - instruction: "Run `python - <<'PY'` to sleep a random 0.3–1.2s, then output JSON with keys: path, score (int). Set score = 1."
    - output_csv_path: /tmp/agent_job_progress_demo_out.csv
    PROMPT
    ```
    
    ## Review feedback addressed
    - Auto-start jobs on spawn; removed run/resume/status/export tools.
    - Auto-export on success.
    - More descriptive tool spec + clearer prompts.
    - Avoid deadlocks on spawn failure; pending/running handled safely.
    - Progress bar no longer scrolls; stable single-line redraw.
    
    ## Tests
    - `cd codex-rs && cargo test -p codex-exec`
    - `cd codex-rs && cargo build -p codex-cli`
  • feat(core) Introduce Feature::RequestPermissions (#11871)
    ## Summary
    Introduces the initial implementation of Feature::RequestPermissions.
    RequestPermissions allows the model to request that a command be run
    inside the sandbox, with additional permissions, like writing to a
    specific folder. Eventually this will include other rules as well, and
    the ability to persist these permissions, but this PR is already quite
    large - let's get the core flow working and go from there!
    
    <img width="1279" height="541" alt="Screenshot 2026-02-15 at 2 26 22 PM"
    src="https://github.com/user-attachments/assets/0ee3ec0f-02ec-4509-91a2-809ac80be368"
    />
    
    ## Testing
    - [x] Added tests
    - [x] Tested locally
    - [x] Feature
  • Add skill approval event/response (#12633)
    Set the stage for skill-level permission approval in addition to
    command-level.
    
    Behind a feature flag.
  • Wire realtime api to core (#12268)
    - Introduce `RealtimeConversationManager` for realtime API management 
    - Add `op::conversation` to start conversation, insert audio, insert
    text, and close conversation.
    - emit conversation lifecycle and realtime events.
    - Move shared realtime payload types into codex-protocol and add core
    e2e websocket tests for start/replace/transport-close paths.
    
    Things to consider:
    - Should we use the same `op::` and `Events` channel to carry audio? I
    think we should try this simple approach and later we can create
    separate one if the channels got congested.
    - Sending text updates to the client: we can start simple and later
    restrict that.
    - Provider auth isn't wired for now intentionally
  • feat: sub-agent injection (#12152)
    This PR adds parent-thread sub-agent completion notifications and change
    the prompt of the model to prevent if from being confused
  • Add model-visible context layout snapshot tests (#12073)
    ## Summary
    - add a dedicated `core/tests/suite/model_visible_layout.rs` snapshot
    suite to materialize model-visible request layout in high-value
    scenarios
    - add three reviewer-focused snapshot scenarios:
      - turn-level context updates (cwd / permissions / personality)
      - first post-resume turn with model hydration + personality change
    - first post-resume turn where pre-turn model override matches rollout
    model
    - wire the new suite into `core/tests/suite/mod.rs`
    - commit generated `insta` snapshots under `core/tests/suite/snapshots/`
    
    ## Why
    This creates a stable, reviewable baseline of model-visible context
    layout against `main` before follow-on context-management refactors. It
    lets subsequent PRs show focused snapshot diffs for behavior changes
    instead of introducing the test surface and behavior changes at once.
    
    ## Testing
    - `just fmt`
    - `INSTA_UPDATE=always cargo test -p codex-core model_visible_layout`
  • add(core): safety check downgrade warning (#11964)
    Add per-turn notice when a request is downgraded to a fallback model due
    to cyber safety checks.
    
    **Changes**
    
    - codex-api: Emit a ServerModel event based on the openai-model response
    header and/or response payload (SSE + WebSocket), including when the
    model changes mid-stream.
    - core: When the server-reported model differs from the requested model,
    emit a single per-turn warning explaining the reroute to gpt-5.2 and
    directing users to Trusted
        Access verification and the cyber safety explainer.
    - app-server (v2): Surface these cyber model-routing warnings as
    synthetic userMessage items with text prefixed by Warning: (and document
    this behavior).
  • Update models.json (#11274)
    Automated update of models.json.
    
    ---------
    
    Co-authored-by: aibrahim-oai <219906144+aibrahim-oai@users.noreply.github.com>
    Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
    Co-authored-by: Sayan Sisodiya <sayan@openai.com>
  • feat: search_tool (#10657)
    **Why We Did This**
    - The goal is to reduce MCP tool context pollution by not exposing the
    full MCP tool list up front
    - It forces an explicit discovery step (`search_tool_bm25`) so the model
    narrows tool scope before making MCP calls, which helps relevance and
    lowers prompt/tool clutter.
    
    **What It Changed**
    - Added a new experimental feature flag `search_tool` in
    `core/src/features.rs:90` and `core/src/features.rs:430`.
    - Added config/schema support for that flag in
    `core/config.schema.json:214` and `core/config.schema.json:1235`.
    - Added BM25 dependency (`bm25`) in `Cargo.toml:129` and
    `core/Cargo.toml:23`.
    - Added new tool handler `search_tool_bm25` in
    `core/src/tools/handlers/search_tool_bm25.rs:18`.
    - Registered the handler and tool spec in
    `core/src/tools/handlers/mod.rs:11` and `core/src/tools/spec.rs:780` and
    `core/src/tools/spec.rs:1344`.
    - Extended `ToolsConfig` to carry `search_tool` enablement in
    `core/src/tools/spec.rs:32` and `core/src/tools/spec.rs:56`.
    - Injected dedicated developer instructions for tool-discovery workflow
    in `core/src/codex.rs:483` and `core/src/codex.rs:1976`, using
    `core/templates/search_tool/developer_instructions.md:1`.
    - Added session state to store one-shot selected MCP tools in
    `core/src/state/session.rs:27` and `core/src/state/session.rs:131`.
    - Added filtering so when feature is enabled, only selected MCP tools
    are exposed on the next request (then consumed) in
    `core/src/codex.rs:3800` and `core/src/codex.rs:3843`.
    - Added E2E suite coverage for
    enablement/instructions/hide-until-search/one-turn-selection in
    `core/tests/suite/search_tool.rs:72`,
    `core/tests/suite/search_tool.rs:109`,
    `core/tests/suite/search_tool.rs:147`, and
    `core/tests/suite/search_tool.rs:218`.
    - Refactored test helper utilities to support config-driven tool
    collection in `core/tests/suite/tools.rs:281`.
    
    **Net Behavioral Effect**
    - With `search_tool` **off**: existing MCP behavior (tools exposed
    normally).
    - With `search_tool` **on**: MCP tools start hidden, model must call
    `search_tool_bm25`, and only returned `selected_tools` are available for
    the next model call.
  • tools: remove get_memory tool and tests (#11198)
    Drop this memory tool as the design changed