Skip to content

Commit df028aa

Browse files
committed
Drop stale sent-history loads from startNewSession
Share a generation counter between the hydrate effect and startNewSession so a late loadSentMessages cannot overwrite browse after a newer session load has already started.
1 parent 014f78d commit df028aa

1 file changed

Lines changed: 23 additions & 9 deletions

File tree

src/tui/hooks/use-message-pipeline.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ export function useMessagePipeline({
116116
const sendCounterRef = useRef(0);
117117
const lastSentMessageRef = useRef<string>("");
118118
const quotaAutoRetryFiredRef = useRef(false);
119+
// Bumped on every sent-history load (and on effect cleanup) so only the latest
120+
// loadSentMessages result can write browse state — startNewSession and the
121+
// hydrate effect share this so neither path can apply a stale session's history.
122+
const sentHistoryLoadGenRef = useRef(0);
119123

120124
sendMessageRef.current = (message: OutboundUserMessage) => {
121125
lastSentMessageRef.current = message.text;
@@ -210,6 +214,17 @@ export function useMessagePipeline({
210214

211215
requestStopRef.current = requestStop;
212216

217+
// Start a sent-history load; only the newest generation may apply. Shared by
218+
// startNewSession and the hydrate effect so rapid /clear or session switches
219+
// cannot write browse from a prior id after a newer load has begun.
220+
const loadSentHistoryBrowse = (sessionId: string) => {
221+
const gen = ++sentHistoryLoadGenRef.current;
222+
void loadSentMessages(cwd, sessionId).then((sent) => {
223+
if (gen !== sentHistoryLoadGenRef.current) return;
224+
setSentHistoryBrowse(createSentHistoryBrowse(sent));
225+
});
226+
};
227+
213228
const startNewSessionRef = useRef<() => void>(() => undefined);
214229
startNewSessionRef.current = () => {
215230
sendAbortRef.current?.abort();
@@ -233,10 +248,10 @@ export function useMessagePipeline({
233248
subAgentSessions?.clear();
234249
onNewSession?.();
235250
if (getSessionId !== undefined) {
236-
void loadSentMessages(cwd, getSessionId()).then((sent) => {
237-
setSentHistoryBrowse(createSentHistoryBrowse(sent));
238-
});
251+
loadSentHistoryBrowse(getSessionId());
239252
} else {
253+
// Invalidate any in-flight load before clearing browse for a no-session path.
254+
sentHistoryLoadGenRef.current++;
240255
setSentHistoryBrowse(createSentHistoryBrowse([]));
241256
}
242257
scroll.scrollToBottom();
@@ -248,14 +263,13 @@ export function useMessagePipeline({
248263
// session switch or unmount cannot write history from a prior session id.
249264
useEffect(() => {
250265
if (getSessionId === undefined) return;
251-
let cancelled = false;
252-
void loadSentMessages(cwd, getSessionId()).then((sent) => {
253-
if (cancelled) return;
254-
setSentHistoryBrowse(createSentHistoryBrowse(sent));
255-
});
266+
loadSentHistoryBrowse(getSessionId());
256267
return () => {
257-
cancelled = true;
268+
sentHistoryLoadGenRef.current++;
258269
};
270+
// loadSentHistoryBrowse closes over cwd/setSentHistoryBrowse; re-run when the
271+
// session identity source or cwd changes.
272+
// eslint-disable-next-line react-hooks/exhaustive-deps
259273
}, [cwd, getSessionId]);
260274

261275
useEffect(() => {

0 commit comments

Comments
 (0)