From 58d3431bf50f581d97bf8667e73ada64226ff45f Mon Sep 17 00:00:00 2001 From: fre$h Date: Fri, 13 Feb 2026 15:30:26 -0500 Subject: [PATCH] fix: ignore deprecated skills extension (#7139) Signed-off-by: anonwurcod Co-authored-by: Warp --- crates/goose/src/config/extensions.rs | 60 +++++++++++++++++++--- crates/goose/src/session/extension_data.rs | 40 +++++++++++++++ 2 files changed, 92 insertions(+), 8 deletions(-) diff --git a/crates/goose/src/config/extensions.rs b/crates/goose/src/config/extensions.rs index bd872fd394..2f51c412c8 100644 --- a/crates/goose/src/config/extensions.rs +++ b/crates/goose/src/config/extensions.rs @@ -1,4 +1,5 @@ use super::base::Config; +use crate::agents::extension::PLATFORM_EXTENSIONS; use crate::agents::ExtensionConfig; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; @@ -31,6 +32,15 @@ pub fn name_to_key(name: &str) -> String { result.to_lowercase() } +pub(crate) fn is_extension_available(config: &ExtensionConfig) -> bool { + match config { + ExtensionConfig::Platform { name, .. } => { + PLATFORM_EXTENSIONS.contains_key(name_to_key(name).as_str()) + } + _ => true, + } +} + fn get_extensions_map_with_config(config: &Config) -> IndexMap { let raw: Mapping = config .get_param(EXTENSIONS_CONFIG_KEY) @@ -46,6 +56,9 @@ fn get_extensions_map_with_config(config: &Config) -> IndexMap(v)) { (serde_yaml::Value::String(key), Ok(entry)) => { + if !is_extension_available(&entry.config) { + continue; + } extensions_map.insert(key, entry); } (k, v) => { @@ -158,13 +171,44 @@ pub fn resolve_extensions_for_new_session( recipe_extensions: Option<&[ExtensionConfig]>, override_extensions: Option>, ) -> Vec { - if let Some(exts) = recipe_extensions { - return exts.to_vec(); - } + let extensions = if let Some(exts) = recipe_extensions { + exts.to_vec() + } else if let Some(exts) = override_extensions { + exts + } else { + get_enabled_extensions() + }; - if let Some(exts) = override_extensions { - return exts; - } - - get_enabled_extensions() + extensions + .into_iter() + .filter(is_extension_available) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_extension_available_filters_unknown_platform() { + let unknown_platform = ExtensionConfig::Platform { + name: "definitely_not_real_platform_extension".to_string(), + description: "unknown".to_string(), + display_name: None, + bundled: None, + available_tools: Vec::new(), + }; + + let builtin = ExtensionConfig::Builtin { + name: "developer".to_string(), + description: "".to_string(), + display_name: Some("Developer".to_string()), + timeout: None, + bundled: None, + available_tools: Vec::new(), + }; + + assert!(!is_extension_available(&unknown_platform)); + assert!(is_extension_available(&builtin)); + } } diff --git a/crates/goose/src/session/extension_data.rs b/crates/goose/src/session/extension_data.rs index 243004c3c7..b286a3e220 100644 --- a/crates/goose/src/session/extension_data.rs +++ b/crates/goose/src/session/extension_data.rs @@ -2,6 +2,7 @@ // Provides a simple way to store extension-specific data with versioned keys use crate::config::base::Config; +use crate::config::extensions::is_extension_available; use crate::config::ExtensionConfig; use crate::session::SessionManager; use anyhow::Result; @@ -114,6 +115,12 @@ impl EnabledExtensionsState { Self { extensions } } + pub fn from_extension_data(extension_data: &ExtensionData) -> Option { + let mut state = ::from_extension_data(extension_data)?; + state.extensions.retain(is_extension_available); + Some(state) + } + pub fn extensions_or_default( extension_data: Option<&ExtensionData>, config: &Config, @@ -259,4 +266,37 @@ mod tests { Some(&json!({"key": "value"})) ); } + + #[test] + fn test_enabled_extensions_state_filters_unavailable_platform() { + let mut extension_data = ExtensionData::new(); + let state = EnabledExtensionsState::new(vec![ + ExtensionConfig::Platform { + name: "definitely_not_real_platform_extension".to_string(), + description: "unknown".to_string(), + display_name: None, + bundled: None, + available_tools: Vec::new(), + }, + ExtensionConfig::Builtin { + name: "developer".to_string(), + description: "".to_string(), + display_name: Some("Developer".to_string()), + timeout: None, + bundled: None, + available_tools: Vec::new(), + }, + ]); + + state.to_extension_data(&mut extension_data).unwrap(); + + let loaded = + EnabledExtensionsState::from_extension_data(&extension_data).expect("state present"); + let names: Vec = loaded.extensions.iter().map(|ext| ext.name()).collect(); + + assert!(names.iter().any(|name| name == "developer")); + assert!(!names + .iter() + .any(|name| name == "definitely_not_real_platform_extension")); + } }