From 1d3d99bb9efe12e43a96e6acb545475eee1d6db4 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Tue, 21 Apr 2026 04:36:16 -0300 Subject: [PATCH] fix(combo): resolve cross-provider thinking 400 and http clipboard (#1444) Integrated into release/v3.7.0 --- open-sse/translator/helpers/claudeHelper.ts | 8 +++++++- src/shared/utils/clipboard.ts | 11 ++++------- tests/unit/translator-helper-branches.test.ts | 3 ++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/open-sse/translator/helpers/claudeHelper.ts b/open-sse/translator/helpers/claudeHelper.ts index 2936b60240..bf4e589d0f 100644 --- a/open-sse/translator/helpers/claudeHelper.ts +++ b/open-sse/translator/helpers/claudeHelper.ts @@ -247,10 +247,16 @@ export function prepareClaudeRequest( let hasToolUse = false; let hasThinking = false; - // Always replace signature for all thinking blocks + // Convert thinking blocks to redacted_thinking and replace signature. + // When requests cross provider boundaries (e.g., combo fallback), the + // original thinking signature is invalid for the new provider, causing + // "Invalid signature in thinking block" 400 errors. redacted_thinking + // blocks are accepted without signature validation. for (const block of content) { if (block.type === "thinking" || block.type === "redacted_thinking") { + block.type = "redacted_thinking"; block.signature = DEFAULT_THINKING_CLAUDE_SIGNATURE; + delete block.thinking; hasThinking = true; } if (block.type === "tool_use") hasToolUse = true; diff --git a/src/shared/utils/clipboard.ts b/src/shared/utils/clipboard.ts index ee6d014dfb..50f6671dc9 100644 --- a/src/shared/utils/clipboard.ts +++ b/src/shared/utils/clipboard.ts @@ -11,13 +11,10 @@ * @returns true if copy succeeded, false otherwise */ export async function copyToClipboard(text: string): Promise { - // Method 1: Clipboard API (requires HTTPS / secure context) - if ( - typeof navigator !== "undefined" && - navigator.clipboard && - typeof window !== "undefined" && - window.isSecureContext - ) { + // Method 1: Clipboard API + // Works on HTTPS, localhost (treated as secure context), and some browsers + // even on HTTP. Try unconditionally — the catch handles failures. + if (typeof navigator !== "undefined" && navigator.clipboard) { try { await navigator.clipboard.writeText(text); return true; diff --git a/tests/unit/translator-helper-branches.test.ts b/tests/unit/translator-helper-branches.test.ts index e31c393352..33a59a0302 100644 --- a/tests/unit/translator-helper-branches.test.ts +++ b/tests/unit/translator-helper-branches.test.ts @@ -306,9 +306,10 @@ test("claudeHelper validates content, ordering and request preparation branches" assert.equal(prepared.messages[4].content[0].type, "tool_result"); assert.deepEqual( prepared.messages[5].content.map((block) => block.type), - ["thinking", "text"] + ["redacted_thinking", "text"] ); assert.ok(prepared.messages[5].content[0].signature); + assert.equal(prepared.messages[5].content[0].thinking, undefined); assert.equal(prepared.tools.length, 2); assert.equal(prepared.tools[0].cache_control, undefined); assert.deepEqual(prepared.tools[1].cache_control, { type: "ephemeral", ttl: "1h" });