What Components Does the ego‑browser Runtime Own? A Deep Dive into ego‑lite's Core CDP Layer

The ego‑browser runtime owns the Chrome DevTools Protocol (CDP) transport, session management, event buffering, and runtime-wide services that higher-level automation helpers depend on.

The ego‑browser runtime is the foundational layer of the ego‑lite agent-automation stack. It sits below navigation, pointer actions, and site-specific learning systems, providing deterministic low-level browser control through CDP. Understanding what the ego-browser runtime owns helps you extend the codebase or debug protocol-level issues.

CDP Transport and Message Plumbing

At its core, the runtime manages all raw CDP communication. This happens through two key functions in src/browser-runtime.ts:

  • rawCdp() – Sends arbitrary CDP messages via globalThis.ego.sendCDPMessage and returns parsed JSON responses
  • browserCdp() – A wrapped variant that injects the current session ID automatically
import { browserCdp } from "./browser-runtime.js";

// Execute a raw CDP command through the runtime
const version = await browserCdp("Browser.getVersion", {}, sessionId);

The transport layer handles WebSocket-level framing, message correlation via id fields, and error conversion through handleSendError() (lines 24–33). When globalThis.ego.sendCDPMessage fails, the runtime produces standardized EgoError objects with consistent wording.

Session Management and Lifecycle

The ego-browser runtime owns session attachment and caching. The ensureSession() function (lines 107–154):

  1. Checks state.ts for a cached session ID with 2-second TTL
  2. Attaches to the current target via Target.attachToTarget if needed
  3. Returns a valid session for subsequent CDP calls

When sessions detach unexpectedly—whether from target crashes or navigation—the runtime automatically re-attaches. Call invalidateSession() to force a fresh attachment on the next call.

import { ensureSession, invalidateSession } from "./browser-runtime.js";

async function robustOperation() {
  try {
    const session = await ensureSession();
    return await browserCdp("Runtime.evaluate", { expression: "1+1" }, session);
  } catch (err) {
    invalidateSession(); // Force re-attachment for next attempt
    throw err;
  }
}

Session state lives in src/state.ts, which exports a singleton storing the current ID, TTL timestamp, and runtime overrides.

Event Buffering and Subscription System

The runtime maintains a ring buffer of 10,000 CDP events for all messages not explicitly subscribed to. This enables retrospective inspection and race-condition-free event handling.

Component Purpose Location
events array Circular buffer storage Lines 8–11
MAX_BUFFERED_EVENTS 10,000 entry limit Constant definition
drainBrowserEvents() Consume buffered events in chronological order Lines 58–66

For targeted event handling, the runtime provides:

  • subscribeBrowserEvent(method, listener) – Register persistent listeners for specific CDP methods (e.g., Target.detachedFromTarget)
  • waitForBrowserEvent(predicate, timeout) – Promise-based wait that resolves when incoming events match a condition
import { waitForBrowserEvent, subscribeBrowserEvent } from "./browser-runtime.js";

// One-off wait for navigation completion
const loaded = await waitForBrowserEvent(
  (ev) => ev.method === "Page.loadEventFired",
  30_000
);

// Persistent subscription for target detachments
subscribeBrowserEvent("Target.detachedFromTarget", (ev) => {
  console.warn("Target lost:", ev.params.targetId);
});

The eventSubscribers set (lines 88–96) tracks persistent listeners, while eventWaiters holds transient promise resolvers matched in handleMessage() (lines 69–84).

JavaScript Dialog Tracking

Page dialogs (alerts, confirms, prompts) require special tracking because they block execution. The runtime owns pendingDialogs, a Map keyed by targetId that stores:

  • Page.javascriptDialogOpening events when dialogs appear
  • Page.javascriptDialogClosed events when they dismiss

Drivers query this map to detect and handle blocking dialogs without polling CDP repeatedly.

Snapshot Reference Mapping

After accessibility tree snapshots, the runtime converts backend node IDs to a workable RefMap via browserSnapshotRefsToRefMap() (lines 293–326). This utility bridges CDP's Accessibility.getFullAXTree responses with the higher-level element resolver in src/element-resolver.ts.

What the Runtime Explicitly Does NOT Own

The ego-browser runtime is deliberately thin. Per the source architecture, these responsibilities live elsewhere:

Responsibility Owner Module
Navigation and history src/driver/nav.ts
Pointer actions (clicks, drags) src/driver/pointer.ts
Keyboard input src/driver/keyboard.ts
Element resolution from selectors src/element-resolver.ts
Site-specific learning data Learning subsystem (separate package)
Public helper API surface src/helpers.ts and src/help-runtime.ts

These modules import from the runtime but the runtime has no reverse dependencies—maintaining clean layer separation.

Key Files and Their Roles

  • src/browser-runtime.ts – Core runtime implementation with all components above
  • src/state.ts – Singleton state storage consumed by the runtime
  • src/cdp-eval.ts – Thin cdp() and js() wrappers built on runtime primitives
  • src/driver/*.ts – High-level automation building on the runtime API
  • src/helpers.ts – Public-facing helpers that agents import; forwards to runtime internally

Summary

  • The ego-browser runtime owns CDP transport, session lifecycle, and event infrastructure in ego-lite
  • Key functions: browserCdp(), ensureSession(), waitForBrowserEvent(), subscribeBrowserEvent(), browserSnapshotRefsToRefMap()
  • Core file: src/browser-runtime.ts with supporting state in src/state.ts
  • 10,000-entry ring buffer enables retrospective event inspection
  • Explicitly excludes navigation, pointer actions, and site logic—those live in src/driver/*

Frequently Asked Questions

How does the runtime handle session expiration?

The runtime caches session IDs with a 2-second TTL in state.ts. ensureSession() checks this cache before attaching; invalidateSession() clears it to force re-attachment. Automatic re-attachment also triggers on detected session loss.

Can I use the runtime directly without the driver helpers?

Yes. Import from browser-runtime.ts for full control over CDP commands, event waiting, and session management. The driver helpers in src/driver/* are optional conveniences built atop these primitives.

What's the difference between subscribeBrowserEvent and waitForBrowserEvent?

subscribeBrowserEvent() registers persistent listeners for specific CDP methods that fire repeatedly. waitForBrowserEvent() creates one-time promise-based waits that resolve when a predicate matches any incoming event, then auto-cleanup.

How large is the event buffer and can it be configured?

The buffer holds 10,000 events via MAX_BUFFERED_EVENTS (line 9). This is currently a compile-time constant; adjust it in src/browser-runtime.ts and rebuild if your use case demands different retention.

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 →