Files
zeekay 450f65932d browser teaches the snapshot loop in its description and the rest on help
The tool put ~120 actions in a Literal enum and ~60 parameters in its
schema, all of it in every agent's context, and most of it Playwright
locator builders that returned a count. Three hand-kept lists (the enum,
extension_actions, _zap_method_for) disagreed: snapshot, wait and
select_option routed to the extension under names it did not answer.

ACTIONS is now the one table: each action once, its topic, its usage
line, and the extension method that serves it. The schema carries the
core loop (navigate, snapshot, click, fill, type, press, read,
screenshot, evaluate, wait, tabs, help) with an open `action` string;
help prints the rest by topic, and their parameters travel in `args`.

snapshot, click-by-ref and friends go to the extension's page engine
(hanzo.snapshot / hanzo.act / hanzo.read / hanzo.wait / hanzo.navigate,
screenshot annotate=true to hanzo.annotate). A tree or a page answers as
text; a refusal ("Stale ref @e4 … run snapshot again", "covered by
<div#consent>") is the error, not a success wrapping "ERR:", and is
never retried on Playwright, which has no refs to act on.

Aliases and no-ops are gone: select_option (select), get_inner_text and
get_value (get_text), clear (fill), content (get_html), new_page
(new_tab), press_key, mouse_wheel (scroll), list_browsers and
list_mcp_instances (browsers), use_browser, set_default_browser,
claim/release_browser, the locator/get_by_*/first/last/nth/filter/all
builders, is_hidden, frame, main_frame, on and off.

Claude-Session: https://claude.ai/code/session_01Dc8DXc3fEQCRHUa6XWK4Ku
2026-09-29 13:05:21 -07:00

3.5 KiB

hanzo-tools-browser

One browser tool that drives the user's own browser, signed in, through the Hanzo extension, or a headless Playwright Chromium when no extension is connected. playwright is the same tool pinned to Playwright; cdp sends a raw DevTools method.

Installation

pip install hanzo-tools-browser
pip install 'hanzo-tools-browser[playwright]' && playwright install chromium   # headless fallback

The loop: snapshot, act on refs, snapshot again

browser(action="navigate", url="https://example.com/login")
browser(action="snapshot", interactive=True)
# Login — https://example.com/login (4 refs)
# - textbox "Email" [ref=e3]
# - textbox "Password" [ref=e4]
# - checkbox "Remember me" [ref=e5]
# - button "Sign in" [ref=e6]
browser(action="fill", selector="@e3", text="[email protected]")
browser(action="fill", selector="@e4", text="…")
browser(action="click", selector="@e6")
browser(action="read")                      # the page as markdown
  • snapshot prints the rendered accessibility tree. Every node an agent can act on carries [ref=eN]; interactive=True lists only those, flat. compact, depth and selector (a CSS selector or a ref) trim it.
  • selector takes a ref (@e3, or e3) or a CSS selector, everywhere.
  • A ref names one element while that element stays on the page, across snapshots. After a navigation, or once its element is removed, it is refused with "run snapshot again".
  • A click on an element under a consent banner, modal or overlay is refused before any event fires, and the error names the cover. Act on the cover, then snapshot again.
  • read returns the page as markdown; outline=True keeps the headings, filter="pricing" only the sections that mention it.
  • screenshot annotate=True boxes every on-screen ref and labels it [N], which is ref @eN, and returns the legend beside the image.

Refs, annotate and the markdown reader live in the extension's page engine; on Playwright snapshot answers an aria tree without refs, so act with CSS selectors there.

Progressive surface

The schema carries the core actions only:

Action Parameters
navigate url — returns once the page has loaded
snapshot interactive, compact, depth, selector
click, fill, type, press selector, text, key
read outline, filter
screenshot annotate
evaluate code
wait selector or text, timeout
tabs — (tab_id targets a tab in any action)
help topic

browser(action="help") lists every other action by topic (interact, navigation, tabs, page, assert, storage, network, emulation, debug) with a one-line usage each. Their parameters travel in args:

browser(action="help", topic="interact")
browser(action="select", selector="@e8", args={"value": "Weekly"})
browser(action="scroll", args={"delta_y": 600})
browser(action="screenshot", args={"full_page": True, "full_res": True})
browser(action="emulate", args={"device": "iphone_14"})     # Playwright

Actions marked (Playwright) in help run on headless Playwright only.

Screenshots

A screenshot comes back as an image block, downscaled to 1280px JPEG; the native capture is written to a file whose path is returned. args.full_res inlines the native pixels.

Parallel agents (Playwright)

browser(action="new_context") opens an isolated Playwright session with its own cookies and storage: one Chromium, one context per agent.