opencode-cache-engine 0.2.0 → 0.3.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 +295 -11
- package/examples/cache-engine.json +6 -0
- package/package.json +3 -2
- package/src/cache-engine-core.mjs +76 -9
- package/src/cache-engine.ts +124 -10
- package/test/cache-engine.test.mjs +206 -1
- package/README_rewritten.md +0 -965
package/src/cache-engine.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DIGEST_TEMPLATE,
|
|
6
6
|
POLICY_GLM53,
|
|
7
7
|
POLICY_GPT56,
|
|
8
|
+
POLICY_MIMO26,
|
|
8
9
|
POLICY_NEUTRAL,
|
|
9
10
|
createRecorder,
|
|
10
11
|
detectPolicy,
|
|
@@ -16,10 +17,13 @@ import {
|
|
|
16
17
|
gptCacheOptionsDelta,
|
|
17
18
|
hitRatePct,
|
|
18
19
|
loadConfig,
|
|
20
|
+
mimoHitRate,
|
|
21
|
+
mimoSessionIdFor,
|
|
19
22
|
nextProcessedCursor,
|
|
20
23
|
observeReasoningEffort,
|
|
21
24
|
policyEnabled,
|
|
22
25
|
prefixChangeReasons,
|
|
26
|
+
providerChangeEvent,
|
|
23
27
|
reasoningEffortFromOptions,
|
|
24
28
|
reasoningIssueReasons,
|
|
25
29
|
relocateVolatileEnvBlock,
|
|
@@ -37,7 +41,7 @@ import {
|
|
|
37
41
|
// cache-engine
|
|
38
42
|
//
|
|
39
43
|
// Provider-aware prompt-cache observability + conservative cache-shape
|
|
40
|
-
// preservation for ONE OpenCode TUI across
|
|
44
|
+
// preservation for ONE OpenCode TUI across four model families:
|
|
41
45
|
//
|
|
42
46
|
// DeepSeek V4.1 Flash -> pure passive. >99.66% hit rate is preserved by never
|
|
43
47
|
// mutating system/options/requests. Observability only.
|
|
@@ -52,16 +56,28 @@ import {
|
|
|
52
56
|
// history stable, and INSTRUMENT preserved-thinking
|
|
53
57
|
// integrity (duplicate/reorder/modified reasoning). No
|
|
54
58
|
// invented cache key (Z.ai exposes none).
|
|
59
|
+
// MiMo-V2.6 -> prefix stability + OpenRouter session affinity. The
|
|
60
|
+
// safe env-block relocation is applied (stabilizeSystem)
|
|
61
|
+
// and provider switches within a session are diagnosed.
|
|
62
|
+
// MiMo caching is provider-managed implicit context
|
|
63
|
+
// caching; no cache key/breakpoint/TTL is invented. The
|
|
64
|
+
// OpenRouter sticky-session id is derived but currently
|
|
65
|
+
// NOT injected: this runtime's OpenRouter request
|
|
66
|
+
// adapter forwards only usage/reasoning/prompt_cache_key
|
|
67
|
+
// and exposes no top-level `session_id` path (verified
|
|
68
|
+
// against the installed runtime; see README).
|
|
55
69
|
//
|
|
56
70
|
// The engine remains conservative: it observes, hashes, compares, records,
|
|
57
71
|
// appends a compaction continuation template, and (for GPT-5.6 only) injects
|
|
58
72
|
// documented cache options. It never rewrites message history, reorders tools,
|
|
59
73
|
// or alters user content. DeepSeek and neutral models are byte-untouched.
|
|
74
|
+
// MiMo/GLM only relocate the identifiable volatile env block, content-preserving.
|
|
60
75
|
//
|
|
61
76
|
// IMPORTANT (terminology): local hashes describe the *observed* prefix shape.
|
|
62
77
|
// A changed hash means request bytes changed; it is NOT proof the provider's
|
|
63
78
|
// cache key changed or that a cache miss occurred. Provider-reported cache
|
|
64
|
-
// token counts are authoritative; hashes are diagnostics only.
|
|
79
|
+
// token counts are authoritative; hashes are diagnostics only. For MiMo, the
|
|
80
|
+
// authoritative cache signal is provider-reported cached_tokens.
|
|
65
81
|
// ---------------------------------------------------------------------------
|
|
66
82
|
|
|
67
83
|
const TOOL_FETCH_TTL_MS = 1500
|
|
@@ -113,6 +129,7 @@ type SessionState = {
|
|
|
113
129
|
cacheRootAt: number | null
|
|
114
130
|
reasoningSeen: Map<string, number>
|
|
115
131
|
reasoningLastSeq: string[] | null
|
|
132
|
+
mimoProvider: { providerID: string; modelID: string } | null
|
|
116
133
|
}
|
|
117
134
|
|
|
118
135
|
const emptyShape = (): Shape => ({
|
|
@@ -154,6 +171,7 @@ export const CacheEngine: Plugin = async ({ client, directory }) => {
|
|
|
154
171
|
cacheRootAt: null,
|
|
155
172
|
reasoningSeen: new Map(),
|
|
156
173
|
reasoningLastSeq: null,
|
|
174
|
+
mimoProvider: null,
|
|
157
175
|
}
|
|
158
176
|
sessions.set(sid, s)
|
|
159
177
|
}
|
|
@@ -397,6 +415,25 @@ export const CacheEngine: Plugin = async ({ client, directory }) => {
|
|
|
397
415
|
recFields.promptTokens = read + write + input
|
|
398
416
|
recFields.glmHitRate = glmHitRatio(read, write, input)
|
|
399
417
|
}
|
|
418
|
+
if (family === POLICY_MIMO26) {
|
|
419
|
+
// Provider-reported cached tokens / total prompt tokens. The runtime's
|
|
420
|
+
// `input` is the non-cached prompt input and `cache.read` is the
|
|
421
|
+
// cached prompt input, so total prompt tokens are derived as read +
|
|
422
|
+
// input (cache.write is a separate accounting bucket). No cache-write
|
|
423
|
+
// value is fabricated; the ratio is null when prompt tokens are 0.
|
|
424
|
+
const promptTokens = read + input
|
|
425
|
+
recFields.promptTokens = promptTokens
|
|
426
|
+
recFields.cachedTokens = read
|
|
427
|
+
recFields.cacheHitRate = mimoHitRate(read, promptTokens)
|
|
428
|
+
if (cfg.policies?.[POLICY_MIMO26]?.stickySession === true) {
|
|
429
|
+
recFields.stickySessionId = mimoSessionIdFor(sid)
|
|
430
|
+
}
|
|
431
|
+
// Prefer the latest live provider identity over the latched one.
|
|
432
|
+
if (s.mimoProvider) {
|
|
433
|
+
recFields.provider = s.mimoProvider.providerID
|
|
434
|
+
recFields.model = s.mimoProvider.modelID
|
|
435
|
+
}
|
|
436
|
+
}
|
|
400
437
|
if (family === POLICY_GPT56 && s.gptInjected) {
|
|
401
438
|
recFields.keyStrategy = "session"
|
|
402
439
|
recFields.mode = cfg.policies?.[POLICY_GPT56]?.mode
|
|
@@ -445,6 +482,48 @@ export const CacheEngine: Plugin = async ({ client, directory }) => {
|
|
|
445
482
|
try {
|
|
446
483
|
const info = rememberModel(input.sessionID, input.model as unknown as ChatParamsModel)
|
|
447
484
|
const family = info?.family
|
|
485
|
+
|
|
486
|
+
// ---- MiMo-V2.6: provider-switch diagnostics (telemetry only) ---------
|
|
487
|
+
// MiMo cache lives at the provider side, so a provider change within one
|
|
488
|
+
// OpenCode session can silently invalidate it. We record identity on every
|
|
489
|
+
// MiMo request and emit a diagnostic when the OpenCode providerID changes.
|
|
490
|
+
// This never forces or overrides provider routing.
|
|
491
|
+
// NOTE: OpenRouter's *upstream* provider selection (e.g. xiaomi/fp8) is
|
|
492
|
+
// not exposed to plugins; only the OpenCode providerID/modelID are
|
|
493
|
+
// observable here.
|
|
494
|
+
if (family === POLICY_MIMO26 && policyEnabled(cfg, POLICY_MIMO26) && info) {
|
|
495
|
+
// Use the LIVE model identity (not the latched one) so a provider
|
|
496
|
+
// switch within the session is actually observable.
|
|
497
|
+
const live = input.model as unknown as ChatParamsModel
|
|
498
|
+
const cur = {
|
|
499
|
+
providerID: String(live?.providerID ?? ""),
|
|
500
|
+
modelID: String(live?.api?.id ?? live?.id ?? ""),
|
|
501
|
+
}
|
|
502
|
+
if (cur.providerID) {
|
|
503
|
+
const s = get(input.sessionID)
|
|
504
|
+
const ev = providerChangeEvent(s.mimoProvider, cur)
|
|
505
|
+
if (ev.changed) {
|
|
506
|
+
const sticky =
|
|
507
|
+
cfg.policies?.[POLICY_MIMO26]?.stickySession === true
|
|
508
|
+
? { stickySessionId: mimoSessionIdFor(input.sessionID) }
|
|
509
|
+
: {}
|
|
510
|
+
rec.record({
|
|
511
|
+
kind: "boundary",
|
|
512
|
+
sid: input.sessionID,
|
|
513
|
+
ts: Date.now(),
|
|
514
|
+
reason: "mimo_provider_changed",
|
|
515
|
+
policy: POLICY_MIMO26,
|
|
516
|
+
from: ev.from,
|
|
517
|
+
to: ev.to,
|
|
518
|
+
...sticky,
|
|
519
|
+
note: "OpenCode providerID changed; upstream routing is not plugin-visible",
|
|
520
|
+
})
|
|
521
|
+
}
|
|
522
|
+
s.mimoProvider = cur
|
|
523
|
+
}
|
|
524
|
+
return
|
|
525
|
+
}
|
|
526
|
+
|
|
448
527
|
if (!(family === POLICY_GPT56 && policyEnabled(cfg, POLICY_GPT56))) {
|
|
449
528
|
// DeepSeek / GLM / neutral: nothing to inject. GLM has no cache-key API;
|
|
450
529
|
// DeepSeek caching is fully passive; we never mutate requests for them.
|
|
@@ -626,28 +705,47 @@ export const CacheEngine: Plugin = async ({ client, directory }) => {
|
|
|
626
705
|
const s = get(sid)
|
|
627
706
|
const family = s.modelInfo?.family
|
|
628
707
|
|
|
629
|
-
// ---- GLM-5.3 input-shape stabilization
|
|
708
|
+
// ---- GLM-5.3 / MiMo-V2.6 input-shape stabilization ------------------
|
|
630
709
|
// Relocate the identifiable volatile env block (per-day date) to the
|
|
631
710
|
// tail of the single system string, content-preserving, ONLY when the
|
|
632
|
-
//
|
|
633
|
-
// touches other content/order; never applied to
|
|
711
|
+
// family opts into system stabilization and the block markers are
|
|
712
|
+
// present exactly. Never touches other content/order; never applied to
|
|
713
|
+
// other families. The runtime passes a single-element system array
|
|
714
|
+
// (verified against the installed runtime), so no generic reordering is
|
|
715
|
+
// involved.
|
|
634
716
|
//
|
|
635
717
|
// In-place mutation note: request.ts keeps using its own local `system`
|
|
636
718
|
// array after the hook (the trigger's returned output is ignored), so
|
|
637
719
|
// reassigning `output.system = [...]` would be lost. We rewrite the
|
|
638
720
|
// single element in place instead.
|
|
639
721
|
let systemText = output.system.join("\n")
|
|
640
|
-
|
|
722
|
+
const glmStabilize =
|
|
641
723
|
family === POLICY_GLM53 &&
|
|
642
724
|
policyEnabled(cfg, POLICY_GLM53) &&
|
|
643
|
-
cfg.policies?.[POLICY_GLM53]?.stabilizeSystem === true
|
|
644
|
-
|
|
645
|
-
|
|
725
|
+
cfg.policies?.[POLICY_GLM53]?.stabilizeSystem === true
|
|
726
|
+
const mimoStabilize =
|
|
727
|
+
family === POLICY_MIMO26 &&
|
|
728
|
+
policyEnabled(cfg, POLICY_MIMO26) &&
|
|
729
|
+
cfg.policies?.[POLICY_MIMO26]?.stabilizeSystem === true
|
|
730
|
+
if ((glmStabilize || mimoStabilize) && output.system.length === 1) {
|
|
646
731
|
const rel = relocateVolatileEnvBlock(output.system[0])
|
|
647
732
|
if (rel.changed) {
|
|
648
733
|
output.system[0] = rel.text
|
|
649
734
|
systemText = rel.text
|
|
650
|
-
|
|
735
|
+
if (family === POLICY_MIMO26) {
|
|
736
|
+
log("debug", "mimo system env block relocated to suffix", { sid })
|
|
737
|
+
rec.record({
|
|
738
|
+
kind: "boundary",
|
|
739
|
+
sid,
|
|
740
|
+
ts: Date.now(),
|
|
741
|
+
reason: "mimo_system_env_relocated",
|
|
742
|
+
policy: POLICY_MIMO26,
|
|
743
|
+
provider: s.modelInfo?.providerID,
|
|
744
|
+
model: s.modelInfo?.modelID,
|
|
745
|
+
})
|
|
746
|
+
} else {
|
|
747
|
+
log("debug", "glm system env block relocated to suffix", { sid })
|
|
748
|
+
}
|
|
651
749
|
}
|
|
652
750
|
}
|
|
653
751
|
|
|
@@ -724,6 +822,22 @@ export const CacheEngine: Plugin = async ({ client, directory }) => {
|
|
|
724
822
|
? { toolCount, prevToolCount: prevCount, semanticToolsChanged: semanticChanged, wireToolsChanged: wireChanged }
|
|
725
823
|
: {}),
|
|
726
824
|
})
|
|
825
|
+
// MiMo-specific explicit diagnostic: the STABLE prefix changed (not
|
|
826
|
+
// just the relocated volatile env suffix). Reported only; the new
|
|
827
|
+
// content is never overwritten with a stale snapshot.
|
|
828
|
+
if (family === POLICY_MIMO26 && reasons.includes("system_stable_prefix_changed")) {
|
|
829
|
+
rec.record({
|
|
830
|
+
kind: "boundary",
|
|
831
|
+
sid,
|
|
832
|
+
ts: Date.now(),
|
|
833
|
+
reason: "mimo_system_prefix_changed",
|
|
834
|
+
policy: POLICY_MIMO26,
|
|
835
|
+
provider: s.modelInfo?.providerID,
|
|
836
|
+
model: s.modelInfo?.modelID,
|
|
837
|
+
changedFields: granular,
|
|
838
|
+
reasons,
|
|
839
|
+
})
|
|
840
|
+
}
|
|
727
841
|
if (cfg.logPrefixChanges) {
|
|
728
842
|
log("warn", "observed prefix shape change", {
|
|
729
843
|
sid,
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
POLICY_DEEPSEEK,
|
|
17
17
|
POLICY_GLM53,
|
|
18
18
|
POLICY_GPT56,
|
|
19
|
+
POLICY_MIMO26,
|
|
19
20
|
POLICY_NEUTRAL,
|
|
20
21
|
canonicalStringify,
|
|
21
22
|
commonPrefixLength,
|
|
@@ -28,8 +29,11 @@ import {
|
|
|
28
29
|
gptCacheOptionsDelta,
|
|
29
30
|
hitRatePct,
|
|
30
31
|
loadConfig,
|
|
32
|
+
mimoHitRate,
|
|
33
|
+
mimoSessionIdFor,
|
|
31
34
|
nextProcessedCursor,
|
|
32
35
|
parseConfig,
|
|
36
|
+
providerChangeEvent,
|
|
33
37
|
relocateVolatileEnvBlock,
|
|
34
38
|
scanPage,
|
|
35
39
|
shapeDiff,
|
|
@@ -221,6 +225,30 @@ test("recorder writes valid JSONL to a real file", () => {
|
|
|
221
225
|
assert.deepEqual(JSON.parse(lines[0]), { kind: "prefix-change", sid: "s", dimensions: ["system"] })
|
|
222
226
|
})
|
|
223
227
|
|
|
228
|
+
// --- 14. TUI target test -----------------------------------------------------
|
|
229
|
+
|
|
230
|
+
test("TUI target exports the expected plugin module", async () => {
|
|
231
|
+
const mod = await import("../src/tui.mjs")
|
|
232
|
+
assert.equal(mod.default.id, "opencode-cache-engine")
|
|
233
|
+
assert.equal(typeof mod.default.tui, "function")
|
|
234
|
+
assert.equal("server" in mod.default, false)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// --- 15. Package manifest test with TUI --------------------------------------
|
|
238
|
+
|
|
239
|
+
test("package exposes separate server and TUI targets", async () => {
|
|
240
|
+
const { readFileSync } = await import("node:fs")
|
|
241
|
+
const { join } = await import("node:path")
|
|
242
|
+
|
|
243
|
+
const pkg = JSON.parse(
|
|
244
|
+
readFileSync(join(process.cwd(), "package.json"), "utf8")
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
assert.equal(pkg.exports["./server"], "./src/cache-engine.ts")
|
|
248
|
+
assert.equal(pkg.exports["./tui"], "./src/tui.mjs")
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
|
|
224
252
|
// ===========================================================================
|
|
225
253
|
// Provider-aware model detection
|
|
226
254
|
// ===========================================================================
|
|
@@ -269,6 +297,32 @@ test("unrelated GLM models do NOT match GLM-5.3 policy", () => {
|
|
|
269
297
|
assert.equal(detectPolicy(M("zai", "glm-4.5")), POLICY_NEUTRAL)
|
|
270
298
|
})
|
|
271
299
|
|
|
300
|
+
test("MiMo V2.6 Flash/Pro match MiMo policy (openrouter + direct)", () => {
|
|
301
|
+
assert.equal(detectPolicy(M("openrouter", "xiaomi/mimo-v2.6-flash")), POLICY_MIMO26)
|
|
302
|
+
assert.equal(detectPolicy(M("openrouter", "xiaomi/mimo-v2.6-pro")), POLICY_MIMO26)
|
|
303
|
+
assert.equal(detectPolicy(M("xiaomi", "mimo-v2.6-flash")), POLICY_MIMO26)
|
|
304
|
+
assert.equal(detectPolicy(M("xiaomi", "mimo-v2.6-pro")), POLICY_MIMO26)
|
|
305
|
+
// full Model shape via api.id
|
|
306
|
+
assert.equal(
|
|
307
|
+
detectPolicy({ providerID: "openrouter", api: { id: "xiaomi/mimo-v2.6-flash", npm: "@openrouter/ai-sdk-provider" } }),
|
|
308
|
+
POLICY_MIMO26,
|
|
309
|
+
)
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test("MiMo V2.5 and Pro-UltraSpeed do NOT match MiMo policy", () => {
|
|
313
|
+
assert.equal(detectPolicy(M("openrouter", "xiaomi/mimo-v2.5")), POLICY_NEUTRAL)
|
|
314
|
+
assert.equal(detectPolicy(M("openrouter", "xiaomi/mimo-v2.5-pro")), POLICY_NEUTRAL)
|
|
315
|
+
assert.equal(detectPolicy(M("xiaomi", "mimo-v2.5")), POLICY_NEUTRAL)
|
|
316
|
+
assert.equal(detectPolicy(M("xiaomi", "mimo-v2.6-pro-ultraspeed")), POLICY_NEUTRAL)
|
|
317
|
+
assert.equal(detectPolicy(M("xiaomi", "mimo-v2.6-flashx")), POLICY_NEUTRAL)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
test("unrelated MiMo/other models do NOT match MiMo policy", () => {
|
|
321
|
+
assert.equal(detectPolicy(M("openrouter", "xiaomi/mimo-v2")), POLICY_NEUTRAL)
|
|
322
|
+
assert.equal(detectPolicy(M("openrouter", "xiaomi/mimo-v2-flash")), POLICY_NEUTRAL)
|
|
323
|
+
assert.equal(detectPolicy(M("anthropic", "claude-sonnet-4-5")), POLICY_NEUTRAL)
|
|
324
|
+
})
|
|
325
|
+
|
|
272
326
|
test("unrelated models match neutral policy", () => {
|
|
273
327
|
assert.equal(detectPolicy(M("anthropic", "claude-sonnet-4-5")), POLICY_NEUTRAL)
|
|
274
328
|
assert.equal(detectPolicy(M("openrouter", "x-ai/grok-4")), POLICY_NEUTRAL)
|
|
@@ -535,10 +589,16 @@ test("empty current sequence -> no anomalies", () => {
|
|
|
535
589
|
// Config: provider policies
|
|
536
590
|
// ===========================================================================
|
|
537
591
|
|
|
538
|
-
test("config defaults enable all
|
|
592
|
+
test("config defaults enable all four policies", () => {
|
|
539
593
|
const cfg = parseConfig({}, {})
|
|
540
594
|
assert.deepEqual(cfg.policies.deepseek, { enabled: true })
|
|
541
595
|
assert.deepEqual(cfg.policies.glm53, { enabled: true, stabilizeSystem: true, preserveThinkingIntegrity: true })
|
|
596
|
+
assert.deepEqual(cfg.policies.mimo26, {
|
|
597
|
+
enabled: true,
|
|
598
|
+
stabilizeSystem: true,
|
|
599
|
+
stickySession: true,
|
|
600
|
+
preserveThinkingIntegrity: true,
|
|
601
|
+
})
|
|
542
602
|
assert.deepEqual(cfg.policies.gpt56, {
|
|
543
603
|
enabled: true,
|
|
544
604
|
promptCacheKey: true,
|
|
@@ -565,6 +625,7 @@ test("config policy overrides are honored", () => {
|
|
|
565
625
|
},
|
|
566
626
|
glm53: { stabilizeSystem: false },
|
|
567
627
|
deepseek: { enabled: true },
|
|
628
|
+
mimo26: { enabled: false, stabilizeSystem: false, stickySession: false },
|
|
568
629
|
},
|
|
569
630
|
},
|
|
570
631
|
{},
|
|
@@ -578,6 +639,10 @@ test("config policy overrides are honored", () => {
|
|
|
578
639
|
assert.equal(cfg.policies.gpt56.ttl, "1h")
|
|
579
640
|
assert.equal(cfg.policies.glm53.stabilizeSystem, false)
|
|
580
641
|
assert.equal(cfg.policies.glm53.preserveThinkingIntegrity, true)
|
|
642
|
+
assert.equal(cfg.policies.mimo26.enabled, false)
|
|
643
|
+
assert.equal(cfg.policies.mimo26.stabilizeSystem, false)
|
|
644
|
+
assert.equal(cfg.policies.mimo26.stickySession, false)
|
|
645
|
+
assert.equal(cfg.policies.mimo26.preserveThinkingIntegrity, true)
|
|
581
646
|
})
|
|
582
647
|
|
|
583
648
|
test("config invalid policy values fall back to defaults", () => {
|
|
@@ -763,3 +828,143 @@ test("boundary: reasoning-integrity reason tokens", () => {
|
|
|
763
828
|
])
|
|
764
829
|
assert.deepEqual(reasoningIssueReasons(null), [])
|
|
765
830
|
})
|
|
831
|
+
|
|
832
|
+
// ===========================================================================
|
|
833
|
+
// MiMo-V2.6: system env relocation (shared content-preserving helper)
|
|
834
|
+
// ===========================================================================
|
|
835
|
+
|
|
836
|
+
const MIMO_SYSTEM = [
|
|
837
|
+
"You are a senior software engineer.",
|
|
838
|
+
"You are powered by the model named mimo-v2.6-flash. The exact model ID is xiaomi/mimo-v2.6-flash",
|
|
839
|
+
"Here is some useful information about the environment you are running in:",
|
|
840
|
+
"<env>",
|
|
841
|
+
"Working directory: /home/dev/project",
|
|
842
|
+
"Today's date: 2026-08-17",
|
|
843
|
+
"</env>",
|
|
844
|
+
"You MUST follow AGENTS.md instructions and keep your responses concise.",
|
|
845
|
+
].join("\n")
|
|
846
|
+
|
|
847
|
+
test("MiMo env block is relocated to the tail, contents preserved exactly", () => {
|
|
848
|
+
const { text, changed } = relocateVolatileEnvBlock(MIMO_SYSTEM)
|
|
849
|
+
assert.equal(changed, true)
|
|
850
|
+
assert.ok(text.endsWith("</env>"))
|
|
851
|
+
const norm = (t) => t.split("\n").filter((l) => l).sort().join("\n")
|
|
852
|
+
assert.equal(norm(text), norm(MIMO_SYSTEM))
|
|
853
|
+
assert.ok(text.indexOf("You MUST follow") < text.indexOf("You are powered"))
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
test("MiMo relocation is a no-op when the block is already at the tail", () => {
|
|
857
|
+
const already = relocateVolatileEnvBlock(MIMO_SYSTEM).text
|
|
858
|
+
const again = relocateVolatileEnvBlock(already)
|
|
859
|
+
assert.equal(again.changed, false)
|
|
860
|
+
assert.equal(again.text, already)
|
|
861
|
+
})
|
|
862
|
+
|
|
863
|
+
test("MiMo relocation is a no-op when start/end markers are missing", () => {
|
|
864
|
+
const missingStart = "Just a system prompt.\n<env>\nToday's date: x\n</env>"
|
|
865
|
+
assert.equal(relocateVolatileEnvBlock(missingStart).changed, false)
|
|
866
|
+
const missingEnd = "You are powered by the model named mimo-v2.6-flash\nbut never closed"
|
|
867
|
+
assert.equal(relocateVolatileEnvBlock(missingEnd).changed, false)
|
|
868
|
+
})
|
|
869
|
+
|
|
870
|
+
// ===========================================================================
|
|
871
|
+
// MiMo-V2.6: sticky-session identity (pure, derived but not injected)
|
|
872
|
+
// ===========================================================================
|
|
873
|
+
|
|
874
|
+
test("mimoSessionIdFor: deterministic + stable for the same session", () => {
|
|
875
|
+
assert.equal(mimoSessionIdFor("ses_abc123"), mimoSessionIdFor("ses_abc123"))
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
test("mimoSessionIdFor: distinct sessions produce distinct ids", () => {
|
|
879
|
+
assert.notEqual(mimoSessionIdFor("ses_abc"), mimoSessionIdFor("ses_xyz"))
|
|
880
|
+
})
|
|
881
|
+
|
|
882
|
+
test("mimoSessionIdFor: printable, no whitespace, within 256 chars", () => {
|
|
883
|
+
const id = mimoSessionIdFor("ses_" + "a".repeat(500))
|
|
884
|
+
assert.ok(id.length <= 256)
|
|
885
|
+
assert.ok(!/\s/.test(id))
|
|
886
|
+
assert.ok(/^[\x20-\x7E]+$/.test(id))
|
|
887
|
+
})
|
|
888
|
+
|
|
889
|
+
test("mimoSessionIdFor: invalid input -> null (no fabrication)", () => {
|
|
890
|
+
assert.equal(mimoSessionIdFor(undefined), null)
|
|
891
|
+
assert.equal(mimoSessionIdFor(null), null)
|
|
892
|
+
assert.equal(mimoSessionIdFor(""), null)
|
|
893
|
+
assert.equal(mimoSessionIdFor(123), null)
|
|
894
|
+
})
|
|
895
|
+
|
|
896
|
+
test("mimoSessionIdFor: transient request fields cannot alter the id", () => {
|
|
897
|
+
// the helper is a pure function of the session id; extra args are ignored
|
|
898
|
+
assert.equal(mimoSessionIdFor("ses_stable"), mimoSessionIdFor("ses_stable", { turn: 7, temperature: 0.9 }))
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
// ===========================================================================
|
|
902
|
+
// MiMo-V2.6: cached/prompt token metrics
|
|
903
|
+
// ===========================================================================
|
|
904
|
+
|
|
905
|
+
test("mimoHitRate = cachedTokens / promptTokens", () => {
|
|
906
|
+
assert.equal(mimoHitRate(47000, 50000), 94)
|
|
907
|
+
assert.equal(mimoHitRate(0, 100), 0)
|
|
908
|
+
assert.equal(mimoHitRate(100, 100), 100)
|
|
909
|
+
})
|
|
910
|
+
|
|
911
|
+
test("mimoHitRate returns null for zero/unknown denominators (no fabrication)", () => {
|
|
912
|
+
assert.equal(mimoHitRate(0, 0), null)
|
|
913
|
+
assert.equal(mimoHitRate(10, 0), null)
|
|
914
|
+
assert.equal(mimoHitRate(NaN, 100), null)
|
|
915
|
+
assert.equal(mimoHitRate(10, NaN), null)
|
|
916
|
+
assert.equal(mimoHitRate(undefined, 100), null)
|
|
917
|
+
assert.equal(mimoHitRate(10, undefined), null)
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
test("mimoHitRate is NOT the read/(read+write) form", () => {
|
|
921
|
+
// read=80, input=20 -> promptTokens derived 100 -> 80%. The read/(read+write)
|
|
922
|
+
// form would give 100% for (80, 0); they must not be conflated.
|
|
923
|
+
assert.equal(mimoHitRate(80, 80 + 20), 80)
|
|
924
|
+
assert.equal(hitRatePct(80, 0), 100)
|
|
925
|
+
})
|
|
926
|
+
|
|
927
|
+
// ===========================================================================
|
|
928
|
+
// MiMo-V2.6: provider-switch diagnostics
|
|
929
|
+
// ===========================================================================
|
|
930
|
+
|
|
931
|
+
test("providerChangeEvent: no event until two real observations exist", () => {
|
|
932
|
+
assert.deepEqual(providerChangeEvent(null, { providerID: "openrouter", modelID: "xiaomi/mimo-v2.6-flash" }), {
|
|
933
|
+
changed: false,
|
|
934
|
+
from: null,
|
|
935
|
+
to: null,
|
|
936
|
+
})
|
|
937
|
+
assert.equal(providerChangeEvent({ providerID: "openrouter" }, null).changed, false)
|
|
938
|
+
})
|
|
939
|
+
|
|
940
|
+
test("providerChangeEvent: same provider -> no change", () => {
|
|
941
|
+
const a = { providerID: "openrouter", modelID: "xiaomi/mimo-v2.6-flash" }
|
|
942
|
+
const b = { providerID: "openrouter", modelID: "xiaomi/mimo-v2.6-pro" }
|
|
943
|
+
assert.equal(providerChangeEvent(a, b).changed, false)
|
|
944
|
+
})
|
|
945
|
+
|
|
946
|
+
test("providerChangeEvent: different provider -> change with from/to", () => {
|
|
947
|
+
const a = { providerID: "openrouter", modelID: "xiaomi/mimo-v2.6-flash" }
|
|
948
|
+
const b = { providerID: "xiaomi", modelID: "mimo-v2.6-flash" }
|
|
949
|
+
const ev = providerChangeEvent(a, b)
|
|
950
|
+
assert.equal(ev.changed, true)
|
|
951
|
+
assert.deepEqual(ev.from, a)
|
|
952
|
+
assert.deepEqual(ev.to, b)
|
|
953
|
+
})
|
|
954
|
+
|
|
955
|
+
// ===========================================================================
|
|
956
|
+
// MiMo-V2.6: no tool-definition mutation
|
|
957
|
+
// ===========================================================================
|
|
958
|
+
|
|
959
|
+
test("MiMo policy performs no tool mutation (fingerprints are pure inputs)", () => {
|
|
960
|
+
// The plugin never adds a MiMo tool-ordering pass: this runtime already sorts
|
|
961
|
+
// tools alphabetically before the wire. Classifying a model as MiMo must not
|
|
962
|
+
// affect tool fingerprints, which are a pure function of the tool definitions.
|
|
963
|
+
const model = { providerID: "openrouter", modelID: "xiaomi/mimo-v2.6-flash" }
|
|
964
|
+
assert.equal(detectPolicy(model), POLICY_MIMO26)
|
|
965
|
+
const before = { sem: toolFingerprint(TOOLS), wire: toolWireFingerprint(TOOLS) }
|
|
966
|
+
const after = { sem: toolFingerprint(TOOLS), wire: toolWireFingerprint(TOOLS) }
|
|
967
|
+
assert.deepEqual(after, before)
|
|
968
|
+
// semantic fingerprint stays order-insensitive regardless of the policy
|
|
969
|
+
assert.equal(toolFingerprint([...TOOLS].reverse()), before.sem)
|
|
970
|
+
})
|