Answer with the connected browser's error, never a Playwright stand-in

In auto mode any extension error fell through to headless Playwright, so
a slow page in the user's own browser came back as Playwright's missing
executable. A connected browser's failure is now the answer; Playwright
stands in only when no browser is on the router. hanzo-tools-browser
0.5.16.
This commit is contained in:
2026-09-30 21:49:37 -07:00
parent 4c0e40351c
commit 32ce9a8595
4 changed files with 50 additions and 5 deletions
@@ -1131,9 +1131,11 @@ class BrowserTool(BaseTool):
if "error" not in ext_result:
return _answer(action, ext_result)
# Refs and labels exist only in the extension, and an explicit
# backend means that browser: no Playwright stand-in for either.
if backend in ("firefox", "chrome", "extension") or by_ref or wire == "annotate":
# Refs and labels exist only in the extension, an explicit backend
# means that browser, and a connected browser's own failure is the
# answer: Playwright stands in only when no browser is connected.
if (backend in ("firefox", "chrome", "extension") or by_ref or wire == "annotate"
or await _check_extension(browser=browser_filter)):
return {"error": ext_result["error"], "action": action, "backend": backend}
if by_ref:
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-tools-browser"
version = "0.5.15"
version = "0.5.16"
description = "Browser automation tools: drive the user's own browser through the Hanzo extension, over the ZAP router every ZAP process embeds."
readme = "README.md"
requires-python = ">=3.12"
@@ -177,3 +177,46 @@ class TestZapdWire:
assert o == len(buf)
assert out == params
class TestConnectedBrowserIsTheAnswer:
"""A connected browser's own failure comes back; Playwright never stands in for it."""
@pytest.fixture
def tool(self, monkeypatch):
import importlib
from hanzo_tools.browser import BrowserTool
browser_tool = importlib.import_module("hanzo_tools.browser.browser_tool")
async def failed(*a, **k):
return {"error": "timed out waiting for browser/dgx/chrome"}
monkeypatch.setattr(browser_tool, "_extension_command", failed)
monkeypatch.setattr(browser_tool, "PLAYWRIGHT_AVAILABLE", False)
t = BrowserTool()
t.backend = "auto"
return t, browser_tool
@pytest.mark.asyncio
async def test_connected(self, tool, monkeypatch):
t, bt = tool
async def yes(**k):
return True
monkeypatch.setattr(bt, "_check_extension", yes)
r = await t.execute(action="navigate", url="https://example.com")
assert r["error"] == "timed out waiting for browser/dgx/chrome"
@pytest.mark.asyncio
async def test_none_connected_falls_through(self, tool, monkeypatch):
t, bt = tool
async def no(**k):
return False
monkeypatch.setattr(bt, "_check_extension", no)
r = await t.execute(action="navigate", url="https://example.com")
assert "timed out" not in str(r)
Generated
+1 -1
View File
@@ -1767,7 +1767,7 @@ provides-extras = ["consensus", "api", "full", "dev"]
[[package]]
name = "hanzo-tools-browser"
version = "0.5.14"
version = "0.5.16"
source = { editable = "pkg/hanzo-tools-browser" }
dependencies = [
{ name = "hanzo-tools" },