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 resolutionreturnByValue: 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:
- Session validation —
ensureSession()verifies or re-establishes a live CDP session - Message dispatch —
ego.sendCDPMessagetransmits the JSON-RPC formatted command - 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) providescdp()for raw protocol commands andjs()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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →