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" }, ]); } async function cacheWithoutBackgroundVisibility() { const storage = new MemoryStorage(); const writer = createCache(storage); const checkpoint = { agents: { generation: "g", afterSeq: 12 }, projects: { generation: "g", afterSeq: 4 }, workspaces: { generation: "g", afterSeq: 8 }, }; const baseline = directory(checkpoint); commitDirectory(writer, SERVER_ID, baseline); writer.commitTimeline(SERVER_ID, "agent-1", timeline()); await writer.flush(); const key = `${SERVER_ID}:workspace:workspace-1`; const row = storage.rows.get(key)!; const payload = JSON.parse(row.payload) as Record; delete payload.background; storage.rows.set(key, { ...row, payload: JSON.stringify(payload) }); return { storage, baseline, reader: createCache(storage) }; } async function expectLegacyWorkspaceRefresh({ storage, baseline, reader, }: Awaited>): Promise { const restored = await reader.readDirectory(SERVER_ID); expect(restored.workspaces.size).toBe(0); expect(restored.checkpoint).toEqual({ agents: baseline.checkpoint.agents, projects: baseline.checkpoint.projects, }); expect(restored.agents).toEqual(baseline.agents); expect(restored.projects).toEqual(baseline.projects); expect(await reader.readTimeline(SERVER_ID, "agent-1")).toEqual(timeline()); expect((await createCache(storage).readDirectory(SERVER_ID)).checkpoint).toEqual( restored.checkpoint, ); baseline.workspaces.set( "workspace-1", normalizeWorkspaceDescriptor({ ...workspacePayload(), background: true }), ); reader.replaceDirectoryBaseline(SERVER_ID, baseline); await reader.flush(); const refreshed = await createCache(storage).readDirectory(SERVER_ID); expect(refreshed.workspaces.get("workspace-1")?.background).toBe(true); expect(refreshed.checkpoint).toEqual(baseline.checkpoint); } describe("ReplicaCache", () => { it("persists the complete newest turn beyond the cache item target", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const items = Array.from({ length: 60 }, (_, index) => ({ ...timelineItem(`item-${index}`), id: `item-${index}`, turnId: "long-run", })); writer.commitTimeline(SERVER_ID, "agent-1", { ...timeline(), items }); await writer.flush(); const restored = await createCache(storage).readTimeline(SERVER_ID, "agent-1"); expect(restored?.items.map((item) => item.id)).toEqual(items.map((item) => item.id)); expect(restored?.range).toBeNull(); }); it("preserves workspace sorting timestamps across cache reopen", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const baseline = directory(); const workspace = normalizeWorkspaceDescriptor({ ...workspacePayload(), createdAt: "2026-07-18T08:00:00.000Z", lastUserMessageAt: "2026-07-18T09:00:00.000Z", }); baseline.workspaces.set(workspace.id, workspace); commitDirectory(writer, SERVER_ID, baseline); await writer.flush(); const reader = createCache(storage); expect((await reader.readDirectory(SERVER_ID)).workspaces.get(workspace.id)).toEqual(workspace); expect((await reader.readWorkspace(SERVER_ID, workspace.id))?.workspace).toEqual(workspace); }); it.each(["createdAt", "lastUserMessageAt"])( "refetches workspaces cached without %s", async (field) => { const cached = await cacheWithoutBackgroundVisibility(); const key = `${SERVER_ID}:workspace:workspace-1`; const row = cached.storage.rows.get(key)!; const payload = JSON.parse(row.payload); payload.background = false; delete payload[field]; cached.storage.rows.set(key, { ...row, payload: JSON.stringify(payload) }); await expectLegacyWorkspaceRefresh(cached); }, ); it.each([true, false, undefined])( "preserves background visibility (%s) when reopening a cached workspace", async (background) => { const storage = new MemoryStorage(); const writer = createCache(storage); const baseline = directory(); baseline.workspaces.set( "workspace-1", normalizeWorkspaceDescriptor({ ...workspacePayload(), background }), ); commitDirectory(writer, SERVER_ID, baseline); await writer.flush(); const reader = createCache(storage); expect( (await reader.readDirectory(SERVER_ID)).workspaces.get("workspace-1")?.background, ).toBe(background ?? false); expect((await reader.readWorkspace(SERVER_ID, "workspace-1"))?.workspace.background).toBe( background ?? false, ); }, ); it("refetches workspaces cached without background visibility after a directory read", async () => { const cached = await cacheWithoutBackgroundVisibility(); await expectLegacyWorkspaceRefresh(cached); }); it("refetches workspaces cached without background visibility after a workspace read", async () => { const cached = await cacheWithoutBackgroundVisibility(); expect(await cached.reader.readWorkspace(SERVER_ID, "workspace-1")).toBeUndefined(); await expectLegacyWorkspaceRefresh(cached); }); 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("keeps a background agent background across cache reopen", async () => { const storage = new MemoryStorage(); const writer = createCache(storage); const helper = { ...agent("helper"), background: true }; const visible = agent("visible"); writer.commitDirectoryMutations(SERVER_ID, [ { kind: "agent", type: "upsert", id: helper.id, value: helper }, { kind: "agent", type: "upsert", id: visible.id, value: visible }, ]); await writer.flush(); const restored = await createCache(storage).readDirectory(SERVER_ID); expect(restored.agents.get("helper")?.background).toBe(true); expect(restored.agents.get("visible")?.background).toBe(false); }); 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("does not return an accepted agent that storage rejected", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); storage.persistentWriteFailure = new Error("disk unavailable"); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); const result = await cache.readAgent(SERVER_ID, "agent-1"); expect(storage.rows.size).toBe(0); expect(result).toBeUndefined(); }); 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 an in-flight read across host removal and reactivation", 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; cache.setHosts([]); cache.setHosts([SERVER_ID]); release.resolve(); expect(await reading).toBeUndefined(); }); 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("retries a timeline read when that same timeline changes in flight", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); cache.commitTimeline(SERVER_ID, "agent-1", timeline("Old")); await cache.flush(); storage.onRead = () => { storage.onRead = null; cache.commitTimeline(SERVER_ID, "agent-1", timeline("New")); }; expect((await cache.readTimeline(SERVER_ID, "agent-1"))?.items).toEqual([timelineItem("New")]); expect(storage.reads).toHaveLength(2); }); it("discards a requested row removed by a directory baseline replacement in flight", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); await cache.flush(); const replacement = directory(); replacement.agents.clear(); storage.onRead = () => { storage.onRead = null; cache.replaceDirectoryBaseline(SERVER_ID, replacement); }; expect(await cache.readAgent(SERVER_ID, "agent-1")).toBeUndefined(); expect(storage.reads).toHaveLength(2); }); 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("reads a persisted timeline across 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); }); describe.each(["timeline", "directory"] as const)( "while another agent streams %s updates", (kind) => { async function streamingCache() { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); cache.commitTimeline(SERVER_ID, "agent-1", timeline()); await cache.flush(); const readsBefore = storage.reads.length; const writesBefore = storage.writes; let chunks = 0; storage.onRead = () => { if (chunks >= 200) return; chunks += 1; if (kind === "timeline") { cache.commitTimeline(SERVER_ID, "agent-2", { ...timeline(`chunk ${chunks}`), agentId: "agent-2", }); } else { cache.commitDirectoryMutations( SERVER_ID, [{ type: "upsert", kind: "agent", id: "agent-2", value: agent("agent-2") }], { agents: { generation: "g", afterSeq: 12 + chunks } }, ); } }; return { cache, expectReads: (count: number, writes = 0) => { expect(storage.reads.length - readsBefore).toBe(count); expect(storage.writes - writesBefore).toBe(writes); expect(chunks).toBe(count); }, }; } it("reads another cached agent without waiting for the stream to stop", async () => { const { cache, expectReads } = await streamingCache(); expect((await cache.readAgent(SERVER_ID, "agent-1"))?.title).toBe("Cached agent"); expectReads(1); await cache.flush(); }); it("reads another cached workspace without waiting for the stream to stop", async () => { const { cache, expectReads } = await streamingCache(); expect((await cache.readWorkspace(SERVER_ID, "workspace-1"))?.workspace.name).toBe("main"); expectReads(2, 1); await cache.flush(); }); it("reads another cached timeline without waiting for the stream to stop", async () => { const { cache, expectReads } = await streamingCache(); expect(await cache.readTimeline(SERVER_ID, "agent-1")).toEqual(timeline()); expectReads(1); await cache.flush(); }); }, ); it("reads the directory after persisting a pending baseline replacement", async () => { const storage = new MemoryStorage(); const cache = createCache(storage); commitDirectory(cache, SERVER_ID, directory()); cache.commitTimeline(SERVER_ID, "agent-1", timeline()); await cache.flush(); const replacement = directory({ agents: { generation: "g2", afterSeq: 1 } }); replacement.projects.clear(); replacement.agents.set("agent-2", agent("agent-2")); cache.replaceDirectoryBaseline(SERVER_ID, replacement); const read = await cache.readDirectory(SERVER_ID); expect([...read.agents.keys()].sort()).toEqual(["agent-1", "agent-2"]); expect(read.projects.size).toBe(0); expect(read.checkpoint).toEqual({ agents: { generation: "g2", afterSeq: 1 } }); expect(await cache.readTimeline(SERVER_ID, "agent-1")).toEqual(timeline()); expect(storage.writes).toBe(2); }); }); 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); });