diff --git a/crates/goose/src/config/extensions.rs b/crates/goose/src/config/extensions.rs index 10e9d5dc67bc..0528553b2da5 100644 --- a/crates/goose/src/config/extensions.rs +++ b/crates/goose/src/config/extensions.rs @@ -54,22 +54,22 @@ fn get_extensions_map() -> IndexMap { } } - if !extensions_map.is_empty() { - for (name, def) in PLATFORM_EXTENSIONS.iter() { - if !extensions_map.contains_key(*name) { - extensions_map.insert( - name.to_string(), - ExtensionEntry { - config: ExtensionConfig::Platform { - name: def.name.to_string(), - description: def.description.to_string(), - bundled: Some(true), - available_tools: Vec::new(), - }, - enabled: def.default_enabled, + // Always inject platform extensions (code_execution, todo, skills, etc.) + // These are internal agent extensions that should always be available + for (name, def) in PLATFORM_EXTENSIONS.iter() { + if !extensions_map.contains_key(*name) { + extensions_map.insert( + name.to_string(), + ExtensionEntry { + config: ExtensionConfig::Platform { + name: def.name.to_string(), + description: def.description.to_string(), + bundled: Some(true), + available_tools: Vec::new(), }, - ); - } + enabled: def.default_enabled, + }, + ); } } extensions_map diff --git a/ui/desktop/src/components/ConfigContext.tsx b/ui/desktop/src/components/ConfigContext.tsx index cee6a79b7c13..add93c0cc00a 100644 --- a/ui/desktop/src/components/ConfigContext.tsx +++ b/ui/desktop/src/components/ConfigContext.tsx @@ -10,6 +10,7 @@ import { providers, getProviderModels as apiGetProviderModels, } from '../api'; +import { syncBundledExtensions } from './settings/extensions'; import type { ConfigResponse, UpsertConfigQuery, @@ -218,7 +219,32 @@ export const ConfigProvider: React.FC = ({ children }) => { // Load extensions try { const extensionsResponse = await apiGetExtensions(); - setExtensionsList(extensionsResponse.data?.extensions || []); + let extensions = extensionsResponse.data?.extensions || []; + + // If no bundled MCP extensions exist, seed config from bundled-extensions.json + // This ensures fresh installs get the default extensions (developer, computercontroller, etc.) + // Platform extensions (code_execution, todo, etc.) are handled by the backend + const hasBundledExtensions = extensions.some( + (ext) => ext.type === 'builtin' && 'bundled' in ext && ext.bundled + ); + + if (!hasBundledExtensions) { + console.log('No bundled extensions found, syncing from bundled-extensions.json'); + const addExtensionForSync = async ( + name: string, + config: ExtensionConfig, + enabled: boolean + ) => { + const query: ExtensionQuery = { name, config, enabled }; + await apiAddExtension({ body: query }); + }; + await syncBundledExtensions(extensions, addExtensionForSync); + // Reload extensions after sync + const refreshedResponse = await apiGetExtensions(); + extensions = refreshedResponse.data?.extensions || []; + } + + setExtensionsList(extensions); setExtensionWarnings(extensionsResponse.data?.warnings || []); } catch (error) { console.error('Failed to load extensions:', error);