How Ego-Lite Handles CDP Session Invalidation When Tabs and Task Spaces Change

Ego-Lite automatically invalidates Chrome DevTools Protocol (CDP) sessions when a browser tab closes, when a CDP command fails with a session-lost error, or when switching between task spaces, ensuring the automation runtime always targets a live browser context.

The citrolabs/ego-lite library manages browser automation through persistent CDP sessions attached to specific tabs. When the underlying target changes or becomes unavailable, the runtime must cleanly discard stale session state to prevent commands from targeting defunct browser contexts. Understanding the session invalidation logic is critical for building resilient automation scripts that recover gracefully from tab closures and task-space transitions.

Three Conditions That Trigger Session Invalidation

Target Destruction or Detachment

In browser-runtime.ts, the handleMessage function listens for CDP events indicating the current target is no longer valid. Specifically, events of type Target.detachedFromTarget or Target.targetDestroyed signal that the tab has closed or disconnected. The handler inspects data.params?.targetId (or targetInfo.targetId) and compares it against state.sessionTargetId. If the IDs match, the runtime immediately calls invalidateSession() to clear the association (lines 52-66).

CDP Request Failures

When executing commands through the browserCdp helper, the runtime wraps requests in a try-catch block that detects session loss. If the error message matches the SESSION_LOST regular expression—/Session (?:with given id )?not found|Target closed|No session/i—the code invokes invalidateSession(), then automatically obtains a fresh session via ensureSession() and retries the original request (lines 95-102).

Stale Session Detection via TTL

The ensureSession() function validates the current session before issuing CDP commands. A session is considered stale if it exceeds SESSION_TTL_MS (2000ms) or if the requested targetId differs from state.sessionTargetId. In either case, invalidateSession() runs before the runtime attaches to a new target via Target.attachToTarget.

The invalidateSession() Cleanup Routine

Located at lines 146-154 in browser-runtime.ts, the invalidateSession() function performs atomic cleanup of session state:

export function invalidateSession() {
  if (state.sessionId) {
    pageEnabledSessions.delete(state.sessionId);
    pendingDialogs.delete(state.sessionId);
  }
  state.sessionId = null;
  state.sessionTargetId = null;
  state.sessionAt = 0;
}

This routine removes the session ID from tracking maps, clears pending dialog bookkeeping, and resets timestamps stored in state.ts.

Task Space Transitions and Session Lifecycle

Task spaces in Ego-Lite are logical containers that encapsulate one or more browser tabs. When a script invokes taskSpaces.switch(), taskSpaces.claim(), or taskSpaces.takeOver() from helpers.ts, these APIs may redirect the automation context to a different tab.

The underlying CDP helpers call ensureSession() before executing commands. If the active task space has changed, ensureSession() detects the mismatched targetId and triggers invalidateSession() followed by a fresh Target.attachToTarget. This ensures changing the active task-space automatically forces session invalidation so subsequent CDP calls target the correct tab.

Automatic Recovery Example

The runtime's retry mechanism allows scripts to recover transparently from transient session loss:

try {
  await cdp('DOM.enable'); // Implicit session injection
} catch (e) {
  // The runtime already called invalidateSession() and retried automatically.
  console.error('Recovered from lost session:', e);
}

When switching contexts explicitly:

await taskSpaces.switch('research-tab'); // May detach the old target
await elementResolver('loc=css:#login');   // Triggers ensureSession → new session if needed

Summary

  • Target destruction triggers invalidation via CDP event handlers in browser-runtime.ts that monitor Target.detachedFromTarget and Target.targetDestroyed.
  • Session-lost errors are caught using the SESSION_LOST regex pattern, triggering cleanup and automatic retry logic.
  • TTL expiration (2 seconds) or target ID mismatches in ensureSession() force fresh session attachment.
  • Task-space switches implicitly invalidate sessions when the underlying targetId changes, ensuring CDP commands always reference the current tab.
  • The invalidateSession() function in browser-runtime.ts clears state.sessionId, state.sessionTargetId, and removes entries from pageEnabledSessions and pendingDialogs.

Frequently Asked Questions

What happens when a browser tab closes while Ego-Lite is connected?

When a tab closes, Chrome emits a Target.targetDestroyed or Target.detachedFromTarget event. The handleMessage function in browser-runtime.ts detects this by comparing the event's targetId with state.sessionTargetId and calls invalidateSession() to clear the stale state.

How does Ego-Lite recover from a lost CDP session during command execution?

If a CDP command throws an error matching the SESSION_LOST regex (/Session (?:with given id )?not found|Target closed|No session/i), the browserCdp catch block invokes invalidateSession(), then calls ensureSession() to establish a new connection and retries the original request automatically.

Why does switching task spaces invalidate the CDP session?

Task spaces manage distinct browser tabs. When taskSpaces.switch() changes the active context, the underlying ensureSession() helper detects a changed targetId compared to state.sessionTargetId. This mismatch triggers invalidateSession() to ensure subsequent CDP commands attach to the correct new tab rather than the previous one.

Where is session state stored in Ego-Lite?

Mutable session metadata—including sessionId, sessionTargetId, and timestamps—is maintained in state.ts. The browser-runtime.ts file contains the logic for invalidateSession() and ensureSession() that manipulates this state, while helpers.ts exposes the task-space API that indirectly triggers these updates.

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 →