# How ego-browser Manages CDP Session Management and Automatic Reconnection

> Discover how ego-browser handles CDP session management and automatic reconnection with TTL cache and transparent retry logic for seamless target reattachment. Learn more!

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: deep-dive
- Published: 2026-08-04

---

**ego-browser centralizes Chrome DevTools Protocol (CDP) session handling in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts), using a 2000ms TTL cache and transparent retry logic to automatically re-attach to targets when sessions expire or disconnect.**

The **ego-lite** browser runtime provides automation capabilities through `ego-browser`, a TypeScript layer that communicates with Chrome via the Chrome DevTools Protocol (CDP). Unlike raw CDP clients that require manual session handling, ego-browser implements a robust state machine in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts) that abstracts attachment logic, TTL-based reuse, and automatic reconnection behind a simple API.

## Session Lifecycle Management

All CDP session operations are centralized in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts). The runtime maintains a single active session per tab through a strict lifecycle protocol.

### Session Creation and Target Attachment

The `ensureSession()` function checks `state.sessionId` to determine if a valid session exists. If the cached session is younger than `SESSION_TTL_MS` (2000ms), it returns the existing identifier. Otherwise, the runtime sends `Target.attachToTarget` via the low-level `rawCdp` method to create a new session attachment. Upon successful attachment, the returned `sessionId` is stored in `state.sessionId` and the target identifier in `state.sessionTargetId` (lines 27-35).

### Event Enablement and Activity Tracking

After attaching to a target, the runtime calls `Page.enable` once per session to receive page-level events such as dialogs and screencasts. A `pageEnabledSessions` Set tracks which sessions have already been initialized to prevent duplicate enablement calls (lines 5-13). The runtime records the attachment timestamp in `state.sessionAt` using `Date.now()`, which drives the TTL validation logic in subsequent calls (lines 36-38).

### Session Invalidation

When a target detaches or is destroyed, the runtime listens for `Target.detachedFromTarget` or `Target.targetDestroyed` events. These trigger `invalidateSession()`, which clears `state.sessionId`, `state.sessionTargetId`, and any pending dialogs (lines 52-64).

## Automatic Reconnection Mechanism

The `browserCdp()` function wraps every CDP request to provide transparent fault tolerance. When a call fails with a session-lost error matching the `SESSION_LOST` regex pattern, the runtime checks if the call was explicitly targeted at a specific session. If not, it automatically calls `invalidateSession()` to clear stale state, invokes `ensureSession()` to establish a fresh attachment, and retries the original CDP method (lines 94-103).

This mechanism ensures that agents continue functioning through page navigations, tab switches, or transient network interruptions without manual intervention.

## TTL-Based Session Reuse Strategy

The `SESSION_TTL_MS` constant is set to 2000 milliseconds, a deliberately short window. This design choice ensures frequent session refreshment, which is safe because the underlying `ego` bridge can re-attach to targets with negligible overhead. The short TTL guarantees that transient network hiccups or tab navigations quickly trigger fresh attachments rather than attempting to reuse stale connections that may fail mid-operation.

## Navigation Helper Integration

High-level navigation functions delegate session management to the runtime layer rather than handling connections directly.

**Page Navigation:** The `goto()` function in [`package/ego-browser/src/driver/nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/nav.ts) calls `browserCdp("Page.navigate", …)`, which internally resolves the active session through `ensureSession()` before executing the navigation command.

**Tab Switching:** The `switchTab()` function explicitly calls `invalidateSession()` to clear the current session cache and sets `state.preferredTargetId` to the new target identifier (lines 54-60). This forces the next CDP call to attach to the newly activated tab rather than attempting to reuse the previous tab's session.

## State Storage Architecture

All session-related state lives in [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts) within a shared mutable singleton:

```typescript
export const state = {
  sessionId: null as string | null,
  sessionTargetId: null as string | null,
  sessionAt: 0,
  sessionInflight: null as Promise<string> | null,
  preferredTargetId: null as string | null,
};

```

The `sessionInflight` field prevents race conditions by tracking in-progress attachment promises, while `preferredTargetId` enables tab switching workflows by specifying which target should receive the next attachment.

## Practical Implementation Examples

Basic navigation with automatic session handling:

```typescript
import { goto } from "./driver/nav.js";

await goto("https://example.com");
// Session is created or reused automatically

```

Manual session retrieval for debugging:

```typescript
import { ensureSession } from "./browser-runtime.js";

const sessionId = await ensureSession();
console.log("Current CDP session:", sessionId);

```

Force reconnection after target changes:

```typescript
import { invalidateSession, setPreferredTarget } from "./browser-runtime.js";
import { switchTab } from "./driver/nav.js";

await switchTab("target-123");
// Old session invalidated, new session attaches automatically

```

Low-level CDP calls with automatic retry:

```typescript
import { browserCdp } from "./browser-runtime.js";

const result = await browserCdp("Runtime.evaluate", { expression: "1+1" });
console.log(result.result.value); // → 2
// Automatically retries if session was lost

```

Error handling for inactive tasks occurs when `ego.sendCDPMessage` itself fails (e.g., when the user task becomes inactive). The `handleSendError` function in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) (lines 22-30) rejects all pending requests with a unified `EgoError`, preventing orphaned promises from timing out indefinitely.

## Summary

- **ego-browser** centralizes CDP session management in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts), providing a single source of truth for attachment state.
- **Automatic reconnection** occurs through the `browserCdp()` wrapper, which detects session-lost errors and transparently retries after re-attaching.
- **TTL-based caching** uses a 2000ms window to balance performance with freshness, ensuring quick recovery from navigation or network issues.
- **Navigation helpers** like `goto` and `switchTab` rely on the session layer, with `switchTab` explicitly invalidating old sessions when changing targets.
- **State management** in [`state.ts`](https://github.com/citrolabs/ego-lite/blob/main/state.ts) tracks session identifiers, timestamps, and pending operations to prevent race conditions.

## Frequently Asked Questions

### What triggers an automatic reconnection in ego-browser?

When a CDP request fails with a session-lost error matching the `SESSION_LOST` regex, the `browserCdp()` wrapper automatically invalidates the stale session, creates a new attachment via `ensureSession()`, and retries the original request. This occurs transparently in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) without requiring explicit reconnection logic in user code.

### How long does ego-browser cache CDP sessions?

The runtime caches sessions for exactly `SESSION_TTL_MS` (2000 milliseconds) as defined in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts). After this TTL expires, `ensureSession()` creates a fresh attachment even if the previous session might still be valid, ensuring rapid recovery from transient connection issues.

### Why does switchTab explicitly invalidate the session while goto does not?

`switchTab` changes the active target to a different tab, making the previous session ID invalid for the new context. It calls `invalidateSession()` to clear cached state and sets `preferredTargetId` to force attachment to the new target. In contrast, `goto` navigates within the same tab, allowing the existing session to remain valid for subsequent operations.

### Where is the session state stored in ego-browser?

All session identifiers, timestamps, and configuration flags reside in the singleton `state` object exported from [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts), including `sessionId`, `sessionTargetId`, `sessionAt`, and `sessionInflight` for tracking concurrent attachment operations.