Ego‑Browser Workflows Explained: Semantic, Visual, and Direct DOM Automation
Ego‑Browser provides three distinct workflows—semantic, visual, and direct DOM/CDP—that agents select based on whether a target page exposes reliable DOM structure, canvas‑based rendering, or requires low‑level browser protocol access.
The ego‑browser automation harness (from the citrolabs/ego-lite repository) gives agents flexible, battle‑tested primitives for interacting with any web page. Rather than forcing a single abstraction onto every site, the runtime exposes three purpose‑built workflows. Each workflow maps to a different layer of the browser stack, letting agents trade convenience for control as the situation demands.
Semantic Workflow: Compact, Human‑Readable Page Automation
The semantic workflow is the default choice for standard websites where the DOM and accessibility tree accurately represent visible content.
How Semantic Mode Works
When you call snapshotText(), the runtime captures a semantic snapshot—a pruned tree of interactive elements with auto‑generated @N references and stable loc= selectors. This produces a compact, text‑based representation that LLMs can reason about efficiently.
From src/helpers.ts, the semantic helpers include:
snapshotText()– generates the ref map and locator snapshotclick('@N' | 'loc=...')– resolves refs or locators to elementsfillInput('@N', value)– types into form fields- Standard navigation and wait helpers
The Element Resolver (src/element-resolver.ts) handles the translation from refs/locators to concrete CDP element IDs, classifying failures as transient (retryable) or permanent.
Semantic Workflow Example
const task = await useOrCreateTaskSpace('news')
await gotoAndWait('https://example.com', { timeout: 30 })
const snapshot = await snapshotText()
const titles = await js(String.raw`() => {
const items = [...document.querySelectorAll('h2')]
return items.map(el => el.innerText)
}`)
cliLog('Found titles:', titles)
await click('@1') // click first ref from snapshot
await snapshotText() // re‑snapshot after navigation
Per the SKILL.md documentation, each snapshotText() call rebuilds the ref map, ensuring @N references stay fresh across navigation or dynamic updates. When a ref expires, helpers fall back to stable locators automatically [SKILL.md#L71‑L84].
Visual Workflow: Coordinate‑Based Control for Canvas Apps
The visual workflow targets pages where the DOM lies—canvas‑heavy editors, virtualized spreadsheets, maps, and design tools where the true editing surface lives in pixels, not markup.
When to Use Visual Mode
| Scenario | Why DOM fails | Visual solution |
|---|---|---|
| Google Docs/Sheets | Content rendered to <canvas> or off‑screen buffers |
Screenshot‑based coordinate interaction |
| Figma, Miro, Excalidraw | Vector graphics, no semantic element tree | Pixel‑precise click, drag, keyboard |
| Map interfaces (Google Maps, Mapbox) | Tiles as images, markers overlaid | Viewport coordinate navigation |
| Virtualized lists (Notion, Asana) | DOM nodes recycled, positions unstable | Screenshot verification + coordinate actions |
Visual Workflow Helpers
From src/helpers.ts, visual primitives include:
captureScreenshot()– viewport image captureclick([x, y])– coordinate‑based clickingdoubleClick([x, y]),moveMouse([x, y])– gesture simulationpressKey(),typeText()– keyboard injection
Visual Workflow Example
await gotoAndWait('https://canvas-app.example.com')
await captureScreenshot()
await click([200, 150]) // start point
await moveMouse([400, 300]) // drag to end point
await captureScreenshot() // verify the drawing
Because the DOM is unreliable, screenshots become the source of truth. Agents validate actions through visual feedback or export/read‑back checks rather than DOM assertions.
Direct DOM / CDP Workflow: Protocol‑Level Control
The direct DOM / CDP workflow exposes the raw browser for scenarios beyond what semantic or visual helpers cover.
Capabilities of Direct Mode
| Use case | Helper | Underlying mechanism |
|---|---|---|
| Custom DOM traversal, state manipulation | js(expression) |
Runtime.evaluate CDP command |
| Protocol features (cookies, network, security) | cdp(method, params) |
Raw Chrome DevTools Protocol |
| Bespoke data extraction | js() with return values |
Full JavaScript execution context |
| Performance profiling, request interception | cdp('Network.*'), etc. |
Direct CDP domain access |
The js() and cdp() helpers are implemented in src/cdp-eval.ts, wrapping Runtime.evaluate and generic protocol methods. The Browser Runtime (src/browser-runtime.ts) manages CDP transport and session lifecycle, providing the conduit for these low‑level calls.
Direct DOM/CDP Example
// Retrieve all cookies via CDP
const cookies = await cdp('Network.getAllCookies')
cliLog('Cookies:', cookies)
// Custom DOM extraction not covered by snapshotText
const shadowData = await js(String.raw`() => {
const host = document.querySelector('my-component')
return host?.shadowRoot?.innerHTML
}`)
Combining Workflows in Practice
Real automation tasks mix workflows strategically. A typical pattern:
- Observe – semantic snapshot or screenshot
- Act – appropriate workflow helper
- Verify – follow‑up snapshot, screenshot, or DOM query
- Report –
cliLog(...)for observability
Mixed Workflow Example
await gotoAndWait('https://form.example.com')
// Semantic: fill form using accessible refs
await snapshotText()
await fillInput('@12', 'John Doe')
await click('@15') // submit button
// Visual: confirm success state appearance
await captureScreenshot()
This flexibility—semantic for structure, visual for rendering, direct DOM/CDP for edge cases—lets agents adapt to any page architecture without abandoning the ego‑browser harness.
Key Source Files
| File | Responsibility |
|---|---|
src/helpers.ts |
Public API surface (snapshotText, click, js, captureScreenshot, etc.) |
src/element-resolver.ts |
Ref/locator/coordinate → CDP element ID resolution |
src/browser-runtime.ts |
CDP connection, sessions, event buffering |
src/cdp-eval.ts |
js() and cdp() helper implementations |
skills/ego-browser/SKILL.md |
Canonical workflow documentation and usage patterns |
Summary
- Semantic workflow uses
snapshotText()and@Nrefs for compact, DOM‑based automation of standard websites. - Visual workflow uses
captureScreenshot()and[x, y]coordinates for canvas apps and virtualized interfaces where DOM structure is unreliable. - Direct DOM/CDP workflow uses
js()andcdp()for raw JavaScript execution and protocol‑level operations. - Workflows combine freely in single tasks; the runtime in
src/browser-runtime.tsensures consistent CDP session management. - The Element Resolver in
src/element-resolver.tsabstracts away the difference between ref‑based, locator‑based, and coordinate‑based targeting.
Frequently Asked Questions
What happens when a semantic ref like @5 no longer exists?
The helper falls back to the stable loc= selector associated with that element. If both fail, the Element Resolver classifies the error as transient (retry after snapshot refresh) or permanent (element genuinely gone). The ref map rebuilds on every snapshotText() call, so stale refs resolve automatically [SKILL.md#L71‑L84].
Can I use the visual workflow on any page, or only canvas apps?
You can use coordinate‑based actions anywhere, but they are brittle on standard DOM pages where responsive layouts shift element positions. The semantic workflow is preferred for structured pages because refs and locators survive layout changes. Reserve visual mode for sites where DOM traversal is impossible or meaningless.
How does js() differ from running code in snapshotText()?
snapshotText() runs internal scripts to build the accessibility tree and ref map—you don't control this execution. js() is agent‑controlled JavaScript via Runtime.evaluate, able to return arbitrary data, manipulate state, or access browser APIs intentionally excluded from the semantic abstraction. Both route through src/cdp-eval.ts, but js() exposes full execution context to the agent.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →