Follow Discord
Sweep 09 Oct 2026 · 17:27Z Build v2.1.296 517 read Stable v2.1.287 Latest v2.1.296 Next v2.1.296 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · api

compaction-threshold changedbuild-with-claude/compaction-threshold

Nearest release: v2.1.296, published an hour after this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Recorded here
Lines+95added
Lines−20removed
From line 2,900 where the diff opens
First seen 21 Sep 2026 this site's first read of the page
Recorded edits8to this page, all time

The whole hunk

from line 2900, old and new numbered
/
lines
from line 2900
29002900 
29012901On Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5, remove the `thinking` and `redacted_thinking` blocks from any assistant turn you re-insert after the compaction block, or send `thinking.block_binding.prefix_mismatch_behavior: "drop_block"` with the `thinking-binding-controls-2026-08-01` [beta header](https://platform.claude.com/docs/en/api/beta-headers). Those blocks were produced when the full history was present, so they no longer pass the [conversation check](https://platform.claude.com/docs/en/build-with-claude/thinking#preserved-in-conversation). Where the check is enforced, the continuation request is rejected with a 400 error. The preserved text and tool blocks can stay as they are. Letting the API summarize everything, without re-inserting earlier turns, avoids this. On Claude Sonnet 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`. With `between_tools`, remove the blocks instead. On Claude Haiku 5.5, `block_binding` works only with `thinking: {"type": "adaptive"}`, so with `thinking: {"type": "disabled"}`, remove the blocks instead.
29022902 
2903Here's an example that uses `pause_after_compaction` to preserve the prior exchange and the current user message (three messages total) verbatim instead of summarizing them:
2903Here's an example that uses `pause_after_compaction` to preserve the prior exchange and the current user message (three messages total) instead of summarizing them. It keeps their text and tool blocks unchanged, removes the `thinking` and `redacted_thinking` blocks from the preserved assistant turn, and drops that turn if nothing else is left:
29042904 
29052905<CodeGroup>
29062906 ```bash cURL
from line 2992
29922992 compaction_block = response.content[0]
29932993 
29942994 # Preserve the prior exchange + current user message (3 messages)
2995 # by including them after the compaction block
2996 preserved_messages = messages[-3:] if len(messages) >= 3 else messages
2995 # by including them after the compaction block, without thinking
2996 # blocks; an assistant turn left with no content is dropped
2997 preserved_messages = [
2998 {
2999 **message,
3000 "content": [
3001 block
3002 for block in message["content"]
3003 if block.type not in ("thinking", "redacted_thinking")
3004 ],
3005 }
3006 if message["role"] == "assistant"
3007 else message
3008 for message in messages[-3:]
3009 ]
3010 preserved_messages = [m for m in preserved_messages if m["content"]]
29973011 
29983012 # Build new message list: compaction + preserved messages
29993013 new_assistant_content = [compaction_block]
from line 3072
30583072 const compactionBlock = response.content[0];
30593073 
30603074 // Preserve the prior exchange + current user message (3 messages)
3061 // by including them after the compaction block
3062 const preservedMessages = messages.length >= 3 ? messages.slice(-3) : [...messages];
3075 // by including them after the compaction block, without thinking
3076 // blocks; an assistant turn left with no content is dropped
3077 const preservedMessages = messages
3078 .slice(-3)
3079 .map((message) =>
3080 message.role === "assistant" && Array.isArray(message.content)
3081 ? {
3082 ...message,
3083 content: message.content.filter(
3084 (block) => block.type !== "thinking" && block.type !== "redacted_thinking"
3085 )
3086 }
3087 : message
3088 )
3089 .filter((message) => message.content.length > 0);
30633090 
30643091 // Build new message list: compaction + preserved messages
30653092 const messagesAfterCompaction: Anthropic.Beta.Messages.BetaMessageParam[] = [
from line 3157
31303157 if (!response.Content[0].TryPickCompaction(out _))
31313158 throw new InvalidOperationException("Expected compaction block");
31323159 
3133 var preserved = messages.Count >= 3
3134 ? messages.Skip(messages.Count - 3).ToList()
3135 : new List<BetaMessageParam>(messages);
3160 // Preserve the prior exchange + current user message (3 messages),
3161 // without thinking blocks; an assistant turn left empty is dropped
3162 var preserved = messages
3163 .Skip(Math.Max(0, messages.Count - 3))
3164 .Select(message => message.Content.TryPickBetaContentBlockParams(out var blocks)
3165 ? new BetaMessageParam
3166 {
3167 Role = message.Role,
3168 Content = blocks
3169 .Where(block => block.Type.GetString() is not ("thinking" or "redacted_thinking"))
3170 .ToList()
3171 }
3172 : message)
3173 .Where(message => !message.Content.TryPickBetaContentBlockParams(out var blocks) || blocks.Count > 0)
3174 .ToList();
31363175 
31373176 var messagesAfterCompaction = new List<BetaMessageParam>
31383177 {
from line 3254
32153254 if response.StopReason == "compaction" {
32163255 compactionParam := response.Content[0].ToParam()
32173256 
3257 // Preserve the prior exchange + current user message (3 messages),
3258 // without thinking blocks; an assistant turn left empty is dropped
32183259 var preserved []anthropic.BetaMessageParam
3219 if len(messages) >= 3 {
3220 preserved = messages[len(messages)-3:]
3221 } else {
3222 preserved = messages
3260 for _, message := range messages[max(0, len(messages)-3):] {
3261 var content []anthropic.BetaContentBlockParamUnion
3262 for _, block := range message.Content {
3263 if block.OfThinking == nil && block.OfRedactedThinking == nil {
3264 content = append(content, block)
3265 }
3266 }
3267 if len(content) == 0 {
3268 continue
3269 }
3270 message.Content = content
3271 preserved = append(preserved, message)
32233272 }
32243273 
32253274 messagesAfterCompaction := []anthropic.BetaMessageParam{
from line 3346
32973346 // Check if compaction occurred and paused
32983347 if (response.stopReason().isPresent()
32993348 && response.stopReason().get().equals(BetaStopReason.COMPACTION)) {
3300 // Preserve the prior exchange + current user message (3 messages)
3301 List<BetaMessageParam> preservedMessages = messages.size() >= 3
3302 ? new ArrayList<>(messages.subList(messages.size() - 3, messages.size()))
3303 : new ArrayList<>(messages);
3349 // Preserve the prior exchange + current user message (3 messages),
3350 // without thinking blocks; an assistant turn left empty is dropped
3351 List<BetaMessageParam> preservedMessages = new ArrayList<>();
3352 for (BetaMessageParam message : messages.subList(Math.max(0, messages.size() - 3), messages.size())) {
3353 if (message.content().isBetaContentBlockParams()) {
3354 message = message.toBuilder()
3355 .contentOfBetaContentBlockParams(message.content().asBetaContentBlockParams().stream()
3356 .filter(block -> !block.isThinking() && !block.isRedactedThinking())
3357 .toList())
3358 .build();
3359 if (message.content().asBetaContentBlockParams().isEmpty()) {
3360 continue;
3361 }
3362 }
3363 preservedMessages.add(message);
3364 }
33043365 
33053366 // Build new message list: compaction + preserved messages
33063367 List<BetaMessageParam> messagesAfterCompaction = new ArrayList<>();
from line 3429
33683429 if ($response->stopReason === 'compaction') {
33693430 $compactionBlock = $response->content[0];
33703431 
3371 $preserved = count($messages) >= 3
3372 ? array_slice($messages, -3)
3373 : $messages;
3432 // Preserve the prior exchange + current user message (3 messages),
3433 // without thinking blocks; an assistant turn left empty is dropped
3434 $preserved = array_values(array_filter(array_map(
3435 fn($message) => $message['role'] === 'assistant'
3436 ? array_merge($message, ['content' => array_values(array_filter(
3437 $message['content'],
3438 fn($block) => !in_array($block->type, ['thinking', 'redacted_thinking'], true)
3439 ))])
3440 : $message,
3441 array_slice($messages, -3)
3442 ), fn($message) => $message['content'] !== []));
33743443 
33753444 $messagesAfterCompaction = array_merge(
33763445 [['role' => 'assistant', 'content' => [$compactionBlock]]],
from line 3500
34313500 if response.stop_reason == :compaction
34323501 compaction_block = response.content[0]
34333502 
3434 preserved = messages.length >= 3 ? messages[-3..-1] : messages.dup
3503 # Preserve the prior exchange + current user message (3 messages),
3504 # without thinking blocks; an assistant turn left empty is dropped
3505 preserved = messages.last(3).map do |message|
3506 next message unless message[:role] == "assistant"
3507 
3508 message.merge(content: message[:content].reject { |block| %i[thinking redacted_thinking].include?(block.type) })
3509 end.reject { |message| message[:content].empty? }
34353510 
34363511 messages_after_compaction = [
34373512 { role: "assistant", content: [compaction_block] }
Feedback