# How an Agent Hands Off a Task Space to a User in ego-lite

> Learn how an agent hands off a task space to a user in ego-lite. Discover the steps involved, including validation, target resolution, and ownership checks, to seamlessly transfer control.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-08-22

---

**TLDR:** In the ego-lite framework, an agent transfers control of a browsing context back to the user by calling the `handOffTaskSpace` helper from [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts), which validates the runtime, resolves the target space, checks ownership, and invokes the low-level `ego.handOffTaskSpace()` method.

Ego-lite, an open-source project from the `citrolabs/ego-lite` repository, defines a **task space** as an isolated browsing context that can be owned by either the agent or the user. When the agent finishes its work in a task space and needs to return control to the user, it executes a hand-off process that involves several validation and switching steps. This article walks through the exact implementation, file paths, and code patterns used to achieve this transition.

## Understanding Task Space Ownership in Ego-lite

Before examining the hand-off process itself, it's important to understand how ego-lite tracks control. Each task space maintains an `ownership` property that takes one of two values: `"agent"` or `"user"`. This distinction drives the entire hand-off flow.

- When `ownership === "agent"`, the agent is the active controller of that browsing context and can hand it off.
- When `ownership === "user"`, the user already controls the space, so a hand-off is unnecessary and skips gracefully.

The `handOffTaskSpace` helper is the public API surface that agents call to transfer control. It lives in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts), alongside supporting utilities like `findTaskSpace` and `selectTaskSpace`.

## The Step-by-Step Hand-Off Process

The hand-off process in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) follows a precise sequence of six steps, each with a specific purpose.

### 1. Validate the Runtime Environment

The first step checks that the global `ego` object exists and that it implements the low-level `handOffTaskSpace` method. Without this, the helper refuses to proceed and throws an error.

```ts
const ego = globalThis.ego;
if (!ego || typeof ego.handOffTaskSpace !== "function") {
  throw new Error("handOffTaskSpace requires ego.handOffTaskSpace");
}

```

This guard ensures the closed-source ego runtime is in place before any operations attempt to run.

### 2. Resolve the Target Task Space

If the caller supplies a name or numeric ID for a specific task space, the helper resolves it using `findTaskSpace`. This utility queries the known spaces via `listTaskSpaces()` and matches the input by name or numeric ID. If no argument is provided, the helper operates on the currently active space.

### 3. Check Ownership and Short-Circuit

Once the target space is resolved, the helper inspects its `ownership` property. If the space is already user-owned, the function returns immediately with a skipped result, preventing unnecessary work.

```ts
if (match.ownership === "user") {
  return { done: false, skipped: "user-owned" as const };
}

```

This guard ensures that handing off an already-user-controlled space is a no-op.

### 4. Select the Target Space

For agent-owned spaces, the helper activates the target context using `selectTaskSpace`, which wraps the low-level `ego.useTaskSpace` call and ensures the correct numeric ID is used.

```ts
await selectTaskSpace(ego, match, "handOffTaskSpace");

```

This step makes the agent the active controller of the context in preparation for the hand-off.

### 5. Invoke the Runtime Hand-Off

The core hand-off call happens here. The helper calls `ego.handOffTaskSpace()` directly. Internally, the runtime:

- Hides the agent overlay UI
- Releases the CDP (Chrome DevTools Protocol) session for user interaction
- Returns control of the browsing context to the human

Errors are normalized through `assertNoEgoError`, which converts any runtime failure into a standard JavaScript exception.

```ts
assertNoEgoError(await ego.handOffTaskSpace(), "handOffTaskSpace");

```

### 6. Report Success

Finally, the helper returns `{ done: true }` to signal that control has successfully transferred to the user.

## Key Architectural Components

The hand-off process relies on several distinct components in the ego-lite codebase. Here's a breakdown of each:

| Component | Purpose | Source Location |
|---|---|---|
| `handOffTaskSpace` | Public helper that orchestrates the hand-off process | [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) |
| `findTaskSpace` | Resolves a task space by name or numeric ID from `listTaskSpaces()` | Same file |
| `selectTaskSpace` | Wraps `ego.useTaskSpace` with proper numeric ID handling | Same file |
| `ego.handOffTaskSpace` | Low-level runtime implementation that hides the agent UI and gives the browser to the user | Provided by the closed-source ego runtime |
| `assertNoEgoError` | Normalizes runtime errors into JavaScript exceptions | Same file |

The shared state that drives `listTaskSpaces` and other helpers lives in [`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts), holding the singleton runtime state used across the task-space system.

## Code Examples: Handing Off in Practice

The following examples show how to use `handOffTaskSpace` in real agent code.

### Basic Hand-Off (Current Task Space)

The simplest case hangs off control of the current task space with no arguments:

```ts
import { handOffTaskSpace } from "ego-browser";

await handOffTaskSpace();
// → { done: true }  // control now belongs to the user

```

### Hand-Off a Specific Space by Name

You can target a named visibility with `handOffTaskSpace("checkout-page")`:

```ts
import { handOffTaskSpace } from "ego-browser";

const result = await handOffTaskSpace("checkout-page");
// result === { done: true }   // if the space was agent-owned
// result === { done: false, skipped: "user-owned" } // if already user-owned

```

### Handling the Skipped Case

When the target space is already user-owned, the helper returns a `skipped` flag. You can handle this explicitly:

```ts
const { done, skipped } = await handOffTaskSpace(42);
if (!done && skipped === "user-owned") {
  console.log("The user already controls this space; nothing to hand off.");
}

```

## Key Source Files in the Ego-lite Repository

For developers looking to explore or contribute to this code, the primary files are:

- **[`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts)** — Implements the public helper `handOffTaskSpace` plus supporting utilities (`findTaskSpace`, `selectTaskSpace`, `assertNoEgoError`).
- **`package/ego-browser/src/taskspace-e2e.test.mjs`** — End-to-end tests that verify hand-off behavior across real task spaces.
- **[`package/ego-browser/src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts)** — Holds the singleton runtime state used by `listTaskSpaces` and other helpers.
- **[`README.md`](https://github.com/citrolabs/ego-lite/blob/main/README.md)** (repo root) — High-level description of the ego-lite architecture and usage.

## Summary

The agent-to-user hand-off process in ego-lite is a well-structured, defensively coded flow that respects task-space ownership and runtime capability.

- The public `handOffTaskSpace` helper in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) orchestrates the entire transition.
- The helper validates the runtime's existence, resolves the target space via `findTaskSpace`, and checks ownership before switching.
- User-owned spaces short-circuit with a `skipped: "user-owned"` result, avoiding needless operations.
- The low-level `ego.handOffTaskSpace` call hides the agent overlay, releases the CDP session, and returns browser control to the user.
- Errors are normalized via `assertNoEgoError`, ensuring consistent exception handling.

## Frequently Asked Questions

### What happens if the task space is already user-owned?

### How does `findTaskSpace` resolve a task space in ego-lite?

### Where is the `handOffTaskSpace` helper implemented?

### Is the hand-off process asynchronous?