From 2ce7a1999695b66875b31a6c423cfa5d7872ecc1 Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Wed, 25 Mar 2026 01:17:21 -0700 Subject: [PATCH] fix --- .../app-server/src/codex_message_processor.rs | 40 ++++++++-- .../core-skills/src/skills/config_rules.rs | 10 +-- codex-rs/core-skills/src/skills/injection.rs | 28 +++---- codex-rs/core-skills/src/skills/loader.rs | 8 +- codex-rs/core-skills/src/skills/manager.rs | 4 +- codex-rs/core/src/agent/role_tests.rs | 11 ++- codex-rs/core/src/codex.rs | 73 ++++++++++++++++--- codex-rs/core/src/codex_tests.rs | 36 ++++----- codex-rs/core/src/codex_tests_guardian.rs | 6 +- codex-rs/core/src/contextual_user_message.rs | 1 - codex-rs/core/src/instructions/mod.rs | 1 - codex-rs/core/src/lib.rs | 11 ++- codex-rs/core/src/plugins/manager.rs | 19 ++++- codex-rs/core/src/plugins/mentions.rs | 2 - codex-rs/core/src/plugins/mod.rs | 1 - codex-rs/core/src/plugins/store.rs | 1 - codex-rs/core/src/skills.rs | 3 + codex-rs/core/src/skills_watcher.rs | 11 ++- codex-rs/core/src/thread_manager.rs | 10 +-- .../tools/runtimes/shell/unix_escalation.rs | 30 ++------ 20 files changed, 196 insertions(+), 110 deletions(-) diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 7c9c43de2f..74510de7e3 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -202,6 +202,8 @@ use codex_core::config::types::McpServerTransportConfig; use codex_core::config_loader::CloudRequirementsLoadError; use codex_core::config_loader::CloudRequirementsLoadErrorCode; use codex_core::config_loader::CloudRequirementsLoader; +use codex_core::config_loader::LoaderOverrides; +use codex_core::config_loader::load_config_layers_state; use codex_core::default_client::set_default_client_residency_requirement; use codex_core::error::CodexErr; use codex_core::error::Result as CodexResult; @@ -281,6 +283,7 @@ use codex_state::StateRuntime; use codex_state::ThreadMetadata; use codex_state::ThreadMetadataBuilder; use codex_state::log_db::LogDbLayer; +use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_json_to_toml::json_to_toml; use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP; use std::collections::HashMap; @@ -5479,15 +5482,31 @@ impl CodexMessageProcessor { } }; let skills_manager = self.thread_manager.skills_manager(); + let plugins_manager = self.thread_manager.plugins_manager(); + let cli_overrides = self.cli_overrides.as_slice(); let mut data = Vec::new(); for cwd in cwds { let extra_roots = extra_roots_by_cwd .get(&cwd) .map_or(&[][..], std::vec::Vec::as_slice); + let cwd_abs = match AbsolutePathBuf::try_from(cwd.as_path()) { + Ok(path) => path, + Err(err) => { + data.push(codex_app_server_protocol::SkillsListEntry { + cwd, + skills: Vec::new(), + errors: errors_to_info(&[codex_core::skills::SkillError { + path: cwd.clone(), + message: err.to_string(), + }]), + }); + continue; + } + }; let config_layer_stack = match load_config_layers_state( &self.codex_home, Some(cwd_abs), - &cli_overrides, + cli_overrides, LoaderOverrides::default(), CloudRequirementsLoader::default(), ) @@ -5495,16 +5514,21 @@ impl CodexMessageProcessor { { Ok(config_layer_stack) => config_layer_stack, Err(err) => { - return SkillLoadOutcome { - errors: vec![crate::skills::model::SkillError { - path: cwd.to_path_buf(), + data.push(codex_app_server_protocol::SkillsListEntry { + cwd, + skills: Vec::new(), + errors: errors_to_info(&[codex_core::skills::SkillError { + path: cwd.clone(), message: err.to_string(), - }], - ..Default::default() - }; + }]), + }); + continue; } }; - let effective_skill_roots = skills_manager.effective_skill_roots(&config_layer_stack); + let effective_skill_roots = plugins_manager.effective_skill_roots_for_layer_stack( + &config_layer_stack, + config.features.enabled(Feature::Plugins), + ); let outcome = skills_manager .skills_for_cwd_with_extra_user_roots( &cwd, diff --git a/codex-rs/core-skills/src/skills/config_rules.rs b/codex-rs/core-skills/src/skills/config_rules.rs index 121443d62e..85e2b8fe8e 100644 --- a/codex-rs/core-skills/src/skills/config_rules.rs +++ b/codex-rs/core-skills/src/skills/config_rules.rs @@ -12,23 +12,23 @@ use tracing::warn; use crate::skills::SkillMetadata; #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub(crate) enum SkillConfigRuleSelector { +pub enum SkillConfigRuleSelector { Name(String), Path(PathBuf), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct SkillConfigRule { +pub struct SkillConfigRule { pub selector: SkillConfigRuleSelector, pub enabled: bool, } #[derive(Debug, Clone, Default, PartialEq, Eq, Hash)] -pub(crate) struct SkillConfigRules { +pub struct SkillConfigRules { pub entries: Vec, } -pub(crate) fn skill_config_rules_from_stack( +pub fn skill_config_rules_from_stack( config_layer_stack: &ConfigLayerStack, ) -> SkillConfigRules { let mut entries = Vec::new(); @@ -71,7 +71,7 @@ pub(crate) fn skill_config_rules_from_stack( SkillConfigRules { entries } } -pub(crate) fn resolve_disabled_skill_paths( +pub fn resolve_disabled_skill_paths( skills: &[SkillMetadata], rules: &SkillConfigRules, ) -> HashSet { diff --git a/codex-rs/core-skills/src/skills/injection.rs b/codex-rs/core-skills/src/skills/injection.rs index 657be4016f..09dc947ff5 100644 --- a/codex-rs/core-skills/src/skills/injection.rs +++ b/codex-rs/core-skills/src/skills/injection.rs @@ -16,12 +16,12 @@ use codex_protocol::user_input::UserInput; use tokio::fs; #[derive(Debug, Default)] -pub(crate) struct SkillInjections { - pub(crate) items: Vec, - pub(crate) warnings: Vec, +pub struct SkillInjections { + pub items: Vec, + pub warnings: Vec, } -pub(crate) async fn build_skill_injections( +pub async fn build_skill_injections( mentioned_skills: &[SkillMetadata], otel: Option<&SessionTelemetry>, analytics_client: &AnalyticsEventsClient, @@ -97,7 +97,7 @@ fn emit_skill_injected_metric( /// Complexity: `O(T + (N_s + N_t) * S)` time, `O(S + M)` space, where: /// `S` = number of skills, `T` = total text length, `N_s` = number of structured skill inputs, /// `N_t` = number of text inputs, `M` = max mentions parsed from a single text input. -pub(crate) fn collect_explicit_skill_mentions( +pub fn collect_explicit_skill_mentions( inputs: &[UserInput], skills: &[SkillMetadata], disabled_paths: &HashSet, @@ -159,7 +159,7 @@ struct SkillSelectionContext<'a> { connector_slug_counts: &'a HashMap, } -pub(crate) struct ToolMentions<'a> { +pub struct ToolMentions<'a> { names: HashSet<&'a str>, paths: HashSet<&'a str>, plain_names: HashSet<&'a str>, @@ -170,17 +170,17 @@ impl<'a> ToolMentions<'a> { self.names.is_empty() && self.paths.is_empty() } - pub(crate) fn plain_names(&self) -> impl Iterator + '_ { + pub fn plain_names(&self) -> impl Iterator + '_ { self.plain_names.iter().copied() } - pub(crate) fn paths(&self) -> impl Iterator + '_ { + pub fn paths(&self) -> impl Iterator + '_ { self.paths.iter().copied() } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) enum ToolMentionKind { +pub enum ToolMentionKind { App, Mcp, Plugin, @@ -194,7 +194,7 @@ const PLUGIN_PATH_PREFIX: &str = "plugin://"; const SKILL_PATH_PREFIX: &str = "skill://"; const SKILL_FILENAME: &str = "SKILL.md"; -pub(crate) fn tool_kind_for_path(path: &str) -> ToolMentionKind { +pub fn tool_kind_for_path(path: &str) -> ToolMentionKind { if path.starts_with(APP_PATH_PREFIX) { ToolMentionKind::App } else if path.starts_with(MCP_PATH_PREFIX) { @@ -213,12 +213,12 @@ fn is_skill_filename(path: &str) -> bool { file_name.eq_ignore_ascii_case(SKILL_FILENAME) } -pub(crate) fn app_id_from_path(path: &str) -> Option<&str> { +pub fn app_id_from_path(path: &str) -> Option<&str> { path.strip_prefix(APP_PATH_PREFIX) .filter(|value| !value.is_empty()) } -pub(crate) fn plugin_config_name_from_path(path: &str) -> Option<&str> { +pub fn plugin_config_name_from_path(path: &str) -> Option<&str> { path.strip_prefix(PLUGIN_PATH_PREFIX) .filter(|value| !value.is_empty()) } @@ -232,11 +232,11 @@ pub(crate) fn normalize_skill_path(path: &str) -> &str { /// Supports explicit resource links in the form `[$tool-name](resource path)`. When a /// resource path is present, it is captured for exact path matching while also tracking /// the name for fallback matching. -pub(crate) fn extract_tool_mentions(text: &str) -> ToolMentions<'_> { +pub fn extract_tool_mentions(text: &str) -> ToolMentions<'_> { extract_tool_mentions_with_sigil(text, TOOL_MENTION_SIGIL) } -pub(crate) fn extract_tool_mentions_with_sigil(text: &str, sigil: char) -> ToolMentions<'_> { +pub fn extract_tool_mentions_with_sigil(text: &str, sigil: char) -> ToolMentions<'_> { let text_bytes = text.as_bytes(); let mut mentioned_names: HashSet<&str> = HashSet::new(); let mut mentioned_paths: HashSet<&str> = HashSet::new(); diff --git a/codex-rs/core-skills/src/skills/loader.rs b/codex-rs/core-skills/src/skills/loader.rs index af9169c354..b671a668cf 100644 --- a/codex-rs/core-skills/src/skills/loader.rs +++ b/codex-rs/core-skills/src/skills/loader.rs @@ -173,12 +173,12 @@ impl fmt::Display for SkillParseError { impl Error for SkillParseError {} -pub(crate) struct SkillRoot { - pub(crate) path: PathBuf, - pub(crate) scope: SkillScope, +pub struct SkillRoot { + pub path: PathBuf, + pub scope: SkillScope, } -pub(crate) fn load_skills_from_roots(roots: I) -> SkillLoadOutcome +pub fn load_skills_from_roots(roots: I) -> SkillLoadOutcome where I: IntoIterator, { diff --git a/codex-rs/core-skills/src/skills/manager.rs b/codex-rs/core-skills/src/skills/manager.rs index 654d423e64..9c1500a56c 100644 --- a/codex-rs/core-skills/src/skills/manager.rs +++ b/codex-rs/core-skills/src/skills/manager.rs @@ -90,7 +90,7 @@ impl SkillsManager { outcome } - pub(crate) fn skill_roots_for_config( + pub fn skill_roots_for_config( &self, cwd: &Path, effective_skill_roots: &[PathBuf], @@ -221,7 +221,7 @@ struct ConfigSkillsCacheKey { skill_config_rules: SkillConfigRules, } -pub(crate) fn bundled_skills_enabled_from_stack( +pub fn bundled_skills_enabled_from_stack( config_layer_stack: &codex_config::ConfigLayerStack, ) -> bool { let effective_config = config_layer_stack.effective_config(); diff --git a/codex-rs/core/src/agent/role_tests.rs b/codex-rs/core/src/agent/role_tests.rs index 7726ce7957..b1625cf55d 100644 --- a/codex-rs/core/src/agent/role_tests.rs +++ b/codex-rs/core/src/agent/role_tests.rs @@ -629,8 +629,15 @@ enabled = false .expect("custom role should apply"); let plugins_manager = Arc::new(PluginsManager::new(home.path().to_path_buf())); - let skills_manager = SkillsManager::new(home.path().to_path_buf(), plugins_manager, true); - let outcome = skills_manager.skills_for_config(&config); + let skills_manager = SkillsManager::new(home.path().to_path_buf(), true); + let plugin_outcome = plugins_manager.plugins_for_config(&config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); + let outcome = skills_manager.skills_for_config( + config.cwd.as_path(), + &effective_skill_roots, + &config.config_layer_stack, + config.bundled_skills_enabled(), + ); let skill = outcome .skills .iter() diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 0b331f21e0..10485e8d38 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -471,7 +471,14 @@ impl Codex { let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY); let (tx_event, rx_event) = async_channel::unbounded(); - let loaded_skills = skills_manager.skills_for_config(&config); + let plugin_outcome = plugins_manager.plugins_for_config(&config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); + let loaded_skills = skills_manager.skills_for_config( + config.cwd.as_path(), + &effective_skill_roots, + &config.config_layer_stack, + config.bundled_skills_enabled(), + ); for err in &loaded_skills.errors { error!( @@ -2440,10 +2447,20 @@ impl Session { &per_turn_config, ) .await; + let plugin_outcome = self + .services + .plugins_manager + .plugins_for_config(&per_turn_config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); let skills_outcome = Arc::new( self.services .skills_manager - .skills_for_config(&per_turn_config), + .skills_for_config( + per_turn_config.cwd.as_path(), + &effective_skill_roots, + &per_turn_config.config_layer_stack, + per_turn_config.bundled_skills_enabled(), + ), ); let mut turn_context: TurnContext = Self::make_turn_context( self.conversation_id, @@ -4500,6 +4517,12 @@ mod handlers { use crate::codex::spawn_review_thread; use crate::config::Config; + use crate::config_loader::CloudRequirementsLoader; + use crate::config_loader::LoaderOverrides; + use crate::config_loader::load_config_layers_state; + use crate::skills::SkillError; + use codex_features::Feature; + use codex_utils_absolute_path::AbsolutePathBuf; use crate::mcp::auth::compute_auth_statuses; use crate::mcp::collect_mcp_snapshot_from_manager; @@ -4935,13 +4958,32 @@ mod handlers { }; let skills_manager = &sess.services.skills_manager; + let plugins_manager = &sess.services.plugins_manager; let config = sess.get_config().await; + let codex_home = sess.codex_home().await; let mut skills = Vec::new(); + let empty_cli_overrides: &[(String, toml::Value)] = &[]; for cwd in cwds { + let cwd_abs = match AbsolutePathBuf::try_from(cwd.as_path()) { + Ok(path) => path, + Err(err) => { + let message = err.to_string(); + let cwd_for_entry = cwd.clone(); + skills.push(SkillsListEntry { + cwd: cwd_for_entry.clone(), + skills: Vec::new(), + errors: super::errors_to_info(&[SkillError { + path: cwd_for_entry, + message, + }]), + }); + continue; + } + }; let config_layer_stack = match load_config_layers_state( - &self.codex_home, + &codex_home, Some(cwd_abs), - &cli_overrides, + empty_cli_overrides, LoaderOverrides::default(), CloudRequirementsLoader::default(), ) @@ -4949,16 +4991,23 @@ mod handlers { { Ok(config_layer_stack) => config_layer_stack, Err(err) => { - return SkillLoadOutcome { - errors: vec![crate::skills::model::SkillError { - path: cwd.to_path_buf(), - message: err.to_string(), - }], - ..Default::default() - }; + let message = err.to_string(); + let cwd_for_entry = cwd.clone(); + skills.push(SkillsListEntry { + cwd: cwd_for_entry.clone(), + skills: Vec::new(), + errors: super::errors_to_info(&[SkillError { + path: cwd_for_entry, + message, + }]), + }); + continue; } }; - let effective_skill_roots = skills_manager.effective_skill_roots(&config_layer_stack); + let effective_skill_roots = plugins_manager.effective_skill_roots_for_layer_stack( + &config_layer_stack, + config.features.enabled(Feature::Plugins), + ); let outcome = skills_manager .skills_for_cwd( &cwd, diff --git a/codex-rs/core/src/codex_tests.rs b/codex-rs/core/src/codex_tests.rs index df3338b197..47dfe7a733 100644 --- a/codex-rs/core/src/codex_tests.rs +++ b/codex-rs/core/src/codex_tests.rs @@ -2506,11 +2506,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() { let (agent_status_tx, _agent_status_rx) = watch::channel(AgentStatus::PendingInit); let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone())); let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager))); - let skills_manager = Arc::new(SkillsManager::new( - config.codex_home.clone(), - Arc::clone(&plugins_manager), - true, - )); + let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true)); let result = Session::new( session_configuration, Arc::clone(&config), @@ -2611,11 +2607,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) { let state = SessionState::new(session_configuration.clone()); let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone())); let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager))); - let skills_manager = Arc::new(SkillsManager::new( - config.codex_home.clone(), - Arc::clone(&plugins_manager), - true, - )); + let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true)); let network_approval = Arc::new(NetworkApprovalService::default()); let environment = Arc::new( codex_exec_server::Environment::create(None) @@ -2683,7 +2675,14 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) { config.js_repl_node_module_dirs.clone(), )); - let skills_outcome = Arc::new(services.skills_manager.skills_for_config(&per_turn_config)); + let plugin_outcome = services.plugins_manager.plugins_for_config(&per_turn_config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); + let skills_outcome = Arc::new(services.skills_manager.skills_for_config( + per_turn_config.cwd.as_path(), + &effective_skill_roots, + &per_turn_config.config_layer_stack, + per_turn_config.bundled_skills_enabled(), + )); let turn_context = Session::make_turn_context( conversation_id, Some(Arc::clone(&auth_manager)), @@ -3447,11 +3446,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx( let state = SessionState::new(session_configuration.clone()); let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone())); let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager))); - let skills_manager = Arc::new(SkillsManager::new( - config.codex_home.clone(), - Arc::clone(&plugins_manager), - true, - )); + let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true)); let network_approval = Arc::new(NetworkApprovalService::default()); let environment = Arc::new( codex_exec_server::Environment::create(None) @@ -3519,7 +3514,14 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx( config.js_repl_node_module_dirs.clone(), )); - let skills_outcome = Arc::new(services.skills_manager.skills_for_config(&per_turn_config)); + let plugin_outcome = services.plugins_manager.plugins_for_config(&per_turn_config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); + let skills_outcome = Arc::new(services.skills_manager.skills_for_config( + per_turn_config.cwd.as_path(), + &effective_skill_roots, + &per_turn_config.config_layer_stack, + per_turn_config.bundled_skills_enabled(), + )); let turn_context = Arc::new(Session::make_turn_context( conversation_id, Some(Arc::clone(&auth_manager)), diff --git a/codex-rs/core/src/codex_tests_guardian.rs b/codex-rs/core/src/codex_tests_guardian.rs index e9050024eb..2173f5042d 100644 --- a/codex-rs/core/src/codex_tests_guardian.rs +++ b/codex-rs/core/src/codex_tests_guardian.rs @@ -429,11 +429,7 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() { CollaborationModesConfig::default(), )); let plugins_manager = Arc::new(PluginsManager::new(config.codex_home.clone())); - let skills_manager = Arc::new(SkillsManager::new( - config.codex_home.clone(), - Arc::clone(&plugins_manager), - true, - )); + let skills_manager = Arc::new(SkillsManager::new(config.codex_home.clone(), true)); let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager))); let skills_watcher = Arc::new(SkillsWatcher::noop()); diff --git a/codex-rs/core/src/contextual_user_message.rs b/codex-rs/core/src/contextual_user_message.rs index 01863498e9..68a86cc885 100644 --- a/codex-rs/core/src/contextual_user_message.rs +++ b/codex-rs/core/src/contextual_user_message.rs @@ -4,7 +4,6 @@ use codex_instructions::SKILL_FRAGMENT; use codex_protocol::items::HookPromptItem; use codex_protocol::items::parse_hook_prompt_fragment; use codex_protocol::models::ContentItem; -use codex_protocol::models::ResponseItem; use codex_protocol::protocol::ENVIRONMENT_CONTEXT_CLOSE_TAG; use codex_protocol::protocol::ENVIRONMENT_CONTEXT_OPEN_TAG; diff --git a/codex-rs/core/src/instructions/mod.rs b/codex-rs/core/src/instructions/mod.rs index 0ef48ff620..5af9a6e890 100644 --- a/codex-rs/core/src/instructions/mod.rs +++ b/codex-rs/core/src/instructions/mod.rs @@ -1,3 +1,2 @@ -pub(crate) use codex_instructions::SkillInstructions; pub use codex_instructions::USER_INSTRUCTIONS_PREFIX; pub(crate) use codex_instructions::UserInstructions; diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 9ec8152bfd..4cd4e4e879 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -68,12 +68,11 @@ pub use utils::path_utils; pub mod personality_migration; pub mod plugins; pub(crate) mod mentions { - pub use crate::plugins::CollectedToolMentions; - pub use crate::plugins::build_connector_slug_counts; - pub use crate::plugins::build_skill_name_counts; - pub use crate::plugins::collect_explicit_app_ids; - pub use crate::plugins::collect_explicit_plugin_mentions; - pub use crate::plugins::collect_tool_mentions_from_messages; + pub(crate) use crate::plugins::build_connector_slug_counts; + pub(crate) use crate::plugins::build_skill_name_counts; + pub(crate) use crate::plugins::collect_explicit_app_ids; + pub(crate) use crate::plugins::collect_explicit_plugin_mentions; + pub(crate) use crate::plugins::collect_tool_mentions_from_messages; } mod sandbox_tags; pub mod sandboxing; diff --git a/codex-rs/core/src/plugins/manager.rs b/codex-rs/core/src/plugins/manager.rs index c732e94808..754dc1dbf5 100644 --- a/codex-rs/core/src/plugins/manager.rs +++ b/codex-rs/core/src/plugins/manager.rs @@ -405,6 +405,23 @@ impl PluginsManager { *cached_enabled_outcome = None; } + /// Resolve plugin skill roots for a config layer stack without touching the plugins cache. + pub fn effective_skill_roots_for_layer_stack( + &self, + config_layer_stack: &ConfigLayerStack, + plugins_feature_enabled: bool, + ) -> Vec { + if !plugins_feature_enabled { + return Vec::new(); + } + load_plugins_from_layer_stack( + config_layer_stack, + &self.store, + self.restriction_product, + ) + .effective_skill_roots() + } + fn cached_enabled_outcome(&self) -> Option { match self.cached_enabled_outcome.read() { Ok(cache) => cache.clone(), @@ -1171,7 +1188,7 @@ impl PluginUninstallError { fn log_plugin_load_errors(outcome: &PluginLoadOutcome) { for plugin in outcome - .plugins + .plugins() .iter() .filter(|plugin| plugin.error.is_some()) { diff --git a/codex-rs/core/src/plugins/mentions.rs b/codex-rs/core/src/plugins/mentions.rs index 37c8680261..e1f88d3144 100644 --- a/codex-rs/core/src/plugins/mentions.rs +++ b/codex-rs/core/src/plugins/mentions.rs @@ -1,13 +1,11 @@ use std::collections::HashMap; use std::collections::HashSet; -use std::path::PathBuf; use codex_protocol::user_input::UserInput; use crate::connectors; use crate::mention_syntax::PLUGIN_TEXT_MENTION_SIGIL; use crate::mention_syntax::TOOL_MENTION_SIGIL; -use crate::skills::SkillMetadata; use crate::skills::injection::ToolMentionKind; use crate::skills::injection::app_id_from_path; use crate::skills::injection::extract_tool_mentions_with_sigil; diff --git a/codex-rs/core/src/plugins/mod.rs b/codex-rs/core/src/plugins/mod.rs index 4501e44ac7..3e4902214c 100644 --- a/codex-rs/core/src/plugins/mod.rs +++ b/codex-rs/core/src/plugins/mod.rs @@ -62,7 +62,6 @@ pub(crate) use startup_sync::read_curated_plugins_sha; pub(crate) use startup_sync::sync_openai_plugins_repo; pub use toggles::collect_plugin_enabled_candidates; -pub(crate) use mentions::CollectedToolMentions; pub(crate) use mentions::build_connector_slug_counts; pub(crate) use mentions::build_skill_name_counts; pub(crate) use mentions::collect_explicit_app_ids; diff --git a/codex-rs/core/src/plugins/store.rs b/codex-rs/core/src/plugins/store.rs index e1a060c420..2020863809 100644 --- a/codex-rs/core/src/plugins/store.rs +++ b/codex-rs/core/src/plugins/store.rs @@ -1,7 +1,6 @@ use super::load_plugin_manifest; use codex_plugin::PLUGIN_MANIFEST_PATH; use codex_plugin::PluginId; -use codex_plugin::PluginIdError; use codex_plugin::validate_plugin_segment; use codex_utils_absolute_path::AbsolutePathBuf; use std::fs; diff --git a/codex-rs/core/src/skills.rs b/codex-rs/core/src/skills.rs index 2b2fe2025d..0447001dd0 100644 --- a/codex-rs/core/src/skills.rs +++ b/codex-rs/core/src/skills.rs @@ -27,6 +27,9 @@ pub use codex_core_skills::skills::config_rules; pub use codex_core_skills::skills::detect_implicit_skill_invocation_for_command; pub use codex_core_skills::skills::filter_skill_load_outcome_for_product; pub use codex_core_skills::skills::injection; +pub use codex_core_skills::skills::injection::SkillInjections; +pub use codex_core_skills::skills::injection::build_skill_injections; +pub use codex_core_skills::skills::injection::collect_explicit_skill_mentions; pub use codex_core_skills::skills::loader; pub use codex_core_skills::skills::manager; pub use codex_core_skills::skills::model; diff --git a/codex-rs/core/src/skills_watcher.rs b/codex-rs/core/src/skills_watcher.rs index fc0b86d90d..7e351a11f8 100644 --- a/codex-rs/core/src/skills_watcher.rs +++ b/codex-rs/core/src/skills_watcher.rs @@ -15,6 +15,7 @@ use crate::file_watcher::Receiver; use crate::file_watcher::ThrottledWatchReceiver; use crate::file_watcher::WatchPath; use crate::file_watcher::WatchRegistration; +use crate::plugins::PluginsManager; use crate::skills::SkillsManager; #[cfg(not(test))] @@ -56,9 +57,17 @@ impl SkillsWatcher { &self, config: &Config, skills_manager: &SkillsManager, + plugins_manager: &PluginsManager, ) -> WatchRegistration { + let plugin_outcome = plugins_manager.plugins_for_config(config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); let roots = skills_manager - .skill_roots_for_config(config) + .skill_roots_for_config( + config.cwd.as_path(), + &effective_skill_roots, + &config.config_layer_stack, + config.bundled_skills_enabled(), + ) .into_iter() .map(|root| WatchPath { path: root.path, diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index 6ce55c6229..ce9756fa4a 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -231,7 +231,6 @@ impl ThreadManager { let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager))); let skills_manager = Arc::new(SkillsManager::new_with_restriction_product( codex_home.clone(), - Arc::clone(&plugins_manager), config.bundled_skills_enabled(), restriction_product, )); @@ -297,7 +296,6 @@ impl ThreadManager { let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager))); let skills_manager = Arc::new(SkillsManager::new_with_restriction_product( codex_home.clone(), - Arc::clone(&plugins_manager), /*bundled_skills_enabled*/ true, restriction_product, )); @@ -835,9 +833,11 @@ impl ThreadManagerState { parent_trace: Option, user_shell_override: Option, ) -> CodexResult { - let watch_registration = self - .skills_watcher - .register_config(&config, self.skills_manager.as_ref()); + let watch_registration = self.skills_watcher.register_config( + &config, + self.skills_manager.as_ref(), + self.plugins_manager.as_ref(), + ); let CodexSpawnOk { codex, thread_id, .. } = Codex::spawn(CodexSpawnArgs { diff --git a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs index c9f448da74..429f79be8f 100644 --- a/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs +++ b/codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs @@ -486,27 +486,13 @@ impl CoreShellActionProvider { /// any skills. async fn find_skill(&self, program: &AbsolutePathBuf) -> Option { let force_reload = false; - let config_layer_stack = match load_config_layers_state( - &self.codex_home, - Some(cwd_abs), - &cli_overrides, - LoaderOverrides::default(), - CloudRequirementsLoader::default(), - ) - .await - { - Ok(config_layer_stack) => config_layer_stack, - Err(err) => { - return SkillLoadOutcome { - errors: vec![crate::skills::model::SkillError { - path: cwd.to_path_buf(), - message: err.to_string(), - }], - ..Default::default() - }; - } - }; - let effective_skill_roots = skills_manager.effective_skill_roots(&config_layer_stack); + let turn_config = self.turn.config.as_ref(); + let plugin_outcome = self + .session + .services + .plugins_manager + .plugins_for_config(turn_config); + let effective_skill_roots = plugin_outcome.effective_skill_roots(); let skills_outcome = self .session .services @@ -515,7 +501,7 @@ impl CoreShellActionProvider { &self.turn.cwd, &effective_skill_roots, force_reload, - &config_layer_stack, + &turn_config.config_layer_stack, ) .await;