chat - improve entitlement and sentiment handling (#265344)

This commit is contained in:
Benjamin Pasero
2025-09-05 13:59:28 +00:00
committed by GitHub
parent 388785d3f7
commit 67eb75414b
3 changed files with 17 additions and 30 deletions
@@ -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
)
@@ -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<IViewContainersRegistry>(Extensions.ViewContainersRegistry).registerViewContainer(
@@ -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<void>; // 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<void>;
update(context: { entitlement: ChatEntitlement; organisations: string[] | undefined; sku: string | undefined }): Promise<void>;
update(context: { installed?: boolean; disabled?: boolean; untrusted?: boolean; hidden?: false; later?: boolean; entitlement?: ChatEntitlement; organisations?: string[]; sku?: string }): Promise<void> {
async update(context: { installed?: boolean; disabled?: boolean; untrusted?: boolean; hidden?: false; later?: boolean; entitlement?: ChatEntitlement; organisations?: string[]; sku?: string }): Promise<void> {
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<string[]>(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);