Clean up obsolete extension paths before redesigning the extension manager (#11645)

This commit is contained in:
Douwe Osinga
2026-08-28 15:10:23 +00:00
committed by GitHub
parent dd8f5ed5c7
commit 2f5ff48b56
63 changed files with 245 additions and 2115 deletions
-4
View File
@@ -144,10 +144,6 @@ export function extensionConfigToGooseExtension(config: ExtensionConfig): GooseE
bundled: config.bundled,
available_tools: availableToolsOrUndefined(config.available_tools),
};
case 'sse':
case 'frontend':
case 'inline_python':
return null;
}
}
@@ -29,7 +29,7 @@ const i18n = defineMessages({
});
interface ExtensionConfigFieldsProps {
type: 'stdio' | 'sse' | 'streamable_http' | 'builtin';
type: 'stdio' | 'streamable_http' | 'builtin';
full_cmd: string;
endpoint: string;
onChange: (key: string, value: string) => void;
@@ -51,7 +51,9 @@ export default function ExtensionConfigFields({
return (
<div className="space-y-4">
<div>
<label className="text-sm font-medium mb-2 block text-text-primary">{intl.formatMessage(i18n.commandLabel)}</label>
<label className="text-sm font-medium mb-2 block text-text-primary">
{intl.formatMessage(i18n.commandLabel)}
</label>
<div className="relative">
<Input
value={full_cmd}
@@ -60,7 +62,9 @@ export default function ExtensionConfigFields({
className={`w-full ${!submitAttempted || isValid ? 'border-border-primary' : 'border-red-500'} text-text-primary`}
/>
{submitAttempted && !isValid && (
<div className="absolute text-xs text-red-500 mt-1">{intl.formatMessage(i18n.commandRequired)}</div>
<div className="absolute text-xs text-red-500 mt-1">
{intl.formatMessage(i18n.commandRequired)}
</div>
)}
</div>
</div>
@@ -69,7 +73,9 @@ export default function ExtensionConfigFields({
} else {
return (
<div>
<label className="text-sm font-medium mb-2 block text-text-primary">{intl.formatMessage(i18n.endpointLabel)}</label>
<label className="text-sm font-medium mb-2 block text-text-primary">
{intl.formatMessage(i18n.endpointLabel)}
</label>
<div className="relative">
<Input
value={endpoint}
@@ -78,7 +84,9 @@ export default function ExtensionConfigFields({
className={`w-full ${!submitAttempted || isValid ? 'border-border-primary' : 'border-red-500'} text-text-primary`}
/>
{submitAttempted && !isValid && (
<div className="absolute text-xs text-red-500 mt-1">{intl.formatMessage(i18n.endpointRequired)}</div>
<div className="absolute text-xs text-red-500 mt-1">
{intl.formatMessage(i18n.endpointRequired)}
</div>
)}
</div>
</div>
@@ -27,10 +27,6 @@ const i18n = defineMessages({
id: 'extensionInfoFields.typeHttp',
defaultMessage: 'HTTP',
},
typeSseUnsupported: {
id: 'extensionInfoFields.typeSseUnsupported',
defaultMessage: 'SSE (unsupported)',
},
typeStandardIo: {
id: 'extensionInfoFields.typeStandardIo',
defaultMessage: 'Standard IO (STDIO)',
@@ -51,7 +47,7 @@ const i18n = defineMessages({
interface ExtensionInfoFieldsProps {
name: string;
type: 'stdio' | 'sse' | 'streamable_http' | 'builtin';
type: 'stdio' | 'streamable_http' | 'builtin';
description: string;
onChange: (key: string, value: string) => void;
submitAttempted: boolean;
@@ -75,7 +71,9 @@ export default function ExtensionInfoFields({
{/* Top row with Name and Type side by side */}
<div className="flex justify-between gap-4">
<div className="flex-1">
<label className="text-sm font-medium mb-2 block text-text-primary">{intl.formatMessage(i18n.extensionName)}</label>
<label className="text-sm font-medium mb-2 block text-text-primary">
{intl.formatMessage(i18n.extensionName)}
</label>
<div className="relative">
<Input
value={name}
@@ -84,14 +82,18 @@ export default function ExtensionInfoFields({
className={`${!submitAttempted || isNameValid() ? 'border-border-primary' : 'border-red-500'} text-text-primary focus:border-border-primary`}
/>
{submitAttempted && !isNameValid() && (
<div className="absolute text-xs text-red-500 mt-1">{intl.formatMessage(i18n.nameRequired)}</div>
<div className="absolute text-xs text-red-500 mt-1">
{intl.formatMessage(i18n.nameRequired)}
</div>
)}
</div>
</div>
{/* Type Dropdown */}
<div className="w-[200px]">
<label className="text-sm font-medium mb-2 block text-text-primary">{intl.formatMessage(i18n.typeLabel)}</label>
<label className="text-sm font-medium mb-2 block text-text-primary">
{intl.formatMessage(i18n.typeLabel)}
</label>
<Select
value={{
value: type,
@@ -100,9 +102,7 @@ export default function ExtensionInfoFields({
? intl.formatMessage(i18n.typeStdio)
: type === 'streamable_http'
? intl.formatMessage(i18n.typeHttp)
: type === 'sse'
? intl.formatMessage(i18n.typeSseUnsupported)
: type.toUpperCase(),
: type.toUpperCase(),
}}
onChange={(newValue: unknown) => {
const option = newValue as { value: string; label: string } | null;
@@ -121,7 +121,9 @@ export default function ExtensionInfoFields({
{/* Bottom row with Description spanning full width */}
<div className="w-full">
<label className="text-sm font-medium mb-2 block text-text-primary">{intl.formatMessage(i18n.descriptionLabel)}</label>
<label className="text-sm font-medium mb-2 block text-text-primary">
{intl.formatMessage(i18n.descriptionLabel)}
</label>
<div className="relative">
<Input
value={description}
@@ -50,7 +50,8 @@ const i18n = defineMessages({
},
unsavedChangesMessage: {
id: 'extensionModal.unsavedChangesMessage',
defaultMessage: 'You have unsaved changes to the extension configuration. Are you sure you want to close without saving?',
defaultMessage:
'You have unsaved changes to the extension configuration. Are you sure you want to close without saving?',
},
closeWithoutSaving: {
id: 'extensionModal.closeWithoutSaving',
@@ -98,7 +99,6 @@ export default function ExtensionModal({
// Check if command/endpoint has changed
const commandChanged =
(formData.type === 'stdio' && formData.cmd !== initialData.cmd) ||
(formData.type === 'sse' && formData.endpoint !== initialData.endpoint) ||
(formData.type === 'streamable_http' && formData.endpoint !== initialData.endpoint);
// Check if headers have changed
@@ -271,7 +271,6 @@ export default function ExtensionModal({
const isConfigValid = () => {
return (
(formData.type === 'stdio' && !!formData.cmd && formData.cmd.trim() !== '') ||
(formData.type === 'sse' && !!formData.endpoint && formData.endpoint.trim() !== '') ||
(formData.type === 'streamable_http' &&
!!formData.endpoint &&
formData.endpoint.trim() !== '')
@@ -373,7 +372,9 @@ export default function ExtensionModal({
};
// Update title based on current state
const modalTitle = showDeleteConfirmation ? intl.formatMessage(i18n.deleteExtensionTitle, { name: formData.name }) : title;
const modalTitle = showDeleteConfirmation
? intl.formatMessage(i18n.deleteExtensionTitle, { name: formData.name })
: title;
return (
<>
@@ -385,17 +386,13 @@ export default function ExtensionModal({
{modalTitle}
</DialogTitle>
{showDeleteConfirmation && (
<DialogDescription>
{intl.formatMessage(i18n.deleteDescription)}
</DialogDescription>
<DialogDescription>{intl.formatMessage(i18n.deleteDescription)}</DialogDescription>
)}
</DialogHeader>
{showDeleteConfirmation ? (
<div className="py-4">
<p className="text-text-primary">
{intl.formatMessage(i18n.deleteDescription)}
</p>
<p className="text-text-primary">{intl.formatMessage(i18n.deleteDescription)}</p>
</div>
) : (
<div className="py-4 space-y-6">
@@ -152,11 +152,9 @@ export function getSubtitle(config: ExtensionConfig) {
command: null,
};
}
case 'sse':
case 'streamable_http': {
const label = config.type === 'sse' ? 'SSE' : 'HTTP';
return {
description: config.description ? `${label}: ${config.description}` : `${label} extension`,
description: config.description ? `HTTP: ${config.description}` : 'HTTP extension',
command: config.uri || null,
};
}
@@ -402,29 +402,6 @@ describe('Extension Utils', () => {
});
});
it('should create sse extension config', () => {
const formData = {
name: 'test-sse',
description: 'Test SSE extension',
type: 'sse' as const,
cmd: '',
endpoint: 'http://api.example.com/sse',
enabled: true,
timeout: 300,
envVars: [],
headers: [],
};
const config = createExtensionConfig(formData);
expect(config).toEqual({
type: 'sse',
name: 'test-sse',
description: 'Test SSE extension',
uri: 'http://api.example.com/sse',
});
});
it('should preserve available tools metadata', () => {
const formData = {
name: 'developer',
@@ -21,7 +21,7 @@ export function nameToKey(name: string): string {
export interface ExtensionFormData {
name: string;
description: string;
type: 'stdio' | 'sse' | 'streamable_http' | 'builtin';
type: 'stdio' | 'streamable_http' | 'builtin';
cmd?: string;
endpoint?: string;
enabled: boolean;
@@ -109,27 +109,18 @@ export function extensionToFormData(extension: FixedExtensionEntry): ExtensionFo
return {
name: extension.name || '',
description: extension.description || '',
type:
extension.type === 'frontend' ||
extension.type === 'inline_python' ||
extension.type === 'platform'
? 'stdio'
: extension.type,
type: extension.type === 'platform' ? 'stdio' : extension.type,
cmd:
extension.type === 'stdio'
? combineCmdAndArgs(extension.cmd, extension.args ?? [])
: undefined,
endpoint:
extension.type === 'streamable_http' || extension.type === 'sse'
? (extension.uri ?? undefined)
: undefined,
endpoint: extension.type === 'streamable_http' ? (extension.uri ?? undefined) : undefined,
enabled: extension.enabled,
timeout: 'timeout' in extension ? (extension.timeout ?? undefined) : undefined,
envVars,
headers,
installation_notes: (extension as Record<string, unknown>)['installation_notes'] as
| string
| undefined,
string | undefined,
...(availableTools ? { available_tools: availableTools } : {}),
...(extension.type === 'streamable_http'
? {
@@ -197,22 +188,15 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
: {}),
...(formData.scopes?.length ? { scopes: formData.scopes } : {}),
};
} else if (formData.type === 'builtin') {
return {
type: formData.type,
name: formData.name,
description: formData.description,
timeout: formData.timeout,
...availableToolsConfig(formData.available_tools),
};
} else {
return {
type: formData.type,
name: formData.name,
description: formData.description,
uri: formData.endpoint || '',
};
}
return {
type: formData.type,
name: formData.name,
description: formData.description,
timeout: formData.timeout,
...availableToolsConfig(formData.available_tools),
};
}
function isWindowsPlatform(): boolean {
-3
View File
@@ -1053,9 +1053,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Typ"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (nicht unterstützt)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1061,9 +1061,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Type"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (unsupported)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Tipo"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (no compatible)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Type"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (non pris en charge)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Entrée/sortie standard (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "प्रकार"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (असमर्थित)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "मानक आईओ (एसटीडीआईओ)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Tipe"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (tidak didukung)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Tipo"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (non supportato)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "タイプ"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE(未対応)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "標準入出力(STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "유형"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (미지원)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "표준 IO(STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Jenis"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (tidak disokong)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Tipo"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (não suportado)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Тип"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (не поддерживается)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Стандартный ввод-вывод (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Tür"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (desteklenmiyor)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standart IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "Loại"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE (không được hỗ trợ)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "Standard IO (STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "类型"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE(不支持)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "标准输入输出(STDIO)"
},
-3
View File
@@ -1054,9 +1054,6 @@
"extensionInfoFields.typeLabel": {
"defaultMessage": "類型"
},
"extensionInfoFields.typeSseUnsupported": {
"defaultMessage": "SSE(不支援)"
},
"extensionInfoFields.typeStandardIo": {
"defaultMessage": "標準 IO (STDIO)"
},
+1 -44
View File
@@ -2,50 +2,7 @@ import type { RecipeExtensionDto } from '@aaif/goose-sdk';
export type Envs = Record<string, string>;
type LegacySseExtensionConfig = {
description?: string | null;
name: string;
type: 'sse';
uri?: string | null;
};
type FrontendTool = {
_meta?: Record<string, unknown>;
annotations?: Record<string, unknown>;
description?: string;
execution?: Record<string, unknown>;
icons?: unknown[];
inputSchema: Record<string, unknown>;
name: string;
outputSchema?: Record<string, unknown>;
title?: string;
};
type FrontendExtensionConfig = {
available_tools?: string[] | null;
bundled?: boolean | null;
description?: string | null;
instructions?: string | null;
name: string;
tools: FrontendTool[];
type: 'frontend';
};
type InlinePythonExtensionConfig = {
available_tools?: string[] | null;
code: string;
dependencies?: string[] | null;
description?: string | null;
name: string;
timeout?: number | null;
type: 'inline_python';
};
export type ExtensionConfig =
| RecipeExtensionDto
| LegacySseExtensionConfig
| FrontendExtensionConfig
| InlinePythonExtensionConfig;
export type ExtensionConfig = RecipeExtensionDto;
export type ExtensionEntry = ExtensionConfig & {
enabled: boolean;
+1 -10
View File
@@ -94,10 +94,7 @@ type ContentIcon = {
};
export type SystemNotificationType =
| 'thinkingMessage'
| 'progressMessage'
| 'inlineMessage'
| 'creditsExhausted';
'thinkingMessage' | 'progressMessage' | 'inlineMessage' | 'creditsExhausted';
export type SystemNotificationContent = {
data?: unknown;
@@ -131,11 +128,6 @@ export type ActionRequiredData =
user_data: unknown;
};
export type FrontendToolRequest = {
id: string;
toolCall: JsonObject;
};
export type ThinkingContent = {
signature: string;
thinking: string;
@@ -203,7 +195,6 @@ export type MessageContent =
| (ToolResponse & { type: 'toolResponse' })
| (ToolConfirmationRequest & { type: 'toolConfirmationRequest' })
| (ActionRequired & { type: 'actionRequired' })
| (FrontendToolRequest & { type: 'frontendToolRequest' })
| (ThinkingContent & { type: 'thinking' })
| (RedactedThinkingContent & { type: 'redactedThinking' })
| (SystemNotificationContent & { type: 'systemNotification' });