返回 DeepSeek-Reasonix
transcript-memory-pressure.test.ts
根目录 / desktop / frontend / src / __tests__ / transcript-memory-pressure.test.ts
1 import assert from "node:assert/strict";
2 import { TranscriptStore } from "../lib/transcriptStore";
3 import { initialState, reducer } from "../lib/useController";
4 import { FakeBackend } from "./helpers/transcriptFakeBackend";
5 import { hasReusableCachedTranscript } from "../lib/hydrateHistoryApply";
6
7 const body = "full content ".repeat(1000);
8 const backend = new FakeBackend([{ role: "user", content: "prompt" }, { role: "assistant", content: "preview" }],
9 new Map([["s1:r0:m1:o0:content", body]]));
10 const store = new TranscriptStore(backend, { historyBodyBudgetBytes: 1024, maxResidentSessions: 1 });
11 store.setPinned("background", true);
12 await store.loadLatest("background", "/background");
13 assert.equal(await store.requestFullContent("background", "s1:r0:m1:o0", "content"), body, "explicit content remains complete");
14 assert.ok(store.totalBodyBytes() <= 1024, "invisible expanded body is reclaimed even with a live pin");
15 assert.ok(store.isResident("background", "/background"), "memory pressure cannot retire the running session");
16 const reads = backend.contentCalls.length;
17 assert.equal(await store.requestFullContent("background", "s1:r0:m1:o0", "content"), body);
18 assert.ok(backend.contentCalls.length > reads, "reclaimed content remains reachable");
19 store.setPinned("background", false);
20 const state = { ...initialState, historyPrefixCount: 1, historyTotalTurns: 12, historyOlderLoading: true, items: [
21 { kind: "user" as const, id: "history", text: "cached" },
22 { kind: "user" as const, id: "pending", text: "pending" },
23 ] };
24 let released = false;
25 store.subscribe("background", change => {
26 if (change.evictedPath === "/background") {
27 released = true;
28 const next = reducer(state, { type: "history_cache_evicted" });
29 assert.deepEqual(next.items.map(item => item.id), ["pending"], "eviction releases cached rows but preserves pending input");
30 assert.equal(hasReusableCachedTranscript(next, {}), false, "pending input cannot masquerade as resident history without a fingerprint");
31 assert.equal(next.historyOlderLoading, false, "eviction releases superseded paging indicators");
32 }
33 });
34 await store.loadLatest("other", "/other");
35 assert.ok(released, "LRU eviction notifies the controller to release its references");
36 assert.ok(!store.isResident("background", "/background"));
37
38 // Canonical bodies can reveal tool rows, so reclamation must also remove their
39 // structural projection rather than publishing only field patches.
40 const canonical = JSON.stringify({ role: "assistant", content: "expanded", tool_calls: [
41 { id: "revealed-call", name: "bash", arguments: "x".repeat(4000) },
42 ] });
43 const canonicalBackend = new FakeBackend([], new Map([["owner:canonicalMessage", canonical]]));
44 const canonicalStore = new TranscriptStore(canonicalBackend, { historyBodyBudgetBytes: 1024 });
45 canonicalStore.noteActiveTab("canonical");
46 canonicalStore.setPinned("canonical", true);
47 canonicalStore.installSlice("canonical", "/canonical", {
48 entries: [{ entryId: "owner", turn: 1, order: 0, message: { role: "assistant", content: "preview" },
49 refs: [{ entryId: "owner", field: "canonicalMessage", size: canonical.length, chunks: 2, revision: 1, digest: "body" }] }],
50 nextCursor: "", hasOlder: false, totalTurns: 1, startTurn: 1, endTurn: 1, stale: false, revision: 1, digest: "body",
51 });
52 let removedTool = false;
53 canonicalStore.subscribe("canonical", change => {
54 if (change.projection?.removeIds?.includes("revealed-call")) removedTool = true;
55 });
56 await canonicalStore.requestFullContent("canonical", "owner", "content");
57 canonicalStore.noteActiveTab("other", "canonical");
58 assert.ok(removedTool, "reclamation publishes removal of tool rows revealed by an expanded canonical body");
59 assert.ok(canonicalStore.isResident("canonical", "/canonical"), "structural reclamation preserves the live session");
60 {
61 const backend = new FakeBackend([{ role: "user", content: "u" }, { role: "assistant", content: "a" }]);
62 const store = new TranscriptStore(backend, { maxResidentSessions: 3 });
63 let previous: string | undefined;
64 for (let i = 0; i < 8; i++) {
65 const tab = `active-${i}`;
66 store.noteActiveTab(tab, previous);
67 await store.loadLatest(tab, `/s/${i}.jsonl`);
68 assert.equal(store.residentSessionCount(), Math.min(i + 1, 3), "continuous navigation includes active window in resident cap");
69 assert.equal(store.isResident(tab, `/s/${i}.jsonl`), true, "active reader is protected");
70 previous = tab;
71 }
72 const protectedStore = new TranscriptStore(backend, { maxResidentSessions: 1 });
73 for (const tab of ["running-a", "running-b"]) {
74 protectedStore.setPinned(tab, true);
75 await protectedStore.loadLatest(tab, `/s/${tab}`);
76 }
77 assert.equal(protectedStore.residentSessionCount(), 2, "budget pressure does not evict live execution owners");
78 protectedStore.setPinned("running-a", false);
79 assert.equal(protectedStore.residentSessionCount(), 1, "ending a live pin immediately restores the cache budget");
80 }
81
82 console.log("transcript memory pressure: passed");
83
83 lines TYPESCRIPT