8a51e34891
Signed-off-by: Bradley Axen <baxen@squareup.com>
129 lines
3.4 KiB
TypeScript
129 lines
3.4 KiB
TypeScript
import { invoke } from "@tauri-apps/api/core";
|
|
import { GooseClient } from "@aaif/goose-sdk";
|
|
import {
|
|
PROTOCOL_VERSION,
|
|
type Client,
|
|
type SessionNotification,
|
|
type RequestPermissionRequest,
|
|
type RequestPermissionResponse,
|
|
} from "@agentclientprotocol/sdk";
|
|
import { createWebSocketStream } from "./createWebSocketStream";
|
|
import { perfLog } from "@/shared/lib/perfLog";
|
|
|
|
let notificationHandler: AcpNotificationHandler | null = null;
|
|
|
|
export interface AcpNotificationHandler {
|
|
handleSessionNotification(notification: SessionNotification): Promise<void>;
|
|
}
|
|
|
|
export function setNotificationHandler(handler: AcpNotificationHandler): void {
|
|
notificationHandler = handler;
|
|
}
|
|
|
|
let clientPromise: Promise<GooseClient> | null = null;
|
|
let resolvedClient: GooseClient | null = null;
|
|
|
|
function createClientCallbacks(): () => Client {
|
|
return () => ({
|
|
requestPermission: async (
|
|
args: RequestPermissionRequest,
|
|
): Promise<RequestPermissionResponse> => {
|
|
const optionId = args.options?.[0]?.optionId ?? "approve";
|
|
return {
|
|
outcome: {
|
|
outcome: "selected",
|
|
optionId,
|
|
},
|
|
};
|
|
},
|
|
|
|
sessionUpdate: async (notification: SessionNotification): Promise<void> => {
|
|
if (notificationHandler) {
|
|
await notificationHandler.handleSessionNotification(notification);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
|
|
function monitorConnection(client: GooseClient): void {
|
|
client.closed
|
|
.then(() => {
|
|
console.warn(
|
|
"[acp] Connection closed. Will reconnect on next getClient().",
|
|
);
|
|
resolvedClient = null;
|
|
clientPromise = null;
|
|
})
|
|
.catch(() => {
|
|
console.warn(
|
|
"[acp] Connection error. Will reconnect on next getClient().",
|
|
);
|
|
resolvedClient = null;
|
|
clientPromise = null;
|
|
});
|
|
}
|
|
|
|
async function initializeConnection(): Promise<GooseClient> {
|
|
const tStart = performance.now();
|
|
const wsUrl: string = await invoke("get_goose_serve_url");
|
|
perfLog(
|
|
`[perf:conn] get_goose_serve_url in ${(performance.now() - tStart).toFixed(1)}ms`,
|
|
);
|
|
|
|
const tStream = performance.now();
|
|
const stream = createWebSocketStream(wsUrl);
|
|
|
|
const client = new GooseClient(createClientCallbacks(), stream);
|
|
perfLog(
|
|
`[perf:conn] ws stream + client created in ${(performance.now() - tStream).toFixed(1)}ms`,
|
|
);
|
|
|
|
const tInit = performance.now();
|
|
await client.initialize({
|
|
protocolVersion: PROTOCOL_VERSION,
|
|
clientCapabilities: {},
|
|
clientInfo: {
|
|
name: "goose2",
|
|
version: "0.1.0",
|
|
},
|
|
});
|
|
perfLog(
|
|
`[perf:conn] client.initialize in ${(performance.now() - tInit).toFixed(1)}ms (total ${(performance.now() - tStart).toFixed(1)}ms)`,
|
|
);
|
|
|
|
monitorConnection(client);
|
|
|
|
return client;
|
|
}
|
|
|
|
export async function getClient(): Promise<GooseClient> {
|
|
if (resolvedClient) {
|
|
return resolvedClient;
|
|
}
|
|
|
|
if (!clientPromise) {
|
|
perfLog("[perf:conn] getClient() → initializing new ACP connection");
|
|
clientPromise = initializeConnection()
|
|
.then((client) => {
|
|
resolvedClient = client;
|
|
return client;
|
|
})
|
|
.catch((error) => {
|
|
clientPromise = null;
|
|
throw error;
|
|
});
|
|
} else {
|
|
perfLog("[perf:conn] getClient() awaiting in-flight initializeConnection");
|
|
}
|
|
|
|
return clientPromise;
|
|
}
|
|
|
|
export function isClientReady(): boolean {
|
|
return resolvedClient !== null;
|
|
}
|
|
|
|
export function getClientSync(): GooseClient | null {
|
|
return resolvedClient;
|
|
}
|