claude-code-cache-fix 1.6.2-debug.1 → 1.6.3
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 +90 -1
- package/README.zh.md +128 -3
- package/package.json +4 -1
- package/preload.mjs +150 -1
package/README.md
CHANGED
|
@@ -94,6 +94,90 @@ This keeps images in the last 3 user messages and replaces older ones with a tex
|
|
|
94
94
|
|
|
95
95
|
Set to `0` (default) to disable.
|
|
96
96
|
|
|
97
|
+
## System prompt rewrite (optional)
|
|
98
|
+
|
|
99
|
+
The interceptor can also rewrite Claude Code's `# Output efficiency` system-prompt section before the request is sent.
|
|
100
|
+
|
|
101
|
+
This feature is **optional** and **disabled by default**. If `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT` is unset, nothing is changed.
|
|
102
|
+
|
|
103
|
+
Enable it by setting a replacement text:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
export CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT=$'# Output efficiency\n\n...'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The rewrite is intentionally narrow:
|
|
110
|
+
|
|
111
|
+
- Only Claude Code's `# Output efficiency` section is replaced
|
|
112
|
+
- Other system prompt sections are preserved
|
|
113
|
+
- Existing system block structure and fields such as `cache_control` are preserved
|
|
114
|
+
|
|
115
|
+
This may be useful for users who want to stay on current Claude Code versions but experiment with a different `Output efficiency` instruction set instead of downgrading to an earlier release.
|
|
116
|
+
|
|
117
|
+
### Prompt variants
|
|
118
|
+
|
|
119
|
+
<details>
|
|
120
|
+
<summary>Anthropic internal / <code>USER_TYPE=ant</code> version</summary>
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
# Output efficiency
|
|
124
|
+
|
|
125
|
+
When sending user-facing text, you're writing for a person, not logging to a console. Assume users can't see most tool calls or thinking - only your text output. Before your first tool call, briefly state what you're about to do. While working, give short updates at key moments: when you find something load-bearing (a bug, a root cause), when changing direction, when you've made progress without an update.
|
|
126
|
+
|
|
127
|
+
When you give updates, assume the recipient may have stepped away and lost the thread. They do not know your internal shorthand, codenames, or half-formed plan. Write in complete, grammatical sentences that can be understood cold. Spell out technical terms when helpful. If unsure, err on the side of a bit more explanation. Adapt to the user's expertise: experts can handle denser updates, but don't make novice users reconstruct context on their own.
|
|
128
|
+
|
|
129
|
+
User-facing text should read like natural prose. Avoid clipped sentence fragments, excessive dashes, symbolic shorthand, or formatting that reads like console output. Use tables only when they genuinely improve scanability, such as compact facts (files, lines, pass/fail) or quantitative comparisons. Keep explanatory reasoning in prose around the table, not inside it. Avoid semantic backtracking: structure sentences so the user can follow them linearly without having to reinterpret earlier clauses after reading later ones.
|
|
130
|
+
|
|
131
|
+
Optimize for fast human comprehension, not minimal surface area. If the user has to reread your summary or ask a follow-up just to understand what happened, you saved the wrong tokens. Match the level of structure to the task: for a simple question, answer in plain prose without unnecessary headings or numbered lists. While staying clear and direct, also be concise and avoid fluff. Skip filler, obvious restatements, and throat-clearing. Get to the point. Don't over-focus on low-signal details from your process. When it helps, use an inverted pyramid structure with the conclusion first and details later.
|
|
132
|
+
|
|
133
|
+
These user-facing text instructions do not apply to code or tool calls.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
</details>
|
|
137
|
+
|
|
138
|
+
<details>
|
|
139
|
+
<summary>Public / default Claude Code version</summary>
|
|
140
|
+
|
|
141
|
+
```text
|
|
142
|
+
# Output efficiency
|
|
143
|
+
|
|
144
|
+
IMPORTANT: Go straight to the point. Try the simplest approach first without going in circles. Do not overdo it. Be extra concise.
|
|
145
|
+
|
|
146
|
+
Your text output is brief, direct, and to the point. Lead with the answer or action, not the reasoning. Omit filler, preamble, and unnecessary transitions. Do not restate the user's request; move directly to the work. When explanation is needed, include only what helps the user understand the outcome.
|
|
147
|
+
|
|
148
|
+
Prioritize user-facing text for:
|
|
149
|
+
- decisions that require user input
|
|
150
|
+
- high-signal progress updates at natural milestones
|
|
151
|
+
- errors or blockers that change the plan
|
|
152
|
+
|
|
153
|
+
If a sentence can do the job, do not turn it into three. Favor short, direct constructions over long explanatory prose. These instructions do not apply to code or tool calls.
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
</details>
|
|
157
|
+
|
|
158
|
+
<details>
|
|
159
|
+
<summary>Example custom replacement(A middle-ground version combining the two versions above)</summary>
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
# Output efficiency
|
|
163
|
+
|
|
164
|
+
When sending user-facing text, write for a person, not a log file. Assume the user cannot see most tool calls or hidden reasoning - only your text output.
|
|
165
|
+
|
|
166
|
+
Keep user-facing text clear, direct, and reasonably concise. Lead with the answer or action. Skip filler, repetition, and unnecessary preamble.
|
|
167
|
+
|
|
168
|
+
Explain enough for the user to understand the reasoning, tradeoffs, or root cause when that would help them learn or make a decision, but do not turn simple answers into long writeups.
|
|
169
|
+
|
|
170
|
+
These instructions apply to user-facing text only. They do not apply to investigation, code reading, tool use, or verification.
|
|
171
|
+
|
|
172
|
+
Before making changes, read the relevant code and understand the surrounding context. Check types, signatures, call sites, and error causes before editing. Do not confuse brevity with rushing, and do not replace understanding with trial and error.
|
|
173
|
+
|
|
174
|
+
While working, give short updates at meaningful moments: when you find the root cause, when the plan changes, when you hit a blocker, or when a meaningful milestone is complete. Do not narrate every step.
|
|
175
|
+
|
|
176
|
+
When reporting results, be accurate and concrete. If you did not verify something, say so plainly. If a check failed, say that plainly too.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
</details>
|
|
180
|
+
|
|
97
181
|
## Monitoring
|
|
98
182
|
|
|
99
183
|
The interceptor includes monitoring for several additional issues identified by the community:
|
|
@@ -146,6 +230,7 @@ Logs are written to `~/.claude/cache-fix-debug.log`. Look for:
|
|
|
146
230
|
- `APPLIED: tool order stabilization` — tools were reordered
|
|
147
231
|
- `APPLIED: fingerprint stabilized from XXX to YYY` — fingerprint was corrected
|
|
148
232
|
- `APPLIED: stripped N images from old tool results` — images were stripped
|
|
233
|
+
- `APPLIED: output efficiency section rewritten` — output-efficiency section was replaced
|
|
149
234
|
- `MICROCOMPACT: N/M tool results cleared` — microcompact degradation detected
|
|
150
235
|
- `BUDGET WARNING: tool result chars at N / 200,000 threshold` — approaching budget cap
|
|
151
236
|
- `FALSE RATE LIMIT: synthetic model detected` — client-side false rate limit
|
|
@@ -154,6 +239,7 @@ Logs are written to `~/.claude/cache-fix-debug.log`. Look for:
|
|
|
154
239
|
- `CACHE TTL: tier=1h create=N read=N hit=N% (1h=N 5m=N)` — TTL tier and cache hit rate per call
|
|
155
240
|
- `PEAK HOUR: weekday 13:00-19:00 UTC` — Anthropic peak hour throttling active
|
|
156
241
|
- `SKIPPED: resume relocation (not a resume or already correct)` — no fix needed
|
|
242
|
+
- `SKIPPED: output efficiency rewrite (section not found)` — no matching output-efficiency section found
|
|
157
243
|
|
|
158
244
|
### Prefix diff mode
|
|
159
245
|
|
|
@@ -172,6 +258,7 @@ Snapshots are saved to `~/.claude/cache-fix-snapshots/` and diff reports are gen
|
|
|
172
258
|
| `CACHE_FIX_DEBUG` | `0` | Enable debug logging to `~/.claude/cache-fix-debug.log` |
|
|
173
259
|
| `CACHE_FIX_PREFIXDIFF` | `0` | Enable prefix snapshot diffing |
|
|
174
260
|
| `CACHE_FIX_IMAGE_KEEP_LAST` | `0` | Keep images in last N user messages (0 = disabled) |
|
|
261
|
+
| `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT` | unset | Replace Claude Code's `# Output efficiency` system-prompt section before the request is sent |
|
|
175
262
|
| `CACHE_FIX_USAGE_LOG` | `~/.claude/usage.jsonl` | Path for per-call usage telemetry log |
|
|
176
263
|
|
|
177
264
|
## Limitations
|
|
@@ -179,6 +266,7 @@ Snapshots are saved to `~/.claude/cache-fix-snapshots/` and diff reports are gen
|
|
|
179
266
|
- **npm installation only** — The standalone Claude Code binary has Zig-level attestation that bypasses Node.js. This fix only works with the npm package (`npm install -g @anthropic-ai/claude-code`).
|
|
180
267
|
- **Overage TTL downgrade** — Exceeding 100% of the 5-hour quota triggers a server-enforced TTL downgrade from 1h to 5m. This is a server-side decision and cannot be fixed client-side. The interceptor prevents the cache instability that can push you into overage in the first place.
|
|
181
268
|
- **Microcompact is not preventable** — The monitoring features detect context degradation but cannot prevent it. The microcompact and budget enforcement mechanisms are server-controlled via GrowthBook flags with no client-side disable option.
|
|
269
|
+
- **System prompt rewrite is experimental** — This hook only rewrites one system-prompt section and is opt-in, but there are still unknowns: it is not proven that this prompt text is responsible for the behavior differences discussed in community reports, and it is not known whether future server-side validation could react to modified system prompts. Use at your own risk.
|
|
182
270
|
- **Version coupling** — The fingerprint salt and block detection heuristics are derived from Claude Code internals. A major refactor could require an update to this package.
|
|
183
271
|
|
|
184
272
|
## Tracked issues
|
|
@@ -189,6 +277,7 @@ Snapshots are saved to `~/.claude/cache-fix-snapshots/` and diff reports are gen
|
|
|
189
277
|
- [#43044](https://github.com/anthropics/claude-code/issues/43044) — Resume loads 0% context on v2.1.91
|
|
190
278
|
- [#43657](https://github.com/anthropics/claude-code/issues/43657) — Resume cache invalidation confirmed on v2.1.92
|
|
191
279
|
- [#44045](https://github.com/anthropics/claude-code/issues/44045) — SDK-level reproduction with token measurements
|
|
280
|
+
- [#32508](https://github.com/anthropics/claude-code/issues/32508) — Community discussion around the `Output efficiency` system-prompt change and its possible effect on model behavior
|
|
192
281
|
|
|
193
282
|
## Related research
|
|
194
283
|
|
|
@@ -197,7 +286,7 @@ Snapshots are saved to `~/.claude/cache-fix-snapshots/` and diff reports are gen
|
|
|
197
286
|
|
|
198
287
|
## Contributors
|
|
199
288
|
|
|
200
|
-
- **[@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,
|
|
289
|
+
- **[@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
|
|
201
290
|
- **[@jmarianski](https://github.com/jmarianski)** — Root cause analysis via MITM proxy capture and Ghidra reverse engineering, multi-mode cache test script
|
|
202
291
|
- **[@cnighswonger](https://github.com/cnighswonger)** — Fingerprint stabilization, tool ordering fix, image stripping, monitoring features, overage TTL downgrade discovery, package maintainer
|
|
203
292
|
- **[@ArkNill](https://github.com/ArkNill)** — Microcompact mechanism analysis, GrowthBook flag documentation, false rate limiter identification
|
package/README.zh.md
CHANGED
|
@@ -52,6 +52,7 @@ chmod +x ~/bin/claude-fixed
|
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
如果你的 npm 全局前缀不同,请相应调整 `CLAUDE_NPM_CLI`。使用以下命令查找:
|
|
55
|
+
|
|
55
56
|
```bash
|
|
56
57
|
npm root -g
|
|
57
58
|
```
|
|
@@ -94,6 +95,90 @@ export CACHE_FIX_IMAGE_KEEP_LAST=3
|
|
|
94
95
|
|
|
95
96
|
设为 `0`(默认)以禁用。
|
|
96
97
|
|
|
98
|
+
## 系统提示词重写(可选)
|
|
99
|
+
|
|
100
|
+
拦截器还可以在请求发出前,重写 Claude Code 的 `# Output efficiency` 系统提示词段落。
|
|
101
|
+
|
|
102
|
+
此功能是**可选的**,并且**默认关闭**。如果未设置 `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT`,则不会做任何修改。
|
|
103
|
+
|
|
104
|
+
通过设置替换文本启用:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
export CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT=$'# Output efficiency\n\n...'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
该重写被刻意限制在很小的范围内:
|
|
111
|
+
|
|
112
|
+
- 只替换 Claude Code 的 `# Output efficiency` 这一节
|
|
113
|
+
- 其他系统提示词段落会被保留
|
|
114
|
+
- 现有的 system block 结构以及 `cache_control` 等字段会被保留
|
|
115
|
+
|
|
116
|
+
这对那些希望继续使用较新版本的 Claude Code、但又想尝试不同 `Output efficiency` 指令集而不是降级到旧版本的用户,可能会有帮助。
|
|
117
|
+
|
|
118
|
+
### 提示词版本
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary>Anthropic 内部 / <code>USER_TYPE=ant</code> 版本</summary>
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
# Output efficiency
|
|
125
|
+
|
|
126
|
+
When sending user-facing text, you're writing for a person, not logging to a console. Assume users can't see most tool calls or thinking - only your text output. Before your first tool call, briefly state what you're about to do. While working, give short updates at key moments: when you find something load-bearing (a bug, a root cause), when changing direction, when you've made progress without an update.
|
|
127
|
+
|
|
128
|
+
When you give updates, assume the recipient may have stepped away and lost the thread. They do not know your internal shorthand, codenames, or half-formed plan. Write in complete, grammatical sentences that can be understood cold. Spell out technical terms when helpful. If unsure, err on the side of a bit more explanation. Adapt to the user's expertise: experts can handle denser updates, but don't make novice users reconstruct context on their own.
|
|
129
|
+
|
|
130
|
+
User-facing text should read like natural prose. Avoid clipped sentence fragments, excessive dashes, symbolic shorthand, or formatting that reads like console output. Use tables only when they genuinely improve scanability, such as compact facts (files, lines, pass/fail) or quantitative comparisons. Keep explanatory reasoning in prose around the table, not inside it. Avoid semantic backtracking: structure sentences so the user can follow them linearly without having to reinterpret earlier clauses after reading later ones.
|
|
131
|
+
|
|
132
|
+
Optimize for fast human comprehension, not minimal surface area. If the user has to reread your summary or ask a follow-up just to understand what happened, you saved the wrong tokens. Match the level of structure to the task: for a simple question, answer in plain prose without unnecessary headings or numbered lists. While staying clear and direct, also be concise and avoid fluff. Skip filler, obvious restatements, and throat-clearing. Get to the point. Don't over-focus on low-signal details from your process. When it helps, use an inverted pyramid structure with the conclusion first and details later.
|
|
133
|
+
|
|
134
|
+
These user-facing text instructions do not apply to code or tool calls.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
</details>
|
|
138
|
+
|
|
139
|
+
<details>
|
|
140
|
+
<summary>公开 / 默认 Claude Code 版本</summary>
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
# Output efficiency
|
|
144
|
+
|
|
145
|
+
IMPORTANT: Go straight to the point. Try the simplest approach first without going in circles. Do not overdo it. Be extra concise.
|
|
146
|
+
|
|
147
|
+
Your text output is brief, direct, and to the point. Lead with the answer or action, not the reasoning. Omit filler, preamble, and unnecessary transitions. Do not restate the user's request; move directly to the work. When explanation is needed, include only what helps the user understand the outcome.
|
|
148
|
+
|
|
149
|
+
Prioritize user-facing text for:
|
|
150
|
+
- decisions that require user input
|
|
151
|
+
- high-signal progress updates at natural milestones
|
|
152
|
+
- errors or blockers that change the plan
|
|
153
|
+
|
|
154
|
+
If a sentence can do the job, do not turn it into three. Favor short, direct constructions over long explanatory prose. These instructions do not apply to code or tool calls.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
</details>
|
|
158
|
+
|
|
159
|
+
<details>
|
|
160
|
+
<summary>自定义替换示例(结合上面两版的折中版本)</summary>
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
# Output efficiency
|
|
164
|
+
|
|
165
|
+
When sending user-facing text, write for a person, not a log file. Assume the user cannot see most tool calls or hidden reasoning - only your text output.
|
|
166
|
+
|
|
167
|
+
Keep user-facing text clear, direct, and reasonably concise. Lead with the answer or action. Skip filler, repetition, and unnecessary preamble.
|
|
168
|
+
|
|
169
|
+
Explain enough for the user to understand the reasoning, tradeoffs, or root cause when that would help them learn or make a decision, but do not turn simple answers into long writeups.
|
|
170
|
+
|
|
171
|
+
These instructions apply to user-facing text only. They do not apply to investigation, code reading, tool use, or verification.
|
|
172
|
+
|
|
173
|
+
Before making changes, read the relevant code and understand the surrounding context. Check types, signatures, call sites, and error causes before editing. Do not confuse brevity with rushing, and do not replace understanding with trial and error.
|
|
174
|
+
|
|
175
|
+
While working, give short updates at meaningful moments: when you find the root cause, when the plan changes, when you hit a blocker, or when a meaningful milestone is complete. Do not narrate every step.
|
|
176
|
+
|
|
177
|
+
When reporting results, be accurate and concrete. If you did not verify something, say so plainly. If a check failed, say that plainly too.
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
</details>
|
|
181
|
+
|
|
97
182
|
## 监控功能
|
|
98
183
|
|
|
99
184
|
拦截器包含社区发现的多项额外问题的监控:
|
|
@@ -137,7 +222,32 @@ node tools/cost-report.mjs --admin-key <key> # 与 Admin API 交叉验证
|
|
|
137
222
|
CACHE_FIX_DEBUG=1 claude-fixed
|
|
138
223
|
```
|
|
139
224
|
|
|
140
|
-
日志写入 `~/.claude/cache-fix-debug.log
|
|
225
|
+
日志写入 `~/.claude/cache-fix-debug.log`。重点关注:
|
|
226
|
+
|
|
227
|
+
- `APPLIED: resume message relocation` — 块散布已检测并修复
|
|
228
|
+
- `APPLIED: tool order stabilization` — 工具已重新排序
|
|
229
|
+
- `APPLIED: fingerprint stabilized from XXX to YYY` — 指纹已被纠正
|
|
230
|
+
- `APPLIED: stripped N images from old tool results` — 已从旧工具结果中剥离图片
|
|
231
|
+
- `APPLIED: output efficiency section rewritten` — `output efficiency` 段已被替换
|
|
232
|
+
- `MICROCOMPACT: N/M tool results cleared` — 检测到微压缩降级
|
|
233
|
+
- `BUDGET WARNING: tool result chars at N / 200,000 threshold` — 接近预算上限
|
|
234
|
+
- `FALSE RATE LIMIT: synthetic model detected` — 检测到客户端侧虚假速率限制
|
|
235
|
+
- `GROWTHBOOK FLAGS: {...}` — 首次调用时记录的服务器控制标志
|
|
236
|
+
- `PROMPT SIZE: system=N tools=N injected=N (skills=N mcp=N ...)` — 每次调用的提示体积明细
|
|
237
|
+
- `CACHE TTL: tier=1h create=N read=N hit=N% (1h=N 5m=N)` — TTL 档位和每次调用的缓存命中率
|
|
238
|
+
- `PEAK HOUR: weekday 13:00-19:00 UTC` — Anthropic 高峰时段限流生效
|
|
239
|
+
- `SKIPPED: resume relocation (not a resume or already correct)` — 无需修复
|
|
240
|
+
- `SKIPPED: output efficiency rewrite (section not found)` — 未找到匹配的 `output efficiency` 段
|
|
241
|
+
|
|
242
|
+
### Prefix diff mode
|
|
243
|
+
|
|
244
|
+
启用跨进程前缀快照差异对比,以诊断重启后的缓存失效:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
CACHE_FIX_PREFIXDIFF=1 claude-fixed
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
快照会保存到 `~/.claude/cache-fix-snapshots/`,并在重启后的第一次 API 调用时生成差异报告。
|
|
141
251
|
|
|
142
252
|
## 环境变量
|
|
143
253
|
|
|
@@ -146,6 +256,7 @@ CACHE_FIX_DEBUG=1 claude-fixed
|
|
|
146
256
|
| `CACHE_FIX_DEBUG` | `0` | 启用调试日志 |
|
|
147
257
|
| `CACHE_FIX_PREFIXDIFF` | `0` | 启用前缀快照差异对比 |
|
|
148
258
|
| `CACHE_FIX_IMAGE_KEEP_LAST` | `0` | 保留最近 N 条用户消息中的图片(0 = 禁用) |
|
|
259
|
+
| `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT` | unset | 在请求发出前替换 Claude Code 的 `# Output efficiency` 系统提示词段落 |
|
|
149
260
|
| `CACHE_FIX_USAGE_LOG` | `~/.claude/usage.jsonl` | 每次调用使用量遥测日志路径 |
|
|
150
261
|
|
|
151
262
|
## 限制
|
|
@@ -153,14 +264,28 @@ CACHE_FIX_DEBUG=1 claude-fixed
|
|
|
153
264
|
- **仅支持 npm 安装** — 独立 Claude Code 二进制文件具有 Zig 级别的证明机制,会绕过 Node.js。本修复仅适用于 npm 包(`npm install -g @anthropic-ai/claude-code`)。
|
|
154
265
|
- **超额 TTL 降级** — 超过 5 小时配额的 100% 会触发服务器端 TTL 从 1h 降级至 5m。这是服务器端决策,无法在客户端修复。拦截器通过防止缓存不稳定来避免你首先进入超额状态。
|
|
155
266
|
- **微压缩不可阻止** — 监控功能可以检测上下文降级,但无法阻止。微压缩和预算执行机制是通过 GrowthBook 标志进行服务器控制的,没有客户端禁用选项。
|
|
267
|
+
- **系统提示词重写是实验性的** — 此 hook 只会重写一个系统提示词段落,并且默认关闭,但仍存在未知因素:目前并未证明这段提示词文本本身就是社区报告中行为差异的根因,也无法确认未来服务端校验是否会对修改后的系统提示词作出反应。使用风险由用户自行承担。
|
|
268
|
+
- **版本耦合** — 指纹 salt 和块检测启发式规则都来自 Claude Code 内部实现。重大重构可能需要更新此包。
|
|
156
269
|
|
|
157
270
|
## 相关问题
|
|
158
271
|
|
|
159
272
|
- [#34629](https://github.com/anthropics/claude-code/issues/34629) — 恢复缓存回归的原始报告
|
|
160
273
|
- [#40524](https://github.com/anthropics/claude-code/issues/40524) — 会话内指纹失效,图片持久化
|
|
161
274
|
- [#42052](https://github.com/anthropics/claude-code/issues/42052) — 社区拦截器开发,TTL 降级发现
|
|
162
|
-
- [#
|
|
163
|
-
- [#
|
|
275
|
+
- [#43044](https://github.com/anthropics/claude-code/issues/43044) — 恢复会话后在 v2.1.91 上仅加载 0% 上下文
|
|
276
|
+
- [#43657](https://github.com/anthropics/claude-code/issues/43657) — 在 v2.1.92 上确认恢复会话导致缓存失效
|
|
277
|
+
- [#44045](https://github.com/anthropics/claude-code/issues/44045) — SDK 层面的复现与 token 测量
|
|
278
|
+
- [#32508](https://github.com/anthropics/claude-code/issues/32508) — 关于 `Output efficiency` 系统提示词变更及其可能影响模型行为的社区讨论
|
|
279
|
+
|
|
280
|
+
## 贡献者
|
|
281
|
+
|
|
282
|
+
- **[@VictorSun92](https://github.com/VictorSun92)** — 原始 v2.1.88 monkey-patch 修复作者,识别出 v2.1.90 中的部分块散布问题,并贡献了前向扫描检测、正确的块排序、更严格的块匹配器,以及可选的 output-efficiency 重写 hook
|
|
283
|
+
- **[@jmarianski](https://github.com/jmarianski)** — 通过 MITM 代理抓包和 Ghidra 逆向分析定位根因,并提供多模式缓存测试脚本
|
|
284
|
+
- **[@cnighswonger](https://github.com/cnighswonger)** — 指纹稳定化、工具顺序修复、图片剥离、监控功能、超额 TTL 降级发现,本包维护者
|
|
285
|
+
- **[@ArkNill](https://github.com/ArkNill)** — 微压缩机制分析、GrowthBook 标志文档整理、虚假速率限制识别
|
|
286
|
+
- **[@Renvect](https://github.com/Renvect)** — 图片重复发送问题发现、跨项目目录污染分析
|
|
287
|
+
|
|
288
|
+
如果你参与了这些问题的社区协作但尚未被列出,欢迎开 issue 或 PR,我们希望正确致谢每一位贡献者。
|
|
164
289
|
|
|
165
290
|
## 许可证
|
|
166
291
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-cache-fix",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "Fixes prompt cache regression in Claude Code that causes up to 20x cost increase on resumed sessions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./preload.mjs",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=18"
|
|
14
14
|
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node --test 'test/**/*.test.mjs'"
|
|
17
|
+
},
|
|
15
18
|
"keywords": [
|
|
16
19
|
"claude-code",
|
|
17
20
|
"claude",
|
package/preload.mjs
CHANGED
|
@@ -276,7 +276,13 @@ function stripSessionKnowledge(text) {
|
|
|
276
276
|
* prepends them to messages[0]. Idempotent across calls.
|
|
277
277
|
*/
|
|
278
278
|
function normalizeResumeMessages(messages) {
|
|
279
|
-
if (!Array.isArray(messages)
|
|
279
|
+
if (!Array.isArray(messages)) return messages;
|
|
280
|
+
// NOTE: We used to return early here for messages.length < 2 (fresh sessions)
|
|
281
|
+
// because there's nothing to relocate. But this left the first call's blocks
|
|
282
|
+
// in CC's raw, non-deterministic order. On call 2+, sorting/pinning would run
|
|
283
|
+
// and produce DIFFERENT bytes — busting cache on the first resume turn.
|
|
284
|
+
// Fix: always run sort+pin, even on single-message calls, so the first call
|
|
285
|
+
// establishes a deterministic baseline. (@bilby91 #44045)
|
|
280
286
|
|
|
281
287
|
let firstUserIdx = -1;
|
|
282
288
|
for (let i = 0; i < messages.length; i++) {
|
|
@@ -503,6 +509,76 @@ function stabilizeToolOrder(tools) {
|
|
|
503
509
|
});
|
|
504
510
|
}
|
|
505
511
|
|
|
512
|
+
// --------------------------------------------------------------------------
|
|
513
|
+
// System prompt rewrite (optional)
|
|
514
|
+
// --------------------------------------------------------------------------
|
|
515
|
+
|
|
516
|
+
const OUTPUT_EFFICIENCY_SECTION_HEADER = "# Output efficiency";
|
|
517
|
+
const OUTPUT_EFFICIENCY_REPLACEMENT_RAW =
|
|
518
|
+
process.env.CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT || "";
|
|
519
|
+
const OUTPUT_EFFICIENCY_SECTION_REPLACEMENT =
|
|
520
|
+
normalizeOutputEfficiencyReplacement(OUTPUT_EFFICIENCY_REPLACEMENT_RAW);
|
|
521
|
+
|
|
522
|
+
function normalizeOutputEfficiencyReplacement(text) {
|
|
523
|
+
const trimmed = typeof text === "string" ? text.trim() : "";
|
|
524
|
+
if (!trimmed) return "";
|
|
525
|
+
return trimmed.startsWith(OUTPUT_EFFICIENCY_SECTION_HEADER)
|
|
526
|
+
? trimmed
|
|
527
|
+
: `${OUTPUT_EFFICIENCY_SECTION_HEADER}\n\n${trimmed}`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Replace Claude Code's entire output-efficiency section in-place while
|
|
532
|
+
* preserving the existing system block structure and cache_control fields.
|
|
533
|
+
*/
|
|
534
|
+
function rewriteOutputEfficiencyInstruction(system) {
|
|
535
|
+
if (!Array.isArray(system) || !OUTPUT_EFFICIENCY_SECTION_REPLACEMENT) {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
let changed = false;
|
|
540
|
+
const rewritten = system.map((block) => {
|
|
541
|
+
if (
|
|
542
|
+
block?.type !== "text" ||
|
|
543
|
+
typeof block.text !== "string" ||
|
|
544
|
+
!block.text.includes(OUTPUT_EFFICIENCY_SECTION_HEADER)
|
|
545
|
+
) {
|
|
546
|
+
return block;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const nextText = replaceOutputEfficiencySection(block.text);
|
|
550
|
+
if (!nextText || nextText === block.text) {
|
|
551
|
+
return block;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
changed = true;
|
|
555
|
+
return { ...block, text: nextText };
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
return changed ? rewritten : null;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function replaceOutputEfficiencySection(text) {
|
|
562
|
+
const start = text.indexOf(OUTPUT_EFFICIENCY_SECTION_HEADER);
|
|
563
|
+
if (start === -1) return null;
|
|
564
|
+
|
|
565
|
+
const afterHeader = start + OUTPUT_EFFICIENCY_SECTION_HEADER.length;
|
|
566
|
+
const remainder = text.slice(afterHeader);
|
|
567
|
+
const nextHeadingMatch = remainder.match(/\n# [^\n]+/);
|
|
568
|
+
|
|
569
|
+
if (!nextHeadingMatch || nextHeadingMatch.index == null) {
|
|
570
|
+
return text.slice(0, start) + OUTPUT_EFFICIENCY_SECTION_REPLACEMENT;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const nextHeadingStart = afterHeader + nextHeadingMatch.index + 1;
|
|
574
|
+
return (
|
|
575
|
+
text.slice(0, start) +
|
|
576
|
+
OUTPUT_EFFICIENCY_SECTION_REPLACEMENT +
|
|
577
|
+
"\n\n" +
|
|
578
|
+
text.slice(nextHeadingStart)
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
|
|
506
582
|
// --------------------------------------------------------------------------
|
|
507
583
|
// Fetch interceptor
|
|
508
584
|
// --------------------------------------------------------------------------
|
|
@@ -518,6 +594,7 @@ import { join } from "node:path";
|
|
|
518
594
|
|
|
519
595
|
const DEBUG = process.env.CACHE_FIX_DEBUG === "1";
|
|
520
596
|
const PREFIXDIFF = process.env.CACHE_FIX_PREFIXDIFF === "1";
|
|
597
|
+
const NORMALIZE_IDENTITY = process.env.CACHE_FIX_NORMALIZE_IDENTITY === "1";
|
|
521
598
|
const LOG_PATH = join(homedir(), ".claude", "cache-fix-debug.log");
|
|
522
599
|
const SNAPSHOT_DIR = join(homedir(), ".claude", "cache-fix-snapshots");
|
|
523
600
|
const USAGE_JSONL = process.env.CACHE_FIX_USAGE_LOG || join(homedir(), ".claude", "usage.jsonl");
|
|
@@ -571,6 +648,7 @@ function dumpGrowthBookFlags() {
|
|
|
571
648
|
cold_compact: features.tengu_cold_compact,
|
|
572
649
|
system_prompt_global_cache: features.tengu_system_prompt_global_cache,
|
|
573
650
|
compact_cache_prefix: features.tengu_compact_cache_prefix,
|
|
651
|
+
onyx_plover: features.tengu_onyx_plover,
|
|
574
652
|
};
|
|
575
653
|
debugLog("GROWTHBOOK FLAGS:", JSON.stringify(interesting, null, 2));
|
|
576
654
|
} catch (e) {
|
|
@@ -843,6 +921,49 @@ globalThis.fetch = async function (url, options) {
|
|
|
843
921
|
}
|
|
844
922
|
}
|
|
845
923
|
|
|
924
|
+
// Bug 6: Identity string normalization for Agent()/SendMessage() cache parity
|
|
925
|
+
// The CC orchestrator emits a different identity string in system[1] depending
|
|
926
|
+
// on whether the call originated from Agent() vs SendMessage() (subagent resume):
|
|
927
|
+
// Agent(): "You are Claude Code, Anthropic's official CLI for Claude."
|
|
928
|
+
// SendMessage(): "You are a Claude agent, built on Anthropic's Claude Agent SDK."
|
|
929
|
+
// Both blocks carry cache_control: ephemeral. The ~50-char identity swap is enough
|
|
930
|
+
// to invalidate the entire cache prefix, producing cache_read=0 on first SendMessage
|
|
931
|
+
// turn even though system[2] (the actual instructions) is byte-identical.
|
|
932
|
+
// Confirmed by @labzink via mitmproxy on #44724.
|
|
933
|
+
// Opt-in because it's a model-perceivable behavior change (subagent thinks it's CC).
|
|
934
|
+
if (NORMALIZE_IDENTITY && payload.system && Array.isArray(payload.system)) {
|
|
935
|
+
const CANONICAL = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
936
|
+
const AGENT_SDK = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
|
|
937
|
+
let normalized = 0;
|
|
938
|
+
payload.system = payload.system.map((block) => {
|
|
939
|
+
if (
|
|
940
|
+
block?.type === "text" &&
|
|
941
|
+
typeof block.text === "string" &&
|
|
942
|
+
block.text.startsWith(AGENT_SDK)
|
|
943
|
+
) {
|
|
944
|
+
normalized++;
|
|
945
|
+
return { ...block, text: CANONICAL + block.text.slice(AGENT_SDK.length) };
|
|
946
|
+
}
|
|
947
|
+
return block;
|
|
948
|
+
});
|
|
949
|
+
if (normalized > 0) {
|
|
950
|
+
modified = true;
|
|
951
|
+
debugLog(`APPLIED: identity normalized on ${normalized} system block(s) (Agent SDK → Claude Code)`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// Optional: rewrite Claude Code's default output-efficiency section
|
|
956
|
+
if (payload.system && OUTPUT_EFFICIENCY_SECTION_REPLACEMENT) {
|
|
957
|
+
const rewritten = rewriteOutputEfficiencyInstruction(payload.system);
|
|
958
|
+
if (rewritten) {
|
|
959
|
+
payload.system = rewritten;
|
|
960
|
+
modified = true;
|
|
961
|
+
debugLog("APPLIED: output efficiency section rewritten");
|
|
962
|
+
} else {
|
|
963
|
+
debugLog("SKIPPED: output efficiency rewrite (section not found)");
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
846
967
|
// Bug 5: 1h TTL enforcement
|
|
847
968
|
// The client gates 1h cache TTL behind a GrowthBook allowlist that checks
|
|
848
969
|
// querySource against patterns like "repl_main_thread*", "sdk", "auto_mode".
|
|
@@ -1120,3 +1241,31 @@ async function drainTTLFromClone(clone, model, quotaHeaders) {
|
|
|
1120
1241
|
}
|
|
1121
1242
|
}
|
|
1122
1243
|
}
|
|
1244
|
+
|
|
1245
|
+
// --------------------------------------------------------------------------
|
|
1246
|
+
// Test exports
|
|
1247
|
+
// --------------------------------------------------------------------------
|
|
1248
|
+
//
|
|
1249
|
+
// These exports exist for unit testing the pure functions in this file. They
|
|
1250
|
+
// have no effect on the interceptor's runtime behavior — production callers
|
|
1251
|
+
// load this module via NODE_OPTIONS=--import and never use named imports.
|
|
1252
|
+
// Tests import from this file directly: `import { sortSkillsBlock } from
|
|
1253
|
+
// '../preload.mjs'`. The fetch patching above runs at import time but is
|
|
1254
|
+
// harmless in a test process since tests do not make fetch calls.
|
|
1255
|
+
|
|
1256
|
+
export {
|
|
1257
|
+
sortSkillsBlock,
|
|
1258
|
+
sortDeferredToolsBlock,
|
|
1259
|
+
pinBlockContent,
|
|
1260
|
+
stripSessionKnowledge,
|
|
1261
|
+
stabilizeFingerprint,
|
|
1262
|
+
computeFingerprint,
|
|
1263
|
+
isSkillsBlock,
|
|
1264
|
+
isDeferredToolsBlock,
|
|
1265
|
+
isHooksBlock,
|
|
1266
|
+
isMcpBlock,
|
|
1267
|
+
isRelocatableBlock,
|
|
1268
|
+
rewriteOutputEfficiencyInstruction,
|
|
1269
|
+
normalizeOutputEfficiencyReplacement,
|
|
1270
|
+
_pinnedBlocks, // exported so tests can reset between runs
|
|
1271
|
+
};
|