Skip to content

Commit 760ef23

Browse files
committed
fix: handle pending ask operations during checkpoint restore
- Add handleWebviewAskResponse call before checkpoint restore to resolve any pending ask promises - This prevents "Current ask promise was ignored" errors when restoring checkpoints - Add comprehensive tests for pending ask scenarios Fixes #8177
1 parent 513fce3 commit 760ef23

File tree

2 files changed

+92
-11
lines changed

2 files changed

+92
-11
lines changed

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ describe("checkpointRestoreHandler", () => {
3030
mockCline.abort = true
3131
}),
3232
checkpointRestore: vi.fn(),
33+
handleWebviewAskResponse: vi.fn(),
34+
lastMessageTs: undefined,
3335
clineMessages: [
3436
{ ts: 1, type: "user", say: "user", text: "First message" },
3537
{ ts: 2, type: "assistant", say: "assistant", text: "Response" },
@@ -238,5 +240,75 @@ describe("checkpointRestoreHandler", () => {
238240
"Error during checkpoint restore: Checkpoint restore failed",
239241
)
240242
})
243+
244+
it("should handle pending ask operations before checkpoint restore", async () => {
245+
// Simulate a pending ask operation
246+
mockCline.lastMessageTs = Date.now()
247+
248+
await handleCheckpointRestoreOperation({
249+
provider: mockProvider,
250+
currentCline: mockCline,
251+
messageTs: 3,
252+
messageIndex: 2,
253+
checkpoint: { hash: "abc123" },
254+
operation: "delete",
255+
})
256+
257+
// Verify handleWebviewAskResponse was called to resolve pending ask
258+
expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith("messageResponse", "", undefined)
259+
260+
// Verify checkpoint restore was still called
261+
expect(mockCline.checkpointRestore).toHaveBeenCalled()
262+
})
263+
264+
it("should handle pending ask operations for edit operations", async () => {
265+
// Simulate a pending ask operation
266+
mockCline.lastMessageTs = Date.now()
267+
268+
const editData = {
269+
editedContent: "Edited content",
270+
images: [],
271+
apiConversationHistoryIndex: 2,
272+
}
273+
274+
await handleCheckpointRestoreOperation({
275+
provider: mockProvider,
276+
currentCline: mockCline,
277+
messageTs: 3,
278+
messageIndex: 2,
279+
checkpoint: { hash: "abc123" },
280+
operation: "edit",
281+
editData,
282+
})
283+
284+
// Verify handleWebviewAskResponse was called to resolve pending ask
285+
expect(mockCline.handleWebviewAskResponse).toHaveBeenCalledWith("messageResponse", "", undefined)
286+
287+
// Verify checkpoint restore was still called
288+
expect(mockCline.checkpointRestore).toHaveBeenCalled()
289+
290+
// Verify pending edit operation was set
291+
expect(mockProvider.setPendingEditOperation).toHaveBeenCalled()
292+
})
293+
294+
it("should not call handleWebviewAskResponse if no pending ask", async () => {
295+
// No pending ask operation (lastMessageTs is undefined)
296+
mockCline.lastMessageTs = undefined
297+
298+
await handleCheckpointRestoreOperation({
299+
provider: mockProvider,
300+
currentCline: mockCline,
301+
messageTs: 3,
302+
messageIndex: 2,
303+
checkpoint: { hash: "abc123" },
304+
operation: "delete",
305+
})
306+
307+
// Verify handleWebviewAskResponse was NOT called
308+
expect(mockCline.handleWebviewAskResponse).not.toHaveBeenCalled()
309+
310+
// Verify checkpoint restore was still called
311+
expect(mockCline.checkpointRestore).toHaveBeenCalled()
312+
})
241313
})
242314
})

src/core/webview/checkpointRestoreHandler.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,27 @@ export async function handleCheckpointRestoreOperation(config: CheckpointRestore
2727
const { provider, currentCline, messageTs, checkpoint, operation, editData } = config
2828

2929
try {
30-
// For delete operations, ensure the task is properly aborted to handle any pending ask operations
30+
// For both delete and edit operations, we need to handle any pending ask operations
3131
// This prevents "Current ask promise was ignored" errors
32-
// For edit operations, we don't abort because the checkpoint restore will handle it
33-
if (operation === "delete" && currentCline && !currentCline.abort) {
34-
currentCline.abortTask()
35-
// Wait a bit for the abort to complete
36-
await pWaitFor(() => currentCline.abort === true, {
37-
timeout: 1000,
38-
interval: 50,
39-
}).catch(() => {
40-
// Continue even if timeout - the abort flag should be set
41-
})
32+
if (currentCline) {
33+
// Handle any pending ask operations by providing a response
34+
// This unblocks any waiting ask promises before the checkpoint restore
35+
if (currentCline.lastMessageTs) {
36+
// Use the public method to set a response for any pending ask
37+
currentCline.handleWebviewAskResponse("messageResponse", "", undefined)
38+
}
39+
40+
// For delete operations, abort the task
41+
if (operation === "delete" && !currentCline.abort) {
42+
currentCline.abortTask()
43+
// Wait a bit for the abort to complete
44+
await pWaitFor(() => currentCline.abort === true, {
45+
timeout: 1000,
46+
interval: 50,
47+
}).catch(() => {
48+
// Continue even if timeout - the abort flag should be set
49+
})
50+
}
4251
}
4352

4453
// For edit operations, set up pending edit data before restoration

0 commit comments

Comments
 (0)