






















@@ -8,6 +8,7 @@ import {
88dropThinkingBlocks,
99isAssistantMessageWithContent,
1010sanitizeThinkingForRecovery,
11+stripInvalidThinkingSignatures,
1112wrapAnthropicStreamWithRecovery,
1213} from "./thinking.js";
1314@@ -156,6 +157,85 @@ describe("dropThinkingBlocks", () => {
156157});
157158});
158159160+describe("stripInvalidThinkingSignatures", () => {
161+it("returns the original reference when no invalid thinking signatures are present", () => {
162+const messages: AgentMessage[] = [
163+castAgentMessage({ role: "user", content: "hello" }),
164+castAgentMessage({
165+role: "assistant",
166+content: [
167+{ type: "thinking", thinking: "internal", thinkingSignature: "sig" },
168+{ type: "text", text: "answer" },
169+],
170+}),
171+];
172+173+const result = stripInvalidThinkingSignatures(messages);
174+175+expect(result).toBe(messages);
176+});
177+178+it("strips thinking blocks with missing, empty, or blank signatures", () => {
179+const messages: AgentMessage[] = [
180+castAgentMessage({
181+role: "assistant",
182+content: [
183+{ type: "thinking", thinking: "missing" },
184+{ type: "thinking", thinking: "empty", thinkingSignature: "" },
185+{ type: "thinking", thinking: "blank", thinkingSignature: " " },
186+{ type: "thinking", thinking: "signed", thinkingSignature: "sig" },
187+{ type: "text", text: "answer" },
188+],
189+}),
190+];
191+192+const result = stripInvalidThinkingSignatures(messages);
193+const assistant = result[0] as Extract<AgentMessage, { role: "assistant" }>;
194+195+expect(result).not.toBe(messages);
196+expect(assistant.content).toEqual([
197+{ type: "thinking", thinking: "signed", thinkingSignature: "sig" },
198+{ type: "text", text: "answer" },
199+]);
200+});
201+202+it("uses non-empty omitted-reasoning text when all thinking signatures are invalid", () => {
203+const messages: AgentMessage[] = [
204+castAgentMessage({
205+role: "assistant",
206+content: [{ type: "thinking", thinking: "reasoning", thinkingSignature: "" }],
207+}),
208+];
209+210+const result = stripInvalidThinkingSignatures(messages);
211+const assistant = result[0] as Extract<AgentMessage, { role: "assistant" }>;
212+213+expect(assistant.content).toEqual([{ type: "text", text: OMITTED_ASSISTANT_REASONING_TEXT }]);
214+});
215+216+it("strips redacted thinking blocks with invalid opaque signatures", () => {
217+const messages: AgentMessage[] = [
218+castAgentMessage({
219+role: "assistant",
220+content: [
221+{ type: "redacted_thinking", data: "" },
222+{ type: "redacted_thinking", signature: " " },
223+{ type: "redacted_thinking", data: "opaque" },
224+{ type: "text", text: "answer" },
225+],
226+}),
227+];
228+229+const result = stripInvalidThinkingSignatures(messages);
230+const assistant = result[0] as Extract<AgentMessage, { role: "assistant" }>;
231+232+expect(assistant.content).toEqual([
233+{ type: "redacted_thinking", data: "opaque" },
234+{ type: "text", text: "answer" },
235+]);
236+});
237+});
238+159239describe("sanitizeThinkingForRecovery", () => {
160240it("drops the latest assistant message when the thinking block is unsigned", () => {
161241const messages = castAgentMessages([
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。