Route curated plugin catalogs by authentication mode (#38429)

## 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
This commit is contained in:
willwang-openai
2026-08-13 20:20:01 +00:00
committed by copyberry
parent 507ef0b371
commit f898ebcafd
28 changed files with 348 additions and 139 deletions
+14 -31
View File
@@ -82,7 +82,6 @@ use codex_hooks::plugin_hook_declarations;
use codex_http_client::HttpClientFactory;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_model_provider::AMAZON_BEDROCK_PROVIDER_ID;
use codex_plugin::AppConnectorId;
use codex_plugin::PluginCapabilitySummary;
use codex_plugin::PluginId;
@@ -491,35 +490,24 @@ impl PluginLoadCacheKey {
}
}
fn target_curated_marketplace(
auth_mode: Option<AuthMode>,
model_provider_id: &str,
) -> TargetCuratedMarketplace {
match auth_mode {
Some(auth_mode) => {
if auth_mode.uses_codex_backend() {
TargetCuratedMarketplace::OpenAiWithRemote
} else {
TargetCuratedMarketplace::OpenAiApi
}
}
// Bedrock can use ambient AWS credentials without producing a stored auth mode.
None if model_provider_id == AMAZON_BEDROCK_PROVIDER_ID => {
TargetCuratedMarketplace::OpenAiApi
}
None => TargetCuratedMarketplace::OpenAi,
fn target_curated_marketplace(auth_mode: Option<AuthMode>) -> TargetCuratedMarketplace {
if auth_mode.is_some_and(AuthMode::uses_codex_backend) {
TargetCuratedMarketplace::OpenAiWithRemote
} else {
TargetCuratedMarketplace::OpenAiApi
}
}
impl PluginsManager {
pub fn new(
codex_home: PathBuf,
auth_mode: Option<AuthMode>,
skill_root_loader: Arc<dyn SkillRootLoader<PluginSkillRoot>>,
) -> Self {
Self::new_with_options(
codex_home,
Some(Product::Codex),
/*auth_mode*/ None,
auth_mode,
skill_root_loader,
)
}
@@ -683,7 +671,7 @@ impl PluginsManager {
remote_global_catalog_active,
);
if !force_reload && let Some(plugins) = self.cached_loaded_plugins(&cache_key) {
return self.resolve_loaded_plugins_for_auth(plugins, &config.model_provider_id);
return self.resolve_loaded_plugins_for_auth(plugins);
}
let Ok(_load_permit) = self.loaded_plugins_load_semaphore.acquire().await else {
@@ -691,7 +679,7 @@ impl PluginsManager {
return PluginLoadOutcome::default();
};
if !force_reload && let Some(plugins) = self.cached_loaded_plugins(&cache_key) {
return self.resolve_loaded_plugins_for_auth(plugins, &config.model_provider_id);
return self.resolve_loaded_plugins_for_auth(plugins);
}
let cache_generation = self.loaded_plugins_cache_generation();
let plugin_skill_snapshots = new_plugin_skill_snapshots();
@@ -712,16 +700,12 @@ impl PluginsManager {
plugins.clone(),
plugin_skill_snapshots,
);
self.resolve_loaded_plugins_for_auth(plugins, &config.model_provider_id)
self.resolve_loaded_plugins_for_auth(plugins)
}
fn resolve_loaded_plugins_for_auth(
&self,
mut plugins: Vec<LoadedPlugin>,
model_provider_id: &str,
) -> PluginLoadOutcome {
fn resolve_loaded_plugins_for_auth(&self, mut plugins: Vec<LoadedPlugin>) -> PluginLoadOutcome {
let auth_mode = self.auth_mode();
let target_curated_marketplace = target_curated_marketplace(auth_mode, model_provider_id);
let target_curated_marketplace = target_curated_marketplace(auth_mode);
plugins.retain(|plugin| {
plugin_is_eligible_for_target_marketplace(
&plugin.config_name,
@@ -796,8 +780,7 @@ impl PluginsManager {
if !config.plugins_enabled {
return PluginHookLoadOutcome::default();
}
let target_curated_marketplace =
target_curated_marketplace(self.auth_mode(), &config.model_provider_id);
let target_curated_marketplace = target_curated_marketplace(self.auth_mode());
load_plugin_hooks_from_layer_stack(
config_layer_stack,
self.remote_installed_plugin_configs(),
@@ -2909,7 +2892,7 @@ impl PluginsManager {
self.codex_home.as_path(),
));
let curated_marketplace_path = if include_openai_curated {
match target_curated_marketplace(self.auth_mode(), &config.model_provider_id) {
match target_curated_marketplace(self.auth_mode()) {
TargetCuratedMarketplace::OpenAi | TargetCuratedMarketplace::OpenAiWithRemote => {
let curated_repo_root = curated_plugins_repo_path(self.codex_home.as_path());
curated_repo_root.is_dir().then_some(curated_repo_root)