claude-code-cache-fix 3.0.2 → 3.0.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.ko.md CHANGED
@@ -1,208 +1,215 @@
1
1
  # claude-code-cache-fix
2
2
 
3
- [English](./README.md) | [中文](./README.zh.md) | 한국어
3
+ [![npm](https://img.shields.io/npm/v/claude-code-cache-fix?color=blue)](https://www.npmjs.com/package/claude-code-cache-fix) [![Node.js](https://img.shields.io/badge/Node.js-18%2B-green)](https://nodejs.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow)](https://opensource.org/licenses/MIT) [![GitHub stars](https://img.shields.io/github/stars/cnighswonger/claude-code-cache-fix)](https://github.com/cnighswonger/claude-code-cache-fix/stargazers)
4
4
 
5
- [Claude Code](https://github.com/anthropics/claude-code)에서 세션 재개 **최대 20배 비용 증가**를 유발하는 프롬프트 캐시 회귀 버그를 수정하고, 자동 컨텍스트 열화를 모니터링합니다. v2.1.107까지 확인 완료.
5
+ [English](./README.md) | [中文](./README.zh.md) | 한국어 | [Português](./docs/guia-pt-br.md)
6
6
 
7
- ## 보안 모델
8
-
9
- > **이 인터셉터는 `globalThis.fetch`를 패치합니다.** 설계상 Claude Code 프로세스의 모든 API 요청·응답에 대한 읽기/쓰기 접근 권한을 가집니다. 이는 fetch 인터셉터, 프록시, 게이트웨이 등 이 방식에서 본질적으로 발생하는 것입니다.
7
+ [Claude Code](https://github.com/anthropics/claude-code)용 캐시 최적화 프록시입니다. 과도한 쿼터 소모를 유발하는 프롬프트 캐시 버그를 수정하고, 요청 접두사를 안정화하며, 자동 회귀를 모니터링합니다. v2.1.113+ Bun 바이너리를 포함한 모든 CC 버전에서 동작합니다.
10
8
 
11
- **하는 것:** 캐시 버그 수정을 위해 요청 구조(블록 순서, 핑거프린트, TTL, git-status)를 수정합니다. 모니터링을 위해 응답 헤더와 SSE 사용량 데이터를 읽습니다.
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)
12
10
 
13
- **하지 않는 것:** 인터셉터에서 네트워크 호출을 하지 않습니다. 모든 텔레메트리는 `~/.claude/` 아래 로컬 파일에 기록됩니다. [claude-code-meter](https://github.com/cnighswonger/claude-code-meter) 공유에 명시적으로 동의하지 않는 데이터가 외부로 전송되지 않습니다.
11
+ > **Opus 4.7 주의사항:** 계측 데이터에 따르면 4.7은 동일한 가시 토큰 대비 **Q5h 쿼터를 4.6의 약 2.4배 속도로 소모**합니다([@ArkNill이 독립 확인](https://github.com/ArkNill/claude-code-hidden-problem-analysis/blob/main/16_OPUS-47-ADVISORY.md)). 두 가지 요인: 새 토크나이저(최대 35% 더 많은 토큰, [문서화됨](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 분석)를 참조하십시오.
14
12
 
15
- **공급망:** 단일 비축소 파일(`preload.mjs`, ~1,700줄). 의존성 1개(`zod`, 테스트 스키마 검증용). 설치 전 코드를 직접 검토하십시오. npm provenance로 각 버전이 소스 커밋에 연결됩니다.
13
+ ## 빠른 시작: 프록시 (권장)
16
14
 
17
- **독립 감사:** @TheAuditorTool에 의해 ["LEGITIMATE TOOL"로 평가](https://github.com/anthropics/claude-code/issues/38335#issuecomment-4244413605) (2026-04-14).
15
+ 프록시는 모든 CC 버전(Node.js 또는 Bun 바이너리)에서 동작합니다. Claude Code와 Anthropic API 사이에 위치하여 핫리로드 확장으로 캐시 수정을 적용합니다.
18
16
 
19
- ## 문제점
17
+ ```bash
18
+ # 설치
19
+ npm install -g claude-code-cache-fix
20
20
 
21
- Claude Code에서 `--resume` 또는 `/resume`를 사용하면 프롬프트 캐시가 자동으로 깨집니다. 캐시된 토큰을 읽는 대신(저렴) 매 턴마다 처음부터 재구축합니다(고비용). 시간당 약 $0.50이어야 할 세션이 아무런 표시 없이 $5-10/시간까지 치솟을 수 있습니다.
21
+ # 프록시 시작 (localhost:9801에서 실행)
22
+ node "$(npm root -g)/claude-code-cache-fix/proxy/server.mjs" &
22
23
 
23
- 가지 버그가 원인입니다:
24
+ # 프록시를 경유하여 Claude Code 실행
25
+ ANTHROPIC_BASE_URL=http://127.0.0.1:9801 claude
26
+ ```
24
27
 
25
- 1. **블록 분산(Partial block scatter)** 스킬 목록, MCP 서버, 지연 도구, 훅 등 첨부 블록이 `messages[0]`에 있어야 하지만, 세션 재개 시 이후 메시지로 이동하여 캐시 접두사가 변경됩니다.
28
+ 이것으로 완료입니다. 프록시가 7개 캐시 수정 확장을 자동으로 적용합니다. 래퍼 스크립트, `NODE_OPTIONS`, 프리로드가 필요 없습니다.
26
29
 
27
- 2. **핑거프린트 불안정(Fingerprint instability)** — `cc_version` 핑거프린트(예: `2.1.92.a3f`)가 메타/첨부 블록을 포함한 `messages[0]` 내용으로 계산됩니다. 블록이 이동하면 핑거프린트가 바뀌고, 시스템 프롬프트가 바뀌고, 캐시가 무효화됩니다.
30
+ ### 프록시의 동작
28
31
 
29
- 3. **도구 정의 순서 비결정적(Non-deterministic tool ordering)** — 도구 정의가 턴 간에 다른 순서로 도착할 수 있어 요청 바이트가 변경되고 캐시 키가 무효화됩니다.
32
+ 모든 `/v1/messages` 요청에 7개 확장이 순서대로 실행됩니다:
30
33
 
31
- 또한 Read 도구로 읽은 이미지가 base64로 대화 기록에 저장되어 이후 모든 API 호출에 함께 전송되며, 토큰 비용이 자동으로 누적됩니다.
34
+ | 확장 | 수정 대상 |
35
+ |------|----------|
36
+ | `fingerprint-strip` | 시스템 프롬프트에서 불안정한 cc_version 핑거프린트를 제거합니다 |
37
+ | `sort-stabilization` | 도구 및 MCP 정의의 결정적 순서를 보장합니다 |
38
+ | `ttl-management` | 서버 TTL 티어를 감지하고 올바른 cache_control 마커를 주입합니다 |
39
+ | `identity-normalization` | 접두사 안정성을 위해 메시지 ID 필드를 정규화합니다 |
40
+ | `fresh-session-sort` | 첫 번째 턴의 비결정적 순서를 수정합니다 |
41
+ | `cache-control-normalize` | 메시지 간 cache_control 마커를 정규화합니다 |
42
+ | `cache-telemetry` | 응답 헤더에서 캐시 통계를 추출하여 `~/.claude/quota-status.json`에 기록합니다 |
32
43
 
33
- ## 설치
44
+ 확장은 핫리로드됩니다 — `proxy/extensions/`에서 `.mjs` 파일을 추가, 제거 또는 수정하면 프록시 재시작 없이 다음 요청부터 적용됩니다. 설정은 `proxy/extensions.json`에 있습니다.
34
45
 
35
- Node.js >= 18 필요, Claude Code가 npm으로 설치되어 있어야 합니다(독립 바이너리 불가).
46
+ ### 서비스로 실행
36
47
 
37
- ```bash
38
- npm install -g claude-code-cache-fix
39
- ```
48
+ **Linux (systemd — 권장):**
40
49
 
41
- ## 사용법
50
+ `~/.config/systemd/user/cache-fix-proxy.service`를 생성합니다:
42
51
 
43
- Node.js 프리로드 모듈로 동작하며, API 요청이 전송되기 전에 인터셉트합니다.
52
+ ```ini
53
+ [Unit]
54
+ Description=Claude Code Cache Fix Proxy (v3.x)
55
+ After=network.target
44
56
 
45
- ### 방법 A: 래퍼 스크립트 (권장)
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
46
63
 
47
- 래퍼 스크립트(예: `~/bin/claude-fixed`)를 생성합니다:
64
+ [Install]
65
+ WantedBy=default.target
66
+ ```
48
67
 
49
68
  ```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"
69
+ systemctl --user daemon-reload
70
+ systemctl --user enable --now cache-fix-proxy
55
71
 
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
72
+ # 선택: 로그인 부팅 시작
73
+ sudo loginctl enable-linger $USER
74
+ ```
61
75
 
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
76
+ `cache-fix-proxy install-service` 서브커맨드는 v3.1.0에서 계획 중입니다([#48](https://github.com/cnighswonger/claude-code-cache-fix/issues/48)).
67
77
 
68
- exec env NODE_OPTIONS="--import $CACHE_FIX" node "$CLAUDE_NPM_CLI" "$@"
69
- ```
78
+ **폴백 (모든 OS):**
70
79
 
71
80
  ```bash
72
- chmod +x ~/bin/claude-fixed
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
73
83
  ```
74
84
 
75
- npm 글로벌 경로가 다른 경우 `npm root -g`로 확인하여 조정하십시오.
76
-
77
- ### 방법 B: 셸 별칭
85
+ ### 상태 확인
78
86
 
79
87
  ```bash
80
- alias claude='NODE_OPTIONS="--import claude-code-cache-fix" node "$(npm root -g)/@anthropic-ai/claude-code/cli.js"'
88
+ curl http://127.0.0.1:9801/health
89
+ # {"status":"ok"}
81
90
  ```
82
91
 
83
- ### 방법 C: 직접 호출
92
+ ## 빠른 시작: 프리로드 (CC v2.1.112 이하)
93
+
94
+ Node.js 기반 CC 버전(v2.1.112 이하)을 사용 중이라면 프록시 없이 프리로드 인터셉터를 사용할 수 있습니다:
84
95
 
85
96
  ```bash
97
+ npm install -g claude-code-cache-fix
86
98
  NODE_OPTIONS="--import claude-code-cache-fix" claude
87
99
  ```
88
100
 
89
- > **참고**: `claude`가 npm/Node 설치를 가리킬 때만 동작합니다. 독립 바이너리는 Node.js 프리로드를 우회하는 다른 실행 경로를 사용합니다.
90
-
91
- ### Windows 사용자
101
+ > **참고:** 프리로드는 CC v2.1.113+(Bun 바이너리)에서 동작하지 않습니다. 위의 프록시를 사용하십시오.
92
102
 
93
- Windows에서는 `NODE_OPTIONS="--import ..."` 방식이 Linux/macOS와 동일하게 동작하지 않습니다. 포함된 `claude-fixed.bat` 래퍼를 사용하십시오:
103
+ 래퍼 스크립트, 별칭, Windows 설명, VS Code 프리로드 모드 연동은 [docs/preload-setup.md](docs/preload-setup.md)를 참조하십시오.
94
104
 
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`)에서 직접 파일을 찾으십시오.
105
+ ## VS Code 확장
106
106
 
107
- 3. 인터셉터가 활성화된 상태로 Claude Code를 실행합니다:
108
- ```bat
109
- claude-fixed [claude 인수...]
110
- ```
107
+ [VS Code 확장](https://github.com/cnighswonger/claude-code-cache-fix-vscode)(v0.5.0)은 프록시와 프리로드 모드를 모두 지원합니다:
111
108
 
112
- ## VS Code 확장
109
+ **프록시 모드 (권장):**
110
+ 1. 프록시를 시작합니다(위 참조)
111
+ 2. VS Code 명령 팔레트에서: **Claude Code Cache Fix: Enable Proxy Mode**
112
+ 3. 활성 Claude Code 세션을 재시작합니다
113
113
 
114
- ### 방법 A: VSIX 확장 (권장)
114
+ **프리로드 모드 (CC ≤v2.1.112):**
115
+ 1. `npm install -g claude-code-cache-fix`
116
+ 2. [GitHub Releases](https://github.com/cnighswonger/claude-code-cache-fix-vscode/releases/latest)에서 VSIX를 다운로드합니다
117
+ 3. 설치: `code --install-extension claude-code-cache-fix-0.5.0.vsix`
118
+ 4. 명령 팔레트에서: **Claude Code Cache Fix: Enable**
115
119
 
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 세션 재시작
120
+ VSIX 없이 수동 VS Code 래퍼를 설정하려면 [docs/preload-setup.md](docs/preload-setup.md#vs-code-preload-mode)를 참조하십시오.
121
121
 
122
- 확장이 활성화 시 `claudeCode.claudeProcessWrapper`를 자동 설정합니다. 수동 설정이 필요 없으며 Windows, macOS, Linux에서 동작합니다.
122
+ ## 보안 모델
123
123
 
124
- ### 알려진 제한 (VS Code)
124
+ > **프록시와 인터셉터는 API 요청 및 응답에 대한 전체 읽기/쓰기 접근 권한을 가집니다.** 이는 이 방식에 내재적인 것으로, 모든 fetch 인터셉터, 프록시, 게이트웨이가 이러한 위치를 가집니다.
125
125
 
126
- - **핑거프린트 수정 자동 비활성화**: VS Code 확장은 CLI와 다르게 `messages[0]`을 구성하여 핑거프린트 안전 검사가 실패합니다. `CACHE_FIX_SKIP_FINGERPRINT=1` 환경변수로 우회하십시오. 다른 모든 수정(재배치, 도구 정렬, TTL, /clear 아티팩트 제거)은 정상 동작합니다.
126
+ **하는 것:** 캐시 버그 수정을 위해 발신 요청 구조(블록 순서, 핑거프린트, TTL, git-status)를 수정합니다. 모니터링을 위해 응답 헤더와 SSE 사용량 데이터를 읽습니다.
127
127
 
128
- ## 동작 원리
128
+ **하지 않는 것:** 프록시 또는 인터셉터에서 네트워크 호출을 하지 않습니다. 모든 텔레메트리는 `~/.claude/` 아래 로컬 파일에 기록됩니다. [claude-code-meter](https://github.com/cnighswonger/claude-code-meter) 공유에 명시적으로 동의하지 않는 한 데이터가 외부로 전송되지 않습니다(별도 패키지, 대화형 동의 필요).
129
129
 
130
- 모듈은 Claude Code가 `/v1/messages`에 API 호출하기 전에 `globalThis.fetch`를 인터셉트합니다.호출에서:
130
+ **공급망:** 프록시 모드: `proxy/extensions/`에 7개 소형 확장 모듈(각 200줄 미만). 프리로드 모드: 단일 비축소 파일(`preload.mjs`, ~1,700줄). 개발 의존성 1개(`zod`, 테스트 스키마 검증용). 설치 전 코드를 직접 검토하십시오. npm provenance로 버전이 소스 커밋에 연결됩니다.
131
131
 
132
- 1. 모든 사용자 메시지에서 재배치 대상 첨부 블록(스킬, MCP, 지연 도구, 훅)을 스캔하여 최신 버전을 `messages[0]`으로 이동합니다.
133
- 2. 도구 정의를 이름 알파벳순으로 정렬하여 결정적 순서를 보장합니다.
134
- 3. 메타/첨부 블록이 아닌 실제 사용자 메시지 텍스트로 `cc_version` 핑거프린트를 재계산합니다.
132
+ **독립 감사:** @TheAuditorTool에 의해 ["LEGITIMATE TOOL"로 평가](https://github.com/anthropics/claude-code/issues/38335#issuecomment-4244413605) (2026-04-14).
135
133
 
136
- 모든 수정은 멱등적입니다 — 수정이 필요 없으면 요청이 그대로 전달됩니다.
134
+ ## 문제점
137
135
 
138
- ## 이미지 제거
136
+ Claude Code에서 `--resume` 또는 `/resume`를 사용하면 프롬프트 캐시가 자동으로 깨집니다. 캐시된 토큰을 읽는 대신(저렴) 매 턴마다 처음부터 재구축합니다(고비용). 시간당 약 $0.50이어야 할 세션이 아무런 표시 없이 $5-10/시간까지 치솟을 수 있습니다.
139
137
 
140
- Read 도구로 읽은 이미지는 base64로 인코딩되어 대화 기록의 `tool_result` 블록에 저장됩니다. 압축될 때까지 **이후 모든 API 호출에** 함께 전송됩니다. 500KB 이미지 하나가 턴당 약 62,500 토큰의 추가 비용을 발생시킵니다.
138
+ 가지 버그가 원인입니다:
141
139
 
142
- 오래된 도구 결과에서 이미지를 제거하려면:
140
+ 1. **블록 분산(Partial block scatter)** — 스킬 목록, MCP 서버, 지연 도구, 훅 등 첨부 블록이 `messages[0]`에 있어야 하지만, 세션 재개 시 이후 메시지로 이동하여 캐시 접두사가 변경됩니다.
143
141
 
144
- ```bash
145
- export CACHE_FIX_IMAGE_KEEP_LAST=3
146
- ```
142
+ 2. **핑거프린트 불안정(Fingerprint instability)** — `cc_version` 핑거프린트(예: `2.1.92.a3f`)가 메타/첨부 블록을 포함한 `messages[0]` 내용으로 계산됩니다. 블록이 이동하면 핑거프린트가 바뀌고, 시스템 프롬프트가 바뀌고, 캐시가 무효화됩니다.
147
143
 
148
- 최근 3 사용자 메시지의 이미지를 유지하고 이전 것은 텍스트 자리 표시자로 대체합니다. `tool_result` 블록(Read 도구 출력)의 이미지만 대상이며, 사용자가 직접 붙여넣은 이미지는 영향받지 않습니다.
144
+ 3. **도구 정의 순서 비결정적(Non-deterministic tool ordering)** 도구 정의가 간에 다른 순서로 도착할 있어 요청 바이트가 변경되고 캐시 키가 무효화됩니다.
149
145
 
150
- `0`(기본값)으로 설정하면 비활성화됩니다.
146
+ 또한 Read 도구로 읽은 이미지가 base64로 대화 기록에 저장되어 이후 모든 API 호출에 함께 전송되며, 토큰 비용이 자동으로 누적됩니다.
151
147
 
152
- ## 시스템 프롬프트 재작성 (선택)
148
+ ## 동작 원리
153
149
 
154
- Claude Code의 `# Output efficiency` 시스템 프롬프트 섹션을 요청 전에 재작성할 있습니다.
150
+ **프록시 모드** (v3.0.0+): `localhost:9801`의 HTTP 서버가 `POST /v1/messages` 요청을 인터셉트합니다. 7개 확장 모듈이 파이프라인을 통해 각 요청을 처리합니다 — 블록 순서 정규화, 핑거프린트 제거, 도구 정렬 안정화, TTL 마커 관리. 확장은 `proxy/extensions.json`에 설정된 핫리로드 `.mjs` 파일입니다. 그 외 모든 트래픽은 변경 없이 통과합니다.
155
151
 
156
- 기능은 **선택적**이며 **기본 비활성화**입니다. `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT`를 설정하지 않으면 아무것도 변경되지 않습니다.
152
+ **프리로드 모드** (v2.x): Claude Code가 API 호출을 하기 전에 `globalThis.fetch`를 패치하는 Node.js `--import` 모듈입니다. 동일한 수정을 인라인으로 적용합니다 — 사용자 메시지에서 재배치된 블록을 스캔하고, 도구를 정렬하고, 핑거프린트를 재계산하고, TTL 마커를 주입합니다.
157
153
 
158
- ```bash
159
- export CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT=$'# Output efficiency\n\n...'
160
- ```
154
+ 두 모드 모두 멱등적입니다 — 수정이 필요 없으면 요청이 그대로 전달됩니다. 두 모드 모두 대화를 수정하지 않으며, API에 전달되기 전 요청 구조만 정규화합니다.
161
155
 
162
- ## 모니터링 기능
156
+ ## 수정 졸업
163
157
 
164
- 인터셉터에는 커뮤니티에서 발견한 추가 문제에 대한 모니터링이 포함됩니다:
158
+ 패키지는 수명 주기가 다른 가지 목적을 수행합니다:
165
159
 
166
- ### 마이크로컴팩트 / 예산 집행
160
+ | 목적 | 예시 | 비활성화 시점 |
161
+ |------|------|-------------|
162
+ | **버그 수정** | 블록 재배치, 핑거프린트, 도구 정렬, TTL | CC가 근본 버그를 수정했을 때 — 상태 표시줄을 확인하십시오 |
163
+ | **모니터링** | 쿼터 추적, 마이크로컴팩트 감지, GrowthBook 플래그 | 영구 유지 — 향후 회귀를 감지합니다 |
164
+ | **최적화** | 이미지 제거, 출력 효율성 재작성 | 워크플로우에 도움이 되는 동안 유지하십시오 |
167
165
 
168
- Claude Code는 서버 제어 메커니즘(GrowthBook 플래그)을 통해 이전 도구 결과를 `[Old tool result content cleared]`로 자동 대체합니다. 200,000자 총 한도와 도구별 한도(Bash: 30K, Grep: 20K)가 알림 없이 이전 결과를 잘라냅니다.
166
+ ### 상태 확인 (프리로드 모드)
169
167
 
170
- ### 가상 속도 제한기
168
+ API 호출 시 인터셉터가 상태 표시줄을 기록합니다(`CACHE_FIX_DEBUG=1` 필요):
171
169
 
172
- 클라이언트가 실제 API 호출 없이 합성 "Rate limit reached" 오류를 생성할 수 있으며, `"model": "<synthetic>"`으로 식별됩니다.
170
+ ```
171
+ cache-fix health: relocate=active(2h ago) fingerprint=dormant(5 clean sessions) tool_sort=active ttl=active identity=waiting
172
+ ```
173
173
 
174
- ### GrowthBook 플래그 덤프
174
+ - **active(Xh ago)** — 수정이 최근에 적용됨
175
+ - **dormant(N clean sessions)** — N개 세션 동안 버그가 감지되지 않음; CC가 수정했을 수 있음
176
+ - **safety-blocked(Nx)** — 왕복 검증 실패; 수정이 자동 비활성화됨
177
+ - **waiting** — 수정이 아직 트리거되지 않음
175
178
 
176
- API 호출 시 `~/.claude.json`을 읽어 비용/캐시 관련 서버 제어 플래그의 현재 상태를 기록합니다.
179
+ ### 회귀 감지
177
180
 
178
- ### 쿼터 추적
181
+ 수정을 비활성화한 후 5회 이상 호출에서 cache_read 비율이 50% 미만으로 떨어지면:
182
+ ```
183
+ REGRESSION WARNING: cache_read ratio averaged 12% across last 5 calls.
184
+ Fixes are disabled — consider re-enabling to recover cache performance.
185
+ ```
179
186
 
180
- 응답 헤더에서 `anthropic-ratelimit-unified-5h-utilization`과 `7d-utilization`을 파싱하여 `~/.claude/quota-status.json`에 저장합니다.
187
+ ## 안전성
181
188
 
182
- ### 피크 시간 감지
189
+ ### 핑거프린트 왕복 검증
183
190
 
184
- Anthropic은 평일 피크 시간(UTC 13:00-19:00, 월-금) 쿼터 소모 속도를 높입니다. 인터셉터가 피크 기간을 감지하여 `quota-status.json`에 `peak_hour: true/false`를 기록합니다.
191
+ `cc_version` 핑거프린트를 재작성하기 전에, 인터셉터는 하드코딩된 salt와 문자 인덱스가 Claude Code가 보낸 핑거프린트를 재현하는지 검증합니다. 검증이 실패하면(CC가 알고리즘을 변경한 경우) 재작성이 자동으로 건너뜁니다. 이로써 인터셉터가 기본 CC보다 캐시 성능을 *악화시키는* 일이 없도록 보장합니다.
185
192
 
186
- ### 사용량 텔레메트리 및 비용 리포트
193
+ ### 페일세이프 설계
187
194
 
188
- API 호출당 사용량 데이터를 `~/.claude/usage.jsonl`에 기록합니다. 내장 비용 리포트 도구로 분석할 수 있습니다:
195
+ 모든 수정은 실패 무작동(no-op)으로 전환되도록 설계되어 있습니다:
196
+ - 블록 감지 정규식이 매칭되지 않으면 → 블록이 재배치되지 않음 (CC 동작)
197
+ - 핑거프린트 형식이 변경되면 → 핑거프린트가 재작성되지 않음 (CC 동작)
198
+ - 도구 정렬이 변경 사항을 생성하지 않으면 → 페이로드가 그대로 전달됨
199
+ - TTL 주입 대상 구조가 변경되면 → TTL이 주입되지 않음 (CC 동작)
189
200
 
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
- ```
201
+ 인터셉터는 *도움을 주거나* *아무것도 하지 않습니다*. 상황을 악화시킬 수 없습니다.
196
202
 
197
- ## 상태 표시줄
203
+ ## 상태 표시줄 — 실시간 쿼터 경고
198
204
 
199
- 인터셉터는 매 API 호출마다 `~/.claude/quota-status.json`에 쿼터 상태를 기록합니다. 포함된 `tools/quota-statusline.sh` 스크립트로 Claude Code에 실시간 상태를 표시할 수 있습니다:
205
+ 프록시와 프리로드 모드 모두 매 API 호출마다 `~/.claude/quota-status.json`에 쿼터 상태를 기록합니다. 포함된 `tools/quota-statusline.sh` 스크립트로 실시간 상태를 표시할 수 있습니다:
200
206
 
201
- - **Q5h %** (소진율)
202
- - **Q7d %** (소진율)
203
- - **TTL 티어** — 정상 시 `TTL:1h`, **서버 다운그레이드 시 빨간색 `TTL:5m`**
204
- - **PEAK** — 피크 시간 시 노란색 표시
207
+ - **Q5h %** (소진율, %/분)
208
+ - **Q7d %** (소진율, %/시간)
209
+ - **TTL 티어** — 정상 시 `TTL:1h`, **서버 다운그레이드 시 빨간색 `TTL:5m`** (일반적으로 Q5h ≥ 100% 시)
210
+ - **PEAK** — 평일 피크 시간(UTC 13:00-19:00) 시 노란색 표시
205
211
  - **캐시 히트율 %**
212
+ - **OVERAGE** 플래그 (활성 시)
206
213
 
207
214
  ### 설정
208
215
 
@@ -212,7 +219,7 @@ cp "$(npm root -g)/claude-code-cache-fix/tools/quota-statusline.sh" ~/.claude/ho
212
219
  chmod +x ~/.claude/hooks/quota-statusline.sh
213
220
  ```
214
221
 
215
- `~/.claude/settings.json`에 추가:
222
+ `~/.claude/settings.json`에 추가합니다:
216
223
 
217
224
  ```json
218
225
  {
@@ -223,80 +230,85 @@ chmod +x ~/.claude/hooks/quota-statusline.sh
223
230
  }
224
231
  ```
225
232
 
233
+ ### 상태 표시줄이 중요한 이유
234
+
235
+ 서버가 TTL을 5m으로 다운그레이드하면(Q5h ≥ 100%에서 쿼터 인식 다운그레이드), **5분 이상의 모든 유휴 시간이 전체 컨텍스트 재구축을 유발합니다**. 상태 표시줄이 없으면 이는 보이지 않습니다. 상태 표시줄의 빨간색 `TTL:5m` 경고는 **작업을 중단하고, Q5h 윈도우가 리셋될 때까지 기다린 후 재개하라**는 신호입니다. 초과 상태에서 작업을 계속하면 소모가 복리로 누적되지만, 일시 중지하면 악순환이 끊깁니다.
236
+
226
237
  ### 권장: git-status 주입 비활성화
227
238
 
228
- Claude Code는 매 호출마다 `git status` 출력을 시스템 프롬프트에 주입합니다. 파일 편집 시마다 git 상태가 바뀌어 전체 접두사 캐시가 무효화됩니다. 비활성화하면 호출당 약 1,800 토큰을 절약하고 시스템 프롬프트를 안정화합니다:
239
+ Claude Code는 매 호출마다 `git status` 출력을 시스템 프롬프트에 주입합니다. 파일 편집 시마다 git 상태가 바뀌어 전체 접두사 캐시가 무효화됩니다. 비활성화하면 호출당 약 1,800 토큰을 절약합니다:
229
240
 
230
241
  ```bash
231
242
  export CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=1
232
243
  ```
233
244
 
234
- 또는 `~/.claude/settings.json`에 `"includeGitInstructions": false`를 추가하십시오.
245
+ 또는 `~/.claude/settings.json`에 `"includeGitInstructions": false`를 추가하십시오. Claude Code는 컨텍스트가 필요할 때 Bash 도구를 통해 `git status`를 직접 실행할 수 있습니다. [@wadabum](https://github.com/cnighswonger/claude-code-cache-fix/issues/11)이 커뮤니티 검증: git 상태 변경 시 캐시 생성 18토큰(이 플래그 없이는 수천 토큰).
246
+
247
+ ## 이미지 제거 (프리로드 모드)
235
248
 
236
- ## 디버그 모드
249
+ Read 도구로 읽은 이미지는 base64로 인코딩되어 대화 기록에 저장되며, 이후 모든 API 호출에 함께 전송됩니다. 500KB 이미지 하나가 Opus 4.6에서 턴당 약 62,500 토큰, **Opus 4.7에서는 새 토크나이저로 인해 약 85,000+ 토큰**의 추가 비용을 발생시킵니다. 4.7에서는 이미지 제거를 강력히 권장합니다.
237
250
 
238
251
  ```bash
239
- CACHE_FIX_DEBUG=1 claude-fixed
252
+ export CACHE_FIX_IMAGE_KEEP_LAST=3
240
253
  ```
241
254
 
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` |
255
+ 최근 3개 사용자 메시지의 이미지를 유지하고 이전 것은 텍스트 자리 표시자로 대체합니다. `tool_result` 블록만 대상이며, 사용자가 직접 붙여넣은 이미지는 영향받지 않습니다.
256
+
257
+ ## 시스템 프롬프트 재작성 (프리로드 모드, 선택)
258
+
259
+ 인터셉터가 Claude Code의 `# Output efficiency` 시스템 프롬프트 섹션을 재작성할 있습니다. 기본 비활성화입니다. `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT`로 활성화하십시오. 가지 알려진 프롬프트 변형과 사용법은 [docs/output-efficiency-prompts.md](docs/output-efficiency-prompts.md)를 참조하십시오.
260
+
261
+ ## 모니터링 & 진단
262
+
263
+ 프리로드 인터셉터에는 마이크로컴팩트 열화, 가상 속도 제한기, GrowthBook 플래그 상태, 사용량 텔레메트리, 비용 리포트에 대한 모니터링이 포함됩니다. 쿼터 추적은 프록시와 프리로드 모드 모두에서 `~/.claude/quota-status.json`을 통해 동작합니다.
264
+
265
+ 전체 상세, 디버그 모드, 접두사 비교, 환경 변수, 내장 쿼터 분석 도구는 [docs/monitoring.md](docs/monitoring.md)를 참조하십시오.
268
266
 
269
267
  ## 제한 사항
270
268
 
271
- - **npm 설치만 지원**독립 Claude Code 바이너리는 Zig 수준 증명을 사용하여 Node.js를 우회합니다. 수정은 npm 패키지(`npm install -g @anthropic-ai/claude-code`)에서만 동작합니다.
272
- - **초과 TTL 다운그레이드** — 5시간 쿼터 100% 초과 시 서버가 TTL을 1h에서 5m으로 강제 다운그레이드합니다. 서버 측 결정이므로 클라이언트에서 수정할 수 없습니다.
273
- - **마이크로컴팩트 방지 불가** — 모니터링은 컨텍스트 열화를 감지할 수 있지만 방지할 수는 없습니다. GrowthBook 플래그를 통한 서버 제어이며 클라이언트 비활성화 옵션이 없습니다.
269
+ - **프록시는 실행 프로세스가 필요합니다** — Claude Code보다 먼저 프록시를 시작해야 합니다. 프록시가 실행 중이지 않은데 `ANTHROPIC_BASE_URL`이 이를 가리키면 CC가 연결에 실패합니다. systemd 서비스 또는 상태 확인 래퍼 스크립트로 실행하는 것을 권장합니다.
270
+ - **초과 TTL 다운그레이드** — 5시간 쿼터 100% 초과 시 서버가 TTL을 1h에서 5m으로 강제 다운그레이드합니다. 서버 측 결정이므로 클라이언트에서 수정할 수 없습니다. 프록시/인터셉터는 초과 상태로 밀어넣을 수 있는 캐시 불안정을 사전에 방지합니다.
271
+ - **마이크로컴팩트 방지 불가** — 모니터링은 컨텍스트 열화를 감지할 수 있지만 방지할 수는 없습니다. 마이크로컴팩트와 예산 집행은 클라이언트 비활성화 옵션이 없는 GrowthBook 플래그를 통한 서버 제어입니다.
272
+ - **시스템 프롬프트 재작성은 실험적입니다** — 프리로드 전용, 선택적. 커뮤니티 보고에서 논의된 동작 차이의 원인으로 입증되지 않았습니다. 사용자 책임하에 사용하십시오.
274
273
  - **버전 결합** — 핑거프린트 salt와 블록 감지 휴리스틱은 Claude Code 내부 구현에서 파생됩니다. 대규모 리팩토링 시 이 패키지 업데이트가 필요할 수 있습니다.
275
274
 
276
- ## 관련 이슈
275
+ ## 추적 이슈
277
276
 
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` 시스템 프롬프트 변경 커뮤니티 논의
277
+ 캐시, 쿼터, 컨텍스트 버그와 관련된 30개 이상의 upstream Claude Code 이슈를 모니터링하고 있습니다. 전체 목록, 관여 현황, 커뮤니티 리서치, 주요 기여자는 [TRACKED_ISSUES.md](TRACKED_ISSUES.md)를 참조하십시오.
283
278
 
284
279
  ## 관련 리서치
285
280
 
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 실시간 모니터링
281
+ - **[@ArkNill/claude-code-hidden-problem-analysis](https://github.com/ArkNill/claude-code-hidden-problem-analysis)** — 38,996건 프록시 기반 분석: 7개 버그(마이크로컴팩트, 예산 상한, 가상 속도 제한기, JSONL 중복, 확장 사고), GrowthBook 기능 플래그 인과 테스트, Opus 4.7 소모율 주의보. v1.1.0 모니터링 기능은 이 리서치에 기반합니다.
282
+ - **[@Renvect/X-Ray-Claude-Code-Interceptor](https://github.com/Renvect/X-Ray-Claude-Code-Interceptor)** — 실시간 대시보드가 있는 진단용 HTTPS 프록시, 시스템 프롬프트 섹션 비교, 도구별 제거 임계값. `ANTHROPIC_BASE_URL`을 지원하는 모든 Claude 클라이언트에서 동작합니다.
283
+ - **[@fgrosswig/claude-usage-dashboard](https://github.com/fgrosswig/claude-usage-dashboard)** — 셀프 호스팅 포렌식 대시보드, SSE 실시간 모니터링, 멀티호스트 집계, 캐시 건전성 점수화. 프록시의 관점과 상호 보완적입니다. 연동 설정은 [docs/dashboard-integration.md](docs/dashboard-integration.md)를 참조하십시오.
284
+
285
+ ## 프로덕션 사용
286
+
287
+ - **[Crunchloop DAP](https://dap.crunchloop.ai)** — Agent SDK / DAP 개발 환경. 팀 전체 배포를 위해 인터셉터를 트렁크에 머지한 최초의 프로덕션 팀(2026-04-10). 실제 테스트를 통해 두 가지 캐시 회귀 패턴(도구 순서 흔들림, 새 세션 정렬 갭)을 식별하고, v1.5.1과 v1.6.2 수정을 이끈 디버그 트레이스를 기여했습니다.
289
288
 
290
289
  ## 기여자
291
290
 
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` 통합 경로 발견
291
+ - **[@VictorSun92](https://github.com/VictorSun92)** — v2.1.88 최초 monkey-patch 수정, v2.1.90에서 부분 분산 식별, 전방 스캔 감지, 올바른 블록 순서, 더 엄격한 블록 매처, 선택적 출력 효율성 재작성 훅 기여
292
+ - **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — Agent SDK / DAP 프로덕션 환경 검증, 1시간 캐시 TTL 확인, 디버그 트레이스를 통한 도구 순서 흔들림 발견(v1.5.1에서 수정), SKILLS SORT 진단을 통한 새 세션 정렬 버그 발견(v1.6.2에서 수정). 인터셉터를 트렁크에 배포한 최초의 프로덕션 팀
293
+ - **[@jmarianski](https://github.com/jmarianski)** — MITM 프록시 캡처 및 Ghidra 역공학을 통한 근본 원인 분석, 다중 모드 캐시 테스트 스크립트
294
+ - **[@cnighswonger](https://github.com/cnighswonger)** — 핑거프린트 안정화, 도구 순서 수정, 이미지 제거, 모니터링 기능, 초과 TTL 다운그레이드 발견, 프록시 아키텍처, 패키지 관리자
295
+ - **[@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) 리서치
296
+ - **[@Renvect](https://github.com/Renvect)** — 이미지 중복 발견, 프로젝트 디렉토리 오염 분석
297
+ - **[@fgrosswig](https://github.com/fgrosswig)** — [claude-usage-dashboard](https://github.com/fgrosswig/claude-usage-dashboard) 포렌식 방법론: 비용 팩터 오버헤드 비율 메트릭, `anthropic-*` 헤더 캡처 패턴, 대시보드 연동 레이어에 참고가 된 프록시 NDJSON 스키마
298
+ - **[@TomTheMenace](https://github.com/TomTheMenace)** — Windows `.bat` 래퍼, 최초 Windows 플랫폼 검증(7.5시간/536호출 Opus 4.6 세션, 98.4% 캐시 히트율)
299
+ - **[@arjansingh](https://github.com/arjansingh)** — 동적 `npm root -g` 경로 해석이 있는 nvm 호환 래퍼 스크립트(PR #15)
300
+ - **[@beekamai](https://github.com/beekamai)** — npm root에 공백이 포함된 경우의 Windows URL 인코딩 수정(PR #17)
301
+ - **[@JEONG-JIWOO](https://github.com/JEONG-JIWOO)** — VS Code 확장 조사: `claudeCode.claudeProcessWrapper`를 동작하는 통합 경로로 발견, Windows용 C 래퍼 작성(#16)
302
+ - **[@X-15](https://github.com/X-15)** — VS Code 확장 검증, v2.1.105에서 안전 검사 동작을 확인한 수정별 상태 분석(#16)
303
+ - **[@deafsquad](https://github.com/deafsquad)** — 범용 smoosh_split un-smoosh 수정(PR #26), 세션 재개 분산 버그의 소스 수준 함수 귀속(anthropics/claude-code#43657), OTEL 텔레메트리 발견, v3.0.0 프록시 아키텍처 제안 및 구축
304
+
305
+ 이 이슈들에 대한 커뮤니티 노력에 기여하셨는데 여기에 이름이 없다면, 이슈 또는 PR을 열어주십시오 — 모든 분을 올바르게 크레딧하고 싶습니다.
306
+
307
+ ## 지원
308
+
309
+ 이 도구가 비용 절감에 도움이 되었다면, 커피 한 잔 사주시는 것을 고려해 주십시오:
310
+
311
+ <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>
300
312
 
301
313
  ## 라이선스
302
314
 
package/README.md CHANGED
@@ -6,7 +6,7 @@ English | [中文](./README.zh.md) | [한국어](./README.ko.md) | [Português](
6
6
 
7
7
  Cache optimization proxy for [Claude Code](https://github.com/anthropics/claude-code). Fixes prompt cache bugs that cause excessive quota burn, stabilizes the request prefix, and monitors for silent regressions. Works with all CC versions including the v2.1.113+ Bun binary.
8
8
 
9
- > **v3.0.1** — Local HTTP proxy with 7 hot-reloadable extensions. A/B tested on v2.1.117: **95.5% cache hit rate through proxy vs 82.3% direct** on first warm turn. [Full release notes →](https://github.com/cnighswonger/claude-code-cache-fix/releases/tag/v3.0.0)
9
+ > **v3.0.3** — Local HTTP proxy with 7 hot-reloadable extensions. A/B tested on v2.1.117: **95.5% cache hit rate through proxy vs 82.3% direct** on first warm turn. [Full release notes →](https://github.com/cnighswonger/claude-code-cache-fix/releases/tag/v3.0.0)
10
10
 
11
11
  > **Opus 4.7 advisory:** Metered data shows 4.7 burns Q5h quota at **~2.4x the rate of 4.6** for equivalent visible token counts ([independently confirmed by @ArkNill](https://github.com/ArkNill/claude-code-hidden-problem-analysis/blob/main/16_OPUS-47-ADVISORY.md)). Two factors: a new tokenizer (up to 35% more tokens, [documented](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7)) and adaptive thinking overhead (~105%, not documented in usage response). The Q5h impact compounds into **Q7d** — the weekly quota ceiling that most heavy users will hit first. Workaround: `CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING=1` reduces burn by ~3.3x but may reduce quality on complex tasks. See [Discussion #25](https://github.com/cnighswonger/claude-code-cache-fix/discussions/25) (initial observation) and [Discussion #42](https://github.com/cnighswonger/claude-code-cache-fix/discussions/42) (controlled A/B data + Q7d analysis).
12
12
 
@@ -89,6 +89,29 @@ curl http://127.0.0.1:9801/health
89
89
  # {"status":"ok"}
90
90
  ```
91
91
 
92
+ ### Corporate environments (proxies, custom CAs)
93
+
94
+ The proxy honors the following environment variables when forwarding to `api.anthropic.com`. Behind Zscaler / Netskope / Forcepoint / Bluecoat / corporate squid, set these in the proxy's environment.
95
+
96
+ | Variable | Effect |
97
+ |----------|--------|
98
+ | `HTTPS_PROXY` / `HTTP_PROXY` (and lowercase variants) | Routes upstream requests through the corporate HTTP CONNECT proxy. |
99
+ | `NO_PROXY` | Comma-separated host list to bypass the proxy. Supports `*` and `.suffix.example.com`. |
100
+ | `CACHE_FIX_PROXY_CA_FILE` | Path to a PEM file with one or more extra CA certificates (for SSL-inspecting proxies). |
101
+ | `NODE_EXTRA_CA_CERTS` | Standard Node mechanism — also honored. |
102
+ | `CACHE_FIX_PROXY_REJECT_UNAUTHORIZED=0` | **Insecure escape hatch.** Disables TLS verification. Use only as a last resort while you wait for IT to provide the corp CA bundle. |
103
+
104
+ Example (Windows PowerShell):
105
+
106
+ ```powershell
107
+ $env:HTTPS_PROXY = 'http://proxy.corp.example:8080'
108
+ $env:NO_PROXY = 'localhost,127.0.0.1,.corp.example'
109
+ $env:CACHE_FIX_PROXY_CA_FILE = 'C:\corp\zscaler-root.pem'
110
+ node "$(npm root -g)\claude-code-cache-fix\proxy\server.mjs"
111
+ ```
112
+
113
+ Stderr will print `[upstream] using proxy http://proxy.corp.example:8080 ...` on first request when the agent is wired correctly. With no proxy/CA env vars set, behavior is unchanged from earlier versions (Node default agent, system trust store).
114
+
92
115
  ## Quick Start: Preload (CC v2.1.112 and earlier)
93
116
 
94
117
  If you're on a Node.js-based CC version (v2.1.112 or earlier), the preload interceptor works without a proxy:
package/README.zh.md CHANGED
@@ -1,297 +1,255 @@
1
1
  # claude-code-cache-fix
2
2
 
3
- [English](./README.md) | 中文
3
+ [![npm](https://img.shields.io/npm/v/claude-code-cache-fix?color=blue)](https://www.npmjs.com/package/claude-code-cache-fix) [![Node.js](https://img.shields.io/badge/Node.js-18%2B-green)](https://nodejs.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow)](https://opensource.org/licenses/MIT) [![GitHub stars](https://img.shields.io/github/stars/cnighswonger/claude-code-cache-fix)](https://github.com/cnighswonger/claude-code-cache-fix/stargazers)
4
4
 
5
- 修复 [Claude Code](https://github.com/anthropics/claude-code) 中导致恢复会话时**成本增加高达 20 倍**的提示缓存回归问题,同时监控静默上下文降级。已在 v2.1.92 v2.1.97 上验证。
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
- 2. **指纹不稳定** `cc_version` 指纹(如 `2.1.92.a3f`)是根据 `messages[0]` 的内容计算的,包括元数据/附件块。当这些块发生偏移时,指纹改变,系统提示改变,缓存失效。
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
- 此外,通过 Read 工具读取的图片会以 base64 形式持久化在对话历史中,在每次后续 API 调用时一并发送,悄然增加 token 成本。
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
- 需要 Node.js >= 18,且 Claude Code 通过 npm 安装(非独立二进制文件)。
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
- if [ ! -f "$CLAUDE_NPM_CLI" ]; then
42
- echo "Error: Claude Code npm package not found at $CLAUDE_NPM_CLI" >&2
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
- exec env NODE_OPTIONS="--import claude-code-cache-fix" node "$CLAUDE_NPM_CLI" "$@"
24
+ # 通过代理启动 Claude Code
25
+ ANTHROPIC_BASE_URL=http://127.0.0.1:9801 claude
48
26
  ```
49
27
 
50
- ```bash
51
- chmod +x ~/bin/claude-fixed
52
- ```
28
+ 就这样。代理会自动应用所有 7 个缓存修复扩展。无需包装脚本、`NODE_OPTIONS` 或预加载。
53
29
 
54
- 如果你的 npm 全局前缀不同,请相应调整 `CLAUDE_NPM_CLI`。使用以下命令查找:
30
+ ### 代理的工作方式
55
31
 
56
- ```bash
57
- npm root -g
58
- ```
32
+ 每个 `/v1/messages` 请求都会按顺序执行 7 个扩展:
59
33
 
60
- ### 方式 B:Shell 别名
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
- ```bash
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
- ### 方式 C:直接调用
46
+ ### 作为服务运行
67
47
 
68
- ```bash
69
- NODE_OPTIONS="--import claude-code-cache-fix" claude
70
- ```
48
+ **Linux(systemd — 推荐):**
71
49
 
72
- > **注意**:仅在 `claude` 指向 npm/Node 安装时有效。独立二进制文件使用不同的执行路径,会绕过 Node.js 预加载。
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
- 模块在 Claude Code 向 `/v1/messages` 发起 API 调用前拦截 `globalThis.fetch`。每次调用时:
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
- 1. **扫描所有用户消息**中的附件块(技能、MCP、延迟工具、钩子),将每种类型的最新版本移回 `messages[0]`,匹配全新会话的布局
79
- 2. **按名称字母顺序排列工具定义**,确保确定性排序
80
- 3. **重新计算 cc_version 指纹**,基于真实用户消息文本而非元数据/附件内容
64
+ [Install]
65
+ WantedBy=default.target
66
+ ```
81
67
 
82
- 所有修复都是幂等的 — 如果无需修复,请求将原样传递。拦截器对你的对话是只读的;它只在请求到达 API 之前规范化请求结构。
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
- 通过 Read 工具读取的图片以 base64 编码存储在对话历史的 `tool_result` 块中。它们会**在每次后续 API 调用中**随行发送,直到压缩。单张 500KB 的图片每轮带来约 62,500 token 的额外开销。
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
- export CACHE_FIX_IMAGE_KEEP_LAST=3
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
- 这将保留最近 3 条用户消息中的图片,并将较早的替换为文本占位符。仅针对 `tool_result` 块(Read 工具输出)中的图片 — 用户粘贴的图片不受影响。文件仍保留在磁盘上,需要时可重新读取。
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
- export CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT=$'# Output efficiency\n\n...'
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
- ```text
124
- # Output efficiency
94
+ 代理在转发到 `api.anthropic.com` 时支持以下环境变量。在 Zscaler / Netskope / Forcepoint / Bluecoat / 企业 squid 等环境下,在代理的环境中设置这些变量。
125
95
 
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.
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
- 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.
104
+ ## 快速开始:预加载(CC v2.1.112 及更早版本)
129
105
 
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.
106
+ 如果使用基于 Node.js CC 版本(v2.1.112 或更早),预加载拦截器无需代理即可工作:
131
107
 
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.
108
+ ```bash
109
+ npm install -g claude-code-cache-fix
110
+ NODE_OPTIONS="--import claude-code-cache-fix" claude
135
111
  ```
136
112
 
137
- </details>
113
+ > **注意:** 预加载不适用于 CC v2.1.113+(Bun 二进制文件)。请使用上述代理方式。
138
114
 
139
- <details>
140
- <summary>公开 / 默认 Claude Code 版本</summary>
115
+ 包装脚本、Shell 别名、Windows 说明和 VS Code 预加载模式集成请参见 [docs/preload-setup.md](docs/preload-setup.md)。
141
116
 
142
- ```text
143
- # Output efficiency
117
+ ## VS Code 扩展
144
118
 
145
- IMPORTANT: Go straight to the point. Try the simplest approach first without going in circles. Do not overdo it. Be extra concise.
119
+ [VS Code 扩展](https://github.com/cnighswonger/claude-code-cache-fix-vscode)(v0.5.0)支持代理和预加载两种模式:
146
120
 
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
- ```
121
+ **代理模式(推荐):**
122
+ 1. 启动代理(见上文)
123
+ 2. VS Code 命令面板中:**Claude Code Cache Fix: Enable Proxy Mode**
124
+ 3. 重启任何活跃的 Claude Code 会话
156
125
 
157
- </details>
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
- <details>
160
- <summary>自定义替换示例(结合上面两版的折中版本)</summary>
132
+ 手动 VS Code 包装器设置(不使用 VSIX)请参见 [docs/preload-setup.md](docs/preload-setup.md#vs-code-preload-mode)。
161
133
 
162
- ```text
163
- # Output efficiency
134
+ ## 安全模型
164
135
 
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.
136
+ > **代理和拦截器对 API 请求和响应具有完全读写访问权限。** 这是该方法固有的特性 任何 fetch 拦截器、代理或网关都处于这个位置。
166
137
 
167
- Keep user-facing text clear, direct, and reasonably concise. Lead with the answer or action. Skip filler, repetition, and unnecessary preamble.
138
+ **它做什么:** 修改出站请求结构(块排序、指纹、TTL、git-status)以修复缓存 bug。读取响应头和 SSE 使用量数据用于监控。
168
139
 
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.
140
+ **它不做什么:** 代理或拦截器不会发起网络调用。所有遥测数据写入 `~/.claude/` 下的本地文件。除非你明确选择加入 [claude-code-meter](https://github.com/cnighswonger/claude-code-meter) 共享(独立包,需要交互式同意),否则数据不会离开你的机器。
170
141
 
171
- These instructions apply to user-facing text only. They do not apply to investigation, code reading, tool use, or verification.
142
+ **供应链:** 代理模式:7 个小型扩展模块在 `proxy/extensions/` 中(每个不到 200 行)。预加载模式:单个未压缩文件(`preload.mjs`,约 1,700 行)。一个开发依赖(`zod`,仅用于测试中的模式验证)。安装前请审查代码。npm 出处(provenance)将每个发布版本链接到其源代码提交。
172
143
 
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.
144
+ **独立审计:** @TheAuditorTool [评估为"合法工具"](https://github.com/anthropics/claude-code/issues/38335#issuecomment-4244413605)(2026-04-14)。
174
145
 
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.
146
+ ## 问题描述
176
147
 
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
- ```
148
+ 当你在 Claude Code 中使用 `--resume` `/resume` 时,提示缓存会静默失效。API 不再读取已缓存的 token(廉价),而是每一轮都从头重建(昂贵)。原本每小时约 $0.50 的会话可能在无任何提示的情况下飙升至 $5-10/小时。
179
149
 
180
- </details>
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
- Claude Code 通过服务器控制机制(GrowthBook 标志)静默替换旧的工具结果为 `[Old tool result content cleared]`。200,000 字符的聚合上限和每工具上限(Bash: 30K, Grep: 20K)会截断较早的结果且无通知。
158
+ 此外,通过 Read 工具读取的图片以 base64 形式持久化在对话历史中,在每次后续 API 调用时一并发送,悄然增加 token 成本。
189
159
 
190
- 拦截器检测已清除的工具结果并记录计数。当总工具结果字符数接近 200K 阈值时,会记录警告。
160
+ ## 工作原理
191
161
 
192
- ### 虚假速率限制器
162
+ **代理模式**(v3.0.0+):一个位于 `localhost:9801` 的 HTTP 服务器拦截 `POST /v1/messages` 请求。七个扩展模块通过流水线处理每个请求 — 规范化块排序、剥离指纹、稳定工具排序、管理 TTL 标记。扩展是可热重载的 `.mjs` 文件,通过 `proxy/extensions.json` 配置。所有其他流量原样传递。
193
163
 
194
- 客户端可以在不发起 API 调用的情况下生成合成的 "Rate limit reached" 错误,可通过 `"model": "<synthetic>"` 识别。拦截器会记录这些事件。
164
+ **预加载模式**(v2.x):一个 Node.js `--import` 模块,在 Claude Code 发起 API 调用前修补 `globalThis.fetch`。应用相同的修复 — 扫描用户消息中的迁移块、排序工具、重新计算指纹、注入 TTL 标记。
195
165
 
196
- ### 配额追踪
166
+ 两种模式都是幂等的 — 如果无需修复,请求原样传递。两种模式都不会修改你的对话;它们只在请求到达 API 之前规范化请求结构。
197
167
 
198
- 解析响应头中的 `anthropic-ratelimit-unified-5h-utilization` `7d-utilization`,保存到 `~/.claude/quota-status.json`,供状态栏钩子或其他工具使用。
168
+ ## 状态栏 实时配额警告
199
169
 
200
- ### 高峰时段检测
170
+ 代理和预加载模式都会在每次 API 调用时将配额状态写入 `~/.claude/quota-status.json`。内置的 `tools/quota-statusline.sh` 脚本显示实时状态栏:
201
171
 
202
- Anthropic 在工作日高峰时段(UTC 13:00-19:00,周一至周五)会提高配额消耗速率。拦截器检测高峰窗口并将 `peak_hour: true/false` 写入 `quota-status.json`。详见 `docs/peak-hours-reference.md`。
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
- 拦截器将每次调用的使用数据记录到 `~/.claude/usage.jsonl` 每次 API 调用一行 JSON,包含模型、token 计数和缓存明细。使用内置的成本报告工具分析费用:
181
+ Claude Code 在每次调用时将实时 `git status` 注入系统提示。任何文件编辑都会改变 git status,从而使整个前缀缓存失效。禁用此功能每次调用可节省约 1,800 token
207
182
 
208
183
  ```bash
209
- node tools/cost-report.mjs # 从拦截器日志查看今日费用
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
- 同样适用于任何包含 Anthropic 使用量字段的 JSONL(`--file`、stdin)— 适合 SDK 用户和代理设置。支持文本、JSON Markdown 输出格式。详见 `docs/cost-report.md`。
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
- CACHE_FIX_DEBUG=1 claude-fixed
194
+ export CACHE_FIX_IMAGE_KEEP_LAST=3
223
195
  ```
224
196
 
225
- 日志写入 `~/.claude/cache-fix-debug.log`。重点关注:
197
+ ## 系统提示重写(预加载模式,可选)
226
198
 
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` 段
199
+ 拦截器可重写 Claude Code `# Output efficiency` 系统提示段落。默认禁用。使用 `CACHE_FIX_OUTPUT_EFFICIENCY_REPLACEMENT` 启用。三种已知提示变体及使用说明请参见 [docs/output-efficiency-prompts.md](docs/output-efficiency-prompts.md)。
241
200
 
242
- ### Prefix diff mode
201
+ ## 监控与诊断
243
202
 
244
- 启用跨进程前缀快照差异对比,以诊断重启后的缓存失效:
203
+ 预加载拦截器包含对微压缩降级、虚假速率限制器、GrowthBook 标志状态、使用量遥测和成本报告的监控。配额追踪在代理和预加载模式下均通过 `~/.claude/quota-status.json` 工作。
245
204
 
246
- ```bash
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
- | `CACHE_FIX_DEBUG` | `0` | 启用调试日志 |
257
- | `CACHE_FIX_PREFIXDIFF` | `0` | 启用前缀快照差异对比 |
258
- | `CACHE_FIX_IMAGE_KEEP_LAST` | `0` | 保留最近 N 条用户消息中的图片(0 = 禁用) |
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
- - **仅支持 npm 安装** 独立 Claude Code 二进制文件具有 Zig 级别的证明机制,会绕过 Node.js。本修复仅适用于 npm 包(`npm install -g @anthropic-ai/claude-code`)。
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
- - [#34629](https://github.com/anthropics/claude-code/issues/34629) — 恢复缓存回归的原始报告
273
- - [#40524](https://github.com/anthropics/claude-code/issues/40524) — 会话内指纹失效,图片持久化
274
- - [#42052](https://github.com/anthropics/claude-code/issues/42052) — 社区拦截器开发,TTL 降级发现
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 开发环境。首个将本拦截器合入 trunk 并团队级部署的生产团队(2026-04-10)。通过真实环境测试发现两类不同的缓存回归问题——工具排序抖动与 fresh-session 排序漏洞,并贡献了驱动 v1.5.1 v1.6.2 修复的调试日志。
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)** — 原始 v2.1.88 monkey-patch 修复作者,识别出 v2.1.90 中的部分块散布问题,并贡献了前向扫描检测、正确的块排序、更严格的块匹配器,以及可选的 output-efficiency 重写 hook
287
- - **[@bilby91](https://github.com/bilby91)** ([Crunchloop DAP](https://dap.crunchloop.ai)) — Agent SDK / DAP 生产环境验证、1h 缓存 TTL 确认、通过调试日志发现工具排序抖动(v1.5.1 修复)、通过 SKILLS SORT 诊断发现 fresh-session 排序 bug(v1.6.2 修复)。首个将本拦截器合入 trunk 的生产团队。
288
- - **[@jmarianski](https://github.com/jmarianski)** — 通过 MITM 代理抓包和 Ghidra 逆向分析定位根因,并提供多模式缓存测试脚本
289
- - **[@cnighswonger](https://github.com/cnighswonger)** — 指纹稳定化、工具顺序修复、图片剥离、监控功能、超额 TTL 降级发现,本包维护者
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
- 如果你参与了这些问题的社区协作但尚未被列出,欢迎开 issue PR,我们希望正确致谢每一位贡献者。
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.2",
3
+ "version": "3.0.3",
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,6 +1,8 @@
1
1
  import https from "node:https";
2
2
  import http from "node:http";
3
3
  import { URL } from "node:url";
4
+ import { readFileSync } from "node:fs";
5
+ import { HttpProxyAgent, HttpsProxyAgent } from "hpagent";
4
6
  import config from "./config.mjs";
5
7
 
6
8
  const STRIP_REQUEST_HEADERS = new Set([
@@ -46,6 +48,105 @@ function filterResponseHeaders(rawHeaders) {
46
48
  return headers;
47
49
  }
48
50
 
51
+ // --- HTTP proxy and custom CA support ---
52
+
53
+ const _agents = new Map(); // cache key → Agent | null
54
+ const _loggedProxies = new Set(); // dedupe stderr "using proxy" lines per (url, isHTTPS)
55
+ let _warnedTlsDisabled = false;
56
+
57
+ function shouldBypassProxy(hostname) {
58
+ if (!config.noProxy) return false;
59
+ const list = config.noProxy.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
60
+ const host = hostname.toLowerCase();
61
+ for (const pattern of list) {
62
+ if (pattern === "*") return true;
63
+ if (pattern.startsWith(".")) {
64
+ // ".example.com" matches "foo.example.com" and "example.com"
65
+ const bare = pattern.slice(1);
66
+ if (host === bare || host.endsWith(pattern)) return true;
67
+ } else if (host === pattern || host.endsWith("." + pattern)) {
68
+ return true;
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+
74
+ function loadCa() {
75
+ if (!config.caFile) return undefined;
76
+ try {
77
+ return readFileSync(config.caFile);
78
+ } catch (err) {
79
+ process.stderr.write(`[upstream] CACHE_FIX_PROXY_CA_FILE read failed: ${err.message}\n`);
80
+ return undefined;
81
+ }
82
+ }
83
+
84
+ // Pick the proxy URL for an upstream, matching curl/Python/Go semantics:
85
+ // https upstream → HTTPS_PROXY, falling back to HTTP_PROXY if unset
86
+ // http upstream → HTTP_PROXY only (HTTPS_PROXY does NOT apply to plain HTTP)
87
+ //
88
+ // Exported for direct unit testing — tests against the live forwardRequest path
89
+ // can't easily reload a fresh config across cases (config is a single module
90
+ // instance), so we expose the pure function for table-driven coverage.
91
+ export function selectProxyUrl(isHTTPS) {
92
+ if (isHTTPS) return config.httpsProxy || config.httpProxy || "";
93
+ return config.httpProxy || "";
94
+ }
95
+
96
+ function buildAgent(isHTTPS, proxyUrl) {
97
+ const ca = loadCa();
98
+ if (proxyUrl) {
99
+ const opts = {
100
+ keepAlive: true,
101
+ proxy: proxyUrl,
102
+ rejectUnauthorized: config.rejectUnauthorized,
103
+ ...(ca ? { ca } : {}),
104
+ };
105
+ return isHTTPS ? new HttpsProxyAgent(opts) : new HttpProxyAgent(opts);
106
+ }
107
+ // No proxy. Only build a custom agent when CA or insecure mode warrants it;
108
+ // otherwise return null so Node uses its global default agent (preserves the
109
+ // pre-change behavior end-to-end, including connection pooling).
110
+ if (ca || !config.rejectUnauthorized) {
111
+ if (isHTTPS) {
112
+ return new https.Agent({
113
+ keepAlive: true,
114
+ rejectUnauthorized: config.rejectUnauthorized,
115
+ ...(ca ? { ca } : {}),
116
+ });
117
+ }
118
+ return new http.Agent({ keepAlive: true });
119
+ }
120
+ return null;
121
+ }
122
+
123
+ function getAgent(isHTTPS, hostname) {
124
+ if (!_warnedTlsDisabled && !config.rejectUnauthorized) {
125
+ _warnedTlsDisabled = true;
126
+ process.stderr.write(
127
+ `[upstream] WARNING: TLS verification disabled (CACHE_FIX_PROXY_REJECT_UNAUTHORIZED=0). This is insecure!\n`
128
+ );
129
+ }
130
+
131
+ const bypass = shouldBypassProxy(hostname);
132
+ const proxyUrl = bypass ? "" : selectProxyUrl(isHTTPS);
133
+ const cacheKey = `${isHTTPS ? "https" : "http"}|${proxyUrl}|${config.caFile}|${config.rejectUnauthorized}`;
134
+
135
+ let agent = _agents.get(cacheKey);
136
+ if (agent === undefined) {
137
+ agent = buildAgent(isHTTPS, proxyUrl);
138
+ _agents.set(cacheKey, agent);
139
+ if (proxyUrl && !_loggedProxies.has(`${proxyUrl}|${isHTTPS}`)) {
140
+ _loggedProxies.add(`${proxyUrl}|${isHTTPS}`);
141
+ process.stderr.write(
142
+ `[upstream] using proxy ${proxyUrl} for ${isHTTPS ? "https" : "http"} upstream ` +
143
+ `(rejectUnauthorized=${config.rejectUnauthorized}, ca=${config.caFile || "default"})\n`
144
+ );
145
+ }
146
+ }
147
+ return agent;
148
+ }
149
+
49
150
  export function forwardRequest(clientReq, body, signal) {
50
151
  return new Promise((resolve, reject) => {
51
152
  const upstreamUrl = new URL(clientReq.url, config.upstream);
@@ -66,6 +167,7 @@ export function forwardRequest(clientReq, body, signal) {
66
167
  method: clientReq.method,
67
168
  headers,
68
169
  timeout: config.timeout,
170
+ agent: getAgent(isHTTPS, upstreamUrl.hostname),
69
171
  };
70
172
 
71
173
  const upstreamReq = transport.request(options, (upstreamRes) => {