diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 621bfe282fa..a47b28b6da1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -107,24 +107,42 @@ class SetupChatAgentImplementation extends Disposable implements IChatAgentImple ChatContextKeys.Setup.fromDialog ); - const id = location === ChatAgentLocation.Panel ? 'setup.chat' : isToolsAgent ? 'setup.agent' : 'setup.edits'; - - const welcomeMessageContent: IChatWelcomeMessageContent = location === ChatAgentLocation.Panel ? - { - title: localize('chatTitle', "Ask Copilot"), - message: new MarkdownString(localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.")), - icon: Codicon.copilotLarge - } : isToolsAgent ? - { - title: localize('editsTitle', "Edit with Copilot"), - message: new MarkdownString(localize('agentMessage', "Ask Copilot to edit your files in agent mode. Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.")), - icon: Codicon.copilotLarge - } : - { - title: localize('editsTitle', "Edit with Copilot"), - message: new MarkdownString(localize('editsMessage', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.")), + let id: string; + let description = localize('chatDescription', "Ask Copilot"); + let welcomeMessageContent: IChatWelcomeMessageContent | undefined; + switch (location) { + case ChatAgentLocation.Panel: + id = 'setup.chat'; + welcomeMessageContent = { + title: description, + message: new MarkdownString(localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.")), icon: Codicon.copilotLarge }; + break; + case ChatAgentLocation.EditingSession: + id = isToolsAgent ? 'setup.agent' : 'setup.edits'; + description = isToolsAgent ? localize('agentDescription', "Edit files in your workspace in agent mode (Experimental)") : localize('editsDescription', "Edit files in your workspace"); + welcomeMessageContent = isToolsAgent ? + { + title: localize('editsTitle', "Edit with Copilot"), + message: new MarkdownString(localize('agentMessage', "Ask Copilot to edit your files in agent mode. Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.")), + icon: Codicon.copilotLarge + } : + { + title: localize('editsTitle', "Edit with Copilot"), + message: new MarkdownString(localize('editsMessage', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.")), + icon: Codicon.copilotLarge + }; + break; + case ChatAgentLocation.Terminal: + id = 'setup.terminal'; + break; + case ChatAgentLocation.Editor: + id = 'setup.editor'; + break; + default: + throw new Error(`Unsupported location: ${location}`); + } const disposable = new DisposableStore(); @@ -141,7 +159,7 @@ class SetupChatAgentImplementation extends Disposable implements IChatAgentImple welcomeMessageContent, helpTextPrefix: SetupChatAgentImplementation.SETUP_WARNING }, - description: location === ChatAgentLocation.Panel ? localize('chatDescription', "Ask Copilot") : isToolsAgent ? localize('agentDescription', "Edit files in your workspace in agent mode (Experimental)") : localize('editsDescription', "Edit files in your workspace"), + description, extensionId: nullExtensionDescription.identifier, extensionDisplayName: nullExtensionDescription.name, extensionPublisherId: nullExtensionDescription.publisher @@ -204,7 +222,7 @@ class SetupChatAgentImplementation extends Disposable implements IChatAgentImple let success = undefined; try { - success = await setup.run(); + success = await setup.run({ disableCopilotViewReveal: true /* preserve the context we are currently in */ }); } catch (error) { this.logService.error(localize('setupError', "Error during setup: {0}", toErrorMessage(error))); } finally { @@ -281,12 +299,12 @@ class ChatSetup { @ILogService private readonly logService: ILogService, ) { } - async run(): Promise { + async run(options?: { disableCopilotViewReveal?: boolean }): Promise { if (this.pendingRun) { return this.pendingRun; } - this.pendingRun = this.doRun(); + this.pendingRun = this.doRun(options); try { return await this.pendingRun; @@ -295,7 +313,7 @@ class ChatSetup { } } - private async doRun(): Promise { + private async doRun(options?: { disableCopilotViewReveal?: boolean }): Promise { let setupStrategy: ChatSetupStrategy; if (this.chatEntitlementService.entitlement === ChatEntitlement.Pro || this.chatEntitlementService.entitlement === ChatEntitlement.Limited) { setupStrategy = ChatSetupStrategy.DefaultSetup; // existing pro/free users setup without a dialog @@ -307,13 +325,13 @@ class ChatSetup { try { switch (setupStrategy) { case ChatSetupStrategy.SetupWithEnterpriseProvider: - success = await this.controller.value.setupWithProvider(true); + success = await this.controller.value.setupWithProvider({ ...options, useEnterpriseProvider: true }); break; case ChatSetupStrategy.SetupWithoutEnterpriseProvider: - success = await this.controller.value.setupWithProvider(false); + success = await this.controller.value.setupWithProvider({ ...options, useEnterpriseProvider: false }); break; case ChatSetupStrategy.DefaultSetup: - success = await this.controller.value.setup(); + success = await this.controller.value.setup(options); break; } } catch (error) { @@ -454,6 +472,8 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr if (!disabled && !registration.value) { registration.value = combinedDisposable( SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Panel, false, context, controller), + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Terminal, false, context, controller), + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Editor, false, context, controller), SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, false, context, controller), SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, true, context, controller) ); @@ -748,7 +768,7 @@ class ChatSetupController extends Disposable { this._onDidChange.fire(); } - async setup(options?: { forceSignIn?: boolean }): Promise { + async setup(options?: { forceSignIn?: boolean; disableCopilotViewReveal?: boolean }): Promise { const watch = new StopWatch(false); const title = localize('setupChatProgress', "Getting Copilot ready..."); const badge = this.activityService.showViewContainerActivity(preferCopilotEditsView(this.viewsService) ? CHAT_EDITING_SIDEBAR_PANEL_ID : CHAT_SIDEBAR_PANEL_ID, { @@ -760,13 +780,13 @@ class ChatSetupController extends Disposable { location: ProgressLocation.Window, command: CHAT_OPEN_ACTION_ID, title, - }, () => this.doSetup(options?.forceSignIn ?? false, watch)); + }, () => this.doSetup(options ?? {}, watch)); } finally { badge.dispose(); } } - private async doSetup(forceSignIn: boolean, watch: StopWatch): Promise { + private async doSetup(options: { forceSignIn?: boolean; disableCopilotViewReveal?: boolean }, watch: StopWatch): Promise { this.context.suspend(); // reduces flicker let focusChatInput = false; @@ -778,9 +798,9 @@ class ChatSetupController extends Disposable { let entitlement: ChatEntitlement | undefined; // Entitlement Unknown or `forceSignIn`: we need to sign-in user - if (this.context.state.entitlement === ChatEntitlement.Unknown || forceSignIn) { + if (this.context.state.entitlement === ChatEntitlement.Unknown || options.forceSignIn) { this.setStep(ChatSetupStep.SigningIn); - const result = await this.signIn(providerId); + const result = await this.signIn(providerId, options); if (!result.session) { this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotSignedIn', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog }); return false; @@ -802,7 +822,7 @@ class ChatSetupController extends Disposable { // Install this.setStep(ChatSetupStep.Installing); - success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, watch); + success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, options, watch); const currentActiveElement = getActiveElement(); focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body; @@ -811,18 +831,20 @@ class ChatSetupController extends Disposable { this.context.resume(); } - if (focusChatInput) { + if (focusChatInput && !options.disableCopilotViewReveal) { (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); } return success; } - private async signIn(providerId: string): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { + private async signIn(providerId: string, options?: { disableCopilotViewReveal?: boolean }): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { let session: AuthenticationSession | undefined; let entitlements; try { - showCopilotView(this.viewsService, this.layoutService); + if (!options?.disableCopilotViewReveal) { + showCopilotView(this.viewsService, this.layoutService); + } ({ session, entitlements } = await this.requests.signIn()); } catch (e) { @@ -838,20 +860,22 @@ class ChatSetupController extends Disposable { }); if (confirmed) { - return this.signIn(providerId); + return this.signIn(providerId, options); } } return { session, entitlement: entitlements?.entitlement }; } - private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, watch: StopWatch,): Promise { + private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, options: { disableCopilotViewReveal?: boolean }, watch: StopWatch): Promise { const wasInstalled = this.context.state.installed; let signUpResult: boolean | { errorCode: number } | undefined = undefined; const setupFromDialog = Boolean(this.configurationService.getValue('chat.experimental.setupFromDialog')); try { - showCopilotView(this.viewsService, this.layoutService); + if (!options?.disableCopilotViewReveal) { + showCopilotView(this.viewsService, this.layoutService); + } if ( entitlement !== ChatEntitlement.Limited && // User is not signed up to Copilot Free @@ -932,7 +956,7 @@ class ChatSetupController extends Disposable { } } - async setupWithProvider(useEnterpriseProvider: boolean): Promise { + async setupWithProvider(options: { useEnterpriseProvider: boolean; disableCopilotViewReveal?: boolean }): Promise { const registry = Registry.as(ConfigurationExtensions.Configuration); registry.registerConfiguration({ 'id': 'copilot.setup', @@ -952,7 +976,7 @@ class ChatSetupController extends Disposable { } }); - if (useEnterpriseProvider) { + if (options.useEnterpriseProvider) { const success = await this.handleEnterpriseInstance(); if (!success) { return false; // not properly configured, abort @@ -964,7 +988,7 @@ class ChatSetupController extends Disposable { existingAdvancedSetting = {}; } - if (useEnterpriseProvider) { + if (options.useEnterpriseProvider) { await this.configurationService.updateValue(`${defaultChat.completionsAdvancedSetting}`, { ...existingAdvancedSetting, 'authProvider': defaultChat.enterpriseProviderId @@ -977,7 +1001,7 @@ class ChatSetupController extends Disposable { await this.configurationService.updateValue(defaultChat.providerUriSetting, undefined, ConfigurationTarget.USER); } - return this.setup({ forceSignIn: true }); + return this.setup({ ...options, forceSignIn: true }); } private async handleEnterpriseInstance(): Promise { @@ -1102,8 +1126,8 @@ class ChatSetupWelcomeContent extends Disposable { buttonContainer.classList.add('button-container'); const button = this._register(new ButtonWithDropdown(buttonContainer, { actions: [ - toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.controller.setupWithProvider(false) }), - toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider(true) }) + toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: false }) }), + toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider({ useEnterpriseProvider: true }) }) ], addPrimaryActionToDropdown: false, contextMenuProvider: this.contextMenuService, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index f3b196eb244..4f2e0b624fa 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -410,7 +410,7 @@ export class InlineChatEnabler { const updateAgent = () => { const agent = chatAgentService.getDefaultAgent(ChatAgentLocation.Editor); - if (agent?.id === 'github.copilot.editor') { + if (agent?.id === 'github.copilot.editor' || agent?.id === 'setup.editor') { this._ctxHasProvider.set(true); this._ctxHasProvider2.reset(); } else if (agent?.id === 'github.copilot.editingSessionEditor') { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts index b0b1e25e6c3..cbdeafdd383 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatEnabler.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IChatAgentService } from '../../../chat/common/chatAgents.js'; import { ChatAgentLocation } from '../../../chat/common/constants.js'; import { TerminalChatContextKeys } from './terminalChat.js'; - export class TerminalChatEnabler { static Id = 'terminalChat.enabler'; @@ -23,7 +23,7 @@ export class TerminalChatEnabler { @IContextKeyService contextKeyService: IContextKeyService, ) { this._ctxHasProvider = TerminalChatContextKeys.hasChatAgent.bindTo(contextKeyService); - this._store.add(chatAgentService.onDidChangeAgents(() => { + this._store.add(Event.runAndSubscribe(chatAgentService.onDidChangeAgents, () => { const hasTerminalAgent = Boolean(chatAgentService.getDefaultAgent(ChatAgentLocation.Terminal)); this._ctxHasProvider.set(hasTerminalAgent); }));