agentHost: support multiple active clients per session#322665
Draft
connor4312 wants to merge 1 commit into
Draft
agentHost: support multiple active clients per session#322665connor4312 wants to merge 1 commit into
connor4312 wants to merge 1 commit into
Conversation
Adopt the agent host protocol update that moves from a single active client
per session to multiple active clients, and make the agent implementations
model and merge them correctly.
Protocol + UI/server:
- SessionState.activeClient? -> activeClients[]; session/activeClientChanged
is split into session/activeClientSet (upsert by clientId) and
session/activeClientRemoved; session/activeClientToolsChanged carries a
clientId.
- The UI adds itself via session/activeClientSet and never removes itself.
- The server removes a client from activeClients (and cancels its in-flight
client tool calls) on unsubscribe, and on reconnect when the client does
not resubscribe to a session where it was still active. Disconnect keeps
the client active during the grace window; the grace timeout removes it and
fails its pending tool calls if it never returns.
IAgent implementations:
- Replace setClientTools/setClientCustomizations with a per-client handle API:
getOrCreateActiveClient(session, {clientId, displayName}) -> IActiveClient
(mutable readonly-array tools/customizations accessors) and
removeActiveClient(session, clientId).
- Add shared node infra ActiveClientToolSet (per-session, clientId-keyed tool
registry with merge-by-name + ownerOf) and adopt it in Copilot, Claude and
Codex so multiple active clients' tools/customizations are stored, merged
(deduped, first-inserted client wins) for the SDK, and tool calls are
stamped to the owning client.
- Guard teardown races: Copilot invalidates an in-flight customization sync on
removeClient; Claude removes tools synchronously and serializes customization
removal through the session sequencer, and prunes cached handles on session
disposal.
Co-authored-by: Copilot <[email protected]>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates VS Code’s agent host protocol and implementation to support multiple active clients per session (instead of a single activeClient), including server-side lifecycle handling (unsubscribe/reconnect/grace) and multi-client tool/customization merging for Copilot, Claude, and Codex.
Changes:
- Protocol/state migration:
activeClient→activeClients[], plus new actionssession/activeClientSetandsession/activeClientRemoved(andclientIdadded tosession/activeClientToolsChanged). - New per-client handle API (
getOrCreateActiveClient/removeActiveClient) and sharedActiveClientToolSetfor merged tool ownership and stamping. - Updated tests across workbench, agent host node, and electron suites to cover multi-client behaviors and reconnect/unsubscribe/grace semantics.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostClientTools.test.ts | Updates lifecycle action naming in tests. |
| src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostChatContribution.test.ts | Migrates workbench contribution tests to activeClients[] and new actions. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts | Workbench handler now upserts active clients and stamps tools per clientId. |
| src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostLocalCustomizations.ts | Documentation updates for activeClientSet. |
| src/vs/sessions/contrib/providers/remoteAgentHost/test/browser/remoteAgentHostSessionsProvider.test.ts | Seeds required activeClients: [] in provider tests. |
| src/vs/sessions/contrib/providers/agentHost/test/browser/localAgentHostSessionsProvider.test.ts | Seeds activeClients and updates customization-change expectations. |
| src/vs/sessions/contrib/providers/agentHost/browser/baseAgentHostSessionsProvider.ts | Detects customization churn across all active clients. |
| src/vs/platform/agentHost/test/node/reducers.test.ts | Seeds activeClients: [] for reducer fixtures. |
| src/vs/platform/agentHost/test/node/protocolServerHandler.test.ts | Adds coverage for unsubscribe/reconnect/grace and tool-call failure behavior. |
| src/vs/platform/agentHost/test/node/protocol/realSdkTestHelpers.ts | Updates shared SDK fixtures to activeClientSet. |
| src/vs/platform/agentHost/test/node/protocol/codexRealSdk.integrationTest.ts | Updates Codex real-SDK integration expectations for new action names. |
| src/vs/platform/agentHost/test/node/mockAgent.ts | Updates mock agent to new IActiveClient handle API. |
| src/vs/platform/agentHost/test/node/customizations/claudeSessionClientCustomizationsModel.test.ts | Adds multi-client merge/removal coverage for Claude customization model. |
| src/vs/platform/agentHost/test/node/copilotAgentSession.test.ts | Switches Copilot tests from ActiveClientState to ActiveClientToolSet. |
| src/vs/platform/agentHost/test/node/copilotAgent.test.ts | Covers multi-client tool merge/ownership and reload semantics in Copilot agent. |
| src/vs/platform/agentHost/test/node/codex/codexMapAppServerEvents.test.ts | Tests contributor stamping via tool ownership resolution. |
| src/vs/platform/agentHost/test/node/clientTools/claudeSessionClientToolsModel.test.ts | Updates Claude client-tools diff/model to multi-client merged view. |
| src/vs/platform/agentHost/test/node/claudeMapSessionEvents.test.ts | Updates mapper tests for owner-resolver callback. |
| src/vs/platform/agentHost/test/node/claudeAgent.test.ts | Migrates Claude agent tests to new per-client handle API. |
| src/vs/platform/agentHost/test/node/agentSideEffects.test.ts | Updates side effects tests for set/remove active client actions. |
| src/vs/platform/agentHost/test/node/agentService.test.ts | Updates agent service tests for activeClients[] initialization. |
| src/vs/platform/agentHost/test/electron-browser/remoteAgentHostProtocolClient.test.ts | Updates electron client tests for active-client set/remove and implicit reads. |
| src/vs/platform/agentHost/test/common/agentSubscription.test.ts | Seeds activeClients: [] in subscription fixtures. |
| src/vs/platform/agentHost/node/protocolServerHandler.ts | Implements reconnect reconciliation + grace/unsubscribe removal for active clients. |
| src/vs/platform/agentHost/node/copilot/copilotSessionLauncher.ts | Updates launch config to use multi-client tool ownership registry. |
| src/vs/platform/agentHost/node/copilot/copilotAgentSession.ts | Stamps tool calls by owning client via ActiveClientToolSet.ownerOf. |
| src/vs/platform/agentHost/node/copilot/copilotAgent.ts | Implements getOrCreateActiveClient/removeActiveClient and multi-client plugin/tool state. |
| src/vs/platform/agentHost/node/codex/codexMapAppServerEvents.ts | Uses ActiveClientToolSet for routing/stamping Codex dynamic tool calls. |
| src/vs/platform/agentHost/node/codex/codexAgent.ts | Implements active-client handle API and multi-client tool merging for Codex. |
| src/vs/platform/agentHost/node/claude/customizations/claudeSessionClientCustomizationsModel.ts | Multi-client customization merge/dedupe keyed by client. |
| src/vs/platform/agentHost/node/claude/clientTools/claudeSessionClientToolsModel.ts | Multi-client client-tools model with merged observable + ownership. |
| src/vs/platform/agentHost/node/claude/claudeSdkPipeline.ts | Switches from single clientId to tool-owner resolver. |
| src/vs/platform/agentHost/node/claude/claudeSdkOptions.ts | Adapts to new consume() return type (merged tools array). |
| src/vs/platform/agentHost/node/claude/claudeSdkMessageRouter.ts | Routes tool ownership via resolver instead of fixed clientId. |
| src/vs/platform/agentHost/node/claude/claudeMapSessionEvents.ts | Stamps client tool calls via owner-resolver callback. |
| src/vs/platform/agentHost/node/claude/claudeAgentSession.ts | Exposes per-client tool/customization mutation + removal APIs. |
| src/vs/platform/agentHost/node/claude/claudeAgent.ts | Implements per-client handle caching and safe removal sequencing. |
| src/vs/platform/agentHost/node/agentSideEffects.ts | Forwards active-client upserts/removals via new handle API. |
| src/vs/platform/agentHost/node/agentService.ts | Initializes session state with activeClients[]. |
| src/vs/platform/agentHost/node/agentHostTelemetryReporter.ts | Adjusts telemetry to report counts across all active clients. |
| src/vs/platform/agentHost/node/activeClientState.ts | Introduces shared ActiveClientToolSet for multi-client tool merge/ownership. |
| src/vs/platform/agentHost/common/state/sessionState.ts | Ensures new session state includes activeClients: []. |
| src/vs/platform/agentHost/common/state/sessionActions.ts | Updates exported action types to set/remove active client. |
| src/vs/platform/agentHost/common/state/protocol/version/registry.ts | Updates action introduction versions for new active-client actions. |
| src/vs/platform/agentHost/common/state/protocol/common/actions.ts | Renames action types and unions for protocol surface. |
| src/vs/platform/agentHost/common/state/protocol/channels-session/state.ts | Protocol session state now uses activeClients[]. |
| src/vs/platform/agentHost/common/state/protocol/channels-session/reducer.ts | Reducer implements active-client upsert/remove and tool updates by clientId. |
| src/vs/platform/agentHost/common/state/protocol/channels-session/commands.ts | Updates command docs to reference activeClientSet. |
| src/vs/platform/agentHost/common/state/protocol/channels-session/actions.ts | Adds session/activeClientRemoved and clientId to tools-changed action. |
| src/vs/platform/agentHost/common/state/protocol/action-origin.generated.ts | Regenerates action unions + dispatchability for new action set. |
| src/vs/platform/agentHost/common/state/protocol/.ahp-version | Bumps synced protocol revision hash. |
| src/vs/platform/agentHost/common/agentService.ts | Updates agent interface to new active-client handle API. |
| src/vs/platform/agentHost/browser/remoteAgentHostProtocolClient.ts | Grants implicit reads for outgoing activeClientSet customizations. |
Copilot's findings
- Files reviewed: 53/53 changed files
- Comments generated: 1
| [ActionType.SessionActiveClientChanged]: '0.1.0', | ||
| [ActionType.SessionActiveClientSet]: '0.5.0', | ||
| [ActionType.SessionActiveClientRemoved]: '0.5.0', | ||
| [ActionType.SessionActiveClientToolsChanged]: '0.1.0', |
Member
Author
|
cc @TylerLeonhardt @Giuspepe if you want to 👀 the codex/claude changes, I plan to test and merge this tomorrow with microsoft/agent-host-protocol#261 |
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.
What
Adopts the agent host protocol update that moves from a single active client per session to multiple active clients, and makes the agent implementations (Copilot, Claude, Codex) model and merge them correctly.
Protocol + UI/server
SessionState.activeClient?→activeClients[].session/activeClientChangedis split intosession/activeClientSet(upsert byclientId) andsession/activeClientRemoved;session/activeClientToolsChangednow carries aclientId. (These are the regenerated protocol files.)session/activeClientSetand never removes itself.activeClients(and cancels its in-flight client tool calls):IAgent implementations
setClientTools/setClientCustomizationswith a per-client handle API:getOrCreateActiveClient(session, { clientId, displayName }): IActiveClientandremoveActiveClient(session, clientId).IActiveClientexposes plain accessor propertiestools/customizations(readonly-array typed); the setters trigger the agent's internal reaction.ActiveClientToolSet— a per-session,clientId-keyed tool registry withmerged()(dedupe by name, first-inserted client wins),ownerOf(), andstructuralEquals(). Adopted by all three agents.ActiveClientholds the tool set + a multi-clientSessionPluginController(customizations keyed byclientId); the SDK snapshot merges tools/plugins across clients; tool calls stamped viaownerOf.SessionClientToolsModel/SessionClientCustomizationsModelmade multi-client; the SDK mapper/router/pipeline use an owner-resolver instead of a singleclientId.clientToolSet; stamping viaownerOf.Teardown safety
removeClient(revision bump) so a late continuation cannot re-publish a removed client's customizations.disposeSession/shutdown.Notes for reviewers
ownerOf). The protocol hints at "preference to the turn-initiating client" — left as a follow-up since it needs turn-context plumbing.IAgentCreateSessionConfig.activeClient(singular eager-claim at create) is unchanged.Validation
typecheck-clientclean;valid-layers-checkclean; ESLint clean on all changed files.🤖 Generated with Copilot CLI