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-controlledownership === "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.sessionIdandstate.sessionTargetId - Wipes
state.sessionAttimestamp - 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 | switchTaskSpace → selectTaskSpace |
helpers.ts |
Validates ownership, calls ego.useTaskSpace |
| Detection | browserCdp → ensureSession |
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.ts—switchTaskSpaceimplementation, ownership validation,ego.useTaskSpacedelegationsrc/browser-runtime.ts—browserCdp,ensureSession,invalidateSession, and CDP attachment logicsrc/state.ts— Mutable runtime state includingsessionId,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
switchTaskSpacechanges the active tab viaego.useTaskSpacebut leaves the CDP session attached to the previous target- Stale sessions are detected lazily in
ensureSessionthrough TTL checks andtargetIdcomparison invalidateSessionperforms 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →