How the State Singleton in ego-browser Manages Session and Target Information
The state singleton exported from src/state.ts centralizes all mutable Chrome DevTools Protocol (CDP) session data—including the active session ID, target ID, and timestamps—allowing the entire ego-browser runtime to share a consistent view of the current browser attachment.
The citrolabs/ego-lite repository provides a lightweight browser automation harness that relies on a single shared state object to coordinate CDP sessions. This state singleton in ego-browser eliminates race conditions and duplicate attachments by tracking session lifecycle metadata in one location, ensuring that every module from element resolvers to network interceptors references the same target context.
Core State Fields in the Singleton
The singleton defined in src/state.ts exports a mutable object that acts as the single source of truth for the runtime. According to the source code, it maintains the following fields:
sessionId: The CDP session identifier attached to the currently active tab. It remainsnulluntil a session is explicitly created.sessionTargetId: The target (tab) ID that the current session is attached to. Changing tabs forces a new attachment sequence.sessionAt: Timestamp of the last successful session acquisition. The runtime uses this to enforce a TTL (2 seconds) after which the session is considered stale.sessionInflight: Holds a pending Promise while a new session is being created, allowing concurrent callers to await the same operation without issuing duplicate attach calls.preferredTargetId: Optional override pointing to a specific tab that callers want to attach to. When set,ensureSessionprioritizes this target over the currently active tab.networkDomainEnabled: Tracks the last known state of the Network domain on the active session, used by other parts of the runtime to conditionally enable network interception.
Session Lifecycle and TTL Management
The ensureSession() function in src/browser-runtime.ts orchestrates the session lifecycle by reading from and writing to the state singleton. The process follows a strict sequence to minimize CDP overhead and prevent attachment conflicts.
Requesting a Session with ensureSession()
Any helper that requires a CDP session invokes ensureSession(). This function checks the singleton’s current state to determine whether it can reuse an existing session or must create a new one.
TTL Check and Caching
To avoid excessive re-attachment, the runtime implements a 2-second TTL (time-to-live) cache. If state.sessionId exists and the elapsed time since state.sessionAt is less than the TTL, the cached ID returns immediately (see lines 7‑10 of browser-runtime.ts):
if (state.sessionId && Date.now() - state.sessionAt < SESSION_TTL_MS) {
return state.sessionId;
}
This logic ensures that rapid successive calls within the same 2-second window reuse the active session without querying the browser.
Inflight Deduplication
When a fresh session is required—either because none exists or the TTL has expired—the runtime stores the creation Promise in state.sessionInflight. This prevents concurrent callers from triggering multiple attach operations for the same target (lines 11‑13):
if (state.sessionInflight) return state.sessionInflight;
state.sessionInflight = (async () => { … })();
Once the Promise resolves, the singleton clears sessionInflight and populates sessionId with the new identifier.
Target Selection Strategy
Before attaching, ensureSession queries the browser via listTabs and selects a target in the following priority order:
- The
preferredTargetIdif explicitly set in the singleton. - The currently active tab reported by the browser.
- The last tab in the list as a fallback.
The chosen target’s ID is stored in state.sessionTargetId, and the runtime compares it against any previously attached target to determine if re-attachment is necessary.
Attachment and Event Enablement
If the selected targetId differs from the previously attached one, or if no session exists, the runtime issues Target.attachToTarget and stores the returned sessionId in state.sessionId. Immediately after attachment, it calls enablePageEvents(state.sessionId) to route page-level events such as dialog tracking to the runtime. Finally, it updates state.sessionAt to the current timestamp, resetting the TTL.
Session Invalidation
When the underlying CDP connection reports a lost session (matched by the SESSION_LOST regex), the invalidateSession() function clears all session-related fields in the singleton (lines 46‑53):
export function invalidateSession() {
if (state.sessionId) {
pageEnabledSessions.delete(state.sessionId);
pendingDialogs.delete(state.sessionId);
}
state.sessionId = null;
state.sessionTargetId = null;
state.sessionAt = 0;
}
This forces the next call to ensureSession to repeat the entire discovery and attach flow.
Integration with CDP and Driver Modules
The state singleton integrates transparently with the evaluation layer and driver helpers. The cdp() function in src/cdp-eval.ts forwards calls to browserCdp(), which automatically injects the current session ID when callers omit it (lines 90‑92):
if (!explicit && !BROWSER_LEVEL(method)) {
effective = await ensureSession();
}
This ensures that CDP commands like Runtime.evaluate or DOM.querySelector always execute within the correct session context without requiring manual ID management.
Element resolvers and driver utilities—such as those in src/driver/pointer.ts—receive a { sessionId } tuple from locator functions that ultimately rely on the singleton’s current session. By centralizing state, the runtime allows any helper or skill to work with the correct target transparently.
Practical Code Examples
The following examples demonstrate how to interact with the state singleton to control session behavior.
Force Attachment to a Specific Tab
To override automatic target selection and force the runtime to attach to a specific tab, use setPreferredTarget() before issuing CDP commands:
import { setPreferredTarget } from "./browser-runtime.js";
import { cdp } from "./cdp-eval.js";
async function clickFirstButtonInTab(targetId) {
// Tell the runtime which tab we want to work with
setPreferredTarget(targetId);
// The next CDP call will automatically attach to that tab
const { result } = await cdp("Runtime.evaluate", {
expression: "document.querySelector('button').click()",
});
return result;
}
Inspect Current Session Information
You can read the singleton directly to debug the current attachment state:
import { state } from "./state.js";
function dumpSessionInfo() {
console.log("Current session ID:", state.sessionId);
console.log("Attached target ID :", state.sessionTargetId);
console.log("Acquired at:", new Date(state.sessionAt).toISOString());
}
Reset to Default Target Behavior
To clear a preferred target and return to the default behavior (attaching to the active tab), call clearPreferredTarget():
import { clearPreferredTarget } from "./browser-runtime.js";
clearPreferredTarget(); // subsequent calls will use the active tab again
Summary
- The state singleton in
src/state.tscentralizes all mutable CDP session data, includingsessionId,sessionTargetId, andsessionAt. - A 2-second TTL cache minimizes redundant re-attachments by reusing active sessions stored in the singleton.
- Inflight deduplication via
sessionInflightprevents race conditions when multiple callers request sessions simultaneously. - The preferred target override (
preferredTargetId) allows explicit control over which tab the runtime attaches to. - Automatic invalidation clears the singleton’s fields when the CDP connection is lost, triggering a fresh discovery flow on the next call.
Frequently Asked Questions
How does the state singleton prevent duplicate CDP attachment requests?
The singleton stores a pending Promise in state.sessionInflight while a new session is being created. Concurrent callers check this field and await the same Promise instead of initiating their own attach operations, eliminating duplicate requests to the browser.
What triggers a session invalidation in ego-browser?
When the underlying CDP connection reports a lost session—detected via the SESSION_LOST regex—the invalidateSession() function nullifies state.sessionId, state.sessionTargetId, and state.sessionAt. This forces the next call to ensureSession() to perform a full target discovery and re-attachment.
Can I force the runtime to use a specific browser tab instead of the active one?
Yes. By calling setPreferredTarget(targetId) from src/browser-runtime.ts, you store a target ID in state.preferredTargetId. The next invocation of ensureSession() prioritizes this ID over the currently active tab, attaching the CDP session to your specified target.
What is the purpose of the sessionAt timestamp?
The sessionAt field records the Unix timestamp of the last successful session acquisition. The runtime compares this value against the current time to enforce a 2-second TTL; if the cached session is younger than this threshold, ensureSession() returns the existing sessionId immediately without querying the browser.
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 →