[codex] Skip local curated discovery for remote plugins (#27311)

## Summary

- skip the local `openai-curated` marketplace before marketplace loading
when tool-suggest discovery uses remote plugins
- preserve existing marketplace listing behavior for all other callers
and when remote plugins are disabled
- add regression coverage proving the curated marketplace is excluded
before its malformed manifest can be read

## Why

Tool-suggest discovery previously loaded every local `openai-curated`
plugin manifest and only discarded that marketplace afterward when
remote plugins were enabled. The remote catalog is used in that mode, so
the local scan consumed CPU without contributing discoverable plugins.

## Impact

Remote-plugin tool suggestion discovery no longer reads the local
curated marketplace and its plugin manifests. `openai-bundled`,
configured marketplaces, normal `plugin/list` behavior, and local
curated discovery when remote plugins are disabled are unchanged.

## Validation

- `just test -p codex-core-plugins
list_marketplaces_can_skip_openai_curated_before_loading`
- `just test -p codex-core
list_tool_suggest_discoverable_plugins_omits_openai_curated_when_remote_enabled`
- `just fmt`
- `git diff --check`
This commit is contained in:
xl-openai
2026-06-10 13:11:09 -07:00
committed by GitHub
Unverified
parent b4445f2758
commit 7011044c1c
7 changed files with 83 additions and 23 deletions
@@ -1147,7 +1147,7 @@ fn configured_marketplace_plugins(
) -> io::Result<BTreeMap<String, HashSet<String>>> {
let plugins_input = config.plugins_config_input();
let marketplaces = plugins_manager
.list_marketplaces_for_config(&plugins_input, &[])
.list_marketplaces_for_config(&plugins_input, &[], /*include_openai_curated*/ true)
.map_err(|err| {
invalid_data_error(format!("failed to list configured marketplaces: {err}"))
})?;
@@ -573,6 +573,7 @@ impl PluginRequestProcessor {
.list_marketplaces_for_config(
&config_for_marketplace_listing,
&roots_for_marketplace_listing,
/*include_openai_curated*/ true,
)?;
Ok::<
(
@@ -837,8 +838,11 @@ impl PluginRequestProcessor {
let config_for_marketplace_listing = plugins_input.clone();
let shared_plugin_ids_by_local_path = load_shared_plugin_ids_by_local_path(config)?;
match tokio::task::spawn_blocking(move || {
let outcome = plugins_manager
.list_marketplaces_for_config(&config_for_marketplace_listing, &roots)?;
let outcome = plugins_manager.list_marketplaces_for_config(
&config_for_marketplace_listing,
&roots,
/*include_openai_curated*/ true,
)?;
Ok::<
(
Vec<PluginMarketplaceEntry>,
+2 -2
View File
@@ -204,7 +204,7 @@ pub async fn run_plugin_list(
..
} = load_plugin_command_context(overrides).await?;
let outcome = manager
.list_marketplaces_for_config(&plugins_input, &[])
.list_marketplaces_for_config(&plugins_input, &[], /*include_openai_curated*/ true)
.context("failed to list marketplace plugins")?;
ensure_configured_marketplace_snapshots_loaded(
codex_home.as_path(),
@@ -609,7 +609,7 @@ fn find_marketplace_for_plugin(
plugin_name: &str,
) -> Result<ConfiguredMarketplace> {
let outcome = manager
.list_marketplaces_for_config(plugins_input, &[])
.list_marketplaces_for_config(plugins_input, &[], /*include_openai_curated*/ true)
.context("failed to list marketplace plugins")?;
ensure_configured_marketplace_snapshots_loaded(
codex_home,
+5 -6
View File
@@ -87,7 +87,11 @@ impl PluginsManager {
}
let marketplaces = self
.list_marketplaces_for_config(&input.plugins, &[])
.list_marketplaces_for_config(
&input.plugins,
&[],
/*include_openai_curated*/ !input.plugins.remote_plugin_enabled,
)
.context("failed to list plugin marketplaces for tool suggestions")?
.marketplaces;
let mut installed_app_connector_ids = self
@@ -110,11 +114,6 @@ impl PluginsManager {
let mut discoverable_plugins = Vec::<ToolSuggestDiscoverablePlugin>::new();
for marketplace in marketplaces {
let marketplace_name = marketplace.name;
if input.plugins.remote_plugin_enabled
&& marketplace_name == OPENAI_CURATED_MARKETPLACE_NAME
{
continue;
}
let use_legacy_local_curated_filter = should_use_legacy_local_curated_discovery_filter(
&marketplace_name,
marketplace.path.as_path(),
+7 -2
View File
@@ -992,14 +992,19 @@ impl PluginsManager {
&self,
config: &PluginsConfigInput,
additional_roots: &[AbsolutePathBuf],
include_openai_curated: bool,
) -> Result<ConfiguredMarketplaceListOutcome, MarketplaceError> {
if !config.plugins_enabled {
return Ok(ConfiguredMarketplaceListOutcome::default());
}
let (installed_plugins, enabled_plugins) = self.configured_plugin_states(config);
let marketplace_outcome =
self.discover_marketplaces_for_config(config, additional_roots)?;
let mut marketplace_roots = self.marketplace_roots(config, additional_roots);
if !include_openai_curated {
let curated_repo_root = curated_plugins_repo_path(self.codex_home.as_path());
marketplace_roots.retain(|root| root.as_path() != curated_repo_root.as_path());
}
let marketplace_outcome = list_marketplaces(&marketplace_roots)?;
let mut seen_plugin_keys = HashSet::new();
let marketplaces = marketplace_outcome
.marketplaces
+61 -9
View File
@@ -1815,7 +1815,11 @@ enabled = false
let config = load_config(tmp.path(), &repo_root).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()])
.list_marketplaces_for_config(
&config,
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
/*include_openai_curated*/ true,
)
.unwrap()
.marketplaces;
@@ -1917,7 +1921,11 @@ enabled = true
let config = load_config(tmp.path(), &repo_root).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()])
.list_marketplaces_for_config(
&config,
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
/*include_openai_curated*/ true,
)
.unwrap()
.marketplaces;
@@ -1965,7 +1973,11 @@ plugins = true
let config = load_config(tmp.path(), &repo_root).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()])
.list_marketplaces_for_config(
&config,
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
/*include_openai_curated*/ true,
)
.unwrap()
.marketplaces;
@@ -2402,7 +2414,11 @@ enabled = true
let config = load_config(tmp.path(), &repo_root).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()])
.list_marketplaces_for_config(
&config,
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
/*include_openai_curated*/ true,
)
.unwrap()
.marketplaces;
@@ -2496,7 +2512,7 @@ plugins = true
let config = load_config(tmp.path(), tmp.path()).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[])
.list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true)
.unwrap()
.marketplaces;
@@ -2534,6 +2550,37 @@ plugins = true
);
}
#[tokio::test]
async fn list_marketplaces_can_skip_openai_curated_before_loading() {
let tmp = tempfile::tempdir().unwrap();
let curated_root = curated_plugins_repo_path(tmp.path());
write_file(
&tmp.path().join(CONFIG_TOML_FILE),
r#"[features]
plugins = true
"#,
);
write_file(
&curated_root.join(".agents/plugins/marketplace.json"),
"{not valid json",
);
let config = load_config(tmp.path(), tmp.path()).await;
let outcome = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ false)
.unwrap();
assert_eq!(outcome.errors, Vec::new());
assert_eq!(
outcome
.marketplaces
.iter()
.any(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME),
false
);
}
#[tokio::test]
async fn list_marketplaces_includes_installed_marketplace_roots() {
let tmp = tempfile::tempdir().unwrap();
@@ -2576,7 +2623,7 @@ source = "/tmp/debug"
.unwrap();
let config = load_config(tmp.path(), tmp.path()).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[])
.list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true)
.unwrap()
.marketplaces;
@@ -2652,7 +2699,7 @@ source = "/tmp/debug"
let config = load_config(tmp.path(), tmp.path()).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[])
.list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true)
.unwrap()
.marketplaces;
@@ -2707,7 +2754,7 @@ plugins = true
.unwrap();
let config = load_config(tmp.path(), tmp.path()).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[])
.list_marketplaces_for_config(&config, &[], /*include_openai_curated*/ true)
.unwrap()
.marketplaces;
@@ -2792,6 +2839,7 @@ enabled = false
AbsolutePathBuf::try_from(repo_a_root).unwrap(),
AbsolutePathBuf::try_from(repo_b_root).unwrap(),
],
/*include_openai_curated*/ true,
)
.unwrap()
.marketplaces;
@@ -2902,7 +2950,11 @@ enabled = true
let config = load_config(tmp.path(), &repo_root).await;
let marketplaces = PluginsManager::new(tmp.path().to_path_buf())
.list_marketplaces_for_config(&config, &[AbsolutePathBuf::try_from(repo_root).unwrap()])
.list_marketplaces_for_config(
&config,
&[AbsolutePathBuf::try_from(repo_root).unwrap()],
/*include_openai_curated*/ true,
)
.unwrap()
.marketplaces;
@@ -359,7 +359,7 @@ fn verified_plugin_install_completed(
) -> bool {
let plugins_input = config.plugins_config_input();
plugins_manager
.list_marketplaces_for_config(&plugins_input, &[])
.list_marketplaces_for_config(&plugins_input, &[], /*include_openai_curated*/ true)
.ok()
.into_iter()
.flat_map(|outcome| outcome.marketplaces)