opencode-cache-engine 0.4.2 → 0.4.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.md CHANGED
@@ -74,7 +74,7 @@ The plugin deliberately avoids pretending that a local hash is proof of a provid
74
74
 
75
75
  # Provider behavior
76
76
 
77
- ## DeepSeek V4.1 Flash
77
+ ## DeepSeek V4 and later
78
78
 
79
79
  ### Policy: passive
80
80
 
@@ -93,6 +93,13 @@ The DeepSeek branch exists primarily to preserve a stable harness while providin
93
93
 
94
94
  This is intentional. The implementation describes DeepSeek as a passive policy whose purpose is to preserve the existing high-cache-rate behavior rather than introduce new request mutations.
95
95
 
96
+ Since v0.4.3 this is formalized as the documented **"DeepSeek V4 and later"**
97
+ family. Canonical ids (`deepseek-flash`, `deepseek-v4-pro`) and the accepted
98
+ `deepseek-v4-flash` aliases resolve to this passive baseline, and pre-V4 or
99
+ unknown future `*deepseek*` ids fall back to the same passive baseline. No cache
100
+ key, cache-control field, prompt rewrite, or OpenRouter affinity is ever added
101
+ for DeepSeek.
102
+
96
103
  The plugin still observes:
97
104
 
98
105
  * system-prompt shape
@@ -419,7 +426,7 @@ and are reported diagnostically; the message content is left untouched.
419
426
 
420
427
  | Policy family | Detection | Prompt text changed? | Cache metadata changed? | OpenRouter affinity header | Primary cache signal |
421
428
  | ------------- | --------- | ------------------- | ----------------------- | -------------------------- | -------------------- |
422
- | DeepSeek | `deepseek` | No | No | None | provider `cache.read` / `cache.write` |
429
+ | DeepSeek | `deepseek` (V4-and-later family + passive fallback) | No | No | None | provider `cache.read` / `cache.write` |
423
430
  | GPT-5.6 and later | version boundary `gpt-<major>[.<minor>] ≥ 5.6` on OpenAI-ish endpoints (includes GPT-6) | No | Yes: `prompt_cache_key` + options | None | provider cache tokens |
424
431
  | GLM-5.3 | `glm-5.3*` | Yes, narrowly (`<env>` tail) | No provider cache key | `x-session-id` on OpenRouter only | provider cache tokens (GLM ratio) |
425
432
  | MiMo-V2.6 | Flash / Pro only | Yes, narrowly (`<env>` tail) | No: implicit caching only | `x-session-id` on OpenRouter only | `cached_tokens / prompt_tokens` |
@@ -285,7 +285,7 @@ made here); **hold** = do not inherit without first-party evidence.
285
285
  | OpenAI | pre-5.6 negative controls: `gpt-5.5`, `gpt-5.4`, `gpt-5.2`, `gpt-5.1`, `gpt-5`, `gpt-4.1`, `gpt-4o` | Implicit only; different min-length class; `in_memory`/`24h` retention; `prompt_cache_key` for routing | neutral | **hold** — do not inherit 5.6 policy | n/a | High | OpenAI *Prompt caching*; *Pricing* | 2026-09-26 |
286
286
  | DeepSeek | V4: `deepseek-v4-pro`, legacy `deepseek-v4-flash` | Provider-wide automatic disk cache; implicit; prefix-unit matching; hit/miss token fields | Passive (no mutation) | keep | none | High | DeepSeek *Context Caching*; *Models & Pricing* | 2026-09-26 |
287
287
  | DeepSeek | V4.1 / current V4-family: `deepseek-flash` (MODEL VERSION "DeepSeek-V4.1-Flash") | Same provider-wide automatic policy; cache-hit pricing listed for both current models | Passive | keep (creator/family baseline) | none documented | High | DeepSeek *Models & Pricing*; *news260910* | 2026-09-26 |
288
- | DeepSeek | future-looking V4+ identifiers: `deepseek-v4.1`, `deepseek-v4`, `deepseek-v5` | Not documented as request ids (`deepseek-v4.1`/`deepseek-v4` invalid or version-string only) | Passive via `/deepseek/i` substring (e.g. `deepseek-v5` already passive) | keep passive; treat as unknown-friendly | none | Medium (detection) / Low (future ids) | DeepSeek *Models & Pricing*; *Chat Completions API* | 2026-09-26 |
288
+ | DeepSeek | future-looking V4+ identifiers: `deepseek-v4.1`, `deepseek-v4`, `deepseek-v5` | Not documented as request ids (`deepseek-v4.1`/`deepseek-v4` invalid or version-string only) | Passive via the V4-and-later family predicate or the safe creator fallback (v0.4.3); no mutation | keep passive; treat as unknown-friendly | none | Medium (detection) / Low (future ids) | DeepSeek *Models & Pricing*; *Chat Completions API* | 2026-09-27 |
289
289
  | DeepSeek | pre-V4 negative controls: `deepseek-chat`, `deepseek-reasoner` | Retired names (retired 2026-07-24); no separate V4+ cache policy claimed | Passive | hold | n/a | High | DeepSeek *Change Log*; *news260424* | 2026-09-26 |
290
290
  | Z.AI | GLM 5.3: `glm-5.3`, `glm-5.3-flash`, `glm-5.3-flashx` | Implicit automatic caching; `cached_tokens`; stable-prompt-first guidance; no documented min/TTL/key | GLM policy: `<env>` relocation; OpenRouter `x-session-id` | keep (env relocation is an exact overlay, not a Z.AI control) | env relocation is the overlay; keep scoped to GLM-5.3 | Medium | Z.AI *Context Caching*; *Chat Completion*; *Pricing* | 2026-09-26 |
291
291
  | Z.AI | current later GLM generations (documented): none newer than 5.3; newest below is `glm-5.2`/`glm-5.1`/`glm-5`/`glm-4.7` | Same implicit mechanism documented service-wide; cached-input price per model | neutral (only `glm-5.3` matched) | **hold** — no doc says 5.3 overlay extends upward; none newer documented | n/a | High (no later gens documented) | Z.AI *New Released*; *Pricing* | 2026-09-26 |
@@ -362,3 +362,25 @@ a newer model inherits an older policy. They must not be resolved by guessing.
362
362
  - The only OpenAI cache controls injected remain `promptCacheKey` +
363
363
  `promptCacheOptions{implicit,30m}`; no breakpoint or prewarm behavior was
364
364
  added. DeepSeek, GLM, MiMo, and OpenRouter affinity behavior are unchanged.
365
+
366
+ ### Follow-up: v0.4.3 DeepSeek V4-and-later passive coverage
367
+
368
+ - DeepSeek first-party docs were re-verified on **2026-09-27**. Confirmed:
369
+ context caching is provider-wide and passive — no `prompt_cache_key`, flag, or
370
+ breakpoint exists, and Anthropic-style `cache_control` is documented as
371
+ **ignored**. Only `user_id` is cache-relevant (KVCache isolation). Usage fields
372
+ are `prompt_cache_hit_tokens`, `prompt_cache_miss_tokens`, and
373
+ `prompt_tokens_details.cached_tokens`.
374
+ - Canonical request ids are `deepseek-flash` (= DeepSeek-V4.1-Flash) and
375
+ `deepseek-v4-pro`; `deepseek-v4-flash`/`deepseek-v4-flash-vision-exp` are
376
+ accepted retired aliases, and `deepseek-chat`/`deepseek-reasoner` are
377
+ discontinued. First-party docs publish **no** generational naming rule, and the
378
+ V4.1 codename id `deepseek-flash` carries no version token.
379
+ - v0.4.3 adds a passive `deepseek.v4-plus` family entry: canonical ids match by
380
+ exact id, `deepseek-v<major≥4>` version tokens match by predicate, and
381
+ pre-V4 / retired / unknown future `*deepseek*` ids fall through to the passive
382
+ creator baseline. No cache-control field, cache key, prompt rewrite, or
383
+ OpenRouter affinity is introduced for DeepSeek.
384
+ - Evidence caveat: first-party pages conflict on whether `deepseek-v4-pro` still
385
+ routes as a distinct model in late 2026; this does not affect the passive
386
+ policy, which carries no mutation either way.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-cache-engine",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "private": false,
5
5
  "description": "Provider-aware prompt-cache optimization and telemetry for OpenCode",
6
6
  "keywords": [
@@ -81,6 +81,27 @@ export function isGpt56OrLater(slug) {
81
81
  return false
82
82
  }
83
83
 
84
+ // DeepSeek V4-and-later coverage (docs/cache-policy-inventory.md §2; 2026-09-27
85
+ // first-party re-check). DeepSeek caching is provider-wide and passive: there is
86
+ // no cache key, flag, or breakpoint, and Anthropic-style `cache_control` is
87
+ // documented as ignored. This predicate therefore only classifies a version
88
+ // token (`deepseek-v<major>[.<minor>]` with major >= 4) into the passive family;
89
+ // it grants no mutation. Because the baseline is passive, matching an unknown
90
+ // future `deepseek-v5+` id is safe by construction.
91
+ //
92
+ // First-party docs do NOT publish a generational naming rule, and the current
93
+ // V4.1 codename id `deepseek-flash` carries no version token, so it is covered
94
+ // by explicit exact ids rather than by this predicate.
95
+ export function isDeepseekV4OrLater(slug) {
96
+ const text = String(slug ?? "").toLowerCase()
97
+ const re = /deepseek-v(\d+)(?:\.(\d+))?(?![\d.])/g
98
+ let m
99
+ while ((m = re.exec(text)) !== null) {
100
+ if (Number(m[1]) >= 4) return true
101
+ }
102
+ return false
103
+ }
104
+
84
105
  // Candidate ids for exact/alias lookup. Includes the raw apiID/modelID, the
85
106
  // lower-cased forms, and a single stripped transport/vendor prefix
86
107
  // (e.g. "openai/gpt-5.6-luna" -> "gpt-5.6-luna", "xiaomi/mimo-v2.6-flash" ->
@@ -357,6 +378,28 @@ export const POLICY_REGISTRY = [
357
378
  inventoryRef: "§4 Xiaomi MiMo",
358
379
  },
359
380
  {
381
+ // v0.4.3: formalize the documented "DeepSeek V4 and later" family. Coverage
382
+ // is passive (no mutation, no overlays). Version ids inherit via the
383
+ // predicate; the V4.1 codename id `deepseek-flash` has no version token and
384
+ // is matched by exact id. Pre-V4 and unknown future ids fall through to the
385
+ // passive creator baseline below, so nothing speculative is ever applied.
386
+ id: "deepseek.v4-plus",
387
+ creator: "deepseek",
388
+ family: "deepseek",
389
+ kind: "family",
390
+ predicate: isDeepseekV4OrLater,
391
+ exactIds: ["deepseek-flash", "deepseek-v4-pro"],
392
+ baseline: "deepseek.kv-cache",
393
+ overlays: [],
394
+ legacy: true,
395
+ runtime: rt("deepseek"),
396
+ boundary: "DeepSeek V4 and later",
397
+ note: "DeepSeek caching is provider-wide and passive (no cache key, flag, breakpoint, or cache-control; Anthropic `cache_control` is ignored). Verified 2026-09-27. Canonical current ids: `deepseek-flash` (V4.1-Flash) and `deepseek-v4-pro`; `deepseek-v4-flash`/`deepseek-v4-flash-vision-exp` are accepted retired aliases.",
398
+ inventoryRef: "§2 DeepSeek",
399
+ },
400
+ {
401
+ // Safe passive fallback for any other `*deepseek*` id (pre-V4, retired, or
402
+ // unknown future models) so DeepSeek always fails safe to observation only.
360
403
  id: "deepseek.baseline",
361
404
  creator: "deepseek",
362
405
  family: "deepseek",
@@ -53,6 +53,7 @@ import {
53
53
  MODEL_ALIASES,
54
54
  OVERLAYS,
55
55
  POLICY_REGISTRY,
56
+ isDeepseekV4OrLater,
56
57
  isGpt56OrLater,
57
58
  resolveLegacyFamily,
58
59
  resolvePolicy,
@@ -1392,11 +1393,12 @@ test("resolvePolicy: pre-5.6 GPT negative controls are neutral with no overlays"
1392
1393
  }
1393
1394
  })
1394
1395
 
1395
- test("resolvePolicy: DeepSeek V4 / V4.1 resolve to the creator baseline (passive, no overlay)", () => {
1396
+ test("resolvePolicy: DeepSeek V4 / V4.1 resolve to the passive baseline (no overlay)", () => {
1396
1397
  const v4 = resolvePolicy(M("deepseek", "deepseek-v4-pro"))
1397
1398
  assert.equal(v4.creator, "deepseek")
1398
1399
  assert.equal(v4.family, "deepseek")
1399
- assert.equal(v4.matchType, "creator")
1400
+ // v0.4.3: deepseek-v4-pro is a documented canonical id, so it matches exactly.
1401
+ assert.equal(v4.matchType, "exact")
1400
1402
  assert.equal(baseId(v4), "deepseek.kv-cache")
1401
1403
  assert.deepEqual(overlayIds(v4), [])
1402
1404
 
@@ -1642,6 +1644,10 @@ async function runPolicyMigrationProbe() {
1642
1644
  { name: "gpt-daybreak-alias", model: { providerID: "openai", id: "gpt-daybreak-blue-latest", api: { id: "gpt-daybreak-blue-latest", npm: "@ai-sdk/openai" } }, expect: { policy: "neutral", env: false, gpt: false, header: false } },
1643
1645
  { name: "deepseek-v4-pro", model: { providerID: "deepseek", id: "deepseek-v4-pro", api: { id: "deepseek-v4-pro" } }, expect: { policy: "deepseek", env: false, gpt: false, header: false } },
1644
1646
  { name: "deepseek-flash", model: { providerID: "deepseek", id: "deepseek-flash", api: { id: "deepseek-flash" } }, expect: { policy: "deepseek", env: false, gpt: false, header: false } },
1647
+ { name: "deepseek-v5-future", model: { providerID: "deepseek", id: "deepseek-v5", api: { id: "deepseek-v5" } }, expect: { policy: "deepseek", env: false, gpt: false, header: false } },
1648
+ { name: "deepseek-v3-pre", model: { providerID: "deepseek", id: "deepseek-v3", api: { id: "deepseek-v3" } }, expect: { policy: "deepseek", env: false, gpt: false, header: false } },
1649
+ { name: "deepseek-openrouter", model: { providerID: "openrouter", id: "deepseek/deepseek-v4-pro", api: { id: "deepseek/deepseek-v4-pro" } }, expect: { policy: "deepseek", env: false, gpt: false, header: false } },
1650
+ { name: "deepseek-gateway", model: { providerID: "acme-gateway", id: "my-deepseek-mirror", api: { id: "my-deepseek-mirror" } }, expect: { policy: "deepseek", env: false, gpt: false, header: false } },
1645
1651
  { name: "glm-5.3-direct", model: { providerID: "zai", id: "glm-5.3", api: { id: "glm-5.3" } }, expect: { policy: "glm53", env: true, gpt: false, header: false } },
1646
1652
  { name: "glm-5.3-openrouter", model: { providerID: "openrouter", id: "z-ai/glm-5.3-flash", api: { id: "z-ai/glm-5.3-flash" } }, expect: { policy: "glm53", env: true, gpt: false, header: true } },
1647
1653
  { name: "glm-5.2", model: { providerID: "zai", id: "glm-5.2", api: { id: "glm-5.2" } }, expect: { policy: "neutral", env: false, gpt: false, header: false } },
@@ -1819,3 +1825,93 @@ test("v0.4.2: pre-5.6 and out-of-family GPT ids get no GPT options", async () =>
1819
1825
  assert.equal(r.runtimePolicy, "neutral", `${name}: neutral runtime`)
1820
1826
  }
1821
1827
  })
1828
+
1829
+ // ===========================================================================
1830
+ // v0.4.3 DeepSeek V4-and-later passive coverage
1831
+ //
1832
+ // Source: DeepSeek first-party docs re-verified 2026-09-27
1833
+ // (docs/cache-policy-inventory.md §2). Caching is provider-wide and passive:
1834
+ // no cache key/flag/breakpoint; Anthropic `cache_control` is ignored.
1835
+ // ===========================================================================
1836
+
1837
+ test("v0.4.3: isDeepseekV4OrLater matches V4+ version tokens only", () => {
1838
+ const inFamily = ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v4.1", "deepseek-v4.5-pro", "deepseek-v5", "deepseek-v10", "deepseek/deepseek-v4-pro"]
1839
+ for (const id of inFamily) assert.equal(isDeepseekV4OrLater(id), true, `${id} is V4+`)
1840
+ const outOfFamily = ["deepseek-v3", "deepseek-v2", "deepseek-chat", "deepseek-reasoner", "deepseek-coder", "deepseek-flash", "my-deepseek-mirror", ""]
1841
+ for (const id of outOfFamily) assert.equal(isDeepseekV4OrLater(id), false, `${id} is not a V4+ version token`)
1842
+ })
1843
+
1844
+ test("v0.4.3: canonical V4 ids and aliases resolve to the passive DeepSeek family", () => {
1845
+ for (const id of ["deepseek-flash", "deepseek-v4-pro"]) {
1846
+ const r = resolvePolicy(M("deepseek", id))
1847
+ assert.equal(r.creator, "deepseek")
1848
+ assert.equal(r.family, "deepseek")
1849
+ assert.equal(baseId(r), "deepseek.kv-cache")
1850
+ assert.deepEqual(overlayIds(r), [])
1851
+ const rt = resolveRuntimePolicy(M("deepseek", id))
1852
+ assert.equal(rt.policy, "deepseek")
1853
+ assert.equal(rt.gptCacheMetadata, false)
1854
+ assert.equal(rt.envRelocation, null)
1855
+ assert.equal(rt.cacheRatio, null)
1856
+ assert.equal(rt.openRouterAffinity, false)
1857
+ }
1858
+ // v0.4.0 alias handling is preserved.
1859
+ const alias = resolvePolicy(M("deepseek", "deepseek-v4-flash"))
1860
+ assert.equal(alias.family, "deepseek")
1861
+ assert.ok(alias.matchReason.startsWith("alias:deepseek-v4-flash"))
1862
+ assert.equal(resolveRuntimePolicy(M("deepseek", "deepseek-v4-flash")).policy, "deepseek")
1863
+ assert.equal(resolvePolicy(M("deepseek", "deepseek-chat")).family, "deepseek")
1864
+ })
1865
+
1866
+ test("v0.4.3: later and unknown DeepSeek ids stay passive (no speculative mutation)", () => {
1867
+ for (const id of ["deepseek-v5", "deepseek-v6.2", "deepseek-v4.1-pro", "deepseek-nova"]) {
1868
+ const r = resolvePolicy(M("deepseek", id))
1869
+ assert.equal(r.creator, "deepseek")
1870
+ assert.equal(r.family, "deepseek")
1871
+ assert.deepEqual(overlayIds(r), [], `${id}: no overlay`)
1872
+ const rt = resolveRuntimePolicy(M("deepseek", id))
1873
+ assert.equal(rt.policy, "deepseek", `${id}: passive policy`)
1874
+ assert.equal(rt.gptCacheMetadata, false)
1875
+ assert.equal(rt.envRelocation, null)
1876
+ assert.equal(rt.cacheRatio, null)
1877
+ assert.equal(rt.openRouterAffinity, false)
1878
+ }
1879
+ })
1880
+
1881
+ test("v0.4.3: pre-V4 DeepSeek ids remain passive and outside the V4+ family entry", () => {
1882
+ for (const id of ["deepseek-v3", "deepseek-v2", "deepseek-coder"]) {
1883
+ const r = resolvePolicy(M("deepseek", id))
1884
+ assert.equal(r.family, "deepseek")
1885
+ // handled by the safe creator fallback, not the V4+ family predicate
1886
+ assert.equal(r.matchType, "creator", `${id}: creator fallback`)
1887
+ assert.equal(resolveRuntimePolicy(M("deepseek", id)).policy, "deepseek")
1888
+ }
1889
+ })
1890
+
1891
+ test("v0.4.3: DeepSeek keeps the generic read/write ratio and no family-specific fields", () => {
1892
+ const rt = resolveRuntimePolicy(M("deepseek", "deepseek-v4-pro"))
1893
+ assert.equal(rt.cacheRatio, null) // generic read/(read+write) accounting is used
1894
+ assert.equal(hitRatePct(30, 70), 30)
1895
+ })
1896
+
1897
+ test("v0.4.3: DeepSeek never receives OpenRouter affinity or GPT/GLM/MiMo fields", async () => {
1898
+ const { results } = await policyMigrationResults()
1899
+ const deepseekCases = [
1900
+ "deepseek-v4-pro",
1901
+ "deepseek-flash",
1902
+ "deepseek-v5-future",
1903
+ "deepseek-v3-pre",
1904
+ "deepseek-openrouter",
1905
+ "deepseek-gateway",
1906
+ ]
1907
+ for (const name of deepseekCases) {
1908
+ const r = results.find((x) => x.name === name)
1909
+ assert.ok(r, `${name} present`)
1910
+ assert.equal(r.runtimePolicy, "deepseek", `${name}: passive policy`)
1911
+ assert.equal(r.detectPolicy, "deepseek", `${name}: detectPolicy agrees`)
1912
+ assert.equal(r.gptOptionInjected, false, `${name}: no GPT options leak`)
1913
+ assert.equal(r.systemRelocated, false, `${name}: no GLM/MiMo env relocation`)
1914
+ assert.equal(r.affinityHeaderAttached, false, `${name}: no OpenRouter affinity`)
1915
+ assert.equal(r.existingHeadersPreserved, true, `${name}: headers preserved`)
1916
+ }
1917
+ })