import { describe, expect, it } from "vite-plus/test"; import type * as EffectAcpSchema from "effect-acp/schema"; import { extractModelConfigId, mergeToolCallState, parsePermissionRequest, parseSessionModeState, parseSessionUpdateEvent, sessionUpdateIsReplay, syntheticLoadSessionResponseFromInitialize, } from "./AcpRuntimeModel.ts"; describe("AcpRuntimeModel", () => { it("parses session mode state from typed ACP session setup responses", () => { const modeState = parseSessionModeState({ sessionId: "session-1", modes: { currentModeId: " code ", availableModes: [ { id: " ask ", name: " Ask ", description: " Request approval " }, { id: " code ", name: " Code " }, ], }, configOptions: [], } satisfies EffectAcpSchema.NewSessionResponse); expect(modeState).toEqual({ currentModeId: "code", availableModes: [ { id: "ask", name: "Ask", description: "Request approval" }, { id: "code", name: "Code" }, ], }); }); it("extracts the model config id from typed ACP config options", () => { const modelConfigId = extractModelConfigId({ sessionId: "session-1", configOptions: [ { id: "approval", name: "Approval Mode", category: "permission", type: "select", currentValue: "ask", options: [{ value: "ask", name: "Ask" }], }, { id: "model", name: "Model", category: "model", type: "select", currentValue: "default", options: [{ value: "default", name: "Auto" }], }, ], } satisfies EffectAcpSchema.NewSessionResponse); expect(modelConfigId).toBe("model"); }); it("detects Grok session replay updates from _meta.isReplay", () => { expect( sessionUpdateIsReplay({ _meta: { isReplay: true }, sessionId: "session-1", update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "replayed" }, }, } satisfies EffectAcpSchema.SessionNotification), ).toBe(true); expect( sessionUpdateIsReplay({ sessionId: "session-1", update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "live" }, }, } satisfies EffectAcpSchema.SessionNotification), ).toBe(false); }); it("builds a synthetic load response from initialize model state", () => { const response = syntheticLoadSessionResponseFromInitialize({ protocolVersion: 1, _meta: { modelState: { currentModelId: "grok-build", availableModels: [{ modelId: "grok-build", name: "Grok Build" }], }, }, } satisfies EffectAcpSchema.InitializeResponse); expect(response.models?.currentModelId).toBe("grok-build"); expect(response._meta).toMatchObject({ t3SessionLoadReady: "replay_idle" }); }); it("accepts initialize model descriptions with null", () => { const response = syntheticLoadSessionResponseFromInitialize({ protocolVersion: 1, _meta: { modelState: { currentModelId: "grok-build", availableModels: [{ modelId: "grok-build", name: "Grok Build", description: null }], }, }, } satisfies EffectAcpSchema.InitializeResponse); expect(response.models?.availableModels[0]?.description).toBeNull(); }); it("ignores malformed initialize model state in synthetic load responses", () => { const response = syntheticLoadSessionResponseFromInitialize({ protocolVersion: 1, _meta: { modelState: { currentModelId: "grok-build", availableModels: [null], }, modeState: { currentModeId: "code", availableModes: [{ id: "code", name: 12 }], }, }, } as EffectAcpSchema.InitializeResponse); expect(response.models).toBeUndefined(); expect(response.modes).toBeUndefined(); expect(response._meta).toMatchObject({ t3SessionLoadReady: "replay_idle" }); }); it("builds a synthetic load response with initialize mode state", () => { const response = syntheticLoadSessionResponseFromInitialize({ protocolVersion: 1, _meta: { modeState: { currentModeId: "code", availableModes: [ { id: "ask", name: "Ask" }, { id: "code", name: "Code" }, ], }, }, } satisfies EffectAcpSchema.InitializeResponse); expect(response.modes?.currentModeId).toBe("code"); expect(response.modes?.availableModes).toHaveLength(2); }); it("projects typed ACP tool call updates into runtime events", () => { const created = parseSessionUpdateEvent({ sessionId: "session-1", update: { sessionUpdate: "tool_call", toolCallId: "tool-1", title: "Terminal", kind: "execute", status: "pending", rawInput: { executable: "bun", args: ["run", "typecheck"], }, content: [ { type: "content", content: { type: "text", text: "Running checks", }, }, ], }, } satisfies EffectAcpSchema.SessionNotification); expect(created.events).toEqual([ { _tag: "ToolCallUpdated", toolCall: { toolCallId: "tool-1", kind: "execute", title: "Ran command", status: "pending", command: "bun run typecheck", detail: "bun run typecheck", data: { toolCallId: "tool-1", kind: "execute", command: "bun run typecheck", rawInput: { executable: "bun", args: ["run", "typecheck"], }, content: [ { type: "content", content: { type: "text", text: "Running checks", }, }, ], }, }, rawPayload: { sessionId: "session-1", update: { sessionUpdate: "tool_call", toolCallId: "tool-1", title: "Terminal", kind: "execute", status: "pending", rawInput: { executable: "bun", args: ["run", "typecheck"], }, content: [ { type: "content", content: { type: "text", text: "Running checks", }, }, ], }, }, }, ]); const updated = parseSessionUpdateEvent({ sessionId: "session-1", update: { sessionUpdate: "tool_call_update", toolCallId: "tool-1", status: "completed", rawOutput: { exitCode: 0 }, }, } satisfies EffectAcpSchema.SessionNotification); expect(updated.events).toHaveLength(1); expect(updated.events[0]?._tag).toBe("ToolCallUpdated"); const createdEvent = created.events[0]; const updatedEvent = updated.events[0]; if (createdEvent?._tag === "ToolCallUpdated" && updatedEvent?._tag === "ToolCallUpdated") { expect(mergeToolCallState(createdEvent.toolCall, updatedEvent.toolCall)).toMatchObject({ toolCallId: "tool-1", status: "completed", title: "Ran command", detail: "bun run typecheck", command: "bun run typecheck", }); } }); it("trims padded current mode updates before emitting a mode change", () => { const result = parseSessionUpdateEvent({ sessionId: "session-1", update: { sessionUpdate: "current_mode_update", currentModeId: " code ", }, } satisfies EffectAcpSchema.SessionNotification); expect(result.modeId).toBe("code"); expect(result.events).toEqual([ { _tag: "ModeChanged", modeId: "code", }, ]); }); it("projects typed ACP plan and content updates", () => { const planResult = parseSessionUpdateEvent({ sessionId: "session-1", update: { sessionUpdate: "plan", entries: [ { content: " Inspect state ", priority: "high", status: "completed" }, { content: "", priority: "medium", status: "in_progress" }, ], }, } satisfies EffectAcpSchema.SessionNotification); expect(planResult.events).toEqual([ { _tag: "PlanUpdated", payload: { plan: [ { step: "Inspect state", status: "completed" }, { step: "Step 2", status: "inProgress" }, ], }, rawPayload: { sessionId: "session-1", update: { sessionUpdate: "plan", entries: [ { content: " Inspect state ", priority: "high", status: "completed" }, { content: "", priority: "medium", status: "in_progress" }, ], }, }, }, ]); const contentResult = parseSessionUpdateEvent({ sessionId: "session-1", update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "hello from acp", }, }, } satisfies EffectAcpSchema.SessionNotification); expect(contentResult.events).toEqual([ { _tag: "ContentDelta", text: "hello from acp", rawPayload: { sessionId: "session-1", update: { sessionUpdate: "agent_message_chunk", content: { type: "text", text: "hello from acp", }, }, }, }, ]); }); it("keeps permission request parsing compatible with loose extension payloads", () => { const request = parsePermissionRequest({ sessionId: "session-1", options: [ { optionId: "allow-once", name: "Allow once", kind: "allow_once", }, ], toolCall: { toolCallId: "tool-1", title: "`cat package.json`", kind: "execute", status: "pending", content: [ { type: "content", content: { type: "text", text: "Not in allowlist", }, }, ], }, }); expect(request).toMatchObject({ kind: "execute", detail: "cat package.json", toolCall: { toolCallId: "tool-1", kind: "execute", status: "pending", command: "cat package.json", }, }); }); });