Repository navigation
Description
Hi! I ran into a routing race when multiple agents use the same persistent browser-harness-js REPL / WebSocket concurrently.
Problem
Session.use(targetId) attaches with flatten:true, then stores the returned CDP session in the single mutable activeSessionId. Every later non-browser-level command implicitly uses that value.
That is convenient for one agent, but unsafe for concurrent callers sharing the REPL. For example:
- Agent A calls
use(tabA)-> active session = A - Agent B calls
use(tabB)-> active session = B - Agent A sends
Runtime.evaluate,DOM.*,Input.*, etc. - A's command is now routed to tab B
This can also happen inside one long /eval request if it awaits between operations and another request changes activeSessionId meanwhile.
The underlying CDP flat-session model already supports multiple attached sessions over one WebSocket: each command can carry its own sessionId. Keeping long-lived sessions is important too (Network/Console/DOM event monitoring, etc.), so simply attach/detach on every CLI invocation would lose one of the useful properties of the persistent REPL.
There is a related event-side issue: onEvent exposes the event sessionId, but waitFor(method, predicate, timeout) currently does not filter by session. With multiple long-lived target sessions on one WebSocket, a same-named event from another tab could satisfy the wait.
Desired behavior
It would be useful to keep:
- one persistent browser WebSocket
- multiple long-lived CDP target sessions on that WebSocket
- full/raw typed CDP surface (no high-level browser abstraction)
use(targetId)as a backwards-compatible single-agent convenience API if desired
while adding a session-scoped routing primitive so concurrent agents never depend on one global active-session pointer.
Conceptually, something like attach(targetId) / forSession(sessionId) could return a scoped view of the same generated CDP domains, permanently bound to that sessionId. Events / waits on that scoped view could likewise filter by sessionId. The exact API naming is flexible; the important property is that routing is carried by the scoped handle / call, not mutable global REPL state.
This would also let an agent explicitly keep and reuse a long-lived CDP session for monitoring, while another agent independently uses another target over the same WebSocket.
Would you be open to this direction? I'm happy to prepare a focused PR with tests for two concurrent target sessions if it fits the project's intended model.
Activity