Skip to content

feat(ego-windows-host): run the ego-browser runtime on stock Edge/Chrome (Windows preview) - #228

Open
Hotragn wants to merge 2 commits into
citrolabs:devfrom
Hotragn:feat/windows-host
Open

feat(ego-windows-host): run the ego-browser runtime on stock Edge/Chrome (Windows preview)#228
Hotragn wants to merge 2 commits into
citrolabs:devfrom
Hotragn:feat/windows-host

Conversation

@Hotragn

@Hotragn Hotragn commented Aug 6, 2026

Copy link
Copy Markdown

Summary

Windows users cannot run ego-browser at all while native Windows support (#203) is under evaluation. This PR adds package/ego-windows-host, a preview host that implements the globalThis.ego contract against stock Microsoft Edge or Chrome over loopback CDP and delegates execution to the unmodified ego-browser runtime. Everything the runtime offers — task spaces, page/locators, snapshots, screenshots, control handoff — works on Windows today, and scripts stay byte-identical when the native app ships. Additive only: no existing file is touched except one new, path-scoped workflow.

Related issue

Closes #227 (proposal). Addresses the Windows-user half of #203 as a stopgap. Follows the host-contract prior art of #134/#202 (credit to @iagogfe and @olenanikita1980-cell); deliberately a sibling package rather than an extension of #202, whose daemon/Unix-socket design doesn't map to Windows and whose review shouldn't be bloated from the side.

Changes

  • package/ego-windows-host/src/ego-bridge.ts — the ego contract: verbatim CDP passthrough (sendCDPMessage / onCDPMessage / onSendCDPMessageError), listTabs/createTab scoped to the selected task space, the full task-space method set resolving the documented { error, error_code } shapes, snapshot() (rejects with EGO_TASK_SPACE_USER_IN_CONTROL during handoff — the contract probeAgentControl/waitForAgentControl rely on), and getBrowserVersion() reporting no update so the notice stays silent. Raw Target.createTarget/activateTarget/closeTarget through the passthrough are sniffed so tab bookkeeping stays consistent however the runtime opens tabs.
  • src/task-spaces.ts — spaces as persisted tab sets with ownership (agent/agentDelegatedToUser/user), atomic JSON state under %LOCALAPPDATA%\ego-windows-host. No daemon: the detached browser is the persistent process.
  • src/ax-snapshot.ts{ content, refs } from Accessibility.getFullAXTree, with [@backendNodeId] marks that resolve through the runtime's existing browserSnapshotRefsToRefMap/parseRef path. Honestly weaker than the app's kernel-level snapshot; documented as such.
  • src/browser-locator.ts, src/chrome-launcher.ts — Edge-first detection across standard Windows install roots (EGO_HOST_BROWSER_PATH override), reuse-or-launch against a fixed loopback CDP port with a dedicated profile; never touches the user's daily browser profile.
  • src/cdp-connection.ts — minimal CDP client over Node ≥ 22's global WebSocket (zero new runtime dependencies, matching the repo's no-Puppeteer/Playwright rule). Two connections per run: host-internal and agent passthrough, so flattened sessions never mix.
  • src/cli.ts, bin/ego-windows-host.mjs — script file / -e / stdin input (PowerShell has no heredocs; same rationale as [Feature]: Accept a script file or inline -e code as CLI input (heredoc-free invocation for Windows shells) #225/feat(ego-browser): align runtime CLI input with the nodejs dispatcher and add a script-file form #226), a leading nodejs accepted for compatibility, --doctor state report.
  • .github/workflows/windows-host.yml — path-scoped test job on windows-latest + ubuntu-latest (npm ci --ignore-scripts until fix(ego-browser): make the repo build, test, and commit on Windows #148 makes the sibling prepare script Windows-safe). A new file so it cannot conflict with feat(ego-linux-host): add persistent Linux browser host #202's CI edits.
  • 57 unit tests across six suites (locator, launcher, CDP client, registry, snapshot renderer, bridge), all offline with injected fakes per the repo's stub-injection style.

Verification

Unit/typecheck, run from package/ego-windows-host (Windows 11, Node 24):

npm test  -> build + typecheck + 57 pass, 0 fail
package/ego-browser npm test unaffected (no existing file modified)

Real end-to-end against stock Edge 151 headless on Windows 11 (transcript):

> node bin/ego-windows-host.mjs -e "...useOrCreate('host smoke'); openOrReuseTab('https://example.com')..."
{
  "taskSpaceId": 1,
  "url": "https://example.com/",
  "heading": "Example Domain",
  "snapshotPreview": [
    "- RootWebArea \"Example Domain\"",
    "  - heading \"Example Domain\" [@13]",
    "  - text: \"This domain is for use in documentation examples...\"",
    "  - link \"Learn more\" [@16]"
  ],
  "screenshot": "C:\\...\\ego-browser-shot-36172-1.png"
}

# separate invocation — persistence and interaction
{ "taskSpaceId": 1, "reusedExistingTab": true,
  "clickNavigatedTo": "https://www.iana.org/help/example-domains",
  "navigationWaitResolved": true }

# handoff / hard stop / takeover / completion
handOff: {"done":true}
(next invocation, browser command) -> the runtime's owned user-control hard-stop guidance, verbatim
--doctor after takeOver -> #1 "host smoke" ownership=agent tabs=2
complete({keep:false}) -> {"done":true}; --doctor -> task spaces: 0

The handoff transcript is worth a look: the bridge only supplies the stable error_code, and the runtime's own output sink produces the identical hard-stop collapse it produces against the real app — which is the point of implementing the contract instead of forking the runtime.

Impact

  • Public helper API or behavior
  • Agent skill or instructions
  • Site learning
  • Installation or update flow
  • Build, CI, or release process
  • Documentation only
  • No externally visible impact

Nothing existing changes: no package/ego-browser file is modified, no skill text changes, and the new workflow is path-scoped to the new package. The host is opt-in (its own bin, never on PATH unless installed). Known limitations are stated in the package README: plainer snapshot than the app, dedicated profile rather than Chrome import, no Spaces UI, loopback CDP port exposure. If the team prefers a different direction (e.g. converging with #202 into one cross-platform host, or waiting for the native app), closing this costs nothing — the contract knowledge is in #227 either way.

Checklist

  • The PR targets the correct base branch (dev for normal changes; only dev may target main).
  • The change is focused and does not include unrelated cleanup.
  • Tests were added or updated for behavior changes, or the reason they are unnecessary is explained above.
  • Relevant tests and validation commands pass locally.
  • Public helper JSDoc and agent-facing documentation are updated when the helper surface changes.
  • No credentials, tokens, cookies, personal data, or other secrets are included.
  • A release-note label is selected (feat).

@Hotragn

Hotragn commented Aug 25, 2026

Copy link
Copy Markdown
Author

Pushed three hardening changes found by actually living on this host for a couple of weeks. All are additive; nothing in the original review scope changed.

1. Launch at a desktop window size (--window-size=1440,960). Chromium's default window is small enough that responsive sites collapse their controls, and the failure mode is silent rather than loud: on Wikipedia the search field is replaced by a toggle button and leaves a 0x0 unfocusable input behind, so fill() writes nothing and reports success. A desktop-sized window keeps agent scripts on the layout desktop users actually see. Overridable via the new windowSize option.

2. Diagnose a launch that never comes up. The bare did not expose CDP on port N timeout is unactionable, and two causes account for nearly all of them — neither prints anything anywhere: the profile is already open in another browser process (Chromium forwards the command line to the running instance and exits, so the port is never bound), or something else holds the port. The error now names the likely cause, flags a lockfile when it sees one, and includes the exact command line so the failure can be reproduced by hand.

3. Opt-in per-space windows (EGO_HOST_SPACE_WINDOWS=1). Opens each new task space in its own browser window rather than a tab, which recovers part of what the README lists as a limitation — no Spaces UI — by making parallel agents visually separate. Off by default.

Verification on Windows 11 / Node 24:

npm test  -> 60 tests, 60 pass (was 57; +3 launcher cases)
prettier --check (changed files) -> clean

# live, against real Edge headless
{"space":1,"url":"https://example.com/","viewport":"1410x865","heading":"Example Domain"}

That 1410x865 is the point of change 1 — a desktop viewport where Chromium's default would have been far narrower.

@Hotragn

Hotragn commented Aug 28, 2026

Copy link
Copy Markdown
Author

Flagging a change in premise rather than leaving this sitting in the queue.

On #203, @nukochow says a Windows version of ego lite is launching soon. I cannot verify that as official — the account has no commits or PRs here, and the association is NONE — so I am not treating it as a roadmap statement. But if it is accurate, it removes most of this PR's reason to exist: the whole case for a preview host was "Windows users have nothing at all while native Windows is evaluated," and that argument does not survive a native build shipping.

So: say the word and I will close this. No explanation needed, and no hard feelings — a stopgap that arrives after the real thing is just review burden. I would rather retire it myself than have it linger.

If it is still useful in the meantime, it is current: rebased, 60/60 tests, and re-verified live against Edge 151 (viewport 1410x865) after the hardening I pushed above.

Two things from this work that stay useful either way, independent of whether a host ships:

  • The globalThis.ego contract is now written down and exercised by 60 offline tests — the task-space ownership states, the { error, error_code } shapes, and specifically that handOff must make both sendCDPMessage and snapshot fail with EGO_TASK_SPACE_USER_IN_CONTROL, which is what makes the runtime's hard-stop guidance and waitForAgentControl behave correctly. That is a usable conformance description for any host, including a native one.
  • The Windows failure modes it surfaced are real regardless: the silent responsive-collapse problem (a small default window leaves a 0x0 unfocusable input, so fill() writes nothing and reports success) and the profile-lock/port-held launch failures that print nothing anywhere.

Whichever way you go on the package itself, #312 is the one I would actually prioritise ahead of a Windows launch — that one blocks Windows contributors on the active branch today.

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.

1 participant