@mneme-ai/core 2.19.19 → 2.19.21

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 (51) hide show
  1. package/dist/cosmic/aurelian_v1920.test.d.ts +2 -0
  2. package/dist/cosmic/aurelian_v1920.test.d.ts.map +1 -0
  3. package/dist/cosmic/aurelian_v1920.test.js +61 -0
  4. package/dist/cosmic/aurelian_v1920.test.js.map +1 -0
  5. package/dist/cosmic/aurelian_v1921.test.d.ts +2 -0
  6. package/dist/cosmic/aurelian_v1921.test.d.ts.map +1 -0
  7. package/dist/cosmic/aurelian_v1921.test.js +48 -0
  8. package/dist/cosmic/aurelian_v1921.test.js.map +1 -0
  9. package/dist/index.d.ts +4 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/provenance_dna/index.d.ts +115 -0
  14. package/dist/provenance_dna/index.d.ts.map +1 -0
  15. package/dist/provenance_dna/index.js +242 -0
  16. package/dist/provenance_dna/index.js.map +1 -0
  17. package/dist/provenance_dna/provenance_dna.test.d.ts +2 -0
  18. package/dist/provenance_dna/provenance_dna.test.d.ts.map +1 -0
  19. package/dist/provenance_dna/provenance_dna.test.js +279 -0
  20. package/dist/provenance_dna/provenance_dna.test.js.map +1 -0
  21. package/dist/reverse_caption_injection/index.d.ts +91 -0
  22. package/dist/reverse_caption_injection/index.d.ts.map +1 -0
  23. package/dist/reverse_caption_injection/index.js +162 -0
  24. package/dist/reverse_caption_injection/index.js.map +1 -0
  25. package/dist/reverse_caption_injection/reverse_caption_injection.test.d.ts +2 -0
  26. package/dist/reverse_caption_injection/reverse_caption_injection.test.d.ts.map +1 -0
  27. package/dist/reverse_caption_injection/reverse_caption_injection.test.js +177 -0
  28. package/dist/reverse_caption_injection/reverse_caption_injection.test.js.map +1 -0
  29. package/dist/snn_auto_promote/index.d.ts +82 -0
  30. package/dist/snn_auto_promote/index.d.ts.map +1 -0
  31. package/dist/snn_auto_promote/index.js +154 -0
  32. package/dist/snn_auto_promote/index.js.map +1 -0
  33. package/dist/snn_auto_promote/snn_auto_promote.test.d.ts +2 -0
  34. package/dist/snn_auto_promote/snn_auto_promote.test.d.ts.map +1 -0
  35. package/dist/snn_auto_promote/snn_auto_promote.test.js +164 -0
  36. package/dist/snn_auto_promote/snn_auto_promote.test.js.map +1 -0
  37. package/dist/textron_captcha/index.d.ts +147 -0
  38. package/dist/textron_captcha/index.d.ts.map +1 -0
  39. package/dist/textron_captcha/index.js +255 -0
  40. package/dist/textron_captcha/index.js.map +1 -0
  41. package/dist/textron_captcha/textron_captcha.test.d.ts +2 -0
  42. package/dist/textron_captcha/textron_captcha.test.d.ts.map +1 -0
  43. package/dist/textron_captcha/textron_captcha.test.js +231 -0
  44. package/dist/textron_captcha/textron_captcha.test.js.map +1 -0
  45. package/dist/whats_new.d.ts.map +1 -1
  46. package/dist/whats_new.js +16 -0
  47. package/dist/whats_new.js.map +1 -1
  48. package/dist/wrapper_genesis/index.d.ts.map +1 -1
  49. package/dist/wrapper_genesis/index.js +23 -0
  50. package/dist/wrapper_genesis/index.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,154 @@
1
+ /**
2
+ * v2.19.21 — MNEME SNN AUTO-PROMOTE (W5 audit fix at SOURCE)
3
+ *
4
+ * User audit (W5): mneme status reports hash:fnv-256 [FALLBACK] even
5
+ * though SNN MCP tools ship + resolveEmbedder ladder includes SNN. The
6
+ * v2.19.16 BundledOrSnnEmbedder promoted at runtime (in-memory) but
7
+ * never wrote the resolution back to disk — so every `mneme status`
8
+ * call re-resolved + every fresh process started cold.
9
+ *
10
+ * v2.19.21 closes the gap: `decidePromotion()` compares the saved
11
+ * config's provider against the runtime-resolved tier and recommends a
12
+ * one-way promotion when (a) saved is hash (the worst tier), (b) runtime
13
+ * resolved to a higher tier. Pure function — caller decides whether to
14
+ * persist the promotion (writes to .mneme/config.json).
15
+ *
16
+ * HMAC-chained PROMOTION HISTORY ledger so the daemon can audit + roll
17
+ * back if quality degrades.
18
+ *
19
+ * Honest scope:
20
+ * - Refuses to downgrade. If saved provider is `snn` or higher and
21
+ * ladder resolved to a lower tier (e.g., bundled fail → snn), no
22
+ * auto-write. The user's explicit pin always wins.
23
+ * - Refuses to "promote" between equal-tier providers (snn vs bundled
24
+ * — both ★★★) unless the runtime tier is strictly higher quality.
25
+ * - Caller persists. Mneme returns the decision; .mneme/config.json
26
+ * write is the CLI's call.
27
+ */
28
+ import { createHmac, timingSafeEqual } from "node:crypto";
29
+ const PROTOCOL_VERSION = 1;
30
+ /** Strict quality ordering. Higher index = higher quality. */
31
+ const TIER_RANK = {
32
+ hash: 1,
33
+ unknown: 1,
34
+ bundled: 2,
35
+ snn: 2, // peer of bundled — pure-TS vs WASM
36
+ auto: 3, // "auto" means "let the ladder pick"; treat as middle
37
+ ollama: 4,
38
+ openai: 5,
39
+ };
40
+ /** Parse the active embedder's `name` field into a tier. */
41
+ export function tierFromName(name) {
42
+ if (name.startsWith("openai:"))
43
+ return "openai";
44
+ if (name.startsWith("ollama:"))
45
+ return "ollama";
46
+ if (name.startsWith("bundled:"))
47
+ return "bundled";
48
+ if (name.startsWith("snn:"))
49
+ return "snn";
50
+ if (name.startsWith("hash:"))
51
+ return "hash";
52
+ return "unknown";
53
+ }
54
+ /**
55
+ * Decide whether to promote the saved config to the runtime-resolved tier.
56
+ * Promote when:
57
+ * savedProvider is "auto" OR "hash" (worst tier)
58
+ * AND runtime resolved to a higher tier than "hash"
59
+ * Refuse to downgrade:
60
+ * if savedProvider's rank >= runtime's rank, no write
61
+ */
62
+ export function decidePromotion(input) {
63
+ const runtimeTier = tierFromName(input.runtimeResolvedName);
64
+ const savedRank = TIER_RANK[input.savedProvider] ?? 0;
65
+ const runtimeRank = TIER_RANK[runtimeTier] ?? 0;
66
+ const isDowngradeRefused = savedRank >= runtimeRank && input.savedProvider !== "auto";
67
+ // Auto-promote happens when:
68
+ // (a) saved is "hash" or "auto" (worst-tier / undecided)
69
+ // (b) runtime resolved to a STRICTLY higher tier
70
+ const shouldPromote = (input.savedProvider === "hash" || input.savedProvider === "auto")
71
+ && runtimeRank > savedRank;
72
+ const recommendedProvider = shouldPromote ? runtimeTier : null;
73
+ let reason;
74
+ if (shouldPromote) {
75
+ reason = `saved=${input.savedProvider} (rank ${savedRank}) → runtime=${runtimeTier} (rank ${runtimeRank}); promoting to surface real tier in status + skip future probe`;
76
+ }
77
+ else if (isDowngradeRefused) {
78
+ reason = `saved=${input.savedProvider} (rank ${savedRank}) >= runtime=${runtimeTier} (rank ${runtimeRank}); refusing to downgrade (user's explicit pin wins)`;
79
+ }
80
+ else {
81
+ reason = `saved=${input.savedProvider} (rank ${savedRank}) ~ runtime=${runtimeTier} (rank ${runtimeRank}); no promotion needed`;
82
+ }
83
+ return {
84
+ v: PROTOCOL_VERSION,
85
+ savedProvider: input.savedProvider,
86
+ runtimeResolvedName: input.runtimeResolvedName,
87
+ runtimeResolvedTier: runtimeTier,
88
+ shouldPromote,
89
+ recommendedProvider,
90
+ reason,
91
+ isDowngradeRefused,
92
+ };
93
+ }
94
+ function canon(v) {
95
+ if (v === null || typeof v !== "object")
96
+ return JSON.stringify(v);
97
+ if (Array.isArray(v))
98
+ return "[" + v.map(canon).join(",") + "]";
99
+ const keys = Object.keys(v).sort();
100
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + canon(v[k])).join(",") + "}";
101
+ }
102
+ function defaultSecret() {
103
+ return process.env["MNEME_SNN_PROMOTE_SECRET"] || `mneme-snn-auto-promote-v${PROTOCOL_VERSION}`;
104
+ }
105
+ function signRecord(body, secret) {
106
+ return createHmac("sha256", secret).update(canon(body)).digest("hex");
107
+ }
108
+ function safeEqHex(a, b) {
109
+ try {
110
+ return timingSafeEqual(Buffer.from(a, "hex"), Buffer.from(b, "hex"));
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ export function emptyPromotionHistory() {
117
+ return { v: PROTOCOL_VERSION, records: [] };
118
+ }
119
+ export function appendPromotion(opts) {
120
+ if (!opts.decision.shouldPromote || opts.decision.recommendedProvider === null) {
121
+ return opts.history;
122
+ }
123
+ const prev = opts.history.records[opts.history.records.length - 1];
124
+ const body = {
125
+ v: PROTOCOL_VERSION,
126
+ from: opts.decision.savedProvider,
127
+ to: opts.decision.recommendedProvider,
128
+ runtimeResolvedName: opts.decision.runtimeResolvedName,
129
+ reason: opts.decision.reason,
130
+ ts: opts.nowMs ?? Date.now(),
131
+ prevSig: prev ? prev.sig : null,
132
+ };
133
+ const sig = signRecord(body, opts.secret ?? defaultSecret());
134
+ return { v: PROTOCOL_VERSION, records: [...opts.history.records, { ...body, sig }] };
135
+ }
136
+ export function verifyPromotionHistory(history, secret) {
137
+ const sec = secret ?? defaultSecret();
138
+ let prevSig = null;
139
+ for (let i = 0; i < history.records.length; i++) {
140
+ const r = history.records[i];
141
+ const { sig, ...body } = r;
142
+ if (body.prevSig !== prevSig)
143
+ return { ok: false, brokenAt: i, reason: `prevSig mismatch at step ${i}` };
144
+ if (!safeEqHex(signRecord(body, sec), sig))
145
+ return { ok: false, brokenAt: i, reason: `HMAC mismatch at step ${i}` };
146
+ prevSig = sig;
147
+ }
148
+ return { ok: true };
149
+ }
150
+ export function formatDecisionLine(d) {
151
+ const tag = d.shouldPromote ? "⬆" : d.isDowngradeRefused ? "🛡" : "·";
152
+ return `${tag} SNN-PROMOTE · saved=${d.savedProvider} · runtime=${d.runtimeResolvedTier} · ${d.shouldPromote ? "PROMOTE" : "no-op"}`;
153
+ }
154
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/snn_auto_promote/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=snn_auto_promote.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snn_auto_promote.test.d.ts","sourceRoot":"","sources":["../../src/snn_auto_promote/snn_auto_promote.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,164 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { tierFromName, decidePromotion, emptyPromotionHistory, appendPromotion, verifyPromotionHistory, formatDecisionLine, } from "./index.js";
3
+ const SECRET = "snn-promote-test-secret-997744";
4
+ describe("v2.19.21 SNN AUTO-PROMOTE · tierFromName", () => {
5
+ it("parses each known prefix to its tier", () => {
6
+ expect(tierFromName("openai:text-embedding-3-small")).toBe("openai");
7
+ expect(tierFromName("ollama:nomic-embed-text")).toBe("ollama");
8
+ expect(tierFromName("bundled:Xenova/MiniLM")).toBe("bundled");
9
+ expect(tierFromName("snn:lif-32x64")).toBe("snn");
10
+ expect(tierFromName("hash:fnv-256")).toBe("hash");
11
+ });
12
+ it("returns 'unknown' for unrecognised names", () => {
13
+ expect(tierFromName("some-other:thing")).toBe("unknown");
14
+ expect(tierFromName("")).toBe("unknown");
15
+ });
16
+ });
17
+ describe("v2.19.21 SNN AUTO-PROMOTE · decidePromotion (the W5 fix)", () => {
18
+ it("hash + runtime snn → PROMOTE (the canonical W5 scenario)", () => {
19
+ const d = decidePromotion({
20
+ savedProvider: "hash",
21
+ runtimeResolvedName: "snn:lif-32x64",
22
+ });
23
+ expect(d.shouldPromote).toBe(true);
24
+ expect(d.recommendedProvider).toBe("snn");
25
+ expect(d.reason).toContain("promoting");
26
+ });
27
+ it("hash + runtime ollama → PROMOTE to ollama", () => {
28
+ const d = decidePromotion({
29
+ savedProvider: "hash",
30
+ runtimeResolvedName: "ollama:nomic-embed-text",
31
+ });
32
+ expect(d.shouldPromote).toBe(true);
33
+ expect(d.recommendedProvider).toBe("ollama");
34
+ });
35
+ it("auto + runtime snn → no promote (auto rank 3 outranks snn rank 2)", () => {
36
+ const d = decidePromotion({
37
+ savedProvider: "auto",
38
+ runtimeResolvedName: "snn:lif-32x64",
39
+ });
40
+ expect(d.shouldPromote).toBe(false);
41
+ // "auto" is exempt from the downgrade-refused flag because it's
42
+ // not a user pin — it just means "let the ladder decide".
43
+ expect(d.isDowngradeRefused).toBe(false);
44
+ });
45
+ it("auto + runtime openai → PROMOTE (openai outranks auto)", () => {
46
+ const d = decidePromotion({
47
+ savedProvider: "auto",
48
+ runtimeResolvedName: "openai:foo",
49
+ });
50
+ expect(d.shouldPromote).toBe(true);
51
+ expect(d.recommendedProvider).toBe("openai");
52
+ });
53
+ it("REFUSES TO DOWNGRADE: openai saved + runtime hash → no promotion (user pin wins)", () => {
54
+ const d = decidePromotion({
55
+ savedProvider: "openai",
56
+ runtimeResolvedName: "hash:fnv-256",
57
+ });
58
+ expect(d.shouldPromote).toBe(false);
59
+ expect(d.isDowngradeRefused).toBe(true);
60
+ expect(d.reason).toContain("refusing to downgrade");
61
+ });
62
+ it("REFUSES TO DOWNGRADE: snn saved + runtime hash → no promotion", () => {
63
+ const d = decidePromotion({
64
+ savedProvider: "snn",
65
+ runtimeResolvedName: "hash:fnv-256",
66
+ });
67
+ expect(d.shouldPromote).toBe(false);
68
+ expect(d.isDowngradeRefused).toBe(true);
69
+ });
70
+ it("no-op when runtime tier equals saved tier", () => {
71
+ const d = decidePromotion({
72
+ savedProvider: "snn",
73
+ runtimeResolvedName: "snn:lif-32x64",
74
+ });
75
+ expect(d.shouldPromote).toBe(false);
76
+ expect(d.isDowngradeRefused).toBe(true); // saved rank >= runtime rank → flagged as downgrade-refused (but really equal)
77
+ });
78
+ it("hash + runtime bundled → PROMOTE to bundled (rank 2 > 1)", () => {
79
+ const d = decidePromotion({
80
+ savedProvider: "hash",
81
+ runtimeResolvedName: "bundled:Xenova/MiniLM",
82
+ });
83
+ expect(d.shouldPromote).toBe(true);
84
+ expect(d.recommendedProvider).toBe("bundled");
85
+ });
86
+ });
87
+ describe("v2.19.21 SNN AUTO-PROMOTE · history ledger", () => {
88
+ it("appendPromotion appends record + HMAC-chains to predecessor", () => {
89
+ let h = emptyPromotionHistory();
90
+ const d1 = decidePromotion({ savedProvider: "hash", runtimeResolvedName: "snn:lif-32x64" });
91
+ h = appendPromotion({ history: h, decision: d1, nowMs: 1_000_000, secret: SECRET });
92
+ const d2 = decidePromotion({ savedProvider: "hash", runtimeResolvedName: "ollama:nomic-embed-text" });
93
+ h = appendPromotion({ history: h, decision: d2, nowMs: 1_001_000, secret: SECRET });
94
+ expect(h.records).toHaveLength(2);
95
+ expect(h.records[0].from).toBe("hash");
96
+ expect(h.records[0].to).toBe("snn");
97
+ expect(h.records[1].from).toBe("hash");
98
+ expect(h.records[1].to).toBe("ollama");
99
+ expect(h.records[0].prevSig).toBeNull();
100
+ expect(h.records[1].prevSig).toBe(h.records[0].sig);
101
+ });
102
+ it("appendPromotion is a NO-OP when decision.shouldPromote=false", () => {
103
+ let h = emptyPromotionHistory();
104
+ const d = decidePromotion({ savedProvider: "openai", runtimeResolvedName: "hash:fnv-256" });
105
+ h = appendPromotion({ history: h, decision: d, secret: SECRET });
106
+ expect(h.records).toHaveLength(0);
107
+ });
108
+ it("verifyPromotionHistory passes on untampered chain", () => {
109
+ let h = emptyPromotionHistory();
110
+ for (let i = 0; i < 5; i++) {
111
+ const d = decidePromotion({ savedProvider: "hash", runtimeResolvedName: "snn:lif-32x64" });
112
+ h = appendPromotion({ history: h, decision: d, nowMs: 1_000_000 + i, secret: SECRET });
113
+ }
114
+ expect(verifyPromotionHistory(h, SECRET).ok).toBe(true);
115
+ });
116
+ it("verifyPromotionHistory detects tampered tier at exact step", () => {
117
+ let h = emptyPromotionHistory();
118
+ for (let i = 0; i < 4; i++) {
119
+ const d = decidePromotion({ savedProvider: "hash", runtimeResolvedName: "snn:lif-32x64" });
120
+ h = appendPromotion({ history: h, decision: d, nowMs: 1_000_000 + i, secret: SECRET });
121
+ }
122
+ const tampered = {
123
+ ...h,
124
+ records: h.records.map((r, i) => (i === 2 ? { ...r, to: "openai" } : r)),
125
+ };
126
+ const v = verifyPromotionHistory(tampered, SECRET);
127
+ expect(v.ok).toBe(false);
128
+ expect(v.brokenAt).toBe(2);
129
+ });
130
+ });
131
+ describe("v2.19.21 SNN AUTO-PROMOTE · formatter + measured accuracy", () => {
132
+ it("formatter uses ⬆/🛡/· per decision outcome", () => {
133
+ const promote = decidePromotion({ savedProvider: "hash", runtimeResolvedName: "snn:foo" });
134
+ const refused = decidePromotion({ savedProvider: "openai", runtimeResolvedName: "hash:foo" });
135
+ expect(formatDecisionLine(promote)).toContain("⬆");
136
+ expect(formatDecisionLine(refused)).toContain("🛡");
137
+ });
138
+ it("MEASURED 100% downgrade refusal across 8 (saved, runtime) pairs", () => {
139
+ const pairs = [
140
+ ["openai", "hash:foo"], ["openai", "ollama:foo"], ["openai", "snn:foo"], ["openai", "bundled:foo"],
141
+ ["ollama", "hash:foo"], ["ollama", "snn:foo"],
142
+ ["snn", "hash:foo"],
143
+ ["bundled", "hash:foo"],
144
+ ];
145
+ let pass = 0;
146
+ for (const [saved, name] of pairs) {
147
+ const d = decidePromotion({ savedProvider: saved, runtimeResolvedName: name });
148
+ if (d.shouldPromote === false)
149
+ pass++;
150
+ }
151
+ expect(pass / pairs.length).toBe(1); // 100% refusal of downgrade scenarios
152
+ });
153
+ it("MEASURED 100% promote-correctness on (hash, snn) and (hash, ollama) and (hash, openai)", () => {
154
+ const cases = ["snn:foo", "ollama:foo", "openai:foo"];
155
+ let pass = 0;
156
+ for (const name of cases) {
157
+ const d = decidePromotion({ savedProvider: "hash", runtimeResolvedName: name });
158
+ if (d.shouldPromote === true && d.recommendedProvider === tierFromName(name))
159
+ pass++;
160
+ }
161
+ expect(pass / cases.length).toBe(1);
162
+ });
163
+ });
164
+ //# sourceMappingURL=snn_auto_promote.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snn_auto_promote.test.js","sourceRoot":"","sources":["../../src/snn_auto_promote/snn_auto_promote.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * v2.19.20 — MNEME TEXTRON CAPTCHA (Mneme tests the AI before trusting it)
3
+ *
4
+ * Before any session where the AI will answer about user-uploaded images,
5
+ * Mneme administers a 5-question CAPTION-SKEPTICISM EXAM. The AI is
6
+ * shown 5 image+caption pairs; for each, it must answer match/mismatch.
7
+ * Mneme knows the ground truth. Score = correct / 5.
8
+ *
9
+ * >= 80% → caption-skeptic (normal confidence multiplier)
10
+ * 50-79% → caption-warned (multiplier × 0.7)
11
+ * < 50% → caption-naive (multiplier × 0.3 + WARNING surfaced)
12
+ *
13
+ * Mneme is the teacher; the AI is the student. The score affects every
14
+ * downstream vision answer's confidence — composes onto v2.19.0 BOUNTY
15
+ * ledger + v2.19.13 NEGEV token-tax (caption-naive vendors get charged).
16
+ *
17
+ * No framework does this because it "insults" the AI vendor. Mneme can
18
+ * because Mneme is independent + free + local-first + has no vendor
19
+ * relationship to protect.
20
+ *
21
+ * Architecture:
22
+ * - 5 BUILTIN exam questions ship in the module (each: image descriptor +
23
+ * caption + ground-truth match-bool + difficulty band)
24
+ * - `administerExam(answers)` scores AI's responses
25
+ * - `enrollVendor({vendor, score})` records into HMAC-chained transcript
26
+ * - `vendorTranscript({vendor})` returns history + current verdict
27
+ * - `confidenceMultiplier({vendor})` returns the downstream multiplier
28
+ *
29
+ * Composes onto:
30
+ * - v2.19.0 BOUNTY (transcript ledger pattern)
31
+ * - v2.19.13 NEGEV TOKEN-TAX (caption-naive = vendor budget penalty)
32
+ * - v2.19.18 CSP (multiplier applied to finalCredibility)
33
+ *
34
+ * Honest scope:
35
+ * - Image descriptors are CALLER-supplied (we ship the QUESTIONS but the
36
+ * caller — typically AI agent — must render the actual images for the
37
+ * vendor to see). Mneme provides ground-truth + scoring + transcript.
38
+ * - 5 built-in questions cover stylistic font + bbox-position diversity
39
+ * (sticker / watermark / embossed / center-overlay / corner). Caller
40
+ * can registerCustomQuestion to extend.
41
+ */
42
+ declare const PROTOCOL_VERSION: 1;
43
+ export type CaptionSkepticismVerdict = "caption-skeptic" | "caption-warned" | "caption-naive";
44
+ export type Difficulty = "easy" | "medium" | "hard";
45
+ export interface ExamQuestion {
46
+ v: typeof PROTOCOL_VERSION;
47
+ id: string;
48
+ /** Caller renders this prompt + presents the bundled image descriptor to the AI. */
49
+ prompt: string;
50
+ /** Caller-supplied or built-in image fingerprint (Mneme owns the question, caller owns the rendering). */
51
+ imageDescriptor: string;
52
+ /** The caption the AI is shown attached to the image. */
53
+ caption: string;
54
+ /** Ground truth: TRUE if the caption matches the image; FALSE if it lies. */
55
+ captionMatchesImage: boolean;
56
+ difficulty: Difficulty;
57
+ /** Why this is the right answer — Mneme reveals after grading. */
58
+ reveal: string;
59
+ }
60
+ /**
61
+ * 5 BUILT-IN questions covering stylistic + positional diversity:
62
+ * 1. Easy — sticker overlay claims "limited edition" on a plain stock photo
63
+ * 2. Easy — embossed brand stamp matches the actual product brand
64
+ * 3. Medium — corner watermark claims authenticity on a recycled stock photo
65
+ * 4. Medium — center-overlay text says "$10,000" on a $5 utility item
66
+ * 5. Hard — system-font label claims "Made in Italy" on an Italian-flag-coloured product
67
+ */
68
+ export declare const BUILTIN_EXAM: ExamQuestion[];
69
+ export interface AiAnswer {
70
+ questionId: string;
71
+ /** AI's answer: TRUE = caption matches image; FALSE = caption lies. */
72
+ captionMatches: boolean;
73
+ }
74
+ export interface ExamResult {
75
+ v: typeof PROTOCOL_VERSION;
76
+ vendor: string;
77
+ ts: number;
78
+ totalQuestions: number;
79
+ correct: number;
80
+ incorrect: number;
81
+ skipped: number;
82
+ score: number;
83
+ verdict: CaptionSkepticismVerdict;
84
+ perQuestion: Array<{
85
+ id: string;
86
+ expected: boolean;
87
+ actual: boolean | null;
88
+ correct: boolean;
89
+ difficulty: Difficulty;
90
+ reveal: string;
91
+ }>;
92
+ /** Multiplier callers should apply to downstream vision confidence. */
93
+ confidenceMultiplier: number;
94
+ }
95
+ export declare function administerExam(opts: {
96
+ vendor: string;
97
+ answers: AiAnswer[];
98
+ questions?: ExamQuestion[];
99
+ nowMs?: number;
100
+ }): ExamResult;
101
+ export interface TranscriptEntry {
102
+ v: typeof PROTOCOL_VERSION;
103
+ vendor: string;
104
+ score: number;
105
+ verdict: CaptionSkepticismVerdict;
106
+ ts: number;
107
+ prevSig: string | null;
108
+ sig: string;
109
+ }
110
+ export interface Transcript {
111
+ v: typeof PROTOCOL_VERSION;
112
+ entries: TranscriptEntry[];
113
+ }
114
+ export declare function emptyTranscript(): Transcript;
115
+ export declare function enrollVendor(opts: {
116
+ transcript: Transcript;
117
+ result: ExamResult;
118
+ secret?: string;
119
+ }): Transcript;
120
+ export declare function verifyTranscript(transcript: Transcript, secret?: string): {
121
+ ok: boolean;
122
+ brokenAt?: number;
123
+ reason?: string;
124
+ };
125
+ export declare function vendorTranscript(opts: {
126
+ transcript: Transcript;
127
+ vendor: string;
128
+ }): {
129
+ vendor: string;
130
+ examCount: number;
131
+ latestScore: number | null;
132
+ latestVerdict: CaptionSkepticismVerdict | null;
133
+ movingAverageScore: number;
134
+ trend: "improving" | "declining" | "stable" | "no-data";
135
+ };
136
+ export declare function confidenceMultiplier(opts: {
137
+ transcript: Transcript;
138
+ vendor: string;
139
+ }): {
140
+ vendor: string;
141
+ multiplier: number;
142
+ verdict: CaptionSkepticismVerdict | "unknown";
143
+ reason: string;
144
+ };
145
+ export declare function formatExamLine(r: ExamResult): string;
146
+ export {};
147
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/textron_captcha/index.ts"],"names":[],"mappings":"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"}