mirror of
https://github.com/openai/codex.git
synced 2026-10-23 18:08:45 +00:00
Propagate remote plugin IDs to skill metadata (#35261)
## What changed - Carry a plugin's local and remote identities together from plugin loading into `SkillMetadata`. - Resolve remote IDs from the installed-plugin snapshot when available, falling back to persisted install metadata only when no snapshot exists. - Include plugin identity in skill cache keys so identity changes refresh cached skill metadata. ## Testing - Cover snapshot and persisted identity resolution, local marketplace isolation, cached skill refreshes, and propagation through plugin skill snapshots. GitOrigin-RevId: aabeeb631a43361fe817358ace7f1ea8ba5db708
This commit is contained in:
@@ -15,6 +15,8 @@ use crate::marketplace_policy::configured_plugins_from_stack;
|
||||
use crate::npm_source::materialize_npm_plugin_source;
|
||||
use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;
|
||||
use crate::remote::RemoteInstalledPlugin;
|
||||
use crate::remote_plugin_id_resolver::RemoteInstalledPluginsSnapshot;
|
||||
use crate::remote_plugin_id_resolver::RemotePluginIdResolver;
|
||||
use crate::store::PluginStore;
|
||||
use crate::store::plugin_version_for_source;
|
||||
use crate::store::plugin_version_for_source_with_fallback_manifest;
|
||||
@@ -45,6 +47,7 @@ use codex_protocol::protocol::SkillScope;
|
||||
use codex_skills::SkillConfigRules;
|
||||
use codex_skills::SkillMetadata;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_plugins::PluginIdentity;
|
||||
use codex_utils_plugins::SkillDiscoveryMode;
|
||||
use codex_utils_plugins::find_plugin_manifest_path;
|
||||
use serde_json::Value as JsonValue;
|
||||
@@ -78,6 +81,7 @@ enum PluginLoadScope<'a> {
|
||||
restriction_product: Option<Product>,
|
||||
skill_config_rules: &'a SkillConfigRules,
|
||||
plugin_skill_snapshots: Option<&'a PluginSkillSnapshots>,
|
||||
remote_plugin_id_resolver: &'a RemotePluginIdResolver,
|
||||
root_scan_slots: Arc<Semaphore>,
|
||||
},
|
||||
HooksOnly,
|
||||
@@ -117,7 +121,7 @@ pub(crate) fn log_plugin_load_errors(plugins: &[LoadedPlugin<McpServerConfig>])
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub(crate) async fn load_plugins_from_layer_stack(
|
||||
config_layer_stack: &ConfigLayerStack,
|
||||
extra_plugins: HashMap<String, PluginConfig>,
|
||||
remote_installed_plugins_snapshot: RemoteInstalledPluginsSnapshot,
|
||||
store: &PluginStore,
|
||||
plugin_skill_snapshots: Option<&PluginSkillSnapshots>,
|
||||
restriction_product: Option<Product>,
|
||||
@@ -125,6 +129,10 @@ pub(crate) async fn load_plugins_from_layer_stack(
|
||||
root_scan_slots: Arc<Semaphore>,
|
||||
) -> Vec<LoadedPlugin<McpServerConfig>> {
|
||||
let skill_config_rules = skill_config_rules_from_stack(config_layer_stack);
|
||||
let RemoteInstalledPluginsSnapshot {
|
||||
configs: extra_plugins,
|
||||
remote_plugin_id_resolver,
|
||||
} = remote_installed_plugins_snapshot;
|
||||
load_plugins_from_layer_stack_with_scope(
|
||||
config_layer_stack,
|
||||
extra_plugins,
|
||||
@@ -134,6 +142,7 @@ pub(crate) async fn load_plugins_from_layer_stack(
|
||||
restriction_product,
|
||||
skill_config_rules: &skill_config_rules,
|
||||
plugin_skill_snapshots,
|
||||
remote_plugin_id_resolver: &remote_plugin_id_resolver,
|
||||
root_scan_slots,
|
||||
},
|
||||
)
|
||||
@@ -753,18 +762,20 @@ async fn load_plugin(
|
||||
scope: &PluginLoadScope<'_>,
|
||||
) -> LoadedPlugin<McpServerConfig> {
|
||||
let plugin_id = PluginId::parse(&config_name);
|
||||
let active_plugin_root = plugin_id
|
||||
let active_plugin_installation = plugin_id
|
||||
.as_ref()
|
||||
.ok()
|
||||
.and_then(|plugin_id| store.active_plugin_root(plugin_id));
|
||||
let root = active_plugin_root
|
||||
.clone()
|
||||
.and_then(|plugin_id| store.active_plugin_installation(plugin_id));
|
||||
let root = active_plugin_installation
|
||||
.as_ref()
|
||||
.map(|installation| installation.root.clone())
|
||||
.unwrap_or_else(|| match &plugin_id {
|
||||
Ok(plugin_id) => store.plugin_base_root(plugin_id),
|
||||
Err(_) => store.root().clone(),
|
||||
});
|
||||
let mut loaded_plugin = LoadedPlugin {
|
||||
config_name,
|
||||
remote_plugin_id: None,
|
||||
manifest_name: None,
|
||||
plugin_namespace: None,
|
||||
manifest_description: None,
|
||||
@@ -784,13 +795,13 @@ async fn load_plugin(
|
||||
return loaded_plugin;
|
||||
}
|
||||
|
||||
let (loaded_plugin_id, plugin_root) = match plugin_id {
|
||||
let (loaded_plugin_id, installation) = match plugin_id {
|
||||
Ok(plugin_id) => {
|
||||
let Some(plugin_root) = active_plugin_root else {
|
||||
let Some(installation) = active_plugin_installation else {
|
||||
loaded_plugin.error = Some("plugin is not installed".to_string());
|
||||
return loaded_plugin;
|
||||
};
|
||||
(plugin_id, plugin_root)
|
||||
(plugin_id, installation)
|
||||
}
|
||||
Err(err) => {
|
||||
loaded_plugin.error = Some(err.to_string());
|
||||
@@ -798,6 +809,16 @@ async fn load_plugin(
|
||||
}
|
||||
};
|
||||
|
||||
loaded_plugin.remote_plugin_id = match scope {
|
||||
PluginLoadScope::AllCapabilities {
|
||||
remote_plugin_id_resolver,
|
||||
..
|
||||
} => remote_plugin_id_resolver.remote_plugin_id_for_installation(&installation),
|
||||
PluginLoadScope::HooksOnly => None,
|
||||
};
|
||||
|
||||
let plugin_root = installation.root;
|
||||
|
||||
if !plugin_root.as_path().is_dir() {
|
||||
loaded_plugin.error = Some("path does not exist or is not a directory".to_string());
|
||||
return loaded_plugin;
|
||||
@@ -815,14 +836,19 @@ async fn load_plugin(
|
||||
restriction_product,
|
||||
skill_config_rules,
|
||||
plugin_skill_snapshots,
|
||||
remote_plugin_id_resolver: _,
|
||||
root_scan_slots,
|
||||
} => {
|
||||
loaded_plugin.manifest_name = Some(manifest.display_name().to_string());
|
||||
loaded_plugin.manifest_description = manifest.description.clone();
|
||||
loaded_plugin.skill_roots = plugin_skill_roots(&plugin_root, manifest_paths);
|
||||
let resolved_skills = load_plugin_skills(
|
||||
let plugin_identity = PluginIdentity {
|
||||
plugin_id: loaded_plugin_id.as_key(),
|
||||
remote_plugin_id: loaded_plugin.remote_plugin_id.clone(),
|
||||
};
|
||||
let resolved_skills = load_plugin_skills_with_identity(
|
||||
&plugin_root,
|
||||
&loaded_plugin_id,
|
||||
&plugin_identity,
|
||||
&manifest,
|
||||
*restriction_product,
|
||||
skill_config_rules,
|
||||
@@ -926,10 +952,35 @@ pub async fn load_plugin_skills(
|
||||
skill_config_rules: &SkillConfigRules,
|
||||
plugin_skill_snapshots: Option<&PluginSkillSnapshots>,
|
||||
root_scan_slots: Arc<Semaphore>,
|
||||
) -> ResolvedPluginSkills {
|
||||
let plugin_identity = PluginIdentity {
|
||||
plugin_id: plugin_id.as_key(),
|
||||
remote_plugin_id: None,
|
||||
};
|
||||
load_plugin_skills_with_identity(
|
||||
plugin_root,
|
||||
&plugin_identity,
|
||||
manifest,
|
||||
restriction_product,
|
||||
skill_config_rules,
|
||||
plugin_skill_snapshots,
|
||||
root_scan_slots,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn load_plugin_skills_with_identity(
|
||||
plugin_root: &AbsolutePathBuf,
|
||||
plugin_identity: &PluginIdentity,
|
||||
manifest: &PluginManifest,
|
||||
restriction_product: Option<Product>,
|
||||
skill_config_rules: &SkillConfigRules,
|
||||
plugin_skill_snapshots: Option<&PluginSkillSnapshots>,
|
||||
root_scan_slots: Arc<Semaphore>,
|
||||
) -> ResolvedPluginSkills {
|
||||
load_plugin_skill_inventory(
|
||||
plugin_root,
|
||||
plugin_id,
|
||||
plugin_identity,
|
||||
manifest,
|
||||
restriction_product,
|
||||
plugin_skill_snapshots,
|
||||
@@ -941,7 +992,7 @@ pub async fn load_plugin_skills(
|
||||
|
||||
pub(crate) async fn load_plugin_skill_inventory(
|
||||
plugin_root: &AbsolutePathBuf,
|
||||
plugin_id: &PluginId,
|
||||
plugin_identity: &PluginIdentity,
|
||||
manifest: &PluginManifest,
|
||||
restriction_product: Option<Product>,
|
||||
plugin_skill_snapshots: Option<&PluginSkillSnapshots>,
|
||||
@@ -953,7 +1004,7 @@ pub(crate) async fn load_plugin_skill_inventory(
|
||||
path,
|
||||
scope: SkillScope::User,
|
||||
file_system: Arc::clone(&LOCAL_FS),
|
||||
plugin_id: Some(plugin_id.as_key()),
|
||||
plugin_identity: Some(plugin_identity.clone()),
|
||||
plugin_namespace: Some(manifest.name.clone()),
|
||||
plugin_root: Some(plugin_root.clone()),
|
||||
discovery_mode: SkillDiscoveryMode::Recursive,
|
||||
|
||||
Reference in New Issue
Block a user