@@ -435,6 +435,122 @@ func TestFilterThinkingBlocksForRetry_StripsEmptyTextBlocks(t *testing.T) {
435435 require .NotEmpty (t , block1 ["text" ])
436436}
437437
438+ func TestFilterThinkingBlocksForRetry_StripsNestedEmptyTextInToolResult (t * testing.T ) {
439+ // Empty text blocks nested inside tool_result content should also be stripped
440+ input := []byte (`{
441+ "messages":[
442+ {"role":"user","content":[
443+ {"type":"tool_result","tool_use_id":"t1","content":[
444+ {"type":"text","text":"valid result"},
445+ {"type":"text","text":""}
446+ ]}
447+ ]}
448+ ]
449+ }` )
450+
451+ out := FilterThinkingBlocksForRetry (input )
452+
453+ var req map [string ]any
454+ require .NoError (t , json .Unmarshal (out , & req ))
455+ msgs := req ["messages" ].([]any )
456+ msg0 := msgs [0 ].(map [string ]any )
457+ content0 := msg0 ["content" ].([]any )
458+ require .Len (t , content0 , 1 )
459+ toolResult := content0 [0 ].(map [string ]any )
460+ require .Equal (t , "tool_result" , toolResult ["type" ])
461+ nestedContent := toolResult ["content" ].([]any )
462+ require .Len (t , nestedContent , 1 )
463+ require .Equal (t , "valid result" , nestedContent [0 ].(map [string ]any )["text" ])
464+ }
465+
466+ func TestFilterThinkingBlocksForRetry_NestedAllEmptyGetsEmptySlice (t * testing.T ) {
467+ // If all nested content blocks in tool_result are empty text, content becomes empty slice
468+ input := []byte (`{
469+ "messages":[
470+ {"role":"user","content":[
471+ {"type":"tool_result","tool_use_id":"t1","content":[
472+ {"type":"text","text":""}
473+ ]},
474+ {"type":"text","text":"hello"}
475+ ]}
476+ ]
477+ }` )
478+
479+ out := FilterThinkingBlocksForRetry (input )
480+
481+ var req map [string ]any
482+ require .NoError (t , json .Unmarshal (out , & req ))
483+ msgs := req ["messages" ].([]any )
484+ msg0 := msgs [0 ].(map [string ]any )
485+ content0 := msg0 ["content" ].([]any )
486+ require .Len (t , content0 , 2 )
487+ toolResult := content0 [0 ].(map [string ]any )
488+ nestedContent := toolResult ["content" ].([]any )
489+ require .Len (t , nestedContent , 0 )
490+ }
491+
492+ func TestStripEmptyTextBlocks (t * testing.T ) {
493+ t .Run ("strips top-level empty text" , func (t * testing.T ) {
494+ input := []byte (`{"messages":[{"role":"user","content":[{"type":"text","text":"hello"},{"type":"text","text":""}]}]}` )
495+ out := StripEmptyTextBlocks (input )
496+ var req map [string ]any
497+ require .NoError (t , json .Unmarshal (out , & req ))
498+ msgs := req ["messages" ].([]any )
499+ content := msgs [0 ].(map [string ]any )["content" ].([]any )
500+ require .Len (t , content , 1 )
501+ require .Equal (t , "hello" , content [0 ].(map [string ]any )["text" ])
502+ })
503+
504+ t .Run ("strips nested empty text in tool_result" , func (t * testing.T ) {
505+ input := []byte (`{"messages":[{"role":"user","content":[{"type":"tool_result","tool_use_id":"t1","content":[{"type":"text","text":"ok"},{"type":"text","text":""}]}]}]}` )
506+ out := StripEmptyTextBlocks (input )
507+ var req map [string ]any
508+ require .NoError (t , json .Unmarshal (out , & req ))
509+ msgs := req ["messages" ].([]any )
510+ content := msgs [0 ].(map [string ]any )["content" ].([]any )
511+ toolResult := content [0 ].(map [string ]any )
512+ nestedContent := toolResult ["content" ].([]any )
513+ require .Len (t , nestedContent , 1 )
514+ require .Equal (t , "ok" , nestedContent [0 ].(map [string ]any )["text" ])
515+ })
516+
517+ t .Run ("no-op when no empty text" , func (t * testing.T ) {
518+ input := []byte (`{"messages":[{"role":"user","content":[{"type":"text","text":"hello"}]}]}` )
519+ out := StripEmptyTextBlocks (input )
520+ require .Equal (t , input , out )
521+ })
522+
523+ t .Run ("preserves non-map blocks in content" , func (t * testing.T ) {
524+ // tool_result content can be a string; non-map blocks should pass through unchanged
525+ input := []byte (`{"messages":[{"role":"user","content":[{"type":"tool_result","tool_use_id":"t1","content":"string content"},{"type":"text","text":""}]}]}` )
526+ out := StripEmptyTextBlocks (input )
527+ var req map [string ]any
528+ require .NoError (t , json .Unmarshal (out , & req ))
529+ msgs := req ["messages" ].([]any )
530+ content := msgs [0 ].(map [string ]any )["content" ].([]any )
531+ require .Len (t , content , 1 )
532+ toolResult := content [0 ].(map [string ]any )
533+ require .Equal (t , "tool_result" , toolResult ["type" ])
534+ require .Equal (t , "string content" , toolResult ["content" ])
535+ })
536+
537+ t .Run ("handles deeply nested tool_result" , func (t * testing.T ) {
538+ // Recursive: tool_result containing another tool_result with empty text
539+ input := []byte (`{"messages":[{"role":"user","content":[{"type":"tool_result","tool_use_id":"t1","content":[{"type":"tool_result","tool_use_id":"t2","content":[{"type":"text","text":""},{"type":"text","text":"deep"}]}]}]}]}` )
540+ out := StripEmptyTextBlocks (input )
541+ var req map [string ]any
542+ require .NoError (t , json .Unmarshal (out , & req ))
543+ msgs := req ["messages" ].([]any )
544+ content := msgs [0 ].(map [string ]any )["content" ].([]any )
545+ outer := content [0 ].(map [string ]any )
546+ innerContent := outer ["content" ].([]any )
547+ inner := innerContent [0 ].(map [string ]any )
548+ deepContent := inner ["content" ].([]any )
549+ require .Len (t , deepContent , 1 )
550+ require .Equal (t , "deep" , deepContent [0 ].(map [string ]any )["text" ])
551+ })
552+ }
553+
438554func TestFilterThinkingBlocksForRetry_PreservesNonEmptyTextBlocks (t * testing.T ) {
439555 // Non-empty text blocks should pass through unchanged
440556 input := []byte (`{
0 commit comments