Commit Graph

2355 Commits

  • [app-server-protocol] Add types for config (#7658)
    Currently the config returned by `config/read` in untyped. Add types so
    it's easier for client to parse the config. Since currently configs are
    all defined in snake case we'll keep that instead of using camel case
    like the rest of V2.
    
    Sample output by testing using the app server test client:
    ```
    {
    <   "id": "f28449f4-b015-459b-b07b-eef06980165d",
    <   "result": {
    <     "config": {
    <       "approvalPolicy": null,
    <       "compactPrompt": null,
    <       "developerInstructions": null,
    <       "features": {
    <         "experimental_use_rmcp_client": true
    <       },
    <       "forcedChatgptWorkspaceId": null,
    <       "forcedLoginMethod": null,
    <       "instructions": null,
    <       "model": "gpt-5.1-codex-max",
    <       "modelAutoCompactTokenLimit": null,
    <       "modelContextWindow": null,
    <       "modelProvider": null,
    <       "modelReasoningEffort": null,
    <       "modelReasoningSummary": null,
    <       "modelVerbosity": null,
    <       "model_providers": {
    <         "local": {
    <           "base_url": "http://localhost:8061/api/codex",
    <           "env_http_headers": {
    <             "ChatGPT-Account-ID": "OPENAI_ACCOUNT_ID"
    <           },
    <           "env_key": "CHATGPT_TOKEN_STAGING",
    <           "name": "local",
    <           "wire_api": "responses"
    <         }
    <       },
    <       "model_reasoning_effort": "medium",
    <       "notice": {
    <         "hide_gpt-5.1-codex-max_migration_prompt": true,
    <         "hide_gpt5_1_migration_prompt": true
    <       },
    <       "profile": null,
    <       "profiles": {},
    <       "projects": {
    <         "/Users/celia/code": {
    <           "trust_level": "trusted"
    <         },
    <         "/Users/celia/code/codex": {
    <           "trust_level": "trusted"
    <         },
    <         "/Users/celia/code/openai": {
    <           "trust_level": "trusted"
    <         }
    <       },
    <       "reviewModel": null,
    <       "sandboxMode": null,
    <       "sandboxWorkspaceWrite": null,
    <       "tools": {
    <         "viewImage": null,
    <         "webSearch": null
    <       }
    <     },
    <     "origins": {
    <       "features.experimental_use_rmcp_client": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model_providers.local.base_url": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model_providers.local.env_http_headers.ChatGPT-Account-ID": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model_providers.local.env_key": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model_providers.local.name": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model_providers.local.wire_api": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "model_reasoning_effort": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "notice.hide_gpt-5.1-codex-max_migration_prompt": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "notice.hide_gpt5_1_migration_prompt": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "projects./Users/celia/code.trust_level": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "projects./Users/celia/code/codex.trust_level": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "projects./Users/celia/code/openai.trust_level": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       },
    <       "tools.web_search": {
    <         "name": "user",
    <         "source": "/Users/celia/.codex/config.toml",
    <         "version": "sha256:a1d8eaedb5d9db5dfdfa69f30fa9df2efec66bb4dd46aa67f149fcc67cd0711c"
    <       }
    <     }
    <   }
    < }
    ```
  • Error when trying to push a release while another release is in progress (#7834)
    <img width="995" height="171" alt="image"
    src="https://github.com/user-attachments/assets/7bab541a-a933-4064-a968-26e9566360ec"
    />
    
    Currently, we just cancel the in progress release which can be annoying
  • fix: Prevent slash command popup from activating on invalid inputs (#7704)
    ## Slash Command popup issue
    
    #7659
    
    When recalling history, the
    composer(`codex_tui::bottom_pane::chat_composer`) restores the previous
    prompt text (which may start with `/`) and then calls
    `sync_command_popup`. The logic in `sync_command_popup` treats any first
    line that starts with `/` and has the caret inside the initial `/name`
    token as an active slash command name:
    
    ```rust
    let is_editing_slash_command_name = if first_line.starts_with('/') && caret_on_first_line {
        let token_end = first_line
            .char_indices()
            .find(|(_, c)| c.is_whitespace())
            .map(|(i, _)| i)
            .unwrap_or(first_line.len());
        cursor <= token_end
    } else {
        false
    };
    ```
    
    This detection does not distinguish between an actual interactive slash
    command being typed and a normal historical prompt that happens to begin
    with `/`. As a result, after history recall, the restored prompt like `/
    test` is interpreted as an "editing command name" context and the
    slash-command popup is (re)activated. Once `active_popup` is
    `ActivePopup::Command`, subsequent `Up` key presses are handled by
    `handle_key_event_with_slash_popup` instead of
    `handle_key_event_without_popup`, so they no longer trigger
    `history.navigate_up(...)` and the session prompt history cannot be
    scrolled.
  • make model optional in config (#7769)
    - Make Config.model optional and centralize default-selection logic in
    ModelsManager, including a default_model helper (with
    codex-auto-balanced when available) so sessions now carry an explicit
    chosen model separate from the base config.
    - Resolve `model` once in `core` and `tui` from config. Then store the
    state of it on other structs.
    - Move refreshing models to be before resolving the default model
  • [app-server] Make sure that config writes preserve comments & order or configs (#7789)
    Make sure that config writes preserve comments and order of configs by
    utilizing the ConfigEditsBuilder in core.
    
    Tested by running a real example and made sure that nothing in the
    config file changes other than the configs to edit.
  • fix: remove duplicated parallel FeatureSpec (#7823)
    regression: #7589
    
    Signed-off-by: Koichi Shiraishi <zchee.io@gmail.com>
  • fix: Upgrade @modelcontextprotocol/sdk to ^1.24.0 (#7817)
    ## What?
    Upgrades @modelcontextprotocol/sdk from ^1.20.2 to ^1.24.0 in the
    TypeScript SDK's devDependencies.
    
    ## Why?
    Related to #7737 - keeping development dependencies up to date with the
    latest MCP SDK version that includes the fix for CVE-2025-66414.
    
    Note: This change does not address the CVE for Codex users, as the MCP
    SDK is only in devDependencies here. The actual MCP integration that
    would be affected by the CVE is in the Rust codebase.
    
    ## How?
    •  Updated dependency version in sdk/typescript/package.json
    •  Ran pnpm install to update lockfile
    •  Fixed formatting (added missing newline in package.json)
    
    ## Related Issue
    Related to #7737
    
    ## Test Status
    ⚠️ After this upgrade, 2 additional tests timeout (1 test was already
    failing on main):
    •  tests/run.test.ts: "sends previous items when run is called twice" 
    •  tests/run.test.ts: "resumes thread by id"
    • tests/runStreamed.test.ts: "sends previous items when runStreamed is
    called twice"
    
    Marking as draft to investigate test timeouts. Maintainer guidance would
    be appreciated.
    
    Co-authored-by: HalfonA <amit@miggo.io>
  • Removed experimental "command risk assessment" feature (#7799)
    This experimental feature received lukewarm reception during internal
    testing. Removing from the code base.
  • refactoring with_escalated_permissions to use SandboxPermissions instead (#7750)
    helpful in the future if we want more granularity for requesting
    escalated permissions:
    e.g when running in readonly sandbox, model can request to escalate to a
    sandbox that allows writes
  • Express rate limit warning as % remaining (#7795)
    <img width="342" height="264" alt="image"
    src="https://github.com/user-attachments/assets/f1e932ff-c550-47b3-9035-0299ada4998d"
    />
    
    Earlier, the warning was expressed as consumed% whereas status was
    expressed as remaining%. This change brings the two into sync to
    minimize confusion and improve visual consistency.
  • Revert "Revert "feat: windows codesign with Azure trusted signing"" (#7757)
    Reverts openai/codex#7753
    
    Updated the tag ref matching at
    https://github.com/openai/openai/pull/594858 so that release with tag
    change can be picked up correctly.
  • Elevated Sandbox 1 (#7788)
    - updating helpers, refactoring some functions that will be used in the
    elevated sandbox
    - better logging
    - better and faster handling of ACL checks/writes
    - No functional change—legacy restricted-token sandbox
    remains the only path.
  • feat: support mcp in-session login (#7751)
    ### Summary
    * Added `mcpServer/oauthLogin` in app server for supporting in session
    MCP server login
    * Added `McpServerOauthLoginParams` and `McpServerOauthLoginResponse` to
    support above method with response returning the auth URL for consumer
    to open browser or display accordingly.
    * Added `McpServerOauthLoginCompletedNotification` which the app server
    would emit on MCP server login success or failure (i.e. timeout).
    * Refactored rmcp-client oath_login to have the ability on starting a
    auth server which the codex_message_processor uses for in-session auth.
  • fix: introduce AbsolutePathBuf and resolve relative paths in config.toml (#7796)
    This PR attempts to solve two problems by introducing a
    `AbsolutePathBuf` type with a special deserializer:
    
    - `AbsolutePathBuf` attempts to be a generally useful abstraction, as it
    ensures, by constructing, that it represents a value that is an
    absolute, normalized path, which is a stronger guarantee than an
    arbitrary `PathBuf`.
    - Values in `config.toml` that can be either an absolute or relative
    path should be resolved against the folder containing the `config.toml`
    in the relative path case. This PR makes this easy to support: the main
    cost is ensuring `AbsolutePathBufGuard` is used inside
    `deserialize_config_toml_with_base()`.
    
    While `AbsolutePathBufGuard` may seem slightly distasteful because it
    relies on thread-local storage, this seems much cleaner to me than using
    than my various experiments with
    https://docs.rs/serde/latest/serde/de/trait.DeserializeSeed.html.
    Further, since the `deserialize()` method from the `Deserialize` trait
    is not async, we do not really have to worry about the deserialization
    work being spread across multiple threads in a way that would interfere
    with `AbsolutePathBufGuard`.
    
    To start, this PR introduces the use of `AbsolutePathBuf` in
    `OtelTlsConfig`. Note how this simplifies `otel_provider.rs` because it
    no longer requires `settings.codex_home` to be threaded through.
    Furthermore, this sets us up better for a world where multiple
    `config.toml` files from different folders could be loaded and then
    merged together, as the absolutifying of the paths must be done against
    the correct parent folder.
  • feat(tui2): add feature-flagged tui2 frontend (#7793)
    Introduce a new codex-tui2 crate that re-exports the existing
    interactive TUI surface and delegates run_main directly to codex-tui.
    This keeps behavior identical while giving tui2 its own crate for future
    viewport work.
    
    Wire the codex CLI to select the frontend via the tui2 feature flag.
    When the merged CLI overrides include features.tui2=true (e.g. via
    --enable tui2), interactive runs are routed through
    codex_tui2::run_main; otherwise they continue to use the original
    codex_tui::run_main.
    
    Register Feature::Tui2 in the core feature registry and add the tui2
    crate and dependency entries so the new frontend builds alongside the
    existing TUI.
    
    This is a stub that only wires up the feature flag for this.
    
    <img width="619" height="364" alt="image"
    src="https://github.com/user-attachments/assets/4893f030-932f-471e-a443-63fe6b5d8ed9"
    />
  • Add vim-style navigation for CLI option selection (#7784)
    ## Summary
    
    Support "j" and "k" keys as aliases for "down" and "up" so vim users
    feel loved. Only support these keys when the selection is not
    searchable.
    
    ## Testing
    - env -u NO_COLOR TERM=xterm-256color cargo test -p codex-tui
    
    
    ------
    [Codex
    Task](https://chatgpt.com/codex/tasks/task_i_693771b53bc8833088669060dfac2083)
  • Fix: gracefully error out for unsupported images (#7478)
    Fix for #7459 
    ## What
    Since codex errors out for unsupported images, stop attempting to
    base64/attach them and instead emit a clear placeholder when the file
    isn’t a supported image MIME.
    
    ## Why
    Local uploads for unsupported formats (e.g., SVG/GIF/etc.) were
    dead-ending after decode failures because of the 400 retry loop. Users
    now get an explicit “cannot attach … unsupported image format …”
    response.
    
    ## How
    Replace the fallback read/encode path with MIME detection that bails out
    for non-image or unsupported image types, returning a consistent
    placeholder. Unreadable and invalid images still produce their existing
    error placeholders.
  • Add vim navigation keys to transcript pager (#7550)
    ## Summary
    - add vim-style pager navigation for transcript overlays (j/k,
    ctrl+f/b/d/u) without removing existing keys
    - add shift-space to page up
    
    ------
    [Codex
    Task](https://chatgpt.com/codex/tasks/task_i_69309d26da508329908b2dc8ca40afb7)
  • fix: allow sendmsg(2) and recvmsg(2) syscalls in our Linux sandbox (#7779)
    This changes our default Landlock policy to allow `sendmsg(2)` and
    `recvmsg(2)` syscalls. We believe these were originally denied out of an
    abundance of caution, but given that `send(2)` nor `recv(2)` are allowed
    today [which provide comparable capability to the `*msg` equivalents],
    we do not believe allowing them grants any privileges beyond what we
    already allow.
    
    Rather than using the syscall as the security boundary, preventing
    access to the potentially hazardous file descriptor in the first place
    seems like the right layer of defense.
    
    In particular, this makes it possible for `shell-tool-mcp` to run on
    Linux when using a read-only sandbox for the Bash process, as
    demonstrated by `accept_elicitation_for_prompt_rule()` now succeeding in
    CI.
  • Vendor ConPtySystem (#7656)
    The repo we were depending on is very large and we need very small part
    of it.
    
    ---------
    
    Co-authored-by: Pavel <pavel@krymets.com>
  • Fix Nix cargo output hashes for rmcp and filedescriptor (#7762)
    Fixes #7759:
    
    - Drop the stale `rmcp` entry from `codex-rs/default.nix`’s
    `cargoLock.outputHashes` since the crate now comes from crates.io and no
    longer needs a git hash.
    - Add the missing hash for the filedescriptor-0.8.3 git dependency (from
    `pakrym/wezterm`) so `buildRustPackage` can vendor it.
  • Fix transcript pager page continuity (#7363)
    ## What
    
    Fix PageUp/PageDown behaviour in the Ctrl+T transcript overlay so that
    paging is continuous and reversible, and add tests to lock in the
    expected behaviour.
    
    ## Why
    
    Today, paging in the transcript overlay uses the raw viewport height
    instead of the effective content height after layout. Because the
    overlay reserves some rows for chrome (header/footer), this can cause:
    
    - PageDown to skip transcript lines between pages.
    - PageUp/PageDown not to “round-trip” cleanly (PageDown then PageUp does
    not always return to the same set of visible lines).
    
    This shows up when inspecting longer transcripts via Ctrl+T; see #7356
    for context.
    
    ## How
    
    - Add a dedicated `PagerView::page_step` helper that computes the page
    size from the last rendered content height and falls back to
    `content_area(viewport_area).height` when that is not yet available.
    - Use `page_step(...)` for both PageUp and PageDown (including SPACE) so
    the scroll step always matches the actual content area height, not the
    full viewport height.
    - Add a focused test
    `transcript_overlay_paging_is_continuous_and_round_trips` that:
      - Renders a synthetic transcript with numbered `line-NN` rows.
    - Asserts that successive PageDown operations show continuous line
    numbers (no gaps).
    - Asserts that PageDown+PageUp and PageUp+PageDown round-trip correctly
    from non-edge offsets.
    
    The change is limited to `codex-rs/tui/src/pager_overlay.rs` and only
    affects the transcript overlay paging semantics.
    
    ## Related issue
    
    - #7356
    
    ## Testing
    
    On Windows 11, using PowerShell 7 in the repo root:
    
    ```powershell
    cargo test
    cargo clippy --tests
    cargo fmt -- --config imports_granularity=Item
    ```
    
    - All tests passed.
    - `cargo clippy --tests` reported some pre-existing warnings that are
    unrelated to this change; no new lints were introduced in the modified
    code.
    
    ---------
    
    Signed-off-by: muyuanjin <24222808+muyuanjin@users.noreply.github.com>
    Co-authored-by: Eric Traut <etraut@openai.com>
  • use chatgpt provider for /models (#7756)
    This endpoint only exist on chatgpt
  • override instructions using ModelInfo (#7754)
    Making sure we can override base instructions
  • fix: pre-main hardening logic must tolerate non-UTF-8 env vars (#7749)
    We received a bug report that Codex CLI crashes when an env var contains
    a non-ASCII character, or more specifically, cannot be decoded as UTF-8:
    
    ```shell
    $ RUST_BACKTRACE=full RÖDBURK=1 codex
    
    thread '<unnamed>' panicked at library/std/src/env.rs:162:57:
    called `Result::unwrap()` on an `Err` value: "RÃ\xB6DBURK"
    stack backtrace:
       0:        0x101905c18 - __mh_execute_header
       1:        0x1012bd76c - __mh_execute_header
       2:        0x1019050e4 - __mh_execute_header
       3:        0x101905ad8 - __mh_execute_header
       4:        0x101905874 - __mh_execute_header
       5:        0x101904f38 - __mh_execute_header
       6:        0x1019347bc - __mh_execute_header
       7:        0x10193472c - __mh_execute_header
       8:        0x101937884 - __mh_execute_header
       9:        0x101b3bcd0 - __mh_execute_header
      10:        0x101b3c0bc - __mh_execute_header
      11:        0x101927a20 - __mh_execute_header
      12:        0x1005c58d8 - __mh_execute_header
    
    thread '<unnamed>' panicked at library/core/src/panicking.rs:225:5:
    panic in a function that cannot unwind
    stack backtrace:
       0:        0x101905c18 - __mh_execute_header
       1:        0x1012bd76c - __mh_execute_header
       2:        0x1019050e4 - __mh_execute_header
       3:        0x101905ad8 - __mh_execute_header
       4:        0x101905874 - __mh_execute_header
       5:        0x101904f38 - __mh_execute_header
       6:        0x101934794 - __mh_execute_header
       7:        0x10193472c - __mh_execute_header
       8:        0x101937884 - __mh_execute_header
       9:        0x101b3c144 - __mh_execute_header
      10:        0x101b3c1a0 - __mh_execute_header
      11:        0x101b3c158 - __mh_execute_header
      12:        0x1005c5ef8 - __mh_execute_header
    thread caused non-unwinding panic. aborting.
    ```
    
    I discovered I could reproduce this on a release build, but not a dev
    build, so between that and the unhelpful stack trace, my mind went to
    the pre-`main()` logic we run in prod builds. Sure enough, we were
    operating on `std::env::vars()` instead of `std::env::vars_os()`, which
    is why the non-UTF-8 environment variable was causing an issue.
    
    This PR updates the logic to use `std::env::vars_os()` and adds a unit
    test.
    
    And to be extra sure, I also verified the fix works with a local release
    build:
    
    ```
    $ cargo build --bin codex --release
    $ RÖDBURK=1 ./target/release/codex --version
    codex-cli 0.0.0
    ```
  • Remove legacy ModelInfo and merge it with ModelFamily (#7748)
    This is a step towards removing the need to know `model` when
    constructing config. We firstly don't need to know `model_info` and just
    respect if the user has already set it. Next step, we don't need to know
    `model` unless the user explicitly set it in `config.toml`
  • feat: windows codesign with Azure trusted signing (#7675)
    ### Summary
    Set up codesign for windows dist with [Azure trusted
    signing](https://azure.microsoft.com/en-us/products/trusted-signing) and
    [its github action
    integration](https://github.com/Azure/trusted-signing-action).
  • Add formatting client version to the x.x.x style. (#7711)
    To avoid regression with special builds like alphas
  • Restore status header after stream recovery (#7660)
    ## Summary
    - restore the previous status header when a non-error event arrives
    after a stream retry
    - add a regression test to ensure the reconnect banner clears once
    streaming resumes
    
    ## Testing
    - cargo fmt -- --config imports_granularity=Item
    - cargo clippy --fix --all-features --tests --allow-dirty -p codex-tui
    - NO_COLOR=0 cargo test -p codex-tui *(fails: vt100 color assertion
    tests expect colored cells but the environment returns Default colors
    even with NO_COLOR cleared and TERM/COLORTERM set)*
    
    ------
    [Codex
    Task](https://chatgpt.com/codex/tasks/task_i_69337f8c77508329b3ea85134d4a7ac7)
  • Enhance model picker (#7709)
    # 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.
  • updating app server types to support execpoilcy amendment (#7747)
    also includes minor refactor merging `ApprovalDecision` with
    `CommandExecutionRequestAcceptSettings`
  • load models from disk and set a ttl and etag (#7722)
    # 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.
  • fix(tui): add missing Ctrl+n/Ctrl+p support to ListSelectionView (#7629)
    ## Summary
    
    Extend Ctrl+n/Ctrl+p navigation support to selection popups (model
    picker, approval mode, etc.)
    
    This is a follow-up to #7530, which added Ctrl+n/Ctrl+p navigation to
    the textarea.
    The same keybindings were missing from `ListSelectionView`, causing
    inconsistent behavior
      when navigating selection popups.
    
      ## Related
    
      - #7530 - feat(tui): map Ctrl-P/N to arrow navigation in textarea
    
      ## Changes
    
      - Added Ctrl+n as alternative to Down arrow in selection popups
      - Added Ctrl+p as alternative to Up arrow in selection popups
      - Added unit tests for the new keybindings
    
      ## Test Plan
    
      - [x] `cargo test -p codex-tui list_selection_view` - all tests pass
    - [x] Manual testing: verified Ctrl+n/p navigation works in model
    selection popup
    
    ---------
    
    Co-authored-by: Eric Traut <etraut@openai.com>
  • feat: linux codesign with sigstore (#7674)
    ### Summary
    Linux codesigning with sigstore and test run output at
    https://github.com/openai/codex/actions/runs/19994328162?pr=7662.
    
    Sigstore is one of the few ways for codesigning for linux platform.
    Linux is open sourced and therefore binary/dist validation comes with
    the build itself instead of a central authority like Windows or Mac.
    Alternative here is to use GPG which again a public key included with
    the bundle for validation. Advantage with Sigstore is that we do not
    have to create a private key for signing but rather with[ keyless
    signing](https://docs.sigstore.dev/cosign/signing/overview/).
    
    This should be sufficient for us at this point and if we want to we can
    support GPG in the future.
  • Make the device auth instructions more clear. (#7745)
    - [x] Make the device auth instructions more clear.