fix(agents): preserve thinking/redacted_thinking blocks in stripThoughtSignatures#29620
fix(agents): preserve thinking/redacted_thinking blocks in stripThoughtSignatures#29620iamhitarth wants to merge 1 commit intoopenclaw:mainfrom
Conversation
…htSignatures stripThoughtSignatures removes thought_signature fields from content blocks for cross-provider compatibility. However, it also modifies thinking and redacted_thinking blocks, which Anthropic's API requires to be identical to the original response. When these modified blocks appear in the latest assistant message on a subsequent turn, the API rejects the request with: "thinking or redacted_thinking blocks in the latest assistant message cannot be modified" Skip thinking/redacted_thinking block types in stripThoughtSignatures to preserve them unchanged. Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 004e91311b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (blockType === "thinking" || blockType === "redacted_thinking") { | ||
| return block; |
There was a problem hiding this comment.
Gate thinking-block bypass to Anthropic requests
Returning early for every thinking/redacted_thinking block disables the allowBase64Only signature sanitization that Gemini paths rely on (resolveTranscriptPolicy sets this for Google/OpenRouter in src/agents/transcript-policy.ts:115-124). In provider-switch sessions (for example, Claude history containing thought_signature: "msg_*"), those non-base64 signatures are now forwarded unchanged, undoing the cross-provider compatibility behavior stripThoughtSignatures is intended to provide and potentially causing Gemini follow-up requests to fail. This bypass should be scoped to Anthropic replay requirements rather than applied unconditionally.
Useful? React with 👍 / 👎.
Greptile SummaryAdds an early return in Changes:
Impact:
Confidence Score: 4/5
Last reviewed commit: 004e913 |
| // Never modify thinking/redacted_thinking blocks — Anthropic requires these | ||
| // to be identical to the original API response. | ||
| const blockType = (block as { type?: unknown }).type; | ||
| if (blockType === "thinking" || blockType === "redacted_thinking") { | ||
| return block; | ||
| } |
There was a problem hiding this comment.
test file needs updating - the test at lines 148-160 in src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts expects thinking blocks with msg_* signatures to be stripped, but this fix preserves them unchanged (which is correct per Anthropic's requirements)
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-helpers/bootstrap.ts
Line: 68-73
Comment:
test file needs updating - the test at lines 148-160 in `src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts` expects `thinking` blocks with `msg_*` signatures to be stripped, but this fix preserves them unchanged (which is correct per Anthropic's requirements)
How can I resolve this? If you propose a fix, please make it concise.|
This pull request has been automatically marked as stale due to inactivity. |
|
Still relevant — this fix prevents Anthropic API rejections when thinking blocks are passed back in a multi-turn conversation. The |
|
Closing as superseded — upstream merged equivalent fixes for thinking block preservation (strip only msg_* signatures, preserve thinking/redacted_thinking blocks verbatim) in the interim. The bug this addressed is no longer present in current main. |
|
Noting that upstream has since merged equivalent fixes for thinking block preservation (stripping only |
…story sanitization The Anthropic API requires thinking and redacted_thinking blocks to be returned byte-for-byte identical in conversation history. Multiple sanitization functions were mutating these blocks via object spread and field deletion, causing API rejections with: "thinking or redacted_thinking blocks in the latest assistant message cannot be modified." Add isImmutableThinkingBlock() guard and apply it in all four mutation sites: - stripThoughtSignatures (bootstrap.ts) — was deleting signature fields - sanitizeToolResult (pi-embedded-subscribe.tools.ts) — was deleting .data - pruneProcessedHistoryImages (history-image-prune.ts) — was replacing blocks - dropThinkingBlocks (thinking.ts) — was missing redacted_thinking type Fixes openclaw#29618 Supersedes openclaw#29620 Co-Authored-By: Claude Opus 4.6 <[email protected]>
…story sanitization The Anthropic API requires thinking and redacted_thinking blocks to be returned byte-for-byte identical in conversation history. Multiple sanitization functions were mutating these blocks via object spread and field deletion, causing API rejections with: "thinking or redacted_thinking blocks in the latest assistant message cannot be modified." Add isImmutableThinkingBlock() guard and apply it in all four mutation sites: - stripThoughtSignatures (bootstrap.ts) — was deleting signature fields - sanitizeToolResult (pi-embedded-subscribe.tools.ts) — was deleting .data - pruneProcessedHistoryImages (history-image-prune.ts) — was replacing blocks - dropThinkingBlocks (thinking.ts) — was missing redacted_thinking type Fixes openclaw#29618 Supersedes openclaw#29620 Co-Authored-By: Claude Opus 4.6 <[email protected]>
…story sanitization The Anthropic API requires thinking and redacted_thinking blocks to be returned byte-for-byte identical in conversation history. Multiple sanitization functions were mutating these blocks via object spread and field deletion, causing API rejections with: "thinking or redacted_thinking blocks in the latest assistant message cannot be modified." Add isImmutableThinkingBlock() guard and apply it in all four mutation sites: - stripThoughtSignatures (bootstrap.ts) — was deleting signature fields - sanitizeToolResult (pi-embedded-subscribe.tools.ts) — was deleting .data - pruneProcessedHistoryImages (history-image-prune.ts) — was replacing blocks - dropThinkingBlocks (thinking.ts) — was missing redacted_thinking type Fixes openclaw#29618 Supersedes openclaw#29620 Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
Closing as duplicate; this was superseded by #58916. |
Summary
stripThoughtSignaturesremovesthought_signaturefields from content blocks for cross-provider compatibility (e.g. Gemini)thinkingandredacted_thinkingblocks via object spread + delete"thinking or redacted_thinking blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response."Fix
Skip
type: "thinking"andtype: "redacted_thinking"blocks instripThoughtSignatures— these blocks should never have their signatures stripped since they must be preserved verbatim for Anthropic.Fixes #29618
Reproduction
sanitizeSessionHistory→sanitizeSessionMessagesImages→stripThoughtSignaturesTest plan
thought_signature)🤖 Generated with Claude Code