From 31491e461bcf77ea4074023acdf5f2bb1ff32aa9 Mon Sep 17 00:00:00 2001 From: Matthew Zeng Date: Wed, 17 Jun 2026 22:50:14 -0700 Subject: [PATCH] Simplify plugin skill root snapshot cache --- codex-rs/core-plugins/src/loader.rs | 16 +-- codex-rs/core-plugins/src/loader_tests.rs | 6 +- codex-rs/core-plugins/src/manager.rs | 20 +-- codex-rs/core-plugins/src/manager_tests.rs | 10 +- codex-rs/core-skills/src/lib.rs | 4 +- codex-rs/core-skills/src/loader.rs | 4 +- ...t_loader.rs => plugin_skill_root_cache.rs} | 116 +++++------------- ...ts.rs => plugin_skill_root_cache_tests.rs} | 35 ++---- codex-rs/core-skills/src/service.rs | 21 ++-- codex-rs/core/src/thread_manager.rs | 4 +- 10 files changed, 80 insertions(+), 156 deletions(-) rename codex-rs/core-skills/src/{root_loader.rs => plugin_skill_root_cache.rs} (54%) rename codex-rs/core-skills/src/{root_loader_tests.rs => plugin_skill_root_cache_tests.rs} (58%) diff --git a/codex-rs/core-plugins/src/loader.rs b/codex-rs/core-plugins/src/loader.rs index 9b21ed1656..4cc27583f5 100644 --- a/codex-rs/core-plugins/src/loader.rs +++ b/codex-rs/core-plugins/src/loader.rs @@ -19,8 +19,8 @@ use codex_config::HooksFile; use codex_config::types::McpServerConfig; use codex_config::types::PluginConfig; use codex_config::types::PluginMcpServerConfig; +use codex_core_skills::PluginSkillRootCache; use codex_core_skills::SkillMetadata; -use codex_core_skills::SkillRootLoader; use codex_core_skills::config_rules::SkillConfigRules; use codex_core_skills::config_rules::resolve_disabled_skill_paths; use codex_core_skills::config_rules::skill_config_rules_from_stack; @@ -72,7 +72,7 @@ enum PluginLoadScope<'a> { AllCapabilities { restriction_product: Option, skill_config_rules: &'a SkillConfigRules, - skill_root_loader: &'a SkillRootLoader, + plugin_skill_root_cache: &'a PluginSkillRootCache, }, HooksOnly, } @@ -114,7 +114,7 @@ pub(crate) async fn load_plugins_from_layer_stack( config_layer_stack: &ConfigLayerStack, extra_plugins: HashMap, store: &PluginStore, - skill_root_loader: &SkillRootLoader, + plugin_skill_root_cache: &PluginSkillRootCache, restriction_product: Option, prefer_remote_curated_conflicts: bool, ) -> Vec> { @@ -127,7 +127,7 @@ pub(crate) async fn load_plugins_from_layer_stack( PluginLoadScope::AllCapabilities { restriction_product, skill_config_rules: &skill_config_rules, - skill_root_loader, + plugin_skill_root_cache, }, ) .await @@ -716,7 +716,7 @@ async fn load_plugin( PluginLoadScope::AllCapabilities { restriction_product, skill_config_rules, - skill_root_loader, + plugin_skill_root_cache, } => { loaded_plugin.manifest_name = Some(manifest.display_name().to_string()); loaded_plugin.manifest_description = manifest.description.clone(); @@ -727,7 +727,7 @@ async fn load_plugin( &manifest, *restriction_product, skill_config_rules, - skill_root_loader, + plugin_skill_root_cache, ) .await; let has_enabled_skills = resolved_skills.has_enabled_skills(); @@ -796,7 +796,7 @@ pub async fn load_plugin_skills( manifest: &PluginManifest, restriction_product: Option, skill_config_rules: &SkillConfigRules, - skill_root_loader: &SkillRootLoader, + plugin_skill_root_cache: &PluginSkillRootCache, ) -> ResolvedPluginSkills { let roots = plugin_skill_roots(plugin_root, &manifest.paths) .into_iter() @@ -809,7 +809,7 @@ pub async fn load_plugin_skills( plugin_root: Some(plugin_root.clone()), }) .collect::>(); - let outcome = skill_root_loader.load_skills_from_roots(roots).await; + let outcome = plugin_skill_root_cache.load_skills_from_roots(roots).await; let had_errors = !outcome.errors.is_empty(); let skills = outcome .skills diff --git a/codex-rs/core-plugins/src/loader_tests.rs b/codex-rs/core-plugins/src/loader_tests.rs index cdec96128a..5c0a26115a 100644 --- a/codex-rs/core-plugins/src/loader_tests.rs +++ b/codex-rs/core-plugins/src/loader_tests.rs @@ -5,7 +5,7 @@ use codex_config::ConfigLayerEntry; use codex_config::ConfigLayerSource; use codex_config::ConfigRequirements; use codex_config::ConfigRequirementsToml; -use codex_core_skills::SkillRootLoader; +use codex_core_skills::PluginSkillRootCache; use codex_plugin::PluginId; use pretty_assertions::assert_eq; use tempfile::TempDir; @@ -156,13 +156,13 @@ enabled = true ) .expect("valid config layer stack"); let store = PluginStore::new(temp_dir.path().to_path_buf()); - let skill_root_loader = SkillRootLoader::default(); + let plugin_skill_root_cache = PluginSkillRootCache::default(); let full = load_plugins_from_layer_stack( &stack, HashMap::new(), &store, - &skill_root_loader, + &plugin_skill_root_cache, Some(Product::Codex), /*prefer_remote_curated_conflicts*/ false, ) diff --git a/codex-rs/core-plugins/src/manager.rs b/codex-rs/core-plugins/src/manager.rs index e081a26136..e7b7d7ea1a 100644 --- a/codex-rs/core-plugins/src/manager.rs +++ b/codex-rs/core-plugins/src/manager.rs @@ -59,8 +59,8 @@ use codex_config::set_user_plugin_enabled; use codex_config::types::PluginConfig; use codex_config::types::ToolSuggestDisabledTool; use codex_config::types::ToolSuggestDiscoverableType; +use codex_core_skills::PluginSkillRootCache; use codex_core_skills::SkillMetadata; -use codex_core_skills::SkillRootLoader; use codex_core_skills::config_rules::SkillConfigRules; use codex_core_skills::config_rules::skill_config_rules_from_stack; use codex_hooks::plugin_hook_declarations; @@ -355,7 +355,7 @@ pub struct PluginsManager { // Keep the cache auth-independent so auth changes only need to resolve capabilities again. loaded_plugins_cache: RwLock, loaded_plugins_load_semaphore: Semaphore, - skill_root_loader: Arc, + plugin_skill_root_cache: Arc, remote_installed_plugins_cache: RwLock>>, remote_installed_plugins_cache_refresh_state: RwLock, global_remote_catalog_cache_refresh_state: RwLock, @@ -412,7 +412,7 @@ impl PluginsManager { non_curated_cache_refresh_state: RwLock::new(NonCuratedCacheRefreshState::default()), loaded_plugins_cache: RwLock::new(LoadedPluginsCache::default()), loaded_plugins_load_semaphore: Semaphore::new(/*permits*/ 1), - skill_root_loader: Arc::new(SkillRootLoader::default()), + plugin_skill_root_cache: Arc::new(PluginSkillRootCache::default()), remote_installed_plugins_cache: RwLock::new(None), remote_installed_plugins_cache_refresh_state: RwLock::new( RemoteInstalledPluginsCacheRefreshState::default(), @@ -438,9 +438,9 @@ impl PluginsManager { true } - /// Returns the loader whose plugin-root snapshots are populated while loading plugins. - pub fn skill_root_loader(&self) -> Arc { - Arc::clone(&self.skill_root_loader) + /// Returns the shared cache populated while loading plugin skill roots. + pub fn plugin_skill_root_cache(&self) -> Arc { + Arc::clone(&self.plugin_skill_root_cache) } pub fn auth_mode(&self) -> Option { @@ -513,7 +513,7 @@ impl PluginsManager { &config.config_layer_stack, self.remote_installed_plugin_configs(), &self.store, - self.skill_root_loader.as_ref(), + self.plugin_skill_root_cache.as_ref(), self.restriction_product, config.remote_plugin_enabled, ) @@ -560,7 +560,7 @@ impl PluginsManager { } fn clear_loaded_plugins_cache(&self) { - self.skill_root_loader.clear_cache(); + self.plugin_skill_root_cache.clear_cache(); let mut cache = match self.loaded_plugins_cache.write() { Ok(cache) => cache, Err(err) => err.into_inner(), @@ -582,7 +582,7 @@ impl PluginsManager { config_layer_stack, self.remote_installed_plugin_configs(), &self.store, - self.skill_root_loader.as_ref(), + self.plugin_skill_root_cache.as_ref(), self.restriction_product, config.remote_plugin_enabled, ) @@ -1607,7 +1607,7 @@ impl PluginsManager { &codex_core_skills::config_rules::skill_config_rules_from_stack( &config.config_layer_stack, ), - self.skill_root_loader.as_ref(), + self.plugin_skill_root_cache.as_ref(), ) .await; let plugin_data_root = self.store.plugin_data_root(&plugin_id); diff --git a/codex-rs/core-plugins/src/manager_tests.rs b/codex-rs/core-plugins/src/manager_tests.rs index 1c639a5dc3..defe1f3f34 100644 --- a/codex-rs/core-plugins/src/manager_tests.rs +++ b/codex-rs/core-plugins/src/manager_tests.rs @@ -34,7 +34,7 @@ use codex_config::McpServerConfig; use codex_config::McpServerOAuthConfig; use codex_config::McpServerToolConfig; use codex_config::types::McpServerTransportConfig; -use codex_core_skills::SkillRootLoader; +use codex_core_skills::PluginSkillRootCache; use codex_core_skills::config_rules::SkillConfigRules; use codex_login::CodexAuth; use codex_plugin::AppDeclaration; @@ -1467,7 +1467,7 @@ async fn load_plugin_skills_dedupes_overlapping_manifest_roots() { interface: None, }; let plugin_id = PluginId::parse("sample@test").expect("plugin id should parse"); - let skill_root_loader = SkillRootLoader::default(); + let plugin_skill_root_cache = PluginSkillRootCache::default(); let resolved = load_plugin_skills( &plugin_root, @@ -1475,7 +1475,7 @@ async fn load_plugin_skills_dedupes_overlapping_manifest_roots() { &manifest, /*restriction_product*/ None, &SkillConfigRules::default(), - &skill_root_loader, + &plugin_skill_root_cache, ) .await; @@ -4823,12 +4823,12 @@ async fn load_plugins_ignores_project_config_files() { ) .expect("config layer stack should build"); - let skill_root_loader = SkillRootLoader::default(); + let plugin_skill_root_cache = PluginSkillRootCache::default(); let plugins = load_plugins_from_layer_stack( &stack, std::collections::HashMap::new(), &PluginStore::new(codex_home.path().to_path_buf()), - &skill_root_loader, + &plugin_skill_root_cache, Some(Product::Codex), /*prefer_remote_curated_conflicts*/ false, ) diff --git a/codex-rs/core-skills/src/lib.rs b/codex-rs/core-skills/src/lib.rs index 8ee6d39f86..a3b3644455 100644 --- a/codex-rs/core-skills/src/lib.rs +++ b/codex-rs/core-skills/src/lib.rs @@ -4,9 +4,9 @@ pub(crate) mod invocation_utils; pub mod loader; mod mention_counts; pub mod model; +mod plugin_skill_root_cache; pub mod remote; pub mod render; -mod root_loader; pub mod service; mod skill_instructions; pub mod system; @@ -20,6 +20,7 @@ pub use model::SkillLoadOutcome; pub use model::SkillMetadata; pub use model::SkillPolicy; pub use model::filter_skill_load_outcome_for_product; +pub use plugin_skill_root_cache::PluginSkillRootCache; pub use render::AvailableSkills; pub use render::SKILLS_HOW_TO_USE_WITH_ABSOLUTE_PATHS; pub use render::SKILLS_HOW_TO_USE_WITH_ALIASES; @@ -30,7 +31,6 @@ pub use render::SkillRenderReport; pub use render::build_available_skills; pub use render::default_skill_metadata_budget; pub use render::render_available_skills_body; -pub use root_loader::SkillRootLoader; pub use service::SkillsLoadInput; pub use service::SkillsService; pub use skill_instructions::SkillInstructions; diff --git a/codex-rs/core-skills/src/loader.rs b/codex-rs/core-skills/src/loader.rs index 346eebbec9..45906a9d94 100644 --- a/codex-rs/core-skills/src/loader.rs +++ b/codex-rs/core-skills/src/loader.rs @@ -5,7 +5,7 @@ use crate::model::SkillLoadOutcome; use crate::model::SkillMetadata; use crate::model::SkillPolicy; use crate::model::SkillToolDependency; -use crate::root_loader::SkillRootLoader; +use crate::plugin_skill_root_cache::PluginSkillRootCache; use crate::system::system_cache_root_dir; use codex_app_server_protocol::ConfigLayerSource; use codex_config::ConfigLayerStack; @@ -164,7 +164,7 @@ pub async fn load_skills_from_roots(roots: I) -> SkillLoadOutcome where I: IntoIterator, { - SkillRootLoader::default() + PluginSkillRootCache::default() .load_skills_from_roots(roots) .await } diff --git a/codex-rs/core-skills/src/root_loader.rs b/codex-rs/core-skills/src/plugin_skill_root_cache.rs similarity index 54% rename from codex-rs/core-skills/src/root_loader.rs rename to codex-rs/core-skills/src/plugin_skill_root_cache.rs index 06f423967f..9cd58caa80 100644 --- a/codex-rs/core-skills/src/root_loader.rs +++ b/codex-rs/core-skills/src/plugin_skill_root_cache.rs @@ -1,11 +1,8 @@ use std::collections::HashMap; use std::collections::HashSet; -use std::hash::Hash; -use std::hash::Hasher; use std::sync::Arc; use std::sync::RwLock; -use codex_exec_server::ExecutorFileSystem; use codex_utils_absolute_path::AbsolutePathBuf; use crate::SkillLoadOutcome; @@ -16,62 +13,16 @@ use crate::model::SkillFileSystemsByPath; const MAX_CACHED_PLUGIN_SKILL_ROOTS: usize = 256; -#[derive(Clone)] -struct FileSystemIdentity(Arc); - -impl PartialEq for FileSystemIdentity { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.0, &other.0) - } -} - -impl Eq for FileSystemIdentity {} - -impl Hash for FileSystemIdentity { - fn hash(&self, state: &mut H) { - (Arc::as_ptr(&self.0) as *const ()).hash(state); - } -} - -#[derive(Clone, PartialEq, Eq, Hash)] -struct SkillRootCacheKey { - path: AbsolutePathBuf, - scope_rank: u8, - file_system: FileSystemIdentity, - plugin_id: String, - plugin_namespace: String, - plugin_root: AbsolutePathBuf, -} - -impl SkillRootCacheKey { - fn from_root(root: &SkillRoot) -> Option { - Some(Self { - path: root.path.clone(), - scope_rank: scope_rank(root.scope), - file_system: FileSystemIdentity(Arc::clone(&root.file_system)), - plugin_id: root.plugin_id.clone()?, - plugin_namespace: root.plugin_namespace.clone()?, - plugin_root: root.plugin_root.clone()?, - }) - } -} - -/// Loads skill roots and reuses parsed plugin-root snapshots until explicitly invalidated. +/// Shares parsed plugin skill-root snapshots between plugin and skill loading. /// /// Non-plugin roots are always loaded directly because their filesystem lifecycle is owned by the /// environment or config layer that supplied them. #[derive(Default)] -struct PluginRootCache { - generation: u64, - snapshots: HashMap, +pub struct PluginSkillRootCache { + snapshots: RwLock>, } -#[derive(Default)] -pub struct SkillRootLoader { - plugin_root_cache: RwLock, -} - -impl SkillRootLoader { +impl PluginSkillRootCache { /// Loads and merges roots, reusing snapshots for roots owned by a plugin. pub async fn load_skills_from_roots(&self, roots: I) -> SkillLoadOutcome where @@ -79,16 +30,28 @@ impl SkillRootLoader { { let mut snapshots = Vec::new(); for root in roots { - let cache_key = SkillRootCacheKey::from_root(&root); - let (cache_generation, cached_snapshot) = cache_key + // Plugin skill roots always use local filesystem and User scope, so the absolute skill + // root path is sufficient to share their snapshot between plugin and skill loading. + let cache_key = root.plugin_root.as_ref().map(|_| root.path.clone()); + let cached_snapshot = cache_key .as_ref() - .map_or((0, None), |key| self.cached_snapshot(key)); + .and_then(|root| match self.snapshots.read() { + Ok(cache) => cache.get(root).cloned(), + Err(err) => err.into_inner().get(root).cloned(), + }); let snapshot = match cached_snapshot { Some(snapshot) => snapshot, None => { let snapshot = load_skill_root(root).await; - if let Some(cache_key) = cache_key { - self.cache_snapshot(cache_generation, cache_key, snapshot.clone()); + if let Some(root) = cache_key { + let mut cache = self + .snapshots + .write() + .unwrap_or_else(std::sync::PoisonError::into_inner); + if cache.len() < MAX_CACHED_PLUGIN_SKILL_ROOTS || cache.contains_key(&root) + { + cache.insert(root, snapshot.clone()); + } } snapshot } @@ -102,34 +65,10 @@ impl SkillRootLoader { /// Invalidates every cached plugin-root snapshot. pub fn clear_cache(&self) { let mut cache = self - .plugin_root_cache + .snapshots .write() .unwrap_or_else(std::sync::PoisonError::into_inner); - cache.generation = cache.generation.wrapping_add(1); - cache.snapshots.clear(); - } - - fn cached_snapshot(&self, key: &SkillRootCacheKey) -> (u64, Option) { - match self.plugin_root_cache.read() { - Ok(cache) => (cache.generation, cache.snapshots.get(key).cloned()), - Err(err) => { - let cache = err.into_inner(); - (cache.generation, cache.snapshots.get(key).cloned()) - } - } - } - - fn cache_snapshot(&self, generation: u64, key: SkillRootCacheKey, snapshot: SkillRootSnapshot) { - let mut cache = self - .plugin_root_cache - .write() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if cache.generation == generation - && (cache.snapshots.len() < MAX_CACHED_PLUGIN_SKILL_ROOTS - || cache.snapshots.contains_key(&key)) - { - cache.snapshots.insert(key, snapshot); - } + cache.clear(); } } @@ -178,9 +117,10 @@ fn merge_skill_root_snapshots(snapshots: Vec) -> SkillLoadOut outcome.skill_root_by_path = Arc::new(skill_root_by_path); outcome.file_systems_by_skill_path = SkillFileSystemsByPath::new(file_systems_by_skill_path); + // The merged outcome includes non-plugin roots, so preserve the global scope ordering. outcome.skills.sort_by(|a, b| { - scope_rank(a.scope) - .cmp(&scope_rank(b.scope)) + merged_skill_scope_rank(a.scope) + .cmp(&merged_skill_scope_rank(b.scope)) .then_with(|| a.name.cmp(&b.name)) .then_with(|| a.path_to_skills_md.cmp(&b.path_to_skills_md)) }); @@ -188,7 +128,7 @@ fn merge_skill_root_snapshots(snapshots: Vec) -> SkillLoadOut outcome } -fn scope_rank(scope: codex_protocol::protocol::SkillScope) -> u8 { +fn merged_skill_scope_rank(scope: codex_protocol::protocol::SkillScope) -> u8 { use codex_protocol::protocol::SkillScope; match scope { @@ -200,5 +140,5 @@ fn scope_rank(scope: codex_protocol::protocol::SkillScope) -> u8 { } #[cfg(test)] -#[path = "root_loader_tests.rs"] +#[path = "plugin_skill_root_cache_tests.rs"] mod tests; diff --git a/codex-rs/core-skills/src/root_loader_tests.rs b/codex-rs/core-skills/src/plugin_skill_root_cache_tests.rs similarity index 58% rename from codex-rs/core-skills/src/root_loader_tests.rs rename to codex-rs/core-skills/src/plugin_skill_root_cache_tests.rs index b5a5711aef..3055803ff0 100644 --- a/codex-rs/core-skills/src/root_loader_tests.rs +++ b/codex-rs/core-skills/src/plugin_skill_root_cache_tests.rs @@ -5,10 +5,8 @@ use codex_exec_server::LOCAL_FS; use codex_protocol::protocol::SkillScope; use codex_utils_absolute_path::test_support::PathExt; -use super::SkillRootCacheKey; -use super::SkillRootLoader; +use super::PluginSkillRootCache; use crate::loader::SkillRoot; -use crate::loader::load_skill_root; fn plugin_root(path: &std::path::Path) -> SkillRoot { SkillRoot { @@ -32,45 +30,26 @@ fn write_skill(path: &std::path::Path, description: &str) { } #[tokio::test] -async fn reuses_plugin_root_snapshot_until_cache_is_cleared() { +async fn reuses_plugin_skill_root_snapshot_until_cache_is_cleared() { let tempdir = tempfile::tempdir().expect("tempdir"); - let loader = SkillRootLoader::default(); + let cache = PluginSkillRootCache::default(); write_skill(tempdir.path(), "first"); - let first = loader + let first = cache .load_skills_from_roots([plugin_root(tempdir.path())]) .await; write_skill(tempdir.path(), "second"); - let cached = loader + let cached = cache .load_skills_from_roots([plugin_root(tempdir.path())]) .await; assert_eq!(cached.skills, first.skills); - loader.clear_cache(); - let refreshed = loader + cache.clear_cache(); + let refreshed = cache .load_skills_from_roots([plugin_root(tempdir.path())]) .await; let mut expected = first.skills; expected[0].description = "second".to_string(); assert_eq!(refreshed.skills, expected); } - -#[tokio::test] -async fn clear_cache_rejects_an_in_flight_snapshot() { - let tempdir = tempfile::tempdir().expect("tempdir"); - let loader = SkillRootLoader::default(); - write_skill(tempdir.path(), "first"); - - let root = plugin_root(tempdir.path()); - let cache_key = SkillRootCacheKey::from_root(&root).expect("plugin root cache key"); - let (generation, cached_snapshot) = loader.cached_snapshot(&cache_key); - assert!(cached_snapshot.is_none()); - - let snapshot = load_skill_root(root).await; - loader.clear_cache(); - loader.cache_snapshot(generation, cache_key.clone(), snapshot); - - let (_, cached_snapshot) = loader.cached_snapshot(&cache_key); - assert!(cached_snapshot.is_none()); -} diff --git a/codex-rs/core-skills/src/service.rs b/codex-rs/core-skills/src/service.rs index 1187565d0a..4db97f289a 100644 --- a/codex-rs/core-skills/src/service.rs +++ b/codex-rs/core-skills/src/service.rs @@ -14,8 +14,8 @@ use tracing::instrument; use tracing::warn; use crate::HostSkillsSnapshot; +use crate::PluginSkillRootCache; use crate::SkillLoadOutcome; -use crate::SkillRootLoader; use crate::build_implicit_skill_path_indexes; use crate::config_rules::SkillConfigRules; use crate::config_rules::resolve_disabled_skill_paths; @@ -56,7 +56,7 @@ impl SkillsLoadInput { pub struct SkillsService { codex_home: AbsolutePathBuf, restriction_product: Option, - skill_root_loader: Arc, + plugin_skill_root_cache: Arc, extra_roots: RwLock>, cache_by_cwd: RwLock>, cache_by_config: RwLock>, @@ -75,7 +75,7 @@ impl SkillsService { let service = Self { codex_home, restriction_product, - skill_root_loader: Arc::new(SkillRootLoader::default()), + plugin_skill_root_cache: Arc::new(PluginSkillRootCache::default()), extra_roots: RwLock::new(Vec::new()), cache_by_cwd: RwLock::new(HashMap::new()), cache_by_config: RwLock::new(HashMap::new()), @@ -90,9 +90,12 @@ impl SkillsService { service } - /// Reuses plugin-root snapshots populated by another owner of the loader. - pub fn with_skill_root_loader(mut self, skill_root_loader: Arc) -> Self { - self.skill_root_loader = skill_root_loader; + /// Reuses plugin-root snapshots populated by another owner of the cache. + pub fn with_plugin_skill_root_cache( + mut self, + plugin_skill_root_cache: Arc, + ) -> Self { + self.plugin_skill_root_cache = plugin_skill_root_cache; self } @@ -207,7 +210,9 @@ impl SkillsService { skill_config_rules: &SkillConfigRules, ) -> SkillLoadOutcome { let outcome = crate::filter_skill_load_outcome_for_product( - self.skill_root_loader.load_skills_from_roots(roots).await, + self.plugin_skill_root_cache + .load_skills_from_roots(roots) + .await, self.restriction_product, ); let disabled_paths = resolve_disabled_skill_paths(&outcome.skills, skill_config_rules); @@ -215,7 +220,7 @@ impl SkillsService { } pub fn clear_cache(&self) { - self.skill_root_loader.clear_cache(); + self.plugin_skill_root_cache.clear_cache(); let cleared_cwd = { let mut cache = self .cache_by_cwd diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index 6ed81cb059..b5040df8bf 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -287,7 +287,7 @@ impl ThreadManager { config.bundled_skills_enabled(), restriction_product, ) - .with_skill_root_loader(plugins_manager.skill_root_loader()), + .with_plugin_skill_root_cache(plugins_manager.plugin_skill_root_cache()), ); Self { state: Arc::new(ThreadManagerState { @@ -382,7 +382,7 @@ impl ThreadManager { /*bundled_skills_enabled*/ true, restriction_product, ) - .with_skill_root_loader(plugins_manager.skill_root_loader()), + .with_plugin_skill_root_cache(plugins_manager.plugin_skill_root_cache()), ); // This test constructor has no Config input. Tests that need a non-local // process store should construct ThreadManager::new with an explicit store.