@@ -471,6 +471,41 @@ def test_auto_truncation_on_token_limit(self) -> None:
471471 # Should have been auto-truncated
472472 vlm .create_message .assert_called_once ()
473473
474+ def test_truncate_deferred_when_last_message_has_tool_use (self ) -> None :
475+ """Truncation must not fire when the last message is an assistant
476+ tool_use whose tool_result hasn't been appended yet."""
477+ vlm = _make_vlm_provider ()
478+ strategy = _make_strategy (vlm_provider = vlm , n_messages_to_keep = 2 )
479+ for i in range (4 ):
480+ role = "user" if i % 2 == 0 else "assistant"
481+ strategy .append_message (MessageParam (role = role , content = f"msg { i } " ))
482+ # Append an assistant message with tool_use (simulates the window
483+ # between _get_next_message and _execute_tools_if_present)
484+ strategy .append_message (
485+ MessageParam (
486+ role = "assistant" ,
487+ content = [
488+ ToolUseBlockParam (
489+ id = "tu_1" , input = {}, name = "tool_a" , type = "tool_use"
490+ ),
491+ ],
492+ )
493+ )
494+ # Truncation should be deferred — VLM must NOT be called
495+ strategy .truncate ()
496+ vlm .create_message .assert_not_called ()
497+ # After appending the matching tool_result, truncation should proceed
498+ strategy .append_message (
499+ MessageParam (
500+ role = "user" ,
501+ content = [
502+ ToolResultBlockParam (tool_use_id = "tu_1" , content = "result" ),
503+ ],
504+ )
505+ )
506+ strategy .truncate ()
507+ vlm .create_message .assert_called_once ()
508+
474509
475510# ---------------------------------------------------------------------------
476511# Edge cases
@@ -743,6 +778,37 @@ def test_auto_truncation_on_token_limit(self) -> None:
743778 strategy .append_message (MessageParam (role = "user" , content = "z" * 300 ))
744779 vlm .create_message .assert_called_once ()
745780
781+ def test_truncate_deferred_when_last_message_has_tool_use (self ) -> None :
782+ """Truncation must not fire when the last message is an assistant
783+ tool_use whose tool_result hasn't been appended yet."""
784+ vlm = _make_vlm_provider ()
785+ strategy = _make_summarizing_strategy (vlm_provider = vlm , n_messages_to_keep = 2 )
786+ for i in range (4 ):
787+ role = "user" if i % 2 == 0 else "assistant"
788+ strategy .append_message (MessageParam (role = role , content = f"msg { i } " ))
789+ strategy .append_message (
790+ MessageParam (
791+ role = "assistant" ,
792+ content = [
793+ ToolUseBlockParam (
794+ id = "tu_1" , input = {}, name = "tool_a" , type = "tool_use"
795+ ),
796+ ],
797+ )
798+ )
799+ strategy .truncate ()
800+ vlm .create_message .assert_not_called ()
801+ strategy .append_message (
802+ MessageParam (
803+ role = "user" ,
804+ content = [
805+ ToolResultBlockParam (tool_use_id = "tu_1" , content = "result" ),
806+ ],
807+ )
808+ )
809+ strategy .truncate ()
810+ vlm .create_message .assert_called_once ()
811+
746812
747813class TestReporterIntegration :
748814 def test_summarizing_strategy_reports_summary_response (self ) -> None :
0 commit comments