From b9a9472cfd67d6136c354f01559df4244ca28fa0 Mon Sep 17 00:00:00 2001 From: Eva Date: Sat, 11 Apr 2026 04:11:08 +0700 Subject: [PATCH] fix: preserve replay invalid on mutating retries --- ...-embedded-subscribe.handlers.tools.test.ts | 57 +++++++++++++++++++ .../pi-embedded-subscribe.handlers.tools.ts | 4 +- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.test.ts b/src/agents/pi-embedded-subscribe.handlers.tools.test.ts index b9a208da35c..dd05ce3d946 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.test.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.test.ts @@ -279,6 +279,63 @@ describe("handleToolExecutionEnd mutating failure recovery", () => { expect(ctx.state.replayInvalid).toBe(true); }); + + it("keeps successful mutating retries replay-invalid after an earlier tool failure", async () => { + const { ctx } = createTestContext(); + + await handleToolExecutionStart( + ctx as never, + { + type: "tool_execution_start", + toolName: "edit", + toolCallId: "tool-edit-fail-first", + args: { + file_path: "/tmp/demo.txt", + old_string: "beta stale", + new_string: "gamma", + }, + } as never, + ); + + await handleToolExecutionEnd( + ctx as never, + { + type: "tool_execution_end", + toolName: "edit", + toolCallId: "tool-edit-fail-first", + isError: true, + result: { error: "Could not find the exact text in /tmp/demo.txt" }, + } as never, + ); + + await handleToolExecutionStart( + ctx as never, + { + type: "tool_execution_start", + toolName: "edit", + toolCallId: "tool-edit-retry-success", + args: { + file_path: "/tmp/demo.txt", + old_string: "beta", + new_string: "gamma", + }, + } as never, + ); + + await handleToolExecutionEnd( + ctx as never, + { + type: "tool_execution_end", + toolName: "edit", + toolCallId: "tool-edit-retry-success", + isError: false, + result: { ok: true }, + } as never, + ); + + expect(ctx.state.lastToolError).toBeUndefined(); + expect(ctx.state.replayInvalid).toBe(true); + }); }); describe("handleToolExecutionEnd timeout metadata", () => { diff --git a/src/agents/pi-embedded-subscribe.handlers.tools.ts b/src/agents/pi-embedded-subscribe.handlers.tools.ts index 7dc19911004..8c87e76c193 100644 --- a/src/agents/pi-embedded-subscribe.handlers.tools.ts +++ b/src/agents/pi-embedded-subscribe.handlers.tools.ts @@ -763,6 +763,7 @@ export async function handleToolExecutionEnd( const startData = toolStartData.get(toolStartKey); toolStartData.delete(toolStartKey); const callSummary = ctx.state.toolMetaById.get(toolCallId); + const completedMutatingAction = !isToolError && Boolean(callSummary?.mutatingAction); const meta = callSummary?.meta; ctx.state.toolMetas.push({ toolName, meta }); ctx.state.toolMetaById.delete(toolCallId); @@ -792,7 +793,8 @@ export async function handleToolExecutionEnd( } else { ctx.state.lastToolError = undefined; } - } else if (callSummary?.mutatingAction) { + } + if (completedMutatingAction) { ctx.state.replayInvalid = true; }