From 1f04bab83f46689611f8f1196f611e4921f52d15 Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Fri, 6 Feb 2026 20:14:28 -0700 Subject: [PATCH] fix: guard getPendingAttemptCompletionToolUseId against multi-tool_use messages When the last assistant message contains attempt_completion alongside other tool_use blocks (e.g. read_file + attempt_completion) and the process crashes before any tool_result is saved, the method previously returned the attempt_completion ID. This caused resumeTaskFromHistory to bypass the rewind and treat the task as completed, while the other tool_uses never executed. Now returns undefined when other tool_use blocks are present alongside attempt_completion, allowing the rewind path to handle the cleanup correctly. Addresses review feedback from @roomote. --- src/core/task/Task.ts | 7 +++++++ src/core/task/__tests__/rewind-integration.spec.ts | 8 +++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index e2d2a303eb..4f04d72397 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2129,6 +2129,13 @@ export class Task extends EventEmitter implements TaskLike { block.type === "tool_use" && block.name === "attempt_completion", ) + // Only treat as pending completion if there are no other unanswered + // tool_use blocks — otherwise the rewind path is more appropriate. + if (toolUse) { + const hasOtherToolUse = content.some((block) => block.type === "tool_use" && block !== toolUse) + if (hasOtherToolUse) return undefined + } + return toolUse?.id } diff --git a/src/core/task/__tests__/rewind-integration.spec.ts b/src/core/task/__tests__/rewind-integration.spec.ts index d09848f287..6d447461c9 100644 --- a/src/core/task/__tests__/rewind-integration.spec.ts +++ b/src/core/task/__tests__/rewind-integration.spec.ts @@ -456,13 +456,15 @@ describe("rewind-integration", () => { expect(parsedContent.status).toBe("completed") }) - it("attempt_completion alongside another tool_use in same assistant message", () => { + it("attempt_completion alongside another tool_use in same assistant message returns undefined", () => { const task = createTask(mockProvider) const now = Date.now() // Edge case: assistant message contains both a read_file tool_use and // an attempt_completion tool_use. getPendingAttemptCompletionToolUseId - // should still detect the attempt_completion. + // should return undefined so the rewind path is used instead — + // the other tool_uses never executed, so treating it as a pending + // completion would bypass the rewind and produce misleading context. task.apiConversationHistory = [ { role: "user", @@ -491,6 +493,6 @@ describe("rewind-integration", () => { ] as any[] const pendingId: string | undefined = (task as any).getPendingAttemptCompletionToolUseId() - expect(pendingId).toBe("toolu_completion_888") + expect(pendingId).toBeUndefined() }) })