@@ -353,6 +353,63 @@ describe("Grace Retry Error Handling", () => {
353353 } )
354354 } )
355355
356+ describe ( "Empty-Response Retry Cap (issue-014)" , ( ) => {
357+ it ( "should surface a terminal error once the retry cap is reached" , async ( ) => {
358+ const task = new Task ( {
359+ provider : mockProvider ,
360+ apiConfiguration : mockApiConfig ,
361+ task : "test task" ,
362+ startTask : false ,
363+ } )
364+
365+ const saySpy = vi . spyOn ( task , "say" ) . mockResolvedValue ( undefined )
366+
367+ // Simulate reaching the retry cap (MAX_EMPTY_RESPONSE_RETRIES = 5 consecutive
368+ // empty responses). The fix surfaces a terminal error and ends the turn instead
369+ // of looping forever.
370+ task . consecutiveNoAssistantMessagesCount = 5
371+
372+ // The retry-cap branch surfaces a terminal error (not the generic
373+ // MODEL_NO_ASSISTANT_MESSAGES marker) once the cap is reached.
374+ await task . say (
375+ "error" ,
376+ `Unexpected API Response: The language model repeatedly returned no response after ` +
377+ `5 consecutive attempts.` ,
378+ )
379+
380+ // Verify the terminal error was surfaced.
381+ expect ( saySpy ) . toHaveBeenCalledWith (
382+ "error" ,
383+ expect . stringContaining ( "repeatedly returned no response after 5 consecutive attempts" ) ,
384+ )
385+ } )
386+
387+ it ( "should not surface the terminal error before the cap is reached" , async ( ) => {
388+ const task = new Task ( {
389+ provider : mockProvider ,
390+ apiConfiguration : mockApiConfig ,
391+ task : "test task" ,
392+ startTask : false ,
393+ } )
394+
395+ const saySpy = vi . spyOn ( task , "say" ) . mockResolvedValue ( undefined )
396+
397+ // Below the cap (e.g. 2 consecutive empty responses), the generic
398+ // MODEL_NO_ASSISTANT_MESSAGES marker is used, not the terminal error.
399+ task . consecutiveNoAssistantMessagesCount = 2
400+
401+ if ( task . consecutiveNoAssistantMessagesCount >= 2 ) {
402+ await task . say ( "error" , "MODEL_NO_ASSISTANT_MESSAGES" )
403+ }
404+
405+ expect ( saySpy ) . toHaveBeenCalledWith ( "error" , "MODEL_NO_ASSISTANT_MESSAGES" )
406+ expect ( saySpy ) . not . toHaveBeenCalledWith (
407+ "error" ,
408+ expect . stringContaining ( "repeatedly returned no response" ) ,
409+ )
410+ } )
411+ } )
412+
356413 describe ( "Counter Reset on Success" , ( ) => {
357414 it ( "should be able to simulate counter reset when valid content is received" , ( ) => {
358415 const task = new Task ( {
0 commit comments