Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as nls from '../../../../../nls.js';
import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js';
import { INotificationService, NeverShowAgainScope, Severity } from '../../../../../platform/notification/common/notification.js';
import { IViewsService } from '../../../../services/views/common/viewsService.js';
import { AGENT_SESSIONS_VIEW_ID } from './agentSessions.js';

const STORAGE_KEY = 'chat.closeWithActiveResponse.doNotShowAgain2';

/**
* Shows a notification when closing a chat with an active response, informing the user
* that the chat will continue running in the background. The notification includes a button
* to open the Agent Sessions view and a "Don't Show Again" option.
*/
export function showCloseActiveChatNotification(
accessor: ServicesAccessor
): void {
const notificationService = accessor.get(INotificationService);
const viewsService = accessor.get(IViewsService);

notificationService.prompt(
Severity.Info,
nls.localize('chat.closeWithActiveResponse', "A chat session is in progress. It will continue running in the background."),
[
{
label: nls.localize('chat.openAgentSessions', "Open Agent Sessions"),
run: async () => {
await viewsService.openView(AGENT_SESSIONS_VIEW_ID, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to wait here or can fire & forget?

}
}
],
{
neverShowAgain: {
id: STORAGE_KEY,
scope: NeverShowAgainScope.APPLICATION
}
}
);
}
15 changes: 11 additions & 4 deletions src/vs/workbench/contrib/chat/browser/chatEditorInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import { CancellationToken } from '../../../../base/common/cancellation.js';
import { Codicon } from '../../../../base/common/codicons.js';
import { Emitter } from '../../../../base/common/event.js';
import { Disposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
import { Schemas } from '../../../../base/common/network.js';
import { isEqual } from '../../../../base/common/resources.js';
Expand All @@ -25,6 +24,7 @@ import { IChatSessionsService, localChatSessionType } from '../common/chatSessio
import { LocalChatSessionUri } from '../common/chatUri.js';
import { ChatAgentLocation, ChatEditorTitleMaxLength } from '../common/constants.js';
import { IClearEditingSessionConfirmationOptions } from './actions/chatActions.js';
import { showCloseActiveChatNotification } from './agentSessions/chatCloseNotification.js';
import type { IChatEditorOptions } from './chatEditor.js';

const ChatEditorIcon = registerIcon('chat-editor-label-icon', Codicon.chatSparkle, nls.localize('chatEditorLabelIcon', 'Icon of the chat editor label.'));
Expand Down Expand Up @@ -77,6 +77,7 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler
@IChatService private readonly chatService: IChatService,
@IDialogService private readonly dialogService: IDialogService,
@IChatSessionsService private readonly chatSessionsService: IChatSessionsService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
) {
super();

Expand Down Expand Up @@ -316,12 +317,18 @@ export class ChatEditorInput extends EditorInput implements IEditorCloseHandler
}
return false;
}

override dispose(): void {
// Check if we're disposing a model with an active request
if (this.modelRef.value?.object.requestInProgress.get()) {
this.instantiationService.invokeFunction(showCloseActiveChatNotification);
}

super.dispose();
}
}

export class ChatEditorModel extends Disposable {
private _onWillDispose = this._register(new Emitter<void>());
readonly onWillDispose = this._onWillDispose.event;

private _isResolved = false;

constructor(
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/contrib/chat/browser/chatViewPane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { IChatModelReference, IChatService } from '../common/chatService.js';
import { IChatSessionsExtensionPoint, IChatSessionsService, localChatSessionType } from '../common/chatSessionsService.js';
import { LocalChatSessionUri } from '../common/chatUri.js';
import { ChatAgentLocation, ChatModeKind } from '../common/constants.js';
import { showCloseActiveChatNotification } from './agentSessions/chatCloseNotification.js';
import { ChatWidget } from './chatWidget.js';
import { ChatViewWelcomeController, IViewWelcomeDelegate } from './viewsWelcome/chatViewWelcomeController.js';

Expand Down Expand Up @@ -148,6 +149,11 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate {
}

private async updateModel(modelRef?: IChatModelReference | undefined) {
// Check if we're disposing a model with an active request
if (this.modelRef.value?.object.requestInProgress.get()) {
Comment on lines +152 to +153
Copy link

Copilot AI Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The notification is shown before checking if a new model will be assigned. When updateModel is called with a modelRef parameter, the code disposes the old model and immediately assigns a new one. The notification should only be shown when actually closing/disposing a chat session, not when switching between sessions. Consider checking if !modelRef or moving the check after confirming there's no replacement model.

Suggested change
// Check if we're disposing a model with an active request
if (this.modelRef.value?.object.requestInProgress.get()) {
// Only show the notification if we're disposing a model with an active request and not replacing it
if (!modelRef && this.modelRef.value?.object.requestInProgress.get()) {

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point?

this.instantiationService.invokeFunction(showCloseActiveChatNotification);
}

this.modelRef.value = undefined;

const ref = modelRef ?? (this.chatService.transferredSessionData?.sessionId && this.chatService.transferredSessionData?.location === this.chatOptions.location
Expand Down
Loading