diff --git a/src/vs/platform/agentHost/node/claude/claudeAgent.ts b/src/vs/platform/agentHost/node/claude/claudeAgent.ts index e488182512aa90..339fe009e471c7 100644 --- a/src/vs/platform/agentHost/node/claude/claudeAgent.ts +++ b/src/vs/platform/agentHost/node/claude/claudeAgent.ts @@ -48,16 +48,20 @@ import { ClaudeSessionMetadataStore, IClaudeSessionOverlay } from './claudeSessi * Returns true if `m` is a Claude-family model that should be advertised * to clients picking a model for the Claude provider. * - * Combines the same surface checks the extension uses (vendor, picker - * eligibility, tool-call support, `/v1/messages` endpoint) with a parse - * of the model id via {@link tryParseClaudeModelId}, which excludes - * synthetic ids like `auto` that aren't real Claude endpoints. + * Intentionally does NOT check `model_picker_enabled`: CAPI sets that flag + * conservatively (false for many models), and the Copilot extension overrides + * it via an A/B experiment (`copilotchat.showInModelPicker`) so the normal + * Claude Code window shows all capable models. The agent host has no access + * to that experiment service, so relying on the raw flag would cause it to + * show a much smaller set than the extension window does. The remaining four + * conditions (Anthropic vendor, `/v1/messages` endpoint, tool-call support, + * parseable model ID) are sufficient guards against synthetic or ineligible + * entries. */ function isClaudeModel(m: CCAModel): boolean { return ( m.vendor === 'Anthropic' && !!m.supported_endpoints?.includes('/v1/messages') && - !!m.model_picker_enabled && !!m.capabilities?.supports?.tool_calls && tryParseClaudeModelId(m.id) !== undefined ); diff --git a/src/vs/platform/agentHost/test/node/claudeAgent.test.ts b/src/vs/platform/agentHost/test/node/claudeAgent.test.ts index 41664ae2219e02..db72bb9649c315 100644 --- a/src/vs/platform/agentHost/test/node/claudeAgent.test.ts +++ b/src/vs/platform/agentHost/test/node/claudeAgent.test.ts @@ -694,6 +694,12 @@ suite('ClaudeAgent', () => { }); test('authenticate populates models filtered to Claude family', async () => { + // model_picker_enabled is intentionally NOT checked — CAPI sets it + // conservatively and the Copilot extension overrides it via + // experiments; the agent host has no experiment service, so we rely + // on vendor/endpoint/tool-call/model-id guards instead. + // ANTHROPIC_PICKER_DISABLED (claude-opus-4.5, model_picker_enabled:false) + // must appear here alongside the other Claude models. const { agent, proxy } = createTestContext(disposables); const accepted = await agent.authenticate('https://api.github.com', 'tok'); @@ -709,6 +715,7 @@ suite('ClaudeAgent', () => { models: [ { provider: 'claude', id: 'claude-opus-4.6', name: 'Claude Opus 4.6', maxContextWindow: 200_000, supportsVision: false, policyState: 'enabled', _meta: { multiplierNumeric: 1 } }, { provider: 'claude', id: 'claude-sonnet-4.6', name: 'Claude Sonnet 4.6', maxContextWindow: 200_000, supportsVision: false, policyState: 'enabled', _meta: { multiplierNumeric: 1 } }, + { provider: 'claude', id: 'claude-opus-4.5', name: 'Claude Opus 4.5', maxContextWindow: 200_000, supportsVision: false, policyState: 'enabled', _meta: { multiplierNumeric: 1 } }, ], }); }); @@ -940,20 +947,22 @@ suite('ClaudeAgent', () => { accepted: true, startTokens: ['tok', 'tok'], disposeCount: 0, - modelIds: [CLAUDE_OPUS.id, CLAUDE_SONNET.id], + modelIds: [CLAUDE_OPUS.id, CLAUDE_SONNET.id, ANTHROPIC_PICKER_DISABLED.id], }); }); test('model filter excludes non-Claude entries', async () => { // Same fixture set as the populate test, but assert on ids only — // catches every exclusion criterion in one snapshot. + // Note: model_picker_enabled is NOT a filter criterion (see isClaudeModel); + // ANTHROPIC_PICKER_DISABLED (claude-opus-4.5) therefore appears here. const { agent } = createTestContext(disposables); await agent.authenticate('https://api.github.com', 'tok'); await tick(); assert.deepStrictEqual( agent.models.get().map(m => m.id), - ['claude-opus-4.6', 'claude-sonnet-4.6'], + ['claude-opus-4.6', 'claude-sonnet-4.6', 'claude-opus-4.5'], ); });