How the Task Space Ownership Model Works in ego-browser

The ego-browser task space ownership model isolates browsing work into separate browser contexts marked as either agent-owned or user-owned, enforcing strict permission checks in src/helpers.ts to prevent the AI from unintentionally controlling user-initiated sessions.

The task space ownership model is the core security mechanism in citrolabs/ego-lite that determines whether the AI agent or the human user controls a specific browser context. Each task space carries an ownership property set to either "agent" or "user", which governs which helper functions can manipulate that space. This design ensures that automated scripts never hijack manually opened pages while allowing seamless collaboration through explicit hand-off and claim operations.

What Is the Task Space Ownership Model?

The model partitions browser instances into discrete task spaces, each marked with an ownership flag. When the AI creates a space via newTaskSpace(), the ownership is automatically set to "agent", granting the script full control over creation, switching, and termination. User-owned spaces originate from manual browser interactions, requiring the agent to explicitly claim the space before any automated manipulation. This boundary prevents agents from accidentally closing tabs that the user is actively working on or taking over private browsing sessions.

Ownership Validation in src/helpers.ts

All ownership logic is centralized in src/helpers.ts (lines 23–114). The system validates the ownership flag before executing sensitive operations, throwing descriptive errors when an agent attempts to modify user-controlled contexts.

Switching Between Task Spaces

The switchTaskSpace() function strictly validates ownership before selecting a space. If the target is user-owned, the operation throws an error to prevent unauthorized context switches:

const space = await findTaskSpace(nameOrId);
if (!isAgentOwned(space.ownership)) {
  throw new Error(
    `switchTaskSpace requires an agent‑owned task space, got ownership ${JSON.stringify(space.ownership)}`
  );
}

(see lines 58–62 of src/helpers.ts)

Creating Agent-Owned Spaces

When initializing a new context, newTaskSpace() automatically marks the space as agent-owned and persists it to the workspace:

const created = normalizeTaskSpace(
  assertNoEgoError(await ego.createTaskSpace(name), "newTaskSpace")
);
taskSpaceNumericId(created, "newTaskSpace");
return selectTaskSpace(ego, created, "newTaskSpace");

(see lines 71–84 of src/helpers.ts)

Conditional Reuse with useOrCreateTaskSpace

The useOrCreateTaskSpace() helper prefers agent-owned spaces but allows read-only access to user-owned contexts without transferring control:

if (isAgentOwned(existing.ownership)) {
  return selectTaskSpace(globalThis.ego, existing, "useOrCreateTaskSpace");
}
if (existing.ownership === "user") {
  // select it as‑is; user stays in control
  return selectTaskSpace(globalThis.ego, existing, "useOrCreateTaskSpace");
}

(see lines 93–114 of src/helpers.ts)

Claiming User-Owned Spaces

To transfer a user-owned space to the agent, claimTaskSpace() updates the ownership flag after lookup:

const space = await findTaskSpace(nameOrId);
const claimed = normalizeTaskSpace(
  assertNoEgoError(await ego.claimTaskSpace(id, space.name), op)
);

(see lines 24–27 and 34–42 of src/helpers.ts)

Completing and Closing Workspaces

The completeTaskSpace() function respects ownership during cleanup. With keep: false, it automatically claims user-owned spaces before closing; with keep: true, it skips user-owned spaces to prevent accidental termination of active user work:

if (options.keep) {
  if (match.ownership === "user") {
    return { done: false, skipped: "user‑owned" };
  }
  await selectTaskSpace(ego, match, "completeTaskSpace");
  await ego.completeTaskSpace();
} else {
  if (match.ownership === "user") {
    await claimResolvedTaskSpace(match, "completeTaskSpace");
  }
  await ego.closeTaskSpace();
}

(see lines 71–99 of src/helpers.ts)

Handing Off Control to Users

The handOffTaskSpace() function returns control to the human user. It short-circuits as a no-op if the space is already user-owned:

if (match.ownership === "user") {
  return { done: false, skipped: "user‑owned" };
}
await selectTaskSpace(ego, match, "handOffTaskSpace");
await ego.handOffTaskSpace();

(see lines 23–36 of src/helpers.ts)

Practical Task Space Workflows

The following patterns demonstrate how the ownership model governs typical interactions between the agent and user:

// 1️⃣ Create a new agent‑owned task space
const myTask = await taskSpaces.new('research‑task');
// → { id: 12, ownership: 'agent', … }

// 2️⃣ Switch to an existing agent‑owned space
await taskSpaces.switch(myTask.id);   // throws if the space is user‑owned

// 3️⃣ Claim a user‑owned space before working on it
await taskSpaces.claim('user‑opened‑page');   // ownership becomes 'agent'

// 4️⃣ Finish work, closing the space (keep:false) or handing it to the user (keep:true)
await taskSpaces.complete(myTask.id, { keep: false });  // closes and discards
await taskSpaces.complete(myTask.id, { keep: true });   // leaves page open for user

// 5️⃣ Hand off control to the user (no‑op if already user‑owned)
await taskSpaces.handOff(myTask.id);

// 6️⃣ Take over a space after a hand‑off
await taskSpaces.takeOver(myTask.id);

Supporting Files

While src/helpers.ts contains the core logic, the task space system relies on additional modules:

  • src/format.ts – Documents the public helper signatures (e.g., taskSpaces.new, taskSpaces.switch) and type definitions for the API.
  • src/env.ts – Resolves the workspace directory where task space metadata persists and manages runtime configuration paths.

Summary

  • The task space ownership model assigns every browser context an ownership flag of either "agent" or "user" to prevent unauthorized automation.
  • Agent-owned spaces allow full CRUD operations via newTaskSpace(), switchTaskSpace(), and completeTaskSpace() without additional authorization checks.
  • User-owned spaces require explicit claiming via claimTaskSpace() before the agent can modify them, ensuring manual browsing sessions remain under human control.
  • The handOffTaskSpace() and takeOverTaskSpace() functions enable safe context switching between AI and human operators without data loss.
  • All permission checks are centralized in src/helpers.ts (lines 23–114), throwing descriptive errors when ownership requirements are violated.

Frequently Asked Questions

What happens if the agent tries to switch to a user-owned task space?

According to the source code in src/helpers.ts (lines 58–62), the switchTaskSpace() function throws an explicit Error stating that the operation requires an agent-owned space. The agent must first call claimTaskSpace() to transfer ownership before switching contexts.

Can the agent close a user-owned task space without claiming it?

No. As implemented in src/helpers.ts (lines 71–99), calling completeTaskSpace() with keep: false on a user-owned space automatically invokes claimResolvedTaskSpace() to transfer ownership before closing. With keep: true, the operation returns { done: false, skipped: "user‑owned" } to preserve the user's active session.

What is the difference between handOffTaskSpace() and takeOverTaskSpace()?

handOffTaskSpace() transfers ownership from the agent back to the user, returning control of the browser context. It is a no-op if the space is already user-owned (lines 23–36). takeOverTaskSpace() allows the agent to reclaim control after a hand-off, forcing the agent overlay back onto the space without requiring manual user intervention.

Where is the ownership state persisted in the ego-browser architecture?

While src/helpers.ts manages the permission logic, src/env.ts defines the workspace directory where task space metadata is stored. The ownership flag is stored on each task space object and validated in-memory by the helper functions before any state-changing operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →