How Ego-Lite Handles Buffered CDP Events: Browser Runtime Architecture

Ego-Lite buffers Chrome DevTools Protocol events in a capped in-memory queue that automatically discards the oldest entries when exceeding 10,000 events, while supporting selective direct delivery to registered subscribers to optimize memory usage.

The citrolabs/ego-lite repository implements a specialized CDP client within its browser runtime to manage high-frequency Chrome DevTools Protocol traffic. Understanding how ego-lite handles buffered CDP events is essential for developers building automation agents that interact with headless Chrome instances, as the buffering strategy directly impacts memory consumption and event retrieval patterns.

The In-Memory Event Queue Structure

The events Array and MAX_BUFFERED_EVENTS Constant

At the core of ego-lite's buffering mechanism is a simple in-memory array named events, defined in package/ego-browser/src/browser-runtime.ts. To prevent unbounded memory growth during long-running browser sessions, the runtime enforces a hard cap of 10,000 events through the MAX_BUFFERED_EVENTS constant declared at lines 6-9.

When incoming CDP traffic pushes the queue beyond this limit, the runtime immediately truncates the oldest surplus entries using Array.prototype.splice at lines 87-90. This ensures the buffer maintains a predictable memory footprint regardless of session duration or event frequency.

How CDP Events Enter the Buffer

The handleMessage Function Logic

Every raw CDP message flows through the handleMessage function (lines 86-92), which discriminates between request responses and autonomous events. Only messages without an id property—indicating they are notifications rather than command responses—are pushed onto the events array.

The implementation applies the buffer cap check during this insertion phase. If the queue exceeds 10,000 entries after the push operation, the code calculates the overflow count and removes the oldest events from the front of the array, maintaining the most recent telemetry for analysis.

Screencast Frame Handling

The buffering logic contains special provisions for screencast frames. Even when a subscriber receives direct delivery of a Page.screencastFrame event, the runtime still adds that frame to the general buffer. This ensures complete session recording while avoiding double-counting for other event types that support selective delivery.

Consuming Buffered Events

Draining the Queue with drainBrowserEvents()

Consumers retrieve accumulated telemetry through the drainBrowserEvents() function implemented at lines 64-67. This method returns a shallow copy of the current events array and atomically clears the internal queue in a single operation.

The shallow copy semantics mean that while the array container is new, the event objects themselves are references to the originals. This design optimizes memory usage during event hand-off while ensuring the internal buffer is immediately ready to collect new CDP traffic without duplication.

Selective Direct Delivery via subscribeBrowserEvent()

Ego-lite provides an alternative consumption path through subscribeBrowserEvent, which bypasses the general buffer for specific event types. When a listener registers for a particular CDP method (except screencast frames), the runtime delivers that event directly to the callback and excludes it from the events array.

This selective delivery mechanism, active around lines 84-88, prevents noise in the general buffer for high-priority events while still maintaining the 10,000-event history for unfiltered consumption.

Practical Implementation Examples

The following patterns demonstrate proper interaction with the buffering system using the ego-browser runtime APIs:

// Retrieve and clear all buffered CDP events
import { drainBrowserEvents } from "ego-browser/src/browser-runtime.js";

async function analyzeSessionTelemetry() {
  const events = drainBrowserEvents();        // atomically empties internal queue
  console.log(`Collected ${events.length} CDP events from buffer`);
  console.dir(events, { depth: 2 });
}
await analyzeSessionTelemetry();
// Subscribe to specific events while maintaining buffer integrity
import { subscribeBrowserEvent, waitForBrowserEvent } from "ego-browser/src/browser-runtime.js";

// Direct delivery bypasses the buffer for this event type
const unsubscribe = subscribeBrowserEvent(
  "Network.requestWillBeSent",
  undefined,
  (event) => {
    console.log("Intercepted request:", event.params?.request?.url);
  },
);

// Wait for a specific event condition with timeout
await waitForBrowserEvent(
  (e) => e.method === "Page.domContentEventFired",
  5000,
);

unsubscribe(); // Clean up direct subscription

Summary

  • Ego-lite maintains a bounded FIFO queue (events array) with a hard limit of 10,000 CDP entries defined by MAX_BUFFERED_EVENTS in browser-runtime.ts.
  • Automatic eviction removes the oldest events via splice when the buffer overflows, protecting against memory exhaustion during extended sessions.
  • drainBrowserEvents() provides atomic retrieval and clearance, returning a shallow copy of accumulated events suitable for batched processing.
  • Selective direct delivery via subscribeBrowserEvent allows high-priority events to bypass the buffer entirely (except screencast frames), optimizing memory usage for specific automation workflows.

Frequently Asked Questions

What is the maximum number of CDP events ego-lite can buffer?

The runtime enforces a hard cap of 10,000 events through the MAX_BUFFERED_EVENTS constant in package/ego-browser/src/browser-runtime.ts. When this limit is exceeded, the oldest surplus events are immediately discarded to maintain bounded memory usage.

How does ego-lite prevent memory exhaustion when handling high-frequency CDP events?

The buffering system prevents memory exhaustion through two mechanisms: automatic queue truncation using Array.prototype.splice when the 10,000-event threshold is crossed, and selective direct delivery that routes high-priority events directly to subscribers rather than storing them in the general buffer.

Can I retrieve buffered events without clearing the queue?

No, the current implementation in browser-runtime.ts only provides drainBrowserEvents(), which atomically returns and clears the buffer. There is no public API for peeking at events without removal; developers must implement their own caching layer if they require non-destructive inspection.

Do all CDP events go through the buffer before reaching subscribers?

No, events delivered via subscribeBrowserEvent bypass the general buffer and route directly to registered listeners, with the exception of screencast frames which are always stored. This selective delivery ensures that frequently triggered handlers do not pollute the 10,000-event history intended for general telemetry analysis.

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 →