How the 2-Second Session TTL and Auto-Reattachment Work in ego-browser

The ego-browser runtime maintains a 2-second TTL on Chrome DevTools Protocol sessions to minimize overhead, automatically invalidating stale sessions and re-attaching to targets when errors indicate session loss.

The citrolabs/ego-lite repository implements a lightweight browser automation layer that communicates with Chrome via the DevTools Protocol. To balance performance against reliability, the runtime employs a 2-second session TTL and auto-reattachment strategy that aggressively caches sessions while transparently recovering from target detachment. This design ensures rapid DOM interactions without the fragility of long-lived connections.

The 2-Second Session TTL Mechanism

The session cache duration is defined by the SESSION_TTL_MS constant in package/ego-browser/src/browser-runtime.ts.

// package/ego-browser/src/browser-runtime.ts
const SESSION_TTL_MS = 2000;  // 2-second window

The ensureSession() function enforces this TTL by comparing the current timestamp against state.sessionAt, which records when the session was last attached. If the cached session is younger than 2 seconds, it is reused; otherwise, a fresh attachment is created.

export async function ensureSession() {
  // Re-use cached session only if within TTL
  if (state.sessionId && Date.now() - state.sessionAt < SESSION_TTL_MS) {
    return state.sessionId;
  }
  // ... attach to target and update state.sessionId / state.sessionAt
}

This short-lived caching avoids the latency of repeated Target.attachToTarget calls during rapid-fire operations while preventing stale session accumulation. The mutable state (sessionId, sessionAt) is maintained in package/ego-browser/src/state.ts.

Automatic Re-attachment on Session Loss

When a CDP request fails due to a disappeared session—such as when a tab closes or Chrome resets the target—the browserCdp() function intercepts the error and initiates recovery.

The runtime uses a SESSION_LOST regex to detect failure messages like "Session not found" or "Target closed". Upon detection, it invalidates the stale cache and retries the original request with a fresh session.

// package/ego-browser/src/browser-runtime.ts
export async function browserCdp(method, params = {}, sessionId, timeoutMs) {
  // ...
  try {
    return await rawCdp(method, params, effective, timeoutMs);
  } catch (error) {
    const lost = SESSION_LOST.test(error?.message || "");
    // Auto-retry only for implicit sessions (no explicit sessionId provided)
    if (lost && !explicit && !BROWSER_LEVEL(method)) {
      invalidateSession();                    // clear state.sessionId
      const fresh = await ensureSession();    // attach new target
      return rawCdp(method, params, fresh, timeoutMs); // retry
    }
    throw error;
  }
}

The invalidateSession() helper clears state.sessionId and state.sessionAt, forcing ensureSession() to create a new attachment on the next invocation. This makes transient session failures invisible to calling code.

Practical Usage Examples

Example 1: Normal Helper Usage (Auto-Reuse)

High-level helper methods automatically benefit from the TTL without manual session handling.

// Both calls share the same session if within 2 seconds
await ego.browser.click('@12');
await ego.browser.evaluate(() => document.title);

Example 2: TTL Expiry Triggers Fresh Attachment

Simulating a delay longer than the TTL forces a new attachment on the next operation.

// Wait beyond the 2-second window
await new Promise(r => setTimeout(r, 2500));

// ensureSession() detects expiry and re-attaches
await ego.browser.evaluate(() => document.body.innerHTML);

Example 3: Transparent Recovery from Session Loss

If Chrome terminates the session externally, the runtime retries automatically.

// If the target closes during this call, the runtime catches the error,
// invalidates the stale session, attaches to the new target, and retries.
// The caller never sees the failure.
await ego.browser.evaluate(() => document.title);

Summary

  • 2-second TTL: Sessions are reused only within a 2000ms window defined by SESSION_TTL_MS in browser-runtime.ts, minimizing attachment overhead.
  • State tracking: state.sessionAt and state.sessionId in state.ts track session freshness.
  • Automatic recovery: The browserCdp() function catches session-loss errors via regex matching, calls invalidateSession(), and retries with a fresh attachment.
  • Transparency: Recovery only occurs for implicit sessions (when the caller does not provide an explicit sessionId), ensuring predictable behavior for manual session management.

Frequently Asked Questions

What triggers the 2-second session TTL reset?

The TTL resets whenever ensureSession() creates a new attachment, updating state.sessionAt to the current timestamp. This occurs when the previous session expires after 2000ms or when invalidateSession() clears the cache due to a detected session loss.

How does ego-browser detect a lost session?

The runtime tests error messages against the SESSION_LOST regex in browser-runtime.ts, which matches patterns like "Session not found" or "Target closed". When browserCdp() catches such an error, it triggers the invalidation and re-attachment flow.

Can I disable automatic re-attachment for specific calls?

Yes. The auto-retry logic only executes when the sessionId parameter is omitted (implicit session). If you explicitly pass a sessionId to browserCdp(), the runtime treats the call as browser-level or manually managed and propagates session errors without retrying.

Where is the session state stored?

Mutable session state—including sessionId, sessionAt, and attachment metadata—is stored in package/ego-browser/src/state.ts. The browser-runtime.ts module imports this state to implement the TTL checks in ensureSession() and the cleanup logic in invalidateSession().

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 →