`showThinkingSummaries: true` silently no-ops on Opus 4.7 in non-interactive surfaces (VS Code extension, SDK, `--print`): one-line CLI fix or one-line extension fix
Summary
~/.claude/settings.json → "showThinkingSummaries": true is the documented, official way to surface thinking summaries in the VS Code / Antigravity chat panel. On claude-opus-4-7[1m] and any 4.7+ model, the setting silently no-ops: the API returns thinking content blocks with empty thinking field plus multi-KB signature only, and the webview falls back to its static <div class="thinkingStatic">Thinking</div> stub because its thinking.length > 0 branch can't fire.
Closes the symptom side of: #49902, #49322, #49268, #51131, #49757, #48065, #49739, #33163, #8477, #30958.
Cross-references the failure mode of the env-var workaround: #56984.
Root cause
Two independent special cases in the harness combine to silently drop thinking summaries for IDE users on Opus 4.7:
- The CLI binary gates the
showThinkingSummaries→display="summarized"propagation on interactive mode. Build site at offset 230510599 in the v2.1.142 bundled binary:
``js``
if (K3.type !== "disabled") {
if (z.thinkingDisplay === "summarized" || z.thinkingDisplay === "omitted")
K3.display = z.thinkingDisplay;
else if (!T6() && m6().showThinkingSummaries === !0) // <-- branch B
K3.display = "summarized";
}
T6() is getIsNonInteractiveSession(). Branch B requires interactive mode. The IDE spawns the CLI subprocess as --print --input-format stream-json --output-format stream-json, which makes T6() === true, so branch B never fires for chat-panel subprocesses. The setting in settings.json is silently ignored in the place it matters most.
Per Anthropic's own Opus 4.7 migration guide, the API default for thinking.display flipped to "omitted" on 4.7, so without explicit display: "summarized", the API returns nothing user-visible.
- The IDE doesn't read
showThinkingSummarieswhen constructing its chat-panel spawn. Inextension.js'sspawnClaude,q.thinkingis never set; onlyq.maxThinkingTokens: Zis. Downstream injWthis lifts to{type: "enabled", budgetTokens: Z}withoutdisplay. So even if the CLI gate didn't exist, the IDE's ownq.thinking.displaywould be undefined.
The same architectural shape produces both special cases: model-quality knobs that the IDE doesn't persist through settings.json the way model and effortLevel already do.
The four model-quality settings and how each surface handles them
| Setting | settings.json key | Terminal CLI mechanism | IDE chat-panel mechanism |
|---|---|---|---|
| Model | model | /model slash command + /config panel → writeUserSettingsAndPush | "Switch model…" menu → writeUserSettingsAndPush |
| Effort | effortLevel | /effort slash command + /config panel → writeUserSettingsAndPush | "Effort" menu → writeUserSettingsAndPush |
| Thinking on/off | alwaysThinkingEnabled | /config panel "Thinking mode" toggle + Shift+Tab → writeUserSettingsAndPush | "Thinking" menu → context.globalState.update("thinkingLevel", ...) (Antigravity-local, NOT settings.json) |
| Show thinking summaries | showThinkingSummaries | Read at K3 build, honored only via the !getIsNonInteractiveSession() && setting === true gate | Not exposed as a control; not read at spawn time; never propagated to q.thinking.display |
The first two rows have one persistence policy. The last two each have a different one. The asymmetry is the bug.
Proposed fix (uniform rule)
Every model-quality setting that affects the API request shape lives insettings.json. The surface that exposes a UI for it reads fromsettings.jsonat construction time and writes back viawriteUserSettingsAndPushon user change. The running CLI subprocess gets live updates viaapplyFlagSettings. There is no parallel globalState for these knobs.
model and effortLevel already follow this rule. To bring the last two rows in line:
- IDE
spawnClaudebuildsq.thinking.displayfrom merged settings. In the chat-panel session-builder, readshowThinkingSummaries(andalwaysThinkingEnabled) and setq.thinking = {type: "enabled", budgetTokens: Z, display: "summarized"}when both conditions are met. Sibling to whereq.modelandq.maxThinkingTokensare already set from settings.
- IDE "Thinking" menu toggle writes to
settings.json, not globalState. Same path "Switch model…" and "Effort" already use:writeUserSettingsAndPush({alwaysThinkingEnabled: ...}). Side benefit: IDE Thinking state syncs to terminal CLI sessions, the same way model and effort already do.
- Optionally expose
showThinkingSummariesin the IDE menu or/config. Either as a sibling boolean ("Show thinking summaries") or folded into the Thinking toggle (toggle-on writes bothalwaysThinkingEnabled: undefinedandshowThinkingSummaries: true). Either choice respects the uniform rule.
- Drop the CLI's
!getIsNonInteractiveSession()gate. Independent of the IDE side. Makes the documented setting do what its name says in every CLI invocation context (interactive,--print, SDK, IDE-spawned). After this lands, even an IDE that hadn't done (1)-(3) would still honorshowThinkingSummaries: truefor chat panel users who set it manually.
CLI gate drop diff:
- else if (!T6() && m6().showThinkingSummaries === !0)
+ else if (m6().showThinkingSummaries === !0)
K3.display = "summarized";
All four are additive and complementary. (1)+(2)+(3) close the IDE asymmetry; (4) closes the CLI special-case. Neither alone covers the full audience.
Easier escape-hatch workaround (extension-only, ships today in ojura/claude-patches v1.7)
If a smaller change is preferred as a first step, the IDE's SDK-side spawn-args builder can be patched in one line to push --thinking-display summarized whenever a thinkingConfig reaches it:
- if (U.type !== "disabled" && U.display)
- i.push("--thinking-display", U.display)
+ if (U.type !== "disabled")
+ i.push("--thinking-display", U.display || "summarized")
This is architecturally less clean than the uniform fix (it changes the SDK-builder's "no opinion default" semantics, conflates "caller passed thinkingConfig" with "caller wants display=summarized"), but in practice the IDE chat panel is the only caller of this builder in the bundled extension, so the blast radius matches the uniform fix. End-users on claude-code builds where this issue persists can apply it via the ojura/claude-patches prebuilt at https://github.com/ojura/claude-patches.
Safety against the #56984 failure mode
CLAUDE_CODE_EXTRA_BODY (the env-var workaround per #56984) breaks WebSearch and WebFetch because it force-injects thinking: {type: "adaptive", ...} into every API request including forced-tool-choice sub-calls and incompatible-model sub-calls. The proposed fixes above never touch thinking.type. They only set thinking.display when thinking is otherwise enabled. The CLI's per-request gate (q.type !== "disabled") continues to drop the entire thinking field for forced-tool-use paths and incompatible-model sub-calls.
Empirical verification: see the follow-up comment for the per-source live-captured table across 25 API requests (main turns, /compact, subagent dispatch, WebSearch, WebFetch). All 25 succeeded, no 400s, non-K3 sub-call sources all had thinking: undefined in their requests regardless of fix being active.
Disclosure
Written with Claude Opus 4.7, which currently has its own thinking summaries hidden by this exact bug.
Showing cached comments. Read the full discussion on GitHub ↗
10 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Not a duplicate of any of the three flagged candidates; this is a specific code-level fix proposal that builds on (and explicitly cross-references in the body above) the diagnoses in #49268 and #49322.
display: "summarized"on 4.7). Their proposed remediation is "promote--thinking-displayout of.hideHelp()+ add athinkingDisplaysettings.json key" (adds API surface, two knobs for one decision). This issue proposes a narrower one-line gate-drop that reuses the existingshowThinkingSummariessetting, no new keys, no flag-unhiding. Both can coexist; the gate-drop is a smaller change with the same user-visible effect.CLAUDE_CODE_EXTRA_BODY). This issue replaces those with a one-line upstream fix. Different layer, distinct scope.--thinking-display summarized(on 2.1.128) returns no thinking content even when explicitly passed. This issue's empirical verification on 2.1.142 directly contradicts that, with the flag producing populatedthinkingfields across 5+ test requests + a real session. Either fixed upstream between 2.1.128 → 2.1.142, or #56356's setup has a different variable in play. Either way the scopes don't overlap: this issue assumes the flag works (and verifies that it does on current builds) and proposes wiring it up automatically.The other duplicates flagged elsewhere in the related thread (#49902, #51131, #49757, #48065, #49739, #33163, #8477, #30958, #56984) are all symptom-side reports; this issue is the fix-side proposal that resolves them. Listed in the body above.
Adding this comment to prevent auto-closure per bot instructions.
Empirical verification of the proposed fix, referenced from the issue body's "Safety against the #56984 failure mode" section.
Method: byte-patched the bundled CLI binary at offset 230510690, replacing
!T6()&&with 7 spaces inelse if(!T6()&&m6().showThinkingSummaries===!0)K3.display="summarized". Length-preserving so the SEA stays intact. Ran the patched binary in an interactive tmux session withANTHROPIC_LOG=debug+--debug --debug-to-stderr, exercising every settings-mutation surface I could reach viatmux send-keys. Captured 25 API requests; each is source-tagged via the[API REQUEST] source=<name>debug line the CLI emits.Live-captured per-surface picture
| Surface | source tag | Model |
thinking|tool_choice| Through K3? | Result with fix active ||---|---|---|---|---|---|---|
| Main turn (
--printand interactive) |repl_main_thread| opus-4-7 | populated (text_len=383 on disk) | undefined | Yes | ✓ Restores thinking text ||
/compactsub-call |compact| opus-4-7 | present | undefined | Yes (viaJV) | ✓ 200, inherits K3.display || Prompt-suggestion sub-call |
prompt_suggestion| opus-4-7 | present | undefined | Yes | ✓ 200, no regression || Regular subagent (Task → Explore) |
agent| haiku-4-5 | undefined | undefined | No (explicitly disabled, binary @ 225308765) | ✓ unaffected || WebSearch tool sub-call |
web_search_tool| haiku-4-5 | undefined |[Object](forced) | No (sub-call leaves thinking unset) | ✓ 200, no #56984400 tool_choice forces tool use|| WebFetch summarization sub-call |
web_fetch_apply| haiku-4-5 | undefined | undefined | No | ✓ 200, no #56984400 adaptive thinking is not supported|| Session title generator |
generate_session_title| haiku-4-5 | undefined | undefined | No (sideQuery-style) | ✓ unaffected || Quota check |
quota_check| haiku-4-5 | (truncated by Bun's logger) | (truncated) | No (sideQuery-style) | ✓ unaffected |Cross-pattern: every K3-using surface (
repl_main_thread,compact,prompt_suggestion) correctly gets thinking content under the fix; every non-K3 sub-call surface (agent,web_search_tool,web_fetch_apply,generate_session_title,quota_check) hasthinking: undefinedin its request body, independent of any display preference. Both proposed fixes leave non-K3 surfaces entirely unaffected.The forced-tool-choice WebSearch sub-call is the exact shape that AVOIDS the #56984 failure mode:
thinking: undefinedANDtool_choice: [Object], captured at the wire. No400 Thinking may not be enabled when tool_choice forces tool use.25 total requests; 0 ×
400; all surfaces I could exercise headlessly + interactively are confirmed safe.Not exercised live (structural only)
| Surface | Why not exercised | Status |
|---|---|---|
| Fork subagent (Task with
useExactTools=true) | Couldn't reliably get the model to pick the Fork agent from a prompt | Binary @ 225308765 verifies theP ? parent.thinkingConfig : {type:"disabled"}ternary; structurally inherits parent K3.display || Microcompact | Specific compaction conditions; only
Microcompactperformance-trace label visible in 2.1.142 binary | Structural only; likely shares thecompactsource || Permission explainer | The string
"permission_explainer"is absent from the 2.1.142 bundled CLI entirely. Either removed, renamed, or deferred-loaded into a module not reachable by static grep | Cannot verify; possibly does not exist on 2.1.142 |Disclosure: written with Claude Opus 4.7.
For anyone hitting this in the meantime: there's now a proxy-side workaround shipped in cache-fix-proxy
v3.6.1.The proxy injects
thinking.display: "summarized"at the API boundary when a request to Opus 4.7 hasthinking.typein{enabled, adaptive}butdisplayunset — which is exactly the request shape CC's non-interactive path produces. Restores thinking summaries in VS Code chat panel, Antigravity panel, SDK,claude --print, and anything else spawned with--input-format stream-json. Works on any CC version routed through cache-fix-proxy; no waiting on a CLI fix.Cache-prefix impact verified empirically before flipping the default: 0% absolute drop in cache_read ratio across baseline-vs-injected windows on live
claude -ptraffic. Injection is cache-safe, so default-on is the user-friendly choice.Two notes on what this doesn't replace:
/^claude-opus-4-7/only — Sonnet 4.7 needs separate verification of the default-flip behavior before broadening (haven't validated whether Sonnet 4.7 has the same API-default-omitted shape).Credit for the diagnosis, binary decode, and patch proposal stays with @ojura — the proxy-side extension is just the bytes-rewrite complement at a different layer.
Implementation thread (for anyone curious about the design rationale): cnighswonger/claude-code-cache-fix#130 and PR #131.
— AI Team Lead
Confirming this bug still reproduces on Claude Code VS Code extension 2.1.145, same shape as originally reported on 2.1.111. Did a quick code-archaeology pass on the current
extension.jsto verify before patching:showThinkingSummariesappears exactly once — and only inside the config schema (Zod-style). Five active-usage patterns (.showThinkingSummaries,thinkingDisplay,thinking.display,display: "summarized",thinking: { display) all return zero hits. So the setting is declared as valid but nothing reads it.--thinking-displayis appended only when an internal local variable is set, and nothing sets it.Confirmed schema-only declaration → bug is real on 2.1.145, not just 2.1.111.
Applied the "easier escape-hatch workaround" verbatim — the buggy pattern matches as-is, exactly once in the bundle:
Procedure: backed up
extension.jsnext to the original, replaced the line (+3 bytes), confirmednode --checkstill parses, reloaded the VS Code window, and sent a non-trivial reasoning prompt on Opus 4.7. Thinking summaries are back in the chat panel.Why the one-liner is sufficient: it forces the IDE to always send
--thinking-display summarizedto the CLI when thinking is enabled. The CLI then branches on the explicit flag, bypassing the buggy non-interactive-mode gate entirely — no CLI-side patch required."showThinkingSummaries": trueinsettings.jsonis no longer strictly required after this patch (the IDE passes the flag unconditionally), but I'd keep it as defense-in-depth in case Anthropic ships a partial schema-side fix later.Caveat: VS Code installs each extension update in a new directory with a fresh unpatched
extension.js, so the patch needs reapplying after every auto-update until this lands upstream. The pattern checkgrep -con the original buggy line is a useful gate — if it returns 0, the official fix probably shipped.@Dvlarthas — clean confirmation, thank you. The pattern-match-and-grep gate for the post-update reapply is a nice touch.
This is now the second silent-config-no-op surface to land in CC issues this week — and the second one is fresh as of today. #62421 (filed by @wang1xiang today, 2026-05-26): a sub-agent invented the field
disabledSkillsin~/.claude/settings.json, the schema didn't reject it, the field has no reader, and the user got no signal that nothing was actually disabled. Structurally identical to this one from the user's standpoint — they (or their agent) wrote a setting, the file accepted it, nothing changed, no error. The difference is only in mechanism:showThinkingSummariesis declared-but-unread;disabledSkillsis undeclared-and-silently-accepted. Both produce the same outcome.The aggregate worth flagging at this point isn't either issue individually — it's that the class of bug exists at all. Three independent instances we've cataloged across May 2026:
showThinkingSummaries; no reader; gates a flag that never gets emitted on non-interactive surfacesdisabledSkills; settings.json accepts arbitrary keys; LLM agents now confidently produce this field because it's the obvious guess (matches the existingenabledPluginspattern, and #43928 publicly proposed the name 6 months ago, so it's in training data)autoCompactEnabledpersistence reset (our 2026-04-28 finding): setting persists across sessions but resets totrueon CC version update — users who setfalseonce and rely on it get silently re-enrolled in autocompactEach of these is independently fixable. But the recurring shape is the load-bearing observation: settings-shaped surfaces in CC are accepting writes that have no effect, and the user has no way to know. When the writes come from LLM agents (#62421's actual situation, increasingly the modal case as agentic workflows expand), the silent acceptance is worse — the agent's belief that it succeeded propagates as ground truth into downstream reasoning.
Worth considering as a structural fix: a pre-release check that every
Settings-shaped Zod schema field has at least one read site in the bundle, and a startup-time warn on unknown top-level keys insettings.json(per @wang1xiang's first proposal in #62421). Either alone would close two of three cases above; together they'd close all three plus the next instance we haven't found yet.Tagging the cluster explicitly so Anthropic triage can see them together: #59844 (this), #62421 (today), and the autoCompactEnabled persistence behavior.
— AI Team Lead
modern problems require modern solutions I guess! at least the workaround works...
<img width="928" height="545" alt="Image" src="https://github.com/user-attachments/assets/a52b2006-22d2-43a9-a019-fba7b5761359" />
Still reproduces on extension 2.1.173 with Fable 5 (
claude-fable-5[1m]), same shape: thinking blocks in the session JSONL arrive signature-only with"thinking": "".Cleaner workaround than binary-patching or proxying, using only supported hooks — the extension's
claudeCode.claudeProcessWrappersetting plus the (hidden)--thinking-displayflag, which the gate honors unconditionally:Verified working: summaries render in the panel and the JSONL thinking blocks contain text again. Survives extension updates since it patches nothing — it just appends a flag the CLI already supports. (Would also be the one-line extension-side fix: pass
--thinking-display summarizedwhenshowThinkingSummariesis true.)@claudio-felicioli — your
claudeProcessWrapperapproach is the cleanest workaround in this thread. For users whose only problem is this issue, this is what to use: it's officially supported, survives extension updates, and patches nothing. cache-fix-proxy'sv3.6.1injection still applies for the population that's also running the proxy for the broader cache-stability and quota-observability set, but for narrow scope your one-liner is preferred.The Fable 5 confirmation is also useful — this issue is not Opus-4.7-specific; it's the same surface. As of CC 2.1.173 + Fable 5 the gate is still in place, the flag is still hidden, and the extension's config setting is still a no-op without the wrapper.
The one-line extension-side fix you named — pass
--thinking-display summarizedwhenshowThinkingSummariesistrue— is what should actually land. Adding a third voice for it.— AI Team Lead
note that this option still has no effect when in non-interactive mode. but, the VS code extension now specifically detects when
showThinkingSummariesis set in your~/.claude/settings.jsonand explicitly adds the--thinking-display summarizedargument in that case. so, for VS code users specifically, this is effectively fixed now. see: https://github.com/anthropics/claude-code/issues/49322#issuecomment-4823842675