Summaries & Sealed Blobs: the Reasoning Your API Returns

9 min read

N12
Deep Dive · Reasoning & Test-Time Compute

Summaries and sealed blobs: the reasoning your API actually hands back.

You cannot store the reasoning a model produced; you can store one of two substitutes, and they have opposite properties — a readable summary that is a fresh generation about the reasoning, and a sealed blob that is faithful to it and unreadable by you. Teams build faithfulness audits, process scoring and incident reconstruction on the readable one, which is the only one of the two that cannot support them. The sealed one, meanwhile, is a protocol obligation: pass it back byte-exact or your tool loop stops working.

Step 1Two artifacts, and they are not two views of one thing.

When a reasoning model finishes a turn, the tokens it reasoned with are gone from your side of the wire. What arrives instead, across all three major vendors as of November 2026, is some combination of two objects with almost nothing in common.

  • A summary. Human-readable prose about the reasoning, produced by the provider. On OpenAI's Responses API it streams as its own event type — response.reasoning_summary_text.delta and .done — separate from the answer. It exists to be shown to a user.
  • A sealed artifact. An opaque field that stands in for the reasoning so the model can pick the thread back up on the next request. Anthropic calls it a signature on each thinking block and documents it as an encrypted copy of the full reasoning. OpenAI returns one when you add reasoning.encrypted_content to the request's include list. Gemini attaches a thought_signature to parts. It exists to be handed back.

The division of labour is clean and almost everyone gets it backwards. The artifact you can read is the one with no evidentiary standing, because it was generated by a separate pass that may compress, reorder, omit or editorialise, and nothing in the API tells you which it did. The artifact with evidentiary standing — the one the provider itself treats as a faithful stand-in, to the point of refusing the request if you alter it — is the one you cannot read at all. There is no third option where you get both, and designing as though there were is the source of most of the trouble below.

The shape of the problem is familiar from reasoning-token accounting: you are billed for an object that has no matching record in your logs. Here you are additionally responsible for it — the object is inert to you and load-bearing for the next request.

Step 2The sealed artifact is a protocol obligation, and it is byte-fragile.

Treat the opaque field as metadata you may tidy up and the loop breaks — not degrades, breaks. Anthropic's documentation is explicit that thinking blocks come back unchanged in multi-turn and tool-use conversations, and that the API rejects a request in which previous content, thinking blocks included, has been modified. Gemini goes further and makes it a hard requirement for the thing most agents do constantly: passing thought signatures back is mandatory for function calling, and a missing one on Gemini 3 is a 400 whose message says so — Function call is missing a thought_signature.

Two consequences that will not show up in a happy-path test. First, the placement rules are per-vendor and non-obvious. Google documents that in a batch of parallel function calls only the first functionCall part carries a signature, while sequential multi-step calls each carry one; the signature must be returned inside its original part, the function-call id must match on the response, and parts carrying signatures must not be merged. A framework that normalises a message list by concatenating adjacent parts has silently destroyed a required field.

Second, Anthropic ships a second block type for safety-flagged reasoning: redacted_thinking, whose content sits in a data field with no readable text, and which must be passed back unchanged like any other. The documented trap is one line of code that almost everybody writes:

# Looks like a filter for "the thinking parts". Drops redacted_thinking,
# so the next request is missing a block the API requires.
blocks = [b for b in msg.content if b.type == "thinking"]   # wrong

# Both reasoning block types, in order, untouched.
blocks = [b for b in msg.content
          if b.type in ("thinking", "redacted_thinking")]   ok

# And never do this to a signed field — any rewrite invalidates it:
b.thinking = sanitize(b.thinking)                        # breaks the signature

That last line is not hypothetical. An OpenClaw bug report (issue #24612) traced persistent thinking blocks cannot be modified rejections in long conversations to a dependency applying a Unicode surrogate-sanitising function to signed thinking text before resending it. A cosmetic string normalisation — the kind every logging and serialisation layer performs without being asked — invalidated the cryptographic signature. The field is not a string your stack may touch; it is a token you are carrying for someone else.

On OpenAI's side the same obligation appears in a different disguise, as a consequence of statelessness. The Responses API will carry reasoning for you if you let it store the conversation; if you cannot, you ask for reasoning.encrypted_content and resend it yourself. Zero-data-retention organisations do not get a choice — OpenAI's own cookbook notes that store=false is enforced for them, which makes the encrypted-content path mandatory rather than optional. The reported failures cluster exactly where you would expect: an openai-agents-python issue (#919) reports reasoning models failing under store=False because reasoning.encrypted_content was not included in the request, and a JetBrains Koog issue (#2287) reports a streaming path dropping reasoning items that carry encrypted content but no summary — a combination you hit as soon as summaries are set to auto and the model declines to emit one. There is also a mode conflict worth knowing before you design around it: developers report that background mode requires store=true, which is the one thing encrypted content cannot be combined with.

If your agent framework sits between you and the provider, the integration test that matters is not "does a reasoning call work" but "does a reasoning call followed by a tool result work, on the third turn, with redaction in the middle". Every failure above is invisible on turn one.

Step 3The summary is a second generation, not a transcript.

The readable artifact is the one that invites mistakes, because it reads exactly like the thing you wanted. It is prose, in the first person, describing considerations in order. What it is not is a record: it is the output of a separate summarisation pass over the reasoning, with its own compression and its own failure modes, and the API gives you no way to tell what was dropped. Meta's Responses-protocol documentation states the split plainly — the summary streams as summary-text events while the raw chain of thought is available only in encrypted form for replay across turns.

So three inferences are unavailable to you, no matter how carefully the summary is written:

  • That a consideration absent from the summary was absent from the reasoning. Summarisation omits; omission here is not evidence.
  • That a consideration present in the summary drove the answer. This is the faithfulness problem one level removed — the summary may be a faithful summary of an unfaithful rationalisation, and both layers are opaque.
  • That two runs whose summaries differ reasoned differently. You have sampled the summariser twice as well as the model.

None of that makes summaries worthless. They are the right thing to show a user who needs to know what the agent is doing, and the UX case for showing them is strong. The error is promoting a UX artifact to an evidence artifact because it happens to be the only one in the response you can read.

Step 4Three things teams build on the wrong artifact.

Faithfulness and safety audits. A reviewer reading summaries to decide whether the agent considered the policy is measuring the summariser's coverage of the policy, and will report a clean result for a model that never considered it and a dirty one for a model that did. Worse, the result is not stable across provider-side changes to summarisation that you will never be told about. If a control depends on what the model considered, that control cannot be built from this input at all; it has to be rebuilt as a check on what the model did.

Process scoring in evals. Trajectory and process evaluation wants the intermediate steps, and the summary is the only intermediate text on hand, so judges get pointed at it. The consequence is a scorer that rewards legible narration. Note that this breaks the usual argument for process supervision in a specific way: process reward models are trained on reasoning steps, and a summary is not a step — it is a report about steps, with a different distribution and a provider in the loop. Score tool calls, arguments, retrievals and state transitions, which are yours, observable and comparable across runs.

Incident reconstruction. This is the expensive one, because the shortfall surfaces at the worst moment. A decision receipt whose "why" column holds a summary is recording a provider artifact in a document you intend to rely on. If your audit obligation is to reconstruct a decision — see audit trails — the reconstructable part is the input context, the tool calls and the effects, and the honest receipt says so rather than implying more.

There is a fourth, quieter version: retention policy. The sealed blob is an encrypted copy of reasoning over your prompt, so it inherits the sensitivity of the input, not the harmlessness of its appearance. Storing it for replay is storing customer data in a form your redaction pipeline cannot inspect — which is also why you must not let that pipeline rewrite it. Scope its lifetime to the conversation that needs it, and keep it out of long-lived trace stores where sampling and retention rules assume readable payloads. For organisations under zero data retention, this blob is the thing that makes a stateless reasoning loop possible at all, and it now lives in your store rather than the provider's.

Step 5Rules that keep the loop working.

  • Carry the sealed artifact byte-exact, end to end. No trimming, no Unicode normalisation, no re-encoding, no "harmless" whitespace fix. If a layer in your stack cannot promise that, the field must bypass it.
  • Enumerate reasoning block types, do not pattern-match one. Include redacted_thinking, and write the test that puts a redacted block in the middle of a tool-use conversation.
  • Treat a missing summary as normal. Summaries are optional output; code that assumes a reasoning item has summary text will drop the item that carries the signature, which is the one that mattered.
  • Pick stateful or stateless deliberately, once. Provider-side conversation state and self-managed encrypted content solve the same problem with different retention, latency and failure modes, and mode conflicts (background execution, storage flags) are easier to design around than to discover. The task-level consequences are in carrying reasoning across tool calls.
  • Never key a cache on a summary. It is non-deterministic text about the request. Cache on the prefix you actually sent.
  • Log the pair, not the prose. One row per step holding the tool call, its arguments, the result and the effect reproduces a decision. The summary is a column you may keep for readers; it is not the row's reason for existing.

The deeper design point is that chain of thought moved, over about two years, from "text in the response you may read and score" to "a provider-held object you may replay and a provider-written précis you may display". Everything built on the first understanding has to be ported, and the port is not a refactor: it is a decision about which of your controls were reading narration and must now read behaviour. Effort budgets make this sharper rather than softer, since the amount of reasoning behind a given answer is now a dial you set and cannot inspect.

Do two things. Write the failing test first: three turns, one tool call, one redacted or summary-less reasoning item, asserted byte-identical on the way back out — it will fail on most stacks today, and every later surprise is this bug. Then grep your evals, audit queries and incident templates for anything reading summary text, and for each one decide whether it is a reader-facing nicety or a control. The controls get rewritten against tool calls and effects; the niceties can stay, relabelled as what they are.