Commit Graph

274 Commits

  • fix: is_inside_git_repo should take the directory as a param (#809)
    https://github.com/openai/codex/pull/800 made `cwd` a property of
    `Config` and made it so the `cwd` is not necessarily
    `std::env::current_dir()`. As such, `is_inside_git_repo()` should check
    `Config.cwd` rather than `std::env::current_dir()`.
    
    This PR updates `is_inside_git_repo()` to take `Config` instead of an
    arbitrary `PathBuf` to force the check to operate on a `Config` where
    `cwd` has been resolved to what the user specified.
  • fix: TUI should use cwd from Config (#808)
    https://github.com/openai/codex/pull/800 made `cwd` a property of
    `Config`, so the TUI should use this instead of running
    `std::env::current_dir()`.
  • feat: make cwd a required field of Config so we stop assuming std::env::current_dir() in a session (#800)
    In order to expose Codex via an MCP server, I realized that we should be
    taking `cwd` as a parameter rather than assuming
    `std::env::current_dir()` as the `cwd`. Specifically, the user may want
    to start a session in a directory other than the one where the MCP
    server has been started.
    
    This PR makes `cwd: PathBuf` a required field of `Session` and threads
    it all the way through, though I think there is still an issue with not
    honoring `workdir` for `apply_patch`, which is something we also had to
    fix in the TypeScript version: https://github.com/openai/codex/pull/556.
    
    This also adds `-C`/`--cd` to change the cwd via the command line.
    
    To test, I ran:
    
    ```
    cargo run --bin codex -- exec -C /tmp 'show the output of ls'
    ```
    
    and verified it showed the contents of my `/tmp` folder instead of
    `$PWD`.
  • 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`.
  • 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
  • feat: introduce mcp-server crate (#792)
    This introduces the `mcp-server` crate, which contains a barebones MCP
    server that provides an `echo` tool that echoes the user's request back
    to them.
    
    To test it out, I launched
    [modelcontextprotocol/inspector](https://github.com/modelcontextprotocol/inspector)
    like so:
    
    ```
    mcp-server$ npx @modelcontextprotocol/inspector cargo run --
    ```
    
    and opened up `http://127.0.0.1:6274` in my browser:
    
    
    ![image](https://github.com/user-attachments/assets/83fc55d4-25c2-4497-80cd-e9702283ff93)
    
    I also had to make a small fix to `mcp-types`, adding
    `#[serde(untagged)]` to a number of `enum`s.
  • fix: mcp-types serialization wasn't quite working (#791)
    While creating a basic MCP server in
    https://github.com/openai/codex/pull/792, I discovered a number of bugs
    with the initial `mcp-types` crate that I needed to fix in order to
    implement the server.
    
    For example, I discovered that when serializing a message, `"jsonrpc":
    "2.0"` was not being included.
    
    I changed the codegen so that the field is added as:
    
    ```rust
        #[serde(rename = "jsonrpc", default = "default_jsonrpc")]
        pub jsonrpc: String,
    ```
    
    This ensures that the field is serialized as `"2.0"`, though the field
    still has to be assigned, which is tedious. I may experiment with
    `Default` or something else in the future. (I also considered creating a
    custom serializer, but I'm not sure it's worth the trouble.)
    
    While here, I also added `MCP_SCHEMA_VERSION` and `JSONRPC_VERSION` as
    `pub const`s for the crate.
    
    I also discovered that MCP rejects sending `null` for optional fields,
    so I had to add `#[serde(skip_serializing_if = "Option::is_none")]` on
    `Option` fields.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/791).
    * #792
    * __->__ #791
  • feat: introduce mcp-types crate (#787)
    This adds our own `mcp-types` crate to our Cargo workspace. We vendor in
    the
    [`2025-03-26/schema.json`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/05f204513641c05bd78d056791af99c6c84520fa/schema/2025-03-26/schema.json)
    from the MCP repo and introduce a `generate_mcp_types.py` script to
    codegen the `lib.rs` from the JSON schema.
    
    Test coverage is currently light, but I plan to refine things as we
    start making use of this crate.
    
    And yes, I am aware that
    https://github.com/modelcontextprotocol/rust-sdk exists, though the
    published https://crates.io/crates/rmcp appears to be a competing
    effort. While things are up in the air, it seems better for us to
    control our own version of this code.
    
    Incidentally, Codex did a lot of the work for this PR. I told it to
    never edit `lib.rs` directly and instead to update
    `generate_mcp_types.py` and then re-run it to update `lib.rs`. It
    followed these instructions and once things were working end-to-end, I
    iteratively asked for changes to the tests until the API looked
    reasonable (and the code worked). Codex was responsible for figuring out
    what to do to `generate_mcp_types.py` to achieve the requested test/API
    changes.
  • Configure HTTPS agent for proxies (#775)
    - Some workflows require you to route openAI API traffic through a proxy
    - See
    https://github.com/openai/openai-node/tree/v4?tab=readme-ov-file#configuring-an-https-agent-eg-for-proxies
    for more details
    
    ---------
    
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
    Co-authored-by: Fouad Matin <fouad@openai.com>
  • feat: use Landlock for sandboxing on Linux in TypeScript CLI (#763)
    Building on top of https://github.com/openai/codex/pull/757, this PR
    updates Codex to use the Landlock executor binary for sandboxing in the
    Node.js CLI. Note that Codex has to be invoked with either `--full-auto`
    or `--auto-edit` to activate sandboxing. (Using `--suggest` or
    `--dangerously-auto-approve-everything` ensures the sandboxing codepath
    will not be exercised.)
    
    When I tested this on a Linux host (specifically, `Ubuntu 24.04.1 LTS`),
    things worked as expected: I ran Codex CLI with `--full-auto` and then
    asked it to do `echo 'hello mbolin' into hello_world.txt` and it
    succeeded without prompting me.
    
    However, in my testing, I discovered that the sandboxing did *not* work
    when using `--full-auto` in a Linux Docker container from a macOS host.
    I updated the code to throw a detailed error message when this happens:
    
    
    ![image](https://github.com/user-attachments/assets/e5b99def-f00e-4ade-a0c5-2394d30df52e)
  • chore: make build process a single script to run (#757)
    This introduces `./codex-cli/scripts/stage_release.sh`, which is a shell
    script that stages a release for the Node.js module in a temp directory.
    It updates the release to include these native binaries:
    
    ```
    bin/codex-linux-sandbox-arm64
    bin/codex-linux-sandbox-x64
    ```
    
    though this PR does not update Codex CLI to use them yet.
    
    When doing local development, run
    `./codex-cli/scripts/install_native_deps.sh` to install these in your
    own `bin/` folder.
    
    This PR also updates `README.md` to document the new workflow.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/757).
    * #763
    * __->__ #757
  • bump(version): 0.1.2504301751 (#768)
    ## `0.1.2504301751`
    
    ### 🚀 Features
    
    - User config api key (#569)
    - `@mention` files in codex (#701)
    - Add `--reasoning` CLI flag (#314)
    - Lower default retry wait time and increase number of tries (#720)
    - Add common package registries domains to allowed-domains list (#414)
    
    ### 🪲 Bug Fixes
    
    - Insufficient quota message (#758)
    - Input keyboard shortcut opt+delete (#685)
    - `/diff` should include untracked files (#686)
    - Only allow running without sandbox if explicitly marked in safe
    container (#699)
    - Tighten up check for /usr/bin/sandbox-exec (#710)
    - Check if sandbox-exec is available (#696)
    - Duplicate messages in quiet mode (#680)
  • feat: @mention files in codex (#701)
    Solves #700
    
    ## State of the World Before
    
    Prior to this PR, when users wanted to share file contents with Codex,
    they had two options:
    - Manually copy and paste file contents into the chat
    - Wait for the assistant to use the shell tool to view the file
    
    The second approach required the assistant to:
    1. Recognize the need to view a file
    2. Execute a shell tool call
    3. Wait for the tool call to complete
    4. Process the file contents
    
    This consumed extra tokens and reduced user control over which files
    were shared with the model.
    
    ## State of the World After
    
    With this PR, users can now:
    - Reference files directly in their chat input using the `@path` syntax
    - Have file contents automatically expanded into XML blocks before being
    sent to the LLM
    
    For example, users can type `@src/utils/config.js` in their message, and
    the file contents will be included in context. Within the terminal chat
    history, these file blocks will be collapsed back to `@path` format in
    the UI for clean presentation.
    
    Tag File suggestions:
    <img width="857" alt="file-suggestions"
    src="https://github.com/user-attachments/assets/397669dc-ad83-492d-b5f0-164fab2ff4ba"
    />
    
    Tagging files in action:
    <img width="858" alt="tagging-files"
    src="https://github.com/user-attachments/assets/0de9d559-7b7f-4916-aeff-87ae9b16550a"
    />
    
    Demo video of file tagging:
    [![Demo video of file
    tagging](https://img.youtube.com/vi/vL4LqtBnqt8/0.jpg)](https://www.youtube.com/watch?v=vL4LqtBnqt8)
    
    ## Implementation Details
    
    This PR consists of 2 main components:
    
    1. **File Tag Utilities**:
    - New `file-tag-utils.ts` utility module that handles both expansion and
    collapsing of file tags
    - `expandFileTags()` identifies `@path` tokens and replaces them with
    XML blocks containing file contents
    - `collapseXmlBlocks()` reverses the process, converting XML blocks back
    to `@path` format for UI display
    - Tokens are only expanded if they point to valid files (directories are
    ignored)
       - Expansion happens just before sending input to the model
    
    2. **Terminal Chat Integration**:
    - Leveraged the existing file system completion system for tabbing to
    support the `@path` syntax
       - Added `updateFsSuggestions` helper to manage filesystem suggestions
    - Added `replaceFileSystemSuggestion` to replace input with filesystem
    suggestions
    - Applied `collapseXmlBlocks` in the chat response rendering so that
    tagged files are shown as simple `@path` tags
    
    The PR also includes test coverage for both the UI and the file tag
    utilities.
    
    ## Next Steps
    
    Some ideas I'd like to implement if this feature gets merged:
    
    - Line selection: `@path[50:80]` to grab specific sections of files
    - Method selection: `@path#methodName` to grab just one function/class
    - Visual improvements: highlight file tags in the UI to make them more
    noticeable
  • fix: insufficient quota message (#758)
    This pull request includes a change to improve the error message
    displayed when there is insufficient quota in the `AgentLoop` class. The
    updated message provides more detailed information and a link for
    managing or purchasing credits.
    
    Error message improvement:
    
    *
    [`codex-cli/src/utils/agent/agent-loop.ts`](diffhunk://#diff-b15957eac2720c3f1f55aa32f172cdd0ac6969caf4e7be87983df747a9f97083L1140-R1140):
    Updated the error message in the `AgentLoop` class to include the
    specific error message (if available) and a link to manage or purchase
    credits.
    
    
    Fixes #751
  • fix: remove unused _writableRoots arg to exec() function (#762)
    I suspect this was done originally so that `execForSandbox()` had a
    consistent signature for both the `SandboxType.NONE` and
    `SandboxType.MACOS_SEATBELT` cases, but that is not really necessary and
    turns out to make the upcoming Landlock support a bit more complicated
    to implement, so I had Codex remove it and clean up the call sites.
  • chore: mark Rust releases as "prerelease" (#761)
    Apparently the URLs for draft releases cannot be downloaded using
    unauthenticated `curl`, which means the DotSlash file only works for
    users who are authenticated with `gh`. According to chat, prereleases
    _can_ be fetched with unauthenticated `curl`, so let's try that.
  • chore: script to create a Rust release (#759)
    For now, keep things simple such that we never update the `version` in
    the `Cargo.toml` for the workspace root on the `main` branch. Instead,
    create a new branch for a release, push one commit that updates the
    `version`, and then tag that branch to kick off a release.
    
    To test, I ran this script and created this release job:
    
    https://github.com/openai/codex/actions/runs/14762580641
  • chore: Rust release, set prerelease:false and version=0.0.2504301132 (#755)
    The generated DotSlash file has URLs that refer to
    `https://github.com/openai/codex/releases/`, so let's set
    `prerelease:false` (but keep `draft:true` for now) so those URLs should
    work.
    
    Also updated `version` in Cargo workspace so I will kick off a build
    once this lands.
  • fix: read version from package.json instead of modifying session.ts (#753)
    I am working to simplify the build process. As a first step, update
    `session.ts` so it reads the `version` from `package.json` at runtime so
    we no longer have to modify it during the build process. I want to get
    to a place where the build looks like:
    
    ```
    cd codex-cli
    pnpm i
    pnpm build
    RELEASE_DIR=$(mktemp -d)
    cp -r bin "$RELEASE_DIR/bin"
    cp -r dist "$RELEASE_DIR/dist"
    cp -r src "$RELEASE_DIR/src" # important if we want sourcemaps to continue to work
    cp ../README.md "$RELEASE_DIR"
    VERSION=$(printf '0.1.%d' $(date +%y%m%d%H%M))
    jq --arg version "$VERSION" '.version = $version' package.json > "$RELEASE_DIR/package.json"
    ```
    
    Then the contents of `$RELEASE_DIR` should be good to `npm publish`, no?
  • 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: remove expected dot after v in rust-v tag name (#742)
    I think this extra dot was not intentional, but I'm not sure. Certainly
    this comment suggests it should not be there:
    
    
    https://github.com/openai/codex/blob/85999d72770832e2b1b457401c6c68d86e08344b/.github/workflows/rust-release.yml#L4
  • chore: fix errors in .github/workflows/rust-release.yml and prep 0.0.2504292006 release (#745)
    Apparently I made two key mistakes in
    https://github.com/openai/codex/pull/740 (fixed in this PR):
    
    * I forgot to redefine `$dest` in the `Stage Linux-only artifacts` step
    * I did not define the `if` check correctly in the `Stage Linux-only
    artifacts` step
    
    This fixes both of those issues and bumps the workspace version to
    `0.0.2504292006` in preparation for another release attempt.
  • fix: primary output of the codex-cli crate is named codex, not codex-cli (#743)
    I just got a bunch of failures in the release workflow:
    
    https://github.com/openai/codex/actions/runs/14745492805/job/41391926707
    
    along the lines of:
    
    ```
    cp: cannot stat 'target/aarch64-unknown-linux-gnu/release/codex-cli': No such file or directory
    ```
  • feat: codex-linux-sandbox standalone executable (#740)
    This introduces a standalone executable that run the equivalent of the
    `codex debug landlock` subcommand and updates `rust-release.yml` to
    include it in the release.
    
    The idea is that we will include this small binary with the TypeScript
    CLI to provide support for Linux sandboxing.
  • [codex-rs] Add rust-release action (#671)
    Taking a pass at building artifacts per platform so we can consider
    different distribution strategies that don't require users to install
    the full `cargo` toolchain.
    
    Right now this grabs just the `codex-repl` and `codex-tui` bins for 5
    different targets and bundles them into a draft release. I think a
    clearly marked pre-release set of artifacts will unblock the next step
    of testing.
  • 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 common package registries domains to allowed-domains list (#414)
    feat: add common package registries domains to allowed-domains list
  • Fixes issue #726 by adding config to configToSave object (#728)
    The saveConfig() function only includes a hardcoded subset of properties
    when writing the config file. Any property not explicitly listed (like
    disableResponseStorage) will be dropped.
    I have added `disableResponseStorage` to the `configToSave` object as
    the immediate fix.
    
    [Linking Issue this fixes.](https://github.com/openai/codex/issues/726)
  • feat: add --reasoning CLI flag (#314)
    This PR adds a new CLI flag: `--reasoning`, which allows users to
    customize the reasoning effort level (`low`, `medium`, or `high`) used
    by OpenAI's `o` models.
    By introducing the `--reasoning` flag, users gain more flexibility when
    working with the models. It enables optimization for either speed or
    depth of reasoning, depending on specific use cases.
    This PR resolves #107
    
    - **Flag**: `--reasoning`
    - **Accepted Values**: `low`, `medium`, `high`
    - **Default Behavior**: If not specified, the model uses the default
    reasoning level.
    
    ## Example Usage
    
    ```bash
    codex --reasoning=low "Write a simple function to calculate factorial"
    
    ---------
    
    Co-authored-by: Fouad Matin <169186268+fouad-openai@users.noreply.github.com>
    Co-authored-by: yashrwealthy <yash.rastogi@wealthy.in>
    Co-authored-by: Thibault Sottiaux <tibo@openai.com>
  • fix: eliminate runtime dependency on patch(1) for apply_patch (#718)
    When processing an `apply_patch` tool call, we were already computing
    the new file content in order to compute the unified diff. Before this
    PR, we were shelling out to `patch(1)` to apply the unified diff once
    the user accepted the change, but this updates the code to just retain
    the new file content and use it to write the file when the user accepts.
    This simplifies deployment because it no longer assumes `patch(1)` is on
    the host.
    
    Note this change is internal to the Codex agent and does not affect
    `protocol.rs`.
  • feat: lower default retry wait time and increase number of tries (#720)
    In total we now guarantee that we will wait for at least 60s before
    giving up.
    
    ---------
    
    Signed-off-by: Thibault Sottiaux <tibo@openai.com>
  • 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: make it possible to set disable_response_storage = true in config.toml (#714)
    https://github.com/openai/codex/pull/642 introduced support for the
    `--disable-response-storage` flag, but if you are a ZDR customer, it is
    tedious to set this every time, so this PR makes it possible to set this
    once in `config.toml` and be done with it.
    
    Incidentally, this tidies things up such that now `init_codex()` takes
    only one parameter: `Config`.
  • 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.
  • fix: tighten up check for /usr/bin/sandbox-exec (#710)
    * In both TypeScript and Rust, we now invoke `/usr/bin/sandbox-exec`
    explicitly rather than whatever `sandbox-exec` happens to be on the
    `PATH`.
    * Changed `isSandboxExecAvailable` to use `access()` rather than
    `command -v` so that:
      *  We only do the check once over the lifetime of the Codex process.
      * The check is specific to `/usr/bin/sandbox-exec`.
    * We now do a syscall rather than incur the overhead of spawning a
    process, dealing with timeouts, etc.
    
    I think there is still room for improvement here where we should move
    the `isSandboxExecAvailable` check earlier in the CLI, ideally right
    after we do arg parsing to verify that we can provide the Seatbelt
    sandbox if that is what the user has requested.
  • fix: increase timeout of test_writable_root (#713)
    Although we made some promising fixes in
    https://github.com/openai/codex/pull/662, we are still seeing some
    flakiness in `test_writable_root()`. If this continues to flake with the
    more generous timeout, we should try something other than simply
    increasing the timeout.
  • fix: drop d as keyboard shortcut for scrolling in the TUI (#704)
    The existing `b` and `space` are sufficient and `d` and `u` default to
    half-page scrolling in `less`, so the way we supported `d` and `u`
    wasn't faithful to that, anyway:
    
    https://man7.org/linux/man-pages/man1/less.1.html
    
    If we decide to bring `d` and `u` back, they should probably match
    `less`?