Repository navigation
v0.4.13
Changes:
- Split
collectUsageintocollectUsageOnce+ a serializing wrapper that chains per-session onSessionState.collectChain. A second idle now re-reads the cursor only after the first advances it: no double count, no lost follow-up. The wrapper's .catch keeps the fire-and-forget voidcollectUsage(...)caller free of unhandled rejections. Message ordering/pruning/cumulative/error behavior unchanged. s.collectChain = s.collectChain.then(() => collectUsageOnce(sid, s)).catch(handler). Two independent layers guarantee recovery:collectUsageOncecatches internally (records a bounded telemetry-error), and the outer.catchreturns a resolved promise and reassigns it tocollectChain. So a rejected collection can never wedge the chain for that session.- Cache-options telemetry now reads
output.options[fieldNames.options], so OpenRouter reports{mode:"implicit",ttl:"30m"}instead of {}. Injection behavior unchanged; schema unchanged. gptCacheOptionFieldNamesnormalizes withString(providerID ?? "").trim().toLowerCase(), matching the F5 affinity-gate boundary. Canonical openrouter still → snake_case; openai/azure/zai/xiaomi → camelCase; policy resolution untouched.scanPagereturns the newest counted assistant's providerID/modelID; usage attribution prefers it, thens.mimoProvider, then the latcheds.modelInfo.- Usage
keyStrategynow followscacheRootKey("cache-root" vs "session"), matching the cache-options convention. - Tool-fetch TTL is keyed by provider\model (
lastToolFetchKey), so a title/small-model request no longer reuses another model's fingerprints.
The previous 2,048-entry FIFO cap could theoretically evict a still-relevant tombstone if ≥2,048 other sessions were deleted while queued work was pending. Hardened three ways:
- Queued work is now bound-independent.
collectUsageOnce(sid, s)receives the exactSessionStateand no longer callsget(); it checkss.deleted. Queued/in-flight work therefore cannot recreate map state regardless of the tombstone store. Thesession.deletedhandler setss.deleted = trueon the removed object first. - Tombstones are time-based.
deletedSessionsis nowMap<sid, deletedAt>with a 10-minute TTL and an 8,192 hard cap; the handler sweeps expired entries before any count-based eviction, so a fresh tombstone is never evicted by count pressure within the TTL (queued/idle work drains in ms). - Cleared only on a genuine new request.
rememberModel(called fromchat.params/system.transform) deletes the tombstone; nothing else clears it.
The initial single-key version only remembered the most-recent identity, so TTL was not enforced per identity (A→B→A refetched A). Now toolFetchAt: Map<provider\0model, ts> with TOOL_FETCH_CACHE_CAP = 64:
- Independent TTL per identity — each identity has its own window; A→B→A suppresses the second A.
- Bounded — at most one tool.list per distinct identity per 1.5 s window; ≤64 entries per session (oldest evicted). Worst case is one fetch per request when identities alternate beyond the cap or after TTL, i.e. bounded by request rate.
Detailed changelog:
- docs: add tracked research findings for harness and transport
- docs: require storing external research findings before web search
- fix: normalize provider id for GPT field names and expose assistant identity in scanPage
- fix: serialize usage collection, bound deleted-session state, and correct usage/tool telemetry
- test: cover usage concurrency, option telemetry, normalization, and attribution
- docs: record OpenCode plugin client throwOnError behavior
Full Changelog: v0.4.12...v0.4.13
Provider-aware prompt-cache optimization and observability for OpenCode.
opencode-cache-engine is an OpenCode npm plugin with two targets:
- Server target — the actual cache-engine runtime and provider policies.
- TUI target — registration with OpenCode's TUI plugin manager.
CacheEngine is an OpenCode plugin designed for long-running agent sessions where prompt-cache efficiency affects both latency and cost. It keeps the harness conservative for providers whose cache behavior is already automatic, while applying provider-specific optimizations where the provider exposes useful cache controls or where prompt structure can be safely improved.
The plugin currently has four cache-policy families:
- DeepSeek — passive cache observability; request structure is preserved.
- GPT-5.6 and later — documented cache-key/options metadata, with prompt text
unchanged. GPT-6 and future 5.6+/6+/7+ versions resolve through the same
documented boundary. - GLM-5.3 and later — GLM implicit-cache baseline and diagnostics; GLM-5.3 additionally uses a narrow, content-preserving
<env>relocation overlay. - MiMo V2.6 and later — MiMo implicit-cache baseline and diagnostics; MiMo V2.6 Flash/Pro additionally use a narrow, content-preserving
<env>relocation overlay.
Initial models were personally chosen for their performance in max programming task completed per dollar. More models can be added based on newer releases or updates.
Future releases will be based on newer versions of models from the above providers, as well as any model that performs optimally over the Pareto distribution on OpenRouter as well as on personal discretion. Since cache stability policy doesn't change much for the providers, there's no need for a code rebase.
Quick Installation (TUI):
opencode plugin opencode-cache-engine
Best complemented with agents for the particular models configured at AlexJoaquimPereira/opencode-agent-config