@mneme-ai/core 2.19.41 → 2.19.43

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.
Files changed (56) hide show
  1. package/dist/cascade_inversion/cascade_inversion.test.d.ts +2 -0
  2. package/dist/cascade_inversion/cascade_inversion.test.d.ts.map +1 -0
  3. package/dist/cascade_inversion/cascade_inversion.test.js +101 -0
  4. package/dist/cascade_inversion/cascade_inversion.test.js.map +1 -0
  5. package/dist/cascade_inversion/index.d.ts +87 -0
  6. package/dist/cascade_inversion/index.d.ts.map +1 -0
  7. package/dist/cascade_inversion/index.js +150 -0
  8. package/dist/cascade_inversion/index.js.map +1 -0
  9. package/dist/cosmic/aurelian_v1942.test.d.ts +2 -0
  10. package/dist/cosmic/aurelian_v1942.test.d.ts.map +1 -0
  11. package/dist/cosmic/aurelian_v1942.test.js +90 -0
  12. package/dist/cosmic/aurelian_v1942.test.js.map +1 -0
  13. package/dist/cosmic/aurelian_v1943.test.d.ts +2 -0
  14. package/dist/cosmic/aurelian_v1943.test.d.ts.map +1 -0
  15. package/dist/cosmic/aurelian_v1943.test.js +76 -0
  16. package/dist/cosmic/aurelian_v1943.test.js.map +1 -0
  17. package/dist/honesty_gate/honesty_gate_v2.test.d.ts +2 -0
  18. package/dist/honesty_gate/honesty_gate_v2.test.d.ts.map +1 -0
  19. package/dist/honesty_gate/honesty_gate_v2.test.js +116 -0
  20. package/dist/honesty_gate/honesty_gate_v2.test.js.map +1 -0
  21. package/dist/honesty_gate/index.d.ts +70 -0
  22. package/dist/honesty_gate/index.d.ts.map +1 -1
  23. package/dist/honesty_gate/index.js +136 -0
  24. package/dist/honesty_gate/index.js.map +1 -1
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/mneme_version.d.ts +18 -7
  30. package/dist/mneme_version.d.ts.map +1 -1
  31. package/dist/mneme_version.js +59 -21
  32. package/dist/mneme_version.js.map +1 -1
  33. package/dist/mneme_version.test.js +15 -0
  34. package/dist/mneme_version.test.js.map +1 -1
  35. package/dist/proof_of_saving/index.d.ts +90 -0
  36. package/dist/proof_of_saving/index.d.ts.map +1 -0
  37. package/dist/proof_of_saving/index.js +153 -0
  38. package/dist/proof_of_saving/index.js.map +1 -0
  39. package/dist/proof_of_saving/proof_of_saving.test.d.ts +2 -0
  40. package/dist/proof_of_saving/proof_of_saving.test.d.ts.map +1 -0
  41. package/dist/proof_of_saving/proof_of_saving.test.js +87 -0
  42. package/dist/proof_of_saving/proof_of_saving.test.js.map +1 -0
  43. package/dist/squadron/acgv_explain.d.ts.map +1 -1
  44. package/dist/squadron/acgv_explain.js +31 -1
  45. package/dist/squadron/acgv_explain.js.map +1 -1
  46. package/dist/squadron/acgv_explain.test.d.ts +2 -0
  47. package/dist/squadron/acgv_explain.test.d.ts.map +1 -0
  48. package/dist/squadron/acgv_explain.test.js +52 -0
  49. package/dist/squadron/acgv_explain.test.js.map +1 -0
  50. package/dist/whats_new.d.ts.map +1 -1
  51. package/dist/whats_new.js +16 -0
  52. package/dist/whats_new.js.map +1 -1
  53. package/dist/wrapper_genesis/index.d.ts.map +1 -1
  54. package/dist/wrapper_genesis/index.js +13 -0
  55. package/dist/wrapper_genesis/index.js.map +1 -1
  56. package/package.json +1 -1
@@ -1,15 +1,26 @@
1
1
  /**
2
2
  * Resolve the running Mneme version from disk (package.json), with the
3
3
  * env-var as a secondary source. NEVER falls back to a hard-coded
4
- * string -- those bit-rot the moment a new version ships (root cause
5
- * of bug #5: MCP server reporting "1.27.9" while CLI shows "1.43.0").
4
+ * string -- those bit-rot the moment a new version ships.
6
5
  *
7
- * Lookup order:
6
+ * Bug history:
7
+ * - v1.43 bug #5: MCP server reporting "1.27.9" while CLI shows "1.43.0"
8
+ * (fixed by walking up for @mneme-ai/core package.json).
9
+ * - v2.19.41 N4: `mneme system upgrade --json '{"mode":"check"}'`
10
+ * reported current=0.0.0 because env was empty AND resolver only
11
+ * accepted @mneme-ai/core name. When MCP server runs from npm-global
12
+ * install, packages/mcp is a sibling of core under node_modules, not
13
+ * a parent -- walking up never found a "@mneme-ai/core" package.json.
14
+ *
15
+ * v2.19.43 lookup order (robust against npm-global install where sibling
16
+ * packages don't have core as a parent):
8
17
  * 1. process.env.npm_package_version (set when launched via `npm run`)
9
- * 2. Walk up from this module's directory looking for `package.json`
10
- * with `"name": "@mneme-ai/core"`. That's the source of truth.
11
- * 3. Final fallback: literal string "0.0.0-unknown" -- chosen so it's
12
- * OBVIOUSLY wrong when surfaced, instead of silently outdated.
18
+ * 2. Walk up from this module's directory for ANY package.json whose
19
+ * name matches the Mneme family (@mneme-ai/{core,mcp,cli,embeddings,
20
+ * correlator}, or the umbrella mneme-ai). All 5 ship lock-step.
21
+ * 3. Walk up for any package.json with a Mneme-shaped name (covers
22
+ * future families we add).
23
+ * 4. Final fallback: literal "0.0.0-unknown" so the bad value is OBVIOUS.
13
24
  */
14
25
  export declare function resolveMnemeVersion(): string;
15
26
  /** For tests: clear the memoized value so a re-read happens. */
@@ -1 +1 @@
1
- {"version":3,"file":"mneme_version.d.ts","sourceRoot":"","sources":["../src/mneme_version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,wBAAgB,mBAAmB,IAAI,MAAM,CA0B5C;AAED,gEAAgE;AAChE,wBAAgB,uBAAuB,IAAI,IAAI,CAAmB"}
1
+ {"version":3,"file":"mneme_version.d.ts","sourceRoot":"","sources":["../src/mneme_version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAoCH,wBAAgB,mBAAmB,IAAI,MAAM,CAwB5C;AAED,gEAAgE;AAChE,wBAAgB,uBAAuB,IAAI,IAAI,CAAmB"}
@@ -1,44 +1,82 @@
1
1
  /**
2
2
  * Resolve the running Mneme version from disk (package.json), with the
3
3
  * env-var as a secondary source. NEVER falls back to a hard-coded
4
- * string -- those bit-rot the moment a new version ships (root cause
5
- * of bug #5: MCP server reporting "1.27.9" while CLI shows "1.43.0").
4
+ * string -- those bit-rot the moment a new version ships.
6
5
  *
7
- * Lookup order:
6
+ * Bug history:
7
+ * - v1.43 bug #5: MCP server reporting "1.27.9" while CLI shows "1.43.0"
8
+ * (fixed by walking up for @mneme-ai/core package.json).
9
+ * - v2.19.41 N4: `mneme system upgrade --json '{"mode":"check"}'`
10
+ * reported current=0.0.0 because env was empty AND resolver only
11
+ * accepted @mneme-ai/core name. When MCP server runs from npm-global
12
+ * install, packages/mcp is a sibling of core under node_modules, not
13
+ * a parent -- walking up never found a "@mneme-ai/core" package.json.
14
+ *
15
+ * v2.19.43 lookup order (robust against npm-global install where sibling
16
+ * packages don't have core as a parent):
8
17
  * 1. process.env.npm_package_version (set when launched via `npm run`)
9
- * 2. Walk up from this module's directory looking for `package.json`
10
- * with `"name": "@mneme-ai/core"`. That's the source of truth.
11
- * 3. Final fallback: literal string "0.0.0-unknown" -- chosen so it's
12
- * OBVIOUSLY wrong when surfaced, instead of silently outdated.
18
+ * 2. Walk up from this module's directory for ANY package.json whose
19
+ * name matches the Mneme family (@mneme-ai/{core,mcp,cli,embeddings,
20
+ * correlator}, or the umbrella mneme-ai). All 5 ship lock-step.
21
+ * 3. Walk up for any package.json with a Mneme-shaped name (covers
22
+ * future families we add).
23
+ * 4. Final fallback: literal "0.0.0-unknown" so the bad value is OBVIOUS.
13
24
  */
14
25
  import { existsSync, readFileSync } from "node:fs";
15
26
  import { dirname, join } from "node:path";
16
27
  import { fileURLToPath } from "node:url";
17
28
  let cached = null;
29
+ const MNEME_FAMILY = new Set([
30
+ "@mneme-ai/core",
31
+ "@mneme-ai/mcp",
32
+ "@mneme-ai/cli",
33
+ "@mneme-ai/embeddings",
34
+ "@mneme-ai/correlator",
35
+ "mneme-ai",
36
+ ]);
37
+ function walkUpForPackageJson(startDir, accept) {
38
+ let dir = startDir;
39
+ for (let i = 0; i < 12; i++) {
40
+ const pkg = join(dir, "package.json");
41
+ if (existsSync(pkg)) {
42
+ try {
43
+ const parsed = JSON.parse(readFileSync(pkg, "utf8"));
44
+ if (typeof parsed.version === "string" && /^\d+\.\d+\.\d+/.test(parsed.version) && accept(parsed.name)) {
45
+ return parsed.version;
46
+ }
47
+ }
48
+ catch { /* keep walking */ }
49
+ }
50
+ const parent = dirname(dir);
51
+ if (parent === dir)
52
+ break;
53
+ dir = parent;
54
+ }
55
+ return null;
56
+ }
18
57
  export function resolveMnemeVersion() {
19
58
  if (cached)
20
59
  return cached;
60
+ // 1) env var (npm run only)
21
61
  const env = process.env["npm_package_version"];
22
62
  if (env && /^\d+\.\d+\.\d+/.test(env)) {
23
63
  cached = env;
24
64
  return env;
25
65
  }
66
+ // 2) walk up for ANY Mneme-family package.json
26
67
  try {
27
68
  const here = fileURLToPath(import.meta.url);
28
- let dir = dirname(here);
29
- for (let i = 0; i < 10; i++) {
30
- const pkg = join(dir, "package.json");
31
- if (existsSync(pkg)) {
32
- const parsed = JSON.parse(readFileSync(pkg, "utf8"));
33
- if (parsed.name === "@mneme-ai/core" && typeof parsed.version === "string") {
34
- cached = parsed.version;
35
- return parsed.version;
36
- }
37
- }
38
- const parent = dirname(dir);
39
- if (parent === dir)
40
- break;
41
- dir = parent;
69
+ const startDir = dirname(here);
70
+ const familyHit = walkUpForPackageJson(startDir, (name) => !!name && MNEME_FAMILY.has(name));
71
+ if (familyHit) {
72
+ cached = familyHit;
73
+ return familyHit;
74
+ }
75
+ // 3) widen the net to any Mneme-shaped name
76
+ const anyHit = walkUpForPackageJson(startDir, (name) => !!name && (name.startsWith("@mneme-ai/") || name === "mneme-ai" || name === "mneme"));
77
+ if (anyHit) {
78
+ cached = anyHit;
79
+ return anyHit;
42
80
  }
43
81
  }
44
82
  catch { /* fall through */ }
@@ -1 +1 @@
1
- {"version":3,"file":"mneme_version.js","sourceRoot":"","sources":["../src/mneme_version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,IAAI,MAAM,GAAkB,IAAI,CAAC;AAEjC,MAAM,UAAU,mBAAmB;IACjC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAC/C,IAAI,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,CAAC;QACb,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YACtC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAwC,CAAC;gBAC5F,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBAC3E,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;oBACxB,OAAO,MAAM,CAAC,OAAO,CAAC;gBACxB,CAAC;YACH,CAAC;YACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,MAAM,KAAK,GAAG;gBAAE,MAAM;YAC1B,GAAG,GAAG,MAAM,CAAC;QACf,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;IAC9B,MAAM,GAAG,eAAe,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,uBAAuB,KAAW,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"mneme_version.js","sourceRoot":"","sources":["../src/mneme_version.ts"],"names":[],"mappings":"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"}
@@ -39,5 +39,20 @@ describe("mneme_version · resolution", () => {
39
39
  const v = resolveMnemeVersion();
40
40
  expect(v).not.toBe("not-a-version");
41
41
  });
42
+ // v2.19.43 N4 regression — pre-v2.19.43 the resolver only accepted
43
+ // @mneme-ai/core; on npm-global install the MCP package is a SIBLING
44
+ // not a parent, so walking up never found core's package.json and the
45
+ // resolver returned "0.0.0-unknown". Caller (system.upgrade) then
46
+ // reported "current: 0.0.0" + advertised an upgrade with wrong baseline.
47
+ it("never returns 0.0.0 when running from a real install (regression for N4)", () => {
48
+ delete process.env["npm_package_version"];
49
+ _resetMnemeVersionCache();
50
+ const v = resolveMnemeVersion();
51
+ expect(v).not.toBe("0.0.0");
52
+ expect(v).not.toBe("0.0.0-unknown");
53
+ // Should pick up a sibling Mneme-family package.json even when running
54
+ // from packages/mcp or packages/cli.
55
+ expect(v).toMatch(/^\d+\.\d+\.\d+/);
56
+ });
42
57
  });
43
58
  //# sourceMappingURL=mneme_version.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mneme_version.test.js","sourceRoot":"","sources":["../src/mneme_version.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAElF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,IAAI,KAAyB,CAAC;IAC9B,UAAU,CAAC,GAAG,EAAE;QACd,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAC3C,uBAAuB,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;;YAC9D,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG,KAAK,CAAC;QAChD,uBAAuB,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC;QAC7C,MAAM,CAAC,mBAAmB,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC;QAC7C,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG,OAAO,CAAC;QAC7C,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;QAChC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;QAC1C,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;QAChC,4EAA4E;QAC5E,iFAAiF;QACjF,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACpC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,GAAG,eAAe,CAAC;QACrD,MAAM,CAAC,GAAG,mBAAmB,EAAE,CAAC;QAChC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"mneme_version.test.js","sourceRoot":"","sources":["../src/mneme_version.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * v2.19.42 — PROOF OF SAVING (the wild idea no other AI tool ships).
3
+ *
4
+ * "OpenAI / Anthropic charge you per token. Enterprises pay millions
5
+ * and have no third-party-verifiable evidence of what they saved by
6
+ * routing through any optimisation layer. PROOF OF SAVING mints an
7
+ * HMAC-signed Merkle-rooted certificate from a batch of Governor
8
+ * decisions: a Saving Manifest the procurement team can hand to
9
+ * auditors. Replayable, tamper-evident, vendor-neutral. The
10
+ * auditor doesn't need to trust Mneme's implementation — they
11
+ * re-hash the decisions and re-derive the same Merkle root.
12
+ *
13
+ * This composes onto v2.19.40 TOKEN GOVERNOR (decisions are the
14
+ * leaves) + v2.19.34 APOSTILLE (same HMAC-chain pattern) + v2.19.34
15
+ * ETERNITY (certificate pins across vendors so the saving survives
16
+ * even if the optimiser provider disappears). It is the missing
17
+ * enterprise procurement primitive: 'show me PROOF you saved my
18
+ * company money.'
19
+ *
20
+ * Wild because no AI optimisation vendor (LangChain / Helicone /
21
+ * Portkey / Vellum / Braintrust) issues replayable savings
22
+ * certificates — they show you a dashboard chart and ask you to
23
+ * trust the SQL behind it. Mneme issues a 4KB JSON certificate
24
+ * auditors can verify offline in 5ms."
25
+ */
26
+ declare const PROTOCOL_VERSION: 1;
27
+ export interface GovernedDecisionShape {
28
+ /** Stable id (some callers use signature; we sha256 the whole object). */
29
+ signature: string;
30
+ tokensUsedActual: number;
31
+ estTokensSavedVsDirect: number;
32
+ stage: number;
33
+ }
34
+ export interface SavingsCertificate {
35
+ v: typeof PROTOCOL_VERSION;
36
+ /** ISO timestamp of cert mint. */
37
+ mintedAt: string;
38
+ /** Window the savings cover. */
39
+ windowStartMs: number;
40
+ windowEndMs: number;
41
+ /** Number of governed AI calls in the window. */
42
+ decisionCount: number;
43
+ /** Total tokens that would have been spent without Mneme. */
44
+ totalDirectTokens: number;
45
+ /** Total tokens actually spent through the Governor cascade. */
46
+ totalActualTokens: number;
47
+ /** totalDirectTokens - totalActualTokens. Always >= 0 by design. */
48
+ totalTokensSaved: number;
49
+ /** Hit rate per stage (Stage 1 = cache, Stage 2 = local, etc). */
50
+ stageBreakdown: Record<string, {
51
+ calls: number;
52
+ tokensSaved: number;
53
+ }>;
54
+ /** Estimated USD saved at caller-supplied $-per-token. */
55
+ estUsdSaved: number;
56
+ /** Caller-supplied $-per-token used for the USD estimate. */
57
+ usdPerToken: number;
58
+ /** Merkle root over decision signatures (replay-verifiable). */
59
+ merkleRoot: string;
60
+ /** Number of leaves in the Merkle tree. */
61
+ merkleLeafCount: number;
62
+ /** HMAC over the canonicalised cert body. */
63
+ hmac: string;
64
+ }
65
+ export interface MintCertInput {
66
+ decisions: GovernedDecisionShape[];
67
+ windowStartMs: number;
68
+ windowEndMs: number;
69
+ /** Caller's blended $-per-token estimate. Default: $0.000002 (≈$2/1M). */
70
+ usdPerToken?: number;
71
+ /** Optional HMAC secret. Default: env MNEME_PROOF_SECRET. */
72
+ secret?: string;
73
+ /** Optional mint timestamp (defaults to Date.now). */
74
+ nowMs?: number;
75
+ }
76
+ /** Mint a savings certificate from a batch of Governor decisions. */
77
+ export declare function mintSavingsCertificate(input: MintCertInput): SavingsCertificate;
78
+ /**
79
+ * Verify a savings certificate. Returns { ok, reason } so callers can
80
+ * surface specific failure reasons (HMAC mismatch / Merkle mismatch /
81
+ * arithmetic violation).
82
+ */
83
+ export declare function verifySavingsCertificate(cert: SavingsCertificate, decisions: GovernedDecisionShape[], secret?: string): {
84
+ ok: boolean;
85
+ reason?: string;
86
+ };
87
+ /** Render a human-readable summary safe to surface to a procurement dashboard. */
88
+ export declare function formatCertificate(cert: SavingsCertificate): string;
89
+ export {};
90
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proof_of_saving/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,QAAA,MAAM,gBAAgB,EAAG,CAAU,CAAC;AAEpC,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAC3B,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gEAAgE;IAChE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACvE,0DAA0D;IAC1D,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,qBAAqB,EAAE,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqCD,qEAAqE;AACrE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,kBAAkB,CA2C/E;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,kBAAkB,EACxB,SAAS,EAAE,qBAAqB,EAAE,EAClC,MAAM,CAAC,EAAE,MAAM,GACd;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBlC;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAkBlE"}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * v2.19.42 — PROOF OF SAVING (the wild idea no other AI tool ships).
3
+ *
4
+ * "OpenAI / Anthropic charge you per token. Enterprises pay millions
5
+ * and have no third-party-verifiable evidence of what they saved by
6
+ * routing through any optimisation layer. PROOF OF SAVING mints an
7
+ * HMAC-signed Merkle-rooted certificate from a batch of Governor
8
+ * decisions: a Saving Manifest the procurement team can hand to
9
+ * auditors. Replayable, tamper-evident, vendor-neutral. The
10
+ * auditor doesn't need to trust Mneme's implementation — they
11
+ * re-hash the decisions and re-derive the same Merkle root.
12
+ *
13
+ * This composes onto v2.19.40 TOKEN GOVERNOR (decisions are the
14
+ * leaves) + v2.19.34 APOSTILLE (same HMAC-chain pattern) + v2.19.34
15
+ * ETERNITY (certificate pins across vendors so the saving survives
16
+ * even if the optimiser provider disappears). It is the missing
17
+ * enterprise procurement primitive: 'show me PROOF you saved my
18
+ * company money.'
19
+ *
20
+ * Wild because no AI optimisation vendor (LangChain / Helicone /
21
+ * Portkey / Vellum / Braintrust) issues replayable savings
22
+ * certificates — they show you a dashboard chart and ask you to
23
+ * trust the SQL behind it. Mneme issues a 4KB JSON certificate
24
+ * auditors can verify offline in 5ms."
25
+ */
26
+ import { createHmac } from "node:crypto";
27
+ const PROTOCOL_VERSION = 1;
28
+ function defaultSecret() {
29
+ return process.env["MNEME_PROOF_SECRET"] || `mneme-proof-of-saving-v${PROTOCOL_VERSION}`;
30
+ }
31
+ function canon(v) {
32
+ if (v === null || typeof v !== "object")
33
+ return JSON.stringify(v);
34
+ if (Array.isArray(v))
35
+ return "[" + v.map(canon).join(",") + "]";
36
+ const keys = Object.keys(v).sort();
37
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + canon(v[k])).join(",") + "}";
38
+ }
39
+ function sha256Hex(s) {
40
+ return createHmac("sha256", "mneme-proof-leaf").update(s).digest("hex");
41
+ }
42
+ function signCert(body, secret) {
43
+ return createHmac("sha256", secret).update(canon(body)).digest("hex");
44
+ }
45
+ /** Build a Merkle root over an array of leaf strings. Deterministic. */
46
+ function merkleRoot(leaves) {
47
+ if (leaves.length === 0)
48
+ return sha256Hex("∅");
49
+ let level = leaves.map(sha256Hex);
50
+ while (level.length > 1) {
51
+ const next = [];
52
+ for (let i = 0; i < level.length; i += 2) {
53
+ const a = level[i];
54
+ const b = level[i + 1] ?? a;
55
+ next.push(sha256Hex(a + ":" + b));
56
+ }
57
+ level = next;
58
+ }
59
+ return level[0];
60
+ }
61
+ /** Mint a savings certificate from a batch of Governor decisions. */
62
+ export function mintSavingsCertificate(input) {
63
+ const usdPerToken = input.usdPerToken ?? 0.000002; // $2 / 1M tokens default
64
+ const secret = input.secret ?? defaultSecret();
65
+ const nowMs = input.nowMs ?? Date.now();
66
+ const stageBreakdown = {
67
+ "1": { calls: 0, tokensSaved: 0 },
68
+ "2": { calls: 0, tokensSaved: 0 },
69
+ "3": { calls: 0, tokensSaved: 0 },
70
+ "4": { calls: 0, tokensSaved: 0 },
71
+ "5": { calls: 0, tokensSaved: 0 },
72
+ };
73
+ let totalActual = 0;
74
+ let totalSaved = 0;
75
+ for (const d of input.decisions) {
76
+ totalActual += d.tokensUsedActual;
77
+ totalSaved += d.estTokensSavedVsDirect;
78
+ const k = String(d.stage);
79
+ if (stageBreakdown[k]) {
80
+ stageBreakdown[k].calls += 1;
81
+ stageBreakdown[k].tokensSaved += d.estTokensSavedVsDirect;
82
+ }
83
+ }
84
+ const totalDirect = totalActual + totalSaved;
85
+ const leaves = input.decisions.map((d) => d.signature);
86
+ const root = merkleRoot(leaves);
87
+ const body = {
88
+ v: PROTOCOL_VERSION,
89
+ mintedAt: new Date(nowMs).toISOString(),
90
+ windowStartMs: input.windowStartMs,
91
+ windowEndMs: input.windowEndMs,
92
+ decisionCount: input.decisions.length,
93
+ totalDirectTokens: totalDirect,
94
+ totalActualTokens: totalActual,
95
+ totalTokensSaved: totalSaved,
96
+ stageBreakdown,
97
+ estUsdSaved: totalSaved * usdPerToken,
98
+ usdPerToken,
99
+ merkleRoot: root,
100
+ merkleLeafCount: leaves.length,
101
+ };
102
+ return { ...body, hmac: signCert(body, secret) };
103
+ }
104
+ /**
105
+ * Verify a savings certificate. Returns { ok, reason } so callers can
106
+ * surface specific failure reasons (HMAC mismatch / Merkle mismatch /
107
+ * arithmetic violation).
108
+ */
109
+ export function verifySavingsCertificate(cert, decisions, secret) {
110
+ const sec = secret ?? defaultSecret();
111
+ // 1) Arithmetic invariants must hold internally.
112
+ if (cert.totalTokensSaved < 0)
113
+ return { ok: false, reason: "negative totalTokensSaved" };
114
+ if (cert.totalDirectTokens !== cert.totalActualTokens + cert.totalTokensSaved) {
115
+ return { ok: false, reason: "totalDirect != actual + saved" };
116
+ }
117
+ if (cert.decisionCount !== decisions.length) {
118
+ return { ok: false, reason: `decisionCount ${cert.decisionCount} != supplied ${decisions.length}` };
119
+ }
120
+ // 2) Merkle root must match the supplied decisions.
121
+ const recomputedRoot = merkleRoot(decisions.map((d) => d.signature));
122
+ if (recomputedRoot !== cert.merkleRoot) {
123
+ return { ok: false, reason: `Merkle root mismatch (expected ${cert.merkleRoot}, recomputed ${recomputedRoot})` };
124
+ }
125
+ // 3) HMAC must verify (using the same secret).
126
+ const { hmac, ...body } = cert;
127
+ const expected = signCert(body, sec);
128
+ if (expected !== hmac)
129
+ return { ok: false, reason: "HMAC mismatch — forged certificate or wrong secret" };
130
+ return { ok: true };
131
+ }
132
+ /** Render a human-readable summary safe to surface to a procurement dashboard. */
133
+ export function formatCertificate(cert) {
134
+ const days = Math.max(1, Math.round((cert.windowEndMs - cert.windowStartMs) / 86_400_000));
135
+ const lines = [];
136
+ lines.push(`🪙 MNEME PROOF OF SAVING — v${cert.v}`);
137
+ lines.push(` Window: ${new Date(cert.windowStartMs).toISOString().slice(0, 10)} → ${new Date(cert.windowEndMs).toISOString().slice(0, 10)} (${days} days)`);
138
+ lines.push(` Calls governed: ${cert.decisionCount.toLocaleString()}`);
139
+ lines.push(` Tokens that would have shipped direct: ${cert.totalDirectTokens.toLocaleString()}`);
140
+ lines.push(` Tokens actually spent via Governor: ${cert.totalActualTokens.toLocaleString()}`);
141
+ lines.push(` Tokens saved: ${cert.totalTokensSaved.toLocaleString()} (${((cert.totalTokensSaved / Math.max(1, cert.totalDirectTokens)) * 100).toFixed(1)}%)`);
142
+ lines.push(` USD saved (@ ${cert.usdPerToken}/token): $${cert.estUsdSaved.toFixed(2)}`);
143
+ lines.push(` Stage breakdown:`);
144
+ for (const [stage, stats] of Object.entries(cert.stageBreakdown)) {
145
+ if (stats.calls === 0)
146
+ continue;
147
+ lines.push(` Stage ${stage}: ${stats.calls} calls, ${stats.tokensSaved.toLocaleString()} tokens saved`);
148
+ }
149
+ lines.push(` Merkle root: ${cert.merkleRoot.slice(0, 16)}… (${cert.merkleLeafCount} leaves)`);
150
+ lines.push(` HMAC: ${cert.hmac.slice(0, 16)}… (verify with mneme.proof.verify)`);
151
+ return lines.join("\n");
152
+ }
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/proof_of_saving/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=proof_of_saving.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proof_of_saving.test.d.ts","sourceRoot":"","sources":["../../src/proof_of_saving/proof_of_saving.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,87 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mintSavingsCertificate, verifySavingsCertificate, formatCertificate, } from "./index.js";
3
+ const SECRET = "proof-test-secret-77";
4
+ function makeDecisions(n, fixedSaving = 100, fixedActual = 50) {
5
+ const out = [];
6
+ for (let i = 0; i < n; i++) {
7
+ out.push({
8
+ signature: `sig-${i}-${Math.random().toString(36).slice(2, 10)}`,
9
+ tokensUsedActual: fixedActual,
10
+ estTokensSavedVsDirect: fixedSaving,
11
+ stage: ((i % 4) + 1),
12
+ });
13
+ }
14
+ return out;
15
+ }
16
+ describe("v2.19.42 PROOF OF SAVING · mint + verify round trip", () => {
17
+ it("mints a certificate with correct arithmetic", () => {
18
+ const decisions = makeDecisions(10);
19
+ const cert = mintSavingsCertificate({ decisions, windowStartMs: 0, windowEndMs: 1000, secret: SECRET });
20
+ expect(cert.decisionCount).toBe(10);
21
+ expect(cert.totalActualTokens).toBe(500);
22
+ expect(cert.totalTokensSaved).toBe(1000);
23
+ expect(cert.totalDirectTokens).toBe(1500);
24
+ expect(cert.merkleLeafCount).toBe(10);
25
+ expect(cert.hmac.length).toBe(64);
26
+ });
27
+ it("verify returns ok on a fresh cert", () => {
28
+ const decisions = makeDecisions(5);
29
+ const cert = mintSavingsCertificate({ decisions, windowStartMs: 0, windowEndMs: 1, secret: SECRET });
30
+ expect(verifySavingsCertificate(cert, decisions, SECRET).ok).toBe(true);
31
+ });
32
+ it("verify detects tampered HMAC", () => {
33
+ const decisions = makeDecisions(3);
34
+ const cert = mintSavingsCertificate({ decisions, windowStartMs: 0, windowEndMs: 1, secret: SECRET });
35
+ const tampered = { ...cert, totalTokensSaved: cert.totalTokensSaved + 100 };
36
+ expect(verifySavingsCertificate(tampered, decisions, SECRET).ok).toBe(false);
37
+ });
38
+ it("verify detects swapped decisions (Merkle mismatch)", () => {
39
+ const a = makeDecisions(3);
40
+ const cert = mintSavingsCertificate({ decisions: a, windowStartMs: 0, windowEndMs: 1, secret: SECRET });
41
+ const b = makeDecisions(3);
42
+ const r = verifySavingsCertificate(cert, b, SECRET);
43
+ expect(r.ok).toBe(false);
44
+ expect(r.reason).toContain("Merkle");
45
+ });
46
+ it("verify detects wrong secret", () => {
47
+ const decisions = makeDecisions(2);
48
+ const cert = mintSavingsCertificate({ decisions, windowStartMs: 0, windowEndMs: 1, secret: SECRET });
49
+ expect(verifySavingsCertificate(cert, decisions, "wrong-secret").ok).toBe(false);
50
+ });
51
+ it("empty decisions list still produces a valid cert (zero savings)", () => {
52
+ const cert = mintSavingsCertificate({ decisions: [], windowStartMs: 0, windowEndMs: 1, secret: SECRET });
53
+ expect(cert.decisionCount).toBe(0);
54
+ expect(cert.totalTokensSaved).toBe(0);
55
+ expect(verifySavingsCertificate(cert, [], SECRET).ok).toBe(true);
56
+ });
57
+ it("USD estimate uses caller-supplied usdPerToken", () => {
58
+ const cert = mintSavingsCertificate({ decisions: makeDecisions(10, 1000, 0), windowStartMs: 0, windowEndMs: 1, usdPerToken: 0.00001, secret: SECRET });
59
+ expect(cert.estUsdSaved).toBe(10000 * 0.00001);
60
+ });
61
+ });
62
+ describe("v2.19.42 PROOF OF SAVING · stage breakdown + format", () => {
63
+ it("stage breakdown counts calls and tokens per stage", () => {
64
+ const decisions = makeDecisions(20);
65
+ const cert = mintSavingsCertificate({ decisions, windowStartMs: 0, windowEndMs: 1, secret: SECRET });
66
+ const total = Object.values(cert.stageBreakdown).reduce((s, b) => s + b.calls, 0);
67
+ expect(total).toBe(20);
68
+ });
69
+ it("formatCertificate produces multi-line human-readable text", () => {
70
+ const cert = mintSavingsCertificate({ decisions: makeDecisions(3), windowStartMs: 0, windowEndMs: 1, secret: SECRET });
71
+ const txt = formatCertificate(cert);
72
+ expect(txt).toContain("PROOF OF SAVING");
73
+ expect(txt).toContain("Tokens saved");
74
+ expect(txt).toContain("Merkle root");
75
+ });
76
+ });
77
+ describe("v2.19.42 PROOF OF SAVING · 1000-iter fuzz", () => {
78
+ it("mint + verify cycle never throws on random batches", () => {
79
+ for (let i = 0; i < 1000; i++) {
80
+ const n = Math.floor(Math.random() * 30);
81
+ const decisions = makeDecisions(n, Math.floor(Math.random() * 1000), Math.floor(Math.random() * 500));
82
+ const cert = mintSavingsCertificate({ decisions, windowStartMs: 0, windowEndMs: i, secret: SECRET, nowMs: i });
83
+ expect(verifySavingsCertificate(cert, decisions, SECRET).ok).toBe(true);
84
+ }
85
+ });
86
+ });
87
+ //# sourceMappingURL=proof_of_saving.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proof_of_saving.test.js","sourceRoot":"","sources":["../../src/proof_of_saving/proof_of_saving.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"acgv_explain.d.ts","sourceRoot":"","sources":["../../src/squadron/acgv_explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,YAAY,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IACnD,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,0DAA0D;AAC1D,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAyE3E;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAc7E"}
1
+ {"version":3,"file":"acgv_explain.d.ts","sourceRoot":"","sources":["../../src/squadron/acgv_explain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,YAAY,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;IACnD,sDAAsD;IACtD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,0DAA0D;AAC1D,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAyE3E;AA+BD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAe7E"}
@@ -84,17 +84,47 @@ export function explain(result, claim) {
84
84
  }
85
85
  }
86
86
  }
87
+ /**
88
+ * v2.19.43 N8 fix — presentation consistency invariant.
89
+ *
90
+ * User audit (2026-05-18): `mneme verify "Mneme is written in Rust AND
91
+ * mneme.truth.forensic is registered"` rendered:
92
+ * 🌑 IMPOSSIBLE -- REFUTED -- language=rust is impossible ...
93
+ * ...
94
+ * ✅ TRUTH-FORENSIC verdict: ACCEPTED. Every assertion grounded ...
95
+ *
96
+ * The headline used trafficLight=black (🌑 IMPOSSIBLE) but the plain
97
+ * text leaked a ✅ from the appended forensic explanation. Both emoji
98
+ * present → user confused about which verdict won.
99
+ *
100
+ * Invariant: the headline emoji is canonical; any conflicting
101
+ * verdict glyphs in the plain block get neutralised to a neutral mark
102
+ * (●). The verdict text itself is preserved so power-users still see
103
+ * "TRUTH-FORENSIC verdict: ACCEPTED" — only the contradicting emoji
104
+ * is stripped.
105
+ */
106
+ function neutraliseConflictingEmoji(plain, headlineLight) {
107
+ if (!plain)
108
+ return plain;
109
+ // Map every other-traffic-light glyph to a neutral bullet.
110
+ const neutralise = (glyph) => `●`;
111
+ const allow = { green: "✅", yellow: "⚠️", red: "❌", black: "🌑" };
112
+ const keep = allow[headlineLight];
113
+ return plain
114
+ .replace(/✅|⚠️|❌|🌑/g, (m) => (m === keep ? m : neutralise(m)));
115
+ }
87
116
  /** Format the explained verdict for terminal output. ~10 lines, scannable. */
88
117
  export function renderExplained(ev, claim) {
89
118
  const glyph = ev.trafficLight === "green" ? "✅" : ev.trafficLight === "yellow" ? "⚠️" : ev.trafficLight === "red" ? "❌" : "🌑";
90
119
  const tone = ev.trafficLight === "green" ? "TRUSTWORTHY" : ev.trafficLight === "yellow" ? "MIXED" : ev.trafficLight === "red" ? "REFUTED" : "IMPOSSIBLE";
120
+ const cleanPlain = neutraliseConflictingEmoji(ev.plain, ev.trafficLight);
91
121
  return [
92
122
  `${glyph} ${tone} -- ${ev.headline}`,
93
123
  ``,
94
124
  `Claim: "${claim.length > 90 ? claim.slice(0, 87) + "..." : claim}"`,
95
125
  ``,
96
126
  `What this means:`,
97
- ` ${ev.plain}`,
127
+ ` ${cleanPlain}`,
98
128
  ``,
99
129
  `Next step:`,
100
130
  ` -> ${ev.nextAction}`,