Path: blob/main/src/vs/workbench/contrib/chat/electron-browser/chat.contribution.ts
4780 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Disposable } from '../../../../base/common/lifecycle.js';6import { autorun } from '../../../../base/common/observable.js';7import { resolve } from '../../../../base/common/path.js';8import { isMacintosh } from '../../../../base/common/platform.js';9import { URI } from '../../../../base/common/uri.js';10import { ipcRenderer } from '../../../../base/parts/sandbox/electron-browser/globals.js';11import { localize } from '../../../../nls.js';12import { registerAction2 } from '../../../../platform/actions/common/actions.js';13import { ICommandService } from '../../../../platform/commands/common/commands.js';14import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';15import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';16import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';17import { ILogService } from '../../../../platform/log/common/log.js';18import { INativeHostService } from '../../../../platform/native/common/native.js';19import { IWorkspaceTrustRequestService } from '../../../../platform/workspace/common/workspaceTrust.js';20import { WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js';21import { ViewContainerLocation } from '../../../common/views.js';22import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-browser/environmentService.js';23import { IExtensionService } from '../../../services/extensions/common/extensions.js';24import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js';25import { ILifecycleService, ShutdownReason } from '../../../services/lifecycle/common/lifecycle.js';26import { ACTION_ID_NEW_CHAT, CHAT_OPEN_ACTION_ID, IChatViewOpenOptions } from '../browser/actions/chatActions.js';27import { IChatWidgetService } from '../browser/chat.js';28import { ChatContextKeys } from '../common/actions/chatContextKeys.js';29import { ChatConfiguration, ChatModeKind } from '../common/constants.js';30import { IChatService } from '../common/chatService/chatService.js';31import { registerChatDeveloperActions } from './actions/chatDeveloperActions.js';32import { HoldToVoiceChatInChatViewAction, InlineVoiceChatAction, KeywordActivationContribution, QuickVoiceChatAction, ReadChatResponseAloud, StartVoiceChatAction, StopListeningAction, StopListeningAndSubmitAction, StopReadAloud, StopReadChatItemAloud, VoiceChatInChatViewAction } from './actions/voiceChatActions.js';33import { NativeBuiltinToolsContribution } from './builtInTools/tools.js';3435class ChatCommandLineHandler extends Disposable {3637static readonly ID = 'workbench.contrib.chatCommandLineHandler';3839constructor(40@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,41@ICommandService private readonly commandService: ICommandService,42@IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService,43@ILogService private readonly logService: ILogService,44@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,45@IContextKeyService private readonly contextKeyService: IContextKeyService46) {47super();4849this.registerListeners();50}5152private registerListeners() {53ipcRenderer.on('vscode:handleChatRequest', (_, ...args: unknown[]) => {54const chatArgs = args[0] as typeof this.environmentService.args.chat;55this.logService.trace('vscode:handleChatRequest', chatArgs);5657this.prompt(chatArgs);58});59}6061private async prompt(args: typeof this.environmentService.args.chat): Promise<void> {62if (!Array.isArray(args?._)) {63return;64}6566const trusted = await this.workspaceTrustRequestService.requestWorkspaceTrust({67message: localize('copilotWorkspaceTrust', "AI features are currently only supported in trusted workspaces.")68});6970if (!trusted) {71return;72}7374const opts: IChatViewOpenOptions = {75query: args._.length > 0 ? args._.join(' ') : '',76mode: args.mode ?? ChatModeKind.Agent,77attachFiles: args['add-file']?.map(file => URI.file(resolve(file))), // use `resolve` to deal with relative paths properly78};7980if (args.maximize) {81const location = this.contextKeyService.getContextKeyValue<ViewContainerLocation>(ChatContextKeys.panelLocation.key);82if (location === ViewContainerLocation.AuxiliaryBar) {83this.layoutService.setAuxiliaryBarMaximized(true);84} else if (location === ViewContainerLocation.Panel && !this.layoutService.isPanelMaximized()) {85this.layoutService.toggleMaximizedPanel();86}87}8889await this.commandService.executeCommand(ACTION_ID_NEW_CHAT);90await this.commandService.executeCommand(CHAT_OPEN_ACTION_ID, opts);91}92}9394class ChatSuspendThrottlingHandler extends Disposable {9596static readonly ID = 'workbench.contrib.chatSuspendThrottlingHandler';9798constructor(99@INativeHostService nativeHostService: INativeHostService,100@IChatService chatService: IChatService,101@IConfigurationService configurationService: IConfigurationService102) {103super();104105if (!configurationService.getValue<boolean>(ChatConfiguration.SuspendThrottling)) {106return;107}108109this._register(autorun(reader => {110const running = chatService.requestInProgressObs.read(reader);111112// When a chat request is in progress, we must ensure that background113// throttling is not applied so that the chat session can continue114// even when the window is not in focus.115nativeHostService.setBackgroundThrottling(!running);116}));117}118}119120class ChatLifecycleHandler extends Disposable {121122static readonly ID = 'workbench.contrib.chatLifecycleHandler';123124constructor(125@ILifecycleService lifecycleService: ILifecycleService,126@IChatService private readonly chatService: IChatService,127@IDialogService private readonly dialogService: IDialogService,128@IChatWidgetService private readonly widgetService: IChatWidgetService,129@IContextKeyService private readonly contextKeyService: IContextKeyService,130@IExtensionService extensionService: IExtensionService,131) {132super();133134this._register(lifecycleService.onBeforeShutdown(e => {135e.veto(this.shouldVetoShutdown(e.reason), 'veto.chat');136}));137138this._register(extensionService.onWillStop(e => {139e.veto(this.chatService.requestInProgressObs.get(), localize('chatRequestInProgress', "A chat request is in progress."));140}));141}142143private shouldVetoShutdown(reason: ShutdownReason): boolean | Promise<boolean> {144const running = this.chatService.requestInProgressObs.read(undefined);145if (!running) {146return false;147}148149if (ChatContextKeys.skipChatRequestInProgressMessage.getValue(this.contextKeyService) === true) {150return false;151}152153return this.doShouldVetoShutdown(reason);154}155156private async doShouldVetoShutdown(reason: ShutdownReason): Promise<boolean> {157158this.widgetService.revealWidget();159160let message: string;161let detail: string;162switch (reason) {163case ShutdownReason.CLOSE:164message = localize('closeTheWindow.message', "A chat request is in progress. Are you sure you want to close the window?");165detail = localize('closeTheWindow.detail', "The chat request will stop if you close the window.");166break;167case ShutdownReason.LOAD:168message = localize('changeWorkspace.message', "A chat request is in progress. Are you sure you want to change the workspace?");169detail = localize('changeWorkspace.detail', "The chat request will stop if you change the workspace.");170break;171case ShutdownReason.RELOAD:172message = localize('reloadTheWindow.message', "A chat request is in progress. Are you sure you want to reload the window?");173detail = localize('reloadTheWindow.detail', "The chat request will stop if you reload the window.");174break;175default:176message = isMacintosh ? localize('quit.message', "A chat request is in progress. Are you sure you want to quit?") : localize('exit.message', "A chat request is in progress. Are you sure you want to exit?");177detail = isMacintosh ? localize('quit.detail', "The chat request will stop if you quit.") : localize('exit.detail', "The chat request will stop if you exit.");178break;179}180181const result = await this.dialogService.confirm({ message, detail });182183return !result.confirmed;184}185}186187registerAction2(StartVoiceChatAction);188189registerAction2(VoiceChatInChatViewAction);190registerAction2(HoldToVoiceChatInChatViewAction);191registerAction2(QuickVoiceChatAction);192registerAction2(InlineVoiceChatAction);193194registerAction2(StopListeningAction);195registerAction2(StopListeningAndSubmitAction);196197registerAction2(ReadChatResponseAloud);198registerAction2(StopReadChatItemAloud);199registerAction2(StopReadAloud);200201registerChatDeveloperActions();202203registerWorkbenchContribution2(KeywordActivationContribution.ID, KeywordActivationContribution, WorkbenchPhase.AfterRestored);204registerWorkbenchContribution2(NativeBuiltinToolsContribution.ID, NativeBuiltinToolsContribution, WorkbenchPhase.AfterRestored);205registerWorkbenchContribution2(ChatCommandLineHandler.ID, ChatCommandLineHandler, WorkbenchPhase.BlockRestore);206registerWorkbenchContribution2(ChatSuspendThrottlingHandler.ID, ChatSuspendThrottlingHandler, WorkbenchPhase.AfterRestored);207registerWorkbenchContribution2(ChatLifecycleHandler.ID, ChatLifecycleHandler, WorkbenchPhase.AfterRestored);208209210