@@ -482,27 +482,29 @@ describe("getKeepMessagesWithToolBlocks", () => {
482482 expect ( result . toolUseBlocksToPreserve ) . toContainEqual ( toolUseBlock2 )
483483 } )
484484
485- it ( "should not crash when tool_result references tool_use beyond search boundary" , ( ) => {
485+ it ( "should find tool_use even when it is far back in the message history (ROO-520 fix)" , ( ) => {
486+ // This test verifies the fix for https://linear.app/roocode/issue/ROO-520
487+ // The bug occurred when tool_use was preserved in an earlier summary message
488+ // after multiple condensations. The bounded search window was too small to find it,
489+ // resulting in orphaned tool_result blocks that caused 400 errors from the API.
486490 const toolResultBlock = {
487491 type : "tool_result" as const ,
488- tool_use_id : "toolu_beyond_boundary " ,
492+ tool_use_id : "toolu_early_in_history " ,
489493 content : "result" ,
490494 }
491495
492- // Tool_use is at ts:1, but with N_MESSAGES_TO_KEEP=3, we only search back 3 messages
493- // from startIndex-1. StartIndex is 7 (messages.length=10, keepCount=3, startIndex=7).
494- // So we search from index 6 down to index 4 (7-1 down to 7-3).
495- // The tool_use at index 0 (ts:1) is beyond the search boundary.
496+ // Tool_use is at index 0, tool_result is in the last 3 messages.
497+ // The search must cover the ENTIRE condensed region to find the tool_use.
496498 const messages : ApiMessage [ ] = [
497499 {
498500 role : "assistant" ,
499501 content : [
500- { type : "text" as const , text : "Way back ..." } ,
502+ { type : "text" as const , text : "Early tool call ..." } ,
501503 {
502504 type : "tool_use" as const ,
503- id : "toolu_beyond_boundary " ,
504- name : "old_tool " ,
505- input : { } ,
505+ id : "toolu_early_in_history " ,
506+ name : "read_file " ,
507+ input : { path : "test.txt" } ,
506508 } ,
507509 ] ,
508510 ts : 1 ,
@@ -522,7 +524,6 @@ describe("getKeepMessagesWithToolBlocks", () => {
522524 { role : "user" , content : "Message 10" , ts : 10 } ,
523525 ]
524526
525- // Should not crash
526527 const result = getKeepMessagesWithToolBlocks ( messages , 3 )
527528
528529 // keepMessages should be the last 3 messages
@@ -531,8 +532,78 @@ describe("getKeepMessagesWithToolBlocks", () => {
531532 expect ( result . keepMessages [ 1 ] . ts ) . toBe ( 9 )
532533 expect ( result . keepMessages [ 2 ] . ts ) . toBe ( 10 )
533534
534- // Should not preserve the tool_use since it's beyond the search boundary
535- expect ( result . toolUseBlocksToPreserve ) . toHaveLength ( 0 )
535+ // With the fix, tool_use should be found even though it's far back in history
536+ expect ( result . toolUseBlocksToPreserve ) . toHaveLength ( 1 )
537+ expect ( result . toolUseBlocksToPreserve [ 0 ] . id ) . toBe ( "toolu_early_in_history" )
538+ } )
539+
540+ it ( "should find tool_use in previous summary message after multiple condensations (ROO-520)" , ( ) => {
541+ // This test simulates the exact scenario from ROO-520:
542+ // After first condensation, tool_use was preserved in summary1.
543+ // After second condensation, the bounded search couldn't find tool_use in summary1
544+ // because it was outside the N_MESSAGES_TO_KEEP search window.
545+ const toolUseBlock = {
546+ type : "tool_use" as const ,
547+ id : "toolu_in_summary" ,
548+ name : "read_file" ,
549+ input : { path : "test.txt" } ,
550+ }
551+ const toolResultBlock = {
552+ type : "tool_result" as const ,
553+ tool_use_id : "toolu_in_summary" ,
554+ content : "file contents" ,
555+ }
556+
557+ // Simulate state after first condensation:
558+ // summary1 contains a preserved tool_use block from the first condense
559+ const summaryMessage : ApiMessage = {
560+ role : "assistant" ,
561+ content : [ { type : "text" as const , text : "Summary of first conversation chunk" } , toolUseBlock ] ,
562+ ts : 5 ,
563+ isSummary : true ,
564+ condenseId : "summary-1" ,
565+ }
566+
567+ const messages : ApiMessage [ ] = [
568+ { role : "user" , content : "First message" , ts : 1 } ,
569+ // Messages 2-4 are tagged with condenseParent from first condensation
570+ { role : "assistant" , content : "Message 2" , ts : 2 , condenseParent : "summary-1" } ,
571+ { role : "user" , content : "Message 3" , ts : 3 , condenseParent : "summary-1" } ,
572+ { role : "assistant" , content : "Message 4" , ts : 4 , condenseParent : "summary-1" } ,
573+ // summary1 with preserved tool_use
574+ summaryMessage ,
575+ // Messages after first condensation
576+ { role : "user" , content : "Message 6" , ts : 6 } ,
577+ { role : "assistant" , content : "Message 7" , ts : 7 } ,
578+ { role : "user" , content : "Message 8" , ts : 8 } ,
579+ { role : "assistant" , content : "Message 9" , ts : 9 } ,
580+ { role : "user" , content : "Message 10" , ts : 10 } ,
581+ { role : "assistant" , content : "Message 11" , ts : 11 } ,
582+ // This user message has tool_result referencing the tool_use in summary1
583+ {
584+ role : "user" ,
585+ content : [ toolResultBlock , { type : "text" as const , text : "Continue" } ] ,
586+ ts : 12 ,
587+ } ,
588+ { role : "assistant" , content : "Message 13" , ts : 13 } ,
589+ { role : "user" , content : "Message 14" , ts : 14 } ,
590+ ]
591+
592+ // Second condensation: keepCount=3 means we keep messages 12, 13, 14
593+ // startIndex = 14 - 3 = 11
594+ // Old bounded search would only look at messages[8:11] = [msg9, msg10, msg11]
595+ // The tool_use in summary1 at index 4 would NOT be found!
596+ const result = getKeepMessagesWithToolBlocks ( messages , 3 )
597+
598+ expect ( result . keepMessages ) . toHaveLength ( 3 )
599+ expect ( result . keepMessages [ 0 ] . ts ) . toBe ( 12 ) // Has tool_result
600+ expect ( result . keepMessages [ 1 ] . ts ) . toBe ( 13 )
601+ expect ( result . keepMessages [ 2 ] . ts ) . toBe ( 14 )
602+
603+ // With the fix, we search the ENTIRE condensed region (messages[0:11])
604+ // and find the tool_use in summary1
605+ expect ( result . toolUseBlocksToPreserve ) . toHaveLength ( 1 )
606+ expect ( result . toolUseBlocksToPreserve [ 0 ] . id ) . toBe ( "toolu_in_summary" )
536607 } )
537608
538609 it ( "should not duplicate tool_use blocks when same tool_result ID appears multiple times" , ( ) => {
0 commit comments