# Ego-Browser Preferred Target Selection in Multi-Tab Scenarios: How It Works

> Discover how Ego-Browser's preferred target selection works in multi-tab scenarios. Learn about the `preferredTargetId` and fallback logic for consistent tab targeting.

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

---

**Ego-Browser uses a `preferredTargetId` state variable combined with fallback logic to consistently select the intended tab when multiple tabs are open.**

When automating browser interactions across multiple tabs, ego-browser must decide which tab receives each command. The mechanism centers on a mutable state property that tracks the most recently addressed tab, with automatic fallback to active or last-resort tabs when needed.

## The Three-Component Architecture

The preferred target selection system spans three source files in the `ego-lite` repository. Each handles a distinct responsibility: state storage, runtime resolution, and navigation-triggered updates.

### State Storage in [`state.ts`](https://github.com/citrolabs/ego-lite/blob/main/state.ts)

The `preferredTargetId` property lives in the global state singleton defined in [[`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts#L36). This property stores the `targetId` string of the tab currently designated as preferred, or `null` when no preference exists.

```typescript
// From state.ts
export const state: {
  // ... other properties
  preferredTargetId: string | null;
  // ...
} = {
  // ... initial values
  preferredTargetId: null,
  // ...
};

```

Storing this value in a singleton ensures persistence across asynchronous operations and multiple script execution rounds.

### Runtime Resolution in [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts)

The actual selection logic executes in [[`package/ego-browser/src/browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/browser-runtime.ts#L118-L124). When the runtime needs to identify which tab should receive a command, it evaluates tabs in priority order:

1. **Preferred target match** — If `state.preferredTargetId` matches a tab's `targetId`, that tab is selected
2. **Active tab** — If no preferred target is set or found, the currently active tab (`t.active === true`) is chosen
3. **Last-resort fallback** — If neither above condition is met, the final tab in the array is used

```typescript
// Conceptual representation from browser-runtime.ts selection logic
const target = tabs.find(t => t.targetId === state.preferredTargetId) 
            || tabs.find(t => t.active) 
            || tabs[tabs.length - 1];

```

This ordered evaluation ensures deterministic behavior even when tabs open, close, or change active status unexpectedly.

### Navigation-Driven Updates in [`driver/nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/driver/nav.ts)

Navigation helpers in [[`package/ego-browser/src/driver/nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/nav.ts)](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/nav.ts#L226) maintain synchronization between user actions and the preferred target state. Key functions include:

- `openTab(url)` — Creates a new tab and sets `state.preferredTargetId` to the new tab's `targetId`
- `switchTab(targetId)` — Updates `state.preferredTargetId` to the specified `targetId`
- `closeTab(targetId)` — Clears `state.preferredTargetId` to `null` if the closed tab was preferred, triggering fallback on next command

```typescript
// Pattern found in nav.ts navigation helpers
if (actionTargetId) {
  state.preferredTargetId = actionTargetId;
}
// Or when closing:
if (state.preferredTargetId === closedTargetId) {
  state.preferredTargetId = null;
}

```

## Practical Usage Patterns

The following examples demonstrate how preferred target selection operates in practice.

### Open and Immediately Target a New Tab

```javascript
import { openTab, type } from 'ego-browser';

// New tab becomes preferred automatically
await openTab('https://dashboard.example.com');

// This types into the newly opened tab, not the original
await type('#api-key', 'sk-live-...');

```

### Explicitly Switch Between Existing Tabs

```javascript
import { switchTab, click, state } from 'ego-browser';

const originalTab = state.preferredTargetId;

// Open secondary tab for reference
await openTab('https://docs.example.com');
const docsTab = state.preferredTargetId;

// Return to original and continue work
await switchTab(originalTab);
await click('#submit-form');

// Reference docs again
await switchTab(docsTab);

```

### Handling Tab Closure with Automatic Fallback

```javascript
import { closeTab, navigate, state } from 'ego-browser';

const tempTab = state.preferredTargetId;

await closeTab(tempTab);  // preferredTargetId becomes null

// Automatically falls back to remaining active tab
await navigate('https://final-step.example.com');
console.log(state.preferredTargetId);  // Now the active tab's ID

```

## Design Benefits

| Benefit | Mechanism |
|---------|-----------|
| **Determinism** | Explicit `preferredTargetId` prevents ambiguity about which tab receives commands |
| **Resilience** | Three-tier fallback (preferred → active → last) handles edge cases like closed tabs |
| **State persistence** | Singleton pattern preserves preference across async boundaries and execution rounds |
| **Minimal API surface** | Navigation helpers manage state automatically; manual override available when needed |

## Summary

- **State storage**: `preferredTargetId` in [`state.ts`](https://github.com/citrolabs/ego-lite/blob/main/state.ts) tracks the preferred tab across operations
- **Resolution logic**: [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) selects tabs in priority order: preferred match, active tab, last fallback
- **Automatic synchronization**: Navigation helpers in [`nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/nav.ts) update the preference when opening, switching, or closing tabs
- **Fallback safety**: The system degrades gracefully when preferred tabs disappear

## Frequently Asked Questions

### What happens if the preferred target tab is closed?

The `closeTab` helper in [`nav.ts`](https://github.com/citrolabs/ego-lite/blob/main/nav.ts) detects when the closing tab matches `state.preferredTargetId` and sets it to `null`. On the next command, [`browser-runtime.ts`](https://github.com/citrolabs/ego-lite/blob/main/browser-runtime.ts) falls back to the active tab or final tab in the list, preventing errors from stale target references.

### Can multiple tabs be preferred simultaneously?

No. The state maintains a single `string | null` value. For operations spanning multiple tabs, store additional target IDs in your own variables and use `switchTab()` to rotate the preference, or operate on specific targets by passing `targetId` directly to low-level methods.

### How does this differ from Puppeteer's target handling?

Ego-browser centralizes preference in mutable state with automatic fallback, whereas Puppeteer typically requires explicit `target` or `page` object references. This design trade-off simplifies multi-tab scripting in ego-browser by reducing the need to pass page handles through every function call.