import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { mkdtemp, rm, readFile, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { readTranscript, appendTranscript, transcriptPath, TRANSCRIPT_DIR } from "../../src/agent/transcript.js"; import type { ModelMessage } from "ai"; const sample: ModelMessage = { role: "user", content: "hello" }; const reply: ModelMessage = { role: "assistant", content: "hi" }; describe("transcript JSONL", () => { let dir: string; let path: string; beforeEach(async () => { dir = await mkdtemp(join(tmpdir(), "hub-transcript-")); path = join(dir, "session.jsonl"); }); afterEach(async () => { await rm(dir, { recursive: true, force: true }); }); it("readTranscript returns empty array for nonexistent file", async () => { expect(await readTranscript(path)).toEqual([]); }); it("appendTranscript creates parent dirs and writes JSONL", async () => { await appendTranscript(path, [sample]); const content = await readFile(path, "utf8"); expect(content.trim()).toBe(JSON.stringify(sample)); }); it("readTranscript reads back appended messages", async () => { await appendTranscript(path, [sample, reply]); const messages = await readTranscript(path); expect(messages).toHaveLength(2); expect(messages[0]).toEqual(sample); expect(messages[1]).toEqual(reply); }); it("multiple appends accumulate", async () => { await appendTranscript(path, [sample]); await appendTranscript(path, [reply]); const messages = await readTranscript(path); expect(messages).toHaveLength(2); }); it("skips corrupt lines (partial write recovery)", async () => { await appendTranscript(path, [sample]); // Append a corrupt line (simulating a crash mid-write). await writeFile(path, '{"broken json\n', { flag: "a" }); const messages = await readTranscript(path); expect(messages).toHaveLength(1); // only the valid line }); it("transcriptPath builds the expected path", () => { const p = transcriptPath("/ws", "sess-1"); expect(p).toBe(join("/ws", TRANSCRIPT_DIR, "sess-1.jsonl")); }); });