46 Commits

  • Simplify Codex CLI README (#26313)
    ## Summary
    
    The codex-rs README was left over from before we moved the docs into the
    developer site. Its contents were very much out of date, and we received
    some bug reports about it.
  • cli: support --profile for codex sandbox (#24110)
    ## Why
    
    `codex sandbox` now always runs the host sandbox backend, so it should
    accept the same profile selection mechanism as the rest of the runtime
    CLI surface. Without `--profile`, sandbox debugging can exercise only
    the default config stack unless users manually translate profile config
    into ad hoc `-c` overrides.
    
    Supporting `--profile` lets sandbox invocations load
    `$CODEX_HOME/<name>.config.toml`, including permission profile
    configuration, before resolving the sandbox policy for the command being
    run.
    
    ## What Changed
    
    - Added `--profile NAME` / `-p NAME` to the host-specific `codex
    sandbox` argument structs as `config_profile`.
    - Allowed root-level `codex --profile NAME sandbox ...` and made a
    sandbox-local `codex sandbox --profile NAME ...` override the root
    selection.
    - Threaded `LoaderOverrides` through sandbox config loading so selected
    config profile files participate in permission resolution before the
    legacy read-only fallback.
    - Documented the new sandbox flag in `codex-rs/README.md`.
    
    ## Verification
    
    - Added parser coverage for `codex sandbox --profile`.
    - Added sandbox config-loader coverage that verifies selected config
    profile loader overrides select the profile config rather than falling
    back to read-only.
    - Ran `cargo test -p codex-cli`.
  • cli: infer host sandbox backend (#24102)
    ## Why
    
    `codex sandbox` previously required an OS subcommand like `linux`,
    `macos`, or `windows`, even though the command can only run the sandbox
    backend available on the current host. That made the CLI imply a
    cross-OS choice that does not exist.
    
    ## What changed
    
    - Collapse `codex sandbox <os>` into `codex sandbox [COMMAND]...` by
    wiring the `sandbox` parser directly to the host-specific backend args
    with `cfg`.
    - Keep the existing backend runners for Seatbelt, Linux sandbox, and
    Windows restricted token.
    - Rename the public Windows debug sandbox runner to
    `run_command_under_windows_sandbox` for clarity.
    - Update the Rust sandbox docs and related README references to describe
    host OS selection and avoid pointing readers at legacy `sandbox_mode`
    config.
    
    ## Arg0 compatibility
    
    The `codex-linux-sandbox` helper path is still handled before normal CLI
    parsing. `arg0_dispatch()` checks whether the executable basename is
    `codex-linux-sandbox` and directly calls
    `codex_linux_sandbox::run_main()`, so removing the `sandbox linux`
    parser branch does not affect the arg0 helper flow.
    
    ## Verification
    
    - `cargo test -p codex-cli`
    - `cargo test -p codex-arg0`
    - `just fix -p codex-cli`
  • revert legacy notify deprecation (#21152)
    # Why
    
    Revert #20524 for now because the computer use plugin has not migrated
    off legacy `notify` yet. Keeping the deprecation in place today would
    show users a warning before the plugin path is ready to move, so this
    rolls the change back until that migration is complete.
    
    # What
    
    - revert the legacy `notify` deprecation change from #20524
    - restore the prior `notify` behavior and remove the temporary
    deprecation metrics/docs from that change
    
    Once the computer use plugin has migrated, we can land the same
    deprecation again.
  • deprecate legacy notify (#20524)
    # Why
    
    `notify` is the remaining compatibility surface from the legacy hook
    implementation. The newer lifecycle hook engine now owns the active hook
    system, so we should start steering users away from adding new `notify`
    configs before removing the old path entirely. This also adds a
    lightweight watchpoint for the deprecation so we can see how much legacy
    usage remains before the clean drop.
    
    # What
    
    - emit a startup deprecation notice when a non-empty `notify` command is
    configured
    - emit `codex.notify.configured` when a session starts with legacy
    `notify` configured
    - emit `codex.notify.run` when the legacy notify path fires after a
    completed turn
    - mark `notify` as deprecated in the config schema and repo docs
    - remove the orphaned `codex-rs/hooks/src/user_notification.rs` file
    that is no longer compiled
    - add regression coverage for the new deprecation notice
    
    # Next steps
    
    A follow-up PR can remove the legacy notify path entirely once we are
    ready for the clean drop. Before then, we can watch
    `codex.notify.configured` and `codex.notify.run` to understand the
    deprecation impact and remaining active usage. The cleanup PR should
    then delete the `notify` config field, the `legacy_notify`
    implementation, the old compatibility dispatch types and callsites that
    only exist for the legacy path, and the remaining compatibility
    docs/tests.
    
    # Testing
    
    - `cargo test -p codex-hooks`
    - `cargo test -p codex-config`
    - `cargo test -p codex-core emits_deprecation_notice_for_notify`
  • chore(cli) deprecate --full-auto (#20133)
    ## Summary
    Starts the process of getting rid of `--full-auto`, with some
    concessions:
    1. Fully removes the command from the tui, since it just resolves to the
    default permissions there, and encourages users to use the one-time
    trust flow if they're not in a trusted repo.
    2. Marks the command as deprecated in `codex exec`, in case users are
    actively relying on this. We'll remove in an upcoming n+X release.
    3. Cleans up some of the `codex sandbox` cli logic, to keep supporting
    legacy sandbox policies for now.
    
    This isn't the cleanest setup, but I think it is worthwhile to warn
    users for one release before hard-removing it.
    
    ## Testing 
    - [x] Updated unit tests
  • Fix codex-rs README grammar (#19514)
    ## Why
    
    Issue #19418 points out a small grammar issue in `codex-rs/README.md`
    under "Code Organization." The current sentence says "we hope this to
    be," which reads awkwardly.
    
    Fixes #19418.
    
    ## What changed
    
    Updated the `core/` crate description so the sentence reads "we hope
    this becomes a library crate."
    
    ## Verification
    
    Documentation-only change. Reviewed the Markdown diff.
  • Significantly improve standalone installer (#17022)
    ## Summary
    
    This PR significantly improves the standalone installer experience.
    
    The main changes are:
    
    1. We now install the codex binary and other dependencies in a
    subdirectory under CODEX_HOME.
    (`CODEX_HOME/packages/standalone/releases/...`)
    
    2. We replace the `codex.js` launcher that npm/bun rely on with logic in
    the Rust binary that automatically resolves its dependencies (like
    ripgrep)
    
    ## Motivation
    
    A few design constraints pushed this work.
    
    1. Currently, the entrypoint to codex is through `codex.js`, which
    forces a node dependency to kick off our rust app. We want to move away
    from this so that the entrypoint to codex does not rely on node or
    external package managers.
    2. Right now, the native script adds codex and its dependencies directly
    to user PATH. Given that codex is likely to add more binary dependencies
    than ripgrep, we want a solution which does not add arbitrary binaries
    to user PATH -- the only one we want to add is the `codex` command
    itself.
    3. We want upgrades to be atomic. We do not want scenarios where
    interrupting an upgrade command can move codex into undefined state (for
    example, having a new codex binary but an old ripgrep binary). This was
    ~possible with the old script.
    4. Currently, the Rust binary uses heuristics to determine which
    installer created it. These heuristics are flaky and are tied to the
    `codex.js` launcher. We need a more stable/deterministic way to
    determine how the binary was installed for standalone.
    5. We do not want conflicting codex installations on PATH. For example,
    the user installing via npm, then installing via brew, then installing
    via standalone would make it unclear which version of codex is being
    launched and make it tough for us to determine the right upgrade
    command.
    
    ## Design
    
    ### Standalone package layout
    
    Standalone installs now live under `CODEX_HOME/packages/standalone`:
    
    ```text
    $CODEX_HOME/
      packages/
        standalone/
          current -> releases/0.111.0-x86_64-unknown-linux-musl
          releases/
            0.111.0-x86_64-unknown-linux-musl/
              codex
              codex-resources/
                rg
    ```
    
    where `standalone/current` is a symlink to a release directory.
    
    On Windows, the release directory has the same shape, with `.exe` names
    and Windows helpers in `codex-resources`:
    
    ```text
    %CODEX_HOME%\
      packages\
        standalone\
          current -> releases\0.111.0-x86_64-pc-windows-msvc
          releases\
            0.111.0-x86_64-pc-windows-msvc\
              codex.exe
              codex-resources\
                rg.exe
                codex-command-runner.exe
                codex-windows-sandbox-setup.exe
    ```
    
    This gives us:
    - atomic upgrades because we can fully stage a release before switching
    `standalone/current`
    - a stable way for the binary to recognize a standalone install from its
    canonical `current_exe()` path under CODEX_HOME
    - a clean place for binary dependencies like `rg`, Windows sandbox
    helpers, and, in the future, our custom `zsh` etc
    
    ### Command location
    
    On Unix, we add a symlink at `~/.local/bin/codex` which points directly
    to the `$CODEX_HOME/packages/standalone/current/codex` binary. This
    becomes the main entrypoint for the CLI.
    
    On Windows, we store the link at
    `%LOCALAPPDATA%\Programs\OpenAI\Codex\bin`.
    
    ### PATH persistence
    
    This is a tricky part of the PR, as there's no ~super reliable way to
    ensure that we end up on PATH without significant tradeoffs.
    
    Most Unix variants will have `~/.local/bin` on PATH already, which means
    we *should* be fine simply registering the command there in most cases.
    However, there are cases where this is not the case. In these cases, we
    directly edit the profile depending on the shell we're in.
    
    - macOS zsh: `~/.zprofile`
    - macOS bash: `~/.bash_profile`
    - Linux zsh: `~/.zshrc`
    - Linux bash: `~/.bashrc`
    - fallback: `~/.profile`
    
    On Windows, we update the User `Path` environment variable directly and
    we don't need to worry about shell profiles.
    
    ### Standalone runtime detection
    
    This PR adds a new shared crate, `codex-install-context`, which computes
    install ownership once per process and caches it in a `OnceLock`.
    
    That context includes:
    - install manager (`Standalone`, `Npm`, `Bun`, `Brew`, `Other`)
    - the managed standalone release directory, when applicable
    - the managed standalone `codex-resources` directory, when present
    - the resolved `rg_command`
    
    The standalone path is detected by canonicalizing `current_exe()`,
    canonicalizing CODEX_HOME via `find_codex_home()`, and checking whether
    the binary is running from under
    `$CODEX_HOME/packages/standalone/releases`.
    
    We intentionally do not use a release metadata file. The binary path is
    the source of truth.
    
    ### Dependency resolution
    
    For standalone installs, `grep_files` now resolves bundled `rg` from
    `codex-resources` next to the Codex binary.
    
    For npm/bun/brew/other installs, `grep_files` falls back to resolving
    `rg` from PATH.
    
    For Windows standalone installs, Windows sandbox helpers are still found
    as direct siblings when present. If they are not direct siblings, the
    lookup also checks the sibling `codex-resources` directory.
    
    ### TUI update path
    
    The TUI now has `UpdateAction::StandaloneUnix` and
    `UpdateAction::StandaloneWindows`, which rerun the standalone install
    commands.
    
    Unix update command:
    
    ```sh
    sh -c "curl -fsSL https://chatgpt.com/codex/install.sh | sh"
    ```
    
    Windows update command:
    
    ```powershell
    powershell -c "irm https://chatgpt.com/codex/install.ps1|iex"
    ```
    
    The Windows updater runs PowerShell directly. We do this because `cmd
    /C` would parse the `|iex` as a cmd pipeline instead of passing it to
    PowerShell.
    
    ## Additional installer behavior
    
    - standalone installs now warn about conflicting npm/bun/brew-managed
    `codex` installs and offer to uninstall them
    - same-version reruns do not redownload the release if it is already
    staged locally
    
    ## Testing
    
    Installer smoke tests run:
    - macOS: fresh install into isolated `HOME` and `CODEX_HOME` with
    `scripts/install/install.sh --release latest`
    - macOS: reran the installer against the same isolated install to verify
    the same-version/update path and PATH block idempotence
    - macOS: verified the installed `codex --version` and bundled
    `codex-resources/rg --version`
    - Windows: parsed `scripts/install/install.ps1` with PowerShell via
    `[scriptblock]::Create(...)`
    - Windows: verified the standalone update action builds a direct
    PowerShell command and does not route the `irm ...|iex` command through
    `cmd /C`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Support Codex CLI stdin piping for codex exec (#15917)
    # Summary
    
    Claude Code supports a useful prompt-plus-stdin workflow:
    
    ```bash
    echo "complex input..." | claude -p "summarize concisely"
    ```
    
    Codex previously did not support the equivalent `codex exec` form. While
    `codex exec` could read the prompt from stdin, it could not combine
    piped input with an explicit prompt argument.
    
    This change adds that missing workflow:
    
    ```bash
    echo "complex input..." | codex exec "summarize concisely"
    ```
    
    With this change, when `codex exec` receives both a positional prompt
    and piped stdin, the prompt remains the instruction and stdin is passed
    along as structured `<stdin>...</stdin>` context.
    
    Example:
    
    ```bash
    curl https://jsonplaceholder.typicode.com/comments \
      | ./target/debug/codex exec --skip-git-repo-check "format the top 20 items into a markdown table" \
      > table.md
    ```
    
    This PR also adds regression coverage for:
    - prompt argument + piped stdin
    - legacy stdin-as-prompt behavior
    - `codex exec -` forced-stdin behavior
    - empty-stdin error cases
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Fix: update parallel tool call exec approval to approve on request id (#11162)
    ### Summary
    
    In parallel tool call, exec command approvals were not approved at
    request level but at a turn level. i.e. when a single request is
    approved, the system currently treats all requests in turn as approved.
    
    ### Before
    
    https://github.com/user-attachments/assets/d50ed129-b3d2-4b2f-97fa-8601eb11f6a8
    
    ### After
    
    https://github.com/user-attachments/assets/36528a43-a4aa-4775-9e12-f13287ef19fc
  • Fix toasts on Windows under WSL 2 (#7137)
    Before this: no notifications or toasts when using Codex CLI in WSL 2.
    
    After this: I get toasts from Codex
  • add codex debug seatbelt --log-denials (#4098)
    This adds a debugging tool for analyzing why certain commands fail to
    execute under the sandbox.
    
    Example output:
    
    ```
    $ codex debug seatbelt --log-denials bash -lc "(echo foo > ~/foo.txt)"
    bash: /Users/nornagon/foo.txt: Operation not permitted
    
    === Sandbox denials ===
    (bash) file-write-data /dev/tty
    (bash) file-write-data /dev/ttys001
    (bash) sysctl-read kern.ngroups
    (bash) file-write-create /Users/nornagon/foo.txt
    ```
    
    It operates by:
    
    1. spawning `log stream` to watch system logs, and
    2. tracking all descendant PIDs using kqueue + proc_listchildpids.
    
    this is a "best-effort" technique, as `log stream` may drop logs(?), and
    kqueue + proc_listchildpids isn't atomic and can end up missing very
    short-lived processes. But it works well enough in my testing to be
    useful :)
  • Windows Sandbox - Alpha version (#4905)
    - Added the new codex-windows-sandbox crate that builds both a library
    entry point (run_windows_sandbox_capture) and a CLI executable to launch
    commands inside a Windows restricted-token sandbox, including ACL
    management, capability SID provisioning, network lockdown, and output
    capture
    (windows-sandbox-rs/src/lib.rs:167, windows-sandbox-rs/src/main.rs:54).
    - Introduced the experimental WindowsSandbox feature flag and wiring so
    Windows builds can opt into the sandbox:
    SandboxType::WindowsRestrictedToken, the in-process execution path, and
    platform sandbox selection now honor the flag (core/src/features.rs:47,
    core/src/config.rs:1224, core/src/safety.rs:19,
    core/src/sandboxing/mod.rs:69, core/src/exec.rs:79,
    core/src/exec.rs:172).
    - Updated workspace metadata to include the new crate and its
    Windows-specific dependencies so the core crate can link against it
    (codex-rs/
        Cargo.toml:91, core/Cargo.toml:86).
    - Added a PowerShell bootstrap script that installs the Windows
    toolchain, required CLI utilities, and builds the workspace to ease
    development
        on the platform (scripts/setup-windows.ps1:1).
    - Landed a Python smoke-test suite that exercises
    read-only/workspace-write policies, ACL behavior, and network denial for
    the Windows sandbox
        binary (windows-sandbox-rs/sandbox_smoketests.py:1).
  • Update Homebrew install instructions to use cask (#5377)
    ## Summary
    - update the README install snippets to use `brew install --cask codex`
    - mirror the same change in the Rust CLI README
    
    Address #5317
    
    https://chatgpt.com/codex/tasks/task_i_68f65682543083269254cd64d290df28
    
    ---------
    
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • docs: align sandbox defaults, dedupe sections and improve getting started guide (#5357)
    Tightened the docs so the sandbox guide matches reality, noted the new
    tools.view_image toggle next to web search, and linked the README to the
    getting-started guide which now owns the familiar tips (backtrack, --cd,
    --add-dir, etc.).
  • feat: add --add-dir flag for extra writable roots (#5335)
    Add a `--add-dir` CLI flag so sessions can use extra writable roots in
    addition to the ones specified in the config file. These are ephemerally
    added during the session only.
    
    Fixes #3303
    Fixes #2797
  • [MCP] Improve docs (#4811)
    Updated, expanded on, clarified, and deduplicated some MCP docs
  • add codex sandbox {linux|macos} (#4782)
    ## Summary
    - add a `codex sandbox` subcommand with macOS and Linux targets while
    keeping the legacy `codex debug` aliases
    - update documentation to highlight the new sandbox entrypoints and
    point existing references to the new command
    - clarify the core README about the linux sandbox helper alias
    
    ## Testing
    - just fmt
    - just fix -p codex-cli
    - cargo test -p codex-cli
    
    
    ------
    https://chatgpt.com/codex/tasks/task_i_68e2e00ca1e8832d8bff53aa0b50b49e
  • fix: separate codex mcp into codex mcp-server and codex app-server (#4471)
    This is a very large PR with some non-backwards-compatible changes.
    
    Historically, `codex mcp` (or `codex mcp serve`) started a JSON-RPC-ish
    server that had two overlapping responsibilities:
    
    - Running an MCP server, providing some basic tool calls.
    - Running the app server used to power experiences such as the VS Code
    extension.
    
    This PR aims to separate these into distinct concepts:
    
    - `codex mcp-server` for the MCP server
    - `codex app-server` for the "application server"
    
    Note `codex mcp` still exists because it already has its own subcommands
    for MCP management (`list`, `add`, etc.)
    
    The MCP logic continues to live in `codex-rs/mcp-server` whereas the
    refactored app server logic is in the new `codex-rs/app-server` folder.
    Note that most of the existing integration tests in
    `codex-rs/mcp-server/tests/suite` were actually for the app server, so
    all the tests have been moved with the exception of
    `codex-rs/mcp-server/tests/suite/mod.rs`.
    
    Because this is already a large diff, I tried not to change more than I
    had to, so `codex-rs/app-server/tests/common/mcp_process.rs` still uses
    the name `McpProcess` for now, but I will do some mechanical renamings
    to things like `AppServer` in subsequent PRs.
    
    While `mcp-server` and `app-server` share some overlapping functionality
    (like reading streams of JSONL and dispatching based on message types)
    and some differences (completely different message types), I ended up
    doing a bit of copypasta between the two crates, as both have somewhat
    similar `message_processor.rs` and `outgoing_message.rs` files for now,
    though I expect them to diverge more in the near future.
    
    One material change is that of the initialize handshake for `codex
    app-server`, as we no longer use the MCP types for that handshake.
    Instead, we update `codex-rs/protocol/src/mcp_protocol.rs` to add an
    `Initialize` variant to `ClientRequest`, which takes the `ClientInfo`
    object we need to update the `USER_AGENT_SUFFIX` in
    `codex-rs/app-server/src/message_processor.rs`.
    
    One other material change is in
    `codex-rs/app-server/src/codex_message_processor.rs` where I eliminated
    a use of the `send_event_as_notification()` method I am generally trying
    to deprecate (because it blindly maps an `EventMsg` into a
    `JSONNotification`) in favor of `send_server_notification()`, which
    takes a `ServerNotification`, as that is intended to be a custom enum of
    all notification types supported by the app server. So to make this
    update, I had to introduce a new variant of `ServerNotification`,
    `SessionConfigured`, which is a non-backwards compatible change with the
    old `codex mcp`, and clients will have to be updated after the next
    release that contains this PR. Note that
    `codex-rs/app-server/tests/suite/list_resume.rs` also had to be update
    to reflect this change.
    
    I introduced `codex-rs/utils/json-to-toml/src/lib.rs` as a small utility
    crate to avoid some of the copying between `mcp-server` and
    `app-server`.
  • docs: fix codex exec heading typo (#2703)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the "Contributing" section
    of the README or your PR may be closed:
    https://github.com/openai/codex#contributing
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
  • README / docs refactor (#2724)
    This PR cleans up the monolithic README by breaking it into a set
    navigable pages under docs/ (install, getting started, configuration,
    authentication, sandboxing and approvals, platform details, FAQ, ZDR,
    contributing, license). The top‑level README is now more concise and
    intuitive, (with corrected screenshots).
    
    It also consolidates overlapping content from codex-rs/README.md into
    the top‑level docs and updates links accordingly. The codex-rs README
    remains in place for now as a pointer and for continuity.
    
    Finally, added an extensive config reference table at the bottom of
    docs/config.md.
    
    ---------
    
    Co-authored-by: easong-openai <easong@openai.com>
  • feat: add support for --sandbox flag (#1476)
    On a high-level, we try to design `config.toml` so that you don't have
    to "comment out a lot of stuff" when testing different options.
    
    Previously, defining a sandbox policy was somewhat at odds with this
    principle because you would define the policy as attributes of
    `[sandbox]` like so:
    
    ```toml
    [sandbox]
    mode = "workspace-write"
    writable_roots = [ "/tmp" ]
    ```
    
    but if you wanted to temporarily change to a read-only sandbox, you
    might feel compelled to modify your file to be:
    
    ```toml
    [sandbox]
    mode = "read-only"
    # mode = "workspace-write"
    # writable_roots = [ "/tmp" ]
    ```
    
    Technically, commenting out `writable_roots` would not be strictly
    necessary, as `mode = "read-only"` would ignore `writable_roots`, but
    it's still a reasonable thing to do to keep things tidy.
    
    Currently, the various values for `mode` do not support that many
    attributes, so this is not that hard to maintain, but one could imagine
    this becoming more complex in the future.
    
    In this PR, we change Codex CLI so that it no longer recognizes
    `[sandbox]`. Instead, it introduces a top-level option, `sandbox_mode`,
    and `[sandbox_workspace_write]` is used to further configure the sandbox
    when when `sandbox_mode = "workspace-write"` is used:
    
    ```toml
    sandbox_mode = "workspace-write"
    
    [sandbox_workspace_write]
    writable_roots = [ "/tmp" ]
    ```
    
    This feels a bit more future-proof in that it is less tedious to
    configure different sandboxes:
    
    ```toml
    sandbox_mode = "workspace-write"
    
    [sandbox_read_only]
    # read-only options here...
    
    [sandbox_workspace_write]
    writable_roots = [ "/tmp" ]
    
    [sandbox_danger_full_access]
    # danger-full-access options here...
    ```
    
    In this scheme, you never need to comment out the configuration for an
    individual sandbox type: you only need to redefine `sandbox_mode`.
    
    Relatedly, previous to this change, a user had to do `-c
    sandbox.mode=read-only` to change the mode on the command line. With
    this change, things are arguably a bit cleaner because the equivalent
    option is `-c sandbox_mode=read-only` (and now `-c
    sandbox_workspace_write=...` can be set separately).
    
    Though more importantly, we introduce the `-s/--sandbox` option to the
    CLI, which maps directly to `sandbox_mode` in `config.toml`, making
    config override behavior easier to reason about. Moreover, as you can
    see in the updates to the various Markdown files, it is much easier to
    explain how to configure sandboxing when things like `--sandbox
    read-only` can be used as an example.
    
    Relatedly, this cleanup also made it straightforward to add support for
    a `sandbox` option for Codex when used as an MCP server (see the changes
    to `mcp-server/src/codex_tool_config.rs`).
    
    Fixes https://github.com/openai/codex/issues/1248.
  • docs: update codex-rs/README.md to list new features in the Rust CLI (#1267)
    Let users know about what the Rust CLI supports that the TypeScript CLI
    doesn't!
  • docs: split the config-related portion of codex-rs/README.md into its own config.md file (#1165)
    Also updated the overview on `codex-rs/README.md` while here.
  • 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.
  • fix: make codex-mini-latest the default model in the Rust TUI (#972)
    It's time to make `codex-mini-latest` the new default, as this should be
    an "evergreen" model pointer.
    
    * Equivalent change in TypeScript
    https://github.com/openai/codex/pull/951
    * See some notes about using `codex-mini-latest` with MCP in
    https://github.com/openai/codex/pull/961
  • feat: make it possible to toggle mouse mode in the Rust TUI (#971)
    I did a bit of research to understand why I could not use my mouse to
    drag to select text to copy to the clipboard in iTerm.
    
    Apparently https://github.com/openai/codex/pull/641 to enable mousewheel
    scrolling broke this functionality. It seems that, unless we put in a
    bit of effort, we can have drag-to-select or scrolling, but not both.
    Though if you know the trick to hold down `Option` will dragging with
    the mouse in iTerm, you can probably get by with this. (I did not know
    about this option prior to researching this issue.)
    
    Nevertheless, users may still prefer to disable mouse capture
    altogether, so this PR introduces:
    
    * the ability to set `tui.disable_mouse_capture = true` in `config.toml`
    to disable mouse capture
    * a new command, `/toggle-mouse-mode` to toggle mouse capture
  • feat: add support for file_opener option in Rust, similiar to #911 (#957)
    This ports the enhancement introduced in
    https://github.com/openai/codex/pull/911 (and the fixes in
    https://github.com/openai/codex/pull/919) for the TypeScript CLI to the
    Rust one.
  • 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.
  • Add codespell support (config, workflow to detect/not fix) and make it fix some typos (#903)
    More about codespell: https://github.com/codespell-project/codespell .
    
    I personally introduced it to dozens if not hundreds of projects already
    and so far only positive feedback.
    
    CI workflow has 'permissions' set only to 'read' so also should be safe.
    
    Let me know if just want to take typo fixes in and get rid of the CI
    
    ---------
    
    Signed-off-by: Yaroslav O. Halchenko <debian@onerussian.com>
  • 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: 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: 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
  • doc: update the config.toml documentation for the Rust CLI in codex-rs/README.md (#795)
    https://github.com/openai/codex/pull/793 had important information on
    the `notify` config option that seemed worth memorializing, so this PR
    updates the documentation about all of the configurable options in
    `~/.codex/config.toml`.
  • chore: remove the REPL crate/subcommand (#754)
    @oai-ragona and I discussed it, and we feel the REPL crate has served
    its purpose, so we're going to delete the code and future archaeologists
    can find it in Git history.
  • fix: make the TUI the default/"interactive" CLI in Rust (#711)
    Originally, the `interactive` crate was going to be a placeholder for
    building out a UX that was comparable to that of the existing TypeScript
    CLI. Though after researching how Ratatui works, that seems difficult to
    do because it is designed around the idea that it will redraw the full
    screen buffer each time (and so any scrolling should be "internal" to
    your Ratatui app) whereas the TypeScript CLI expects to render the full
    history of the conversation every time(*) (which is why you can use your
    terminal scrollbar to scroll it).
    
    While it is possible to use Ratatui in a way that acts more like what
    the TypeScript CLI is doing, it is awkward and seemingly results in
    tedious code, so I think we should abandon that approach. As such, this
    PR deletes the `interactive/` folder and the code that depended on it.
    
    Further, since we added support for mousewheel scrolling in the TUI in
    https://github.com/openai/codex/pull/641, it certainly feels much better
    and the need for scroll support via the terminal scrollbar is greatly
    diminished. This is now a more appropriate default UX for the
    "multitool" CLI.
    
    (*) Incidentally, I haven't verified this, but I think this results in
    O(N^2) work in rendering, which seems potentially problematic for long
    conversations.
  • 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.