Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,22 @@

## [Unreleased]

## [4.1.0] - 2026-06-10

### Added

- **Optional `request_id` field on usage-log rows.** Sources from the upstream `request-id` response header. Gated default-off via `CACHE_FIX_USAGE_LOG_REQID=on` for the v4.1.0 release window. When enabled, every `~/.claude/usage.jsonl` row gains the field, recovering per-CC-session attribution that the proxy-boot-sticky `sid` field alone cannot provide. The field is the natural post-hoc join key against CC's per-session JSONL transcripts at `~/.claude/projects/<project>/<session-uuid>.jsonl` (which already carry `requestId` for every API call). **Cross-repo contract:** `claude-code-meter >= v0.5.0` is required to ingest rows that carry the field — older meter installs reject unknown keys via the strict-object schema. The gate is therefore default-off in this release and will flip default-on in v4.2.0 once meter has shipped. Schema stays at `v: 1` (pure addition; no consumer's reading of existing fields changes). See [`docs/directives/proxy-usage-log-request-id.md`](docs/directives/proxy-usage-log-request-id.md) for the full design and the release-ordering contract.
- **Optional `request_id` field on usage-log rows (#210).** Sources from the upstream `request-id` response header. Gated default-off via `CACHE_FIX_USAGE_LOG_REQID=on` for this release. When enabled, every `~/.claude/usage.jsonl` row gains the field, recovering per-CC-session attribution that the proxy-boot-sticky `sid` field alone cannot provide. The field is the natural post-hoc join key against CC's per-session JSONL transcripts at `~/.claude/projects/<project>/<session-uuid>.jsonl` (which already carry `requestId` for every API call). **Cross-repo contract:** `claude-code-meter >= v0.7.0` accepts the optional field; older meter installs reject unknown keys via the strict-object schema, hence the gate stays default-off in this release. The gate flips default-on in v4.2.0; operators upgrading to v4.2.0 must run claude-meter v0.7.0+. Schema stays at `v: 1` (pure addition; no consumer's reading of existing fields changes). See [`docs/directives/proxy-usage-log-request-id.md`](docs/directives/proxy-usage-log-request-id.md) for the full design and the release-ordering contract.
- **server.mjs debug logging (#190).** Opt-in per-request trace log via `CACHE_FIX_DEBUG=1`, written to `~/.claude/cache-fix-debug.log` (override path via `CACHE_FIX_DEBUG_LOG`). Captures route-level Claude → Proxy → Upstream traffic for operators debugging proxy behavior. Authorization / x-api-key / cookie / proxy-authorization headers are redacted at capture time (the log writer never touches raw header values). Dispatcher catches async handler rejections inside an awaited try/catch so promise rejections from `preForward()` or pipeline hooks no longer escape to `unhandledRejection`. The 500 fallback body is generic — no internal `error.message` echo. Contributed by [@nisqatsi](https://github.com/nisqatsi).
- **`tools/cache_analysis.py` reference helper (#138).** Python helper for reading the proxy's per-session quota-status files at `~/.claude/quota-status/sessions/<id>.json` (with v3.4.x fallback to `~/.claude/quota-status.json`). Now version-controlled in this repo and shipped via the existing `tools/` package.json entry. Closes part 1 of [`cnighswonger/claude-code-meter#22`](https://github.com/cnighswonger/claude-code-meter/issues/22) — the host-installed copy at `~/.claude/mcp/cache_analysis.py` had been silently returning `None` for 15 days post-v3.5.0 because the local helper lacked the new-path fallback.
- **install-service threads `CACHE_FIX_PROXY_CA_FILE` and `CACHE_FIX_PROXY_REJECT_UNAUTHORIZED` to the rendered unit (#189).** Corp-proxy and custom-CA configurations now survive install-service round-trips on both systemd and launchd. Hardens the systemd `Environment=` value escape against `%` (specifier expansion) and `\` (C-string unescape), and the launchd plist value escape against all five XML entities. Contributed by [@nisqatsi](https://github.com/nisqatsi).

### Fixed

- **Preserve base-path component in upstream URL forming (#188).** Configurations that chain cache-fix-proxy through another reverse proxy that mounts the Anthropic API under a path prefix (e.g., `https://corp.example/api/anthropic/v1/messages`) now forward correctly. Previously, the proxy concatenated only the upstream host with the inbound path, dropping the base-path component. Adds a pure `buildUpstreamUrl(base, clientUrl)` helper with 8 regression cases (no-path, trailing-slash, mirror, multi-segment, query strings, http+port). Contributed by [@nisqatsi](https://github.com/nisqatsi).

### Documentation

- **Scrub npm token location, org name, and rotation cadence from public docs (#208).** `docs/release-workflow.md` step 8 previously named the on-disk token path, the `vsitsllc` npm org, and the 90-day rotation cadence — the combination narrowed an attacker's search surface. Replaced both load-bearing references with "internal deployment notes" placeholders per CLAUDE.md "Public-Repo Information Hygiene". The historical disclosure remains in immutable git history at the prior PR refs (cannot be retracted); this PR stops further on-main propagation.

## [4.0.0] - 2026-06-07

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ The `usage-log` extension (opt-in via `proxy/extensions.json`) appends one JSON
| `overage_disabled_reason` | string ≤64 (optional) | overage-disabled-reason header |
| `cache_hit_rate` | float 0–1 | `cache_read_input_tokens / (input + cache_creation + cache_read)` |
| `q5h_delta`, `q7d_delta` | float | per-call delta from the previous row's q5h/q7d; 0 on first call after restart |
| `request_id` | string ≤64 (optional, gated) | upstream `request-id` response header. Default-off; enable with `CACHE_FIX_USAGE_LOG_REQID=on`. **Cross-repo gate:** older `claude-code-meter` installs reject unknown keys; the field is gated default-off until meter v0.5.0+ ships. |
| `request_id` | string ≤64 (optional, gated) | upstream `request-id` response header. Default-off; enable with `CACHE_FIX_USAGE_LOG_REQID=on`. **Cross-repo gate:** `claude-code-meter >= v0.7.0` accepts the optional field; older meter installs reject unknown keys via the strict-object schema. |

**Why `request_id` matters operationally.** The `sid` field is generated once at proxy boot and shared across every CC session that proxy serves. On hosts running multiple concurrent CC sessions through one proxy (common in agent fleets), every session's rows collapse into the same `sid` — there's no way to ask "which session burned 80% of today's Opus tokens?" from `usage.jsonl` alone. CC's per-session JSONL transcripts at `~/.claude/projects/<project>/<session-uuid>.jsonl` already carry `requestId` for every API call. Capturing the same value in the meter row makes the post-hoc join trivial:

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "claude-code-cache-fix",
"version": "4.0.0",
"version": "4.1.0",
"description": "Cache optimization proxy and interceptor for Claude Code. Fixes prompt cache bugs, stabilizes prefix, reduces quota burn.",
"type": "module",
"exports": {
Expand Down
12 changes: 6 additions & 6 deletions proxy/extensions/usage-log.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@
// CACHE_FIX_USAGE_LOG_REQID=on emits the optional `request_id` field
// (sourced from the upstream `request-id` response header). Default-off in
// v4.1.0 to avoid breaking unpatched claude-meter installs whose strict-
// object schema rejects unknown keys. Flips default-on in v4.2.0 once
// claude-meter v0.5.0+ ships the schema acceptance. The field is the
// post-hoc join key against CC's per-session JSONL transcripts
// object schema rejects unknown keys. claude-meter v0.7.0+ accepts the
// optional field; the v4.2.0 flip to default-on assumes that floor.
// The field is the post-hoc join key against CC's per-session JSONL
// transcripts
// (`~/.claude/projects/<project>/<session-uuid>.jsonl` carry `requestId`
// for every API call), which recovers per-CC-session attribution that
// `sid` alone cannot provide. See docs/directives/proxy-usage-log-request-id.md.
Expand Down Expand Up @@ -224,10 +225,9 @@ export function assembleRecord({ start, delta, quota, requestedModel, sid, prevQ
// claude-meter's strict-object validation.
// Env read happens per-call so operators can flip it at runtime without
// proxy restart, matching the image-strip debug-gate pattern.
// Cross-repo contract: claude-code-meter v0.5.0+ accepts this optional
// Cross-repo contract: claude-code-meter v0.7.0+ accepts this optional
// field; older meter installs reject rows that carry it, so the gate
// stays default-off until the meter side ships. Default flips on in
// cache-fix v4.2.0.
// stays default-off in v4.1.0. Default flips on in cache-fix v4.2.0.
if (
process.env.CACHE_FIX_USAGE_LOG_REQID === "on" &&
typeof requestId === "string" &&
Expand Down
Loading