Files
codex/codex-rs/core-plugins/src/remote_metadata.rs
vkg-oai 0c9be8a836 Preserve plugin caches across display metadata refreshes (#46309)
## Why

Renewed image URLs and other display metadata changes unnecessarily invalidate loaded plugins and MCP and skill caches, even when installed plugin behavior is unchanged.

## What changed

- Compare installed plugin metadata by identity, version, enablement, policy, and availability before invalidating derived caches. Continue storing the full updated payload so display consumers receive fresh metadata.
- Preserve invalidation when behavioral metadata changes or reconciliation requires an effective plugin refresh.
- Export `remote_catalog_metadata_eq` to compare catalogs independently of display metadata and plugin display order, while retaining marketplace order significance.

## Testing

Add regression tests for display-only updates, behavioral changes, catalog ordering, and preservation of loaded skills and tool suggestions. Add an app-server integration test verifying that image URL renewals and badge updates preserve live MCP sessions and cached skill resources, while an authentication policy change invalidates resource caches.

GitOrigin-RevId: 22b9ba1234a9f850201c6890e03d1ef899a56c54
2026-09-17 21:35:44 +00:00

78 lines
3.9 KiB
Rust

//! Compares the metadata that determines installed-plugin behavior.
//!
//! Display metadata (including image URLs and store capability badges) is excluded. Callers must
//! still publish the unmodified payload so display consumers receive fresh metadata.
use crate::remote::RemoteInstalledPlugin;
use crate::remote::RemoteMarketplace;
pub(crate) fn installed_plugin_metadata_eq(
previous: &[RemoteInstalledPlugin],
current: &[RemoteInstalledPlugin],
) -> bool {
previous.len() == current.len()
&& previous.iter().zip(current).all(|(previous, current)| {
previous.marketplace_name == current.marketplace_name
&& previous.id == current.id
&& previous.name == current.name
&& previous.canonical_app_id == current.canonical_app_id
&& previous.version == current.version
&& previous.installed_at == current.installed_at
&& previous.enabled == current.enabled
&& previous.install_policy == current.install_policy
&& previous.install_policy_source == current.install_policy_source
&& previous.must_show_installation_interstitial
== current.must_show_installation_interstitial
&& previous.auth_policy == current.auth_policy
&& previous.availability == current.availability
&& previous.disabled_reason == current.disabled_reason
&& previous.eligible_plan_types == current.eligible_plan_types
})
}
/// Compare installed catalog identity, versions, enablement, policy and availability.
///
/// Plugin display order is ignored. Marketplace ordering remains significant.
/// Callers must still store the unmodified current catalog for display consumers.
pub fn remote_catalog_metadata_eq(
previous: &[RemoteMarketplace],
current: &[RemoteMarketplace],
) -> bool {
previous.len() == current.len()
&& previous.iter().zip(current).all(|(previous, current)| {
if previous.name != current.name || previous.plugins.len() != current.plugins.len() {
return false;
}
// Core sorts this view by display name, which can change independently of behavior.
let mut previous = previous.plugins.iter().collect::<Vec<_>>();
let mut current = current.plugins.iter().collect::<Vec<_>>();
previous.sort_unstable_by(|a, b| a.remote_plugin_id.cmp(&b.remote_plugin_id));
current.sort_unstable_by(|a, b| a.remote_plugin_id.cmp(&b.remote_plugin_id));
previous
.into_iter()
.zip(current)
.all(|(previous, current)| {
previous.id == current.id
&& previous.remote_plugin_id == current.remote_plugin_id
&& previous.name == current.name
&& previous.version == current.version
&& previous.local_version == current.local_version
&& previous.installed == current.installed
&& previous.installed_at == current.installed_at
&& previous.enabled == current.enabled
&& previous.install_policy == current.install_policy
&& previous.install_policy_source == current.install_policy_source
&& previous.must_show_installation_interstitial
== current.must_show_installation_interstitial
&& previous.auth_policy == current.auth_policy
&& previous.availability == current.availability
&& previous.disabled_reason == current.disabled_reason
&& previous.eligible_plan_types == current.eligible_plan_types
})
})
}
#[cfg(test)]
#[path = "remote_metadata_tests.rs"]
mod tests;