From aae3af1c044bd96c9c0004645348600a76e656b5 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Tue, 17 Feb 2026 20:34:36 +1100 Subject: [PATCH] Hide models contributed by background agents (#295669) * Hide models contributed by background agents * Updates * Add additioknal tests --- .../browser/widget/input/chatInputPart.ts | 3 +- .../widget/input/modelPickerActionItem.ts | 4 +- .../promptHeaderAutocompletion.ts | 2 +- .../promptHeaderAutocompletion.test.ts | 41 +++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts index a6493ae2c62..8800e0f570d 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/chatInputPart.ts @@ -2172,7 +2172,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.setCurrentLanguageModel(model); this.renderAttachedContext(); }, - getModels: () => this.getModels() + getModels: () => this.getModels(), + canManageModels: () => !this.getCurrentSessionType() }; return this.modelWidget = this.instantiationService.createInstance(ModelPickerActionItem, action, undefined, itemDelegate, pickerOptions); } else if (action.id === OpenModePickerAction.ID && action instanceof MenuItemAction) { diff --git a/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts b/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts index 5a64dde9506..525e9ee8236 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/input/modelPickerActionItem.ts @@ -29,6 +29,7 @@ export interface IModelPickerDelegate { readonly currentModel: IObservable; setModel(model: ILanguageModelChatMetadataAndIdentifier): void; getModels(): ILanguageModelChatMetadataAndIdentifier[]; + canManageModels(): boolean; } type ChatModelChangeClassification = { @@ -165,9 +166,10 @@ export class ModelPickerActionItem extends ChatInputPickerActionViewItem { run: () => { } }; + const baseActionBarActionProvider = getModelPickerActionBarActionProvider(commandService, chatEntitlementService, productService); const modelPickerActionWidgetOptions: Omit = { actionProvider: modelDelegateToWidgetActionsProvider(delegate, telemetryService, pickerOptions), - actionBarActionProvider: getModelPickerActionBarActionProvider(commandService, chatEntitlementService, productService), + actionBarActionProvider: { getActions: () => delegate.canManageModels() ? baseActionBarActionProvider.getActions() : [] }, reporter: { id: 'ChatModelPicker', name: 'ChatModelPicker', includeOptions: true }, }; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptHeaderAutocompletion.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptHeaderAutocompletion.ts index 3ca82e90d52..c02f3dfcd53 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptHeaderAutocompletion.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageProviders/promptHeaderAutocompletion.ts @@ -317,7 +317,7 @@ export class PromptHeaderAutocompletion implements CompletionItemProvider { const result = []; for (const model of this.languageModelsService.getLanguageModelIds()) { const metadata = this.languageModelsService.lookupLanguageModel(model); - if (metadata && metadata.isUserSelectable !== false) { + if (metadata && metadata.isUserSelectable !== false && !metadata.targetChatSessionType) { if (!agentModeOnly || ILanguageModelChatMetadata.suitableForAgentMode(metadata)) { result.push({ name: ILanguageModelChatMetadata.asQualifiedName(metadata), diff --git a/src/vs/workbench/contrib/chat/test/browser/promptSyntax/languageProviders/promptHeaderAutocompletion.test.ts b/src/vs/workbench/contrib/chat/test/browser/promptSyntax/languageProviders/promptHeaderAutocompletion.test.ts index db6a9d11f99..fe90e7a434e 100644 --- a/src/vs/workbench/contrib/chat/test/browser/promptSyntax/languageProviders/promptHeaderAutocompletion.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/promptSyntax/languageProviders/promptHeaderAutocompletion.test.ts @@ -56,6 +56,7 @@ suite('PromptHeaderAutocompletion', () => { { id: 'mae-4', name: 'MAE 4', vendor: 'olama', version: '1.0', family: 'mae', modelPickerCategory: undefined, extension: new ExtensionIdentifier('a.b'), isUserSelectable: true, maxInputTokens: 8192, maxOutputTokens: 1024, capabilities: { agentMode: true, toolCalling: true }, isDefaultForLocation: { [ChatAgentLocation.Chat]: true } } satisfies ILanguageModelChatMetadata, { id: 'mae-4.1', name: 'MAE 4.1', vendor: 'copilot', version: '1.0', family: 'mae', modelPickerCategory: undefined, extension: new ExtensionIdentifier('a.b'), isUserSelectable: true, maxInputTokens: 8192, maxOutputTokens: 1024, capabilities: { agentMode: true, toolCalling: true }, isDefaultForLocation: { [ChatAgentLocation.Chat]: true } } satisfies ILanguageModelChatMetadata, { id: 'gpt-4', name: 'GPT 4', vendor: 'openai', version: '1.0', family: 'gpt', modelPickerCategory: undefined, extension: new ExtensionIdentifier('a.b'), isUserSelectable: true, maxInputTokens: 8192, maxOutputTokens: 1024, capabilities: { agentMode: false, toolCalling: true }, isDefaultForLocation: { [ChatAgentLocation.Chat]: true } } satisfies ILanguageModelChatMetadata, + { id: 'bg-agent-model', name: 'BG Agent Model', vendor: 'copilot', version: '1.0', family: 'bg', modelPickerCategory: undefined, extension: new ExtensionIdentifier('a.b'), isUserSelectable: true, maxInputTokens: 8192, maxOutputTokens: 1024, capabilities: { agentMode: true, toolCalling: true }, isDefaultForLocation: { [ChatAgentLocation.Chat]: true }, targetChatSessionType: 'background' } satisfies ILanguageModelChatMetadata, ]; instaService.stub(ILanguageModelsService, { @@ -361,6 +362,33 @@ suite('PromptHeaderAutocompletion', () => { { label: 'true', result: 'disable-model-invocation: true' }, ].sort(sortByLabel)); }); + + test('exclude models with targetChatSessionType from agent model completions', async () => { + const content = [ + '---', + 'description: "Test"', + 'model: |', + '---', + ].join('\n'); + + const actual = await getCompletions(content, PromptsType.agent); + const labels = actual.map(a => a.label); + // BG Agent Model has targetChatSessionType set, so it should be excluded + assert.ok(!labels.includes('BG Agent Model (copilot)'), 'Models with targetChatSessionType should be excluded from agent model completions'); + }); + + test('exclude models with targetChatSessionType from agent model array completions', async () => { + const content = [ + '---', + 'description: "Test"', + 'model: [|]', + '---', + ].join('\n'); + + const actual = await getCompletions(content, PromptsType.agent); + const labels = actual.map(a => a.label); + assert.ok(!labels.includes('BG Agent Model (copilot)'), 'Models with targetChatSessionType should be excluded from agent model array completions'); + }); }); suite('claude agent header completions', () => { @@ -546,5 +574,18 @@ suite('PromptHeaderAutocompletion', () => { { label: 'GPT 4 (openai)', result: 'model: GPT 4 (openai)' }, ].sort(sortByLabel)); }); + + test('exclude models with targetChatSessionType from prompt model completions', async () => { + const content = [ + '---', + 'description: "Test"', + 'model: |', + '---', + ].join('\n'); + + const actual = await getCompletions(content, PromptsType.prompt); + const labels = actual.map(a => a.label); + assert.ok(!labels.includes('BG Agent Model (copilot)'), 'Models with targetChatSessionType should be excluded from prompt model completions'); + }); }); });