Commit Graph

1727 Commits

  • Add layered config.toml support to app server (#9510)
    This PR adds support for chained (layered) config.toml file merging for
    clients that use the app server interface. This feature already exists
    for the TUI, but it does not work for GUI clients.
    
    It does the following:
    * Changes code paths for new thread, resume thread, and fork thread to
    use the effective config based on the cwd.
    * Updates the `config/read` API to accept an optional `cwd` parameter.
    If specified, the API returns the effective config based on that cwd
    path. Also optionally includes all layers including project config
    files. If cwd is not specified, the API falls back on its older behavior
    where it considers only the global (non-project) config files when
    computing the effective config.
    
    The changes in codex_message_processor.rs look deceptively large. They
    mostly just involve moving existing blocks of code to a later point in
    some functions so it can use the cwd to calculate the config.
    
    This PR builds upon #9509 and should be reviewed and merged after that
    PR.
    
    Tested:
    * Verified change with (dependent, as-yet-uncommitted) changes to IDE
    Extension and confirmed correct behavior
    
    The full fix requires additional changes in the IDE Extension code base,
    but they depend on this PR.
  • Add collaboration_mode to TurnContextItem (#9583)
    ## Summary
    - add optional `collaboration_mode` to `TurnContextItem` in rollouts
    - persist the current collaboration mode when recording turn context
    (sampling + compaction)
    
    ## Rationale
    We already persist turn context data for resume logic. Capturing
    collaboration mode in the rollout gives us the mode context for each
    turn, enabling follow‑up work to diff mode instructions correctly on
    resume.
    
    ## Changes
    - protocol: add optional `collaboration_mode` field to `TurnContextItem`
    - core: persist collaboration mode alongside other turn context settings
    in rollouts
  • Chore: update plan mode output in prompt (#9592)
    ### Summary
    * Update plan prompt output
    * Update requestUserInput response to be a single key value pair
    `answer: String`.
  • chore: defensive shell snapshot (#9609)
    This PR adds 2 defensive mechanisms for shell snapshotting:
    * Filter out invalid env variables (containing `-` for example) without
    dropping the whole snapshot
    * Validate the snapshot before considering it as valid by running a mock
    command with a shell snapshot
  • Added "codex." prefix to "conversation.turn.count" metric name (#9594)
    All other metrics names start with "codex.", so I presume this was an
    unintended omission.
  • feat: max threads config (#9483)
    # 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.
  • Reject ask user question tool in Execute and Custom (#9560)
    ## Summary
    - Keep `request_user_input` in the tool list but reject it at runtime in
    Execute/Custom modes with a clear model-facing error.
    - Add a session accessor for current collaboration mode and enforce the
    gate in the request_user_input handler.
    - Update core/app-server tests to use Plan mode for success and add
    Execute/Custom rejection coverage.
  • feat: rename experimental_instructions_file to model_instructions_file (#9555)
    A user who has `experimental_instructions_file` set will now see this:
    
    <img width="888" height="660" alt="image"
    src="https://github.com/user-attachments/assets/51c98312-eb9b-4881-81f1-bea6677e158d"
    />
    
    And a `codex exec` would include this warning:
    
    <img width="888" height="660" alt="image"
    src="https://github.com/user-attachments/assets/a89f62be-1edf-4593-a75e-e0b4a762ed7d"
    />
  • [codex-tui] exit when terminal is dumb (#9293)
    Using terminal with TERM=dumb specifically mean that TUIs and the like
    don't work. Ensure that codex doesn't run in these environments and exit
    with odd errors like crossterm's "Error: The cursor position could not
    be read within a normal duration"
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • fix(core): require approval for force delete on Windows (#8590)
    ### What
    Implemented detection for dangerous "force delete" commands on Windows
    to trigger the user approval prompt when `--ask-for-approval on-request`
    is set. This aligns Windows behavior with the existing safety checks for
    `rm -rf` on Linux.
    
    ### Why
    Fixes #8567 - a critical safety gap where destructive Windows commands
    could bypass the approval prompt. This prevents accidental data loss by
    ensuring the user explicitly confirms operations that would otherwise
    suppress the OS's native confirmation prompts.
    
    ### How
    Updated the Windows command safety module to identify and flag the
    following patterns as dangerous:
    *   **PowerShell**:
    * Detects `Remove-Item` (and aliases `rm`, `ri`, `del`, `erase`, `rd`,
    `rmdir`) when used with the `-Force` flag.
    * Uses token-based analysis to robustly detect these patterns even
    inside script blocks (`{...}`), sub-expression `(...)`, or
    semicolon-chained sequences.
    *   **CMD**:
        *   Detects `del /f` (force delete files).
        *   Detects `rd /s /q` (recursive delete quiet).
    * **Command Chaining**: Added support for analyzing chained commands
    (using `&`, `&&`, `|`, `||`) to separate and check individual commands
    (e.g., catching `del /f` hidden in `echo log & del /f data`).
    
    ### Testing
    Added comprehensive unit tests covering:
    * **PowerShell**: `Remove-Item -Path 'test' -Recurse -Force` (Exact
    reproduction case).
    * **Complex Syntax**: Verified detection inside blocks (e.g., `if
    ($true) { rm -Force }`) and with trailing punctuation.
    *   **CMD**:
        *   `del /f` (Flagged).
        *   `rd /s /q` (Flagged).
        *   Chained commands: `echo hi & del /f file` (Flagged).
    *   **False Positives**:
        *   `rd /s` (Not flagged - relies on native prompt).
        *   Standard deletions without force flags.
    
    Verified with `cargo test` and `cargo clippy`.
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • Add total (non-partial) TextElement placeholder accessors (#9545)
    ## Summary
    - Make `TextElement` placeholders private and add a text-backed accessor
    to avoid assuming `Some`.
    - Since they are optional in the protocol, we want to make sure any
    accessors properly handle the None case (getting the placeholder using
    the byte range in the text)
    - Preserve placeholders during protocol/app-server conversions using the
    accessor fallback.
    - Update TUI composer/remap logic and tests to use the new
    constructor/accessor.
  • merge remote models (#9547)
    We have `models.json` and `/models` response
    Behavior:
    1. New models from models endpoint gets added
    2. Shared models get replaced by remote ones
    3. Existing models in `models.json` but not `/models` are kept
    4. Mark highest priority as default
  • fix(core): don't update the file's mtime on resume (#9553)
    Remove `FileTimes::new().set_modified(SystemTime::now())` when resuming
    a thread.
    
    Context: It's awkward in UI built on top of app-server that resuming a
    thread bumps the `updated_at` timestamp, even if no message is sent. So
    if you open a thread (perhaps to just view its contents), it
    automatically reorders it to the top which is almost certainly not what
    you want.
  • fix: prevent repeating interrupted turns (#9043)
    ## What
    Record a model-visible `<turn_aborted>` marker in history when a turn is
    interrupted, and treat it as a session prefix.
    
    ## Why
    When a turn is interrupted, Codex emits `TurnAborted` but previously did
    not persist anything model-visible in the conversation history. On the
    next user turn, the model can’t tell the previous work was aborted and
    may resume/repeat earlier actions (including duplicated side effects
    like re-opening PRs).
    
    Fixes: https://github.com/openai/codex/issues/9042
    
    ## How
    On `TurnAbortReason::Interrupted`, append a hidden user message
    containing a `<turn_aborted>…</turn_aborted>` marker and flush.
    Treat `<turn_aborted>` like `<environment_context>` for session-prefix
    filtering.
    Add a regression test to ensure follow-up turns don’t repeat side
    effects from an aborted turn.
    
    ## Testing
    `just fmt`
    `just fix -p codex-core`
    `cargo test -p codex-core -- --test-threads=1`
    `cargo test --all-features -- --test-threads=1`
    
    ---------
    
    Co-authored-by: Skylar Graika <sgraika127@gmail.com>
    Co-authored-by: jif-oai <jif@openai.com>
    Co-authored-by: Eric Traut <etraut@openai.com>
  • Fixed config merging issue with profiles (#9509)
    This PR fixes a small issue with chained (layered) config.toml file
    merging. The old logic didn't properly handle profiles.
    
    In particular, if a lower-layer config overrides a profile defined in a
    higher-layer config, the override did not take effect. This prevents
    users from having project-specific profile overrides and contradicts the
    (soon-to-be) documented behavior of config merging.
    
    The change adds a unit test for this case. It also exposes a function
    from the config crate that is needed by the app server code paths to
    implement support for layered configs.
  • fix: memory leak issue (#9543)
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • feat(personality) introduce model_personality config (#9459)
    ## Summary
    Introduces the concept of a config model_personality. I would consider
    this an MVP for testing out the feature. There are a number of
    follow-ups to this PR:
    
    - More sophisticated templating with validation
    - In-product experience to manage this
    
    ## Testing
    - [x] Testing locally
  • feat: support proxy for ws connection (#9409)
    unfortunately tokio-tungstenite doesn't support proxy configuration
    outbox, while https://github.com/snapview/tokio-tungstenite/pull/370 is
    in review, we can depend on source code for now.
  • Only load config from trusted folders (#9533)
    Config includes multiple code execution entrypoints. 
    
    Now, we load the config from predetermined locations first
    (~/.codex/config.toml etc), use those to learn which folders are
    'trusted', and only load additional config from the CWD if it is
    trusted.
  • Persist text elements through TUI input and history (#9393)
    Continuation of breaking up this PR
    https://github.com/openai/codex/pull/9116
    
    ## Summary
    - Thread user text element ranges through TUI/TUI2 input, submission,
    queueing, and history so placeholders survive resume/edit flows.
    - Preserve local image attachments alongside text elements and rehydrate
    placeholders when restoring drafts.
    - Keep model-facing content shapes clean by attaching UI metadata only
    to user input/events (no API content changes).
    
    ## Key Changes
    - TUI/TUI2 composer now captures text element ranges, trims them with
    text edits, and restores them when submission is suppressed.
    - User history cells render styled spans for text elements and keep
    local image paths for future rehydration.
    - Initial chat widget bootstraps accept empty `initial_text_elements` to
    keep initialization uniform.
    - Protocol/core helpers updated to tolerate the new InputText field
    shape without changing payloads sent to the API.
  • fix(core) Preserve base_instructions in SessionMeta (#9427)
    ## Summary
    This PR consolidates base_instructions onto SessionMeta /
    SessionConfiguration, so we ensure `base_instructions` is set once per
    session and should be (mostly) immutable, unless:
    - overridden by config on resume / fork
    - sub-agent tasks, like review or collab
    
    
    In a future PR, we should convert all references to `base_instructions`
    to consistently used the typed struct, so it's less likely that we put
    other strings there. See #9423. However, this PR is already quite
    complex, so I'm deferring that to a follow-up.
    
    ## Testing
    - [x] Added a resume test to assert that instructions are preserved. In
    particular, `resume_switches_models_preserves_base_instructions` fails
    against main.
    
    Existing test coverage thats assert base instructions are preserved
    across multiple requests in a session:
    - Manual compact keeps baseline instructions:
    core/tests/suite/compact.rs:199
    - Auto-compact keeps baseline instructions:
    core/tests/suite/compact.rs:1142
    - Prompt caching reuses the same instructions across two requests:
    core/tests/suite/prompt_caching.rs:150 and
    core/tests/suite/prompt_caching.rs:157
    - Prompt caching with explicit expected string across two requests:
    core/tests/suite/prompt_caching.rs:213 and
    core/tests/suite/prompt_caching.rs:222
    - Resume with model switch keeps original instructions:
    core/tests/suite/resume.rs:136
    - Compact/resume/fork uses request 0 instructions for later expected
    payloads: core/tests/suite/compact_resume_fork.rs:215
  • Feat: plan mode prompt update (#9495)
    ### Summary
    * Added instruction on using `request_user_input`
    * Added the output to be json with `plan` key and the actual plan as the
    value.
    * Remove `PLAN.md` write because that gets into sandbox issue. We can
    add it back later.
  • Act on reasoning-included per turn (#9402)
    - Reset reasoning-included flag each turn and update compaction test
  • Feat: request user input tool (#9472)
    ### Summary
    * Add `requestUserInput` tool that the model can use for gather
    feedback/asking question mid turn.
    
    
    ### Tool input schema
    ```
    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "requestUserInput input",
      "type": "object",
      "additionalProperties": false,
      "required": ["questions"],
      "properties": {
        "questions": {
          "type": "array",
          "description": "Questions to show the user (1-3). Prefer 1 unless multiple independent decisions block progress.",
          "minItems": 1,
          "maxItems": 3,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "header", "question"],
            "properties": {
              "id": {
                "type": "string",
                "description": "Stable identifier for mapping answers (snake_case)."
              },
              "header": {
                "type": "string",
                "description": "Short header label shown in the UI (12 or fewer chars)."
              },
              "question": {
                "type": "string",
                "description": "Single-sentence prompt shown to the user."
              },
              "options": {
                "type": "array",
                "description": "Optional 2-3 mutually exclusive choices. Put the recommended option first and suffix its label with \"(Recommended)\". Only include \"Other\" option if we want to include a free form option. If the question is free form in nature, do not include any option.",
                "minItems": 2,
                "maxItems": 3,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": ["value", "label", "description"],
                  "properties": {
                    "value": {
                      "type": "string",
                      "description": "Machine-readable value (snake_case)."
                    },
                    "label": {
                      "type": "string",
                      "description": "User-facing label (1-5 words)."
                    },
                    "description": {
                      "type": "string",
                      "description": "One short sentence explaining impact/tradeoff if selected."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    ```
    
    ### Tool output schema
    ```
    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "requestUserInput output",
      "type": "object",
      "additionalProperties": false,
      "required": ["answers"],
      "properties": {
        "answers": {
          "type": "object",
          "description": "Map of question id to user answer.",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "required": ["selected"],
            "properties": {
              "selected": {
                "type": "array",
                "items": { "type": "string" }
              },
              "other": {
                "type": ["string", "null"]
              }
            }
          }
        }
      }
    }
    ```
  • Fix invalid input error on Azure endpoint (#9387)
    Users of Azure endpoints are reporting that when they use `/review`,
    they sometimes see an error "Invalid 'input[3].id". I suspect this is
    specific to the Azure implementation of the `responses` API. The Azure
    team generally copies the OpenAI code for this endpoint, but they do
    have minor differences and sometimes lag in rolling out bug fixes or
    updates.
    
    The error appears to be triggered because the `/review` implementation
    is using a user ID with a colon in it.
    
    Addresses #9360
  • chore(deps): bump arc-swap from 1.7.1 to 1.8.0 in /codex-rs (#9468)
    Bumps [arc-swap](https://github.com/vorner/arc-swap) from 1.7.1 to
    1.8.0.
    <details>
    <summary>Changelog</summary>
    <p><em>Sourced from <a
    href="https://github.com/vorner/arc-swap/blob/master/CHANGELOG.md">arc-swap's
    changelog</a>.</em></p>
    <blockquote>
    <h1>1.8.0</h1>
    <ul>
    <li>Support for Pin (<a
    href="https://redirect.github.com/vorner/arc-swap/issues/185">#185</a>,
    <a
    href="https://redirect.github.com/vorner/arc-swap/issues/183">#183</a>).</li>
    <li>Fix (hopefully) crash on ARM (<a
    href="https://redirect.github.com/vorner/arc-swap/issues/164">#164</a>).</li>
    <li>Fix Miri check (<a
    href="https://redirect.github.com/vorner/arc-swap/issues/186">#186</a>,
    <a
    href="https://redirect.github.com/vorner/arc-swap/issues/156">#156</a>).</li>
    <li>Fix support for Rust 1.31.0.</li>
    <li>Some minor clippy lints.</li>
    </ul>
    </blockquote>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/2540d266a837143948a0541a05d200fa1087a7db"><code>2540d26</code></a>
    Version bump to 1.8.0</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/9981e3af2351d82fe6f77761ee1e4a8479ec1fc7"><code>9981e3a</code></a>
    Keep &quot;old&quot; Cargo.lock around</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/57a8abbfc4100d918bcc4511eaa3c61740fe9c10"><code>57a8abb</code></a>
    Fix documentation links</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/346c5b642b00acb30ea8756f8186599a30e1edbc"><code>346c5b6</code></a>
    Fix some clippy warnings</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/0bd349a56bd448e0712a034f8892edfb6d4a41f2"><code>0bd349a</code></a>
    Fix support for Rust 1.31.0</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/57aa5224c19124ad2fa26eae70fa7778dd2224ac"><code>57aa522</code></a>
    Merge pull request <a
    href="https://redirect.github.com/vorner/arc-swap/issues/185">#185</a>
    from SpriteOvO/pin</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/4c0c4ab3218beeb0ae0b73d00e2a6c71b5b612f3"><code>4c0c4ab</code></a>
    Implement <code>RefCnt</code> for <code>Pin\&lt;Arc&gt;</code> and
    <code>Pin\&lt;Rc&gt;</code></li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/e596275acf37fceceb643a835e8b42563c42d919"><code>e596275</code></a>
    Avoid warnings about hidden lifetimes</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/d849a2d17e02b66c58a67f95beff8f072e6a306c"><code>d849a2d</code></a>
    Use SeqCst in debt-lists</li>
    <li><a
    href="https://github.com/vorner/arc-swap/commit/1f9b221da9907d690ff10a119c7d0155e99d09cb"><code>1f9b221</code></a>
    Merge pull request <a
    href="https://redirect.github.com/vorner/arc-swap/issues/186">#186</a>
    from nbdd0121/prov</li>
    <li>Additional commits viewable in <a
    href="https://github.com/vorner/arc-swap/compare/v1.7.1...v1.8.0">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=arc-swap&package-manager=cargo&previous-version=1.7.1&new-version=1.8.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot merge` will merge this PR after your CI passes on it
    - `@dependabot squash and merge` will squash and merge this PR after
    your CI passes on it
    - `@dependabot cancel merge` will cancel a previously requested merge
    and block automerging
    - `@dependabot reopen` will reopen this PR if it is closed
    - `@dependabot close` will close this PR and stop Dependabot recreating
    it. You can achieve the same result by closing it manually
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Fixed symlink support for config.toml (#9445)
    We already support reading from `config.toml` through a symlink, but the
    code was not properly handling updates to a symlinked config file. This
    PR generalizes safe symlink-chain resolution and atomic writes into
    path_utils, updating all config write paths to use the shared logic
    (including set_default_oss_provider, which previously didn't use the
    common path), and adds tests for symlink chains and cycles.
    
    This resolves #6646.
    
    Notes:
    * Symlink cycles or resolution failures replace the top-level symlink
    with a real file.
    * Shared config write path now handles symlinks consistently across
    edits, defaults, and empty-user-layer creation.
    
    This PR was inspired by https://github.com/openai/codex/pull/9437, which
    was contributed by @ryoppippi
  • Add collaboration modes test prompts (#9443)
    # 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 collaboration developer instructions (#9424)
    - Add additional instructions when they are available
    - Make sure to update them on change either UserInput or UserTurn
  • chore(instructions) Remove unread SessionMeta.instructions field (#9423)
    ### Description
    - Remove the now-unused `instructions` field from the session metadata
    to simplify SessionMeta and stop propagating transient instruction text
    through the rollout recorder API. This was only saving
    user_instructions, and was never being read.
    - Stop passing user instructions into the rollout writer at session
    creation so the rollout header only contains canonical session metadata.
    
    ### Testing
    
    - Ran `just fmt` which completed successfully.
    - Ran `just fix -p codex-protocol`, `just fix -p codex-core`, `just fix
    -p codex-app-server`, `just fix -p codex-tui`, and `just fix -p
    codex-tui2` which completed (Clippy fixes applied) as part of
    verification.
    - Ran `cargo test -p codex-protocol` which passed (28 tests).
    - Ran `cargo test -p codex-core` which showed failures in a small set of
    tests (not caused by the protocol type change directly):
    `default_client::tests::test_create_client_sets_default_headers`,
    several `models_manager::manager::tests::refresh_available_models_*`,
    and `shell_snapshot::tests::linux_sh_snapshot_includes_sections` (these
    tests failed in this CI run).
    - Ran `cargo test -p codex-app-server` which reported several failing
    integration tests (including
    `suite::codex_message_processor_flow::test_codex_jsonrpc_conversation_flow`,
    `suite::output_schema::send_user_turn_*`, and
    `suite::user_agent::get_user_agent_returns_current_codex_user_agent`).
    - `cargo test -p codex-tui` and `cargo test -p codex-tui2` were
    attempted but aborted due to disk space exhaustion (`No space left on
    device`).
    
    ------
    [Codex
    Task](https://chatgpt.com/codex/tasks/task_i_696bd8ce632483228d298cf07c7eb41c)
  • chore(core) Create instructions module (#9422)
    ## Summary
    We have a variety of things we refer to as instructions in the code
    base: our current canonical terms are:
    - base instructions (raw string)
    - developer instructions (has a type in protocol)
    - user instructions
    
    We also have `instructions` floating around in various places. We should
    standardize on the above, and start using types to prevent them from
    ending up in the wrong place. There will be additional PRs, but I'm
    going to keep these small so we can easily follow them!
    
    ## Testing
    - [x] Tests pass, this is purely a file move
  • Expose collaboration presets (#9421)
    Expose collaboration presets for clients
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • fix unified_exec::tests::unified_exec_timeouts to use a more unique match value (#9414)
    Fix unified_exec_timeouts to use a unique variable value rather than
    "codex" which was causing false positives when running tests locally
    (presumably from my bash prompts). Discovered while running tests to
    validate another change.
    
    Fixes https://github.com/openai/codex/issues/9413
    
    Test Plan:
    
    Ran test locally on my fedora 43 x86_64 machine with:
    ```
    cd codex/cargo-rs
    cargo nextest run --all-features --no-fail-fast unified_exec::tests::unified_exec_timeouts
    ```
    
    Before, unified_exec_timeouts fails:
    ```
        Finished `test` profile [unoptimized + debuginfo] target(s) in 0.38s
    ────────────
     Nextest run ID fa2b4949-a66c-408c-8002-32c52c70ec4f with nextest profile: default
        Starting 1 test across 107 binaries (3211 tests skipped)
            FAIL [   5.667s] codex-core unified_exec::tests::unified_exec_timeouts
      stdout ───
    
        running 1 test
        test unified_exec::tests::unified_exec_timeouts ... FAILED
    
        failures:
    
        failures:
            unified_exec::tests::unified_exec_timeouts
    
        test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 774 filtered out; finished in 5.66s
    
      stderr ───
    
        thread 'unified_exec::tests::unified_exec_timeouts' (459601) panicked at core/src/unified_exec/mod.rs:381:9:
        timeout too short should yield incomplete output
        note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    
    ────────────
         Summary [   5.677s] 1 test run: 0 passed, 1 failed, 3211 skipped
            FAIL [   5.667s] codex-core unified_exec::tests::unified_exec_timeouts
    error: test run failed
    
    ```
    
    After, works:
    ```
        Finished `test` profile [unoptimized + debuginfo] target(s) in 0.34s
    ────────────
     Nextest run ID f49e9004-e30b-4049-b0ff-283b543a1cd7 with nextest profile: default
        Starting 1 test across 107 binaries (3211 tests skipped)
            SLOW [> 15.000s] codex-core unified_exec::tests::unified_exec_timeouts
            PASS [  17.666s] codex-core unified_exec::tests::unified_exec_timeouts
    ────────────
         Summary [  17.676s] 1 test run: 1 passed (1 slow), 3211 skipped
    ```
  • fix(codex-api): treat invalid_prompt as non-retryable (#9400)
    **Goal**: Prevent response.failed events with `invalid_prompt` from
    being treated as retryable errors so the UI shows the actual error
    message instead of continually retrying.
    
    **Before**: Codex would continue to retry despite the prompt being
    marked as disallowed
    **After**: Codex will stop retrying once prompt is marked disallowed