Skip to content

feat(paseo-shared-browser): add low-latency tab video with image fallback - #275

Draft
RyanEwen wants to merge 70 commits into
omercnet:mainfrom
RyanEwen:shared-browser-low-latency-streaming
Draft

RyanEwen wants to merge 70 commits into
omercnet:mainfrom
RyanEwen:shared-browser-low-latency-streaming

Conversation

@RyanEwen

@RyanEwen RyanEwen commented Oct 4, 2026 •

Copy link
Copy Markdown

Summary

Opt-in encoded tab video for the Shared Browser web client, with JPEG as the default and fallback. The capture extension is not loaded on the default image-only path. Enable video explicitly with PASEO_SHARED_BROWSER_VIDEO=1.

The unpublished Android host-video adapter has been removed from this adoption branch. Native clients retain JPEG; historical companion-app or physical-phone results do not qualify this head.

Changes and boundaries

  • Capture the exact workspace tab through a private Chromium helper, encode with WebCodecs, and decode into a retained web canvas.
  • Bound capture, transport and decoder queues; preserve native source age, document/viewport identity, control ownership and no-replay checks.
  • Separate display validity from press eligibility. Display-only packets continue decoding and painting, preserving the following delta, but never grant input authority or retain an older actionable receipt.
  • Wait for the helper document to load before calling its capture function. A startup race previously produced startCapture is not defined; the fix uses readiness, not sleeps or retries.
  • Keep video bitrate/frame-rate controls distinct from JPEG quality and rendering density. Image capture defaults to Medium (65), with the original 800 KB bound. Agent viewport requests are bounded to 1600 x 1200. Explicit High remains available; no agent device tool is added.
  • Retain the reviewed input, Compose and owned-runtime/Xvfb safety corrections. Xvfb is a separate opt-in, not a video prerequisite.

Encoded packets use authenticated Paseo RPC. This adds no public media listener or TURN requirement and is not WebRTC. Video remains lossy; higher density/bitrate costs CPU and bandwidth. Timing observations are not a universal latency guarantee.

Exact reviewed head and validation

Head: 799692eb3a0b4dee4212ba31ec5a74769a7b5208. Astra approved the integrated production branch, the display-only decoder correction, the helper-readiness delta and this final test-only Windows fixture correction, with no remaining code-review findings.

Main independently verified TypeScript, Biome, SDK client/server compilation and 652 unit tests at this exact head. Production is byte-identical to the reviewed 49ea97d parent, where Main verified the production JS-launcher runtime, 5 runtime smokes and both mouse/touch video smokes. One initial local video process exited 143 before test results; its log is retained separately from the successful complete-module recheck.

Astra independently verified the actual-caller decoder regression, complete hook/decoder modules, 24 native-capture tests and four readiness-expression cases. Real mounted checks observed 20 distinct paints while a pointer was held, followed by a successful independent press. A separate wire-observed exercise included display-only packets and 18 of 18 presses landing.

Native CI run 37796787869 passed the shared-browser jobs on Linux, macOS and Windows at this exact head. All 19 exact-head screenshots below are attached and independently verified rendering.

Linux web viewport emulation is not physical-phone/native-app qualification. Native Windows/macOS video, arbitrary network latency and older host combinations are not claimed verified by the local checks. Initial JPEG presentation after a daemon restart can be delayed; instant first-frame display is not claimed.

Stack accounting

This is a cumulative branch containing earlier input/display/Xvfb work. The current #274 now includes the earlier video and independent-tab changes, rather than being an input-only prerequisite. Coordinate the final adoption across these overlapping branches; do not merge the old circular stack order blindly. #280 remains explicitly held as overlapping work with blockers.

Automatic Shuni feedback has not yet been observed. Draft status and the final cumulative-stack adoption decision remain explicit. No merge has been performed.

AI disclosure: this description and the related code were written with the assistance of AI.

Exact-head visual evidence

19 screenshots: wide and compact scenarios

Real synthetic Linux Chromium web captures at this exact head. CI was pending at capture and is tracked separately above. Counters can include earlier setup actions; conclusions use the corresponding before/after and beacon observations. These are not physical-phone or native-client screenshots. Some later recovery images retain an earlier refused-gesture error banner. Warmed press pairs are not a first-load latency guarantee; the first cold pair took about 2.5 s.

PR 275 799692e: wide image 01 fixture controlled cdp

PR 275 799692e: wide image 02 two consecutive presses

PR 275 799692e: wide image 03 press during frozen page stale presentation

PR 275 799692e: wide image 04 refused press not replayed after recovery

PR 275 799692e: wide image 05 fresh press after recovery

PR 275 799692e: wide image 06 quality menu medium 65 default

PR 275 799692e: wide video 01 fixture controlled video

PR 275 799692e: wide video 02 two consecutive presses

PR 275 799692e: wide video 03 video stalled updating video

PR 275 799692e: wide video 04 press during stall refused not replayed

PR 275 799692e: wide video 05 fresh press after resume

PR 275 799692e: wide video 06 panel hidden diff tab active

PR 275 799692e: wide video 07 panel resumed video live

PR 275 799692e: wide video 08 quality menu video bitrate fps

PR 275 799692e: wide video 09 held input video keeps painting

PR 275 799692e: compact image 01 fixture controlled cdp

PR 275 799692e: compact image 02 two consecutive presses

PR 275 799692e: compact video 01 fixture controlled video

PR 275 799692e: compact video 02 two consecutive presses

…contracts

Add encoded packet limits, independent density and encoder targets, and versioned display preferences. Keep additions compatible with existing image clients and separate continuous human admission from strict agent frame targeting.

Validate the contract and settings regressions and the complete TypeScript build.
Capture the exact workspace tab through a private immutable extension and isolate encoder cohorts. Bound encoded buffers, retire idle media, and recover source failures without changing geometry or resetting healthy peer encoders.

Cover packet backlog, byte conversion, failure isolation, cleanup, codec allocation and source timestamps with native helper regressions.
… control

Fence media and input across native attachment, document, bridge and viewport transitions. Admit continuous human geometry while retaining strict agent receipts, reserve control and heartbeat capacity, and reconcile navigation without blocking steady video reads.

Add redacted operation diagnostics, demand-driven JPEG capture, bounded recovery and transition regressions. Register shared contract tests in the standard unit gate. TypeScript and all 475 checkpoint tests pass.
Decode bounded native packets into a retained canvas, keep input continuous during paints, and hand off to qualified image fallback without flashing stale frames. Add independent encoder and capture-density controls.

Fence delayed mutations across viewer and controller replacement and split panel styles, chrome, native keyboard and presence into focused modules. Include decoder, mounted settlement and real native capture-to-canvas regressions.
…servation

Do not invalidate a pending acquisition when media first observes the same viewer as controller. Competing ownership and viewer replacement still revoke delayed replies. Reproduce the race in the full React panel and verify the returned token reaches the next navigation.
…n input

Bind in-flight agent work to the original ticket and native page. Validate agent requests with the shared schemas and preserve the original failure while attempting paired cleanup once.
Add direct keyboard, pointer and touch controls, display presets and favorites, toolbar menus, sharp captures, native desktop hover, and stable frame buffering. Preserve page state during mode changes and reduce redundant frame transfers. Include regression tests and fork reconciliation notes.
Format the embedded extension program and document frame units, peer ownership, codec recovery and teardown contracts without changing its capture protocol.
… review

Apply pinned formatting, type the viewport fixture, align the smoke with default capture quality, and document isolated validation and the supported SDK.
…d SDK

Require Paseo 0.11 with the tested beta allowance. Ship design and research references, document isolated validation and quality trade-offs, and add real SDK compilation and Linux video decoding to CI.
Resolve CSS auto cursors over selectable glyphs and editable surfaces while preserving explicit cursor styles and existing control authority. Cover native text, padding, vertical text and shadow content with isolated Chromium checks.
Use recognizable height labels, add 1440-wide desktop choices, and project favorites in picker order without rewriting saved IDs. Include shared contract tests in the normal regression command.
Use each icon control accessibility label as its native web hover title, including contextual mode labels. Keep native accessibility and shared toolbar behavior intact.
…oded receipt per press

Remove the time-unbounded retained input admission. Every independent press (mouse down or
first touch contact) in JPEG and video modes must carry a decoded frame receipt that is
within its five second lifetime and not spent by earlier acknowledged input; reopening an
idle channel needs a newer frame. Held drags, releases and keys keep their ordered channel,
and no uncertain input is replayed.
…before relaunch

Replace the immediate private-display discard with close, bounded exit observation, then forced termination. The daemon is identified by PID plus kernel start time and executable captured at launch and revalidated before any signal; a missing, replaced or reused PID is never signalled and forced exit is confirmed before shutdown returns.
…it attach

An explicit bridge-authorised attach now replaces a lost runtime exactly once, including loss first observed by that attach. Agent status and capture establish viewers without replacement authority. Generic errors are never retried.
…t through every primitive

All discrete agent/human input kinds (type, key, move, scroll, click, drag) now run inside a
runtime-owned channel: the captured document is reconciled first, native begin pins it with
expectedInputGeneration, each primitive carries the channel so the runtime re-checks the
attachment/document, and end releases on the original attachment. The capture is spent in a
finally even when admission or cleanup fails. The socket hot-path test now asserts the safe
contract: a stalled metadata read holds back every primitive, and input is not delayed by a
pending video read.
…h chain

Replace executable matching with a per-runtime nonce inherited through the launcher and daemon. Only a PID whose environment carries the nonce gets a verified identity (plus start time against PID reuse) and may be signalled; unowned or unverifiable PIDs are observed with signal 0, never signalled, and an unconfirmed exit is reported rather than assumed. Covers the shipped JS launcher layout.
Probe the daemon as owned, foreign, absent or unknown; only absence or proven replacement is a confirmed exit. A launched runtime with no readable PID record or identity fails shutdown after the bounded wait without signalling or clearing metadata. Concurrent shutdowns share one operation and a failed shutdown is retried, never remembered as success. A lost workspace entry is kept until its stop succeeds.
…visor ensure boundary

Carry the explicit replaceLost intent from the bridge-authorised attach through session creation to the supervisor ensure, which now reports a lost runtime instead of stopping or recreating it for implicit agent and protocol callers. Mark the daemon as possibly launched before open is published so a failed open is observed, stopped and confirmed, and keep the original creation error when cleanup is unconfirmed.
…gent viewports at the ticket boundary

Pass explicit low/medium/high agent capture qualities through and default only when omitted.
Enforce the 1600x1200 agent viewport in the supervisor agent branch before any mutation, using
one MAX_AGENT_VIEWPORT constant shared with the MCP adapter; human presets up to 2560 are
unchanged. The JPEG menu now badges the actual DEFAULT_CAPTURE_QUALITY instead of High.
… the approved 293 net input, Compose and Xvfb core

Reconcile the 275 qualification (per-press frame receipts, pinned discrete input with
expectedInputGeneration, opt-in extension-free image path, restored defaults and agent bounds,
0.38.2 runtime) with 293 6ebe280: owner-bound held-input release and publication-boundary
pointer tracking, one fenced Compose control (native keyboard relay removed), opt-in Xvfb
with ownership-bound daemon shutdown and explicit lost-runtime recovery. Shutdown now fences
media first, then runs the 293 daemon shutdown inside the native video barrier.
Native video no longer depends on a private display; Xvfb stays an opt-in covered by its own smoke.
… revoked the spent receipt

Video drops its input authority the moment a press is acknowledged and regains it only when the
next packet paints, while JPEG keeps the spent frame visible. A second consecutive press in a
mounted video panel was refused with 'Waiting for a current decoded frame' instead of waiting
for that frame. The queue now treats missing authority after a spent receipt like a spent
visible frame: bounded wait, then press with the newer receipt.
…ent, not at local press

A press began a new epoch locally, but the host only revoked earlier receipts when it published
the input. Reads requested after the local press yet answered before publication were painted
as actionable and then refused as stale by the next press; packets captured before the
host's revocation floor were issued no receipt at all. The host now flags each video packet
actionable or not, and the viewer starts a new epoch when the host acknowledges a
receipt-revoking input.
… tests portable

Integrate only the Astra-approved test delta 6ebe280..6359527 (57f044d, 6359527): the simulated
ownership mock reads its map and the Linux-only coverage is preserved. The 0.38.2 runtime
fixture is kept. Production code is untouched.
…ignalCode

Apply cfe9e50: Windows reports no signalCode for a killed child, so assert the runtime's
SIGKILL request for the owned pid and the process's observed absence. The 0.38.2 fixture is
unchanged and production code is untouched.
…granting input

The mounted panel's isCurrent rejected packets the host flagged actionable:false, and the
video hook reused it as decoder source eligibility, so a display-only packet was skipped,
the encoded sequence advanced past it, and the next actionable delta lost its chain and
painted nothing. Source/document/geometry validity (decode and paint) is now separate from
input eligibility (actionable receipt): display-only packets decode and paint, never publish
frontRef or input authority, and no longer leave an older actionable receipt current.
…before starting capture

Runtime.evaluate can arrive while the helper document is still loading, so
startCapture was a ReferenceError before the recorder script ran, causing a
recovery cooldown and no output. Wait for the document load event and refuse
to call a capture entry point the document never defined.

Same minimal change as 827dbfb on the current 274 line; no second implementation.
…le to Windows

The opt-in test wrote a POSIX-shebang script as the owned CLI, which Windows cannot
execute (spawn ENOENT before the expected stop-before-browser error), and the
extension test hardcoded /tmp. Simulate the owned CLI at the child_process boundary as
daemon-shutdown.test.ts does, and use os.tmpdir()/join. Every opt-in, default,
no-extension and allowlist assertion is unchanged.
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.

2 participants