Skip to content

agentHost: Claude per-session provider selection - #329331

Merged
Tyler James Leonhardt (TylerLeonhardt) merged 8 commits into
mainfrom
claude-per-session-provider
Aug 7, 2026
Merged

agentHost: Claude per-session provider selection#329331
Tyler James Leonhardt (TylerLeonhardt) merged 8 commits into
mainfrom
claude-per-session-provider

Conversation

@TylerLeonhardt

@TylerLeonhardt Tyler James Leonhardt (TylerLeonhardt) commented Aug 6, 2026

Copy link
Copy Markdown
Member

Closes #329656

What

Lets each Claude agent-host session pick a model whose provider decides its transport, instead of the whole host being pinned to one transport:

  • anthropic-provider models route to the native Anthropic SDK.
  • copilot (and any unknown) provider routes to the Copilot-CAPI proxy.

This mirrors how the Codex provider already behaves, and is always-on (no experimental flag).

How

  • Provider-qualified model ids — a small codec (claudeModelSelection.ts) encodes/decodes @provider=anthropic:… / @provider=copilot:…. Bare/legacy ids (no explicit provider) resume on the host default transport, so already-persisted sessions need no migration.
  • Merged catalog — the model list is fetched from both the native and proxy sources, each self-gated on its own credential/native-setup, and re-stamped with provider: 'anthropic' | 'copilot'. The picker groups the rows by vendor ("Anthropic" / "Copilot").
  • Per-session transport resolutionresolveClaudeSessionTransport({ model, defaultMode }) maps the selected model's provider to a transport at materialize time.
  • Live switching — selecting a model on the other transport defers via a pending-switch flag and re-materializes the session's subprocess on the next send, so the rebuilt process is routed to the newly-selected provider. Same-transport model changes still hot-swap in place.

Notes / intended behavioral consequences

  • Merged/qualified catalog is now the default. A Copilot-only user sees a single "Copilot" group; a dual-credential user sees both. Routing is unchanged (vendor stays agent-host-claude).
  • Copilot sign-in becomes lazy — the Copilot resource is advertised required: false; sign-in defers to first send of a Copilot-routed model (matches Codex).

Testing

  • Unit: claudeModelSelection.test.ts, claudeAgent.test.ts (codec, merged-catalog refresh, per-session transport resolution, live cross-transport switch, signed-out native bootstrap).
  • Agent-host e2e suite (test/node/e2e/) with modelProviders: ['copilot', 'anthropic'].
  • npm run typecheck-client.
  • Live smoke against a dual-credential build (picker grouping + lazy Copilot sign-in + native↔Copilot switch).

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings August 6, 2026 06:26

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

Adds per-session Claude provider selection, routing models through either Anthropic’s native SDK or the Copilot proxy.

Changes:

  • Adds provider-qualified model IDs and merged catalogs.
  • Supports deferred cross-transport model switching.
  • Adds provider grouping and expanded unit/E2E coverage.
Show a summary per file
File Description
agentHostLanguageModelProvider.test.ts Tests Claude provider grouping.
agentHostChatContribution.ts Registers Anthropic picker metadata.
claudeAgentHostE2E.integrationTest.ts Allows both Claude model providers.
claudeModelSelection.test.ts Tests codec, merging, and routing.
claudeAgent.test.ts Tests catalogs, authentication, and switching.
claudeSdkPipeline.ts Buffers configuration for transport rebinds.
claudeSdkOptions.ts Decodes qualified SDK model IDs.
claudeModelSelection.ts Implements model qualification and routing.
claudeAgentSession.ts Adds deferred transport switching.
claudeAgent.ts Integrates per-session routing and catalogs.
claudeProviders.ts Defines shared provider tokens.

Review details

  • Files reviewed: 11/11 changed files
  • Comments generated: 4
  • Review effort level: Balanced

// register the matching vendor so the picker labels their group "Anthropic".
// Copilot-routed Claude models group under the global `copilot` vendor. Dormant
// while the feature is off — no Claude model carries this provider then.
vendor: CLAUDE_PROVIDER_ANTHROPIC,
Comment on lines +2309 to +2311
const isSwitch =
sess.isPipelineReady &&
claudeTransportForProvider(parseClaudeModelSelection(model).provider) !== sess.transportKind;
Comment on lines +649 to +659
// Reuse the transport captured at materialize for an ordinary rebuild,
// so a runtime flip of the host default (config change / Copilot
// sign-in mutating the agent's live transport mode) never reroutes the
// live conversation. Only a deliberate per-session provider switch
// (`_pendingTransportSwitch`) re-resolves from the session's *current*
// provisional model, rebuilding onto the new transport; a signed-out
// switch-to-proxy throws here (from `_ensureAuthenticated`), landing in
// the catch below so the diffs stay dirty and the next send retries.
// The `_materializedTransport` fallback is defensive — it is always set
// once `materialize` has run, but re-resolving keeps a rebuild correct
// rather than crashing if it somehow has not.

/** Re-id each model with its provider-qualified selection id and re-stamp its `provider` with the same token, leaving all other fields intact. */
function withQualifiedProvider(models: readonly IAgentModelInfo[], provider: string): IAgentModelInfo[] {
return models.map(model => ({ ...model, id: toClaudeModelSelectionId(provider, model.id), provider }));

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.

Review details

Suppressed comments (3)

src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatContribution.ts:322

  • anthropic is already contributed as a language-model vendor by extensions/copilot/package.json:1748. This add is therefore rejected as a duplicate, but the unconditional disposer later removes that pre-existing vendor (and its provider/model cache). Only include the fallback descriptor when this contribution actually owns the registration.
			...(agent.provider === CLAUDE_AGENT_PROVIDER_ID ? [{

src/vs/platform/agentHost/node/claude/claudeAgentSession.ts:678

  • This long inline narrative exceeds the repository's one-line limit for comments inside method bodies and obscures the transport choice it explains. Keep the non-obvious pinning rule in one line and let the named fields and guard express the control flow.
				// Reuse the transport captured at materialize for an ordinary rebuild,
				// so a runtime flip of the host default (config change / Copilot
				// sign-in mutating the agent's live transport mode) never reroutes the
				// live conversation. A deliberate per-session provider switch instead
				// rebuilds onto the transport the agent pushed in through `send`'s

src/vs/platform/agentHost/node/claude/claudeAgentSession.ts:969

  • This bypasses the codec's bare/legacy-id semantics: parseClaudeModelSelection labels a bare id as Copilot only as a fallback, while resolveClaudeSessionTransport intentionally routes it through the host default. For example, with a native host default and a live explicitly-Copilot session, changing to a bare id is treated as same-transport and remains on the proxy instead of switching to native. Resolve the desired transport in ClaudeAgent and pass it into setModel, just as the send path does.
		const crossesTransport =
			this.isPipelineReady &&
			claudeTransportForProvider(parseClaudeModelSelection(model).provider) !== this._transportKind;
  • Files reviewed: 11/11 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +603 to +607
} catch (err) {
// GitHub sign-in itself succeeded; only the Copilot proxy failed to
// start. Don't fail sign-in — the merged catalog still serves any native
// models, and a Copilot-routed model surfaces `AHP_AUTH_REQUIRED` on its
// first send (which re-drives sign-in, retrying `start()`). Leave
@TylerLeonhardt
Tyler James Leonhardt (TylerLeonhardt) marked this pull request as ready for review August 7, 2026 07:58
Pure precursor for per-session provider selection in the Claude harness.
Mirrors Codex's `@provider=` convention: toClaudeModelSelectionId encodes a
provider + model id into one opaque ModelSelection.id; parseClaudeModelSelection
splits it back, with a bare/malformed/legacy id defaulting to the Copilot
(proxy) provider so nothing needs a data migration. claudeTransportForProvider
maps the token to a transport (anthropic -> native, everything else -> proxy).

Dead code until the merged-catalog + per-session routing core wires it in;
landed first because it's a leaf with zero behavioral risk. Fully unit-tested
with no mocks, mirroring codexModelSelection.test.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…laude

Two pure precursors for per-session provider selection, co-located with the
model-selection id codec and covered by the same test suite:

- mergeClaudeModelCatalogs(proxy, native): flattens the two provider catalogs
  into one picker list, proxy-first (preserving models[0]-is-default), each id
  provider-qualified so a row carries its transport and the same model under
  both providers yields two non-colliding rows. Either side may be empty so one
  source failing to fetch never blanks the other.
- resolveClaudeSessionTransport({ perSessionProviderEnabled, model, defaultMode }):
  the per-session counterpart to the host-global resolver — off, or no model,
  inherits the host default (identical to today); on, the selected model's
  provider decides.

Both are dead code until the flag-gated wiring lands; kept as a separately
reviewable, fully-tested unit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d-path

Slice 2a (flag plumbing) + 2b (backend read-path) of per-session provider
selection for the Claude agent host, gated behind the off-by-default
experimentation flag `chat.agentHost.claude.perSessionProvider`.

Flag plumbing (2a): register the boolean setting (APPLICATION scope,
experimental/advanced), its customization-config key, and the forwarder
contribution.

Backend read-path (2b): ClaudeAgent merges the proxy (Copilot-CAPI) and
native (Anthropic) model catalogs into one provider-qualified picker list,
and resolves each session's transport from its selected model's provider
when the flag is on (inheriting the host default when off, or when no model
is selected).

Review fixes folded in:
- A: gate the constructor / hydration model-refresh on the flag so the
  native catalog bootstraps signed-out without a manual refresh.
- B: _resolveParentSession inherits a never-materialized parent's provisional
  model so a forked peer chat keeps its native transport.
- C: a runtime flag toggle re-enumerates and repopulates the merged catalog.
- D: a failing proxy start no longer fails native-default sign-in.
- E: toClaudeSdkModelId strips the `@provider=` qualification before the SDK /
  CAPI boundary — the wrapper is unparseable downstream and would 400 both
  transports whenever the flag is on and a model is explicitly selected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n provider toggle

The refresh stale-write guards captured only the transport token/mode, not the
per-session provider flag. Because `_startModelRefresh` supersedes an in-flight
refresh as the coalescing target but never cancels it, a false-flip of the flag
mid-refresh left the superseded refresh running; when it settled it published a
stale wrong-mode catalog (merged provider-qualified over the bare single-transport
list the flag-off refresh had already published, or vice versa), clobbering the
correct one.

Capture `_perSessionProviderEnabled` at the start of both `_refreshModelsSingle`
and `_refreshModelsMerged` and bail in the stale-write guard when it moved, so the
superseded refresh drops its result.

Tests: a flag-on→off toggle mid-merge (native half parked on a gate so the merged
refresh is provably still in-flight) asserts the bare single catalog survives the
straggler; and a flag-off regression that a forked peer chat still inherits its
never-materialized parent's explicit model (the inheritance in `_resolveParentSession`
is intentionally not flag-gated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With per-session provider selection on, the merged catalog now stamps each
model's `provider` with its transport token (`copilot` for the Copilot-CAPI
proxy, `anthropic` for the user's own Anthropic account) alongside the
provider-qualified id, so the chat model picker — which buckets by
`provider` — splits Claude into a Copilot group and an Anthropic group. The
same model offered by both transports yields two distinct, separately
selectable rows.

- common/claudeProviders.ts (new): the two transport-provider tokens live in
  one `common` module so the backend that stamps `model.provider` and the
  frontend vendor descriptor that names the group are a single,
  compile-checked source of truth rather than two literals that can drift.
- claudeModelSelection.ts: `withQualifiedProvider` re-stamps each model's
  `provider` with its transport token; re-exports the tokens for node callers.
- agentHostChatContribution.ts: register the `anthropic` group vendor
  (localized "Anthropic"), mirroring the Codex `chatgpt` second-vendor
  registration, so the native group resolves a clean label. Copilot-routed
  Claude models keep grouping under the global `copilot` vendor. Dormant while
  the flag is off — no Claude model carries the `anthropic` provider then.
- claudeAgent.ts: correct the stale `toAgentModelInfo` doc — the picker
  *groups* (does not filter) by `provider`.

Flag-off path is unchanged: the single-catalog refresh still stamps the
harness provider and no second vendor is registered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complete the Claude per-session provider feature. Switching a live
session's model to one on a different transport now re-routes the running
subprocess, and the feature ships unconditionally (no experimental flag).

Live provider switch:
- Refactor IMaterializeContext.transport into a resolveTransport callback so
  the transport is re-resolved inside materialize and on every rebuild — a
  provider switch re-routes the rebuilt subprocess onto the new transport.
- A cross-transport model change defers via a pending-switch flag; the next
  send() rebuilds onto the newly-selected transport and commits it once the
  new subprocess is live. Same-transport changes still hot-swap in place.

Remove the experimental flag:
- Delete chat.agentHost.claude.perSessionProvider, revert the generic flag
  plumbing, and delete the setting-to-root-config forwarder contribution.
- Collapse the per-session-provider gates in claudeAgent.ts to always-on and
  remove the now-dead members.
- Drop the perSessionProviderEnabled parameter from
  resolveClaudeSessionTransport; bare/legacy ids still resume on the host
  default transport with no migration.
- Add modelProviders to the e2e CLAUDE_CONFIG and de-flag the unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up cleanup on the per-session-provider read-path, now that the
feature is unconditional and the single-catalog path is gone.

- Collapse the cached `_transportMode` and its reactive
  `_resolveTransportMode`/`_applyTransportModeChange` machinery into a
  read-on-demand `_defaultTransportMode()`. The host default is only the
  fallback for model-less / bare-id sessions, so it needs no caching or
  config/sign-in re-resolve — the next session reads live availability.
- `authenticate()`: a Copilot proxy-start failure is now uniformly soft.
  GitHub sign-in still succeeds and both `_githubToken` and `_proxyHandle`
  stay uncommitted, so a retry re-attempts `start()`; a Copilot-routed
  model re-drives sign-in on its first send. Drops the native/proxy
  default special-casing and the mid-flight transport-mode flip.
- Replace `IMaterializeContext.resolveTransport` (a callback the session
  re-invoked on every rebuild) with a `transport` value the agent pins at
  materialize. A per-session provider switch is pushed in through `send`'s
  new `switchTransport` (staged in `_pendingSwitchTransport`); ordinary and
  SDK-recover rebuilds reuse the materialized transport. A throwing guard
  replaces the defensive re-resolve.
- Move cross-transport switch detection into `ClaudeAgentSession.setModel`
  (the session owns it); drop the agent-computed `deferForTransportSwitch`
  option and expose `hasPendingTransportSwitch` in place of `transportKind`.
- Inline the one-off `_settledCatalog` helper into `_refreshModels` and drop
  the `_refreshModels` -> `_refreshModelsMerged` forwarder.
- Drop the now-unused provider-token re-export from `claudeModelSelection`;
  tests import the tokens from `common/claudeProviders` directly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes five review comments on the per-session provider slice:

- Keep IAgentModelInfo.provider as the `claude` routing owner and carry the
  transport/group token in `_meta.modelGroupId`, so a model-selected
  create_session no longer misroutes to a `copilot`/`anthropic` agent that
  the node provider registry can't resolve.
- Drop the agent-host `anthropic` picker vendor that clobbered the Copilot
  extension's shared `anthropic` vendor on dispose; reuse the shared one.
- On a replacement-token proxy start() failure, tear down the stale account
  (handle, token, and merged catalog) instead of leaving it live behind a
  "successful" sign-in that would silently serve the superseded account.
- Guard setModel's cross-transport detection on an explicit provider so a
  bare/legacy id (parser-fallback `copilot`) can't spuriously reroute a
  native session.
- Condense the over-long rematerializer transport-pinning comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TylerLeonhardt
Tyler James Leonhardt (TylerLeonhardt) merged commit 511fd2d into main Aug 7, 2026
31 checks passed
@TylerLeonhardt
Tyler James Leonhardt (TylerLeonhardt) deleted the claude-per-session-provider branch August 7, 2026 08:29
@vs-code-engineering vs-code-engineering Bot added this to the 1.133.0 milestone Aug 7, 2026
Tyler James Leonhardt (TylerLeonhardt) added a commit that referenced this pull request Aug 7, 2026
Since #329331 the Claude model picker shows Copilot-proxy and native-Anthropic
models together, each carrying a provider-qualified id, and a session routes on
the provider of the model it was started with (resolveClaudeSessionTransport).
That makes the host-global override redundant — and, worse, incoherent.

Model enumeration (_refreshModels) gates the proxy half on holding a GitHub
token and the native half on detectExistingClaudeSetup; neither consulted the
setting. getProtectedResources() advertises the Copilot resource required:false
unconditionally. So `claudeUseCopilotProxy: false` never stopped Copilot-routed
Claude from being offered or used by a signed-in user, which is exactly what its
title ("Route Claude Through Copilot") promised. Its only remaining effect was
on the model-less fallback, where an explicit `true` broke the one state the
signed-out feature exists to serve: opt-in on, signed out, local Anthropic
credential present — forced to proxy and dead-ended on AHP_AUTH_REQUIRED.

resolveClaudeTransportMode drops its explicitProxy input and reduces to three:
opt-in off => proxy; signed in => proxy; else local setup => native; else proxy.

Clean deletion, not a deprecation: the key was never forwarded from a VS Code
setting (AgentHostRootConfigForwarder never managed it) and was only ever
hand-written into agent-host-config.json. A profile carrying a stale value
starts with no error and drops the key on the first config write.

Also corrects mergeClaudeModelCatalogs' claim that proxy-first ordering makes
Copilot the session default. It does not: the picker re-buckets by the _meta
vendor token, so the Anthropic group sorts first and the pre-selected model
routes native. Verified end-to-end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tyler James Leonhardt (TylerLeonhardt) added a commit that referenced this pull request Aug 7, 2026
Since #329331 the Claude model picker shows Copilot-proxy and native-Anthropic
models together, each carrying a provider-qualified id, and a session routes on
the provider of the model it was started with (resolveClaudeSessionTransport).
That makes the host-global override redundant — and, worse, incoherent.

Model enumeration (_refreshModels) gates the proxy half on holding a GitHub
token and the native half on detectExistingClaudeSetup; neither consulted the
setting. getProtectedResources() advertises the Copilot resource required:false
unconditionally. So `claudeUseCopilotProxy: false` never stopped Copilot-routed
Claude from being offered or used by a signed-in user, which is exactly what its
title ("Route Claude Through Copilot") promised. Its only remaining effect was
on the model-less fallback, where an explicit `true` broke the one state the
signed-out feature exists to serve: opt-in on, signed out, local Anthropic
credential present — forced to proxy and dead-ended on AHP_AUTH_REQUIRED.

resolveClaudeTransportMode drops its explicitProxy input and reduces to three:
opt-in off => proxy; signed in => proxy; else local setup => native; else proxy.

Clean deletion, not a deprecation: the key was never forwarded from a VS Code
setting (AgentHostRootConfigForwarder never managed it) and was only ever
hand-written into agent-host-config.json. A profile carrying a stale value
starts with no error and drops the key on the first config write.

Also corrects mergeClaudeModelCatalogs' claim that proxy-first ordering makes
Copilot the session default. It does not: the picker re-buckets by the _meta
vendor token, so the Anthropic group sorts first and the pre-selected model
routes native. Verified end-to-end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tyler James Leonhardt (TylerLeonhardt) added a commit that referenced this pull request Aug 7, 2026
Since #329331 the Claude model picker shows Copilot-proxy and native-Anthropic
models together, each carrying a provider-qualified id, and a session routes on
the provider of the model it was started with (resolveClaudeSessionTransport).
That makes the host-global override redundant — and, worse, incoherent.

Model enumeration (_refreshModels) gates the proxy half on holding a GitHub
token and the native half on detectExistingClaudeSetup; neither consulted the
setting. getProtectedResources() advertises the Copilot resource required:false
unconditionally. So `claudeUseCopilotProxy: false` never stopped Copilot-routed
Claude from being offered or used by a signed-in user, which is exactly what its
title ("Route Claude Through Copilot") promised. Its only remaining effect was
on the model-less fallback, where an explicit `true` broke the one state the
signed-out feature exists to serve: opt-in on, signed out, local Anthropic
credential present — forced to proxy and dead-ended on AHP_AUTH_REQUIRED.

resolveClaudeTransportMode drops its explicitProxy input and reduces to three:
opt-in off => proxy; signed in => proxy; else local setup => native; else proxy.

Clean deletion, not a deprecation: the key was never forwarded from a VS Code
setting (AgentHostRootConfigForwarder never managed it) and was only ever
hand-written into agent-host-config.json. A profile carrying a stale value
starts with no error and drops the key on the first config write.

Also corrects mergeClaudeModelCatalogs' claim that proxy-first ordering makes
Copilot the session default. It does not: the picker re-buckets by the _meta
vendor token, so the Anthropic group sorts first and the pre-selected model
routes native. Verified end-to-end.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Tyler James Leonhardt (TylerLeonhardt) added a commit that referenced this pull request Aug 8, 2026
…sts (#329718)

* agentHost: require GitHub for Claude unless a BYO-Anthropic setup exists

Since #329331 ClaudeAgent advertised the GitHub Copilot protected resource as
`required: false` unconditionally, on the reasoning that per-session routing
means no host-global mode can make Copilot strictly required. But `required` is
not a question about routing -- it is read only by
protectedResourcesRequireGitHubCopilotSignIn, which backs the Agents window gate
and the per-session-type gate. Answering it "optional" for a user with no
Anthropic credential claimed Claude was usable without GitHub when every model
the merged catalog can offer for them is Copilot-routed.

Restore the pre-#329331 condition: the resource is optional only when a local
BYOK setup is discovered. New private _hasUsableNativeSetup() is the single
"can Claude run without GitHub right now?" fact -- the allowSignedOutWhenUsable
opt-in AND detectExistingClaudeSetup (ANTHROPIC_API_KEY /
CLAUDE_CODE_OAUTH_TOKEN in the process env or the `env` block of
~/.claude/settings.json). _defaultTransportMode() now feeds hasExistingSetup
from the same helper, so the advertised requirement and the fallback transport
cannot disagree.

Two deliberate choices. The resource is still *kept* in the list rather than
dropped, so the silent probe survives: authenticateProtectedResources matches on
`resource` and ignores `required`, so an already-signed-in user still has a token
forwarded and authenticate() still acquires the proxy handle Copilot-routed
models need. And sign-in state is deliberately not an input -- whether GitHub is
*required* is a property of what the user can run without it, not of whether
they happen to be connected at this instant.

Making this derived rather than constant introduces a staleness risk, so it was
verified live: AgentSideEffects already republishes agent infos on
RootConfigChanged, and _publishAgentInfos' `equals` guard limits the dispatch to
real changes.

Verified end-to-end against a signed-out Agents window (--use-mock-keychain),
reading the published protectedResources off the AHP JSONL log:

  - no credential, opt-in on  => required=true,  "Sign in to use Agents" gate
  - ANTHROPIC_API_KEY, opt-in on  => required=false, window opens signed out
    with Claude selected and the discovered-config nudge
  - ANTHROPIC_API_KEY, opt-in off => required=true,  gate (kill switch holds)

The third run also exercised the republish path: the host started with a stale
`true` in its agent-host-config.json, published required=false, then flipped to
required=true 93ms after the client pushed the setting.

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

* agentHost: close the Copilot-optional test hole; trim comments

Review feedback on #329718.

The protected-resource tests left the opt-in at its default `false` for the
no-credential context, so the matrix never covered opt-in ON + no credential --
the exact regression this PR fixes. An implementation deciding `required` purely
from the opt-in passed every assertion. Replaced with the full 2x2; verified by
mutation (stubbing _hasUsableNativeSetup to read only the opt-in now fails on
`optInOnNoCredential`, and passed before).

Also trimmed the comments on _hasUsableNativeSetup and getProtectedResources to
the non-obvious contract. The JSDoc had claimed a credential appearing "takes
effect without restarting the host", which is wrong: nothing watches
~/.claude/settings.json at agent level (the customization watcher is per-session)
and AgentModelRefreshScheduler only ticks providers that have started a turn.
Claim removed rather than kept; the underlying staleness predates this change
via the models==0 path and is tracked separately.

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

agentHost: per-session provider (transport) selection for Claude sessions

3 participants