返回 DeepSeek-Reasonix
turnBoundaryReads.ts
根目录 / desktop / frontend / src / lib / turnBoundaryReads.ts
1 // The per-tab reads that index a session's persisted turns: the checkpoints its
2 // rewind menu offers and the boundaries its fork entries offer. A turn that
3 // commits, and a transcript that is replaced, move both, so the two are read
4 // together where a surface settles; each read keeps its own stale-response
5 // guard, because one answering late must not overwrite a newer answer.
6
7 import { asArray } from "./array";
8 import { app } from "./bridge";
9 import { createForkTargetsRefresh, type ForkTurnAction } from "./forkTurn";
10 import type { CheckpointMeta } from "./types";
11
12 /** The actions these reads dispatch into a tab's state. */
13 export type TurnBoundaryAction = { type: "checkpoints"; checkpoints: CheckpointMeta[] } | ForkTurnAction;
14
15 /** The turn-index reads one controller binds, dispatch included. */
16 export interface TurnBoundaryReads {
17 /** Drops in-flight checkpoint and fork reads for a replaced session binding. */
18 invalidateCheckpoints(tabId: string): void;
19 /** Re-reads a tab's checkpoints alone, for a reconcile that patches only its active turn. */
20 refreshCheckpoints(tabId: string): Promise<void>;
21 /** Re-reads the checkpoints and the fork boundaries a settled turn changes. */
22 refreshTurnBoundaries(tabId: string): Promise<void>;
23 /**
24 * Applies the checkpoints one hydration read returned and reads the fork
25 * boundaries that settle with them; undefined checkpoints dispatch nothing.
26 */
27 settleCheckpoints(tabId: string, checkpoints: CheckpointMeta[] | undefined): Promise<void>;
28 }
29
30 /** Binds the turn-index reads to one dispatch sink. */
31 export function createTurnBoundaryReads(dispatch: (tabId: string, action: TurnBoundaryAction) => void): TurnBoundaryReads {
32 const checkpointSeq = new Map<string, number>();
33 const bumpCheckpoints = (tabId: string): number => {
34 const seq = (checkpointSeq.get(tabId) ?? 0) + 1;
35 checkpointSeq.set(tabId, seq);
36 return seq;
37 };
38 const refreshCheckpoints = async (tabId: string): Promise<void> => {
39 const seq = bumpCheckpoints(tabId);
40 const checkpoints = await app.CheckpointsForTab(tabId).catch(() => undefined);
41 if (checkpointSeq.get(tabId) !== seq || checkpoints === undefined) return;
42 dispatch(tabId, { type: "checkpoints", checkpoints: asArray(checkpoints) });
43 };
44 const forkTargets = createForkTargetsRefresh(dispatch);
45 return {
46 invalidateCheckpoints: (tabId) => {
47 bumpCheckpoints(tabId);
48 forkTargets.invalidate(tabId);
49 },
50 refreshCheckpoints,
51 refreshTurnBoundaries: (tabId) => Promise.all([refreshCheckpoints(tabId), forkTargets.refresh(tabId)]).then(() => undefined),
52 settleCheckpoints: async (tabId, checkpoints) => {
53 if (checkpoints !== undefined) dispatch(tabId, { type: "checkpoints", checkpoints: asArray(checkpoints) });
54 await forkTargets.refresh(tabId);
55 },
56 };
57 }
58
58 lines TYPESCRIPT