How the CDP Evaluation Module in ego-lite Executes Raw CDP Commands and JavaScript

The CDP evaluation module in ego-lite exposes two main helpers—cdp() for raw Chrome DevTools Protocol commands and js() for in-page JavaScript execution—both backed by a singleton runtime state and session manager that handle CDP message transport and response parsing.

The ego-lite browser-automation framework provides AI agents with low-level browser control through its dedicated evaluation layer. Located in package/ego-browser/src/cdp-eval.ts, this module bridges high-level agent scripts with the Chrome DevTools Protocol (CDP), enabling both protocol-level commands and arbitrary JavaScript execution without requiring manual imports or complex setup.

Core Helper Functions

The module exports two primary functions that are automatically injected into every agent script's scope.

cdp(command, params?) — Raw CDP Command Execution

The cdp helper sends arbitrary CDP commands to the underlying browser instance. It accepts a command name string and an optional parameters object, then forwards these to the runtime's CDP transport layer.

// Enable network tracking
await cdp('Network.enable');

// Navigate to a URL
await cdp('Page.navigate', { url: 'https://example.com' });

// Capture a screenshot
await cdp('Page.captureScreenshot', { format: 'png' });

Under the hood, cdp calls ego.sendCDPMessage, defined in package/ego-browser/src/browser-runtime.ts. This method marshals the request into JSON, transmits it over the CDP socket, awaits the response, and returns the parsed result. Protocol-level errors are converted to CdpError exceptions.

js(expr) — JavaScript Expression Evaluation

The js helper evaluates JavaScript expressions within the current page's execution context. It wraps the supplied expression in an Immediately-Invoked Function Expression (IIFE) to ensure proper return value handling, then invokes Runtime.evaluate via the internal cdp helper.

// Get the page title
const title = await js('document.title');

// Count DOM elements
const count = await js('document.querySelectorAll("a").length');

// Execute complex page-side logic
const data = await js(`
  (function() {
    const rows = document.querySelectorAll('table tr');
    return Array.from(rows).map(r => r.textContent);
  })()
`);

The helper passes these flags to Runtime.evaluate:

  • awaitPromise: true — Waits for Promise resolution
  • returnByValue: true — Returns serializable values directly

Execution errors surface as JsEvaluationError instances with original stack traces preserved.

Runtime Architecture

Both helpers depend on a coordinated infrastructure spanning three core files:

Component File Path Responsibility
State Management package/ego-browser/src/state.ts Maintains singleton runtime state including the active CDP session and pending request map
Session Manager package/ego-browser/src/browser-runtime.ts Handles connection lifecycle, message routing, and ensureSession() re-attachment logic
Helper Injection package/ego-browser/src/helpers.ts Registers cdp and js in helperContext() for automatic script scope availability

Execution Flow

When either helper is invoked, the following sequence occurs:

  1. Session validationensureSession() verifies or re-establishes a live CDP session
  2. Message dispatchego.sendCDPMessage transmits the JSON-RPC formatted command
  3. Response processing — The runtime parses the CDP response, resolves the Promise with the result value, or propagates errors

This design eliminates the need for agents to manage connection state manually.

Practical Code Examples

Enable Console Logging and Capture Events

// Enable Console domain via raw CDP
await cdp('Console.enable');

// Set up event listener for console messages
await cdp('Runtime.enable');
const logs = [];
const { sessionId } = await cdp('Target.attachToTarget', {
  targetId: (await cdp('Target.getTargets')).targetInfos[0].targetId,
  flatten: true
});

Extract Structured Page Data

// Combine navigation and JavaScript evaluation
await cdp('Page.navigate', { url: 'https://news.ycombinator.com' });
await cdp('Page.loadEventFired'); // Wait for load

// Extract story titles and URLs
const stories = await js(`
  Array.from(document.querySelectorAll('.storylink')).map(a => ({
    title: a.textContent,
    url: a.href
  }))
`);
console.log(`Found ${stories.length} stories`);

Error Handling Pattern

try {
  // This will fail if the selector doesn't exist
  const result = await js('document.querySelector("#nonexistent").textContent');
} catch (err) {
  if (err.name === 'JsEvaluationError') {
    console.error('Page evaluation failed:', err.message);
    // err contains the original JavaScript exception details
  }
}

Type Safety and Documentation

The cdp and js helpers are fully implemented in TypeScript with JSDoc annotations. This enables ego-lite's help() introspection feature, allowing agents to query function signatures and usage examples at runtime. The type definitions ensure IDE autocomplete and compile-time validation for common CDP commands.

Summary

  • The CDP evaluation module (cdp-eval.ts) provides cdp() for raw protocol commands and js() for in-page JavaScript execution
  • Both helpers rely on singleton runtime state (state.ts) and session management (browser-runtime.ts) for connection handling
  • Automatic scope injection via helperContext() eliminates import boilerplate in agent scripts
  • Full TypeScript coverage with JSDoc enables runtime help() introspection
  • Structured error types (CdpError, JsEvaluationError) distinguish protocol failures from JavaScript exceptions

Frequently Asked Questions

How does the js() helper handle async JavaScript?

The js() helper automatically sets awaitPromise: true when calling Runtime.evaluate, so any Promise returned by the evaluated expression is awaited before the result is passed back. This allows agents to write await js('fetch("/api").then(r => r.json())') and receive the resolved JSON directly.

What happens if the CDP session disconnects during execution?

The ensureSession() call in browser-runtime.ts automatically re-attaches to the browser target if the previous session expired or was terminated. Agents typically don't need to handle reconnection logic manually, though long-running scripts may observe brief delays during session recovery.

Can I use cdp() to access experimental or domain-specific CDP features?

Yes. The cdp() helper accepts any valid CDP command string, including experimental domains and browser-specific extensions. The module performs no command validation—it forwards the command directly to the browser and returns whatever response the protocol provides, enabling access to new CDP features without framework updates.

Where are the tests for CDP evaluation functionality?

Unit tests reside in package/ego-browser/src/cdp-eval.test.mjs, covering command forwarding, JavaScript evaluation, Promise handling, and error propagation scenarios.

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 →