@@ -44,6 +44,7 @@ import {
4444 createSessionEventSubscriberRegistry ,
4545 createSessionMessageSubscriberRegistry ,
4646 createToolEventRecipientRegistry ,
47+ type AgentEventHandlerOptions ,
4748} from "./server-chat.js" ;
4849import { loadGatewaySessionRow } from "./server-chat.load-gateway-session-row.runtime.js" ;
4950import { loadSessionEntry } from "./session-utils.js" ;
@@ -79,14 +80,16 @@ describe("agent event handler", () => {
7980 resolveSessionKeyForRun ?: ( runId : string ) => string | undefined ;
8081 lifecycleErrorRetryGraceMs ?: number ;
8182 isChatSendRunActive ?: ( runId : string ) => boolean ;
83+ clearTrackedActiveRun ?: AgentEventHandlerOptions [ "clearTrackedActiveRun" ] ;
8284 } ) {
8385 const nowSpy =
8486 params ?. now === undefined ? undefined : vi . spyOn ( Date , "now" ) . mockReturnValue ( params . now ) ;
8587 const broadcast = vi . fn ( ) ;
8688 const broadcastToConnIds = vi . fn ( ) ;
8789 const nodeSendToSession = vi . fn ( ) ;
8890 const clearAgentRunContext = vi . fn ( ) ;
89- const clearTrackedActiveRun = vi . fn ( ) ;
91+ const clearTrackedActiveRun =
92+ vi . fn < NonNullable < AgentEventHandlerOptions [ "clearTrackedActiveRun" ] > > ( ) ;
9093 const agentRunSeq = new Map < string , number > ( ) ;
9194 const chatRunState = createChatRunState ( ) ;
9295 const toolEventRecipients = createToolEventRecipientRegistry ( ) ;
@@ -107,7 +110,7 @@ describe("agent event handler", () => {
107110 loadGatewaySessionRowForSnapshot : loadGatewaySessionRow ,
108111 lifecycleErrorRetryGraceMs : params ?. lifecycleErrorRetryGraceMs ,
109112 isChatSendRunActive : params ?. isChatSendRunActive ,
110- clearTrackedActiveRun,
113+ clearTrackedActiveRun : params ?. clearTrackedActiveRun ?? clearTrackedActiveRun ,
111114 } ) ;
112115
113116 return {
@@ -1895,6 +1898,43 @@ describe("agent event handler", () => {
18951898 ) ;
18961899 } ) ;
18971900
1901+ it ( "keeps chat send retry guards while hiding terminal session projection" , ( ) => {
1902+ const trackedActiveRuns = new Map <
1903+ string ,
1904+ { sessionKey : string ; projectSessionActive ?: boolean }
1905+ > ( [ [ "client-run" , { sessionKey : "session-finished" } ] ] ) ;
1906+ const { chatRunState, handler } = createHarness ( {
1907+ clearTrackedActiveRun : ( { runId, clientRunId, sessionKey } ) => {
1908+ for ( const candidateRunId of new Set ( [ runId , clientRunId ] ) ) {
1909+ const entry = trackedActiveRuns . get ( candidateRunId ) ;
1910+ if ( entry ?. sessionKey === sessionKey ) {
1911+ entry . projectSessionActive = false ;
1912+ }
1913+ }
1914+ } ,
1915+ } ) ;
1916+ chatRunState . registry . add ( "provider-run" , {
1917+ sessionKey : "session-finished" ,
1918+ clientRunId : "client-run" ,
1919+ } ) ;
1920+
1921+ handler ( {
1922+ runId : "provider-run" ,
1923+ seq : 2 ,
1924+ stream : "lifecycle" ,
1925+ ts : 1_800 ,
1926+ data : {
1927+ phase : "end" ,
1928+ startedAt : 900 ,
1929+ endedAt : 1_700 ,
1930+ } ,
1931+ } ) ;
1932+
1933+ const retryGuard = trackedActiveRuns . get ( "client-run" ) ;
1934+ expect ( retryGuard ) . toBeDefined ( ) ;
1935+ expect ( retryGuard ?. projectSessionActive ) . toBe ( false ) ;
1936+ } ) ;
1937+
18981938 it ( "keeps aborted chat run markers through terminal lifecycle cleanup" , ( ) => {
18991939 const { broadcast, chatRunState, handler } = createHarness ( ) ;
19001940 chatRunState . registry . add ( "run-aborted" , {
0 commit comments