@@ -16,6 +16,7 @@ const mocks = vi.hoisted(() => ({
1616 resolveActiveEmbeddedRunHandleSessionIdBySessionFile : vi . fn ( ) ,
1717 resolveEmbeddedSessionLane : vi . fn ( ( key : string ) => `session:${ key } ` ) ,
1818 waitForEmbeddedAgentRunEnd : vi . fn ( ) ,
19+ clearDiagnosticSessionActivity : vi . fn ( ) ,
1920 getDiagnosticSessionActivitySnapshot : vi . fn ( ) ,
2021 diag : {
2122 debug : vi . fn ( ) ,
@@ -68,6 +69,7 @@ vi.mock("./diagnostic-runtime.js", () => ({
6869} ) ) ;
6970
7071vi . mock ( "./diagnostic-run-activity.js" , ( ) => ( {
72+ clearDiagnosticSessionActivity : mocks . clearDiagnosticSessionActivity ,
7173 getDiagnosticSessionActivitySnapshot : mocks . getDiagnosticSessionActivitySnapshot ,
7274} ) ) ;
7375
@@ -98,6 +100,13 @@ function resetMocks() {
98100 mocks . resolveActiveEmbeddedRunHandleSessionIdBySessionFile . mockReset ( ) ;
99101 mocks . resolveEmbeddedSessionLane . mockClear ( ) ;
100102 mocks . waitForEmbeddedAgentRunEnd . mockReset ( ) ;
103+ mocks . clearDiagnosticSessionActivity . mockReset ( ) ;
104+ mocks . clearDiagnosticSessionActivity . mockReturnValue ( {
105+ activeEmbeddedRunsCleared : 0 ,
106+ activeToolsCleared : 0 ,
107+ activeModelCallsCleared : 0 ,
108+ activitiesCleared : 0 ,
109+ } ) ;
101110 mocks . getDiagnosticSessionActivitySnapshot . mockReset ( ) ;
102111 mocks . getDiagnosticSessionActivitySnapshot . mockReturnValue ( { } ) ;
103112 mocks . diag . debug . mockReset ( ) ;
@@ -453,11 +462,51 @@ describe("stuck session recovery", () => {
453462 } ) ;
454463
455464 expect ( mocks . resetCommandLane ) . toHaveBeenCalledWith ( "session:agent:main:main" ) ;
465+ expect ( mocks . clearDiagnosticSessionActivity ) . toHaveBeenCalledWith ( {
466+ sessionId : "stale-session" ,
467+ sessionKey : "agent:main:main" ,
468+ reason : "stuck_recovery:no_active_work" ,
469+ } ) ;
456470 expect ( warnLogMessages ( ) ) . toEqual ( [
457471 "stuck session recovery outcome: status=noop action=none sessionId=stale-session sessionKey=agent:main:main lane=session:agent:main:main reason=no_active_work" ,
458472 ] ) ;
459473 } ) ;
460474
475+ it ( "clears orphaned diagnostic tool activity when recovery releases stale queued state" , async ( ) => {
476+ mocks . resolveActiveEmbeddedRunHandleSessionId . mockReturnValue ( undefined ) ;
477+ mocks . resolveActiveEmbeddedRunSessionId . mockReturnValue ( undefined ) ;
478+ mocks . isEmbeddedAgentRunActive . mockReturnValue ( false ) ;
479+ mocks . resetCommandLane . mockReturnValue ( 0 ) ;
480+ mocks . clearDiagnosticSessionActivity . mockReturnValue ( {
481+ activeEmbeddedRunsCleared : 0 ,
482+ activeToolsCleared : 1 ,
483+ activeModelCallsCleared : 0 ,
484+ activitiesCleared : 1 ,
485+ } ) ;
486+
487+ const outcome = await recoverStuckDiagnosticSession ( {
488+ sessionId : "stale-tool-session" ,
489+ sessionKey : "agent:main:telegram:group:1" ,
490+ ageMs : 180_000 ,
491+ queueDepth : 2 ,
492+ } ) ;
493+
494+ expect ( outcome ) . toMatchObject ( {
495+ status : "released" ,
496+ action : "release_lane" ,
497+ sessionId : "stale-tool-session" ,
498+ sessionKey : "agent:main:telegram:group:1" ,
499+ } ) ;
500+ expect ( mocks . clearDiagnosticSessionActivity ) . toHaveBeenCalledWith ( {
501+ sessionId : "stale-tool-session" ,
502+ sessionKey : "agent:main:telegram:group:1" ,
503+ reason : "stuck_recovery:release_lane" ,
504+ } ) ;
505+ expect ( warnLogMessages ( ) ) . toContain (
506+ "stuck session recovery cleared diagnostic activity: sessionId=stale-tool-session sessionKey=agent:main:telegram:group:1 reason=stuck_recovery:release_lane activeEmbeddedRunsCleared=0 activeToolsCleared=1 activeModelCallsCleared=0" ,
507+ ) ;
508+ } ) ;
509+
461510 it ( "clears stale queued processing state even when the lane has no active work" , async ( ) => {
462511 mocks . resolveActiveEmbeddedRunHandleSessionId . mockReturnValue ( undefined ) ;
463512 mocks . resolveActiveEmbeddedRunSessionId . mockReturnValue ( undefined ) ;
0 commit comments