import { Disposable } from "@codingame/monaco-vscode-api/vscode/vs/base/common/lifecycle"; import { IConfigurationService } from "@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configuration.service"; import { IContextKeyService } from "@codingame/monaco-vscode-api/vscode/vs/platform/contextkey/common/contextkey.service"; import { IStorageService } from "@codingame/monaco-vscode-api/vscode/vs/platform/storage/common/storage.service"; import { IWorkbenchContribution } from "@codingame/monaco-vscode-api/vscode/vs/workbench/common/contributions"; import { IExtensionService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/services/extensions/common/extensions.service"; import { ILanguageModelsConfigurationService } from "@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/chat/common/languageModelsConfiguration.service"; /** * Owns the `github.copilot.hasByokModels` context key. The key is true iff: * - `github.copilot.clientByokEnabled` is true (set by `ChatEntitlementService` + Copilot extension), * - `chat.offlineByok` is enabled and `chat.aiDisabled` is off, and * - the `LanguageModelsService` has at least one resolved, user-selectable non-Copilot model * (signalled by the `chatNonCopilotModelsAreUserSelectable` context key). * * Verifying the last condition is expensive: it requires activating each BYOK-contributing * extension and round-tripping into its provider. To avoid paying that cost just to gate UI, * this contribution never triggers such activations itself. The strategy is: * * 1. On startup, optimistically restore the last persisted answer from * `chat.hasByokModels.lastKnown` so UI surfaces gated by this key are correct on warm * reload before anything else runs. * 2. Whenever the `chatNonCopilotModelsAreUserSelectable` signal flips on — which happens * naturally when something else resolves a BYOK vendor (notably `ChatInputPart`, which * activates the previously selected model's vendor when restoring its persisted selection) — * record `true` and persist it. * 3. Once extensions are fully scanned, if there are no configured non-Copilot vendors in the * language-models configuration, override a stale optimistic `true` with `false`. Same on * runtime removal of all groups, and when the feature flag flips off. * * The trade-off is the first-time experience: a brand-new user must pick a BYOK model once * before this contribution observes the signal and persists the answer. From then on, the * answer survives reloads without any activation cost — users without BYOK pay nothing. * * Eager so the key is bound at workbench startup before any sign-in UI surfaces render. */ export declare class HasByokModelsContribution extends Disposable implements IWorkbenchContribution { private readonly _languageModelsConfigurationService; private readonly _contextKeyService; private readonly _configurationService; private readonly _storageService; static readonly ID = "workbench.contrib.hasByokModels"; private static readonly STORAGE_KEY_LAST_KNOWN; private static readonly TRACKED_KEYS; private readonly _hasByokModels; private _extensionsRegistered; constructor(_languageModelsConfigurationService: ILanguageModelsConfigurationService, _contextKeyService: IContextKeyService, _configurationService: IConfigurationService, _storageService: IStorageService, extensionService: IExtensionService); private _isFeatureEnabled; private _restore; private _setResult; private _update; }