import type { ContentBlock, NewSessionResponse, LoadSessionResponse, PromptResponse, } from "@agentclientprotocol/sdk"; import { getClient } from "./acpConnection"; import { perfLog } from "@/shared/lib/perfLog"; export interface AcpProvider { id: string; label: string; } export interface AcpSessionInfo { sessionId: string; title: string | null; updatedAt: string | null; messageCount: number; } const DEPRECATED_PROVIDER_IDS = new Set(["claude-code", "codex", "gemini-cli"]); const DEFAULT_PROVIDER: AcpProvider = { id: "goose", label: "Goose (Default)", }; export async function listProviders(): Promise { const client = await getClient(); const result = await client.goose.GooseProvidersList({ providerIds: [], }); const providers = result.entries .filter((entry) => !DEPRECATED_PROVIDER_IDS.has(entry.providerId)) .map((entry) => ({ id: entry.providerId, label: entry.providerName, })); return [DEFAULT_PROVIDER, ...providers]; } 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({}); return response.sessions.map( (info: { sessionId: string; title?: string; updatedAt?: string; _meta?: Record; }) => ({ 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 { const client = await getClient(); const result = await client.goose.GooseSessionExport({ sessionId }); // biome-ignore lint/suspicious/noExplicitAny: SDK doesn't expose data field on export result return (result as any).data; } export async function importSession(json: string): Promise { const client = await getClient(); const result = await client.goose.GooseSessionImport({ data: json }); return result as unknown as AcpSessionInfo; } export async function forkSession(sessionId: string): Promise { const client = await getClient(); const response = await client.unstable_forkSession({ sessionId, cwd: "~/.goose/artifacts", }); return { sessionId: response.sessionId, title: (response._meta?.title as string) ?? null, updatedAt: null, messageCount: (response._meta?.messageCount as number) ?? 0, }; } export async function setModel( sessionId: string, modelId: string, ): Promise { const sid = sessionId.slice(0, 8); const tClient = performance.now(); const client = await getClient(); const tCall = performance.now(); await client.setSessionConfigOption({ sessionId, configId: "model", value: modelId, }); perfLog( `[perf:api] ${sid} setModel(${modelId}) getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`, ); } export async function setProvider( sessionId: string, providerId: string, ): Promise { const sid = sessionId.slice(0, 8); const tClient = performance.now(); const client = await getClient(); const tCall = performance.now(); await client.setSessionConfigOption({ sessionId, configId: "provider", value: providerId, }); perfLog( `[perf:api] ${sid} setProvider(${providerId}) getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`, ); } export async function updateWorkingDir( sessionId: string, workingDir: string, ): Promise { const client = await getClient(); await client.extMethod("goose/working_dir/update", { sessionId, workingDir }); } export async function cancelSession(sessionId: string): Promise { const client = await getClient(); await client.cancel({ sessionId }); } export async function newSession( workingDir: string, providerId?: string, ): Promise { const tClient = performance.now(); const client = await getClient(); const request: Parameters[0] & { meta?: Record; } = { cwd: workingDir, mcpServers: [], }; if (providerId) { request.meta = { provider: providerId }; } const tCall = performance.now(); const response = await client.newSession(request); const sid = response.sessionId.slice(0, 8); perfLog( `[perf:api] ${sid} newSession getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`, ); return response; } export async function loadSession( sessionId: string, workingDir: string, ): Promise { const sid = sessionId.slice(0, 8); const tClient = performance.now(); const client = await getClient(); const tCall = performance.now(); const response = await client.loadSession({ sessionId, cwd: workingDir, mcpServers: [], }); perfLog( `[perf:api] ${sid} loadSession getClient=${(tCall - tClient).toFixed(1)}ms wire=${(performance.now() - tCall).toFixed(1)}ms`, ); return response; } export async function prompt( sessionId: string, content: ContentBlock[], ): Promise { const client = await getClient(); return client.prompt({ sessionId, prompt: content }); }