Task Space Ownership States in Ego-Browser: A Complete Guide
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 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:
| 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, 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:
// 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:
// 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:
// 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– High-level documentation of task-space usage and ownership policysrc/helpers.ts– API implementations foruseOrCreateTaskSpace,handOffTaskSpace, andclaimTaskSpacesrc/state.ts– Runtime state management including theownershipfieldsrc/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:
// 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, anduser - 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, throwing errors for unauthorized access - End-to-end tests in
taskspace-e2e.test.mjsvalidate 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, 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →