mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
e444a50cf022b694d689017dbe54e47ca4e11084
20 Commits
-
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.
Michael Bolin ·
2025-07-07 22:31:30 -07:00 -
feat: show number of tokens remaining in UI (#1388)
When using the OpenAI Responses API, we now record the `usage` field for a `"response.completed"` event, which includes metrics about the number of tokens consumed. We also introduce `openai_model_info.rs`, which includes current data about the most common OpenAI models available via the API (specifically `context_window` and `max_output_tokens`). If Codex does not recognize the model, you can set `model_context_window` and `model_max_output_tokens` explicitly in `config.toml`. When then introduce a new event type to `protocol.rs`, `TokenCount`, which includes the `TokenUsage` for the most recent turn. Finally, we update the TUI to record the running sum of tokens used so the percentage of available context window remaining can be reported via the placeholder text for the composer:  We could certainly get much fancier with this (such as reporting the estimated cost of the conversation), but for now, we are just trying to achieve feature parity with the TypeScript CLI. Though arguably this improves upon the TypeScript CLI, as the TypeScript CLI uses heuristics to estimate the number of tokens used rather than using the `usage` information directly: https://github.com/openai/codex/blob/296996d74e345b1b05d8c3451a06ace21c5ada96/codex-cli/src/utils/approximate-tokens-used.ts#L3-L16 Fixes https://github.com/openai/codex/issues/1242
Michael Bolin ·
2025-06-25 23:31:11 -07:00 -
chore: rename AskForApproval::UnlessAllowListed to AskForApproval::UnlessTrusted (#1385)
We could just rename to `Untrusted` instead of `UnlessTrusted`, but I think `AskForApproval::UnlessTrusted` reads a bit better.
Michael Bolin ·
2025-06-25 12:26:13 -07:00 -
chore: rename unless-allow-listed to untrusted (#1378)
For the `approval_policy` config option, renames `unless-allow-listed` to `untrusted`. In general, when it comes to exec'ing commands, I think "trusted" is a more accurate term than "safe." Also drops the `AskForApproval::AutoEdit` variant, as we were not really making use of it, anyway. Fixes https://github.com/openai/codex/issues/1250. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/1378). * #1379 * __->__ #1378
Michael Bolin ·
2025-06-24 22:19:21 -07:00 -
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.
Michael Bolin ·
2025-06-24 16:59:47 -07:00 -
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.
Michael Bolin ·
2025-05-27 23:11:44 -07:00 -
fix: overhaul how we spawn commands under seccomp/landlock on Linux (#1086)
Historically, we spawned the Seatbelt and Landlock sandboxes in substantially different ways: For **Seatbelt**, we would run `/usr/bin/sandbox-exec` with our policy specified as an arg followed by the original command: https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/core/src/exec.rs#L147-L219 For **Landlock/Seccomp**, we would do `tokio::runtime::Builder::new_current_thread()`, _invoke Landlock/Seccomp APIs to modify the permissions of that new thread_, and then spawn the command: https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/core/src/exec_linux.rs#L28-L49 While it is neat that Landlock/Seccomp supports applying a policy to only one thread without having to apply it to the entire process, it requires us to maintain two different codepaths and is a bit harder to reason about. The tipping point was https://github.com/openai/codex/pull/1061, in which we had to start building up the `env` in an unexpected way for the existing Landlock/Seccomp approach to continue to work. This PR overhauls things so that we do similar things for Mac and Linux. It turned out that we were already building our own "helper binary" comparable to Mac's `sandbox-exec` as part of the `cli` crate: https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/cli/Cargo.toml#L10-L12 We originally created this to build a small binary to include with the Node.js version of the Codex CLI to provide support for Linux sandboxing. Though the sticky bit is that, at this point, we still want to deploy the Rust version of Codex as a single, standalone binary rather than a CLI and a supporting sandboxing binary. To satisfy this goal, we use "the arg0 trick," in which we: * use `std::env::current_exe()` to get the path to the CLI that is currently running * use the CLI as the `program` for the `Command` * set `"codex-linux-sandbox"` as arg0 for the `Command` A CLI that supports sandboxing should check arg0 at the start of the program. If it is `"codex-linux-sandbox"`, it must invoke `codex_linux_sandbox::run_main()`, which runs the CLI as if it were `codex-linux-sandbox`. When acting as `codex-linux-sandbox`, we make the appropriate Landlock/Seccomp API calls and then use `execvp(3)` to spawn the original command, so do _replace_ the process rather than spawn a subprocess. Incidentally, we do this before starting the Tokio runtime, so the process should only have one thread when `execvp(3)` is called. Because the `core` crate that needs to spawn the Linux sandboxing is not a CLI in its own right, this means that every CLI that includes `core` and relies on this behavior has to (1) implement it and (2) provide the path to the sandboxing executable. While the path is almost always `std::env::current_exe()`, we needed to make this configurable for integration tests, so `Config` now has a `codex_linux_sandbox_exe: Option<PathBuf>` property to facilitate threading this through, introduced in https://github.com/openai/codex/pull/1089. This common pattern is now captured in `codex_linux_sandbox::run_with_sandbox()` and all of the `main.rs` functions that should use it have been updated as part of this PR. The `codex-linux-sandbox` crate added to the Cargo workspace as part of this PR now has the bulk of the Landlock/Seccomp logic, which makes `core` a bit simpler. Indeed, `core/src/exec_linux.rs` and `core/src/landlock.rs` were removed/ported as part of this PR. I also moved the unit tests for this code into an integration test, `linux-sandbox/tests/landlock.rs`, in which I use `env!("CARGO_BIN_EXE_codex-linux-sandbox")` as the value for `codex_linux_sandbox_exe` since `std::env::current_exe()` is not appropriate in that case.
Michael Bolin ·
2025-05-23 11:37:07 -07:00 -
feat: add
codex_linux_sandbox_exe: Option<PathBuf>field to Config (#1089)https://github.com/openai/codex/pull/1086 is a work-in-progress to make Linux sandboxing work more like Seatbelt where, for the command we want to sandbox, we build up the command and then hand it, and some sandbox configuration flags, to another command to set up the sandbox and then run it. In the case of Seatbelt, macOS provides this helper binary and provides it at `/usr/bin/sandbox-exec`. For Linux, we have to build our own and pass it through (which is what #1086 does), so this makes the new `codex_linux_sandbox_exe` available on `Config` so that it will later be available in `exec.rs` when we need it in #1086.
Michael Bolin ·
2025-05-22 21:52:28 -07:00 -
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.
Michael Bolin ·
2025-05-19 16:08:18 -07:00 -
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.
Michael Bolin ·
2025-05-15 16:26:23 -07:00 -
chore: handle all cases for EventMsg (#936)
For now, this removes the `#[non_exhaustive]` directive on `EventMsg` so that we are forced to handle all `EventMsg` by default. (We may revisit this if/when we publish `core/` as a `lib` crate.) For now, it is helpful to have this as a forcing function because we have effectively two UIs (`tui` and `exec`) and usually when we add a new variant to `EventMsg`, we want to be sure that we update both.
Michael Bolin ·
2025-05-14 13:36:43 -07:00 -
feat: add mcp subcommand to CLI to run Codex as an MCP server (#934)
Previously, running Codex as an MCP server required a standalone binary in our Cargo workspace, but this PR makes it available as a subcommand (`mcp`) of the main CLI. Ran this with: ``` RUST_LOG=debug npx @modelcontextprotocol/inspector cargo run --bin codex -- mcp ``` and verified it worked as expected in the inspector at `http://127.0.0.1:6274/`.
Michael Bolin ·
2025-05-14 13:15:41 -07:00 -
fix: change EventMsg enum so every variant takes a single struct (#925)
https://github.com/openai/codex/pull/922 did this for the `SessionConfigured` enum variant, and I think it is generally helpful to be able to work with the values as each enum variant as their own type, so this converts the remaining variants and updates all of the callsites. Added a simple unit test to verify that the JSON-serialized version of `Event` does not have any unexpected nesting.
Michael Bolin ·
2025-05-13 20:44:42 -07:00 -
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`.
Michael Bolin ·
2025-05-13 16:52:52 -07:00 -
Disallow expect via lints (#865)
Adds `expect()` as a denied lint. Same deal applies with `unwrap()` where we now need to put `#[expect(...` on ones that we legit want. Took care to enable `expect()` in test contexts. # Tests ``` cargo fmt cargo clippy --all-features --all-targets --no-deps -- -D warnings cargo test ```
jcoens-openai ·
2025-05-12 08:45:46 -07:00 -
Workspace lints and disallow unwrap (#855)
Sets submodules to use workspace lints. Added denying unwrap as a workspace level lint, which found a couple of cases where we could have propagated errors. Also manually labeled ones that were fine by my eye.
jcoens-openai ·
2025-05-08 09:46:18 -07:00 -
feat: read
model_providerandmodel_providersfrom config.toml (#853)This is the first step in supporting other model providers in the Rust CLI. Specifically, this PR adds support for the new entries in `Config` and `ConfigOverrides` to specify a `ModelProviderInfo`, which is the basic config needed for an LLM provider. This PR does not get us all the way there yet because `client.rs` still categorically appends `/responses` to the URL and expects the endpoint to support the OpenAI Responses API. Will fix that next!
Michael Bolin ·
2025-05-07 17:38:28 -07:00 -
Update cargo to 2024 edition (#842)
Some effects of this change: - New formatting changes across many files. No functionality changes should occur from that. - Calls to `set_env` are considered unsafe, since this only happens in tests we wrap them in `unsafe` blocks
jcoens-openai ·
2025-05-07 08:37:48 -07:00 -
feat: make Codex available as a tool when running it as an MCP server (#811)
This PR replaces the placeholder `"echo"` tool call in the MCP server with a `"codex"` tool that calls Codex. Events such as `ExecApprovalRequest` and `ApplyPatchApprovalRequest` are not handled properly yet, but I have `approval_policy = "never"` set in my `~/.codex/config.toml` such that those codepaths are not exercised. The schema for this MPC tool is defined by a new `CodexToolCallParam` struct introduced in this PR. It is fairly similar to `ConfigOverrides`, as the param is used to help create the `Config` used to start the Codex session, though it also includes the `prompt` used to kick off the session. This PR also introduces the use of the third-party `schemars` crate to generate the JSON schema, which is verified in the `verify_codex_tool_json_schema()` unit test. Events that are dispatched during the Codex session are sent back to the MCP client as MCP notifications. This gives the client a way to monitor progress as the tool call itself may take minutes to complete depending on the complexity of the task requested by the user. In the video below, I launched the server via: ```shell mcp-server$ RUST_LOG=debug npx @modelcontextprotocol/inspector cargo run -- ``` In the video, you can see the flow of: * requesting the list of tools * choosing the **codex** tool * entering a value for **prompt** and then making the tool call Note that I left the other fields blank because when unspecified, the values in my `~/.codex/config.toml` were used: https://github.com/user-attachments/assets/1975058c-b004-43ef-8c8d-800a953b8192 Note that while using the inspector, I did run into https://github.com/modelcontextprotocol/inspector/issues/293, though the tip about ensuring I had only one instance of the **MCP Inspector** tab open in my browser seemed to fix things.
Michael Bolin ·
2025-05-05 07:16:19 -07:00 -
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:  I also had to make a small fix to `mcp-types`, adding `#[serde(untagged)]` to a number of `enum`s.
Michael Bolin ·
2025-05-02 17:25:58 -07:00