CLI 3.0 harness: Input.dispatchMouseEvent/key events silently dropped in a long-lived session while Runtime.evaluate stays healthy (stale Input session; ensure_real_tab doesn't recover)
Summary
During an automated A/B benchmark driving the CLI 3.0 harness (via Hermes Agent's browser_exec integration, browser-use 0.1.8, macOS 26.6 / arm64, local Chromium), one session got into a state where click_at_xy() and non-printable press_key() events were silently dropped — Input.dispatchMouseEvent completed without error but no events ever reached the document. The state persisted for the remainder of that session (~25 further executions) and cost the driving agent a long forensic workaround. I could not reproduce it deterministically afterwards; filing with the full evidence in case the sequence rings a bell.
Evidence from the incident session (agent-authored, captured in our tool-call log)
The driving agent proved event loss with a capture-phase listener rather than assuming:
js("window.__hits=[]; document.addEventListener('click', e => window.__hits.push(...), true)")click_at_xy(x, y)at correct CSS coords (verified viagetBoundingClientRect), then again at devicePixelRatio-scaled coordswindow.__hitsremained[]in both cases — zero events delivered- Keyboard fallback:
press_key("space"),press_key("Enter"), arrows on a focused radio input — also no effect ensure_real_tab()was called and did not clear the state- Manual re-attach attempt —
cdp("Target.attachToTarget", targetId=..., flatten=True)then passing the fresh sessionId — hung the IPC socket (call never returned; daemon stilldaemon_alive() == Trueafterwards) - What DID work: DOM-level
el.click()andel.focus()+type_text()(i.e.Input.insertText), which the agent used to complete the form; the final server echo confirmed real values, so JS-evaluate paths were healthy while Input dispatch paths were not.
So within one daemon: Runtime.evaluate routed to the correct page the whole time, while Input.dispatchMouseEvent / raw key events went somewhere that wasn't the visible document — consistent with Input dispatch running against a stale/wrong CDP session while JS evaluation used a live one.
Possibly-relevant session prelude
The first execution of the incident session was model-authored Playwright-JS fed to the Python harness:
const page = await context.newPage();
which crashed the harness run with SyntaxError (in browser_harness/run.py:387 exec(code, globals())) — i.e. the daemon's first spawn/attach happened under an immediately-aborted exec. All subsequent executions reused that daemon (session: "httpbin"). The click/key deadness was discovered ~12 executions later, after normal goto_url navigation and several successful js()/fill_input() calls.
Repro attempts (all negative)
Same machine, same 0.1.8 install, immediately after:
- fresh session,
new_tab()→ capture-listener +click_at_xy→ event delivered, radio checked ✅ - fresh session,
goto_url()as first navigation (matching the incident) → ✅ - direct CLI heredoc (no integration layer) → ✅
- replaying the SyntaxError-first prelude in a fresh named session, then clicking → not yet re-triggered
Ask
- Should
Input.*dispatch in the daemon pin to the same session resolution asRuntime.evaluate, so the two can't diverge? (daemon.pyresolvesreq.get("session_id") or self.sessionfor both, but the incident shows they can end up effectively split.) ensure_real_tab()appears intended as the recovery path — in this state it returned successfully yet did not restore Input delivery; a health check that round-trips a synthetic click against a listener would make recovery verifiable.- The explicit-session escape hatch (
cdp(..., session_id=...)) hanging the IPC socket turns the last-resort recovery into a lockup; a timeout there would keep sessions debuggable.
Happy to provide the full tool-call sequence (28 executions) from our logs if useful.
Source: browser-use/browser-use