881 lines · 29.1 KB · TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test"; import type * as EffectAcpSchema from "effect-acp/schema"; import {  decideToolCallUpdateEmission,  extractModelConfigId,  mergeToolCallState,  parsePermissionRequest,  parseSessionModeState,  parseSessionUpdateEvent,  sessionUpdateIsReplay,  syntheticLoadSessionResponseFromInitialize,  toolCallProgressLength,  type AcpToolCallState,} 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 thought chunks separate from assistant text", () => {    const notification = {      sessionId: "session-1",      update: {        sessionUpdate: "agent_thought_chunk",        content: { type: "text", text: "Inspect the current implementation first." },      },    } satisfies EffectAcpSchema.SessionNotification;     expect(parseSessionUpdateEvent(notification).events).toEqual([      {        _tag: "ThoughtDelta",        text: "Inspect the current implementation first.",        rawPayload: notification,      },    ]);  });   it("preserves native command inputs and empty command lists", () => {    const availableCommands = [      { name: "plan", description: "Plan a task", input: { hint: "task" } },      { name: "logout", description: "Sign out" },    ] satisfies ReadonlyArray<EffectAcpSchema.AvailableCommand>;     for (const commands of [availableCommands, []]) {      const notification = {        sessionId: "session-1",        update: { sessionUpdate: "available_commands_update", availableCommands: commands },      } satisfies EffectAcpSchema.SessionNotification;      expect(parseSessionUpdateEvent(notification).events).toEqual([        {          _tag: "AvailableCommandsUpdated",          availableCommands: commands,          rawPayload: notification,        },      ]);    }  });   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",      },    });  });   it("bounds an oversized cumulative tool_call_update content buffer to a tail window", () => {    // Mirrors Grok's ACP CLI resending the ENTIRE accumulated terminal output on every    // tool_call_update notification instead of a delta (see upstream #6556).    const hugeText = Array.from({ length: 2_000 }, (_, i) => `line ${i}: ${"x".repeat(50)}`).join(      "\n",    );    expect(hugeText.length).toBeGreaterThan(60_000);     const result = parseSessionUpdateEvent({      sessionId: "session-1",      update: {        // Real ACP `tool_call_update` deltas typically omit `title` (already established by        // the initial `tool_call`); that is also the shape that surfaces raw content as detail.        sessionUpdate: "tool_call_update",        toolCallId: "tool-1",        kind: "other",        status: "in_progress",        content: [{ type: "content", content: { type: "text", text: hugeText } }],      },    } satisfies EffectAcpSchema.SessionNotification);     expect(result.events).toHaveLength(1);    const event = result.events[0];    if (event?._tag !== "ToolCallUpdated") {      throw new Error("expected a ToolCallUpdated event");    }     expect(event.toolCall.detail).toBeDefined();    const detail = event.toolCall.detail!;    // 8000 chars of tail plus the truncation marker, regardless of input size.    expect(detail.length).toBe(8_028);    expect(detail.startsWith("[Earlier output truncated]")).toBe(true);    expect(detail.endsWith(hugeText.slice(-100))).toBe(true);     // The raw payload threaded through for logging/persistence must not smuggle the full    // cumulative buffer back in either.    const rawUpdate = (      event.rawPayload as {        readonly update: {          readonly content: ReadonlyArray<{ readonly content: { text: string } }>;        };      }    ).update;    expect(rawUpdate.content[0]?.content.text.length).toBeLessThan(8_100);    expect(JSON.stringify(event).length).toBeLessThan(hugeText.length);  });   it("coalesces 1000 rapid cumulative tool_call_update notifications for a redrawing progress bar", () => {    let previous: AcpToolCallState | undefined;    let lastEmittedDetailLength: number | undefined;    let skippedSinceEmit = 0;    let emittedCount = 0;    let emittedBytes = 0;    let notificationBytes = 0;    let largestEmittedEventBytes = 0;    let finalDetail: string | undefined;    let cumulativeBuffer = "";     for (let i = 0; i < 1_000; i += 1) {      // Grok resends the FULL accumulated buffer, not a delta, on every redraw.      cumulativeBuffer += `frame ${i}: ${"#".repeat(50)}\n`;      const isLast = i === 999;       const notification = {        sessionId: "session-1",        update: {          sessionUpdate: "tool_call_update",          toolCallId: "tool-1",          kind: "other",          status: isLast ? "completed" : "in_progress",          content: [{ type: "content", content: { type: "text", text: cumulativeBuffer } }],        },      } satisfies EffectAcpSchema.SessionNotification;      notificationBytes += JSON.stringify(notification).length;       const { events } = parseSessionUpdateEvent(notification);       const event = events[0];      if (event?._tag !== "ToolCallUpdated") {        continue;      }

Lines 1–500 of 881.