How to Implement `waitForAgentControl` for User Handoff Synchronization in ego‑browser

Use waitForAgentControl to pause agent execution until a user returns control of a browsing session after a handoff in ego‑browser's task space system.

The ego-browser runtime isolates automation scripts inside task spaces—dedicated browsing contexts with discrete ownership states. When an agent relinquishes control via handOffTaskSpace, the underlying ego bindings transition the overlay to user‑control mode. The waitForAgentControl primitive provides the synchronization mechanism to resume automation once the user completes their manual interaction.

How waitForAgentControl Works Internally

The implementation resides in package/ego-browser/src/helpers.ts and follows a polling‑based detection pattern. Here is the precise execution flow as implemented in the citrolabs/ego‑lite source code.

Step 1: Task Space Selection

The helper first invokes selectTaskSpaceIfProvided to ensure subsequent operations target the correct isolated context:

await selectTaskSpaceIfProvided(ego, nameOrId, "waitForAgentControl");

Source: [helpers.ts line 398](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts#L398)

Step 2: Agent Control Probing

The private probeAgentControl function performs a minimal snapshot test using ego.snapshot({maxResultLength: 1}):

  • Success: Snapshot returns data → agent has control → resolve immediately
  • User‑control error: isEgoUserControlError returns true → return false to continue polling
  • Other errors: Bubble up and abort the wait
async function probeAgentControl() { … }

Source: [helpers.ts lines 564‑572](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts#L564-L572)

Step 3: Configurable Polling Loop

The main loop in waitForAgentControl repeatedly probes at a configurable interval (default: 20 seconds) until success or timeout (default: 600 seconds) expiration:

while (true) {
    if (await probeAgentControl()) return;
    if (Date.now() >= deadline) throw new Error(`waitForAgentControl timed out after ${timeout}s`);
    await waits.waitForTimeout(interval * 1000);
}

Source: [helpers.ts lines 580‑607](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts#L580-L607)

Step 4: Handoff Integration

After handOffTaskSpace completes, the user operates freely. When ready, the UI or external script invokes ego.takeOverTaskSpace(), restoring the agent overlay. The next probeAgentControl iteration succeeds, unblocking the waiting script.

Practical Implementation Patterns

Basic Handoff and Wait Sequence

This pattern demonstrates the complete lifecycle: automation → handoff → wait → resumption:

// Acquire or create a task space for the workflow
const ts = await taskSpaces.useOrCreate('order-456');

// Execute automated steps
await taskSpaces.click('#add-to-cart');
await taskSpaces.click('#checkout');

// Transfer control to the user for manual address entry
await taskSpaces.handOffTaskSpace();   // Returns {done: true}

// Block execution until user clicks "Take over" in the UI
await taskSpaces.waitForAgentControl(ts.id, { interval: 10, timeout: 300 });

// Resume automation after control restoration
await taskSpaces.click('#confirm-order');
await taskSpaces.completeTaskSpace(ts.id, { keep: true });

Source: [handOffTaskSpace implementation, helpers.ts lines 326‑339](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts#L326-L339)

Explicit Task Space Targeting

When working with spaces created elsewhere or referenced by ID:

// Assume a user-owned space exists from prior interaction
const userSpaceId = 12;

// Agent hands off (no-op if already user-owned)
await taskSpaces.handOffTaskSpace(userSpaceId);

// Block until user manually triggers control return
await taskSpaces.waitForAgentControl(userSpaceId);

Custom Polling Configuration

Adjust sensitivity for latency‑tolerant or time‑constrained scenarios:

await taskSpaces.waitForAgentControl('profile-edit', {
  interval: 5,   // Probe every 5 seconds
  timeout: 120,  // Maximum 2 minutes waiting
});

Why Polling Is the Required Approach

The ego runtime lacks an event‑driven "ownership change" subscription. The read‑only poll‑until‑snapshot‑succeeds pattern eliminates race conditions: waitForAgentControl never invokes takeOverTaskSpace itself, preserving user autonomy over when to return control. This design prevents agent overreach while maintaining deterministic synchronization.

Key Source Files

File Purpose Location
src/helpers.ts Core implementations of handOffTaskSpace, takeOverTaskSpace, waitForAgentControl, and probeAgentControl [helpers.ts](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/helpers.ts)
src/format.ts Public API surface registration (taskSpaces.waitForAgentControl entry) [format.ts](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/format.ts)
src/index.ts Runtime exposure via installEgoSdk and task‑space façade registration [index.ts](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/index.ts)
src/driver/waits.ts waits.waitForTimeout implementation for polling delays [waits.ts](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/driver/waits.ts)
src/state.ts Singleton runtime state and task‑space cache [state.ts](https://github.com/citrolabs/ego-lite/blob/main/package/ego-browser/src/state.ts)

Summary

  • waitForAgentControl pauses agent execution until the user returns control of a task space
  • Polling mechanism: Probes via ego.snapshot at configurable intervals, detecting agent control restoration without race conditions
  • Handoff pairing: Use after handOffTaskSpace to implement seamless human‑in‑the‑loop workflows
  • Read‑only safety: Never triggers takeOverTaskSpace, ensuring user-initiated control transitions only
  • Configurable timeouts: Default 20‑second interval, 600‑second timeout; override per operation requirements

Frequently Asked Questions

What happens if the user never returns control?

waitForAgentControl throws a timeout error when the configured timeout duration elapses. The default is 600 seconds (10 minutes). Catch this error to implement fallback logic or escalation workflows.

Can multiple agents wait on the same task space?

The ego runtime serializes access; only one agent context operates per task space. Concurrent waitForAgentControl calls from different agent instances would queue behind active ownership, though this pattern is discouraged—coordinate via external state instead.

How does waitForAgentControl differ from takeOverTaskSpace?

waitForAgentControl is passive—it polls until detecting that something else (typically a UI action) has restored agent control. takeOverTaskSpace is active—it immediately attempts to seize control, failing if conditions aren't met. Use waitForAgentControl for cooperative handoffs; use takeOverTaskSpace for explicit control assertions.

Is there a webhook or callback alternative to polling?

No. The ego runtime as implemented in citrolabs/ego‑lite does not expose ownership change events. The polling pattern in waitForAgentControl is the architecturally prescribed solution—minimal overhead (single‑byte snapshot probes) with guaranteed eventual consistency.

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 →