Commit Graph

3718 Commits

  • feat: max threads config (#9483)
    # 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.
    
    Include a link to a bug report or enhancement request.
  • Add request-user-input overlay (#9585)
    - Add request-user-input overlay and routing in the TUI
  • don't ask for approval for just fix (#9586)
    It blocks all my skills from executing because it asks to run just fmt.
    It's quick command that doesn't need approval.
    
    
    <img width="967" height="120" alt="image"
    src="https://github.com/user-attachments/assets/f8e6ca76-a650-49e9-beb2-ce98ba48d310"
    />
  • Reject ask user question tool in Execute and Custom (#9560)
    ## Summary
    - Keep `request_user_input` in the tool list but reject it at runtime in
    Execute/Custom modes with a clear model-facing error.
    - Add a session accessor for current collaboration mode and enforce the
    gate in the request_user_input handler.
    - Update core/app-server tests to use Plan mode for success and add
    Execute/Custom rejection coverage.
  • Prompt Expansion: Preserve Text Elements (#9518)
    Summary
    - Preserve `text_elements` through custom prompt argument parsing and
    expansion (named and numeric placeholders).
    - Translate text element ranges through Shlex parsing using sentinel
    substitution, and rehydrate text + element ranges per arg.
    - Drop image attachments when their placeholder does not survive prompt
    expansion, keeping attachments consistent with rendered elements.
    - Mirror changes in TUI2 and expand tests for prompt parsing/expansion
    edge cases.
    
    Tests
    - placeholders with spaces as single tokens (positional + key=value,
    quoted + unquoted),
      - prompt expansion with image placeholders,
      - large paste + image arg combinations,
      - unused image arg dropped after expansion.
  • feat: rename experimental_instructions_file to model_instructions_file (#9555)
    A user who has `experimental_instructions_file` set will now see this:
    
    <img width="888" height="660" alt="image"
    src="https://github.com/user-attachments/assets/51c98312-eb9b-4881-81f1-bea6677e158d"
    />
    
    And a `codex exec` would include this warning:
    
    <img width="888" height="660" alt="image"
    src="https://github.com/user-attachments/assets/a89f62be-1edf-4593-a75e-e0b4a762ed7d"
    />
  • Show session header before configuration (#9568)
    We were skipping if we know the model. We shouldn't
  • feat(tui2): add /experimental menu (#9562)
    Adds an /experimental slash command and bottom-pane view to toggle beta
    features.
    
    Persists feature-flag updates to config.toml, matching tui behavior.
  • [codex-tui] exit when terminal is dumb (#9293)
    Using terminal with TERM=dumb specifically mean that TUIs and the like
    don't work. Ensure that codex doesn't run in these environments and exit
    with odd errors like crossterm's "Error: The cursor position could not
    be read within a normal duration"
    
    ---------
    
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • fix(core): require approval for force delete on Windows (#8590)
    ### What
    Implemented detection for dangerous "force delete" commands on Windows
    to trigger the user approval prompt when `--ask-for-approval on-request`
    is set. This aligns Windows behavior with the existing safety checks for
    `rm -rf` on Linux.
    
    ### Why
    Fixes #8567 - a critical safety gap where destructive Windows commands
    could bypass the approval prompt. This prevents accidental data loss by
    ensuring the user explicitly confirms operations that would otherwise
    suppress the OS's native confirmation prompts.
    
    ### How
    Updated the Windows command safety module to identify and flag the
    following patterns as dangerous:
    *   **PowerShell**:
    * Detects `Remove-Item` (and aliases `rm`, `ri`, `del`, `erase`, `rd`,
    `rmdir`) when used with the `-Force` flag.
    * Uses token-based analysis to robustly detect these patterns even
    inside script blocks (`{...}`), sub-expression `(...)`, or
    semicolon-chained sequences.
    *   **CMD**:
        *   Detects `del /f` (force delete files).
        *   Detects `rd /s /q` (recursive delete quiet).
    * **Command Chaining**: Added support for analyzing chained commands
    (using `&`, `&&`, `|`, `||`) to separate and check individual commands
    (e.g., catching `del /f` hidden in `echo log & del /f data`).
    
    ### Testing
    Added comprehensive unit tests covering:
    * **PowerShell**: `Remove-Item -Path 'test' -Recurse -Force` (Exact
    reproduction case).
    * **Complex Syntax**: Verified detection inside blocks (e.g., `if
    ($true) { rm -Force }`) and with trailing punctuation.
    *   **CMD**:
        *   `del /f` (Flagged).
        *   `rd /s /q` (Flagged).
        *   Chained commands: `echo hi & del /f file` (Flagged).
    *   **False Positives**:
        *   `rd /s` (Not flagged - relies on native prompt).
        *   Standard deletions without force flags.
    
    Verified with `cargo test` and `cargo clippy`.
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • Improve UI spacing for queued messages (#9162)
    Despite good spacing between queued messages and assistant message text:
    <img width="462" height="322" alt="Screenshot 2026-01-12 at 4 54 50 PM"
    src="https://github.com/user-attachments/assets/e8b46252-0b33-40d2-b431-cb73b9a3bd2e"
    />
    
    Codex has confusing spacing between queued messages and shimmering
    status text (making the queued message seem like a sub-item of the
    shimmering status text)
    <img width="615" height="217" alt="Screenshot 2026-01-12 at 4 54 18 PM"
    src="https://github.com/user-attachments/assets/ee5e6095-8fe9-4863-88d2-10472cab8bd6"
    />
    
    This PR changes the spacing between the queued message(s) and shimmering
    status text to make it less confusing:
    <img width="440" height="240" alt="Screenshot 2026-01-13 at 11 20 36 AM"
    src="https://github.com/user-attachments/assets/02dcc690-cbe9-4943-87de-c7300ef51120"
    />
    
    While working on the status/queued spacing change, we noticed two
    paste‑burst tests were timing‑sensitive and could fail
    on slower CI. We added a small test‑only helper to keep the paste‑burst
    state active and refreshed during these tests. This
    removes dependence on tight timing and makes the tests deterministic
    without affecting runtime behavior.
  • fix(cli): add execute permission to bin/codex.js (#9532)
    ## Summary
    Fixes #9520
    
    The `bin/codex.js` file was missing execute permissions (`644` instead
    of `755`), causing the `codex` command to fail after npm global
    installation.
    
    ## Changes
    - Added execute permission (`+x`) to `codex-cli/bin/codex.js`
    
    ## Verification
    After this fix, npm tarballs will include the correct file permissions:
    ```bash
    # Before: -rw-r--r-- (644)
    # After:  -rwxr-xr-x (755)
    ```
    
    ---
    🤖 Generated with Claude Code
    
    Co-authored-by: Claude <noreply@anthropic.com>
  • Add total (non-partial) TextElement placeholder accessors (#9545)
    ## Summary
    - Make `TextElement` placeholders private and add a text-backed accessor
    to avoid assuming `Some`.
    - Since they are optional in the protocol, we want to make sure any
    accessors properly handle the None case (getting the placeholder using
    the byte range in the text)
    - Preserve placeholders during protocol/app-server conversions using the
    accessor fallback.
    - Update TUI composer/remap logic and tests to use the new
    constructor/accessor.
  • merge remote models (#9547)
    We have `models.json` and `/models` response
    Behavior:
    1. New models from models endpoint gets added
    2. Shared models get replaced by remote ones
    3. Existing models in `models.json` but not `/models` are kept
    4. Mark highest priority as default
  • fix(windows-sandbox): parse PATH list entries for audit roots (#9319)
    ## Summary
    - Use `std::env::split_paths` to parse PATH entries in audit candidate
    collection
    - Add a unit test covering multiple PATH entries (including spaces)
    
    ## Testing
    - `cargo test -p codex-windows-sandbox` (Windows)
    
    Fixes #9317
  • fix(windows-sandbox): deny .git file entries under writable roots (#9314)
    ## Summary
    - Deny `.git` entries under writable roots even when `.git` is a file
    (worktrees/submodules)
    - Add a unit test for `.git` file handling
    
    ## Testing
    - `cargo test -p codex-windows-sandbox` (Windows)
    
    Fixes #9313
  • lookup system SIDs instead of hardcoding English strings. (#9552)
    The elevated setup does not work on non-English windows installs where
    Users/Administrators/etc are in different languages. This PR uses the
    well-known SIDs instead, which do not vary based on locale
  • fix(core): don't update the file's mtime on resume (#9553)
    Remove `FileTimes::new().set_modified(SystemTime::now())` when resuming
    a thread.
    
    Context: It's awkward in UI built on top of app-server that resuming a
    thread bumps the `updated_at` timestamp, even if no message is sent. So
    if you open a thread (perhaps to just view its contents), it
    automatically reorders it to the top which is almost certainly not what
    you want.
  • fix: prevent repeating interrupted turns (#9043)
    ## What
    Record a model-visible `<turn_aborted>` marker in history when a turn is
    interrupted, and treat it as a session prefix.
    
    ## Why
    When a turn is interrupted, Codex emits `TurnAborted` but previously did
    not persist anything model-visible in the conversation history. On the
    next user turn, the model can’t tell the previous work was aborted and
    may resume/repeat earlier actions (including duplicated side effects
    like re-opening PRs).
    
    Fixes: https://github.com/openai/codex/issues/9042
    
    ## How
    On `TurnAbortReason::Interrupted`, append a hidden user message
    containing a `<turn_aborted>…</turn_aborted>` marker and flush.
    Treat `<turn_aborted>` like `<environment_context>` for session-prefix
    filtering.
    Add a regression test to ensure follow-up turns don’t repeat side
    effects from an aborted turn.
    
    ## Testing
    `just fmt`
    `just fix -p codex-core`
    `cargo test -p codex-core -- --test-threads=1`
    `cargo test --all-features -- --test-threads=1`
    
    ---------
    
    Co-authored-by: Skylar Graika <sgraika127@gmail.com>
    Co-authored-by: jif-oai <jif@openai.com>
    Co-authored-by: Eric Traut <etraut@openai.com>
  • Fixed config merging issue with profiles (#9509)
    This PR fixes a small issue with chained (layered) config.toml file
    merging. The old logic didn't properly handle profiles.
    
    In particular, if a lower-layer config overrides a profile defined in a
    higher-layer config, the override did not take effect. This prevents
    users from having project-specific profile overrides and contradicts the
    (soon-to-be) documented behavior of config merging.
    
    The change adds a unit test for this case. It also exposes a function
    from the config crate that is needed by the app server code paths to
    implement support for layered configs.
  • fix: memory leak issue (#9543)
    Co-authored-by: Josh McKinney <joshka@openai.com>
  • feat(personality) introduce model_personality config (#9459)
    ## Summary
    Introduces the concept of a config model_personality. I would consider
    this an MVP for testing out the feature. There are a number of
    follow-ups to this PR:
    
    - More sophisticated templating with validation
    - In-product experience to manage this
    
    ## Testing
    - [x] Testing locally
  • fix: writable_roots doesn't recognize home directory symbol in non-windows OS (#9193)
    Fixes:
    ```
    [sandbox_workspace_write]
    writable_roots = ["~/code/"]
    ```
    
    translates to
    ```
    /Users/ccunningham/.codex/~/code
    ```
    (i.e. the home dir symbol isn't recognized)
  • Tui: use collaboration mode instead of model and effort (#9507)
    - Only use collaboration modes in the tui state to track model and
    effort.
    - No behavior change without the collaboration modes flag.
    - Change model and effort on /model, /collab (behind a flag), and
    shift+tab (behind flag)
  • feat: support proxy for ws connection (#9409)
    unfortunately tokio-tungstenite doesn't support proxy configuration
    outbox, while https://github.com/snapview/tokio-tungstenite/pull/370 is
    in review, we can depend on source code for now.
  • Only load config from trusted folders (#9533)
    Config includes multiple code execution entrypoints. 
    
    Now, we load the config from predetermined locations first
    (~/.codex/config.toml etc), use those to learn which folders are
    'trusted', and only load additional config from the CWD if it is
    trusted.
  • nit: do not render terminal interactions if no task running (#9374)
    To prevent race where the terminal interaction message is processed
    after the last message
  • fix(tui) fix user message light mode background (#9407)
    ## Summary
    Fixes the user message styles for light mode.
    
    ## Testing
    Attaching 2 screenshots from ghostty, but I also tried various styles in
    Terminal.app and iTerm2.
    **Before**
    <img width="888" height="560" alt="Screenshot 2026-01-16 at 5 22 36 PM"
    src="https://github.com/user-attachments/assets/73d9decb-a01a-4ece-b88e-ea49a33cc0c6"
    />
    
    **After**
    <img width="890" height="281" alt="Screenshot 2026-01-16 at 5 22 59 PM"
    src="https://github.com/user-attachments/assets/6689e286-d699-4ceb-b0cb-579a31b047bf"
    />
  • Persist text elements through TUI input and history (#9393)
    Continuation of breaking up this PR
    https://github.com/openai/codex/pull/9116
    
    ## Summary
    - Thread user text element ranges through TUI/TUI2 input, submission,
    queueing, and history so placeholders survive resume/edit flows.
    - Preserve local image attachments alongside text elements and rehydrate
    placeholders when restoring drafts.
    - Keep model-facing content shapes clean by attaching UI metadata only
    to user input/events (no API content changes).
    
    ## Key Changes
    - TUI/TUI2 composer now captures text element ranges, trims them with
    text edits, and restores them when submission is suppressed.
    - User history cells render styled spans for text elements and keep
    local image paths for future rehydration.
    - Initial chat widget bootstraps accept empty `initial_text_elements` to
    keep initialization uniform.
    - Protocol/core helpers updated to tolerate the new InputText field
    shape without changing payloads sent to the API.
  • fix(core) Preserve base_instructions in SessionMeta (#9427)
    ## Summary
    This PR consolidates base_instructions onto SessionMeta /
    SessionConfiguration, so we ensure `base_instructions` is set once per
    session and should be (mostly) immutable, unless:
    - overridden by config on resume / fork
    - sub-agent tasks, like review or collab
    
    
    In a future PR, we should convert all references to `base_instructions`
    to consistently used the typed struct, so it's less likely that we put
    other strings there. See #9423. However, this PR is already quite
    complex, so I'm deferring that to a follow-up.
    
    ## Testing
    - [x] Added a resume test to assert that instructions are preserved. In
    particular, `resume_switches_models_preserves_base_instructions` fails
    against main.
    
    Existing test coverage thats assert base instructions are preserved
    across multiple requests in a session:
    - Manual compact keeps baseline instructions:
    core/tests/suite/compact.rs:199
    - Auto-compact keeps baseline instructions:
    core/tests/suite/compact.rs:1142
    - Prompt caching reuses the same instructions across two requests:
    core/tests/suite/prompt_caching.rs:150 and
    core/tests/suite/prompt_caching.rs:157
    - Prompt caching with explicit expected string across two requests:
    core/tests/suite/prompt_caching.rs:213 and
    core/tests/suite/prompt_caching.rs:222
    - Resume with model switch keeps original instructions:
    core/tests/suite/resume.rs:136
    - Compact/resume/fork uses request 0 instructions for later expected
    payloads: core/tests/suite/compact_resume_fork.rs:215
  • Migrate tui to use UserTurn (#9497)
    - `tui/` and `tui2/` submit `Op::UserTurn` and own full turn context
    (cwd/approval/sandbox/model/etc.).
    - `Op::UserInput` is documented as legacy in `codex-protocol` (doc-only;
    no `#[deprecated]` to avoid `-D warnings` fallout).
    - Remove obsolete `#[allow(deprecated)]` and the unused `ConversationId`
    alias/re-export.
  • tui: avoid Esc interrupt when skill popup active (#9451)
    Fixes #9450
    
    ## What
    - When a task is running and the skills autocomplete popup is open,
    `Esc` now dismisses the popup instead of sending `Op::Interrupt`.
    - `Esc` still interrupts a running task when no popup is active.
    
    ## Tests
    - `cargo test -p codex-tui`
    
    ---------
    
    Co-authored-by: prateek <199982+prateek@users.noreply.github.com>
  • Feat: plan mode prompt update (#9495)
    ### Summary
    * Added instruction on using `request_user_input`
    * Added the output to be json with `plan` key and the actual plan as the
    value.
    * Remove `PLAN.md` write because that gets into sandbox issue. We can
    add it back later.
  • Act on reasoning-included per turn (#9402)
    - Reset reasoning-included flag each turn and update compaction test
  • Feat: request user input tool (#9472)
    ### Summary
    * Add `requestUserInput` tool that the model can use for gather
    feedback/asking question mid turn.
    
    
    ### Tool input schema
    ```
    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "requestUserInput input",
      "type": "object",
      "additionalProperties": false,
      "required": ["questions"],
      "properties": {
        "questions": {
          "type": "array",
          "description": "Questions to show the user (1-3). Prefer 1 unless multiple independent decisions block progress.",
          "minItems": 1,
          "maxItems": 3,
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "header", "question"],
            "properties": {
              "id": {
                "type": "string",
                "description": "Stable identifier for mapping answers (snake_case)."
              },
              "header": {
                "type": "string",
                "description": "Short header label shown in the UI (12 or fewer chars)."
              },
              "question": {
                "type": "string",
                "description": "Single-sentence prompt shown to the user."
              },
              "options": {
                "type": "array",
                "description": "Optional 2-3 mutually exclusive choices. Put the recommended option first and suffix its label with \"(Recommended)\". Only include \"Other\" option if we want to include a free form option. If the question is free form in nature, do not include any option.",
                "minItems": 2,
                "maxItems": 3,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": ["value", "label", "description"],
                  "properties": {
                    "value": {
                      "type": "string",
                      "description": "Machine-readable value (snake_case)."
                    },
                    "label": {
                      "type": "string",
                      "description": "User-facing label (1-5 words)."
                    },
                    "description": {
                      "type": "string",
                      "description": "One short sentence explaining impact/tradeoff if selected."
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    ```
    
    ### Tool output schema
    ```
    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": "requestUserInput output",
      "type": "object",
      "additionalProperties": false,
      "required": ["answers"],
      "properties": {
        "answers": {
          "type": "object",
          "description": "Map of question id to user answer.",
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "required": ["selected"],
            "properties": {
              "selected": {
                "type": "array",
                "items": { "type": "string" }
              },
              "other": {
                "type": ["string", "null"]
              }
            }
          }
        }
      }
    }
    ```
  • TUI: collaboration mode UX + always submit UserTurn when enabled (#9461)
    - Adds experimental collaboration modes UX in TUI: Plan / Pair
    Programming / Execute.
    - Gated behind `Feature::CollaborationModes`; existing behavior remains
    unchanged when disabled.
    - Selection UX:
    - `Shift+Tab` cycles modes while idle (no task running, no modal/popup).
    - `/collab` cycles; `/collab <plan|pair|pp|execute|exec>` sets
    explicitly.
    - Footer flash after changes + shortcut overlay shows `Shift+Tab` “to
    change mode”.
      - `/status` shows “Collaboration mode”.
    - Submission semantics:
    - When enabled: every submit uses `Op::UserTurn` and always includes
    `collaboration_mode: Some(...)` (default Pair Programming).
      - Removes the one-shot “pending collaboration mode” behavior.
    - Implementation:
    - New `tui/src/collaboration_modes.rs` (selection enum/cycle, `/collab`
    parsing, resolve to `CollaborationMode`, footer flash line).
    - Fallback: `resolve_mode_or_fallback` synthesizes a `CollaborationMode`
    when presets are missing (uses current model + reasoning effort; no
    `developer_instructions`) to avoid core falling back to `Custom`.
      - TODO: migrate TUI to use `Op::UserTurn`.