Commit Graph

40 Commits

  • Set user-agent (#2230)
    Use the same well-defined value in all cases when sending user-agent
    header
  • [1/3] Parse exec commands and format them more nicely in the UI (#2095)
    # Note for reviewers
    The bulk of this PR is in in the new file, `parse_command.rs`. This file
    is designed to be written TDD and implemented with Codex. Do not worry
    about reviewing the code, just review the unit tests (if you want). If
    any cases are missing, we'll add more tests and have Codex fix them.
    
    I think the best approach will be to land and iterate. I have some
    follow-ups I want to do after this lands. The next PR after this will
    let us merge (and dedupe) multiple sequential cells of the same such as
    multiple read commands. The deduping will also be important because the
    model often reads the same file multiple times in a row in chunks
    
    ===
    
    This PR formats common commands like reading, formatting, testing, etc
    more nicely:
    
    It tries to extract things like file names, tests and falls back to the
    cmd if it doesn't. It also only shows stdout/err if the command failed.
    
    <img width="770" height="238" alt="CleanShot 2025-08-09 at 16 05 15"
    src="https://github.com/user-attachments/assets/0ead179a-8910-486b-aa3d-7d26264d751e"
    />
    <img width="348" height="158" alt="CleanShot 2025-08-09 at 16 05 32"
    src="https://github.com/user-attachments/assets/4302681b-5e87-4ff3-85b4-0252c6c485a9"
    />
    <img width="834" height="324" alt="CleanShot 2025-08-09 at 16 05 56 2"
    src="https://github.com/user-attachments/assets/09fb3517-7bd6-40f6-a126-4172106b700f"
    />
    
    Part 2: https://github.com/openai/codex/pull/2097
    Part 3: https://github.com/openai/codex/pull/2110
  • [core] Separate tools config from openai client (#1858)
    ## Summary
    In an effort to make tools easier to work with and more configurable,
    I'm introducing `ToolConfig` and updating `Prompt` to take in a general
    list of Tools. I think this is simpler and better for a few reasons:
    - We can easily assemble tools from various sources (our own harness,
    mcp servers, etc.) and we can consolidate the logic for constructing the
    logic in one place that is separate from serialization.
    - client.rs no longer needs arbitrary config values, it just takes in a
    list of tools to serialize
    
    A hefty portion of the PR is now updating our conversion of
    `mcp_types::Tool` to `OpenAITool`, but considering that @bolinfest
    accurately called this out as a TODO long ago, I think it's time we
    tackled it.
    
    ## Testing
    - [x] Experimented locally, no changes, as expected
    - [x] Added additional unit tests
    - [x] Responded to rust-review
  • fix: when using --oss, ensure correct configuration is threaded through correctly (#1859)
    This PR started as an investigation with the goal of eliminating the use
    of `unsafe { std::env::set_var() }` in `ollama/src/client.rs`, as
    setting environment variables in a multithreaded context is indeed
    unsafe and these tests were observed to be flaky, as a result.
    
    Though as I dug deeper into the issue, I discovered that the logic for
    instantiating `OllamaClient` under test scenarios was not quite right.
    In this PR, I aimed to:
    
    - share more code between the two creation codepaths,
    `try_from_oss_provider()` and `try_from_provider_with_base_url()`
    - use the values from `Config` when setting up Ollama, as we have
    various mechanisms for overriding config values, so we should be sure
    that we are always using the ultimate `Config` for things such as the
    `ModelProviderInfo` associated with the `oss` id
    
    Once this was in place,
    `OllamaClient::try_from_provider_with_base_url()` could be used in unit
    tests for `OllamaClient` so it was possible to create a properly
    configured client without having to set environment variables.
  • Introduce --oss flag to use gpt-oss models (#1848)
    This adds support for easily running Codex backed by a local Ollama
    instance running our new open source models. See
    https://github.com/openai/gpt-oss for details.
    
    If you pass in `--oss` you'll be prompted to install/launch ollama, and
    it will automatically download the 20b model and attempt to use it.
    
    We'll likely want to expand this with some options later to make the
    experience smoother for users who can't run the 20b or want to run the
    120b.
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • chore: introduce ModelFamily abstraction (#1838)
    To date, we have a number of hardcoded OpenAI model slug checks spread
    throughout the codebase, which makes it hard to audit the various
    special cases for each model. To mitigate this issue, this PR introduces
    the idea of a `ModelFamily` that has fields to represent the existing
    special cases, such as `supports_reasoning_summaries` and
    `uses_local_shell_tool`.
    
    There is a `find_family_for_model()` function that maps the raw model
    slug to a `ModelFamily`. This function hardcodes all the knowledge about
    the special attributes for each model. This PR then replaces the
    hardcoded model name checks with checks against a `ModelFamily`.
    
    Note `ModelFamily` is now available as `Config::model_family`. We should
    ultimately remove `Config::model` in favor of
    `Config::model_family::slug`.
  • Add a TurnDiffTracker to create a unified diff for an entire turn (#1770)
    This lets us show an accumulating diff across all patches in a turn.
    Refer to the docs for TurnDiffTracker for implementation details.
    
    There are multiple ways this could have been done and this felt like the
    right tradeoff between reliability and completeness:
    *Pros*
    * It will pick up all changes to files that the model touched including
    if they prettier or another command that updates them.
    * It will not pick up changes made by the user or other agents to files
    it didn't modify.
    
    *Cons*
    * It will pick up changes that the user made to a file that the model
    also touched
    * It will not pick up changes to codegen or files that were not modified
    with apply_patch
  • [sandbox] Filter out certain non-sandbox errors (#1804)
    ## Summary
    Users frequently complain about re-approving commands that have failed
    for non-sandbox reasons. We can't diagnose with complete accuracy which
    errors happened because of a sandbox failure, but we can start to
    eliminate some common simple cases.
    
    This PR captures the most common case I've seen, which is a `command not
    found` error.
    
    ## Testing
    - [x] Added unit tests
    - [x] Ran a few cases locally
  • chore: refactor exec.rs: create separate seatbelt.rs and spawn.rs files (#1762)
    At 550 lines, `exec.rs` was a bit large. In particular, I found it hard
    to locate the Seatbelt-related code quickly without a file with
    `seatbelt` in the name, so this refactors things so:
    
    - `spawn_command_under_seatbelt()` and dependent code moves to a new
    `seatbelt.rs` file
    - `spawn_child_async()` and dependent code moves to a new `spawn.rs`
    file
  • fix: run apply_patch calls through the sandbox (#1705)
    Building on the work of https://github.com/openai/codex/pull/1702, this
    changes how a shell call to `apply_patch` is handled.
    
    Previously, a shell call to `apply_patch` was always handled in-process,
    never leveraging a sandbox. To determine whether the `apply_patch`
    operation could be auto-approved, the
    `is_write_patch_constrained_to_writable_paths()` function would check if
    all the paths listed in the paths were writable. If so, the agent would
    apply the changes listed in the patch.
    
    Unfortunately, this approach afforded a loophole: symlinks!
    
    * For a soft link, we could fix this issue by tracing the link and
    checking whether the target is in the set of writable paths, however...
    * ...For a hard link, things are not as simple. We can run `stat FILE`
    to see if the number of links is greater than 1, but then we would have
    to do something potentially expensive like `find . -inum <inode_number>`
    to find the other paths for `FILE`. Further, even if this worked, this
    approach runs the risk of a
    [TOCTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)
    race condition, so it is not robust.
    
    The solution, implemented in this PR, is to take the virtual execution
    of the `apply_patch` CLI into an _actual_ execution using `codex
    --codex-run-as-apply-patch PATCH`, which we can run under the sandbox
    the user specified, just like any other `shell` call.
    
    This, of course, assumes that the sandbox prevents writing through
    symlinks as a mechanism to write to folders that are not in the writable
    set configured by the sandbox. I verified this by testing the following
    on both Mac and Linux:
    
    ```shell
    #!/usr/bin/env bash
    set -euo pipefail
    
    # Can running a command in SANDBOX_DIR write a file in EXPLOIT_DIR?
    
    # Codex is run in SANDBOX_DIR, so writes should be constrianed to this directory.
    SANDBOX_DIR=$(mktemp -d -p "$HOME" sandboxtesttemp.XXXXXX)
    # EXPLOIT_DIR is outside of SANDBOX_DIR, so let's see if we can write to it.
    EXPLOIT_DIR=$(mktemp -d -p "$HOME" sandboxtesttemp.XXXXXX)
    
    echo "SANDBOX_DIR: $SANDBOX_DIR"
    echo "EXPLOIT_DIR: $EXPLOIT_DIR"
    
    cleanup() {
      # Only remove if it looks sane and still exists
      [[ -n "${SANDBOX_DIR:-}" && -d "$SANDBOX_DIR" ]] && rm -rf -- "$SANDBOX_DIR"
      [[ -n "${EXPLOIT_DIR:-}" && -d "$EXPLOIT_DIR" ]] && rm -rf -- "$EXPLOIT_DIR"
    }
    
    trap cleanup EXIT
    
    echo "I am the original content" > "${EXPLOIT_DIR}/original.txt"
    
    # Drop the -s to test hard links.
    ln -s "${EXPLOIT_DIR}/original.txt" "${SANDBOX_DIR}/link-to-original.txt"
    
    cat "${SANDBOX_DIR}/link-to-original.txt"
    
    if [[ "$(uname)" == "Linux" ]]; then
        SANDBOX_SUBCOMMAND=landlock
    else
        SANDBOX_SUBCOMMAND=seatbelt
    fi
    
    # Attempt the exploit
    cd "${SANDBOX_DIR}"
    
    codex debug "${SANDBOX_SUBCOMMAND}" bash -lc "echo pwned > ./link-to-original.txt" || true
    
    cat "${EXPLOIT_DIR}/original.txt"
    ```
    
    Admittedly, this change merits a proper integration test, but I think I
    will have to do that in a follow-up PR.
  • Add support for a separate chatgpt auth endpoint (#1712)
    Adds a `CodexAuth` type that encapsulates information about available
    auth modes and logic for refreshing the token.
    Changes `Responses` API to send requests to different endpoints based on
    the auth type.
    Updates login_with_chatgpt to support API-less mode and skip the key
    exchange.
  • Add an experimental plan tool (#1726)
    This adds a tool the model can call to update a plan. The tool doesn't
    actually _do_ anything but it gives clients a chance to read and render
    the structured plan. We will likely iterate on the prompt and tools
    exposed for planning over time.
  • chore: split apply_patch logic out of codex.rs and into apply_patch.rs (#1703)
    This is a straight refactor, moving apply-patch-related code from
    `codex.rs` and into the new `apply_patch.rs` file. The only "logical"
    change is inlining `#[allow(clippy::unwrap_used)]` instead of declaring
    `#![allow(clippy::unwrap_used)]` at the top of the file (which is
    currently the case in `codex.rs`).
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1703).
    * #1705
    * __->__ #1703
    * #1702
    * #1698
    * #1697
  • chore: update Codex::spawn() to return a struct instead of a tuple (#1677)
    Also update `init_codex()` to return a `struct` instead of a tuple, as well.
  • feat: expand the set of commands that can be safely identified as "trusted" (#1668)
    This PR updates `is_known_safe_command()` to account for "safe
    operators" to expand the set of commands that can be run without
    approval. This concept existed in the TypeScript CLI, and we are
    [finally!] porting it to the Rust one:
    
    
    https://github.com/openai/codex/blob/c9e2def49487585cfe6f8bb7b2be442e8c0b5e1b/codex-cli/src/approvals.ts#L531-L541
    
    The idea is that if we have `EXPR1 SAFE_OP EXPR2` and `EXPR1` and
    `EXPR2` are considered safe independently, then `EXPR1 SAFE_OP EXPR2`
    should be considered safe. Currently, `SAFE_OP` includes `&&`, `||`,
    `;`, and `|`.
    
    In the TypeScript implementation, we relied on
    https://www.npmjs.com/package/shell-quote to parse the string of Bash,
    as it could provide a "lightweight" parse tree, parsing `'beep || boop >
    /byte'` as:
    
    ```
    [ 'beep', { op: '||' }, 'boop', { op: '>' }, '/byte' ]
    ```
    
    Though in this PR, we introduce the use of
    https://crates.io/crates/tree-sitter-bash for parsing (which
    incidentally we were already using in
    [`codex-apply-patch`](https://github.com/openai/codex/blob/c9e2def49487585cfe6f8bb7b2be442e8c0b5e1b/codex-rs/apply-patch/Cargo.toml#L18)),
    which gives us a richer parse tree. (Incidentally, if you have never
    played with tree-sitter, try the
    [playground](https://tree-sitter.github.io/tree-sitter/7-playground.html)
    and select **Bash** from the dropdown to see how it parses various
    expressions.)
    
    As a concrete example, prior to this change, our implementation of
    `is_known_safe_command()` could verify things like:
    
    ```
    ["bash", "-lc", "grep -R \"Cargo.toml\" -n"]
    ```
    
    but not:
    
    ```
    ["bash", "-lc", "grep -R \"Cargo.toml\" -n || true"]
    ```
    
    With this change, the version with `|| true` is also accepted.
    
    Admittedly, this PR does not expand the safety check to support
    subshells, so it would reject, e.g. `bash -lc 'ls || (pwd && echo hi)'`,
    but that can be addressed in a subsequent PR.
  • Record Git metadata to rollout (#1598)
    # Summary
    
    - Writing effective evals for codex sessions requires context of the
    overall repository state at the moment the session began
    - This change adds this metadata (git repository, branch, commit hash)
    to the top of the rollout of the session (if available - if not it
    doesn't add anything)
    - Currently, this is only effective on a clean working tree, as we can't
    track uncommitted/untracked changes with the current metadata set.
    Ideally in the future we may want to track unclean changes somehow, or
    perhaps prompt the user to stash or commit them.
    
    # Testing
    - Added unit tests
    - `cargo test && cargo clippy --tests && cargo fmt -- --config
    imports_granularity=Item`
    
    ### Resulting Rollout
    <img width="1243" height="127" alt="Screenshot 2025-07-17 at 1 50 00 PM"
    src="https://github.com/user-attachments/assets/68108941-f015-45b2-985c-ea315ce05415"
    />
  • feat: show number of tokens remaining in UI (#1388)
    When using the OpenAI Responses API, we now record the `usage` field for
    a `"response.completed"` event, which includes metrics about the number
    of tokens consumed. We also introduce `openai_model_info.rs`, which
    includes current data about the most common OpenAI models available via
    the API (specifically `context_window` and `max_output_tokens`). If
    Codex does not recognize the model, you can set `model_context_window`
    and `model_max_output_tokens` explicitly in `config.toml`.
    
    When then introduce a new event type to `protocol.rs`, `TokenCount`,
    which includes the `TokenUsage` for the most recent turn.
    
    Finally, we update the TUI to record the running sum of tokens used so
    the percentage of available context window remaining can be reported via
    the placeholder text for the composer:
    
    ![Screenshot 2025-06-25 at 11 20
    55 PM](https://github.com/user-attachments/assets/6fd6982f-7247-4f14-84b2-2e600cb1fd49)
    
    We could certainly get much fancier with this (such as reporting the
    estimated cost of the conversation), but for now, we are just trying to
    achieve feature parity with the TypeScript CLI.
    
    Though arguably this improves upon the TypeScript CLI, as the TypeScript
    CLI uses heuristics to estimate the number of tokens used rather than
    using the `usage` information directly:
    
    
    https://github.com/openai/codex/blob/296996d74e345b1b05d8c3451a06ace21c5ada96/codex-cli/src/utils/approximate-tokens-used.ts#L3-L16
    
    Fixes https://github.com/openai/codex/issues/1242
  • feat: add support for login with ChatGPT (#1212)
    This does not implement the full Login with ChatGPT experience, but it
    should unblock people.
    
    **What works**
    
    * The `codex` multitool now has a `login` subcommand, so you can run
    `codex login`, which should write `CODEX_HOME/auth.json` if you complete
    the flow successfully. The TUI will now read the `OPENAI_API_KEY` from
    `auth.json`.
    * The TUI should refresh the token if it has expired and the necessary
    information is in `auth.json`.
    * There is a `LoginScreen` in the TUI that tells you to run `codex
    login` if both (1) your model provider expects to use `OPENAI_API_KEY`
    as its env var, and (2) `OPENAI_API_KEY` is not set.
    
    **What does not work**
    
    * The `LoginScreen` does not support the login flow from within the TUI.
    Instead, it tells you to quit, run `codex login`, and then run `codex`
    again.
    * `codex exec` does read from `auth.json` yet, nor does it direct the
    user to go through the login flow if `OPENAI_API_KEY` is not be found.
    * The `maybeRedeemCredits()` function from `get-api-key.tsx` has not
    been ported from TypeScript to `login_with_chatgpt.py` yet:
    
    
    https://github.com/openai/codex/blob/a67a67f3258fc21e147b6786a143fe3e15e6d5ba/codex-cli/src/utils/get-api-key.tsx#L84-L89
    
    **Implementation**
    
    Currently, the OAuth flow requires running a local webserver on
    `127.0.0.1:1455`. It seemed wasteful to incur the additional binary cost
    of a webserver dependency in the Rust CLI just to support login, so
    instead we implement this logic in Python, as Python has a `http.server`
    module as part of its standard library. Specifically, we bundle the
    contents of a single Python file as a string in the Rust CLI and then
    use it to spawn a subprocess as `python3 -c
    {{SOURCE_FOR_PYTHON_SERVER}}`.
    
    As such, the most significant files in this PR are:
    
    ```
    codex-rs/login/src/login_with_chatgpt.py
    codex-rs/login/src/lib.rs
    ```
    
    Now that the CLI may load `OPENAI_API_KEY` from the environment _or_
    `CODEX_HOME/auth.json`, we need a new abstraction for reading/writing
    this variable, so we introduce:
    
    ```
    codex-rs/core/src/openai_api_key.rs
    ```
    
    Note that `std::env::set_var()` is [rightfully] `unsafe` in Rust 2024,
    so we use a LazyLock<RwLock<Option<String>>> to store `OPENAI_API_KEY`
    so it is read in a thread-safe manner.
    
    Ultimately, it should be possible to go through the entire login flow
    from the TUI. This PR introduces a placeholder `LoginScreen` UI for that
    right now, though the new `codex login` subcommand introduced in this PR
    should be a viable workaround until the UI is ready.
    
    **Testing**
    
    Because the login flow is currently implemented in a standalone Python
    file, you can test it without building any Rust code as follows:
    
    ```
    rm -rf /tmp/codex_home && mkdir /tmp/codex_home
    CODEX_HOME=/tmp/codex_home python3 codex-rs/login/src/login_with_chatgpt.py
    ```
    
    For reference:
    
    * the original TypeScript implementation was introduced in
    https://github.com/openai/codex/pull/963
    * support for redeeming credits was later added in
    https://github.com/openai/codex/pull/974
  • feat: make reasoning effort/summaries configurable (#1199)
    Previous to this PR, we always set `reasoning` when making a request
    using the Responses API:
    
    
    https://github.com/openai/codex/blob/d7245cbbc9d8ff5446da45e5951761103492476d/codex-rs/core/src/client.rs#L108-L111
    
    Though if you tried to use the Rust CLI with `--model gpt-4.1`, this
    would fail with:
    
    ```shell
    "Unsupported parameter: 'reasoning.effort' is not supported with this model."
    ```
    
    We take a cue from the TypeScript CLI, which does a check on the model
    name:
    
    
    https://github.com/openai/codex/blob/d7245cbbc9d8ff5446da45e5951761103492476d/codex-cli/src/utils/agent/agent-loop.ts#L786-L789
    
    This PR does a similar check, though also adds support for the following
    config options:
    
    ```
    model_reasoning_effort = "low" | "medium" | "high" | "none"
    model_reasoning_summary = "auto" | "concise" | "detailed" | "none"
    ```
    
    This way, if you have a model whose name happens to start with `"o"` (or
    `"codex"`?), you can set these to `"none"` to explicitly disable
    reasoning, if necessary. (That said, it seems unlikely anyone would use
    the Responses API with non-OpenAI models, but we provide an escape
    hatch, anyway.)
    
    This PR also updates both the TUI and `codex exec` to show `reasoning
    effort` and `reasoning summaries` in the header.
  • fix: introduce create_tools_json() and share it with chat_completions.rs (#1177)
    The main motivator behind this PR is that `stream_chat_completions()`
    was not adding the `"tools"` entry to the payload posted to the
    `/chat/completions` endpoint. This (1) refactors the existing logic to
    build up the `"tools"` JSON from `client.rs` into `openai_tools.rs`, and
    (2) updates the use of responses API (`client.rs`) and chat completions
    API (`chat_completions.rs`) to both use it.
    
    Note this PR alone is not sufficient to get tool calling from chat
    completions working: that is done in
    https://github.com/openai/codex/pull/1167.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1177).
    * #1167
    * __->__ #1177
  • fix: overhaul how we spawn commands under seccomp/landlock on Linux (#1086)
    Historically, we spawned the Seatbelt and Landlock sandboxes in
    substantially different ways:
    
    For **Seatbelt**, we would run `/usr/bin/sandbox-exec` with our policy
    specified as an arg followed by the original command:
    
    
    https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/core/src/exec.rs#L147-L219
    
    For **Landlock/Seccomp**, we would do
    `tokio::runtime::Builder::new_current_thread()`, _invoke
    Landlock/Seccomp APIs to modify the permissions of that new thread_, and
    then spawn the command:
    
    
    https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/core/src/exec_linux.rs#L28-L49
    
    While it is neat that Landlock/Seccomp supports applying a policy to
    only one thread without having to apply it to the entire process, it
    requires us to maintain two different codepaths and is a bit harder to
    reason about. The tipping point was
    https://github.com/openai/codex/pull/1061, in which we had to start
    building up the `env` in an unexpected way for the existing
    Landlock/Seccomp approach to continue to work.
    
    This PR overhauls things so that we do similar things for Mac and Linux.
    It turned out that we were already building our own "helper binary"
    comparable to Mac's `sandbox-exec` as part of the `cli` crate:
    
    
    https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/cli/Cargo.toml#L10-L12
    
    We originally created this to build a small binary to include with the
    Node.js version of the Codex CLI to provide support for Linux
    sandboxing.
    
    Though the sticky bit is that, at this point, we still want to deploy
    the Rust version of Codex as a single, standalone binary rather than a
    CLI and a supporting sandboxing binary. To satisfy this goal, we use
    "the arg0 trick," in which we:
    
    * use `std::env::current_exe()` to get the path to the CLI that is
    currently running
    * use the CLI as the `program` for the `Command`
    * set `"codex-linux-sandbox"` as arg0 for the `Command`
    
    A CLI that supports sandboxing should check arg0 at the start of the
    program. If it is `"codex-linux-sandbox"`, it must invoke
    `codex_linux_sandbox::run_main()`, which runs the CLI as if it were
    `codex-linux-sandbox`. When acting as `codex-linux-sandbox`, we make the
    appropriate Landlock/Seccomp API calls and then use `execvp(3)` to spawn
    the original command, so do _replace_ the process rather than spawn a
    subprocess. Incidentally, we do this before starting the Tokio runtime,
    so the process should only have one thread when `execvp(3)` is called.
    
    Because the `core` crate that needs to spawn the Linux sandboxing is not
    a CLI in its own right, this means that every CLI that includes `core`
    and relies on this behavior has to (1) implement it and (2) provide the
    path to the sandboxing executable. While the path is almost always
    `std::env::current_exe()`, we needed to make this configurable for
    integration tests, so `Config` now has a `codex_linux_sandbox_exe:
    Option<PathBuf>` property to facilitate threading this through,
    introduced in https://github.com/openai/codex/pull/1089.
    
    This common pattern is now captured in
    `codex_linux_sandbox::run_with_sandbox()` and all of the `main.rs`
    functions that should use it have been updated as part of this PR.
    
    The `codex-linux-sandbox` crate added to the Cargo workspace as part of
    this PR now has the bulk of the Landlock/Seccomp logic, which makes
    `core` a bit simpler. Indeed, `core/src/exec_linux.rs` and
    `core/src/landlock.rs` were removed/ported as part of this PR. I also
    moved the unit tests for this code into an integration test,
    `linux-sandbox/tests/landlock.rs`, in which I use
    `env!("CARGO_BIN_EXE_codex-linux-sandbox")` as the value for
    `codex_linux_sandbox_exe` since `std::env::current_exe()` is not
    appropriate in that case.
  • feat: introduce support for shell_environment_policy in config.toml (#1061)
    To date, when handling `shell` and `local_shell` tool calls, we were
    spawning new processes using the environment inherited from the Codex
    process itself. This means that the sensitive `OPENAI_API_KEY` that
    Codex needs to talk to OpenAI models was made available to everything
    run by `shell` and `local_shell`. While there are cases where that might
    be useful, it does not seem like a good default.
    
    This PR introduces a complex `shell_environment_policy` config option to
    control the `env` used with these tool calls. It is inevitably a bit
    complex so that it is possible to override individual components of the
    policy so without having to restate the entire thing.
    
    Details are in the updated `README.md` in this PR, but here is the
    relevant bit that explains the individual fields of
    `shell_environment_policy`:
    
    | Field | Type | Default | Description |
    | ------------------------- | -------------------------- | ------- |
    -----------------------------------------------------------------------------------------------------------------------------------------------
    |
    | `inherit` | string | `core` | Starting template for the
    environment:<br>`core` (`HOME`, `PATH`, `USER`, …), `all` (clone full
    parent env), or `none` (start empty). |
    | `ignore_default_excludes` | boolean | `false` | When `false`, Codex
    removes any var whose **name** contains `KEY`, `SECRET`, or `TOKEN`
    (case-insensitive) before other rules run. |
    | `exclude` | array&lt;string&gt; | `[]` | Case-insensitive glob
    patterns to drop after the default filter.<br>Examples: `"AWS_*"`,
    `"AZURE_*"`. |
    | `set` | table&lt;string,string&gt; | `{}` | Explicit key/value
    overrides or additions – always win over inherited values. |
    | `include_only` | array&lt;string&gt; | `[]` | If non-empty, a
    whitelist of patterns; only variables that match _one_ pattern survive
    the final step. (Generally used with `inherit = "all"`.) |
    
    
    In particular, note that the default is `inherit = "core"`, so:
    
    * if you have extra env variables that you want to inherit from the
    parent process, use `inherit = "all"` and then specify `include_only`
    * if you have extra env variables where you want to hardcode the values,
    the default `inherit = "core"` will work fine, but then you need to
    specify `set`
    
    This configuration is not battle-tested, so we will probably still have
    to play with it a bit. `core/src/exec_env.rs` has the critical business
    logic as well as unit tests.
    
    Though if nothing else, previous to this change:
    
    ```
    $ cargo run --bin codex -- debug seatbelt -- printenv OPENAI_API_KEY
    # ...prints OPENAI_API_KEY...
    ```
    
    But after this change it does not print anything (as desired).
    
    One final thing to call out about this PR is that the
    `configure_command!` macro we use in `core/src/exec.rs` has to do some
    complex logic with respect to how it builds up the `env` for the process
    being spawned under Landlock/seccomp. Specifically, doing
    `cmd.env_clear()` followed by `cmd.envs(&$env_map)` (which is arguably
    the most intuitive way to do it) caused the Landlock unit tests to fail
    because the processes spawned by the unit tests started failing in
    unexpected ways! If we forgo `env_clear()` in favor of updating env vars
    one at a time, the tests still pass. The comment in the code talks about
    this a bit, and while I would like to investigate this more, I need to
    move on for the moment, but I do plan to come back to it to fully
    understand what is going on. For example, this suggests that we might
    not be able to spawn a C program that calls `env_clear()`, which would
    be...weird. We may still have to fiddle with our Landlock config if that
    is the case.
  • chore: move types out of config.rs into config_types.rs (#1054)
    `config.rs` is already quite long without these definitions. Since they
    have no real dependencies of their own, let's move them to their own
    file so `config.rs` can focus on the business logic of loading a config.
  • feat: record messages from user in ~/.codex/history.jsonl (#939)
    This is a large change to support a "history" feature like you would
    expect in a shell like Bash.
    
    History events are recorded in `$CODEX_HOME/history.jsonl`. Because it
    is a JSONL file, it is straightforward to append new entries (as opposed
    to the TypeScript file that uses `$CODEX_HOME/history.json`, so to be
    valid JSON, each new entry entails rewriting the entire file). Because
    it is possible for there to be multiple instances of Codex CLI writing
    to `history.jsonl` at once, we use advisory file locking when working
    with `history.jsonl` in `codex-rs/core/src/message_history.rs`.
    
    Because we believe history is a sufficiently useful feature, we enable
    it by default. Though to provide some safety, we set the file
    permissions of `history.jsonl` to be `o600` so that other users on the
    system cannot read the user's history. We do not yet support a default
    list of `SENSITIVE_PATTERNS` as the TypeScript CLI does:
    
    
    https://github.com/openai/codex/blob/3fdf9df1335ac9501e3fb0e61715359145711e8b/codex-cli/src/utils/storage/command-history.ts#L10-L17
    
    We are going to take a more conservative approach to this list in the
    Rust CLI. For example, while `/\b[A-Za-z0-9-_]{20,}\b/` might exclude
    sensitive information like API tokens, it would also exclude valuable
    information such as references to Git commits.
    
    As noted in the updated documentation, users can opt-out of history by
    adding the following to `config.toml`:
    
    ```toml
    [history]
    persistence = "none" 
    ```
    
    Because `history.jsonl` could, in theory, be quite large, we take a[n
    arguably overly pedantic] approach in reading history entries into
    memory. Specifically, we start by telling the client the current number
    of entries in the history file (`history_entry_count`) as well as the
    inode (`history_log_id`) of `history.jsonl` (see the new fields on
    `SessionConfiguredEvent`).
    
    The client is responsible for keeping new entries in memory to create a
    "local history," but if the user hits up enough times to go "past" the
    end of local history, then the client should use the new
    `GetHistoryEntryRequest` in the protocol to fetch older entries.
    Specifically, it should pass the `history_log_id` it was given
    originally and work backwards from `history_entry_count`. (It should
    really fetch history in batches rather than one-at-a-time, but that is
    something we can improve upon in subsequent PRs.)
    
    The motivation behind this crazy scheme is that it is designed to defend
    against:
    
    * The `history.jsonl` being truncated during the session such that the
    index into the history is no longer consistent with what had been read
    up to that point. We do not yet have logic to enforce a `max_bytes` for
    `history.jsonl`, but once we do, we will aspire to implement it in a way
    that should result in a new inode for the file on most systems.
    * New items from concurrent Codex CLI sessions amending to the history.
    Because, in absence of truncation, `history.jsonl` is an append-only
    log, so long as the client reads backwards from `history_entry_count`,
    it should always get a consistent view of history. (That said, it will
    not be able to read _new_ commands from concurrent sessions, but perhaps
    we will introduce a `/` command to reload latest history or something
    down the road.)
    
    Admittedly, my testing of this feature thus far has been fairly light. I
    expect we will find bugs and introduce enhancements/fixes going forward.
  • feat: introduce --profile for Rust CLI (#921)
    This introduces a much-needed "profile" concept where users can specify
    a collection of options under one name and then pass that via
    `--profile` to the CLI.
    
    This PR introduces the `ConfigProfile` struct and makes it a field of
    `CargoToml`. It further updates
    `Config::load_from_base_config_with_overrides()` to respect
    `ConfigProfile`, overriding default values where appropriate. A detailed
    unit test is added at the end of `config.rs` to verify this behavior.
    
    Details on how to use this feature have also been added to
    `codex-rs/README.md`.
  • feat: add support for AGENTS.md in Rust CLI (#885)
    The TypeScript CLI already has support for including the contents of
    `AGENTS.md` in the instructions sent with the first turn of a
    conversation. This PR brings this functionality to the Rust CLI.
    
    To be considered, `AGENTS.md` must be in the `cwd` of the session, or in
    one of the parent folders up to a Git/filesystem root (whichever is
    encountered first).
    
    By default, a maximum of 32 KiB of `AGENTS.md` will be included, though
    this is configurable using the new-in-this-PR `project_doc_max_bytes`
    option in `config.toml`.
  • feat: experimental env var: CODEX_SANDBOX_NETWORK_DISABLED (#879)
    When using Codex to develop Codex itself, I noticed that sometimes it
    would try to add `#[ignore]` to the following tests:
    
    ```
    keeps_previous_response_id_between_tasks()
    retries_on_early_close()
    ```
    
    Both of these tests start a `MockServer` that launches an HTTP server on
    an ephemeral port and requires network access to hit it, which the
    Seatbelt policy associated with `--full-auto` correctly denies. If I
    wasn't paying attention to the code that Codex was generating, one of
    these `#[ignore]` annotations could have slipped into the codebase,
    effectively disabling the test for everyone.
    
    To that end, this PR enables an experimental environment variable named
    `CODEX_SANDBOX_NETWORK_DISABLED` that is set to `1` if the
    `SandboxPolicy` used to spawn the process does not have full network
    access. I say it is "experimental" because I'm not convinced this API is
    quite right, but we need to start somewhere. (It might be more
    appropriate to have an env var like `CODEX_SANDBOX=full-auto`, but the
    challenge is that our newer `SandboxPolicy` abstraction does not map to
    a simple set of enums like in the TypeScript CLI.)
    
    We leverage this new functionality by adding the following code to the
    aforementioned tests as a way to "dynamically disable" them:
    
    ```rust
    if std::env::var(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR).is_ok() {
        println!(
            "Skipping test because it cannot execute when network is disabled in a Codex sandbox."
        );
        return;
    }
    ```
    
    We can use the `debug seatbelt --full-auto` command to verify that
    `cargo test` fails when run under Seatbelt prior to this change:
    
    ```
    $ cargo run --bin codex -- debug seatbelt --full-auto -- cargo test
    ---- keeps_previous_response_id_between_tasks stdout ----
    
    thread 'keeps_previous_response_id_between_tasks' panicked at /Users/mbolin/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wiremock-0.6.3/src/mock_server/builder.rs:107:46:
    Failed to bind an OS port for a mock server.: Os { code: 1, kind: PermissionDenied, message: "Operation not permitted" }
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    
    
    failures:
        keeps_previous_response_id_between_tasks
    
    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    
    error: test failed, to rerun pass `-p codex-core --test previous_response_id`
    ```
    
    Though after this change, the above command succeeds! This means that,
    going forward, when Codex operates on Codex itself, when it runs `cargo
    test`, only "real failures" should cause the command to fail.
    
    As part of this change, I decided to tighten up the codepaths for
    running `exec()` for shell tool calls. In particular, we do it in `core`
    for the main Codex business logic itself, but we also expose this logic
    via `debug` subcommands in the CLI in the `cli` crate. The logic for the
    `debug` subcommands was not quite as faithful to the true business logic
    as I liked, so I:
    
    * refactored a bit of the Linux code, splitting `linux.rs` into
    `linux_exec.rs` and `landlock.rs` in the `core` crate.
    * gating less code behind `#[cfg(target_os = "linux")]` because such
    code does not get built by default when I develop on Mac, which means I
    either have to build the code in Docker or wait for CI signal
    * introduced `macro_rules! configure_command` in `exec.rs` so we can
    have both sync and async versions of this code. The synchronous version
    seems more appropriate for straight threads or potentially fork/exec.
  • feat: support the chat completions API in the Rust CLI (#862)
    This is a substantial PR to add support for the chat completions API,
    which in turn makes it possible to use non-OpenAI model providers (just
    like in the TypeScript CLI):
    
    * It moves a number of structs from `client.rs` to `client_common.rs` so
    they can be shared.
    * It introduces support for the chat completions API in
    `chat_completions.rs`.
    * It updates `ModelProviderInfo` so that `env_key` is `Option<String>`
    instead of `String` (for e.g., ollama) and adds a `wire_api` field
    * It updates `client.rs` to choose between `stream_responses()` and
    `stream_chat_completions()` based on the `wire_api` for the
    `ModelProviderInfo`
    * It updates the `exec` and TUI CLIs to no longer fail if the
    `OPENAI_API_KEY` environment variable is not set
    * It updates the TUI so that `EventMsg::Error` is displayed more
    prominently when it occurs, particularly now that it is important to
    alert users to the `CodexErr::EnvVar` variant.
    * `CodexErr::EnvVar` was updated to include an optional `instructions`
    field so we can preserve the behavior where we direct users to
    https://platform.openai.com if `OPENAI_API_KEY` is not set.
    * Cleaned up the "welcome message" in the TUI to ensure the model
    provider is displayed.
    * Updated the docs in `codex-rs/README.md`.
    
    To exercise the chat completions API from OpenAI models, I added the
    following to my `config.toml`:
    
    ```toml
    model = "gpt-4o"
    model_provider = "openai-chat-completions"
    
    [model_providers.openai-chat-completions]
    name = "OpenAI using Chat Completions"
    base_url = "https://api.openai.com/v1"
    env_key = "OPENAI_API_KEY"
    wire_api = "chat"
    ```
    
    Though to test a non-OpenAI provider, I installed ollama with mistral
    locally on my Mac because ChatGPT said that would be a good match for my
    hardware:
    
    ```shell
    brew install ollama
    ollama serve
    ollama pull mistral
    ```
    
    Then I added the following to my `~/.codex/config.toml`:
    
    ```toml
    model = "mistral"
    model_provider = "ollama"
    ```
    
    Note this code could certainly use more test coverage, but I want to get
    this in so folks can start playing with it.
    
    For reference, I believe https://github.com/openai/codex/pull/247 was
    roughly the comparable PR on the TypeScript side.
  • feat: read model_provider and model_providers from config.toml (#853)
    This is the first step in supporting other model providers in the Rust
    CLI. Specifically, this PR adds support for the new entries in `Config`
    and `ConfigOverrides` to specify a `ModelProviderInfo`, which is the
    basic config needed for an LLM provider. This PR does not get us all the
    way there yet because `client.rs` still categorically appends
    `/responses` to the URL and expects the endpoint to support the OpenAI
    Responses API. Will fix that next!
  • feat: save session transcripts when using Rust CLI (#845)
    This adds support for saving transcripts when using the Rust CLI. Like
    the TypeScript CLI, it saves the transcript to `~/.codex/sessions`,
    though it uses JSONL for the file format (and `.jsonl` for the file
    extension) so that even if Codex crashes, what was written to the
    `.jsonl` file should generally still be valid JSONL content.
  • chore: introduce codex-common crate (#843)
    I started this PR because I wanted to share the `format_duration()`
    utility function in `codex-rs/exec/src/event_processor.rs` with the TUI.
    The question was: where to put it?
    
    `core` should have as few dependencies as possible, so moving it there
    would introduce a dependency on `chrono`, which seemed undesirable.
    `core` already had this `cli` feature to deal with a similar situation
    around sharing common utility functions, so I decided to:
    
    * make `core` feature-free
    * introduce `common`
    * `common` can have as many "special interest" features as it needs,
    each of which can declare their own deps
    * the first two features of common are `cli` and `elapsed`
    
    In practice, this meant updating a number of `Cargo.toml` files,
    replacing this line:
    
    ```toml
    codex-core = { path = "../core", features = ["cli"] }
    ```
    
    with these:
    
    ```toml
    codex-core = { path = "../core" }
    codex-common = { path = "../common", features = ["cli"] }
    ```
    
    Moving `format_duration()` into its own file gave it some "breathing
    room" to add a unit test, so I had Codex generate some tests and new
    support for durations over 1 minute.
  • feat: support mcp_servers in config.toml (#829)
    This adds initial support for MCP servers in the style of Claude Desktop
    and Cursor. Note this PR is the bare minimum to get things working end
    to end: all configured MCP servers are launched every time Codex is run,
    there is no recovery for MCP servers that crash, etc.
    
    (Also, I took some shortcuts to change some fields of `Session` to be
    `pub(crate)`, which also means there are circular deps between
    `codex.rs` and `mcp_tool_call.rs`, but I will clean that up in a
    subsequent PR.)
    
    `codex-rs/README.md` is updated as part of this PR to explain how to use
    this feature. There is a bit of plumbing to route the new settings from
    `Config` to the business logic in `codex.rs`. The most significant
    chunks for new code are in `mcp_connection_manager.rs` (which defines
    the `McpConnectionManager` struct) and `mcp_tool_call.rs`, which is
    responsible for tool calls.
    
    This PR also introduces new `McpToolCallBegin` and `McpToolCallEnd`
    event types to the protocol, but does not add any handlers for them.
    (See https://github.com/openai/codex/pull/836 for initial usage.)
    
    To test, I added the following to my `~/.codex/config.toml`:
    
    ```toml
    # Local build of https://github.com/hideya/mcp-server-weather-js
    [mcp_servers.weather]
    command = "/Users/mbolin/code/mcp-server-weather-js/dist/index.js"
    args = []
    ```
    
    And then I ran the following:
    
    ```
    codex-rs$ cargo run --bin codex exec 'what is the weather in san francisco'
    [2025-05-06T22:40:05] Task started: 1
    [2025-05-06T22:40:18] Agent message: Here’s the latest National Weather Service forecast for San Francisco (downtown, near 37.77° N, 122.42° W):
    
    This Afternoon (Tue):
    • Sunny, high near 69 °F
    • West-southwest wind around 12 mph
    
    Tonight:
    • Partly cloudy, low around 52 °F
    • SW wind 7–10 mph
    ...
    ```
    
    Note that Codex itself is not able to make network calls, so it would
    not normally be able to get live weather information like this. However,
    the weather MCP is [currently] not run under the Codex sandbox, so it is
    able to hit `api.weather.gov` and fetch current weather information.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/829).
    * #836
    * __->__ #829
  • feat: configurable notifications in the Rust CLI (#793)
    With this change, you can specify a program that will be executed to get
    notified about events generated by Codex. The notification info will be
    packaged as a JSON object. The supported notification types are defined
    by the `UserNotification` enum introduced in this PR. Initially, it
    contains only one variant, `AgentTurnComplete`:
    
    ```rust
    pub(crate) enum UserNotification {
        #[serde(rename_all = "kebab-case")]
        AgentTurnComplete {
            turn_id: String,
    
            /// Messages that the user sent to the agent to initiate the turn.
            input_messages: Vec<String>,
    
            /// The last message sent by the assistant in the turn.
            last_assistant_message: Option<String>,
        },
    }
    ```
    
    This is intended to support the common case when a "turn" ends, which
    often means it is now your chance to give Codex further instructions.
    
    For example, I have the following in my `~/.codex/config.toml`:
    
    ```toml
    notify = ["python3", "/Users/mbolin/.codex/notify.py"]
    ```
    
    I created my own custom notifier script that calls out to
    [terminal-notifier](https://github.com/julienXX/terminal-notifier) to
    show a desktop push notification on macOS. Contents of `notify.py`:
    
    ```python
    #!/usr/bin/env python3
    
    import json
    import subprocess
    import sys
    
    
    def main() -> int:
        if len(sys.argv) != 2:
            print("Usage: notify.py <NOTIFICATION_JSON>")
            return 1
    
        try:
            notification = json.loads(sys.argv[1])
        except json.JSONDecodeError:
            return 1
    
        match notification_type := notification.get("type"):
            case "agent-turn-complete":
                assistant_message = notification.get("last-assistant-message")
                if assistant_message:
                    title = f"Codex: {assistant_message}"
                else:
                    title = "Codex: Turn Complete!"
                input_messages = notification.get("input_messages", [])
                message = " ".join(input_messages)
                title += message
            case _:
                print(f"not sending a push notification for: {notification_type}")
                return 0
    
        subprocess.check_output(
            [
                "terminal-notifier",
                "-title",
                title,
                "-message",
                message,
                "-group",
                "codex",
                "-ignoreDnD",
                "-activate",
                "com.googlecode.iterm2",
            ]
        )
    
        return 0
    
    
    if __name__ == "__main__":
        sys.exit(main())
    ```
    
    For reference, here are related PRs that tried to add this functionality
    to the TypeScript version of the Codex CLI:
    
    * https://github.com/openai/codex/pull/160
    * https://github.com/openai/codex/pull/498
  • fix: overhaul SandboxPolicy and config loading in Rust (#732)
    Previous to this PR, `SandboxPolicy` was a bit difficult to work with:
    
    
    https://github.com/openai/codex/blob/237f8a11e11fdcc793a09e787e48215676d9b95b/codex-rs/core/src/protocol.rs#L98-L108
    
    Specifically:
    
    * It was an `enum` and therefore options were mutually exclusive as
    opposed to additive.
    * It defined things in terms of what the agent _could not_ do as opposed
    to what they _could_ do. This made things hard to support because we
    would prefer to build up a sandbox config by starting with something
    extremely restrictive and only granting permissions for things the user
    as explicitly allowed.
    
    This PR changes things substantially by redefining the policy in terms
    of two concepts:
    
    * A `SandboxPermission` enum that defines permissions that can be
    granted to the agent/sandbox.
    * A `SandboxPolicy` that internally stores a `Vec<SandboxPermission>`,
    but externally exposes a simpler API that can be used to configure
    Seatbelt/Landlock.
    
    Previous to this PR, we supported a `--sandbox` flag that effectively
    mapped to an enum value in `SandboxPolicy`. Though now that
    `SandboxPolicy` is a wrapper around `Vec<SandboxPermission>`, the single
    `--sandbox` flag no longer makes sense. While I could have turned it
    into a flag that the user can specify multiple times, I think the
    current values to use with such a flag are long and potentially messy,
    so for the moment, I have dropped support for `--sandbox` altogether and
    we can bring it back once we have figured out the naming thing.
    
    Since `--sandbox` is gone, users now have to specify `--full-auto` to
    get a sandbox that allows writes in `cwd`. Admittedly, there is no clean
    way to specify the equivalent of `--full-auto` in your `config.toml`
    right now, so we will have to revisit that, as well.
    
    Because `Config` presents a `SandboxPolicy` field and `SandboxPolicy`
    changed considerably, I had to overhaul how config loading works, as
    well. There are now two distinct concepts, `ConfigToml` and `Config`:
    
    * `ConfigToml` is the deserialization of `~/.codex/config.toml`. As one
    might expect, every field is `Optional` and it is `#[derive(Deserialize,
    Default)]`. Consistent use of `Optional` makes it clear what the user
    has specified explicitly.
    * `Config` is the "normalized config" and is produced by merging
    `ConfigToml` with `ConfigOverrides`. Where `ConfigToml` contains a raw
    `Option<Vec<SandboxPermission>>`, `Config` presents only the final
    `SandboxPolicy`.
    
    The changes to `core/src/exec.rs` and `core/src/linux.rs` merit extra
    special attention to ensure we are faithfully mapping the
    `SandboxPolicy` to the Seatbelt and Landlock configs, respectively.
    
    Also, take note that `core/src/seatbelt_readonly_policy.sbpl` has been
    renamed to `codex-rs/core/src/seatbelt_base_policy.sbpl` and that
    `(allow file-read*)` has been removed from the `.sbpl` file as now this
    is added to the policy in `core/src/exec.rs` when
    `sandbox_policy.has_full_disk_read_access()` is `true`.
  • feat: add debug landlock subcommand comparable to debug seatbelt (#715)
    This PR adds a `debug landlock` subcommand to the Codex CLI for testing
    how Codex would execute a command using the specified sandbox policy.
    
    Built and ran this code in the `rust:latest` Docker container. In the
    container, hitting the network with vanilla `curl` succeeds:
    
    ```
    $ curl google.com
    <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
    <TITLE>301 Moved</TITLE></HEAD><BODY>
    <H1>301 Moved</H1>
    The document has moved
    <A HREF="http://www.google.com/">here</A>.
    </BODY></HTML>
    ```
    
    whereas this fails, as expected:
    
    ```
    $ cargo run -- debug landlock -s network-restricted -- curl google.com
    curl: (6) getaddrinfo() thread failed to start
    ```
  • feat: add ZDR support to Rust implementation (#642)
    This adds support for the `--disable-response-storage` flag across our
    multiple Rust CLIs to support customers who have opted into Zero-Data
    Retention (ZDR). The analogous changes to the TypeScript CLI were:
    
    * https://github.com/openai/codex/pull/481
    * https://github.com/openai/codex/pull/543
    
    For a client using ZDR, `previous_response_id` will never be available,
    so the `input` field of an API request must include the full transcript
    of the conversation thus far. As such, this PR changes the type of
    `Prompt.input` from `Vec<ResponseInputItem>` to `Vec<ResponseItem>`.
    
    Practically speaking, `ResponseItem` was effectively a "superset" of
    `ResponseInputItem` already. The main difference for us is that
    `ResponseItem` includes the `FunctionCall` variant that we have to
    include as part of the conversation history in the ZDR case.
    
    Another key change in this PR is modifying `try_run_turn()` so that it
    returns the `Vec<ResponseItem>` for the turn in addition to the
    `Vec<ResponseInputItem>` produced by `try_run_turn()`. This is because
    the caller of `run_turn()` needs to record the `Vec<ResponseItem>` when
    ZDR is enabled.
    
    To that end, this PR introduces `ZdrTranscript` (and adds
    `zdr_transcript: Option<ZdrTranscript>` to `struct State` in `codex.rs`)
    to take responsibility for maintaining the conversation transcript in
    the ZDR case.
  • feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
    As stated in `codex-rs/README.md`:
    
    Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
    run it. For a number of users, this runtime requirement inhibits
    adoption: they would be better served by a standalone executable. As
    maintainers, we want Codex to run efficiently in a wide range of
    environments with minimal overhead. We also want to take advantage of
    operating system-specific APIs to provide better sandboxing, where
    possible.
    
    To that end, we are moving forward with a Rust implementation of Codex
    CLI contained in this folder, which has the following benefits:
    
    - The CLI compiles to small, standalone, platform-specific binaries.
    - Can make direct, native calls to
    [seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
    [landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
    order to support sandboxing on Linux.
    - No runtime garbage collection, resulting in lower memory consumption
    and better, more predictable performance.
    
    Currently, the Rust implementation is materially behind the TypeScript
    implementation in functionality, so continue to use the TypeScript
    implmentation for the time being. We will publish native executables via
    GitHub Releases as soon as we feel the Rust version is usable.