Commit Graph

3718 Commits

  • chore: remove responses-api-proxy from the multitool (#4404)
    This removes the `codex responses-api-proxy` subcommand in favor of
    running it as a standalone CLI.
    
    As part of this change, we:
    
    - remove the dependency on `tokio`/`async/await` as well as `codex_arg0`
    - introduce the use of `pre_main_hardening()` so `CODEX_SECURE_MODE=1`
    is not required
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/4404).
    * #4406
    * __->__ #4404
    * #4403
  • Improve update nudge (#4405)
    Makes the update nudge larger and adds a link to see latest release:
    
    <img width="542" height="337" alt="Screenshot 2025-09-28 at 11 19 05 AM"
    src="https://github.com/user-attachments/assets/1facce96-72f0-4a97-910a-df8b5b8b07af"
    />
  • fix: remove default timeout of 30s in the proxy (#4336)
    This is likely the reason that I saw some conversations "freeze up" when
    using the proxy.
    
    Note the client in `core` does not specify a timeout when making
    requests to the Responses API, so the proxy should not, either.
  • tui: separator above final agent message (#4324)
    Adds a separator line before the final agent message
    
    <img width="1011" height="884" alt="Screenshot 2025-09-26 at 4 55 01 PM"
    src="https://github.com/user-attachments/assets/7c91adbf-6035-4578-8b88-a6921f11bcbc"
    />
  • [MCP] Add experimental support for streamable HTTP MCP servers (#4317)
    This PR adds support for streamable HTTP MCP servers when the
    `experimental_use_rmcp_client` is enabled.
    
    To set one up, simply add a new mcp server config with the url:
    ```
    [mcp_servers.figma]
    url = "http://127.0.0.1:3845/mcp"
    ```
    
    It also supports an optional `bearer_token` which will be provided in an
    authorization header. The full oauth flow is not supported yet.
    
    The config parsing will throw if it detects that the user mixed and
    matched config fields (like command + bearer token or url + env).
    
    The best way to review it is to review `core/src` and then
    `rmcp-client/src/rmcp_client.rs` first. The rest is tests and
    propagating the `Transport` struct around the codebase.
    
    Example with the Figma MCP:
    <img width="5084" height="1614" alt="CleanShot 2025-09-26 at 13 35 40"
    src="https://github.com/user-attachments/assets/eaf2771e-df3e-4300-816b-184d7dec5a28"
    />
  • update composer + user message styling (#4240)
    Changes:
    
    - the composer and user messages now have a colored background that
    stretches the entire width of the terminal.
    - the prompt character was changed from a cyan `▌` to a bold `›`.
    - the "working" shimmer now follows the "dark gray" color of the
    terminal, better matching the terminal's color scheme
    
    | Terminal + Background        | Screenshot |
    |------------------------------|------------|
    | iTerm with dark bg | <img width="810" height="641" alt="Screenshot
    2025-09-25 at 11 44 52 AM"
    src="https://github.com/user-attachments/assets/1317e579-64a9-4785-93e6-98b0258f5d92"
    /> |
    | iTerm with light bg | <img width="845" height="540" alt="Screenshot
    2025-09-25 at 11 46 29 AM"
    src="https://github.com/user-attachments/assets/e671d490-c747-4460-af0b-3f8d7f7a6b8e"
    /> |
    | iTerm with color bg | <img width="825" height="564" alt="Screenshot
    2025-09-25 at 11 47 12 AM"
    src="https://github.com/user-attachments/assets/141cda1b-1164-41d5-87da-3be11e6a3063"
    /> |
    | Terminal.app with dark bg | <img width="577" height="367"
    alt="Screenshot 2025-09-25 at 11 45 22 AM"
    src="https://github.com/user-attachments/assets/93fc4781-99f7-4ee7-9c8e-3db3cd854fe5"
    /> |
    | Terminal.app with light bg | <img width="577" height="367"
    alt="Screenshot 2025-09-25 at 11 46 04 AM"
    src="https://github.com/user-attachments/assets/19bf6a3c-91e0-447b-9667-b8033f512219"
    /> |
    | Terminal.app with color bg | <img width="577" height="367"
    alt="Screenshot 2025-09-25 at 11 45 50 AM"
    src="https://github.com/user-attachments/assets/dd7c4b5b-342e-4028-8140-f4e65752bd0b"
    /> |
  • Add turn started/completed events and correct exit code on error (#4309)
    Adds new event for session completed that includes usage. Also ensures
    we return 1 on failures.
    ```
    {
      "type": "session.created",
      "session_id": "019987a7-93e7-7b20-9e05-e90060e411ea"
    }
    {
      "type": "turn.started"
    }
    ...
    {
      "type": "turn.completed",
      "usage": {
        "input_tokens": 78913,
        "cached_input_tokens": 65280,
        "output_tokens": 1099
      }
    }
    ```
  • reject dangerous commands for AskForApproval::Never (#4307)
    If we detect a dangerous command but approval_policy is Never, simply
    reject the command.
  • /status followup (#4304)
    - Render `send a message to load usage data` in the beginning of the
    session
    - Render `data not available yet` if received no rate limits 
    - nit case
    - Deleted stall snapshots that were moved to
    `codex-rs/tui/src/status/snapshots`
  • [MCP] Introduce an experimental official rust sdk based mcp client (#4252)
    The [official Rust
    SDK](https://github.com/modelcontextprotocol/rust-sdk/tree/57fc428c578a1a3fe851ee0838bf068bda120eb3)
    has come a long way since we first started our mcp client implementation
    5 months ago and, today, it is much more complete than our own
    stdio-only implementation.
    
    This PR introduces a new config flag `experimental_use_rmcp_client`
    which will use a new mcp client powered by the sdk instead of our own.
    
    To keep this PR simple, I've only implemented the same stdio MCP
    functionality that we had but will expand on it with future PRs.
    
    ---------
    
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • Add todo-list tool support (#4255)
    Adds a 1-per-turn todo-list item and item.updated event
    
    ```jsonl
    {"type":"item.started","item":{"id":"item_6","item_type":"todo_list","items":[{"text":"Record initial two-step plan  now","completed":false},{"text":"Update progress to next step","completed":false}]}}
    {"type":"item.updated","item":{"id":"item_6","item_type":"todo_list","items":[{"text":"Record initial two-step plan  now","completed":true},{"text":"Update progress to next step","completed":false}]}}
    {"type":"item.completed","item":{"id":"item_6","item_type":"todo_list","items":[{"text":"Record initial two-step plan  now","completed":true},{"text":"Update progress to next step","completed":false}]}}
    ```
  • feat: introduce responses-api-proxy (#4246)
    Details are in `responses-api-proxy/README.md`, but the key contribution
    of this PR is a new subcommand, `codex responses-api-proxy`, which reads
    the auth token for use with the OpenAI Responses API from `stdin` at
    startup and then proxies `POST` requests to `/v1/responses` over to
    `https://api.openai.com/v1/responses`, injecting the auth token as part
    of the `Authorization` header.
    
    The expectation is that `codex responses-api-proxy` is launched by a
    privileged user who has access to the auth token so that it can be used
    by unprivileged users of the Codex CLI on the same host.
    
    If the client only has one user account with `sudo`, one option is to:
    
    - run `sudo codex responses-api-proxy --http-shutdown --server-info
    /tmp/server-info.json` to start the server
    - record the port written to `/tmp/server-info.json`
    - relinquish their `sudo` privileges (which is irreversible!) like so:
    
    ```
    sudo deluser $USER sudo || sudo gpasswd -d $USER sudo || true
    ```
    
    - use `codex` with the proxy (see `README.md`)
    - when done, make a `GET` request to the server using the `PORT` from
    `server-info.json` to shut it down:
    
    ```shell
    curl --fail --silent --show-error "http://127.0.0.1:$PORT/shutdown"
    ```
    
    To protect the auth token, we:
    
    - allocate a 1024 byte buffer on the stack and write `"Bearer "` into it
    to start
    - we then read from `stdin`, copying to the contents into the buffer
    after the prefix
    - after verifying the input looks good, we create a `String` from that
    buffer (so the data is now on the heap)
    - we zero out the stack-allocated buffer using
    https://crates.io/crates/zeroize so it is not optimized away by the
    compiler
    - we invoke `.leak()` on the `String` so we can treat its contents as a
    `&'static str`, as it will live for the rest of the processs
    - on UNIX, we `mlock(2)` the memory backing the `&'static str`
    - when using the `&'static str` when building an HTTP request, we use
    `HeaderValue::from_static()` to avoid copying the `&str`
    - we also invoke `.set_sensitive(true)` on the `HeaderValue`, which in
    theory indicates to other parts of the HTTP stack that the header should
    be treated with "special care" to avoid leakage:
    
    
    https://github.com/hyperium/http/blob/439d1c50d71e3be3204b6c4a1bf2255ed78e1f93/src/header/value.rs#L346-L376
  • fix: token usage for compaction (#4281)
    Emit token usage update when draining compaction
  • Show exec output on success with trimmed display (#4113)
    - Refactor Exec Cell into its own module
    - update exec command rendering to inline the first command line
    - limit continuation lines
    - always show trimmed output
  • Move approvals to use ListSelectionView (#4275)
    Unify selection menus:
    - Move approvals to the vertical menu `ListSelectionView`
    - Add header section to `ListSelectionView`
    
    <img width="502" height="214" alt="image"
    src="https://github.com/user-attachments/assets/f4b43ddf-3549-403c-ad9e-a523688714e4"
    />
    
    <img width="748" height="214" alt="image"
    src="https://github.com/user-attachments/assets/f94ac7b5-dc94-4dc0-a1df-7a8e3ba2453b"
    />
    
    ---------
    
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • Refactor the footer logic to a new file (#4259)
    This will help us have more control over the footer
    
    ---------
    
    Co-authored-by: pakrym-oai <pakrym@openai.com>
  • ref: state - 2 (#4229)
    Extracting tasks in a module and start abstraction behind a Trait (more
    to come on this but each task will be tackled in a dedicated PR)
    The goal was to drop the ActiveTask and to have a (potentially) set of
    tasks during each turn
  • core: add potentially dangerous command check (#4211)
    Certain shell commands are potentially dangerous, and we want to check
    for them.
    Unless the user has explicitly approved a command, we will *always* ask
    them for approval
    when one of these commands is encountered, regardless of whether they
    are in a sandbox, or what their approval policy is.
    
    The first (of probably many) such examples is `git reset --hard`. We
    will be conservative and check for any `git reset`
  • Actually mount sse once (#4264)
    Mock server was responding with the same result many times.
  • Add codex exec testing helpers (#4254)
    Add a shortcut to create working directories and run codex exec with
    fake server.
  • Fixed login failure with API key in IDE extension when a .codex directory doesn't exist (#4258)
    This addresses bug #4092
    
    Testing:
    * Confirmed error occurs prior to fix if logging in using API key and no
    `~/.codex` directory exists
    * Confirmed after fix that `~/.codex` directory is properly created and
    error doesn't occur
  • fix typo in sandbox doc (#4256)
    just fixes a simple typo I noticed.
  • [codex exec] Add item.started and support it for command execution (#4250)
    Adds a new `item.started` event to `codex exec` and implements it for
    command_execution item type.
    
    ```jsonl
    {"type":"session.created","session_id":"019982d1-75f0-7920-b051-e0d3731a5ed8"}
    {"type":"item.completed","item":{"id":"item_0","item_type":"reasoning","text":"**Executing commands securely**\n\nI'm thinking about how the default harness typically uses \"bash -lc,\" while historically \"bash\" is what we've been using. The command should be executed as a string in our CLI, so using \"bash -lc 'echo hello'\" is optimal but calling \"echo hello\" directly feels safer. The sandbox makes sure environment variables like CODEX_SANDBOX_NETWORK_DISABLED=1 are set, so I won't ask for approval. I just need to run \"echo hello\" and correctly present the output."}}
    {"type":"item.completed","item":{"id":"item_1","item_type":"reasoning","text":"**Preparing for tool calls**\n\nI realize that I need to include a preamble before making any tool calls. So, I'll first state the preamble in the commentary channel, then proceed with the tool call. After that, I need to present the final message along with the output. It's possible that the CLI will show the output inline, but I must ensure that I present the result clearly regardless. Let's move forward and get this organized!"}}
    {"type":"item.completed","item":{"id":"item_2","item_type":"assistant_message","text":"Running `echo` to confirm shell access and print output."}}
    {"type":"item.started","item":{"id":"item_3","item_type":"command_execution","command":"bash -lc echo hello","aggregated_output":"","exit_code":null,"status":"in_progress"}}
    {"type":"item.completed","item":{"id":"item_3","item_type":"command_execution","command":"bash -lc echo hello","aggregated_output":"hello\n","exit_code":0,"status":"completed"}}
    {"type":"item.completed","item":{"id":"item_4","item_type":"assistant_message","text":"hello"}}
    ```
  • fix (#4251)
    # External (non-OpenAI) Pull Request Requirements
    
    Before opening this Pull Request, please read the dedicated
    "Contributing" markdown file or your PR may be closed:
    https://github.com/openai/codex/blob/main/docs/contributing.md
    
    If your PR conforms to our contribution guidelines, replace this text
    with a detailed and high quality description of your changes.
  • fix bug when resizing to a smaller width (#4248)
    The composer and key hint lines were using line styles, causing ratatui
    to print spaces all the way to the right side of the terminal. this
    meant that resizing the terminal to be narrower would result in
    rewrapping those lines, causing the bottom area to rerender and push all
    content up.
    
    Before
    
    
    https://github.com/user-attachments/assets/8b14555a-1fc5-4f78-8df7-1410ee25e07a
    
    After
    
    
    https://github.com/user-attachments/assets/707645ab-89c7-4c7f-b556-02f53cef8a2f
  • make tests pass cleanly in sandbox (#4067)
    This changes the reqwest client used in tests to be sandbox-friendly,
    and skips a bunch of other tests that don't work inside the
    sandbox/without network.
  • Fix error message (#4204)
    Co-authored-by: Ahmed Ibrahim <aibrahim@openai.com>
  • Add explicit codex exec events (#4177)
    This pull request add a new experimental format of JSON output.
    
    You can try it using `codex exec --experimental-json`.
    
    Design takes a lot of inspiration from Responses API items and stream
    format.
    
    # Session and items
    Each invocation of `codex exec` starts or resumes a session. 
    
    Session contains multiple high-level item types:
    1. Assistant message 
    2. Assistant thinking 
    3. Command execution 
    4. File changes
    5. To-do lists
    6. etc.
    
    # Events 
    Session and items are going through their life cycles which is
    represented by events.
    
    Session is `session.created` or `session.resumed`
    Items are `item.added`, `item.updated`, `item.completed`,
    `item.require_approval` (or other item types like `item.output_delta`
    when we need streaming).
    
    So a typical session can look like:
    
    <details>
    
    ```
    {
      "type": "session.created",
      "session_id": "01997dac-9581-7de3-b6a0-1df8256f2752"
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_0",
        "item_type": "assistant_message",
        "text": "I’ll locate the top-level README and remove its first line. Then I’ll show a quick summary of what changed."
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_1",
        "item_type": "command_execution",
        "command": "bash -lc ls -la | sed -n '1,200p'",
        "aggregated_output": "pyenv: cannot rehash: /Users/pakrym/.pyenv/shims isn't writable\ntotal 192\ndrwxr-xr-x@  33 pakrym  staff   1056 Sep 24 14:36 .\ndrwxr-xr-x   41 pakrym  staff   1312 Sep 24 09:17 ..\n-rw-r--r--@   1 pakrym  staff      6 Jul  9 16:16 .codespellignore\n-rw-r--r--@   1 pakrym  staff    258 Aug 13 09:40 .codespellrc\ndrwxr-xr-x@   5 pakrym  staff    160 Jul 23 08:26 .devcontainer\n-rw-r--r--@   1 pakrym  staff   6148 Jul 22 10:03 .DS_Store\ndrwxr-xr-x@  15 pakrym  staff    480 Sep 24 14:38 .git\ndrwxr-xr-x@  12 pakrym  staff    384 Sep  2 16:00 .github\n-rw-r--r--@   1 pakrym  staff    778 Jul  9 16:16 .gitignore\ndrwxr-xr-x@   3 pakrym  staff     96 Aug 11 09:37 .husky\n-rw-r--r--@   1 pakrym  staff    104 Jul  9 16:16 .npmrc\n-rw-r--r--@   1 pakrym  staff     96 Sep  2 08:52 .prettierignore\n-rw-r--r--@   1 pakrym  staff    170 Jul  9 16:16 .prettierrc.toml\ndrwxr-xr-x@   5 pakrym  staff    160 Sep 14 17:43 .vscode\ndrwxr-xr-x@   2 pakrym  staff     64 Sep 11 11:37 2025-09-11\n-rw-r--r--@   1 pakrym  staff   5505 Sep 18 09:28 AGENTS.md\n-rw-r--r--@   1 pakrym  staff     92 Sep  2 08:52 CHANGELOG.md\n-rw-r--r--@   1 pakrym  staff   1145 Jul  9 16:16 cliff.toml\ndrwxr-xr-x@  11 pakrym  staff    352 Sep 24 13:03 codex-cli\ndrwxr-xr-x@  38 pakrym  staff   1216 Sep 24 14:38 codex-rs\ndrwxr-xr-x@  18 pakrym  staff    576 Sep 23 11:01 docs\n-rw-r--r--@   1 pakrym  staff   2038 Jul  9 16:16 flake.lock\n-rw-r--r--@   1 pakrym  staff   1434 Jul  9 16:16 flake.nix\n-rw-r--r--@   1 pakrym  staff  10926 Jul  9 16:16 LICENSE\ndrwxr-xr-x@ 465 pakrym  staff  14880 Jul 15 07:36 node_modules\n-rw-r--r--@   1 pakrym  staff    242 Aug  5 08:25 NOTICE\n-rw-r--r--@   1 pakrym  staff    578 Aug 14 12:31 package.json\n-rw-r--r--@   1 pakrym  staff    498 Aug 11 09:37 pnpm-lock.yaml\n-rw-r--r--@   1 pakrym  staff     58 Aug 11 09:37 pnpm-workspace.yaml\n-rw-r--r--@   1 pakrym  staff   2402 Jul  9 16:16 PNPM.md\n-rw-r--r--@   1 pakrym  staff   4393 Sep 12 14:36 README.md\ndrwxr-xr-x@   4 pakrym  staff    128 Sep 18 09:28 scripts\ndrwxr-xr-x@   2 pakrym  staff     64 Sep 11 11:34 tmp\n",
        "exit_code": 0,
        "status": "completed"
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_2",
        "item_type": "reasoning",
        "text": "**Reviewing README.md file**\n\nI've located the README.md file at the root, and it’s 4393 bytes. Now, I need to remove the first line, but first, I should check its content to make sure I’m patching it correctly. I’ll use sed to display the first 20 lines. By reviewing those lines, I can determine exactly what needs to be removed before I proceed with the editing. Let's do this carefully!"
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_3",
        "item_type": "command_execution",
        "command": "bash -lc sed -n '1,40p' README.md",
        "aggregated_output": "<h1 align=\"center\">OpenAI Codex CLI</h1>\n\n<p align=\"center\"><code>npm i -g @openai/codex</code><br />or <code>brew install codex</code></p>\n\n<p align=\"center\"><strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.\n</br>\n</br>If you want Codex in your code editor (VS Code, Cursor, Windsurf), <a href=\"https://developers.openai.com/codex/ide\">install in your IDE</a>\n</br>If you are looking for the <em>cloud-based agent</em> from OpenAI, <strong>Codex Web</strong>, go to <a href=\"https://chatgpt.com/codex\">chatgpt.com/codex</a></p>\n\n<p align=\"center\">\n  <img src=\"./.github/codex-cli-splash.png\" alt=\"Codex CLI splash\" width=\"80%\" />\n  </p>\n\n---\n\n## Quickstart\n\n### Installing and running Codex CLI\n\nInstall globally with your preferred package manager. If you use npm:\n\n```shell\nnpm install -g @openai/codex\n```\n\nAlternatively, if you use Homebrew:\n\n```shell\nbrew install codex\n```\n\nThen simply run `codex` to get started:\n\n```shell\ncodex\n```\n\n<details>\n<summary>You can also go to the <a href=\"https://github.com/openai/codex/releases/latest\">latest GitHub Release</a> and download the appropriate binary for your platform.</summary>\n\n",
        "exit_code": 0,
        "status": "completed"
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_4",
        "item_type": "reasoning",
        "text": "**Updating README file**\n\nThe user wants to update the README by removing the first line, which is an HTML h1 tag. I'll proceed to delete that line while keeping the rest intact. To do this, I need to use the apply_patch tool and create a patch in the diff format. I’ll take care to include context lines around the change. After removing the header, I’m considering whether to delete the following blank line as well to keep it tidy."
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_5",
        "item_type": "reasoning",
        "text": "**Crafting the patch**\n\nThe user has asked to remove just the first line, without mentioning the blank lines. Technically, leaving a blank line at the start is acceptable, but it's not ideal. Ideally, I'd prefer to remove the first line while maintaining the subsequent blank line. However, if I can also remove that blank line, it could tidy things up even more. Still, since the user's request is strictly to remove only the first line, I'll stick to that and craft the patch accordingly."
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_6",
        "item_type": "assistant_message",
        "text": "Applying the change by removing the first line from `README.md`."
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_7",
        "item_type": "file_change",
        "changes": [
          {
            "path": "/Users/pakrym/code/codex/README.md",
            "kind": "update"
          }
        ],
        "status": "completed"
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_8",
        "item_type": "reasoning",
        "text": "**Validating README Update**\n\nI updated the README.md and now I need to validate that the first line has been successfully removed. To do this, I'll check the first 10 lines of the document to ensure everything looks correct. It’s always good to double-check these kinds of changes, so I’m going to take that extra step to make sure the update was executed properly. Let's see what those initial lines say!"
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_9",
        "item_type": "command_execution",
        "command": "bash -lc sed -n '1,20p' README.md",
        "aggregated_output": "\n<p align=\"center\"><code>npm i -g @openai/codex</code><br />or <code>brew install codex</code></p>\n\n<p align=\"center\"><strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.\n</br>\n</br>If you want Codex in your code editor (VS Code, Cursor, Windsurf), <a href=\"https://developers.openai.com/codex/ide\">install in your IDE</a>\n</br>If you are looking for the <em>cloud-based agent</em> from OpenAI, <strong>Codex Web</strong>, go to <a href=\"https://chatgpt.com/codex\">chatgpt.com/codex</a></p>\n\n<p align=\"center\">\n  <img src=\"./.github/codex-cli-splash.png\" alt=\"Codex CLI splash\" width=\"80%\" />\n  </p>\n\n---\n\n## Quickstart\n\n### Installing and running Codex CLI\n\nInstall globally with your preferred package manager. If you use npm:\n\n",
        "exit_code": 0,
        "status": "completed"
      }
    }
    {
      "type": "item.completed",
      "item": {
        "id": "itm_10",
        "item_type": "assistant_message",
        "text": "- Updated `README.md:1` by removing the opening H1 line.\n- The README now starts with the install instructions paragraph.\n\nWant me to also remove the leading blank line at the top?"
      }
    }
    ```
    
    </details>
    
    The idea is to give users fully formatted items they can use directly in
    their rendering/application logic and avoid having them building up
    items manually based on events (unless they want to for streaming).
    
    This PR implements only the `item.completed` payload for some event
    types, more event types and item types to come.
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • chore: refactor attempt_stream_responses() out of stream_responses() (#4194)
    I would like to be able to swap in a different way to resolve model
    sampling requests, so this refactoring consolidates things behind
    `attempt_stream_responses()` to make that easier. Ideally, we would
    support an in-memory backend that we can use in our integration tests,
    for example.
  • fix: esc w/ queued messages overwrites draft in composer (#4237)
    Instead of overwriting the contents of the composer when pressing
    <kbd>Esc</kbd> when there's a queued message, prepend the queued
    message(s) to the composer draft.
  • feat: add support for CODEX_SECURE_MODE=1 to restrict process observability (#4220)
    Because the `codex` process could contain sensitive information in
    memory, such as API keys, we add logic so that when
    `CODEX_SECURE_MODE=1` is specified, we avail ourselves of whatever the
    operating system provides to restrict observability/tampering, which
    includes:
    
    - disabling `ptrace(2)`, so it is not possible to attach to the process
    with a debugger, such as `gdb`
    - disabling core dumps
    
    Admittedly, a user with root privileges can defeat these safeguards.
    
    For now, we only add support for this in the `codex` multitool, but we
    may ultimately want to support this in some of the smaller CLIs that are
    buildable out of our Cargo workspace.
  • ref: full state refactor (#4174)
    ## Current State Observations
    - `Session` currently holds many unrelated responsibilities (history,
    approval queues, task handles, rollout recorder, shell discovery, token
    tracking, etc.), making it hard to reason about ownership and lifetimes.
    - The anonymous `State` struct inside `codex.rs` mixes session-long data
    with turn-scoped queues and approval bookkeeping.
    - Turn execution (`run_task`) relies on ad-hoc local variables that
    should conceptually belong to a per-turn state object.
    - External modules (`codex::compact`, tests) frequently poke the raw
    `Session.state` mutex, which couples them to implementation details.
    - Interrupts, approvals, and rollout persistence all have bespoke
    cleanup paths, contributing to subtle bugs when a turn is aborted
    mid-flight.
    
    ## Desired End State
    - Keep a slim `Session` object that acts as the orchestrator and façade.
    It should expose a focused API (submit, approvals, interrupts, event
    emission) without storing unrelated fields directly.
    - Introduce a `state` module that encapsulates all mutable data
    structures:
    - `SessionState`: session-persistent data (history, approved commands,
    token/rate-limit info, maybe user preferences).
    - `ActiveTurn`: metadata for the currently running turn (sub-id, task
    kind, abort handle) and an `Arc<TurnState>`.
    - `TurnState`: all turn-scoped pieces (pending inputs, approval waiters,
    diff tracker, review history, auto-compact flags, last agent message,
    outstanding tool call bookkeeping).
    - Group long-lived helpers/managers into a dedicated `SessionServices`
    struct so `Session` does not accumulate "random" fields.
    - Provide clear, lock-safe APIs so other modules never touch raw
    mutexes.
    - Ensure every turn creates/drops a `TurnState` and that
    interrupts/finishes delegate cleanup to it.
  • github: update codespell action to v2.1 in workflow (#4205)
    Old version fails to find python 3.8 docker image
  • chore: drop unused values from env_flags (#4188)
    For the most part, we try to avoid environment variables in favor of
    config options so the environment variables do not leak into child
    processes. These environment variables are no longer honored, so let's
    delete them to be clear.
    
    Ultimately, I would also like to eliminate `CODEX_RS_SSE_FIXTURE` in
    favor of something cleaner.
  • adds a windows-specific method to check if a command is safe (#4119)
    refactors command_safety files into its own package, so we can add
    platform-specific ones
    Also creates a windows-specific of `is_known_safe_command` that just
    returns false always, since that is what happens today.
  • improve MCP tool call styling (#3871)
    <img width="760" height="213" alt="Screenshot 2025-09-18 at 12 29 15 PM"
    src="https://github.com/user-attachments/assets/48a205b7-b95a-4988-8c76-efceb998dee7"
    />
  • hide the status indicator when the answer stream starts (#4101)
    This eliminates a "bounce" at the end of streaming where we hide the
    status indicator at the end of the turn and the composer moves up two
    lines.
    
    Also, simplify streaming further by removing the HistorySink and
    inverting control, and collapsing a few single-element structures.
  • Simplify tool implemetations (#4160)
    Use Result<String, FunctionCallError> for all tool handling code and
    rely on error propagation instead of creating failed items everywhere.