From c401e7e8cb96af63bcc3eb89ca13e1d9fe5f6018 Mon Sep 17 00:00:00 2001 From: SidQin-cyber Date: Tue, 3 Mar 2026 16:58:07 +0800 Subject: [PATCH] fix(agents): set preserveSignatures to isAnthropic in resolveTranscriptPolicy When preserveSignatures was unconditionally false, stripThoughtSignatures removed msg_-prefixed thought_signature fields from Anthropic thinking blocks, breaking the API contract that requires thinking/redacted_thinking blocks to be returned byte-identical. Now preserveSignatures is true only for Anthropic (and Bedrock-Anthropic) providers. Closes #32526 Made-with: Cursor --- src/agents/transcript-policy.test.ts | 44 ++++++++++++++++++++++++++++ src/agents/transcript-policy.ts | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/agents/transcript-policy.test.ts b/src/agents/transcript-policy.test.ts index 13686c2f6fb..796cd2f43ed 100644 --- a/src/agents/transcript-policy.test.ts +++ b/src/agents/transcript-policy.test.ts @@ -76,6 +76,50 @@ describe("resolveTranscriptPolicy", () => { expect(policy.sanitizeMode).toBe("full"); }); + it("preserves thinking signatures for Anthropic provider (#32526)", () => { + const policy = resolveTranscriptPolicy({ + provider: "anthropic", + modelId: "claude-opus-4-5", + modelApi: "anthropic-messages", + }); + expect(policy.preserveSignatures).toBe(true); + }); + + it("preserves thinking signatures for Bedrock Anthropic (#32526)", () => { + const policy = resolveTranscriptPolicy({ + provider: "amazon-bedrock", + modelId: "us.anthropic.claude-opus-4-6-v1", + modelApi: "bedrock-converse-stream", + }); + expect(policy.preserveSignatures).toBe(true); + }); + + it("does not preserve signatures for Google provider (#32526)", () => { + const policy = resolveTranscriptPolicy({ + provider: "google", + modelId: "gemini-2.0-flash", + modelApi: "google-generative-ai", + }); + expect(policy.preserveSignatures).toBe(false); + }); + + it("does not preserve signatures for OpenAI provider (#32526)", () => { + const policy = resolveTranscriptPolicy({ + provider: "openai", + modelId: "gpt-4o", + modelApi: "openai", + }); + expect(policy.preserveSignatures).toBe(false); + }); + + it("does not preserve signatures for Mistral provider (#32526)", () => { + const policy = resolveTranscriptPolicy({ + provider: "mistral", + modelId: "mistral-large-latest", + }); + expect(policy.preserveSignatures).toBe(false); + }); + it("keeps OpenRouter on its existing turn-validation path", () => { const policy = resolveTranscriptPolicy({ provider: "openrouter", diff --git a/src/agents/transcript-policy.ts b/src/agents/transcript-policy.ts index 43238786e63..189dd7a3e80 100644 --- a/src/agents/transcript-policy.ts +++ b/src/agents/transcript-policy.ts @@ -123,7 +123,7 @@ export function resolveTranscriptPolicy(params: { (!isOpenAi && sanitizeToolCallIds) || requiresOpenAiCompatibleToolIdSanitization, toolCallIdMode, repairToolUseResultPairing, - preserveSignatures: false, + preserveSignatures: isAnthropic, sanitizeThoughtSignatures: isOpenAi ? undefined : sanitizeThoughtSignatures, sanitizeThinkingSignatures: false, dropThinkingBlocks,