# ego-lite Task Space Ownership Models: agent, agentDelegatedToUser, and user Explained

> Understand ego-lite Task Space ownership models: agent, agentDelegatedToUser, and user. Learn which side AI or human executes browser actions to manage your automation effectively.

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

---

**ego-lite defines three ownership states for Task Spaces—agent, agentDelegatedToUser, and user—that control which side (AI agent or human user) can execute browser actions.**

Task Spaces in ego-lite provide isolated browsing contexts where AI agents and human users collaborate. Each space carries an **`ownership`** field that determines access rights and governs how helper functions behave. This article details the three ownership models defined in the source code, their runtime implications, and how to transition between them.

---

## The Three Task Space Ownership Models

The ownership policy is implemented in **[`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts)** (lines 118–132) and documented in **[`skills/ego-browser/SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md)** (lines 75–85). Three distinct values control access:

### 1. agent

The space was created by the agent and remains under full agent control.

- All helpers operate normally without additional checks
- The agent can use `switchTaskSpace`, `useOrCreateTaskSpace`, or `takeOverTaskSpace` freely
- This is the default state for agent-initiated browsing sessions

### 2. agentDelegatedToUser

The agent created the space but has **temporarily handed control to the user**—for example, after a `handOffTaskSpace` call or GUI takeover.

- The agent can still **select** the space (treated as agent-owned for `switchTaskSpace` and `takeOverTaskSpace`)
- **Real browser actions are blocked** until the user returns control
- The native bridge enforces the user-control boundary at the runtime level
- Ownership automatically returns to `agent` once `claimTaskSpace` succeeds

### 3. user

The space belongs to the user; the agent **does not own it**.

Helpers react to user-owned spaces with two distinct behaviors:

| Helper | Behavior on user-owned space |
|--------|------------------------------|
| `handOffTaskSpace` | Skips action, returns `{ done: false, skipped: "user-owned" }` |
| `completeTaskSpace { keep: true }` | Skips action, returns `{ done: false, skipped: "user-owned" }` |
| `switchTaskSpace` | **Throws error** |

To operate on a user-owned space, the agent must first **claim** it with `claimTaskSpace`, which transfers ownership to `agent`.

---

## Ownership Transitions in Practice

The following examples demonstrate creating, delegating, reclaiming, and handling errors across ownership states.

### Creating and Delegating a Task Space

```javascript
// Create a new agent-owned task space
const task = await newTaskSpace('search github issues');
console.log(`Created agent-owned space ${task.id}`);

// Hand control to the user (e.g., for CAPTCHA solving)
const result = await handOffTaskSpace(task.id);
// result: { done: false, skipped: "user-owned" }
// Ownership is now agentDelegatedToUser

```

### Reclaiming Control from the User

```javascript
// User confirms readiness → agent claims the space back
const claimed = await claimTaskSpace(task.id);
console.log(`Reclaimed ownership: ${claimed.ownership}`);
// Output: "agent"

```

### Error Handling for Unauthorized Access

```javascript
// Attempting to switch to a user-owned space without claiming throws
try {
  await switchTaskSpace(unclaimedUserSpace.id);
} catch (e) {
  console.error('Blocked:', e.message);
  // Error: operation not permitted on user-owned space
}

```

### Conditional Completion with keep Flag

```javascript
// Complete while keeping page visible — skips if user-owned
const completion = await completeTaskSpace(task.id, { keep: true });
// On user-owned space: { done: false, skipped: "user-owned" }

```

---

## Runtime Enforcement and Native Bridge

The ownership model is not merely a metadata field—it carries runtime consequences through ego-lite's native bridge.

- **Selection vs. Action distinction**: `agentDelegatedToUser` spaces allow selection helpers (`switchTaskSpace`, `takeOverTaskSpace`) but block execution helpers that would mutate browser state
- **Claim requirement**: The native bridge requires successful `claimTaskSpace` before accepting commands on `user`-owned spaces
- **Automatic cleanup**: Delegated spaces that remain unclaimed may timeout based on session policies defined in the runtime

This design prevents race conditions where an agent might overwrite user input or navigate away from a page the user is actively editing.

---

## Source Code Reference

Key files implementing and documenting Task Space ownership models in ego-lite:

| File | Lines | Purpose |
|------|-------|---------|
| [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) | 118–132 | Core ownership policy table and helper implementations |
| [`skills/ego-browser/SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/SKILL.md) | 75–85 | User-facing documentation mirroring the ownership table |
| [`package/ego-browser/src/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts) | — | Public API exports for all task-space functions |
| [`package/ego-browser/src/env.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/env.ts) | — | Workspace path resolution for task-space metadata storage |

The ownership field is stored in task-space metadata within the workspace directory resolved by [`env.ts`](https://github.com/citrolabs/ego-lite/blob/main/env.ts), making it persistent across agent sessions until explicitly transferred or the space is destroyed.

---

## Summary

- **Three ownership states** govern all Task Spaces in ego-lite: `agent` (full control), `agentDelegatedToUser` (selection allowed, actions blocked), and `user` (claim required)
- **Claim requirement**: Any operation beyond selection on a `user`-owned space requires explicit `claimTaskSpace` call
- **Delegation pattern**: Use `handOffTaskSpace` to safely transfer control for human intervention, then `claimTaskSpace` to resume
- **Error vs. skip behavior**: Helpers either throw errors or return skipped results rather than silently failing on permission violations

---

## Frequently Asked Questions

### What happens if I try to use browser actions on an agentDelegatedToUser space?

The native bridge blocks real browser actions while allowing selection operations. You can call `switchTaskSpace` or `takeOverTaskSpace` to focus the space, but navigation, form submission, or DOM manipulation helpers will fail until you successfully call `claimTaskSpace` to return ownership to `agent`.

### How do I detect the current ownership of a Task Space?

The ownership field is included in task space metadata. After calling helpers like `claimTaskSpace` or `handOffTaskSpace`, inspect the returned object—the `ownership` property reflects the updated state. The raw metadata is stored in the workspace directory resolved by [`src/env.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/env.ts).

### Can a user-owned space become agent-owned without the agent calling claimTaskSpace?

No. The ownership transition from `user` to `agent` requires an explicit `claimTaskSpace` call. This design prevents accidental agent takeover of spaces the user created for personal browsing. The native bridge rejects any helper that would modify browser state on a `user`-owned space.

### What is the difference between handOffTaskSpace and completeTaskSpace with keep: true?

`handOffTaskSpace` actively transfers ownership from `agent` to `agentDelegatedToUser`, enabling human intervention. `completeTaskSpace({ keep: true })` attempts to finalize a task while keeping the browser tab open—if the space is already `user`-owned, it skips and returns a skipped result rather than throwing.