diff --git a/product.json b/product.json index fe7d07a079b..af053a48660 100644 --- a/product.json +++ b/product.json @@ -116,6 +116,7 @@ "name": "Apple" } }, + "providerExtensionId": "vscode.github-authentication", "providerUriSetting": "github-enterprise.uri", "providerScopes": [ [ diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 2602572ba07..8325d6789f0 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -360,6 +360,7 @@ export interface IDefaultChatAgent { apple: { id: string; name: string }; }; + readonly providerExtensionId: string; readonly providerUriSetting: string; readonly providerScopes: string[][]; diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetup.ts index 5a059de225b..868420e051d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetup.ts @@ -4,11 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { ICommandService } from '../../../../../platform/commands/common/commands.js'; +import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; import product from '../../../../../platform/product/common/product.js'; +import { localize } from '../../../../../nls.js'; +import { EnablementState } from '../../../../services/extensionManagement/common/extensionManagement.js'; +import { IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js'; const defaultChat = { completionsRefreshTokenCommand: product.defaultChatAgent?.completionsRefreshTokenCommand ?? '', chatRefreshTokenCommand: product.defaultChatAgent?.chatRefreshTokenCommand ?? '', + providerExtensionId: product.defaultChatAgent?.providerExtensionId ?? '', }; export type InstallChatClassification = { @@ -59,3 +65,44 @@ export function refreshTokens(commandService: ICommandService): void { commandService.executeCommand(defaultChat.completionsRefreshTokenCommand); commandService.executeCommand(defaultChat.chatRefreshTokenCommand); } + +/** + * Ensures the authentication provider extension is enabled. + * If the extension is found locally but disabled, it will be + * re-enabled and running extensions will be updated. + * + * @returns `true` if the extension was re-enabled, `false` otherwise. + */ +export async function maybeEnableAuthExtension( + extensionsWorkbenchService: IExtensionsWorkbenchService, + logService: ILogService +): Promise { + if (!defaultChat.providerExtensionId) { + return false; + } + + const providerExtension = extensionsWorkbenchService.local.find( + e => ExtensionIdentifier.equals(e.identifier.id, defaultChat.providerExtensionId) + ); + + if (!providerExtension) { + return false; + } + + if ( + providerExtension.enablementState === EnablementState.DisabledGlobally || + providerExtension.enablementState === EnablementState.DisabledWorkspace + ) { + logService.info(`[chat setup] auth provider extension '${defaultChat.providerExtensionId}' is disabled, re-enabling it`); + try { + await extensionsWorkbenchService.setEnablement([providerExtension], EnablementState.EnabledGlobally); + await extensionsWorkbenchService.updateRunningExtensions(localize('enableAuthExtension', "Enabling GitHub Authentication")); + return true; + } catch (error) { + logService.error(`[chat setup] failed to re-enable auth provider extension '${defaultChat.providerExtensionId}'`, error); + return false; + } + } + + return false; +} diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupController.ts b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupController.ts index 0379543f996..eb5a4f427b9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupController.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupController.ts @@ -28,7 +28,7 @@ import { IExtensionsWorkbenchService } from '../../../extensions/common/extensio import { ChatEntitlement, ChatEntitlementContext, ChatEntitlementRequests, isProUser } from '../../../../services/chat/common/chatEntitlementService.js'; import { CHAT_OPEN_ACTION_ID } from '../actions/chatActions.js'; import { ChatViewId, ChatViewContainerId } from '../chat.js'; -import { ChatSetupAnonymous, ChatSetupStep, ChatSetupResultValue, InstallChatEvent, InstallChatClassification, refreshTokens } from './chatSetup.js'; +import { ChatSetupAnonymous, ChatSetupStep, ChatSetupResultValue, InstallChatEvent, InstallChatClassification, refreshTokens, maybeEnableAuthExtension } from './chatSetup.js'; import { IDefaultAccount } from '../../../../../base/common/defaultAccount.js'; import { IDefaultAccountService } from '../../../../../platform/defaultAccount/common/defaultAccount.js'; @@ -153,6 +153,11 @@ export class ChatSetupController extends Disposable { } private async signIn(options: IChatSetupControllerOptions): Promise<{ defaultAccount: IDefaultAccount | undefined; entitlement: ChatEntitlement | undefined }> { + const authExtensionReEnabled = await maybeEnableAuthExtension(this.extensionsWorkbenchService, this.logService); + if (authExtensionReEnabled) { + refreshTokens(this.commandService); + } + let entitlements; let defaultAccount; try { diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts index 14541ee5f4b..1ff128a1f67 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup/chatSetupProviders.ts @@ -47,13 +47,14 @@ import { ACTION_START as INLINE_CHAT_START } from '../../../inlineChat/common/in import { IPosition } from '../../../../../editor/common/core/position.js'; import { IMarker, IMarkerService, MarkerSeverity } from '../../../../../platform/markers/common/markers.js'; import { ChatSetupController } from './chatSetupController.js'; -import { ChatSetupAnonymous, ChatSetupStep, IChatSetupResult } from './chatSetup.js'; +import { ChatSetupAnonymous, ChatSetupStep, IChatSetupResult, maybeEnableAuthExtension, refreshTokens } from './chatSetup.js'; import { ChatSetup } from './chatSetupRunner.js'; import { chatViewsWelcomeRegistry } from '../viewsWelcome/chatViewsWelcome.js'; import { CommandsRegistry, ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IDefaultAccountService } from '../../../../../platform/defaultAccount/common/defaultAccount.js'; import { IHostService } from '../../../../services/host/browser/host.js'; import { IOutputService } from '../../../../services/output/common/output.js'; +import { IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -197,6 +198,8 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { @IViewsService private readonly viewsService: IViewsService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IOutputService private readonly outputService: IOutputService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @ICommandService private readonly commandService: ICommandService, ) { super(); @@ -315,6 +318,15 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation { } private async doForwardRequestToChatWhenReady(requestModel: IChatRequestModel, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatAgentService: IChatAgentService, chatWidgetService: IChatWidgetService, languageModelToolsService: ILanguageModelToolsService): Promise { + + // Ensure auth extension is enabled before waiting for chat readiness. + // This must run before the readiness event listeners are set up because + // updateRunningExtensions restarts all extension hosts. + const authExtensionReEnabled = await maybeEnableAuthExtension(this.extensionsWorkbenchService, this.logService); + if (authExtensionReEnabled) { + refreshTokens(this.commandService); + } + const widget = chatWidgetService.getWidgetBySessionResource(requestModel.session.sessionResource); const modeInfo = widget?.input.currentModeInfo;