Skip to content

Commit 7baefef

Browse files
committed
reduce checkpoint changes
1 parent ab46f94 commit 7baefef

File tree

6 files changed

+86
-117
lines changed

6 files changed

+86
-117
lines changed

src/core/task/Task.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ export class Task extends EventEmitter<ClineEvents> {
181181
// LLM Messages & Chat Messages
182182
apiConversationHistory: ApiMessage[] = []
183183
clineMessages: ClineMessage[] = []
184-
public pendingUserMessageCheckpoint?: Record<string, unknown>
185184

186185
// Ask
187186
private askResponse?: ClineAskResponse
@@ -718,17 +717,13 @@ export class Task extends EventEmitter<ClineEvents> {
718717
}
719718

720719
if (type === "user_feedback") {
721-
// Automatically use and clear the pending checkpoint for user_feedback messages
722-
const feedbackCheckpoint = checkpoint || this.pendingUserMessageCheckpoint
723-
this.pendingUserMessageCheckpoint = undefined // Clear it after use
724-
725720
await this.addToClineMessages({
726721
ts: sayTs,
727722
type: "say",
728723
say: type,
729724
text,
730725
images,
731-
checkpoint: feedbackCheckpoint,
726+
checkpoint,
732727
contextCondense,
733728
})
734729
} else {

src/core/webview/ClineProvider.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ interface PendingEditOperation {
8787
images?: string[]
8888
messageIndex: number
8989
apiConversationHistoryIndex: number
90-
originalCheckpoint: { hash: string }
9190
timeoutId: NodeJS.Timeout
9291
createdAt: number
9392
}
@@ -268,7 +267,6 @@ export class ClineProvider
268267
images?: string[]
269268
messageIndex: number
270269
apiConversationHistoryIndex: number
271-
originalCheckpoint: { hash: string }
272270
},
273271
): void {
274272
// Clear any existing operation with the same ID
@@ -720,11 +718,6 @@ export class ClineProvider
720718
)
721719
}
722720

723-
// If there was an original checkpoint, preserve it for the new message
724-
if (pendingEdit.originalCheckpoint) {
725-
cline.pendingUserMessageCheckpoint = pendingEdit.originalCheckpoint
726-
}
727-
728721
// Process the edited message
729722
await cline.handleWebviewAskResponse(
730723
"messageResponse",

src/core/webview/__tests__/checkpointRestoreHandler.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ describe("checkpointRestoreHandler", () => {
134134
images: ["image1.png"],
135135
messageIndex: 2,
136136
apiConversationHistoryIndex: 2,
137-
originalCheckpoint: { hash: "abc123" },
138137
})
139138

140139
// Verify checkpoint restore was called with edit operation

src/core/webview/__tests__/webviewMessageHandler.checkpoint.spec.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,14 @@ describe("webviewMessageHandler - checkpoint operations", () => {
2727
taskId: "test-task-123",
2828
clineMessages: [
2929
{ ts: 1, type: "user", say: "user", text: "First message" },
30-
{ ts: 2, type: "assistant", say: "assistant", text: "Response" },
31-
{
32-
ts: 3,
33-
type: "user",
34-
say: "user",
35-
text: "Checkpoint message",
36-
checkpoint: { hash: "abc123" },
37-
},
38-
{ ts: 4, type: "assistant", say: "assistant", text: "After checkpoint" },
30+
{ ts: 2, type: "assistant", say: "checkpoint_saved", text: "abc123" },
31+
{ ts: 3, type: "user", say: "user", text: "Message to delete" },
32+
{ ts: 4, type: "assistant", say: "assistant", text: "After message" },
3933
],
4034
apiConversationHistory: [
4135
{ ts: 1, role: "user", content: [{ type: "text", text: "First message" }] },
42-
{ ts: 2, role: "assistant", content: [{ type: "text", text: "Response" }] },
43-
{ ts: 3, role: "user", content: [{ type: "text", text: "Checkpoint message" }] },
44-
{ ts: 4, role: "assistant", content: [{ type: "text", text: "After checkpoint" }] },
36+
{ ts: 3, role: "user", content: [{ type: "text", text: "Message to delete" }] },
37+
{ ts: 4, role: "assistant", content: [{ type: "text", text: "After message" }] },
4538
],
4639
checkpointRestore: vi.fn(),
4740
overwriteClineMessages: vi.fn(),
@@ -130,7 +123,7 @@ describe("webviewMessageHandler - checkpoint operations", () => {
130123
editData: {
131124
editedContent: "Edited checkpoint message",
132125
images: undefined,
133-
apiConversationHistoryIndex: 2,
126+
apiConversationHistoryIndex: 1,
134127
},
135128
})
136129
})

src/core/webview/checkpointRestoreHandler.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ export async function handleCheckpointRestoreOperation(config: CheckpointRestore
5151
images: editData.images,
5252
messageIndex: config.messageIndex,
5353
apiConversationHistoryIndex: editData.apiConversationHistoryIndex,
54-
originalCheckpoint: checkpoint,
5554
})
5655
}
5756

src/core/webview/webviewMessageHandler.ts

Lines changed: 79 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -102,18 +102,18 @@ export const webviewMessageHandler = async (
102102
* Handles message deletion operations with user confirmation
103103
*/
104104
const handleDeleteOperation = async (messageTs: number): Promise<void> => {
105-
// Check if the message has a checkpoint
105+
// Check if there's a checkpoint before this message
106106
const currentCline = provider.getCurrentCline()
107107
let hasCheckpoint = false
108108
if (currentCline) {
109109
const { messageIndex } = findMessageIndices(messageTs, currentCline)
110110
if (messageIndex !== -1) {
111-
const targetMessage = currentCline.clineMessages[messageIndex]
112-
hasCheckpoint = !!(
113-
targetMessage?.checkpoint &&
114-
typeof targetMessage.checkpoint === "object" &&
115-
"hash" in targetMessage.checkpoint
116-
)
111+
// Find the last checkpoint before this message
112+
const checkpoints = currentCline.clineMessages
113+
.filter((msg) => msg.say === "checkpoint_saved" && msg.ts < messageTs)
114+
.sort((a, b) => b.ts - a.ts)
115+
116+
hasCheckpoint = checkpoints.length > 0
117117
} else {
118118
console.log("[webviewMessageHandler] Message not found! Looking for ts:", messageTs)
119119
}
@@ -149,16 +149,29 @@ export const webviewMessageHandler = async (
149149
try {
150150
const targetMessage = currentCline.clineMessages[messageIndex]
151151

152-
// If checkpoint restoration is requested, restore to the checkpoint first
153-
if (restoreCheckpoint && hasValidCheckpoint(targetMessage)) {
154-
await handleCheckpointRestoreOperation({
155-
provider,
156-
currentCline,
157-
messageTs: targetMessage.ts!,
158-
messageIndex,
159-
checkpoint: targetMessage.checkpoint as ValidCheckpoint,
160-
operation: "delete",
161-
})
152+
// If checkpoint restoration is requested, find and restore to the last checkpoint before this message
153+
if (restoreCheckpoint) {
154+
// Find the last checkpoint before this message
155+
const checkpoints = currentCline.clineMessages
156+
.filter((msg) => msg.say === "checkpoint_saved" && msg.ts < messageTs)
157+
.sort((a, b) => b.ts - a.ts)
158+
159+
const lastCheckpoint = checkpoints[0]
160+
161+
if (lastCheckpoint && lastCheckpoint.text) {
162+
await handleCheckpointRestoreOperation({
163+
provider,
164+
currentCline,
165+
messageTs: targetMessage.ts!,
166+
messageIndex,
167+
checkpoint: { hash: lastCheckpoint.text },
168+
operation: "delete",
169+
})
170+
} else {
171+
// No checkpoint found before this message
172+
console.log("[handleDeleteMessageConfirm] No checkpoint found before message")
173+
vscode.window.showWarningMessage("No checkpoint found before this message")
174+
}
162175
} else {
163176
// For non-checkpoint deletes, preserve checkpoint associations for remaining messages
164177
// Store checkpoints from messages that will be preserved
@@ -200,43 +213,33 @@ export const webviewMessageHandler = async (
200213
* Handles message editing operations with user confirmation
201214
*/
202215
const handleEditOperation = async (messageTs: number, editedContent: string, images?: string[]): Promise<void> => {
203-
// Always check if the message has a checkpoint first
216+
// Check if there's a checkpoint before this message
204217
const currentCline = provider.getCurrentCline()
205218
let hasCheckpoint = false
206219
if (currentCline) {
207220
const { messageIndex } = findMessageIndices(messageTs, currentCline)
208221
if (messageIndex !== -1) {
209-
const targetMessage = currentCline.clineMessages[messageIndex]
210-
hasCheckpoint = !!(
211-
targetMessage?.checkpoint &&
212-
typeof targetMessage.checkpoint === "object" &&
213-
"hash" in targetMessage.checkpoint
214-
)
222+
// Find the last checkpoint before this message
223+
const checkpoints = currentCline.clineMessages
224+
.filter((msg) => msg.say === "checkpoint_saved" && msg.ts < messageTs)
225+
.sort((a, b) => b.ts - a.ts)
226+
227+
hasCheckpoint = checkpoints.length > 0
215228
} else {
216229
console.log("[webviewMessageHandler] Edit - Message not found in clineMessages!")
217230
}
218231
} else {
219232
console.log("[webviewMessageHandler] Edit - No currentCline available!")
220233
}
221-
// If there's a checkpoint, show the checkpoint dialog even when skipping confirmation
222-
if (hasCheckpoint) {
223-
await provider.postMessageToWebview({
224-
type: "showEditMessageDialog",
225-
messageTs,
226-
text: editedContent,
227-
hasCheckpoint,
228-
images,
229-
})
230-
} else {
231-
// Send message to webview to show edit confirmation dialog
232-
await provider.postMessageToWebview({
233-
type: "showEditMessageDialog",
234-
messageTs,
235-
text: editedContent,
236-
hasCheckpoint,
237-
images,
238-
})
239-
}
234+
235+
// Send message to webview to show edit confirmation dialog
236+
await provider.postMessageToWebview({
237+
type: "showEditMessageDialog",
238+
messageTs,
239+
text: editedContent,
240+
hasCheckpoint,
241+
images,
242+
})
240243
}
241244

242245
/**
@@ -267,27 +270,38 @@ export const webviewMessageHandler = async (
267270
try {
268271
const targetMessage = currentCline.clineMessages[messageIndex]
269272

270-
// Preserve the original checkpoint data for the edited message
271-
const originalCheckpoint = targetMessage?.checkpoint
272-
273-
// If checkpoint restoration is requested, restore to the checkpoint first
274-
if (restoreCheckpoint && hasValidCheckpoint(targetMessage)) {
275-
await handleCheckpointRestoreOperation({
276-
provider,
277-
currentCline,
278-
messageTs: targetMessage.ts!,
279-
messageIndex,
280-
checkpoint: targetMessage.checkpoint as ValidCheckpoint,
281-
operation: "edit",
282-
editData: {
283-
editedContent,
284-
images,
285-
apiConversationHistoryIndex,
286-
},
287-
})
288-
// The task will be cancelled and reinitialized by checkpointRestore
289-
// The pending edit will be processed in the reinitialized task
290-
return
273+
// If checkpoint restoration is requested, find and restore to the last checkpoint before this message
274+
if (restoreCheckpoint) {
275+
// Find the last checkpoint before this message
276+
const checkpoints = currentCline.clineMessages
277+
.filter((msg) => msg.say === "checkpoint_saved" && msg.ts < messageTs)
278+
.sort((a, b) => b.ts - a.ts)
279+
280+
const lastCheckpoint = checkpoints[0]
281+
282+
if (lastCheckpoint && lastCheckpoint.text) {
283+
await handleCheckpointRestoreOperation({
284+
provider,
285+
currentCline,
286+
messageTs: targetMessage.ts!,
287+
messageIndex,
288+
checkpoint: { hash: lastCheckpoint.text },
289+
operation: "edit",
290+
editData: {
291+
editedContent,
292+
images,
293+
apiConversationHistoryIndex,
294+
},
295+
})
296+
// The task will be cancelled and reinitialized by checkpointRestore
297+
// The pending edit will be processed in the reinitialized task
298+
return
299+
} else {
300+
// No checkpoint found before this message
301+
console.log("[handleEditMessageConfirm] No checkpoint found before message")
302+
vscode.window.showWarningMessage("No checkpoint found before this message")
303+
// Continue with non-checkpoint edit
304+
}
291305
}
292306

293307
// For non-checkpoint edits, preserve checkpoint associations for remaining messages
@@ -319,12 +333,6 @@ export const webviewMessageHandler = async (
319333
})
320334

321335
// Process the edited message as a regular user message
322-
// Preserve the original checkpoint for the new message
323-
if (originalCheckpoint) {
324-
// Store the checkpoint to be attached to the new message
325-
currentCline.pendingUserMessageCheckpoint = originalCheckpoint
326-
}
327-
328336
webviewMessageHandler(provider, {
329337
type: "askResponse",
330338
askResponse: "messageResponse",
@@ -495,25 +503,7 @@ export const webviewMessageHandler = async (
495503
await provider.postStateToWebview()
496504
break
497505
case "askResponse":
498-
// Save checkpoint BEFORE processing the user message if checkpoints are enabled
499-
const currentCline = provider.getCurrentCline()
500-
if (currentCline && currentCline.enableCheckpoints && message.askResponse === "messageResponse") {
501-
try {
502-
const checkpointResult = await currentCline.checkpointSave(true) // Force checkpoint save
503-
if (checkpointResult?.commit) {
504-
// Store checkpoint data temporarily to be used when creating the user_feedback message
505-
currentCline.pendingUserMessageCheckpoint = {
506-
hash: checkpointResult.commit,
507-
timestamp: Date.now(),
508-
type: "user_message",
509-
}
510-
}
511-
} catch (error) {
512-
console.error("[webviewMessageHandler] Failed to save checkpoint before user message:", error)
513-
}
514-
}
515-
516-
currentCline?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
506+
provider.getCurrentCline()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
517507
break
518508
case "autoCondenseContext":
519509
await updateGlobalState("autoCondenseContext", message.bool)

0 commit comments

Comments
 (0)