How an Agent Hands Off a Task Space to a User in ego-lite
TLDR: In the ego-lite framework, an agent transfers control of a browsing context back to the user by calling the handOffTaskSpace helper from package/ego-browser/src/helpers.ts, which validates the runtime, resolves the target space, checks ownership, and invokes the low-level ego.handOffTaskSpace() method.
Ego-lite, an open-source project from the citrolabs/ego-lite repository, defines a task space as an isolated browsing context that can be owned by either the agent or the user. When the agent finishes its work in a task space and needs to return control to the user, it executes a hand-off process that involves several validation and switching steps. This article walks through the exact implementation, file paths, and code patterns used to achieve this transition.
Understanding Task Space Ownership in Ego-lite
Before examining the hand-off process itself, it's important to understand how ego-lite tracks control. Each task space maintains an ownership property that takes one of two values: "agent" or "user". This distinction drives the entire hand-off flow.
- When
ownership === "agent", the agent is the active controller of that browsing context and can hand it off. - When
ownership === "user", the user already controls the space, so a hand-off is unnecessary and skips gracefully.
The handOffTaskSpace helper is the public API surface that agents call to transfer control. It lives in package/ego-browser/src/helpers.ts, alongside supporting utilities like findTaskSpace and selectTaskSpace.
The Step-by-Step Hand-Off Process
The hand-off process in package/ego-browser/src/helpers.ts follows a precise sequence of six steps, each with a specific purpose.
1. Validate the Runtime Environment
The first step checks that the global ego object exists and that it implements the low-level handOffTaskSpace method. Without this, the helper refuses to proceed and throws an error.
const ego = globalThis.ego;
if (!ego || typeof ego.handOffTaskSpace !== "function") {
throw new Error("handOffTaskSpace requires ego.handOffTaskSpace");
}
This guard ensures the closed-source ego runtime is in place before any operations attempt to run.
2. Resolve the Target Task Space
If the caller supplies a name or numeric ID for a specific task space, the helper resolves it using findTaskSpace. This utility queries the known spaces via listTaskSpaces() and matches the input by name or numeric ID. If no argument is provided, the helper operates on the currently active space.
3. Check Ownership and Short-Circuit
Once the target space is resolved, the helper inspects its ownership property. If the space is already user-owned, the function returns immediately with a skipped result, preventing unnecessary work.
if (match.ownership === "user") {
return { done: false, skipped: "user-owned" as const };
}
This guard ensures that handing off an already-user-controlled space is a no-op.
4. Select the Target Space
For agent-owned spaces, the helper activates the target context using selectTaskSpace, which wraps the low-level ego.useTaskSpace call and ensures the correct numeric ID is used.
await selectTaskSpace(ego, match, "handOffTaskSpace");
This step makes the agent the active controller of the context in preparation for the hand-off.
5. Invoke the Runtime Hand-Off
The core hand-off call happens here. The helper calls ego.handOffTaskSpace() directly. Internally, the runtime:
- Hides the agent overlay UI
- Releases the CDP (Chrome DevTools Protocol) session for user interaction
- Returns control of the browsing context to the human
Errors are normalized through assertNoEgoError, which converts any runtime failure into a standard JavaScript exception.
assertNoEgoError(await ego.handOffTaskSpace(), "handOffTaskSpace");
6. Report Success
Finally, the helper returns { done: true } to signal that control has successfully transferred to the user.
Key Architectural Components
The hand-off process relies on several distinct components in the ego-lite codebase. Here's a breakdown of each:
| Component | Purpose | Source Location |
|---|---|---|
handOffTaskSpace |
Public helper that orchestrates the hand-off process | package/ego-browser/src/helpers.ts |
findTaskSpace |
Resolves a task space by name or numeric ID from listTaskSpaces() |
Same file |
selectTaskSpace |
Wraps ego.useTaskSpace with proper numeric ID handling |
Same file |
ego.handOffTaskSpace |
Low-level runtime implementation that hides the agent UI and gives the browser to the user | Provided by the closed-source ego runtime |
assertNoEgoError |
Normalizes runtime errors into JavaScript exceptions | Same file |
The shared state that drives listTaskSpaces and other helpers lives in package/ego-browser/src/state.ts, holding the singleton runtime state used across the task-space system.
Code Examples: Handing Off in Practice
The following examples show how to use handOffTaskSpace in real agent code.
Basic Hand-Off (Current Task Space)
The simplest case hangs off control of the current task space with no arguments:
import { handOffTaskSpace } from "ego-browser";
await handOffTaskSpace();
// → { done: true } // control now belongs to the user
Hand-Off a Specific Space by Name
You can target a named visibility with handOffTaskSpace("checkout-page"):
import { handOffTaskSpace } from "ego-browser";
const result = await handOffTaskSpace("checkout-page");
// result === { done: true } // if the space was agent-owned
// result === { done: false, skipped: "user-owned" } // if already user-owned
Handling the Skipped Case
When the target space is already user-owned, the helper returns a skipped flag. You can handle this explicitly:
const { done, skipped } = await handOffTaskSpace(42);
if (!done && skipped === "user-owned") {
console.log("The user already controls this space; nothing to hand off.");
}
Key Source Files in the Ego-lite Repository
For developers looking to explore or contribute to this code, the primary files are:
package/ego-browser/src/helpers.ts— Implements the public helperhandOffTaskSpaceplus supporting utilities (findTaskSpace,selectTaskSpace,assertNoEgoError).package/ego-browser/src/taskspace-e2e.test.mjs— End-to-end tests that verify hand-off behavior across real task spaces.package/ego-browser/src/state.ts— Holds the singleton runtime state used bylistTaskSpacesand other helpers.README.md(repo root) — High-level description of the ego-lite architecture and usage.
Summary
The agent-to-user hand-off process in ego-lite is a well-structured, defensively coded flow that respects task-space ownership and runtime capability.
- The public
handOffTaskSpacehelper inpackage/ego-browser/src/helpers.tsorchestrates the entire transition. - The helper validates the runtime's existence, resolves the target space via
findTaskSpace, and checks ownership before switching. - User-owned spaces short-circuit with a
skipped: "user-owned"result, avoiding needless operations. - The low-level
ego.handOffTaskSpacecall hides the agent overlay, releases the CDP session, and returns browser control to the user. - Errors are normalized via
assertNoEgoError, ensuring consistent exception handling.
Frequently Asked Questions
What happens if the task space is already user-owned?
How does findTaskSpace resolve a task space in ego-lite?
Where is the handOffTaskSpace helper implemented?
Is the hand-off process asynchronous?
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 →