Commit Graph

394 Commits

  • fix: Auto-recover from corrupted sqlite databases (#26859)
    Further investigation of the sqlite incidents showed that the problems
    are due to corruption from the older version of SQLite that we recently
    upgraded, and that the data is truly corrupted in the root database --
    recovery of all data is not possible. Given that the data is
    reconstructable from the rollouts on disk, we should just auto-backup
    the database and let codex rebuild the rollout info from the disk
    rollouts.
    
    The new behavior is that appserver auto-backs-up and rebuilds (with logs
    reflecting that behavior). The CLI now pops a message letting you know
    this happened and the paths of the backed-up corrupt db and the new
    database. There is also context added so that the desktop app can read
    the rebuild info from it and inform the user with it.
  • feat(doctor): report editor and pager environment (#27081)
    ## Background
    
    This was prompted by
    [#26858](https://github.com/openai/codex/issues/26858), where the
    attached doctor report did not include the editor selection and I had to
    [ask which editor was in
    use](https://github.com/openai/codex/issues/26858#issuecomment-4653829891)
    before investigating the external-editor newline issue. Capturing these
    variables in doctor makes that context available up front in future
    reports.
    
    `codex doctor` is intended to capture enough local context to diagnose
    startup and terminal behavior, but it did not report the environment
    variables that select an external editor or configure command pagers.
    
    The TUI [prefers `VISUAL` over
    `EDITOR`](https://github.com/openai/codex/blob/56554904babcaacf4444a2cc90716880837dff7c/codex-rs/tui/src/external_editor.rs#L31-L38),
    so missing or unexpected values can explain why the external-editor
    shortcut fails or launches the wrong command. Pager values are also
    useful inherited-shell context even though [unified exec normalizes its
    effective pager variables to
    `cat`](https://github.com/openai/codex/blob/56554904babcaacf4444a2cc90716880837dff7c/codex-rs/core/src/unified_exec/process_manager.rs#L60-L70).
    
    These variables can contain arbitrary command arguments or inline
    environment assignments. The human report is local, but `codex doctor
    --json` may be attached to feedback, so the machine-readable report
    should not include their raw contents.
    
    ## What Changed
    
    - Report `VISUAL` and `EDITOR` in the system environment details, using
    `not set` when either variable is absent.
    - Report inherited `PAGER`, `GIT_PAGER`, `GH_PAGER`, and `LESS` values
    when present.
    - Preserve full values in local human output while reducing these fields
    to `set` or `not set` in redacted JSON output.
    - Add structured check, JSON-redaction, rendered-output, and snapshot
    coverage.
    
    ## How to Test
    
    1. From `codex-rs`, run Codex with explicit editor and pager variables:
    
       ```sh
    env VISUAL='code --wait' EDITOR=vim PAGER='less -R' GIT_PAGER=delta
    GH_PAGER=less LESS=-FRX \
         cargo run -p codex-cli --bin codex -- doctor --no-color
       ```
    
    2. Confirm the `system` details show the full values for all six
    variables.
    3. Unset the pager variables and rerun the command. Confirm pager rows
    are omitted while missing editor variables are shown as `not set`.
    4. Run the same configured environment with `doctor --json`. Confirm
    each configured editor or pager field is reported as `set` and none of
    the raw commands or arguments appear in the JSON.
    
    Targeted tests:
    
    - `just test -p codex-cli` (279 tests passed)
  • Enforce configured network proxy in codex sandbox (#27035)
    ## Why
    
    `codex sandbox` can start a network proxy from a configured permission
    profile. Previously, sandbox-level containment was tied to managed
    network requirements rather than whether a proxy was actually active.
    This meant config-driven proxy policies were not consistently enforced
    as the sandbox's only network path.
    
    ## What changed
    
    - Enable proxy-only network containment whenever `codex sandbox` starts
    a network proxy.
    - Apply the same active-proxy check to the macOS and Linux sandbox
    paths.
    - Add a Linux regression test that verifies a sandboxed command cannot
    establish a direct connection while the configured proxy is active.
    
    ## Test plan
    
    - `just test -p codex-cli debug_sandbox::tests`
    - `sandbox_with_network_proxy_blocks_direct_loopback_access` runs on
    Linux to cover the config-driven proxy path end to end.
  • cli: add -P sandbox permissions profile alias (#27054)
    ## Why
    
    `codex sandbox --permissions-profile` is useful when running commands
    under a named permissions profile, but the long option is cumbersome for
    a debugging-oriented command. `-p` is already used for the config
    profile selector, so `-P` gives the permissions profile selector a
    compact, non-conflicting alias.
    
    ## What Changed
    
    - Added `short = 'P'` to the `permissions_profile` option for the macOS,
    Linux, and Windows sandbox command structs in
    [`codex-rs/cli/src/lib.rs`](https://github.com/openai/codex/blob/6d9f9c5cdcaa0a156aa2dabbde259ae5e9e8bc0b/codex-rs/cli/src/lib.rs#L29-L112).
    - Added parser coverage for `codex sandbox -P :workspace -- echo` in
    [`codex-rs/cli/src/main.rs`](https://github.com/openai/codex/blob/6d9f9c5cdcaa0a156aa2dabbde259ae5e9e8bc0b/codex-rs/cli/src/main.rs#L2883-L2896).
    
    ## Verification
    
    - `just test -p codex-cli` passed, including the new
    `sandbox_parses_permissions_profile_short_alias` parser test.
  • [plugins] Expose marketplace source in marketplace list JSON (#27009)
    ## Summary
    - Follow-up to #26417 and #26631
    - Add `marketplaceSource` to `codex plugin marketplace list --json`
    entries for configured marketplaces
    - Reuse the existing `marketplaceSource` shape from `codex plugin list
    --json`
    - Keep human-readable marketplace list output unchanged
    - Add CLI coverage for configured local and git marketplace sources
    
    Example:
    
    ```json
    {
      "marketplaces": [
        {
          "name": "debug",
          "root": "/path/to/.codex/.tmp/marketplaces/debug",
          "marketplaceSource": {
            "sourceType": "git",
            "source": "https://example.com/acme/agent-skills.git"
          }
        }
      ]
    }
    ```
    
    ## Validation
    - `just fmt`
    - `just fix -p codex-cli`
    - `just test -p codex-cli marketplace_list`
    - `just test -p codex-cli`
  • fix(tui): accept prompts with resume and fork (#26818)
    ## Why
    
    Interactive `codex resume` and `codex fork` expose both a session ID
    positional and an initial prompt positional. With `--last`, Clap still
    assigns the first positional to the session ID, so a command such as
    `codex fork --last "/compact focus on auth"` either fails parsing or
    attempts to look up the prompt as a session ID instead of sending it to
    the latest session.
    
    This makes it impossible to select the latest session and immediately
    provide a follow-up prompt, even though `codex exec resume --last`
    already supports that workflow.
    
    <img width="1746" height="1024" alt="CleanShot 2026-06-06 at 17 00
    47@2x"
    src="https://github.com/user-attachments/assets/86885c07-a23c-48ee-b0ee-47f2484f6eb7"
    />
    
    ## What Changed
    
    - Reinterpret the first positional as the initial prompt when
    interactive `resume --last` or `fork --last` is used and no explicit
    second prompt was parsed.
    - Preserve the existing `resume SESSION_ID PROMPT` and `fork SESSION_ID
    PROMPT` behavior.
    - Add parser-level regression coverage for latest-session and
    explicit-session prompt forms.
    
    ## How to Test
    
    1. Start an interactive session, exit it, then run `codex resume --last
    "continue from the latest session"`.
    2. Confirm Codex resumes the latest session and submits the supplied
    prompt instead of treating it as a session ID.
    3. Run `codex fork --last "take a different approach"`.
    4. Confirm Codex forks the latest session and submits the supplied
    prompt.
    5. Also verify `codex resume SESSION_ID "continue here"` and `codex fork
    SESSION_ID "branch here"` still target the explicit session and submit
    the prompt.
    
    Targeted tests:
    - `just test -p codex-cli` (267 passed)
  • [codex-rs] support v2 personal access tokens (#25731)
    ## Summary
    
    - add v2 personal access token support for `codex login
    --with-access-token` and `CODEX_ACCESS_TOKEN`
    - classify opaque `at-` tokens separately from legacy Agent Identity
    JWTs
    - hydrate required ChatGPT account metadata through AuthAPI
    `/v1/user-auth-credential/whoami`
    - use PATs directly as bearer tokens while preserving existing ChatGPT
    account surfaces
    - expose PAT-backed auth as the explicit `personalAccessToken`
    app-server auth mode
    
    ## Implementation
    
    PAT auth is intentionally small and stateless. Loading a PAT performs
    one AuthAPI metadata request, stores the hydrated metadata in the
    in-memory auth object, and redacts the secret from debug output. Legacy
    Agent Identity JWT handling remains unchanged. The shared access-token
    classifier lives in a private neutral module because it dispatches
    between both credential types.
    
    PAT hydration fails closed when AuthAPI omits any required metadata,
    including email. Hydrated metadata is intentionally not persisted:
    startup performs a live `whoami` preflight so revoked tokens or changed
    account metadata are not accepted from a stale cache.
    
    ## Workspace restriction scope
    
    This change intentionally does **not** apply
    `forced_chatgpt_workspace_id` to PAT authentication. The setting is a
    client-side config guardrail, not an authorization boundary, and PAT
    does not currently require workspace-ID parity. The PAT login and
    `CODEX_ACCESS_TOKEN` paths therefore validate through AuthAPI without
    threading workspace-restriction state through access-token loading.
    Existing workspace checks for non-PAT auth remain on their established
    paths.
    
    ## App-server compatibility
    
    The public app-server `AuthMode` is shared across v1 and v2, and
    PAT-backed auth reports `personalAccessToken` through both APIs.
    Following human review, this intentionally removes the temporary v1
    compatibility mapping that reported PATs as `chatgpt`; the deprecated v1
    API is kept in parity with v2 rather than maintaining a separate closed
    enum. Clients with exhaustive auth-mode handling in either API version
    must add the new case and should generally treat it as ChatGPT-backed
    unless they need PAT-specific behavior.
    
    The v1 auth-status response still omits the raw PAT when `includeToken`
    is requested because that response cannot carry the account metadata
    needed to reuse the credential safely. Persisted PAT auth also omits the
    new enum value so older Codex builds can deserialize `auth.json` and
    infer PAT auth from the credential field after a rollback.
    
    ## Validation
    
    Latest review-fix validation:
    
    - `CARGO_INCREMENTAL=0 just test -p codex-login` (126 passed)
    - `CARGO_INCREMENTAL=0 just test -p codex-cli` (263 passed)
    - `CARGO_INCREMENTAL=0 just test -p codex-cli
    stored_auth_validation_handles_personal_access_token`
    - `CARGO_INCREMENTAL=0 just test -p codex-app-server-protocol` (226
    passed)
    - `CARGO_INCREMENTAL=0 just test -p codex-models-manager
    refresh_available_models_uses_remote_only_catalog_for_chatgpt_auth`
    - `CARGO_INCREMENTAL=0 just test -p codex-tui
    existing_non_oauth_chatgpt_login_counts_as_signed_in`
    - `CARGO_INCREMENTAL=0 just fix -p codex-login -p
    codex-app-server-protocol -p codex-models-manager -p codex-tui -p
    codex-cli`
    - `just fmt`
    - `git diff --check`
    
    The broader `codex-tui` suite previously compiled and ran 2,834 tests.
    Three unrelated environment-sensitive guardian/IDE-socket tests failed
    after retries; the PAT-relevant TUI coverage passed.
  • Add JSON output for plugin subcommands (#26631)
    ## Summary
    - Follow-up to #25330 and #26417
    - Add `--json` output for `codex plugin add` and `codex plugin remove`
    - Add `--json` output for `codex plugin marketplace
    add/list/upgrade/remove`
    - Keep existing human-readable output unchanged
    - Keep existing error handling/stderr behavior unchanged; `--json`
    changes successful stdout output only
    - Align marketplace add/remove JSON field names with the existing
    app-server protocol shape
    - Add CLI coverage for plugin and marketplace JSON outputs
    
    ## Validation
    - `just fmt`
    - `just fix -p codex-cli`
    - `just test -p codex-cli`
  • Open Windows app workspaces via deep link (#26500)
    ## Summary
    
    Fixes #26423.
    
    On Windows, `codex app PATH` detected Codex Desktop and launched the app
    shell target, then only printed a manual instruction to open the
    workspace. The Desktop app already supports
    `codex://threads/new?path=...`, so the CLI can open the requested
    workspace directly.
    
    This updates the Windows launcher to normalize the workspace path,
    encode it into a `codex://threads/new` deep link, and open that URL when
    Codex Desktop is installed. The installer fallback still opens the
    Windows installer and prints the workspace path for after installation.
  • Expose configured marketplace source in plugin list JSON (#26417)
    ## Summary
    - Follow-up to #25330
    - Add `marketplaceSource` to `codex plugin list --json` entries for
    configured marketplaces
    - Keep the existing per-plugin `source` field unchanged; this still
    reports the local plugin source path
    - Include only the configured marketplace `sourceType` and `source` from
    `config.toml`
    - Keep human-readable output unchanged
    - Add CLI coverage for configured local and git marketplace sources
    
    Example:
    
    ```json
    {
      "source": {
        "source": "local",
        "path": "/path/to/.codex/.tmp/marketplaces/debug/plugins/sample"
      },
      "marketplaceSource": {
        "sourceType": "git",
        "source": "https://example.com/acme/agent-skills.git"
      }
    }
    ```
    
    ## Validation
    - `just fmt`
    - `just fix -p codex-cli`
    - `just test -p codex-cli plugin_list`
  • [codex] Add plugin list JSON output (#25330)
    ## Summary
    - add `--json` output to `codex plugin list` with `installed` and
    `available` arrays
    - add `--available` for JSON output only; using it without `--json` is
    rejected
    - keep the existing non-JSON table output unchanged
    - add CLI coverage for JSON installed/available output and the
    `--available`/`--json` requirement
    
    ## Validation
    - `just test -p codex-cli plugin_list`
    - `just fix -p codex-cli`
    - `git diff --check`
    
    Note: `just fmt` ran Rust formatting first, then failed in the Python
    ruff step because `openai-codex-cli-bin==0.132.0` has no wheel for this
    Linux platform.
  • Wire managed MITM CA trust into child env (#22668)
    ## Stack
    1. Parent PR: #18240 uses named MITM permissions config.
    2. This PR wires managed MITM CA trust into spawned child processes.
    
    ## Why
    When Codex terminates HTTPS for limited mode or MITM hooks, child HTTPS
    clients need to trust Codex's managed MITM CA. Exporting proxy URLs
    alone is not enough, but blindly replacing user CA settings would be
    wrong: it can break custom enterprise/test roots, leak unreadable CA
    files into generated bundles, or make the child env disagree with its
    sandbox policy.
    
    ## Summary
    1. Build immutable managed CA bundles under `$CODEX_HOME/proxy` that
    include native roots, the managed MITM CA, and only inherited or
    command-scoped CA bundles the child is allowed to read.
    2. Export curated CA env vars alongside managed proxy env vars while
    preserving user CA override semantics, including nested Codex
    `SSL_CERT_FILE` precedence.
    3. Thread generated CA bundle paths into child sandbox readable roots,
    including debug sandbox execution, so the exported env vars work inside
    sandboxed commands.
    4. Remove only Codex-generated MITM CA bundle env when a child
    intentionally drops managed proxying for escalation or no-proxy retry.
    5. Document the managed CA bundle behavior and cover env injection,
    per-child bundle generation, sandbox readable roots, and no-proxy
    cleanup in tests.
    
    ## Validation
    1. Ran `just test -p codex-network-proxy`.
    2. Ran `just test -p codex-protocol`.
    3. Ran `just fix -p codex-network-proxy -p codex-protocol`.
    4. Tried focused `codex-core` validation, but the crate currently fails
    to compile in `core/tests/suite/guardian_review.rs` because an existing
    `Op::UserInput` initializer is missing `additional_context`.
    
    ---------
    
    Co-authored-by: Eva Wong <evawong@openai.com>
  • Add reasoning-only status surface item (#25504)
    Closes #24886.
    
    ## Why
    Users can configure the TUI status line and terminal title with
    `model-with-reasoning`, but issue #24886 asks for a compact
    reasoning-only item. That lets a setup show just `default`, `low`,
    `medium`, `high`, or `xhigh` without repeating the model name.
    
    ## What changed
    - Added a `reasoning` item for `/statusline` and `/title` setup flows.
    - Rendered the item from the effective reasoning effort, including
    collaboration-mode overrides.
    - Registered `reasoning` with `codex doctor` so Codex-generated
    terminal-title config is not reported as invalid.
    - Updated TUI setup snapshots so the picker previews include the new
    item.
  • Use deep links for macOS codex app paths (#25485)
    ## Why
    
    `codex app [PATH]` is the documented CLI entry point for opening Codex
    Desktop on a workspace. Recent desktop builds can focus the app while
    failing to honor paths passed as macOS document-open arguments via `open
    -a Codex.app <workspace>`, which broke `codex app .` for users. See
    #25333; related report: #25166.
    
    The desktop app still supports the explicit
    `codex://threads/new?path=...` route, so the CLI should use that
    app-owned launch surface instead of depending on folder-open event
    delivery.
    
    ## What Changed
    
    - Build a `codex://threads/new?path=<workspace>` URL in the macOS app
    launcher.
    - Pass that URL to `open -a <Codex.app>` instead of passing the
    workspace path as a document argument.
    - Add coverage that workspace paths needing escaping round-trip through
    URL query encoding.
    
    ## Verification
    
    - `just test -p codex-cli codex_new_thread_url_encodes_workspace_path`
  • Add thread archive CLI commands (#25021)
    ## Problem
    
    Saved threads can already be archived through app-server RPCs, but the
    command line did not expose direct archive or unarchive commands.
    
    ## Solution
    
    Add `codex archive <thread>` and `codex unarchive <thread>`, resolving
    UUIDs or exact thread names before calling the existing `thread/archive`
    and `thread/unarchive` RPCs. The commands support scoped remote flags so
    callers can target remote app-server endpoints when archiving or
    unarchiving threads.
    
    This also fixes a long-standing bug in `codex resume <thread id>` and
    `codex fork <thread id>` that I found when testing the new commands.
    These operations shouldn't be allowed on archived sessions. They now
    fail with an error that tells the user to run `codex unarchive <thread
    id>` first.
    
    ## Verification
    
    Added app-server coverage for rejecting archived thread resume by id and
    checking that the error includes the matching `codex unarchive <thread
    id>` command.
  • Add Windows sandbox provisioning setup command (#24831)
    ## Why
    
    Some Windows users do not have local admin access, so they cannot
    complete the elevated portion of the Windows sandbox setup when Codex
    first needs it. This adds an alpha provisioning path that an admin or IT
    deployment script can run ahead of time for the Codex user.
    
    The intended managed-deployment shape is:
    
    ```powershell
    codex sandbox setup --elevated --user "$env:COMPUTERNAME\Alice" --codex-home "C:\Users\Alice\.codex"
    ```
    
    `--elevated` is treated as the requested sandbox setup level, not as
    proof that the process is elevated. The Windows sandbox setup
    orchestration still checks that the caller is actually elevated before
    launching the helper without a UAC prompt.
    
    ## What changed
    
    - Added `codex sandbox setup --elevated` with explicit user selection
    via either `--current-user` or `--user ... --codex-home ...`.
    - Moved the CLI implementation into `cli/src/sandbox_setup.rs` instead
    of growing `cli/src/main.rs`.
    - Added a Windows sandbox `ProvisionOnly` helper mode that runs the
    elevation-required provisioning work without requiring a workspace cwd
    or runtime sandbox policy.
    - Reused the existing elevated helper path for creating/updating sandbox
    users, configuring firewall/WFP rules, and applying sandbox directory
    ACLs.
    - Persisted `windows.sandbox = "elevated"` into the target `CODEX_HOME`
    so the desktop app does not show the initial sandbox setup banner after
    pre-provisioning succeeds.
    
    ## Validation
    
    - `cargo fmt -p codex-windows-sandbox -p codex-core -p codex-cli`
    - `cargo test -p codex-cli sandbox_setup --target-dir
    target\sandbox-setup-check`
    - `cargo test -p codex-windows-sandbox
    payload_accepts_provision_only_mode --target-dir
    target\sandbox-setup-check`
    - `git diff --check`
    - Manual Windows alpha flow with a standard local user (`Mandi Lavida`):
    ran the new setup command from an admin shell, verified the target
    `.codex` contents, sandbox marker/secrets, ACLs, firewall rules, and
    desktop startup without the sandbox setup banner once experimental
    network proxy requirements were disabled.
    
    ## Notes
    
    This intentionally does not solve later elevated update coordination for
    IT-managed deployments. The setup command can still apply provisioning
    updates when run again, but a broader coordination/process story is out
    of scope for this alpha.
  • windows-sandbox: pass workspace roots to runner (#24108)
    ## Why
    
    #23813 switches the Windows sandbox runner path to `PermissionProfile`,
    but it still left one runtime anchor for resolving symbolic
    `:workspace_roots` entries. That is not enough once a turn has multiple
    effective workspace roots: exact entries and deny globs under
    `:workspace_roots` need to be materialized for every runtime root before
    the command runner chooses token mode or builds ACL plans.
    
    ## What Changed
    
    - Replaces the Windows runner/setup `permission_profile_cwd` plumbing
    with `workspace_roots: Vec<AbsolutePathBuf>`.
    - Resolves Windows-local `PermissionProfile` data with
    `materialize_project_roots_with_workspace_roots(...)` instead of the
    single-cwd helper.
    - Threads `Config::effective_workspace_roots()` through core execution,
    unified exec, TUI setup/read-grant flows, app-server setup, app-server
    `command/exec`, and `debug sandbox` on Windows.
    - Preserves those workspace roots through the zsh-fork escalation
    executor instead of rebuilding them from `sandbox_policy_cwd`.
    - Makes `ExecRequest::new(...)` and the remaining
    `build_exec_request(...)` helper path take
    `windows_sandbox_workspace_roots` explicitly so new call sites cannot
    silently fall back to `vec![cwd]`.
    - Clarifies the `debug sandbox` non-Windows comment: remaining
    cwd-dependent resolution still uses `sandbox_policy_cwd`, while
    `:workspace_roots` entries are already materialized from config roots.
    - Updates elevated runner IPC `SpawnRequest` to send `workspace_roots`
    and bumps the framed IPC protocol version to `3` for the payload shape
    change.
    - Adds Windows-local resolver coverage for expanding exact and glob
    `:workspace_roots` entries across multiple roots, plus core helper
    coverage proving explicit roots are preserved.
    
    ## Verification
    
    - `cargo check -p codex-windows-sandbox -p codex-core -p codex-tui -p
    codex-cli -p codex-app-server`
    - `cargo test -p codex-windows-sandbox`
    - `cargo test -p codex-core windows_sandbox`
    - `cargo test -p codex-core unix_escalation`
    - `cargo test -p codex-app-server windows_sandbox`
    - `cargo test -p codex-tui windows_sandbox`
    - `cargo test -p codex-cli debug_sandbox`
    - `just test -p codex-core unified_exec`
    - `just test -p codex-core
    build_exec_request_preserves_windows_workspace_roots`
    - `env -u CODEX_NETWORK_PROXY_ACTIVE -u
    CODEX_NETWORK_ALLOW_LOCAL_BINDING just test -p codex-app-server --lib
    command_exec`
    - `just test -p codex-windows-sandbox`
    - `just test -p codex-exec sandbox`
    - `just fix -p codex-core -p codex-app-server -p codex-windows-sandbox`
    
    A local macOS cross-check with `cargo check --target
    x86_64-pc-windows-msvc ...` did not reach crate Rust code because native
    dependencies require Windows SDK headers (`windows.h` / `assert.h`) in
    this environment; Windows CI remains the real target validation.
    
    Two local targeted filters compile but do not run assertions on macOS:
    `env -u CODEX_NETWORK_PROXY_ACTIVE -u CODEX_NETWORK_ALLOW_LOCAL_BINDING
    just test -p codex-app-server --lib command_exec_processor` matched zero
    tests, and `just test -p codex-linux-sandbox landlock` matched zero
    tests because the landlock suite is Linux-only.
  • Add codex app-server --stdio alias (#24940)
    ## Summary
    - Add `--stdio` as a direct alias for `codex app-server --listen
    stdio://`.
    - Keep `--stdio` and `--listen` mutually exclusive.
    - Update the app-server README to document both forms.
  • Allow API-key auth for remote exec-server registration (#24666)
    ## Overview
    Allow remote `codex exec-server` registration to use existing API-key
    auth while restricting where those credentials can be sent.
    
    - Accept `CodexAuth::ApiKey` for the normal `--remote` registration
    path.
    - Restrict API-key remote registration to HTTPS `openai.com` and
    `openai.org` hosts and subdomains, with explicit HTTP loopback support
    for local development.
    - Disable registry registration redirects so credentials cannot be
    forwarded to an unvalidated destination.
    - Retain `--use-agent-identity-auth` as the explicit Agent Identity
    path.
    - Document remote registration using `CODEX_API_KEY`.
    
    ## Big picture
    Callers can now provide an API key directly to `exec-server`
    registration without first establishing ChatGPT login state:
    
    ```sh
    CODEX_API_KEY="$OPENAI_API_KEY" \
    codex exec-server \
      --remote "https://<host>.openai.org/api" \
      --environment-id "$ENVIRONMENT_ID"
    ```
    
    ## Validation
    - `cargo fmt --all` (`just fmt` is not installed on this host)
    - `cargo test -p codex-cli -p codex-exec-server`
  • Uprev Rust toolchain pins to 1.95.0 (#24684)
    ## Summary
    - Bump the workspace Rust toolchain from `1.93.0` to `1.95.0` across
    Cargo, Bazel, CI, release workflows, devcontainers, and the Codex
    environment config.
    - Refresh `MODULE.bazel.lock` so the Bazel Rust toolchain artifacts
    match the new version.
    - Leave purpose-specific toolchains unchanged, including the
    `argument-comment-lint` nightly and the upstream `rusty_v8` `1.91.0`
    build pin.
    - Includes fixes for new lints from `just fix` and a few codex-authored
    fixes for lints without a suggestion.
  • windows-sandbox: remove SandboxPolicy runner plumbing (#23813)
    ## Why
    
    The Windows sandbox runner still carried the old `SandboxPolicy`
    compatibility path even though core now computes `PermissionProfile`.
    That meant Windows command-runner execution could only see the legacy
    projection, so profile-only filesystem rules such as deny globs were not
    part of the runner input.
    
    ## What Changed
    
    - Removed the Windows-local `SandboxPolicy` parser/export and deleted
    `windows-sandbox-rs/src/policy.rs`.
    - Changed restricted-token capture/session setup, elevated setup,
    world-writable audit, read-root grant, and command-runner session APIs
    to accept `PermissionProfile` plus the profile cwd.
    - Bumped the elevated command-runner IPC protocol to version 2 because
    `SpawnRequest` now carries `permission_profile` /
    `permission_profile_cwd` instead of the legacy `policy_json_or_preset` /
    `sandbox_policy_cwd` fields.
    - Updated core exec, unified exec, debug-sandbox, TUI setup/grant flows,
    and app-server setup to pass the actual effective `PermissionProfile`.
    - Left regression coverage asserting the old IPC policy fields are
    absent and the runner serializes tagged `PermissionProfile` JSON.
    
    ## Verification
    
    - `cargo test -p codex-windows-sandbox`
    - `cargo test -p codex-core windows_sandbox`
    - `cargo test -p codex-app-server
    request_processors::windows_sandbox_processor`
    - `just fix -p codex-windows-sandbox -p codex-core -p codex-app-server
    -p codex-cli -p codex-tui`
    - `just fix -p codex-cli -p codex-tui`
    - `just fix -p codex-windows-sandbox -p codex-tui`
    - `rg "\\bSandboxPolicy\\b" codex-rs/windows-sandbox-rs` returned no
    matches.
    
    Note: `cargo test -p codex-cli` was attempted but did not reach crate
    tests because local disk filled while compiling dependencies (`No space
    left on device`). The targeted clippy pass compiled the affected CLI/TUI
    surfaces afterward.
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23813).
    * #24108
    * __->__ #23813
  • Move memory state to a dedicated SQLite DB (#24591)
    ## Summary
    
    Generated memory rows and their stage-one/stage-two job state currently
    live in `state_5.sqlite` alongside thread metadata. That makes memory
    cleanup and regeneration share the main state schema even though those
    rows are memory-pipeline data and can be rebuilt independently from the
    durable thread records.
    
    This PR moves the memory-owned tables into a dedicated
    `memories_1.sqlite` runtime database while keeping thread metadata in
    `state_5.sqlite`.
    
    ## Changes
    
    - Adds a separate memories DB runtime, migrator, path helpers, telemetry
    kind, and Bazel compile data for `state/memory_migrations`.
    - Introduces `MemoryStore` behind `StateRuntime::memories()` and moves
    memory table/job operations onto that store.
    - Drops the old memory tables from the state DB and recreates their
    schema in `state/memory_migrations/0001_memories.sql`.
    - Updates memory startup, citation usage tracking, rollout pollution
    handling, `debug clear-memories`, and app-server `memory/reset` to
    operate through the memories DB.
    - Preserves cross-DB behavior by hydrating thread metadata from the
    state DB when selecting visible memory outputs and checking stage-one
    staleness.
    
    ## Verification
    
    - Added/updated `codex-state` tests for deleted-thread memory visibility
    and already-polluted phase-two enqueue behavior.
    - Updated `debug clear-memories`, app-server `memory/reset`, and
    memories startup tests to seed and assert memory rows through
    `memories_1.sqlite`.
  • Add doctor thread inventory audit (#24305)
    ## Why
    
    Users have been reporting missing sessions in the app. The app server
    thread listing is backed by the SQLite state DB, but the durable source
    of truth for a thread still exists on disk as rollout JSONL. When the
    state DB is incomplete, doctor should be able to show the mismatch
    directly instead of leaving users with a generic state health result.
    
    ## What changed
    
    This adds a `threads` doctor check that compares active and archived
    rollout files under `CODEX_HOME` with rows in the SQLite `threads`
    table. The check reports missing rollout rows, stale DB rows, archive
    flag mismatches, duplicate rollout thread IDs, duplicate DB paths,
    source/provider summaries, and bounded samples of affected rollout
    paths.
    
    It also adds a read-only state audit helper in `codex-rs/state` so
    doctor can inspect thread rows without creating, migrating, or repairing
    the database.
    
    ## Sample output
    
    ```text
      ⚠ threads      rollout files are missing from the state DB
          default model provider   openai
          rollout DB active files  3910
          rollout DB archived files 2037
          rollout DB scan errors   0
          rollout DB malformed file names 0
          rollout DB scan cap reached false
          rollout DB rows          5499
          rollout DB active rows   3462
          rollout DB archived rows 2037
          rollout DB missing active rows 448
          rollout DB missing archived rows 0
          rollout DB stale rows    0
          rollout DB archive mismatches 0
          rollout DB duplicate rollout thread ids 0
          rollout DB duplicate DB paths 0
          rollout DB model providers openai=5359, lmstudio=35, mock_provider=33, lite_llm=26, proxy=26, ollama=15, lms=4, local-usage-limit=1
          rollout DB sources       vscode=2587, cli=1494, subagent:thread_spawn=577, subagent:other=502, exec=281, subagent:memory_consolidation=46, subagent:review=9, unknown=3
          rollout DB missing active sample ~/.codex/sessions/2026/0…857e-a923c712e066.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…877a-766dff25c68d.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…a8b1-7bbadc836f6e.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…a218-e6197f3f62f8.jsonl
          rollout DB missing active sample ~/.codex/sessions/2025/0…9011-7e30784f9932.jsonl
    ```
  • Report app-server version in codex doctor (#24311)
    ## Why
    
    We are seeing cases where users have an old background app-server still
    running. `codex doctor` already reports background server state, but
    without the running app-server version it is harder to diagnose
    behaviors that depend on the daemon build.
    
    ## What changed
    
    - Reused the app-server daemon's passive initialize probe through a
    narrow `probe_app_server_version` helper.
    - Updated the `codex doctor` Background Server section to report
    `app-server version: <version>` when the socket is reachable.
    - Preserved the not-running OK behavior and report `app-server version:
    unavailable (<short error>)` when a socket exists but the passive probe
    fails.
  • feat(doctor): add environment diagnostics (#24261)
    ## Why
    
    Issue #23031 was hard to diagnose from existing `codex doctor` output
    because support could not see the OS language, resolved Git install, Git
    repo metadata, Windows console mode/code page, or terminal-title inputs
    that affect the TUI startup path. This adds those read-only signals to
    `codex doctor` so Windows, Linux, and macOS reports carry the context
    needed to investigate similar terminal rendering regressions.
    
    Refs #23031
    
    ## What Changed
    
    - Add a `system.environment` check for OS type/version, OS language, and
    locale env vars.
    - Add a `git.environment` check for the selected Git executable, PATH
    Git candidates, version, exec path/build options, repository root,
    branch, `.git` entry, and `core.fsmonitor`.
    - Add Windows console code page and VT-processing mode details to
    terminal diagnostics.
    - Add a `terminal.title` check for configured/default title items and
    resolved project-title source/value.
    - Surface startup warning counts in config diagnostics and teach human
    output to render the new categories.
    
    ## How to Test
    
    1. On Windows, check out this branch and run `cargo run -p codex-cli --
    doctor --summary`.
    2. Confirm the Environment section includes `system`, `git`, `terminal`,
    and `title` rows.
    3. Run `cargo run -p codex-cli -- doctor --json`.
    4. Confirm the JSON contains `system.environment`, `git.environment`,
    and `terminal.title`; on Windows, confirm `terminal.env` details include
    console code pages and `VT processing` for stdout/stderr.
    5. From a non-git directory, run the same `doctor --json` command and
    confirm the Git check reports `repo detected: false` rather than
    warning.
    
    Targeted tests:
    
    - `cargo test -p codex-cli doctor`
    - `cargo test -p codex-cli`
  • Support OAuth options in codex mcp add (#24120)
    ## Summary
    - add `--oauth-client-id` and `--oauth-resource` options for streamable
    HTTP `codex mcp add` registrations
    - persist those options in MCP server config and use them during the
    immediate OAuth login flow
    - cover add-time serialization of both OAuth options in the CLI
    integration tests
    
    ## Testing
    - `just fmt`
    - `cargo test -p codex-cli`
    - `just fix -p codex-cli`
  • cli: support --profile for codex sandbox (#24110)
    ## Why
    
    `codex sandbox` now always runs the host sandbox backend, so it should
    accept the same profile selection mechanism as the rest of the runtime
    CLI surface. Without `--profile`, sandbox debugging can exercise only
    the default config stack unless users manually translate profile config
    into ad hoc `-c` overrides.
    
    Supporting `--profile` lets sandbox invocations load
    `$CODEX_HOME/<name>.config.toml`, including permission profile
    configuration, before resolving the sandbox policy for the command being
    run.
    
    ## What Changed
    
    - Added `--profile NAME` / `-p NAME` to the host-specific `codex
    sandbox` argument structs as `config_profile`.
    - Allowed root-level `codex --profile NAME sandbox ...` and made a
    sandbox-local `codex sandbox --profile NAME ...` override the root
    selection.
    - Threaded `LoaderOverrides` through sandbox config loading so selected
    config profile files participate in permission resolution before the
    legacy read-only fallback.
    - Documented the new sandbox flag in `codex-rs/README.md`.
    
    ## Verification
    
    - Added parser coverage for `codex sandbox --profile`.
    - Added sandbox config-loader coverage that verifies selected config
    profile loader overrides select the profile config rather than falling
    back to read-only.
    - Ran `cargo test -p codex-cli`.
  • cli: infer host sandbox backend (#24102)
    ## Why
    
    `codex sandbox` previously required an OS subcommand like `linux`,
    `macos`, or `windows`, even though the command can only run the sandbox
    backend available on the current host. That made the CLI imply a
    cross-OS choice that does not exist.
    
    ## What changed
    
    - Collapse `codex sandbox <os>` into `codex sandbox [COMMAND]...` by
    wiring the `sandbox` parser directly to the host-specific backend args
    with `cfg`.
    - Keep the existing backend runners for Seatbelt, Linux sandbox, and
    Windows restricted token.
    - Rename the public Windows debug sandbox runner to
    `run_command_under_windows_sandbox` for clarity.
    - Update the Rust sandbox docs and related README references to describe
    host OS selection and avoid pointing readers at legacy `sandbox_mode`
    config.
    
    ## Arg0 compatibility
    
    The `codex-linux-sandbox` helper path is still handled before normal CLI
    parsing. `arg0_dispatch()` checks whether the executable basename is
    `codex-linux-sandbox` and directly calls
    `codex_linux_sandbox::run_main()`, so removing the `sandbox linux`
    parser branch does not affect the arg0 helper flow.
    
    ## Verification
    
    - `cargo test -p codex-cli`
    - `cargo test -p codex-arg0`
    - `just fix -p codex-cli`
  • config: remove legacy profile v1 resolution (#24051)
    ## Why
    
    [#23883](https://github.com/openai/codex/pull/23883) moved user-facing
    `--profile` selection onto profile v2, and
    [#23886](https://github.com/openai/codex/pull/23886) removed the old CLI
    `config_profile` override path. Core still had a second legacy path:
    `profile = "..."` could select `[profiles.*]` values while runtime
    config was built. Keeping that resolver alive preserves the old
    precedence model and profile-carrying surfaces even though profile
    selection now points at `$CODEX_HOME/<name>.config.toml`.
    
    ## What
    
    - Reject legacy top-level `profile = "..."` config while loading runtime
    config, with an error that points callers at `--profile <name>` and
    `<name>.config.toml` in the [core load
    path](https://github.com/openai/codex/blob/3d923366eca10a29143623124c6c6e538f058269/codex-rs/core/src/config/mod.rs#L2524-L2531).
    - Remove the remaining profile-v1 merge points from runtime config
    resolution, including features, permissions, model/provider selection,
    web search, Windows sandbox settings, TUI settings, role reloads, and
    OSS provider lookup.
    - Drop the leftover profile override surface from
    [`ConfigOverrides`](https://github.com/openai/codex/blob/3d923366eca10a29143623124c6c6e538f058269/codex-rs/core/src/config/mod.rs#L2118-L2148)
    and from the MCP server `codex` tool schema.
    - Prune profile-precedence tests that only exercised the removed
    resolver and replace them with rejection coverage for the legacy
    selector.
    
    ## Testing
    
    - Not run in this metadata pass.
    - Added
    [`legacy_profile_selection_is_rejected`](https://github.com/openai/codex/blob/3d923366eca10a29143623124c6c6e538f058269/codex-rs/core/src/config/config_tests.rs#L7942-L7965)
    coverage for the new runtime guard.
  • mcp: surface profile migration guidance under --profile (#23890)
    ## Why
    
    `codex --profile <name> mcp ...` should reach the same profile-v2
    migration guard as runtime commands. Otherwise legacy
    `[profiles.<name>]` users see the generic command-scope rejection
    instead of the existing guidance to move settings into
    `$CODEX_HOME/<name>.config.toml`.
    
    ## What
    
    - Allow `codex mcp` through the `--profile` subcommand gate.
    - Pass profile loader overrides into the MCP entry point only to
    validate profile-v2 migration when a profile is present.
    - Keep MCP add/remove/list/get/login/logout behavior otherwise
    unchanged; this does not add profile-scoped MCP server management.
    - Cover the legacy profile migration error for `codex --profile work mcp
    list`.
    
    ## Testing
    
    - `cargo test -p codex-cli`
  • cli: remove legacy profile v1 plumbing (#23886)
    ## Why
    
    [#23883](https://github.com/openai/codex/pull/23883) moved the
    user-facing `--profile` flag onto profile v2. The shared CLI option
    layer still carried the old `config_profile` slot and several CLI
    entrypoints still copied that value into legacy config overrides.
    Leaving that path around makes the CLI surface look like it still
    selects legacy `[profiles.*]` state even though `--profile` now means
    `$CODEX_HOME/<name>.config.toml`.
    
    ## What
    
    - Remove the legacy `config_profile` field and merge/copy path from
    [`SharedCliOptions`](https://github.com/openai/codex/blob/95baaf72920c8db22097df8d15a0bb76c84528b6/codex-rs/utils/cli/src/shared_options.rs#L8-L177).
    - Stop forwarding profile-v1 overrides from CLI, exec, TUI, doctor,
    debug, feature, and exec-server paths; runtime profile selection remains
    on `config_profile_v2` through
    [`loader_overrides_for_profile`](https://github.com/openai/codex/blob/95baaf72920c8db22097df8d15a0bb76c84528b6/codex-rs/cli/src/main.rs#L1606-L1619).
    - Resolve local OSS provider selection from the base config in exec and
    TUI now that the legacy profile argument is gone.
    
    ## Testing
    
    - Not run (cleanup-only follow-up to #23883).
  • Route MCP servers through explicit environments (#23583)
    ## Summary
    - route each configured MCP server through an explicit per-server
    `environment_id` instead of a manager-wide remote toggle
    - default omitted `environment_id` to `local`, resolve named ids through
    `EnvironmentManager`, and fail only the affected MCP server when an
    explicit id is unknown
    - keep local stdio on the existing local launcher path for now, while
    named-environment stdio uses the selected environment backend and
    requires an absolute `cwd`
    - allow local HTTP MCP servers to keep using the ambient HTTP client
    when no local `Environment` is configured; named-environment HTTP MCPs
    use that environment's HTTP client
    
    ## Validation
    - devbox Bazel build: `bazel build --bes_backend= --bes_results_url=
    //codex-rs/cli:codex //codex-rs/rmcp-client:test_stdio_server
    //codex-rs/rmcp-client:test_streamable_http_server`
    - devbox app-server config matrix with real `config.toml` /
    `environments.toml` files covering omitted local, explicit local,
    omitted local under remote default, explicit remote stdio, local HTTP
    without local env, explicit remote HTTP, local stdio without local env,
    unknown explicit env, and remote stdio without `cwd`
  • cli: rename profile v2 flag to --profile (#23883)
    ## Why
    
    Profile v2 is taking over the user-facing profile selection path, so the
    CLI no longer needs to expose the transitional `--profile-v2` spelling.
    This switches the public args surface to `--profile` before the
    remaining legacy profile plumbing is removed separately.
    
    ## What
    
    - Rebind `--profile` and `-p` to the v2 profile name argument that
    selects `$CODEX_HOME/<name>.config.toml`.
    - Stop parsing the legacy shared CLI profile argument while keeping its
    implementation path in place for follow-up cleanup.
    - Update CLI validation, profile-name parse errors, and the
    legacy-profile collision message/tests to refer to `--profile`.
    
    ## Testing
    
    - `cargo test -p codex-cli -p codex-config -p codex-protocol -p
    codex-utils-cli`
  • feat(plugins): tabulate plugin list output (#23727)
    ## Summary
    - render `codex plugin list` as one table per marketplace with the
    marketplace manifest path shown above each table
    - surface the installed plugin version in the CLI output by threading
    `installed_version` through marketplace listing state
    - narrow the system-root exemption so only known bundled/runtime
    marketplaces skip missing-manifest failures, and keep `VERSION` empty
    for cached-but-unconfigured plugins
    
    ## Rationale
    The plugin list UX was hard to scan as a flat list and did not show
    which installed version was active. This change makes the CLI output
    easier to read in the real multi-marketplace case, keeps the plugin path
    visible, fixes the Sapphire regression where bundled/runtime marketplace
    roots were blocking `plugin list`, and addresses the two review findings
    that came out of the follow-up deep review.
    
    ## Key Decisions
    - kept the CLI output grouped per marketplace instead of one global
    table so the marketplace path can live with the rows it owns
    - kept `VERSION` as the installed version, which means it is empty until
    a plugin is actually installed
    - handled the bundled/runtime regression in the CLI snapshot validation
    path rather than widening app-server protocol or changing marketplace
    loading behavior
    - narrowed the exemption to known system marketplace names plus expected
    system paths, so user-configured marketplaces under those directories
    still fail loudly
    - gated `installed_version` on actual installed state so `VERSION`
    cannot show stale cache state for `not installed` rows
    
    ## Validation
    - `just fmt`
    - Sapphire: `cargo test -p codex-cli --test plugin_cli` (`14 passed; 0
    failed`)
    - Sapphire smoke test: bundled/runtime roots still work
      - `cargo run -q -p codex-cli -- plugin add sample@debug`
      - `cargo run -q -p codex-cli -- plugin list`
    - verified the bundled/runtime-root scenario no longer errors and shows
    the expected marketplace table output
    - Sapphire smoke test: custom marketplace under bundled path still
    errors
    - verified `failed to load configured marketplace snapshot(s)` for
    `custom-marketplace`
    - Sapphire smoke test: cached-but-unconfigured plugin hides version
    - verified `sample@debug not installed` renders with an empty `VERSION`
    column
    
    ## Sample Output
    ```text
    /tmp/custom-marketplace/plugin.json
    NAME          VERSION  STATUS         DESCRIPTION
    sample@debug  1.0.0    enabled        Debug sample plugin
    other@local            not installed  Local development plugin
    ```
  • cli: add strict config to exec-server (#23719)
    ## Why
    
    PR #20559 added opt-in strict config parsing to the config-loading
    command surfaces, but `codex exec-server` was left out. That meant
    `codex exec-server --strict-config` was rejected even though the command
    can load config for remote registration, and local server startup had no
    way to fail fast on misspelled config keys.
    
    ## What Changed
    
    - Added `--strict-config` to `codex exec-server`.
    - Allowed root-level inheritance from `codex --strict-config
    exec-server`.
    - Validated config before local exec-server startup when strict mode is
    requested.
    - Reused the loaded strict-config-aware config for remote exec-server
    registration auth.
    - Added CLI coverage showing `codex exec-server --strict-config` rejects
    unknown config fields.
    
    ## Verification
    
    - `cargo test -p codex-cli`
    - New integration test:
    `strict_config_rejects_unknown_config_fields_for_exec_server`
    
    ## Documentation
    
    Any strict-config command list on developers.openai.com/codex should
    include `codex exec-server` with the other supported config-loading
    entry points.
  • Migrate exec-server remote registration to environments (#23633)
    ## Summary
    - migrate exec-server remote registration naming from executor to
    environment
    - align CLI, public Rust exports, registry error messages, and relay
    test fixtures with the environment registry contract
    - keep the live registration path and response model consistent with
    `/cloud/environment/{environment_id}/register`
    
    ## Verification
    - `cargo test -p codex-exec-server
    remote::tests::register_environment_posts_with_auth_provider_headers
    --manifest-path /Users/richardlee/code/codex/codex-rs/Cargo.toml`
    - `cargo test -p codex-exec-server --test relay
    multiplexed_remote_environment_routes_independent_virtual_streams
    --manifest-path /Users/richardlee/code/codex/codex-rs/Cargo.toml`
    - `cargo check -p codex-cli --manifest-path
    /Users/richardlee/code/codex/codex-rs/Cargo.toml` (still running when PR
    opened; will update after completion if needed)
  • runtime: detect Codex package layout (#23596)
    ## Why
    
    The package-builder stack now creates a canonical Codex package
    directory where the entrypoint lives under `bin/`, bundled helper
    resources live under `codex-resources/`, and bundled PATH-style tools
    live under `codex-path/`. That layout is not specific to the standalone
    installer: npm, brew, install scripts, and manually unpacked artifacts
    should all be able to use the same package shape.
    
    The Rust runtime still only knew about the legacy standalone release
    layout, where resources sit next to the executable. A packaged binary
    therefore would not identify its package root or prefer the bundled `rg`
    from `codex-path/`.
    
    ## What changed
    
    - Adds `CodexPackageLayout` to `codex-install-context` and detects it
    from an executable path shaped like `<package>/bin/<entrypoint>` when
    `<package>/codex-package.json` is present.
    - Splits `InstallContext` into an install `method` plus an optional
    package layout so the layout is shared across npm, bun, brew,
    standalone, and other launch contexts.
    - Stores package-layout paths as `AbsolutePathBuf` values.
    - Keeps `codex-resources/` and `codex-path/` optional so Codex can still
    run with degraded behavior if sidecar directories are missing.
    - Updates `InstallContext::rg_command()` to prefer bundled
    `codex-path/rg` or `rg.exe`, then fall back to the legacy standalone
    resources location, then system `rg`.
    - Updates `codex doctor` reporting so package installs show package,
    bin, resources, and path directories, and so bundled search detection
    recognizes `codex-path/` for any install method.
    
    ## Test plan
    
    - `cargo test -p codex-install-context`
    - `cargo test -p codex-cli`
    - `cargo test -p codex-tui
    update_action::tests::maps_install_context_to_update_action`
    - `just bazel-lock-check`
  • feat: dedicated goal DB (#23300)
    ## Why
    
    Thread goals are moving toward extension-owned runtime behavior, but
    their persisted state was still stored in the shared state database.
    This makes the goal store harder to isolate and keeps future storage
    splits tied to ad hoc runtime plumbing.
    
    This PR gives goals their own SQLite database while keeping the existing
    `StateRuntime` entry point. The goal is to make this the pattern for
    adding more dedicated runtime databases later.
    
    This also reduce load on existing DB and reduce contention
    
    ## Limitation
    Thread preview from goal is not supported anymore. I'm looking into this
    [EDIT]: solved
    
    ## What changed
    
    - Added a dedicated `goals_1.sqlite` database with its own
    `goals_migrations` directory.
    - Moved `thread_goals` creation into the goals DB migration set.
    - Dropped the old `thread_goals` table from the main state DB with a
    normal state migration. There is intentionally no backfill for existing
    goal rows.
    - Changed `GoalStore` to be backed only by the goals DB pool.
    - Removed the old goal-write side effect that filled empty
    `threads.preview` values from the goal objective.
    - Added shared runtime DB path metadata so startup, telemetry, `codex
    doctor`, and repair handling can include future DBs without bespoke path
    lists.
    - Updated Bazel compile data so the new goals migration directory is
    available to `sqlx::migrate!`.
    
    ## Verification
    
    - `cargo check --tests -p codex-state -p codex-cli -p codex-core -p
    codex-app-server`
    - `just fix -p codex-state`
    - `just fix -p codex-cli`
    - `just fix -p codex-app-server`
  • Improve codex remote-control CLI UX (#22878)
    ## Description
    
    This PR makes `codex remote-control` behave like a foreground CLI
    command by default. Running it now starts remote control, waits for
    readiness, prints a clear status message with the machine name, and
    stays alive until Ctrl-C.
    
    Users who want daemon behavior can use `codex remote-control start`, and
    `codex remote-control stop` now prints concise human-readable output.
    `--json` remains available for scripts.
    
    Implementation-wise, this now verifies the real app-server state instead
    of just assuming startup worked. The CLI starts or connects to
    app-server, probes its control socket, calls the `remoteControl/enable`
    API, and waits for the remote-control status response/notification
    before printing success.
    
    For daemon mode, `codex remote-control start` also reports which managed
    app-server binary was used, including its path and best-effort `codex
    --version`, so failures are easier to diagnose.
    
    ## Examples
    
    Example output:
    ```
    > codex remote-control
    Starting app-server with remote control enabled...
    This machine is available for remote control as com-97826.
    Press Ctrl-C to stop.
    ```
    
    Error case using daemon (currently expected based on our publicly
    released CLI version):
    ```
    > ./target/debug/codex remote-control start
    Starting app-server daemon with remote control enabled...
    Error: app server did not become ready on /Users/owen/.codex/app-server-control/app-server-control.sock
    
    Daemon used app-server:
      path: /Users/owen/.codex/packages/standalone/current/codex
      version: 0.130.0
    
    Managed app-server stderr (/Users/owen/.codex/app-server-daemon/app-server.stderr.log):
      error: unexpected argument '--remote-control' found
      
      Usage: codex app-server [OPTIONS] [COMMAND]
      
      For more information, try '--help'.
    
    Caused by:
        0: failed to connect to /Users/owen/.codex/app-server-control/app-server-control.sock
        1: No such file or directory (os error 2)
    ```
    
    ## What changed
    
    - `codex remote-control` now runs remote control in the foreground and
    prints a Ctrl-C stop hint.
    - `codex remote-control start` starts the daemon and waits for remote
    control readiness before reporting success.
    - `codex remote-control stop` reports stopped/not-running status in
    plain language.
    - Startup failures now include recent managed app-server stderr to make
    daemon issues easier to diagnose.
    - Added coverage for CLI output, readiness waiting, foreground shutdown,
    and stderr log tailing.
  • Clarify resume hints for renamed threads (#23234)
    Addresses #23181
    
    ## Why
    Renamed threads can share names, so hints that suggest resuming directly
    by name are ambiguous. Issue #23181 asks for the picker hint to include
    the thread name and thread ID in parens so users can disambiguate
    safely.
    
    ## What
    - Adds a shared resume hint formatter for named threads: run `codex
    resume`, then select `<name> (<thread-id>)`.
    - Uses that hint for /rename confirmations, TUI session summaries, and
    CLI/TUI exit messages.
    - Keeps direct `codex resume <thread-id>` guidance for unnamed threads.
    
    ## Verification
    Manually verified that message after `/rename` and after `/exit` include
    session ID in parens.
    
    ---------
    
    Co-authored-by: Felipe Coury <felipe.coury@openai.com>
  • Support --output-schema for exec resume (#23123)
    ## Why
    
    `codex exec resume` should have the same structured-output support as
    top-level `codex exec`. Without `--output-schema`, multi-turn automation
    has to choose between resumed session context and schema-validated JSON
    output.
    
    Fixes #22998.
    
    ## What changed
    
    - Marked `--output-schema` as a global `codex exec` flag so it can be
    passed after `resume`.
    - Reused the existing output schema plumbing so resumed turns attach the
    schema to the final response request while preserving session context.
  • exec-server: support auth-backed remote executor registration (#22769)
    This updates remote `exec-server` registration to use normal Codex auth
    instead of a registry-issued credential. The registry request is built
    from the existing auth-provider path, which preserves the biscuit-only
    registry contract introduced in
    [openai/openai#924101](https://github.com/openai/openai/pull/924101)
    while removing the old remote registry bearer env var and its direct
    transport assumptions.
    
    The default remote flow uses persisted ChatGPT auth from the normal
    Codex config/storage path. This PR also includes the containerized Agent
    Identity path needed by
    [openai/openai#924260](https://github.com/openai/openai/pull/924260):
    remote `exec-server` accepts `--allow-agent-identity-auth`, permits
    Agent Identity auth loaded from `CODEX_ACCESS_TOKEN` only when that flag
    is present, and reuses the existing Agent task registration plus derived
    `AgentAssertion` header generation. API-key auth remains unsupported,
    and Agent Identity stays opt-in.
    
    Validation performed beyond normal presubmit coverage:
    - `cargo fmt --all --check`
    - `cargo check -p codex-cli`
    - `cargo test -p codex-exec-server`
    - `cargo test -p codex-cli exec_server_agent_identity_auth_flag_`
    - `cargo test -p codex-cli remote_exec_server_auth_mode_`
    
    I also attempted `cargo test -p codex-cli`. The new CLI tests passed
    inside that run, but the suite ended on an unrelated local
    marketplace-state failure in
    `plugin_list_excludes_unconfigured_repo_local_marketplaces`.
  • Fix Windows doctor npm root probe (#22967)
    ## Why
    On Windows npm-managed installs expose the working shim as `npm.cmd`.
    `codex doctor` probed bare `npm`, which could incorrectly report that
    npm global-root inspection was unavailable even when the install was
    healthy.
    
    Fixes #22964.
    
    ## What changed
    - Use `npm.cmd` for the doctor npm-root probe on Windows.
    - Keep the existing `npm` probe on non-Windows platforms.
  • Preserve image detail in app-server inputs (#20693)
    ## Summary
    
    - Add optional image detail to user image inputs across core, app-server
    v2, thread history/event mapping, and the generated app-server
    schemas/types.
    - Preserve requested detail when serializing Responses image inputs:
    omitted detail stays on the existing `high` default, while explicit
    `original` keeps local images on the original-resolution path.
    - Support `high`/`original` consistently for tool image outputs,
    including MCP `codex/imageDetail`, code-mode image helpers, and
    `view_image`.
  • tui: recover local state db startup failures (#22734)
    ## Why
    
    #22580 made app-server startup fail when the local SQLite state database
    cannot be initialized. Embedded/local TUI startup still continued on the
    permissive path, which left the CLI inconsistent and could hide a real
    startup problem behind unrelated UI. This brings local TUI startup onto
    the same fail-closed behavior while keeping recovery humane for the two
    failure modes we are seeing in practice: damaged database files and
    startup stalls caused by another process holding the database write
    lock.
    
    ## What changed
    
    - Embedded TUI startup now uses `state_db::try_init(...)` and returns a
    typed `LocalStateDbStartupError` that preserves the affected database
    path plus the underlying failure detail.
    - CLI startup handles that failure before entering the interactive TUI:
    - lock-contention failures tell users to quit other Codex processes and
    try again
    - failures consistent with a broken local database offer a safe repair
    that backs up Codex-owned SQLite files, rebuilds local database files,
    and retries startup once
    - declined or unsuccessful repairs print concise guidance plus technical
    details
    - Shared startup error plumbing lives in `tui/src/startup_error.rs`,
    while CLI recovery policy and focused recovery tests live in
    `cli/src/state_db_recovery.rs`.
    
    ## Verification
    
    - `cargo test -p codex-tui
    embedded_state_db_failure_is_typed_for_cli_recovery`
    - `cargo test -p codex-cli state_db_recovery`
    - Manually held an exclusive SQLite lock on `state_5.sqlite` and
    confirmed the CLI shows lock-specific guidance without offering repair.
    - Manually exercised the repair path with a deliberately invalid
    `sqlite_home` and confirmed it backs up the blocking path and resumes
    startup.
  • permissions: resolve profile identity with constraints (#22683)
    ## Why
    
    This PR is the invariant-cleanup layer that follows the workspace-roots
    base merged in [#22610](https://github.com/openai/codex/pull/22610).
    
    #22610 adds `[permissions.<id>.workspace_roots]` and keeps runtime
    workspace roots separate from the raw permission profile, but its
    in-memory representation is intentionally transitional: `Permissions`
    still carries the selected profile identity next to a constrained
    `PermissionProfile`. That makes APIs such as
    `set_constrained_permission_profile_with_active_profile()` fragile
    because the id and value only mean the right thing when every caller
    keeps them in sync.
    
    This PR introduces a single resolved profile state so profile identity,
    `extends`, the profile value, and profile-declared workspace roots
    travel together. The next PR,
    [#22611](https://github.com/openai/codex/pull/22611), builds on this by
    changing the app-server turn API to select permission profiles by id
    plus runtime workspace roots.
    
    ## Stack Context
    
    - #22610, now merged: adds profile-declared `workspace_roots`, runtime
    workspace roots, and `:workspace_roots` materialization.
    - This PR: replaces the parallel active-profile/profile-value fields
    with `PermissionProfileState`.
    - #22611: switches app-server turn updates toward profile ids plus
    runtime workspace roots.
    - #22612: updates TUI/exec summaries to show the effective workspace
    roots.
    
    Keeping this separate from #22611 is deliberate: reviewers can validate
    the internal state invariant before reviewing the app-server protocol
    migration.
    
    ## What Changed
    
    - Added `ResolvedPermissionProfile::{Legacy, BuiltIn, Named}` and
    `PermissionProfileState`.
    - Typed built-in profile ids with `BuiltInPermissionProfileId`.
    - Moved selected profile identity and profile-declared workspace roots
    into the resolved state.
    - Replaced `Permissions` parallel profile fields with one
    `permission_profile_state`.
    - Removed `set_constrained_permission_profile_with_active_profile()`
    from session sync paths.
    - Kept trusted session replay/`SessionConfigured` compatibility through
    explicit session snapshot helpers.
    - Updated session configuration, MCP initialization, app-server, exec,
    TUI, and guardian call sites to consume `&PermissionProfile` directly.
    
    ## Review Guide
    
    Start with `codex-rs/core/src/config/resolved_permission_profile.rs`; it
    is the new invariant boundary. Then review
    `codex-rs/core/src/config/mod.rs` to see how config loading records
    active profile identity and profile workspace roots. The remaining
    call-site changes are mostly mechanical fallout from
    `Permissions::permission_profile()` returning `&PermissionProfile`
    instead of `&Constrained<PermissionProfile>`.
    
    ## Verification
    
    The existing config/session coverage now constructs and asserts through
    `PermissionProfileState`. The workspace-root config test also asserts
    that profile-declared roots are preserved in the resolved state, which
    is the behavior #22611 relies on when runtime roots become mutable
    through the app-server API.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22683).
    * #22612
    * #22611
    * __->__ #22683
  • permissions: support workspace roots in profiles (#22610)
    ## Why
    
    This is the configuration/model half of the alternative permissions
    migration we discussed as a comparison point for
    [#22401](https://github.com/openai/codex/pull/22401) and
    [#22402](https://github.com/openai/codex/pull/22402).
    
    The old `workspace-write` model mixes three concerns that we want to
    keep separate:
    - reusable profile rules that should stay immutable once selected
    - user/runtime workspace roots from `cwd`, `--add-dir`, and legacy
    workspace-write config
    - internal Codex writable roots such as memories, which should not be
    shown as user workspace roots
    
    This PR gives permission profiles first-class `workspace_roots` so users
    can opt multiple repositories into the same `:workspace_roots` rules
    without using broad absolute-path write grants. It also starts
    separating the raw selected profile from the effective runtime profile
    by making `Permissions` expose explicit accessors instead of public
    mutable fields.
    
    A representative `config.toml` looks like this:
    
    ```toml
    default_permissions = "dev"
    
    [permissions.dev.workspace_roots]
    "~/code/openai" = true
    "~/code/developers-website" = true
    
    [permissions.dev.filesystem.":workspace_roots"]
    "." = "write"
    ".codex" = "read"
    ".git" = "read"
    ".vscode" = "read"
    ```
    
    If Codex starts in `~/code/codex` with that profile selected, the
    effective workspace-root set becomes:
    - `~/code/codex` from the runtime `cwd`
    - `~/code/openai` from the profile
    - `~/code/developers-website` from the profile
    
    The `:workspace_roots` rules are materialized across each root, so
    `.git`, `.codex`, and `.vscode` stay scoped the same way everywhere.
    Runtime additions such as `--add-dir` can still layer on later stack
    entries without mutating the selected profile.
    
    ## Stack Shape
    
    This PR intentionally stops before the profile-identity cleanup in
    [#22683](https://github.com/openai/codex/pull/22683) so the base review
    stays focused on config loading, workspace-root materialization, and
    compatibility with legacy `workspace-write`.
    
    The representation in this PR is therefore transitional: `Permissions`
    carries enough state to distinguish the raw constrained profile from the
    effective runtime profile, and there are still call sites that must keep
    the active profile identity and constrained profile value in sync. The
    follow-up PR replaces that with a single resolved profile state
    (`ResolvedPermissionProfile` / `PermissionProfileState`) that keeps the
    profile id, immutable `PermissionProfile`, and profile-declared
    workspace roots together. That follow-up removes APIs such as
    `set_constrained_permission_profile_with_active_profile()` where
    separate arguments could drift out of sync.
    
    Downstream PRs then build on this base to switch app-server turn updates
    to profile ids plus runtime workspace roots and to finish the
    user-visible summary behavior. Reviewers should judge this PR as the
    workspace-roots foundation, not as the final in-memory shape of selected
    permission profiles.
    
    ## Review Guide
    
    Suggested review order:
    
    1. Start with `codex-rs/core/src/config/mod.rs`.
    This is the main shape change in the base slice. `Permissions` now
    stores a private raw `Constrained<PermissionProfile>` plus runtime
    `workspace_roots`. Callers use `permission_profile()` when they need the
    raw constrained value and `effective_permission_profile()` when they
    need a materialized runtime profile. As noted above,
    [#22683](https://github.com/openai/codex/pull/22683) replaces this
    transitional shape with a resolved profile state that keeps identity and
    profile data together.
    
    2. Review `codex-rs/config/src/permissions_toml.rs` and
    `codex-rs/core/src/config/permissions.rs`.
    These add `[permissions.<id>.workspace_roots]`, resolve enabled entries
    relative to the policy cwd, and keep `:workspace_roots` deny-read glob
    patterns symbolic until the actual roots are known.
    
    3. Review `codex-rs/protocol/src/permissions.rs` and
    `codex-rs/protocol/src/models.rs`.
    These add the policy/profile materialization helpers that expand exact
    `:workspace_roots` entries and scoped deny-read globs over every
    workspace root. This is also where `ActivePermissionProfileModification`
    is removed from the core model.
    
    4. Review the legacy bridge in
    `Config::load_from_base_config_with_overrides` and
    `Config::set_legacy_sandbox_policy`.
    This is where legacy `workspace-write` roots become runtime workspace
    roots, while Codex internal writable roots stay internal and do not
    appear as user-facing workspace roots.
    
    5. Then skim downstream call sites.
    The interesting pattern is raw-vs-effective access: state/proxy/bwrap
    paths keep the raw constrained profile, while execution, summaries, and
    user-visible status use the effective profile and workspace-root list.
    
    ## What Changed
    
    - added `[permissions.<id>.workspace_roots]` to the config model and
    schema
    - added runtime `workspace_roots` state to `Config`/`Permissions` and
    `ConfigOverrides`
    - made `Permissions` profile fields private and replaced direct mutation
    with accessors/setters
    - added `PermissionProfile` and `FileSystemSandboxPolicy` helpers for
    materializing `:workspace_roots` exact paths and deny-read globs across
    all roots
    - moved legacy additional writable roots into runtime workspace-root
    state instead of active profile modifications
    - removed `ActivePermissionProfileModification` and its app-server
    protocol/schema export
    - updated sandbox/status summary paths so internal writable roots are
    not reported as user workspace roots
    
    ## Verification Strategy
    
    The targeted tests cover the behavior at the layers where regressions
    are most likely:
    - `codex-rs/core/src/config/config_tests.rs` verifies config loading,
    legacy workspace-root seeding, effective profile materialization, and
    memory-root handling.
    - `codex-rs/core/src/config/permissions_tests.rs` verifies profile
    `workspace_roots` parsing and `:workspace_roots` scoped/glob
    compilation.
    - `codex-rs/protocol/src/permissions.rs` unit tests verify exact and
    glob materialization over multiple workspace roots.
    - `codex-rs/tui/src/status/tests.rs` and
    `codex-rs/utils/sandbox-summary/src/sandbox_summary.rs` verify the
    user-facing summaries show effective workspace roots and hide internal
    writes.
    
    I also ran `cargo check --tests` locally after the latest stack refresh
    to catch cross-crate API breakage from the private-field/accessor
    changes.
    
    
    
    
    
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22610).
    * #22612
    * #22611
    * #22683
    * __->__ #22610
  • Trim TUI legacy core helper usage (#22695)
    ## Why
    
    The TUI still had a few low-risk dependencies flowing through the
    transitional `legacy_core` namespace after the app-server migration.
    These helpers either already have clearer non-core owners or are
    presentation logic that does not belong in `codex-core`, so moving them
    out reduces the compatibility surface without changing product behavior.
    
    ## What changed
    
    This is a low-risk change, almost completely mechanical in nature.
    
    - Route TUI Codex-home lookup through `codex-utils-home-dir`, use
    `Config::log_dir` directly, and call
    `codex-sandboxing::system_bwrap_warning` without going through
    `legacy_core`.
    - Move shared `codex resume` hint formatting from `codex-core` into
    `codex-utils-cli`.
    - Update CLI and TUI call sites to use the shared CLI utility, and keep
    the resume-command behavior covered by tests in its new home.
    
    ## Verification
    
    - `cargo test -p codex-utils-cli`
    - `cargo test -p codex-utils-cli resume_command`
  • [codex] Support multiple forced ChatGPT workspaces (#18161)
    ## Summary
    
    This change lets `forced_chatgpt_workspace_id` accept multiple workspace
    IDs instead of a single value.
    
    It keeps the existing config key name, adds backward-compatible parsing
    for a single string in `config.toml`, and normalizes the setting into an
    allowed workspace list across login enforcement, app-server config
    surfaces, and local ChatGPT auth helpers.
    
    ## Why
    
    Workspace-restricted deployments may need to allow more than one ChatGPT
    workspace without dropping the guardrail entirely.
    
    ## Server-side impact
    
    Codex's local server and app-server protocol needed changes because they
    previously assumed a single workspace ID. The local login flow now
    matches the auth backend interface by sending the allowed workspace list
    as a single comma-separated `allowed_workspace_id` query parameter.
    
    ## Validation
    
    This was tested with:
    
    - A single workspace config
    - With multi-workspace configs
    - With multiple workspaces in the config
    - The user only being a part of a subset of them
    
    All were successful.
    
    Automated coverage:
    
    - `cargo test -p codex-login`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-tui local_chatgpt_auth`
    - `cargo test --locked -p codex-app-server
    login_account_chatgpt_includes_forced_workspace_allowlist_query_param`