# Task Space Ownership States in Ego-Browser: A Complete Guide

> Understand ego-browser task space ownership states: agent, agentDelegatedToUser, and user. Discover how these states control browsing context in this comprehensive guide.

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

---

**Ego-Browser defines three ownership states for task spaces—`agent`, `agentDelegatedToUser`, and `user`—that determine which party controls the browsing context at any given moment.**

The **ego-browser** skill in the `citrolabs/ego-lite` repository provides isolated browsing contexts called *task spaces* where AI agents can execute automated actions. Every task space carries an `ownership` property that enforces strict access control, preventing conflicts when both agents and users need to interact with the same browser window. Understanding these states is essential for building reliable agent workflows that handle user hand-offs gracefully.

## The Three Ownership States

According to the [SKILL.md](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md) documentation, specifically lines 75-80, the ownership policy defines these mutually exclusive states:

### `agent`

The task space is **fully owned and controlled by the AI agent**. The agent may execute any browser action—navigation, clicking, form filling, JavaScript injection—without restriction. This is the default state when you create a task space with `useOrCreateTaskSpace()`.

### `agentDelegatedToUser`

Control has been **temporarily handed off to the user**. This state typically occurs when:
- A **CAPTCHA** requires human solving
- **Multi-factor authentication** needs manual input
- A **login flow** demands credentials the agent cannot access

While in this state, any agent-initiated action fails with a "user is controlling" error. The agent must explicitly reclaim the space before resuming automation.

### `user`

The task space is **owned by the user from the start**. This rare configuration creates a browser window for direct user interaction without any agent involvement. Use this when the user wants a persistent, manually-controlled browsing session alongside agent-driven spaces.

## How Ownership Transitions Work

Ownership changes are triggered by specific API calls defined in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts):

| Function | Ownership Transition | Use Case |
|----------|----------------------|----------|
| `useOrCreateTaskSpace(name)` | → `agent` (default) | Start or resume agent-controlled automation |
| `handOffTaskSpace(id)` | `agent` → `agentDelegatedToUser` | Pause for user input |
| `claimTaskSpace(id)` | `agentDelegatedToUser` → `agent` | Resume agent control after user finishes |

The runtime state is maintained in [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts), which stores the `ownership` field alongside other task-space metadata.

## Practical Code Examples

### Creating an Agent-Owned Task Space

Start automation with default `agent` ownership:

```javascript
// Create (or reuse) a task space that the agent fully controls
const task = await useOrCreateTaskSpace('search-github-issues')
await nav.goto('https://github.com')
await click('a[href="/search"]')
await completeTaskSpace(task.id, { keep: false })

```

### Handing Off for User Interaction

Handle CAPTCHAs or manual logins with proper state management:

```javascript
// Agent runs until a point where user input is required
await nav.goto('https://example.com/login')
await fill('input[name="email"]', 'user@example.com')
await click('button[type="submit"]')

// Hand off the task space so the user can solve the captcha
await handOffTaskSpace(task.id)
cliLog('Please solve the captcha, then come back.')

// After the user finishes, reclaim the space
await claimTaskSpace(task.id)
await click('#continue')
await completeTaskSpace(task.id, { keep: false })

```

### Inspecting Current Ownership

Audit task spaces to debug permission issues:

```javascript
// List all task spaces and inspect their ownership
const spaces = await listTaskSpaces()
for (const s of spaces) {
  cliLog(`Task ${s.id} (${s.name}) is owned by ${s.ownership}`)
}

```

## Core Implementation Files

- **[`skills/ego-browser/SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md)** – High-level documentation of task-space usage and ownership policy
- **[`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts)** – API implementations for `useOrCreateTaskSpace`, `handOffTaskSpace`, and `claimTaskSpace`
- **[`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts)** – Runtime state management including the `ownership` field
- **`src/taskspace-e2e.test.mjs`** – End-to-end tests validating ownership transitions and hand-off behavior

## Common Ownership Errors

The "user is controlling" error occurs when your agent attempts actions during `agentDelegatedToUser` state. Always verify state or wrap agent actions in error handling:

```javascript
// Defensive pattern with ownership checking
const space = await getTaskSpace(task.id)
if (space.ownership !== 'agent') {
  await claimTaskSpace(task.id) // Or prompt user to finish
}
// Now safe to proceed with agent actions

```

## Summary

- **Three ownership states** govern task space control: `agent`, `agentDelegatedToUser`, and `user`
- **Default agent ownership** enables full automation; hand-offs are explicit via `handOffTaskSpace()`
- **Reclaim with `claimTaskSpace()`** after user interaction completes
- **State is enforced at runtime** in [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts), throwing errors for unauthorized access
- **End-to-end tests** in `taskspace-e2e.test.mjs` validate all transition paths

## Frequently Asked Questions

### How do I check if a task space is currently controlled by the user?

Call `listTaskSpaces()` or `getTaskSpace(id)` and inspect the `ownership` property. If the value is `agentDelegatedToUser` or `user`, the agent cannot execute browser actions until ownership returns to `agent`.

### What happens if my agent tries to act during a hand-off?

The action fails immediately with a "user is controlling" error. This is a safety mechanism to prevent the agent from interfering with manual user interaction. You must call `claimTaskSpace(id)` successfully before resuming automation.

### Can I create a task space that starts with user ownership instead of agent?

Yes, though it's rarely needed. Pass `ownership: 'user'` when creating the space. Most workflows start with `agent` ownership and transition to `agentDelegatedToUser` only when user intervention is required.

### Is the ownership state persisted across script restarts?

According to the source code in [`src/state.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/state.ts), task space metadata including ownership is maintained in the runtime state. Persistent behavior depends on how `ego-browser` is integrated into your deployment—review the state management implementation for your specific setup.