From 05fa5a101c56c81a70d8b90576ff2c86743ee54e Mon Sep 17 00:00:00 2001 From: Henkey Date: Sat, 2 May 2026 17:41:43 +0100 Subject: [PATCH] fix(acp): compact Zed tool replay rendering --- acp_adapter/tools.py | 39 ++++++++++++++++++++++++--------------- tests/acp/test_tools.py | 18 ++++++++++++------ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/acp_adapter/tools.py b/acp_adapter/tools.py index f2c2c7452e..e7e53a6277 100644 --- a/acp_adapter/tools.py +++ b/acp_adapter/tools.py @@ -425,6 +425,7 @@ def _format_web_search_result(result: Optional[str]) -> Optional[str]: def _format_web_extract_result(result: Optional[str]) -> Optional[str]: + """Return only web_extract errors for ACP; success stays compact via title.""" data = _json_loads_maybe(result) if not isinstance(data, dict): return None @@ -433,20 +434,24 @@ def _format_web_extract_result(result: Optional[str]) -> Optional[str]: results = data.get("results") if not isinstance(results, list): return None - lines = [f"Web extract: {len(results)} URL{'s' if len(results) != 1 else ''}"] + + failures: list[str] = [] for item in results[:10]: if not isinstance(item, dict): continue + error = str(item.get("error") or "").strip() + if not error or error in {"None", "null"}: + continue url = str(item.get("url") or "").strip() title = str(item.get("title") or url or "Untitled").strip() - error = str(item.get("error") or "").strip() - status = "failed" if error and error not in {"None", "null"} else "extracted" - suffix = f" — {status}" - lines.append(f"- {title}" + (f" — {url}" if url and url != title else "") + suffix) - if status == "failed": - lines.append(f" Error: {_truncate_text(error, limit=500)}") - if len(results) > 10: - lines.append(f"... {len(results) - 10} more result(s) omitted") + failures.append( + f"- {title}" + (f" — {url}" if url and url != title else "") + f"\n Error: {_truncate_text(error, limit=500)}" + ) + + if not failures: + return None + lines = [f"Web extract failed for {len(failures)} URL{'s' if len(failures) != 1 else ''}"] + lines.extend(failures) return "\n".join(lines) @@ -1139,12 +1144,16 @@ def build_tool_complete( ) -> ToolCallProgress: """Create a ToolCallUpdate (progress) event for a completed tool call.""" kind = get_tool_kind(tool_name) - content = _build_tool_complete_content( - tool_name, - result, - function_args=function_args, - snapshot=snapshot, - ) + if tool_name == "web_extract": + error_text = _format_web_extract_result(result) + content = [_text(error_text)] if error_text else None + else: + content = _build_tool_complete_content( + tool_name, + result, + function_args=function_args, + snapshot=snapshot, + ) return acp.update_tool_call( tool_call_id, kind=kind, diff --git a/tests/acp/test_tools.py b/tests/acp/test_tools.py index f600bcabff..f9b0dac6d6 100644 --- a/tests/acp/test_tools.py +++ b/tests/acp/test_tools.py @@ -412,19 +412,25 @@ class TestBuildToolComplete: assert "private long memory" not in text assert result.raw_output is None - def test_build_tool_complete_for_web_extract_summarizes_urls_without_page_content(self): + def test_build_tool_complete_for_web_extract_success_stays_compact(self): result = build_tool_complete( "tc-web-extract", "web_extract", '{"results":[{"url":"https://example.com","title":"Example","content":"# Intro\\nThis is extracted content."}]}', ) + assert result.content is None + assert result.raw_output is None + + def test_build_tool_complete_for_web_extract_error_shows_error(self): + result = build_tool_complete( + "tc-web-extract-error", + "web_extract", + '{"results":[{"url":"https://example.com","title":"Example","error":"timeout"}]}', + ) text = result.content[0].content.text - assert "Web extract: 1 URL" in text - assert "Example" in text + assert "Web extract failed" in text assert "https://example.com" in text - assert "Content:" not in text - assert "Intro" not in text - assert "extracted content" not in text + assert "timeout" in text assert result.raw_output is None def test_build_tool_complete_truncates_large_output(self):