opencode-cache-engine 0.3.5 → 0.4.0

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
@@ -3,21 +3,35 @@
3
3
  Provider-aware prompt-cache optimization and observability for
4
4
  [OpenCode](https://opencode.ai).
5
5
 
6
- `opencode-cache-engine` is an OpenCode npm plugin with two targets:
6
+ `opencode-cache-engine` is distributed as an npm package. The Git repository is
7
+ the development source of truth; the published package is a release artifact.
8
+ The package exposes two separate targets required by OpenCode's installed
9
+ plugin model:
7
10
 
8
- - **Server target** — the actual cache-engine runtime and provider policies.
9
- - **TUI target** — registration with OpenCode's TUI plugin manager.
11
+ - **`./server`** — the CacheEngine runtime, hooks, provider policies, and telemetry.
12
+ - **`./tui`** — plugin-manager registration and enable/disable integration; it
13
+ has no CacheEngine-specific UI.
10
14
 
11
- The server target handles cache optimization, prompt-shape diagnostics, compaction handling, and cache telemetry. The TUI target provides the plugin-manager integration and enable/disable state for the TUI-facing plugin entry.
15
+ For local development, use the package from this Git checkout through the
16
+ repository's OpenCode/package development path. Do not maintain or edit a copied
17
+ plugin under `~/.config/opencode/plugins/`. For released installs where
18
+ reproducibility matters, pin an exact package version rather than relying on
19
+ `@latest` resolution or a moving cache entry; see [Installation](#installation).
12
20
 
13
21
  `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.
14
22
 
15
23
  The plugin currently has four cache-policy families:
16
24
 
17
- * **DeepSeek V4.1 Flash** — passive cache-stability and observability
18
- * **GPT-5.6 Luna** — active cache-control configuration
19
- * **GLM-5.3 Flash** — conservative system-prompt stabilization
20
- * **MiMo-V2.6 (Flash / Pro)** — prefix stability and OpenRouter session-affinity diagnostics
25
+ * **DeepSeek** — passive cache observability; request structure is preserved.
26
+ * **GPT-5.6** — documented cache-key/options metadata, with prompt text unchanged.
27
+ * **GLM-5.3** — narrow, content-preserving `<env>` relocation and diagnostics.
28
+ * **MiMo-V2.6** — narrow, content-preserving `<env>` relocation and diagnostics.
29
+
30
+ For both MiMo-V2.6 and GLM-5.3, CacheEngine adds its deterministic
31
+ `x-session-id` request header only when OpenCode identifies the actual provider
32
+ as `openrouter`. It does not add that OpenRouter-specific header for
33
+ non-OpenRouter providers; direct provider endpoints retain their provider-native
34
+ caching behavior.
21
35
 
22
36
  The central design principle is:
23
37
 
@@ -38,7 +52,7 @@ It:
38
52
  6. Applies GPT-5.6 cache-control metadata.
39
53
  7. Applies the GLM-5.3 and MiMo-V2.6 volatile-environment relocation.
40
54
  8. Records diagnostics that help determine whether prompt-shape changes correlate with cache behavior.
41
- 9. Records MiMo-V2.6 provider identity and provider-switch diagnostics.
55
+ 9. Records MiMo/GLM affinity outcomes and provider-identity changes.
42
56
 
43
57
  The plugin deliberately avoids pretending that a local hash is proof of a provider cache hit. Provider-reported token usage remains the authoritative signal.
44
58
 
@@ -165,7 +179,9 @@ This prevents a compaction-specific prompt from sharing the same GPT cache names
165
179
 
166
180
  ### Policy: input-shape optimization
167
181
 
168
- GLM-5.3 receives the only prompt-text transformation in the current plugin.
182
+ GLM-5.3 and MiMo-V2.6 use the only prompt-text transformation in the current
183
+ plugin: a narrow, content-preserving relocation of the identifiable `<env>`
184
+ block for the eligible model family.
169
185
 
170
186
  The plugin identifies OpenCode's volatile `<env>` section and moves it to the **tail of the system prompt**.
171
187
 
@@ -225,6 +241,22 @@ It only occurs when:
225
241
 
226
242
  The plugin does not arbitrarily rearrange unrelated prompt content.
227
243
 
244
+ ### OpenRouter session affinity
245
+
246
+ For GLM-5.3 requests whose actual OpenCode provider identity is `openrouter`,
247
+ CacheEngine adds its deterministic `x-session-id` request header unless a
248
+ case-insensitive `x-session-id` is already present in model or plugin headers.
249
+ The existing value is preserved. This header is affinity metadata, not a prompt
250
+ transformation or cache-control field.
251
+
252
+ For direct Z.AI or any other non-OpenRouter endpoint, CacheEngine does not add
253
+ the OpenRouter-specific affinity header. It leaves the endpoint's native cache
254
+ behavior intact.
255
+
256
+ Affinity observations record eligibility, the observed provider identity,
257
+ whether a header was already present or added, and provider-identity changes
258
+ (`glm_provider_changed`). They do not record the header value.
259
+
228
260
 
229
261
  ## MiMo-V2.6 (Flash / Pro)
230
262
 
@@ -287,28 +319,24 @@ Explicit telemetry events:
287
319
  * `mimo_system_env_relocated`
288
320
  * `mimo_system_prefix_changed`
289
321
 
290
- ### OpenRouter sticky session — derived but not injected
291
-
292
- OpenRouter documents a top-level `session_id` request field for sticky provider
293
- routing, which keeps a session's requests on the same upstream provider so
294
- provider-side prompt caches stay warm.
322
+ ### OpenRouter session affinity
295
323
 
296
- The plugin includes a pure, session-scoped derivation (`mimoSessionIdFor`):
297
- deterministic, distinct per session, printable/no-whitespace, and well under the
298
- 256-character cap. However, **the derived id is not injected into requests**.
324
+ For MiMo-V2.6 requests whose actual OpenCode provider identity is `openrouter`,
325
+ CacheEngine adds its existing deterministic, session-scoped `x-session-id`
326
+ request header. If a case-insensitive `x-session-id` already exists in model or
327
+ plugin headers, CacheEngine preserves it and does not replace it. Eligibility
328
+ uses both the MiMo-V2.6 family and the actual provider identity; a matching model
329
+ slug on another endpoint is not enough.
299
330
 
300
- Rationale (verified against the installed runtime): OpenCode's OpenRouter
301
- request adapter forwards only `usage`, `reasoning`, and `prompt_cache_key` from
302
- provider options, and exposes no top-level `session_id` path. Adding an
303
- unsupported field would be guessing, so the id is recorded as telemetry only,
304
- and `mimo26.stickySession` currently gates that recording. If a future runtime
305
- gains a verified `session_id` path, the helper is already in place.
331
+ For Xiaomi's direct endpoint and every other non-OpenRouter provider, CacheEngine
332
+ does not add its OpenRouter-specific `x-session-id`. Direct provider endpoints
333
+ retain their provider-native caching behavior. Any header already supplied by
334
+ the user or runtime is left untouched.
306
335
 
307
- Note that OpenCode itself sets `x-session-affinity` / `X-Session-Id` HTTP
308
- headers for non-opencode providers, and can set a flat `promptCacheKey` for
309
- OpenRouter when `setCacheKey: true` is configured. Those are HTTP routing
310
- headers and an OpenAI-style cache key respectively — they are not OpenRouter's
311
- documented body `session_id`.
336
+ The `x-session-id` header is not a top-level request-body `session_id`, a
337
+ `promptCacheKey`, or a cache-control option. MiMo uses provider-managed implicit
338
+ caching: CacheEngine sends no undocumented `promptCacheKey`, `cacheControl`,
339
+ cache breakpoint, or TTL.
312
340
 
313
341
  ### MiMo cache metrics
314
342
 
@@ -374,12 +402,16 @@ and are reported diagnostically; the message content is left untouched.
374
402
 
375
403
  # Provider comparison
376
404
 
377
- | Provider | Detection | Prompt text changed? | Cache metadata changed? | Primary cache signal |
378
- | ------------------- | ---------------------------------- | --------------------------- | ---------------------------------- | --------------------------------------- |
379
- | DeepSeek V4.1 Flash | `deepseek` | No | No | provider `cache.read`/`cache.write` |
380
- | GPT-5.6 Luna | `gpt-5.6*` on OpenAI-ish endpoints | No | Yes: `prompt_cache_key` + options | provider cache tokens |
381
- | GLM-5.3 Flash | `glm-5.3*` | Yes, narrowly (`<env>` tail) | No provider cache key | provider cache tokens (GLM ratio) |
382
- | MiMo-V2.6 Flash/Pro | `mimo-v2.6-flash` / `mimo-v2.6-pro` | Yes, narrowly (`<env>` tail) | No: implicit caching only | `cached_tokens / prompt_tokens` |
405
+ | Policy family | Detection | Prompt text changed? | Cache metadata changed? | OpenRouter affinity header | Primary cache signal |
406
+ | ------------- | --------- | ------------------- | ----------------------- | -------------------------- | -------------------- |
407
+ | DeepSeek | `deepseek` | No | No | None | provider `cache.read` / `cache.write` |
408
+ | GPT-5.6 | `gpt-5.6*` on OpenAI-ish endpoints | No | Yes: `prompt_cache_key` + options | None | provider cache tokens |
409
+ | 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) |
410
+ | MiMo-V2.6 | Flash / Pro only | Yes, narrowly (`<env>` tail) | No: implicit caching only | `x-session-id` on OpenRouter only | `cached_tokens / prompt_tokens` |
411
+
412
+ `x-session-id` is an HTTP affinity header, not a provider cache key or
413
+ cache-control field. Non-OpenRouter endpoints do not receive CacheEngine's
414
+ OpenRouter-specific affinity value; their native cache behavior is unchanged.
383
415
 
384
416
 
385
417
  # Prompt-cache strategy
@@ -614,9 +646,17 @@ Telemetry is intended to answer questions such as:
614
646
  * Did the GLM system stabilization actually change the observed prompt shape?
615
647
  * Did MiMo's environment relocation fire (`mimo_system_env_relocated`)?
616
648
  * Did MiMo's stable system prefix change (`mimo_system_prefix_changed`)?
617
- * Did the MiMo provider change within a session (`mimo_provider_changed`)?
649
+ * Was MiMo/GLM affinity eligible, and did CacheEngine add its header?
650
+ * Was affinity bypassed for a non-OpenRouter or missing provider identity?
651
+ * Did the MiMo provider change (`mimo_provider_changed`) or the GLM provider
652
+ change (`glm_provider_changed`) within a session?
618
653
  * What was MiMo's provider-reported cache hit rate (`cacheHitRate`)?
619
654
 
655
+ Affinity observations are `boundary` records. They contain provider/model
656
+ identity and booleans/source classification such as `eligible`,
657
+ `headerPresent`, `headerAttached`, and `headerSource`; they do not include the
658
+ `x-session-id` header value or full request headers.
659
+
620
660
  A MiMo usage record adds the provider-reported cache fields:
621
661
 
622
662
  ```json
@@ -792,6 +832,10 @@ Defaults to:
792
832
 
793
833
  Existing request options are not overwritten by the plugin.
794
834
 
835
+ The established **272K pricing boundary** is controlled by user/harness-side
836
+ configuration and remains unchanged. CacheEngine does not set or raise GPT
837
+ context or output limits; apply the existing harness/user-side limits.
838
+
795
839
 
796
840
  # GLM-5.3 configuration
797
841
 
@@ -844,10 +888,11 @@ Enables relocation of the volatile `<env>` section to the system-prompt tail
844
888
 
845
889
  ### `stickySession`
846
890
 
847
- Gates derivation/recording of the OpenRouter sticky-session id
848
- (`mimoSessionIdFor`). The id is recorded as telemetry; it is **not** injected
849
- into the request because this runtime exposes no verified OpenRouter top-level
850
- `session_id` path. See "OpenRouter sticky session — derived but not injected".
891
+ Controls whether MiMo usage/provider-change telemetry includes the derived
892
+ `stickySessionId` field. It does not control the existing `x-session-id` header
893
+ injection, which is gated by MiMo family plus actual `openrouter` provider
894
+ identity. The telemetry field contains the derived identifier, not request
895
+ headers or prompt data.
851
896
 
852
897
  ### `preserveThinkingIntegrity`
853
898
 
@@ -898,7 +943,10 @@ This plugin is compatible with OpenRouter because the cache policy is based on t
898
943
 
899
944
  For cache-sensitive workloads, provider stability remains important.
900
945
 
901
- The plugin does not attempt to compensate for provider switching by rewriting prompts. It records MiMo provider identity and provider-switch diagnostics so routing instability is at least observable.
946
+ The plugin does not attempt to compensate for provider switching by rewriting
947
+ prompts. For MiMo and GLM it records observed provider identity and provider
948
+ changes so routing instability is observable; it never overrides the selected
949
+ provider or inspects OpenRouter's hidden upstream provider selection.
902
950
 
903
951
  For that reason, a stable provider route is preferable when your goal is to measure and maximize prefix reuse.
904
952
 
@@ -929,7 +977,9 @@ export const CacheEngine: Plugin = async ({ client, directory }) => {
929
977
  }
930
978
  ```
931
979
 
932
- The identifier `CacheEngine` is the OpenCode plugin export name. It does not determine the eventual npm package name.
980
+ `CacheEngine` is the exported plugin factory. The npm package name remains
981
+ `opencode-cache-engine`; the server and TUI package exports are listed in
982
+ [File layout](#file-layout).
933
983
 
934
984
  ---
935
985
 
@@ -1118,7 +1168,8 @@ For reliable cache measurements:
1118
1168
 
1119
1169
  # File layout
1120
1170
 
1121
- A typical standalone repository can use:
1171
+ This Git repository is the canonical development source. The npm package is
1172
+ built from this tree and exposes the runtime entry points separately:
1122
1173
 
1123
1174
  ```text
1124
1175
  opencode-cache-engine/
@@ -1135,51 +1186,49 @@ opencode-cache-engine/
1135
1186
  └── LICENSE
1136
1187
  ```
1137
1188
 
1138
- The OpenCode plugin export remains:
1139
-
1140
- ```ts
1141
- export const CacheEngine
1142
- ```
1143
-
1144
- regardless of the eventual npm package name.
1145
-
1146
- For example, the npm package could be named:
1189
+ The package exports in `package.json` are:
1147
1190
 
1148
- ```text
1149
- opencode-cache-engine
1191
+ ```json
1192
+ {
1193
+ "./server": "./src/cache-engine.ts",
1194
+ "./tui": "./src/tui.mjs"
1195
+ }
1150
1196
  ```
1151
1197
 
1152
- without changing the `CacheEngine` export identifier.
1198
+ The server target owns all CacheEngine runtime hooks and request behavior. The
1199
+ TUI target only registers the package with OpenCode's plugin manager; it does
1200
+ not duplicate server logic.
1153
1201
 
1154
1202
  ---
1155
1203
 
1156
1204
  # Installation
1157
1205
 
1158
- Install the plugin into the OpenCode plugins directory according to your OpenCode plugin-loading setup.
1206
+ ### Local development
1159
1207
 
1160
- `opencode-cache-engine` is distributed as an npm package.
1208
+ Develop against this repository/package checkout using the project's OpenCode
1209
+ plugin development path. Edit and test the Git working tree as the source of
1210
+ truth; do not copy the plugin into `~/.config/opencode/plugins/` or keep a
1211
+ second active source tree there.
1161
1212
 
1162
- ## Server/runtime plugin
1213
+ ### Released package
1163
1214
 
1164
- Add the package to the OpenCode runtime plugin configuration:
1215
+ OpenCode loads the server and TUI targets from the npm package's separate
1216
+ exports. For reproducible released installs, pin an exact version. For this
1217
+ release, use:
1165
1218
 
1166
1219
  ```json
1167
1220
  {
1168
1221
  "plugin": [
1169
- "opencode-cache-engine"
1222
+ "opencode-cache-engine@0.3.6"
1170
1223
  ]
1171
1224
  }
1172
1225
  ```
1173
1226
 
1174
- The runtime entry should expose:
1175
-
1176
- ```ts
1177
- export const CacheEngine: Plugin = async ({ client, directory }) => {
1178
- // ...
1179
- }
1180
- ```
1227
+ Avoid a bare package name that resolves a moving `@latest` version when
1228
+ reproducibility matters. Update the pinned version deliberately when upgrading.
1181
1229
 
1182
- After installation, verify that OpenCode loads the plugin successfully before benchmarking cache behavior.
1230
+ After installation, verify that OpenCode loads the plugin successfully before
1231
+ benchmarking cache behavior.
1183
1232
 
1184
1233
  ---
1185
1234
 
@@ -1209,7 +1258,7 @@ GLM-5.3:
1209
1258
  MiMo-V2.6:
1210
1259
  volatile env block relocated when eligible
1211
1260
  no GPT/GLM-only cache fields present
1212
- no OpenRouter top-level session_id injected (unsupported by this runtime)
1261
+ x-session-id added only for actual OpenRouter provider identity
1213
1262
  telemetry carries provider/model/promptTokens/cachedTokens/cacheHitRate
1214
1263
  ```
1215
1264
 
@@ -1278,14 +1327,15 @@ not matched.
1278
1327
 
1279
1328
  ---
1280
1329
 
1281
- ## No OpenRouter `session_id` is sent for MiMo
1330
+ ## OpenRouter affinity header is not added
1282
1331
 
1283
- This is expected. The installed OpenCode runtime's OpenRouter request adapter
1284
- forwards only `usage`, `reasoning`, and `prompt_cache_key` from provider options
1285
- and exposes no top-level `session_id` path. The plugin derives a stable
1286
- `stickySessionId` and records it as telemetry, but does not inject it rather than
1287
- send an unsupported field. This may change if a future runtime exposes a verified
1288
- path.
1332
+ CacheEngine adds its `x-session-id` only for a detected MiMo-V2.6 or GLM-5.3
1333
+ request when the actual OpenCode `providerID` is exactly `openrouter`. A direct
1334
+ provider route or missing provider identity is bypassed. If a case-insensitive
1335
+ `x-session-id` is already present in model or plugin headers, it is preserved
1336
+ and CacheEngine does not replace it. Check the `openrouter_affinity_*` boundary
1337
+ records for eligibility, provider identity, and whether CacheEngine added the
1338
+ header; the record does not include the header value.
1289
1339
 
1290
1340
  ---
1291
1341
 
@@ -1311,9 +1361,9 @@ The current implementation is intentionally conservative:
1311
1361
 
1312
1362
  ```text
1313
1363
  DeepSeek -> preserve and measure
1314
- GPT-5.6 -> configure cache controls
1315
- GLM-5.3 -> isolate volatile prompt content
1316
- MiMo-V2.6 -> stabilize prefix + observe provider/cache reality
1364
+ GPT-5.6 -> documented cache key/options; user/harness controls the 272K pricing boundary
1365
+ GLM-5.3 -> preserve-content <env> relocation + OpenRouter affinity header
1366
+ MiMo-V2.6 -> preserve-content <env> relocation + OpenRouter affinity header
1317
1367
  ```
1318
1368
 
1319
1369
  That separation is the core design of the project.