diff --git a/src/session.test.ts b/src/session.test.ts index 3e22b1b..8f11244 100644 --- a/src/session.test.ts +++ b/src/session.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { Session } from "./session.js"; -import type { SDKMessage, WireMessage } from "./types.js"; +import type { MessageWire, SDKMessage, WireMessage } from "./types.js"; const BUFFER_LIMIT = 100; @@ -96,6 +96,26 @@ function createAssistantMessage(index: number): WireMessage { } as WireMessage; } +function createApprovalRequestMessage( + index: number, + toolCall: { + name: string; + arguments: string; + tool_call_id: string; + }, +): MessageWire { + return { + type: "message", + session_id: "session-1", + message_type: "approval_request_message", + id: `message-approval-${index}`, + date: "2026-01-01T00:00:00.000000+00:00", + uuid: `approval-${index}`, + tool_call: toolCall, + tool_calls: [toolCall], + }; +} + function createResultMessage(): WireMessage { return { type: "result", @@ -340,6 +360,48 @@ describe("Session", () => { }); }); + describe("transformMessage tool-call mapping", () => { + test("maps approval_request_message to SDK tool_call message", () => { + const session = new Session(); + const wireMsg = createApprovalRequestMessage(1, { + name: "Bash", + arguments: JSON.stringify({ command: "pwd" }), + tool_call_id: "call-approval-1", + }); + + // @ts-expect-error - accessing private method for regression coverage + const transformed = session.transformMessage(wireMsg) as SDKMessage | null; + + expect(transformed).toEqual({ + type: "tool_call", + toolCallId: "call-approval-1", + toolName: "Bash", + toolInput: { command: "pwd" }, + uuid: "approval-1", + }); + }); + + test("falls back to raw tool arguments when approval_request_message args are not JSON", () => { + const session = new Session(); + const wireMsg = createApprovalRequestMessage(2, { + name: "Read", + arguments: "path=/tmp/foo.txt", + tool_call_id: "call-approval-2", + }); + + // @ts-expect-error - accessing private method for regression coverage + const transformed = session.transformMessage(wireMsg) as SDKMessage | null; + + expect(transformed).toEqual({ + type: "tool_call", + toolCallId: "call-approval-2", + toolName: "Read", + toolInput: { raw: "path=/tmp/foo.txt" }, + uuid: "approval-2", + }); + }); + }); + describe("background pump parity", () => { test("handles can_use_tool control requests before stream iteration starts", async () => { let callbackInvocations = 0; diff --git a/src/session.ts b/src/session.ts index 29939e6..11f3832 100644 --- a/src/session.ts +++ b/src/session.ts @@ -12,6 +12,7 @@ import type { SDKInitMessage, SDKAssistantMessage, SDKResultMessage, + MessageWire, WireMessage, ControlRequest, CanUseToolControlRequest, @@ -669,7 +670,7 @@ export class Session implements AsyncDisposable { /** * Transform wire message to SDK message */ - private transformMessage(wireMsg: WireMessage): SDKMessage | null { + private transformMessage(wireMsg: WireMessage | MessageWire): SDKMessage | null { // Init message if (wireMsg.type === "system" && "subtype" in wireMsg && wireMsg.subtype === "init") { const msg = wireMsg as WireMessage & {