Repository navigation
feat(computer): WebsiteBrowserEnvironment, the base class for website browsers - #449
Merged
earakely-scale merged 6 commits intoNov 2, 2026
Conversation
… browsers A website browser subclasses agentenv_protocol.website_browser's WebsiteBrowserEnvironment, a computer app, and agentenv owns the rest, inside a computer and alone: - The flags after the subclass's command: its profile, the debugging port on loopback, no first run or default-browser check, --lang, and the site proxy when AGENTENV_SITE_PROXY names one. Alone, loopback goes through the proxy too, and the browser refuses to start without one. In a computer, loopback is bypassed so the apps beside it stay reachable. Alone it also runs headless (headful on the open web) at its window size. declaration() refuses a subclass command that carries any of these, or --remote-allow-origins. - BrowserSettings (start_urls, window, locale, time_zone, as TZ), the write_policies install step, @cdp with Chromium's DevToolsActivePort default and a ready check on that address, browser/v1 params on the card, and the skill. The app SDK gains what a browser alone needs, with every other app as before: ComputerAppEnvironment's _launch_for and _on_desktop hooks; an optional Launch.env, left out of stored declarations when unset; and an AppRuntime that waits for X only when DISPLAY is set and adds the menu launcher only on a desktop. A fixture browser bundle tests it in a computer on the shipped Chrome for Testing build. FD-4937 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ns on the desktop - Its argv is split on whitespace, since Chromium rewrites its process title as one line. - The time zone is read from a page, Intl's, since that rewrite also clobbers /proc/<pid>/environ. - The loopback pages are served by the script that opens them, and its tab is brought to the front: on the desktop a background tab doesn't finish loading. FD-4937 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ready waits on Alone, the launch dropped all of its ready, not just the window wait, so a subclass that also waits on a port or a command lost those checks. It now drops only the window, and the runtime takes the ready timeout from the launch as declared, so the timeout holds even when nothing else is left to wait on. The ready check's wait on the debugging address is devtools_timeout_s. FD-4937 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chromium now runs with --remote-debugging-pipe instead of --remote-debugging-port=0. Its app server launches it with a new pipe's ends as fds 3 and 4 and keeps the other ends as self.devtools, a DevToolsPipe. No port listens and no DevToolsActivePort is written, so nothing else in a computer, such as an agent with a terminal, can drive the browser over raw CDP and bypass the way in its task allows. The ready check asks Browser.getVersion over the pipe. @cdp and devtools_address() are gone, since there is no address. Agents get theirs from the front door (M8.6). - session.launch takes fds, moved into place by agentenv_protocol.computer._with_fds, so the server runs no Python between fork and exec. AppRuntime asks the app for them before each launch (_launch_fds) and closes its copies after. - relaunch_on_start: a restarted server stops a browser it finds running and launches it again, since only the launching server holds the pipe. - Headless Chromium takes one start URL on its command line, so it opens the rest over the pipe, behind the first, and is ready once they're open. FD-4937 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rd library python -m agentenv_protocol.computer._with_fds imported the protocol package and the whole computer SDK, pydantic included, in the child before it exec'd the app, with the pipe fds open. Each launch was slower, and a failed import there broke it. session.launch now runs _with_fds.py by path with -I -S: about 64 ms here, against about 236 ms for importing the computer package. The helper also tolerates no moves. FD-4937 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mand too With relaunch_on_start, an app that was launched since boot, at boot or on demand, is stopped and launched again when its server restarts, so a browser launched on demand gets a new pipe instead of being stranded. One that was never launched stays unlaunched. DevToolsPipe's default wait is the named call_timeout_s. FD-4937 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
deleted the
edgararakelyan/fd-4937-m58-websitebrowserenvironment-the-base-class-for-website
branch
November 2, 2026 21:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
This adds the base class every website browser subclasses,
agentenv_protocol.website_browser.WebsiteBrowserEnvironment. It is M5.8 of the website browser design (FD-4937), built to the signed-off design note. A subclass says how its build installs and starts. agentenv owns the rest, the same inside a computer and alone:What the base class adds:
Flags after the subclass's command, then
start_urls: the profile (--user-data-dir=~/<profile>),--remote-debugging-pipe,--no-first-run,--no-default-browser-checkand--lang.The site proxy, when
AGENTENV_SITE_PROXYnames one, with WebRTC's non-proxied UDP off:<-loopback>), and the browser refuses to start without a proxy.*.localhostnames still go through.Alone (
AGENTENV_BROWSER_ALONE=1), it also takes--window-sizeand--headless=new, except on the open web (AGENTENV_BROWSER_OPEN_WEB=1).Owned flags are refused.
declaration()refuses a subclass command that carries any of these,--remote-allow-origins, or any other way to open a debugging port.BrowserSettings:start_urls["about:blank"]-, soenv_paramscan't smuggle in a flagwindowlocaleen-US--langtime_zoneUTCTZin the launch'senvwrite_policies, an install step: agentenv's unattended-use policies, then the subclass'spoliciesover them, written topolicy_dir.A debugging pipe, not a port. The app server launches Chromium with a new pipe's ends as its fds 3 and 4, and keeps the other ends as
self.devtools, aDevToolsPipe.await self.devtools.call(method, params)returns the result, or raisesDevToolsError.DevToolsActivePortis written. So nothing else in a computer, such as an agent that opens a terminal, can drive the browser over raw CDP around the way in its task allows, the action log, or context scoping.devtools_answers, asksBrowser.getVersionover the pipe.connect_over_cdpclients at once and refused a wrong token.browser/v1on the card, params only:chromium,clock: "real"and, alone,window.The skill: sites by their real names,
~/Uploadsand~/Downloads.Changes to the app SDK (
computer/app.py,computer/app_runtime.py,computer/session.py, and the newcomputer/_with_fds.py). Every other app is unchanged.ComputerAppEnvironment.__init__setsself.launch = self._launch_for(...), so the launch op, the ready timeout, the card and the runtime all see one launch._on_desktop()tells the runtime whether the app has a desktop. Both hooks are private.Launch.env, optional andNoneby default. Declarations are stored withexclude_none, so existing apps store exactly what they did, and older readers still parse them.AppRuntime.startwaits for X only whenDISPLAYis set, and writes the menu launcher only on a desktop.session.launchtakesfds, moved into place bycomputer/_with_fds.py, run by path with-I -Sso it loads only the standard library. The server runs no Python between fork and exec.AppRuntimeasks the app for them before each launch (_launch_fds) and closes its copies after.relaunch_on_start, a restarted server stops a boot app it finds running and launches it again. That's for an app only the launching server can talk to, as a browser on its pipe.Not in this PR:
browser/v1's operations (M5.11);For M5.9: the shipped
chromiumlaunches throughchromium.pyas a wrapper script, and it passes its own--user-data-dir. It has to launch Chrome directly and drop those flags before moving onto this class, ordeclaration()refuses it.The CUA session reviewed the design note against main (4 points folded in) and has nothing in progress in the SDK files this touches.
How it was tested
ruff check: clean.packages/agentenv-protocol/tests). That includes 21 new ones intest_website_browser.py, which cover:TZ, and a start URL that is a flag;session.launchand_with_fds: a new pipe at each launch, the ready check going false when it stops and true when relaunched, and the headless start tabs opened over the pipe;tst/integration/computer/website_browser_test.py, slow tier), with a fixture browser bundle on the shipped Chrome for Testing 156 build. Run on an agent-environments-admin VM:--remote-debugging-pipeand--lang=fr-FR, then the start tabs;DevToolsActivePort;Europe/Paris;browser/v1params are right;data/resetrelaunches it with the profile template, on a new pipe the app's readiness answers over.chromium_app_test.py(2 tests) andcomputer_app_test.py(3) still pass.TZset and noDISPLAY;DevToolsActivePort, and no launcher;http://site.localhost/andhttp://127.0.0.1:8765/) go through the proxy, and nothing reaches the loopback server directly.site.localhostgoes through the proxy and loopback is reached directly.AGENTENV_SITE_PROXY.AGENTENV_SITE_PROXYthere until M5.5/M5.7.DevToolsActivePortin port mode, and none of either on the pipe.chrome://policy, read over the pipe, lists agentenv's policies.🤖 Generated with Claude Code


Confidence Score: 5/5
The latest changes appear safe to merge; both numbered previous findings are fixed.Summary
Adds
WebsiteBrowserEnvironmentwith browser settings, managed policies, proxy flags, and a debugging pipe.Diagram
sequenceDiagram participant Server as App server participant Runtime as AppRuntime participant Session as session.launch participant Browser as Chromium Server->>Runtime: Start or recover browser Runtime->>Server: Request new debugging pipe Runtime->>Session: Launch with pipe ends Session->>Browser: Execute with fds 3 and 4 Server->>Browser: Browser.getVersion over pipe Browser-->>Server: Version responseReviews (5) · Last reviewed commit: "fix(computer): a restarted server relaun..." · Reviewed by Greptile