Commit Graph

11 Commits

  • Extract shared plugin MCP config parsing (#27863)
    ## Why
    
    We want a thread-selected plugin to eventually expose stdio MCP servers
    that run on the executor owning that plugin.
    
    The existing plugin MCP parser lived inside `core-plugins` and was
    coupled to the host filesystem loader. Reusing it from an executor
    provider would either duplicate MCP normalization or make the plugin
    package layer own MCP runtime semantics. This PR creates the shared
    MCP-owned boundary first.
    
    In simple terms:
    
    ```text
    plugin .mcp.json
            |
            v
    shared parser in codex-mcp
            |
            +-- Declared placement: preserve current local-plugin behavior
            |
            +-- Environment placement: produce config bound to one executor
    ```
    
    This builds on the authority-bound plugin descriptors from #27692. It
    intentionally does not discover, register, or launch executor MCP
    servers yet.
    
    ## What changed
    
    - Moved plugin MCP file parsing and normalization from `core-plugins`
    into `codex-mcp`.
    - Kept support for both existing file shapes: a top-level server map and
    an object containing `mcpServers`.
    - Kept per-server failure isolation: one invalid server does not discard
    valid siblings, while malformed top-level JSON still fails the whole
    file.
    - Updated the existing local plugin loader to use `Declared` placement,
    preserving its current transport, OAuth, relative `cwd`, and error
    behavior.
    - Added `Environment` placement for the next stacked PR:
    - the selected environment ID overrides anything declared by the plugin;
      - missing stdio `cwd` defaults to the plugin root;
    - relative `cwd` is resolved beneath the plugin root and cannot traverse
    outside it;
    - bare or source-less environment-variable references resolve on a
    non-local executor;
    - explicit orchestrator environment-variable forwarding is rejected for
    executor-owned plugins.
    
    ## User impact
    
    None in this PR. Existing local plugin MCP loading follows the same
    behavior through the shared parser. The executor placement mode is not
    connected to thread startup until the follow-up registration PR.
    
    ## Assumptions
    
    - A selected capability root's environment is authoritative. A plugin
    cannot redirect its stdio process to the orchestrator or another
    executor.
    - Relative working directories belong under the plugin package root.
    Explicit absolute working directories remain valid within the owning
    environment.
    - For a non-local executor, unqualified environment-variable names refer
    to that executor. Reading an orchestrator variable requires an explicit
    contract and is rejected for now.
    - Parsing only produces normalized `McpServerConfig` values. Process
    startup remains owned by the existing MCP runtime and connection
    manager.
    
    ## Follow-ups
    
    1. Add the executor MCP provider and catalog registration: read the
    selected plugin's MCP config through the same executor filesystem,
    support stdio only, freeze the result per active thread, apply managed
    policy, and resolve name collisions as discovered plugin < selected
    plugin < explicit config.
    2. Install that provider in app-server and add an end-to-end test
    proving `thread/start.selectedCapabilityRoots` launches and calls the
    MCP tool on the selected executor, preserves the frozen registration
    across refresh, and does not expose it to an unselected thread.
    3. After the initial executor-stdio vertical, define
    resume/fork/environment-replacement semantics, executor HTTP placement,
    warning delivery, common MCP tool-context bounds, and move remaining MCP
    source composition above core.
    
    ## Verification
    
    - `cargo check -p codex-mcp -p codex-core-plugins --tests`
    - `just bazel-lock-check`
    - Added focused parser coverage for legacy local normalization, executor
    authority, working-directory handling, and environment-variable
    sourcing.
  • Add executor-owned plugin resolution (#27692)
    ## Why
    
    CCA can select a capability root that lives in an executor environment,
    but
    Codex only had a host-filesystem plugin loader. Before selected executor
    plugins can contribute MCP servers, we need a small package boundary
    that can
    answer:
    
    > Does this selected root contain a plugin, and if so, what does its
    manifest
    > declare?
    
    The answer must come from the selected environment's filesystem. A
    failed
    executor lookup must never fall back to the orchestrator filesystem.
    
    ## What this changes
    
    This PR introduces:
    
    ```rust
    PluginProvider::resolve(root)
        -> Result<Option<ResolvedPlugin>, Error>
    ```
    
    `ExecutorPluginProvider` resolves one `SelectedCapabilityRoot` through
    its
    exact `environment_id`. It checks the recognized manifest locations,
    reads the
    manifest through that environment's `ExecutorFileSystem`, and returns an
    inert
    `ResolvedPlugin` containing:
    
    - the opaque selected-root ID;
    - the environment-bound plugin root;
    - the authority-bound manifest resource;
    - parsed metadata and authority-bound component locators.
    
    Descriptor construction rejects manifest or component paths outside the
    selected package root, so consumers cannot accidentally lose the package
    boundary when they receive a resolved plugin.
    
    If the root has no plugin manifest, resolution returns `None`, allowing
    the
    caller to treat it as a standalone capability such as a skill.
    
    ```text
    selected root: repo -> env-1:/workspace/repo
                             |
                             | env-1 filesystem only
                             v
                 .codex-plugin/plugin.json
                             |
                             v
            ResolvedPlugin { authority, root, manifest }
    ```
    
    The existing host loader and the new executor provider now share the
    same
    manifest parser. Existing `codex-core-plugins::manifest` type paths
    remain
    available through re-exports, so host behavior and callers are
    unchanged.
    
    ## Scope
    
    This is intentionally a non-user-visible package-resolution PR. It does
    not:
    
    - parse or register plugin MCP server configurations;
    - activate skills, connectors, hooks, or MCP servers;
    - change app-server wiring;
    - introduce host fallback, caching, or lifecycle behavior.
    
    #27670 has merged, and this PR is now based directly on `main`. Together
    with
    the resolved MCP catalog from #27634, it establishes the inputs needed
    for the
    executor stdio MCP vertical without changing the existing MCP runtime.
    
    ## Follow-up
    
    The next PR will consume `ResolvedPlugin`, read its declared/default MCP
    config
    through the same executor filesystem, bind supported stdio servers to
    that
    environment, and feed those registrations into the resolved MCP catalog.
    An
    app-server E2E will prove that selecting an executor plugin exposes and
    invokes
    its tool on the owning executor.
    
    Resume/fork semantics, dynamic environment replacement, and non-stdio
    placement remain separate lifecycle decisions.
    
    ## Validation
    
    - `just fmt`
    - `cargo check --tests -p codex-plugin -p codex-core-plugins`
    - `just bazel-lock-check`
    - `git diff --check`
    
    Test targets were compiled but not executed locally; CI will run the
    test and
    Clippy suites.
  • Preserve plugin app manifest order (#25491)
    ## Summary
    - Preserve app declaration order when loading plugin .app.json files.
    - Keep plugin connector summaries in plugin app order after connector
    metadata is merged and filtered.
    - Add regression coverage for .app.json order and connector summary
    order.
    
    ## Validation
    - just fmt
    - just test -p codex-chatgpt
    connectors_for_plugin_apps_returns_only_requested_plugin_apps
    - just test -p codex-core-plugins
    effective_apps_preserves_app_config_order
    - just fix -p codex-core-plugins (passes with existing clippy
    large_enum_variant warning in core-plugins/src/manifest.rs)
    - just fix -p codex-chatgpt
    - just bazel-lock-update
    - just bazel-lock-check
  • fix(plugins): keep version upgrades additive (#23356)
    ## Why
    
    Windows can reject plugin cache upgrades when a running MCP server still
    has its working directory inside the currently active plugin version.
    The existing cache refresh path replaces
    `plugins/cache/<marketplace>/<plugin>` as a whole, so a live handle
    under the old version can make an otherwise ordinary version bump fail.
    
    This PR keeps the existing plugin-selection model intact while making
    version bumps less disruptive.
    
    ## What changed
    
    - When installing a new version beside an existing plugin cache root,
    move only the staged version directory into place instead of replacing
    the whole plugin root.
    - Best-effort prune older sibling version directories after the new
    version is activated.
    - Preserve the existing whole-root replacement path for first installs
    and same-version refreshes.
    - Add regression coverage for upgrading from `1.0.0` to `2.0.0` without
    replacing the plugin root.
    
    ## Verification
    
    - `cargo test -p codex-core-plugins install_with_new_version`
    - `cargo fmt --package codex-core-plugins --check`
  • Show plugin hooks in plugin details (#21447)
    Supersedes the abandoned #19859, rebuilt on latest `main`.
    
    # Why
    
    PR #19705 adds discovery for hooks bundled with plugins, but `/plugins`
    still only shows skills, apps, and MCP servers. This follow-up makes
    bundled hooks visible in the same plugin detail view so users can
    inspect the full plugin surface in one place.
    
    We also need `PluginHookSummary` to populate Plugin Hooks in the app;
    `hooks/list` is not enough there because plugin detail needs to show
    hooks for disabled plugins too.
    
    # What
    
    - extend `plugin/read` with `PluginHookSummary` entries for bundled
    hooks
    - summarize plugin hooks while loading plugin details
    - render a `Hooks` row in the `/plugins` detail popup
    
    <img width="3456" height="848" alt="CleanShot 2026-04-27 at 11 45 34@2x"
    src="https://github.com/user-attachments/assets/fe3a38d6-a260-4351-8513-fb04c93d725b"
    />
  • feat: Cache remote plugin bundles on install (#19914)
    Remote installs now fetch, validate, download, and cache the plugin
    bundle locally
  • refactor: route Codex auth through AuthProvider (#18811)
    ## Summary
    
    This PR moves Codex backend request authentication from direct
    bearer-token handling to `AuthProvider`.
    
    The new `codex-auth-provider` crate defines the shared request-auth
    trait. `CodexAuth::provider()` returns a provider that can apply all
    headers needed for the selected auth mode.
    
    This lets ChatGPT token auth and AgentIdentity auth share the same
    callsite path:
    - ChatGPT token auth applies bearer auth plus account/FedRAMP headers
    where needed.
    - AgentIdentity auth applies AgentAssertion plus account/FedRAMP headers
    where needed.
    
    Reference old stack: https://github.com/openai/codex/pull/17387/changes
    
    ## Callsite Migration
    
    | Area | Change |
    | --- | --- |
    | backend-client | accepts an `AuthProvider` instead of a raw
    token/header |
    | chatgpt client/connectors | applies auth through
    `CodexAuth::provider()` |
    | cloud tasks | keeps Codex-backend gating, applies auth through
    provider |
    | cloud requirements | uses Codex-backend auth checks and provider
    headers |
    | app-server remote control | applies provider headers for backend calls
    |
    | MCP Apps/connectors | gates on `uses_codex_backend()` and keys caches
    from generic account getters |
    | model refresh | treats AgentIdentity as Codex-backend auth |
    | OpenAI file upload path | rejects non-Codex-backend auth before
    applying headers |
    | core client setup | keeps model-provider auth flow and allows
    AgentIdentity through provider-backed OpenAI auth |
    
    ## Stack
    
    1. https://github.com/openai/codex/pull/18757: full revert
    2. https://github.com/openai/codex/pull/18871: isolated Agent Identity
    crate
    3. https://github.com/openai/codex/pull/18785: explicit AgentIdentity
    auth mode and startup task allocation
    4. This PR: migrate Codex backend auth callsites through AuthProvider
    5. https://github.com/openai/codex/pull/18904: accept AgentIdentity JWTs
    and load `CODEX_AGENT_IDENTITY`
    
    ## Testing
    
    Tests: targeted Rust checks, cargo-shear, Bazel lock check, and CI.
  • Move marketplace add/remove and startup sync out of core. (#19099)
    Move more things to core-plugins.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • 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.
  • 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>