Commit Graph

32 Commits

  • 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.
  • 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.
  • 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`.
  • 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: 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`?
  • feat: load defaults into Config and introduce ConfigOverrides (#677)
    This changes how instantiating `Config` works and also adds
    `approval_policy` and `sandbox_policy` as fields. The idea is:
    
    * All fields of `Config` have appropriate default values.
    * `Config` is initially loaded from `~/.codex/config.toml`, so values in
    `config.toml` will override those defaults.
    * Clients must instantiate `Config` via
    `Config::load_with_overrides(ConfigOverrides)` where `ConfigOverrides`
    has optional overrides that are expected to be settable based on CLI
    flags.
    
    The `Config` should be defined early in the program and then passed
    down. Now functions like `init_codex()` take fewer individual parameters
    because they can just take a `Config`.
    
    Also, `Config::load()` used to fail silently if `~/.codex/config.toml`
    had a parse error and fell back to the default config. This seemed
    really bad because it wasn't clear why the values in my `config.toml`
    weren't getting picked up. I changed things so that
    `load_with_overrides()` returns `Result<Config>` and verified that the
    various CLIs print a reasonable error if `config.toml` is malformed.
    
    Finally, I also updated the TUI to show which **sandbox** value is being
    used, as we do for other key values like **model** and **approval**.
    This was also a reminder that the various values of `--sandbox` are
    honored on Linux but not macOS today, so I added some TODOs about fixing
    that.
  • fix: write logs to ~/.codex/log instead of /tmp (#669)
    Previously, the Rust TUI was writing log files to `/tmp`, which is
    world-readable and not available on Windows, so that isn't great.
    
    This PR tries to clean things up by adding a function that provides the
    path to the "Codex config dir," e.g., `~/.codex` (though I suppose we
    could support `$CODEX_HOME` to override this?) and then defines other
    paths in terms of the result of `codex_dir()`.
    
    For example, `log_dir()` returns the folder where log files should be
    written which is defined in terms of `codex_dir()`. I updated the TUI to
    use this function. On UNIX, we even go so far as to `chmod 600` the log
    file by default, though as noted in a comment, it's a bit tedious to do
    the equivalent on Windows, so we just let that go for now.
    
    This also changes the default logging level to `info` for `codex_core`
    and `codex_tui` when `RUST_LOG` is not specified. I'm not really sure if
    we should use a more verbose default (it may be helpful when debugging
    user issues), though if so, we should probably also set up log rotation?
  • fix: small fixes so Codex compiles on Windows (#673)
    Small fixes required:
    
    * `ExitStatusExt` differs because UNIX expects exit code to be `i32`
    whereas Windows does `u32`
    * Marking a file "executable only by owner" is a bit more involved on
    Windows. We just do something approximate for now (and add a TODO) to
    get things compiling.
    
    I created this PR on my personal Windows machine and `cargo test` and
    `cargo clippy` succeed. Once this is in, I'll rebase
    https://github.com/openai/codex/pull/665 on top so Windows stays fixed!
  • fix: remove dependency on expanduser crate (#667)
    In putting up https://github.com/openai/codex/pull/665, I discovered
    that the `expanduser` crate does not compile on Windows. Looking into
    it, we do not seem to need it because we were only using it with a value
    that was passed in via a command-line flag, so the shell expands `~` for
    us before we see it, anyway. (I changed the type in `Cli` from `String`
    to `PathBuf`, to boot.)
    
    If we do need this sort of functionality in the future,
    https://docs.rs/shellexpand/latest/shellexpand/fn.tilde.html seems
    promising.
  • fix: flipped the sense of Prompt.store in #642 (#663)
    I got the sense of this wrong in
    https://github.com/openai/codex/pull/642. In that PR, I made
    `--disable-response-storage` work, but broke the default case.
    
    With this fix, both cases work and I think the code is a bit cleaner.
  • [codex-rs] Improve linux sandbox timeouts (#662)
    * Fixes flaking rust unit test
    * Adds explicit sandbox exec timeout handling
  • 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(tui-rs): add support for mousewheel scrolling (#641)
    It is intuitive to try to scroll the conversation history using the
    mouse in the TUI, but prior to this change, we only supported scrolling
    via keyboard events.
    
    This PR enables mouse capture upon initialization (and disables it on
    exit) such that we get `ScrollUp` and `ScrollDown` events in
    `codex-rs/tui/src/app.rs`. I initially mapped each event to scrolling by
    one line, but that felt sluggish. I decided to introduce
    `ScrollEventHelper` so we could debounce scroll events and measure the
    number of scroll events in a 100ms window to determine the "magnitude"
    of the scroll event. I put in a basic heuristic to start, but perhaps
    someone more motivated can play with it over time.
    
    `ScrollEventHelper` takes care of handling the atomic fields and thread
    management to ensure an `AppEvent::Scroll` event is pumped back through
    the event loop at the appropriate time with the accumulated delta.
  • [codex-rs] Reliability pass on networking (#658)
    We currently see a behavior that looks like this:
    ```
    2025-04-25T16:52:24.552789Z  WARN codex_core::codex: stream disconnected - retrying turn (1/10 in 232ms)...
    codex> event: BackgroundEvent { message: "stream error: stream disconnected before completion: Transport error: error decoding response body; retrying 1/10 in 232ms…" }
    2025-04-25T16:52:54.789885Z  WARN codex_core::codex: stream disconnected - retrying turn (2/10 in 418ms)...
    codex> event: BackgroundEvent { message: "stream error: stream disconnected before completion: Transport error: error decoding response body; retrying 2/10 in 418ms…" }
    ```
    
    This PR contains a few different fixes that attempt to resolve/improve
    this:
    1. **Remove overall client timeout.** I think
    [this](https://github.com/openai/codex/pull/658/files#diff-c39945d3c42f29b506ff54b7fa2be0795b06d7ad97f1bf33956f60e3c6f19c19L173)
    is perhaps the big fix -- it looks to me like this was actually timing
    out even if events were still coming through, and that was causing a
    disconnect right in the middle of a healthy stream.
    2. **Cap response sizes.** We were frequently sending MUCH larger
    responses than the upstream typescript `codex`, and that was definitely
    not helping. [Fix
    here](https://github.com/openai/codex/pull/658/files#diff-d792bef59aa3ee8cb0cbad8b176dbfefe451c227ac89919da7c3e536a9d6cdc0R21-R26)
    for that one.
    3. **Much higher idle timeout.** Our idle timeout value was much lower
    than typescript.
    4. **Sub-linear backoff.** We were much too aggressively backing off,
    [this](https://github.com/openai/codex/pull/658/files#diff-5d5959b95c6239e6188516da5c6b7eb78154cd9cfedfb9f753d30a7b6d6b8b06R30-R33)
    makes it sub-exponential but maintains the jitter and such.
    
    I was seeing that `stream error: stream disconnected` behavior
    constantly, and anecdotally I can no longer reproduce. It feels much
    snappier.
  • feat: introduce codex_execpolicy crate for defining "safe" commands (#634)
    As described in detail in `codex-rs/execpolicy/README.md` introduced in
    this PR, `execpolicy` is a tool that lets you define a set of _patterns_
    used to match [`execv(3)`](https://linux.die.net/man/3/execv)
    invocations. When a pattern is matched, `execpolicy` returns the parsed
    version in a structured form that is amenable to static analysis.
    
    The primary use case is to define patterns match commands that should be
    auto-approved by a tool such as Codex. This supports a richer pattern
    matching mechanism that the sort of prefix-matching we have done to
    date, e.g.:
    
    
    https://github.com/openai/codex/blob/5e40d9d2211737f46136610497bcd9a8271009e0/codex-cli/src/approvals.ts#L333-L354
    
    Note we are still playing with the API and the `system_path` option in
    particular still needs some work.
  • [codex-rs] More fine-grained sandbox flag support on Linux (#632)
    ##### What/Why
    This PR makes it so that in Linux we actually respect the different
    types of `--sandbox` flag, such that users can apply network and
    filesystem restrictions in combination (currently the only supported
    behavior), or just pick one or the other.
    
    We should add similar support for OSX in a future PR.
    
    ##### Testing
    From Linux devbox, updated tests to use more specific flags:
    ```
    test linux::tests_linux::sandbox_blocks_ping ... ok
    test linux::tests_linux::sandbox_blocks_getent ... ok
    test linux::tests_linux::test_root_read ... ok
    test linux::tests_linux::test_dev_null_write ... ok
    test linux::tests_linux::sandbox_blocks_dev_tcp_redirection ... ok
    test linux::tests_linux::sandbox_blocks_ssh ... ok
    test linux::tests_linux::test_writable_root ... ok
    test linux::tests_linux::sandbox_blocks_curl ... ok
    test linux::tests_linux::sandbox_blocks_wget ... ok
    test linux::tests_linux::sandbox_blocks_nc ... ok
    test linux::tests_linux::test_root_write - should panic ... ok
    ```
    
    ##### Todo
    - [ ] Add negative tests (e.g. confirm you can hit the network if you
    configure filesystem only restrictions)
  • 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.