Commit Graph

2070 Commits

  • mcp: accept foreign absolute cwd for remote stdio (#29493)
    ## Why
    
    Remote stdio MCP servers can run in an environment whose path convention
    differs from the Codex host. A Windows cwd such as
    `C:\Users\openai\share` is absolute for the executor but was rejected by
    a POSIX orchestrator.
    
    Built on #29501, now merged, which only clarifies the host-native
    `PathUri` constructor name.
    
    ## What changed
    
    - Deserialize MCP cwd values as `LegacyAppPathString` so config does not
    apply host path rules.
    - Interpret that spelling as host-native for local launches and convert
    it to `PathUri` at executor launch.
    - Skip host filesystem and command resolution checks for remote stdio in
    `codex doctor`.
    - Add host-independent config and executor-boundary coverage using the
    foreign path convention for each test platform.
    
    ## Validation
    
    - `just test -p codex-utils-path-uri -p codex-config -p codex-mcp -p
    codex-rmcp-client` (408 passed)
    - `just test -p codex-cli -p codex-rmcp-client` (372 passed)
    - `cargo check --workspace --tests`
    - `just test` (11,311 passed; 43 unrelated environment/timing failures)
    - `just fix -p codex-cli -p codex-config -p codex-core -p codex-mcp -p
    codex-mcp-extension -p codex-rmcp-client -p codex-tui`
  • chore: improve expired Bedrock credential errors (#28992)
    ## Why
    
    Amazon Bedrock returns a `401 Unauthorized` response containing
    `Signature expired:` when an AWS credential, including a short-lived
    `AWS_BEARER_TOKEN_BEDROCK`, has expired. Codex currently surfaces that
    response as a generic `unexpected status` error, which does not explain
    how to recover.
    
    Environment-provided bearer tokens cannot be refreshed automatically, so
    the error should direct users to refresh their AWS credentials or
    replace or remove the environment token and restart Codex. This
    classification belongs to the Amazon Bedrock provider so similar
    responses from other providers retain their existing behavior.
    
    ## What changed
    
    - Add a synchronous `ModelProvider::map_api_error` hook that defaults to
    the existing provider-neutral API error mapping, and route model
    request, stream, WebSocket, and terminal unauthorized errors through the
    active provider.
    - Override the hook for Amazon Bedrock. After preserving the structured
    status, body, URL, and request metadata, recognize `401` responses
    containing `Signature expired:` and attach actionable credential
    guidance.
    - Keep `codex-protocol` provider-neutral by representing the guidance as
    an optional `user_message`. Error rendering prefers this message while
    continuing to append the URL, request ID, Cloudflare ray, and
    authorization diagnostics.
    - Add model-provider coverage for expired signatures and negative cases,
    core coverage for provider dispatch after unauthorized recovery, and a
    TUI snapshot for the rendered error.
    
    ## Testing
    Tested with a real request with expired bedrock key:
    <img width="962" height="126" alt="Screenshot 2026-06-22 at 3 56 51 PM"
    src="https://github.com/user-attachments/assets/7e21cc7c-798e-4662-8467-7f304a2f2b59"
    />
  • TUI Plugin Sharing 4 - cover remote plugin catalog flows (#26704)
    Remote plugin catalogs now span workspace, shared, and local sources, so
    their TUI behavior needs focused regression coverage across loading,
    navigation, actions, and refreshes.
    
    This PR:
    
    - Covers product labels and rendered loading/error states for Workspace,
    Shared with me, Shared with me (link), and Local tabs, including tab
    persistence across refresh and detail navigation.
    - Covers remote/local deduplication, Installed-tab remote detail
    routing, marketplace load-error handling, and disabled install/uninstall
    navigation.
    - Adds a full detail snapshot for local shared-plugin metadata plus
    focused snapshots for marketplace labels and admin-disabled status.
    - Verifies shared plugins remain eligible for mentions and successful
    uninstalls trigger a catalog refresh.
  • [plugins] Add dark-mode logo metadata (#29488)
    Adds additive dark-mode plugin logo metadata across manifests, remote
    catalogs, and the app-server protocol while keeping uninstalled Git
    listings free of synthetic local paths.
    
    Supersedes #28945. This replacement uses an upstream branch so trusted
    CI can use the repository-provided remote Bazel configuration.
    
    ## Current state
    
    Plugin interfaces expose only the default logo asset. Clients therefore
    cannot select a dedicated dark-mode logo even when a plugin provides
    one.
    
    ## What this PR changes
    
    - Adds nullable `logoDark` and `logoUrlDark` fields to
    `PluginInterface`.
    - Resolves local `interface.logoDark` assets and maps remote
    `logo_url_dark` values.
    - Removes path-backed interface assets, including `logoDark`, from
    uninstalled Git fallback listings until the plugin has a real local
    root.
    - Updates the bundled plugin validator and manifest reference.
    - Regenerates the app-server JSON schemas and TypeScript types.
    
    Local manifests expose `interface.logoDark` as a package-relative asset
    path. Remote catalog responses expose `logo_url_dark`. These values map
    into separate app-server fields so clients can preserve local-path and
    remote-URL handling.
    
    ## Risk
    
    The fields are additive and nullable, so existing clients retain their
    current logo behavior. The main risks are an incomplete mapping path or
    exposing a synthetic local path for an uninstalled Git plugin.
    Local-manifest, remote-catalog, fallback-listing, protocol
    serialization, and app-server integration tests cover those paths.
    
    Spiciness: 2/5
    
    ## Testing
    
    - `just write-app-server-schema`
    - `just fmt`
    - Regression test first failed with `logo_dark` resolved to
    `/assets/logo-dark.png`, then passed after the fallback-listing fix.
    - `just test -p codex-core-plugins` (267 tests passed)
    - `just test -p codex-app-server 'suite::v2::plugin'` (114 tests passed)
    - `just test -p codex-app-server-protocol -p codex-core-plugins -p
    codex-plugin -p codex-skills` (517 tests passed before the follow-up)
    - `just test -p codex-tui plugin` (47 tests passed)
    - Validated a local plugin manifest containing `interface.logoDark` with
    the bundled validator.
    
    ## Manual verification
    
    Create a local plugin with both `interface.logo` and
    `interface.logoDark`, then call `plugin/list` or `plugin/read`. Confirm
    the response contains separate `logo` and `logoDark` paths. For a remote
    catalog entry, confirm `logoUrlDark` is populated from `logo_url_dark`.
    For an uninstalled Git marketplace entry, confirm path-backed interface
    assets remain absent until installation.
    
    Issue: N/A - coordinated maintainer change.
  • fix(config): address permission profile review follow-ups (#29479)
    ## Summary
    
    - rename `Config::permission_profile_allowed` to
    `is_permission_profile_allowed`
    - use `BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS` in the TUI and
    its assertion
    - follow up on the late review comments from #26678
    
    The previous `:danger-no-sandbox` value was an invalid built-in profile
    ID. #26678 corrected it to `:danger-full-access`; this PR centralizes
    the value to prevent future drift.
    
    ## Testing
    
    - Not run per request; `cargo fmt` only
    
    Co-authored-by: Codex <noreply@openai.com>
  • permission profiles: expose availability to clients (#26678)
    ## Why
    
    `permissionProfile/list` currently advertises every built-in and
    configured profile even when effective enterprise requirements prevent
    selecting it. That forces each client to reconstruct policy from
    lower-level requirement fields, which is easy to miss and difficult to
    keep consistent.
    
    The catalog should remain complete so clients can explain that an option
    was disabled by an administrator, while also reporting whether each
    profile is selectable.
    
    ## What
    
    - Add an `allowed` field to each permission profile summary.
    - Build a shared catalog from the effective config and current
    requirements, including `allowed_sandbox_modes`, `allowed_permissions`,
    and filesystem restrictions.
    - Use the shared catalog in app-server and the TUI so disallowed
    profiles remain visible but cannot be selected.
    - Use the canonical `:danger-full-access` profile ID in the TUI.
    - Update the app-server schemas, API documentation, behavioral tests,
    and TUI snapshots.
    
    ## Scope
    
    This PR targets `main` directly and is independent of #24852. It
    preserves the current behavior where built-in profiles are constrained
    by sandbox-mode requirements and `allowed_permissions` applies to
    configured profiles.
    
    ## Testing
    
    - `just test -p codex-core
    permission_profile_catalog_marks_profiles_disallowed_by_requirements`
    - `just test -p codex-app-server permission_profile_list`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-tui profile_permissions`
    - `just fix -p codex-core`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    - `just fix -p codex-tui`
    - `just fmt`
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
    Co-authored-by: Joey Trasatti <joey.trasatti@openai.com>
  • Allow ChatGPT accounts without email (#28991)
    # Summary
    
    Codex required every ChatGPT account to have an email address. A
    service-account personal access token can return valid account metadata
    without one, so PAT login failed while decoding the metadata response.
    
    This change makes email optional in the account metadata type that owns
    it and preserves that absence through authentication, provider account
    state, the app-server API, generated clients, and TUI bootstrap.
    Existing accounts with email addresses keep the same behavior.
    
    ## Behavior-changing call sites
    
    | Call site | Behavior after this change |
    | --- | --- |
    | `login/src/auth/personal_access_token.rs` | PAT metadata accepts a
    missing or null email and retains `None`. |
    | `agent-identity/src/lib.rs` | Agent Identity JWT claims accept an
    omitted email. |
    | `login/src/auth/storage.rs` and `login/src/auth/agent_identity.rs` |
    Stored and managed Agent Identity records carry `Option<String>`.
    Deserialization maps the legacy empty-string sentinel to `None`. |
    | `login/src/auth/manager.rs` | `get_account_email` returns the stored
    option, and managed identity bootstrap no longer converts `None` to an
    empty string. |
    | `model-provider/src/provider.rs` and `protocol/src/account.rs` | A
    ChatGPT provider account requires a plan type but may carry no email. |
    | `app-server-protocol/src/protocol/v2/account.rs` | `account/read`
    keeps the `email` field on the wire and returns `null` when the account
    has no email. Generated TypeScript and JSON schemas describe a required,
    nullable field. |
    | `sdk/python/src/openai_codex/generated/v2_all.py` | The generated
    Python `ChatgptAccount` model accepts `None` for email. |
    | `tui/src/app_server_session.rs` | Email-less ChatGPT accounts
    bootstrap normally, keep external feedback routing, omit account-email
    telemetry, and display the plan in account status. |
    
    ## Design decisions
    
    - Missing email remains `None` at every layer. The code never uses an
    empty string as a substitute.
    - The app-server response includes `"email": null` instead of omitting
    the field. Clients retain a stable response shape.
    - Plan type remains required for provider account state. This change
    relaxes only the email assumption.
    
    ## Testing
    
    Tests: affected test targets compile, scoped Clippy and formatting pass,
    a focused TUI snapshot covers plan-only account status, real
    before/after PAT login smoke covers metadata without email, app-server
    smoke covers `account/read` with `email: null`, and a regression smoke
    covers an existing email-bearing PAT. Unit tests run in CI.
    
    ## Evidence
    
    Visual smoke evidence will be attached here.
  • PAC 2 - Add shared auth system proxy contract (#26707)
    ## Summary
    
    Stacked on #26706.
    
    Adds the shared auth/system-proxy contract that later platform resolver
    PRs plug into. This PR moves Codex-owned auth and startup HTTP clients
    through a common route-aware boundary, but does not yet add Windows or
    macOS system proxy resolution.
    
    The default path remains unchanged when `respect_system_proxy` is absent
    or disabled.
    
    ## Implementation
    
    - Adds `codex-client/src/outbound_proxy.rs` with the shared
    route-selection model:
      - `OutboundProxyConfig`;
      - `ClientRouteClass`;
      - `RouteFailureClass`;
      - `build_reqwest_client_for_route`.
    - Preserves the existing reqwest/default-client behavior when no route
    config is supplied.
    - Uses the fixed MVP routing policy when route config is supplied:
    platform system/PAC/WPAD discovery, then explicit env proxy variables,
    then direct connection.
    - Keeps platform-specific system discovery behind the shared client
    boundary. This PR provides the contract and fallback behavior; later
    resolver PRs plug in Windows and macOS discovery.
    - Adds `login::AuthRouteConfig` so auth call sites depend on a small
    policy type instead of platform resolver details.
    - Maps the resolved `Config.respect_system_proxy` boolean into
    `AuthRouteConfig` for auth-owned clients.
    - Wires the route config through browser login, device-code login,
    access-token login, login status, logout/revoke, token refresh, API-key
    exchange, app-server account login, TUI/app startup, cloud-config
    bootstrap, cloud tasks, plugin auth, and exec startup config loading.
    
    ## End-user behavior
    
    - No behavior changes by default.
    - When `respect_system_proxy = true`, auth-owned clients opt into the
    shared route-aware client path.
    - On platforms without a resolver implementation in this PR, system
    discovery is unavailable and the route-aware path falls back to explicit
    env proxy handling, then direct connection.
    - Custom CA handling remains separate from proxy route selection and
    still runs through the shared client builder.
    - No proxy URLs, PAC contents, or resolved platform details are exposed
    through the public config surface introduced here.
    
    ## Tests
    
    Adds or updates coverage for:
    
    - preserving default auth-client fallback behavior when no route config
    is provided;
    - injected environment-proxy fallback without mutating process
    environment;
    - existing login-server E2E flows using explicit `auth_route_config:
    None` to guard unchanged default behavior;
    - updated auth manager, login, logout, cloud-config, startup, and
    plugin-auth call sites passing route config explicitly.
  • [codex] Fix usage-limit reset copy and state (#28793)
    ## Why
    
    The reset flow introduced in #28154 still describes earned reset credits
    as "rate-limit resets" and uses generic reset-scope copy. It can also
    retain a stale available-credit count after redemption or an account
    change, leaving the reset action enabled after the last credit is used.
    
    This follow-up updates terminology only within that reset feature.
    Existing rate-limit wording elsewhere in the CLI and TUI is unchanged.
    
    ## What changed
    
    - Rename reset-specific `/usage` menu items, startup hints, and reset
    dialogs to "usage limit reset."
    - Describe monthly resets for Free, Go, and accounts that report a
    monthly usage window; otherwise describe the current 5-hour and weekly
    limits.
    - Recheck a cached zero balance when `/usage` is reopened, and refresh
    the balance after redemption so the final reset immediately disables the
    action.
    - Correlate async refresh results before updating snapshots and clear
    account-derived reset state, warnings, prompts, and status surfaces when
    the account changes.
    
    ## Validation
    
    - `just test -p codex-tui chatwidget::tests::usage` — 29 passed.
    - `just test -p codex-tui chatwidget::tests::status_command_tests` — 7
    passed.
    - Account-boundary prompt and plan-mode prompt regression tests passed.
    - `cargo insta pending-snapshots` from `codex-rs/tui` — no pending
    snapshots.\
    
    <img width="814" height="318" alt="image"
    src="https://github.com/user-attachments/assets/2a460e96-458b-4805-8d9f-c759382d21a4"
    />
    view for monthly
    <img width="905" height="243" alt="image"
    src="https://github.com/user-attachments/assets/179f88e3-08fb-4af5-8dc6-ce6a944ed681"
    />
  • core: rename metadata -> internal_chat_message_metadata_passthrough (#28968)
    ## Description
    This PR cuts Codex over from generic `ResponseItem.metadata` (introduced
    here: https://github.com/openai/codex/pull/28355) to
    `ResponseItem.internal_chat_message_metadata_passthrough`, which is the
    blessed path and has strongly-typed keys.
    
    For now we have to drop this MAv2 usage of `metadata`:
    https://github.com/openai/codex/pull/28561 until we figure out where
    that should live.
  • TUI Plugin Sharing 3 - render remote plugin catalog sections (#26703)
    ## Summary
    
    [#26701](https://github.com/openai/codex/pull/26701) added remote plugin
    identity support, [#26702](https://github.com/openai/codex/pull/26702)
    added remote-section fetching and state, and
    [#28768](https://github.com/openai/codex/pull/28768) extracted the
    catalog rendering module. This PR builds the product-facing `/plugins`
    catalog on that foundation so remote records appear as OpenAI Curated,
    Workspace, and Shared with me sections rather than backend marketplace
    implementation details.
    
    Plugin details remain read-only for sharing metadata. This PR does not
    add share-authoring actions or change the app-server protocol.
    
    ## Changes
    
    - Renders OpenAI Curated, Workspace, and Shared with me sections with
    loading, empty, and error states.
    - Preserves section selection and stable tab ordering as remote sections
    transition between fallback and populated states.
    - Shows OpenAI Curated loading only when the explicit vertical fallback
    request was issued.
    - Centralizes remote marketplace identity matching around the existing
    marketplace constants.
    - Uses product labels for remote marketplaces and identifies the
    personal marketplace as Local by its path.
    - Shows read-only source, authentication, version, and sharing metadata
    in plugin detail views.
    - Applies narrow display deduplication for local and remote records
    sharing a remote plugin ID:
      - installed records take precedence;
    - local mapped sources are preferred for details only when their
    installed state matches the selected record.
    - Returns from detail and confirmation views through the current plugin
    cache so newly loaded remote sections are not overwritten by an older
    captured response.
    - Keeps admin-disabled plugins view-only and labels default-installed
    plugins as Available by default.
    
    ## Tests
    
    New tests:
    
    - `plugins_popup_admin_disabled_available_plugin_has_view_only_hint`
    - `plugins_popup_remote_section_fallback_states_snapshot`
    -
    `plugins_popup_installed_remote_row_keeps_remote_detail_when_local_share_is_uninstalled`
    
    Updated existing plugin catalog tests and snapshots for product labels,
    detail metadata, personal-marketplace labeling, and stable tab ordering.
    
    Verification:
    
    - `cargo clippy -p codex-tui --all-targets -- -D warnings`
    
    ## Follow-ups
    
    - Local/remote duplicate normalization should eventually move into
    app-server. This PR intentionally keeps the compatibility behavior
    narrow and display-only.
    - PR5 will sanitize sensitive components before displaying Git source
    URLs.
  • Filter noisy targets from persistent logs (#29457)
    ## Why
    
    The local SQLite log sink currently enables TRACE for every target. This
    persists high-volume dependency logs bridged through `target=log` and
    duplicates OpenTelemetry mirror events in `codex_otel.log_only` and
    `codex_otel.trace_safe`.
    
    These records rapidly consume the per-partition log budget and cause
    unnecessary SQLite insert-and-prune churn.
    
    ## What changed
    
    - Keep TRACE persistence for other targets.
    - Exclude bridged `target=log` events from the SQLite sink.
    - Exclude the two `codex_otel` mirror targets from the SQLite sink.
    - Share the same filter between app-server and TUI.
    
    Remote OpenTelemetry export and metrics are unchanged.
  • Add workspace messages app-server API (#29001)
    ## Summary
    
    - Add backend-client types and fetch support for active workspace
    messages.
    - Add the app-server v2 `account/workspaceMessages/read` method,
    generated schemas, and README documentation.
    - Delegate workspace-message eligibility to the Codex backend feature
    gate; map a backend 404 to `featureEnabled: false`.
    
    ## Testing
    
    - `just write-app-server-schema`
    - `just test -p codex-backend-client`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-app-server workspace_messages`
    - `just fix -p codex-backend-client -p codex-app-server-protocol -p
    codex-app-server`
    - `just fmt`
    
    ## Stack
    
    - Base PR for #28232, which adds the TUI status-line integration.
  • Persist session IDs across thread resume (#29327)
    ## Summary
    
    A cold-resumed subagent kept its durable thread ID but could receive a
    new session ID, splitting one agent tree across multiple sessions after
    a restart.
    
    Persist the root session ID in every rollout `SessionMeta`, carry it
    through thread creation, and restore it before initializing the resumed
    `Session` and `AgentControl`.
    
    ## Behavior
    
    For a nested agent tree:
    
    ```text
    root session R
      parent thread P
        child thread C
    ```
    
    The child rollout stores:
    
    ```text
    session_id:       R
    parent_thread_id: P
    id:               C
    ```
    
    After a cold resume, the child still belongs to root session `R` while
    its immediate parent remains `P`. The integration coverage uses distinct
    values for all three IDs so it catches restoring the session from
    `parent_thread_id`.
    
    ## Legacy rollouts
    
    Previous rollouts have `id` but no `session_id`. `SessionMetaLine`
    deserialization treats a missing `session_id` as `id`, keeping those
    files readable, listable, and resumable. When a legacy subagent is
    resumed through its root, that synthesized child ID no longer overrides
    the inherited root-scoped `AgentControl`. New rollouts always persist
    the explicit root session ID.
  • Propagate safety buffering events to app-server clients (#29371)
    Responses API safety buffering metadata currently stops at the transport
    boundary, so app-server clients cannot render the in-progress safety
    review state.
    
    This change:
    - decodes and deduplicates `safety_buffering` metadata from Responses
    API SSE and WebSocket events without suppressing the original response
    event
    - emits a typed core event containing the requested model plus backend
    use cases and reasons
    - forwards that event as `turn/safetyBuffering/updated` through
    app-server v2 and updates generated protocol schemas
    - keeps the side-channel event out of persisted rollouts and turn timing
    
    This supports the Codex Apps buffering UX and depends on the Responses
    API backend work in https://github.com/openai/openai/pull/1044569 and
    https://github.com/openai/openai/pull/1044571.
    
    Validation:
    - focused `codex-core` safety-buffering integration test passes
    - `cargo check -p codex-core -p codex-app-server -p
    codex-app-server-protocol`
    - `just fix -p codex-api -p codex-protocol -p codex-core -p
    codex-app-server-protocol -p codex-app-server -p codex-rollout -p
    codex-rollout-trace -p codex-otel`
    - `just fmt`
    - broad package test run: 4,430/4,492 passed; 62 unrelated
    local-environment/concurrency failures involved unavailable test
    binaries, MCP subprocess setup, and app-server timeouts
  • Allow resume and settings commands during tasks and MCP startup (#29154)
    ## Why
    
    The TUI treats both an active turn and MCP startup as a running task.
    That currently blocks `/resume` and several settings commands even
    though they do not compete with turn execution, which is especially
    frustrating when MCP startup is slow.
    
    Model, permissions, personality, and service-tier selections already
    update thread settings independently of the running turn. Other clients
    can send those updates mid-turn, while the current turn continues with
    its captured settings. Allowing the same updates from local slash
    commands makes the TUI consistent with that existing behavior.
    
    ## What changed
    
    - Allow `/resume` while a task is running.
    - Allow `/model`, `/permissions`, `/personality`, and service-tier
    commands such as `/fast` while a task is running.
    - Keep the existing behavior where the active turn uses its captured
    settings and updates apply to subsequent turns.
    - Exercise the commands under the busy state in the existing TUI tests
    and retain coverage for commands that should remain blocked.
    
    ## Behavior note
    
    Turn settings such as model selection and reasoning effort are captured
    when a turn starts. Changing them during an active turn affects the next
    turn, not the turn already in progress. The status bar updates
    immediately, so it may temporarily display the newly selected setting
    before that setting is actually in effect.
    
    ## Verification
    
    - Focused `codex-tui` tests for resume dispatch, settings popups,
    `/fast`, and disabled-command behavior.
    
    ## Related issues
    
    Closes #19015.
    
    Addresses the next-turn-safe model/reasoning switching portion of
    #14356; dedicated shortcuts and the proposed depth meter remain out of
    scope.
  • Expose thread-level multi-agent mode (#28792)
    ## Why
    
    Once multi-agent mode can be selected per turn, clients also need to
    choose the initial selection when creating a thread and observe that
    selection through lifecycle and settings APIs.
    
    The selected value is intentionally distinct from the effective
    model-visible value: no client selection is represented as `null`, even
    though an eligible multi-agent v2 turn derives `explicitRequestOnly` as
    its effective default.
    
    ## What changed
    
    - Add the optional experimental `thread/start.multiAgentMode` parameter
    and pass it through thread creation.
    - Preserve an omitted initial value as an unset selection rather than
    eagerly storing `explicitRequestOnly`.
    - Apply an explicit `thread/start` selection to the first turn through
    the session configuration established at thread creation.
    - Restore the latest persisted effective mode as the selected baseline
    on cold resume when rollout history contains one.
    - Inherit the optional selected mode from a loaded parent when creating
    related runtime threads.
    - Return the current selected `multiAgentMode` from `thread/start`,
    `thread/resume`, `thread/fork`, and thread settings, using `null` when
    no mode is selected.
    - Keep lifecycle reporting independent from model capability and feature
    eligibility; core turn construction remains responsible for calculating
    and persisting the effective mode.
    
    ## Not covered
    
    - Clearing an existing loaded-session selection back to unset through
    `turn/start`; omitted or `null` currently retains the session's
    selection.
    - A TUI control, slash command, or `config.toml` preference.
    
    ## Verification
    
    - `CARGO_INCREMENTAL=0 just test -p codex-app-server-protocol`
    - `CARGO_INCREMENTAL=0 just test -p codex-app-server multi_agent_mode`
    
    The focused app-server coverage verifies explicit `thread/start`
    initialization, first-turn prompting, nullable reporting for an omitted
    selection, and retention of selections that are not currently
    runtime-eligible.
    
    ## Stack
    
    Stacked on #28685. This PR contains only the thread initialization and
    lifecycle/settings API layer.
  • Add per-turn multi-agent mode (#28685)
    ## Why
    
    Multi-agent v2 currently carries an explicit-request-only delegation
    rule in its static usage hint. That provides a safe default, but it
    prevents clients from selecting proactive delegation per turn without
    changing static guidance or rewriting prior model context.
    
    This change makes delegation mode a session selection that can be
    updated through `turn/start`, while deriving the effective model-visible
    mode separately for each turn. Eligible multi-agent v2 turns remain
    explicit-request-only unless proactive mode is both selected and
    enabled.
    
    ## What changed
    
    - Add the experimental `turn/start.multiAgentMode` parameter with
    `explicitRequestOnly` and `proactive` values. Omission retains the
    loaded session's current optional selection.
    - Add the default-off `features.multi_agent_mode` feature gate. Eligible
    multi-agent v2 turns use the selected mode when enabled; an unset
    selection or disabled gate resolves to `explicitRequestOnly`.
    - Treat mode prompting as inapplicable for multi-agent v1 and other
    unsupported session configurations, producing no multi-agent mode
    developer message rather than rejecting the turn.
    - Move the explicit-request-only rule out of the static v2 usage hint
    and into a bounded, tagged developer context fragment.
    - Emit the effective mode in initial context and only when that
    effective mode changes on later turns.
    - Persist the effective mode in `TurnContextItem` as the durable
    baseline for resume and context-update comparisons.
    
    Historical rollout items are not rewritten. Later mode developer
    messages establish the current rule incrementally.
    
    ## Not covered
    
    - Initial selection through `thread/start` and selected-mode reporting
    from thread lifecycle/settings APIs; those are isolated in the stacked
    #28792.
    - A TUI control or slash command for selecting the mode.
    - Persisting a preferred mode to `config.toml`; selection remains
    session/turn scoped.
    - Changes to multi-agent concurrency limits, tool availability, or model
    catalog capability declarations.
    - Rewriting historical rollout prompt items. Cold resume restores the
    latest persisted effective mode when available while leaving historical
    developer messages intact.
    
    ## Verification
    
    - `CARGO_INCREMENTAL=0 just test -p codex-core multi_agent_mode`
    - Focused app-server coverage verifies that `turn/start.multiAgentMode`
    produces proactive developer instructions for an eligible v2 turn.
    
    ## Stack
    
    Followed by #28792, which adds `thread/start` initialization and
    lifecycle/settings observability.
  • core: load AGENTS.md from foreign environments (#28958)
    ## Why
    
    Make it possible to load AGENTS.md from remote exec-servers whose OS is
    different than app-server.
    
    ## What
    
    - keep `AGENTS.md` discovery and provenance as `PathUri`, with
    root-aware parent and ancestor traversal
    - expose lifecycle instruction sources as legacy app-server path strings
    in events while retaining `PathUri` internally
    - preserve and test mixed POSIX and Windows paths in model context and
    TUI status output
    - cover remote Windows loading end to end by seeding the Wine prefix
    through host filesystem APIs
    - fix bug in `PathUri`'s parent() implementation that would erase
    Windows drive letters
  • feat: opt ChatGPT auth into agent identity (#19049)
    ## Stack
    
    This is PR 2 of the simplified HAI single-run-task stack:
    
    - [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
    assertion and task-registration primitives, including the shared
    run-task helper used by existing Agent Identity JWT auth.
    - [#19049](https://github.com/openai/codex/pull/19049)
    Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
    Agent Identity runtime auth and its single run task.
    - [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
    provider auth that uses one backend-owned task id for first-party
    inference and compaction requests.
    
    [#19054](https://github.com/openai/codex/pull/19054) collapsed out of
    the active stack because the simplified design no longer needs a
    separate background/control-plane task helper.
    
    ## Summary
    
    This PR adds the disabled-by-default path for normal ChatGPT-login Codex
    sessions to obtain Agent Identity runtime auth through the Codex
    backend. Existing Agent Identity JWT startup mode remains a separate
    path and does not require the feature flag.
    
    What changed:
    
    - adds the experimental `use_agent_identity` feature flag and config
    schema entry
    - adds an explicit `AgentIdentityAuthPolicy` so call sites choose
    `JwtOnly` or `ChatGptAuth` instead of passing a bare boolean
    - stores standalone Agent Identity JWT credentials separately from
    backend-registered Agent Identity records
    - persists the registered Agent Identity record, private key, and single
    run task id in `auth.json` so process restarts reuse the same identity
    - derives the agent/task registration base URL from ChatGPT/Codex auth
    config while keeping JWT JWKS lookup separate
    - provisions and caches ChatGPT-derived Agent Identity runtime auth when
    `use_agent_identity` is enabled
    - reuses the shared run-task registration helper from PR1 rather than
    adding a second task-registration path
    
    This PR intentionally does not switch model inference over to
    `AgentAssertion` auth. The provider-auth integration lands in the next
    PR.
    
    ## Testing
    
    - `just test -p codex-login`
  • Emit Trusted MCP App Identity on Tool-Call Items (#27132)
    ## Summary
    
    - Add optional `appContext` to app-server MCP tool-call items with
    trusted `connectorId`, `linkId`, and `mcpAppResourceUri` metadata.
    - Preserve that context across tool-call events, persisted history,
    reconnects, and thread resume.
    - Keep the deprecated top-level `mcpAppResourceUri` temporarily for
    client migration.
    
    The consumer contract is `{ appContext: { connectorId, linkId,
    mcpAppResourceUri }, tool }`.
    
    ## Validation
    
    - Full GitHub Actions suite passes, including CLA, Bazel tests, clippy,
    release builds, and argument-comment lint.
    
    ---------
    
    Co-authored-by: martinauyeung-oai <280153141+martinauyeung-oai@users.noreply.github.com>
  • TUI: improve unified mention selection visibility (#28959)
    ## Summary
    
    [@milanglacier reported in
    #28653](https://github.com/openai/codex/issues/28653) that the active
    mention candidate is hard to distinguish. I suspect [@binbjz’s #28500
    report](https://github.com/openai/codex/issues/28500) _(where arrow-key
    navigation appeared not to work)_ may describe the same presentation
    problem: the selection may have been changing, but the UI was not
    showing the active row clearly in their terminal. This PR makes two
    small changes to the selection indication behavior:
    
    - Reserve a two-character gutter and mark the active candidate with `> `
    for color-agnostic indicator coverage.
    - Apply the shared theme-aware accent to the entire selected row for
    extra emphasis.
    - Update the existing popup snapshot.
    
    Reverse-video styling was considered, but avoided it because it is
    overly dependent on the user’s terminal palette.
    
    <img width="2046" height="482" alt="image"
    src="https://github.com/user-attachments/assets/b5eb62c3-fd24-4c09-906e-7bd66913b5c6"
    />
    
    ## Testing
    
    - `just test -p codex-tui default_unified_mention_popup_snapshot`
    - `just clippy -p codex-tui`
    - `just fmt`
    - Compiled `codex-cli` and tested the unified mentions picker in the
    terminal.
  • Add app-server current-time impl (varlatency 3/n) (#28835)
    ## What
    
    Server should request:
    
    ```
    {
      "id": 42,
      "method": "currentTime/read",
      "params": {
        "threadId": "11111111-1111-1111-1111-aaaaafdc2c11"
      }
    }
    ```
    
    Client should respond with something like:
    
    ```rust
    {
      "id": 42,
      "result": {
        "currentTimeAt": 1781717655
      }
    }
    ```
    
    ## Why
    
    Sessions configured with `clock_source = "external"` need a
    thread-specific external time source before inference. The system clock
    remains the default production provider.
    
    ## Validation
    
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server --test all
    current_time_read_round_trip_adds_reminder_to_model_input`
    - `cargo test -p codex-app-server
    first_attestation_capable_connection_for_thread_only_uses_thread_subscribers`
    - `cargo test -p codex-analytics`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    
    Stacked on #28824.
  • Support openai/form extended form elicitations (#27500)
    # Summary
    Allow App Server clients to opt into `openai/form` MCP elicitations.
  • Pause active goals before TUI interrupts (#28813)
    Fixes #28104.
    
    ## Summary
    Active `/goal` turns should leave the persisted goal paused whenever the
    TUI interrupts the running turn. The bug in #28104 showed this most
    visibly through `Esc`: some interrupt paths aborted the turn without
    updating the goal status, so the goal could remain active and continue
    automatically.
    
    This change makes `ChatWidget` pause an active goal before the TUI sends
    an interrupt from the status-row path, the pending-steer path, `Ctrl+C`,
    or a request-user-input overlay. The modal overlay now reports whether a
    key will interrupt the turn, which keeps modal `Esc` and `Ctrl+C`
    behavior aligned with the normal interrupt paths.
    
    ## Manual Testing
    Built the local CLI with `just codex --help`, then launched the local
    TUI with goals enabled. Started an active `/goal` turn and interrupted
    it with `Esc`, then resumed and repeated with `Ctrl+C`; both paths
    showed `Goal paused`, the interrupted-conversation message, and the
    `Goal paused (/goal resume)` footer. I also stopped the background
    terminal and exited the TUI cleanly after the run.
    
    I did not find a reliable standalone manual path to force the
    request-user-input overlay case, so that path is covered by the focused
    automated test.
  • unified-exec: retain PathUri in command events (#28780)
    ## Why
    
    App-server must report command events containing foreign-platform paths
    without changing existing client or rollout path-string formats.
    
    ## What changed
    
    - retain `PathUri` through exec command begin/end events
    - convert cwd values to `LegacyAppPathString` at the app-server
    compatibility boundary
    - drop command actions with foreign paths and log them
    - serialize rollout-trace cwd values using their inferred native path
    representation
    - restore Wine coverage for retained Windows cwd values and successful
    completion
  • [codex] Track plugin install and import telemetry failures (#28731)
    ## Summary
    - Track plugin install failures through the unified
    `codex_plugin_install_failed` event for local installs, remote install
    preflight failures, bundle failures, and remote catalog/backend
    failures.
    - Send classified `error_type` values in plugin install failure
    analytics instead of raw error strings.
    - Stop sending raw external-agent import errors in analytics while
    preserving raw failure details in app-facing import
    notifications/history.
    - Keep raw plugin/migration diagnostics in `tracing::warn!` logs.
    - Keep remote failure plugin names as the existing local placeholder
    (`unknown`) and remove the extra telemetry plugin-name override.
    - Change `ExternalAgentConfigImportParams.source` from a generated enum
    to `string | null`, with legacy `claudeCode` / `claudeCowork` inputs
    normalized to existing analytics values.
    
    ## Testing
  • Extract TUI plugin catalog rendering (#28768)
    This mechanically extracts the existing TUI plugin catalog and detail
    popup rendering from `chatwidget/plugins.rs` into a new
    `chatwidget/plugin_catalog.rs` module. `plugins.rs` now keeps the
    stateful plugin workflow and orchestration, while `plugin_catalog.rs`
    owns the presentation-heavy catalog/detail popup construction and its
    pure helpers. The goal is to keep `plugins.rs` focused before later
    plugin sharing work adds more catalog behavior.
    
    - Moves existing catalog/detail popup builders and related pure helpers
    into `plugin_catalog.rs`
    - Leaves plugin fetch/state/key handling in `plugins.rs`
    - Adds only minimal sibling-module visibility/import wiring
    - Intentionally makes no product behavior or UI changes beyond the code
    move
  • [codex] Restore thread recency with compatible migration history (#28671)
    ## Summary
    
    - Revert #28655, restoring the thread `recencyAt` behavior introduced by
    #27910.
    - Move `threads_recency_at` to migration 0039 so it no longer collides
    with `external_agent_config_imports` at version 0038.
    - Repair databases that already applied the recency migration as version
    38 by moving the matching migration-history row to version 39 before
    SQLx validation. The current version-38 migration can then apply
    normally.
    
    ## Validation
    
    - `just test -p codex-state
    migrations::tests::repairs_recency_migration_that_was_applied_as_version_38`
    - `just test -p codex-state -p codex-rollout -p codex-thread-store -p
    codex-app-server-protocol -p codex-tui`: 3,439 passed; six TUI tests
    could not open the machine's existing read-only incident database at
    `~/.codex/sqlite/state_5.sqlite`.
    - `just fix -p codex-state`
    - `just fmt`
    - Verified that state migration versions are unique.
  • feat: add run task identity primitives (#19047)
    ## Stack
    
    This is PR 1 of the simplified HAI single-run-task stack:
    
    - [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
    assertion and task-registration primitives, including the shared
    run-task helper used by existing Agent Identity JWT auth.
    - [#19049](https://github.com/openai/codex/pull/19049)
    Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
    Agent Identity runtime auth and its single run task.
    - [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
    provider auth that uses one backend-owned task id for first-party
    inference and compaction requests.
    
    [#19054](https://github.com/openai/codex/pull/19054) collapsed out of
    the active stack because the simplified design no longer needs a
    separate background/control-plane task helper.
    
    ## Summary
    
    The simplified POC shape is one backend-owned task per Agent Identity
    run. This PR makes the first layer match that final shape directly
    instead of introducing task targets, caller-owned external task refs, or
    intermediate wrappers that later PRs would need to undo.
    
    What changed:
    
    - keeps the `AgentAssertion` wire payload as `agent_runtime_id`,
    `task_id`, `timestamp`, and `signature`
    - exposes `register_agent_task` as the single task-registration helper
    for both existing Agent Identity JWT auth and the ChatGPT-registration
    path added later in the stack
    - makes task registration send only the signed registration timestamp;
    the backend owns the returned opaque task id
    - removes the unused target/task-kind/external-task-ref surfaces from
    `codex-agent-identity`
    - keeps Agent Identity JWT JWKS lookup separate from agent/task
    registration URL derivation
    - updates Agent Identity JWT auth to register one run task during auth
    construction and share that task across cloned auth handles
    
    This PR intentionally does not enable ChatGPT-derived Agent Identity.
    That opt-in and config gate are added in the next PR.
    
    ## Testing
    
    - `just test -p codex-agent-identity`
  • Scope command approvals by execution environment (#28738)
    ## Why
    
    Command approval cache keys included the command and working directory,
    but not the execution environment. An approval for `/workspace` locally
    could therefore be reused for the same command and path on an executor.
    
    ## What changed
    
    - Include the selected environment ID in shell and unified-exec approval
    cache keys.
    - Carry that ID through the normal command approval request so clients
    can show which environment is being approved.
    - Expose the environment through app-server as a required nullable
    `environmentId` and show it in the inline TUI approval prompt.
    - Keep older recorded approval events compatible when the environment is
    absent.
    
    For example, `echo ok` in local `/workspace` and `echo ok` in executor
    `/workspace` now produce different approval keys and separate prompts.
    
    ## Scope
    
    This PR does not change network approvals, Guardian review actions, MCP
    elicitation, full-screen TUI rendering, or environment-ID validation.
    Remote `shell_command` execution itself remains in #28722; this PR only
    makes its approval key environment-aware.
  • Revert thread recencyAt for sidebar ordering (#28655)
    ## Why
    
    Revert #27910 to remove the newly introduced thread `recencyAt`
    persistence and API behavior from `main`.
    
    ## What changed
    
    This reverts commit `fac3158c2a783095768076489815f361fa9b0db4`,
    including the state migration, thread-store propagation, app-server API
    surface, generated schemas, and related tests.
    
    ## Validation
    
    Not run before opening; relying on CI for the initial fast signal.
  • Add thread recencyAt for sidebar ordering (#27910)
    ## Summary
    
    Add a server-owned `recencyAt` timestamp and `recency_at` thread-list
    sort key for product recency ordering while preserving the existing
    meaning of `updatedAt` as the latest persisted thread mutation.
    
    This is the server-side alternative to #27697. Rather than narrowing
    `updatedAt`, clients can sort the sidebar by `recency_at` and continue
    treating `updatedAt` as mutation time.
    
    Paired Codex Apps PR:
    [openai/openai#1024599](https://github.com/openai/openai/pull/1024599)
    
    ## Contract
    
    - `recencyAt` initializes when a thread is created.
    - A turn start advances `recencyAt` monotonically.
    - Commentary, agent output, tool results, token/accounting updates, turn
    completion, archive, unarchive, resume, and generic metadata writes do
    not advance it.
    - `updatedAt` retains its existing behavior and continues to advance for
    persisted thread mutations.
    - Current servers populate `recencyAt`; the response field is optional
    in generated TypeScript so clients connected to older servers can fall
    back to `updatedAt`.
    - Filesystem-only fallback uses existing updated/mtime ordering when
    SQLite is unavailable.
    
    ## Persistence and compatibility
    
    Migration 0038 adds second- and millisecond-precision recency columns,
    backfills them from the existing updated timestamp, creates list
    indexes, and includes an insert trigger so older binaries writing to a
    migrated database seed recency without causing later mutations to
    advance it.
    
    Generic metadata upserts preserve existing recency values. Turn-start
    updates use a dedicated monotonic touch, and process-local allocation
    keeps millisecond cursor values unique. State DB list, search, read,
    filtered-list repair, rollout fallback propagation, and app-server
    conversions all carry the new field.
    
    ## API
    
    `Thread` responses include:
    
    ```ts
    recencyAt?: number
    ```
    
    `thread/list` and `thread/search` accept:
    
    ```json
    { "sortKey": "recency_at" }
    ```
    
    Generated TypeScript and JSON schemas are included.
    
    ## Validation
    
    - `just test -p codex-state` — 146 passed
    - `just test -p codex-rollout` — 69 passed
    - `just test -p codex-thread-store` — 81 passed
    - `just test -p codex-app-server-protocol` — 231 passed
    - Focused app-server list ordering, response mapping, archive/unarchive,
    and resume lifecycle tests passed
    - Scoped `just fix` for state, rollout, thread-store,
    app-server-protocol, and app-server
    - `just fmt`
    - `git diff --check`
    - Independent correctness, simplicity, elegance, security, and
    test-quality reviews; actionable ordering, lifecycle, query-projection,
    and timestamp-uniqueness findings were addressed
  • Clarify model-generated and legacy app path types (#28577)
    ## Why
    
    `ApiPathString` kind of implies that it can be used anywhere we pull a
    path out of JSON, but it's not really appropriate for tool arguments
    when the model might generate relative paths.
    
    Prefer `String` for model-generated paths and we can handle the
    conversion per feature for now and define a shared abstraction later if
    it makes sense.
    
    # What
    
    Rename `ApiPathString` to `AppLegacyPathString` to clarify its role.
    
    Expand the `path-types` skill to tell the model to leave tool args as
    bare strings.
  • feat(tui): add rate-limit reset redemption to /usage (#28154)
    ## Why
    
    Codex users can earn personal rate-limit reset credits, but the CLI does
    not currently provide a way to view or redeem them. The `/usage` command
    restored in #27925 is intended to be the entry point for usage-related
    actions, so reset redemption belongs there rather than in a separate
    dashed slash command.
    
    Depends on #28143 for the app-server and backend-client reset-credit
    APIs.
    
    ## What changed
    
    - Turn bare `/usage` into a menu with entries for token activity and
    earned rate-limit resets while preserving `/usage daily`, `/usage
    weekly`, and `/usage cumulative`.
    - Add loading, empty, confirmation, success, retry, and error states
    with a caller-generated UUID idempotency key reused across retries of
    the same logical reset.
    - Show an availability hint only for backend-classified rate-limit
    errors with credits available.
    - Hide the reset entry for workspace accounts.
    
    ## Validation
    
    - `just test -p codex-tui chatwidget::tests::usage` — 19 passed.
    - `just fix -p codex-tui` — passed.
    - `just fmt` — passed.
    - `cargo insta pending-snapshots` from `codex-rs/tui` — no pending
    snapshots.
    
    ## Examples
    <img width="1168" height="304" alt="image"
    src="https://github.com/user-attachments/assets/caa4c1e3-e996-494d-ae17-50b521f5dce8"
    />
    <img width="908" height="260" alt="image"
    src="https://github.com/user-attachments/assets/e38a726b-77cc-4bd0-9ea8-9f3ad21c5768"
    />
    
    
    ### Reset flow
    <img width="1509" height="312" alt="image"
    src="https://github.com/user-attachments/assets/d987013c-78a5-48a2-ad8d-c61ad267a327"
    />
    <img width="585" height="190" alt="image"
    src="https://github.com/user-attachments/assets/de32be19-79b9-4a3e-8574-6f1c208c98ae"
    />
    <img width="600" height="210" alt="image"
    src="https://github.com/user-attachments/assets/88a165cf-796d-4fdc-a7bc-ea89917573da"
    />
    
    <img width="512" height="193" alt="image"
    src="https://github.com/user-attachments/assets/d2353998-5aa8-442e-a5f8-3a8a5b832753"
    />
  • fix(tui): highlight C++ module files (#28554)
    ## Why
    
    Codex syntax-highlights diffs for conventional C++ extensions such as
    `.cpp` and `.cxx`, but C++ module interface files using `.cppm`, `.ixx`,
    or `.cxxm` fall back to plain diff coloring. The bundled syntax set
    already includes C++, but it does not resolve those module extensions by
    itself.
    
    Closes #28223.
    
    ## What changed
    
    - map `.cppm`, `.ixx`, and `.cxxm` to the existing `cpp` syntax in
    `render/highlight.rs`
    - extend alias-resolution coverage for all three module extensions
    - verify `.cpp`, `.cppm`, `.ixx`, and `.cxxm` diffs produce
    syntax-highlighted RGB spans while unknown extensions retain the plain
    fallback
    - snapshot the syntax-colored token segmentation for the supported C++
    module extensions
    
    ## How to Test
    
    1. Ask Codex to create or modify a C++ module interface file using
    `.cppm`, `.ixx`, or `.cxxm`.
    2. Confirm C++ tokens in the rendered diff receive syntax colors instead
    of only the red/green diff treatment.
    3. Modify an equivalent `.cpp` file and confirm its existing
    highlighting remains unchanged.
    4. Modify a file with an unknown extension and confirm it still uses the
    plain diff fallback.
    
    Targeted tests:
    
    - `just test -p codex-tui -E
    'test(find_syntax_resolves_languages_and_aliases) |
    test(cpp_module_extensions_use_cpp_highlighting) |
    test(unknown_extension_falls_back_without_syntax_highlighting)'`
  • chore: side prompt (#28553)
    Fix side bug with prompt
  • fix(tui): restore TUI after suspend (#28342)
    ## Why
    
    On Linux, suspending Codex with `Ctrl+Z` and returning with `fg` can
    leave the composer misaligned or inject terminal response bytes such as
    focus reports into the prompt. Shell job-control output moves the cursor
    while Codex is suspended, and terminal input polling can race with the
    responses used to restore the inline viewport.
    
    Fixes #26564.
    
    ## What changed
    
    - preserve and restore keyboard reporting without disturbing the parent
    terminal stack
    - pause terminal event polling while Codex is suspended and flush
    buffered input before resuming it
    - force crossterm's cached raw-mode state back in sync after the shell
    completes its `fg` handoff
    - probe the actual post-`fg` cursor position with the tolerant
    terminal-response parser, then realign the inline viewport before
    redrawing
    
    ## How to Test
    
    1. On Linux, start the development TUI with `just c`.
    2. Type text into the composer without submitting it.
    3. Press `Ctrl+Z`, run any harmless shell command, then run `fg`.
    4. Confirm the composer redraws below the shell output, the draft text
    is preserved, and no raw escape sequences appear.
    5. Repeat the suspend/resume cycle and confirm normal typing still
    works.
    
    Targeted tests:
    
    - `cargo test -p codex-tui --lib parses_cursor_position_as_zero_based -j
    1`
    - `cargo test -p codex-tui --lib tui::event_stream::tests -j 1`
  • [codex] expose Bedrock credential source in account/read (#27751)
    ## Why
    
    `account/read` currently reports only `type: "amazonBedrock"`, so
    clients cannot distinguish a Codex-managed Bedrock API key from
    credentials supplied by AWS. The app UI needs that distinction to render
    the appropriate account state without duplicating provider-auth logic.
    
    Credential-source selection belongs to the Bedrock model provider
    because it already owns the precedence between managed Bedrock auth and
    the external AWS credential path. This builds on #27443 and #27689.
    
    ## What changed
    
    - Added `AmazonBedrockCredentialSource` with `codexManaged` and
    `awsManaged` values.
    - Included the selected credential source in
    `ProviderAccount::AmazonBedrock` and the app-server `Account` response.
    - Made `AmazonBedrockModelProvider::account_state()` classify the source
    from its managed-auth state.
    - Regenerated the app-server JSON and TypeScript schemas.
    - Updated app-server account documentation and downstream TUI matches.
    
    `codexManaged` means the provider found a managed Bedrock API key.
    `awsManaged` identifies the provider's external AWS credential path; it
    does not assert that the AWS credential chain has been validated.
    
    ## Testing
    
    - Added model-provider coverage for Codex-managed precedence and
    AWS-managed fallback.
    - Added app-server protocol serialization coverage for both wire values.
    - Added app-server integration coverage for both `account/read`
    responses.
    - `just test -p codex-protocol -p codex-model-provider -p
    codex-app-server-protocol` (497 tests passed).
    
    After rebasing onto #27711, the `codex-app-server` test target compiled
    past the image-generation `PathUri` migration. Local linking was then
    interrupted by disk exhaustion (`No space left on device`).
  • [codex] Record external agent import results (#28396)
    ## Summary
    - restore `externalAgentConfig/import/progress` notifications while
    keeping `externalAgentConfig/import/completed` as the must-deliver event
    - persist completed external-agent config imports in state DB by
    `importId`, including concrete success/failure details for config,
    AGENTS.md, skills, plugins, MCP servers, subagents, hooks, commands, and
    sessions
    - add `externalAgentConfig/import/readHistories` so clients can recover
    persisted import results after missing the live completion notification
    - include `errorType` on import failures in protocol
    responses/notifications and persisted DB JSON so future code can
    classify failures without another wire/storage shape change
    
    ## Validation
    - `git diff --check`
    - `just test -p codex-state external_agent_config_imports`
    - `just test -p codex-app-server-protocol`
    - `CODEX_SQLITE_HOME=/private/tmp/codex-app-server-sqlite-read-details
    just test -p codex-app-server
    external_agent_config_import_sends_completion_notification_for_sync_only_import`
    
    Also ran earlier broader checks before publishing:
    - `just test -p codex-state`
    -
    `CODEX_SQLITE_HOME=/private/tmp/codex-app-server-external-agent-test-sqlite
    just test -p codex-app-server external_agent_config`
    - `just test -p codex-external-agent-migration`
  • [codex] Use expect in integration tests (#28441)
    The workspace denies `clippy::expect_used` in production. Although
    `clippy.toml` allows `expect` in tests, Bazel Clippy compiles
    integration-test helper code in a way that does not receive that
    exemption, which encouraged verbose `unwrap_or_else(... panic!(...))`
    and equivalent `match`/`let else` forms.
    
    This allows `clippy::expect_used` once at each integration-test crate
    root (including aggregated suites and test-support libraries), then
    replaces manual panic-based Result and Option unwraps with
    `expect`/`expect_err`. Standalone `tests/*.rs` files remain their own
    crate roots. Intentional assertion and unexpected-variant panics remain
    unchanged, and the production `expect_used = "deny"` lint remains in
    place.
    
    The cleanup is mechanical and net-negative in line count.
  • [codex] Add interruptible sleep tool (#28429)
    ## Why
    
    Models sometimes need to pause briefly while waiting for external work,
    but using a shell command for that delay ties the wait to a process and
    does not naturally resume when new turn input arrives.
    
    ## What changed
    
    - add a built-in `sleep` tool behind the under-development `sleep_tool`
    feature
    - accept a bounded `duration_ms` argument, matching the millisecond
    convention used by unified exec
    - end the sleep early when either steered user input or mailbox input
    arrives
    - include elapsed wall-clock time in completed and interrupted outputs
    - emit a dedicated core `SleepItem` through `item/started` and
    `item/completed`
    - expose the sleep item as app-server v2 `ThreadItem::Sleep` and retain
    it in reconstructed thread history
    - regenerate the configuration schema for the new feature flag
    - regenerate app-server JSON and TypeScript schema fixtures
    
    ## Test plan
    
    - `just test -p codex-core sleep_tool_follows_feature_gate`
    - `just test -p codex-core any_new_input_interrupts_sleep`
    - `just test -p codex-app-server-protocol`
    - `just test -p codex-app-server
    sleep_emits_started_and_completed_items`
  • Use ApiPathString in app-server filesystem permission paths (#28367)
    ## Why
    
    Clients running an app-server on one OS and an exec-server on another OS
    need to be able to pass sandbox config to app-server that refers to
    resources on the executor's foreign OS.
    
    ## What
    
    `AbsolutePathBuf` can't represent these paths and we don't want users to
    be exposed to `PathUri` yet, so this moves the public app-server API to
    be expressed in terms of `ApiPathString`.
    
    Stacked on #28165.
    
    - change app-server v2 filesystem permission paths, including legacy
    read/write roots, to `ApiPathString`
    - localize API paths through `PathUri` when converting into the current
    native core permission types
    - make path-bearing permission conversions fallible and surface
    localization failures instead of silently treating malformed grants as
    ordinary denials
    - propagate conversion failures through app-server and TUI approval
    handling
    - regenerate the app-server JSON and TypeScript schemas
    - leave migration TODOs on native-path conversions so they can be
    removed once core permission paths use `PathUri`
  • [codex] Load API curated marketplace by auth (#28383)
    ## Summary
    - choose the local OpenAI curated marketplace manifest based on auth:
    Codex backend auth gets the existing marketplace, direct provider auth
    gets `api_marketplace.json`
    - include Bedrock API key auth in the direct-provider API marketplace
    path
    - safely skip the API marketplace when `api_marketplace.json` is absent
    
    ## Validation
    - `just fmt`
    - `git diff --check origin/main...HEAD`
    - CI should run the full validation
    
    ## Manual Testing
    
    ### - New api marketplace not available for API key sign
    1. Safely not display anything from api marketplace
    <img width="1161" height="289" alt="Screenshot 2026-06-15 at 21 37 43"
    src="https://github.com/user-attachments/assets/a5f16642-8a20-4ac1-a0de-1274a4c7b5b2"
    />
    
    ### - New api marketplace for API key sign in
    1. Setup api_marketplace.json
    ```
    {
      "name": "openai-curated",
      "interface": {
        "displayName": "Codex official"
      },
      "plugins": [
        {
          "name": "linear",
          "source": {
            "source": "local",
            "path": "./plugins/linear"
          },
          "policy": {
            "installation": "AVAILABLE",
            "authentication": "ON_INSTALL"
          },
          "category": "Productivity"
        }
      ]
    }
    ```
    
    2. Log in with API key, observe that only the defined plugin from
    api_marketplace.json is available from "Codex Official" (outside of
    local testing marketplaces)
    <img width="1167" height="446" alt="Screenshot 2026-06-15 at 21 16 53"
    src="https://github.com/user-attachments/assets/7cf61477-d826-4ef6-bc05-0a23ac1c0259"
    />
    
    also checked functionality on codex app
    
    ### - SiWC users 
    Still uses 'default' marketplace.json and renders all plugins
    <img width="1171" height="502" alt="Screenshot 2026-06-15 at 21 40 25"
    src="https://github.com/user-attachments/assets/d212ea9b-0aa5-470b-8ea4-450efe65bb2b"
    />
    
    also checked functionality on codex app
    
    
    ## Notes
    - `just test -p codex-core-plugins` was started locally before splitting
    branches, but I stopped relying on local tests per follow-up and left
    final validation to PR CI.
  • feat(core): add metadata field to ResponseItem (#28355)
    ## Description
    
    This PR adds an optional `metadata` field to `ResponseItem` for
    Responses API calls. Only mechanical plumbing, no actual values
    populated and sent yet. Turns out just adding a new field to
    `ResponseItem` has quite a large blast radius already.
    
    This change is backwards compatible because `metadata` is optional and
    omitted when absent, so existing response items and rollout history
    without it still deserialize and requests that do not set it keep the
    same wire shape. For provider compatibility, we strip out `metadata`
    before non-OpenAI Responses requests so Azure and AWS Bedrock never see
    this field.
    
    My followup PR here will actually make use of it to start storing and
    passing along `turn_id`: https://github.com/openai/codex/pull/28360
    
    ## What changed
    
    - Added `ResponseItemMetadata` with optional `turn_id`, plus optional
    `metadata` on Responses API item variants and inter-agent communication.
    - Preserved item metadata through response-item rewrites such as
    truncation, missing tool-output synthesis, compaction history
    rebuilding, visible-history conversion, rollout/resume, and generated
    app-server schemas/types.
    - Strip item metadata from non-OpenAI Responses requests while
    preserving it for OpenAI-shaped requests.
    - Updated the mechanical fixture/test construction churn required by the
    new optional field.
  • feat(app-server): expose rate-limit reset credits (#28143)
    ## Why
    
    Codex users can earn personal rate-limit reset credits, but app-server
    clients do not currently have an API for reading or redeeming them. This
    adds the backend and protocol foundation used by the `/usage` TUI flow
    in #28154.
    
    ## What changed
    
    - Extend `account/rateLimits/read` with a nullable
    `rateLimitResetCredits` summary sourced from the existing usage
    response.
    - Add backend-client and app-server support for consuming a reset with a
    caller-generated idempotency key. A UUID is recommended, and clients
    reuse the same key when retrying the same logical reset.
    - Return only the consume `outcome`; clients refetch
    `account/rateLimits/read` for updated window state.
    - Document the response field and each consume outcome, and regenerate
    the JSON and TypeScript schema fixtures.
    - Clarify in `AGENTS.md` that new app-server string enum values use
    camelCase on the wire.
    - Update the existing TUI response fixture for the expanded protocol
    shape.
    - Add coverage for authentication, response mapping, backend failures,
    consume outcomes, and request timeout behavior.
    
    ## Validation
    
    - `just test -p codex-app-server-protocol` — 231 passed.
    - `just test -p codex-backend-client` — 14 passed.
    - Focused `codex-app-server` reset-credit tests — 5 passed.
    - Focused `codex-tui` protocol response fixture test — passed.
    - `just fix -p codex-backend-client -p codex-app-server-protocol -p
    codex-app-server` — passed.
    - `just fmt` — passed.
  • Add request user input auto-resolution timer (#28235)
    ## Summary
    - Add TUI auto-resolution handling for `request_user_input` prompts when
    `autoResolutionMs` is present.
    - Use a 60s hidden grace period followed by a 60s visible countdown,
    then submit an empty answer response if the user does not interact.
    - Snooze auto-resolution on key or paste interaction and add
    snapshot/test coverage for the countdown UI.
    
    ## Notes
    - The TUI currently treats `autoResolutionMs` as an enable signal and
    intentionally does not use the provided duration value for the countdown
    policy.
    
    ### Auto resolution
    
    
    https://github.com/user-attachments/assets/5323152f-2ece-4aba-b75d-c32aa776f544
    
    
    ### Snooze after interaction
    
    
    https://github.com/user-attachments/assets/100d54c4-3a41-4c6c-9c07-cd28075a0d62
  • TUI Plugin Sharing 2 - add remote plugin section plumbing (#26702)
    This adds the background plumbing for remote-backed plugin catalog
    sections while leaving the fuller directory presentation to the next PR.
    The TUI can fetch section-specific remote marketplace results, keep
    local plugin data available, and carry section errors forward for later
    rendering.
    
    - Fetches explicit remote marketplace kinds for curated, workspace, and
    shared-with-me sections.
    - Gates shared-with-me loading on the plugin sharing feature flag.
    - Adds section-level error state and user-actionable error copy.
    - Merges remote marketplace results into the cached plugin list without
    discarding local results.
  • Remove terminal resize reflow flag gates (#27794)
    ## Why
    
    `terminal_resize_reflow` is now stable and should behave as always on.
    Keeping the disabled runtime paths around made the feature look
    configurable even though the rollout is complete, and old config could
    still suggest there was a supported off mode.
    
    ## What Changed
    
    - Marked `terminal_resize_reflow` as `Stage::Removed` while keeping it
    default-enabled for compatibility.
    - Ignored `[features].terminal_resize_reflow` config entries so stale
    `false` settings no longer affect the effective feature set.
    - Removed TUI branches that depended on the flag being disabled, so
    draw, replay buffering, stream finalization, and resize scheduling all
    assume resize reflow is active.
    - Simplified resize smoke coverage to exercise the always-on behavior
    only.
    
    ## Verification
    
    - `just test -p codex-features`
    - `just test -p codex-tui resize_reflow`
    - `just test -p codex-tui initial_replay_buffer
    thread_switch_replay_buffer`