Commit Graph

20 Commits

  • [exec] Fix exec sandbox arg (#2034)
    ## Summary
    From codex-cli 馃榿 
    `-s/--sandbox` now correctly affects sandbox mode.
    
    What changed
    - In `codex-rs/exec/src/cli.rs`:
    - Added `value_enum` to the `--sandbox` flag so Clap parses enum values
    into `
    SandboxModeCliArg`.
    - This ensures values like `-s read-only`, `-s workspace-write`, and `-s
    dange
    r-full-access` are recognized and propagated.
    
    Why this fixes it
    - The enum already derives `ValueEnum`, but without `#[arg(value_enum)]`
    Clap ma
    y not map the string into the enum, leaving the option ineffective at
    runtime. W
    ith `value_enum`, `sandbox_mode` is parsed and then converted to
    `SandboxMode` i
    n `run_main`, which feeds into `ConfigOverrides` and ultimately into the
    effecti
    ve `sandbox_policy`.
  • Introduce --oss flag to use gpt-oss models (#1848)
    This adds support for easily running Codex backed by a local Ollama
    instance running our new open source models. See
    https://github.com/openai/gpt-oss for details.
    
    If you pass in `--oss` you'll be prompted to install/launch ollama, and
    it will automatically download the 20b model and attempt to use it.
    
    We'll likely want to expand this with some options later to make the
    experience smoother for users who can't run the 20b or want to run the
    120b.
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • feat: add --json flag to codex exec (#1603)
    This is designed to facilitate programmatic use of Codex in a more
    lightweight way than using `codex mcp`.
    
    Passing `--json` to `codex exec` will print each event as a line of JSON
    to stdout. Note that it does not print the individual tokens as they are
    streamed, only full messages, as this is aimed at programmatic use
    rather than to power UI.
    
    <img width="1348" height="1307" alt="image"
    src="https://github.com/user-attachments/assets/fc7908de-b78d-46e4-a6ff-c85de28415c7"
    />
    
    I changed the existing `EventProcessor` into a trait and moved the
    implementation to `EventProcessorWithHumanOutput`. Then I introduced an
    alternative implementation, `EventProcessorWithJsonOutput`. The `--json`
    flag determines which implementation to use.
  • 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.
  • feat: add --dangerously-bypass-approvals-and-sandbox (#1384)
    This PR reworks `assess_command_safety()` so that the combination of
    `AskForApproval::Never` and `SandboxPolicy::DangerFullAccess` ensures
    that commands are run without _any_ sandbox and the user should never be
    prompted. In turn, it adds support for a new
    `--dangerously-bypass-approvals-and-sandbox` flag (that cannot be used
    with `--approval-policy` or `--full-auto`) that sets both of those
    options.
    
    Fixes https://github.com/openai/codex/issues/1254
  • chore: improve docstring for --full-auto (#1379)
    Reference `-c sandbox.mode=workspace-write` in the docstring and users
    can read the config docs for `sandbox` for more information.
  • feat: redesign sandbox config (#1373)
    This is a major redesign of how sandbox configuration works and aims to
    fix https://github.com/openai/codex/issues/1248. Specifically, it
    replaces `sandbox_permissions` in `config.toml` (and the
    `-s`/`--sandbox-permission` CLI flags) with a "table" with effectively
    three variants:
    
    ```toml
    # Safest option: full disk is read-only, but writes and network access are disallowed.
    [sandbox]
    mode = "read-only"
    
    # The cwd of the Codex task is writable, as well as $TMPDIR on macOS.
    # writable_roots can be used to specify additional writable folders.
    [sandbox]
    mode = "workspace-write"
    writable_roots = []  # Optional, defaults to the empty list.
    network_access = false  # Optional, defaults to false.
    
    # Disable sandboxing: use at your own risk!!!
    [sandbox]
    mode = "danger-full-access"
    ```
    
    This should make sandboxing easier to reason about. While we have
    dropped support for `-s`, the way it works now is:
    
    - no flags => `read-only`
    - `--full-auto` => `workspace-write`
    - currently, there is no way to specify `danger-full-access` via a CLI
    flag, but we will revisit that as part of
    https://github.com/openai/codex/issues/1254
    
    Outstanding issue:
    
    - As noted in the `TODO` on `SandboxPolicy::is_unrestricted()`, we are
    still conflating sandbox preferences with approval preferences in that
    case, which needs to be cleaned up.
  • feat: for codex exec, if PROMPT is not specified, read from stdin if not a TTY (#1178)
    This attempts to make `codex exec` more flexible in how the prompt can
    be passed:
    
    * as before, it can be passed as a single string argument
    * if `-` is passed as the value, the prompt is read from stdin
    * if no argument is passed _and stdin is a tty_, prints a warning to
    stderr that no prompt was specified an exits non-zero.
    * if no argument is passed _and stdin is NOT a tty_, prints `Reading
    prompt from stdin...` to stderr to let the user know that Codex will
    wait until it reads EOF from stdin to proceed. (You can repro this case
    by doing `yes | just exec` since stdin is not a TTY in that case but it
    also never reaches EOF).
  • feat: add support for -c/--config to override individual config items (#1137)
    This PR introduces support for `-c`/`--config` so users can override
    individual config values on the command line using `--config
    name=value`. Example:
    
    ```
    codex --config model=o4-mini
    ```
    
    Making it possible to set arbitrary config values on the command line
    results in a more flexible configuration scheme and makes it easier to
    provide single-line examples that can be copy-pasted from documentation.
    
    Effectively, it means there are four levels of configuration for some
    values:
    
    - Default value (e.g., `model` currently defaults to `o4-mini`)
    - Value in `config.toml` (e.g., user could override the default to be
    `model = "o3"` in their `config.toml`)
    - Specifying `-c` or `--config` to override `model` (e.g., user can
    include `-c model=o3` in their list of args to Codex)
    - If available, a config-specific flag can be used, which takes
    precedence over `-c` (e.g., user can specify `--model o3` in their list
    of args to Codex)
    
    Now that it is possible to specify anything that could be configured in
    `config.toml` on the command line using `-c`, we do not need to have a
    custom flag for every possible config option (which can clutter the
    output of `--help`). To that end, as part of this PR, we drop support
    for the `--disable-response-storage` flag, as users can now specify `-c
    disable_response_storage=true` to get the equivalent functionality.
    
    Under the hood, this works by loading the `config.toml` into a
    `toml::Value`. Then for each `key=value`, we create a small synthetic
    TOML file with `value` so that we can run the TOML parser to get the
    equivalent `toml::Value`. We then parse `key` to determine the point in
    the original `toml::Value` to do the insert/replace. Once all of the
    overrides from `-c` args have been applied, the `toml::Value` is
    deserialized into a `ConfigToml` and then the `ConfigOverrides` are
    applied, as before.
  • feat: experimental --output-last-message flag to exec subcommand (#1037)
    This introduces an experimental `--output-last-message` flag that can be
    used to identify a file where the final message from the agent will be
    written. Two use cases:
    
    - Ultimately, we will likely add a `--quiet` option to `exec`, but even
    if the user does not want any output written to the terminal, they
    probably want to know what the agent did. Writing the output to a file
    makes it possible to get that information in a clean way.
    - Relatedly, when using `exec` in CI, it is easier to review the
    transcript written "normally," (i.e., not as JSON or something with
    extra escapes), but getting programmatic access to the last message is
    likely helpful, so writing the last message to a file gets the best of
    both worlds.
    
    I am calling this "experimental" because it is possible that we are
    overfitting and will want a more general solution to this problem that
    would justify removing this flag.
  • 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`.
  • chore: introduce codex-common crate (#843)
    I started this PR because I wanted to share the `format_duration()`
    utility function in `codex-rs/exec/src/event_processor.rs` with the TUI.
    The question was: where to put it?
    
    `core` should have as few dependencies as possible, so moving it there
    would introduce a dependency on `chrono`, which seemed undesirable.
    `core` already had this `cli` feature to deal with a similar situation
    around sharing common utility functions, so I decided to:
    
    * make `core` feature-free
    * introduce `common`
    * `common` can have as many "special interest" features as it needs,
    each of which can declare their own deps
    * the first two features of common are `cli` and `elapsed`
    
    In practice, this meant updating a number of `Cargo.toml` files,
    replacing this line:
    
    ```toml
    codex-core = { path = "../core", features = ["cli"] }
    ```
    
    with these:
    
    ```toml
    codex-core = { path = "../core" }
    codex-common = { path = "../common", features = ["cli"] }
    ```
    
    Moving `format_duration()` into its own file gave it some "breathing
    room" to add a unit test, so I had Codex generate some tests and new
    support for durations over 1 minute.
  • feat: 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`.
  • 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: 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.
  • feat: add ZDR support to Rust implementation (#642)
    This adds support for the `--disable-response-storage` flag across our
    multiple Rust CLIs to support customers who have opted into Zero-Data
    Retention (ZDR). The analogous changes to the TypeScript CLI were:
    
    * https://github.com/openai/codex/pull/481
    * https://github.com/openai/codex/pull/543
    
    For a client using ZDR, `previous_response_id` will never be available,
    so the `input` field of an API request must include the full transcript
    of the conversation thus far. As such, this PR changes the type of
    `Prompt.input` from `Vec<ResponseInputItem>` to `Vec<ResponseItem>`.
    
    Practically speaking, `ResponseItem` was effectively a "superset" of
    `ResponseInputItem` already. The main difference for us is that
    `ResponseItem` includes the `FunctionCall` variant that we have to
    include as part of the conversation history in the ZDR case.
    
    Another key change in this PR is modifying `try_run_turn()` so that it
    returns the `Vec<ResponseItem>` for the turn in addition to the
    `Vec<ResponseInputItem>` produced by `try_run_turn()`. This is because
    the caller of `run_turn()` needs to record the `Vec<ResponseItem>` when
    ZDR is enabled.
    
    To that end, this PR introduces `ZdrTranscript` (and adds
    `zdr_transcript: Option<ZdrTranscript>` to `struct State` in `codex.rs`)
    to take responsibility for maintaining the conversation transcript in
    the ZDR case.
  • feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
    As stated in `codex-rs/README.md`:
    
    Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
    run it. For a number of users, this runtime requirement inhibits
    adoption: they would be better served by a standalone executable. As
    maintainers, we want Codex to run efficiently in a wide range of
    environments with minimal overhead. We also want to take advantage of
    operating system-specific APIs to provide better sandboxing, where
    possible.
    
    To that end, we are moving forward with a Rust implementation of Codex
    CLI contained in this folder, which has the following benefits:
    
    - The CLI compiles to small, standalone, platform-specific binaries.
    - Can make direct, native calls to
    [seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
    [landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
    order to support sandboxing on Linux.
    - No runtime garbage collection, resulting in lower memory consumption
    and better, more predictable performance.
    
    Currently, the Rust implementation is materially behind the TypeScript
    implementation in functionality, so continue to use the TypeScript
    implmentation for the time being. We will publish native executables via
    GitHub Releases as soon as we feel the Rust version is usable.