How to Wait for Specific Browser Events in ego-lite: A Complete Guide
ego-lite exposes a unified event-waiting API through waitForBrowserEvent() that pauses agent execution until a Chrome DevTools Protocol (CDP) event matches your predicate.
Waiting for specific browser events is essential when building reliable browser agents. In the citrolabs/ego-lite repository, the event-waiting system leverages the Chrome DevTools Protocol to synchronize agent actions with browser state. This guide explains how to wait for specific browser events in ego-lite using both low-level primitives and high-level helpers.
How the Event-Waiting Architecture Works
The core mechanism resides in src/browser-runtime.ts, which manages low-level CDP communication and maintains an internal buffer of incoming events.
The Core Components
Three files orchestrate the waiting behavior:
browser-runtime.ts: Handles CDP messaging and exposeswaitForBrowserEvent(). This function creates a promise that resolves when an incoming CDP message satisfies your predicate.driver/waits.ts: Provides ergonomic wrappers likewaitForLoadState()andwaitForSelector()built atop the core API.state.ts: Stores global configuration includingstate.defaultTimeoutand session identifiers.
The Event Resolution Workflow
When you invoke waitForBrowserEvent(), the system executes the following steps (implemented in browser-runtime.ts, lines 69–85):
- Create a waiter: Instantiate a promise containing your predicate function and timeout duration.
- Register the waiter: Push the waiter into the global
eventWaiterslist. - Process incoming messages: As CDP messages arrive via
handleMessage(), each message is tested against every registered predicate. - Resolve on match: When a predicate returns
true, the waiter resolves and is immediately removed from the list.
Events that do not match any predicate are buffered in the events array for potential future matching.
Using the Low-Level waitForBrowserEvent API
For maximum flexibility, import waitForBrowserEvent directly from ego-browser and provide a custom predicate.
Waiting for Generic CDP Events
The following example waits for a specific network request using the Network.requestWillBeSent event:
import { waitForBrowserEvent } from 'ego-browser';
// Resolve when the first request to "example.com/api" is sent
await waitForBrowserEvent(
(event) =>
event.method === 'Network.requestWillBeSent' &&
event.params?.request?.url?.includes('example.com/api'),
5000 // optional timeout in milliseconds
);
This code inspects the event.method string and event.params object to identify the target request. The predicate receives every CDP event circulating through the browser runtime until it returns true or the timeout expires.
High-Level Wait Helpers
While waitForBrowserEvent() provides complete control, the driver/waits.ts module offers specialized functions for common scenarios. These helpers handle domain enablement, predicate construction, and timeout management automatically.
Page Load State Monitoring
Use waitForLoadState() to pause execution until the document reaches a specific lifecycle state. According to the source code in driver/waits.ts (lines 54–69), this function delegates to waitForDocumentLoad() and ultimately to waitForBrowserEvent():
import { waitForLoadState } from 'ego-browser';
// Wait for the 'load' event using the default timeout from state.defaultTimeout
await waitForLoadState('load');
Network Idle Detection
To wait until network activity ceases, use the networkidle option with waitForLoadState(). The underlying waitForNetworkIdle() function (lines 46–81 in driver/waits.ts) enables the Network domain, drains existing events, and resolves only after the specified idle window passes:
import { waitForLoadState } from 'ego-browser';
// Wait until no network requests occur for 500ms, with a 15-second cap
await waitForLoadState('networkidle', { idleMs: 500, timeout: 15000 });
Monitoring Specific Requests and Responses
The waitForRequest() and waitForResponse() functions allow precise interception of network traffic. Both rely on waitForNetworkMatch() (lines 50–65 in driver/waits.ts), which constructs predicates for waitForBrowserEvent():
import { waitForRequest, waitForResponse } from 'ego-browser';
// Wait for any request whose URL ends with ".png"
const request = await waitForRequest(/\.png$/);
// Wait for the corresponding response
const response = await waitForResponse((resp) => resp.url().endsWith('.png'));
Element Visibility Waits
For DOM synchronization, waitForSelector() (lines 85–131 in driver/waits.ts) repeatedly resolves element handles and checks visibility via CDP. It utilizes cdp-eval.ts to execute JavaScript evaluation in the page context:
import { waitForSelector } from 'ego-browser';
const found = await waitForSelector('#submit-button', {
state: 'visible',
timeout: 8000,
});
if (!found) throw new Error('Button never became visible');
This function integrates with element-resolver.ts to handle CSS selectors, XPath expressions, and role-based locators.
Key Implementation Files
Understanding the source structure helps when debugging complex wait scenarios:
| File | Purpose |
|---|---|
src/browser-runtime.ts |
CDP messaging, event buffering, and waitForBrowserEvent() implementation (lines 69–85). |
src/driver/waits.ts |
High-level helpers: waitForLoadState(), waitForNetworkIdle(), waitForRequest(), waitForResponse(), and waitForSelector(). |
src/state.ts |
Global runtime state including state.defaultTimeout and session management. |
src/cdp-eval.ts |
JavaScript execution context for selector-based waits. |
src/element-resolver.ts |
Locator resolution logic for waitForSelector(). |
Summary
- ego-lite provides a unified waiting mechanism through
waitForBrowserEvent()inbrowser-runtime.ts. - The system maintains an
eventWaitersregistry that tests every incoming CDP message against registered predicates. - High-level helpers in
driver/waits.tsabstract common patterns like network idle, page load, and element visibility. - All wait operations support configurable timeouts via
state.defaultTimeoutor explicit parameters. - Core logic resides in
src/browser-runtime.ts(lines 69–85); high-level APIs live insrc/driver/waits.ts.
Frequently Asked Questions
What is the default timeout for wait operations in ego-lite?
When you omit the timeout parameter, functions delegate to state.defaultTimeout defined in src/state.ts. You can modify this global value to change the default behavior across all wait operations in your agent session.
How does ego-lite handle multiple concurrent event waiters?
The handleMessage() function in browser-runtime.ts iterates through the eventWaiters array and evaluates each predicate against incoming CDP events. Multiple waiters can coexist; each resolves independently when its predicate returns true, and the system removes resolved waiters immediately to prevent memory leaks.
Can I wait for custom JavaScript events using waitForBrowserEvent?
Yes. Since waitForBrowserEvent() accepts arbitrary predicates inspecting the event.method and event.params fields, you can construct predicates that listen for Runtime.consoleAPICalled, DOM mutations, or custom CDP events emitted by injected scripts. Combine this with cdp-eval.ts to emit custom events from the page context.
What is the difference between waitForRequest and waitForResponse?
waitForRequest() resolves when the browser initiates an HTTP request (CDP event Network.requestWillBeSent), providing access to request headers and payload. waitForResponse() resolves upon receiving the corresponding Network.responseReceived event, exposing status codes and response headers. Both use waitForNetworkMatch() in driver/waits.ts but filter on different CDP event types.
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 →