From 483790d8c97b189984ae3ce9036f129c4273da87 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Tue, 26 May 2026 11:36:27 +0700 Subject: [PATCH] Replay completed tool results from provider history --- .../agent/providers/claude/agent.test.ts | 43 +++++++++++++++++++ .../server/agent/providers/claude/agent.ts | 4 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/server/src/server/agent/providers/claude/agent.test.ts b/packages/server/src/server/agent/providers/claude/agent.test.ts index ae8f575be..28c525280 100644 --- a/packages/server/src/server/agent/providers/claude/agent.test.ts +++ b/packages/server/src/server/agent/providers/claude/agent.test.ts @@ -56,6 +56,49 @@ describe("convertClaudeHistoryEntry", () => { expect(Array.isArray(mapBlocks.mock.calls[0][0])).toBe(true); }); + test("replays persisted Claude tool results as completed tool calls", () => { + const entry = { + type: "user", + message: { + role: "user", + content: [ + { + type: "tool_result", + tool_use_id: "toolu_persisted", + content: "done", + }, + ], + }, + toolUseResult: { + stdout: "done", + stderr: "", + interrupted: false, + }, + }; + + const completedToolCall: AgentTimelineItem[] = [ + { + type: "tool_call", + callId: "toolu_persisted", + name: "Bash", + status: "completed", + detail: { + type: "shell", + command: "echo done", + output: "done", + exitCode: 0, + }, + error: null, + }, + ]; + + const mapPersistedToolResultBlocks = (): AgentTimelineItem[] => completedToolCall; + + expect(convertClaudeHistoryEntry(entry, mapPersistedToolResultBlocks)).toEqual( + completedToolCall, + ); + }); + test("returns user messages when no tool blocks exist", () => { const entry = { type: "user", diff --git a/packages/server/src/server/agent/providers/claude/agent.ts b/packages/server/src/server/agent/providers/claude/agent.ts index 02f36805b..16b48868b 100644 --- a/packages/server/src/server/agent/providers/claude/agent.ts +++ b/packages/server/src/server/agent/providers/claude/agent.ts @@ -1188,7 +1188,7 @@ function isToolResultUserEntry(entry: unknown): boolean { } function isSyntheticHistoryUserEntry(entry: Record): boolean { - return isSyntheticUserEntry(entry); + return isSyntheticUserEntry(entry) && !isToolResultUserEntry(entry); } function firstTrimmedString(sources: readonly unknown[]): string | null { @@ -4666,7 +4666,7 @@ function convertClaudeHistoryEntryPreamble( if (entry.isCompactSummary) { return { shortCircuit: [] }; } - if (entry.type === "user" && isSyntheticUserEntry(entry)) { + if (entry.type === "user" && isSyntheticHistoryUserEntry(entry)) { return { shortCircuit: [] }; }