`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

Status Open
Maintainer reply None cached
Activity 11 comments · opened May 16, 2026

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:

  1. The CLI binary gates the showThinkingSummariesdisplay="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.

  1. The IDE doesn't read showThinkingSummaries when constructing its chat-panel spawn. In extension.js's spawnClaude, q.thinking is never set; only q.maxThinkingTokens: Z is. Downstream in jW this lifts to {type: "enabled", budgetTokens: Z} without display. So even if the CLI gate didn't exist, the IDE's own q.thinking.display would 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 in settings.json. The surface that exposes a UI for it reads from settings.json at construction time and writes back via writeUserSettingsAndPush on user change. The running CLI subprocess gets live updates via applyFlagSettings. There is no parallel globalState for these knobs.

model and effortLevel already follow this rule. To bring the last two rows in line:

  1. IDE spawnClaude builds q.thinking.display from merged settings. In the chat-panel session-builder, read showThinkingSummaries (and alwaysThinkingEnabled) and set q.thinking = {type: "enabled", budgetTokens: Z, display: "summarized"} when both conditions are met. Sibling to where q.model and q.maxThinkingTokens are already set from settings.
  1. 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.
  1. Optionally expose showThinkingSummaries in the IDE menu or /config. Either as a sibling boolean ("Show thinking summaries") or folded into the Thinking toggle (toggle-on writes both alwaysThinkingEnabled: undefined and showThinkingSummaries: true). Either choice respects the uniform rule.
  1. 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 honor showThinkingSummaries: true for 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.

View original on GitHub ↗

10 Comments

github-actions[bot] · 3 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/49268
  2. https://github.com/anthropics/claude-code/issues/49322
  3. https://github.com/anthropics/claude-code/issues/56356

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ojura · 3 months ago

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.

  • #49268 is the root-cause diagnosis (harness doesn't set display: "summarized" on 4.7). Their proposed remediation is "promote --thinking-display out of .hideHelp() + add a thinkingDisplay settings.json key" (adds API surface, two knobs for one decision). This issue proposes a narrower one-line gate-drop that reuses the existing showThinkingSummaries setting, no new keys, no flag-unhiding. Both can coexist; the gate-drop is a smaller change with the same user-visible effect.
  • #49322 discusses user-side workarounds (wrapper scripts, CLAUDE_CODE_EXTRA_BODY). This issue replaces those with a one-line upstream fix. Different layer, distinct scope.
  • #56356 is the opposite report: claims --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 populated thinking fields 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.

ojura · 3 months ago

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 in else if(!T6()&&m6().showThinkingSummaries===!0)K3.display="summarized". Length-preserving so the SEA stays intact. Ran the patched binary in an interactive tmux session with ANTHROPIC_LOG=debug + --debug --debug-to-stderr, exercising every settings-mutation surface I could reach via tmux 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 (--print and interactive) | repl_main_thread | opus-4-7 | populated (text_len=383 on disk) | undefined | Yes | ✓ Restores thinking text |
| /compact sub-call | compact | opus-4-7 | present | undefined | Yes (via JV) | ✓ 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 #56984 400 tool_choice forces tool use |
| WebFetch summarization sub-call | web_fetch_apply | haiku-4-5 | undefined | undefined | No | ✓ 200, no #56984 400 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) has thinking: undefined in 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: undefined AND tool_choice: [Object], captured at the wire. No 400 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 the P ? parent.thinkingConfig : {type:"disabled"} ternary; structurally inherits parent K3.display |
| Microcompact | Specific compaction conditions; only Microcompact performance-trace label visible in 2.1.142 binary | Structural only; likely shares the compact source |
| 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.

cnighswonger · 3 months ago

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 has thinking.type in {enabled, adaptive} but display unset — 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.

npm install -g claude-code-cache-fix@latest
# Auto-enabled for claude-opus-4-7 — no env var needed.
# To force-suppress instead (agent runtimes that don't want thinking blocks at all):
#   export CACHE_FIX_THINKING_DISPLAY=omitted
# To disable the extension entirely:
#   export CACHE_FIX_THINKING_DISPLAY=disabled

Cache-prefix impact verified empirically before flipping the default: 0% absolute drop in cache_read ratio across baseline-vs-injected windows on live claude -p traffic. Injection is cache-safe, so default-on is the user-friendly choice.

Two notes on what this doesn't replace:

  • Only helps users routing CC through cache-fix-proxy. For the rest of the user base, the upstream CLI fix in the OP is still the right answer.
  • Model-gated to /^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

Dvlarthas · 3 months ago

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.js to verify before patching:

  • showThinkingSummaries appears 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.
  • The spawn-args builder still contains the same gated push the issue describes: --thinking-display is 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:

- if(L.type!=="disabled"&&L.display)i.push("--thinking-display",L.display)
+ if(L.type!=="disabled")i.push("--thinking-display",L.display||"summarized")

Procedure: backed up extension.js next to the original, replaced the line (+3 bytes), confirmed node --check still 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 summarized to 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": true in settings.json is 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 check grep -c on the original buggy line is a useful gate — if it returns 0, the official fix probably shipped.

_Disclosure: written with Claude Opus 4.7, running Claude Code extension 2.1.145 on VS Code 1.114.0 — and seeing the thinking summaries this comment was drafted with._
cnighswonger · 3 months ago

@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 disabledSkills in ~/.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: showThinkingSummaries is declared-but-unread; disabledSkills is 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:

  • #59844 (this issue): Zod schema declares showThinkingSummaries; no reader; gates a flag that never gets emitted on non-interactive surfaces
  • #62421 (today): No schema entry for disabledSkills; settings.json accepts arbitrary keys; LLM agents now confidently produce this field because it's the obvious guess (matches the existing enabledPlugins pattern, and #43928 publicly proposed the name 6 months ago, so it's in training data)
  • autoCompactEnabled persistence reset (our 2026-04-28 finding): setting persists across sessions but resets to true on CC version update — users who set false once and rely on it get silently re-enrolled in autocompact

Each 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 in settings.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

BBPSBB · 2 months ago

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" />

claudio-felicioli · 2 months ago

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.claudeProcessWrapper setting plus the (hidden) --thinking-display flag, which the gate honors unconditionally:

#!/usr/bin/env bash
# ~/.claude/vscode-claude-wrapper.sh — inject the flag into the main
# conversation process only (identified by --output-format).
args=("$@")
for a in "$@"; do
  if [ "$a" = "--output-format" ]; then
    args+=("--thinking-display" "summarized")
    break
  fi
done
exec "${args[@]}"
// VS Code settings (machine/user scope)
{ "claudeCode.claudeProcessWrapper": "/home/<user>/.claude/vscode-claude-wrapper.sh" }

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 summarized when showThinkingSummaries is true.)

cnighswonger · 2 months ago

@claudio-felicioli — your claudeProcessWrapper approach 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's v3.6.1 injection 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 summarized when showThinkingSummaries is true — is what should actually land. Adding a third voice for it.

— AI Team Lead

shawnz · 2 months ago

note that this option still has no effect when in non-interactive mode. but, the VS code extension now specifically detects when showThinkingSummaries is set in your ~/.claude/settings.json and explicitly adds the --thinking-display summarized argument 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

Showing cached comments. Read the full discussion on GitHub ↗