@@ -27,6 +27,7 @@ import {
2727 hasOpenInvestigation ,
2828 pollSettledTranscript ,
2929} from "./settled-transcript" ;
30+ import { navigateIntentApplies } from "./turn-navigation" ;
3031import { teardownCancelsTurn , unmountTeardown } from "./turn-teardown" ;
3132import { useAgentMessageQuota } from "./useAgentMessageQuota" ;
3233import { useTriggerUriResolver } from "./useTriggerUriResolver" ;
@@ -103,6 +104,11 @@ export function DashboardAgentChat({
103104 const location = useLocation ( ) ;
104105 const toast = useToast ( ) ;
105106
107+ // The path this chat last rendered on. React never unmounts on a page teardown, so an
108+ // unmount whose live URL has moved is the router having navigated out from under it.
109+ const renderedPathRef = useRef ( location . pathname ) ;
110+ renderedPathRef . current = location . pathname ;
111+
106112 const prefilledSeq = useRef < number | undefined > ( undefined ) ;
107113 useEffect ( ( ) => {
108114 if ( ! prefill || prefilledSeq . current === prefill . seq ) return ;
@@ -287,10 +293,27 @@ export function DashboardAgentChat({
287293 navigatedRef . current = new Set ( ) ;
288294 pendingNavigateIntents ( initialMessages , navigatedRef . current ) ;
289295 }
296+ // Where the running turn was asked for. Never cleared on settle: the navigate intent can be
297+ // committed alongside the status going ready, and it is the started-at path it belongs to.
298+ const turnStartedPathRef = useRef < string | null > ( null ) ;
299+ const turnWasInFlight = useRef ( false ) ;
300+ useEffect ( ( ) => {
301+ const inFlight = status === "submitted" || status === "streaming" ;
302+ if ( inFlight && ! turnWasInFlight . current ) turnStartedPathRef . current = renderedPathRef . current ;
303+ turnWasInFlight . current = inFlight ;
304+ } , [ status ] ) ;
305+
290306 useEffect ( ( ) => {
291307 const pending = pendingNavigateIntents ( messages , navigatedRef . current ! ) ;
292308 const target = pending . at ( - 1 ) ;
293- if ( target ) void goTo ( target ) ;
309+ if ( ! target ) return ;
310+ // Marked handled above either way, so a dropped navigation stays dropped and the answer's
311+ // own button remains the way to take it.
312+ const applies = navigateIntentApplies ( {
313+ startedPath : turnStartedPathRef . current ,
314+ currentPath : renderedPathRef . current ,
315+ } ) ;
316+ if ( applies ) void goTo ( target ) ;
294317 } , [ messages , goTo ] ) ;
295318
296319 const watchProposedRef = useRef < Set < string > | null > ( null ) ;
@@ -309,11 +332,6 @@ export function DashboardAgentChat({
309332 aiStop ( ) ;
310333 } , [ transport , chatId , aiStop ] ) ;
311334
312- // The path this chat last rendered on. React never unmounts on a page teardown, so an
313- // unmount whose live URL has moved is the router having navigated out from under it.
314- const renderedPathRef = useRef ( location . pathname ) ;
315- renderedPathRef . current = location . pathname ;
316-
317335 const teardownRef = useRef < ( ) => void > ( ( ) => { } ) ;
318336 teardownRef . current = ( ) => {
319337 if ( status !== "streaming" && status !== "submitted" ) return ;
0 commit comments