From 747b84972ed5053b35fe36872de4bae57206f30b Mon Sep 17 00:00:00 2001 From: Dominic Date: Tue, 27 Jan 2026 19:03:20 -0600 Subject: [PATCH] fix: skip synthetic toolResult for aborted/errored assistant messages --- src/agents/session-transcript-repair.test.ts | 45 ++++++++++++++++++++ src/agents/session-transcript-repair.ts | 3 ++ 2 files changed, 48 insertions(+) diff --git a/src/agents/session-transcript-repair.test.ts b/src/agents/session-transcript-repair.test.ts index ccc63ec7f..423b080d8 100644 --- a/src/agents/session-transcript-repair.test.ts +++ b/src/agents/session-transcript-repair.test.ts @@ -109,4 +109,49 @@ describe("sanitizeToolUseResultPairing", () => { expect(out.some((m) => m.role === "toolResult")).toBe(false); expect(out.map((m) => m.role)).toEqual(["user", "assistant"]); }); + + it("does not insert synthetic toolResult for aborted assistant message", () => { + const input = [ + { + role: "assistant", + content: [{ type: "toolCall", id: "call_aborted", name: "edit", arguments: {} }], + stopReason: "aborted", + }, + { role: "user", content: "next message" }, + ] as AgentMessage[]; + + const out = sanitizeToolUseResultPairing(input); + expect(out.filter((m) => m.role === "toolResult")).toHaveLength(0); + expect(out.map((m) => m.role)).toEqual(["assistant", "user"]); + }); + + it("does not insert synthetic toolResult for errored assistant message", () => { + const input = [ + { + role: "assistant", + content: [{ type: "toolCall", id: "call_errored", name: "cron", arguments: {} }], + stopReason: "error", + }, + { role: "user", content: "next message" }, + ] as AgentMessage[]; + + const out = sanitizeToolUseResultPairing(input); + expect(out.filter((m) => m.role === "toolResult")).toHaveLength(0); + expect(out.map((m) => m.role)).toEqual(["assistant", "user"]); + }); + + it("still inserts synthetic toolResult for normal assistant message with missing result", () => { + const input = [ + { + role: "assistant", + content: [{ type: "toolCall", id: "call_normal", name: "read", arguments: {} }], + stopReason: "toolUse", + }, + { role: "user", content: "next message" }, + ] as AgentMessage[]; + + const out = sanitizeToolUseResultPairing(input); + expect(out.filter((m) => m.role === "toolResult")).toHaveLength(1); + expect((out[1] as { toolCallId?: string }).toolCallId).toBe("call_normal"); + }); }); diff --git a/src/agents/session-transcript-repair.ts b/src/agents/session-transcript-repair.ts index d680beb4d..920c906c0 100644 --- a/src/agents/session-transcript-repair.ts +++ b/src/agents/session-transcript-repair.ts @@ -8,6 +8,9 @@ type ToolCallLike = { function extractToolCallsFromAssistant( msg: Extract, ): ToolCallLike[] { + const stopReason = (msg as { stopReason?: unknown }).stopReason; + if (stopReason === "error" || stopReason === "aborted") return []; + const content = msg.content; if (!Array.isArray(content)) return [];