Skip to content

v0.4.13

Choose a tag to compare

@AlexJoaquimPereira AlexJoaquimPereira released this 03 Oct 09:26
· 329 commits to master since this release

Changes:

  • Split collectUsage into collectUsageOnce + a serializing wrapper that chains per-session on SessionState.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 void collectUsage(...) 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: collectUsageOnce catches internally (records a bounded telemetry-error), and the outer .catch returns a resolved promise and reassigns it to collectChain. 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.
  • gptCacheOptionFieldNames normalizes with String(providerID ?? "").trim().toLowerCase(), matching the F5 affinity-gate boundary. Canonical openrouter still → snake_case; openai/azure/zai/xiaomi → camelCase; policy resolution untouched.
  • scanPage returns the newest counted assistant's providerID/modelID; usage attribution prefers it, then s.mimoProvider, then the latched s.modelInfo.
  • Usage keyStrategy now follows cacheRootKey ("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 exact SessionState and no longer calls get(); it checks s.deleted. Queued/in-flight work therefore cannot recreate map state regardless of the tombstone store. The session.deleted handler sets s.deleted = true on the removed object first.
  • Tombstones are time-based. deletedSessions is now Map<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 from chat.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:

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