# How ensureSession() Works in ego-lite: CDP Session Management Explained

> Explore how ensureSession() in ego-lite manages CDP sessions. Learn about TTL validation, deduplication, tab discovery, and event listeners for robust session handling.

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

---

**The `ensureSession()` function in citrolabs/ego-lite guarantees a valid Chrome DevTools Protocol (CDP) session by validating cached sessions against a TTL, deduplicating concurrent attach requests, discovering active browser tabs, and initializing page event listeners before returning a session identifier.**

The `ensureSession()` function serves as the foundational gatekeeper for all browser-level operations in ego-lite, an open-source browser automation framework. Before any CDP command executes, this helper validates connection state to ensure seamless interaction with the embedded Chrome instance. According to the source code in [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts), the function implements an eight-stage lifecycle that balances performance caching with connection reliability.

## The Eight-Stage Session Lifecycle

The implementation spans lines 107-140 of [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts), executing atomically through the following phases:

1. **Cache Validation** – If `state.sessionId` exists and `Date.now() - state.sessionAt < SESSION_TTL_MS`, the function returns the cached identifier immediately, avoiding unnecessary re-attachment overhead.

2. **In-Flight Deduplication** – When multiple callers request sessions simultaneously, the first caller creates a promise stored in `state.sessionInflight`. Subsequent callers return this same promise, ensuring only one attach sequence runs concurrently.

3. **Tab Discovery** – The function invokes `browserEgo().listTabs()` to retrieve current target information. It prioritizes `state.preferredTargetId` if set, otherwise selects the first active tab, falling back to the last available tab.

4. **Tab Validation** – If no active tab is discovered, the function throws an explicit error: `"no active tab to attach session"`, preventing silent failures downstream.

5. **Session Attachment** – When the discovered `targetId` differs from `state.sessionTargetId` or no session exists, the function calls `Target.attachToTarget` to establish a new CDP session, storing the returned `sessionId`.

6. **Event Initialization** – Immediately after attachment, `enablePageEvents(state.sessionId)` registers listeners for page-level CDP events including DOM updates and console messages.

7. **Timestamp Refresh** – The function updates `state.sessionAt = Date.now()` to reset the TTL clock for subsequent cache checks.

8. **Cleanup** – Regardless of success or failure, the `finally` block clears `state.sessionInflight = null`, allowing future calls to initiate new attach sequences.

## Source Code Architecture

In [`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts), the `ensureSession()` function interacts with mutable state managed in [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts). The state object tracks `sessionId`, `sessionAt`, `sessionInflight`, `preferredTargetId`, and `sessionTargetId` across the application lifecycle.

The function distinguishes between browser-level operations and page-level commands through the `BROWSER_LEVEL(method)` check. Unless operating at browser scope, higher-level helpers like `cdp()` and `js()` automatically invoke `ensureSession()` before transmitting commands, making session management transparent to end users.

## Practical Usage Examples

Direct session retrieval leverages the caching mechanism for optimal performance:

```typescript
import { ensureSession } from 'ego-browser';

// Obtain a cached session ID valid for SESSION_TTL_MS
const sessionId = await ensureSession();
console.log('Active CDP session:', sessionId);

```

Higher-level helpers implicitly manage sessions during CDP execution:

```typescript
import { cdp } from 'ego-browser';

// Runtime.evaluate automatically triggers ensureSession()
const result = await cdp('Runtime.evaluate', { 
  expression: 'document.title' 
});
console.log('Page title:', result.result.value);

```

Concurrent calls demonstrate the deduplication logic:

```typescript
// Both calls share the same attach promise
const [s1, s2] = await Promise.all([
  ensureSession(), 
  ensureSession()
]);
console.log(s1 === s2); // true - single attachment performed

```

Force re-attachment after navigation invalidates the current tab:

```typescript
import { invalidateSession, ensureSession } from 'ego-browser';

await invalidateSession(); // Clears cached state
const freshId = await ensureSession(); // Re-attaches to current active tab

```

## Supporting Files and Dependencies

The session management ecosystem spans multiple modules:

- **[`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts)** – Maintains runtime mutable state including session timestamps and inflight promises.
- **[`package/ego-browser/src/cdp-eval.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/cdp-eval.ts)** – Exports `cdp()` and `js()` wrappers that depend on `ensureSession()` for non-browser-level operations.
- **`package/ego-browser/src/driver/`** – Navigation, screencast, and download drivers invoke `ensureSession()` before issuing protocol commands.
- **`package/ego-browser/src/browser-runtime.test.mjs`** – Validates caching behavior, TTL expiration, concurrent deduplication, and tab selection logic.

## Summary

- **`ensureSession()`** validates cached CDP sessions against `SESSION_TTL_MS` before initiating expensive attach operations.
- **Concurrent deduplication** prevents redundant attachment attempts when multiple async callers request sessions simultaneously.
- **Tab selection logic** prioritizes preferred targets, then active tabs, falling back to the last available tab to ensure connection stability.
- **Automatic lifecycle management** enables event listeners and state cleanup without manual intervention, supporting transparent operation through higher-level APIs like `cdp()` and `js()`.

## Frequently Asked Questions

### What triggers ensureSession() to attach a new session instead of using the cache?

The function attaches a new session when the cached session ID is missing, the TTL has expired (`Date.now() - state.sessionAt >= SESSION_TTL_MS`), or when the current target ID differs from `state.sessionTargetId`. This ensures the CDP session always matches the active browser tab after navigation or context switches.

### How does ensureSession() handle multiple simultaneous calls?

When `ensureSession()` receives concurrent requests while no valid session exists, the first call creates a promise stored in `state.sessionInflight`. Subsequent calls detect this promise and return it immediately, ensuring only one `Target.attachToTarget` operation executes despite multiple callers.

### What happens if no browser tabs are available when ensureSession() runs?

If `browserEgo().listTabs()` returns an empty list or no active tab is found, the function throws `new Error("no active tab to attach session")` at lines 123-125 of [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts). This explicit failure prevents undefined behavior in downstream CDP operations.

### Can I force ensureSession() to bypass the cache and create a fresh session?

While `ensureSession()` itself does not accept parameters to bypass caching, you can invalidate the current session by calling `invalidateSession()`, which clears `state.sessionId` and `state.sessionTargetId`. The next call to `ensureSession()` will then execute a fresh attach sequence to the current active tab.