# How to Perform Control Handoff Between an ego-lite Agent and the User

> Learn about control handoff in ego-lite. Use handOffTaskSpace to transfer browser control to the user, waitForAgentControl to pause automation, and takeOverTaskSpace to resume.

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

---

**Transfer browser control using `handOffTaskSpace()` to give the user access, `waitForAgentControl()` to pause until they finish, and `takeOverTaskSpace()` to resume automation.**

ego-lite isolates browser work into *task-spaces* that enforce strict ownership—only one party (either the **agent** or the **user**) can issue commands at any time. Understanding how to perform a control handoff between an ego-lite agent and the user is essential for workflows that require interactive steps like CAPTCHA solving, manual login, or user confirmation.

## Understanding Task-Space Ownership

Each task-space maintains exclusive ownership status. When the agent owns the space, it can execute browser commands freely. When the user owns the space, the agent is blocked from issuing commands until control is explicitly returned. This ownership model prevents race conditions and ensures that automated scripts do not interfere with manual user interactions.

The control handoff flow is implemented in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) and exposed through three public helper functions that manage this ownership transition.

## Core Control Handoff Methods

### handOffTaskSpace

The `handOffTaskSpace([nameOrId])` helper transfers ownership from the agent to the user. According to the source code in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) at lines 326-340, this method first checks the current ownership status. If the space is already user-owned, it returns `{ done: false, skipped: "user-owned" }`. Otherwise, it selects the specified space and invokes the internal `ego.handOffTaskSpace()` runtime call, returning `{ done: true }` upon success.

Use this method when the task requires the user to perform an interactive step that the agent cannot automate, such as entering a password or solving a visual puzzle.

### waitForAgentControl

The `waitForAgentControl(nameOrId, {interval?, timeout?})` helper pauses execution until the agent regains control but **does not** change ownership itself. As implemented in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) at lines 84-108, this method polls a lightweight probe (`ego.snapshot`) through the private `probeAgentControl()` function. 

If the probe returns a user-control error, the loop continues; if the snapshot succeeds, the agent has regained control. The method respects configurable parameters:
- **interval**: Polling frequency in seconds (default: 20)
- **timeout**: Maximum wait time in seconds (default: 600)

Call this inside a single heredoc after `handOffTaskSpace()` to suspend the script until the user signals completion.

### takeOverTaskSpace

The `takeOverTaskSpace([nameOrId])` helper returns control to the agent. Located at lines 47-53 in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts), this method selects the space and calls `ego.takeOverTaskSpace()`. The runtime does not enforce an ownership check, so your code must only call this after explicit user consent, as documented in [`skills/ego-browser/SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md) at lines 97-108.

## Step-by-Step Implementation Workflow

A typical control handoff sequence follows these steps:

1. **Determine interaction necessity** – The agent identifies that manual user input is required.
2. **Transfer control** – Call `await handOffTaskSpace(task.id)` to switch ownership and hide the agent overlay.
3. **Instruct the user** – Display clear instructions about what the user must complete.
4. **Pause execution** – (Optional) Call `await waitForAgentControl(task.id)` to block until the user finishes.
5. **Resume or complete** – After user confirmation, either call `await takeOverTaskSpace(task.id)` to continue automation, or `await completeTaskSpace(task.id, {keep: false})` to finish the task.

## Code Examples

Here is a complete implementation showing how to hand off control for a CAPTCHA challenge:

```javascript
// Transfer control so the user can solve a CAPTCHA
const task = await useOrCreateTaskSpace('solve-captcha');
await handOffTaskSpace(task.id);

// Inform the user what to do next
await askUser('Please solve the CAPTCHA, then click Continue.');

// Wait inside the same heredoc until the user finishes
await waitForAgentControl(task.id, { interval: 10, timeout: 300 });

// Take back control and continue automation
await takeOverTaskSpace(task.id);
await click('#submit');

```

Handle cases where the user already owns the space to prevent redundant operations:

```javascript
const result = await handOffTaskSpace(task.id);
if (!result.done) {
  // Space already user-owned, skip redundant handoff
  console.log('User already has control');
}

```

## Error Handling and Edge Cases

When implementing control handoff between an ego-lite agent and the user, always check the return value of `handOffTaskSpace()`. The **skipped** state indicates that the user already owns the task-space, allowing your script to proceed gracefully without attempting redundant ownership transfers.

The `waitForAgentControl()` method will throw if the timeout expires before the agent regains control, so wrap polling calls in appropriate try-catch blocks for production use.

## Summary

- **Task-spaces enforce single ownership** – Only the owner (agent or user) can issue browser commands at any time.
- **Three helpers manage transitions** – `handOffTaskSpace()` gives control to the user, `waitForAgentControl()` pauses until completion, and `takeOverTaskSpace()` resumes agent automation.
- **Check return values** – Handle skipped states when the space is already user-owned to avoid redundant operations.
- **Confirm user readiness** – The agent must explicitly ask the user before calling `takeOverTaskSpace()`, as the runtime does not enforce consent checks.

## Frequently Asked Questions

### What happens if I call handOffTaskSpace when the user already owns the task-space?

The method returns `{ done: false, skipped: "user-owned" }` without attempting another handoff. This allows your script to continue execution gracefully, knowing the user already has control of the browser.

### How long does waitForAgentControl wait by default?

By default, `waitForAgentControl()` polls every 20 seconds (interval) and times out after 600 seconds (10 minutes). You can customize these values by passing an options object: `{ interval: 10, timeout: 300 }` for 10-second intervals and a 5-minute timeout.

### Can the agent take over control without user consent?

Technically yes—the runtime does not enforce an ownership verification when `takeOverTaskSpace()` is called. However, the [`SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/SKILL.md) documentation and best practices require that agents explicitly ask the user to continue before reclaiming control. Always implement a user confirmation step before calling this method.

### Where are the control handoff methods implemented?

The core logic resides in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) at lines 326-340 for `handOffTaskSpace`, lines 47-53 for `takeOverTaskSpace`, and lines 84-108 for `waitForAgentControl`. These are exposed through the task-space façade in [`src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/index.ts) according to the JSON-RPC signatures defined in [`src/format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/format.ts).