feat(opencode): capture cache-diagnostics and usage cache accounting per request - #159
feat(opencode): capture cache-diagnostics and usage cache accounting per request#159iceteaSA wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Triaged all bot findings against the code; Fixed in
Refuted, with the code path:
Declined:
|
There was a problem hiding this comment.
All reported issues were addressed across 5 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
|
Second round was right on both counts —
|
…per request The plugin has sent cache-diagnosis-2026-04-07 in its beta list on every request without enabling the feature: the beta requires a request-body opt-in (diagnostics.previous_message_id), and nothing sent it or read the response. Turn the dormant channel on, measure-only. Request side: eligible OAuth requests gain the diagnostics opt-in inside the fail-closed rewriteRequestBody pipeline. The id sent is only ever one captured from a prior Anthropic response (bounded per-session tracker) - opencode mints its own msg_01-shaped ids for every provider, and a foreign id fails silently as previous_message_not_found. Response side: the existing SSE wrapper exposes message_start.message through a typed callback; per valid eligible response one MC-CACHE-DIAG single-line JSON record (contract v:1) is emitted via the logger with verbatim usage, TTL-bucket accounting, diag_state (absent|server_null|pending|populated), and populated cache_miss_reason.type. Short-gap previous_message_not_found logs a warn-level canary: the id capture broke, not a fingerprint expiry. Dumps: responses gain artifacts (status/id/model/usage/diagnostics, never content); CacheKeep prewarms are dumped tagged -prewarm-cachekeep- and emit records through the same chain, since any write resets the server TTL clock. Verified live: null(write=24831) -> null(read=24831, hit) -> system_changed(cache_missed_input_tokens=23963) on a forced system change against a warm prefix. Comparison engages only on cacheable requests (documented in README limitations). Closes cortexkit#157
7b45b0f to
fc2073b
Compare
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
…re fallback passthrough
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Closes #157.
The plugin has sent
cache-diagnosis-2026-04-07in its beta list on every request without ever enabling the feature: the beta requires a request-body opt-in (diagnostics.previous_message_id), and nothing sent it or read the response. This PR turns the dormant channel on, measure-only.What it does
Request side: eligible requests (OAuth routes whose beta set already includes the diagnosis beta) gain
"diagnostics": {"previous_message_id": <captured id | null>}inside the existing fail-closedrewriteRequestBodypipeline. API-key fallback routes are untouched. The id sent is only ever one captured from a prior Anthropic response — a bounded per-session tracker holds it. That distinction matters: opencode mints its ownmsg_01…-shaped message ids, and sending one of those fails silently asprevious_message_not_found, which the API also uses for benign fingerprint expiry. The tracker asserts identity (send-what-was-captured), never format.Response side: the existing SSE wrapper exposes
message_start.messagethrough a new typed callback (same pattern as the current fallback-outcome callback; emitted bytes untouched). Per valid eligible response, one machine-parseable log line is emitted through the existing logger:Values are verbatim from the response — no derived hit/miss flags, no normalization. The four documented diagnostics states (absent / null / comparison-pending / populated) stay distinct. Malformed or error envelopes never fabricate a record.
Dumps: when dumping is enabled, responses now get an artifact (status, id, model, usage, diagnostics — never content blocks), and CacheKeep prewarm requests are dumped too, tagged
-prewarm-cachekeep-in the filename andtagin metadata so traffic censuses can exclude keepalive noise. Prewarm responses also feed the same id chain — the server's TTL clock runs from the last write by any caller, so an unrecorded prewarm would silently reset it.Canary:
previous_message_not_foundon a short-gap turn is logged at warn — at short gaps the fingerprint can't plausibly have expired, so it means the id capture broke. This makes the one silent failure mode of the feature self-detecting.Verified against the live API
Chained smoke on a real cacheable prefix (~24.8K tokens):
One behavior worth knowing that the docs don't state: the comparison only engages on cacheable requests. A request below the model's cacheable minimum returns
diagnostics: nulleven when it genuinely changed — sonullmeans "not compared" unless cache activity is nonzero. Documented in the README limitations.Gates
bun run test1069 pass / 0 fail ·bun run typecheckclean ·bun run lintclean · e2e 26/27 — the one failure is the pre-existingtool-prefix.test.tsstale-Opus-bridge case, which fails identically on a cleanb1d8f8cworktree (verified side-by-side; unrelated to this diff).Pi behavior is unchanged (all new core params optional; call sites read to confirm, not just typechecked).
Relation to #155
Same file (
cachekeep.ts), no overlap: this branch deliberately carries none of #155's error-containment scaffolding, so the two merge independently in either order.Tests were written red-first throughout; the concurrency test binds two different predecessors across interleaved in-flight sessions specifically so a mutable-current-request regression cannot pass it.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Capture Anthropic prompt-cache diagnostics end to end and preserve provider response IDs across requests and CacheKeep prewarms without changing response delivery. Previously the beta header was sent but not opted in; eligible OAuth requests now include
diagnostics.previous_message_idand we emit versionedMC-CACHE-DIAGv1 records with usage and TTL buckets. Streaming is safer: we drain complete SSE frames, bound the pending buffer before fallback passthrough, and bypass SSE consumers in JSON mode with an 8 MiB diagnostics cap.Request/response: inject
diagnostics.previous_message_idonly on OAuth routes already sending thecache-diagnosis-2026-04-07beta; API‑key routes remain off. Track the last provider message ID per session, carry diagnostics across sticky retries, summarize TTL sent (1h/5m/null), and threadis_subagent. Exposemessage_start.messagevia the stream wrapper and emit oneMC-CACHE-DIAG v1record per eligible response (session, ts, model,is_subagent, TTL sent, usage, providermessage_id, sentprevious_message_id,diag_statein {absent,server_null,pending,populated}, optional miss fields). Parsecache_miss_reasonprecisely, warn on short‑gapprevious_message_not_found, ignore malformed envelopes. Bound streaming diagnostics pending, drain complete SSE frames before applying the cap and before fallback passthrough; cap JSON‑mode diagnostics at 8 MiB and bypass all SSE‑stateful consumers in JSON mode.Dumps/CacheKeep: write
.response.jsonartifacts with status/id/model/usage/diagnostics only.dumpDirectRequest/dumpRelayRequestreturn an optional handle anddumpResponseArtifactwrites the response artifact;sendViaRelayexposesonDumpCreated. CacheKeep prewarms are dumped and tagged-prewarm-cachekeep-and feed the same provider‑ID chain.CacheKeepManageradds optionalprepareBody/onResponsehooks and tracksisSubagent. ExportsetBoundedfrom@cortexkit/anthropic-auth-core.Migration
diag_state: "server_null"where"null"was expected.dumpDirectRequest/dumpRelayRequesttodumpResponseArtifact; relay callers can subscribe viaonDumpCreated. Existing callers continue to work unchanged.Written for commit 8d599a8. Summary will update on new commits.
Greptile Summary
This PR enables per-request Anthropic cache diagnostics, preserves provider response IDs across regular and CacheKeep requests, and adds sanitized response dump artifacts.
Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains.
Important Files Changed
Sequence Diagram
Reviews (9): Last reviewed commit: "fix(opencode): drain complete sse frames..." | Re-trigger Greptile