claude-code-cache-fix 1.10.0 → 2.0.0-beta.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.ko.md +303 -0
- package/README.md +5 -2
- package/package.json +1 -1
- package/preload.mjs +73 -8
package/README.ko.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# claude-code-cache-fix
|
|
2
|
+
|
|
3
|
+
[English](./README.md) | [中文](./README.zh.md) | 한국어
|
|
4
|
+
|
|
5
|
+
[Claude Code](https://github.com/anthropics/claude-code)에서 세션 재개 시 **최대 20배 비용 증가**를 유발하는 프롬프트 캐시 회귀 버그를 수정하고, 자동 컨텍스트 열화를 모니터링합니다. v2.1.107까지 확인 완료.
|
|
6
|
+
|
|
7
|
+
## 보안 모델
|
|
8
|
+
|
|
9
|
+
> **이 인터셉터는 `globalThis.fetch`를 패치합니다.** 설계상 Claude Code 프로세스의 모든 API 요청·응답에 대한 읽기/쓰기 접근 권한을 가집니다. 이는 fetch 인터셉터, 프록시, 게이트웨이 등 이 방식에서 본질적으로 발생하는 것입니다.
|
|
10
|
+
|
|
11
|
+
**하는 것:** 캐시 버그 수정을 위해 요청 구조(블록 순서, 핑거프린트, TTL, git-status)를 수정합니다. 모니터링을 위해 응답 헤더와 SSE 사용량 데이터를 읽습니다.
|
|
12
|
+
|
|
13
|
+
**하지 않는 것:** 인터셉터에서 네트워크 호출을 하지 않습니다. 모든 텔레메트리는 `~/.claude/` 아래 로컬 파일에 기록됩니다. [claude-code-meter](https://github.com/cnighswonger/claude-code-meter) 공유에 명시적으로 동의하지 않는 한 데이터가 외부로 전송되지 않습니다.
|
|
14
|
+
|
|
15
|
+
**공급망:** 단일 비축소 파일(`preload.mjs`, ~1,700줄). 의존성 1개(`zod`, 테스트 스키마 검증용). 설치 전 코드를 직접 검토하십시오. npm provenance로 각 버전이 소스 커밋에 연결됩니다.
|
|
16
|
+
|
|
17
|
+
**독립 감사:** @TheAuditorTool에 의해 ["LEGITIMATE TOOL"로 평가](https://github.com/anthropics/claude-code/issues/38335#issuecomment-4244413605) (2026-04-14).
|
|
18
|
+
|
|
19
|
+
## 문제점
|
|
20
|
+
|
|
21
|
+
Claude Code에서 `--resume` 또는 `/resume`를 사용하면 프롬프트 캐시가 자동으로 깨집니다. 캐시된 토큰을 읽는 대신(저렴) 매 턴마다 처음부터 재구축합니다(고비용). 시간당 약 $0.50이어야 할 세션이 아무런 표시 없이 $5-10/시간까지 치솟을 수 있습니다.
|
|
22
|
+
|
|
23
|
+
세 가지 버그가 원인입니다:
|
|
24
|
+
|
|
25
|
+
1. **블록 분산(Partial block scatter)** — 스킬 목록, MCP 서버, 지연 도구, 훅 등 첨부 블록이 `messages[0]`에 있어야 하지만, 세션 재개 시 이후 메시지로 이동하여 캐시 접두사가 변경됩니다.
|
|
26
|
+
|
|
27
|
+
2. **핑거프린트 불안정(Fingerprint instability)** — `cc_version` 핑거프린트(예: `2.1.92.a3f`)가 메타/첨부 블록을 포함한 `messages[0]` 내용으로 계산됩니다. 블록이 이동하면 핑거프린트가 바뀌고, 시스템 프롬프트가 바뀌고, 캐시가 무효화됩니다.
|
|
28
|
+
|
|
29
|
+
3. **도구 정의 순서 비결정적(Non-deterministic tool ordering)** — 도구 정의가 턴 간에 다른 순서로 도착할 수 있어 요청 바이트가 변경되고 캐시 키가 무효화됩니다.
|
|
30
|
+
|
|
31
|
+
또한 Read 도구로 읽은 이미지가 base64로 대화 기록에 저장되어 이후 모든 API 호출에 함께 전송되며, 토큰 비용이 자동으로 누적됩니다.
|
|
32
|
+
|
|
33
|
+
## 설치
|
|
34
|
+
|
|
35
|
+
Node.js >= 18 필요, Claude Code가 npm으로 설치되어 있어야 합니다(독립 바이너리 불가).
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g claude-code-cache-fix
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 사용법
|
|
42
|
+
|
|
43
|
+
Node.js 프리로드 모듈로 동작하며, API 요청이 전송되기 전에 인터셉트합니다.
|
|
44
|
+
|
|
45
|
+
### 방법 A: 래퍼 스크립트 (권장)
|
|
46
|
+
|
|
47
|
+
래퍼 스크립트(예: `~/bin/claude-fixed`)를 생성합니다:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
#!/bin/bash
|
|
51
|
+
NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null)"
|
|
52
|
+
|
|
53
|
+
CLAUDE_NPM_CLI="$NPM_GLOBAL_ROOT/@anthropic-ai/claude-code/cli.js"
|
|
54
|
+
CACHE_FIX="$NPM_GLOBAL_ROOT/claude-code-cache-fix/preload.mjs"
|
|
55
|
+
|
|
56
|
+
if [ ! -f "$CLAUDE_NPM_CLI" ]; then
|
|
57
|
+
echo "Error: Claude Code npm package not found at $CLAUDE_NPM_CLI" >&2
|
|
58
|
+
echo "Install with: npm install -g @anthropic-ai/claude-code" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [ ! -f "$CACHE_FIX" ]; then
|
|
63
|
+
echo "Error: claude-code-cache-fix not found at $CACHE_FIX" >&2
|
|
64
|
+
echo "Install with: npm install -g claude-code-cache-fix" >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
exec env NODE_OPTIONS="--import $CACHE_FIX" node "$CLAUDE_NPM_CLI" "$@"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
chmod +x ~/bin/claude-fixed
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
npm 글로벌 경로가 다른 경우 `npm root -g`로 확인하여 조정하십시오.
|
|
76
|
+
|
|
77
|
+
### 방법 B: 셸 별칭
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
alias claude='NODE_OPTIONS="--import claude-code-cache-fix" node "$(npm root -g)/@anthropic-ai/claude-code/cli.js"'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 방법 C: 직접 호출
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
NODE_OPTIONS="--import claude-code-cache-fix" claude
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
> **참고**: `claude`가 npm/Node 설치를 가리킬 때만 동작합니다. 독립 바이너리는 Node.js 프리로드를 우회하는 다른 실행 경로를 사용합니다.
|
|
90
|
+
|
|
91
|
+
### Windows 사용자
|
|
92
|
+
|
|
93
|
+
Windows에서는 `NODE_OPTIONS="--import ..."` 방식이 Linux/macOS와 동일하게 동작하지 않습니다. 포함된 `claude-fixed.bat` 래퍼를 사용하십시오:
|
|
94
|
+
|
|
95
|
+
1. 두 패키지를 글로벌 설치합니다:
|
|
96
|
+
```bat
|
|
97
|
+
npm install -g claude-code-cache-fix
|
|
98
|
+
npm install -g @anthropic-ai/claude-code
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
2. `claude-fixed.bat`를 PATH에 있는 디렉토리로 복사합니다(예: `C:\Users\<이름>\bin\`):
|
|
102
|
+
```bat
|
|
103
|
+
copy "%NPM_ROOT%\claude-code-cache-fix\claude-fixed.bat" C:\Users\%USERNAME%\bin\
|
|
104
|
+
```
|
|
105
|
+
또는 npm 글로벌 루트(`npm root -g`)에서 직접 파일을 찾으십시오.
|
|
106
|
+
|
|
107
|
+
3. 인터셉터가 활성화된 상태로 Claude Code를 실행합니다:
|
|
108
|
+
```bat
|
|
109
|
+
claude-fixed [claude 인수...]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## VS Code 확장
|
|
113
|
+
|
|
114
|
+
### 방법 A: VSIX 확장 (권장)
|
|
115
|
+
|
|
116
|
+
1. 인터셉터 설치: `npm install -g claude-code-cache-fix`
|
|
117
|
+
2. [GitHub Releases](https://github.com/cnighswonger/claude-code-cache-fix-vscode/releases/latest)에서 VSIX 다운로드
|
|
118
|
+
3. 설치: `code --install-extension claude-code-cache-fix-0.1.0.vsix`
|
|
119
|
+
(또는 VS Code: 확장 → `...` 메뉴 → "VSIX에서 설치...")
|
|
120
|
+
4. 활성 Claude Code 세션 재시작
|
|
121
|
+
|
|
122
|
+
확장이 활성화 시 `claudeCode.claudeProcessWrapper`를 자동 설정합니다. 수동 설정이 필요 없으며 Windows, macOS, Linux에서 동작합니다.
|
|
123
|
+
|
|
124
|
+
### 알려진 제한 (VS Code)
|
|
125
|
+
|
|
126
|
+
- **핑거프린트 수정 자동 비활성화**: VS Code 확장은 CLI와 다르게 `messages[0]`을 구성하여 핑거프린트 안전 검사가 실패합니다. `CACHE_FIX_SKIP_FINGERPRINT=1` 환경변수로 우회하십시오. 다른 모든 수정(재배치, 도구 정렬, TTL, /clear 아티팩트 제거)은 정상 동작합니다.
|
|
127
|
+
|
|
128
|
+
## 동작 원리
|
|
129
|
+
|
|
130
|
+
모듈은 Claude Code가 `/v1/messages`에 API 호출하기 전에 `globalThis.fetch`를 인터셉트합니다. 각 호출에서:
|
|
131
|
+
|
|
132
|
+
1. 모든 사용자 메시지에서 재배치 대상 첨부 블록(스킬, MCP, 지연 도구, 훅)을 스캔하여 최신 버전을 `messages[0]`으로 이동합니다.
|
|
133
|
+
2. 도구 정의를 이름 알파벳순으로 정렬하여 결정적 순서를 보장합니다.
|
|
134
|
+
3. 메타/첨부 블록이 아닌 실제 사용자 메시지 텍스트로 `cc_version` 핑거프린트를 재계산합니다.
|
|
135
|
+
|
|
136
|
+
모든 수정은 멱등적입니다 — 수정이 필요 없으면 요청이 그대로 전달됩니다.
|
|
137
|
+
|
|
138
|
+
## 이미지 제거
|
|
139
|
+
|
|
140
|
+
Read 도구로 읽은 이미지는 base64로 인코딩되어 대화 기록의 `tool_result` 블록에 저장됩니다. 압축될 때까지 **이후 모든 API 호출에** 함께 전송됩니다. 500KB 이미지 하나가 턴당 약 62,500 토큰의 추가 비용을 발생시킵니다.
|
|
141
|
+
|
|
142
|
+
오래된 도구 결과에서 이미지를 제거하려면:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
export CACHE_FIX_IMAGE_KEEP_LAST=3
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
최근 3개 사용자 메시지의 이미지를 유지하고 이전 것은 텍스트 자리 표시자로 대체합니다. `tool_result` 블록(Read 도구 출력)의 이미지만 대상이며, 사용자가 직접 붙여넣은 이미지는 영향받지 않습니다.
|
|
149
|
+
|
|
150
|
+
`0`(기본값)으로 설정하면 비활성화됩니다.
|
|
151
|
+
|
|
152
|
+
## 시스템 프롬프트 재작성 (선택)
|
|
153
|
+
|
|
154
|
+
Claude Code의 `# Output efficiency` 시스템 프롬프트 섹션을 요청 전에 재작성할 수 있습니다.
|
|
155
|
+
|
|
156
|
+
이 기능은 **선택적**이며 **기본 비활성화**입니다. `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT`를 설정하지 않으면 아무것도 변경되지 않습니다.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
export CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT=$'# Output efficiency\n\n...'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 모니터링 기능
|
|
163
|
+
|
|
164
|
+
인터셉터에는 커뮤니티에서 발견한 추가 문제에 대한 모니터링이 포함됩니다:
|
|
165
|
+
|
|
166
|
+
### 마이크로컴팩트 / 예산 집행
|
|
167
|
+
|
|
168
|
+
Claude Code는 서버 제어 메커니즘(GrowthBook 플래그)을 통해 이전 도구 결과를 `[Old tool result content cleared]`로 자동 대체합니다. 200,000자 총 한도와 도구별 한도(Bash: 30K, Grep: 20K)가 알림 없이 이전 결과를 잘라냅니다.
|
|
169
|
+
|
|
170
|
+
### 가상 속도 제한기
|
|
171
|
+
|
|
172
|
+
클라이언트가 실제 API 호출 없이 합성 "Rate limit reached" 오류를 생성할 수 있으며, `"model": "<synthetic>"`으로 식별됩니다.
|
|
173
|
+
|
|
174
|
+
### GrowthBook 플래그 덤프
|
|
175
|
+
|
|
176
|
+
첫 API 호출 시 `~/.claude.json`을 읽어 비용/캐시 관련 서버 제어 플래그의 현재 상태를 기록합니다.
|
|
177
|
+
|
|
178
|
+
### 쿼터 추적
|
|
179
|
+
|
|
180
|
+
응답 헤더에서 `anthropic-ratelimit-unified-5h-utilization`과 `7d-utilization`을 파싱하여 `~/.claude/quota-status.json`에 저장합니다.
|
|
181
|
+
|
|
182
|
+
### 피크 시간 감지
|
|
183
|
+
|
|
184
|
+
Anthropic은 평일 피크 시간(UTC 13:00-19:00, 월-금)에 쿼터 소모 속도를 높입니다. 인터셉터가 피크 기간을 감지하여 `quota-status.json`에 `peak_hour: true/false`를 기록합니다.
|
|
185
|
+
|
|
186
|
+
### 사용량 텔레메트리 및 비용 리포트
|
|
187
|
+
|
|
188
|
+
API 호출당 사용량 데이터를 `~/.claude/usage.jsonl`에 기록합니다. 내장 비용 리포트 도구로 분석할 수 있습니다:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
node tools/cost-report.mjs # 오늘 비용
|
|
192
|
+
node tools/cost-report.mjs --date 2026-04-08 # 특정 날짜
|
|
193
|
+
node tools/cost-report.mjs --since 2h # 최근 2시간
|
|
194
|
+
node tools/cost-report.mjs --admin-key <key> # Admin API 교차 검증
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 상태 표시줄
|
|
198
|
+
|
|
199
|
+
인터셉터는 매 API 호출마다 `~/.claude/quota-status.json`에 쿼터 상태를 기록합니다. 포함된 `tools/quota-statusline.sh` 스크립트로 Claude Code에 실시간 상태를 표시할 수 있습니다:
|
|
200
|
+
|
|
201
|
+
- **Q5h %** (소진율)
|
|
202
|
+
- **Q7d %** (소진율)
|
|
203
|
+
- **TTL 티어** — 정상 시 `TTL:1h`, **서버 다운그레이드 시 빨간색 `TTL:5m`**
|
|
204
|
+
- **PEAK** — 피크 시간 시 노란색 표시
|
|
205
|
+
- **캐시 히트율 %**
|
|
206
|
+
|
|
207
|
+
### 설정
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
mkdir -p ~/.claude/hooks
|
|
211
|
+
cp "$(npm root -g)/claude-code-cache-fix/tools/quota-statusline.sh" ~/.claude/hooks/
|
|
212
|
+
chmod +x ~/.claude/hooks/quota-statusline.sh
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`~/.claude/settings.json`에 추가:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"statusLine": {
|
|
220
|
+
"type": "command",
|
|
221
|
+
"command": "~/.claude/hooks/quota-statusline.sh"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 권장: git-status 주입 비활성화
|
|
227
|
+
|
|
228
|
+
Claude Code는 매 호출마다 `git status` 출력을 시스템 프롬프트에 주입합니다. 파일 편집 시마다 git 상태가 바뀌어 전체 접두사 캐시가 무효화됩니다. 비활성화하면 호출당 약 1,800 토큰을 절약하고 시스템 프롬프트를 안정화합니다:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
export CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=1
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
또는 `~/.claude/settings.json`에 `"includeGitInstructions": false`를 추가하십시오.
|
|
235
|
+
|
|
236
|
+
## 디버그 모드
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
CACHE_FIX_DEBUG=1 claude-fixed
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
로그는 `~/.claude/cache-fix-debug.log`에 기록됩니다. 주요 확인 항목:
|
|
243
|
+
|
|
244
|
+
- `APPLIED: resume message relocation` — 블록 분산이 감지되어 수정됨
|
|
245
|
+
- `APPLIED: tool order stabilization` — 도구가 재정렬됨
|
|
246
|
+
- `APPLIED: fingerprint stabilized from XXX to YYY` — 핑거프린트가 보정됨
|
|
247
|
+
- `MICROCOMPACT: N/M tool results cleared` — 마이크로컴팩트 열화 감지
|
|
248
|
+
- `FALSE RATE LIMIT: synthetic model detected` — 클라이언트 측 가상 속도 제한 감지
|
|
249
|
+
- `CACHE TTL: tier=1h create=N read=N hit=N%` — TTL 티어 및 캐시 히트율
|
|
250
|
+
- `PEAK HOUR: weekday 13:00-19:00 UTC` — Anthropic 피크 시간 스로틀링 활성
|
|
251
|
+
|
|
252
|
+
## 환경 변수
|
|
253
|
+
|
|
254
|
+
| 변수 | 기본값 | 설명 |
|
|
255
|
+
|------|--------|------|
|
|
256
|
+
| `CACHE_FIX_DEBUG` | `0` | 디버그 로그 활성화 |
|
|
257
|
+
| `CACHE_FIX_PREFIXDIFF` | `0` | 접두사 스냅숏 비교 활성화 |
|
|
258
|
+
| `CACHE_FIX_IMAGE_KEEP_LAST` | `0` | 최근 N개 사용자 메시지의 이미지 유지 (0 = 비활성화) |
|
|
259
|
+
| `CACHE_FIX_USAGE_LOG` | `~/.claude/usage.jsonl` | 호출별 사용량 텔레메트리 로그 경로 |
|
|
260
|
+
| `CACHE_FIX_DISABLED` | `0` | 모든 버그 수정 비활성화, 모니터링은 유지 |
|
|
261
|
+
| `CACHE_FIX_SKIP_RELOCATE` | `0` | 블록 재배치 수정 건너뛰기 |
|
|
262
|
+
| `CACHE_FIX_SKIP_FINGERPRINT` | `0` | 핑거프린트 안정화 건너뛰기 |
|
|
263
|
+
| `CACHE_FIX_SKIP_TOOL_SORT` | `0` | 도구 정렬 안정화 건너뛰기 |
|
|
264
|
+
| `CACHE_FIX_SKIP_TTL` | `0` | TTL 주입 건너뛰기 |
|
|
265
|
+
| `CACHE_FIX_STRIP_GIT_STATUS` | `0` | 접두사 안정화를 위해 git-status 제거 |
|
|
266
|
+
| `CACHE_FIX_TTL_MAIN` | `1h` | 메인 스레드 요청 TTL: `1h`, `5m`, 또는 `none` |
|
|
267
|
+
| `CACHE_FIX_TTL_SUBAGENT` | `1h` | 서브에이전트 요청 TTL: `1h`, `5m`, 또는 `none` |
|
|
268
|
+
|
|
269
|
+
## 제한 사항
|
|
270
|
+
|
|
271
|
+
- **npm 설치만 지원** — 독립 Claude Code 바이너리는 Zig 수준 증명을 사용하여 Node.js를 우회합니다. 이 수정은 npm 패키지(`npm install -g @anthropic-ai/claude-code`)에서만 동작합니다.
|
|
272
|
+
- **초과 TTL 다운그레이드** — 5시간 쿼터 100% 초과 시 서버가 TTL을 1h에서 5m으로 강제 다운그레이드합니다. 서버 측 결정이므로 클라이언트에서 수정할 수 없습니다.
|
|
273
|
+
- **마이크로컴팩트 방지 불가** — 모니터링은 컨텍스트 열화를 감지할 수 있지만 방지할 수는 없습니다. GrowthBook 플래그를 통한 서버 제어이며 클라이언트 비활성화 옵션이 없습니다.
|
|
274
|
+
- **버전 결합** — 핑거프린트 salt와 블록 감지 휴리스틱은 Claude Code 내부 구현에서 파생됩니다. 대규모 리팩토링 시 이 패키지 업데이트가 필요할 수 있습니다.
|
|
275
|
+
|
|
276
|
+
## 관련 이슈
|
|
277
|
+
|
|
278
|
+
- [#34629](https://github.com/anthropics/claude-code/issues/34629) — 세션 재개 캐시 회귀 최초 보고
|
|
279
|
+
- [#40524](https://github.com/anthropics/claude-code/issues/40524) — 세션 내 핑거프린트 무효화, 이미지 지속
|
|
280
|
+
- [#42052](https://github.com/anthropics/claude-code/issues/42052) — 커뮤니티 인터셉터 개발, TTL 다운그레이드 발견
|
|
281
|
+
- [#44045](https://github.com/anthropics/claude-code/issues/44045) — SDK 수준 재현 및 토큰 측정
|
|
282
|
+
- [#32508](https://github.com/anthropics/claude-code/issues/32508) — `Output efficiency` 시스템 프롬프트 변경 커뮤니티 논의
|
|
283
|
+
|
|
284
|
+
## 관련 리서치
|
|
285
|
+
|
|
286
|
+
- **[@ArkNill/claude-code-hidden-problem-analysis](https://github.com/ArkNill/claude-code-hidden-problem-analysis)** — 프록시 기반 체계적 분석: 마이크로컴팩트, 예산 집행, 가상 속도 제한기 등 11개 버그 + 30,477건 요청 데이터셋. v1.1.0 모니터링 기능은 이 리서치에 기반합니다.
|
|
287
|
+
- **[@Renvect/X-Ray-Claude-Code-Interceptor](https://github.com/Renvect/X-Ray-Claude-Code-Interceptor)** — 실시간 대시보드가 있는 진단용 HTTPS 프록시
|
|
288
|
+
- **[@fgrosswig/claude-usage-dashboard](https://github.com/fgrosswig/claude-usage-dashboard)** — 셀프 호스팅 포렌식 대시보드, SSE 실시간 모니터링
|
|
289
|
+
|
|
290
|
+
## 기여자
|
|
291
|
+
|
|
292
|
+
- **[@VictorSun92](https://github.com/VictorSun92)** — v2.1.88 최초 monkey-patch 수정, 부분 블록 분산 식별
|
|
293
|
+
- **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — 프로덕션 환경 검증, 도구 정렬 흔들림 발견
|
|
294
|
+
- **[@jmarianski](https://github.com/jmarianski)** — MITM 프록시 캡처 및 Ghidra 역공학을 통한 근본 원인 분석
|
|
295
|
+
- **[@cnighswonger](https://github.com/cnighswonger)** — 핑거프린트 안정화, 모니터링 기능, 패키지 관리자
|
|
296
|
+
- **[@ArkNill](https://github.com/ArkNill)** — 마이크로컴팩트 메커니즘 분석, GrowthBook 플래그 문서화, 가상 속도 제한기 식별
|
|
297
|
+
- **[@TomTheMenace](https://github.com/TomTheMenace)** — Windows `.bat` 래퍼, 최초 Windows 플랫폼 검증 (7.5시간/536호출, 98.4% 캐시 히트율)
|
|
298
|
+
- **[@fgrosswig](https://github.com/fgrosswig)** — 포렌식 대시보드, 비용 팩터 메트릭 방법론
|
|
299
|
+
- **[@JEONG-JIWOO](https://github.com/JEONG-JIWOO)** — VS Code 확장 조사, `claudeCode.claudeProcessWrapper` 통합 경로 발견
|
|
300
|
+
|
|
301
|
+
## 라이선스
|
|
302
|
+
|
|
303
|
+
[MIT](LICENSE)
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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
|
+
|
|
5
|
+
English | [中文](./README.zh.md) | [한국어](./README.ko.md) | [Português](./docs/guia-pt-br.md)
|
|
4
6
|
|
|
5
7
|
Fixes prompt cache regressions in [Claude Code](https://github.com/anthropics/claude-code) that cause **up to 20x cost increase** on resumed sessions, plus monitoring for silent context degradation. Confirmed through v2.1.107.
|
|
6
8
|
|
|
@@ -176,7 +178,7 @@ Then set in VS Code `settings.json`:
|
|
|
176
178
|
|
|
177
179
|
### Known limitations (VS Code)
|
|
178
180
|
|
|
179
|
-
- **Fingerprint fix
|
|
181
|
+
- **Fingerprint fix**: Fixed in v1.11.0 — the safety check now handles both the v2.1.108+ extraction method and the legacy method. No workaround needed. (Previously required `CACHE_FIX_SKIP_FINGERPRINT=1`.)
|
|
180
182
|
|
|
181
183
|
Credit: [@JEONG-JIWOO](https://github.com/JEONG-JIWOO) and [@X-15](https://github.com/X-15) for the VS Code extension investigation and C wrapper ([#16](https://github.com/cnighswonger/claude-code-cache-fix/issues/16)).
|
|
182
184
|
|
|
@@ -602,6 +604,7 @@ measurable signature of cache-efficiency degradation.
|
|
|
602
604
|
- **[@beekamai](https://github.com/beekamai)** — Windows URL-encoding fix for `claude-fixed.bat` when npm root contains spaces (PR #17)
|
|
603
605
|
- **[@JEONG-JIWOO](https://github.com/JEONG-JIWOO)** — VS Code extension investigation: discovered `claudeCode.claudeProcessWrapper` as the working integration path, wrote the C wrapper for Windows (#16)
|
|
604
606
|
- **[@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)
|
|
607
|
+
- **[@ArkNill](https://github.com/ArkNill)** — Fingerprint verification fix for CC v2.1.108+ (`isMeta` filter change, PR #21), Korean README (PR #22), original [claude-code-hidden-problem-analysis](https://github.com/ArkNill/claude-code-hidden-problem-analysis) research
|
|
605
608
|
|
|
606
609
|
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.
|
|
607
610
|
|
package/package.json
CHANGED
package/preload.mjs
CHANGED
|
@@ -128,14 +128,37 @@ function stabilizeFingerprint(system, messages) {
|
|
|
128
128
|
|
|
129
129
|
// --- SAFETY: Round-trip verification ---
|
|
130
130
|
// Verify our salt/indices reproduce CC's fingerprint for the ORIGINAL
|
|
131
|
-
// message text
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
// message text that CC used.
|
|
132
|
+
//
|
|
133
|
+
// Prior to v2.1.108, CC computed the fingerprint from messages[0] content
|
|
134
|
+
// (including meta/attachment blocks). Starting in v2.1.108, CC switched to
|
|
135
|
+
// an internal `!isMeta` filter (HoY function), which skips synthetic
|
|
136
|
+
// system-reminder blocks. In the API payload, this is equivalent to finding
|
|
137
|
+
// the first text block that doesn't start with "<system-reminder>".
|
|
138
|
+
//
|
|
139
|
+
// We try both extraction methods: the new "real user message" first (v2.1.108+),
|
|
140
|
+
// then fall back to the legacy "first message text" for older versions.
|
|
141
|
+
// This keeps the safety check working across CC versions.
|
|
142
|
+
//
|
|
143
|
+
// Discovered by @ArkNill via mitmproxy + CC source analysis on v2.1.108.
|
|
144
|
+
const realText = extractRealUserMessageText(messages);
|
|
145
|
+
const realVerification = computeFingerprint(realText, baseVersion);
|
|
146
|
+
const legacyText = extractFirstMessageText(messages);
|
|
147
|
+
const legacyVerification = computeFingerprint(legacyText, baseVersion);
|
|
148
|
+
|
|
149
|
+
let verificationPassed = false;
|
|
150
|
+
if (realVerification === oldFingerprint) {
|
|
151
|
+
verificationPassed = true;
|
|
152
|
+
debugLog("FINGERPRINT VERIFY: matched via real user message text (v2.1.108+ path)");
|
|
153
|
+
} else if (legacyVerification === oldFingerprint) {
|
|
154
|
+
verificationPassed = true;
|
|
155
|
+
debugLog("FINGERPRINT VERIFY: matched via legacy messages[0] text (pre-v2.1.108 path)");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!verificationPassed) {
|
|
136
159
|
debugLog(
|
|
137
160
|
"FINGERPRINT SAFETY: round-trip verification failed.",
|
|
138
|
-
`CC sent '${oldFingerprint}',
|
|
161
|
+
`CC sent '${oldFingerprint}', real='${realVerification}', legacy='${legacyVerification}'.`,
|
|
139
162
|
"Salt/indices may have changed in this CC version. Skipping rewrite."
|
|
140
163
|
);
|
|
141
164
|
recordFixResult("fingerprint", "safety_blocked");
|
|
@@ -143,8 +166,7 @@ function stabilizeFingerprint(system, messages) {
|
|
|
143
166
|
}
|
|
144
167
|
// --- END SAFETY ---
|
|
145
168
|
|
|
146
|
-
// Compute stable fingerprint from real user text
|
|
147
|
-
const realText = extractRealUserMessageText(messages);
|
|
169
|
+
// Compute stable fingerprint from real user text (already extracted above)
|
|
148
170
|
const stableFingerprint = computeFingerprint(realText, baseVersion);
|
|
149
171
|
|
|
150
172
|
if (stableFingerprint === oldFingerprint) return null; // already correct
|
|
@@ -658,6 +680,7 @@ const DEBUG = process.env.CACHE_FIX_DEBUG === "1";
|
|
|
658
680
|
const PREFIXDIFF = process.env.CACHE_FIX_PREFIXDIFF === "1";
|
|
659
681
|
const NORMALIZE_IDENTITY = process.env.CACHE_FIX_NORMALIZE_IDENTITY === "1";
|
|
660
682
|
const STRIP_GIT_STATUS = process.env.CACHE_FIX_STRIP_GIT_STATUS === "1";
|
|
683
|
+
const NORMALIZE_CWD = process.env.CACHE_FIX_NORMALIZE_CWD === "1";
|
|
661
684
|
const TTL_MAIN = (process.env.CACHE_FIX_TTL_MAIN || "1h").toLowerCase();
|
|
662
685
|
const TTL_SUBAGENT = (process.env.CACHE_FIX_TTL_SUBAGENT || "1h").toLowerCase();
|
|
663
686
|
const LOG_PATH = join(homedir(), ".claude", "cache-fix-debug.log");
|
|
@@ -702,6 +725,7 @@ const _STATS_SCHEMA = {
|
|
|
702
725
|
ttl: { applied: 0, skipped: 0, lastApplied: null },
|
|
703
726
|
identity: { applied: 0, skipped: 0, lastApplied: null },
|
|
704
727
|
git_status: { applied: 0, skipped: 0, lastApplied: null },
|
|
728
|
+
cwd_normalize: { applied: 0, skipped: 0, lastApplied: null },
|
|
705
729
|
};
|
|
706
730
|
|
|
707
731
|
function _createEmptyStats() {
|
|
@@ -1282,6 +1306,47 @@ globalThis.fetch = async function (url, options) {
|
|
|
1282
1306
|
}
|
|
1283
1307
|
}
|
|
1284
1308
|
|
|
1309
|
+
// Optimization: normalize CWD and path references in system prompt
|
|
1310
|
+
// CC injects the full working directory path, additional directories, and
|
|
1311
|
+
// path references into system text blocks. These change per project/worktree,
|
|
1312
|
+
// busting the prefix cache across different working directories.
|
|
1313
|
+
// Opt-in via CACHE_FIX_NORMALIZE_CWD=1.
|
|
1314
|
+
// The model can still discover paths via Bash (pwd, ls) when needed.
|
|
1315
|
+
if (NORMALIZE_CWD && shouldApplyFix("cwd_normalize") && payload.system && Array.isArray(payload.system)) {
|
|
1316
|
+
let normalized = 0;
|
|
1317
|
+
payload.system = payload.system.map((block) => {
|
|
1318
|
+
if (block?.type !== "text" || typeof block.text !== "string") return block;
|
|
1319
|
+
let newText = block.text;
|
|
1320
|
+
// Normalize "Primary working directory: /path/to/project"
|
|
1321
|
+
newText = newText.replace(
|
|
1322
|
+
/( - Primary working directory: ).+/g,
|
|
1323
|
+
"$1[normalized by cache-fix]"
|
|
1324
|
+
);
|
|
1325
|
+
// Normalize "Additional working directories:" section
|
|
1326
|
+
newText = newText.replace(
|
|
1327
|
+
/( - Additional working directories:\n)((?: - .+\n)*)/g,
|
|
1328
|
+
"$1 - [normalized by cache-fix]\n"
|
|
1329
|
+
);
|
|
1330
|
+
// Normalize "Contents of /path/to/..." in claudeMd/memory references
|
|
1331
|
+
newText = newText.replace(
|
|
1332
|
+
/Contents of \/[^\s(]+/g,
|
|
1333
|
+
"Contents of [path normalized by cache-fix]"
|
|
1334
|
+
);
|
|
1335
|
+
if (newText !== block.text) {
|
|
1336
|
+
normalized++;
|
|
1337
|
+
return { ...block, text: newText };
|
|
1338
|
+
}
|
|
1339
|
+
return block;
|
|
1340
|
+
});
|
|
1341
|
+
if (normalized > 0) {
|
|
1342
|
+
modified = true;
|
|
1343
|
+
debugLog(`APPLIED: CWD/paths normalized in ${normalized} system block(s)`);
|
|
1344
|
+
recordFixResult("cwd_normalize", "applied");
|
|
1345
|
+
} else {
|
|
1346
|
+
recordFixResult("cwd_normalize", "skipped");
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1285
1350
|
// Bug 5: TTL enforcement (configurable per request type)
|
|
1286
1351
|
// The client gates 1h cache TTL behind a GrowthBook allowlist that checks
|
|
1287
1352
|
// querySource against patterns like "repl_main_thread*", "sdk", "auto_mode".
|