## Why
Runtime metadata and tool suggestions do not require resolved image URLs or creator names. Callers that need display enrichment must still be able to distinguish a complete display snapshot from one fetched without it.
## What changed
- Add `InstalledPluginDisplayAssets` to select whether installed plugin requests include display enrichment, preserving `Include` as the default.
- Send `includeDisplayAssets=false` on every request page when `Skip` is selected.
- Track display enrichment in the installed plugin cache. Allow tool suggestions to use snapshots without enrichment, while treating those snapshots as cache misses for display callers.
- Avoid invalidating effective runtime metadata when only the cache's display enrichment state changes.
## Testing
Add coverage for paginated requests without display enrichment, cache eligibility and enrichment state transitions, and discovery using snapshots fetched with or without display assets.
GitOrigin-RevId: 1fdfd6443a300a03753416f2f6c633b52a97d0fb
## Why
Repeated plugin discovery and loading reparse unchanged manifests and repeat warnings for invalid fields.
## What changed
- Share a manifest cache through `PluginStore` across marketplace listing, plugin loading, capability inspection, and tool suggestion metadata.
- Reuse successful parses by plugin root, manifest path, and content digests, including the Codex overlay. Reread files on each load so manifest edits and overlay changes or removal remain visible.
- Bound the cache to 128 entries and bypass caching for inputs larger than 64 KiB. Retry failed parses and share entries across store clones while keeping independent stores isolated.
- Reuse loaded manifest data when loading apps and MCP metadata.
## Testing
Add unit coverage for parse reuse, warning deduplication, revision invalidation, repair after parse failures, equivalent Windows paths, store isolation, and oversized inputs. Add an app-server regression test that verifies repeated `plugin/list` requests observe manifest and overlay revisions in one running server.
GitOrigin-RevId: ed082cd7d198015cd8ed35db7f2af74e13f49915
## Why
Remote plugin requests always sent `OAI-Product-Sku: codex`, ignoring the existing `apps_mcp_product_sku` setting.
## What changed
Pass `apps_mcp_product_sku` through plugin configuration and use it for authenticated remote plugin requests, including discovery, search, installation, and sharing. Keep `codex` as the default and include the SKU when comparing remote service configurations.
## Testing
Extend app-server plugin listing and core agent-turn tests to check default and configured SKU headers. Update the CLI worktree test to wait for the user prompt's model request, skipping background title generation requests.
GitOrigin-RevId: 508c9ef9aa3e41b87c92383c3088eb1eb9227fd8
## Why
Older Desktop clients can still provide bundled Sites to an independently updated SSH app-server. A cached remote Sites install must take precedence, even when disabled, while a missing remote bundle must preserve the bundled fallback.
## What changed
- Remove persisted bundled-plugin exclusions, Sites migration checks, and the migration wait when loading local plugin configuration.
- Suppress `sites@openai-bundled` during plugin loading when the remote global catalog is active and a cached remote Sites install is available.
- Remove exclusion-based catalog filtering and read/install guards, and simplify `install_plugin` to accept `ConfigLayerStack`.
## Testing
Expand the agent-turn Sites test to cover enabled remote precedence, disabled remote suppression of bundled Sites, and fallback when the remote bundle is missing.
GitOrigin-RevId: e2758596e5493ccee051cdfbf6b4afbe4f98948c
## Why
When the remote Sites plugin is installed and its local bundle is available, exposing the bundled copy as well creates duplicate catalog entries and can load the wrong skills.
## What changed
- Persist an account- and backend-scoped exclusion for `sites@openai-bundled` once the remote replacement is installed and loadable.
- Apply the exclusion to plugin catalogs, search, direct reads and installs, runtime loading, hooks, and skill caches while preserving the remote plugin's server-owned enabled state.
- Restore the bundled fallback when the remote plugin is absent or its local files are unavailable, and throttle repeated availability checks for 60 seconds.
## Testing
- Added manager tests for persistence across restarts, account/backend isolation, fallback restoration, concurrent checks, and refresh throttling.
- Added app-server and agent-turn tests confirming that catalog, search, and skill loading prefer remote Sites.
GitOrigin-RevId: bc1154f79d3107910fee9fa27389a0ddb48ce6cf
## What changed
- Scope remote installed-plugin and loaded-plugin snapshots to the active account, and discard in-flight loads when the account changes.
- Serialize bundle reconciliation with direct installs and uninstalls, and use cache generations to prevent stale refreshes from overwriting newer state.
- Validate the complete installed-plugin snapshot before downloading bundles or removing stale cache entries, while retaining valid installed metadata when materialization fails.
## Testing
- Cover account changes during plugin loading, incomplete snapshots, refresh/reconciliation races, and reconciliation cancellation recovery.
GitOrigin-RevId: 694c25577b420b93dacc6c65e3c0ecb7b8cc64c3
## Why
Repository manifests and user-configured sources must not be able to claim names reserved for managed or remote marketplaces.
## What changed
- Reject reserved marketplace names from unmanaged sources during discovery, installation, configuration projection, and configured marketplace upgrades, even when source restrictions are disabled.
- Recognize managed marketplaces only at their expected paths. Preserve the required macOS and Windows path equivalences without allowing symlink aliases to inherit managed provenance.
## Testing
- Cover reserved-name rejection for plugin list and install RPCs, configured upgrades, unrestricted policy, and remote marketplace names.
- Cover symlink spoofing and Windows managed-path normalization.
GitOrigin-RevId: a610bdc382857f306dd3e244f0992de2ec279673
## What changed
- Give `PluginsManager` a shared `AuthManager` instead of a separately mutable authentication-mode snapshot.
- Read the current authentication mode and credentials from that shared manager for plugin discovery, startup tasks, CLI commands, MCP setup, and external-agent migration.
- Update test helpers and coverage to exercise plugin projections and curated marketplace selection as authentication changes.
GitOrigin-RevId: 600c94de5130eda2da5727e1a0b4d39083fefc56
## Why
The model provider does not reliably identify which curated plugin catalog is
available. ChatGPT authentication can be used with a custom provider, while an
unauthenticated session should use the API-compatible catalog regardless of its
provider.
## What changed
- Select the ChatGPT curated catalog only for authentication modes that use the
Codex backend; use the API curated catalog for API-key and unauthenticated
sessions.
- Initialize standalone plugin managers with the current authentication mode
across CLI, app-server, MCP, and external-agent migration paths.
- Preserve authentication mode while detecting and importing migrated plugins.
## Testing
- Cover catalog and skill routing across ChatGPT, API-key, unauthenticated,
Bedrock, and custom-provider configurations.
- Verify authenticated plugin migration uses the ChatGPT curated marketplace.
GitOrigin-RevId: 660a339ee8891c33aad961078d3a979242a6a166
## What changed
- Inject the host skill loader into `PluginsManager` so plugin discovery and agent turns use the same loading and product-policy behavior.
- Share plugin skill snapshots across those paths, preserving a consistent view of skills across workspaces.
- Apply migrated-command precedence after product filtering, allowing an eligible migrated command to replace a filtered native skill with the same name.
## Testing
- Add coverage for product-restricted plugin skills, native-versus-migrated command precedence, and the skills exposed to agent turns.
GitOrigin-RevId: f5ef0d0766ebeeb30d73ffaf044d003c2906ea4d
## What changed
- Fetch the paginated installed-plugin snapshot without a `scope` query instead of issuing separate requests for global, user, and workspace plugins.
- Use the combined snapshot for installed-plugin caching and bundle synchronization while continuing to reconcile each scope's marketplace independently.
## Testing
- Cover pagination across mixed scopes, request query parameters, marketplace reconciliation, stale cache cleanup, and discoverable-plugin filtering.
GitOrigin-RevId: 7428a9db81d7ec04e65cc4b236b652278b200956
## Why
The curated plugin startup sync can fall back from Git to HTTP, but those
requests did not use Codex's effective outbound proxy policy.
## What changed
- Thread the configured HTTP client factory into curated plugin sync.
- Route GitHub API, zipball, backup metadata, and returned download URLs
through the route-aware client when system proxy support is enabled.
- Preserve the existing default HTTP client behavior, including its custom CA
fallback, when system proxy support is disabled.
- Apply the standard Codex headers to startup-sync HTTP requests.
## Testing
Add coverage that records route selection for repository, archive, metadata,
and backend-supplied download URLs, and verifies standard headers are sent.
GitOrigin-RevId: e1ea13bdab58a84c9948a2c8b82a5ec5c964ea99
## 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.
## 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.
## 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.
## 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.
## 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.
## 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.
## 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`
## 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`
## Context
The installed-app suggestion expansion added in #24996 reads plugin
details for trusted file-backed marketplace candidates because the list
response does not include app ids. On Windows-backed WSL mounts, the
local `openai-curated` checkout lives under `$CODEX_HOME/.tmp/plugins`,
and those per-plugin detail reads can be very slow.
Remote curated already has cached app ids, so it does not need the same
local filesystem traversal.
## Summary
- Keep only the WSL Windows-backed local `openai-curated` checkout on
the legacy fallback/configured discovery path.
- Preserve installed-app expansion for non-WSL file-backed marketplaces
and remote curated.
- Add focused tests for the WSL local curated path predicate.
## Test
- `just test -p codex-core-plugins discoverable`
- `just test -p codex-core plugins::discoverable::tests`