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:
isEgoUserControlErrorreturnstrue→ returnfalseto 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
waitForAgentControlpauses agent execution until the user returns control of a task space- Polling mechanism: Probes via
ego.snapshotat configurable intervals, detecting agent control restoration without race conditions - Handoff pairing: Use after
handOffTaskSpaceto 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →