# ego-browser Harness Architecture: Modular CDP-Based Agent Runtime

> Explore the ego-browser harness architecture, a modular CDP-based agent runtime. Discover how it uses a Node.js layered approach and JavaScript SDK to expose Chrome DevTools Protocol capabilities for AI agents.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: architecture
- Published: 2026-07-31

---

**The ego-browser harness implements a layered Node.js architecture that exposes Chrome DevTools Protocol (CDP) capabilities to AI agents through a JavaScript SDK, separating transport, execution, element resolution, and site-specific learning into distinct modules.**

The `citrolabs/ego-lite` repository provides a lightweight, extensible runtime that enables AI agents to programmatically control the closed-source ego lite browser. This architecture leverages CDP bindings to translate high-level agent scripts into low-level browser operations while maintaining strict separation between concerns. Understanding the ego-browser harness architecture reveals how the system balances flexibility for agent developers with robust state management and error handling.

## Entry Points and SDK Installation

The harness exposes two primary entry mechanisms in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts). The **`runMain()`** function serves as the CLI entry point, reading JavaScript source from STDIN and executing it within the harness context. When the package is imported as a module, **`installEgoSdk()`** automatically installs the SDK onto `globalThis`, making helper functions available without explicit imports.

This dual-entry design supports both standalone script execution and embedded runtime integration, ensuring agents can run code regardless of how the harness is initialized.

## Helper Context and Public API

The [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) module generates a single source of truth for all public helpers available to agent scripts. This **helper context** includes functions for CDP communication (`cdp`), JavaScript evaluation (`js`), navigation, file upload, and task-space management.

These helpers are injected into the execution context of user scripts, providing a sandboxed but powerful API surface that abstracts the complexity of raw CDP commands.

## Runtime Execution Layer

The [`src/run.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/run.ts) module wraps user scripts in an async function closure, injects the helper context, and manages the execution lifecycle. It handles output flushing, error boundaries, and graceful termination.

By wrapping scripts in an async IIFE (Immediately Invoked Function Expression), the runtime ensures that top-level await expressions work correctly while maintaining isolation between the agent code and harness internals.

## CDP Transport and Session Management

At the core of the architecture lies [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts), which manages low-level CDP messaging. This module provides:

- **`cdp()`** function for raw CDP command dispatch
- Automatic session attachment and lifecycle management
- A buffered event queue with a 10,000 message cap to prevent memory exhaustion
- Dialog tracking and handling

The transport layer handles connection multiplexing and ensures that CDP events are queued during temporary disconnections, providing reliable communication even during page navigations.

## Expression Evaluation Engine

The [`src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/cdp-eval.ts) module offers two primary evaluation mechanisms. The **`cdp()`** function allows direct CDP method invocation, while **`js()`** evaluates JavaScript expressions within the page context, automatically wrapping top-level return statements in an IIFE when necessary.

This dual approach lets agents choose between raw protocol access for advanced scenarios and convenient expression evaluation for simple DOM queries or function calls.

## Element Resolution and Locator Strategies

The [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) module implements a sophisticated selector resolution system that accepts multiple locator formats:

- **`@N`** references (numeric handles from previous snapshots)
- **`loc=css:`** for CSS selectors
- **`loc=role:`** for ARIA role queries
- **`loc=href:`** for link URL patterns
- XPath expressions
- Raw CSS selectors

The resolver classifies failures as either *transient* (retryable, such as elements not yet loaded) or *permanent* (invalid selectors), enabling intelligent retry logic in the higher-level API.

## Reference Management System

To bridge the gap between agent-friendly numeric references and CDP backend node IDs, the harness implements a reference mapping system across [`src/ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-map.ts) and [`src/ref-state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/ref-state.ts). This system maintains a bidirectional map where **`@21`** style references correspond to specific backend node IDs.

The reference map rebuilds automatically on each DOM snapshot. If an agent attempts to use a reference while the map is empty, the system triggers an automatic re-snapshot to refresh the element mappings, preventing stale reference errors.

## Task-Space Isolation Architecture

The harness implements a sophisticated task-space system for isolating browsing contexts with distinct ownership models (**`agent`** vs **`user`**). Key functions exposed through the helper context include:

- **`newTaskSpace()`** – Creates isolated browsing contexts
- **`switchTaskSpace()`** – Changes active context
- **`useOrCreateTaskSpace()`** – Conditional context retrieval
- **`claimTaskSpace()`** – Ownership assertion
- **`completeTaskSpace()`** – Cleanup with optional persistence

This architecture prevents cross-contamination between different agent tasks while allowing persistent sessions when needed.

## Driver Modules for Browser Actions

Low-level browser interactions are segmented into focused driver modules under `src/driver/`, each handling specific interaction domains:

- **[`driver/nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/nav.ts)** – Tab management and URL navigation
- **[`driver/pointer.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/pointer.ts)** – Click, scroll, and drag operations
- **[`driver/keyboard.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/keyboard.ts)** – Key event dispatch and input sequences
- **[`driver/files.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/files.ts)** – `setInputFiles` for file upload handling
- **[`driver/screencast.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/screencast.ts)** – Video capture and encoding
- **[`driver/observe.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/observe.ts)** – DOM snapshots and screenshots
- **[`driver/element-ops.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/element-ops.ts)** – Backend object ID management

All drivers consume the shared CDP runtime from [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts), ensuring consistent session state across different interaction types.

## Site-Specific Learning Subsystem

The [`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts) module implements a plugin architecture for per-site automation scripts called **learnings**. Each learning pack contains a [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) defining available tools and optional JavaScript implementations.

The subsystem exposes **`runSiteTool()`** and **`runSiteBrowserTool()`** helpers, allowing agents to invoke site-specific automation logic (such as specialized login flows or search interfaces) without hardcoding site logic into the agent script. The learning loader validates pack formats and injects site-specific helpers into the execution context.

## State Management and Environment

Global mutable runtime state is centralized in [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts), while [`src/env.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/env.ts) resolves workspace locations and environment-specific configurations. This separation ensures that ephemeral execution data (current page, active task space) remains distinct from environment configuration (paths, feature flags).

The state module tracks active sessions, pending operations, and runtime flags that coordinate between the transport layer and high-level helpers.

## Runtime Documentation System

Uniquely, the harness parses its own JSDoc comments at runtime through [`src/help-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/help-runtime.ts) to implement the **`help()`** helper. This introspective approach ensures that documentation always matches the actual implementation, as the help text is derived directly from source code comments rather than external documentation files.

## Output Buffering and Notices

The [`src/output-sink.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/output-sink.ts) module manages console output buffering, notice trailers, and graceful termination sequences. It ensures that all agent output is captured and flushed appropriately, with special handling for notices that need to be appended to script results.

## Practical Usage Examples

The following patterns demonstrate typical agent interactions with the harness architecture:

```javascript
// Navigation and element interaction using references
await nav("https://example.com");
await click("@5");               // Uses ref from previous snapshot
await waitFor("[role=button]");  // Falls back to CSS selector resolution

```

```javascript
// Invoking site-specific learning tools
const results = await runSiteTool("github.com", "searchIssues", {
  query: "bug",
  repo: "openai/gpt-4"
});
console.log(results);

```

```javascript
// Task-space lifecycle management
const ts = await newTaskSpace("my-session");
await switchTaskSpace(ts.id);
await navigate("https://news.ycombinator.com");
await completeTaskSpace(ts.id, { keep: true });

```

```javascript
// Screencast capture with driver integration
await startScreencast({ path: "/tmp/cast.webm", fps: 30 });
await nav("https://example.org");
await stopScreencast();

```

## Summary

The ego-browser harness architecture demonstrates a clean separation of concerns optimized for AI agent automation:

- **Transport Layer** ([`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts)) handles CDP session management and event buffering
- **Execution Layer** ([`run.ts`](https://github.com/citrolabs/ego-lite/blob/main/run.ts), [`index.ts`](https://github.com/citrolabs/ego-lite/blob/main/index.ts)) wraps scripts and injects the SDK context
- **Resolution Layer** ([`element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/element-resolver.ts), [`ref-map.ts`](https://github.com/citrolabs/ego-lite/blob/main/ref-map.ts)) translates high-level selectors to backend node IDs
- **Action Layer** (`driver/` modules) implements specific browser interactions
- **Extension Layer** ([`learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/learning/index.ts)) enables site-specific automation packs
- **Isolation Layer** (task-space helpers) maintains context boundaries between operations

This modular design allows developers to extend functionality by adding new drivers or learnings while maintaining a stable public API for agent scripts.

## Frequently Asked Questions

### What role does the Chrome DevTools Protocol play in the ego-browser harness?

The Chrome DevTools Protocol (CDP) serves as the underlying transport mechanism in [`src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/browser-runtime.ts), enabling the harness to send commands to and receive events from the browser. The architecture abstracts CDP complexity through the `cdp()` and `js()` helpers in [`src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/cdp-eval.ts), allowing agents to interact with the browser using either raw protocol methods or high-level JavaScript evaluation without managing CDP session details manually.

### How does the element resolver handle different selector types?

The [`src/element-resolver.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/element-resolver.ts) module normalizes multiple locator strategies—including `@N` numeric references, `loc=css:`, `loc=role:`, `loc=href:` prefixes, XPath, and raw CSS selectors—into a unified resolution pipeline. It categorizes resolution failures as transient (triggering automatic retry) or permanent (immediate error propagation), ensuring robust element location even in dynamic web applications.

### What is a task-space in the ego-browser architecture?

A task-space is an isolated browsing context managed through functions like `newTaskSpace()` and `switchTaskSpace()` in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts). Task-spaces maintain separate cookies, local storage, and session state, with explicit ownership models distinguishing between `agent` and `user` contexts. This isolation prevents state leakage between different automation tasks while supporting persistent sessions across multiple script executions.

### How do site-specific learnings extend the harness functionality?

The learning subsystem in [`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts) loads per-site "skill packs" containing [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) definitions and tool implementations. These packs expose site-specific helpers through `runSiteTool()` and `runSiteBrowserTool()`, allowing the harness to encapsulate complex site interactions—such as authentication flows or specialized search interfaces—without requiring agents to hardcode site-specific logic.