-
Notifications
You must be signed in to change notification settings - Fork 24
Open
Description
Problem
When a new browser window is created, there's a race condition between:
- The controller extension sending
window_createdWebSocket message to register ownership - HTTP requests from the agent arriving at the server for that window
The HTTP request often arrives before the WebSocket message is processed, causing:
- Requests to route to the wrong window (falls back to
primaryClientId) - Multi-profile support to break
- Scheduled jobs executing in incorrect windows
Current Workaround
A hacky 1-second delay is used in scheduledJobRuns.ts:
// FIXME: Race condition - the controller-ext extension sends a window_created
// WebSocket message to register window ownership, but our HTTP request may arrive
// at the server before that registration completes. This delay is a temporary fix.
await new Promise((resolve) => setTimeout(resolve, 1000))This causes poor UX and doesn't guarantee the race is resolved.
Affected Files
apps/server/src/browser/extension/bridge.ts(line 143-147)apps/agent/entrypoints/background/scheduledJobRuns.ts(line 116-120)
Proposed Solution
Implement async window registration with polling in ControllerBridge.sendRequest():
- When a request arrives with a
windowIdthat's not yet registered - Poll/wait for up to 500ms for the ownership registration to complete
- Only fall back to
primaryClientIdafter timeout - Remove the hacky 1-second delay from
scheduledJobRuns.ts
Implementation Approach
// In ControllerBridge.sendRequest()
async waitForWindowOwnership(windowId: number, timeoutMs: number = 500): Promise<string | null> {
const startTime = Date.now()
const pollInterval = 50 // ms
while (Date.now() - startTime < timeoutMs) {
const ownerClientId = this.windowOwnership.get(windowId)
if (ownerClientId && this.clients.has(ownerClientId)) {
return ownerClientId
}
await new Promise(resolve => setTimeout(resolve, pollInterval))
}
return null // Timeout - fall back to primary
}Then in sendRequest():
let targetClientId = this.primaryClientId
if (windowId !== undefined) {
const ownerClientId = await this.waitForWindowOwnership(windowId, 500)
if (ownerClientId) {
targetClientId = ownerClientId
} else {
this.logger.warn('Window ownership timeout, using primary', { windowId })
}
}Benefits
- Proper multi-window/multi-profile support
- Removes hacky delays
- Better UX for scheduled jobs
- More reliable window routing
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels