Session Invalidation Flow When Switching Task Spaces in ego-lite: A Deep Dive

When you call switchTaskSpace() in ego-lite, the framework automatically invalidates the current CDP session and creates a fresh one bound to the newly-selected task space through a three-step validation and re-attachment process.

The session invalidation flow is a critical mechanism in ego-lite that ensures AI agents operate in isolated browser contexts when navigating between task spaces. This article examines how the framework handles CDP session lifecycle management using actual source code from the citrolabs/ego-lite repository.

How switchTaskSpace Triggers Session Invalidation

The entry point for task space switching is switchTaskSpace(nameOrId) in src/helpers.ts. This function performs ownership validation before delegating to the native runtime.

Step 1: Resolving and Selecting the Target Space

When invoked, switchTaskSpace first calls findTaskSpace to locate the requested space. It enforces a strict ownership check:

  • ownership === "agent" — fully agent-controlled
  • ownership === "agentDelegatedToUser" — delegated but still agent-managed

If validation passes, control flows to selectTaskSpace, which invokes ego.useTaskSpace (see helpers.ts lines 52-64). This native SDK call notifies the Ego runtime which tab should become the active CDP target for the current Node process.

// Example: Switch to another agent-owned task space
await switchTaskSpace('my-new-space');   // throws if user-owned

// The runtime now knows space-B is active, but CDP session still points to space-A

The runtime switch happens immediately, but the existing CDP session remains attached to the previous tab. This is where lazy invalidation becomes essential.

Step 2: Detecting Stale Sessions in browserCdp

The Ego runtime does not proactively detach CDP sessions when tabs change. Instead, staleness detection happens on the next CDP command through browserCdp in src/browser-runtime.ts.

The ensureSession Validation Logic

Every CDP-bound helper (js(), cdp(), element resolvers) routes through browserCdp, which calls ensureSession first. This function checks two conditions:

Check Purpose
TTL expiration Session older than 2 seconds triggers refresh
Target ID mismatch state.sessionTargetId differs from active tab

If either condition is true, ensureSession calls invalidateSession() before proceeding (see browser-runtime.ts lines 46-55).

// Pseudo-code illustrating the staleness detection
function ensureSession() {
  if (state.sessionId && !isExpired(state.sessionAt) && 
      state.sessionTargetId === getActiveTargetId()) {
    return;  // Session still valid
  }
  invalidateSession();  // ← clears all pending state
  // ... proceed to attach new session
}

This lazy evaluation pattern avoids unnecessary session churn during rapid task space switches without intervening CDP calls.

Step 3: invalidateSession and Fresh Session Creation

The invalidateSession() function performs surgical cleanup of session state:

  • Clears state.sessionId and state.sessionTargetId
  • Wipes state.sessionAt timestamp
  • Discards pending dialog handlers
  • Flushes buffered CDP events
  • Rejects/voids pending CDP promises

After invalidation, ensureSession executes the attachment sequence:

// Re-attachment flow following invalidateSession()
const tabs = await ego.listTabs();              // enumerate available tabs
const targetTab = tabs.find(t => t.taskSpaceId === selectedSpaceId);
const { sessionId } = await Target.attachToTarget(targetTab.targetId);
await enablePageEvents(sessionId);              // Page.* events for DOM interaction

// Update state for future validation
state.sessionId = sessionId;
state.sessionTargetId = targetTab.targetId;
state.sessionAt = Date.now();

The new session is now scoped exclusively to the selected task space. All subsequent helper calls operate against this isolated context without additional agent intervention.

Complete Invalidation Flow: Visual Summary

Phase Function File Action
Initiation switchTaskSpaceselectTaskSpace helpers.ts Validates ownership, calls ego.useTaskSpace
Detection browserCdpensureSession browser-runtime.ts Compares sessionTargetId vs. active tab; checks TTL
Invalidation invalidateSession browser-runtime.ts Wipes sessionId, caches, pending dialogs, events
Re-creation ensureSession (post-invalidation) browser-runtime.ts Attaches to new target via Target.attachToTarget
Activation enablePageEvents browser-runtime.ts Enables Page domain for DOM operations

Practical Code Examples

Automatic Session Recovery After Switch

// Space A is currently active with an established CDP session
await js('document.title');  // executes against space A

// Switch to space B — session becomes stale but not yet invalidated
await switchTaskSpace('space-B');

// Next CDP call triggers full invalidation + re-attachment
const url = await js('window.location.href');  // new session to space B
console.log('Now operating on:', url);

Invalidation Side-Effects Are Transparent

await switchTaskSpace('checkout-flow');
await Promise.all([
  js('document.querySelector("#total").textContent'),   // triggers ensureSession
  js('document.querySelector("#tax").textContent')      // reuses fresh session
]);
// Both execute against checkout-flow's isolated context

Key Implementation Files

Understanding the session invalidation flow requires familiarity with these source locations:

  • src/helpers.tsswitchTaskSpace implementation, ownership validation, ego.useTaskSpace delegation
  • src/browser-runtime.tsbrowserCdp, ensureSession, invalidateSession, and CDP attachment logic
  • src/state.ts — Mutable runtime state including sessionId, sessionTargetId, sessionAt, and associated caches

These modules collectively ensure that task space isolation is enforced at the CDP protocol level, preventing cross-contamination between agent-owned and user-owned browsing contexts.

Summary

  • switchTaskSpace changes the active tab via ego.useTaskSpace but leaves the CDP session attached to the previous target
  • Stale sessions are detected lazily in ensureSession through TTL checks and targetId comparison
  • invalidateSession performs complete cleanup of session state, pending operations, and event buffers
  • Fresh sessions attach automatically to the newly-selected task space's tab without manual agent intervention
  • This lazy invalidation pattern balances performance with isolation guarantees across task space boundaries

Frequently Asked Questions

What triggers session invalidation in ego-lite?

Session invalidation is triggered when ensureSession detects either a 2-second TTL expiration or a mismatch between state.sessionTargetId and the currently active tab selected by switchTaskSpace. This check runs on every CDP-bound operation, making invalidation lazy rather than immediate.

Does switchTaskSpace immediately detach the CDP session?

No. The switchTaskSpace function only calls ego.useTaskSpace to update the runtime's active tab selection. The existing CDP session remains attached until the next CDP command flows through browserCdp, at which point ensureSession detects the staleness and invokes invalidateSession.

What happens to pending operations during invalidation?

invalidateSession clears all pending state: unresolved CDP promises are rejected, buffered events are discarded, and dialog handlers are removed. This ensures no stale callbacks execute against the new task space's context. The agent must re-issue any interrupted operations after the switch completes.

Can agents prevent automatic session invalidation?

No. The invalidation flow is mandatory and ensures security isolation between task spaces. Agents cannot bypass the targetId validation in ensureSession. However, agents can minimize session churn by batching CDP operations within a single task space before switching, reducing the frequency of re-attachment overhead.

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 →