@@ -35,6 +43,9 @@ const TaskItemFooter: React.FC
= ({ item, variant, isSelect
{variant === "full" && }
+ {variant === "full" && onDeleteCheckpoints && (
+
+ )}
{onDelete && }
)}
diff --git a/webview-ui/src/components/history/__tests__/DeleteCheckpointsButton.spec.tsx b/webview-ui/src/components/history/__tests__/DeleteCheckpointsButton.spec.tsx
new file mode 100644
index 00000000000..bad648b4039
--- /dev/null
+++ b/webview-ui/src/components/history/__tests__/DeleteCheckpointsButton.spec.tsx
@@ -0,0 +1,21 @@
+import { render, screen, fireEvent } from "@/utils/test-utils"
+
+import { DeleteCheckpointsButton } from "../DeleteCheckpointsButton"
+
+vi.mock("@src/i18n/TranslationContext", () => ({
+ useAppTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+describe("DeleteCheckpointsButton", () => {
+ it("calls onDeleteCheckpoints when clicked", () => {
+ const onDeleteCheckpoints = vi.fn()
+ render()
+
+ const deleteCheckpointsButton = screen.getByRole("button")
+ fireEvent.click(deleteCheckpointsButton)
+
+ expect(onDeleteCheckpoints).toHaveBeenCalledWith("test-id")
+ })
+})
diff --git a/webview-ui/src/components/history/__tests__/DeleteCheckpointsDialog.spec.tsx b/webview-ui/src/components/history/__tests__/DeleteCheckpointsDialog.spec.tsx
new file mode 100644
index 00000000000..c034997ff5a
--- /dev/null
+++ b/webview-ui/src/components/history/__tests__/DeleteCheckpointsDialog.spec.tsx
@@ -0,0 +1,74 @@
+import { render, screen, fireEvent, act } from "@/utils/test-utils"
+
+import { DeleteCheckpointsDialog } from "../DeleteCheckpointsDialog"
+import { vscode } from "@/utils/vscode"
+
+vi.mock("@src/i18n/TranslationContext", () => ({
+ useAppTranslation: () => ({
+ t: (key: string) => key,
+ }),
+}))
+
+vi.mock("@/utils/vscode", () => ({
+ vscode: {
+ postMessage: vi.fn(),
+ },
+}))
+
+describe("DeleteCheckpointsDialog", () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ })
+
+ it("renders dialog with correct content", async () => {
+ await act(async () => {
+ render( {}} />)
+ })
+
+ expect(screen.getByText("history:deleteCheckpoints")).toBeInTheDocument()
+ expect(screen.getByText("history:deleteCheckpointsMessage")).toBeInTheDocument()
+ })
+
+ it("calls vscode.postMessage when delete is confirmed", async () => {
+ const onOpenChange = vi.fn()
+ await act(async () => {
+ render()
+ })
+
+ await act(async () => {
+ fireEvent.click(screen.getByText("history:delete"))
+ })
+
+ expect(vscode.postMessage).toHaveBeenCalledWith({
+ type: "deleteTaskCheckpointsWithId",
+ text: "test-id",
+ })
+ expect(onOpenChange).toHaveBeenCalledWith(false)
+ })
+
+ it("calls onOpenChange when cancel is clicked", async () => {
+ const onOpenChange = vi.fn()
+ await act(async () => {
+ render()
+ })
+
+ await act(async () => {
+ fireEvent.click(screen.getByText("history:cancel"))
+ })
+
+ expect(vscode.postMessage).not.toHaveBeenCalled()
+ })
+
+ it("does not call vscode.postMessage when taskId is empty", async () => {
+ const onOpenChange = vi.fn()
+ await act(async () => {
+ render()
+ })
+
+ await act(async () => {
+ fireEvent.click(screen.getByText("history:delete"))
+ })
+
+ expect(vscode.postMessage).not.toHaveBeenCalled()
+ })
+})
diff --git a/webview-ui/src/i18n/locales/en/history.json b/webview-ui/src/i18n/locales/en/history.json
index 608be93140c..51ef105aaec 100644
--- a/webview-ui/src/i18n/locales/en/history.json
+++ b/webview-ui/src/i18n/locales/en/history.json
@@ -11,6 +11,9 @@
"mostTokens": "Most Tokens",
"mostRelevant": "Most Relevant",
"deleteTaskTitle": "Delete Task (Shift + Click to skip confirmation)",
+ "deleteCheckpointsTitle": "Delete Checkpoints Only (Shift + Click to skip confirmation)",
+ "deleteCheckpoints": "Delete Checkpoints",
+ "deleteCheckpointsMessage": "Are you sure you want to delete the checkpoints for this task? The task history will be preserved, but you will lose the ability to restore file changes. This action cannot be undone.",
"copyPrompt": "Copy Prompt",
"exportTask": "Export Task",
"deleteTask": "Delete Task",