@@ -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,24 +245,28 @@ 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 ( ) ;
240255 forceRender ( ( n ) => n + 1 ) ;
241256 } ;
242257 const startNewSession = ( ) => startNewSessionRef . current ( ) ;
243258
244- // Send the initial task once the App (and its gate listeners) is mounted, so
245- // the run is driven through the same abortable path as interactive sends .
259+ // Hydrate sent-message history for the active session. Cancel stale loads so a
260+ // session switch or unmount cannot write history from a prior session id .
246261 useEffect ( ( ) => {
247262 if ( getSessionId === undefined ) return ;
248- void loadSentMessages ( cwd , getSessionId ( ) ) . then ( ( sent ) => {
249- setSentHistoryBrowse ( createSentHistoryBrowse ( sent ) ) ;
250- } ) ;
263+ loadSentHistoryBrowse ( getSessionId ( ) ) ;
264+ return ( ) => {
265+ sentHistoryLoadGenRef . current ++ ;
266+ } ;
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
251270 } , [ cwd , getSessionId ] ) ;
252271
253272 useEffect ( ( ) => {
0 commit comments