import { writeFileSync } from "node:fs"; import { stat } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import type { SessionHeader } from "../src/core/session-manager.js"; import { SessionManager } from "../src/core/session-manager.js"; import { initTheme } from "../src/modes/interactive/theme/theme.js"; function createSessionFile(path: string): void { const header: SessionHeader = { type: "session", id: "test-session", version: 3, timestamp: new Date(0).toISOString(), cwd: "/tmp", }; writeFileSync(path, `${JSON.stringify(header)}\n`, "utf8"); // SessionManager only persists once it has seen at least one assistant message. // Add a minimal assistant entry so subsequent appends are persisted. const mgr = SessionManager.open(path); mgr.appendMessage({ role: "assistant", content: [{ type: "text", text: "hi" }], api: "openai-completions", provider: "openai", model: "test", usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, totalTokens: 2, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }, stopReason: "stop", timestamp: Date.now(), }); } describe("SessionInfo.modified", () => { beforeAll(() => initTheme("dark")); afterEach(() => { vi.restoreAllMocks(); }); it("uses last user/assistant message timestamp instead of file mtime", async () => { const filePath = join(tmpdir(), `companion-session-${Date.now()}-modified.jsonl`); createSessionFile(filePath); const before = await stat(filePath); // Ensure the file mtime can differ from our message timestamp even on coarse filesystems. await new Promise((r) => setTimeout(r, 10)); const mgr = SessionManager.open(filePath); const msgTime = Date.now(); mgr.appendMessage({ role: "assistant", content: [{ type: "text", text: "later" }], api: "openai-completions", provider: "openai", model: "test", usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, totalTokens: 2, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }, stopReason: "stop", timestamp: msgTime, }); const sessions = await SessionManager.list( "/tmp", filePath.replace(/\/[^/]+$/, ""), ); const s = sessions.find((x) => x.path === filePath); expect(s).toBeDefined(); expect(s!.modified.getTime()).toBe(msgTime); expect(s!.modified.getTime()).not.toBe(before.mtime.getTime()); }); });