# How Task Space Operations Handle Ownership Transfers in ego-lite

> Learn how ego-lite task space operations like claim and complete manage ownership transfers. Understand agent control and user-owned contexts for efficient task management.

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

---

**In ego-lite, `claimTaskSpace` transfers a user-owned browsing context to agent control, while `completeTaskSpace` conditionally claims or skips user-owned spaces based on the `keep` option before finalizing or closing the task.**

The **ego-lite** framework provides isolated browsing contexts called **task spaces** that enforce strict ownership boundaries between the **agent** and the **user**. Understanding how task space operations work with ownership transfers is essential for building reliable agent skills. This article examines the implementation in `citrolabs/ego-lite` to explain the mechanics behind `claimTaskSpace` and `completeTaskSpace`.

## How Task Space Ownership Works

Task spaces in ego-lite carry an `ownership` field with three possible values:

- `"agent"` — The agent has full control
- `"agentDelegatedToUser"` — The agent temporarily yielded control
- `"user"` — The user owns the browsing context

Ownership changes are mediated through the **native `ego` bridge**, which exposes runtime methods for claiming, selecting, completing, and closing task spaces. The helper functions in [`package/ego-browser/src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts) enforce these policies and handle all bridge interactions.

## Claiming a Task Space with `claimTaskSpace`

The `claimTaskSpace` function transfers ownership from **user to agent**. It resolves the target space by name or ID, then invokes `ego.claimTaskSpace(id, name)` to perform the transfer.

In [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts) (lines 224-236), the implementation follows this pattern:

```typescript
export async function claimTaskSpace(nameOrId) {
  const space = await findTaskSpace(nameOrId);
  return claimResolvedTaskSpace(space, "claimTaskSpace");
}

async function claimResolvedTaskSpace(space, op = "claimTaskSpace") {
  const ego = globalThis.ego;
  if (!ego || typeof ego.claimTaskSpace !== "function") {
    throw new Error(`${op} requires ego.claimTaskSpace`);
  }
  const id = taskSpaceNumericId(space, op);
  const claimed = normalizeTaskSpace(
    assertNoEgoError(await ego.claimTaskSpace(id, space.name), op),
  );
  // ...select the claimed space...
}

```

After claiming, the helper automatically selects the space via `ego.useTaskSpace(id)`, making it the active context for subsequent operations.

## Completing a Task Space with `completeTaskSpace`

The `completeTaskSpace` function handles two scenarios based on the `keep` option:

- **`keep: true`** — Finalizes agent work but leaves the page open for the user
- **`keep: false`** — Closes the space after finishing

Critically, `completeTaskSpace` applies **ownership-aware logic** when encountering user-owned spaces:

| Scenario | Ownership | Behavior |
|----------|-----------|----------|
| `keep: true` + user-owned | `"user"` | Skips operation, returns `{ done: false, skipped: "user-owned" }` |
| `keep: false` + user-owned | `"user"` | **Claims the space first**, then closes it |
| Agent-owned | `"agent"` or `"agentDelegatedToUser"` | Proceeds directly to complete/close |

The implementation in [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts) (lines 274-315):

```typescript
export async function completeTaskSpace(nameOrId, options) {
  // ...validation omitted...
  const match = findMatchingTaskSpace(await listTaskSpaces(), nameOrId);
  if (options.keep) {
    if (match.ownership === "user") {
      return { done: false, skipped: "user-owned" };
    }
    await selectTaskSpace(ego, match, "completeTaskSpace");
    assertNoEgoError(await ego.completeTaskSpace(), "completeTaskSpace");
  } else {
    if (match.ownership === "user") {
      await claimResolvedTaskSpace(match, "completeTaskSpace");
    } else {
      await selectTaskSpace(ego, match, "completeTaskSpace");
    }
    assertNoEgoError(await ego.closeTaskSpace(), "completeTaskSpace");
  }
  return { done: true };
}

```

## Key Implementation Details

### Space Resolution and ID Enforcement

Both operations first resolve the target via `findTaskSpace(nameOrId)`, which supports lookup by **numeric ID** or **human-readable name**. The internal `taskSpaceNumericId` helper ensures the space has a valid numeric ID before any native bridge call—this is a hard requirement for the `ego` runtime.

### Native Bridge Method Reference

According to the ego-lite source code, the native bridge exposes these task space methods:

- `ego.claimTaskSpace(id, name)` — Transfers ownership to agent
- `ego.useTaskSpace(id)` — Selects active task space
- `ego.completeTaskSpace()` — Finalizes agent work (keeps page)
- `ego.closeTaskSpace()` — Closes the browsing context

These are validated at runtime; missing methods throw explicit errors naming the required capability.

### Runtime State Access

Helpers access the native bridge through `globalThis.ego`, managed by [`state.ts`](https://github.com/citrolabs/ego-lite/blob/main/state.ts). This singleton pattern ensures consistent bridge access across all task space operations.

## Practical Code Examples

Claim a user-owned task space and begin agent work:

```typescript
// Transfers ownership → agent, then selects the space
await claimTaskSpace('checkout-flow');

```

Finish work while preserving the page for user review:

```typescript
// If user-owned: no-op with skip notice
// If agent-owned: finalizes and keeps open
await completeTaskSpace('checkout-flow', { keep: true });
// Returns: { done: false, skipped: 'user-owned' } or { done: true }

```

Finish work and close the context:

```typescript
// Claims first if user-owned, then closes
await completeTaskSpace('checkout-flow', { keep: false });
// Returns: { done: true }

```

## Summary

- **Task spaces** carry an `ownership` field determining agent vs. user control
- **`claimTaskSpace`** resolves by name/ID, calls `ego.claimTaskSpace()`, then selects the space via `ego.useTaskSpace()`
- **`completeTaskSpace`** applies conditional logic: skips user-owned spaces when `keep: true`, claims them first when `keep: false`
- **Native bridge methods** in `globalThis.ego` perform the actual ownership transfers and lifecycle operations
- **Enforcement resides in [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts)** (lines 224-236 for claiming, lines 274-315 for completion)

## Frequently Asked Questions

### What happens if I call `claimTaskSpace` on an already agent-owned space?

The native `ego.claimTaskSpace` call still executes; the bridge handles idempotent claims. The helper proceeds with space selection regardless of prior ownership state.

### Can an agent complete a user-owned task space without claiming it?

Only when `keep: true`—and even then, the operation is skipped with `{ done: false, skipped: "user-owned" }`. To actually finish or close a user-owned space, the agent must first claim it.

### Where is the ownership policy documented outside the code?

The [`SKILL.md`](https://github.com/citrolabs/ego-lite/blob/main/SKILL.md) file in `skills/ego-browser/` mirrors the ownership table enforced in [`helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/helpers.ts), providing agent-facing documentation of these rules.

### What validation ensures the native bridge is available?

Each helper checks `globalThis.ego` and the specific method type before invoking—`claimResolvedTaskSpace` explicitly validates `ego.claimTaskSpace` is a function and throws descriptive errors if the bridge is missing or incomplete.