From 67eb75414b30401ac261031f7e47f7685a9a97bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 5 Sep 2025 15:59:28 +0200 Subject: [PATCH] chat - improve entitlement and sentiment handling (#265344) --- .../browser/chatParticipant.contribution.ts | 12 ++++-------- .../contrib/chat/browser/chatSessions.ts | 17 ++++------------- .../chat/common/chatEntitlementService.ts | 18 +++++++++--------- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts index 5c107f03499d..1bccbd54590f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts @@ -66,14 +66,10 @@ const chatViewDescriptor: IViewDescriptor[] = [{ }, ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.Panel }]), when: ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.hidden.negate(), - ChatContextKeys.Setup.disabled.negate() // do not pretend a working Chat view if extension is explicitly disabled - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.installed, - ChatContextKeys.Setup.disabled.negate() // do not pretend a working Chat view if extension is explicitly disabled - ), + ContextKeyExpr.or( + ChatContextKeys.Setup.hidden, + ChatContextKeys.Setup.disabled + )?.negate(), ChatContextKeys.panelParticipantRegistered, ChatContextKeys.extensionInvalid ) diff --git a/src/vs/workbench/contrib/chat/browser/chatSessions.ts b/src/vs/workbench/contrib/chat/browser/chatSessions.ts index 50dc5561ba1c..5ae9187ad9b7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSessions.ts @@ -82,6 +82,7 @@ import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRend import { allowedChatMarkdownHtmlTags } from './chatMarkdownRenderer.js'; import product from '../../../../platform/product/common/product.js'; import { truncate } from '../../../../base/common/strings.js'; +import { IChatEntitlementService } from '../common/chatEntitlementService.js'; export const VIEWLET_ID = 'workbench.view.chat.sessions'; @@ -272,7 +273,7 @@ export class ChatSessionsView extends Disposable implements IWorkbenchContributi @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IChatSessionsService private readonly chatSessionsService: IChatSessionsService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IChatEntitlementService private readonly chatEntitlementService: IChatEntitlementService ) { super(); @@ -318,19 +319,9 @@ export class ChatSessionsView extends Disposable implements IWorkbenchContributi return; } - const copilotEnabledExpr = ContextKeyExpr.or( - ContextKeyExpr.and( - ChatContextKeys.Setup.hidden.negate(), - ChatContextKeys.Setup.disabled.negate() - ), - ContextKeyExpr.and( - ChatContextKeys.Setup.installed, - ChatContextKeys.Setup.disabled.negate() - )); - const isCopilotEnabled = this.contextKeyService.contextMatchesRules(copilotEnabledExpr); - if (!isCopilotEnabled) { - return; + if (this.chatEntitlementService.sentiment.hidden || this.chatEntitlementService.sentiment.disabled) { + return; // do not register container as AI features are hidden or disabled } Registry.as(Extensions.ViewContainersRegistry).registerViewContainer( diff --git a/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts b/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts index b412ae1b9329..1c5284f93887 100644 --- a/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts @@ -1013,7 +1013,7 @@ export class ChatEntitlementContext extends Disposable { private updateBarrier: Barrier | undefined = undefined; constructor( - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IStorageService private readonly storageService: IStorageService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @ILogService private readonly logService: ILogService, @@ -1101,9 +1101,11 @@ export class ChatEntitlementContext extends Disposable { update(context: { hidden: false }): Promise; // legacy UI state from before we had a setting to hide, keep around to still support users who used this update(context: { later: boolean }): Promise; update(context: { entitlement: ChatEntitlement; organisations: string[] | undefined; sku: string | undefined }): Promise; - update(context: { installed?: boolean; disabled?: boolean; untrusted?: boolean; hidden?: false; later?: boolean; entitlement?: ChatEntitlement; organisations?: string[]; sku?: string }): Promise { + async update(context: { installed?: boolean; disabled?: boolean; untrusted?: boolean; hidden?: false; later?: boolean; entitlement?: ChatEntitlement; organisations?: string[]; sku?: string }): Promise { this.logService.trace(`[chat entitlement context] update(): ${JSON.stringify(context)}`); + const oldState = JSON.stringify(this._state); + if (typeof context.installed === 'boolean' && typeof context.disabled === 'boolean' && typeof context.untrusted === 'boolean') { this._state.installed = context.installed; this._state.disabled = context.disabled; @@ -1134,6 +1136,10 @@ export class ChatEntitlementContext extends Disposable { } } + if (oldState === JSON.stringify(this._state)) { + return; // state did not change + } + this.storageService.store(ChatEntitlementContext.CHAT_ENTITLEMENT_CONTEXT_STORAGE_KEY, { ...this._state, later: undefined // do not persist this across restarts for now @@ -1160,13 +1166,7 @@ export class ChatEntitlementContext extends Disposable { this.businessContextKey.set(state.entitlement === ChatEntitlement.Business); this.enterpriseContextKey.set(state.entitlement === ChatEntitlement.Enterprise); - const organisations = this.contextKeyService.getContextKeyValue(ChatContextKeys.Entitlement.organisations.key); - const currentSet = new Set(organisations); - const newSet = new Set(state.organisations); - if (currentSet.size !== newSet.size || !Array.from(currentSet).every(org => newSet.has(org))) { - this.organisationsContextKey.set(state.organisations); - } - + this.organisationsContextKey.set(state.organisations); this.isInternalContextKey.set(Boolean(state.organisations?.some(org => org === 'github' || org === 'microsoft'))); this.skuContextKey.set(state.sku);