Commit Graph

913 Commits

  • fix: taking plan type from usage endpoint instead of thru auth token (#7610)
    pull plan type from the usage endpoint, persist it in session state /
    tui state, and propagate through rate limit snapshots
  • feat: exec policy integration in shell mcp (#7609)
    adding execpolicy support into the `posix` mcp
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • Call models endpoint in models manager (#7616)
    - Introduce `with_remote_overrides` and update
    `refresh_available_models`
    - Put `auth_manager` instead of `auth_mode` on `models_manager`
    - Remove `ShellType` and `ReasoningLevel` to use already existing
    structs
  • chore: refactor to move Arc<RwLock> concern outside exec_policy_for (#7615)
    The caller should decide whether wrapping the policy in `Arc<RwLock>` is
    necessary. This should make https://github.com/openai/codex/pull/7609 a
    bit smoother.
    
    - `exec_policy_for()` -> `load_exec_policy_for_features()`
    - introduce `load_exec_policy()` that does not take `Features` as an arg
    - both return `Result<Policy, ExecPolicyError>` instead of
    Result<Arc<RwLock<Policy>>, ExecPolicyError>`
    
    This simplifies the tests as they have no need for `Arc<RwLock>`.
  • Add models endpoint (#7603)
    - Use the codex-api crate to introduce models endpoint. 
    - Add `models` to codex core tests helpers
    - Add `ModelsInfo` for the endpoint return type
  • Inline response recording and remove process_items indirection (#7310)
    - Inline response recording during streaming: `run_turn` now records
    items as they arrive instead of building a `ProcessedResponseItem` list
    and post‑processing via `process_items`.
    - Simplify turn handling: `handle_output_item_done` returns the
    follow‑up signal + optional tool future; `needs_follow_up` is set only
    there, and in‑flight tool futures are drained once at the end (errors
    logged, no extra state writes).
    - Flattened stream loop: removed `process_items` indirection and the
    extra output queue
    - - Tests: relaxed `tool_parallelism::tool_results_grouped` to allow any
    completion order while still requiring matching call/output IDs.
  • remove model_family from `config (#7571)
    - Remove `model_family` from `config`
    - Make sure to still override config elements related to `model_family`
    like supporting reasoning
  • chore(core): test apply_patch_cli on Windows (#7554)
    ## Summary
    These tests pass on windows, let's enable them.
    
    ## Testing
    - [x] These are more tests
  • feat: update sandbox policy to allow TTY (#7580)
    **Change**: Seatbelt now allows file-ioctl on /dev/ttys[0-9]+ even
    without the sandbox extension so pre-created PTYs remain interactive
    (Python REPL, shells).
    
    **Risk**: A seatbelted process that already holds a PTY fd (including
    one it shouldn’t) could issue tty ioctls like TIOCSTI or termios changes
    on that fd. This doesn’t allow opening new PTYs or reading/writing them;
    it only broadens ioctl capability on existing fds.
    
    **Why acceptable**: We already hand the child its PTY for interactive
    use; restoring ioctls is required for isatty() and prompts to work. The
    attack requires being given or inheriting a sensitive PTY fd; by design
    we don’t hand untrusted processes other users’ PTYs (we don't hand them
    any PTYs actually), so the practical exposure is limited to the PTY
    intentionally allocated for the session.
    
    **Validation**:
    Running
    ```
    start a python interpreter and keep it running
    ```
    Followed by:
    * `calculate 1+1 using it` -> works as expected
    * `Use this Python session to run the command just fix in
    /Users/jif/code/codex/codex-rs` -> does not work as expected
  • Refactor execpolicy fallback evaluation (#7544)
    ## Refactor of the `execpolicy` crate
    
    To illustrate why we need this refactor, consider an agent attempting to
    run `apple | rm -rf ./`. Suppose `apple` is allowed by `execpolicy`.
    Before this PR, `execpolicy` would consider `apple` and `pear` and only
    render one rule match: `Allow`. We would skip any heuristics checks on
    `rm -rf ./` and immediately approve `apple | rm -rf ./` to run.
    
    To fix this, we now thread a `fallback` evaluation function into
    `execpolicy` that runs when no `execpolicy` rules match a given command.
    In our example, we would run `fallback` on `rm -rf ./` and prevent
    `apple | rm -rf ./` from being run without approval.
  • whitelist command prefix integration in core and tui (#7033)
    this PR enables TUI to approve commands and add their prefixes to an
    allowlist:
    <img width="708" height="605" alt="Screenshot 2025-11-21 at 4 18 07 PM"
    src="https://github.com/user-attachments/assets/56a19893-4553-4770-a881-becf79eeda32"
    />
    
    note: we only show the option to whitelist the command when 
    1) command is not multi-part (e.g `git add -A && git commit -m 'hello
    world'`)
    2) command is not already matched by an existing rule
  • Migrate codex max (#7566)
    - make codex max the default
    - fix: we were doing some async work in sync function which caused tui
    to panic
  • Migrate model family to models manager (#7565)
    This PR moves `ModelsFamily` to `openai_models`. It also propagates
    `ModelsManager` to session services and use it to drive model family. We
    also make `derive_default_model_family` private because it's a step
    towards what we want: one place that gives model configuration.
    
    This is a second step at having one source of truth for models
    information and config: `ModelsManager`.
    
    Next steps would be to remove `ModelsFamily` from config. That's massive
    because it's being used in 41 occasions mostly pre launching `codex`.
    Also, we need to make `find_family_for_model` private. It's also big
    because it's being used in 21 occasions ~ all tests.
  • Migrate tui to use models manager (#7555)
    - This PR treats the `ModelsManager` like `AuthManager` and propagate it
    into the tui, replacing the `builtin_model_presets`
    - We are also decreasing the visibility of `builtin_model_presets`
    
    based on https://github.com/openai/codex/pull/7552
  • fix: Features should be immutable over the lifetime of a session/thread (#7540)
    I noticed that `features: Features` was defined on `struct
    SessionConfiguration`, which is commonly owned by `SessionState`, which
    is in turn owned by `Session`.
    
    Though I do not believe that `Features` should be allowed to be modified
    over the course of a session (if the feature state is not invariant, it
    makes it harder to reason about), which argues that it should live on
    `Session` rather than `SessionState` or `SessionConfiguration`.
    
    This PR moves `Features` to `Session` and updates all call sites. It
    appears the only place we were mutating `Features` was:
    
    - in tests
    - the sub-agent config for a review task:
    
    
    https://github.com/openai/codex/blob/3ef76ff29d5eed258fb6b8550e0e2b973d0dca21/codex-rs/core/src/tasks/review.rs#L86-L89
    
    Note this change also means it is no longer an `async` call to check the
    state of a feature, eliminating the possibility of a
    [TOCTTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)
    error between checking the state of a feature and acting on it:
    
    
    https://github.com/openai/codex/blob/3ef76ff29d5eed258fb6b8550e0e2b973d0dca21/codex-rs/core/src/codex.rs#L1069-L1076
  • feat: Support listing and selecting skills via $ or /skills (#7506)
    List/Select skills with $-mention or /skills
  • Migrate model preset (#7542)
    - Introduce `openai_models` in `/core`
    - Move `PRESETS` under it
    - Move `ModelPreset`, `ModelUpgrade`, `ReasoningEffortPreset`,
    `ReasoningEffortPreset`, and `ReasoningEffortPreset` to `protocol`
    - Introduce `Op::ListModels` and `EventMsg::AvailableModels`
    
    Next steps:
    - migrate `app-server` and `tui` to use the introduced Operation
  • chore: update unified exec sandboxing detection (#7541)
    No integration test for now because it would make them flaky. Tracking
    it in my todos to add some once we have a clock based system for
    integration tests
  • feat: support list mcp servers in app server (#7505)
    ### Summary
    Added `mcp/servers/list` which is equivalent to `/mcp` slash command in
    CLI for response. This will be used in VSCE MCP settings to show log in
    status, available tools etc.
  • seatbelt: allow openpty() (#7507)
    This allows `openpty(3)` to run in the default sandbox. Also permit
    reading `kern.argmax`, which is the maximum number of arguments to
    exec().
  • feat: codex tool tips (#7440)
    <img width="551" height="316" alt="Screenshot 2025-12-01 at 12 22 26"
    src="https://github.com/user-attachments/assets/6ca3deff-8ef8-4f74-a8e1-e5ea13fd6740"
    />
  • feat: retroactive image placeholder to prevent poisoning (#6774)
    If an image can't be read by the API, it will poison the entire history,
    preventing any new turn on the conversation.
    This detect such cases and replace the image by a placeholder
  • fix(unified_exec): use platform default shell when unified_exec shell… (#7486)
    # Unified Exec Shell Selection on Windows
    
    ## Problem
    
    reference issue #7466
    
    The `unified_exec` handler currently deserializes model-provided tool
    calls into the `ExecCommandArgs` struct:
    
    ```rust
    #[derive(Debug, Deserialize)]
    struct ExecCommandArgs {
        cmd: String,
        #[serde(default)]
        workdir: Option<String>,
        #[serde(default = "default_shell")]
        shell: String,
        #[serde(default = "default_login")]
        login: bool,
        #[serde(default = "default_exec_yield_time_ms")]
        yield_time_ms: u64,
        #[serde(default)]
        max_output_tokens: Option<usize>,
        #[serde(default)]
        with_escalated_permissions: Option<bool>,
        #[serde(default)]
        justification: Option<String>,
    }
    ```
    
    The `shell` field uses a hard-coded default:
    
    ```rust
    fn default_shell() -> String {
        "/bin/bash".to_string()
    }
    ```
    
    When the model returns a tool call JSON that only contains `cmd` (which
    is the common case), Serde fills in `shell` with this default value.
    Later, `get_command` uses that value as if it were a model-provided
    shell path:
    
    ```rust
    fn get_command(args: &ExecCommandArgs) -> Vec<String> {
        let shell = get_shell_by_model_provided_path(&PathBuf::from(args.shell.clone()));
        shell.derive_exec_args(&args.cmd, args.login)
    }
    ```
    
    On Unix, this usually resolves to `/bin/bash` and works as expected.
    However, on Windows this behavior is problematic:
    
    - The hard-coded `"/bin/bash"` is not a valid Windows path.
    - `get_shell_by_model_provided_path` treats this as a model-specified
    shell, and tries to resolve it (e.g. via `which::which("bash")`), which
    may or may not exist and may not behave as intended.
    - In practice, this leads to commands being executed under a non-default
    or non-existent shell on Windows (for example, WSL bash), instead of the
    expected Windows PowerShell or `cmd.exe`.
    
    The core of the issue is that **"model did not specify `shell`" is
    currently interpreted as "the model explicitly requested `/bin/bash`"**,
    which is both Unix-specific and wrong on Windows.
    
    ## Proposed Solution
    
    Instead of hard-coding `"/bin/bash"` into `ExecCommandArgs`, we should
    distinguish between:
    
    1. **The model explicitly specifying a shell**, e.g.:
    
       ```json
       {
         "cmd": "echo hello",
         "shell": "pwsh"
       }
       ```
    
    In this case, we *do* want to respect the model’s choice and use
    `get_shell_by_model_provided_path`.
    
    2. **The model omitting the `shell` field entirely**, e.g.:
    
       ```json
       {
         "cmd": "echo hello"
       }
       ```
    
    In this case, we should *not* assume `/bin/bash`. Instead, we should use
    `default_user_shell()` and let the platform decide.
    
    To express this distinction, we can:
    
    1. Change `shell` to be optional in `ExecCommandArgs`:
    
       ```rust
       #[derive(Debug, Deserialize)]
       struct ExecCommandArgs {
           cmd: String,
           #[serde(default)]
           workdir: Option<String>,
           #[serde(default)]
           shell: Option<String>,
           #[serde(default = "default_login")]
           login: bool,
           #[serde(default = "default_exec_yield_time_ms")]
           yield_time_ms: u64,
           #[serde(default)]
           max_output_tokens: Option<usize>,
           #[serde(default)]
           with_escalated_permissions: Option<bool>,
           #[serde(default)]
           justification: Option<String>,
       }
       ```
    
    Here, the absence of `shell` in the JSON is represented as `shell:
    None`, rather than a hard-coded string value.
  • fix: inline function marked as dead code (#7508)
    I was debugging something else and noticed we could eliminate an
    instance of `#[allow(dead_code)]` pretty easily.
  • improve resume performance (#7303)
    Reading the tail can be costly if we have a very big rollout item. we
    can just read the file metadata
  • chore: make create_approval_requirement_for_command an async fn (#7501)
    I think this might help with https://github.com/openai/codex/pull/7033
    because `create_approval_requirement_for_command()` will soon need
    access to `Session.state`, which is a `tokio::sync::Mutex` that needs to
    be accessed via `async`.
  • Trim history.jsonl when history.max_bytes is set (#6242)
    This PR honors the `history.max_bytes` configuration parameter by
    trimming `history.jsonl` whenever it grows past the configured limit.
    While appending new entries we retain the newest record, drop the oldest
    lines to stay within the byte budget, and serialize the compacted file
    back to disk under the same lock to keep writers safe.
  • persisting credits if new snapshot does not contain credit info (#7490)
    in response to incoming changes to responses headers where the header
    may sometimes not contain credits info (no longer forcing a credit
    check)
  • Show token used when context window is unknown (#7497)
    - Show context window usage in tokens instead of percentage when the
    window length is unknown.
  • feat: add warning message for the model (#7445)
    Add a warning message as a user turn to the model if the model does not
    behave as expected (here, for example, if the model opens too many
    `unified_exec` sessions)
  • feat: experimental support for skills.md (#7412)
    This change prototypes support for Skills with the CLI. This is an
    **experimental** feature for internal testing.
    
    ---------
    
    Co-authored-by: Gav Verma <gverma@openai.com>
  • fix(core): enable history lookup on windows (#7457)
    - Add portable history log id helper to support inode-like tracking on
    Unix and creation time on Windows
    - Refactor history metadata and lookup to share code paths and allow
    nonzero log ids across platforms
    - Add coverage for lookup stability after appends
  • fix(apply_patch) tests for shell_command (#7307)
    ## Summary
    Adds test coverage for invocations of apply_patch via shell_command with
    heredoc, to validate behavior.
    
    ## Testing
    - [x] These are tests
  • Fix recent_commits(limit=0) returning 1 commit instead of 0 (#7334)
    Fixes #7333
    
    This is a small bug fix.
    
    This PR fixes an inconsistency in `recent_commits` where `limit == 0`
    still returns 1 commit due to the use of `limit.max(1)` when
    constructing the `git log -n` argument.
    
    Expected behavior: requesting 0 commits should return an empty list.
    
    This PR:
    - returns an empty `Vec` when `limit == 0`
    - adds a test for `recent_commits(limit == 0)` that fails before the
    change and passes afterwards
    - maintains existing behavior for `limit > 0`
    
    This aligns behavior with API expectations and avoids downstream
    consumers misinterpreting the repository as having commit history when
    `limit == 0` is used to explicitly request none.
    
    Happy to adjust if the current behavior is intentional.
  • chore: improve rollout session init errors (#7336)
    Title: Improve rollout session initialization error messages
    
    Issue: https://github.com/openai/codex/issues/7283
    
    What: add targeted mapping for rollout/session initialization errors so
    users get actionable messages when Codex cannot access session files.
    
    Why: session creation previously returned a generic internal error,
    hiding permissions/FS issues and making support harder.
    
    How:
    - Added rollout::error::map_session_init_error to translate the more
    common io::Error kinds into user-facing hints (permission, missing dir,
    file blocking, corruption). Others are passed through directly with
    `CodexErr::Fatal`.
    - Reused the mapper in Codex session creation to preserve root causes
    instead of returning InternalAgentDied.