Commit Graph

403 Commits

  • Add turn-scoped environment selections (#18416)
    ## Summary
    - add experimental turn/start.environments params for per-turn
    environment id + cwd selections
    - pass selections through core protocol ops and resolve them with
    EnvironmentManager before TurnContext creation
    - treat omitted selections as default behavior, empty selections as no
    environment, and non-empty selections as first environment/cwd as the
    turn primary
    
    ## Testing
    - ran `just fmt`
    - ran `just write-app-server-schema`
    - not run: unit tests for this stacked PR
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Support multiple managed environments (#18401)
    ## Summary
    - refactor EnvironmentManager to own keyed environments with
    default/local lookup helpers
    - keep remote exec-server client creation lazy until exec/fs use
    - preserve disabled agent environment access separately from internal
    local environment access
    
    ## Validation
    - not run (per Codex worktree instruction to avoid tests/builds unless
    requested)
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • fix: fully revert agent identity runtime wiring (#18757)
    ## Summary
    
    This PR fully reverts the previously merged Agent Identity runtime
    integration from the old stack:
    https://github.com/openai/codex/pull/17387/changes
    
    It removes the Codex-side task lifecycle wiring, rollout/session
    persistence, feature flag plumbing, lazy `auth.json` mutation,
    background task auth paths, and request callsite changes introduced by
    that stack.
    
    This leaves the repo in a clean pre-AgentIdentity integration state so
    the follow-up PRs can reintroduce the pieces in smaller reviewable
    layers.
    
    ## Stack
    
    1. This PR: full revert
    2. https://github.com/openai/codex/pull/18871: move Agent Identity
    business logic into a crate
    3. https://github.com/openai/codex/pull/18785: add explicit
    AgentIdentity auth mode and startup task allocation
    4. https://github.com/openai/codex/pull/18811: migrate auth callsites
    through AuthProvider
    
    ## Testing
    
    Tests: targeted Rust checks, cargo-shear, Bazel lock check, and CI.
  • Load app-server config through ConfigManager (#18870)
    ## Summary
    - Load app-server startup config through `ConfigManager` instead of
    direct `ConfigBuilder` calls.
    - Move `ConfigManager` constructor-owned state (`cli_overrides`, runtime
    feature map, cloud requirements loader) behind internal manager fields.
    - Pass `ConfigManager` into `MessageProcessor` directly instead of
    reconstructing it from raw args.
    
    ## Tests
    - `cargo check -p codex-app-server`
    - `cargo test -p codex-app-server`
    - `just fix -p codex-app-server`
    - `just fmt`
  • Refactor app-server config loading into ConfigManager (#18442)
    Localize app-server configuration loading in one place.
  • Propagate thread id in MCP tool metadata (#18093)
    ## Summary
    - attach the authoritative Codex thread id to MCP tool request
    `_meta.threadId` for model-initiated tool calls
    - attach the same thread id for manual `mcpServer/tool/call` requests
    before invoking the MCP server
    - cover both metadata helper behavior and the manual app-server MCP path
    in tests
    
    
    needed because the Rust app-server is the last place that still has
    authoritative knowledge of “this model-generated MCP tool call belongs
    to conversation/thread X” before the request leaves Codex and reaches
    Hoopa. It adds threadId to MCP request metadata in the model-generated
    tool-call path, using sess.conversation_id, and also does the same for
    the manual mcpServer/tool/call path.
    
    ## Test plan
    - `cargo test -p codex-core
    mcp_tool_call_thread_id_meta_is_added_to_request_meta --lib`
    - `cargo test -p codex-app-server
    mcp_server_tool_call_returns_tool_result`
    
    Paired Hoopa consumer PR: https://github.com/openai/openai/pull/833263
  • app-server: define device key v2 protocol (#18428)
    ## Why
    
    Clients need a stable app-server protocol surface for enrolling a local
    device key, retrieving its public key, and producing a device-bound
    proof.
    
    The protocol reports `protectionClass` explicitly so clients can
    distinguish hardware-backed keys from an explicitly allowed OS-protected
    fallback. Signing uses a tagged `DeviceKeySignPayload` enum rather than
    arbitrary bytes so each signed statement is auditable at the API
    boundary.
    
    ## What changed
    
    - Added v2 JSON-RPC methods for `device/key/create`,
    `device/key/public`, and `device/key/sign`.
    - Added request/response types for device-key metadata, SPKI public
    keys, protection classes, and ECDSA signatures.
    - Added `DeviceKeyProtectionPolicy` with hardware-only default behavior
    and an explicit `allow_os_protected_nonextractable` option.
    - Added the initial `remoteControlClientConnection` signing payload
    variant.
    - Regenerated JSON Schema and TypeScript fixtures for app-server
    clients.
    
    ## Stack
    
    This is PR 1 of 4 in the device-key app-server stack.
    
    ## Validation
    
    - `just write-app-server-schema`
    - `cargo test -p codex-app-server-protocol`
  • [tool search] support namespaced deferred dynamic tools (#18413)
    Deferred dynamic tools need to round-trip a namespace so a tool returned
    by `tool_search` can be called through the same registry key that core
    uses for dispatch.
    
    This change adds namespace support for dynamic tool specs/calls,
    persists it through app-server thread state, and routes dynamic tool
    calls by full `ToolName` while still sending the app the leaf tool name.
    Deferred dynamic tools must provide a namespace; non-deferred dynamic
    tools may remain top-level.
    
    It also introduces `LoadableToolSpec` as the shared
    function-or-namespace Responses shape used by both `tool_search` output
    and dynamic tool registration, so dynamic tools use the same wrapping
    logic in both paths.
    
    Validation:
    - `cargo test -p codex-tools`
    - `cargo test -p codex-core tool_search`
    
    ---------
    
    Co-authored-by: Sayan Sisodiya <sayan@openai.com>
  • chore: document intentional await-holding cases (#18423)
    ## Why
    
    This PR prepares the stack to enable Clippy await-holding lints that
    were left disabled in #18178. The mechanical lock-scope cleanup is
    handled separately; this PR is the documentation/configuration layer for
    the remaining await-across-guard sites.
    
    Without explicit annotations, reviewers and future maintainers cannot
    tell whether an await-holding warning is a real concurrency smell or an
    intentional serialization boundary.
    
    ## What changed
    
    - Configures `clippy.toml` so `await_holding_invalid_type` also covers
    `tokio::sync::{MutexGuard,RwLockReadGuard,RwLockWriteGuard}`.
    - Adds targeted `#[expect(clippy::await_holding_invalid_type, reason =
    ...)]` annotations for intentional async guard lifetimes.
    - Documents the main categories of intentional cases: active-turn state
    transitions that must remain atomic, session-owned MCP manager accesses,
    remote-control websocket serialization, JS REPL kernel/process
    serialization, OAuth persistence, external bearer token refresh
    serialization, and tests that intentionally serialize shared global or
    session-owned state.
    - For external bearer token refresh, documents the existing
    serialization boundary: holding `cached_token` across the provider
    command prevents concurrent cache misses from starting duplicate refresh
    commands, and the current behavior is small enough that an explicit
    expectation is easier to maintain than adding another synchronization
    primitive.
    
    ## Verification
    
    - `cargo clippy -p codex-login --all-targets`
    - `cargo clippy -p codex-connectors --all-targets`
    - `cargo clippy -p codex-core --all-targets`
    - The follow-up PR #18698 enables `await_holding_invalid_type` and
    `await_holding_lock` as workspace `deny` lints, so any undocumented
    remaining offender will fail Clippy.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18423).
    * #18698
    * __->__ #18423
  • Add remote_sandbox_config to our config requirements (#18763)
    ## Why
    
    Customers need finer-grained control over allowed sandbox modes based on
    the host Codex is running on. For example, they may want stricter
    sandbox limits on devboxes while keeping a different default elsewhere.
    
    Our current cloud requirements can target user/account groups, but they
    cannot vary sandbox requirements by host. That makes remote development
    environments awkward because the same top-level `allowed_sandbox_modes`
    has to apply everywhere.
    
    ## What
    
    Adds a new `remote_sandbox_config` section to `requirements.toml`:
    
    ```toml
    allowed_sandbox_modes = ["read-only"]
    
    [[remote_sandbox_config]]
    hostname_patterns = ["*.org"]
    allowed_sandbox_modes = ["read-only", "workspace-write"]
    
    [[remote_sandbox_config]]
    hostname_patterns = ["*.sh", "runner-*.ci"]
    allowed_sandbox_modes = ["read-only", "danger-full-access"]
    ```
    
    During requirements resolution, Codex resolves the local host name once,
    preferring the machine FQDN when available and falling back to the
    cleaned kernel hostname. This host classification is best effort rather
    than authenticated device proof.
    
    Each requirements source applies its first matching
    `remote_sandbox_config` entry before it is merged with other sources.
    The shared merge helper keeps that `apply_remote_sandbox_config` step
    paired with requirements merging so new requirements sources do not have
    to remember the extra call.
    
    That preserves source precedence: a lower-precedence requirements file
    with a matching `remote_sandbox_config` cannot override a
    higher-precedence source that already set `allowed_sandbox_modes`.
    
    This also wires the hostname-aware resolution through app-server,
    CLI/TUI config loading, config API reads, and config layer metadata so
    they all evaluate remote sandbox requirements consistently.
    
    ## Verification
    
    - `cargo test -p codex-config remote_sandbox_config`
    - `cargo test -p codex-config host_name`
    - `cargo test -p codex-core
    load_config_layers_applies_matching_remote_sandbox_config`
    - `cargo test -p codex-core
    system_remote_sandbox_config_keeps_cloud_sandbox_modes`
    - `cargo test -p codex-config`
    - `cargo test -p codex-core` unit tests passed; `tests/all.rs`
    integration matrix was intentionally stopped after the relevant focused
    tests passed
    - `just fix -p codex-config`
    - `just fix -p codex-core`
    - `cargo check -p codex-app-server`
  • Make MCP resource read threadless (#18292)
    ## Summary
    
    Making thread id optional so that we can better cache resources for MCPs
    for connectors since their resource templates is universal and not
    particular to projects.
    
    - Make `mcpServer/resource/read` accept an optional `threadId`
    - Read resources from the current MCP config when no thread is supplied
    - Keep the existing thread-scoped path when `threadId` is present
    - Update the generated schemas, README, and integration coverage
    
    ## Testing
    - `just write-app-server-schema`
    - `just fmt`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-mcp`
    - `cargo test -p codex-app-server --test all mcp_resource`
    - `just fix -p codex-mcp`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
  • feat: add a built-in Amazon Bedrock model provider (#18744)
    ## Why
    
    Codex needs a first-class `amazon-bedrock` model provider so users can
    select Bedrock without copying a full provider definition into
    `config.toml`. The provider has Codex-owned defaults for the pieces that
    should stay consistent across users: the display `name`, Bedrock
    `base_url`, and `wire_api`.
    
    At the same time, users still need a way to choose the AWS credential
    profile used by their local environment. This change makes
    `amazon-bedrock` a partially modifiable built-in provider: code owns the
    provider identity and endpoint defaults, while user config can set
    `model_providers.amazon-bedrock.aws.profile`.
    
    For example:
    
    ```toml
    model_provider = "amazon-bedrock"
    
    [model_providers.amazon-bedrock.aws]
    profile = "codex-bedrock"
    ```
    
    ## What Changed
    
    - Added `amazon-bedrock` to the built-in model provider map with:
      - `name = "Amazon Bedrock"`
      - `base_url = "https://bedrock-mantle.us-east-1.api.aws/v1"`
      - `wire_api = "responses"`
    - Added AWS provider auth config with a profile-only shape:
    `model_providers.<id>.aws.profile`.
    - Kept AWS auth config restricted to `amazon-bedrock`; custom providers
    that set `aws` are rejected.
    - Allowed `model_providers.amazon-bedrock` through reserved-provider
    validation so it can act as a partial override.
    - During config loading, only `aws.profile` is copied from the
    user-provided `amazon-bedrock` entry onto the built-in provider. Other
    Bedrock provider fields remain hard-coded by the built-in definition.
    - Updated the generated config schema for the new provider AWS profile
    config.
  • Add session config loader interface (#18208)
    ## Why
    
    Cloud-hosted sessions need a way for the service that starts or manages
    a thread to provide session-owned config without treating all config as
    if it came from the same user/project/workspace TOML stack.
    
    The important boundary is ownership: some values should be controlled by
    the session/orchestrator, some by the authenticated user, and later some
    may come from the executor. The earlier broad config-store shape made
    that boundary too fuzzy and overlapped heavily with the existing
    filesystem-backed config loader. This PR starts with the smaller piece
    we need now: a typed session config loader that can feed the existing
    config layer stack while preserving the normal precedence and merge
    behavior.
    
    ## What Changed
    
    - Added `ThreadConfigLoader` and related typed payloads in
    `codex-config`.
    - `SessionThreadConfig` currently supports `model_provider`,
    `model_providers`, and feature flags.
    - `UserThreadConfig` is present as an ownership boundary, but does not
    yet add TOML-backed fields.
    - `NoopThreadConfigLoader` preserves existing behavior when no external
    loader is configured.
      - `StaticThreadConfigLoader` supports tests and simple callers.
    
    - Taught thread config sources to produce ordinary `ConfigLayerEntry`
    values so the existing `ConfigLayerStack` remains the place where
    precedence and merging happen.
    
    - Wired the loader through `ConfigBuilder`, the config loader, and
    app-server startup paths so app-server can provide session-owned config
    before deriving a thread config.
    
    - Added coverage for:
      - translating typed thread config into config layers,
    - inserting thread config layers into the stack at the right precedence,
    - applying session-provided model provider and feature settings when
    app-server derives config from thread params.
    
    ## Follow-Ups
    
    This intentionally stops short of adding the remote/service transport.
    The next pieces are expected to be:
    
    1. Define the proto/API shape for this interface.
    2. Add a client implementation that can source session config from the
    service side.
    
    ## Verification
    
    - Added unit coverage in `codex-config` for the loader and layer
    conversion.
    - Added `codex-core` config loader coverage for thread config layer
    precedence.
    - Added app-server coverage that verifies session thread config wins
    over request-provided config for model provider and feature settings.
  • Read conversation summaries through thread store (#18716)
    Migrate the conversation summary App Server methods to ThreadStore
    
    Because this app server api allows explicitly fetching the thread by
    rollout path, intercept that case in the app server code and (a) route
    directly to underlying local thread store methods if we're using a local
    thread store, or (b) throw an unsupported error if we're using a remote
    thread store. This keeps the thread store API clean and all filesystem
    operations inside of the local thread store, which pushing the
    "fundamental incompatibility" check as early as possible.
  • feat: cascade thread archive (#18112)
    Cascade the thread archive endpoint to all the sub-agents in the agent
    tree
    
    Fix: https://github.com/openai/codex/issues/17867
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Add experimental remote thread store config (#18714)
    Add experimental config to use remote thread store rather than local
    thread store implementation in app server
  • codex: move unloaded thread writes into store (#18361)
    - Migrates unloaded `thread/name/set` and `thread/memoryModeSet`
    app-server writes behind the generic
    `ThreadStore::update_thread_metadata` API rather than adding one-off
    store methods for setting thread name or memory mode.
    - Implements the local ThreadStore metadata patch path for thread name
    and memory mode, including rollout append, legacy name index updates,
    SessionMeta validation/update, SQLite reconciliation, and re-reading the
    stored thread.
    - Adds focused local thread-store unit coverage plus app-server
    integration coverage for the migrated unloaded write paths.
  • [codex] Use background task auth for additional backend calls (#18260)
    ## Summary
    
    Splits the larger PR4.1 background task auth rollout by moving
    additional backend/control-plane call sites into this downstream PR.
    
    This PR keeps callers on the same design as PR4.1: most code asks
    `AuthManager` for the default ChatGPT backend authorization header, and
    `AuthManager` decides bearer vs background AgentAssertion internally.
    Task-pinned inference auth remains separate because it needs the
    thread's registered task id.
    
    ## Stack
    
    - PR1: https://github.com/openai/codex/pull/17385 - add
    `features.use_agent_identity`
    - PR2: https://github.com/openai/codex/pull/17386 - register agent
    identities when enabled
    - PR3: https://github.com/openai/codex/pull/17387 - register agent tasks
    when enabled
    - PR3.1: https://github.com/openai/codex/pull/17978 - persist and
    prewarm registered tasks per thread
    - PR4: https://github.com/openai/codex/pull/17980 - use task-scoped
    `AgentAssertion` for downstream calls
    - PR4.1: https://github.com/openai/codex/pull/18094 - introduce
    AuthManager-owned background/control-plane `AgentAssertion` auth
    - PR4.2: this PR - use background task auth for additional
    backend/control-plane calls
    
    ## What Changed
    
    - pass full authorization header values through backend-client and
    cloud-tasks-client call paths where needed
    - move ChatGPT client, cloud requirements, cloud tasks, thread-manager,
    and models-manager background auth usage into this downstream slice
    - make app-server remote control enrollment/websocket auth ask
    `AuthManager` for the local backend authorization header instead of
    threading a background auth mode through transport options
    - keep the same feature-gated bearer fallback behavior from PR4.1
    
    ## Validation
    
    - `just fmt`
    - `cargo check -p codex-core -p codex-login -p codex-analytics -p
    codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
    codex-models-manager -p codex-chatgpt -p codex-model-provider -p
    codex-mcp -p codex-core-skills`
    - `cargo test -p codex-login agent_identity`
    - `cargo test -p codex-model-provider bearer_auth_provider`
    - `cargo test -p codex-core agent_assertion`
    - `cargo test -p codex-app-server remote_control`
    - `cargo test -p codex-cloud-requirements fetch_cloud_requirements`
    - `cargo test -p codex-models-manager manager::tests`
    - `cargo test -p codex-chatgpt`
    - `cargo test -p codex-cloud-tasks`
    - `just fix -p codex-core -p codex-login -p codex-analytics -p
    codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
    codex-models-manager -p codex-chatgpt -p codex-model-provider -p
    codex-mcp -p codex-core-skills`
    - `just fix -p codex-app-server`
    - `git diff --check`
  • [codex] Use background agent task auth for backend calls (#18094)
    ## Summary
    
    Introduces a single background/control-plane agent task for ChatGPT
    backend requests that do not have a thread-scoped task, with
    `AuthManager` owning the default ChatGPT backend authorization decision.
    
    Callers now ask `AuthManager` for the default ChatGPT backend
    authorization header. `AuthManager` decides whether that is bearer or
    background AgentAssertion based on config/internal state, while
    low-level bootstrap paths can explicitly request bearer-only auth.
    
    This PR is stacked on PR4 and focuses on the shared background task auth
    plumbing plus the first tranche of backend/control-plane consumers. The
    remaining callsite wiring is split into PR4.2 to keep review size down.
    
    ## Stack
    
    - PR1: https://github.com/openai/codex/pull/17385 - add
    `features.use_agent_identity`
    - PR2: https://github.com/openai/codex/pull/17386 - register agent
    identities when enabled
    - PR3: https://github.com/openai/codex/pull/17387 - register agent tasks
    when enabled
    - PR3.1: https://github.com/openai/codex/pull/17978 - persist and
    prewarm registered tasks per thread
    - PR4: https://github.com/openai/codex/pull/17980 - use task-scoped
    `AgentAssertion` for downstream calls
    - PR4.1: this PR - introduce AuthManager-owned background/control-plane
    `AgentAssertion` auth
    - PR4.2: https://github.com/openai/codex/pull/18260 - use background
    task auth for additional backend/control-plane calls
    
    ## What Changed
    
    - add background task registration and assertion minting inside
    `codex-login`
    - persist `agent_identity.background_task_id` separately from
    per-session task state
    - make `BackgroundAgentTaskManager` private to `codex-login`; call sites
    do not instantiate or pass it around
    - teach `AuthManager` the ChatGPT backend base URL and feature-derived
    background auth mode from resolved config
    - expose bearer-only helpers for bootstrap/registration/refresh-style
    paths that must not use AgentAssertion
    - wire `AuthManager` default ChatGPT authorization through app listing,
    connector directory listing, remote plugins, MCP status/listing,
    analytics, and core-skills remote calls
    - preserve bearer fallback when the feature is disabled, the backend
    host is unsupported, or background task registration is not available
    
    ## Validation
    
    - `just fmt`
    - `cargo check -p codex-core -p codex-login -p codex-analytics -p
    codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
    codex-models-manager -p codex-chatgpt -p codex-model-provider -p
    codex-mcp -p codex-core-skills`
    - `cargo test -p codex-login agent_identity`
    - `cargo test -p codex-model-provider bearer_auth_provider`
    - `cargo test -p codex-core agent_assertion`
    - `cargo test -p codex-app-server remote_control`
    - `cargo test -p codex-cloud-requirements fetch_cloud_requirements`
    - `cargo test -p codex-models-manager manager::tests`
    - `cargo test -p codex-chatgpt`
    - `cargo test -p codex-cloud-tasks`
    - `just fix -p codex-core -p codex-login -p codex-analytics -p
    codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
    codex-models-manager -p codex-chatgpt -p codex-model-provider -p
    codex-mcp -p codex-core-skills`
    - `just fix -p codex-app-server`
    - `git diff --check`
  • [codex] Add marketplace/remove app-server RPC (#17751)
    ## Summary
    
    Add a new app-server `marketplace/remove` RPC on top of the shared
    marketplace-remove implementation.
    
    This change:
    - adds `MarketplaceRemoveParams` / `MarketplaceRemoveResponse` to the
    app-server protocol
    - wires the new request through `codex_message_processor`
    - reuses the shared core marketplace-remove flow from the stacked
    refactor PR
    - updates generated schema files and adds focused app-server coverage
    
    ## Validation
    
    - `just write-app-server-schema`
    - `just fmt`
    - heavy compile/test coverage deferred to GitHub CI per request
  • [5/6] Wire executor-backed MCP stdio (#18212)
    ## Summary
    - Add the executor-backed RMCP stdio transport.
    - Wire MCP stdio placement through the executor environment config.
    - Cover local and executor-backed stdio paths with the existing MCP test
    helpers.
    
    ## Stack
    ```text
    o  #18027 [6/6] Fail exec client operations after disconnect
    │
    @  #18212 [5/6] Wire executor-backed MCP stdio
    │
    o  #18087 [4/6] Abstract MCP stdio server launching
    │
    o  #18020 [3/6] Add pushed exec process events
    │
    o  #18086 [2/6] Support piped stdin in exec process API
    │
    o  #18085 [1/6] Add MCP server environment config
    │
    o  main
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • [codex] Add owner nudge app-server API (#18220)
    ## Summary
    
    Second PR in the split from #17956. Stacked on #18227.
    
    - adds app-server v2 protocol/schema support for
    `account/sendAddCreditsNudgeEmail`
    - adds the backend-client `send_add_credits_nudge_email` request and
    request body mapping
    - handles the app-server request with auth checks, backend call, and
    cooldown mapping
    - adds the disabled `workspace_owner_usage_nudge` feature flag and
    focused app-server/backend tests
    
    ## Validation
    
    - `cargo test -p codex-backend-client`
    - `cargo test -p codex-app-server-protocol`
    - `cargo test -p codex-app-server rate_limits`
    - `cargo test -p codex-tui workspace_`
    - `cargo test -p codex-tui status_`
    - `just fmt`
    - `just fix -p codex-backend-client`
    - `just fix -p codex-app-server-protocol`
    - `just fix -p codex-app-server`
    - `just fix -p codex-tui`
  • feat: Budget skill metadata and surface trimming as a warning (#18298)
    Cap the model-visible skills section to a small share of the context
    window, with a fallback character budget, and keep only as many implicit
    skills as fit within that budget.
    
    Emit a non-fatal warning when enabled skills are omitted, and add a new
    app-server warning notification
    
    Record thread-start skill metrics for total enabled skills, kept skills,
    and whether truncation happened
    
    ---------
    
    Co-authored-by: Matthew Zeng <mzeng@openai.com>
    Co-authored-by: Codex <noreply@openai.com>
  • feat: Add remote plugin fields to plugin API (#17277)
    ## Summary
    Update the plugin API for the new remote plugin model.
    
    The mental model is no longer “keep local plugin state in sync with
    remote.” Instead, local and remote plugins are becoming separate
    sources. Remote catalog entries can be shown directly from the remote
    API before installation; after installation they are still downloaded
    into the local cache for execution, but remote installed state will come
    from the API and be held in memory rather than being read from config.
    
    • ## API changes
    - Remove `forceRemoteSync` from `plugin/list`, `plugin/install`, and
    `plugin/uninstall`.
      - Remove `remoteSyncError` from `plugin/list`.
      - Add remote-capable metadata to `plugin/list` / `plugin/read`:
        - nullable `marketplaces[].path`
        - `source: { type: "remote", downloadUrl }`
        - URL asset fields alongside local path fields:
      `composerIconUrl`, `logoUrl`, `screenshotUrls`
      - Make `plugin/read` and `plugin/install` source-compatible:
        - `marketplacePath?: AbsolutePathBuf | null`
        - `remoteMarketplaceName?: string | null`
        - exactly one source is required at runtime
  • [codex] Add cross-repo plugin sources to marketplace manifests (#18017)
    ## Summary
    - add first-class marketplace support for git-backed plugin sources
    - keep the newer marketplace parsing behavior from `main`, including
    alternate manifest locations and string local sources
    - materialize remote plugin sources during install, detail reads, and
    non-curated cache refresh
    - expose git plugin source metadata through the app-server protocol
    
    ## Details
    This teaches the marketplace parser to accept all of the following:
    - local string sources such as `"source": "./plugins/foo"`
    - local object sources such as
    `{"source":"local","path":"./plugins/foo"}`
    - remote repo-root sources such as
    `{"source":"url","url":"https://github.com/org/repo.git"}`
    - remote subdir sources such as
    `{"source":"git-subdir","url":"owner/repo","path":"plugins/foo","ref":"main","sha":"..."}`
    
    It also preserves the newer tolerant behavior from `main`: invalid or
    unsupported plugin entries are skipped instead of breaking the whole
    marketplace.
    
    ## Validation
    - `cargo test -p codex-core plugins::marketplace::tests`
    - `just fix -p codex-core`
    - `just fmt`
    
    ## Notes
    - A full `cargo test -p codex-core` run still hit unrelated existing
    failures in agent and multi-agent tests during this session; the
    marketplace-focused suite passed after the rebase resolution.
  • refactor: narrow async lock guard lifetimes (#18211)
    Follow-up to https://github.com/openai/codex/pull/18178, where we called
    out enabling the await-holding lint as a follow-up.
    
    The long-term goal is to enable Clippy coverage for async guards held
    across awaits. This PR is intentionally only the first, low-risk cleanup
    pass: it narrows obvious lock guard lifetimes and leaves
    `codex-rs/Cargo.toml` unchanged so the lint is not enabled until the
    remaining cases are fixed or explicitly justified. It intentionally
    leaves the active-turn/turn-state locking pattern alone because those
    checks and mutations need to stay atomic.
    
    ## Common fixes used here
    
    These are the main patterns reviewers should expect in this PR, and they
    are also the patterns to reach for when fixing future `await_holding_*`
    findings:
    
    - **Scope the guard to the synchronous work.** If the code only needs
    data from a locked value, move the lock into a small block, clone or
    compute the needed values, and do the later `.await` after the block.
    - **Use direct one-line mutations when there is no later await.** Cases
    like `map.lock().await.remove(&id)` are acceptable when the guard is
    only needed for that single mutation and the statement ends before any
    async work.
    - **Drain or clone work out of the lock before notifying or awaiting.**
    For example, the JS REPL drains pending exec senders into a local vector
    and the websocket writer clones buffered envelopes before it serializes
    or sends them.
    - **Use a `Semaphore` only when serialization is intentional across
    async work.** The test serialization guards intentionally span awaited
    setup or execution, so using a semaphore communicates "one at a time"
    without holding a mutex guard.
    - **Remove the mutex when there is only one owner.** The PTY stdin
    writer task owns `stdin` directly; the old `Arc<Mutex<_>>` did not
    protect shared access because nothing else had access to the writer.
    - **Do not split locks that protect an atomic invariant.** This PR
    deliberately leaves active-turn/turn-state paths alone because those
    checks and mutations need to stay atomic. Those cases should be fixed
    separately with a design change or documented with `#[expect]`.
    
    ## What changed
    
    - Narrow scoped async mutex guards in app-server, JS REPL, network
    approval, remote-control websocket, and the RMCP test server.
    - Replace test-only async mutex serialization guards with semaphores
    where the guard intentionally lives across async work.
    - Let the PTY pipe writer task own stdin directly instead of wrapping it
    in an async mutex.
    
    ## Verification
    
    - `just fix -p codex-core -p codex-app-server -p codex-rmcp-client -p
    codex-shell-escalation -p codex-utils-pty -p codex-utils-readiness`
    - `just clippy -p codex-core`
    - `cargo test -p codex-core -p codex-app-server -p codex-rmcp-client -p
    codex-shell-escalation -p codex-utils-pty -p codex-utils-readiness` was
    run; the app-server suite passed, and `codex-core` failed in the local
    sandbox on six otel approval tests plus
    `suite::user_shell_cmd::user_shell_command_does_not_set_network_sandbox_env_var`,
    which appear to depend on local command approval/default rules and
    `CODEX_SANDBOX_NETWORK_DISABLED=1` in this environment.
  • Add sorting/backwardsCursor to thread/list and new thread/turns/list api (#17305)
    To improve performance of UI loads from the app, add two main
    improvements:
    1. The `thread/list` api now gets a `sortDirection` request field and a
    `backwardsCursor` to the response, which lets you paginate forwards and
    backwards from a window. This lets you fetch the first few items to
    display immediately while you paginate to fill in history, then can
    paginate "backwards" on future loads to catch up with any changes since
    the last UI load without a full reload of the entire data set.
    2. Added a new `thread/turns/list` api which also has sortDirection and
    backwardsCursor for the same behavior as `thread/list`, allowing you the
    same small-fetch for immediate display followed by background fill-in
    and resync catchup.
  • codex: route thread/read persistence through thread store (#18352)
    Summary
    - replace the thread/read persisted-load helper with
    ThreadStore::read_thread
    - move SQLite/rollout summary, name, fork metadata, and history loading
    for persisted reads into LocalThreadStore
    - leave getConversationSummary unchanged for a later PR
    
    Context
    - Replaces closed stacked PR #18232 after PR #18231 merged and its base
    branch was deleted.
  • [codex] Revoke ChatGPT tokens on logout (#17825)
    ## Summary
    
    This changes Codex logout so managed ChatGPT auth is revoked against
    AuthAPI before local auth state is removed. CLI logout, TUI `/logout`,
    and the app-server account logout path now use the token-revoking logout
    flow instead of only deleting `auth.json` / credential store state.
    
    ## Root Cause
    
    Logout previously cleared only local auth storage. That removed Codex's
    local credentials but did not ask the backend to invalidate the
    refresh/access token state associated with a managed ChatGPT login.
    
    ## Behavior
    
    For managed ChatGPT auth, logout sends the stored refresh token to
    `https://auth.openai.com/oauth/revoke` with `token_type_hint:
    refresh_token` and the Codex OAuth client id, then deletes all local
    auth stores after revocation succeeds. If only an access token is
    available, it falls back to revoking that access token. API key auth and
    externally supplied `chatgptAuthTokens` are still only cleared locally
    because Codex does not own a refresh token for those modes.
    
    Revocation failures are fail-closed: if Codex cannot load stored auth or
    the backend revoke call fails, logout returns an error and leaves local
    auth in place so the user can retry instead of silently clearing local
    state while backend tokens remain valid.
    
    ## Validation
    ran local version of `codex-cli` with staging overrides/harness for auth
    
    ran `codex login` then `codex logout`:
    
    saw auth.json clear and  backend revocation endpoints were called
    
    ```
    POST /oauth/revoke
    status: 200
    
    revoking access token
    should clear auth session
    clearing auth session due to token revocation
    successfully revoked session and access token
    CANONICAL-API-LINE Response: status='200' method='POST' path='/oauth/revoke
    ```
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • codex: split thread/read view loading (#18231)
    Summary
    - refactor thread/read into explicit persisted-load, live-load, and
    merge steps
    - preserve existing SQLite/filesystem/live-thread behavior exactly
    - keep ThreadStore migration out of this PR so the next PR is easier to
    review
    
    Validation
    - this one's a pure reorganization that relies on existing test coverage
  • Refactor config loading to use filesystem abstraction (#18209)
    Initial pass propagating FileSystem through config loading.
  • fix(app-server): replay token usage after resume and fork (#18023)
    ## Problem
    
    When a user resumed or forked a session, the TUI could render the
    restored thread history immediately, but it did not receive token usage
    until a later model turn emitted a fresh usage event. That left the
    context/status UI blank or stale during the exact window where the user
    expects resumed state to look complete. Core already reconstructed token
    usage from the rollout; the missing behavior was app-server lifecycle
    replay to the client that just attached.
    
    ## Mental model
    
    Token usage has two representations. The rollout is the durable source
    of historical `TokenCount` events, and the core session cache is the
    in-memory snapshot reconstructed from that rollout on resume or fork.
    App-server v2 clients do not read core state directly; they learn about
    usage through `thread/tokenUsage/updated`. The fix keeps those roles
    separate: core exposes the restored `TokenUsageInfo`, and app-server
    sends one targeted notification after a successful `thread/resume` or
    `thread/fork` response when that restored snapshot exists.
    
    This notification is not a new model event. It is a replay of
    already-persisted state for the client that just attached. That
    distinction matters because using the normal core event path here would
    risk duplicating `TokenCount` entries in the rollout and making future
    resumes count historical usage twice.
    
    ## Non-goals
    
    This change does not add a new protocol method or payload shape. It
    reuses the existing v2 `thread/tokenUsage/updated` notification and the
    TUI’s existing handler for that notification.
    
    This change does not alter how token usage is computed, accumulated,
    compacted, or written during turns. It only exposes the token usage that
    resume and fork reconstruction already restored.
    
    This change does not broadcast historical usage replay to every
    subscribed client. The replay is intentionally scoped to the connection
    that requested resume or fork so already-attached clients are not
    surprised by an old usage update while they may be rendering live
    activity.
    
    ## Tradeoffs
    
    Sending the usage notification after the JSON-RPC response preserves a
    clear lifecycle order: the client first receives the thread object, then
    receives restored usage for that thread. The tradeoff is that usage is
    still a notification rather than part of the `thread/resume` or
    `thread/fork` response. That keeps the protocol shape stable and avoids
    duplicating usage fields across response types, but clients must
    continue listening for notifications after receiving the response.
    
    The helper selects the latest non-in-progress turn id for the replayed
    usage notification. This is conservative because restored usage belongs
    to completed persisted accounting, not to newly attached in-flight work.
    The fallback to the last turn preserves a stable wire payload for
    unusual histories, but histories with no meaningful completed turn still
    have a weak attribution story.
    
    ## Architecture
    
    Core already seeds `Session` token state from the last persisted rollout
    `TokenCount` during `InitialHistory::Resumed` and
    `InitialHistory::Forked`. The new core accessor exposes the complete
    `TokenUsageInfo` through `CodexThread` without giving app-server direct
    session mutation authority.
    
    App-server calls that accessor from three lifecycle paths: cold
    `thread/resume`, running-thread resume/rejoin, and `thread/fork`. In
    each path, the server sends the normal response first, then calls a
    shared helper that converts core usage into
    `ThreadTokenUsageUpdatedNotification` and sends it only to the
    requesting connection.
    
    The tests build fake rollouts with a user turn plus a persisted token
    usage event. They then exercise `thread/resume` and `thread/fork`
    without starting another model turn, proving that restored usage arrives
    before any next-turn token event could be produced.
    
    ## Observability
    
    The primary debug path is the app-server JSON-RPC stream. After
    `thread/resume` or `thread/fork`, a client should see the response
    followed by `thread/tokenUsage/updated` when the source rollout includes
    token usage. If the notification is absent, check whether the rollout
    contains an `event_msg` payload of type `token_count`, whether core
    reconstruction seeded `Session::token_usage_info`, and whether the
    connection stayed attached long enough to receive the targeted
    notification.
    
    The notification is sent through the existing
    `OutgoingMessageSender::send_server_notification_to_connections` path,
    so existing app-server tracing around server notifications still
    applies. Because this is a replay, not a model turn event, debugging
    should start at the resume/fork handlers rather than the turn event
    translation in `bespoke_event_handling`.
    
    ## Tests
    
    The focused regression coverage is `cargo test -p codex-app-server
    emits_restored_token_usage`, which covers both resume and fork. The core
    reconstruction guard is `cargo test -p codex-core
    record_initial_history_seeds_token_info_from_rollout`.
    
    Formatting and lint/fix passes were run with `just fmt`, `just fix -p
    codex-core`, and `just fix -p codex-app-server`. Full crate test runs
    surfaced pre-existing unrelated failures in command execution and plugin
    marketplace tests; the new token usage tests passed in focused runs and
    within the app-server suite before the unrelated command execution
    failure.
  • Refactor AGENTS.md discovery into AgentsMdManager (#18035)
    Encapsulate Agents MD processing a bit and drop user_instructions_path
    from config.
  • Auto-upgrade configured marketplaces (#17425)
    ## Summary
    - Add best-effort auto-upgrade for user-configured Git marketplaces
    recorded in `config.toml`.
    - Track the last activated Git revision with `last_revision` so
    unchanged marketplace sources skip clone work.
    - Trigger the upgrade from plugin startup and `plugin/list`, while
    preserving existing fail-open plugin behavior with warning logs rather
    than new user-visible errors.
    
    ## Details
    - Remote configured marketplaces use `git ls-remote` to compare the
    source/ref against the recorded revision.
    - Upgrades clone into a staging directory, validate that
    `.agents/plugins/marketplace.json` exists and that the manifest name
    matches the configured marketplace key, then atomically activate the new
    root.
    - Local `.agents/plugins/marketplace.json` marketplaces remain live
    filesystem state and are not auto-pulled.
    - Existing non-curated plugin cache refresh is kicked after successful
    marketplace root upgrades.
    
    ## Validation
    - `just write-config-schema`
    - `cargo test -p codex-core marketplace_upgrade`
    - `cargo check -p codex-cli -p codex-app-server`
    - `just fix -p codex-core`
    
    Did not run the complete `cargo test` suite because the repo
    instructions require asking before a full core workspace run.
  • chore: unify memory drop endpoints (#18134)
    Unify all the memories drop behind a single implementation that drops
    both the main memories and the extensions
  • Fix MCP startup cancellation through app server (#18078)
    Addresses https://github.com/openai/codex/issues/17143
    
    Problem: TUI interrupts without an active turn stopped cancelling slow
    MCP startup after routing through the app-server APIs.
    
    Solution: Route no-active-turn interrupts through app-server as startup
    cancels, acknowledge them immediately, and emit cancelled MCP startup
    updates.
    
    Testing: I manually confirmed that MCP cancellation didn't work prior to
    this PR and works after the fix was in place.
  • Extract plugin loading and marketplace logic into codex-core-plugins (#18070)
    Split plugin loading, marketplace, and related infrastructure out of
    core into codex-core-plugins, while keeping the core-facing
    configuration and orchestration flow in codex-core.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Async config loading (#18022)
    Parts of config will come from executor. Prepare for that by making
    config loading methods async.
  • Migrate archive/unarchive to local ThreadStore (#17892)
    # Summary
    - implement local ThreadStore archive/unarchive operations
    - implement local ThreadStore read_thread operation
    - break up the various ThreadStore local method implementations into
    separate files
    - migrate app-server archive/unarchive and core archive fixture to use
    ThreadStore (but not all read operations yet!)
    - use the ThreadStore's read operation as a proxy check for thread
    persistence/existence in the app server code
    - move all other filesystem operations related to archive (path
    validation etc) into the local thread store.
    
    # Tests
    - add dedicated local store archive/unarchive tests
  • [codex] Add local thread store listing (#17824)
    Builds on top of #17659 
    
    Move the filesystem + sqlite thread listing-related operations inside of
    a local ThreadStore implementation and call ThreadStore from the places
    that used to perform these filesystem/sqlite operations.
    
    This is the first of a series of PRs that will implement the rest of the
    local ThreadStore.
    
    Testing:
    - added unit tests for the thread store implementation
    - adjusted some unit tests in the realtime + personality packages whose
    callsites changed. Specifically I'm trying to hide ThreadMetadata inside
    of the local implementation and make ThreadMetadata a sqlite
    implementation detail concern rather than a public interface, preferring
    the more generate StoredThread interface instead
    - added a corner case test for the personality migration package that
    wasn't covered by the existing test suite
    - adjust the behavior of searched thread listing to run the existing
    local rollout repair/backfill pass _before_ querying SQLite results, so
    callers using ThreadStore::list_threads do not miss matches after a
    partial metadata warm-up
  • Make skill loading filesystem-aware (#17720)
    Migrates skill loading to support reading repo skills from the remote
    environment.
  • Spread AbsolutePathBuf (#17792)
    Mechanical change to promote absolute paths through code.
  • Moving updated-at timestamps to unique millisecond times (#17489)
    To allow the ability to have guaranteed-unique cursors, we make two
    important updates:
    * Add new updated_at_ms and created_at_ms columns that are in
    millisecond precision
    * Guarantee uniqueness -- if multiple items are inserted at the same
    millisecond, bump the new one by one millisecond until it becomes unique
    
    This lets us use single-number cursors for forwards and backwards paging
    through resultsets and guarantee that the cursor is a fixed point to do
    (timestamp > cursor) and get new items only.
    
    This updated implementation is backwards-compatible since multiple
    appservers can be running and won't handle the previous method well.
  • Add realtime output modality and transcript events (#17701)
    - Add outputModality to thread/realtime/start and wire text/audio output
    selection through app-server, core, API, and TUI.\n- Rename the realtime
    transcript delta notification and add a separate transcript done
    notification that forwards final text from item done without correlating
    it with deltas.
  • [codex-analytics] feature plumbing and emittance (#16640)
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16640).
    * #16870
    * #16706
    * #16641
    * __->__ #16640
  • Refactor plugin loading to async (#17747)
    Simplifies skills migration.
  • [codex] Refactor marketplace add into shared core flow (#17717)
    ## Summary
    
    Move `codex marketplace add` onto a shared core implementation so the
    CLI and app-server path can use one source of truth.
    
    This change:
    - adds shared marketplace-add orchestration in `codex-core`
    - switches the CLI command to call that shared implementation
    - removes duplicated CLI-only marketplace add helpers
    - preserves focused parser and add-path coverage while moving the shared
    behavior into core tests
    
    ## Why
    
    The new `marketplace/add` RPC should reuse the same underlying
    marketplace-add flow as the CLI. This refactor lands that consolidation
    first so the follow-up app-server PR can be mostly protocol and handler
    wiring.
    
    ## Validation
    
    - `cargo test -p codex-core marketplace_add`
    - `cargo test -p codex-cli marketplace_cmd`
    - `just fix -p codex-core`
    - `just fix -p codex-cli`
    - `just fmt`
  • Add turn item injection API (#17703)
    ## Summary
    - Add `turn/inject_items` app-server v2 request support for appending
    raw Responses API items to a loaded thread history without starting a
    turn.
    - Generate JSON schema and TypeScript protocol artifacts for the new
    params and empty response.
    - Document the new endpoint and include a request/response example.
    - Preserve compatibility with the typo alias `turn/injet_items` while
    returning the canonical method name.
    
    ## Testing
    - Not run (not requested)