返回 DeepSeek-Reasonix
transcriptSessionBinding.ts
根目录 / desktop / frontend / src / lib / transcriptSessionBinding.ts
1 import type { SessionTranscript } from "./transcriptStoreTypes";
2 import { releaseHistoryRead } from "./historyReadScope";
3
4 export type TranscriptTabBinding = {
5 key: string;
6 sessionPath: string;
7 bindingKey: string;
8 };
9
10 function legacySessionKeyFor(tabId: string, sessionPath: string): string {
11 return `${tabId}\n${sessionPath}`;
12 }
13
14 function stableSessionKeyFor(bindingKey: string): string | undefined {
15 return bindingKey.startsWith("s\0") ? `stable\0${bindingKey}` : undefined;
16 }
17
18 export function boundSessionKey(
19 bindings: Map<string, TranscriptTabBinding>,
20 tabId: string,
21 sessionPath: string,
22 ): string {
23 const binding = bindings.get(tabId);
24 return binding?.sessionPath === sessionPath ? binding.key : legacySessionKeyFor(tabId, sessionPath);
25 }
26
27 /** Eviction may retire an old canonical cache after its tab has been rebound. */
28 export function releaseTranscriptSessionRead(bindings: Map<string, TranscriptTabBinding>, session: SessionTranscript): void {
29 if (bindings.get(session.tabId)?.key === session.key) releaseHistoryRead(session.tabId);
30 }
31
32 export function bindTranscriptSession(
33 bindings: Map<string, TranscriptTabBinding>,
34 sessions: Map<string, SessionTranscript>,
35 tabId: string,
36 sessionPath: string,
37 bindingKey: string,
38 create: (key: string) => SessionTranscript,
39 evict: (session: SessionTranscript) => void,
40 touch: (session: SessionTranscript) => void,
41 ): boolean {
42 const previous = bindings.get(tabId);
43 const key = stableSessionKeyFor(bindingKey) ?? legacySessionKeyFor(tabId, sessionPath);
44 if (previous?.key === key && previous.sessionPath === sessionPath && previous.bindingKey === bindingKey) return false;
45 if (previous) releaseHistoryRead(tabId);
46 const replaced = Boolean(previous);
47 bindings.delete(tabId);
48 if (previous && (previous.key !== key || (!key.startsWith("stable\0") && previous.bindingKey !== bindingKey))) {
49 const previousSession = sessions.get(previous.key);
50 if (previousSession && !previous.key.startsWith("stable\0")) evict(previousSession);
51 }
52 let session = sessions.get(key);
53 if (!session) {
54 session = create(key);
55 sessions.set(key, session);
56 } else if (session.tabId !== tabId || session.sessionPath !== sessionPath) {
57 session.generation += 1;
58 session.pendingContent.clear();
59 for (const [otherTabId, binding] of bindings) {
60 if (binding.key === key) bindings.delete(otherTabId);
61 }
62 session.tabId = tabId;
63 session.sessionPath = sessionPath;
64 }
65 session.bindingKey = bindingKey;
66 bindings.set(tabId, { key, sessionPath, bindingKey });
67 touch(session);
68 return replaced;
69 }
70
71 export function detachTranscriptTab(
72 bindings: Map<string, TranscriptTabBinding>,
73 sessions: Map<string, SessionTranscript>,
74 tabId: string,
75 evict: (session: SessionTranscript) => void,
76 ): void {
77 releaseHistoryRead(tabId);
78 const binding = bindings.get(tabId);
79 bindings.delete(tabId);
80 if (binding && !binding.key.startsWith("stable\0")) {
81 const session = sessions.get(binding.key);
82 if (session) evict(session);
83 } else if (!binding) {
84 for (const session of Array.from(sessions.values())) {
85 if (session.tabId === tabId && !session.key.startsWith("stable\0")) evict(session);
86 }
87 }
88 }
89
89 lines TYPESCRIPT