Allow installed plugin metadata to skip display enrichment (#56483)

## 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
This commit is contained in:
bkotsopoulos
2026-10-23 14:15:03 +00:00
committed by copyberry
parent 7d592f6dd9
commit de06fd0be4
8 changed files with 238 additions and 13 deletions
+32
View File
@@ -1282,9 +1282,32 @@ fn build_remote_marketplace(
}))
}
/// Selects whether installed-plugin metadata needs display enrichment.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum InstalledPluginDisplayAssets {
/// Resolve image URLs and creator names.
#[default]
Include,
/// Skip display lookups; retain stored display fields and execution metadata.
Skip,
}
pub(crate) async fn fetch_remote_installed_plugins(
config: &RemotePluginServiceConfig,
auth: Option<&CodexAuth>,
) -> Result<Vec<RemoteInstalledPlugin>, RemotePluginCatalogError> {
fetch_remote_installed_plugins_with_display_assets(
config,
auth,
InstalledPluginDisplayAssets::Include,
)
.await
}
pub(crate) async fn fetch_remote_installed_plugins_with_display_assets(
config: &RemotePluginServiceConfig,
auth: Option<&CodexAuth>,
display_assets: InstalledPluginDisplayAssets,
) -> Result<Vec<RemoteInstalledPlugin>, RemotePluginCatalogError> {
let auth = ensure_chatgpt_auth(auth)?;
let mut installed_plugins = fetch_installed_plugins(
@@ -1292,6 +1315,7 @@ pub(crate) async fn fetch_remote_installed_plugins(
auth,
RemoteInstalledPluginScope::All,
/*include_download_urls*/ false,
display_assets,
)
.await?
.into_iter()
@@ -2175,6 +2199,7 @@ async fn fetch_installed_plugins_for_scope(
auth,
RemoteInstalledPluginScope::Single(scope),
/*include_download_urls*/ false,
InstalledPluginDisplayAssets::Include,
)
.await
}
@@ -2184,6 +2209,7 @@ async fn fetch_installed_plugins(
auth: &CodexAuth,
scope: RemoteInstalledPluginScope,
include_download_urls: bool,
display_assets: InstalledPluginDisplayAssets,
) -> Result<Vec<RemotePluginInstalledItem>, RemotePluginCatalogError> {
// Only a complete inventory is authoritative: every failure discards all fetched pages
// before callers can publish the snapshot or remove plugins absent from it.
@@ -2198,6 +2224,7 @@ async fn fetch_installed_plugins(
scope,
page_token.as_deref(),
include_download_urls,
display_assets,
)
.await?;
plugins.extend(response.plugins);
@@ -2275,6 +2302,7 @@ async fn get_remote_plugin_installed_page(
scope: RemoteInstalledPluginScope,
page_token: Option<&str>,
include_download_urls: bool,
display_assets: InstalledPluginDisplayAssets,
) -> Result<RemotePluginInstalledResponse, RemotePluginCatalogError> {
let base_url = config.chatgpt_base_url.trim_end_matches('/');
let mut url = Url::parse(&format!("{base_url}/ps/plugins/installed"))
@@ -2289,6 +2317,10 @@ async fn get_remote_plugin_installed_page(
url.query_pairs_mut()
.append_pair("includeDownloadUrls", "true");
}
if display_assets == InstalledPluginDisplayAssets::Skip {
url.query_pairs_mut()
.append_pair("includeDisplayAssets", "false");
}
if let Some(page_token) = page_token {
url.query_pairs_mut().append_pair("pageToken", page_token);
}