From aaf1f8c82376b57f3971c4887132c3aecf9ddbf4 Mon Sep 17 00:00:00 2001 From: Ahmed Allam Date: Sat, 24 Oct 2026 17:44:27 +0000 Subject: [PATCH] fix(agents): finish_scan drops its coverage reconciliation step when the coverage tools are off --- strix/agents/factory.py | 11 +++++++++++ tests/test_agent_tool_registration.py | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/strix/agents/factory.py b/strix/agents/factory.py index 39a52255e..1b8742ba4 100644 --- a/strix/agents/factory.py +++ b/strix/agents/factory.py @@ -620,6 +620,15 @@ def _finish_tool_use_behavior( _COVERAGE_TOOLS: tuple[Tool, ...] = (record_coverage, update_coverage, list_coverage) +_FINISH_SCAN_COVERAGE_STEP = re.compile( + r"\n5\. \*\*Coverage reconciliation\.\*\*.*?(?=\n\n)", re.DOTALL +) + + +def _without_coverage_step(tool: FunctionTool) -> FunctionTool: + description = _FINISH_SCAN_COVERAGE_STEP.sub("", tool.description) + return dataclasses.replace(tool, description=description) + _BASE_TOOLS: tuple[Tool, ...] = ( think, @@ -771,6 +780,8 @@ def build_strix_agent( base_tools = [report_tool if t is create_vulnerability_report else t for t in _BASE_TOOLS] if not coverage_tools: base_tools = [t for t in base_tools if t not in _COVERAGE_TOOLS] + if finish_tool is finish_scan: + finish_tool = _without_coverage_step(finish_scan) if is_root: tools: list[Tool] = [*base_tools, *agent_tools, finish_tool] else: diff --git a/tests/test_agent_tool_registration.py b/tests/test_agent_tool_registration.py index 8bec65ab3..6ebb2fddf 100644 --- a/tests/test_agent_tool_registration.py +++ b/tests/test_agent_tool_registration.py @@ -104,6 +104,10 @@ def test_report_tool_replaces_create_vulnerability_report() -> None: assert "create_vulnerability_report" not in agent.instructions +def _descriptions(tools: list[Any]) -> list[str]: + return [t.description for t in tools if isinstance(t, FunctionTool)] + + def test_coverage_tools_can_be_turned_off() -> None: coverage = {"record_coverage", "update_coverage", "list_coverage"} for is_root in (True, False): @@ -116,6 +120,12 @@ def test_coverage_tools_can_be_turned_off() -> None: assert "- COVERAGE" in on.instructions assert "## Recording Closure" in on.instructions assert not any(name in off.instructions for name in coverage) + on_descriptions = "\n".join(_descriptions(on.tools)) + off_descriptions = "\n".join(_descriptions(off.tools)) + if is_root: + assert "Coverage reconciliation" in on_descriptions + assert "Coverage reconciliation" not in off_descriptions + assert not any(name in off_descriptions for name in coverage) def test_instructions_override_is_used_verbatim() -> None: