Observed friction during an Azure portal task:
- Daemon's default session went stale (user closed the attached tab),
which broke every subsequent call including browser-level Target.*.
- new_tab() had been removed in the previous simplification pass but
was needed to recover.
- Azure portal renders blade panels in iframes; js() on the main page
returned nothing for picker contents.
Changes:
- daemon: browser-level Target.* calls now bypass self.session entirely
(so a stale session doesn't poison them). On "Session with given id
not found" for session-scoped calls, clear + re-attach + retry once.
Merged start() and the new recovery path into attach_first_page().
- helpers: add new_tab(); js() accepts target_id for iframe queries;
iframe_target(substr) to find blade iframes; ensure_real_tab() now
resilient to stale-session exceptions.
- SKILL.md: one-line note on iframe-site workflow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Session of 7 real tasks (Upwork, X, Google Flights, HN, Netflix, iPhone
comparison) used only half the original helper surface — every DOM
interaction went through js() + a bespoke selector, never through the
indexed-DOM helpers. Dropped 14 that never fired: get_dom,
click_element, type_in, element_pos, save_cookies, load_cookies,
set_viewport, screenshot_full (folded into screenshot full=True),
double_click, right_click, move_mouse, new_tab, close_tab,
handle_dialog, back, reload.
Also:
- daemon: shorter identifiers, collapsed boilerplate, TOCTOU fix via
try/except on read_text, is_real_page module-level, no variable
shadowing of `url`.
- helpers: one shared INTERNAL_URL_SCHEMES tuple instead of three
subtly-different ones; current_tab() collapsed from 4 round-trips
with dead fallback to one; wait_for_load() stops draining the shared
event buffer.
- SKILL.md / AGENTS.md: cut recipes and anything the LLM already knows
(what click does, what CDP is). Kept only project-specific context.
Net: 8600 -> 4030 tokens across the project (53% reduction); the file
the LLM reads every skill invocation (helpers.py) dropped from 2193
to 1219 tokens.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-process architecture: daemon.py holds one persistent CDP WebSocket
to the user's running Chrome (via chrome://inspect), short-lived run.py
processes talk to it over a Unix socket, helpers.py is the transparent
layer the LLM reads and edits at will.
Philosophy: no CLI, no fixed API surface. The LLM writes Python blocks
against ~13 tiny helpers (cdp, click, type_text, screenshot, get_dom,
etc.) and edits helpers.py on the fly when a pattern repeats. Coordinate
clicks default because they pass through iframes/shadow DOM/cross-origin
at the compositor level.
Uses cdp-use internally for send_raw only (ignores its 36k lines of
typed wrappers — raw CDP strings tokenize better than typed calls).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>