@mneme-ai/core 0.22.2 → 0.23.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 (68) hide show
  1. package/dist/retrieve/ddtree.d.ts +67 -0
  2. package/dist/retrieve/ddtree.d.ts.map +1 -0
  3. package/dist/retrieve/ddtree.js +156 -0
  4. package/dist/retrieve/ddtree.js.map +1 -0
  5. package/dist/retrieve/ddtree.test.d.ts +2 -0
  6. package/dist/retrieve/ddtree.test.d.ts.map +1 -0
  7. package/dist/retrieve/ddtree.test.js +181 -0
  8. package/dist/retrieve/ddtree.test.js.map +1 -0
  9. package/dist/retrieve/index.d.ts +3 -0
  10. package/dist/retrieve/index.d.ts.map +1 -1
  11. package/dist/retrieve/index.js +3 -0
  12. package/dist/retrieve/index.js.map +1 -1
  13. package/dist/retrieve/leviathan.d.ts +68 -0
  14. package/dist/retrieve/leviathan.d.ts.map +1 -0
  15. package/dist/retrieve/leviathan.js +192 -0
  16. package/dist/retrieve/leviathan.js.map +1 -0
  17. package/dist/retrieve/leviathan.test.d.ts +2 -0
  18. package/dist/retrieve/leviathan.test.d.ts.map +1 -0
  19. package/dist/retrieve/leviathan.test.js +124 -0
  20. package/dist/retrieve/leviathan.test.js.map +1 -0
  21. package/dist/retrieve/search.d.ts +7 -0
  22. package/dist/retrieve/search.d.ts.map +1 -1
  23. package/dist/retrieve/search.js +31 -3
  24. package/dist/retrieve/search.js.map +1 -1
  25. package/dist/retrieve/stream.d.ts +93 -0
  26. package/dist/retrieve/stream.d.ts.map +1 -0
  27. package/dist/retrieve/stream.js +52 -0
  28. package/dist/retrieve/stream.js.map +1 -0
  29. package/dist/retrieve/stream.test.d.ts +2 -0
  30. package/dist/retrieve/stream.test.d.ts.map +1 -0
  31. package/dist/retrieve/stream.test.js +118 -0
  32. package/dist/retrieve/stream.test.js.map +1 -0
  33. package/dist/retrieve/synthesize.d.ts.map +1 -1
  34. package/dist/retrieve/synthesize.js +17 -1
  35. package/dist/retrieve/synthesize.js.map +1 -1
  36. package/dist/util/constraint-pruner.d.ts +76 -0
  37. package/dist/util/constraint-pruner.d.ts.map +1 -0
  38. package/dist/util/constraint-pruner.js +89 -0
  39. package/dist/util/constraint-pruner.js.map +1 -0
  40. package/dist/util/constraint-pruner.test.d.ts +2 -0
  41. package/dist/util/constraint-pruner.test.d.ts.map +1 -0
  42. package/dist/util/constraint-pruner.test.js +101 -0
  43. package/dist/util/constraint-pruner.test.js.map +1 -0
  44. package/dist/util/index.d.ts +1 -0
  45. package/dist/util/index.d.ts.map +1 -1
  46. package/dist/util/index.js +1 -0
  47. package/dist/util/index.js.map +1 -1
  48. package/dist/wisdom/index.d.ts +2 -0
  49. package/dist/wisdom/index.d.ts.map +1 -1
  50. package/dist/wisdom/index.js +2 -0
  51. package/dist/wisdom/index.js.map +1 -1
  52. package/dist/wisdom/mutant-adapt.d.ts +69 -0
  53. package/dist/wisdom/mutant-adapt.d.ts.map +1 -0
  54. package/dist/wisdom/mutant-adapt.js +216 -0
  55. package/dist/wisdom/mutant-adapt.js.map +1 -0
  56. package/dist/wisdom/mutant-adapt.test.d.ts +2 -0
  57. package/dist/wisdom/mutant-adapt.test.d.ts.map +1 -0
  58. package/dist/wisdom/mutant-adapt.test.js +184 -0
  59. package/dist/wisdom/mutant-adapt.test.js.map +1 -0
  60. package/dist/wisdom/session.d.ts +60 -0
  61. package/dist/wisdom/session.d.ts.map +1 -0
  62. package/dist/wisdom/session.js +193 -0
  63. package/dist/wisdom/session.js.map +1 -0
  64. package/dist/wisdom/session.test.d.ts +2 -0
  65. package/dist/wisdom/session.test.d.ts.map +1 -0
  66. package/dist/wisdom/session.test.js +161 -0
  67. package/dist/wisdom/session.test.js.map +1 -0
  68. package/package.json +1 -1
@@ -0,0 +1,67 @@
1
+ /**
2
+ * DDTree — Best-First Search through a commit ancestor tree.
3
+ *
4
+ * Inspired by KAT-0B's DDTree (a BinaryHeap-driven candidate exploration
5
+ * loop bounded by an exploration budget). Mneme uses it under `mneme why`
6
+ * to walk *back* through parent commits looking for the deepest plausible
7
+ * "this is why X exists" answer — strictly better than flat semantic
8
+ * retrieval, which can't follow causal ancestry.
9
+ *
10
+ * Semantics:
11
+ * 1. Caller supplies a `scoreFn(commit, depth, parentScore)` — typically
12
+ * relevance × depth-decay × parent-score.
13
+ * 2. We maintain a max-heap keyed by score and pop the best candidate
14
+ * until either the budget is exhausted or the heap is empty.
15
+ * 3. Each pop is classified: accepted, pruned-floor, pruned-depth, or
16
+ * pruned-budget (for nodes still on the heap when budget runs out).
17
+ * 4. Parents of an accepted node are scored and pushed back in.
18
+ *
19
+ * Heap implementation: a simple binary max-heap on a flat array. We avoid
20
+ * Node v22's experimental `node:priority-queue` for portability — Mneme
21
+ * supports Node 18+. See `MaxHeap` below.
22
+ *
23
+ * Cycle handling: merge commits can — in theory — produce diamond-shaped
24
+ * ancestry where the same hash appears twice. We track a `visited` Set so
25
+ * each commit is processed at most once (the first/highest-score path wins).
26
+ */
27
+ import type { Commit } from "../types.js";
28
+ export interface DDTreeOptions {
29
+ /** Max nodes to explore (controls cost). Default 32. */
30
+ budget?: number;
31
+ /** Max depth from root before pruning. Default 6. */
32
+ maxDepth?: number;
33
+ /** Score floor — nodes below this are pruned. Default 0.05. */
34
+ scoreFloor?: number;
35
+ }
36
+ export interface DDTreeNode {
37
+ commit: Commit;
38
+ depth: number;
39
+ /** Combined score: relevance × decay-by-depth × parent score. */
40
+ score: number;
41
+ /** Parent commit hash (the *child* in graph terms — i.e. the node we
42
+ * expanded from to reach this commit). Undefined for root nodes. */
43
+ parent?: string;
44
+ /** Why this node was pruned, or "accepted". */
45
+ status: "accepted" | "pruned-floor" | "pruned-depth" | "pruned-budget";
46
+ }
47
+ export interface DDTreeResult {
48
+ /** All explored nodes with verdict (in pop order). */
49
+ visited: DDTreeNode[];
50
+ /** Accepted nodes only, sorted by score (highest first). */
51
+ accepted: DDTreeNode[];
52
+ budgetUsed: number;
53
+ }
54
+ /** Score function: relevance × decay-by-depth × log_priors etc. Caller provides. */
55
+ export type ScoreFn = (commit: Commit, depth: number, parentScore: number) => number;
56
+ /** Look up a commit's parent hashes. Caller provides (typically from store). */
57
+ export type ParentResolver = (hash: string) => string[];
58
+ /** Look up a commit by hash. Caller provides. */
59
+ export type CommitResolver = (hash: string) => Commit | undefined;
60
+ /**
61
+ * Best-First Search through commit ancestors. See module docstring.
62
+ *
63
+ * Returns every node we touched (with verdict), the sorted list of
64
+ * accepted nodes, and how much of the budget we consumed.
65
+ */
66
+ export declare function exploreDDTree(rootHashes: string[], scoreFn: ScoreFn, parents: ParentResolver, commits: CommitResolver, opts?: DDTreeOptions): DDTreeResult;
67
+ //# sourceMappingURL=ddtree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddtree.d.ts","sourceRoot":"","sources":["../../src/retrieve/ddtree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd;yEACqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+CAA+C;IAC/C,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,cAAc,GAAG,eAAe,CAAC;CACxE;AAED,MAAM,WAAW,YAAY;IAC3B,sDAAsD;IACtD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;AAErF,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;AAExD,iDAAiD;AACjD,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAqElE;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,EACvB,IAAI,GAAE,aAAkB,GACvB,YAAY,CA+Fd"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Simple binary max-heap on score. Stable enough for our needs — when two
3
+ * nodes tie on score, insertion order roughly wins (heap structure means
4
+ * "roughly", not "strictly", but this is fine for exploration).
5
+ */
6
+ class MaxHeap {
7
+ data = [];
8
+ get size() {
9
+ return this.data.length;
10
+ }
11
+ push(item) {
12
+ this.data.push(item);
13
+ this.siftUp(this.data.length - 1);
14
+ }
15
+ pop() {
16
+ const n = this.data.length;
17
+ if (n === 0)
18
+ return undefined;
19
+ const top = this.data[0];
20
+ const last = this.data.pop();
21
+ if (n > 1) {
22
+ this.data[0] = last;
23
+ this.siftDown(0);
24
+ }
25
+ return top;
26
+ }
27
+ /** Drain remaining items (for "pruned-budget" reporting). */
28
+ drain() {
29
+ const out = [...this.data];
30
+ this.data = [];
31
+ return out;
32
+ }
33
+ siftUp(i) {
34
+ while (i > 0) {
35
+ const parent = (i - 1) >> 1;
36
+ if (this.data[parent].score >= this.data[i].score)
37
+ break;
38
+ [this.data[parent], this.data[i]] = [this.data[i], this.data[parent]];
39
+ i = parent;
40
+ }
41
+ }
42
+ siftDown(i) {
43
+ const n = this.data.length;
44
+ while (true) {
45
+ const l = 2 * i + 1;
46
+ const r = 2 * i + 2;
47
+ let best = i;
48
+ if (l < n && this.data[l].score > this.data[best].score)
49
+ best = l;
50
+ if (r < n && this.data[r].score > this.data[best].score)
51
+ best = r;
52
+ if (best === i)
53
+ break;
54
+ [this.data[best], this.data[i]] = [this.data[i], this.data[best]];
55
+ i = best;
56
+ }
57
+ }
58
+ }
59
+ /**
60
+ * Best-First Search through commit ancestors. See module docstring.
61
+ *
62
+ * Returns every node we touched (with verdict), the sorted list of
63
+ * accepted nodes, and how much of the budget we consumed.
64
+ */
65
+ export function exploreDDTree(rootHashes, scoreFn, parents, commits, opts = {}) {
66
+ const budget = opts.budget ?? 32;
67
+ const maxDepth = opts.maxDepth ?? 6;
68
+ const scoreFloor = opts.scoreFloor ?? 0.05;
69
+ const heap = new MaxHeap();
70
+ const visited = [];
71
+ const seen = new Set(); // cycle / diamond protection
72
+ let budgetUsed = 0;
73
+ // Seed: each root scored with parentScore=1 (no prior).
74
+ for (const hash of rootHashes) {
75
+ const c = commits(hash);
76
+ if (!c)
77
+ continue;
78
+ const score = scoreFn(c, 0, 1);
79
+ heap.push({ hash, depth: 0, score });
80
+ }
81
+ while (heap.size > 0 && budgetUsed < budget) {
82
+ const item = heap.pop();
83
+ if (seen.has(item.hash))
84
+ continue; // cycle / diamond
85
+ seen.add(item.hash);
86
+ const commit = commits(item.hash);
87
+ if (!commit)
88
+ continue;
89
+ budgetUsed++;
90
+ // Floor check first — cheap and short-circuits deep pruning.
91
+ if (item.score < scoreFloor) {
92
+ visited.push({
93
+ commit,
94
+ depth: item.depth,
95
+ score: item.score,
96
+ parent: item.parent,
97
+ status: "pruned-floor",
98
+ });
99
+ continue;
100
+ }
101
+ // Depth check.
102
+ if (item.depth >= maxDepth) {
103
+ visited.push({
104
+ commit,
105
+ depth: item.depth,
106
+ score: item.score,
107
+ parent: item.parent,
108
+ status: "pruned-depth",
109
+ });
110
+ continue;
111
+ }
112
+ // Accept and expand.
113
+ visited.push({
114
+ commit,
115
+ depth: item.depth,
116
+ score: item.score,
117
+ parent: item.parent,
118
+ status: "accepted",
119
+ });
120
+ const childDepth = item.depth + 1;
121
+ for (const parentHash of parents(item.hash)) {
122
+ if (seen.has(parentHash))
123
+ continue;
124
+ const parentCommit = commits(parentHash);
125
+ if (!parentCommit)
126
+ continue;
127
+ const childScore = scoreFn(parentCommit, childDepth, item.score);
128
+ heap.push({
129
+ hash: parentHash,
130
+ depth: childDepth,
131
+ score: childScore,
132
+ parent: item.hash,
133
+ });
134
+ }
135
+ }
136
+ // Anything still in the heap when we hit the budget wall is "pruned-budget".
137
+ for (const leftover of heap.drain()) {
138
+ if (seen.has(leftover.hash))
139
+ continue;
140
+ const c = commits(leftover.hash);
141
+ if (!c)
142
+ continue;
143
+ visited.push({
144
+ commit: c,
145
+ depth: leftover.depth,
146
+ score: leftover.score,
147
+ parent: leftover.parent,
148
+ status: "pruned-budget",
149
+ });
150
+ }
151
+ const accepted = visited
152
+ .filter((n) => n.status === "accepted")
153
+ .sort((a, b) => b.score - a.score);
154
+ return { visited, accepted, budgetUsed };
155
+ }
156
+ //# sourceMappingURL=ddtree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddtree.js","sourceRoot":"","sources":["../../src/retrieve/ddtree.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ddtree.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddtree.test.d.ts","sourceRoot":"","sources":["../../src/retrieve/ddtree.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,181 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { exploreDDTree } from "./ddtree.js";
3
+ /** Build a fake commit. We only care about hash for tree shape; everything
4
+ * else is filled with throwaway values to satisfy the type. */
5
+ function makeCommit(hash, parents = []) {
6
+ return {
7
+ hash,
8
+ shortHash: hash.slice(0, 7),
9
+ authorName: "Test",
10
+ authorEmail: "test@example.com",
11
+ authorDate: "2025-01-01T00:00:00Z",
12
+ committerDate: "2025-01-01T00:00:00Z",
13
+ subject: `commit ${hash}`,
14
+ body: "",
15
+ files: [],
16
+ parents,
17
+ };
18
+ }
19
+ /** Build resolvers from a flat list of commits. */
20
+ function makeResolvers(commits) {
21
+ const byHash = new Map(commits.map((c) => [c.hash, c]));
22
+ return {
23
+ parents: (h) => byHash.get(h)?.parents ?? [],
24
+ commits: (h) => byHash.get(h),
25
+ };
26
+ }
27
+ describe("exploreDDTree — basic ancestry walk", () => {
28
+ it("explores root → 3 parents and accepts all under generous budget", () => {
29
+ // Tree: A -> B, C, D (A has three parents)
30
+ const a = makeCommit("A", ["B", "C", "D"]);
31
+ const b = makeCommit("B", []);
32
+ const c = makeCommit("C", []);
33
+ const d = makeCommit("D", []);
34
+ const { parents, commits } = makeResolvers([a, b, c, d]);
35
+ // Constant score 1.0 — everything stays well above the floor.
36
+ const score = () => 1;
37
+ const result = exploreDDTree(["A"], score, parents, commits, {
38
+ budget: 5,
39
+ maxDepth: 6,
40
+ scoreFloor: 0.05,
41
+ });
42
+ expect(result.budgetUsed).toBe(4);
43
+ expect(result.accepted.map((n) => n.commit.hash).sort()).toEqual([
44
+ "A",
45
+ "B",
46
+ "C",
47
+ "D",
48
+ ]);
49
+ expect(result.visited.every((n) => n.status === "accepted")).toBe(true);
50
+ });
51
+ it("records parent hash for each non-root accepted node", () => {
52
+ const a = makeCommit("A", ["B"]);
53
+ const b = makeCommit("B", []);
54
+ const { parents, commits } = makeResolvers([a, b]);
55
+ const r = exploreDDTree(["A"], () => 1, parents, commits, { budget: 5 });
56
+ const accB = r.accepted.find((n) => n.commit.hash === "B");
57
+ expect(accB?.parent).toBe("A");
58
+ const accA = r.accepted.find((n) => n.commit.hash === "A");
59
+ expect(accA?.parent).toBeUndefined();
60
+ });
61
+ });
62
+ describe("exploreDDTree — pruning", () => {
63
+ it("prunes deep nodes when depth >= maxDepth", () => {
64
+ // Linear chain A -> B -> C -> D -> E
65
+ const chain = ["A", "B", "C", "D", "E"].map((h, i, arr) => makeCommit(h, arr[i + 1] ? [arr[i + 1]] : []));
66
+ const { parents, commits } = makeResolvers(chain);
67
+ const r = exploreDDTree(["A"], () => 1, parents, commits, {
68
+ budget: 100,
69
+ maxDepth: 2, // accept depths 0, 1; prune at depth 2
70
+ scoreFloor: 0,
71
+ });
72
+ const acc = r.accepted.map((n) => `${n.commit.hash}@${n.depth}`).sort();
73
+ expect(acc).toEqual(["A@0", "B@1"]);
74
+ const prunedDepth = r.visited.filter((n) => n.status === "pruned-depth");
75
+ expect(prunedDepth.map((n) => n.commit.hash)).toEqual(["C"]);
76
+ });
77
+ it("prunes nodes whose score falls below the scoreFloor", () => {
78
+ // A -> B; score decays exponentially with depth.
79
+ const a = makeCommit("A", ["B"]);
80
+ const b = makeCommit("B", []);
81
+ const { parents, commits } = makeResolvers([a, b]);
82
+ // depth 0 -> 1.0, depth 1 -> 0.01 (below floor 0.05)
83
+ const score = (_c, depth) => Math.pow(0.01, depth);
84
+ const r = exploreDDTree(["A"], score, parents, commits, {
85
+ budget: 100,
86
+ maxDepth: 6,
87
+ scoreFloor: 0.05,
88
+ });
89
+ expect(r.accepted.map((n) => n.commit.hash)).toEqual(["A"]);
90
+ const floored = r.visited.filter((n) => n.status === "pruned-floor");
91
+ expect(floored.map((n) => n.commit.hash)).toEqual(["B"]);
92
+ });
93
+ it("marks remaining heap as pruned-budget when budget exhausted", () => {
94
+ // Wide tree: root A with parents B,C,D,E,F (5 parents).
95
+ const a = makeCommit("A", ["B", "C", "D", "E", "F"]);
96
+ const others = ["B", "C", "D", "E", "F"].map((h) => makeCommit(h, []));
97
+ const { parents, commits } = makeResolvers([a, ...others]);
98
+ // Distinct scores so only the highest-score parents get accepted first.
99
+ const scoreMap = { A: 1, B: 0.9, C: 0.8, D: 0.7, E: 0.6, F: 0.5 };
100
+ const score = (c) => scoreMap[c.hash];
101
+ const r = exploreDDTree(["A"], score, parents, commits, {
102
+ budget: 3, // accept A + 2 best parents; rest -> pruned-budget
103
+ maxDepth: 6,
104
+ scoreFloor: 0,
105
+ });
106
+ expect(r.budgetUsed).toBe(3);
107
+ expect(r.accepted.map((n) => n.commit.hash)).toEqual(["A", "B", "C"]);
108
+ const budgetPruned = r.visited.filter((n) => n.status === "pruned-budget");
109
+ expect(budgetPruned.map((n) => n.commit.hash).sort()).toEqual([
110
+ "D",
111
+ "E",
112
+ "F",
113
+ ]);
114
+ });
115
+ });
116
+ describe("exploreDDTree — robustness", () => {
117
+ it("handles a cycle in the parent graph without looping forever", () => {
118
+ // Pathological: A -> B, B -> A (cycle).
119
+ const a = makeCommit("A", ["B"]);
120
+ const b = makeCommit("B", ["A"]);
121
+ const { parents, commits } = makeResolvers([a, b]);
122
+ const r = exploreDDTree(["A"], () => 1, parents, commits, {
123
+ budget: 100,
124
+ maxDepth: 10,
125
+ scoreFloor: 0,
126
+ });
127
+ // Should terminate; each node visited once.
128
+ expect(r.accepted.map((n) => n.commit.hash).sort()).toEqual(["A", "B"]);
129
+ expect(r.budgetUsed).toBe(2);
130
+ });
131
+ it("handles a diamond (merge) — same hash reachable two ways, processed once", () => {
132
+ // A -> B, A -> C, both B and C -> D.
133
+ const a = makeCommit("A", ["B", "C"]);
134
+ const b = makeCommit("B", ["D"]);
135
+ const c = makeCommit("C", ["D"]);
136
+ const d = makeCommit("D", []);
137
+ const { parents, commits } = makeResolvers([a, b, c, d]);
138
+ const r = exploreDDTree(["A"], () => 1, parents, commits, {
139
+ budget: 100,
140
+ maxDepth: 10,
141
+ scoreFloor: 0,
142
+ });
143
+ const dCount = r.visited.filter((n) => n.commit.hash === "D").length;
144
+ expect(dCount).toBe(1);
145
+ expect(r.accepted.map((n) => n.commit.hash).sort()).toEqual([
146
+ "A",
147
+ "B",
148
+ "C",
149
+ "D",
150
+ ]);
151
+ });
152
+ it("ignores root hashes that don't resolve to a commit", () => {
153
+ const a = makeCommit("A", []);
154
+ const { parents, commits } = makeResolvers([a]);
155
+ const r = exploreDDTree(["MISSING", "A"], () => 1, parents, commits, {
156
+ budget: 5,
157
+ });
158
+ expect(r.accepted.map((n) => n.commit.hash)).toEqual(["A"]);
159
+ });
160
+ it("returns an empty result when no roots resolve", () => {
161
+ const { parents, commits } = makeResolvers([]);
162
+ const r = exploreDDTree(["NOPE"], () => 1, parents, commits);
163
+ expect(r.accepted).toEqual([]);
164
+ expect(r.visited).toEqual([]);
165
+ expect(r.budgetUsed).toBe(0);
166
+ });
167
+ it("orders accepted by score descending", () => {
168
+ const a = makeCommit("A", ["B", "C"]);
169
+ const b = makeCommit("B", []);
170
+ const c = makeCommit("C", []);
171
+ const { parents, commits } = makeResolvers([a, b, c]);
172
+ const scores = { A: 0.5, B: 0.9, C: 0.7 };
173
+ const r = exploreDDTree(["A"], (cm) => scores[cm.hash], parents, commits, {
174
+ budget: 5,
175
+ scoreFloor: 0,
176
+ });
177
+ const order = r.accepted.map((n) => n.commit.hash);
178
+ expect(order).toEqual(["B", "C", "A"]);
179
+ });
180
+ });
181
+ //# sourceMappingURL=ddtree.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddtree.test.js","sourceRoot":"","sources":["../../src/retrieve/ddtree.test.ts"],"names":[],"mappings":"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"}
@@ -3,4 +3,7 @@ export * from "./rerank.js";
3
3
  export * from "./intent.js";
4
4
  export * from "./synthesize.js";
5
5
  export * from "./novel-scoring.js";
6
+ export * from "./ddtree.js";
7
+ export * from "./stream.js";
8
+ export * from "./leviathan.js";
6
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
@@ -3,4 +3,7 @@ export * from "./rerank.js";
3
3
  export * from "./intent.js";
4
4
  export * from "./synthesize.js";
5
5
  export * from "./novel-scoring.js";
6
+ export * from "./ddtree.js";
7
+ export * from "./stream.js";
8
+ export * from "./leviathan.js";
6
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/retrieve/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Leviathan citation verification — speculative-decoding-style draft/target
3
+ * loop applied to LLM answers.
4
+ *
5
+ * Algorithm 1 from the Leviathan paper: a fast "draft" model proposes,
6
+ * a strict "target" rejects un-supported tokens, residual sampling re-drafts.
7
+ *
8
+ * Mneme's adaptation:
9
+ * - Draft = the LLM's synthesized answer (already produced by `synthesize()`)
10
+ * - Target = a deterministic citation matcher (each backticked hash must
11
+ * exist in the evidence pool AND the surrounding sentence must
12
+ * contextually match the cited commit's subject)
13
+ * - Reject = mark the unverified claim with `[unverified: ...]` so the
14
+ * user sees what was filtered (no silent deletion)
15
+ * - Residual = `degraded: true` when too many rejections — caller should
16
+ * warn the user that trust is reduced
17
+ *
18
+ * The keyword/token-overlap match is intentionally lightweight (no embedding
19
+ * call). Cosine on a sentence-level embedding would be more accurate but
20
+ * Mneme's core stays dep-free; embeddings live in the embeddings package and
21
+ * we don't want a runtime cycle. Empirically the 0.2 token-overlap floor
22
+ * catches "hash present, sentence completely unrelated" without flagging
23
+ * legitimate paraphrases.
24
+ */
25
+ import type { EventSink } from "./stream.js";
26
+ export interface LeviathanInput {
27
+ /** The raw LLM answer text. */
28
+ answer: string;
29
+ /** Evidence pool (ordered by relevance). Each must have hash + subject. */
30
+ evidence: Array<{
31
+ hash: string;
32
+ shortHash: string;
33
+ subject: string;
34
+ }>;
35
+ /** Optional event sink for tracing rejections. */
36
+ events?: EventSink;
37
+ }
38
+ /** Per-claim verdict. */
39
+ export type LeviathanVerdict = "verified" | "hash-not-in-evidence" | "claim-not-supported" | "no-citation";
40
+ export interface LeviathanResult {
41
+ /** The cleaned answer with unverified claims wrapped in `[unverified: ...]`. */
42
+ cleanedAnswer: string;
43
+ /** Per-claim verification trace. */
44
+ verifications: Array<{
45
+ claimText: string;
46
+ citedHash?: string;
47
+ verdict: LeviathanVerdict;
48
+ reason: string;
49
+ }>;
50
+ /** 0..1 — fraction of citation-bearing claims that verified. */
51
+ trustScore: number;
52
+ /** True if too many rejections (trustScore < 0.6). */
53
+ degraded: boolean;
54
+ }
55
+ /**
56
+ * Run Algorithm-1-style verification on an LLM answer.
57
+ *
58
+ * The loop is single-pass (we don't actually re-draft, since the answer is
59
+ * already paid-for). Instead we tag rejected spans inline so the renderer
60
+ * can dim/strikethrough them.
61
+ */
62
+ export declare function verifyAnswerLeviathan(input: LeviathanInput): LeviathanResult;
63
+ /**
64
+ * Lightweight Jaccard-like token overlap: shared tokens / total unique tokens
65
+ * after dropping stopwords + the cited hash itself. Symmetric, in [0,1].
66
+ */
67
+ export declare function tokenOverlap(sentence: string, subject: string): number;
68
+ //# sourceMappingURL=leviathan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leviathan.d.ts","sourceRoot":"","sources":["../../src/retrieve/leviathan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,kDAAkD;IAClD,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,yBAAyB;AACzB,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,sBAAsB,GACtB,qBAAqB,GACrB,aAAa,CAAC;AAElB,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,EAAE,KAAK,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,gBAAgB,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAQD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CA6E5E;AAiDD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQtE"}