import type { AssistantMessage, ToolResultMessage, Usage } from "@earendil-works/pi-ai"; import { describe, expect, it } from "vitest"; import { computeFileLists, createFileOps, extractFileOpsFromMessage, formatFileOperations, } from "../src/core/compaction/utils.js"; const usage: Usage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }; function assistant(content: AssistantMessage["content"]): AssistantMessage { return { role: "assistant", content, api: "test", provider: "test", model: "test", usage, stopReason: "toolUse", timestamp: 0, }; } function toolResult(details: unknown, toolName = "ipython"): ToolResultMessage { return { role: "toolResult", toolCallId: "call-1", toolName, content: [], details, isError: false, timestamp: 0 }; } function toolCall(name: string, args: Record) { return { type: "toolCall", id: "call-1", name, arguments: args } as const; } describe("compaction file-op extraction", () => { it("records native edit tool calls", () => { const fileOps = createFileOps(); extractFileOpsFromMessage(assistant([toolCall("edit", { path: "src/native-edit.ts" })]) as never, fileOps); expect([...fileOps.edited]).toEqual(["src/native-edit.ts"]); }); it("records kernel-reported edits from ipython tool results", () => { const fileOps = createFileOps(); extractFileOpsFromMessage( toolResult({ status: "ok", diffs: [ { path: "pkg/a.ts", oldStr: "x", newStr: "y" }, { path: "pkg/b.ts", oldStr: "x", newStr: "y" }, ], }) as never, fileOps, ); expect([...fileOps.edited]).toEqual(["pkg/a.ts", "pkg/b.ts"]); }); it("ignores non-ipython tool results and malformed diff payloads", () => { const fileOps = createFileOps(); extractFileOpsFromMessage(toolResult({ diffs: [{ path: "pkg/c.ts" }] }, "bash") as never, fileOps); extractFileOpsFromMessage(toolResult({ diffs: "not-an-array" }) as never, fileOps); extractFileOpsFromMessage(toolResult({ diffs: [{ noPath: true }, { path: 42 }, null] }) as never, fileOps); extractFileOpsFromMessage(toolResult(undefined) as never, fileOps); expect(fileOps.edited.size).toBe(0); }); it("caps kernel file lists to keep summary blocks bounded", () => { const fileOps = createFileOps(); const diffs = Array.from({ length: 250 }, (_, i) => ({ path: `pkg/file-${String(i).padStart(3, "0")}.ts`, oldStr: "a", newStr: "b", })); extractFileOpsFromMessage(toolResult({ diffs }) as never, fileOps); const { readFiles, modifiedFiles } = computeFileLists(fileOps); expect(modifiedFiles).toHaveLength(200); expect(modifiedFiles[0]).toBe("pkg/file-000.ts"); expect(modifiedFiles[199]).toBe("pkg/file-199.ts"); expect(modifiedFiles).not.toContain("pkg/file-249.ts"); const summary = formatFileOperations(readFiles, modifiedFiles); expect(summary).not.toContain("pkg/file-249.ts"); }); it("feeds kernel edits into modified-files for summaries", () => { const fileOps = createFileOps(); extractFileOpsFromMessage( toolResult({ diffs: [{ path: "src/kernel-edit.ts", oldStr: "a", newStr: "b" }] }) as never, fileOps, ); const { readFiles, modifiedFiles } = computeFileLists(fileOps); expect(readFiles).toEqual([]); expect(modifiedFiles).toEqual(["src/kernel-edit.ts"]); expect(formatFileOperations(readFiles, modifiedFiles)).toContain( "\nsrc/kernel-edit.ts\n", ); }); });