How Page Events Are Buffered and Drained in the Ego Lite Runtime
The Ego‑Lite runtime buffers CDP events in a capped in‑memory array and drains them via drainBrowserEvents(), with subscribers receiving real‑time callbacks and waitForBrowserEvent() enabling predicate‑based resolution.
The Ego‑Lite browser automation framework provides a robust event‑handling system for Chrome DevTools Protocol (CDP) messages. Understanding how page events are buffered and drained in the runtime is essential for building reliable agents that capture browser activity without exhausting memory. This article examines the implementation in browser-runtime.ts, including buffer management, subscriber dispatch, and retrieval patterns.
Event Buffer Architecture
The runtime maintains a single in‑memory queue for all incoming page events. This design prioritizes low latency over persistence, making it ideal for ephemeral automation tasks.
Buffer Storage and Limits
The core buffer is defined in browser-runtime.ts:
events(line 20): An array that stores every ingested CDP eventMAX_BUFFERED_EVENTS(line 8): A constant fixed at 10 000 entries
When the buffer reaches capacity, the oldest events are discarded to maintain this bound. The enforcement logic appears at lines 88‑90, ensuring memory usage remains predictable during long‑running sessions.
Event Ingestion Pipeline
The handleMessage callback (lines 31‑87) processes every raw CDP message from the browser:
- Parses the message structure
- Checks against screencast frame duplicates (already delivered to subscribers are skipped)
- Invokes matching subscribers from
eventSubscribers(lines 77‑85) - Appends the event to
events(line 87)
This ordering—notify first, buffer second—ensures subscribers receive events immediately while the buffer captures history for later retrieval.
Subscriber Matching Logic
Subscribers are stored in eventSubscribers and matched by:
method: The CDP method name (e.g.,"Page.loadEventFired")sessionId(optional): For multi‑session browser connections
When both criteria match, the listener fires synchronously before buffering continues.
Draining the Event Buffer
The runtime exports two primary mechanisms for consuming buffered events:
drainBrowserEvents()
Located at lines 64‑66, this function empties the entire buffer in a single operation:
import { drainBrowserEvents } from "./browser-runtime.js";
// Retrieve and clear all buffered events
const allEvents = drainBrowserEvents();
console.log(`Recovered ${allEvents.length} events from buffer`);
The implementation uses events.splice(0, events.length) for O(n) extraction and immediate queue reset. This pattern suits end‑of‑task cleanup or batch analysis scenarios.
waitForBrowserEvent(predicate, timeoutMs)
For targeted event retrieval, lines 70‑86 expose a promise‑based waiter:
import { waitForBrowserEvent } from "./browser-runtime.js";
// Pause execution until a specific network request completes
const targetEvent = await waitForBrowserEvent(
(e) => e.method === "Network.responseReceived"
&& e.params?.response?.url?.includes("/api/data"),
5000 // 5 second timeout
);
Waiters register in eventWaiters (line 21) and evaluate against every incoming event (lines 92‑106). The first matching event resolves the promise and removes the waiter.
Practical Usage Patterns
Real‑Time Subscription
Capture live events without buffering overhead for the consumer:
import { subscribeBrowserEvent } from "./browser-runtime.js";
const unsubscribe = subscribeBrowserEvent(
"Page.navigatedWithinDocument",
undefined,
(event) => console.log("Fragment navigation:", event.params.url)
);
// Later: unsubscribe() to stop receiving callbacks
Batch Event Recovery
Combine draining with processing for post‑action analysis:
// After completing a workflow...
const buffered = drainBrowserEvents();
const networkErrors = buffered.filter(
e => e.method === "Network.loadingFailed"
);
console.error(`Detected ${networkErrors.length} failed requests`);
Key Implementation Files
| File | Role |
|---|---|
browser-runtime.ts |
Core buffer (events), MAX_BUFFERED_EVENTS enforcement, drainBrowserEvents(), handleMessage() dispatch loop, and waitForBrowserEvent() implementation (lines 8, 20‑21, 31‑106) |
state.ts |
Runtime‑wide configuration including default timeouts for waiter operations |
driver/waits.ts |
Consumer patterns using drainBrowserEvents() for network idle detection |
Summary
- The Ego‑Lite runtime buffers CDP events in a 10 000‑entry capped array to prevent unbounded memory growth
- Subscribers receive synchronous callbacks before events enter the buffer, eliminating latency for real‑time consumers
drainBrowserEvents()provides bulk retrieval with complete buffer clearance viasplice()waitForBrowserEvent()enables precise, predicate‑driven event resolution with configurable timeouts- The screencast frame deduplication logic prevents duplicate deliveries for frame‑heavy operations
Frequently Asked Questions
What happens when the event buffer exceeds 10 000 entries?
The oldest events are automatically removed. The runtime enforces MAX_BUFFERED_EVENTS at lines 88‑90 of browser-runtime.ts using array truncation, preserving only the most recent entries.
Can multiple subscribers receive the same event?
Yes. The eventSubscribers iteration at lines 77‑85 invokes every matching subscriber before buffering. There is no early‑return; all registered listeners for a given method and sessionId receive the event.
How does waitForBrowserEvent differ from subscribeBrowserEvent?
subscribeBrowserEvent registers a persistent callback for all future matching events, while waitForBrowserEvent creates a one‑time promise that resolves on the first predicate match. Waiters are destroyed after resolution; subscribers persist until explicitly unsubscribed.
Is the event buffer persisted to disk?
No. The events array exists only in memory. For durability, agents must call drainBrowserEvents() and store results externally before process termination.
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 →