feat: stream Agent Manager browser previews with public HTTPS - #13804
Conversation
Code Review SummaryStatus: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (incremental delta since df41127)
Inline publication was blocked: the bot account already has a pending review on this pull request, so the finding above is reported in the summary only. Fix these issues in Kilo Cloud Previous Review Summary (commit df41127)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit df41127)Status: 1 Issue Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
Files Reviewed (5 files)
Inline publication was blocked: the bot account already has a pending review on this pull request, so the finding above is reported in the summary only. Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0 Review guidance: REVIEW.md from base branch |
… browser-public-https-13618
…public-https-13618 # Conflicts: # packages/kilo-vscode/src/agent-manager/vscode-host.ts # packages/kilo-vscode/src/services/browser-automation/browser-broker.ts # packages/kilo-vscode/src/services/browser-automation/index.ts # packages/kilo-vscode/tests/unit/browser-broker.test.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/ar.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/br.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/bs.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/da.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/de.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/en.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/es.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/fa.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/fr.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/it.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/ja.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/ko.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/nl.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/no.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/pl.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/ru.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/th.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/tr.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/uk.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/zh.ts # packages/kilo-vscode/webview-ui/agent-manager/i18n/zht.ts # packages/kilo-vscode/webview-ui/src/i18n/en.ts # packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts # packages/opencode/src/kilocode/tool/browser-open.txt
…public-https-13618
…public-https-13618
…public-https-13618
Status update: two worktrees, performance, and fixesI tested the streamed browser again in a real VS Code instance with two Agent Manager worktrees open at the same time (macOS, Apple M4 Pro, Chrome 153). Works
Measured cost (two live browsers, 15 s samples)
Only one stream can be visible in each Agent Manager window, so streaming cost does not grow with the number of worktrees. Fixed (pushed in 4ef8aeb)
Known limits
ChecksTypecheck, lint, and knip pass. The browser test files pass (97 tests), including new tests for disconnect recovery, page crash, concurrent missing-browser errors, and the unreachable-server message. |
…vigation Restart Chromium after it disconnects and replace crashed pages on refresh. Report unreachable local servers instead of a proxy HTTP 403. Serialize agent screenshots with stream resizes so agent-opened pages stream. Add back and forward buttons and open 0.0.0.0 dev server URLs on loopback.
WebReflection
left a comment
There was a problem hiding this comment.
I think this looks good but I wonder if the default should not be localhost instead of 127.0.0.1. Reason being: browsers already handle localhost as if it was https equivalent which is usually the expected developer experience while debugging/developing locally, otherwise simple APIs such as crypto.randomUUID() will fail at 127.0.0.1 or 0.0.0.0. If this is meant though, I am OK with the current choice.
|
@WebReflection The crypto.randomUUID() concern does not apply to 127.0.0.1, only to 0.0.0.0.
|
|
We can do localhost then, it should be a one line change |
Keep the origin that dev tools usually configure, so cookies, OAuth redirects, and CORS allowlists for localhost apply.
Approach origin and comparisonThe streamed approach in this PR follows the deprecated VS Code Browser Preview extension by Kenneth Auchenberg (Microsoft):
That extension launched headless Chromium with Puppeteer, streamed CDP screencast frames as base64 JPEG or PNG, drew each frame on a canvas in a webview, and forwarded CDP input events. Its This PR keeps that model: VS Code stopped that model. Its current Integrated Browser embeds a real Chromium through an Electron
We keep streaming instead of The security layer is the main difference from the old extension. The old extension had no proxy, no origin allowlist, and no per-session isolation. This PR adds |
Future Playwright extensionsA later step can point the built-in Playwright MCP server ( Do not connect Playwright to that endpoint directly. A direct The safe design is the one VS Code uses for the Integrated Browser: put a CDP proxy in front of the browser. The proxy multiplexes clients, filters targets per session with Reference:
|
…public-https-13618 # Conflicts: # bun.lock
uhm ... that might be a chromium only thing or maybe that's now the de-facto standard, pretty sure once upon a time 127.0.0.1 wasn't handled the same, but I think thanks for the change/check though P.S. in PyScript we were redirecting all 0.0.0.0 to localhost because Python httpserver starts suggesting 0.0.0.0 by default 🤦 |
What Problem This Solves
Agent Manager's browser is limited to HTTP loopback applications. Its iframe preview is separate from the Chromium page used for automation, so framing restrictions can prevent a page from appearing and the visible page is not necessarily the page being inspected. Local applications also need public CDN modules and other secure resources.
This implements the public HTTPS use case tracked in #13618. It deliberately changes the preview architecture instead of removing framing headers. It also expands resource access beyond that issue's original same-origin-only proposal: public HTTPS/WSS resources may load across origins, while new document origins require approval.
Why This Change Was Made
Architecture
The preview is not an iframe, and it does not use VS Code's integrated browser API. A headless Chrome process runs outside the webview; only encoded frames and input events cross the boundary. This is the same family of design as the older VS Code browser-preview extensions: the extension host owns the browser and enforces policy where connections are actually made, while the webview only paints pixels and forwards input.
flowchart LR subgraph HOST["VS Code extension host (Node.js)"] BRK["BrowserBroker<br/>authenticated loopback API"] STR["BrowserStream<br/>CDP screencast + input"] NET["BrowserNetwork + BrowserProxy<br/>request policy, DNS pinning"] end subgraph WEB["Agent Manager webview (SolidJS)"] VP["StreamViewport<br/>canvas + input capture"] end subgraph CHROME["Headless Chrome (separate OS process)"] CTX["BrowserContext per session<br/>one page + per-context proxy"] end CLI["CLI agent<br/>browser_open tool"] VP -- "postMessage: viewport / interact" --> BRK BRK -- "postMessage: frame (JPEG + identity)" --> VP STR -- "newCDPSession, Input.*" --> CTX STR -- "Page.startScreencast / ack" --> CTX NET -- "proxy + Fetch gate" --> CTX CLI -- "Bearer token over 127.0.0.1" --> BRKOne Chrome process is shared by the extension host, and each session gets its own isolated
BrowserContext. Chrome sends JPEG frames over a CDP screencast; the webview decodes them onto a canvas and drops stale frames by identity. Pointer, keyboard, wheel, IME, and clipboard events travel back and are replayed with CDP input calls.Policy is enforced at the connection boundary, not by inspecting page content:
flowchart TB subgraph CONTENT["Page content (untrusted)"] PUB["Public HTTPS document"] LOCAL["Approved localhost app"] end FG["Browser-target Fetch gate<br/>rejects unowned frames before first request"] PX["Per-context authenticated proxy<br/>validates DNS, dials pinned IP"] PUB --> FG LOCAL --> FG FG --> PX PX -->|public origins| EXT["Public HTTPS / WSS"] PX -->|approved origin only| LOOP["localhost:port"] PX -.->|denied| PRIV["Private / link-local / LAN"]Local HTTP applications remain limited to their approved origin, public pages cannot reach private or loopback destinations, navigation to a new origin requires approval, and certificate validation stays with Chromium. While the agent sandbox is enabled, the agent cannot drive this browser at all, so the stream cannot become a network bypass; the manual Browser panel still works.
Key files: BrowserBroker, BrowserStream, BrowserNetwork, BrowserProxy, StreamViewport, and the browser_open tool.
User Impact
browser_open, alongside HTTP localhost applications. Bare public hostnames in the address bar use HTTPS.http://0.0.0.0:<port>URLs printed by dev servers. They open ashttp://localhost:<port>, because 0.0.0.0 is a listen address, not a destination, and localhost is the origin dev tools usually configure.Known limits:
Evidence
Latest changes (
4ef8aeb55b), verified in an isolated VS Code instance with two Agent Manager worktrees on macOS:browser_openwith the panel closed streamed in 3 of 3 runs (0 of 3 before the fix). The cause was a Playwright screenshot restoring the viewport during a concurrent stream resize; screenshots now run in the stream queue.Fresh checks after updating the branch to
1536aef0fb:bunxbootstrap failed; the resulting binary passed its version, models, and sandbox-worker smoke tests.During implementation, controlled real-Chromium regressions verified rejected preflights send no mutation, allowed preflights complete, private/loopback redirects and workers make no forbidden contact, and unowned popup/target requests are blocked. A focused run on both installed Chrome and pinned Chromium 143 passed 139 tests, with 3 opt-in external probes skipped.
Isolated VS Code checks verified the missing-browser settings/retry recovery flow, local CDN/WebGL loading, copy/cut followed by custom paste handling, and stream visibility behavior. A real Chromium page with an eight-second blocking copy handler closed in 14 ms and reopened in a fresh context. These are scoped macOS results, not cross-platform performance guarantees.
Streamed local fixture after copy/cut and a page-handled paste:
Missing-browser recovery guidance:
Manual verification: enable Browser Automation in a trusted workspace; open localhost and an approved HTTPS page; test clipboard cancellation/custom handling; close and reopen a busy page; and check the recovery actions when the selected browser is unavailable.