Best Practices for Using ego-lite: Reliable Browser Automation for AI Agents

The best practices for using ego-lite center on isolating work in dedicated task spaces, preferring stable loc= selectors over ephemeral @N references, and always verifying state changes with snapshotText() or captureScreenshot() before proceeding.

ego-lite is a Chromium-based browser built from the ground up to let AI agents work side-by-side with human users. Its architecture revolves around isolated task spaces, a thin Node.js runtime (ego-browser) that injects a rich set of helpers, and a highly-optimized snapshot system. Understanding these core concepts is essential for writing reliable, token-efficient automation scripts that handle complex web interactions without brittle selectors or failed handoffs.

Core Architecture Components

Understanding the five core components of ego-lite is the foundation for applying best practices effectively.

Task Spaces

Task spaces are isolated browsing contexts that inherit the user’s login state. Agents own a space, the user can take control, and spaces can be handed off or closed. According to the canonical documentation in skills/ego-browser/SKILL.md, proper task-space management prevents duplicate contexts and ensures persistence across heredoc script rounds.

Helper Context

The HelperContext singleton registers all public helpers (click, snapshotText, js, etc.) and injects them into the heredoc script at runtime. This registration logic and ownership enforcement live in src/helpers.ts, which serves as the primary interface between your automation code and the browser runtime.

Snapshot Engine

The snapshot engine produces a semantic tree via snapshotText() with stable @N refs and loc= selectors, enabling fast, token-efficient observations. The implementation in src/browser-runtime.ts builds this accessibility tree and manages the mapping between semantic elements and their CDP-based selectors.

Driver Layer

Low-level browser interaction is handled by the driver layer, including CDP transport in src/cdp-eval.ts (which implements js() and cdp() wrappers), along with navigation, pointer, keyboard, and file-upload drivers under src/driver/.

Learning Subsystem

Site-specific "learnings" enrich the skill set with custom tools for complex applications like Notion or Google Docs. This subsystem is managed by src/learning/index.ts, which injects domain-specific helpers when recognized sites are detected.

Seven Key Design Principles

Following these principles ensures your ego-lite automations remain stable, efficient, and maintainable.

1. Isolation First

Always start a script with useOrCreateTaskSpace(name) (or takeOverTaskSpace after a hand-off). This guarantees that the agent re-uses the same tabs across heredoc rounds and prevents accidental creation of duplicate spaces that waste resources and fragment state.

2. Stable Locators

Prefer loc= selectors (e.g., loc=css:button.primary) or robust CSS/XPath strings over raw @N refs. Because @N references are refreshed with each snapshotText() call, they are ephemeral and prone to invalidation during DOM updates, whereas loc= selectors derive from the accessibility tree and remain valid across snapshots.

3. Workflow Selection

Choose the most appropriate interaction pattern for your target:

  • SemanticsnapshotText() plus refs/locators (default for normal pages with standard DOM).
  • VisualcaptureScreenshot() plus coordinate actions (for canvas-heavy editors where DOM elements are hidden).
  • Direct CDPjs() or cdp() for low-level operations requiring custom Chrome DevTools Protocol commands.

4. Control Handoff

When a step requires user interaction (e.g., CAPTCHA), call handOffTaskSpace() and wait for the user’s confirmation before proceeding. Never retry a failed operation that requires human input; instead, surface a clear Ask and block with waitForAgentControl().

5. Explicit Cleanup

End every successful automation with completeTaskSpace(id, { keep: false }). Use { keep: true } only when the user explicitly wants the page to remain open for further manual inspection.

6. Verification Loop

After any navigation, click, or input, re-run snapshotText(), pageInfo(), or captureScreenshot() before assuming success. This guards against transient failures, delayed DOM re-renders, and race conditions in single-page applications.

7. Minimal Token Use

Because the agent sends code as plain text, keep each heredoc focused: observe → act → verify → cliLog the result. Large monolithic scripts waste tokens, increase latency, and are harder to debug when failures occur.

Implementation Patterns and Code Examples

These concrete patterns demonstrate the best practices in action.

Creating and Reusing Task Spaces

Always establish your execution context before performing any browser actions.

// Start (or resume) a dedicated space for the whole task
const task = await useOrCreateTaskSpace('search github issues');

// Open a new tab or reuse an existing one
await openOrReuseTab('https://github.com/citrolabs/ego-lite/issues', {
  wait: true,
  timeout: 20,
});

Why this works: The space persists across subsequent heredocs, allowing the agent to keep the same tabs and avoid re-logins.

Semantic Workflow with Stable Selectors

Use loc= selectors for reliable element interaction on standard web pages.

// Grab a full-page snapshot (default scope is fine)
const snap = await snapshotText();

// Use a stable selector from the snapshot output
await click('loc=css:a[href*="issue"]', { label: 'open first issue' });

// Verify navigation succeeded
cliLog(await pageInfo());        // prints { url, title, … }

Why this works: loc= selectors survive across snapshots; they are derived from the accessibility tree in src/browser-runtime.ts and are less brittle than raw @N refs.

Visual Workflow for Rich Editors

When the DOM is opaque or canvas-based, switch to coordinate-based interaction.

// Capture a screenshot, then click by coordinates
await captureScreenshot();      // optional visual debug
await click({ x: 420, y: 260 }, { label: 'place cursor in canvas' });
await typeText('Hello world!');

// Confirm the change with another screenshot
cliLog('Result screenshot saved');
await captureScreenshot();

Why this works: Rich editors often hide DOM elements; coordinate actions coupled with visual verification are more reliable than attempting to target non-existent accessibility nodes.

Direct CDP for Custom Logic

Execute complex page-side logic in a single round-trip to minimize overhead.

// Run a single-shot script inside the page
const titles = await js(String.raw`(() => {
  return [...document.querySelectorAll('h1')].map(el => el.innerText);
})()`);

cliLog('Page titles: ' + JSON.stringify(titles));

Why this works: Keeps all browser-side logic inside one js() call via the wrapper in src/cdp-eval.ts, avoiding the overhead of multiple evaluations and reducing token consumption.

Handling Human-in-the-Loop Interactions

Properly manage ownership when users must intervene.

// Ask the user to solve a captcha
await handOffTaskSpace(task.id);
await waitForAgentControl(task.id);   // block until user resumes

// After user confirms, take back control and continue
await takeOverTaskSpace(task.id);
await click('button.submit', { label: 'submit after captcha' });

Why this works: Respects the ownership model defined in the task-space lifecycle and prevents the agent from hard-failing when the user is in control.

Resource Cleanup

Always terminate sessions cleanly to prevent resource leaks.

// Close everything once the job is done
await completeTaskSpace(task.id, { keep: false });
cliLog('Task completed and space closed.');

Why this works: Guarantees no stray tabs or spaces linger, keeping the browser tidy for future tasks and preventing the accumulation of orphaned CDP sessions.

Essential Source Files

These files embody the design decisions that make ego-lite a fast, token-efficient, and agent-friendly platform:

File Purpose
src/helpers.ts Registers all public helpers (click, snapshotText, js, …) and enforces ownership checks.
src/browser-runtime.ts Core snapshot and CDP transport layer; builds the semantic tree used by snapshotText.
src/cdp-eval.ts Implements js() and cdp() wrappers for evaluating code inside the page.
src/driver/* Low-level drivers for navigation, pointer, keyboard, file upload, and event handling.
skills/ego-browser/SKILL.md Canonical user-facing documentation; defines the public helper surface and workflow recommendations.

Summary

  • Isolate every task in a dedicated space using useOrCreateTaskSpace() to maintain persistence across script rounds.
  • Prefer loc= selectors over @N references for stability across accessibility tree snapshots.
  • Verify every action with snapshotText() or captureScreenshot() to guard against DOM changes and navigation failures.
  • Hand off control to the user via handOffTaskSpace() for interactive steps like CAPTCHAs, and never retry failed human-required steps.
  • Always clean up completed tasks with completeTaskSpace(id, { keep: false }) to prevent resource leaks and session fragmentation.
  • Keep heredocs minimal: observe, act, verify, then log results to optimize token usage and debuggability.

Frequently Asked Questions

What is a task space in ego-lite?

A task space is an isolated browsing context that inherits the user’s login state and is owned by either the agent or the human user. According to skills/ego-browser/SKILL.md, task spaces allow agents to persist tabs and cookies across multiple heredoc script executions while preventing interference with the user’s main browsing session or other concurrent automations.

How do loc= selectors differ from @N references?

loc= selectors (e.g., loc=css:button.primary) are stable CSS or XPath-based locators derived from the accessibility tree that remain valid across multiple snapshotText() calls. In contrast, @N references (like @1, @2) are ephemeral numeric IDs assigned to elements during a single snapshot capture, as implemented in src/browser-runtime.ts, and become invalid when the DOM updates or a new snapshot is taken.

When should I use the visual workflow instead of the semantic workflow?

Use the semantic workflow (snapshotText() with locators) for standard web pages with accessible DOM elements. Switch to the visual workflow (captureScreenshot() with coordinate-based clicking) when automating rich text editors, canvas-based applications, or other interfaces where the DOM is hidden or non-semantic, making traditional selectors unreliable.

How do I properly handle user interactions like CAPTCHAs?

When encountering a CAPTCHA or any step requiring human judgment, call handOffTaskSpace(task.id) to transfer ownership to the user, then block execution with waitForAgentControl(task.id). Once the user completes the interaction and control returns, resume automation with takeOverTaskSpace(task.id). Never implement retry loops for human-required steps; instead, surface clear asks and wait for explicit confirmation.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →