claude-code-cache-fix 3.6.0 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,6 +43,8 @@ On every `/v1/messages` request, 7 extensions run in order:
43
43
 
44
44
  Extensions are hot-reloadable — add, remove, or modify `.mjs` files in `proxy/extensions/` and changes apply to the next request without restarting. Configuration in `proxy/extensions.json`.
45
45
 
46
+ **Developing a new extension?** See [docs/parallel-proxy-test-harness.md](docs/parallel-proxy-test-harness.md) for the pattern we use to test extensions end-to-end against real `claude -p` traffic without disturbing the production proxy.
47
+
46
48
  ### Running as a service
47
49
 
48
50
  **Recommended (Linux/macOS) — `install-service` subcommand:**
@@ -204,6 +206,8 @@ Options (all optional; all fall back to the same env vars used by the CLI):
204
206
 
205
207
  **CLI invocation is unchanged.** `node proxy/server.mjs`, `cache-fix-proxy server`, and the wrapper's child-fork path all auto-listen and install SIGTERM/SIGINT handlers as before. Library imports never trigger that behavior — the auto-listen is gated behind a main-module check.
206
208
 
209
+ *The embeddable factory was contributed by [@bilby91](https://github.com/bilby91) at [Crunchloop DAP](https://dap.crunchloop.ai) — see [PR #123](https://github.com/cnighswonger/claude-code-cache-fix/pull/123).*
210
+
207
211
  ## Quick Start: Preload (CC v2.1.112 and earlier)
208
212
 
209
213
  If you're on a Node.js-based CC version (v2.1.112 or earlier), the preload interceptor works without a proxy:
@@ -613,6 +617,35 @@ The Mode A/B separation protects against cases where the sentinel might be follo
613
617
  | `CACHE_FIX_MICROCOMPACT_REDACT_LEN` | `64` | Mode B prefix length in dump records. Set to `0` to suppress the prefix entirely. |
614
618
  | `CACHE_FIX_DUMP_MICROCOMPACT_INCLUDE_NORMALIZED` | unset | Add post-normalization text alongside (not replacing) raw `sentinel_text` in dump records. |
615
619
 
620
+ ## Thinking summaries (proxy mode, opt-in, Opus 4.7+)
621
+
622
+ On Opus 4.7, Anthropic flipped the API default for `thinking.display` from `"summarized"` to `"omitted"`. In parallel, Claude Code's CLI has a `!getIsNonInteractiveSession()` gate that propagates `display: "summarized"` only when the session is interactive. The combination means every CC subprocess spawned with `--input-format stream-json` — the VS Code chat panel, the Antigravity panel, the SDK, `claude --print` — sends a thinking-enabled request (`thinking.type` is either `"enabled"` or `"adaptive"` depending on CC version) without `display`, and the API responds with thinking blocks whose `thinking` field is empty (plus a multi-KB signature). The UI shows a static "Thinking" stub while the agent runs but never any reasoning content.
623
+
624
+ Upstream root cause and patch proposed in [anthropics/claude-code#59844](https://github.com/anthropics/claude-code/issues/59844) (credit: [@ojura](https://github.com/ojura)). This extension is the proxy-side complement: when a request to an Opus 4.7 endpoint has thinking enabled but `display` unset, inject the configured mode at the API boundary. Works on any CC version routed through cache-fix-proxy, no waiting on Anthropic to ship the CLI fix.
625
+
626
+ ```sh
627
+ # Restore summaries (the built-in default — non-interactive surfaces get reasoning content)
628
+ export CACHE_FIX_THINKING_DISPLAY=summarized
629
+
630
+ # Force-suppress override (agent runtimes that don't want thinking blocks at all)
631
+ export CACHE_FIX_THINKING_DISPLAY=omitted
632
+
633
+ # Explicit no-op (extension passes through unchanged)
634
+ export CACHE_FIX_THINKING_DISPLAY=disabled
635
+ ```
636
+
637
+ The extension is **default-on** as of v3.6.1. The cache-prefix test measured 0% absolute drop in steady-state `cache_read` ratio when injection is active on Opus 4.7 (5 sequential `claude -p` calls per window, baseline vs injected — both windows held 1.000 cache_read ratio from call 2 onward). Adding `thinking.display` to the request body changes the bytes Anthropic hashes, but Anthropic's cache layer accepts and indexes the injected-prefix the same way it does any other prefix. Users who want the older "no injection" behavior (e.g. to avoid any request-body mutation at all) explicitly set `CACHE_FIX_THINKING_DISPLAY=disabled`.
638
+
639
+ Scoping rules baked into the extension:
640
+
641
+ - **Model-gated.** Only fires on requests whose `model` matches `/^claude-opus-4-7/` — covers `claude-opus-4-7` and `claude-opus-4-7-1m`. Sonnet 4.7 needs separate verification (the API default-flip may differ); future versions (4.8+) require an explicit cache-fix bump rather than auto-applying unverified behavior.
642
+ - **User opt-out preserved.** If the request already has `thinking.display` set (either `"summarized"` or `"omitted"`), the extension never overwrites. Explicit user choice always wins.
643
+ - **Thinking-active types only.** The extension fires on `thinking.type` ∈ `{ "enabled", "adaptive" }` — the two active modes that produce thinking blocks on Opus 4.7. Other values (`"disabled"`, future modes) are skipped. Conservative: if Anthropic ships a new thinking type with different display semantics, we'd rather miss the fix than auto-apply incorrect behavior.
644
+
645
+ | Env var | Default | Purpose |
646
+ |---------|---------|---------|
647
+ | `CACHE_FIX_THINKING_DISPLAY` | `summarized` (built-in) | One of `summarized` / `omitted` / `disabled`. `summarized` restores thinking summaries (default). `omitted` force-suppresses thinking blocks. `disabled` opts the extension out entirely. |
648
+
616
649
  ## System prompt rewrite (preload mode, optional)
617
650
 
618
651
  The interceptor can rewrite Claude Code's `# Output efficiency` system-prompt section. Disabled by default. Enable with `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT`. See [docs/output-efficiency-prompts.md](docs/output-efficiency-prompts.md) for the three known prompt variants and usage instructions.
@@ -643,13 +676,13 @@ We monitor 30+ upstream Claude Code issues related to cache, quota, and context
643
676
 
644
677
  ## Used in production
645
678
 
646
- - **[Crunchloop DAP](https://dap.crunchloop.ai)** — Agent SDK / DAP development environment. First production team to merge the interceptor to trunk for team-wide deployment (2026-04-10). Identified two distinct cache regression patterns through real-world testing — tool ordering jitter and the fresh-session sort gap — and contributed debug traces that drove the v1.5.1 and v1.6.2 fixes.
679
+ - **[Crunchloop DAP](https://dap.crunchloop.ai)** — Agent SDK / DAP development environment. First production team to merge the interceptor to trunk for team-wide deployment (2026-04-10). Identified two distinct cache regression patterns through real-world testing — tool ordering jitter and the fresh-session sort gap — and contributed debug traces that drove the v1.5.1 and v1.6.2 fixes. Contributed the embeddable proxy factory (v3.6.0) that lets the proxy run in-process inside Bun-compiled and DAP-style agent binaries without forking a Node child.
647
680
  - **[VM Farms](https://vmfarms.com)** ([@vmfarms](https://github.com/vmfarms)) — Agent development environment running concurrent multi-runner workloads with `--resume --fork-session`. Surfaced three cache-fix proxy-mode bugs: the resume-marker regex no-op (#96), TTL tier detection gap vs preload mode (#97), and image-strip stderr leak past `CACHE_FIX_DEBUG` (#98) — all addressed in the v3.4.0 release.
648
681
 
649
682
  ## Contributors
650
683
 
651
684
  - **[@VictorSun92](https://github.com/VictorSun92)** — Original monkey-patch fix for v2.1.88, identified partial scatter on v2.1.90, contributed forward-scan detection, correct block ordering, tighter block matchers, and the optional output-efficiency rewrite hook
652
- - **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — Agent SDK / DAP production environment validation, 1h cache TTL confirmation, tool ordering jitter discovery via debug trace (fixed in v1.5.1), fresh-session sort bug discovery via SKILLS SORT diagnostic (fixed in v1.6.2). First production team to roll the interceptor to trunk.
685
+ - **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — Agent SDK / DAP production environment validation, 1h cache TTL confirmation, tool ordering jitter discovery via debug trace (fixed in v1.5.1), fresh-session sort bug discovery via SKILLS SORT diagnostic (fixed in v1.6.2). First production team to roll the interceptor to trunk. Designed and contributed the embeddable proxy factory (`startProxy()` / `createProxyServer()`) shipped in v3.6.0 (PR #123).
653
686
  - **[@jmarianski](https://github.com/jmarianski)** — Root cause analysis via MITM proxy capture and Ghidra reverse engineering, multi-mode cache test script
654
687
  - **[@cnighswonger](https://github.com/cnighswonger)** — Fingerprint stabilization, tool ordering fix, image stripping, monitoring features, overage TTL downgrade discovery, proxy architecture, package maintainer
655
688
  - **[@ArkNill](https://github.com/ArkNill)** — Microcompact mechanism analysis, GrowthBook flag documentation, false rate limiter identification, fingerprint verification fix for CC v2.1.108+ (PR #21), Korean README (PR #22), [claude-code-hidden-problem-analysis](https://github.com/ArkNill/claude-code-hidden-problem-analysis) research
@@ -662,6 +695,7 @@ We monitor 30+ upstream Claude Code issues related to cache, quota, and context
662
695
  - **[@X-15](https://github.com/X-15)** — VS Code extension validation, per-fix health status analysis confirming safety check behavior on v2.1.105 (#16)
663
696
  - **[@deafsquad](https://github.com/deafsquad)** — Universal smoosh_split un-smoosh fix (PR #26), source-level function attribution of resume scatter bug (anthropics/claude-code#43657), OTEL telemetry discovery, proposed and built proxy architecture for v3.0.0
664
697
  - **[@vmfarms](https://github.com/vmfarms)** — Concurrent multi-runner production validation, surfaced proxy-mode resume-marker regex no-op (#96), TTL tier detection gap (#97), and image-strip stderr leak (#98)
698
+ - **[@ojura](https://github.com/ojura)** — Opus 4.7 thinking-summaries root-cause analysis: filed [anthropics/claude-code#59844](https://github.com/anthropics/claude-code/issues/59844) with the CLI-binary decode (`!getIsNonInteractiveSession()` gate at offset 230510599 in v2.1.142) and the two-stacked-special-cases framing, which made the `thinking-display` extension (v3.6.1) a clean proxy-side complement to the proposed upstream fix
665
699
 
666
700
  If you contributed to the community effort on these issues and aren't listed here, please open an issue or PR — we want to credit everyone properly.
667
701
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-cache-fix",
3
- "version": "3.6.0",
3
+ "version": "3.6.1",
4
4
  "description": "Cache optimization proxy and interceptor for Claude Code. Fixes prompt cache bugs, stabilizes prefix, reduces quota burn.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -0,0 +1,79 @@
1
+ // thinking-display extension
2
+ //
3
+ // Restores Opus 4.7 thinking summaries in non-interactive CC surfaces
4
+ // (VS Code chat panel, SDK, `claude --print`, anything spawned with
5
+ // `--input-format stream-json`).
6
+ //
7
+ // Background: Anthropic flipped the `thinking.display` default to `"omitted"`
8
+ // on Opus 4.7. CC's CLI propagates `display: "summarized"` only when the
9
+ // session is interactive (the `!getIsNonInteractiveSession()` gate); every
10
+ // non-interactive subprocess gets the API default of omitted thinking, which
11
+ // renders as an empty stub in the IDE. Upstream root cause and patch proposed
12
+ // in anthropics/claude-code#59844 (credit: @ojura).
13
+ //
14
+ // This extension is the proxy-side workaround. When a request has thinking
15
+ // enabled but `display` unset, inject the configured mode at the API
16
+ // boundary. Works on any CC version routed through cache-fix-proxy without
17
+ // waiting for Anthropic to ship the CLI fix.
18
+ //
19
+ // Config (env var; built-in default is "summarized"):
20
+ // CACHE_FIX_THINKING_DISPLAY=summarized — inject display: "summarized"
21
+ // (main case; restores summaries in IDE/SDK/--print). DEFAULT.
22
+ // CACHE_FIX_THINKING_DISPLAY=omitted — inject display: "omitted"
23
+ // (force-suppress override; for agent runtimes that don't want thinking
24
+ // blocks at all, regardless of what their CLI sends)
25
+ // CACHE_FIX_THINKING_DISPLAY=disabled — no injection; extension is a no-op
26
+ //
27
+ // Default flipped to "summarized" in v3.6.1 after the cache-prefix test on
28
+ // Opus 4.7 measured 0% absolute drop in steady-state cache_read ratio with
29
+ // injection enabled (well inside the ≤5% "preserved" threshold). Users who
30
+ // want the older "no injection" behavior set CACHE_FIX_THINKING_DISPLAY=disabled.
31
+
32
+ const MODEL_REGEX = /^claude-opus-4-7/;
33
+
34
+ function resolveMode() {
35
+ const v = process.env.CACHE_FIX_THINKING_DISPLAY;
36
+ if (v === "summarized" || v === "omitted" || v === "disabled") return v;
37
+ return "summarized";
38
+ }
39
+
40
+ // Thinking types that produce thinking blocks. CC v2.1.131+ ships
41
+ // `type: "adaptive"` (dynamic-budget mode) by default for the Bun binary's
42
+ // non-interactive paths; older versions and explicit-budget configs may
43
+ // still send `"enabled"`. Both produce the same empty-thinking symptom
44
+ // when `display` is unset on Opus 4.7, so both are in scope.
45
+ const ACTIVE_THINKING_TYPES = new Set(["enabled", "adaptive"]);
46
+
47
+ function shouldInject(body) {
48
+ if (!body || typeof body !== "object") return false;
49
+ if (typeof body.model !== "string") return false;
50
+ if (!MODEL_REGEX.test(body.model)) return false;
51
+ if (!body.thinking || typeof body.thinking !== "object") return false;
52
+ if (!ACTIVE_THINKING_TYPES.has(body.thinking.type)) return false;
53
+ // Only inject when display is unset. Preserve any explicit user choice
54
+ // (including explicit "omitted" for compliance opt-out).
55
+ return body.thinking.display === undefined;
56
+ }
57
+
58
+ export { MODEL_REGEX, ACTIVE_THINKING_TYPES, resolveMode, shouldInject };
59
+
60
+ export default {
61
+ name: "thinking-display",
62
+ description:
63
+ "Inject thinking.display on Opus 4.7 requests when unset, to restore " +
64
+ "thinking summaries lost to CC's non-interactive CLI gate (claude-code#59844)",
65
+ enabled: false,
66
+ order: 360,
67
+
68
+ async onRequest(ctx) {
69
+ const mode = resolveMode();
70
+ if (mode === "disabled") return;
71
+ if (!shouldInject(ctx.body)) return;
72
+
73
+ ctx.body.thinking.display = mode;
74
+
75
+ if (ctx.meta) {
76
+ ctx.meta.thinkingDisplayInjected = mode;
77
+ }
78
+ },
79
+ };
@@ -9,6 +9,7 @@
9
9
  "content-strip": { "enabled": true, "order": 330 },
10
10
  "tool-input-normalize": { "enabled": true, "order": 340 },
11
11
  "microcompact-stability": { "enabled": true, "order": 350 },
12
+ "thinking-display": { "enabled": true, "order": 360 },
12
13
  "cache-control-normalize": { "enabled": true, "order": 400 },
13
14
  "messages-cache-breakpoint": { "enabled": true, "order": 410 },
14
15
  "ttl-management": { "enabled": true, "order": 500 },