diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatForkActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatForkActions.ts index c0768d6b70a..d7f8eed7462 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatForkActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatForkActions.ts @@ -39,6 +39,7 @@ export function registerChatForkActions() { order: 3, when: ContextKeyExpr.and( ChatContextKeys.isRequest, + ChatContextKeys.isFirstRequest.negate(), ContextKeyExpr.or( ChatContextKeys.lockedToCodingAgent.negate(), ChatContextKeys.chatSessionSupportsFork diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index 4913141987b..4669ede9df9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts @@ -583,7 +583,7 @@ registerAction2(class RestoreCheckpointAction extends Action2 { id: MenuId.ChatMessageCheckpoint, group: 'navigation', order: 2, - when: ContextKeyExpr.and(ChatContextKeys.isRequest, ChatContextKeys.lockedToCodingAgent.negate()) + when: ContextKeyExpr.and(ChatContextKeys.isRequest, ChatContextKeys.lockedToCodingAgent.negate(), ChatContextKeys.isFirstRequest.negate()) } ] }); @@ -617,6 +617,42 @@ registerAction2(class RestoreCheckpointAction extends Action2 { } }); +registerAction2(class StartOverAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.startOver', + title: localize2('chat.startOver.label', "Start Over"), + tooltip: localize2('chat.startOver.tooltip', "Clears the chat and undoes all changes"), + f1: false, + category: CHAT_CATEGORY, + menu: [ + { + id: MenuId.ChatMessageCheckpoint, + group: 'navigation', + order: 2, + when: ContextKeyExpr.and(ChatContextKeys.isRequest, ChatContextKeys.lockedToCodingAgent.negate(), ChatContextKeys.isFirstRequest) + } + ] + }); + } + + async run(accessor: ServicesAccessor, ...args: unknown[]) { + let item = args[0] as ChatTreeItem | undefined; + const chatWidgetService = accessor.get(IChatWidgetService); + const widget = (isChatTreeItem(item) && chatWidgetService.getWidgetBySessionResource(item.sessionResource)) || chatWidgetService.lastFocusedWidget; + if (!isResponseVM(item) && !isRequestVM(item)) { + item = widget?.getFocus(); + } + + if (!item) { + return; + } + + widget?.viewModel?.model.setCheckpoint(item.id); + await restoreSnapshotWithConfirmation(accessor, item); + } +}); + registerAction2(class RestoreLastCheckpoint extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts index a33f39ab401..742a67eeb14 100644 --- a/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/widget/chatListRenderer.ts @@ -709,6 +709,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer('chatResponse', false, { type: 'boolean', description: localize('chatResponse', "The chat item is a response.") }); export const isRequest = new RawContextKey('chatRequest', false, { type: 'boolean', description: localize('chatRequest', "The chat item is a request") }); + export const isFirstRequest = new RawContextKey('chatFirstRequest', false, { type: 'boolean', description: localize('chatFirstRequest', "The chat item is the first request in the session.") }); export const isPendingRequest = new RawContextKey('chatRequestIsPending', false, { type: 'boolean', description: localize('chatRequestIsPending', "True when the chat request item is pending in the queue.") }); export const itemId = new RawContextKey('chatItemId', '', { type: 'string', description: localize('chatItemId', "The id of the chat item.") }); export const lastItemId = new RawContextKey('chatLastItemId', [], { type: 'string', description: localize('chatLastItemId', "The id of the last chat item.") });