1
>
/*---------------------------------------------------------------------------------------------
chatEntitlementService.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import product from '../../../../platform/product/common/product.js';
7
>
import { Barrier } from '../../../../base/common/async.js';
8
>
import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
9
>
import { Emitter, Event } from '../../../../base/common/event.js';
10
>
import { Lazy } from '../../../../base/common/lazy.js';
11
>
import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
12
>
import { IRequestContext } from '../../../../base/parts/request/common/request.js';
13
>
import { localize } from '../../../../nls.js';
14
>
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
15
>
import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
16
>
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
17
>
import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
18
>
import { ILogService, LogLevel } from '../../../../platform/log/common/log.js';
19
>
import { IProductService } from '../../../../platform/product/common/productService.js';
20
>
import { asText, IRequestService } from '../../../../platform/request/common/request.js';
21
>
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
22
>
import { ITelemetryService, TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js';
23
>
import { AuthenticationSession, IAuthenticationService } from '../../authentication/common/authentication.js';
24
>
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
25
>
import { URI } from '../../../../base/common/uri.js';
26
>
import Severity from '../../../../base/common/severity.js';
27
>
import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
28
>
import { isWeb } from '../../../../base/common/platform.js';
29
>
import { ILifecycleService } from '../../lifecycle/common/lifecycle.js';
30
>
import { Mutable } from '../../../../base/common/types.js';
31
>
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
32
>
import { IObservable, observableFromEvent } from '../../../../base/common/observable.js';
33
>
import { IDefaultAccountService } from '../../../../platform/defaultAccount/common/defaultAccount.js';
34
>
import { IDefaultAccount, IEntitlementsData } from '../../../../base/common/defaultAccount.js';
35
>
36
>
export namespace ChatEntitlementContextKeys {
37
>
38
>
export const Setup = {
39
>
hidden: new RawContextKey<boolean>('chatSetupHidden', false, true), // True when chat setup is explicitly hidden.
40
>
installed: new RawContextKey<boolean>('chatSetupInstalled', false, true), // True when the chat extension is installed and enabled.
41
>
disabled: new RawContextKey<boolean>('chatSetupDisabled', false, true), // True when the chat extension is disabled due to any other reason than workspace trust.
42
>
disabledInWorkspace: new RawContextKey<boolean>('chatSetupDisabledInWorkspace', false, true), // True when chat is disabled at the workspace level via settings.
43
>
untrusted: new RawContextKey<boolean>('chatSetupUntrusted', false, true), // True when the chat extension is disabled due to workspace trust.
44
>
later: new RawContextKey<boolean>('chatSetupLater', false, true), // True when the user wants to finish setup later.
45
>
registered: new RawContextKey<boolean>('chatSetupRegistered', false, true), // True when the user has registered as Free or Pro user.
46
>
completed: new RawContextKey<boolean>('chatSetupCompleted', false, true) // True when the user has completed the setup flow, regardless of the outcome.
47
>
};
48
>
49
>
export const Entitlement = {
50
>
signedOut: new RawContextKey<boolean>('chatEntitlementSignedOut', false, true), // True when user is signed out.
51
>
canSignUp: new RawContextKey<boolean>('chatPlanCanSignUp', false, true), // True when user can sign up to be a chat free user.
52
>
53
>
planFree: new RawContextKey<boolean>('chatPlanFree', false, true), // True when user is a chat free user.
54
>
planPro: new RawContextKey<boolean>('chatPlanPro', false, true), // True when user is a chat pro user.
55
>
planEdu: new RawContextKey<boolean>('chatPlanEdu', false, true), // True when user is a chat edu user.
56
>
planProPlus: new RawContextKey<boolean>('chatPlanProPlus', false, true), // True when user is a chat pro plus user.
57
>
planMax: new RawContextKey<boolean>('chatPlanMax', false, true), // True when user is a chat max user.
58
>
planBusiness: new RawContextKey<boolean>('chatPlanBusiness', false, true), // True when user is a chat business user.
59
>
planEnterprise: new RawContextKey<boolean>('chatPlanEnterprise', false, true), // True when user is a chat enterprise user.
60
>
61
>
organisations: new RawContextKey<string[]>('chatEntitlementOrganisations', undefined, true), // The organizations the user belongs to.
62
>
internal: new RawContextKey<boolean>('chatEntitlementInternal', false, true), // True when user belongs to internal organisation.
63
>
sku: new RawContextKey<string>('chatEntitlementSku', undefined, true), // The SKU of the user.
64
>
};
65
>
66
>
export const chatQuotaExceeded = new RawContextKey<boolean>('chatQuotaExceeded', false, true);
67
>
export const completionsQuotaExceeded = new RawContextKey<boolean>('completionsQuotaExceeded', false, true);
68
>
69
>
export const chatAnonymous = new RawContextKey<boolean>('chatAnonymous', false, true);
70
>
71
>
export const clientByokEnabled = new RawContextKey<boolean>('github.copilot.clientByokEnabled', true, true);
72
>
73
>
export const hasByokModels = new RawContextKey<boolean>('github.copilot.hasByokModels', false, true);
74
>
}
75
>
76
>
export const IChatEntitlementService = createDecorator<IChatEntitlementService>('chatEntitlementService');
77
>
78
>
export enum ChatEntitlement {
79
>
/** Signed out */
80
>
Unknown = 1,
81
>
/** Signed in but not yet resolved */
82
>
Unresolved = 2,
83
>
/** Signed in and entitled to Free */
84
>
Available = 3,
85
>
/** Signed in but not entitled to Free */
86
>
Unavailable = 4,
87
>
/** Signed-up to Free */
88
>
Free = 5,
89
>
/** Signed-up to EDU */
90
>
EDU = 10,
91
>
/** Signed-up to Pro */
92
>
Pro = 6,
93
>
/** Signed-up to Pro Plus */
94
>
ProPlus = 7,
95
>
/** Signed-up to Business */
96
>
Business = 8,
97
>
/** Signed-up to Enterprise */
98
>
Enterprise = 9,
99
>
/** Signed-up to Max */
100
>
Max = 11,
101
>
}
102
>
103
>
export interface IChatSentiment {
104
>
105
>
/**
106
>
* Whether the user has completed the setup flow or not, regardless of the outcome
107
>
*/
108
>
completed?: boolean;
109
>
110
>
/**
111
>
* User has Chat installed.
112
>
*/
113
>
installed?: boolean;
114
>
115
>
/**
116
>
* User signals no intent in using Chat.
117
>
*
118
>
* Note: in contrast to `disabled`, this should not only disable
119
>
* Chat but also hide all of its UI.
120
>
*/
121
>
hidden?: boolean;
122
>
123
>
/**
124
>
* User signals intent to disable Chat.
125
>
*
126
>
* Note: in contrast to `hidden`, this should not hide
127
>
* Chat but but disable its functionality.
128
>
*/
129
>
disabled?: boolean;
130
>
131
>
/**
132
>
* Chat is disabled at the workspace level
133
>
*
134
>
* Note: in contrast to `hidden` (which hides all UI globally),
135
>
* this only disables Chat in the current workspace while
136
>
* keeping its UI visible so the user can re-enable it.
137
>
*/
138
>
disabledInWorkspace?: boolean;
139
>
140
>
/**
141
>
* Chat is disabled due to missing workspace trust.
142
>
*
143
>
* Note: even though this disables Chat, we want to treat it
144
>
* different from the `disabled` state that is by explicit
145
>
* user choice.
146
>
*/
147
>
untrusted?: boolean;
148
>
149
>
/**
150
>
* User signals intent to use Chat later.
151
>
*/
152
>
later?: boolean;
153
>
154
>
/**
155
>
* User has registered as Free or Pro user.
156
>
*/
157
>
registered?: boolean;
158
>
}
159
>
160
>
/**
161
>
* The inputs needed to decide whether Chat still requires the user to run setup
162
>
* (sign in / sign up / trust / enable) before it can service a request.
163
>
*/
164
>
export interface IChatSetupRequirement {
165
>
/** Whether the setup flow has been completed (any outcome). */
166
>
readonly completed: boolean;
167
>
/** Whether the chat extension is disabled for a reason other than trust. */
168
>
readonly disabled: boolean;
169
>
/** Whether the chat extension is disabled because the workspace is untrusted. */
170
>
readonly untrusted: boolean;
171
>
/** The user's last known or resolved entitlement. */
172
>
readonly entitlement: ChatEntitlement;
173
>
/** Whether anonymous (signed-out) Chat access is enabled. */
174
>
readonly anonymous: boolean;
175
>
/** Whether BYOK models are available. */
176
>
readonly hasByokModels: boolean;
177
>
}
178
>
179
>
/**
180
>
* Single source of truth for whether Chat still requires setup before it can
181
>
* service a request. Shared by the setup agent (which routes a sent message
182
>
* through setup) and the model picker (which surfaces a "Sign in to use Copilot"
183
>
* state instead of a misleading lone "Auto"). BYOK models and anonymous access
184
>
* intentionally satisfy the entitlement-based checks so those flows keep working.
185
>
*/
186
>
export function chatRequiresSetup(context: IChatSetupRequirement): boolean {
187
return (
188
(!context.completed && !context.hasByokModels) || // Setup not completed (unless BYOK models are available)