Fix port collisions for Agent Web Socket Server by moving it into singleton#2171
Merged
GeorgeNgMsft merged 18 commits intomainfrom Apr 9, 2026
Merged
Conversation
Set exit code instead of calling exit directly. Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…be a critical exception
…icrosoft/TypeAgent into dev/georgeng/fix_as_easddr_use
…, rather than naive incremental retries
…icrosoft/TypeAgent into dev/georgeng/fix_as_easddr_use
Contributor
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent port-collision crashes when multiple agent-cli clients connect concurrently by (1) sharing the browser agent’s AgentWebSocketServer as a singleton per agent process and (2) switching local view servers to OS-assigned ports (listen(0)) with the bound port reported back to the dispatcher via IPC.
Changes:
- Removes
portBase-based port assignment and instead reserves port0formanifest.localViewagents, with bound ports reported back viaSessionContext.setLocalHostPort(). - Promotes the browser agent’s
AgentWebSocketServer(8081)to a module-level singleton and wires it into browser session contexts. - Updates browser/markdown view server startup IPC to return the bound port (and updates callers to persist it back to the dispatcher).
Reviewed changes
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| ts/packages/shell/src/main/instance.ts | Removes portBase option usage when creating the dispatcher. |
| ts/packages/dispatcher/dispatcher/src/execute/sessionContext.ts | Adds SessionContext.setLocalHostPort() for agents to report OS-assigned ports. |
| ts/packages/dispatcher/dispatcher/src/context/commandHandlerContext.ts | Updates docs/types to remove portBase and document OS-assigned port flow. |
| ts/packages/dispatcher/dispatcher/src/context/appAgentManager.ts | Switches localView “reserved port” to 0 and adds setLocalHostPort() storage. |
| ts/packages/agentSdk/src/agentInterface.ts | Extends SessionContext with setLocalHostPort(port). |
| ts/packages/agents/taskflow/benchmark/run-benchmark.mts | Removes portBase usage in benchmark dispatcher creation. |
| ts/packages/agents/scriptflow/benchmark/run-benchmark.mts | Removes portBase usage in benchmark dispatcher creation. |
| ts/packages/agents/montage/src/route/route.ts | Adds server error handling; still starts the HTTP server for montage local view. |
| ts/packages/agents/montage/src/agent/montageActionHandler.ts | Ensures montage view child process is killed on context close. |
| ts/packages/agents/markdown/src/view/route/service.ts | Reports OS-bound port to parent via IPC and adds startup error handling. |
| ts/packages/agents/markdown/src/agent/markdownActionHandler.ts | Consumes IPC {type:'Success', port} and persists port back via setLocalHostPort(). |
| ts/packages/agents/browser/src/views/server/server.mts | Reports OS-bound port to parent via IPC {type:'Success', port}. |
| ts/packages/agents/browser/src/views/server/core/baseServer.ts | Captures and exposes the bound port after listen, improves startup/runtime error handling. |
| ts/packages/agents/browser/src/agent/websiteMemory.mts | Updates a SessionContext stub to include setLocalHostPort (currently with a type mismatch). |
| ts/packages/agents/browser/src/agent/browserActionHandler.mts | Introduces module-level AgentWebSocketServer singleton and adds closeAgentContext. |
| ts/packages/agents/browser/benchmark/test-webflow-grammar.mts | Removes portBase usage in benchmark dispatcher creation. |
| ts/packages/agentRpc/src/types.ts | Adds setLocalHostPort RPC method to agent RPC surface. |
| ts/packages/agentRpc/src/server.ts | Exposes SessionContext.setLocalHostPort() through RPC. |
| ts/packages/agentRpc/src/client.ts | Implements setLocalHostPort invoke handler to call session context method. |
| ts/docs/architecture/dispatcher.md | Documents OS-assigned local view port flow and setLocalHostPort() reporting. |
| ts/.vscode/settings.json | Adds watcher/search excludes for node_modules and dist. |
Comments suppressed due to low confidence (1)
ts/packages/agents/browser/src/agent/browserActionHandler.mts:552
- closeBrowserContext() stops the AgentWebSocketServer, but the server is a module-level singleton shared by all session contexts. Closing any one session would shut down the shared WebSocket server and break other active sessions. Consider not stopping the singleton here (or add ref-counting / process-lifetime ownership so the server only stops when the agent process exits).
async function closeBrowserContext(
context: SessionContext<BrowserActionContext>,
) {
if (context.agentContext.agentWebSocketServer) {
context.agentContext.agentWebSocketServer.stop();
delete context.agentContext.agentWebSocketServer;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…serContext Agent-Logs-Url: https://github.com/microsoft/TypeAgent/sessions/c3f2ef68-c2f2-4b0d-9658-63f68525eb00 Co-authored-by: GeorgeNgMsft <146492653+GeorgeNgMsft@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The browser agent's AgentWebSocketServer was being instantiated once per session context (initializeAgentContext call). When multiple agent-cli clients connected to the agent-server simultaneously, each triggered a separate initializeAgentContext RPC call on the shared browser agent process, resulting in two new AgentWebSocketServer(8081) calls and an EADDRINUSE crash on the second one.
The root cause is that the browser agent runs as a single out-of-process RPC server (agentProcess.ts) shared across all sessions, but session contexts are created independently per session — there was no deduplication of the port bind.
Fix: promote AgentWebSocketServer to a module-level singleton in browserActionHandler.mts, constructed once when the agent process loads. All session contexts reference the same instance. Construction was also moved from updateAgentContext (called once per action schema enable, raceable) to initializeAgentContext (called once per session, guarded by sessionContextP).