claude-code-cache-fix 3.0.2 → 3.0.4
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.ko.md +186 -174
- package/README.md +38 -1
- package/README.zh.md +153 -195
- package/package.json +4 -1
- package/proxy/config.mjs +15 -0
- package/proxy/extensions/cache-telemetry.mjs +89 -1
- package/proxy/upstream.mjs +102 -0
package/README.zh.md
CHANGED
|
@@ -1,297 +1,255 @@
|
|
|
1
1
|
# claude-code-cache-fix
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
[](https://www.npmjs.com/package/claude-code-cache-fix) [](https://nodejs.org/) [](https://opensource.org/licenses/MIT) [](https://github.com/cnighswonger/claude-code-cache-fix/stargazers)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[English](./README.md) | 中文 | [한국어](./README.ko.md) | [Português](./docs/guia-pt-br.md)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
当你在 Claude Code 中使用 `--resume` 或 `/resume` 时,提示缓存会静默失效。API 不再读取已缓存的 token(廉价),而是每一轮都从头重建(昂贵)。原本每小时约 $0.50 的会话可能在无任何提示的情况下飙升至 $5-10/小时。
|
|
10
|
-
|
|
11
|
-
三个 bug 导致了这个问题:
|
|
12
|
-
|
|
13
|
-
1. **附件块散布** — 技能列表、MCP 服务器、延迟工具、钩子等附件块应当位于 `messages[0]` 中。恢复会话时,它们会漂移到后续消息中,改变缓存前缀。
|
|
7
|
+
[Claude Code](https://github.com/anthropics/claude-code) 的缓存优化代理。修复导致配额过度消耗的提示缓存 bug,稳定请求前缀,并监控静默回归。支持所有 CC 版本,包括 v2.1.113+ Bun 二进制文件。
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
3. **工具定义排序不确定** — 工具定义在不同轮次间可能以不同顺序到达,改变请求字节并使缓存键失效。
|
|
9
|
+
> **v3.0.3** — 具有 7 个热重载扩展的本地 HTTP 代理。在 v2.1.117 上 A/B 测试:首次热启动轮次 **代理经由 95.5% 缓存命中率 vs 直连 82.3%**。[完整发布说明 →](https://github.com/cnighswonger/claude-code-cache-fix/releases/tag/v3.0.0)
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
> **Opus 4.7 注意事项:** 计量数据显示 4.7 在相同可见 token 数量下 **Q5h 配额消耗速率约为 4.6 的 2.4 倍**([@ArkNill 独立确认](https://github.com/ArkNill/claude-code-hidden-problem-analysis/blob/main/16_OPUS-47-ADVISORY.md))。两个因素:新的分词器(最多增加 35% token,[已记录](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7))和自适应思考开销(约 105%,未在使用量响应中记录)。Q5h 影响会复合累积到 **Q7d** — 大多数重度用户最先触及的周配额上限。解决方法:`CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` 可将消耗降低约 3.3 倍,但可能降低复杂任务的质量。参见 [Discussion #25](https://github.com/cnighswonger/claude-code-cache-fix/discussions/25)(初始观察)和 [Discussion #42](https://github.com/cnighswonger/claude-code-cache-fix/discussions/42)(对照 A/B 数据 + Q7d 分析)。
|
|
20
12
|
|
|
21
|
-
##
|
|
13
|
+
## 快速开始:代理(推荐)
|
|
22
14
|
|
|
23
|
-
|
|
15
|
+
代理适用于任何 CC 版本 — Node.js 或 Bun 二进制文件。它位于 Claude Code 和 Anthropic API 之间,通过热重载扩展应用缓存修复。
|
|
24
16
|
|
|
25
17
|
```bash
|
|
18
|
+
# 安装
|
|
26
19
|
npm install -g claude-code-cache-fix
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## 使用方法
|
|
30
|
-
|
|
31
|
-
本修复以 Node.js 预加载模块的形式工作,在 API 请求离开本机之前进行拦截。
|
|
32
|
-
|
|
33
|
-
### 方式 A:包装脚本(推荐)
|
|
34
|
-
|
|
35
|
-
创建包装脚本(如 `~/bin/claude-fixed`):
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
#!/bin/bash
|
|
39
|
-
CLAUDE_NPM_CLI="$HOME/.npm-global/lib/node_modules/@anthropic-ai/claude-code/cli.js"
|
|
40
20
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
echo "Install with: npm install -g @anthropic-ai/claude-code" >&2
|
|
44
|
-
exit 1
|
|
45
|
-
fi
|
|
21
|
+
# 启动代理(在 localhost:9801 上运行)
|
|
22
|
+
node "$(npm root -g)/claude-code-cache-fix/proxy/server.mjs" &
|
|
46
23
|
|
|
47
|
-
|
|
24
|
+
# 通过代理启动 Claude Code
|
|
25
|
+
ANTHROPIC_BASE_URL=http://127.0.0.1:9801 claude
|
|
48
26
|
```
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
chmod +x ~/bin/claude-fixed
|
|
52
|
-
```
|
|
28
|
+
就这样。代理会自动应用所有 7 个缓存修复扩展。无需包装脚本、`NODE_OPTIONS` 或预加载。
|
|
53
29
|
|
|
54
|
-
|
|
30
|
+
### 代理的工作方式
|
|
55
31
|
|
|
56
|
-
|
|
57
|
-
npm root -g
|
|
58
|
-
```
|
|
32
|
+
每个 `/v1/messages` 请求都会按顺序执行 7 个扩展:
|
|
59
33
|
|
|
60
|
-
|
|
34
|
+
| 扩展 | 修复内容 |
|
|
35
|
+
|------|----------|
|
|
36
|
+
| `fingerprint-strip` | 移除系统提示中不稳定的 cc_version 指纹 |
|
|
37
|
+
| `sort-stabilization` | 确保工具和 MCP 定义的确定性排序 |
|
|
38
|
+
| `ttl-management` | 检测服务器 TTL 层级,注入正确的 cache_control 标记 |
|
|
39
|
+
| `identity-normalization` | 规范化消息身份字段以保持前缀稳定性 |
|
|
40
|
+
| `fresh-session-sort` | 修复首次轮次的非确定性排序 |
|
|
41
|
+
| `cache-control-normalize` | 规范化消息间的 cache_control 标记 |
|
|
42
|
+
| `cache-telemetry` | 从响应头提取缓存统计 → `~/.claude/quota-status.json` |
|
|
61
43
|
|
|
62
|
-
|
|
63
|
-
alias claude='NODE_OPTIONS="--import claude-code-cache-fix" node "$(npm root -g)/@anthropic-ai/claude-code/cli.js"'
|
|
64
|
-
```
|
|
44
|
+
扩展支持热重载 — 在 `proxy/extensions/` 中添加、删除或修改 `.mjs` 文件,更改将在下一次请求时生效,无需重启。配置在 `proxy/extensions.json` 中。
|
|
65
45
|
|
|
66
|
-
###
|
|
46
|
+
### 作为服务运行
|
|
67
47
|
|
|
68
|
-
|
|
69
|
-
NODE_OPTIONS="--import claude-code-cache-fix" claude
|
|
70
|
-
```
|
|
48
|
+
**Linux(systemd — 推荐):**
|
|
71
49
|
|
|
72
|
-
|
|
50
|
+
创建 `~/.config/systemd/user/cache-fix-proxy.service`:
|
|
73
51
|
|
|
74
|
-
|
|
52
|
+
```ini
|
|
53
|
+
[Unit]
|
|
54
|
+
Description=Claude Code Cache Fix Proxy (v3.x)
|
|
55
|
+
After=network.target
|
|
75
56
|
|
|
76
|
-
|
|
57
|
+
[Service]
|
|
58
|
+
Type=simple
|
|
59
|
+
ExecStart=/usr/local/bin/node /path/to/claude-code-cache-fix/proxy/server.mjs
|
|
60
|
+
Restart=on-failure
|
|
61
|
+
RestartSec=5
|
|
62
|
+
Environment=CACHE_FIX_PROXY_PORT=9801
|
|
77
63
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
64
|
+
[Install]
|
|
65
|
+
WantedBy=default.target
|
|
66
|
+
```
|
|
81
67
|
|
|
82
|
-
|
|
68
|
+
```bash
|
|
69
|
+
systemctl --user daemon-reload
|
|
70
|
+
systemctl --user enable --now cache-fix-proxy
|
|
83
71
|
|
|
84
|
-
|
|
72
|
+
# 可选:开机启动(无需登录)
|
|
73
|
+
sudo loginctl enable-linger $USER
|
|
74
|
+
```
|
|
85
75
|
|
|
86
|
-
|
|
76
|
+
v3.1.0 计划提供 `cache-fix-proxy install-service` 子命令([#48](https://github.com/cnighswonger/claude-code-cache-fix/issues/48))。
|
|
87
77
|
|
|
88
|
-
|
|
78
|
+
**备选方案(任何操作系统):**
|
|
89
79
|
|
|
90
80
|
```bash
|
|
91
|
-
|
|
81
|
+
nohup node "$(npm root -g)/claude-code-cache-fix/proxy/server.mjs" > /tmp/cache-fix-proxy.log 2>&1 &
|
|
82
|
+
echo 'export ANTHROPIC_BASE_URL=http://127.0.0.1:9801' >> ~/.bashrc
|
|
92
83
|
```
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
设为 `0`(默认)以禁用。
|
|
97
|
-
|
|
98
|
-
## 系统提示词重写(可选)
|
|
99
|
-
|
|
100
|
-
拦截器还可以在请求发出前,重写 Claude Code 的 `# Output efficiency` 系统提示词段落。
|
|
101
|
-
|
|
102
|
-
此功能是**可选的**,并且**默认关闭**。如果未设置 `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT`,则不会做任何修改。
|
|
103
|
-
|
|
104
|
-
通过设置替换文本启用:
|
|
85
|
+
### 健康检查
|
|
105
86
|
|
|
106
87
|
```bash
|
|
107
|
-
|
|
88
|
+
curl http://127.0.0.1:9801/health
|
|
89
|
+
# {"status":"ok"}
|
|
108
90
|
```
|
|
109
91
|
|
|
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>
|
|
92
|
+
### 企业环境(代理、自定义 CA)
|
|
122
93
|
|
|
123
|
-
|
|
124
|
-
# Output efficiency
|
|
94
|
+
代理在转发到 `api.anthropic.com` 时支持以下环境变量。在 Zscaler / Netskope / Forcepoint / Bluecoat / 企业 squid 等环境下,在代理的环境中设置这些变量。
|
|
125
95
|
|
|
126
|
-
|
|
96
|
+
| 变量 | 效果 |
|
|
97
|
+
|------|------|
|
|
98
|
+
| `HTTPS_PROXY` / `HTTP_PROXY`(及小写变体) | 通过企业 HTTP CONNECT 代理路由上游请求。 |
|
|
99
|
+
| `NO_PROXY` | 逗号分隔的主机列表,绕过代理。支持 `*` 和 `.suffix.example.com`。 |
|
|
100
|
+
| `CACHE_FIX_PROXY_CA_FILE` | PEM 文件路径,包含一个或多个额外 CA 证书(用于 SSL 检查代理)。 |
|
|
101
|
+
| `NODE_EXTRA_CA_CERTS` | Node.js 标准机制 — 同样支持。 |
|
|
102
|
+
| `CACHE_FIX_PROXY_REJECT_UNAUTHORIZED=0` | **不安全的逃生通道。** 禁用 TLS 验证。仅在等待 IT 提供企业 CA 证书包时作为最后手段使用。 |
|
|
127
103
|
|
|
128
|
-
|
|
104
|
+
## 快速开始:预加载(CC v2.1.112 及更早版本)
|
|
129
105
|
|
|
130
|
-
|
|
106
|
+
如果使用基于 Node.js 的 CC 版本(v2.1.112 或更早),预加载拦截器无需代理即可工作:
|
|
131
107
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
108
|
+
```bash
|
|
109
|
+
npm install -g claude-code-cache-fix
|
|
110
|
+
NODE_OPTIONS="--import claude-code-cache-fix" claude
|
|
135
111
|
```
|
|
136
112
|
|
|
137
|
-
|
|
113
|
+
> **注意:** 预加载不适用于 CC v2.1.113+(Bun 二进制文件)。请使用上述代理方式。
|
|
138
114
|
|
|
139
|
-
|
|
140
|
-
<summary>公开 / 默认 Claude Code 版本</summary>
|
|
115
|
+
包装脚本、Shell 别名、Windows 说明和 VS Code 预加载模式集成请参见 [docs/preload-setup.md](docs/preload-setup.md)。
|
|
141
116
|
|
|
142
|
-
|
|
143
|
-
# Output efficiency
|
|
117
|
+
## VS Code 扩展
|
|
144
118
|
|
|
145
|
-
|
|
119
|
+
[VS Code 扩展](https://github.com/cnighswonger/claude-code-cache-fix-vscode)(v0.5.0)支持代理和预加载两种模式:
|
|
146
120
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
```
|
|
121
|
+
**代理模式(推荐):**
|
|
122
|
+
1. 启动代理(见上文)
|
|
123
|
+
2. 在 VS Code 命令面板中:**Claude Code Cache Fix: Enable Proxy Mode**
|
|
124
|
+
3. 重启任何活跃的 Claude Code 会话
|
|
156
125
|
|
|
157
|
-
|
|
126
|
+
**预加载模式(CC ≤v2.1.112):**
|
|
127
|
+
1. `npm install -g claude-code-cache-fix`
|
|
128
|
+
2. 从 [GitHub Releases](https://github.com/cnighswonger/claude-code-cache-fix-vscode/releases/latest) 下载 VSIX
|
|
129
|
+
3. 安装:`code --install-extension claude-code-cache-fix-0.5.0.vsix`
|
|
130
|
+
4. 命令面板:**Claude Code Cache Fix: Enable**
|
|
158
131
|
|
|
159
|
-
|
|
160
|
-
<summary>自定义替换示例(结合上面两版的折中版本)</summary>
|
|
132
|
+
手动 VS Code 包装器设置(不使用 VSIX)请参见 [docs/preload-setup.md](docs/preload-setup.md#vs-code-preload-mode)。
|
|
161
133
|
|
|
162
|
-
|
|
163
|
-
# Output efficiency
|
|
134
|
+
## 安全模型
|
|
164
135
|
|
|
165
|
-
|
|
136
|
+
> **代理和拦截器对 API 请求和响应具有完全读写访问权限。** 这是该方法固有的特性 — 任何 fetch 拦截器、代理或网关都处于这个位置。
|
|
166
137
|
|
|
167
|
-
|
|
138
|
+
**它做什么:** 修改出站请求结构(块排序、指纹、TTL、git-status)以修复缓存 bug。读取响应头和 SSE 使用量数据用于监控。
|
|
168
139
|
|
|
169
|
-
|
|
140
|
+
**它不做什么:** 代理或拦截器不会发起网络调用。所有遥测数据写入 `~/.claude/` 下的本地文件。除非你明确选择加入 [claude-code-meter](https://github.com/cnighswonger/claude-code-meter) 共享(独立包,需要交互式同意),否则数据不会离开你的机器。
|
|
170
141
|
|
|
171
|
-
|
|
142
|
+
**供应链:** 代理模式:7 个小型扩展模块在 `proxy/extensions/` 中(每个不到 200 行)。预加载模式:单个未压缩文件(`preload.mjs`,约 1,700 行)。一个开发依赖(`zod`,仅用于测试中的模式验证)。安装前请审查代码。npm 出处(provenance)将每个发布版本链接到其源代码提交。
|
|
172
143
|
|
|
173
|
-
|
|
144
|
+
**独立审计:** 被 @TheAuditorTool [评估为"合法工具"](https://github.com/anthropics/claude-code/issues/38335#issuecomment-4244413605)(2026-04-14)。
|
|
174
145
|
|
|
175
|
-
|
|
146
|
+
## 问题描述
|
|
176
147
|
|
|
177
|
-
|
|
178
|
-
```
|
|
148
|
+
当你在 Claude Code 中使用 `--resume` 或 `/resume` 时,提示缓存会静默失效。API 不再读取已缓存的 token(廉价),而是每一轮都从头重建(昂贵)。原本每小时约 $0.50 的会话可能在无任何提示的情况下飙升至 $5-10/小时。
|
|
179
149
|
|
|
180
|
-
|
|
150
|
+
三个 bug 导致了这个问题:
|
|
181
151
|
|
|
182
|
-
|
|
152
|
+
1. **附件块散布** — 技能列表、MCP 服务器、延迟工具、钩子等附件块应当位于 `messages[0]`。恢复会话时,它们会漂移到后续消息中,改变缓存前缀。
|
|
183
153
|
|
|
184
|
-
|
|
154
|
+
2. **指纹不稳定** — `cc_version` 指纹(如 `2.1.92.a3f`)基于 `messages[0]` 的内容计算,包括元数据/附件块。当这些块偏移时,指纹改变,系统提示改变,缓存失效。
|
|
185
155
|
|
|
186
|
-
|
|
156
|
+
3. **工具定义排序不确定** — 工具定义在不同轮次间可能以不同顺序到达,改变请求字节并使缓存键失效。
|
|
187
157
|
|
|
188
|
-
|
|
158
|
+
此外,通过 Read 工具读取的图片以 base64 形式持久化在对话历史中,在每次后续 API 调用时一并发送,悄然增加 token 成本。
|
|
189
159
|
|
|
190
|
-
|
|
160
|
+
## 工作原理
|
|
191
161
|
|
|
192
|
-
|
|
162
|
+
**代理模式**(v3.0.0+):一个位于 `localhost:9801` 的 HTTP 服务器拦截 `POST /v1/messages` 请求。七个扩展模块通过流水线处理每个请求 — 规范化块排序、剥离指纹、稳定工具排序、管理 TTL 标记。扩展是可热重载的 `.mjs` 文件,通过 `proxy/extensions.json` 配置。所有其他流量原样传递。
|
|
193
163
|
|
|
194
|
-
|
|
164
|
+
**预加载模式**(v2.x):一个 Node.js `--import` 模块,在 Claude Code 发起 API 调用前修补 `globalThis.fetch`。应用相同的修复 — 扫描用户消息中的迁移块、排序工具、重新计算指纹、注入 TTL 标记。
|
|
195
165
|
|
|
196
|
-
|
|
166
|
+
两种模式都是幂等的 — 如果无需修复,请求原样传递。两种模式都不会修改你的对话;它们只在请求到达 API 之前规范化请求结构。
|
|
197
167
|
|
|
198
|
-
|
|
168
|
+
## 状态栏 — 实时配额警告
|
|
199
169
|
|
|
200
|
-
|
|
170
|
+
代理和预加载模式都会在每次 API 调用时将配额状态写入 `~/.claude/quota-status.json`。内置的 `tools/quota-statusline.sh` 脚本显示实时状态栏:
|
|
201
171
|
|
|
202
|
-
|
|
172
|
+
- **Q5h %** 及消耗速率(%/分钟)
|
|
173
|
+
- **Q7d %** 及消耗速率(%/小时)
|
|
174
|
+
- **TTL 层级** — 健康时显示 `TTL:1h`,**服务器降级时以红色显示 `TTL:5m`**(通常在 Q5h ≥ 100% 时)
|
|
175
|
+
- **PEAK** 在工作日高峰时段(UTC 13:00-19:00)以黄色显示
|
|
176
|
+
- **缓存命中率 %**
|
|
177
|
+
- **OVERAGE** 标志(激活时)
|
|
203
178
|
|
|
204
|
-
###
|
|
179
|
+
### 建议:禁用 git-status 注入
|
|
205
180
|
|
|
206
|
-
|
|
181
|
+
Claude Code 在每次调用时将实时 `git status` 注入系统提示。任何文件编辑都会改变 git status,从而使整个前缀缓存失效。禁用此功能每次调用可节省约 1,800 token:
|
|
207
182
|
|
|
208
183
|
```bash
|
|
209
|
-
|
|
210
|
-
node tools/cost-report.mjs --date 2026-04-08 # 指定日期
|
|
211
|
-
node tools/cost-report.mjs --since 2h # 最近 2 小时
|
|
212
|
-
node tools/cost-report.mjs --admin-key <key> # 与 Admin API 交叉验证
|
|
184
|
+
export CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=1
|
|
213
185
|
```
|
|
214
186
|
|
|
215
|
-
|
|
187
|
+
或在 `~/.claude/settings.json` 中添加 `"includeGitInstructions": false`。社区验证者 [@wadabum](https://github.com/cnighswonger/claude-code-cache-fix/issues/11):跨 git 状态变化仅 18 token 缓存创建(禁用前为数千 token)。
|
|
216
188
|
|
|
217
|
-
##
|
|
189
|
+
## 图片剥离(预加载模式)
|
|
218
190
|
|
|
219
|
-
|
|
191
|
+
通过 Read 工具读取的图片以 base64 持久化在对话历史中,在每次后续 API 调用时随行发送。单张 500KB 图片在 Opus 4.6 上每轮带来约 62,500 token 开销,**在 Opus 4.7 上约 85,000+ token**(因新分词器)。强烈建议在 4.7 上启用图片剥离。
|
|
220
192
|
|
|
221
193
|
```bash
|
|
222
|
-
|
|
194
|
+
export CACHE_FIX_IMAGE_KEEP_LAST=3
|
|
223
195
|
```
|
|
224
196
|
|
|
225
|
-
|
|
197
|
+
## 系统提示重写(预加载模式,可选)
|
|
226
198
|
|
|
227
|
-
|
|
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` 段
|
|
199
|
+
拦截器可重写 Claude Code 的 `# Output efficiency` 系统提示段落。默认禁用。使用 `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT` 启用。三种已知提示变体及使用说明请参见 [docs/output-efficiency-prompts.md](docs/output-efficiency-prompts.md)。
|
|
241
200
|
|
|
242
|
-
|
|
201
|
+
## 监控与诊断
|
|
243
202
|
|
|
244
|
-
|
|
203
|
+
预加载拦截器包含对微压缩降级、虚假速率限制器、GrowthBook 标志状态、使用量遥测和成本报告的监控。配额追踪在代理和预加载模式下均通过 `~/.claude/quota-status.json` 工作。
|
|
245
204
|
|
|
246
|
-
|
|
247
|
-
CACHE_FIX_PREFIXDIFF=1 claude-fixed
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
快照会保存到 `~/.claude/cache-fix-snapshots/`,并在重启后的第一次 API 调用时生成差异报告。
|
|
205
|
+
完整详情、调试模式、前缀差异对比、环境变量和内置配额分析工具请参见 [docs/monitoring.md](docs/monitoring.md)。
|
|
251
206
|
|
|
252
|
-
##
|
|
207
|
+
## 限制
|
|
253
208
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
| `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT` | unset | 在请求发出前替换 Claude Code 的 `# Output efficiency` 系统提示词段落 |
|
|
260
|
-
| `CACHE_FIX_USAGE_LOG` | `~/.claude/usage.jsonl` | 每次调用使用量遥测日志路径 |
|
|
209
|
+
- **代理需要运行中的进程** — 必须在 Claude Code 之前启动代理。建议作为 systemd 服务运行或使用带健康检查的包装脚本。
|
|
210
|
+
- **超额 TTL 降级** — 超过 5 小时配额的 100% 会触发服务器端 TTL 从 1h 降级至 5m。这是服务器端决策,无法在客户端修复。代理/拦截器防止可能推你进入超额状态的缓存不稳定。
|
|
211
|
+
- **微压缩不可阻止** — 监控功能可以检测上下文降级但无法阻止。微压缩和预算执行是通过 GrowthBook 标志的服务器控制,没有客户端禁用选项。
|
|
212
|
+
- **系统提示重写是实验性的** — 仅预加载模式,可选。未证明是社区报告中讨论的行为差异的原因。使用风险由用户自行承担。
|
|
213
|
+
- **版本耦合** — 指纹 salt 和块检测启发式规则源自 Claude Code 内部实现。重大重构可能需要更新此包。
|
|
261
214
|
|
|
262
|
-
##
|
|
215
|
+
## 追踪的问题
|
|
263
216
|
|
|
264
|
-
|
|
265
|
-
- **超额 TTL 降级** — 超过 5 小时配额的 100% 会触发服务器端 TTL 从 1h 降级至 5m。这是服务器端决策,无法在客户端修复。拦截器通过防止缓存不稳定来避免你首先进入超额状态。
|
|
266
|
-
- **微压缩不可阻止** — 监控功能可以检测上下文降级,但无法阻止。微压缩和预算执行机制是通过 GrowthBook 标志进行服务器控制的,没有客户端禁用选项。
|
|
267
|
-
- **系统提示词重写是实验性的** — 此 hook 只会重写一个系统提示词段落,并且默认关闭,但仍存在未知因素:目前并未证明这段提示词文本本身就是社区报告中行为差异的根因,也无法确认未来服务端校验是否会对修改后的系统提示词作出反应。使用风险由用户自行承担。
|
|
268
|
-
- **版本耦合** — 指纹 salt 和块检测启发式规则都来自 Claude Code 内部实现。重大重构可能需要更新此包。
|
|
217
|
+
我们监控 30 多个与缓存、配额和上下文 bug 相关的上游 Claude Code 问题。完整列表、我们的参与情况、社区研究和关键贡献者请参见 [TRACKED_ISSUES.md](TRACKED_ISSUES.md)。
|
|
269
218
|
|
|
270
|
-
##
|
|
219
|
+
## 相关研究
|
|
271
220
|
|
|
272
|
-
- [
|
|
273
|
-
- [
|
|
274
|
-
- [
|
|
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` 系统提示词变更及其可能影响模型行为的社区讨论
|
|
221
|
+
- **[@ArkNill/claude-code-hidden-problem-analysis](https://github.com/ArkNill/claude-code-hidden-problem-analysis)** — 38,996 请求的代理分析:7 个 bug(微压缩、预算上限、虚假速率限制器、JSONL 重复、扩展思考)、GrowthBook 功能标志因果测试、Opus 4.7 消耗率警告。v1.1.0 的监控功能基于此研究。
|
|
222
|
+
- **[@Renvect/X-Ray-Claude-Code-Interceptor](https://github.com/Renvect/X-Ray-Claude-Code-Interceptor)** — 带实时仪表板的诊断 HTTPS 代理、系统提示段落差异对比、按工具的剥离阈值。支持所有使用 `ANTHROPIC_BASE_URL` 的 Claude 客户端。
|
|
223
|
+
- **[@fgrosswig/claude-usage-dashboard](https://github.com/fgrosswig/claude-usage-dashboard)** — 自托管取证仪表板,SSE 实时监控、多主机聚合、缓存健康评分。与我们代理的视角互补。连接设置请参见 [docs/dashboard-integration.md](docs/dashboard-integration.md)。
|
|
279
224
|
|
|
280
225
|
## 生产环境使用
|
|
281
226
|
|
|
282
|
-
- **[Crunchloop DAP](https://dap.crunchloop.ai)** — Agent SDK / DAP
|
|
227
|
+
- **[Crunchloop DAP](https://dap.crunchloop.ai)** — Agent SDK / DAP 开发环境。首个将拦截器合入 trunk 并团队级部署的生产团队(2026-04-10)。通过实际测试发现两类缓存回归问题 — 工具排序抖动与新会话排序缺口,并贡献了驱动 v1.5.1 和 v1.6.2 修复的调试日志。
|
|
283
228
|
|
|
284
229
|
## 贡献者
|
|
285
230
|
|
|
286
|
-
- **[@VictorSun92](https://github.com/VictorSun92)** —
|
|
287
|
-
- **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — Agent SDK / DAP
|
|
288
|
-
- **[@jmarianski](https://github.com/jmarianski)** — 通过 MITM 代理抓包和 Ghidra
|
|
289
|
-
- **[@cnighswonger](https://github.com/cnighswonger)** —
|
|
290
|
-
- **[@ArkNill](https://github.com/ArkNill)** — 微压缩机制分析、GrowthBook
|
|
291
|
-
- **[@Renvect](https://github.com/Renvect)** —
|
|
231
|
+
- **[@VictorSun92](https://github.com/VictorSun92)** — v2.1.88 原始 monkey-patch 修复,v2.1.90 部分散布识别,前向扫描检测、正确块排序、更严格的块匹配器及可选 output-efficiency 重写钩子
|
|
232
|
+
- **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — Agent SDK / DAP 生产环境验证,1h 缓存 TTL 确认,调试追踪发现工具排序抖动(v1.5.1 修复),SKILLS SORT 诊断发现新会话排序 bug(v1.6.2 修复)。首个将拦截器合入 trunk 的生产团队。
|
|
233
|
+
- **[@jmarianski](https://github.com/jmarianski)** — 通过 MITM 代理抓包和 Ghidra 逆向分析定位根因,多模式缓存测试脚本
|
|
234
|
+
- **[@cnighswonger](https://github.com/cnighswonger)** — 指纹稳定化、工具排序修复、图片剥离、监控功能、超额 TTL 降级发现、代理架构,包维护者
|
|
235
|
+
- **[@ArkNill](https://github.com/ArkNill)** — 微压缩机制分析、GrowthBook 标志文档、虚假速率限制器识别、CC v2.1.108+ 指纹验证修复(PR #21)、韩文 README(PR #22)、[claude-code-hidden-problem-analysis](https://github.com/ArkNill/claude-code-hidden-problem-analysis) 研究
|
|
236
|
+
- **[@Renvect](https://github.com/Renvect)** — 图片重复发现、跨项目目录污染分析
|
|
237
|
+
- **[@fgrosswig](https://github.com/fgrosswig)** — [claude-usage-dashboard](https://github.com/fgrosswig/claude-usage-dashboard) 取证方法论:成本因子开销比率指标、`anthropic-*` 头部捕获模式、为仪表板互通层提供参考的代理 NDJSON 模式
|
|
238
|
+
- **[@TomTheMenace](https://github.com/TomTheMenace)** — Windows `.bat` 包装器、首个 Windows 平台验证(7.5 小时/536 调用 Opus 4.6 会话,98.4% 缓存命中率)
|
|
239
|
+
- **[@arjansingh](https://github.com/arjansingh)** — 动态 `npm root -g` 路径解析的 nvm 兼容包装脚本(PR #15)
|
|
240
|
+
- **[@beekamai](https://github.com/beekamai)** — npm root 包含空格时的 Windows URL 编码修复(PR #17)
|
|
241
|
+
- **[@JEONG-JIWOO](https://github.com/JEONG-JIWOO)** — VS Code 扩展调查:发现 `claudeCode.claudeProcessWrapper` 作为可行的集成路径,编写 Windows C 包装器(#16)
|
|
242
|
+
- **[@X-15](https://github.com/X-15)** — VS Code 扩展验证、v2.1.105 安全检查行为确认的每项修复状态分析(#16)、Windows 代理管道修复(#53)、企业代理支持(PR #54)
|
|
243
|
+
- **[@deafsquad](https://github.com/deafsquad)** — 通用 smoosh_split un-smoosh 修复(PR #26)、恢复散布 bug 的源码级函数归因(anthropics/claude-code#43657)、OTEL 遥测发现、v3.0.0 代理架构提议与构建
|
|
244
|
+
|
|
245
|
+
如果你参与了这些问题的社区协作但尚未被列出,欢迎开 issue 或 PR — 我们希望正确致谢每一位贡献者。
|
|
246
|
+
|
|
247
|
+
## 支持
|
|
248
|
+
|
|
249
|
+
如果这个工具帮你省了钱,请考虑请我喝杯咖啡:
|
|
292
250
|
|
|
293
|
-
|
|
251
|
+
<a href="https://buymeacoffee.com/vsits" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
294
252
|
|
|
295
253
|
## 许可证
|
|
296
254
|
|
|
297
|
-
MIT
|
|
255
|
+
[MIT](LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-cache-fix",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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": "./preload.mjs",
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
"test": "node --test",
|
|
24
24
|
"postinstall": "node postinstall.js"
|
|
25
25
|
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"hpagent": "^1.2.0"
|
|
28
|
+
},
|
|
26
29
|
"keywords": [
|
|
27
30
|
"claude-code",
|
|
28
31
|
"claude",
|
package/proxy/config.mjs
CHANGED
|
@@ -10,6 +10,10 @@ function envInt(name, fallback) {
|
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
|
|
13
|
+
// Existing fields are read once at module init (preserving prior behavior).
|
|
14
|
+
// New corp-proxy/CA fields are getters so they reflect live env — important
|
|
15
|
+
// for test isolation (see test/proxy-upstream-corp-proxy.test.mjs) and for
|
|
16
|
+
// callers that legitimately want to flip env at runtime.
|
|
13
17
|
const config = {
|
|
14
18
|
port: envInt("CACHE_FIX_PROXY_PORT", 9801),
|
|
15
19
|
bind: process.env.CACHE_FIX_PROXY_BIND || "127.0.0.1",
|
|
@@ -18,6 +22,17 @@ const config = {
|
|
|
18
22
|
extensionsDir: process.env.CACHE_FIX_EXTENSIONS_DIR || join(__dirname, "extensions"),
|
|
19
23
|
extensionsConfig: process.env.CACHE_FIX_EXTENSIONS_CONFIG || join(__dirname, "extensions.json"),
|
|
20
24
|
debug: process.env.CACHE_FIX_DEBUG === "1",
|
|
25
|
+
get httpsProxy() { return process.env.HTTPS_PROXY || process.env.https_proxy || ""; },
|
|
26
|
+
get httpProxy() { return process.env.HTTP_PROXY || process.env.http_proxy || ""; },
|
|
27
|
+
get noProxy() { return process.env.NO_PROXY || process.env.no_proxy || ""; },
|
|
28
|
+
get caFile() { return process.env.CACHE_FIX_PROXY_CA_FILE || ""; },
|
|
29
|
+
get rejectUnauthorized() {
|
|
30
|
+
const raw = process.env.CACHE_FIX_PROXY_REJECT_UNAUTHORIZED;
|
|
31
|
+
if (raw === undefined || raw === "") return true;
|
|
32
|
+
if (raw === "1" || raw.toLowerCase() === "true") return true;
|
|
33
|
+
if (raw === "0" || raw.toLowerCase() === "false") return false;
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
21
36
|
};
|
|
22
37
|
|
|
23
38
|
export default config;
|
|
@@ -1,8 +1,63 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
|
|
5
|
+
const QUOTA_PATH = join(homedir(), ".claude", "quota-status.json");
|
|
6
|
+
|
|
7
|
+
function parseHeaders(headers) {
|
|
8
|
+
const get = (key) => headers[key] || "";
|
|
9
|
+
const num = (key) => parseFloat(get(key)) || 0;
|
|
10
|
+
|
|
11
|
+
const q5h_util = num("anthropic-ratelimit-unified-5h-utilization");
|
|
12
|
+
const q7d_util = num("anthropic-ratelimit-unified-7d-utilization");
|
|
13
|
+
const q5h_reset = parseInt(get("anthropic-ratelimit-unified-5h-reset")) || 0;
|
|
14
|
+
const q7d_reset = parseInt(get("anthropic-ratelimit-unified-7d-reset")) || 0;
|
|
15
|
+
const status = get("anthropic-ratelimit-unified-status") || get("anthropic-ratelimit-unified-5h-status");
|
|
16
|
+
const overage_status = get("anthropic-ratelimit-unified-overage-status");
|
|
17
|
+
const overage_util = num("anthropic-ratelimit-unified-overage-utilization");
|
|
18
|
+
const overage_reset = parseInt(get("anthropic-ratelimit-unified-overage-reset")) || 0;
|
|
19
|
+
const fallback_pct = get("anthropic-ratelimit-unified-fallback-percentage");
|
|
20
|
+
const representative = get("anthropic-ratelimit-unified-representative-claim");
|
|
21
|
+
const surpassed = get("anthropic-ratelimit-unified-7d-surpassed-threshold");
|
|
22
|
+
|
|
23
|
+
if (!q5h_reset && !q7d_reset) return null;
|
|
24
|
+
|
|
25
|
+
const now = new Date();
|
|
26
|
+
const hour = now.getUTCHours();
|
|
27
|
+
const day = now.getUTCDay();
|
|
28
|
+
const peak = day >= 1 && day <= 5 && hour >= 13 && hour < 19;
|
|
29
|
+
|
|
30
|
+
const allHeaders = {};
|
|
31
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
32
|
+
if (k.startsWith("anthropic-") || k === "cf-ray" || k === "request-id") {
|
|
33
|
+
allHeaders[k] = v;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
five_hour: { utilization: q5h_util, pct: Math.round(q5h_util * 100), resets_at: q5h_reset },
|
|
39
|
+
seven_day: { utilization: q7d_util, pct: Math.round(q7d_util * 100), resets_at: q7d_reset },
|
|
40
|
+
status: status || "unknown",
|
|
41
|
+
overage_status: overage_status || "unknown",
|
|
42
|
+
peak_hour: peak,
|
|
43
|
+
all_headers: allHeaders,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
1
47
|
export default {
|
|
2
48
|
name: "cache-telemetry",
|
|
3
|
-
description: "Extract cache
|
|
49
|
+
description: "Extract cache stats from response stream, persist quota state to ~/.claude/quota-status.json",
|
|
4
50
|
order: 600,
|
|
5
51
|
|
|
52
|
+
async onResponseStart(ctx) {
|
|
53
|
+
if (!ctx.headers) return;
|
|
54
|
+
|
|
55
|
+
const quota = parseHeaders(ctx.headers);
|
|
56
|
+
if (!quota) return;
|
|
57
|
+
|
|
58
|
+
ctx.meta._quotaData = quota;
|
|
59
|
+
},
|
|
60
|
+
|
|
6
61
|
async onStreamEvent(ctx) {
|
|
7
62
|
const { event, telemetry } = ctx;
|
|
8
63
|
if (!event || !telemetry) return;
|
|
@@ -19,6 +74,39 @@ export default {
|
|
|
19
74
|
if (event.type === "message_delta" && event.usage) {
|
|
20
75
|
if (!ctx.meta.cacheStats) ctx.meta.cacheStats = {};
|
|
21
76
|
ctx.meta.cacheStats.outputTokens = event.usage.output_tokens || 0;
|
|
77
|
+
|
|
78
|
+
const stats = ctx.meta.cacheStats;
|
|
79
|
+
const quota = ctx.meta._quotaData;
|
|
80
|
+
if (!quota) return;
|
|
81
|
+
|
|
82
|
+
const cr = stats.cacheRead || 0;
|
|
83
|
+
const cc = stats.cacheCreation || 0;
|
|
84
|
+
const total = cr + cc;
|
|
85
|
+
const hitRate = total > 0 ? ((cr / total) * 100).toFixed(1) : "N/A";
|
|
86
|
+
|
|
87
|
+
const ephemeral1h = cc;
|
|
88
|
+
const ephemeral5m = 0;
|
|
89
|
+
|
|
90
|
+
const ttl = cr > 0 ? "1h" : (cc > 0 ? "5m" : "unknown");
|
|
91
|
+
|
|
92
|
+
const output = {
|
|
93
|
+
cache: {
|
|
94
|
+
ttl_tier: ttl,
|
|
95
|
+
cache_creation: cc,
|
|
96
|
+
cache_read: cr,
|
|
97
|
+
ephemeral_1h: ephemeral1h,
|
|
98
|
+
ephemeral_5m: ephemeral5m,
|
|
99
|
+
hit_rate: hitRate,
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
},
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
...quota,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
mkdirSync(join(homedir(), ".claude"), { recursive: true });
|
|
108
|
+
writeFileSync(QUOTA_PATH, JSON.stringify(output, null, 2));
|
|
109
|
+
} catch {}
|
|
22
110
|
}
|
|
23
111
|
},
|
|
24
112
|
};
|