diff --git a/src/features/chat/panel/composition.ts b/src/features/chat/panel/composition.ts index bc40ecf8..980c7b20 100644 --- a/src/features/chat/panel/composition.ts +++ b/src/features/chat/panel/composition.ts @@ -13,7 +13,7 @@ import type { ToolbarPanelController } from "./toolbar-controller"; import type { ChatConnectionController } from "../session/connection-controller"; import type { ChatReconnectActions } from "../session/reconnect-actions"; import type { PendingRequestController } from "../requests/pending-request-controller"; -import { createServerRequestActions } from "../requests/server-request-actions"; +import { rejectServerRequest, respondToServerRequest } from "../requests/server-request-responder"; import type { ComposerSubmissionActions } from "../turns/composer-submission-actions"; import type { RestoredThreadController } from "../threads/restored-thread-controller"; import type { ThreadIdentityActions } from "../threads/thread-identity-actions"; @@ -105,14 +105,15 @@ export function createChatViewControllers(ports: ChatControllerCompositionPorts) connection, goals, }); - const serverRequestResponder = createServerRequestActions({ + const serverRequestHost = { currentClient: ports.client.getClient, - }); + }; const controller = createChatInboundController(ports, { serverMetadata, serverDiagnostics, threadRename, - serverRequestResponder, + respondToServerRequest: (requestId, result) => respondToServerRequest(serverRequestHost, requestId, result), + rejectServerRequest: (requestId, code, message) => rejectServerRequest(serverRequestHost, requestId, code, message), }); const { connectionController } = createChatConnectionControllers(ports, { connection, diff --git a/src/features/chat/requests/server-request-actions.ts b/src/features/chat/requests/server-request-actions.ts deleted file mode 100644 index 1bdd1370..00000000 --- a/src/features/chat/requests/server-request-actions.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { AppServerClient } from "../../../app-server/client"; - -type RespondRequestId = Parameters[0]; -type RejectRequestId = Parameters[0]; - -export interface ServerRequestActionsHost { - currentClient: () => AppServerClient | null; -} - -export interface ServerRequestActions { - respond: (requestId: RespondRequestId, result: unknown) => boolean; - reject: (requestId: RejectRequestId, code: number, message: string) => boolean; -} - -export function createServerRequestActions(host: ServerRequestActionsHost): ServerRequestActions { - return { - respond: (requestId, result) => respondToServerRequest(host, requestId, result), - reject: (requestId, code, message) => rejectServerRequest(host, requestId, code, message), - }; -} - -function respondToServerRequest(host: ServerRequestActionsHost, requestId: RespondRequestId, result: unknown): boolean { - try { - const client = host.currentClient(); - client?.respondToServerRequest(requestId, result); - return Boolean(client); - } catch { - return false; - } -} - -function rejectServerRequest(host: ServerRequestActionsHost, requestId: RejectRequestId, code: number, message: string): boolean { - try { - const client = host.currentClient(); - client?.rejectServerRequest(requestId, code, message); - return Boolean(client); - } catch { - return false; - } -} diff --git a/src/features/chat/requests/server-request-responder.ts b/src/features/chat/requests/server-request-responder.ts new file mode 100644 index 00000000..e0faec46 --- /dev/null +++ b/src/features/chat/requests/server-request-responder.ts @@ -0,0 +1,28 @@ +import type { AppServerClient } from "../../../app-server/client"; + +type RespondRequestId = Parameters[0]; +type RejectRequestId = Parameters[0]; + +export interface ServerRequestResponderHost { + currentClient: () => AppServerClient | null; +} + +export function respondToServerRequest(host: ServerRequestResponderHost, requestId: RespondRequestId, result: unknown): boolean { + try { + const client = host.currentClient(); + client?.respondToServerRequest(requestId, result); + return Boolean(client); + } catch { + return false; + } +} + +export function rejectServerRequest(host: ServerRequestResponderHost, requestId: RejectRequestId, code: number, message: string): boolean { + try { + const client = host.currentClient(); + client?.rejectServerRequest(requestId, code, message); + return Boolean(client); + } catch { + return false; + } +} diff --git a/src/features/chat/session/composition.ts b/src/features/chat/session/composition.ts index 98615c24..5c0b001b 100644 --- a/src/features/chat/session/composition.ts +++ b/src/features/chat/session/composition.ts @@ -6,7 +6,7 @@ import { createChatServerMetadataActions, type ChatServerMetadataActions } from import { createChatServerThreadActions } from "../server-actions/thread-actions"; import { ChatConnectionController } from "./connection-controller"; import { createChatReconnectActions } from "./reconnect-actions"; -import type { ServerRequestActions } from "../requests/server-request-actions"; +import type { rejectServerRequest, respondToServerRequest } from "../requests/server-request-responder"; import type { ChatThreadGoalActions } from "../threads/thread-goal-actions"; import type { ThreadRenameController } from "../threads/thread-rename-controller"; import { ChatInboundController } from "../inbound/controller"; @@ -63,7 +63,8 @@ export function createChatInboundController( serverMetadata: ChatServerMetadataActions; serverDiagnostics: ChatServerDiagnosticsActions; threadRename: ThreadRenameController; - serverRequestResponder: ServerRequestActions; + respondToServerRequest: (requestId: Parameters[1], result: unknown) => boolean; + rejectServerRequest: (requestId: Parameters[1], code: number, message: string) => boolean; }, ): ChatInboundController { const { plugin, thread, render } = context; @@ -86,8 +87,8 @@ export function createChatInboundController( refs.serverDiagnostics.recordMcpStartupStatus(name, status, message); render.schedule(); }, - respondToServerRequest: (requestId, result) => refs.serverRequestResponder.respond(requestId, result), - rejectServerRequest: (requestId, code, message) => refs.serverRequestResponder.reject(requestId, code, message), + respondToServerRequest: refs.respondToServerRequest, + rejectServerRequest: refs.rejectServerRequest, }); } diff --git a/tests/features/chat/requests/server-request-actions.test.ts b/tests/features/chat/requests/server-request-actions.test.ts deleted file mode 100644 index 6c0bd065..00000000 --- a/tests/features/chat/requests/server-request-actions.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; - -import type { AppServerClient } from "../../../../src/app-server/client"; -import { createServerRequestActions } from "../../../../src/features/chat/requests/server-request-actions"; - -describe("createServerRequestActions", () => { - it("responds through the current app-server client", () => { - const respondToServerRequest = vi.fn(); - const responder = createServerRequestActions({ - currentClient: () => ({ respondToServerRequest }) as unknown as AppServerClient, - }); - - expect(responder.respond(7, { ok: true })).toBe(true); - - expect(respondToServerRequest).toHaveBeenCalledWith(7, { ok: true }); - }); - - it("rejects through the current app-server client", () => { - const rejectServerRequest = vi.fn(); - const responder = createServerRequestActions({ - currentClient: () => ({ rejectServerRequest }) as unknown as AppServerClient, - }); - - expect(responder.reject(7, -32000, "No")).toBe(true); - - expect(rejectServerRequest).toHaveBeenCalledWith(7, -32000, "No"); - }); - - it("reports failure when there is no client or the client throws", () => { - expect(createServerRequestActions({ currentClient: () => null }).respond(7, null)).toBe(false); - expect( - createServerRequestActions({ - currentClient: () => - ({ - rejectServerRequest: () => { - throw new Error("closed"); - }, - }) as unknown as AppServerClient, - }).reject(7, -32000, "No"), - ).toBe(false); - }); -}); diff --git a/tests/features/chat/requests/server-request-responder.test.ts b/tests/features/chat/requests/server-request-responder.test.ts new file mode 100644 index 00000000..4c0612d8 --- /dev/null +++ b/tests/features/chat/requests/server-request-responder.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it, vi } from "vitest"; + +import type { AppServerClient } from "../../../../src/app-server/client"; +import { rejectServerRequest, respondToServerRequest } from "../../../../src/features/chat/requests/server-request-responder"; + +describe("server request responder", () => { + it("responds through the current app-server client", () => { + const respond = vi.fn(); + const host = { + currentClient: () => ({ respondToServerRequest: respond }) as unknown as AppServerClient, + }; + + expect(respondToServerRequest(host, 7, { ok: true })).toBe(true); + + expect(respond).toHaveBeenCalledWith(7, { ok: true }); + }); + + it("rejects through the current app-server client", () => { + const reject = vi.fn(); + const host = { + currentClient: () => ({ rejectServerRequest: reject }) as unknown as AppServerClient, + }; + + expect(rejectServerRequest(host, 7, -32000, "No")).toBe(true); + + expect(reject).toHaveBeenCalledWith(7, -32000, "No"); + }); + + it("reports failure when there is no client or the client throws", () => { + expect(respondToServerRequest({ currentClient: () => null }, 7, null)).toBe(false); + expect( + rejectServerRequest( + { + currentClient: () => + ({ + rejectServerRequest: () => { + throw new Error("closed"); + }, + }) as unknown as AppServerClient, + }, + 7, + -32000, + "No", + ), + ).toBe(false); + }); +});