@mneme-ai/core 0.12.0 → 0.14.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.
Files changed (45) hide show
  1. package/dist/insights/cluster.d.ts +52 -0
  2. package/dist/insights/cluster.d.ts.map +1 -0
  3. package/dist/insights/cluster.js +154 -0
  4. package/dist/insights/cluster.js.map +1 -0
  5. package/dist/insights/cluster.test.d.ts +2 -0
  6. package/dist/insights/cluster.test.d.ts.map +1 -0
  7. package/dist/insights/cluster.test.js +106 -0
  8. package/dist/insights/cluster.test.js.map +1 -0
  9. package/dist/insights/export-bundle.d.ts +53 -0
  10. package/dist/insights/export-bundle.d.ts.map +1 -0
  11. package/dist/insights/export-bundle.js +156 -0
  12. package/dist/insights/export-bundle.js.map +1 -0
  13. package/dist/insights/export-bundle.test.d.ts +2 -0
  14. package/dist/insights/export-bundle.test.d.ts.map +1 -0
  15. package/dist/insights/export-bundle.test.js +92 -0
  16. package/dist/insights/export-bundle.test.js.map +1 -0
  17. package/dist/insights/index.d.ts +4 -0
  18. package/dist/insights/index.d.ts.map +1 -1
  19. package/dist/insights/index.js +4 -0
  20. package/dist/insights/index.js.map +1 -1
  21. package/dist/insights/manage.d.ts +60 -0
  22. package/dist/insights/manage.d.ts.map +1 -0
  23. package/dist/insights/manage.js +110 -0
  24. package/dist/insights/manage.js.map +1 -0
  25. package/dist/insights/manage.test.d.ts +2 -0
  26. package/dist/insights/manage.test.d.ts.map +1 -0
  27. package/dist/insights/manage.test.js +94 -0
  28. package/dist/insights/manage.test.js.map +1 -0
  29. package/dist/insights/network.d.ts +59 -0
  30. package/dist/insights/network.d.ts.map +1 -0
  31. package/dist/insights/network.js +231 -0
  32. package/dist/insights/network.js.map +1 -0
  33. package/dist/insights/network.test.d.ts +2 -0
  34. package/dist/insights/network.test.d.ts.map +1 -0
  35. package/dist/insights/network.test.js +100 -0
  36. package/dist/insights/network.test.js.map +1 -0
  37. package/dist/retrieve/hallucination-guard.test.d.ts +2 -0
  38. package/dist/retrieve/hallucination-guard.test.d.ts.map +1 -0
  39. package/dist/retrieve/hallucination-guard.test.js +130 -0
  40. package/dist/retrieve/hallucination-guard.test.js.map +1 -0
  41. package/dist/retrieve/synthesize.d.ts +38 -2
  42. package/dist/retrieve/synthesize.d.ts.map +1 -1
  43. package/dist/retrieve/synthesize.js +135 -5
  44. package/dist/retrieve/synthesize.js.map +1 -1
  45. package/package.json +1 -1
@@ -0,0 +1,231 @@
1
+ const STOPWORDS = new Set([
2
+ "the", "a", "an", "of", "to", "in", "on", "for", "and", "or", "with", "add", "added", "fix",
3
+ "remove", "update", "change", "make", "made", "new", "old", "this", "that", "is", "are", "be",
4
+ "feat", "chore", "docs", "test", "refactor", "style", "build", "ci", "perf", "revert",
5
+ "merge", "branch", "pr", "issue", "close", "closes", "fixes", "from", "by", "at", "into",
6
+ ]);
7
+ export function buildNetwork(commits, opts = {}) {
8
+ const coTimeWindow = (opts.coTimeWindowDays ?? 7) * 86_400_000;
9
+ const minEdge = opts.minEdgeWeight ?? 0.05;
10
+ const minAuthor = opts.minAuthorCommits ?? 2;
11
+ const sorted = [...commits].sort((a, b) => a.authorDate.localeCompare(b.authorDate));
12
+ if (sorted.length === 0) {
13
+ return { windowCommits: 0, nodes: [], edges: [], silos: [], bridges: [] };
14
+ }
15
+ // Author commit counts + tokens + files
16
+ const authorCommits = new Map();
17
+ const authorTokens = new Map();
18
+ const authorFiles = new Map();
19
+ const tokenizedPerCommit = new Map();
20
+ for (const c of sorted) {
21
+ const a = c.authorName || c.authorEmail;
22
+ let arr = authorCommits.get(a);
23
+ if (!arr) {
24
+ arr = [];
25
+ authorCommits.set(a, arr);
26
+ }
27
+ arr.push(c);
28
+ const tokens = tokenizeSet(`${c.subject}\n${c.body || ""}`);
29
+ tokenizedPerCommit.set(c.hash, tokens);
30
+ let at = authorTokens.get(a);
31
+ if (!at) {
32
+ at = new Set();
33
+ authorTokens.set(a, at);
34
+ }
35
+ for (const t of tokens)
36
+ at.add(t);
37
+ let af = authorFiles.get(a);
38
+ if (!af) {
39
+ af = new Set();
40
+ authorFiles.set(a, af);
41
+ }
42
+ for (const f of c.files)
43
+ af.add(f);
44
+ }
45
+ // Filter to authors meeting min
46
+ const eligible = [...authorCommits.entries()]
47
+ .filter(([, cs]) => cs.length >= minAuthor)
48
+ .map(([a]) => a);
49
+ // Build pairwise edges
50
+ const edges = [];
51
+ for (let i = 0; i < eligible.length; i++) {
52
+ for (let j = i + 1; j < eligible.length; j++) {
53
+ const a = eligible[i];
54
+ const b = eligible[j];
55
+ const aFiles = authorFiles.get(a);
56
+ const bFiles = authorFiles.get(b);
57
+ const aTokens = authorTokens.get(a);
58
+ const bTokens = authorTokens.get(b);
59
+ const aCommits = authorCommits.get(a);
60
+ const bCommits = authorCommits.get(b);
61
+ const coEdit = jaccard(aFiles, bFiles);
62
+ const coTopic = jaccard(aTokens, bTokens);
63
+ const coTime = computeCoTime(aCommits, bCommits, coTimeWindow);
64
+ const weight = Number((0.4 * coEdit + 0.2 * coTime + 0.4 * coTopic).toFixed(3));
65
+ if (weight < minEdge)
66
+ continue;
67
+ const sharedTerms = [...aTokens]
68
+ .filter((t) => bTokens.has(t))
69
+ .slice(0, 5);
70
+ edges.push({
71
+ authorA: a,
72
+ authorB: b,
73
+ weight,
74
+ axes: {
75
+ coEdit: Number(coEdit.toFixed(3)),
76
+ coTime: Number(coTime.toFixed(3)),
77
+ coTopic: Number(coTopic.toFixed(3)),
78
+ },
79
+ sharedTerms,
80
+ });
81
+ }
82
+ }
83
+ edges.sort((a, b) => b.weight - a.weight);
84
+ // Build nodes with centrality (simple degree-normalized)
85
+ const degree = new Map();
86
+ for (const e of edges) {
87
+ degree.set(e.authorA, (degree.get(e.authorA) ?? 0) + e.weight);
88
+ degree.set(e.authorB, (degree.get(e.authorB) ?? 0) + e.weight);
89
+ }
90
+ const maxDeg = Math.max(0, ...degree.values());
91
+ const nodes = eligible.map((author) => ({
92
+ author,
93
+ commits: authorCommits.get(author).length,
94
+ collaborators: edges.filter((e) => e.authorA === author || e.authorB === author).length,
95
+ centrality: maxDeg === 0 ? 0 : Number(((degree.get(author) ?? 0) / maxDeg).toFixed(3)),
96
+ }));
97
+ nodes.sort((a, b) => b.centrality - a.centrality);
98
+ // Detect silos via connected components on the (filtered) graph
99
+ const silos = detectSilos(eligible, edges);
100
+ // Bridges: authors with edges to ≥ 2 silos
101
+ const bridges = detectBridges(eligible, edges, silos);
102
+ return {
103
+ windowCommits: sorted.length,
104
+ nodes,
105
+ edges,
106
+ silos,
107
+ bridges,
108
+ };
109
+ }
110
+ function tokenizeSet(text) {
111
+ const out = new Set();
112
+ for (const t of text
113
+ .toLowerCase()
114
+ .replace(/[^a-z0-9_/.-]+/g, " ")
115
+ .split(/\s+/)) {
116
+ if (!t || t.length < 3)
117
+ continue;
118
+ if (STOPWORDS.has(t))
119
+ continue;
120
+ out.add(t);
121
+ }
122
+ return out;
123
+ }
124
+ function jaccard(a, b) {
125
+ if (a.size === 0 && b.size === 0)
126
+ return 1;
127
+ let inter = 0;
128
+ for (const x of a)
129
+ if (b.has(x))
130
+ inter++;
131
+ const union = a.size + b.size - inter;
132
+ return union === 0 ? 0 : inter / union;
133
+ }
134
+ function computeCoTime(a, b, windowMs) {
135
+ if (a.length === 0 || b.length === 0)
136
+ return 0;
137
+ // For each commit in a, count if any commit in b is within window
138
+ const bTimes = b.map((c) => new Date(c.authorDate).getTime()).sort((x, y) => x - y);
139
+ let hits = 0;
140
+ for (const ca of a) {
141
+ const t = new Date(ca.authorDate).getTime();
142
+ // binary search for any time within [t - window, t + window]
143
+ const target = t;
144
+ let lo = 0, hi = bTimes.length - 1;
145
+ while (lo <= hi) {
146
+ const mid = (lo + hi) >> 1;
147
+ const v = bTimes[mid];
148
+ if (Math.abs(v - target) <= windowMs) {
149
+ hits++;
150
+ break;
151
+ }
152
+ if (v < target)
153
+ lo = mid + 1;
154
+ else
155
+ hi = mid - 1;
156
+ }
157
+ }
158
+ return hits / a.length;
159
+ }
160
+ function detectSilos(authors, edges) {
161
+ // Union-find over authors
162
+ const parent = new Map();
163
+ const find = (x) => {
164
+ let p = parent.get(x) ?? x;
165
+ while (p !== x) {
166
+ const grand = parent.get(p) ?? p;
167
+ parent.set(x, grand);
168
+ x = grand;
169
+ p = parent.get(x) ?? x;
170
+ }
171
+ return x;
172
+ };
173
+ const union = (a, b) => {
174
+ const ra = find(a), rb = find(b);
175
+ if (ra !== rb)
176
+ parent.set(ra, rb);
177
+ };
178
+ for (const a of authors)
179
+ parent.set(a, a);
180
+ // Only edges with weight ≥ 0.15 join
181
+ const joinFloor = 0.15;
182
+ for (const e of edges) {
183
+ if (e.weight >= joinFloor)
184
+ union(e.authorA, e.authorB);
185
+ }
186
+ const groups = new Map();
187
+ for (const a of authors) {
188
+ const r = find(a);
189
+ if (!groups.has(r))
190
+ groups.set(r, []);
191
+ groups.get(r).push(a);
192
+ }
193
+ return [...groups.values()].filter((g) => g.length >= 2);
194
+ }
195
+ function detectBridges(authors, edges, silos) {
196
+ if (silos.length < 2)
197
+ return [];
198
+ // For each author, count how many silos they have weak (<0.15) cross-edges to
199
+ const siloOf = new Map();
200
+ for (let i = 0; i < silos.length; i++) {
201
+ for (const a of silos[i])
202
+ siloOf.set(a, i);
203
+ }
204
+ const otherSilosByAuthor = new Map();
205
+ for (const e of edges) {
206
+ if (e.weight >= 0.15)
207
+ continue; // only weak/cross-cluster edges
208
+ const sa = siloOf.get(e.authorA);
209
+ const sb = siloOf.get(e.authorB);
210
+ if (sa === undefined || sb === undefined)
211
+ continue;
212
+ if (sa === sb)
213
+ continue;
214
+ let setA = otherSilosByAuthor.get(e.authorA);
215
+ if (!setA) {
216
+ setA = new Set();
217
+ otherSilosByAuthor.set(e.authorA, setA);
218
+ }
219
+ setA.add(sb);
220
+ let setB = otherSilosByAuthor.get(e.authorB);
221
+ if (!setB) {
222
+ setB = new Set();
223
+ otherSilosByAuthor.set(e.authorB, setB);
224
+ }
225
+ setB.add(sa);
226
+ }
227
+ return [...otherSilosByAuthor.entries()]
228
+ .filter(([, silosSet]) => silosSet.size >= 2)
229
+ .map(([a]) => a);
230
+ }
231
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/insights/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=network.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.test.d.ts","sourceRoot":"","sources":["../../src/insights/network.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildNetwork } from "./network.js";
3
+ function mk(p) {
4
+ return {
5
+ hash: p.hash,
6
+ shortHash: p.hash.slice(0, 7),
7
+ authorName: p.author,
8
+ authorEmail: p.author.toLowerCase() + "@x.com",
9
+ authorDate: p.date,
10
+ committerDate: p.date,
11
+ subject: p.subject,
12
+ body: "",
13
+ files: p.files ?? [],
14
+ parents: [],
15
+ };
16
+ }
17
+ describe("buildNetwork", () => {
18
+ it("returns empty for empty input", () => {
19
+ const n = buildNetwork([]);
20
+ expect(n.nodes).toHaveLength(0);
21
+ expect(n.edges).toHaveLength(0);
22
+ expect(n.silos).toHaveLength(0);
23
+ });
24
+ it("filters out authors below minAuthorCommits", () => {
25
+ const commits = [
26
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice", files: ["a.ts"] }),
27
+ mk({ hash: "b1", date: "2024-01-02", subject: "x", author: "Bob", files: ["b.ts"] }),
28
+ ];
29
+ const n = buildNetwork(commits, { minAuthorCommits: 3 });
30
+ expect(n.nodes).toHaveLength(0);
31
+ });
32
+ it("creates an edge between authors who touched the same files", () => {
33
+ const commits = [
34
+ mk({ hash: "a1", date: "2024-01-01", subject: "auth work", author: "Alice", files: ["src/auth.ts"] }),
35
+ mk({ hash: "a2", date: "2024-01-02", subject: "auth work", author: "Alice", files: ["src/auth.ts"] }),
36
+ mk({ hash: "b1", date: "2024-01-03", subject: "auth fix", author: "Bob", files: ["src/auth.ts"] }),
37
+ mk({ hash: "b2", date: "2024-01-04", subject: "auth tweak", author: "Bob", files: ["src/auth.ts"] }),
38
+ ];
39
+ const n = buildNetwork(commits, { minAuthorCommits: 2 });
40
+ const edge = n.edges.find((e) => (e.authorA === "Alice" && e.authorB === "Bob") || (e.authorA === "Bob" && e.authorB === "Alice"));
41
+ expect(edge).toBeDefined();
42
+ expect(edge.axes.coEdit).toBeGreaterThan(0);
43
+ });
44
+ it("co-topic axis fires when authors share commit vocabulary", () => {
45
+ const commits = [
46
+ mk({ hash: "a1", date: "2024-01-01", subject: "rewrite payment processor", author: "Alice", files: ["a.ts"] }),
47
+ mk({ hash: "a2", date: "2024-01-02", subject: "rewrite payment validation", author: "Alice", files: ["b.ts"] }),
48
+ mk({ hash: "b1", date: "2024-01-03", subject: "rewrite payment retry", author: "Bob", files: ["c.ts"] }),
49
+ mk({ hash: "b2", date: "2024-01-04", subject: "rewrite payment receipts", author: "Bob", files: ["d.ts"] }),
50
+ ];
51
+ const n = buildNetwork(commits, { minAuthorCommits: 2, minEdgeWeight: 0 });
52
+ const edge = n.edges[0];
53
+ expect(edge).toBeDefined();
54
+ expect(edge.axes.coTopic).toBeGreaterThan(0);
55
+ expect(edge.sharedTerms).toContain("payment");
56
+ });
57
+ it("centrality is highest for the most-connected author", () => {
58
+ const commits = [
59
+ // Alice connected to both Bob and Carol via shared files
60
+ mk({ hash: "a1", date: "2024-01-01", subject: "x", author: "Alice", files: ["src/x.ts", "src/y.ts"] }),
61
+ mk({ hash: "a2", date: "2024-01-02", subject: "x", author: "Alice", files: ["src/x.ts", "src/y.ts"] }),
62
+ mk({ hash: "b1", date: "2024-01-03", subject: "x", author: "Bob", files: ["src/x.ts"] }),
63
+ mk({ hash: "b2", date: "2024-01-04", subject: "x", author: "Bob", files: ["src/x.ts"] }),
64
+ mk({ hash: "c1", date: "2024-01-05", subject: "x", author: "Carol", files: ["src/y.ts"] }),
65
+ mk({ hash: "c2", date: "2024-01-06", subject: "x", author: "Carol", files: ["src/y.ts"] }),
66
+ ];
67
+ const n = buildNetwork(commits, { minAuthorCommits: 2, minEdgeWeight: 0 });
68
+ expect(n.nodes[0].author).toBe("Alice");
69
+ expect(n.nodes[0].centrality).toBeGreaterThanOrEqual(n.nodes[1].centrality);
70
+ });
71
+ it("detects silos when groups have no inter-group edges above threshold", () => {
72
+ const commits = [
73
+ mk({ hash: "a1", date: "2024-01-01", subject: "auth", author: "Alice", files: ["src/auth/x.ts"] }),
74
+ mk({ hash: "a2", date: "2024-01-02", subject: "auth", author: "Alice", files: ["src/auth/x.ts"] }),
75
+ mk({ hash: "b1", date: "2024-01-01", subject: "auth", author: "Bob", files: ["src/auth/x.ts"] }),
76
+ mk({ hash: "b2", date: "2024-01-02", subject: "auth", author: "Bob", files: ["src/auth/x.ts"] }),
77
+ mk({ hash: "c1", date: "2024-01-01", subject: "payments", author: "Carol", files: ["src/pay/y.ts"] }),
78
+ mk({ hash: "c2", date: "2024-01-02", subject: "payments", author: "Carol", files: ["src/pay/y.ts"] }),
79
+ mk({ hash: "d1", date: "2024-01-01", subject: "payments", author: "Dave", files: ["src/pay/y.ts"] }),
80
+ mk({ hash: "d2", date: "2024-01-02", subject: "payments", author: "Dave", files: ["src/pay/y.ts"] }),
81
+ ];
82
+ const n = buildNetwork(commits, { minAuthorCommits: 2 });
83
+ expect(n.silos.length).toBeGreaterThanOrEqual(1);
84
+ });
85
+ it("edges sorted by weight descending", () => {
86
+ const commits = [
87
+ mk({ hash: "a1", date: "2024-01-01", subject: "auth", author: "Alice", files: ["x.ts"] }),
88
+ mk({ hash: "a2", date: "2024-01-02", subject: "auth", author: "Alice", files: ["x.ts"] }),
89
+ mk({ hash: "b1", date: "2024-01-03", subject: "auth", author: "Bob", files: ["x.ts"] }),
90
+ mk({ hash: "b2", date: "2024-01-04", subject: "auth", author: "Bob", files: ["x.ts"] }),
91
+ mk({ hash: "c1", date: "2024-01-05", subject: "different", author: "Carol", files: ["y.ts"] }),
92
+ mk({ hash: "c2", date: "2024-01-06", subject: "different", author: "Carol", files: ["y.ts"] }),
93
+ ];
94
+ const n = buildNetwork(commits, { minAuthorCommits: 2, minEdgeWeight: 0 });
95
+ for (let i = 1; i < n.edges.length; i++) {
96
+ expect(n.edges[i - 1].weight).toBeGreaterThanOrEqual(n.edges[i].weight);
97
+ }
98
+ });
99
+ });
100
+ //# sourceMappingURL=network.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.test.js","sourceRoot":"","sources":["../../src/insights/network.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hallucination-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hallucination-guard.test.d.ts","sourceRoot":"","sources":["../../src/retrieve/hallucination-guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,130 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { findUnverifiedCitations, synthesize } from "./synthesize.js";
3
+ function mkResult(hash, subject = "init") {
4
+ return {
5
+ commit: {
6
+ hash,
7
+ shortHash: hash.slice(0, 7),
8
+ authorName: "Alice",
9
+ authorEmail: "a@x.com",
10
+ authorDate: "2024-01-01",
11
+ committerDate: "2024-01-01",
12
+ subject,
13
+ body: "",
14
+ files: [],
15
+ parents: [],
16
+ },
17
+ score: 0.05,
18
+ matchedChunks: [],
19
+ };
20
+ }
21
+ describe("findUnverifiedCitations", () => {
22
+ it("returns empty when answer cites only evidence hashes", () => {
23
+ const evidence = ["abc1234567890abc", "def9876543210def"];
24
+ const answer = "Because of `abc1234`, see also `def9876`.";
25
+ expect(findUnverifiedCitations(answer, evidence)).toEqual([]);
26
+ });
27
+ it("flags hashes that don't match any evidence", () => {
28
+ const evidence = ["abc1234567890abc"];
29
+ const answer = "Because of `abc1234`, also `bad9999`.";
30
+ const unverified = findUnverifiedCitations(answer, evidence);
31
+ expect(unverified).toContain("bad9999");
32
+ expect(unverified).not.toContain("abc1234");
33
+ });
34
+ it("matches by prefix — short hash in answer + long hash in evidence", () => {
35
+ const evidence = ["abc1234567890abcdef0000000000000000000000"];
36
+ const answer = "See `abc1234`.";
37
+ expect(findUnverifiedCitations(answer, evidence)).toEqual([]);
38
+ });
39
+ it("matches by prefix in either direction", () => {
40
+ // Long hash in answer, short hash prefix in evidence
41
+ const evidence = ["abc1234"];
42
+ const answer = "See `abc1234567890`.";
43
+ expect(findUnverifiedCitations(answer, evidence)).toEqual([]);
44
+ });
45
+ it("deduplicates repeated mentions", () => {
46
+ const evidence = ["abc1234"];
47
+ const answer = "See `bad9999`. Yes, `bad9999` again. And once more `bad9999`.";
48
+ expect(findUnverifiedCitations(answer, evidence)).toEqual(["bad9999"]);
49
+ });
50
+ it("ignores backtick tokens that aren't hex (e.g. `mneme_ask`)", () => {
51
+ const evidence = ["abc1234"];
52
+ const answer = "Use `mneme_ask` and `npm install` — see `abc1234`.";
53
+ expect(findUnverifiedCitations(answer, evidence)).toEqual([]);
54
+ });
55
+ it("is case-insensitive", () => {
56
+ const evidence = ["ABC1234"];
57
+ const answer = "See `abc1234`.";
58
+ expect(findUnverifiedCitations(answer, evidence)).toEqual([]);
59
+ });
60
+ });
61
+ describe("synthesize — audit mode", () => {
62
+ it("refuses below confidence floor in audit mode", async () => {
63
+ const out = await synthesize("why does X exist?", [mkResult("abc1234")], "low", undefined, { auditMode: true, auditFloor: "medium" });
64
+ expect(out.source).toBe("audit-refused");
65
+ expect(out.trustScore).toBe(0);
66
+ expect(out.answer).toContain("Audit mode refused");
67
+ });
68
+ it("does NOT refuse when confidence meets floor", async () => {
69
+ const out = await synthesize("why does X exist?", [mkResult("abc1234")], "high", undefined, { auditMode: true, auditFloor: "medium" });
70
+ expect(out.source).toBe("extractive");
71
+ expect(out.trustScore).toBeGreaterThan(0);
72
+ });
73
+ it("refuses when LLM cites unverified hashes in audit mode", async () => {
74
+ const enricher = {
75
+ name: "fake",
76
+ enrich: async () => ({
77
+ text: "Because of `bad9999` and `cafebabe`. Real reason `abc1234`.",
78
+ }),
79
+ };
80
+ const out = await synthesize("why does X exist?", [mkResult("abc1234567890abc")], "high", enricher, { auditMode: true });
81
+ expect(out.source).toBe("audit-refused");
82
+ expect(out.unverifiedCitations).toEqual(expect.arrayContaining(["bad9999", "cafebabe"]));
83
+ });
84
+ it("returns LLM answer (with unverified flagged) when audit mode is OFF", async () => {
85
+ const enricher = {
86
+ name: "fake",
87
+ enrich: async () => ({
88
+ text: "Real reason `abc1234`. Also `bad9999`.",
89
+ }),
90
+ };
91
+ const out = await synthesize("why does X exist?", [mkResult("abc1234567890abc")], "high", enricher);
92
+ expect(out.source).toBe("llm");
93
+ expect(out.unverifiedCitations).toContain("bad9999");
94
+ expect(out.trustScore).toBeLessThan(0.95);
95
+ });
96
+ });
97
+ describe("synthesize — trust score", () => {
98
+ it("trustScore is 0 for no-context", async () => {
99
+ const out = await synthesize("vague", [], "none");
100
+ expect(out.trustScore).toBe(0);
101
+ });
102
+ it("extractive trustScore depends on confidence", async () => {
103
+ const high = await synthesize("q", [mkResult("abc1234")], "high");
104
+ const med = await synthesize("q", [mkResult("abc1234")], "medium");
105
+ const low = await synthesize("q", [mkResult("abc1234")], "low");
106
+ expect(high.trustScore).toBeGreaterThan(med.trustScore);
107
+ expect(med.trustScore).toBeGreaterThan(low.trustScore);
108
+ });
109
+ it("LLM with verified citations has higher trust than extractive", async () => {
110
+ const enricher = {
111
+ name: "fake",
112
+ enrich: async () => ({ text: "See `abc1234`." }),
113
+ };
114
+ const llm = await synthesize("q", [mkResult("abc1234567890abc")], "high", enricher);
115
+ const ext = await synthesize("q", [mkResult("abc1234567890abc")], "high");
116
+ expect(llm.trustScore).toBeGreaterThan(ext.trustScore);
117
+ expect(llm.unverifiedCitations).toEqual([]);
118
+ });
119
+ it("LLM with unverified citations has reduced trust", async () => {
120
+ const enricher = {
121
+ name: "fake",
122
+ enrich: async () => ({ text: "See `abc1234` and `bad9999` and `cafebabe`." }),
123
+ };
124
+ const out = await synthesize("q", [mkResult("abc1234567890abc")], "high", enricher);
125
+ expect(out.unverifiedCitations.length).toBeGreaterThanOrEqual(2);
126
+ // Should be penalized
127
+ expect(out.trustScore).toBeLessThan(0.7);
128
+ });
129
+ });
130
+ //# sourceMappingURL=hallucination-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hallucination-guard.test.js","sourceRoot":"","sources":["../../src/retrieve/hallucination-guard.test.ts"],"names":[],"mappings":"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"}
@@ -35,14 +35,44 @@ export interface SynthesizedAnswer {
35
35
  /** The paragraph the user reads at the top of `mneme ask` output. */
36
36
  answer: string;
37
37
  /** Provenance: where the answer came from. */
38
- source: "llm" | "extractive" | "no-context";
38
+ source: "llm" | "extractive" | "no-context" | "audit-refused";
39
39
  /** The same confidence label that was passed in (echoed for caller convenience). */
40
40
  confidence: ConfidenceLabel;
41
41
  /** Hashes of the commits the answer is grounded in. */
42
42
  evidenceCommitHashes: string[];
43
+ /**
44
+ * Hallucination guard — list of inline backtick-hashes the LLM cited that
45
+ * are NOT present in the retrieved evidence. Empty array means audit-clean.
46
+ */
47
+ unverifiedCitations: string[];
48
+ /**
49
+ * Trust score 0..1 — combines confidence label + citation validity.
50
+ * - "audit-refused" → 0
51
+ * - "no-context" → 0
52
+ * - extractive → 0.5–0.7 (no LLM risk)
53
+ * - LLM clean (no unverified citations) → 0.8–1.0
54
+ * - LLM with unverified citations → < 0.5 (risk of hallucination)
55
+ */
56
+ trustScore: number;
43
57
  /** Per-call latency for telemetry / "how slow is this?" calibration. */
44
58
  durationMs: number;
45
59
  }
60
+ /** Options for the synthesizer. */
61
+ export interface SynthesizeOptions {
62
+ /** Top-K to ground the answer in (default 5). */
63
+ maxResults?: number;
64
+ /**
65
+ * Audit mode — refuse to answer below a confidence floor instead of
66
+ * returning best-effort prose. Use this when the consumer is an audit
67
+ * surface (CI gate, MCP tool result, security review).
68
+ */
69
+ auditMode?: boolean;
70
+ /**
71
+ * Minimum confidence label that audit mode allows. Defaults to "medium".
72
+ * Anything below is refused with a "audit-refused" source.
73
+ */
74
+ auditFloor?: ConfidenceLabel;
75
+ }
46
76
  /**
47
77
  * Synthesize an answer from retrieval results.
48
78
  *
@@ -53,5 +83,11 @@ export interface SynthesizedAnswer {
53
83
  * @param enricher — optional. If undefined or fails, we fall back to extractive.
54
84
  * @param maxResults — how many top results to ground the answer in (default 5).
55
85
  */
56
- export declare function synthesize(question: string, results: SearchResult[], confidence: ConfidenceLabel, enricher?: SynthesisEnricher, maxResults?: number): Promise<SynthesizedAnswer>;
86
+ export declare function synthesize(question: string, results: SearchResult[], confidence: ConfidenceLabel, enricher?: SynthesisEnricher, optsOrMax?: number | SynthesizeOptions): Promise<SynthesizedAnswer>;
87
+ /**
88
+ * Extract every backtick-quoted token from the answer that LOOKS like a
89
+ * commit hash (≥4 hex chars), then return the subset that is NOT present
90
+ * (as prefix) in any evidence hash. These are candidate hallucinations.
91
+ */
92
+ export declare function findUnverifiedCitations(answer: string, evidenceHashes: string[]): string[];
57
93
  //# sourceMappingURL=synthesize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,UAAU,SAAI,GACb,OAAO,CAAC,iBAAiB,CAAC,CAoD5B"}
1
+ {"version":3,"file":"synthesize.d.ts","sourceRoot":"","sources":["../../src/retrieve/synthesize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,KAAK,EAAE;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,EAAE,KAAK,GAAG,YAAY,GAAG,YAAY,GAAG,eAAe,CAAC;IAC9D,oFAAoF;IACpF,UAAU,EAAE,eAAe,CAAC;IAC5B,uDAAuD;IACvD,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,mCAAmC;AACnC,MAAM,WAAW,iBAAiB;IAChC,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAmBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,EAAE,EACvB,UAAU,EAAE,eAAe,EAC3B,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,SAAS,GAAE,MAAM,GAAG,iBAAqB,GACxC,OAAO,CAAC,iBAAiB,CAAC,CAiH5B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,EAAE,CAaV"}