fix: preserve replay invalid on mutating retries

This commit is contained in:
Eva
2026-04-11 04:11:08 +07:00
committed by Peter Steinberger
parent 6b100ca559
commit b9a9472cfd
2 changed files with 60 additions and 1 deletions

View File

@@ -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", () => {

View File

@@ -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;
}