Skip to content

agentHost: support multiple active clients per session#322665

Draft
connor4312 wants to merge 1 commit into
mainfrom
connor/agent-host-multi-active-client
Draft

agentHost: support multiple active clients per session#322665
connor4312 wants to merge 1 commit into
mainfrom
connor/agent-host-multi-active-client

Conversation

@connor4312

Copy link
Copy Markdown
Member

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/activeClientChanged is split into session/activeClientSet (upsert by clientId) and session/activeClientRemoved; session/activeClientToolsChanged now carries a clientId. (These are the regenerated protocol files.)
  • UI adds itself via session/activeClientSet and never removes itself.
  • Server removes a client from activeClients (and cancels its in-flight client tool calls):
    • on explicit unsubscribe from a session, and
    • on reconnect when the client does not resubscribe to a session where it was still active.
  • Disconnect now keeps the client active during the grace window; the grace timeout removes it (and fails its pending tool calls) only if it never returns.

IAgent implementations

  • Replace setClientTools / setClientCustomizations with a per-client handle API:
    • getOrCreateActiveClient(session, { clientId, displayName }): IActiveClient and removeActiveClient(session, clientId).
    • IActiveClient exposes plain accessor properties tools / customizations (readonly-array typed); the setters trigger the agent's internal reaction.
  • New shared node infra ActiveClientToolSet — a per-session, clientId-keyed tool registry with merged() (dedupe by name, first-inserted client wins), ownerOf(), and structuralEquals(). Adopted by all three agents.
    • Copilot: ActiveClient holds the tool set + a multi-client SessionPluginController (customizations keyed by clientId); the SDK snapshot merges tools/plugins across clients; tool calls stamped via ownerOf.
    • Claude: SessionClientToolsModel / SessionClientCustomizationsModel made multi-client; the SDK mapper/router/pipeline use an owner-resolver instead of a single clientId.
    • Codex: session + mapState carry a clientToolSet; stamping via ownerOf.

Teardown safety

  • Copilot invalidates an in-flight customization sync on removeClient (revision bump) so a late continuation cannot re-publish a removed client's customizations.
  • Claude removes tools synchronously and serializes customization removal through the session sequencer, and prunes cached active-client handles on disposeSession / shutdown.

Notes for reviewers

  • Duplicate tool name across clients resolves deterministically to the first-inserted client (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-client clean; valid-layers-check clean; ESLint clean on all changed files.
  • Node unit suite 11454 passing / 0 failing (incl. new coverage for cross-client merge/dedup, owner-stamped tool calls, per-client removal isolation, and the unsubscribe/reconnect/grace server behaviors).
  • Electron AgentHost suite 1053 passing.

🤖 Generated with Copilot CLI

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]>
Copilot AI review requested due to automatic review settings June 24, 2026 03:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: activeClientactiveClients[], plus new actions session/activeClientSet and session/activeClientRemoved (and clientId added to session/activeClientToolsChanged).
  • New per-client handle API (getOrCreateActiveClient / removeActiveClient) and shared ActiveClientToolSet for 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',
@connor4312

Copy link
Copy Markdown
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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants