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
This commit is contained in:
SidQin-cyber 2026-03-03 16:58:07 +08:00 committed by Josh Lehman
parent 709dc671e4
commit c401e7e8cb
No known key found for this signature in database
GPG Key ID: D141B425AC7F876B
2 changed files with 45 additions and 1 deletions

View File

@ -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",

View File

@ -123,7 +123,7 @@ export function resolveTranscriptPolicy(params: {
(!isOpenAi && sanitizeToolCallIds) || requiresOpenAiCompatibleToolIdSanitization,
toolCallIdMode,
repairToolUseResultPairing,
preserveSignatures: false,
preserveSignatures: isAnthropic,
sanitizeThoughtSignatures: isOpenAi ? undefined : sanitizeThoughtSignatures,
sanitizeThinkingSignatures: false,
dropThinkingBlocks,