Skip to content

Commit 41481f5

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 010db39 commit 41481f5

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
@@ -114,6 +114,10 @@ export function useMessagePipeline({
114114
const sendCounterRef = useRef(0);
115115
const lastSentMessageRef = useRef<string>("");
116116
const quotaAutoRetryFiredRef = useRef(false);
117+
// Bumped on every sent-history load (and on effect cleanup) so only the latest
118+
// loadSentMessages result can write browse state — startNewSession and the
119+
// hydrate effect share this so neither path can apply a stale session's history.
120+
const sentHistoryLoadGenRef = useRef(0);
117121

118122
sendMessageRef.current = (message: OutboundUserMessage) => {
119123
lastSentMessageRef.current = message.text;
@@ -208,6 +212,17 @@ export function useMessagePipeline({
208212

209213
requestStopRef.current = requestStop;
210214

215+
// Start a sent-history load; only the newest generation may apply. Shared by
216+
// startNewSession and the hydrate effect so rapid /clear or session switches
217+
// cannot write browse from a prior id after a newer load has begun.
218+
const loadSentHistoryBrowse = (sessionId: string) => {
219+
const gen = ++sentHistoryLoadGenRef.current;
220+
void loadSentMessages(cwd, sessionId).then((sent) => {
221+
if (gen !== sentHistoryLoadGenRef.current) return;
222+
setSentHistoryBrowse(createSentHistoryBrowse(sent));
223+
});
224+
};
225+
211226
const startNewSessionRef = useRef<() => void>(() => undefined);
212227
startNewSessionRef.current = () => {
213228
sendAbortRef.current?.abort();
@@ -230,10 +245,10 @@ export function useMessagePipeline({
230245
subAgentSessions?.clear();
231246
onNewSession?.();
232247
if (getSessionId !== undefined) {
233-
void loadSentMessages(cwd, getSessionId()).then((sent) => {
234-
setSentHistoryBrowse(createSentHistoryBrowse(sent));
235-
});
248+
loadSentHistoryBrowse(getSessionId());
236249
} else {
250+
// Invalidate any in-flight load before clearing browse for a no-session path.
251+
sentHistoryLoadGenRef.current++;
237252
setSentHistoryBrowse(createSentHistoryBrowse([]));
238253
}
239254
scroll.scrollToBottom();
@@ -245,14 +260,13 @@ export function useMessagePipeline({
245260
// session switch or unmount cannot write history from a prior session id.
246261
useEffect(() => {
247262
if (getSessionId === undefined) return;
248-
let cancelled = false;
249-
void loadSentMessages(cwd, getSessionId()).then((sent) => {
250-
if (cancelled) return;
251-
setSentHistoryBrowse(createSentHistoryBrowse(sent));
252-
});
263+
loadSentHistoryBrowse(getSessionId());
253264
return () => {
254-
cancelled = true;
265+
sentHistoryLoadGenRef.current++;
255266
};
267+
// loadSentHistoryBrowse closes over cwd/setSentHistoryBrowse; re-run when the
268+
// session identity source or cwd changes.
269+
// eslint-disable-next-line react-hooks/exhaustive-deps
256270
}, [cwd, getSessionId]);
257271

258272
useEffect(() => {

0 commit comments

Comments
 (0)