@@ -661,6 +661,67 @@ describe("runCliAgent reliability", () => {
661661 expect ( supervisorSpawnMock ) . toHaveBeenCalledTimes ( 1 ) ;
662662 } ) ;
663663
664+ it ( "does not retry context overflow after a confirmed message send" , async ( ) => {
665+ supervisorSpawnMock . mockClear ( ) ;
666+ supervisorSpawnMock . mockImplementationOnce ( async ( ...args : unknown [ ] ) => {
667+ const input = args [ 0 ] as Parameters < ReturnType < typeof getProcessSupervisor > [ "spawn" ] > [ 0 ] ;
668+ const captureHandle = markMcpLoopbackToolCallStarted ( {
669+ captureKey : input . env ?. OPENCLAW_MCP_CLI_CAPTURE_KEY ?? "" ,
670+ toolName : "message" ,
671+ args : {
672+ action : "send" ,
673+ channel : "telegram" ,
674+ target : "chat123" ,
675+ message : "sent before overflow" ,
676+ } ,
677+ } ) ;
678+ if ( ! captureHandle ) {
679+ throw new Error ( "Expected message delivery capture" ) ;
680+ }
681+ recordMcpLoopbackToolCallResult ( {
682+ captureHandle,
683+ toolName : "message" ,
684+ args : {
685+ action : "send" ,
686+ channel : "telegram" ,
687+ target : "chat123" ,
688+ message : "sent before overflow" ,
689+ } ,
690+ result : { status : "sent" } ,
691+ isError : false ,
692+ } ) ;
693+ markMcpLoopbackToolCallFinished ( captureHandle ) ;
694+ return createManagedRun ( {
695+ reason : "exit" ,
696+ exitCode : 1 ,
697+ exitSignal : null ,
698+ durationMs : 150 ,
699+ stdout : "" ,
700+ stderr : "Prompt is too long" ,
701+ timedOut : false ,
702+ noOutputTimedOut : false ,
703+ } ) ;
704+ } ) ;
705+ const context = buildPreparedContext ( {
706+ sessionKey : "agent:main:delivered-overflow" ,
707+ runId : "run-delivered-overflow" ,
708+ cliSessionId : "stale-cli-session" ,
709+ provider : "claude-cli" ,
710+ model : "opus" ,
711+ openClawHistoryPrompt : CLI_RESEED_PROMPT ,
712+ } ) ;
713+ context . mcpDeliveryCapture = true ;
714+
715+ const result = await runPreparedCliAgent ( context ) ;
716+
717+ expect ( result . payloads ) . toBeUndefined ( ) ;
718+ expect ( result . didSendViaMessagingTool ) . toBe ( true ) ;
719+ expect ( result . messagingToolSentTexts ) . toEqual ( [ "sent before overflow" ] ) ;
720+ expect ( result . meta . executionTrace ?. attempts ?. [ 0 ] ?. result ) . toBe ( "error" ) ;
721+ expect ( result . meta . agentMeta ?. clearCliSessionBinding ) . toBe ( true ) ;
722+ expect ( supervisorSpawnMock ) . toHaveBeenCalledTimes ( 1 ) ;
723+ } ) ;
724+
664725 it ( "preserves first-turn delivery through cleanup without binding the OpenClaw session id" , async ( ) => {
665726 supervisorSpawnMock . mockClear ( ) ;
666727 supervisorSpawnMock . mockImplementationOnce ( async ( ...args : unknown [ ] ) => {
@@ -1390,6 +1451,48 @@ describe("runCliAgent reliability", () => {
13901451 expect ( clearBeforeRetry ) . not . toHaveBeenCalled ( ) ;
13911452 } ) ;
13921453
1454+ it ( "does not fresh retry context overflow when the run timeout budget is exhausted" , async ( ) => {
1455+ supervisorSpawnMock . mockClear ( ) ;
1456+ const clearBeforeRetry = vi . fn ( async ( ) => true ) ;
1457+ supervisorSpawnMock . mockResolvedValueOnce (
1458+ createManagedRun ( {
1459+ reason : "exit" ,
1460+ exitCode : 1 ,
1461+ exitSignal : null ,
1462+ durationMs : 150 ,
1463+ stdout : "" ,
1464+ stderr : "Prompt is too long" ,
1465+ timedOut : false ,
1466+ noOutputTimedOut : false ,
1467+ } ) ,
1468+ ) ;
1469+ const context = buildPreparedContext ( {
1470+ sessionKey : "agent:main:expired-overflow-budget" ,
1471+ runId : "run-expired-overflow-budget" ,
1472+ cliSessionId : "stale-cli-session" ,
1473+ provider : "claude-cli" ,
1474+ model : "opus" ,
1475+ openClawHistoryPrompt : CLI_RESEED_PROMPT ,
1476+ } ) ;
1477+ const expiredBudgetContext = {
1478+ ...context ,
1479+ started : Date . now ( ) - context . params . timeoutMs - 1 ,
1480+ } ;
1481+
1482+ await expect (
1483+ runPreparedCliAgent ( {
1484+ ...expiredBudgetContext ,
1485+ params : {
1486+ ...expiredBudgetContext . params ,
1487+ onBeforeFreshCliSessionRetry : clearBeforeRetry ,
1488+ } ,
1489+ } ) ,
1490+ ) . rejects . toThrow ( "Prompt is too long" ) ;
1491+
1492+ expect ( supervisorSpawnMock ) . toHaveBeenCalledTimes ( 1 ) ;
1493+ expect ( clearBeforeRetry ) . not . toHaveBeenCalled ( ) ;
1494+ } ) ;
1495+
13931496 it ( "does not fresh retry a no-output timeout after CLI diagnostic output" , async ( ) => {
13941497 supervisorSpawnMock . mockClear ( ) ;
13951498 enqueueSystemEventMock . mockClear ( ) ;
@@ -1468,7 +1571,7 @@ describe("runCliAgent reliability", () => {
14681571 expect ( clearBeforeRetry ) . not . toHaveBeenCalled ( ) ;
14691572 } ) ;
14701573
1471- it . each ( [ "timeout" , "unknown" ] as const ) (
1574+ it . each ( [ "timeout" , "unknown" , "context_overflow" ] as const ) (
14721575 "retries a fresh CLI session after recoverable %s failover without a failed agent_end" ,
14731576 async ( reason ) => {
14741577 const hookRunner = {
@@ -1500,6 +1603,18 @@ describe("runCliAgent reliability", () => {
15001603 noOutputTimedOut : true ,
15011604 } ) ;
15021605 }
1606+ if ( spawnCount === 1 && reason === "context_overflow" ) {
1607+ return createManagedRun ( {
1608+ reason : "exit" ,
1609+ exitCode : 1 ,
1610+ exitSignal : null ,
1611+ durationMs : 150 ,
1612+ stdout : "" ,
1613+ stderr : "Prompt is too long" ,
1614+ timedOut : false ,
1615+ noOutputTimedOut : false ,
1616+ } ) ;
1617+ }
15031618 if ( spawnCount === 1 ) {
15041619 return createManagedRun ( {
15051620 reason : "exit" ,
@@ -1552,6 +1667,8 @@ describe("runCliAgent reliability", () => {
15521667 } ) ;
15531668
15541669 expect ( result . payloads ) . toEqual ( [ { text : "hello from fresh cli" } ] ) ;
1670+ expect ( result . meta . finalPromptText ) . toContain ( "User: earlier context" ) ;
1671+ expect ( result . meta . finalPromptText ) . toContain ( "<next_user_message>" ) ;
15551672 expect ( supervisorSpawnMock ) . toHaveBeenCalledTimes ( 2 ) ;
15561673 expect ( events ) . toEqual ( [ "spawn-1" , `clear-${ reason } ` , "spawn-2" ] ) ;
15571674 if ( reason === "timeout" ) {
0 commit comments