@@ -467,6 +467,60 @@ describe("dispatchReplyFromConfig stale visible admission recovery", () => {
467467 expect ( dispatcher . sendFinalReply ) . toHaveBeenCalledTimes ( 1 ) ;
468468 } ) ;
469469
470+ it ( "does not clear active reply work when recovery fails" , async ( ) => {
471+ vi . useFakeTimers ( ) ;
472+ const sessionKey = "agent:main:telegram:direct:recovery-failed" ;
473+ const activeOperation = createReplyOperation ( {
474+ sessionKey,
475+ sessionId : "active-session" ,
476+ resetTriggered : false ,
477+ } ) ;
478+ activeOperation . setPhase ( "running" ) ;
479+ const dispatcher = createDispatcher ( ) ;
480+ const replyResolver = vi . fn ( async ( ) => ( { text : "telegram reply" } ) satisfies ReplyPayload ) ;
481+ diagnosticMocks . requestStuckDiagnosticSessionRecovery . mockResolvedValue ( {
482+ status : "failed" ,
483+ action : "none" ,
484+ reason : "exception" ,
485+ sessionId : "active-session" ,
486+ sessionKey,
487+ error : "recovery failed" ,
488+ } ) ;
489+
490+ const resultPromise = dispatchReplyFromConfig ( {
491+ ctx : buildTestCtx ( {
492+ Provider : "telegram" ,
493+ Surface : "telegram" ,
494+ OriginatingChannel : "telegram" ,
495+ OriginatingTo : "user:1" ,
496+ ChatType : "direct" ,
497+ SessionKey : sessionKey ,
498+ MessageThreadId : "501.000" ,
499+ BodyForAgent : "second telegram direct turn" ,
500+ } ) ,
501+ cfg : {
502+ diagnostics : {
503+ stuckSessionWarnMs : 1_000 ,
504+ stuckSessionAbortMs : 1_000 ,
505+ } ,
506+ } as OpenClawConfig ,
507+ dispatcher,
508+ replyResolver,
509+ } ) ;
510+
511+ await vi . advanceTimersByTimeAsync ( 1_000 ) ;
512+ const result = await resultPromise ;
513+
514+ expect ( diagnosticMocks . requestStuckDiagnosticSessionRecovery ) . toHaveBeenCalledTimes ( 1 ) ;
515+ expect ( result ) . toMatchObject ( {
516+ queuedFinal : false ,
517+ counts : { tool : 0 , block : 0 , final : 0 } ,
518+ } ) ;
519+ expect ( activeOperation . result ) . toBeNull ( ) ;
520+ expect ( replyResolver ) . not . toHaveBeenCalled ( ) ;
521+ expect ( dispatcher . sendFinalReply ) . not . toHaveBeenCalled ( ) ;
522+ } ) ;
523+
470524 it ( "clears stale reply work after recovery releases lane state" , async ( ) => {
471525 vi . useFakeTimers ( ) ;
472526 const sessionKey = "agent:main:telegram:direct:released-lane" ;
0 commit comments