import { describe, expect, it } from "vitest"; import type { WorkspaceDescriptorPayload } from "@getpaseo/protocol/messages"; import { normalizeProjectDescriptor, normalizeWorkspaceDescriptor, type Agent, } from "@/stores/session-store"; import type { StreamItem } from "@/types/stream"; import { normalizeAgentSnapshot } from "@/utils/agent-snapshots"; import { ReplicaCache } from "."; import type { DirectoryCheckpoint } from "@/runtime/replica-cache"; import type { ReplicaHostRows, ReplicaRow, ReplicaRowChanges, ReplicaRowStore } from "./row-store"; const SERVER_ID = "cached-host"; function deferred(): { promise: Promise; resolve(): void } { let resolve!: () => void; const promise = new Promise((resolvePromise) => { resolve = resolvePromise; }); return { promise, resolve }; } class MemoryStorage implements ReplicaRowStore { readonly rows = new Map(); readonly changes: ReplicaRowChanges[] = []; readonly reads: Array<{ serverId: string; kinds: readonly ReplicaRow["kind"][]; ids?: readonly string[]; }> = []; writes = 0; cleanups = 0; nextWriteFailure: Error | null = null; persistentWriteFailure: Error | null = null; readGate: Promise | null = null; onRead: (() => void) | null = null; /** Throws once a read goes past this count, so a non-terminating read loop fails the test. */ readLimit: number | null = null; private key(row: Pick): string { return `${row.serverId}:${row.kind}:${row.id}`; } async open(): Promise {} async read( serverId: string, kinds: readonly ReplicaRow["kind"][], ids?: readonly string[], ): Promise { this.reads.push({ serverId, kinds, ...(ids ? { ids } : {}) }); if (this.readLimit !== null && this.reads.length > this.readLimit) { throw new Error( `replica cache read ${this.reads.length} passed the limit of ${this.readLimit}`, ); } this.onRead?.(); await this.readGate; const acceptedKinds = new Set(kinds); const acceptedIds = ids ? new Set(ids) : null; return [...this.rows.values()].filter( (row) => row.serverId === serverId && acceptedKinds.has(row.kind) && (!acceptedIds || acceptedIds.has(row.id)), ); } async readAll(): Promise { const hosts = new Map(); for (const row of this.rows.values()) { const rows = hosts.get(row.serverId) ?? []; rows.push(row); hosts.set(row.serverId, rows); } return [...hosts].map(([serverId, rows]) => ({ serverId, rows })); } async apply(changes: ReplicaRowChanges): Promise { this.writes += 1; if (this.persistentWriteFailure) throw this.persistentWriteFailure; if (this.nextWriteFailure) { const error = this.nextWriteFailure; this.nextWriteFailure = null; throw error; } this.changes.push(changes); for (const key of changes.deletes) this.rows.delete(this.key(key)); for (const row of changes.upserts) this.rows.set(this.key(row), row); } async deleteHost(serverId: string): Promise { for (const [key, row] of this.rows) if (row.serverId === serverId) this.rows.delete(key); } async renameHost(oldServerId: string, newServerId: string): Promise { for (const [key, row] of this.rows) { if (row.serverId !== oldServerId) continue; this.rows.delete(key); const renamed = { ...row, serverId: newServerId }; this.rows.set(this.key(renamed), renamed); } } async clear(): Promise { this.rows.clear(); } } const noLegacyCleanup = { clearLegacyCache: async () => undefined }; function createCache(storage: MemoryStorage, maxBytes?: number): ReplicaCache { const cache = new ReplicaCache(storage, { ...noLegacyCleanup, ...(maxBytes ? { maxBytes } : {}), }); cache.setHosts([SERVER_ID]); return cache; } function requireTimelineRow(storage: MemoryStorage): ReplicaRow { const row = [...storage.rows.values()].find((candidate) => candidate.kind === "timeline"); if (!row) throw new Error("timeline row was not written"); return row; } function agent(id = "agent-1"): Agent { return { ...normalizeAgentSnapshot( { id, provider: "codex", cwd: "/repo/paseo", workspaceId: "workspace-1", model: null, createdAt: "2026-07-18T08:00:00.000Z", updatedAt: "2026-07-18T08:01:00.000Z", lastUserMessageAt: "2026-07-18T08:01:00.000Z", status: "idle", capabilities: { supportsStreaming: true, supportsSessionPersistence: true, supportsDynamicModes: true, supportsMcpServers: true, supportsReasoningStream: true, supportsToolInvocations: true, }, currentModeId: null, availableModes: [], pendingPermissions: [], persistence: null, title: "Cached agent", labels: {}, }, SERVER_ID, ), projectPlacement: null, }; } function workspacePayload(): WorkspaceDescriptorPayload { return { id: "workspace-1", projectId: "project-1", projectDisplayName: "Paseo", projectRootPath: "/repo/paseo", workspaceDirectory: "/repo/paseo", projectKind: "git", workspaceKind: "local_checkout", name: "main", status: "running", statusEnteredAt: "2026-07-18T08:00:00.000Z", activityAt: null, archivingAt: null, diffStat: null, scripts: [], }; } function timelineItem(text = "Cached"): StreamItem { return { kind: "assistant_message", id: "message-1", text, timestamp: new Date("2026-07-18T08:02:00.000Z"), timelineCursor: { epoch: "epoch-1", seq: 12 }, }; } function directory( checkpoint: DirectoryCheckpoint = { agents: { generation: "g", afterSeq: 12 } }, ) { const cachedAgent = agent(); const workspace = normalizeWorkspaceDescriptor(workspacePayload()); const project = normalizeProjectDescriptor({ projectId: "project-1", projectDisplayName: "Paseo", projectRootPath: "/repo/paseo", projectKind: "git", }); return { agents: new Map([[cachedAgent.id, cachedAgent]]), workspaces: new Map([[workspace.id, workspace]]), projects: new Map([[project.projectId, project]]), checkpoint, }; } function timeline(text = "Cached") { return { agentId: "agent-1", items: [timelineItem(text)], range: { epoch: "epoch-1", startSeq: 1, endSeq: 12 }, hasOlder: true, }; } function commitDirectory( cache: ReplicaCache, serverId: string, value: ReturnType, ): void { cache.commitDirectoryMutations( serverId, [ ...Array.from(value.agents.values(), (cachedAgent) => ({ kind: "agent" as const, type: "upsert" as const, id: cachedAgent.id, value: cachedAgent, })), ...Array.from(value.workspaces.values(), (workspace) => ({ kind: "workspace" as const, type: "upsert" as const, id: workspace.id, value: workspace, })), ...Array.from(value.projects.values(), (project) => ({ kind: "project" as const, type: "upsert" as const, id: project.projectId, value: project, })), ], value.checkpoint, ); } function deleteDirectory(cache: ReplicaCache, serverId: string): void { cache.commitDirectoryMutations(serverId, [ { kind: "agent", type: "delete", id: "agent-1" }, { kind: "workspace", type: "delete", id: "workspace-1" }, { kind: "project", type: "delete", id: "project-1" }, ]); } describe("ReplicaCache", () => { it("rejects pre-baseline-fix checkpoints without discarding cached rows or timelines", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const baseline = directory(); writer.replaceDirectoryBaseline(SERVER_ID, baseline); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); await writer.flush(); await storage.apply({ deletes: [], upserts: [ { serverId: SERVER_ID, kind: "checkpoint", id: "singleton", payload: JSON.stringify(baseline.checkpoint), }, ], }); const reader = createCache(storage); const restored = await reader.readDirectory(SERVER_ID); expect(restored.checkpoint).toBeUndefined(); expect([...restored.workspaces.keys()]).toEqual([...baseline.workspaces.keys()]); expect([...restored.projects.keys()]).toEqual([...baseline.projects.keys()]); expect([...restored.agents.keys()]).toEqual([...baseline.agents.keys()]); expect(await reader.readTimeline(SERVER_ID, "agent-1")).toEqual(timeline()); expect(await storage.read(SERVER_ID, ["checkpoint"])).toEqual([]); reader.replaceDirectoryBaseline(SERVER_ID, baseline); await reader.flush(); expect((await createCache(storage).readDirectory(SERVER_ID)).checkpoint).toEqual( baseline.checkpoint, ); }); it("does nothing until an owner explicitly commits data", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); await cache.flush(); expect(storage.writes).toBe(0); expect(storage.rows.size).toBe(0); }); it("round-trips explicit directory and timeline commits", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); commitDirectory(writer, SERVER_ID, directory()); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); await writer.flush(); const reader = createCache(storage); const restoredDirectory = await reader.readDirectory(SERVER_ID); const restoredTimeline = await reader.readTimeline(SERVER_ID, "agent-1"); expect(restoredDirectory.agents.get("agent-1")?.title).toBe("Cached agent"); expect(restoredDirectory.workspaces.get("workspace-1")?.name).toBe("main"); expect(restoredDirectory.projects.get("project-1")?.projectDisplayName).toBe("Paseo"); expect(restoredDirectory.checkpoint).toEqual({ agents: { generation: "g", afterSeq: 12 } }); expect(restoredTimeline).toEqual(timeline()); }); it("preserves pending timeline updates across directory baseline replacement", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); writer.commitTimeline(SERVER_ID, "agent-1", timeline("Latest visible reply")); writer.replaceDirectoryBaseline(SERVER_ID, directory()); await writer.flush(); const reader = createCache(storage); expect(await reader.readTimeline(SERVER_ID, "agent-1")).toEqual( timeline("Latest visible reply"), ); }); it("preserves clearing a timeline across directory baseline replacement", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); await writer.flush(); writer.commitTimeline(SERVER_ID, "agent-1", { ...timeline(), items: [], range: null }); writer.replaceDirectoryBaseline(SERVER_ID, directory()); await writer.flush(); const reader = createCache(storage); expect(await reader.readTimeline(SERVER_ID, "agent-1")).toEqual({ ...timeline(), items: [], range: null, hasOlder: false, }); }); it("serializes and writes only keyed directory mutations", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); const changed = agent("changed-agent"); let titleReads = 0; Object.defineProperty(changed, "title", { configurable: true, enumerable: true, get: () => { titleReads += 1; return "Changed agent"; }, }); cache.commitDirectoryMutations( SERVER_ID, [{ kind: "agent", type: "upsert", id: changed.id, value: changed }], { agents: { generation: "g", afterSeq: 13 } }, ); expect(titleReads).toBe(0); await cache.flush(); expect(titleReads).toBe(1); expect(storage.changes).toHaveLength(1); expect(storage.changes[0]?.upserts.map(({ kind, id }) => ({ kind, id }))).toEqual([ { kind: "agent", id: "changed-agent" }, { kind: "checkpoint", id: "singleton" }, ]); expect(storage.changes[0]?.deletes).toEqual([]); }); it("round-trips identified and anonymous open turns without changing protocol status", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const identified = agent("identified"); identified.turn = { phase: "open", turnId: "turn-1", startedAt: new Date("2026-08-31T12:00:00.000Z"), cancellationRequestId: null, }; const anonymous = agent("anonymous"); anonymous.turn = { phase: "open", turnId: null, startedAt: null, cancellationRequestId: null, }; writer.commitDirectoryMutations(SERVER_ID, [ { kind: "agent", type: "upsert", id: identified.id, value: identified }, { kind: "agent", type: "upsert", id: anonymous.id, value: anonymous }, ]); await writer.flush(); const restored = await createCache(storage).readDirectory(SERVER_ID); expect(restored.agents.get("identified")).toMatchObject({ status: "idle", turn: { phase: "open", turnId: "turn-1", startedAt: new Date("2026-08-31T12:00:00.000Z"), cancellationRequestId: null, }, }); expect(restored.agents.get("anonymous")).toMatchObject({ status: "idle", turn: { phase: "open", turnId: null, startedAt: null, cancellationRequestId: null, }, }); }); it("coalesces timeline values before serialization", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); let textReads = 0; const changedTimeline = timeline(); Object.defineProperty(changedTimeline.items[0], "text", { configurable: true, enumerable: true, get: () => { textReads += 1; return "Latest"; }, }); cache.commitTimeline(SERVER_ID, "agent-1", timeline("Old")); cache.commitTimeline(SERVER_ID, "agent-1", changedTimeline); expect(textReads).toBe(0); await cache.flush(); expect(textReads).toBe(1); expect(storage.changes[0]?.upserts.map(({ kind, id }) => ({ kind, id }))).toEqual([ { kind: "timeline", id: "agent-1" }, ]); }); it("never reads directory rows older than an accepted deferred deletion", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); deleteDirectory(cache, SERVER_ID); expect(await cache.readAgent(SERVER_ID, "agent-1")).toBeUndefined(); expect(await cache.readWorkspace(SERVER_ID, "workspace-1")).toBeUndefined(); expect((await cache.readDirectory(SERVER_ID)).projects.size).toBe(0); }); it("fails closed when an accepted deletion cannot be persisted before a read", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); storage.nextWriteFailure = new Error("disk busy"); deleteDirectory(cache, SERVER_ID); expect(await cache.readWorkspace(SERVER_ID, "workspace-1")).toBeUndefined(); }); it("gives up a read instead of retrying while the store keeps rejecting writes", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); storage.persistentWriteFailure = new Error("QuotaExceededError"); storage.reads.length = 0; storage.readLimit = 5; deleteDirectory(cache, SERVER_ID); expect(await cache.readWorkspace(SERVER_ID, "workspace-1")).toBeUndefined(); expect(storage.reads.length).toBeLessThanOrEqual(1); }); it("still reads a host whose rows are stored when another host's write is rejected", async () => { const storage = new MemoryStorage(); const cache = new ReplicaCache(storage, noLegacyCleanup); cache.setHosts([SERVER_ID, "other-host"]); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); storage.persistentWriteFailure = new Error("QuotaExceededError"); commitDirectory(cache, "other-host", directory()); expect((await cache.readWorkspace(SERVER_ID, "workspace-1"))?.workspace.id).toBe("workspace-1"); }); it("discards a durable read when the host changes while it is in flight", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); const started = deferred(); const release = deferred(); storage.onRead = started.resolve; storage.readGate = release.promise; const reading = cache.readAgent(SERVER_ID, "agent-1"); await started.promise; deleteDirectory(cache, SERVER_ID); release.resolve(); expect(await reading).toBeUndefined(); }); it("never reads a timeline older than an accepted deferred replacement", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); cache.commitTimeline(SERVER_ID, "agent-1", timeline("Old")); await cache.flush(); cache.commitTimeline(SERVER_ID, "agent-1", timeline("New")); expect((await cache.readTimeline(SERVER_ID, "agent-1"))?.items).toEqual([timelineItem("New")]); }); it("round-trips plugin timeline items", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const pluginItem: StreamItem = { kind: "plugin", id: "reports/test-report/1", pluginId: "reports", pluginItemId: "test-report/1", itemKind: "test-report", version: 1, data: { passed: 4, failed: 0 }, timestamp: new Date("2026-07-18T08:02:00.000Z"), timelineCursor: { epoch: "epoch-1", seq: 12 }, }; writer.commitTimeline(SERVER_ID, "agent-1", { agentId: "agent-1", items: [pluginItem], range: { epoch: "epoch-1", startSeq: 12, endSeq: 12 }, hasOlder: true, }); await writer.flush(); const reader = createCache(storage); expect((await reader.readTimeline(SERVER_ID, "agent-1"))?.items).toEqual([pluginItem]); }); it("drops cached plugin timeline items without a plugin-local id", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const pluginItem = { kind: "plugin", id: "reports/test-report/1", pluginId: "reports", pluginItemId: "test-report/1", itemKind: "test-report", version: 1, data: { passed: 4 }, timestamp: new Date("2026-07-18T08:02:00.000Z"), } satisfies StreamItem; writer.commitTimeline(SERVER_ID, "agent-1", { agentId: "agent-1", items: [pluginItem], range: { epoch: "epoch-1", startSeq: 12, endSeq: 12 }, hasOlder: true, }); await writer.flush(); const row = requireTimelineRow(storage); const payload = JSON.parse(row.payload) as { items: Array> }; delete payload.items[0]?.pluginItemId; storage.rows.set(`${row.serverId}:${row.kind}:${row.id}`, { ...row, payload: JSON.stringify(payload), }); expect(await createCache(storage).readTimeline(SERVER_ID, "agent-1")).toBeUndefined(); }); it("refetches old cached Markdown fragments instead of offering them as whole messages", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); await writer.flush(); const row = requireTimelineRow(storage); const payload = JSON.parse(row.payload) as { items: Array> }; Object.assign(payload.items[0]!, { blockGroupId: "message-1", blockIndex: 0 }); storage.rows.set(`${row.serverId}:${row.kind}:${row.id}`, { ...row, payload: JSON.stringify(payload), }); expect(await createCache(storage).readTimeline(SERVER_ID, "agent-1")).toBeUndefined(); }); it("reads one requested agent and the focused timeline without scanning directory rows", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); commitDirectory(writer, SERVER_ID, directory()); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); await writer.flush(); const reader = createCache(storage); expect((await reader.readAgent(SERVER_ID, "agent-1"))?.id).toBe("agent-1"); expect((await reader.readTimeline(SERVER_ID, "agent-1"))?.items).toEqual([timelineItem()]); expect(storage.reads).toEqual([ { serverId: SERVER_ID, kinds: ["agent"], ids: ["agent-1"] }, { serverId: SERVER_ID, kinds: ["timeline"], ids: ["agent-1"] }, ]); }); it("reads one requested workspace and its project without scanning the directory", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); commitDirectory(writer, SERVER_ID, directory()); await writer.flush(); const reader = createCache(storage); const restored = await reader.readWorkspace(SERVER_ID, "workspace-1"); expect(restored?.workspace.id).toBe("workspace-1"); expect(restored?.project?.projectId).toBe("project-1"); expect(storage.reads).toEqual([ { serverId: SERVER_ID, kinds: ["workspace"], ids: ["workspace-1"] }, { serverId: SERVER_ID, kinds: ["project"], ids: ["project-1"] }, ]); }); it("treats a corrupt row as a scoped miss", async () => { const storage = new MemoryStorage(); storage.rows.set(`${SERVER_ID}:agent:agent-1`, { serverId: SERVER_ID, kind: "agent", id: "agent-1", payload: "{bad", }); storage.rows.set(`${SERVER_ID}:project:project-1`, { serverId: SERVER_ID, kind: "project", id: "project-1", payload: JSON.stringify({ projectId: "project-1", projectDisplayName: "Paseo", projectCustomName: null, projectCustomIconRevision: null, projectRootPath: "/repo/paseo", projectKind: "git", }), }); const cache = createCache(storage); const restored = await cache.readDirectory(SERVER_ID); expect(restored.agents.size).toBe(0); expect(restored.projects.get("project-1")?.projectDisplayName).toBe("Paseo"); expect(storage.rows.has(`${SERVER_ID}:agent:agent-1`)).toBe(false); expect(storage.rows.has(`${SERVER_ID}:project:project-1`)).toBe(true); }); it("removes a targeted corrupt row from eviction bookkeeping", async () => { const otherServerId = "other-host"; const storage = new MemoryStorage(); const writer = new ReplicaCache(storage, noLegacyCleanup); writer.setHosts([SERVER_ID, otherServerId]); commitDirectory(writer, SERVER_ID, directory()); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); commitDirectory(writer, otherServerId, directory()); await writer.flush(); storage.rows.set(`${SERVER_ID}:agent:agent-1`, { serverId: SERVER_ID, kind: "agent", id: "agent-1", payload: `{${"x".repeat(5_000)}`, }); const initialBytes = [...storage.rows.values()].reduce( (total, row) => total + Buffer.byteLength(row.payload), 0, ); const cache = new ReplicaCache(storage, { ...noLegacyCleanup, maxBytes: initialBytes + 100 }); cache.setHosts([SERVER_ID, otherServerId]); commitDirectory(cache, otherServerId, directory()); await cache.flush(); expect(await cache.readAgent(SERVER_ID, "agent-1")).toBeUndefined(); cache.commitTimeline(otherServerId, "agent-1", timeline("x".repeat(1_000))); await cache.flush(); expect(storage.rows.has(`${SERVER_ID}:timeline:agent-1`)).toBe(true); }); it("atomically removes a targeted corrupt row and its matching persisted cursor", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); commitDirectory( writer, SERVER_ID, directory({ agents: { generation: "g", afterSeq: 12 }, projects: { generation: "g", afterSeq: 4 }, }), ); await writer.flush(); storage.rows.set(`${SERVER_ID}:agent:agent-1`, { serverId: SERVER_ID, kind: "agent", id: "agent-1", payload: "{bad", }); expect(await createCache(storage).readAgent(SERVER_ID, "agent-1")).toBeUndefined(); expect(storage.changes.at(-1)).toMatchObject({ deletes: [{ serverId: SERVER_ID, kind: "agent", id: "agent-1" }], upserts: [{ serverId: SERVER_ID, kind: "checkpoint", id: "singleton" }], }); expect((await createCache(storage).readDirectory(SERVER_ID)).checkpoint).toEqual({ projects: { generation: "g", afterSeq: 4 }, }); }); it("drops only the cursor whose cached entity baseline is corrupt", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); commitDirectory( writer, SERVER_ID, directory({ agents: { generation: "g", afterSeq: 12 }, projects: { generation: "g", afterSeq: 4 }, }), ); await writer.flush(); storage.rows.set(`${SERVER_ID}:agent:agent-1`, { serverId: SERVER_ID, kind: "agent", id: "agent-1", payload: "{bad", }); const restored = await createCache(storage).readDirectory(SERVER_ID); expect(restored.checkpoint).toEqual({ projects: { generation: "g", afterSeq: 4 }, }); expect(storage.changes.at(-1)).toMatchObject({ deletes: [{ serverId: SERVER_ID, kind: "agent", id: "agent-1" }], upserts: [{ serverId: SERVER_ID, kind: "checkpoint", id: "singleton" }], }); const reopened = await createCache(storage).readDirectory(SERVER_ID); expect(reopened.checkpoint).toEqual({ projects: { generation: "g", afterSeq: 4 }, }); }); it("commits directory rows and their checkpoint in one storage transaction", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); expect(storage.changes).toHaveLength(1); expect(storage.changes[0]?.upserts.map((row) => row.kind).sort()).toEqual([ "agent", "checkpoint", "project", "workspace", ]); }); it("retries an explicit commit after a storage failure", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); storage.nextWriteFailure = new Error("disk busy"); cache.commitTimeline(SERVER_ID, "agent-1", timeline("Retry me")); await cache.flush(); expect(storage.rows.size).toBe(0); await cache.flush(); expect((await cache.readTimeline(SERVER_ID, "agent-1"))?.items).toEqual([ timelineItem("Retry me"), ]); }); it("retries a timeline read invalidated by a concurrent directory commit", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); cache.commitTimeline(SERVER_ID, "agent-1", timeline("Persisted timeline")); await cache.flush(); storage.onRead = () => { storage.onRead = null; commitDirectory(cache, SERVER_ID, directory()); }; expect((await cache.readTimeline(SERVER_ID, "agent-1"))?.items).toEqual([ timelineItem("Persisted timeline"), ]); }); it("rebuilds every directory row before restoring its checkpoint after eviction", async () => { const storage = new MemoryStorage(); const cache = new ReplicaCache(storage, { ...noLegacyCleanup, maxBytes: 2_500 }); cache.setHosts([SERVER_ID, "other-host"]); const cachedDirectory = directory(); commitDirectory(cache, SERVER_ID, cachedDirectory); await cache.flush(); commitDirectory(cache, "other-host", cachedDirectory); await cache.flush(); expect([...storage.rows.values()].some((row) => row.serverId === SERVER_ID)).toBe(false); cache.commitDirectoryMutations( SERVER_ID, [{ kind: "agent", type: "upsert", id: "agent-1", value: agent() }], { agents: { generation: "new-generation", afterSeq: 99 } }, ); await cache.flush(); expect([...storage.rows.values()].some((row) => row.serverId === SERVER_ID)).toBe(false); expect((await cache.readDirectory(SERVER_ID)).checkpoint).toBeUndefined(); cache.replaceDirectoryBaseline(SERVER_ID, cachedDirectory); await cache.flush(); expect( [...storage.rows.values()] .filter((row) => row.serverId === SERVER_ID) .map((row) => row.kind) .sort(), ).toEqual(["agent", "checkpoint", "project", "workspace"]); }); it("runs legacy cleanup once when storage is first used", async () => { const storage = new MemoryStorage(); const cache = new ReplicaCache(storage, { clearLegacyCache: async () => { storage.cleanups += 1; }, }); cache.setHosts([SERVER_ID]); await cache.readAgent(SERVER_ID, "missing"); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); expect(storage.cleanups).toBe(1); }); }); it("retains semantic agent messages across disk-cache reconstruction", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const value = timeline(); value.items = [ { kind: "tool_call", id: "delivery", timestamp: new Date(1), timelineCursor: { epoch: "epoch-1", seq: 12 }, payload: { source: "agent", data: { provider: "codex", callId: "delivery", name: "agent_message", status: "completed", error: null, detail: { type: "plain_text", text: "Done" }, agentMessage: { event: "finished", sender: { id: "remote::worker", title: "Reviewer" }, text: "Done", }, }, }, }, ]; writer.commitTimeline(SERVER_ID, "agent-1", value); await writer.flush(); expect(await createCache(storage).readTimeline(SERVER_ID, "agent-1")).toEqual(value); });