diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.contribution.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.contribution.ts index 69c8a73471f..6aa4e11f8f4 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.contribution.ts @@ -13,7 +13,7 @@ import { IAgentSessionsService, AgentSessionsService } from './agentSessionsServ import { LocalAgentsSessionsProvider } from './localAgentSessionsProvider.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../common/contributions.js'; import { ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js'; -import { ArchiveAgentSessionAction, ArchiveAgentSessionSectionAction, UnarchiveAgentSessionAction, OpenAgentSessionInEditorGroupAction, OpenAgentSessionInNewEditorGroupAction, OpenAgentSessionInNewWindowAction, ShowAgentSessionsSidebar, HideAgentSessionsSidebar, ToggleAgentSessionsSidebar, RefreshAgentSessionsViewerAction, FindAgentSessionInViewerAction, MarkAgentSessionUnreadAction, MarkAgentSessionReadAction, FocusAgentSessionsAction, SetAgentSessionsOrientationAutoAction, SetAgentSessionsOrientationStackedAction, SetAgentSessionsOrientationSideBySideAction, ToggleChatViewSessionsAction, PickAgentSessionAction, ArchiveAllAgentSessionsAction, RenameAgentSessionAction } from './agentSessionsActions.js'; +import { ArchiveAgentSessionAction, ArchiveAgentSessionSectionAction, UnarchiveAgentSessionAction, OpenAgentSessionInEditorGroupAction, OpenAgentSessionInNewEditorGroupAction, OpenAgentSessionInNewWindowAction, ShowAgentSessionsSidebar, HideAgentSessionsSidebar, ToggleAgentSessionsSidebar, RefreshAgentSessionsViewerAction, FindAgentSessionInViewerAction, MarkAgentSessionUnreadAction, MarkAgentSessionReadAction, FocusAgentSessionsAction, SetAgentSessionsOrientationAutoAction, SetAgentSessionsOrientationStackedAction, SetAgentSessionsOrientationSideBySideAction, ToggleChatViewSessionsAction, PickAgentSessionAction, ArchiveAllAgentSessionsAction, RenameAgentSessionAction, DeleteAgentSessionAction, DeleteAllLocalSessionsAction } from './agentSessionsActions.js'; //#region Actions and Menus @@ -24,6 +24,8 @@ registerAction2(ArchiveAgentSessionSectionAction); registerAction2(ArchiveAgentSessionAction); registerAction2(UnarchiveAgentSessionAction); registerAction2(RenameAgentSessionAction); +registerAction2(DeleteAgentSessionAction); +registerAction2(DeleteAllLocalSessionsAction); registerAction2(MarkAgentSessionUnreadAction); registerAction2(MarkAgentSessionReadAction); registerAction2(OpenAgentSessionInNewWindowAction); diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts index eb0f513ab4f..54b47048b3c 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessions.ts @@ -71,3 +71,6 @@ export const agentSessionSelectedUnfocusedBadgeBorder = registerColor( { dark: transparent(foreground, 0.3), light: transparent(foreground, 0.3), hcDark: foreground, hcLight: foreground }, localize('agentSessionSelectedUnfocusedBadgeBorder', "Border color for the badges in selected agent session items when the view is unfocused.") ); + +export const AGENT_SESSION_RENAME_ACTION_ID = 'agentSession.rename'; +export const AGENT_SESSION_DELETE_ACTION_ID = 'agentSession.delete'; diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsActions.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsActions.ts index 847bb188875..96d82d1c18e 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsActions.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsActions.ts @@ -8,7 +8,7 @@ import { AgentSessionSection, IAgentSession, IAgentSessionSection, IMarshalledAg import { Action2, MenuId, MenuRegistry } from '../../../../../platform/actions/common/actions.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; -import { AgentSessionProviders, AgentSessionsViewerOrientation, IAgentSessionsControl } from './agentSessions.js'; +import { AGENT_SESSION_DELETE_ACTION_ID, AGENT_SESSION_RENAME_ACTION_ID, AgentSessionProviders, AgentSessionsViewerOrientation, IAgentSessionsControl } from './agentSessions.js'; import { IChatService } from '../../common/chatService.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { IChatEditorOptions } from '../chatEditor.js'; @@ -37,6 +37,7 @@ import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; //#region Chat View export class ToggleChatViewSessionsAction extends Action2 { + constructor() { super({ id: 'workbench.action.chat.toggleChatViewSessions', @@ -135,6 +136,7 @@ export class SetAgentSessionsOrientationSideBySideAction extends Action2 { } export class PickAgentSessionAction extends Action2 { + constructor() { super({ id: `workbench.action.chat.history`, @@ -182,8 +184,8 @@ export class ArchiveAllAgentSessionsAction extends Action2 { constructor() { super({ - id: 'workbench.action.chat.clearHistory', - title: localize2('chat.clear.label', "Archive All Workspace Agent Sessions"), + id: 'workbench.action.chat.archiveAllAgentSessions', + title: localize2('archiveAll.label', "Archive All Workspace Agent Sessions"), precondition: ChatContextKeys.enabled, category: CHAT_CATEGORY, f1: true, @@ -263,7 +265,7 @@ export class ArchiveAgentSessionSectionAction extends Action2 { abstract class BaseAgentSessionAction extends Action2 { - run(accessor: ServicesAccessor, context?: IAgentSession | IMarshalledAgentSessionContext): void { + async run(accessor: ServicesAccessor, context?: IAgentSession | IMarshalledAgentSessionContext): Promise { const agentSessionsService = accessor.get(IAgentSessionsService); const viewsService = accessor.get(IViewsService); @@ -280,7 +282,7 @@ abstract class BaseAgentSessionAction extends Action2 { } if (session) { - this.runWithSession(session, accessor); + await this.runWithSession(session, accessor); } } @@ -421,9 +423,8 @@ export class RenameAgentSessionAction extends BaseAgentSessionAction { constructor() { super({ - id: 'agentSession.rename', + id: AGENT_SESSION_RENAME_ACTION_ID, title: localize2('rename', "Rename..."), - icon: Codicon.edit, keybinding: { primary: KeyCode.F2, mac: { @@ -455,6 +456,79 @@ export class RenameAgentSessionAction extends BaseAgentSessionAction { } } +export class DeleteAgentSessionAction extends BaseAgentSessionAction { + + constructor() { + super({ + id: AGENT_SESSION_DELETE_ACTION_ID, + title: localize2('delete', "Delete..."), + menu: { + id: MenuId.AgentSessionsContext, + group: 'edit', + order: 4, + when: ChatContextKeys.agentSessionType.isEqualTo(AgentSessionProviders.Local) + } + }); + } + + async runWithSession(session: IAgentSession, accessor: ServicesAccessor): Promise { + const chatService = accessor.get(IChatService); + const dialogService = accessor.get(IDialogService); + const widgetService = accessor.get(IChatWidgetService); + + const confirmed = await dialogService.confirm({ + message: localize('deleteSession.confirm', "Are you sure you want to delete this chat session?"), + detail: localize('deleteSession.detail', "This action cannot be undone."), + primaryButton: localize('deleteSession.delete', "Delete") + }); + + if (!confirmed.confirmed) { + return; + } + + // Clear chat widget + await widgetService.getWidgetBySessionResource(session.resource)?.clear(); + + // Remove from storage + await chatService.removeHistoryEntry(session.resource); + } +} + +export class DeleteAllLocalSessionsAction extends Action2 { + + constructor() { + super({ + id: 'workbench.action.chat.clearHistory', + title: localize2('agentSessions.deleteAll', "Delete All Local Workspace Chat Sessions"), + precondition: ChatContextKeys.enabled, + category: CHAT_CATEGORY, + f1: true, + }); + } + + async run(accessor: ServicesAccessor, ...args: unknown[]) { + const chatService = accessor.get(IChatService); + const widgetService = accessor.get(IChatWidgetService); + const dialogService = accessor.get(IDialogService); + + const confirmed = await dialogService.confirm({ + message: localize('deleteAllChats.confirm', "Are you sure you want to delete all local workspace chat sessions?"), + detail: localize('deleteAllChats.detail', "This action cannot be undone."), + primaryButton: localize('deleteAllChats.button', "Delete All") + }); + + if (!confirmed.confirmed) { + return; + } + + // Clear all chat widgets + await Promise.all(widgetService.getAllWidgets().map(widget => widget.clear())); + + // Remove from storage + await chatService.clearAllHistoryEntries(); + } +} + abstract class BaseOpenAgentSessionAction extends BaseAgentSessionAction { async runWithSession(session: IAgentSession, accessor: ServicesAccessor): Promise { diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsPicker.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsPicker.ts index 26a61cbf7a4..32a675f8fc5 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentSessionsPicker.ts @@ -9,13 +9,14 @@ import { fromNow } from '../../../../../base/common/date.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { localize } from '../../../../../nls.js'; +import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js'; -import { IChatService } from '../../common/chatService.js'; import { openSession } from './agentSessionsOpener.js'; import { IAgentSession, isLocalAgentSessionItem } from './agentSessionsModel.js'; import { IAgentSessionsService } from './agentSessionsService.js'; import { AgentSessionsSorter, groupAgentSessions } from './agentSessionsViewer.js'; +import { AGENT_SESSION_DELETE_ACTION_ID, AGENT_SESSION_RENAME_ACTION_ID } from './agentSessions.js'; interface ISessionPickItem extends IQuickPickItem { readonly session: IAgentSession; @@ -36,6 +37,11 @@ const renameButton: IQuickInputButton = { tooltip: localize('renameSession', "Rename") }; +const deleteButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(Codicon.trash), + tooltip: localize('deleteSession', "Delete") +}; + export class AgentSessionsPicker { private readonly sorter = new AgentSessionsSorter(); @@ -44,7 +50,7 @@ export class AgentSessionsPicker { @IAgentSessionsService private readonly agentSessionsService: IAgentSessionsService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IChatService private readonly chatService: IChatService, + @ICommandService private readonly commandService: ICommandService, ) { } async pickAgentSession(): Promise { @@ -75,17 +81,24 @@ export class AgentSessionsPicker { disposables.add(picker.onDidTriggerItemButton(async e => { const session = e.item.session; + let reopenResolved: boolean = false; if (e.button === renameButton) { - const title = await this.quickInputService.input({ prompt: localize('newChatTitle', "New agent session title"), value: session.label }); - if (title) { - this.chatService.setChatSessionTitle(session.resource, title); - } + reopenResolved = true; + await this.commandService.executeCommand(AGENT_SESSION_RENAME_ACTION_ID, session); + } else if (e.button === deleteButton) { + reopenResolved = true; + await this.commandService.executeCommand(AGENT_SESSION_DELETE_ACTION_ID, session); } else { const newArchivedState = !session.isArchived(); session.setArchived(newArchivedState); } - picker.items = this.createPickerItems(); + if (reopenResolved) { + await this.agentSessionsService.model.resolve(session.providerType); + this.pickAgentSession(); + } else { + picker.items = this.createPickerItems(); + } })); disposables.add(picker.onDidHide(() => disposables.dispose())); @@ -117,6 +130,7 @@ export class AgentSessionsPicker { const buttons: IQuickInputButton[] = []; if (isLocalAgentSessionItem(session)) { buttons.push(renameButton); + buttons.push(deleteButton); } buttons.push(session.isArchived() ? unarchiveButton : archiveButton);