src/vs/platform/agentHost/architecture.md
Keep this document in sync with the code. If you change the IPC contract, add new event types, modify the process lifecycle, or restructure files, update this document as part of the same change.
For design decisions, see design.md. For the client-server state protocol, see protocol.md. For chat session wiring, see sessions.md.
The agent host runs as either an Electron utility process (desktop) or a standalone WebSocket server (headless / development). It hosts agent backends (CopilotAgent, MockAgent) and exposes session state to clients through two communication layers:
AgentHostServiceClient proxies IAgentService methods and forwards action/notification events.In both modes, the server holds an authoritative state tree (SessionStateManager) mutated by actions flowing through pure reducers. Raw IAgentProgressEvents from agent backends are mapped to state actions via agentEventMapper.ts.
The entire feature is gated behind the chat.agentHost.enabled setting (default false). When disabled, the process is not spawned and no agents are registered.
+--------------------------------------------------------------+
| Renderer Window (Desktop) |
| |
| AgentHostContribution (discovers agents via listAgents()) |
| +-- per agent: SessionHandler, ListCtrl, LMProvider |
| +-- SessionClientState (write-ahead reconciliation) |
| +-- stateToProgressAdapter (state -> IChatProgress[]) |
| |
| AgentHostServiceClient (IAgentHostService singleton) |
| +-- ProxyChannel over delayed MessagePort |
| (revive() applied to event payloads) |
+---------------- MessagePort (direct) -------------------------+
| Agent Host Utility Process (agentHostMain.ts) |
| -- or -- |
| Standalone Server (agentHostServerMain.ts) |
| |
| SessionStateManager (server-authoritative state tree) |
| +-- rootReducer / sessionReducer |
| +-- action envelope sequencing |
| |
| ProtocolServerHandler (JSON-RPC routing, broadcasts) |
| +-- per-client subscriptions, replay buffer |
| |
| Agent registry (Map<AgentProvider, IAgent>) |
| +-- CopilotAgent (id='copilot') |
| | +-- CopilotClient (@github/copilot-sdk) |
| +-- ScriptedMockAgent (id='mock', opt-in via flag) |
| |
| agentEventMapper.ts |
| +-- IAgentProgressEvent -> ISessionAction mapping |
+---------------- UtilityProcess lifecycle ---------------------+
| Main Process (Desktop only) |
| |
| ElectronAgentHostStarter (IAgentHostStarter) |
| +-- Spawns utility process, brokers MessagePort to windows |
| AgentHostProcessManager |
| +-- Lazy start on first window connection, crash recovery |
+---------------------------------------------------------------+
src/vs/platform/agentHost/
+-- common/
| +-- agent.ts # IAgentHostStarter, IAgentHostConnection (starter contract)
| +-- agentService.ts # IAgent, IAgentService, IAgentHostService interfaces,
| # IPC data types, IAgentProgressEvent union,
| # AgentSession namespace (URI helpers),
| # AgentHostEnabledSettingId
| +-- state/
| +-- sessionState.ts # Immutable state types (RootState, SessionState, Turn, etc.)
| +-- sessionActions.ts # Action discriminated union + ActionEnvelope + Notifications
| +-- sessionReducers.ts # Pure reducer functions (rootReducer, sessionReducer)
| +-- sessionProtocol.ts # JSON-RPC message types, request params/results
| +-- sessionCapabilities.ts # Version constants + ProtocolCapabilities
| +-- sessionClientState.ts # Client-side state manager with write-ahead reconciliation
| +-- sessionTransport.ts # IProtocolTransport / IProtocolServer abstractions
| +-- versions/
| +-- v1.ts # v1 wire format types (tip -- editable, compiler-enforced compat)
| +-- versionRegistry.ts # Compile-time compat checks + runtime action->version map
+-- electron-browser/
| +-- agentHostService.ts # AgentHostServiceClient (renderer singleton, direct MessagePort)
+-- electron-main/
| +-- electronAgentHostStarter.ts # Spawns utility process, brokers MessagePort connections
+-- node/
| +-- agentHostMain.ts # Entry point inside the Electron utility process
| +-- agentHostServerMain.ts # Entry point for standalone WebSocket server
| +-- agentService.ts # AgentService: dispatches to registered IAgent providers
| +-- agentHostService.ts # AgentHostProcessManager: lifecycle, crash recovery
| +-- agentEventMapper.ts # Maps IAgentProgressEvent -> ISessionAction
| +-- sessionStateManager.ts # Server-authoritative state tree + reducer dispatch
| +-- protocolServerHandler.ts # JSON-RPC routing, client subscriptions, action broadcast
| +-- webSocketTransport.ts # WebSocket IProtocolTransport + IProtocolServer impl
| +-- nodeAgentHostStarter.ts # Node.js (non-Electron) starter
| +-- copilot/
| +-- copilotAgent.ts # CopilotAgent: IAgent backed by Copilot SDK
| +-- copilotSessionWrapper.ts
| +-- copilotToolDisplay.ts # Copilot-specific tool name -> display string mapping
+-- test/
+-- (test files)
src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/
+-- agentHostChatContribution.ts # AgentHostContribution: discovers agents, registers dynamically
+-- agentHostLanguageModelProvider.ts # ILanguageModelChatProvider for SDK models
+-- agentHostSessionHandler.ts # AgentHostSessionHandler: generic, config-driven
+-- agentHostSessionListController.ts # Lists persisted sessions from agent host
+-- stateToProgressAdapter.ts # Converts protocol state -> IChatProgress[] for chat UI
src/vs/workbench/contrib/chat/electron-browser/
+-- chat.contribution.ts # Desktop-only: registers AgentHostContribution
Sessions are identified by URIs where the scheme is the provider name and the path is the raw session ID: copilot:/<uuid>. Helper functions in the AgentSession namespace:
| Helper | Purpose |
|---|---|
AgentSession.uri(provider, rawId) | Create a session URI |
AgentSession.id(session) | Extract raw session ID from URI |
AgentSession.provider(session) | Extract provider name from URI scheme |
The renderer uses UI resource schemes (agent-host-copilot) for session resources. The AgentHostSessionHandler converts these to provider URIs before IPC calls.
The IAgent interface in agentService.ts is what each agent backend implements. It fires IAgentProgressEvents (raw SDK events) and exposes methods for session management:
| Method | Description |
|---|---|
createSession(config?) | Create a new session (returns session URI) |
sendMessage(session, prompt, attachments?) | Send a user message |
abortSession(session) | Abort the current turn |
respondToPermissionRequest(requestId, approved) | Grant/deny a permission |
getDescriptor() | Return agent metadata |
listModels() | List available models |
listSessions() | List persisted sessions |
setAuthToken(token) | Set auth credentials |
changeModel?(session, model) | Change model for a session |
The server maps raw IAgentProgressEvents to state actions via agentEventMapper.ts, dispatches them through SessionStateManager, and broadcasts to subscribed clients. See protocol.md for the full JSON-RPC specification, action types, state model, and versioning.
AgentHostServiceClient in electron-browser/agentHostService.ts connects to the utility process via MessagePort and proxies IAgentService methods. It also forwards action envelopes and notifications as events so the renderer can feed them into SessionClientState.
The chat.agentHost.enabled setting (default false) controls the entire feature:
app.ts): skips creating ElectronAgentHostStarter + AgentHostProcessManagerAgentHostServiceClient): skips MessagePort connectionAgentHostContribution): returns early without discovering or registering agentsElectronAgentHostStarter is created in app.ts (if setting enabled) and handed to AgentHostProcessManager.vs/platform/agent/node/agentHostMain.acquirePort('vscode:createAgentHostMessageChannel', ...).The agent host can also run as a standalone WebSocket server (agentHostServerMain.ts):
node out/vs/platform/agentHost/node/agentHostServerMain.js [--port <port>] [--enable-mock-agent]
This mode creates a WebSocketProtocolServer and ProtocolServerHandler directly without Electron. Useful for development and headless scenarios.
On startup (if the setting is enabled), AgentHostContribution calls listAgents() to discover available backends from the agent host process. Each returned IAgentDescriptor contains:
| Field | Purpose |
|---|---|
provider | Agent provider ID ('copilot') |
displayName | Human-readable name for UI |
description | Description string |
requiresAuth | Whether the renderer should push a GitHub auth token |
For each descriptor, the contribution dynamically registers:
agent-host-{provider})AgentHostSessionHandler configured with the descriptor's metadataAgentHostSessionListController for the session sidebarAgentHostLanguageModelProvider for the model pickerrequiresAuth is true)Only agents with requiresAuth: true (currently Copilot) get auth wiring:
IAgentHostService.setAuthToken(token)CopilotAgent passes it to CopilotClient({ githubToken }) on next client creationAgentHostProcessManager monitors the utility process exit. On unexpected termination, it automatically restarts (up to 5 times).
| File | Purpose |
|---|---|
build/next/index.ts | Agent host entry point in esbuild config |
build/buildfile.ts | Agent host entry point in legacy bundler config |
build/gulpfile.vscode.ts | Strip wrong-arch copilot packages; ASAR unpack copilot binaries |
build/.moduleignore | Strip unnecessary copilot prebuilds/ripgrep/clipboard |
build/darwin/create-universal-app.ts | macOS universal binary support for copilot CLI |
build/darwin/verify-macho.ts | Skip copilot binaries in Mach-O verification |
| Component | Pattern | Key Difference |
|---|---|---|
| Pty Host | Singleton utility process, MessagePort, lazy start, crash recovery | Also has heartbeat monitoring and reconnect logic |
| Shared Process | Singleton utility process, MessagePort | Much heavier, hosts many services |
| Extension Host | Per-window utility process, custom RPCProtocol | Uses custom RPC, not standard channels |