返回 DeepSeek-Reasonix
sessionTarget.ts
根目录 / desktop / frontend / src / app-runtime / sessionTarget.ts
1 import { sessionIdentityBaseKey, sessionIdentityStableKey, type SessionIdentityRef } from "../lib/sessionIdentity";
2
3 export type SessionIdentityInput = {
4 tabId?: string;
5 session?: SessionIdentityRef | null;
6 /** TabMeta compatibility identity: remote tab metas publish the canonical
7 * session id here and never carry a SessionRef (desktop/remote_projects.go). */
8 sessionId?: string;
9 remote?: { hostId: string } | null;
10 sessionPath?: string;
11 sessionGeneration?: number;
12 scope?: string;
13 workspaceRoot?: string;
14 topicId?: string;
15 };
16
17 /** Runtime session identity; intentionally distinct from draft/workspace keys. */
18 export function sessionIdentityKey(input: SessionIdentityInput): string {
19 const canonical = sessionIdentityStableKey(input);
20 if (canonical) return canonical;
21 // One remote tab rotates through many canonical sessions, so the id must
22 // fence them apart even when the tab, topic and path are reused. The
23 // generation is deliberately excluded here: a remote tab's generation is
24 // its event-pump reconnect counter, not a session identity.
25 const sessionId = input.sessionId?.trim();
26 if (sessionId) return sessionIdentityBaseKey({ session: { hostId: input.remote?.hostId || "local", sessionId } });
27 return [
28 "topic",
29 input.scope ?? "",
30 input.workspaceRoot ?? "",
31 input.topicId ?? "",
32 input.tabId ?? "",
33 ].join("\u0000");
34 }
35
36 export type SessionSurfaceOwnership = Readonly<{
37 revision: number;
38 tabId: string;
39 sessionKey: string;
40 }>;
41
42 /** Commit-owned UI fence; A → B → A advances revision and never revives A. */
43 export function createSessionSurfaceFence() {
44 let revision = 0;
45 let current: SessionSurfaceOwnership | undefined;
46 const owns = (ownership: SessionSurfaceOwnership): boolean => Boolean(
47 current
48 && current.revision === ownership.revision
49 && current.tabId === ownership.tabId
50 && current.sessionKey === ownership.sessionKey,
51 );
52 return {
53 commit(tabId: string | undefined, sessionKey: string): SessionSurfaceOwnership | undefined {
54 if (!tabId) {
55 if (current) revision += 1;
56 current = undefined;
57 return undefined;
58 }
59 if (!current || current.tabId !== tabId || current.sessionKey !== sessionKey) revision += 1;
60 current = Object.freeze({ revision, tabId, sessionKey });
61 return current;
62 },
63 capture(): SessionSurfaceOwnership | undefined {
64 return current;
65 },
66 owns,
67 ownsUnknown(ownership: unknown): boolean {
68 if (!ownership || typeof ownership !== "object") return false;
69 return owns(ownership as SessionSurfaceOwnership);
70 },
71 dispose(): void {
72 revision += 1;
73 current = undefined;
74 },
75 };
76 }
77
77 lines TYPESCRIPT