Skip to content

agentHost: mirror Fusion to remote and tunnel Agent Hosts - #342136

Merged
Bhavya U (bhavyaus) merged 3 commits into
mainfrom
bhavyaus/fusion-remote-agent-host
Oct 17, 2026
Merged

Bhavya U (bhavyaus) merged 3 commits into
mainfrom
bhavyaus/fusion-remote-agent-host

Conversation

@bhavyaus

@bhavyaus Bhavya U (bhavyaus) commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Forward Fusion to remote and tunnel Agent Hosts while preserving the existing account preview-feature restriction, without adding a public enterprise policy.

Previously, chat.copilot.ensemble.enabled and the account preview-feature gate were combined by a local-only forwarder. Remote/tunnel hosts did not receive that result, so enabling the client setting or its experiment default normally left remote sessions showing Auto.

Changes

  • Have the protocol client combine the globally resolved setting with chat_preview_features_enabled !== false, then send the result on connection, reconnection, setting changes, and account policy-data changes.
  • Send both fusion and legacy ensemble for hosts predating the rename in Rename Ensemble to Fusion across the codebase #341932.
  • Remove the duplicate local Fusion writer. Local HydraFusion flag derivation and the separate High-routing option remain unchanged.
  • Keep the existing chat.copilot.ensemble.enabled setting ID, startup experiment, false default, and application-machine scope. Do not overwrite the user's preference when the account restricts preview features.
  • Remove the proposed CopilotFusion policy and its support/catalog entries. The canonically regenerated policy catalog exactly matches the pre-PR catalog.
  • Keep three focused Fusion tests: a connected remote lifecycle with restriction application/removal and reconnect; an initially restricted connection that never sends an ungated true; and an experiment default reaching a tunnel with the legacy alias. Reuse existing generic scope/reconnect coverage instead of repeating a ten-case matrix.

Validation

  • npm run build-fast -- --client-only — passed.
  • npm run typecheck-client — passed.
  • Changed-TypeScript ESLint — passed:
npm run eslint -- src\vs\platform\agentHost\browser\agentHostProtocolClient.ts src\vs\platform\agentHost\common\agentHostPolicySupport.ts src\vs\platform\agentHost\common\fusionConfiguration.ts src\vs\platform\agentHost\test\common\agentHostConfigurationSync.test.ts src\vs\platform\agentHost\test\electron-browser\agentHostProtocolClient.test.ts src\vs\platform\agentHost\test\electron-browser\sshRemoteAgentHostService.test.ts src\vs\platform\agentHost\test\electron-browser\wslRemoteAgentHostService.test.ts src\vs\workbench\contrib\chat\test\browser\agentSessions\agentHostChatContribution.test.ts src\vs\workbench\contrib\chat\test\browser\agentSessions\agentHostCopilotCliSettingsContribution.test.ts
  • After reducing the test matrix, npm run eslint -- src\vs\platform\agentHost\test\electron-browser\agentHostProtocolClient.test.ts — passed.
  • Affected suites — 1,057 tests passed:
Remove-Item Env:COPILOT_HOME -ErrorAction SilentlyContinue
.\scripts\test.bat --run src\vs\platform\agentHost\test\electron-browser\agentHostProtocolClient.test.ts --run src\vs\platform\agentHost\test\electron-browser\sshRemoteAgentHostService.test.ts --run src\vs\platform\agentHost\test\electron-browser\wslRemoteAgentHostService.test.ts --run src\vs\platform\agentHost\test\common\agentHostConfigurationSync.test.ts --run src\vs\platform\agentHost\test\node\agentHostPolicySupport.test.ts --run src\vs\workbench\contrib\chat\test\browser\agentSessions\agentHostCopilotCliSettingsContribution.test.ts --run src\vs\workbench\contrib\chat\test\browser\agentSessions\agentHostChatContribution.test.ts --reporter dot

The inherited COPILOT_HOME was cleared only in the test subprocess; otherwise two existing attachment-path fixtures used the caller's home instead of their test home.

  • Canonical generation and check — both passed with freshly compiled client output:
$env:VSCODE_SKIP_PRELAUNCH = '1'
npm run export-policy-data -- --skip-transpile
npm run export-policy-data -- --check --skip-transpile
  • git diff --check — passed.

The Windows native build toolchain lacks Spectre libraries. Required exporter native modules were restored from the installed official VS Code after checking exact package versions and identical Electron version/ABI/architecture. The exporter completed despite warnings for other unavailable native components. Live tunnel UI verification has not been performed for this revision.

After merging main (incl. #342200 and #342167): npm run typecheck-client passed. ESLint on the changed files passed. The affected suites (protocol client, SSH/WSL, settings contribution, chat contribution, model picker telemetry) — 1,107 passing, 0 failing. The Fusion routing settings and wire keys keep #342200's names. This PR only updates the chat.copilot.ensemble.enabled description in fusionConfiguration.ts to say "Standard, High, or Max".

Manual verification

  1. Use a client built from this branch and connect to a tunnel Agent Host with Fusion/Ensemble support.
  2. Set "chat.copilot.ensemble.enabled": true; confirm the host receives fusion=true and ensemble=true.
  3. Confirm the model picker presents Fusion rather than Auto. A pre-rename host may still call it Ensemble.
  4. Disable account preview features while connected; confirm both flags become false without rewriting the user's setting. Remove the restriction and confirm the enabled preference takes effect again.
  5. Disconnect, change the account restriction, and reconnect; confirm the current restricted value is sent.

A runtime restart is conditional on startup configuration changing and can be deferred while work is active, so a restart log alone is not the verification criterion.

Scope and caveats

  • Root configuration remains host-wide and last-writer-wins across connected windows; this is not per-session account-policy enforcement.
  • The separate Fusion High-routing setting (chat.copilot.ensemble.intelligenceUsesHydraFusion) remains local-only.
  • WebPubSub/Mission Control relay connections continue to avoid forwarding client root configuration.

`chat.copilot.ensemble.enabled` (Fusion) was forwarded only to the local Agent
Host by `AgentHostRootConfigForwarder`, because its value also depends on the
account's `chat_preview_features_enabled` gate, which remote hosts cannot read.
Sessions on a remote or tunnel host therefore always showed plain Auto, even
when the user enabled Fusion or was assigned the experiment.

Fold the preview-features gate into the setting itself with a `CopilotFusion`
policy (locked to false when the account disables preview features) and mirror
the setting declaratively with `agentHost`, so every host - local, remote
extension host, and tunnel - receives the policy- and experiment-resolved
value. Also write the legacy `ensemble` key for hosts built before the
Ensemble to Fusion rename. The local-only forwarder no longer owns the key;
it still derives the HydraFusion keys from Fusion for the local host, and
remote hosts already couple HydraFusion to Fusion themselves.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 16, 2026 17:56

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.

🔵 Needs a closer look

The new enterprise-policy contract needs maintainer resolution of the legacy-channel restriction and canonical export validation.

1 open finding
What changed in this PR

Extends Fusion configuration mirroring to remote and tunnel Agent Hosts, preserving compatibility with older hosts.

Changes:

  • Moves Fusion registration into a shared module and mirrors both fusion and legacy ensemble.
  • Adds the CopilotFusion policy and removes local-only Fusion forwarding.
  • Tests cross-host mirroring and preview-feature gating.
File Description
src/​vs/​workbench/​contrib/​chat/​test/​browser/​agentSessions/​agentHostCopilotCliSettingsContribution.test.ts Updates local forwarding expectations.
src/​vs/​workbench/​contrib/​chat/​browser/​chat.shared.contribution.ts Registers the shared Fusion definition.
src/​vs/​workbench/​contrib/​chat/​browser/​agentSessions/​agentHost/​agentHostCopilotCliSettingsContribution.ts Removes local-only Fusion forwarding.
src/​vs/​platform/​agentHost/​test/​common/​agentHostConfigurationSync.test.ts Tests mirroring and policy resolution.
src/​vs/​platform/​agentHost/​common/​fusionConfiguration.ts Defines Fusion mirroring and policy metadata.
src/​vs/​platform/​agentHost/​common/​agentHostPolicySupport.ts Marks the policy as enforced.
build/​lib/​policies/​policyData.jsonc Adds the enterprise-policy catalog entry.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Preserve account preview-feature gating in the protocol client and cover remote transitions with three focused tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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.

🟢 Approval recommended

No blocking defects remain identified, and focused regression coverage supports the forwarding behavior, although live tunnel UI verification remains outstanding.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

…e-agent-host

# Conflicts:
#	src/vs/platform/agentHost/browser/agentHostProtocolClient.ts
#	src/vs/platform/agentHost/test/electron-browser/agentHostProtocolClient.test.ts
#	src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostCopilotCliSettingsContribution.ts
#	src/vs/workbench/contrib/chat/browser/chat.shared.contribution.ts
#	src/vs/workbench/contrib/chat/test/browser/agentSessions/agentHostCopilotCliSettingsContribution.test.ts
@bhavyaus
Bhavya U (bhavyaus) force-pushed the bhavyaus/fusion-remote-agent-host branch from 1bb7bfb to bbe5953 Compare October 17, 2026 00:11
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Screenshot Changes

Base: 51266f6f Current: 57ae1617

Changed (1)

sessions/chatCompositeBar/OverflowingTabs/Light
Before After
before after

@bhavyaus
Bhavya U (bhavyaus) enabled auto-merge (squash) October 17, 2026 00:16

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.

🔵 Needs a closer look

Shared remote runtime configuration and unresolved account-initialization and experiment-tracking concerns require maintainer review.

2 open issues

Review effort: Max

@bhavyaus
Bhavya U (bhavyaus) merged commit e97039d into main Oct 17, 2026
50 checks passed
@bhavyaus
Bhavya U (bhavyaus) deleted the bhavyaus/fusion-remote-agent-host branch October 17, 2026 00:51
@mohammed-9003

Copy link
Copy Markdown

@vs-code-engineering vs-code-engineering Bot added this to the 1.143.0 milestone Oct 17, 2026
Bhavya U (bhavyaus) added a commit that referenced this pull request Oct 17, 2026
…preview-features gate

`chat.copilot.dynamicAutoTiers.enabled` was forwarded only to the local Agent Host,
and without the account's `chat_preview_features_enabled` gate. So an experiment
could turn on server-driven tiers, which can offer HydraFusion, for accounts that
disabled preview features, while remote and tunnel Agent Hosts never got the setting.

Forward it the way #342136 forwards Fusion: the protocol client dispatches
`dynamicAutoTiers` to every host (local, remote, and tunnel) on connect, on
reconnect, when the setting changes outside the workspace layers, and when the
account's policy changes, and only while preview features are allowed. The setting
is registered beside Fusion in `fusionConfiguration.ts`, and the local-only
forwarder no longer owns the key.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bhavya U (bhavyaus) added a commit that referenced this pull request Oct 17, 2026
…lot.dynamicAutoTiers.enabled (#341688)

* Agent host: read Auto tiers from runtime models.list behind chat.copilot.dynamicAutoTiers.enabled

Adds a client-owned opt-in, off by default, that launches the Copilot
runtime with DYNAMIC_AUTO_TIERS. With it on, server-level models.list
returns the provider's Auto tiers from CAPI /meta (SDK 1.0.23-preview.0,
github/copilot-agent-runtime#26406), and Auto's "Optimize for" picker is
built from them instead of the hard-coded efficiency/balance/intelligence.

- The setting is a startup key: changing it restarts the runtime after
  active turns finish, and the restart drops the old runtime's tiers so
  sessions never send a tier the new runtime rejects.
- Sessions accept exactly the enabled auto/fusion tiers the provider
  advertises; without Auto metadata the built-in tiers apply.
- HydraFusion Max is offered only with HydraFusion v2, since the runtime
  rejects the hydrafusion-max preference without HYDRAFUSION_PLAN_V2.
- Committed Auto tiers reported by the runtime (for example hydrafusion)
  are kept for edit attribution instead of being dropped.

Based on the proof of concept in #341429.

Co-authored-by: Andy Salerno <ansalern@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: show unavailable Auto tiers disabled with their reason, per copilot-api#39882

Follows the /meta integration rules in github/copilot-api#39882 and fixes
the review findings on this PR.

- Tiers the provider lists with status.enabled false are shown in the
  picker, in the provider's order, but cannot be selected; status.message
  (or a generic reason) is their tooltip. The protocol tier enum still
  holds only selectable tiers; the full list travels in the Auto model's
  _meta (vscode.chat.autoTiers), so other clients keep working. A saved
  tier that becomes unavailable is dropped and reads as the default.
- Stale models.list responses no longer overwrite tier metadata: the
  metadata is applied together with the catalog, under the same token
  and generation checks, and schemas are built from the response's own
  metadata. The fallback Auto model is built per metadata as well.
- Metadata whose tiers are all unavailable now accepts no preference,
  instead of falling back to the built-in tiers the runtime would reject.
- A provider tier restored before the Auto model registers keeps its
  value and provenance until the schema can validate it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: show the service's Fusion tier promotions in the Fusion discount

Wires /meta service_messages promotions (github/copilot-api#39882) into
how Fusion presents its discount.

- The agent host reads service_messages from the same models.list
  response as the Auto tiers (defensively, until the SDK types it),
  keeps promotion kinds that target auto_tier IDs with valid dates, and
  attaches them to the Auto model's _meta (vscode.chat.autoTierPromotions).
- With Fusion, a running promotion replaces Fusion's own discount on the
  tiers it covers ("Models routed via Fusion Max receive a 30% discount."),
  and the header chip shows it when it covers every Fusion tier. The
  card shows the service's message: running ones with their end date,
  upcoming ones with their start date; ended ones are hidden. The picker
  re-publishes when a promotion starts or ends.
- Fusion now names the provider's tiers Lite (balance), Balance
  (hydrafusion), and Max (hydrafusion-max), including unavailable ones,
  and no longer adds the HydraFusion models as extra Balance/Max options
  when its tiers already offer them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: keep Auto tier availability consistent across restarts and chat-scoped choices

Addresses review feedback on the provider Auto tiers.

- A runtime restart that keeps DYNAMIC_AUTO_TIERS re-derives the accepted
  tiers from the retained provider metadata with the current HydraFusion
  v2 setting, so a restart that turns v2 off stops accepting
  hydrafusion-max before the models refresh.
- When the provider lists picker tiers but none are selectable, the Auto
  tier property stays with an empty enum and no default, so the picker
  can still list the unavailable tiers and their reasons.
- A chat's own tier choices (the per-chat configuration report) keep the
  provider's unavailable tiers disabled, in the shared order, rebuilding
  enum, labels, descriptions and enumItemDisabled together instead of
  leaving them misaligned.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: tighten Fusion's presentation of provider Auto tiers

Addresses a full review of the provider Auto tiers.

- A chat on a HydraFusion model moves to Fusion with that model's tier
  when Fusion's provider tiers offer it, so the picker shows it selected
  instead of an unrelated tier. Unavailable or read-only tiers stay put.
- Fusion offers Balance once: the legacy intelligence tier is left out
  when the provider's HydraFusion tier is Fusion's Balance.
- Fusion renames only the tiers it knows (balance, intelligence,
  hydrafusion, hydrafusion-max) and keeps the provider's label for others.
- Fusion's default falls back to Balance only when offered, else the
  first tier, and a saved Efficiency runs as Balance only while the
  provider accepts it; otherwise the runtime's default applies.
- Tiers the provider keeps out of the picker (status.tier_picker_enabled
  false, github/copilot-api#39882) are hidden, read defensively until the
  SDK types it; they stay accepted, like fast.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: never save a Fusion tier the picker derives from a HydraFusion model

Found by driving the Agents window against the real runtime and a mock
/meta (Auto and Fusion with provider tiers).

- A new chat can start on the HydraFusion model because it is the first
  model available before the catalog loads. Showing that chat as Fusion
  with the hydrafusion tier wrote the tier through the global model
  configuration when the picker had no chat-scoped configuration, so
  every later chat inherited Balance (HydraFusion) as an explicit choice.
  The tier is now only presented for the chat; without chat-scoped
  configuration the selection is left as it is.
- In that case the card keeps the HydraFusion model as an option, so the
  chat's model is still shown as selected.
- Presenting a provider tier (for example hydrafusion) now marks it as
  session state like a built-in tier, rather than leaving it to be
  treated as an explicit selection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: trim comments and redundant tests for provider Auto tiers

Keeps only comments that explain non-obvious behavior, and drops tests
whose behavior is already covered by other tests in the change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address Copilot review: override precedence, shared tier validation, promotion refresh, hidden Max

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: offer only runtime-listed HydraFusion with DYNAMIC_AUTO_TIERS

With DYNAMIC_AUTO_TIERS the runtime's /meta catalog decides whether an
account can run HydraFusion (as Auto tiers, disabled with a reason when
unavailable), so stop injecting the setting-driven HydraFusion and
HydraFusion Max rows in _publishModels. Injecting them offered HydraFusion
to accounts the runtime rejects before routing (#339959).
Runtimes without the flag keep the injected rows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: read Auto tier picker visibility and promotions in the runtime's shape

github/copilot-agent-runtime#27103 applies `/meta` `service_messages` in the runtime
and exposes them on `models.list().auto`, so match its SDK contract instead of the
CAPI field names:

- Picker visibility is `auto.tiers[].status.pickerVisible` (absent means visible),
  not `tier_picker_enabled` / `tierPickerEnabled`.
- Promotions are per tier on `auto.tiers[].billing.promo`, not a top-level
  `service_messages` list. The runtime already drops unknown kinds and targets and
  ended promotions and keeps one per tier, so the agent host only groups them by
  promotion into the tiers each covers.
- `id`, `message` and `discountPercent` are optional in that contract: an unnamed
  promotion is keyed by its content, a missing discount is an announcement, and a
  promotion without a message changes the discount line without a banner.

The SDK does not type these fields yet, so they are read defensively and light up
with the next SDK bump.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: mirror Dynamic Auto Tiers to every Agent Host behind the preview-features gate

`chat.copilot.dynamicAutoTiers.enabled` was forwarded only to the local Agent Host,
and without the account's `chat_preview_features_enabled` gate. So an experiment
could turn on server-driven tiers, which can offer HydraFusion, for accounts that
disabled preview features, while remote and tunnel Agent Hosts never got the setting.

Forward it the way #342136 forwards Fusion: the protocol client dispatches
`dynamicAutoTiers` to every host (local, remote, and tunnel) on connect, on
reconnect, when the setting changes outside the workspace layers, and when the
account's policy changes, and only while preview features are allowed. The setting
is registered beside Fusion in `fusionConfiguration.ts`, and the local-only
forwarder no longer owns the key.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: let the service's Auto tier promotions alone decide Fusion's discount

While `/meta` promotes Auto's tiers, Fusion also kept Auto's own discount from the
model catalog: the picker row and the card's header showed "10% discount" and the
other tiers said "receive a 10% discount", next to promotions for 20% and 30%.

While any promotion is published for Auto's tiers, the agent host no longer
publishes Auto's own discount, so the discount comes only from the promotions:
the header shows a running promotion that covers every tier, and a tier's line
shows the running promotion that covers it. Without promotions, or once they end,
Auto's own discount returns.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Agent host: test that DYNAMIC_AUTO_TIERS keeps HydraFusion hidden while signed out

Covers #342040 with the runtime flag on. Signed out, only BYOK models are published:
no Auto, and no HydraFusion row. Signed in, the picker shows only the runtime's catalog,
with HydraFusion offered only as an Auto tier.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Andy Salerno <ansalern@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

4 participants