Print/SDK mode persists empty thinking blocks in session transcript (signature only) — interactive mode persists full text
Description
In non-interactive mode (claude -p, the stream-json SDK path, and sessions spawned byclaude remote-control — all recorded with entrypoint: sdk-cli), the session
transcript JSONL persists thinking blocks as {"type": "thinking", "thinking": "", — the reasoning text is always empty. The same conversation in
"signature": "<sig>"}
interactive mode (TUI / desktop app) persists the full thinking text.
This breaks auditability of agent reasoning for remote-control and SDK-driven
sessions: the conversation is fully recorded, but the reasoning that preceded each
response is lost.
Repro (Windows 11, Claude Code 2.1.226 and 2.1.235, native installer)
printf '{"type":"user","message":{"role":"user","content":[{"type":"text","text":"Why is mergesort stable? 2 sentences."}]}}\n' | \
claude -p --input-format stream-json --output-format stream-json --verbose \
--effort high --model claude-fable-5 > out.json
Then inspect the new ~/.claude/projects/<proj>/<uuid>.jsonl:
- the assistant message contains
{"type":"thinking","thinking":"","signature":"..."}(empty text, valid signature) - with
--include-partial-messages,thinking_deltastream events are emitted but theirthinkingfield is empty (0 chars)
Interactive sessions on the same machine/model (e.g. desktop app running Claude Code
2.1.229) persist full thinking text in the same JSONL format — measured 100% of blocks
across 5 sessions. In a sample of 29 real sessions, every sdk-cli session had 0%
thinking text persisted and every current interactive session had 100%.
Expected
Thinking text persisted to the session transcript in print/SDK mode, matching
interactive behavior — or at least an opt-in flag/env (e.g.--include-thinking / CLAUDE_CODE_PERSIST_THINKING=1).
Notes
- Tried without success:
--effort high,MAX_THINKING_TOKENS,
--include-partial-messages, CLAUDE_CODE_JSONL_TRANSCRIPT=1.
- The transcript writer appears to receive the block already without the text field
(it is "healed" to an empty string — telemetry tengu_content_block_healed,
missingThinking: true), so the text is dropped at stream-accumulation time in
non-interactive mode, before writing.