@@ -721,9 +721,7 @@ describe("tui-event-handlers: streaming watchdog", () => {
721721
722722 expect ( setActivityStatus ) . toHaveBeenLastCalledWith ( "idle" ) ;
723723 expect ( state . activeChatRunId ) . toBeNull ( ) ;
724- expect ( chatLog . addSystem ) . toHaveBeenCalledWith (
725- expect . stringContaining ( "streaming watchdog" ) ,
726- ) ;
724+ expect ( chatLog . addSystem ) . toHaveBeenCalledWith ( expect . stringContaining ( "streaming watchdog" ) ) ;
727725
728726 handlers . dispose ?.( ) ;
729727 } ) ;
@@ -751,8 +749,6 @@ describe("tui-event-handlers: streaming watchdog", () => {
751749
752750 vi . advanceTimersByTime ( 3_000 ) ;
753751
754- // 6s total, but the latest delta was only 3s ago, so the watchdog must not
755- // have fired yet.
756752 expect ( setActivityStatus ) . not . toHaveBeenCalledWith ( "idle" ) ;
757753 expect ( state . activeChatRunId ) . toBe ( "run-flow" ) ;
758754
@@ -784,8 +780,6 @@ describe("tui-event-handlers: streaming watchdog", () => {
784780
785781 vi . advanceTimersByTime ( 10_000 ) ;
786782
787- // After a normal final, the watchdog timer must have been cancelled and
788- // cannot later re-overwrite the status or emit the warning banner.
789783 const statusCalls = setActivityStatus . mock . calls . map ( ( c ) => c [ 0 ] ) ;
790784 expect ( statusCalls . filter ( ( s ) => s === "idle" ) . length ) . toBe ( 1 ) ;
791785 expect ( chatLog . addSystem ) . not . toHaveBeenCalledWith (
@@ -817,6 +811,47 @@ describe("tui-event-handlers: streaming watchdog", () => {
817811 handlers . dispose ?.( ) ;
818812 } ) ;
819813
814+ it ( "does not let an older run steal the active run watchdog" , ( ) => {
815+ const { state, chatLog, setActivityStatus, handlers } = createHarness ( {
816+ streamingWatchdogMs : 5_000 ,
817+ } ) ;
818+
819+ handlers . handleChatEvent ( {
820+ runId : "run-old" ,
821+ sessionKey : state . currentSessionKey ,
822+ state : "delta" ,
823+ message : { content : "old" } ,
824+ } satisfies ChatEvent ) ;
825+
826+ vi . advanceTimersByTime ( 5_001 ) ;
827+ expect ( state . activeChatRunId ) . toBeNull ( ) ;
828+
829+ handlers . handleChatEvent ( {
830+ runId : "run-new" ,
831+ sessionKey : state . currentSessionKey ,
832+ state : "delta" ,
833+ message : { content : "new" } ,
834+ } satisfies ChatEvent ) ;
835+ expect ( state . activeChatRunId ) . toBe ( "run-new" ) ;
836+
837+ vi . advanceTimersByTime ( 3_000 ) ;
838+
839+ handlers . handleChatEvent ( {
840+ runId : "run-old" ,
841+ sessionKey : state . currentSessionKey ,
842+ state : "delta" ,
843+ message : { content : "old again" } ,
844+ } satisfies ChatEvent ) ;
845+
846+ vi . advanceTimersByTime ( 2_001 ) ;
847+
848+ expect ( setActivityStatus ) . toHaveBeenLastCalledWith ( "idle" ) ;
849+ expect ( state . activeChatRunId ) . toBeNull ( ) ;
850+ expect ( chatLog . addSystem ) . toHaveBeenCalledTimes ( 2 ) ;
851+
852+ handlers . dispose ?.( ) ;
853+ } ) ;
854+
820855 it ( "dispose clears a pending watchdog without firing it" , ( ) => {
821856 const { setActivityStatus, chatLog, handlers, state } = createHarness ( {
822857 streamingWatchdogMs : 5_000 ,
0 commit comments