mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
dev
140 Commits
-
[codex] Enable remote plugins by default (#30297)
## Summary - enable the remote plugin feature by default - promote the remote plugin feature from under development to stable - preserve the existing `features.remote_plugin` override for explicitly disabling it - keep legacy disabled-path coverage explicit in TUI and app-server tests ## Impact Remote plugin functionality is enabled by default for configurations that do not set the feature flag. The existing Codex backend authentication gate still applies. ## Validation - `just fmt` - `just test -p codex-features` - `just test -p codex-tui plugins_popup_remote_section_fallback_states_snapshot` - targeted `codex-app-server` plugin-list and skills-list tests - `git diff --check` The full TUI and app-server suites were also exercised locally. All remote-plugin-related coverage passed; unrelated local sandbox/test-binary failures remain outside this change.
xl-openai ·
2026-06-28 11:46:25 -07:00 -
[plugins] Enforce marketplace source policy at runtime (#29691)
## Summary - project effective marketplace/plugin config through the enterprise source policy so blocked installed plugins become inactive - filter plugin list/read/discovery and CLI marketplace source/snapshot reporting using the same policy - enforce source admission for background marketplace cache refreshes - continue refreshing/upgrading independent marketplaces and plugins when one entry fails, returning per-entry errors - include policy-projected plugin state in cache and refresh keys so requirement changes invalidate stale results ## Stack This is PR 2 of 2 and is based on #29690. Review the admission model and source matcher in #29690 first; this PR contains only runtime enforcement. ## Test plan - `just test -p codex-core-plugins` (287 tests) - `just test -p codex-cli plugin_list_ignores_implicit_system_marketplace_roots_without_manifests` - `cargo check -p codex-cli -p codex-app-server --tests`
xl-openai ·
2026-06-27 15:22:05 -07:00 -
[codex] Support npm marketplace plugin sources (#29375)
## Why Marketplace source deserialization treated `{"source":"npm", ...}` as unsupported. The loader logged and skipped the entry, so npm-backed plugins never appeared in `plugin list --available` and `plugin add` returned "plugin not found". Codex plugins are installed from a plugin root, not from an npm dependency tree. For npm-backed marketplace entries, Codex should fetch the published package contents without running package scripts or installing unrelated dependencies. ## What changed - Add `npm` marketplace plugin sources with `package`, optional semver `version` or version range, and optional HTTPS `registry`. - Reject unsafe npm source fields before materialization, including invalid package names, non-semver version selectors, plaintext or credential-bearing registry URLs, and registry query/fragment data. - Materialize npm plugins with `npm pack --ignore-scripts`, then unpack the resulting tarball through the existing hardened plugin bundle extractor. - Enforce npm archive and extracted-size limits, require the standard npm `package/` archive root, and verify the extracted `package.json` name matches the requested package before installing. - Keep plugin listings, install-source descriptions, CLI JSON/human output, app-server v2 `PluginSource`, TUI source summaries, regenerated schema fixtures, and app-server documentation in sync. ## Impact Marketplaces can distribute Codex plugins from public or configured private HTTPS npm registries using the same install flow as existing materialized plugin sources. `npm` must be available on `PATH` when an npm-backed plugin is installed. Fixes #27831 ## Validation - `just write-app-server-schema` - `just test -p codex-core-plugins -p codex-app-server-protocol -p codex-app-server -p codex-cli` - npm/schema/core-plugin coverage passed in the run. - The full focused command finished with `1739 passed`, `11 failed`, and `6 timed out`; the failures were unrelated local app-server environment failures from `sandbox-exec: sandbox_apply: Operation not permitted` plus one missing `test_stdio_server` helper binary. - Installed an npm-published Codex plugin package through a throwaway local marketplace and throwaway `CODEX_HOME` to exercise the real npm materialization path end to end.charlesgong-openai ·
2026-06-26 17:24:46 -04:00 -
[codex] Populate remote plugin local versions (#29956)
# What - Carry installed remote release versions through remote plugin summaries as `localVersion`. - Keep the app-server mapping a pure adapter by populating that value in the remote catalog layer. # Why Remote plugin summaries always returned `localVersion: null` even after their versioned bundles had been installed locally. Consumers such as scheduled-task template discovery use `localVersion` to resolve a plugin's materialized root, so templates from remote curated plugins were silently skipped.
Abhinav ·
2026-06-25 03:13:03 +00:00 -
Represent MCP authentication with an enum (#29924)
## Why MCP authentication has distinct OAuth and ChatGPT-session flows. Representing that choice as `use_chatgpt_auth` makes one flow implicit and allows the configuration model to express the distinction only through a boolean. ChatGPT credential forwarding also needs a first-party trust boundary. A configurable `chatgpt_base_url` controls routing, but must not grant an MCP server permission to receive session credentials. This change builds on #29733, where the boolean was introduced. ## What changed - Replace `use_chatgpt_auth` with an `auth` field backed by the exhaustive `McpServerAuth` enum. - Support `auth = "oauth"` and `auth = "chatgpt"`, with OAuth remaining the default. - Trust only the origin derived from the existing hardcoded `CHATGPT_CODEX_BASE_URL` when granting ChatGPT auth to an MCP server. - Keep configured bearer tokens and authorization headers ahead of the selected authentication flow. - Update config writers, schema output, fixtures, and integration-test setup to use the enum. ## Verification Integration coverage exercises the complete streamable HTTP startup path in two independent configurations: - A directly constructed MCP configuration verifies that matching an overridden `chatgpt_base_url` does not grant ChatGPT auth. - A persisted `config.toml` containing an attacker-controlled `chatgpt_base_url` and `auth = "chatgpt"` verifies the same boundary through normal config parsing. Both tests complete MCP initialization and tool listing and assert that the full captured request sequence contains no authorization headers. Separate integration coverage verifies that configured authorization takes precedence over ChatGPT auth.
Ahmed Ibrahim ·
2026-06-24 19:51:51 -07:00 -
Allow ChatGPT-hosted MCP servers to use session auth (#29733)
## Why ChatGPT session authentication was inferred from the reserved Codex Apps server name. That couples credential routing to Codex Apps-specific behavior and prevents other MCP endpoints hosted by ChatGPT from explicitly using the current session. The opt-in also needs a clear security boundary: an arbitrary MCP configuration must not be able to redirect ChatGPT credentials to another origin. ## What changed - Add `use_chatgpt_auth` to HTTP MCP server configuration, defaulting to `false`. - Honor the setting only when the parsed server URL has the same HTTP(S) origin as the configured `chatgpt_base_url`; otherwise remove the capability before startup. - Resolve bearer tokens and static or environment-backed authorization headers before selecting authentication, with configured authorization taking precedence over ChatGPT session auth. - Enable the setting for the built-in Codex Apps and hosted plugin runtime endpoints while keeping Codex Apps caching and tool normalization scoped to the reserved server. - Persist the setting through MCP config rewrite paths and expose it in the generated config schema. - Load the current login state for `codex mcp list` so reported auth status matches runtime behavior. ## Verification Core integration coverage exercises the complete streamable HTTP MCP startup path and verifies that: - a same-origin opted-in server receives the current ChatGPT access token; - an explicitly configured authorization header takes precedence; - a different-origin server completes MCP initialization and tool listing without receiving any ChatGPT authorization header.
Ahmed Ibrahim ·
2026-06-24 19:21:28 -07:00 -
Isolate curated plugin sync Git environment (#29785)
## Why Several users have reported data loss from this bug, including tracked files being deleted or replaced and branches appearing to be reset to the curated plugins repository. This can happen during startup, before the model chooses to edit anything. Ambient repository variables such as `GIT_DIR` and `GIT_WORK_TREE` can override the repository selected by `git -C`, redirecting startup sync's `git reset --hard` and `git clean -fdx` into the user's active workspace. ## What Route every startup-sync Git invocation through a shared command builder that removes repository-local environment variables before execution. Add regression coverage to keep those variables isolated. Fixes #27416
Eric Traut ·
2026-06-24 16:04:51 -07:00 -
Add a connector declaration snapshot (#29851)
## Why Connector declarations currently enter Codex through broad plugin capability summaries, then MCP setup, turn tooling, and `app/list` each reconstruct the same information. That makes executor-selected connectors difficult to add without coupling connector behavior to the host plugin loader. This PR introduces a small connector-owned value that later stack layers can populate before thread startup. ## What changed - Move the pure app-declaration parser into `codex-connectors`, preserving declaration order and category cleanup while leaving host-side validation and deduplication unchanged. - Add an immutable `ConnectorSnapshot` with ordered connector IDs and plugin display-name provenance. - Adapt the existing local-plugin capability summaries into that snapshot at current consumer boundaries. - Use the snapshot for MCP tool provenance, turn connector inventory, and `app/list`. - Keep the crate API narrow: no test-only snapshot accessors are exposed. The externally visible behavior is unchanged. Connector tools still come from the orchestrator-owned `/ps/mcp` server, and local plugin enablement remains owned by the existing plugin loader. ## Stack scope This is the foundation only. It does not read selected executor packages or change thread startup. #29852 adds the executor-backed declaration reader, and #29856 composes selected declarations into a thread snapshot.
jif ·
2026-06-24 23:24:01 +01:00 -
Keep executor plugin MCP paths URI-native (#29628)
## Why Executor-owned plugin roots are `PathUri`, but MCP config normalization still converts them into a native `Path` using the app-server host's rules. Relative `cwd` values can therefore resolve against the wrong filesystem when host and executor path conventions differ. This PR keeps executor MCP paths URI-native until the selected environment launches the server, while retaining the existing host parser behavior. ## What changed - Keep one shared MCP normalization path with narrow host-`Path` and executor-`PathUri` entrypoints. - Preserve native host resolution for locally installed plugin MCP configs. - For executor configs, default `cwd` to the plugin root and resolve relative working directories with the root URI's path convention. - Accept explicit executor `file:` URIs only when they remain within the selected plugin root. - Preserve the selected environment id and existing remote environment-variable ownership rules. - Route the executor plugin provider through the URI-native entrypoint without converting the root on the host. - Ensure `codex doctor` does not probe executor-owned stdio commands or foreign working directories on the host. - Cover foreign Windows roots, relative and absolute executor working directories, traversal rejection, runtime resolution, and doctor behavior. ```text plugin root: file:///C:/plugins/demo configured cwd: scripts | v resolved cwd: file:///C:/plugins/demo/scripts | v launch through the selected executor ``` No new provider or filesystem abstraction is introduced. ## Stack 1. #29614 — add lexical `PathUri` containment. 2. #29620 — share URI-native manifest path resolution. 3. #28918 — keep selected plugin roots and resources URI-native. 4. #29626 — load executor skills without host path conversion. 5. **This PR** — resolve executor MCP working directories without host path conversion.jif ·
2026-06-24 09:46:07 +01:00 -
config: own layer provenance types (#29722)
## Why Config layer provenance describes how effective configuration was assembled, so it belongs with the config loader rather than in app-server's serialized API types. ## What changed - Moved `ConfigLayerSource`, `ConfigLayerMetadata`, and `ConfigLayer` ownership into `codex-config`. - Kept app-server's wire payloads unchanged and added explicit conversions at the app boundary. - Removed lower-level app-server-protocol dependencies from config consumers. ## Stack This is PR 3 of 6, stacked on [PR #29721](https://github.com/openai/codex/pull/29721). Review only the delta from `codex/split-auth-domain-types`. Next: [PR #29723](https://github.com/openai/codex/pull/29723). ## Validation - `codex-config` coverage passed. - App-server config-manager and config RPC coverage passed.
Adam Perry @ OpenAI ·
2026-06-24 04:03:04 +00:00 -
[plugins] Enforce marketplace source admission requirements (#29753)
## Why Managed marketplace source requirements only become effective when every local marketplace mutation path applies the same admission decision. This change centralizes that decision so CLI, app-server, and external-agent migration flows cannot add, install from, or refresh a disallowed source. ## What changed - Match exact normalized Git repository URLs with an optional exact `ref`. - Match Git hosts with managed regular expressions. - Match local marketplaces by exact absolute path. - Preserve the expected path/name boundary for managed OpenAI marketplaces. - Enforce source admission during marketplace add, plugin install, and configured Git marketplace upgrade. - Continue upgrading independent marketplaces when one source is rejected and return a per-marketplace error. - Load the effective requirements stack at CLI, app-server, and external-agent migration entry points. This PR does not filter already configured marketplaces at runtime; that remains in draft follow-up #29691. ## Stack This is PR 2 of 3 and is based on #29690, which introduces the requirements data shape and merge behavior. ## Test plan - Source matcher coverage for Git URL/ref, host-pattern, local-path, and managed marketplace cases. - Marketplace add and plugin install coverage for allowed and rejected sources. - Marketplace upgrade coverage for rejection and per-marketplace continuation.
xl-openai ·
2026-06-23 20:13:11 -07:00 -
auth: move domain mode below app wire types (#29721)
## Why Authentication mode is a domain concept used by login, model selection, telemetry, and transports. Keeping the canonical type in app-server protocol forces those lower-level crates to depend on an unrelated wire API. ## What changed - Added canonical `codex_protocol::auth::AuthMode` domain values. - Kept the app-server wire DTO unchanged and added an explicit app-side conversion. - Removed production app-server-protocol dependencies from login, model-provider-info, models-manager, and otel call paths. ## Stack This is PR 2 of 6, stacked on [PR #29714](https://github.com/openai/codex/pull/29714). Review only the delta from `codex/split-json-rpc-protocols`. Next: [PR #29722](https://github.com/openai/codex/pull/29722). ## Validation - Auth and login coverage passed in the focused protocol/domain test run. - App-server account and auth conversion coverage passed.
Adam Perry @ OpenAI ·
2026-06-24 03:10:20 +00:00 -
[codex] Ignore local curated plugins when remote catalog is active (#29765)
## Summary - suppress configured `openai-curated` plugins when the remote plugin feature is enabled and auth uses the Codex backend - preserve `openai-api-curated` and non-Codex-backend behavior while including remote catalog activation in the plugin load cache key - add core plugin coverage and an app-server integration test for runtime feature enablement ## Why The Codex app enables remote plugins through process-local runtime feature enablement, which can happen after app-server startup tasks have already observed legacy local plugin state. The existing conflict logic only preferred a remote plugin when the same plugin was already installed remotely, so a configured legacy-only plugin could continue exposing skills and other capabilities from `openai-curated`. ## Impact When the remote catalog is active, legacy `openai-curated` plugins no longer contribute skills, MCP servers, apps, or hooks. Remote installed plugins continue to load normally, and `openai-api-curated` remains unaffected. This does not change remote fetch, bundle sync, or uninstall behavior. ## Validation - `just test -p codex-core-plugins remote_global_catalog_ignores_local_curated_plugins remote_plugin_feature_keeps_local_curated_without_codex_backend` - `just test -p codex-app-server runtime_remote_plugin_enablement_excludes_local_curated_plugin_skills` - `just fmt` - `git diff --check`
xl-openai ·
2026-06-23 19:51:31 -07:00 -
Make selected plugin roots URI-native (#28918)
## Why Selected capability roots belong to the executor filesystem, not the app-server host. Converting their path strings into the host's native `Path` breaks whenever the two machines use different path conventions, such as a Windows executor behind a Unix app-server. This PR establishes `PathUri` as the selected-plugin boundary so the executor remains authoritative for its paths. ## What changed - Require `selectedCapabilityRoots[].location.path` to be a canonical `file:` URI and deserialize it directly as `PathUri`; native path strings are rejected. - Update the app-server schema, generated TypeScript, examples, and request coverage for the URI contract. - Keep selected roots, resolved plugin locations, manifest paths, and manifest resources as `PathUri`. - Inspect and read plugin roots and manifests only through the selected environment's `ExecutorFileSystem`. - Parse executor manifests with the shared URI-native parser from #29620 instead of projecting them onto the host filesystem. - Enforce resource containment lexically and preserve the root URI's POSIX or Windows path convention. - Cover foreign Windows plugin roots and URI-native manifest resources. ```text thread/start selectedCapabilityRoots[].location.path = "file:///C:/plugins/demo" | PathUri v ExecutorFileSystem | +--> plugin.json +--> manifest resources ``` This PR stops at the shared selected-plugin representation. The next two PRs remove the remaining host-path projections in the skill and MCP consumers. ## Stack 1. #29614 — add lexical `PathUri` containment. 2. #29620 — share URI-native manifest path resolution. 3. **This PR** — keep selected plugin roots and resources URI-native. 4. #29626 — load executor skills without host path conversion. 5. #29628 — resolve executor MCP working directories without host path conversion.
jif ·
2026-06-23 22:51:19 +01:00 -
Decouple plugin manifest path resolution (#29620)
## Why Plugin manifests use the same schema whether the package lives on the host or in an executor. Only the path representation differs: host callers need native `Path` inputs and `AbsolutePathBuf` outputs, while executor callers need `PathUri` throughout. Maintaining separate parsing or resolver implementations would duplicate the manifest rules and allow them to drift. This PR instead makes URI-native resolution the single parsing path and keeps host conversion at the boundary. ## What changed - Make `parse_plugin_manifest_uri` the shared manifest parser and resolve every path-bearing field as `PathUri`. - Keep the existing host entrypoint as a thin adapter: convert its native root and manifest path to `PathUri`, run the shared parser, then map resources back to `AbsolutePathBuf`. - Expose `PluginManifest::try_map_resources` so callers can convert the generic resource type without duplicating manifest construction. - Resolve relative manifest paths using the root URI's convention: backslashes are separators for Windows roots and ordinary filename characters for POSIX roots. - Apply lexical containment after URI resolution, rejecting absolute paths and parent traversal outside the plugin root. - Make encoded backslashes fail containment only for Windows URIs; encoded `/` remains unsafe for every convention. - Use a host-native synthetic root for marketplace fallback manifests so the host adapter also works on Windows. ```text host Path --------> PathUri --\ +--> one manifest parser --> PluginManifest<PathUri> executor PathUri -------------/ host result: PluginManifest<PathUri> --> PluginManifest<AbsolutePathBuf> ``` Existing host manifest behavior is preserved; #28918 is the first executor consumer. ## Verification - `just test -p codex-utils-path-uri` - `just test -p codex-plugin` - `just test -p codex-core-plugins` ## Stack 1. #29614 — add lexical `PathUri` containment. 2. **This PR** — share URI-native manifest path resolution. 3. #28918 — keep selected plugin roots and resources URI-native. 4. #29626 — load executor skills without host path conversion. 5. #29628 — resolve executor MCP working directories without host path conversion.jif ·
2026-06-23 20:33:59 +00:00 -
Separate local and remote plugin analytics IDs (#29495)
## Why Plugin analytics overloaded `plugin_id`: most events used the Codex `<plugin>@<marketplace>` identity, while remote install events used the backend plugin ID. That makes the same field change meaning across event types and complicates downstream identity resolution. This change makes the contract unambiguous: - `plugin_id`: the local Codex `<plugin>@<marketplace>` identity, when resolved - `remote_plugin_id`: the backend plugin identity, when available For a remote install failure that happens before plugin details resolve, `plugin_id` is `null` and `remote_plugin_id` remains populated. ## What changed All six plugin analytics events use the same identity contract: - `codex_plugin_installed` - `codex_plugin_install_failed` - `codex_plugin_uninstalled` - `codex_plugin_enabled` - `codex_plugin_disabled` - `codex_plugin_used` Remote identity is resolved from the current installed-plugin snapshot first, with persisted install metadata as fallback. The telemetry metadata type keeps local identity optional for failures that occur before remote details are available. The app-server test client's manual analytics smokes now find remote mutation events through `remote_plugin_id` and validate that `plugin_id` remains local. ## Remote uninstall Resolve and capture telemetry metadata before removing the local plugin cache, then emit `codex_plugin_uninstalled` after the backend confirms success. The event is also emitted when backend uninstall succeeds but local cache cleanup reports `CacheRemove`. If a concurrent remote-cache refresh removes the local bundle before telemetry capture, the already-fetched remote plugin detail supplies fallback capability metadata. ## Validation - `just test -p codex-analytics` — 82 passed - `just test -p codex-core-plugins` — 271 passed - `just test -p codex-app-server-test-client` — 5 passed - `just test -p codex-plugin` — 3 passed - `just test -p codex-app-server plugin_install` — 37 passed - `just test -p codex-app-server plugin_uninstall` — 10 passed The production app-server install/uninstall flow was also exercised against `plugins~Plugin_f1b845ac33888191ac156169c58733c2` (`build-ios-apps@openai-curated-remote`), and the plugin's original uninstalled state was restored.
jameswt-oai ·
2026-06-23 12:27:14 -07:00 -
[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.
Drew ·
2026-06-22 16:01:27 -07:00 -
[codex-core-plugins] Remote Plugin ID Persisted to File (#27669)
## This PR Remote plugin analytics cannot rely only on the in-memory installed-plugin snapshot because that snapshot is refreshed asynchronously after startup. This PR persists the authoritative backend identity alongside each cached remote plugin bundle so later consumers can resolve it without a network request. ### Behavior - Store Codex-owned remote installation metadata in an atomic `.codex-remote-plugin-install.json` sidecar under the plugin cache root. - Use a versioned, snake_case schema: ```json { "schema_version": 1, "remote_plugin_id": "plugins~Plugin_..." } ``` - Write the metadata during remote bundle installation. - Backfill it when bundle sync finds an already-current cached bundle. - Clear it when a generic/local install replaces the cache. - Let existing uninstall and stale-cache removal delete it with the plugin cache root. - Reject unsupported schema versions rather than silently misreading future formats. This PR does not change analytics serialization or event behavior. ### Review surface The implementation is limited to four `codex-core-plugins` files: - `store.rs`: owns the versioned sidecar read/write/remove lifecycle. - `remote_bundle.rs`: persists the backend ID after a remote bundle install. - `remote/remote_installed_plugin_sync.rs`: backfills metadata for an already-current cached bundle. - Tests cover the storage lifecycle and both remote write paths. ## Testing / Validation ### Automated - `just test -p codex-core-plugins` (268 tests passed) - `just fix -p codex-core-plugins` passes with one pre-existing `large_enum_variant` warning in `manifest.rs`. - Coverage verifies the exact filename and JSON schema, identity replacement, local reinstall clearing, uninstall cleanup, remote bundle installation, unsupported schema rejection, and installed-plugin sync backfill. ### Live manual validation Validated the production app-server RPC path with an isolated temporary `CODEX_HOME` and the PR-built Codex binary. The app-server communicated over stdio and did not bind a port. Test plugin: `plugins~Plugin_b80dd84519148191a409cde181c9b3d6` (`build-macos-apps@openai-curated-remote`). 1. Confirmed `plugin/read` initially reported the plugin uninstalled. 2. Installed it through `plugin/install` and confirmed version `0.1.4` was cached. 3. Verified `$CODEX_HOME/plugins/cache/openai-curated-remote/build-macos-apps/.codex-remote-plugin-install.json` was created beside the `0.1.4/` bundle directory with mode `0600` and the expected contents: ```json { "schema_version": 1, "remote_plugin_id": "plugins~Plugin_b80dd84519148191a409cde181c9b3d6" } ``` 4. Deleted only the sidecar, restarted the app-server, and confirmed installed-plugin startup sync recreated it with the same contents. 5. Uninstalled through `plugin/uninstall`, confirmed `plugin/read` returned `installed: false`, and verified the local plugin cache root was removed. 6. Restored the account's original uninstalled state and removed the isolated home and copied credentials. ## Split Overview ```text main ├── #27093 Debug analytics capture merged │ └── #27099 Non-mutating plugin smoke merged │ └── #27100 Remote install/uninstall smoke merged └── #27102 Plugin telemetry metadata refactor merged └── #27669 Persist remote plugin identity ← this PR Next: └── Final PR: add explicit local and remote IDs to plugin analytics ``` This PR is based directly on `main`; prerequisite [#27102](https://github.com/openai/codex/pull/27102) has merged. The original combined [#26281](https://github.com/openai/codex/pull/26281) remains the aggregate reference until the final replacement PR is published.jameswt-oai ·
2026-06-22 14:28:39 -07:00 -
[codex] Centralize Plugin Analytics Metadata (#27102)
This PR moves construction of `PluginTelemetryMetadata` from loader and model helpers into `PluginsManager`, which already owns installed plugin state and will eventually perform remote identity enrichment. The metadata type remains in `codex-plugin`, and serialized analytics events remain unchanged. ## Before ```mermaid flowchart LR subgraph Events["Analytics event paths"] direction TB Lifecycle["Local install / uninstall"] Config["Enable / disable"] Remote["Remote install"] Used["Plugin used"] end subgraph Construction["Metadata construction"] direction TB Loader["Loader telemetry helpers"] Summary["PluginCapabilitySummary::telemetry_metadata"] Override["Caller adds remote_plugin_id"] end Metadata["PluginTelemetryMetadata"] Lifecycle --> Loader Config --> Loader Remote --> Loader Loader -->|"local events"| Metadata Loader -->|"remote install"| Override Override --> Metadata Used --> Summary Summary --> Metadata ``` Telemetry metadata was constructed through loader helpers, a capability-summary method, and a remote-install call-site override. ## After ```mermaid flowchart LR subgraph Events["Analytics event paths"] direction TB Lifecycle["Local install / uninstall"] Config["Enable / disable"] Remote["Remote install"] Used["Plugin used"] end Manager["PluginsManager — single construction owner"] Metadata["PluginTelemetryMetadata"] Lifecycle --> Manager Config --> Manager Remote -->|"authoritative remote ID"| Manager Used -->|"capability summary"| Manager Manager --> Metadata ``` Every analytics path delegates metadata construction to `PluginsManager`. Remote install still supplies its authoritative backend ID explicitly. ## What Changes - Make loader code return a focused plugin capability summary instead of constructing analytics metadata. - Centralize immutable plugin telemetry metadata construction in `PluginsManager`. - Route local install/uninstall, remote install, enable/disable, and plugin-used emitters through the manager. - Preserve the current serialized analytics contract exactly. Normal metadata still has no remote override. Remote install continues to provide its authoritative backend ID explicitly, so the existing serializer continues reporting that ID through `plugin_id`. Snapshot-based enrichment is intentionally deferred to the final PR. ## Testing - `just test -p codex-core-plugins` (238 tests passed) - `just test -p codex-plugin` (3 tests passed) - Scoped Clippy/compile checks passed for `codex-plugin`, `codex-core-plugins`, `codex-app-server`, and `codex-core`. ## Split Overview ```text main ├── #27093 Debug analytics capture (merged) ├── #27099 Non-mutating plugin smoke (merged) ├── #27100 Remote install/uninstall smoke (merged) └── #27102 Plugin telemetry metadata refactor ← you are here └── #27669 Persist remote plugin identity After #27102 and #27669 merge: └── Final PR: add explicit local and remote IDs to plugin analytics ``` Review order and dependencies: 1. [#27093 Add debug-only analytics event capture](https://github.com/openai/codex/pull/27093) (merged) 2. [#27099 Add a plugin analytics smoke workflow](https://github.com/openai/codex/pull/27099) (merged) 3. [#27100 Add a remote plugin analytics mutation smoke workflow](https://github.com/openai/codex/pull/27100) (merged) 4. This metadata refactor, independent and based on `main` 5. [#27669 Persist remote plugin identity](https://github.com/openai/codex/pull/27669), stacked on this PR 6. Final remote-ID behavior PR, created after the prerequisites merge The original [#26281](https://github.com/openai/codex/pull/26281) remains open as the aggregate reference until the final replacement PR is published.jameswt-oai ·
2026-06-22 10:27:23 -07:00 -
[codex] Skip curated repo sync for remote plugins (#29005)
## Summary - skip the legacy `openai-curated` startup repository sync when remote plugins are enabled and the current auth uses the Codex backend - keep the curated sync for API-key, Bedrock, and unauthenticated sessions that fall back to the local marketplace - preserve configured marketplace upgrades and all remote plugin startup warmups ## Why The remote catalog owns plugin discovery and materialization only when it is usable for the current auth mode. Starting the legacy curated repository sync in that case performs an unnecessary Git/HTTP/archive download and cache refresh. API-key and Bedrock sessions still require the local curated marketplace, so they must continue syncing it. ## User impact Codex startup no longer downloads or refreshes the local `openai-curated` snapshot when the remote catalog is active. Behavior is unchanged for auth modes that use the local curated marketplace. ## Validation - `just fmt` - `git diff --check` Rust tests were not run per the repository's local verification policy for this narrow conditional change.
xl-openai ·
2026-06-18 18:02:14 -07:00 -
[plugins] Refresh plugin and tool caches after remote install (#28951)
Summary - Refresh the installed remote-plugin snapshot and Codex Apps tools after completing a remote JIT install. - Gate `completed: true` on every expected `app_connector_id` appearing after the uncached `tools/list` refresh, while continuing to skip local bundle verification for server-side installs. - Keep the cached recommendations response and filter refreshed installed remote IDs locally, so this does not add another recommendations fetch. - Add regression coverage for tools appearing after the hard refresh and remaining absent after the refresh. The resumed model request sees the refreshed tool router when installation completes. Root Cause - Remote suggestions from `openai-curated-remote` returned `true` before taking the existing connector refresh path, leaving the resumed turn with the pre-install Apps tool catalog. Validation - `just test -p codex-core request_plugin_install` - `just test -p codex-core-plugins recommended_plugin_candidates_filter_installed_and_disabled_plugins` - `just test -p codex-core-plugins` - `just fix -p codex-core-plugins` - `just fix -p codex-core` - `just fmt` - `just test -p codex-core` was not fully clean locally: 2,729 passed, 26 failed, and 16 skipped. The failures were dominated by local Seatbelt/network/timing issues, including plugin-install timeouts under full-suite contention; the focused plugin-install runs pass.
Alex Daley ·
2026-06-18 20:08:04 -04:00 -
[codex] Reuse parsed plugin skills during session startup (#28844)
## Summary - Preserve raw plugin skill-root snapshots in the matching loaded-plugin cache entry, keyed by the effective plugin root identity including namespace. - Pass those snapshots through `SkillsLoadInput` as an optional preload, so session startup reuses plugin parsing while ordinary skill loads pass `None`. - Keep plugin skill loading cohesive: the existing loaders accept the optional snapshots directly, and uncached or marketplace-detail paths do not create a cache. ## Why Plugin discovery already parses plugin skills to determine available capabilities. Cold session startup then scanned and parsed the same roots again while building the skills snapshot. This solves the same duplicate-work problem as #28623 while keeping ownership narrow: `PluginsManager` creates and owns `PluginSkillSnapshots` only for its loaded-plugin cache entry; `SkillsService` consumes an optional clone. Entry replacement or clearing naturally drops the snapshots, with no separate generation, capacity policy, or watcher coupling. ## Validation - `cargo clippy -p codex-core-skills --all-targets -- -D warnings` - `just test -p codex-core-plugins skills_service_reuses_skills_parsed_during_plugin_load` - `just test -p codex-core-skills namespaces_plugin_skills_using_provided_namespace` - `just fmt`
xl-openai ·
2026-06-18 16:45:58 -07:00 -
[codex] Support marketplace plugin manifest fallback (#28789)
## Summary Support marketplace plugins whose source directory does not include a discoverable plugin manifest. Metadata-rich `marketplace.json` entries now act as fallback plugin manifests for listing, local detail reads, install, and non-curated cache refresh. The fallback preserves marketplace-entry plugin fields wholesale, then adds the small Codex-facing compatibility bridge for presentation metadata. A real source `plugin.json` always wins when present. ## Details - Capture flattened marketplace-entry fields into `MarketplacePluginManifestFallback`, preserving fields such as `version`, `description`, `skills`, `mcpServers`, `apps`, `hooks`, `agents`, `commands`, `strict`, `author`, and future manifest fields without a per-field translation list. - Bridge Claude-style top-level `displayName`, `author.name`, `homepage`, and marketplace `category` into Codex's nested `interface` fields only when the nested values are absent. - Treat fallback metadata as installable only when the marketplace entry contributes metadata beyond bare `name` and `source`; existing missing-manifest behavior remains for metadata-free entries. - Read local plugin details from the already parsed fallback manifest, including fallback-declared app and MCP paths, instead of rereading only an on-disk manifest. - Pass fallback contents into `PluginStore`, which validates them and injects `.codex-plugin/plugin.json` into Store's existing atomic copy. Local marketplace source directories are never mutated, and the fallback path no longer needs an additional staging directory. - Keep Git source materialization unchanged; Git clones still use the existing marketplace source staging area before Store installation.
charlesgong-openai ·
2026-06-18 15:49:27 -07:00 -
[codex] Preserve remote plugin download status errors (#28863)
## Summary - preserve the original HTTP status when a remote plugin bundle download returns a non-success response - retain at most 8 KiB of the error response body and annotate truncation or body-read failures - add regression coverage for an oversized error response ## Root cause The non-success response path reused the normal size-limited body reader. When an error response exceeded 8 KiB, that reader returned `DownloadTooLarge` before the code constructed `DownloadStatus`, masking the upstream HTTP status and response context. ## Impact Remote plugin installation failures now retain the actionable upstream HTTP status without allowing unbounded error bodies into logs. ## Validation - `just test -p codex-app-server plugin_install_preserves_status_when_remote_bundle_error_body_is_too_large` - `just fmt` - `git diff --check`
xl-openai ·
2026-06-18 14:29:01 -07:00 -
[codex] Cache plugin metadata for tool suggestions (#27812)
## Why `built_tools` runs for every sampling request, and local plugin discovery was repeatedly rereading plugin manifests, skills, MCP configuration, and app declarations to build the same tool-suggest metadata. That source-derived metadata is stable until the existing plugin manager reloads its cache. Runtime eligibility still needs to reflect the current install, disable, policy, app-overlap, and authentication state. ## What changed - Add a bounded, in-memory tool-suggest metadata cache owned by `PluginsManager`. - Key cached metadata by plugin identity and source, while applying authentication routing each time the metadata is projected. - Invalidate the metadata alongside the existing loaded-plugin cache, including its normal configuration, marketplace refresh, and remote-installed-plugin invalidation paths. - Guard against an in-flight load repopulating stale metadata after invalidation. - Keep marketplace membership and all runtime eligibility filtering live rather than introducing a separate catalog or revision model. ## Impact Repeated sampling requests reuse already-loaded plugin capability metadata while retaining the existing plugin-manager lifecycle as the single freshness boundary. ## Validation - `just test -p codex-core-plugins` — 252 passed - Added focused coverage for cache invalidation and authentication reprojection.
Matthew Zeng ·
2026-06-18 12:25:07 -07:00 -
[codex] Pass plugin namespace into skill loading (#28608)
## What changed - retain the parsed plugin manifest namespace on loaded plugins - carry that namespace through `PluginSkillRoot` and `SkillRoot` - use the provided namespace when qualifying plugin skill names - include the namespace in the skills cache key ## Why Plugin loading has already parsed `plugin.json`, but skill parsing currently walks every `SKILL.md` ancestor and probes/reads the manifest again to reconstruct the same namespace. Passing the parsed namespace removes those repeated filesystem calls, which are particularly costly on remote filesystems. Context: https://openai.slack.com/archives/C0ARA9GF5D4/p1781639496496439?thread_ts=1781202444.891669&cid=C0ARA9GF5D4 ## Impact Plugin skill names remain unchanged. A regression test uses a deliberately different on-disk manifest name to verify that plugin roots use the provided parsed namespace. ## Validation - `just test -p codex-core-skills -p codex-core-plugins -p codex-plugin -p codex-utils-plugins` (352 passed) - `just fix -p codex-core-skills -p codex-core-plugins -p codex-plugin -p codex-utils-plugins` - `just fmt`
Matthew Zeng ·
2026-06-18 00:16:46 -07:00 -
[codex] Split plugin and skill warmup tracing (#28605)
## What changed - promote plugin config loading to an info-level `plugins_for_config` span - promote skill config loading to an info-level `skills_for_config` span - attach stable OpenTelemetry names to both spans ## Why `session_init.plugin_skill_warmup` currently combines plugin loading and skill loading, which makes cold-start traces unable to identify which phase dominates. These child spans preserve the existing aggregate while making the two costs independently visible. Context: https://openai.slack.com/archives/C0ARA9GF5D4/p1781639496496439?thread_ts=1781202444.891669&cid=C0ARA9GF5D4 ## Impact This is observability-only. It does not change plugin or skill loading behavior. ## Validation - `just test -p codex-core-skills -p codex-core-plugins` (347 passed) - `just fmt`
Matthew Zeng ·
2026-06-17 22:45:10 -07:00 -
[codex] Support plugin manifest path lists (#28790)
## Summary Allow plugin manifests to declare `skills` as either a single path string or an array of path strings in the core plugin loader. ## Why Some plugin packages need to expose skills from more than one directory. Before this change, `plugin.json` only accepted a single string for `skills`, so manifests like this were ignored as an invalid `skills` shape: ```json { "skills": ["./skills/abc", "./skills/edk"] } ``` This keeps the existing single-string form working while adding support for the list form. The final scope is intentionally limited to the core plugin manifest/load path for `skills`; `apps`, file-backed `mcpServers`, and the bundled plugin-creator assets are unchanged in this PR. ## What changed - Parse `skills` as either a string or an array of strings in `plugin.json`. - Store resolved skill paths as a list in `PluginManifestPaths`. - Load manifest-declared skill roots in addition to the default `./skills` root. - Deduplicate exact duplicate skill roots before loading. - Rely on existing skill-loader dedupe by canonical `SKILL.md` path for overlapping roots such as `./skills` plus `./skills/abc`. - Update plugin manifest tests to cover: - single string `skills` - list of string `skills` - duplicate skill roots - `./skills` as a manifest path - explicit child roots like `./skills/abc` and `./skills/edk` - overlapping-root dedupe ## Validation - `just test -p codex-plugin` - `just test -p codex-core-plugins` - `just test -p codex-mcp-extension` - `git diff --check`charlesgong-openai ·
2026-06-17 21:33:53 -07:00 -
[codex] trace tools build latency (#28782)
Add more tracing spans around tool building.
Owen Lin ·
2026-06-17 14:53:54 -07:00 -
fix(plugins): support root local marketplace plugins (#28771)
## Summary - allow local marketplace `source.path: "."` and `source.path: "./"` to resolve to the marketplace root - keep `""` invalid and preserve rejection of non-root paths without `./` plus non-normal/traversal paths - add focused regression coverage for repo-root plugin layouts and rejected local paths ## Tests - `RUSTUP_TOOLCHAIN=stable just fmt` - `RUSTUP_TOOLCHAIN=stable just test -p codex-core-plugins` - `RUSTUP_TOOLCHAIN=stable just fix -p codex-core-plugins` Note: plain pinned-toolchain `just fmt` was blocked locally by a rustup `clippy` component conflict, so validation used the working stable 1.95 toolchain fallback.
Casey Chow ·
2026-06-17 14:06:42 -07:00 -
[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
charlesgong-openai ·
2026-06-17 13:16:34 -07:00 -
[codex] Support object-valued plugin MCP manifests (#28580)
## Summary This fixes plugin manifest parsing for MCP servers declared as an object directly in `plugin.json`. Before this change, Codex modeled `mcpServers` as only a string path, for example: ```json { "name": "counter-sample", "version": "1.1.1", "mcpServers": "./.mcp.json" } ``` Some migrated plugins instead provide the server map directly in the manifest: ```json { "name": "counter-sample", "version": "1.1.1", "description": "Plugin that declares MCP servers in the manifest", "mcpServers": { "counter": { "type": "http", "url": "https://sample.example/counter/mcp" } } } ``` That object form previously failed during install/load with an error like: ```text failed to parse plugin manifest: invalid type: map, expected a string ``` ## What changed - Add a manifest representation for `mcpServers` as either `Path(Resource)` or `Object(map)`. - Parse `plugin.json` `mcpServers` as either a string path or an object. - Route object-valued MCP server maps through the existing plugin MCP config parser instead of adding a second parser. - Apply existing per-plugin MCP server policy to object-valued MCP servers the same way as file-backed MCP servers. - Include object-valued MCP server names in plugin telemetry/capability metadata. - Support object-valued MCP config for executor plugins without requiring a `.mcp.json` filesystem read. - Update the bundled plugin-creator validator and `plugin-json-spec.md` so generated-plugin validation accepts the same object-valued shape. ## Compatibility Existing plugin manifests that use `"mcpServers": "./.mcp.json"` continue to work. Plugins can now also use the object shape shown above. ## Tests Added coverage for the new manifest attribute shape at the install, normal load, telemetry, and executor-provider layers: - `install_accepts_manifest_mcp_server_objects` - `load_plugins_loads_manifest_mcp_server_objects` - `plugin_telemetry_metadata_uses_manifest_mcp_server_objects` - `reads_manifest_object_config_without_executor_file_system_access` Also smoke-tested the plugin-creator validator against both supported forms: - `mcpServers` as a direct object in `plugin.json` - `mcpServers` as `"./.mcp.json"` with a companion `.mcp.json` ## Validation - `just test -p codex-plugin` - `just test -p codex-core-plugins` - `just test -p codex-mcp-extension` - `just bazel-lock-update` - `just bazel-lock-check` - `just fmt` - `git diff --check` - Focused rename/object-form rerun: `just test -p codex-core-plugins manager::tests::load_plugins_loads_manifest_mcp_server_objects manager::tests::plugin_telemetry_metadata_uses_manifest_mcp_server_objects store::tests::install_accepts_manifest_mcp_server_objects` - Focused executor rerun: `just test -p codex-mcp-extension executor_plugin::provider::tests::reads_manifest_object_config_without_executor_file_system_access` - `python3 codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py /private/tmp/codex-validator-object` - `python3 codex-rs/skills/src/assets/samples/plugin-creator/scripts/validate_plugin.py /private/tmp/codex-validator-path`charlesgong-openai ·
2026-06-16 19:22:57 -07:00 -
[codex] Gate remote plugin catalog by auth (#28625)
## Summary - Treat the remote global plugin catalog as active only when `remote_plugin` is enabled and the current auth uses the Codex backend. - Skip the local OpenAI curated marketplace for remote-enabled ChatGPT users while preserving configured marketplaces. - Keep the local curated marketplace for API-key users, unauthenticated fallback, and ChatGPT users with `remote_plugin` disabled. - Apply the same effective-remote gate to the remote installed-marketplace cache. ## Root cause The tool-suggestion discovery path unconditionally included the local OpenAI curated marketplace. For remote-enabled ChatGPT users, that made remote discovery additive: Codex parsed every local curated `plugin.json` before also loading the remote catalog. ## Validation - `just fmt` - `cargo build -p codex-cli --bin codex` - Targeted auth/feature matrix tests pass, including API-key auth with `remote_plugin` enabled. - Manual CLI validation confirmed: - ChatGPT + remote off includes local curated. - ChatGPT + remote on excludes local curated. - API-key auth keeps local curated when remote is enabled. - `just test -p codex-core-plugins`: 235 passed; one unrelated existing marketplace test failed because it loaded the developer's home marketplace configuration.
xl-openai ·
2026-06-16 17:24:48 -07:00 -
[codex] [3/4] Activate endpoint plugin recommendations (#27704)
Summary\n- Await endpoint recommendation selection while constructing each authenticated turn, removing the first-turn cache race.\n- Snapshot and filter endpoint candidates once per turn, then use that same set for the bounded contextual user fragment, tool exposure, and exact install validation.\n- Keep recommendation selection ephemeral: do not persist recommendation state in or gate resumed threads on prior context.\n- Hide the legacy list tool in endpoint mode and preserve legacy discovery unchanged when the endpoint is disabled or unavailable.\n- Keep remote plugin and connector app identities out of model-visible context and attach them only to Codex-owned elicitation metadata.\n\nStack\n- 3/4, based on #28400.\n- Endpoint client and cache: #28399.\n- Generalized suggestion presentation: #28400.\n- Install-schema follow-up: #28403.\n\nValidation\n- \n- \n- \n- \n- Full : 2,649 passed and 88 environment-dependent tests failed because this sandbox cannot write , nest Seatbelt, or locate auxiliary test binaries.
Alex Daley ·
2026-06-16 23:04:07 +00:00 -
[codex] [1/4] Add recommended plugin endpoint cache (#28399)
Summary - Add authenticated parsing for `/ps/plugins/suggested?scope=GLOBAL`, including remote plugin and connector app identities. - Validate, deduplicate, sort, and cap endpoint candidates before caching them by backend and account identity. - Deduplicate concurrent cache misses and warm recommendations from the existing remote-installed-plugin refresh path used at startup and after account changes. - Keep endpoint results model-invisible in this PR; failures and responses without `enabled: true` resolve to legacy mode. Stack - 1/3. Follow-up: #28400 generalizes plugin suggestion presentation without activating endpoint recommendations. - Final activation: #27704. Validation - `just test -p codex-core-plugins recommended_plugins` - `just fix -p codex-core-plugins` - `just fmt` - `git diff --check`
Alex Daley ·
2026-06-16 22:22:21 +00:00 -
[codex] exec-server: stream files in chunks (#28354)
## Why `fs/readFile` buffers the entire file in one response, which makes large remote reads expensive and prevents callers from applying backpressure. We need an opt-in streaming path with bounded block sizes while preserving the existing single-call API for small and sandboxed reads. ## What changed - Add `ExecServerClient::stream`, returning a named `FileReadStream` that implements `futures::Stream` and yields immutable 1 MiB byte blocks. - Add internal `fs/open`, `fs/readBlock`, and `fs/close` RPCs. `fs/readBlock` accepts an explicit offset and length. - Keep unsandboxed files open between block reads, cap open handles per connection, and clean them up on EOF, error, stream drop, explicit close, or connection shutdown. - Reject platform-sandboxed streaming opens instead of turning the one-shot sandbox helper into a persistent server. Existing `fs/readFile` behavior is unchanged. ## Testing - `just test -p codex-exec-server` - Integration coverage for 1 MiB chunking, exact block-boundary EOF, sandbox rejection, and continued reads from the opened file after path replacement. - Handle-manager coverage for non-sequential offsets, variable block lengths, the 128-handle limit, and capacity release after close.
pakrym-oai ·
2026-06-16 09:50:55 -07:00 -
[codex] Clarify plugin load and runtime capability stages (#28472)
## Summary Plugin loading and auth projection both previously produced `PluginLoadOutcome`. That made an unfiltered load result look like runtime-ready capabilities and generated capability summaries before auth routing had run. This change keeps loaded plugin records in the cache, applies the current auth policy in `PluginsManager`, and only then builds `PluginLoadOutcome` and its summaries. Auth changes still reuse the cached disk load and re-resolve apps and MCP servers without reloading plugins. The updated tests cover cached auth changes and verify that capability summaries match the effective app/MCP surface. ## Testing - `just test -p codex-core-plugins` - `just test -p codex-plugin` - `just fix -p codex-core-plugins`
xl-openai ·
2026-06-16 12:57:21 +01:00 -
[codex] Make plugin details capability aware (#27958)
## Summary Makes plugin details/read flows capability-aware so auth-filtered plugin surfaces report the same usable app/MCP/skill shape as the marketplace and install flows. ## Validation Not run; this change was rebased onto the current plugin auth stack and pushed as a draft PR. **Manual test** 1. set up a local marketplace with a plugin that has both app and mcp declarations ``` // .app.json { "apps": { "linear": { "id": "some_id" } } } ``` ``` // .mcp.json { "mcpServers": { "linear": { "type": "http", "url": "https://mcp.linear.app/mcp", "oauth_resource": "https://mcp.linear.app/mcp" }, "linear2": { "type": "http", "url": "https://mcp.linear2.app/mcp", "oauth_resource": "https://mcp.linear2.app/mcp" } } } ``` 2a. **login in with api key** and observe plugin details page which shows no apps (note we don't show "app not available due to api key log in as there's no way to differentiate between no apps and app without substitute mcp exists" without significantly more code changes, i've separated this to a follow up if we want that behaviour. <img width="1170" height="279" alt="Screenshot 2026-06-15 at 23 45 40" src="https://github.com/user-attachments/assets/d36cb160-fbec-461e-9643-9c761dbae7bb" /> <img width="975" height="640" alt="Screenshot 2026-06-15 at 18 40 30" src="https://github.com/user-attachments/assets/90ec0bc8-7506-4b90-bbd3-070720de799e" /> 2b. **log in with chat** and observe intended conflict resolution logic <img width="1165" height="224" alt="Screenshot 2026-06-15 at 17 17 30" src="https://github.com/user-attachments/assets/80adfbf2-7dac-4f08-8b76-8eeeab6c95e7" /> <img width="968" height="567" alt="Screenshot 2026-06-15 at 18 38 59" src="https://github.com/user-attachments/assets/9ea92c5e-535b-4aa4-8ad0-ee513b57bc3c" />felixxia-oai ·
2026-06-16 01:25:22 +00:00 -
[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.
felixxia-oai ·
2026-06-16 01:16:11 +00:00 -
[codex] Centralize plugin auth capability filtering (#27902)
## Summary This is the first step in making plugin auth routing consistent. The rule should not live as one-off checks in every place that loads or displays plugin capabilities. This PR introduces a small resolver for the auth-level policy: given a plugin's declared apps, MCP servers, current auth mode, and active state, return the capabilities that are actually usable in that context. ## Why Product rule: - SiWC auth can use app connectors, so app declarations stay available. - API-key/direct auth cannot use app connectors, so app declarations are removed. - When an active plugin has both an app and an MCP server with the same name, the app route wins for Codex-backed auth and the conflicting MCP server is hidden. Putting that rule in `capabilities.rs` gives the rest of the stack one place to ask instead of duplicating auth checks in loader, manager, marketplace, and details code. ## Validation - `cargo fmt` - `cargo test -p codex-core-plugins`
felixxia-oai ·
2026-06-16 00:13:27 +00:00 -
[codex] Preserve remote plugin directory order (#28395)
## Summary - preserve the plugin directory endpoint's response order while merging installed state - append unmatched installed-only plugins afterward when requested - add focused coverage for directory order and installed-only placement ## Why The remote marketplace merge currently reconstructs plugins through ordered maps and sets, then sorts the result alphabetically by display name. That discards any ordering supplied by the plugin directory endpoint before the list reaches Desktop. ## Implementation Directory plugin IDs are unique, so the merge now iterates the directory vector directly in response order. For each directory plugin, it removes matching installed state from an ID-indexed map and builds the summary. Any entries left in the installed map are installed-only plugins and are appended when `include_installed_only` is enabled. There is no separate rank field, rank map, or final sort. Desktop therefore receives directory order—including any backend ranking—and can preserve it within its existing stable UI state tiers. ## Testing - `just test -p codex-core-plugins` (225 passed)
jameswt-oai ·
2026-06-15 16:09:43 -07:00 -
[codex] Add created-by-me remote plugin marketplace (#28203)
## Summary - add the `created-by-me-remote` marketplace backed by paginated `scope=USER` plugin directory and installed-plugin requests - include USER plugins in installed-plugin caching, bundle sync, and stale-cache cleanup without client-side discoverability filtering - expose the marketplace through app-server v2 and regenerate the protocol schemas ## Testing - `cargo build -p codex-app-server --bin codex-app-server` - production-auth `plugin/list` smoke test for `created-by-me-remote` (returned the expected USER plugin as installed and enabled) - `just test -p codex-core-plugins` (221 passed) - `just test -p codex-app-server-protocol` (231 passed) - `just test -p codex-app-server suite::v2::plugin_list::` (37 passed) - `just fix -p codex-core-plugins -p codex-app-server-protocol -p codex-app-server` - `just fmt`
Eric Ning ·
2026-06-15 22:07:07 +00:00 -
[codex] Skip plugin MCP OAuth for matching app routes (#27461)
## Context This is PR5 in the plugin auth-routing stack. Earlier PRs make plugin surface projection auth-aware, narrow App/MCP conflicts by App declaration name, and keep connector listings auth-aware. This PR applies the same name-based App/MCP conflict rule into plugin MCP loading, so install-time MCP OAuth and plugin detail metadata both reflect the MCPs available for the current auth route. ## Stack - PR1: #27652 seed plugin manager auth at construction. - PR2: #27459 route plugin surfaces by auth mode. - PR3: #27607 dedupe plugin MCP servers by App declaration name. - PR4: #27602 preserve plugin Apps in connector listings. - PR5: #27461 skip install-time plugin MCP OAuth for matching App routes. ## Summary - Make `load_plugin_mcp_servers` auth-aware and let it load App declarations before filtering same-name MCP servers for Codex-backend auth. - Use that filtered MCP list for both install-time MCP OAuth and marketplace plugin detail metadata. - Preserve API-key/direct auth behavior so plugin MCP servers remain visible and can still start OAuth. ## Validation ```bash cargo fmt --all cargo test -p codex-core-plugins read_plugin_for_config_filters_mcp_servers_for_codex_backend_auth cargo check -p codex-core-plugins -p codex-app-server git diff --check git diff --cached --check ```
felixxia-oai ·
2026-06-15 14:04:01 +01:00 -
Discover stdio MCP servers from selected executor plugins (#27870)
## Why **In short:** this PR discovers MCP registrations by reading a selected plugin's `.mcp.json` on its executor. #27884 then resolves those registrations in the shared catalog. `thread/start.selectedCapabilityRoots` can select a plugin root owned by an executor, and Codex can resolve that package through the executor filesystem. MCP declarations inside the selected plugin are still ignored. This PR adds the source-specific discovery layer on top of the selected-plugin catalog boundary in #27884: ```text selected capability root | v resolve the plugin through its executor filesystem | v read and normalize its MCP config through the same filesystem | v contribute stdio registrations bound to that environment ID ``` The existing MCP launcher and connection manager remain unchanged. MCP config parsing is shared with local plugins through #27863. ## What changed - Added an executor plugin MCP provider in the MCP extension. - Retained only the exact filesystem capability used for package resolution and reused it for the selected plugin's MCP config, with no host-filesystem fallback or unrelated process/HTTP authority. - Read either the manifest-declared MCP config or the default `.mcp.json`; a missing default file means the plugin has no MCP servers. - Accepted stdio servers only for this first vertical. Executor-owned HTTP declarations are skipped with a warning until their placement semantics are defined. - Normalized stdio registrations with the owning environment's stable logical ID and plugin-root working directory. - Resolved environment-variable names on the owning executor and rejected explicit local forwarding for non-local plugins. - Froze discovered declarations once per active thread runtime, then applied current managed plugin and MCP requirements when contributing them. - Carried the selected root ID, display name, and selection order into the catalog contribution defined by #27884. ## Behavior and scope There is intentionally no production behavior change yet. This PR provides the executor provider and contribution boundary, but app-server does not install it in this change. Existing local plugin MCP loading is unchanged, and no MCP process is launched by this PR alone. ## Assumptions - The selected root ID is the plugin policy identity; the manifest display name is presentation metadata. - An environment ID is a stable logical authority. Reconnection or replacement under the same ID does not change ownership. - Selected plugin packages and their manifests are trusted inputs. - The selected package and MCP discovery snapshot remain frozen for the active thread runtime. ## Follow-up The next PR installs this contributor in app-server and adds an end-to-end test proving that a selected plugin MCP tool launches on its owning executor, can be called by the model, survives an explicit MCP refresh, and is invisible when its root was not selected. Resume, fork, environment removal or ID changes, dynamic catalog reload, and executor-owned HTTP MCP placement remain separate lifecycle decisions. ## Verification Focused tests cover executor-only filesystem reads, missing and malformed config, stdio filtering and normalization, managed requirements, package attribution, and selection order. CI owns execution of the test suite.
jif ·
2026-06-15 11:52:05 +02:00 -
build: run buildifier from just fmt (#28125)
## Intent Keep Bazel and Starlark files consistently formatted without requiring contributors to install or version buildifier themselves. ## Implementation - Add a SHA-256-pinned, cross-platform DotSlash manifest for buildifier v8.5.1. - Run buildifier from the shared `just fmt` and `just fmt-check` driver, with Windows-safe explicit DotSlash invocation. - Provision DotSlash in formatting CI and contributor devcontainers, and document the source-build prerequisite. - Apply the initial mechanical buildifier formatting baseline.
Adam Perry @ OpenAI ·
2026-06-13 21:43:39 -07:00 -
[codex] Dedupe plugin MCPs by app declaration name (#27607)
## Context This is the next step in the plugin auth-routing stack. The earlier PRs make `PluginsManager` auth-aware and move the broad App/MCP surface decision into that layer. This PR narrows the ChatGPT/SIWC behavior so we only hide a plugin MCP server when it conflicts with an App declaration of the same name. In product terms: if a plugin exposes both an App route and MCP route for `foo`, ChatGPT/SIWC sessions should use the App route for `foo`. If the same plugin also exposes a separate MCP server like `foo2`, that MCP server should remain available. ```json // .app.json { "apps": { "foo": { "id": "connector_abc" } } } ``` ```json // .mcp.json { "mcpServers": { "foo": { "url": "https://mcp.foo.com/mcp" }, "foo2": { "url": "https://mcp.foo2.com/mcp" } } } ``` ## Stack - PR1: #27652 seed plugin manager auth at construction. - PR2: #27459 route plugin surfaces by auth mode. - PR3: #27607 dedupe plugin MCP servers by App declaration name. - PR4: #27602 preserve plugin Apps in connector listings. - PR5: #27461 skip install-time plugin MCP OAuth for matching App routes. ## Summary - Preserve App declaration names in loaded plugin metadata. - Keep public effective App outputs as deduped connector IDs for existing callers. - For ChatGPT/SIWC, suppress only plugin MCP servers whose names match declared App names. ## Validation ```bash cargo fmt --all cargo test -p codex-core-plugins plugin_auth_projection cargo test -p codex-core-plugins effective_apps cargo test -p codex-core-plugins read_plugin_for_config_installed_git_source_reads_from_cache_without_cloning cargo test -p codex-core explicit_plugin_mentions_use_apps_for_chatgpt_dual_surface_plugins cargo test -p codex-core explicit_plugin_mentions_keep_non_conflicting_mcp_for_chatgpt_auth cargo test -p codex-app-server --test all plugin_install_filters_disallowed_apps_needing_auth git diff --check ``` --------- Co-authored-by: Xin Lin <xl@openai.com>felixxia-oai ·
2026-06-13 17:53:09 -07:00 -
[codex] Gate plugin MCP servers by auth route (#27459)
## Context Some plugins expose both Apps and MCP servers. This PR moves auth-aware surface projection into `core-plugins::PluginsManager`, so callers get a consistent effective plugin view. Later PRs narrow the conflict rule and update listing/install paths. The high level goal of this PR is to set up the plumbing to conditionally filter App/MCP in the plugin manager layer. We start by removing MCP servers when using SIWC/Codex-backend auth, and removing Apps when using API-key-style auth. This PR is now stacked on #27652, which contains only the constructor plumbing for seeding `PluginsManager` with the current auth mode. ## Stack - PR1: #27652 seed plugin manager auth at construction. - PR2: #27459 route plugin surfaces by auth mode. - PR3: #27607 dedupe plugin MCP servers by App declaration name. - PR4: #27602 preserve plugin Apps in connector listings. - PR5: #27461 skip install-time plugin MCP OAuth for matching App routes. ## Summary - API-key/non-ChatGPT routes hide plugin Apps and keep plugin MCPs. - ChatGPT/SIWC with Apps enabled keeps plugin Apps and suppresses MCPs for dual-surface plugins. - MCP-only plugins stay available for ChatGPT/SIWC sessions. - Cached plugin load outcomes are re-projected when auth mode changes. ## Validation ```bash cargo test -p codex-core-plugins plugin_auth_projection cargo test -p codex-core list_tool_suggest_discoverable_plugins git diff --check ```
felixxia-oai ·
2026-06-12 19:42:11 -07:00 -
[codex] Add auth mode to plugin manager constructor (#27652)
## Context Plugins can expose more than one way for Codex to use them: App connectors for ChatGPT/SIWC-backed sessions and MCP servers for API key login sessions. The broader goal is to make `PluginsManager` the place that understands which plugin surfaces should be visible for the current auth route, so callers do not each have to make that decision themselves. This PR is the small setup step for that work. It lets the plugin manager be created with the current `AuthMode`, which gives the followup auth routing PRs the information they need without relying on setter injection. ## Stack - PR1: #27652 seed plugin manager auth at construction. - PR2: #27459 route plugin surfaces by auth mode. - PR3: #27607 dedupe plugin MCP servers by App declaration name. - PR4: #27602 preserve plugin Apps in connector listings. - PR5: #27461 skip install-time plugin MCP OAuth for matching App routes. ## Summary - Let `PluginsManager::new_with_restriction_product` accept an initial `AuthMode`. - Keep `PluginsManager::new` behavior unchanged for ordinary callers. ## Validation ```bash cargo test -p codex-core-plugins plugins_manager_tracks_auth_mode cargo test -p codex-core list_tool_suggest_discoverable_plugins git diff --check ``` --------- Co-authored-by: Xin Lin <xl@openai.com>
felixxia-oai ·
2026-06-12 18:00:31 -07:00 -
[codex] Limit app-based plugin suggestions to remote catalogs (#27988)
## Summary - Keep local plugin suggestions bounded to fallback and explicitly configured plugins. - Preserve app-overlap recommendations for remote plugins using cached catalog metadata. - Remove the WSL-specific local discovery exception and move manager-owned discovery tests into `codex-core-plugins`. ## Why Local curated marketplaces were allowlisted before plugin detail loading, so every uninstalled candidate could be deep-read before its app IDs were checked. That caused per-turn reads of candidate plugin manifests, skills, app configs, hooks, and MCP configs, which is especially expensive on slow disks. Remote discovery does not need those local candidate reads because app IDs are already available in the cached remote catalog. Installed local plugins are still loaded when needed to determine the user's installed app IDs. ## Validation - `just fmt` - `just test -p codex-core-plugins discoverable::tests` (13 passed) - `just test -p codex-core plugins::discoverable::tests` (4 passed) - `just bazel-lock-update` - `just bazel-lock-check` - `git diff --check`
xl-openai ·
2026-06-12 17:51:09 -07:00 -
[codex] add latency tracing spans (#27710)
## Why We have some large gaps in our thread start, resume, and pre-sampling traces that make it hard to tell where latency is coming from. ## What Changed - Added coarse spans around thread start/resume, turn context construction, rollout reconstruction, skill/plugin loading, and tool preparation. - Added a breakdown of discoverable-tool preparation across connector loading, plugin discovery, and local plugin details. ## Testing - `cargo check -p codex-app-server -p codex-core -p codex-core-skills -p codex-core-plugins` - Built the app-server locally and exercised thread start, first turn, follow-up turn, server restart, thread resume, and a resumed turn.
rphilizaire-openai ·
2026-06-12 17:11:32 -07:00