[codex] Cache remote plugin catalog for suggestions (#25457)

## Summary
- cache the global remote plugin catalog when remote plugin listing runs
and warm it during startup
- use the cached remote catalog in plugin install recommendations with
canonical `plugin@openai-curated-remote` ids
- reuse the session `PluginsManager` for plugin recommendations so
remote cache state is visible on the recommend path
- skip core installed-state verification for remote plugin install
suggestions while leaving local plugin and connector verification
unchanged

## Testing
- `just fmt`
- `git diff --check`
- `cargo test -p codex-core
list_tool_suggest_discoverable_plugins_includes_cached_remote_global_plugins`
- `cargo test -p codex-core
remote_plugin_install_suggestions_skip_core_installed_verification`
- `cargo test -p codex-app-server
plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled`

Earlier focused checks during the same branch: codex-tools TUI filter
test, request_plugin_install tests, and codex-app-server build.
This commit is contained in:
xl-openai authored and GitHub committed 2026-06-01 22:10:52 -07:00
1 parent cb63ee7f5d
commit f2b725102b
13 files changed
+771 -62

No files matched your search

+47 -1
View File
@@ -592,6 +592,31 @@ impl PluginsManager {
Some(crate::remote::group_remote_installed_plugins_by_marketplaces(plugins, visible_scopes))
}
pub fn cached_global_remote_discoverable_plugins_for_config(
&self,
config: &PluginsConfigInput,
auth: Option<&CodexAuth>,
) -> Vec<crate::remote::RemoteDiscoverablePlugin> {
if !config.plugins_enabled || !config.remote_plugin_enabled {
return Vec::new();
}
let Some(auth) = auth.filter(|auth| auth.uses_codex_backend()) else {
return Vec::new();
};
let Some(account_id) = auth.get_account_id() else {
return Vec::new();
};
if account_id.is_empty() {
return Vec::new();
}
crate::remote::cached_global_remote_discoverable_plugins(
self.codex_home.as_path(),
&remote_plugin_service_config(config),
auth,
)
}
pub async fn build_and_cache_remote_installed_plugin_marketplaces(
&self,
config: &PluginsConfigInput,
@@ -1548,9 +1573,30 @@ impl PluginsManager {
);
manager.maybe_start_remote_installed_plugin_bundle_sync(
&config_for_remote_sync,
auth,
auth.clone(),
on_effective_plugins_changed,
);
if config_for_remote_sync.remote_plugin_enabled {
match crate::remote::fetch_and_cache_global_remote_plugin_catalog(
manager.codex_home.as_path(),
&remote_plugin_service_config(&config_for_remote_sync),
auth.as_ref(),
)
.await
{
Ok(()) => {}
Err(
RemotePluginCatalogError::AuthRequired
| RemotePluginCatalogError::UnsupportedAuthMode,
) => {}
Err(err) => {
warn!(
error = %err,
"failed to warm remote plugin catalog cache"
);
}
}
}
});
let config = config.clone();