# Session Caching Mechanism in browser-runtime.ts: How ego-lite Manages CDP Sessions with 2-Second TTL

> Discover ego-lite's session caching mechanism in browser-runtime.ts. Learn how it manages CDP sessions with a 2-second TTL, preventing redundant calls and recovering lost connections.

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

---

**The [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) module implements a lightweight session cache that retains Chrome DevTools Protocol (CDP) sessions for exactly 2 seconds, preventing redundant `Target.attachToTarget` calls while automatically recovering from lost connections.**

The [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) file in the citrolabs/ego-lite repository serves as the core transport layer for browser automation, managing CDP session lifecycle through a time-based caching strategy. This session caching mechanism reduces communication overhead by reusing existing sessions within a strict 2000-millisecond window, ensuring efficient target interaction without requiring re-attachment on every operation.

## Cache State Architecture

The session caching layer maintains mutable state through the centralized `state` object defined in [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts).

### State Properties for Session Management

Three critical properties track the cache condition:

- **`sessionId`**: Stores the active CDP session identifier returned by `Target.attachToTarget`
- **`sessionAt`**: Records the `Date.now()` timestamp of the last successful session acquisition
- **`sessionInflight`**: Holds a promise that resolves to a new session while establishment is pending, preventing duplicate attachment requests

### TTL Constants and Validation Logic

The 2-second cache window is enforced by the `SESSION_TTL_MS` constant defined at lines 5-6 of [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts):

```typescript
const SESSION_TTL_MS = 2000;

```

When `ensureSession()` executes, it validates cache freshness through a timestamp comparison at lines 12-14:

```typescript
if (state.sessionId && Date.now() - state.sessionAt < SESSION_TTL_MS) {
  return state.sessionId;               // ✅ Cached session still valid
}

```

This check determines whether to return the existing `sessionId` or proceed with new session establishment.

## Session Acquisition Flow

The `ensureSession()` function (lines 7-44) orchestrates the cache logic and CDP attachment protocol.

### Cache Hit Behavior

When the timestamp delta falls within the 2000ms threshold, the function immediately returns the cached identifier without network overhead. This optimization eliminates redundant `Target.attachToTarget` commands for rapid successive calls.

### Cache Miss and Session Creation

If the cache is stale or empty, `ensureSession()` executes the following sequence:

1. **Enumerate targets** via `browserEgo().listTabs()` (lines 16-18)
2. **Select a target** using priority logic: preferred target, active tab, or last available tab (lines 19-23)
3. **Attach to target** by sending the `Target.attachToTarget` CDP command when the target differs from the previous session (lines 28-33)
4. **Enable page events** through `Page.enable` to activate domain notifications
5. **Update cache metadata** by storing the new `sessionId` and recording the current timestamp in `state.sessionAt` (line 37)

## Session Recovery and Invalidation

The caching mechanism includes automatic failure recovery to handle dropped CDP connections.

### Detecting Session Loss

The `browserCdp()` function monitors request failures using the `SESSION_LOST` regex pattern defined at line 9. When a request returns a session-not-found error, the system recognizes the cached session as invalid.

### Invalidation Strategy

Upon detecting connection loss, `browserCdp()` invokes `invalidateSession()` (lines 99-103), which:

- Clears `state.sessionId` and `state.sessionAt`
- Removes associated event listeners
- Forces the next `ensureSession()` call to establish a fresh attachment

This recovery pattern ensures that transient network issues or target crashes do not permanently break automation workflows.

## Implementation Examples

### Basic Helper Usage

Helper functions automatically leverage the cache without explicit session management:

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

async function clickSelector(selector: string) {
  // Automatically uses cached session if <2s old
  await browserCdp("Runtime.evaluate", { 
    expression: `document.querySelector('${selector}').click()` 
  });
}

```

### Manual Cache Inspection

For debugging or advanced control, interact with the cache state directly:

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

async function demoCache() {
  const first = await ensureSession();           // Creates new session
  console.log("First session:", first);

  // Wait 1s – still within TTL
  await new Promise(r => setTimeout(r, 1000));
  const second = await ensureSession();          // Returns cached session
  console.log("Second (cached) session:", second);

  // Wait 2s – TTL expired
  await new Promise(r => setTimeout(r, 2000));
  const third = await ensureSession();           // Creates fresh session
  console.log("Third (new) session:", third);
}

```

### Forcing Session Refresh

Manually invalidate the cache to recover from specific error conditions:

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

async function recoverFromLoss() {
  invalidateSession();          // Clears cache and event maps
  const fresh = await ensureSession(); // Guarantees fresh attachment
  console.log("Recovered session:", fresh);
}

```

## Summary

- **[`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts)** implements a time-based session cache with a hardcoded **2000ms TTL** via `SESSION_TTL_MS`
- **Cache state** is centralized in [`state.ts`](https://github.com/citrolabs/ego-lite/blob/main/state.ts) through `sessionId`, `sessionAt`, and `sessionInflight` properties
- **`ensureSession()`** returns cached identifiers when `Date.now() - state.sessionAt < 2000`, avoiding redundant `Target.attachToTarget` calls
- **Automatic recovery** occurs through `invalidateSession()` when the `SESSION_LOST` regex detects disconnected sessions
- **Concurrent safety** is maintained via `sessionInflight` promises that prevent duplicate attachment attempts during cache misses

## Frequently Asked Questions

### What triggers a session cache miss in browser-runtime.ts?

A cache miss occurs when `state.sessionId` is undefined, `state.sessionAt` is undefined, or when `Date.now() - state.sessionAt` equals or exceeds `SESSION_TTL_MS` (2000ms). Additionally, explicit calls to `invalidateSession()` or detection of a lost session through the `SESSION_LOST` error pattern immediately clear the cache and force a new attachment.

### How does the 2-second TTL impact browser automation performance?

The 2-second window balances connection reuse against stale session detection. According to the citrolabs/ego-lite source code, this TTL reduces CDP attachment overhead for rapid successive operations—such as multi-step form interactions—while ensuring that automation recovers quickly from target crashes or navigation events that invalidate existing sessions.

### Can the session TTL be configured or disabled?

No, `SESSION_TTL_MS` is defined as a constant at lines 5-6 of [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) with a fixed value of 2000ms. The repository does not expose configuration options for this parameter, as the 2-second duration is optimized for the typical lifecycle of browser automation tasks while preventing resource leaks from abandoned CDP sessions.

### How does session recovery work after a Target.detachFromTarget event?

When `browserCdp()` encounters a "session lost" error matching the `SESSION_LOST` regex, it automatically invokes `invalidateSession()` (lines 99-103). This function clears `state.sessionId` and `state.sessionAt`, then forces the next CDP request to trigger `ensureSession()` and establish a fresh `Target.attachToTarget` connection. This recovery mechanism operates transparently without requiring manual intervention in helper functions.