# How Ego-Lite Isolates Task Spaces and Implements Its Ownership Model

> Discover how Ego-Lite isolates task spaces for browser automation with dedicated resources. Learn about its ownership model preventing unauthorized context mutations. Learn more!

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

---

**Ego-Lite isolates browser automation scripts inside short-lived task spaces—each with dedicated tabs, snapshots, and CDP sessions—while enforcing an ownership model that prevents agents from mutating user-owned contexts without explicit hand-off or claim.**

This article explains how the **ego-lite** SDK from Citrolabs achieves task space isolation and implements its ownership policy. We'll examine the source code in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) and [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) to understand how agents create, switch, and claim task spaces while respecting ownership boundaries.

## What Is a Task Space in Ego-Lite?

A **task space** is an isolated browsing context that contains:

- Its own set of browser tabs
- Snapshot state for restoration
- A dedicated Chrome DevTools Protocol (CDP) session

The native bridge (the closed-source ego-lite application) enforces this isolation at the system level. The JavaScript SDK exposes a **task-space façade** called `taskSpaces` that agents use to manage these spaces programmatically.

## The Three Ownership States

Ego-Lite's ownership model defines who controls a task space and what operations are permitted. The SDK recognizes three ownership values:

| Ownership value | Description |
|---------------|-------------|
| `"agent"` | Created by and fully owned by the automation agent; full mutating access permitted |
| `"agentDelegatedToUser"` | Created by the agent but temporarily handed over to the user (e.g., after `handOff`) |
| `"user"` | Created by and owned by the human user; agent cannot mutate without claiming |

This ownership policy is documented in the source code comments at [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts) lines 18-30.

## Core Helper Functions and Ownership Enforcement

The ownership system is implemented in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts). Here's how key functions handle ownership:

### Checking Ownership: `isAgentOwned`

```typescript
// src/helpers.ts lines 43-45
function isAgentOwned(space: TaskSpace): boolean {
  return space.owner === 'agent';
}

```

This simple predicate underlies all ownership decisions in the SDK.

### Switching Spaces: `switchTaskSpace`

The `switchTaskSpace` helper enforces ownership before switching contexts:

```typescript
// src/helpers.ts lines 57-62
async function switchTaskSpace(name: string): Promise<void> {
  const space = await getTaskSpace(name);
  if (!isAgentOwned(space)) {
    throw new UserControlError(`Task space "${name}" is owned by user`);
  }
  await ego.useTaskSpace(name);
}

```

If the target space has `"user"` ownership, the function throws `UserControlError` to prevent the agent from hijacking a user session.

### Using or Creating Spaces: `useOrCreateTaskSpace`

When agents want to reuse an existing space or create one if missing, `useOrCreateTaskSpace` handles user-owned spaces gracefully at lines 94-110:

- If the space exists and is agent-owned: switch to it
- If the space exists but is user-owned: throw `UserControlError` (agent must explicitly `claim` it)
- If the space doesn't exist: create it with `"agent"` ownership

### Claiming User-Owned Spaces: `claimTaskSpace`

To take control of a user-created space, agents use `claimTaskSpace`:

```typescript
// src/helpers.ts lines 24-27
async function claimTaskSpace(name: string): Promise<void> {
  await ego.transferTaskSpaceOwnership(name, 'agent');
  await ego.useTaskSpace(name);
}

```

This transfers ownership from `"user"` to `"agent"` before selection, making it safe to mutate.

### Non-Mutating Operations Bypass Ownership

Functions that only read state—like `waitForAgentControl` and `takeOverTaskSpace`—ignore ownership checks because they don't modify the task space. This allows agents to observe user activity without interfering.

## Exposing the Task Space Façade

The SDK registers all helper methods under the `taskSpaces` global in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts). The `LEGACY_GLOBAL_HELPERS` array (lines 119-133) includes:

```typescript
// src/index.ts lines 119-133 (excerpt)
const LEGACY_GLOBAL_HELPERS = [
  // ... other helpers ...
  {
    name: 'taskSpaces',
    factory: createTaskSpaceHelpers,
    methods: ['new', 'useOrCreate', 'switch', 'claim', 'handOff', 'complete']
  }
];

```

This registration pattern makes task space operations available as `taskSpaces.new()`, `taskSpaces.switch()`, etc., directly from automation scripts.

## Practical Usage Examples

Here are complete patterns for working with task spaces in ego-lite:

### Create and Switch to a New Agent-Owned Space

```typescript
// Creates space and automatically switches to it
await taskSpaces.new('myAgentSpace');
// Now operating in isolated context with "agent" ownership

```

### Safely Use an Existing Space

```typescript
// Use if agent-owned, create if missing
try {
  await taskSpaces.useOrCreate('sharedSpace');
} catch (e) {
  if (e instanceof UserControlError) {
    // Space exists but is user-owned; claim it first
    await taskSpaces.claim('sharedSpace');
  }
}

```

### Hand Off to User Without Closing

```typescript
// Delegates ownership to user, keeps space alive
await taskSpaces.handOff();  // Skips user-owned spaces automatically

```

### Clean Up After Completion

```typescript
// Close the task space when done
await taskSpaces.complete('myAgentSpace', { keep: false });

```

## Isolation Guarantees

Together, these mechanisms provide two critical guarantees:

1. **Resource isolation**—Each task space maintains independent tab state, snapshots, and CDP sessions. The `ego.useTaskSpace` call ensures the correct browser context is active.

2. **Security boundary**—The ownership model prevents automation scripts from accidentally interfering with user-owned browsing sessions. Mutations require either original agent ownership or an explicit claim operation.

## Summary

- **Task spaces** in ego-lite are isolated browsing contexts with dedicated tabs and CDP sessions, enforced by the native bridge.

- The **ownership model** uses three states (`"agent"`, `"agentDelegatedToUser"`, `"user"`) to control who can mutate a space.

- Core implementation lives in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), with ownership checks in `switchTaskSpace` (lines 57-62) and ownership transfer in `claimTaskSpace` (lines 24-27).

- The `taskSpaces` façade exposed via [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) (lines 119-133) provides the agent-facing API.

- **Non-mutating operations** bypass ownership checks, while **mutating operations** require agent ownership or explicit claiming.

## Frequently Asked Questions

### What happens if an agent tries to switch to a user-owned task space?

The `switchTaskSpace` function throws a `UserControlError` with a message like `Task space "name" is owned by user`. The agent must first call `taskSpaces.claim()` to transfer ownership.

### Can multiple agents share the same task space?

Only one agent can own a task space at a time. If agent A has `"agent"` ownership, agent B cannot claim it until agent A completes or hands off the space. The native bridge enforces this serialization.

### How does task space isolation differ from browser profiles?

Task spaces are lighter-weight than full browser profiles—they share the same browser instance but isolate CDP sessions, tabs, and execution context. This makes them faster to create and destroy for short-lived automation tasks.

### What is the difference between `handOff` and `claim`?

`handOff` changes ownership from `"agent"` to `"agentDelegatedToUser"` (or skips if already user-owned), letting the user take control without closing the space. `claim` does the opposite: it transfers ownership from `"user"` to `"agent"` so the automation can proceed.