Skip to content

fix(agent-browser): route oversized tabs snapshots to the tab list - #297

Merged
nedtwigg merged 1 commit into
mainfrom
fix/issue-296
Jul 27, 2026
Merged

fix(agent-browser): route oversized tabs snapshots to the tab list#297
nedtwigg merged 1 commit into
mainfrom
fix/issue-296

Conversation

@dormouse-bot

Copy link
Copy Markdown
Collaborator

Problem

Closes #296. In agent-browser-connection.ts, handleMessage routed stream messages purely by size: any payload longer than FRAME_PULSE_THRESHOLD (16384) was treated as a frame. A tabs snapshot carries url + title per tab, so a user with many open tabs (long URLs/titles) can push a tabs message past 16KB. When that happened the snapshot was silently dropped — hashed and emitted as a bare frame-pulse — and the tab list / active-tab selection never updated. Because the daemon re-broadcasts tabs on its heartbeat, a consistently-oversized tab list would appear frozen until a smaller snapshot happened to arrive.

The idle path (wantFrameData() false) is the dominant real-world case: wantsProvisionalFrame() returns false at steady state on hosts with crisp screenshots, so the fix had to cover it — not just the hover path.

Solution

Discriminate by content, not just size. A frame's bulk is a base64 JPEG body, and the base64 alphabet contains no " or :, so a compact "type":"tabs" / "type":"status" substring can never occur inside a real frame. It's a zero-false-positive marker for an oversized control message:

  • If a large payload contains that marker → route it through the normal control dispatch (shared dispatchControl helper).
  • Otherwise → unchanged frame fast path (hash + pulse, parse only when wantFrameData()).

This preserves the threshold's performance intent — frames never pay an extra parse, only rare oversized control messages do — and the change is strictly monotonic: it can only recover a previously-dropped snapshot, never misroute a frame.

One assumption: control messages are serialized as compact JSON (no space after the colon), which every in-repo fixture and JSON.stringify's default output confirm. If a daemon ever emitted spaced JSON, this fix would be a no-op for that case rather than a regression.

Testing

Added a regression test in the existing agent-browser-connection.test.ts that emits an ~24KB tabs snapshot (80 tabs with long URLs) on the default idle path and asserts the 80 tabs reach the snapshot. It fails on main ([] — dropped) and passes with the fix. Full file suite (8 tests) green; tsc --noEmit on the lib clean.


Closes #296 — automated triage

A stream message longer than FRAME_PULSE_THRESHOLD (16384) was treated
unconditionally as a frame, so a `tabs` snapshot with many long URLs/titles
that crossed the threshold was silently dropped — the tab list never updated.

Discriminate by content, not just size: a base64 JPEG frame body contains no
` or : characters, so a compact "type":"tabs"/"type":"status" substring is a
zero-false-positive marker for an oversized control message. Only those pay a
parse; frames keep their hash+pulse fast path untouched.

Closes #296
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0f3b5a6
Status: ✅  Deploy successful!
Preview URL: https://14cda9f4.mouseterm.pages.dev
Branch Preview URL: https://fix-issue-296.mouseterm.pages.dev

View logs

@nedtwigg
nedtwigg merged commit 00b8e9e into main Jul 27, 2026
7 checks passed
@nedtwigg
nedtwigg deleted the fix/issue-296 branch July 27, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

agent-browser: large (>16KB) tabs snapshots misclassified as frames and dropped

2 participants