From 21da10e2ff354d7d91b07b2a3f1cbbc64b34e38d Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Mon, 20 Apr 2026 22:25:31 +1000 Subject: [PATCH 1/6] upgrade goose sdk and tui to be compatible with the latest agentclientprotocol/sdk package --- ui/goose2/src/shared/api/acpApi.ts | 12 ++++-------- ui/goose2/src/test/mocks/goose-sdk.ts | 4 ++++ ui/pnpm-lock.yaml | 19 +++++-------------- ui/sdk/package.json | 6 +++--- ui/sdk/src/goose-client.ts | 6 ++---- ui/text/package.json | 4 ++-- ui/text/src/configure.tsx | 11 ++++++++--- ui/text/src/tui.tsx | 6 +++--- 8 files changed, 31 insertions(+), 37 deletions(-) diff --git a/ui/goose2/src/shared/api/acpApi.ts b/ui/goose2/src/shared/api/acpApi.ts index f5bf706007b3..3927ab5aed39 100644 --- a/ui/goose2/src/shared/api/acpApi.ts +++ b/ui/goose2/src/shared/api/acpApi.ts @@ -34,17 +34,13 @@ export async function listProviders(): Promise { export async function listSessions(): Promise { const client = await getClient(); - // GooseClient.unstable_listSessions doesn't work with SDK 0.19 (renamed to listSessions). - // Bypass GooseClient and call the connection directly. Fix when ui/acp is updated. - // biome-ignore lint/suspicious/noExplicitAny: SDK doesn't expose conn property - const conn = (client as any).conn; - const response = await conn.listSessions({}); + const response = await client.listSessions({}); return response.sessions.map( (info: { sessionId: string; - title?: string; - updatedAt?: string; - _meta?: Record; + title?: string | null; + updatedAt?: string | null; + _meta?: Record | null; }) => ({ sessionId: info.sessionId, title: info.title ?? null, diff --git a/ui/goose2/src/test/mocks/goose-sdk.ts b/ui/goose2/src/test/mocks/goose-sdk.ts index 4dbdbaacb4ec..0be1cec09295 100644 --- a/ui/goose2/src/test/mocks/goose-sdk.ts +++ b/ui/goose2/src/test/mocks/goose-sdk.ts @@ -2,4 +2,8 @@ export class GooseClient { closed = Promise.resolve(); async initialize(..._args: unknown[]): Promise {} + + async listSessions(..._args: unknown[]): Promise<{ sessions: unknown[] }> { + return { sessions: [] }; + } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 73351962ed13..231b3dd8f9ce 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -653,8 +653,8 @@ importers: version: 3.25.76 devDependencies: '@agentclientprotocol/sdk': - specifier: ^0.14.1 - version: 0.14.1(zod@3.25.76) + specifier: ^0.19.0 + version: 0.19.0(zod@3.25.76) '@hey-api/openapi-ts': specifier: ^0.92.3 version: 0.92.4(magicast@0.5.2)(typescript@5.9.3) @@ -693,8 +693,8 @@ importers: specifier: workspace:* version: link:../sdk '@agentclientprotocol/sdk': - specifier: ^0.14.1 - version: 0.14.1(zod@4.3.6) + specifier: ^0.19.0 + version: 0.19.0(zod@4.3.6) '@inkjs/ui': specifier: ^2.0.0 version: 2.0.0(ink@6.8.0(@types/react@19.2.14)(react@19.2.4)) @@ -744,11 +744,6 @@ packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@agentclientprotocol/sdk@0.14.1': - resolution: {integrity: sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w==} - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - '@agentclientprotocol/sdk@0.19.0': resolution: {integrity: sha512-U9I8ws9WTOk6jCBAWpXefGSDgVXn14/kV6HFzwWGcstQ02mOQgClMAROHmoIn9GqZbDBDEOkdIbP4P4TEMQdug==} peerDependencies: @@ -8859,14 +8854,10 @@ snapshots: '@adobe/css-tools@4.4.4': {} - '@agentclientprotocol/sdk@0.14.1(zod@3.25.76)': + '@agentclientprotocol/sdk@0.19.0(zod@3.25.76)': dependencies: zod: 3.25.76 - '@agentclientprotocol/sdk@0.14.1(zod@4.3.6)': - dependencies: - zod: 4.3.6 - '@agentclientprotocol/sdk@0.19.0(zod@4.3.6)': dependencies: zod: 4.3.6 diff --git a/ui/sdk/package.json b/ui/sdk/package.json index d13dd63b77d1..21a6ae7f7474 100644 --- a/ui/sdk/package.json +++ b/ui/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose-sdk", - "version": "0.17.0", + "version": "0.18.0", "description": "Agent Client Protocol (ACP) SDK for Goose AI agent", "license": "Apache-2.0", "repository": { @@ -43,7 +43,7 @@ "zod": "^3.25.76" }, "peerDependencies": { - "@agentclientprotocol/sdk": "*" + "@agentclientprotocol/sdk": "^0.19.0" }, "optionalDependencies": { "@aaif/goose-binary-darwin-arm64": "workspace:*", @@ -53,7 +53,7 @@ "@aaif/goose-binary-win32-x64": "workspace:*" }, "devDependencies": { - "@agentclientprotocol/sdk": "^0.14.1", + "@agentclientprotocol/sdk": "^0.19.0", "@hey-api/openapi-ts": "^0.92.3", "@types/node": "^20.0.0", "prettier": "^3.8.1", diff --git a/ui/sdk/src/goose-client.ts b/ui/sdk/src/goose-client.ts index 306632838c01..c697dfccad0b 100644 --- a/ui/sdk/src/goose-client.ts +++ b/ui/sdk/src/goose-client.ts @@ -92,10 +92,8 @@ export class GooseClient { return this.conn.unstable_forkSession(params); } - unstable_listSessions( - params: ListSessionsRequest, - ): Promise { - return this.conn.unstable_listSessions(params); + listSessions(params: ListSessionsRequest): Promise { + return this.conn.listSessions(params); } unstable_resumeSession( diff --git a/ui/text/package.json b/ui/text/package.json index 6051330fb8b7..db1321524492 100644 --- a/ui/text/package.json +++ b/ui/text/package.json @@ -1,6 +1,6 @@ { "name": "@aaif/goose", - "version": "0.17.0", + "version": "0.18.0", "description": "Goose - an open-source AI agent", "license": "Apache-2.0", "repository": { @@ -28,7 +28,7 @@ }, "dependencies": { "@aaif/goose-sdk": "workspace:*", - "@agentclientprotocol/sdk": "^0.14.1", + "@agentclientprotocol/sdk": "^0.19.0", "@inkjs/ui": "^2.0.0", "ink": "^6.8.0", "ink-multiline-input": "^0.1.0", diff --git a/ui/text/src/configure.tsx b/ui/text/src/configure.tsx index 5e2d0b363e08..0874c11ddb56 100644 --- a/ui/text/src/configure.tsx +++ b/ui/text/src/configure.tsx @@ -432,10 +432,15 @@ export default function ConfigureScreen({ } await client.goose.GooseConfigUpsert({ key: "GOOSE_PROVIDER", value: provider.name }); await client.goose.GooseConfigUpsert({ key: "GOOSE_MODEL", value: model }); - await client.goose.GooseSessionProviderUpdate({ + await client.setSessionConfigOption({ sessionId, - provider: provider.name, - model, + configId: "provider", + value: provider.name, + }); + await client.setSessionConfigOption({ + sessionId, + configId: "model", + value: model, }); onComplete(); } catch (e: unknown) { diff --git a/ui/text/src/tui.tsx b/ui/text/src/tui.tsx index b82cf4c5b8e2..c2d4ad546b3d 100644 --- a/ui/text/src/tui.tsx +++ b/ui/text/src/tui.tsx @@ -14,7 +14,7 @@ import type { ToolCall, ToolCallUpdate, } from "@agentclientprotocol/sdk"; -import { ndJsonStream } from "@agentclientprotocol/sdk"; +import { PROTOCOL_VERSION, ndJsonStream } from "@agentclientprotocol/sdk"; import { GooseClient } from "@aaif/goose-sdk"; import { resolveGooseBinary } from "@aaif/goose-sdk/node"; import Onboarding from "./onboarding.js"; @@ -841,7 +841,7 @@ function App({ setStatus("handshaking…"); await client.initialize({ - protocolVersion: 0, + protocolVersion: PROTOCOL_VERSION, clientInfo: { name: "goose-text", version: "0.1.0" }, clientCapabilities: {}, }); @@ -1255,7 +1255,7 @@ async function runTextMode(serverConnection: Stream | string, prompt: string) { ); await client.initialize({ - protocolVersion: 0, + protocolVersion: PROTOCOL_VERSION, clientInfo: { name: "goose-text", version: "0.1.0" }, clientCapabilities: {}, }); From eb9a7d39344400dccd58b01b8f010800c001fc03 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Tue, 21 Apr 2026 17:45:03 +1000 Subject: [PATCH 2/6] cleanup --- ui/goose2/src/shared/api/acpApi.ts | 8 ++------ ui/goose2/src/test/mocks/goose-sdk.ts | 9 --------- ui/goose2/vitest.config.ts | 1 - 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 ui/goose2/src/test/mocks/goose-sdk.ts diff --git a/ui/goose2/src/shared/api/acpApi.ts b/ui/goose2/src/shared/api/acpApi.ts index 3927ab5aed39..d76f2f656d78 100644 --- a/ui/goose2/src/shared/api/acpApi.ts +++ b/ui/goose2/src/shared/api/acpApi.ts @@ -3,6 +3,7 @@ import type { NewSessionResponse, LoadSessionResponse, PromptResponse, + SessionInfo, } from "@agentclientprotocol/sdk"; import { getClient } from "./acpConnection"; import { perfLog } from "@/shared/lib/perfLog"; @@ -36,12 +37,7 @@ export async function listSessions(): Promise { const client = await getClient(); const response = await client.listSessions({}); return response.sessions.map( - (info: { - sessionId: string; - title?: string | null; - updatedAt?: string | null; - _meta?: Record | null; - }) => ({ + (info: SessionInfo) => ({ sessionId: info.sessionId, title: info.title ?? null, updatedAt: info.updatedAt ?? null, diff --git a/ui/goose2/src/test/mocks/goose-sdk.ts b/ui/goose2/src/test/mocks/goose-sdk.ts deleted file mode 100644 index 0be1cec09295..000000000000 --- a/ui/goose2/src/test/mocks/goose-sdk.ts +++ /dev/null @@ -1,9 +0,0 @@ -export class GooseClient { - closed = Promise.resolve(); - - async initialize(..._args: unknown[]): Promise {} - - async listSessions(..._args: unknown[]): Promise<{ sessions: unknown[] }> { - return { sessions: [] }; - } -} diff --git a/ui/goose2/vitest.config.ts b/ui/goose2/vitest.config.ts index 158d7a49f2b6..6f66312cda2a 100644 --- a/ui/goose2/vitest.config.ts +++ b/ui/goose2/vitest.config.ts @@ -7,7 +7,6 @@ export default defineConfig({ resolve: { alias: { "@": resolve(__dirname, "./src"), - "@aaif/goose-sdk": resolve(__dirname, "./src/test/mocks/goose-sdk.ts"), }, }, test: { From bd306827d19a0202362547d0f353ddf1d7f2842e Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Tue, 21 Apr 2026 18:49:14 +1000 Subject: [PATCH 3/6] fixed mismatch --- ui/text/src/configure.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/text/src/configure.tsx b/ui/text/src/configure.tsx index 0874c11ddb56..238134074b15 100644 --- a/ui/text/src/configure.tsx +++ b/ui/text/src/configure.tsx @@ -73,12 +73,10 @@ const ModelSelector = React.memo(function ModelSelector({ try { setLoading(true); setError(null); - const resp = await client.goose.GooseProvidersModels({ - providerName: provider.name, - }); + const knownModels = provider.knownModels?.map((model) => model.name) ?? []; if (!cancelled) { - setModels(resp.models); - const defaultIdx = resp.models.findIndex((m) => m === provider.defaultModel); + setModels(knownModels); + const defaultIdx = knownModels.findIndex((m) => m === provider.defaultModel); setSelectedIdx(defaultIdx >= 0 ? defaultIdx : 0); setLoading(false); clearTimeout(timeoutId); @@ -96,7 +94,7 @@ const ModelSelector = React.memo(function ModelSelector({ cancelled = true; clearTimeout(timeoutId); }; - }, [client, provider.name, provider.defaultModel]); + }, [provider.knownModels, provider.defaultModel]); const filtered = (() => { if (!searchQuery) return models; From cb4e5722bb1b3089ed3e84467a1449d61299cd5e Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Tue, 21 Apr 2026 19:36:19 +1000 Subject: [PATCH 4/6] fixed lint --- ui/goose2/src/shared/api/acpApi.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ui/goose2/src/shared/api/acpApi.ts b/ui/goose2/src/shared/api/acpApi.ts index d76f2f656d78..9a4da99a2327 100644 --- a/ui/goose2/src/shared/api/acpApi.ts +++ b/ui/goose2/src/shared/api/acpApi.ts @@ -36,14 +36,12 @@ export async function listProviders(): Promise { export async function listSessions(): Promise { const client = await getClient(); const response = await client.listSessions({}); - return response.sessions.map( - (info: SessionInfo) => ({ - sessionId: info.sessionId, - title: info.title ?? null, - updatedAt: info.updatedAt ?? null, - messageCount: (info._meta?.messageCount as number) ?? 0, - }), - ); + return response.sessions.map((info: SessionInfo) => ({ + sessionId: info.sessionId, + title: info.title ?? null, + updatedAt: info.updatedAt ?? null, + messageCount: (info._meta?.messageCount as number) ?? 0, + })); } export async function exportSession(sessionId: string): Promise { From ae32e96b1a3560830674de0aebe792001f417548 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Tue, 21 Apr 2026 19:55:31 +1000 Subject: [PATCH 5/6] address review comments --- ui/text/src/configure.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ui/text/src/configure.tsx b/ui/text/src/configure.tsx index 238134074b15..19cc031572bd 100644 --- a/ui/text/src/configure.tsx +++ b/ui/text/src/configure.tsx @@ -73,10 +73,21 @@ const ModelSelector = React.memo(function ModelSelector({ try { setLoading(true); setError(null); - const knownModels = provider.knownModels?.map((model) => model.name) ?? []; + const inventory = await client.goose.GooseProvidersInventory({ + providerIds: [provider.name], + }); + const entry = inventory.entries.find( + (inventoryEntry) => inventoryEntry.providerId === provider.name, + ); + const inventoryModels = entry?.models.map((m) => m.name) ?? []; + const metadataModels = provider.knownModels?.map((m) => m.name) ?? []; + const availableModels = + inventoryModels.length > 0 ? inventoryModels : metadataModels; if (!cancelled) { - setModels(knownModels); - const defaultIdx = knownModels.findIndex((m) => m === provider.defaultModel); + setModels(availableModels); + const defaultIdx = availableModels.findIndex( + (m) => m === provider.defaultModel, + ); setSelectedIdx(defaultIdx >= 0 ? defaultIdx : 0); setLoading(false); clearTimeout(timeoutId); @@ -94,7 +105,7 @@ const ModelSelector = React.memo(function ModelSelector({ cancelled = true; clearTimeout(timeoutId); }; - }, [provider.knownModels, provider.defaultModel]); + }, [client, provider.name, provider.knownModels, provider.defaultModel]); const filtered = (() => { if (!searchQuery) return models; From 78c96b4ae5980feadef8eea1136b092daa76e915 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Wed, 22 Apr 2026 12:11:01 +1000 Subject: [PATCH 6/6] fixed merge conflicts --- ui/text/src/configure.tsx | 58 ++++++++------------------------------- ui/text/src/tui.tsx | 24 ++++++++++++++++ 2 files changed, 36 insertions(+), 46 deletions(-) diff --git a/ui/text/src/configure.tsx b/ui/text/src/configure.tsx index 0f5b7aa8ddf8..07a4bd85360f 100644 --- a/ui/text/src/configure.tsx +++ b/ui/text/src/configure.tsx @@ -37,7 +37,6 @@ interface ConfigureProps { } interface ModelSelectorProps { - client: GooseClient; provider: ProviderInventoryEntryDto; height: number; onSelect: (model: string) => void; @@ -45,7 +44,6 @@ interface ModelSelectorProps { } const ModelSelector = React.memo(function ModelSelector({ - client, provider, height, onSelect, @@ -53,7 +51,6 @@ const ModelSelector = React.memo(function ModelSelector({ }: ModelSelectorProps) { const [loading, setLoading] = useState(true); const [models, setModels] = useState([]); - const [error, setError] = useState(null); const [selectedIdx, setSelectedIdx] = useState(0); const [searchQuery, setSearchQuery] = useState(""); const [manualEntry, setManualEntry] = useState(false); @@ -61,44 +58,12 @@ const ModelSelector = React.memo(function ModelSelector({ const columns = stdout?.columns ?? 80; useEffect(() => { - let cancelled = false; - const timeoutId = setTimeout(() => { - if (!cancelled) { - setError("Request timed out. The provider may be slow to respond."); - setLoading(false); - } - }, LOAD_MODELS_TIMEOUT_MS); - - (async () => { - try { - setLoading(true); - setError(null); - const resp = await client.goose.GooseProvidersModels({ - providerName: provider.providerId, - }); - if (!cancelled) { - setModels(availableModels); - const defaultIdx = availableModels.findIndex( - (m) => m === provider.defaultModel, - ); - setSelectedIdx(defaultIdx >= 0 ? defaultIdx : 0); - setLoading(false); - clearTimeout(timeoutId); - } - } catch (e: unknown) { - if (!cancelled) { - setError(e instanceof Error ? e.message : String(e)); - setLoading(false); - clearTimeout(timeoutId); - } - } - })(); - - return () => { - cancelled = true; - clearTimeout(timeoutId); - }; - }, [client, provider.providerId, provider.defaultModel]); + const availableModels = provider.models.map((model) => model.id); + setModels(availableModels); + const defaultIdx = availableModels.findIndex((model) => model === provider.defaultModel); + setSelectedIdx(defaultIdx >= 0 ? defaultIdx : 0); + setLoading(false); + }, [provider.models, provider.defaultModel]); const filtered = (() => { if (!searchQuery) return models; @@ -201,7 +166,7 @@ const ModelSelector = React.memo(function ModelSelector({ ); } - if (error) { + if (models.length === 0) { return ( @@ -209,11 +174,13 @@ const ModelSelector = React.memo(function ModelSelector({ ◆ Select model ◆ - ⚠ Failed to load models + ⚠ No models available - {error} + + This provider does not currently expose any models in inventory. + @@ -444,7 +411,7 @@ export default function ConfigureScreen({ await client.setSessionConfigOption({ sessionId, configId: "provider", - value: provider.name, + value: provider.providerId, }); await client.setSessionConfigOption({ sessionId, @@ -551,7 +518,6 @@ export default function ConfigureScreen({ if (phase === "select_model" && selectedProvider) { return ( ({ + requestPermission: async ( + params: RequestPermissionRequest, + ): Promise => { + const optionId = params.options?.[0]?.optionId ?? "approve"; + return { + outcome: { + outcome: "selected", + optionId, + }, + }; + }, sessionUpdate: async (params: SessionNotification) => { const update = params.update; if (update.sessionUpdate === "agent_message_chunk") { @@ -1218,6 +1231,17 @@ async function runTextMode(serverConnection: Stream | string, prompt: string) { try { const client = new GooseClient( () => ({ + requestPermission: async ( + params: RequestPermissionRequest, + ): Promise => { + const optionId = params.options?.[0]?.optionId ?? "approve"; + return { + outcome: { + outcome: "selected", + optionId, + }, + }; + }, sessionUpdate: async (params: SessionNotification) => { const update = params.update; if (update.sessionUpdate === "agent_message_chunk") {