@mneme-ai/core 0.15.0 → 0.16.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.
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Mneme Guardian — the 24/7 self-healing engine.
3
+ *
4
+ * while (true) {
5
+ * diagnose();
6
+ * fix();
7
+ * learn();
8
+ * sleep(interval);
9
+ * }
10
+ *
11
+ * The Guardian is a long-running diagnostic + auto-remediation loop. It
12
+ * watches for weaknesses (regressions in index quality, drift between
13
+ * HEAD and the index, missing embeddings, schema-version mismatch) and
14
+ * threats (data corruption signals, token expiry on remote APIs, secret
15
+ * patterns slipping through redaction) and applies safe corrective
16
+ * actions automatically. Anything risky is *logged with a recommendation*
17
+ * rather than auto-applied.
18
+ *
19
+ * Pure data structures + dispatch — no side effects in this module
20
+ * (file I/O lives in the CLI command). Caller decides whether to
21
+ * actually apply the recommended action or just observe.
22
+ *
23
+ * Design principles:
24
+ * • SAFE BY DEFAULT — auto-apply only the actions that are demonstrably
25
+ * reversible (re-index, calibrate). Anything that could lose data
26
+ * gets recommended, not executed.
27
+ * • OBSERVABLE — every diagnosis + action is appended to a JSONL log so
28
+ * the operator can audit what the daemon did.
29
+ * • DETERMINISTIC — same inputs → same diagnosis. No flaky heuristics.
30
+ */
31
+ import type { Commit } from "../types.js";
32
+ import type { IndexQualityReport } from "../indexer/quality.js";
33
+ export type WeaknessKind = "drift" | "missing-embeddings" | "low-quality" | "stale-calibration" | "schema-drift" | "token-expiry" | "redaction-gap";
34
+ export type ThreatKind = "tamper" | "secret-leak" | "outlier-author" | "deletion-storm";
35
+ export type Severity = "low" | "medium" | "high" | "critical";
36
+ export type ActionPolicy = "auto" | "recommended" | "observe";
37
+ export interface GuardianFinding {
38
+ kind: WeaknessKind | ThreatKind;
39
+ severity: Severity;
40
+ /** One-line summary for the operator. */
41
+ message: string;
42
+ /** Suggested CLI command to apply the fix (if any). */
43
+ suggestedAction?: string;
44
+ /** Whether the daemon should auto-apply. */
45
+ policy: ActionPolicy;
46
+ /** Free-form structured detail. */
47
+ detail?: Record<string, unknown>;
48
+ }
49
+ export interface GuardianInput {
50
+ /** Latest HEAD commits in git (most recent first). */
51
+ headCommits: Commit[];
52
+ /** Commits currently indexed (most recent first). */
53
+ indexedCommits: Commit[];
54
+ /** Most recent index quality report (if available). */
55
+ quality: IndexQualityReport | null;
56
+ /** Last-known good index quality (for regression detection). */
57
+ lastQualityScore: number | null;
58
+ /** Schema version recorded in the store. */
59
+ storeSchemaVersion: number;
60
+ /** Schema version expected by the running binary. */
61
+ expectedSchemaVersion: number;
62
+ /** Number of feedback events since last `mneme calibrate`. */
63
+ feedbackEventsSinceCalibrate: number;
64
+ /** Threshold above which calibrate is recommended. Default 25. */
65
+ feedbackCalibrateThreshold?: number;
66
+ /** Threshold below which low-quality is flagged (0..1). Default 0.55 (= grade C). */
67
+ qualityFloor?: number;
68
+ /** Quality regression threshold (drop in score). Default 0.1. */
69
+ qualityRegressionThreshold?: number;
70
+ }
71
+ export interface GuardianReport {
72
+ generatedAt: string;
73
+ findings: GuardianFinding[];
74
+ summary: {
75
+ findings: number;
76
+ autoActions: number;
77
+ recommendations: number;
78
+ threats: number;
79
+ };
80
+ }
81
+ /**
82
+ * Run one diagnostic pass. Pure function — same input → same output.
83
+ *
84
+ * The CLI wraps this in `while (true)` with a sleep, but the diagnosis
85
+ * itself is stateless.
86
+ */
87
+ export declare function diagnose(input: GuardianInput): GuardianReport;
88
+ /**
89
+ * Decide which findings the daemon should auto-apply this tick. Returns
90
+ * findings ordered by severity (high → low).
91
+ */
92
+ export declare function selectAutoActions(report: GuardianReport): GuardianFinding[];
93
+ //# sourceMappingURL=guardian.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardian.d.ts","sourceRoot":"","sources":["../../src/guardian/guardian.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,oBAAoB,GACpB,aAAa,GACb,mBAAmB,GACnB,cAAc,GACd,cAAc,GACd,eAAe,CAAC;AAEpB,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,aAAa,GACb,gBAAgB,GAChB,gBAAgB,CAAC;AAErB,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9D,MAAM,MAAM,YAAY,GACpB,MAAM,GACN,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,YAAY,GAAG,UAAU,CAAC;IAChC,QAAQ,EAAE,QAAQ,CAAC;IACnB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,MAAM,EAAE,YAAY,CAAC;IACrB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,qDAAqD;IACrD,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,uDAAuD;IACvD,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACnC,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,4CAA4C;IAC5C,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8DAA8D;IAC9D,4BAA4B,EAAE,MAAM,CAAC;IACrC,kEAAkE;IAClE,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CA0H7D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,EAAE,CAU3E"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Mneme Guardian — the 24/7 self-healing engine.
3
+ *
4
+ * while (true) {
5
+ * diagnose();
6
+ * fix();
7
+ * learn();
8
+ * sleep(interval);
9
+ * }
10
+ *
11
+ * The Guardian is a long-running diagnostic + auto-remediation loop. It
12
+ * watches for weaknesses (regressions in index quality, drift between
13
+ * HEAD and the index, missing embeddings, schema-version mismatch) and
14
+ * threats (data corruption signals, token expiry on remote APIs, secret
15
+ * patterns slipping through redaction) and applies safe corrective
16
+ * actions automatically. Anything risky is *logged with a recommendation*
17
+ * rather than auto-applied.
18
+ *
19
+ * Pure data structures + dispatch — no side effects in this module
20
+ * (file I/O lives in the CLI command). Caller decides whether to
21
+ * actually apply the recommended action or just observe.
22
+ *
23
+ * Design principles:
24
+ * • SAFE BY DEFAULT — auto-apply only the actions that are demonstrably
25
+ * reversible (re-index, calibrate). Anything that could lose data
26
+ * gets recommended, not executed.
27
+ * • OBSERVABLE — every diagnosis + action is appended to a JSONL log so
28
+ * the operator can audit what the daemon did.
29
+ * • DETERMINISTIC — same inputs → same diagnosis. No flaky heuristics.
30
+ */
31
+ /**
32
+ * Run one diagnostic pass. Pure function — same input → same output.
33
+ *
34
+ * The CLI wraps this in `while (true)` with a sleep, but the diagnosis
35
+ * itself is stateless.
36
+ */
37
+ export function diagnose(input) {
38
+ const findings = [];
39
+ const feedbackThreshold = input.feedbackCalibrateThreshold ?? 25;
40
+ const qualityFloor = input.qualityFloor ?? 0.55;
41
+ const qualityRegression = input.qualityRegressionThreshold ?? 0.1;
42
+ // ── Weakness 1: index drift ────────────────────────────────────────
43
+ const indexedHashes = new Set(input.indexedCommits.map((c) => c.hash));
44
+ const driftingCommits = input.headCommits.filter((c) => !indexedHashes.has(c.hash));
45
+ if (driftingCommits.length > 0) {
46
+ const sev = driftingCommits.length > 50
47
+ ? "high"
48
+ : driftingCommits.length > 10
49
+ ? "medium"
50
+ : "low";
51
+ findings.push({
52
+ kind: "drift",
53
+ severity: sev,
54
+ message: `${driftingCommits.length} commit(s) on HEAD not yet indexed.`,
55
+ suggestedAction: "mneme index",
56
+ policy: "auto",
57
+ detail: {
58
+ driftingCount: driftingCommits.length,
59
+ headNewest: input.headCommits[0]?.hash,
60
+ indexNewest: input.indexedCommits[0]?.hash,
61
+ },
62
+ });
63
+ }
64
+ // ── Weakness 2: missing embeddings ─────────────────────────────────
65
+ if (input.quality) {
66
+ const embedRatio = input.quality.metrics.embedRatio;
67
+ if (embedRatio < 0.95 && input.quality.indexedChunks > 0) {
68
+ findings.push({
69
+ kind: "missing-embeddings",
70
+ severity: embedRatio < 0.5 ? "high" : "medium",
71
+ message: `${Math.round((1 - embedRatio) * 100)}% of chunks have no embedding (${input.quality.indexedChunks - input.quality.embeddedChunks} chunks).`,
72
+ suggestedAction: "mneme index",
73
+ policy: "auto",
74
+ detail: { embedRatio },
75
+ });
76
+ }
77
+ }
78
+ // ── Weakness 3: low overall quality ────────────────────────────────
79
+ if (input.quality && input.quality.overallScore < qualityFloor) {
80
+ findings.push({
81
+ kind: "low-quality",
82
+ severity: input.quality.overallScore < 0.4 ? "high" : "medium",
83
+ message: `Index quality grade ${input.quality.grade} (${Math.round(input.quality.overallScore * 100)}/100) is below recommended floor.`,
84
+ suggestedAction: "mneme heal",
85
+ policy: "recommended",
86
+ detail: {
87
+ score: input.quality.overallScore,
88
+ grade: input.quality.grade,
89
+ },
90
+ });
91
+ }
92
+ // ── Weakness 3b: quality regression ────────────────────────────────
93
+ if (input.quality &&
94
+ input.lastQualityScore !== null &&
95
+ input.lastQualityScore - input.quality.overallScore >= qualityRegression) {
96
+ findings.push({
97
+ kind: "low-quality",
98
+ severity: "medium",
99
+ message: `Index quality dropped from ${Math.round(input.lastQualityScore * 100)} to ${Math.round(input.quality.overallScore * 100)}.`,
100
+ suggestedAction: "mneme index --analyze",
101
+ policy: "observe",
102
+ detail: {
103
+ before: input.lastQualityScore,
104
+ after: input.quality.overallScore,
105
+ regression: input.lastQualityScore - input.quality.overallScore,
106
+ },
107
+ });
108
+ }
109
+ // ── Weakness 4: stale calibration ──────────────────────────────────
110
+ if (input.feedbackEventsSinceCalibrate >= feedbackThreshold) {
111
+ findings.push({
112
+ kind: "stale-calibration",
113
+ severity: "low",
114
+ message: `${input.feedbackEventsSinceCalibrate} feedback events since last calibrate — retrieval knobs may need re-tuning.`,
115
+ suggestedAction: "mneme calibrate",
116
+ policy: "auto",
117
+ detail: {
118
+ eventsSinceCalibrate: input.feedbackEventsSinceCalibrate,
119
+ threshold: feedbackThreshold,
120
+ },
121
+ });
122
+ }
123
+ // ── Weakness 5: schema drift ───────────────────────────────────────
124
+ if (input.storeSchemaVersion < input.expectedSchemaVersion) {
125
+ findings.push({
126
+ kind: "schema-drift",
127
+ severity: "high",
128
+ message: `Store schema is v${input.storeSchemaVersion}, binary expects v${input.expectedSchemaVersion}.`,
129
+ suggestedAction: "mneme index",
130
+ policy: "auto",
131
+ detail: {
132
+ storeVersion: input.storeSchemaVersion,
133
+ expectedVersion: input.expectedSchemaVersion,
134
+ },
135
+ });
136
+ }
137
+ const summary = {
138
+ findings: findings.length,
139
+ autoActions: findings.filter((f) => f.policy === "auto").length,
140
+ recommendations: findings.filter((f) => f.policy === "recommended").length,
141
+ threats: findings.filter((f) => isThreat(f.kind)).length,
142
+ };
143
+ return {
144
+ generatedAt: new Date().toISOString(),
145
+ findings,
146
+ summary,
147
+ };
148
+ }
149
+ /**
150
+ * Decide which findings the daemon should auto-apply this tick. Returns
151
+ * findings ordered by severity (high → low).
152
+ */
153
+ export function selectAutoActions(report) {
154
+ const order = {
155
+ critical: 0,
156
+ high: 1,
157
+ medium: 2,
158
+ low: 3,
159
+ };
160
+ return report.findings
161
+ .filter((f) => f.policy === "auto")
162
+ .sort((a, b) => order[a.severity] - order[b.severity]);
163
+ }
164
+ function isThreat(kind) {
165
+ return (kind === "tamper" ||
166
+ kind === "secret-leak" ||
167
+ kind === "outlier-author" ||
168
+ kind === "deletion-storm");
169
+ }
170
+ //# sourceMappingURL=guardian.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardian.js","sourceRoot":"","sources":["../../src/guardian/guardian.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=guardian.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardian.test.d.ts","sourceRoot":"","sources":["../../src/guardian/guardian.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,154 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { diagnose, selectAutoActions } from "./guardian.js";
3
+ function mkCommit(hash, date = "2024-01-01") {
4
+ return {
5
+ hash,
6
+ shortHash: hash.slice(0, 7),
7
+ authorName: "A",
8
+ authorEmail: "a@x.com",
9
+ authorDate: date,
10
+ committerDate: date,
11
+ subject: "x",
12
+ body: "",
13
+ files: [],
14
+ parents: [],
15
+ };
16
+ }
17
+ function mkQuality(score, embedRatio = 1) {
18
+ return {
19
+ indexedCommits: 10,
20
+ indexedChunks: 30,
21
+ embeddedChunks: Math.round(30 * embedRatio),
22
+ metrics: {
23
+ chunkDensity: 1,
24
+ embedRatio,
25
+ subjectQuality: 1,
26
+ bodyRatio: 1,
27
+ prRatio: 1,
28
+ issueRatio: 1,
29
+ duplicateRatio: 0,
30
+ tokenizerHealth: 1,
31
+ },
32
+ overallScore: score,
33
+ grade: score >= 0.85 ? "A" : score >= 0.7 ? "B" : score >= 0.55 ? "C" : score >= 0.4 ? "D" : "F",
34
+ recommendations: [],
35
+ };
36
+ }
37
+ const baseInput = {
38
+ headCommits: [],
39
+ indexedCommits: [],
40
+ quality: mkQuality(0.9),
41
+ lastQualityScore: 0.9,
42
+ storeSchemaVersion: 3,
43
+ expectedSchemaVersion: 3,
44
+ feedbackEventsSinceCalibrate: 0,
45
+ };
46
+ describe("diagnose", () => {
47
+ it("returns no findings when everything is healthy", () => {
48
+ const r = diagnose(baseInput);
49
+ expect(r.findings).toHaveLength(0);
50
+ expect(r.summary.autoActions).toBe(0);
51
+ });
52
+ it("detects index drift when HEAD has commits not in index", () => {
53
+ const r = diagnose({
54
+ ...baseInput,
55
+ headCommits: [mkCommit("new1"), mkCommit("new2"), mkCommit("indexed")],
56
+ indexedCommits: [mkCommit("indexed")],
57
+ });
58
+ const drift = r.findings.find((f) => f.kind === "drift");
59
+ expect(drift).toBeDefined();
60
+ expect(drift.policy).toBe("auto");
61
+ expect(drift.suggestedAction).toBe("mneme index");
62
+ });
63
+ it("escalates drift severity by count", () => {
64
+ const lots = Array.from({ length: 60 }, (_, i) => mkCommit("c" + i));
65
+ const r = diagnose({
66
+ ...baseInput,
67
+ headCommits: lots,
68
+ indexedCommits: [],
69
+ });
70
+ const drift = r.findings.find((f) => f.kind === "drift");
71
+ expect(drift.severity).toBe("high");
72
+ });
73
+ it("detects missing embeddings", () => {
74
+ const r = diagnose({
75
+ ...baseInput,
76
+ quality: mkQuality(0.9, 0.7),
77
+ });
78
+ const missing = r.findings.find((f) => f.kind === "missing-embeddings");
79
+ expect(missing).toBeDefined();
80
+ expect(missing.policy).toBe("auto");
81
+ });
82
+ it("flags low quality below floor", () => {
83
+ const r = diagnose({
84
+ ...baseInput,
85
+ quality: mkQuality(0.4),
86
+ });
87
+ const low = r.findings.find((f) => f.kind === "low-quality");
88
+ expect(low).toBeDefined();
89
+ expect(low.policy).toBe("recommended");
90
+ });
91
+ it("flags quality regression even when above floor", () => {
92
+ const r = diagnose({
93
+ ...baseInput,
94
+ quality: mkQuality(0.7),
95
+ lastQualityScore: 0.9,
96
+ });
97
+ const reg = r.findings.find((f) => f.kind === "low-quality" &&
98
+ f.message.toLowerCase().includes("dropped"));
99
+ expect(reg).toBeDefined();
100
+ });
101
+ it("recommends calibrate when feedback accumulates", () => {
102
+ const r = diagnose({
103
+ ...baseInput,
104
+ feedbackEventsSinceCalibrate: 50,
105
+ });
106
+ const stale = r.findings.find((f) => f.kind === "stale-calibration");
107
+ expect(stale).toBeDefined();
108
+ expect(stale.policy).toBe("auto");
109
+ expect(stale.suggestedAction).toBe("mneme calibrate");
110
+ });
111
+ it("flags schema drift when store < expected", () => {
112
+ const r = diagnose({
113
+ ...baseInput,
114
+ storeSchemaVersion: 2,
115
+ expectedSchemaVersion: 3,
116
+ });
117
+ const sd = r.findings.find((f) => f.kind === "schema-drift");
118
+ expect(sd).toBeDefined();
119
+ expect(sd.severity).toBe("high");
120
+ });
121
+ it("returns deterministic output for same input", () => {
122
+ const a = diagnose({
123
+ ...baseInput,
124
+ headCommits: [mkCommit("a")],
125
+ indexedCommits: [],
126
+ });
127
+ const b = diagnose({
128
+ ...baseInput,
129
+ headCommits: [mkCommit("a")],
130
+ indexedCommits: [],
131
+ });
132
+ expect(a.findings).toEqual(b.findings);
133
+ });
134
+ });
135
+ describe("selectAutoActions", () => {
136
+ it("returns only auto-policy findings, sorted by severity", () => {
137
+ const report = diagnose({
138
+ ...baseInput,
139
+ headCommits: [mkCommit("new1")], // low/medium drift → auto
140
+ indexedCommits: [],
141
+ storeSchemaVersion: 2, // high schema-drift → auto
142
+ expectedSchemaVersion: 3,
143
+ quality: mkQuality(0.4), // medium low-quality → recommended (not auto)
144
+ feedbackEventsSinceCalibrate: 30, // low stale-calib → auto
145
+ });
146
+ const actions = selectAutoActions(report);
147
+ // High severity should come first
148
+ expect(actions[0].severity).toBe("high");
149
+ // No "recommended" findings should be in the auto list
150
+ for (const a of actions)
151
+ expect(a.policy).toBe("auto");
152
+ });
153
+ });
154
+ //# sourceMappingURL=guardian.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardian.test.js","sourceRoot":"","sources":["../../src/guardian/guardian.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./guardian.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guardian/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./guardian.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/guardian/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
package/dist/index.d.ts CHANGED
@@ -10,4 +10,5 @@ export * as util from "./util/index.js";
10
10
  export * as wisdom from "./wisdom/index.js";
11
11
  export * as insights from "./insights/index.js";
12
12
  export * as quant from "./quant/index.js";
13
+ export * as guardian from "./guardian/index.js";
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -10,4 +10,5 @@ export * as util from "./util/index.js";
10
10
  export * as wisdom from "./wisdom/index.js";
11
11
  export * as insights from "./insights/index.js";
12
12
  export * as quant from "./quant/index.js";
13
+ export * as guardian from "./guardian/index.js";
13
14
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,KAAK,GAAG,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,OAAO,MAAM,oBAAoB,CAAC;AAC9C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,iBAAiB,CAAC;AACxC,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAC;AAC5C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC"}
@@ -2,4 +2,5 @@ export * from "./search.js";
2
2
  export * from "./rerank.js";
3
3
  export * from "./intent.js";
4
4
  export * from "./synthesize.js";
5
+ export * from "./novel-scoring.js";
5
6
  //# 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"}
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"}
@@ -2,4 +2,5 @@ export * from "./search.js";
2
2
  export * from "./rerank.js";
3
3
  export * from "./intent.js";
4
4
  export * from "./synthesize.js";
5
+ export * from "./novel-scoring.js";
5
6
  //# 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"}
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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Novel scoring algorithms for Mneme retrieval.
3
+ *
4
+ * These formulas are designed to outperform pure semantic similarity by
5
+ * exploiting signals that other tools (which only embed file contents)
6
+ * cannot see: time decay, regret patterns, author diversity, and the
7
+ * causal graph implicit in commit/PR/issue references.
8
+ *
9
+ * All formulas are pure functions. They take the set of search results
10
+ * + (optional) commit history and produce a re-ranked list. Use them as
11
+ * post-processors over base BM25/cosine search.
12
+ *
13
+ * Algorithms:
14
+ * 1. TDWE — Time-Decay Weighted Embedding score
15
+ * Formula: w(c) = exp(-λ · age_days / half_life)
16
+ * adjusted = base × w(c)
17
+ *
18
+ * 2. RACB — Regret-Aware Chunk Boosting
19
+ * Formula: boost(c) = 1 + ln(1 + days_to_followup × severity_factor)
20
+ * Severity: revert=3, hotfix=2, fix=1, sameFiles=0.5, none=0
21
+ *
22
+ * 3. ADS — Author Diversity Score re-ranking
23
+ * Formula: penalty = α · (same_author_above_in_ranking / K)
24
+ * final = base × (1 - penalty)
25
+ *
26
+ * 4. CGAR — Causal Graph Augmented Retrieval (light)
27
+ * Walks PR/issue links 2 hops; chunks reachable get boost = 0.85^hops
28
+ */
29
+ import type { Commit, SearchResult } from "../types.js";
30
+ export interface TdweOptions {
31
+ /** Reference time (UTC ms). Defaults to Date.now(). */
32
+ nowMs?: number;
33
+ /**
34
+ * Half-life in days. After this many days, weight decays to 0.5.
35
+ * Default 365 — a year-old commit is half as relevant as today's.
36
+ * Set higher for stable codebases, lower for fast-moving repos.
37
+ */
38
+ halfLifeDays?: number;
39
+ /**
40
+ * λ — decay coefficient. Higher = more aggressive decay.
41
+ * Default 0.693 (ln 2) so that age = halfLifeDays gives weight 0.5.
42
+ */
43
+ lambda?: number;
44
+ }
45
+ /**
46
+ * Apply time-decay weighting to a base score.
47
+ * - A commit from today gets weight ≈ 1.0
48
+ * - A commit at half-life age gets weight ≈ 0.5
49
+ * - A commit at 2× half-life age gets weight ≈ 0.25
50
+ */
51
+ export declare function timeDecayWeight(commitDateIso: string, opts?: TdweOptions): number;
52
+ export declare function applyTdwe(results: SearchResult[], opts?: TdweOptions): SearchResult[];
53
+ export type RegretKind = "revert" | "hotfix" | "fix" | "sameFiles" | "none";
54
+ export interface RegretSignal {
55
+ /** Commit hash that exhibits a regret follow-up. */
56
+ commitHash: string;
57
+ /** What kind of follow-up was detected. */
58
+ kind: RegretKind;
59
+ /** Days from commit to its follow-up. */
60
+ daysToFollowup: number;
61
+ }
62
+ export interface RacbOptions {
63
+ /**
64
+ * Maximum boost cap (multiplicative). Default 2.5 — chunks from highly
65
+ * regretted commits get up to 2.5× boost.
66
+ */
67
+ maxBoost?: number;
68
+ /** Map regret kind → severity factor used in formula. */
69
+ severityMap?: Record<RegretKind, number>;
70
+ }
71
+ /**
72
+ * Compute regret boost for a single signal.
73
+ * boost = 1 + ln(1 + days × severity)
74
+ *
75
+ * Why ln? Because the "wisdom" in a regret saturates. A 1-day-to-fix is
76
+ * very informative; a 30-day-to-fix is *more* informative but not 30×
77
+ * more. Logarithmic growth captures diminishing returns.
78
+ */
79
+ export declare function regretBoost(signal: RegretSignal, opts?: RacbOptions): number;
80
+ export declare function applyRacb(results: SearchResult[], signals: RegretSignal[], opts?: RacbOptions): SearchResult[];
81
+ export interface AdsOptions {
82
+ /** Penalty coefficient 0..1. Higher = more aggressive diversification. Default 0.4. */
83
+ alpha?: number;
84
+ /** Cap on how many results to consider. Default Infinity. */
85
+ topK?: number;
86
+ }
87
+ /**
88
+ * Re-rank results to penalize over-representation of any single author.
89
+ *
90
+ * For each result at position i:
91
+ * sameAuthorAbove = count of (j < i) where author(j) == author(i)
92
+ * penalty = α × (sameAuthorAbove / total)
93
+ * final = base × (1 - penalty)
94
+ *
95
+ * Then re-sort by final.
96
+ *
97
+ * Why this matters: when one author dominates a topic, retrieval becomes
98
+ * monocultural. Diversity surfaces the second-most-knowledgeable
99
+ * contributor, which the user often actually needs.
100
+ */
101
+ export declare function applyAds(results: SearchResult[], opts?: AdsOptions): SearchResult[];
102
+ export interface CgarOptions {
103
+ /** Max hops to walk in the causal graph. Default 2. */
104
+ maxHops?: number;
105
+ /** Decay per hop. Default 0.85 — each hop reduces boost by 15%. */
106
+ hopDecay?: number;
107
+ /** Initial boost for direct causal neighbors. Default 1.3. */
108
+ initialBoost?: number;
109
+ }
110
+ /**
111
+ * Build adjacency of "causal references" between commits using:
112
+ * - PR numbers in body / subject
113
+ * - Issue refs (closes #N, fixes #N, etc.)
114
+ * - Reverts that name a commit hash
115
+ *
116
+ * Returns a map: commitHash → Set<referenced commit hash>
117
+ */
118
+ export declare function buildCausalGraph(commits: Commit[]): Map<string, Set<string>>;
119
+ /**
120
+ * Boost search results by causal proximity to the top hit.
121
+ *
122
+ * If a chunk's commit is referenced (directly or transitively up to
123
+ * maxHops) by another result's commit, its score gets boosted.
124
+ */
125
+ export declare function applyCgar(results: SearchResult[], commits: Commit[], opts?: CgarOptions): SearchResult[];
126
+ export interface EnsembleOptions {
127
+ tdwe?: TdweOptions | false;
128
+ racb?: {
129
+ signals: RegretSignal[];
130
+ opts?: RacbOptions;
131
+ } | false;
132
+ ads?: AdsOptions | false;
133
+ cgar?: {
134
+ commits: Commit[];
135
+ opts?: CgarOptions;
136
+ } | false;
137
+ }
138
+ /**
139
+ * Apply all four novel scoring layers in sequence:
140
+ * 1. TDWE — temporal relevance
141
+ * 2. RACB — regret-derived wisdom boost
142
+ * 3. CGAR — causal graph propagation
143
+ * 4. ADS — diversity re-ranking (last, after scoring is final)
144
+ */
145
+ export declare function applyNovelScoring(results: SearchResult[], ensemble: EnsembleOptions): SearchResult[];
146
+ //# sourceMappingURL=novel-scoring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"novel-scoring.d.ts","sourceRoot":"","sources":["../../src/retrieve/novel-scoring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Novel scoring algorithms for Mneme retrieval.
3
+ *
4
+ * These formulas are designed to outperform pure semantic similarity by
5
+ * exploiting signals that other tools (which only embed file contents)
6
+ * cannot see: time decay, regret patterns, author diversity, and the
7
+ * causal graph implicit in commit/PR/issue references.
8
+ *
9
+ * All formulas are pure functions. They take the set of search results
10
+ * + (optional) commit history and produce a re-ranked list. Use them as
11
+ * post-processors over base BM25/cosine search.
12
+ *
13
+ * Algorithms:
14
+ * 1. TDWE — Time-Decay Weighted Embedding score
15
+ * Formula: w(c) = exp(-λ · age_days / half_life)
16
+ * adjusted = base × w(c)
17
+ *
18
+ * 2. RACB — Regret-Aware Chunk Boosting
19
+ * Formula: boost(c) = 1 + ln(1 + days_to_followup × severity_factor)
20
+ * Severity: revert=3, hotfix=2, fix=1, sameFiles=0.5, none=0
21
+ *
22
+ * 3. ADS — Author Diversity Score re-ranking
23
+ * Formula: penalty = α · (same_author_above_in_ranking / K)
24
+ * final = base × (1 - penalty)
25
+ *
26
+ * 4. CGAR — Causal Graph Augmented Retrieval (light)
27
+ * Walks PR/issue links 2 hops; chunks reachable get boost = 0.85^hops
28
+ */
29
+ /**
30
+ * Apply time-decay weighting to a base score.
31
+ * - A commit from today gets weight ≈ 1.0
32
+ * - A commit at half-life age gets weight ≈ 0.5
33
+ * - A commit at 2× half-life age gets weight ≈ 0.25
34
+ */
35
+ export function timeDecayWeight(commitDateIso, opts = {}) {
36
+ const nowMs = opts.nowMs ?? Date.now();
37
+ const halfLife = opts.halfLifeDays ?? 365;
38
+ const lambda = opts.lambda ?? Math.LN2;
39
+ const t = new Date(commitDateIso).getTime();
40
+ if (Number.isNaN(t))
41
+ return 1;
42
+ const ageDays = Math.max(0, (nowMs - t) / 86_400_000);
43
+ return Math.exp(-lambda * (ageDays / halfLife));
44
+ }
45
+ export function applyTdwe(results, opts = {}) {
46
+ return results
47
+ .map((r) => ({
48
+ ...r,
49
+ score: r.score * timeDecayWeight(r.commit.authorDate, opts),
50
+ }))
51
+ .sort((a, b) => b.score - a.score);
52
+ }
53
+ const DEFAULT_SEVERITY = {
54
+ revert: 3,
55
+ hotfix: 2,
56
+ fix: 1,
57
+ sameFiles: 0.5,
58
+ none: 0,
59
+ };
60
+ /**
61
+ * Compute regret boost for a single signal.
62
+ * boost = 1 + ln(1 + days × severity)
63
+ *
64
+ * Why ln? Because the "wisdom" in a regret saturates. A 1-day-to-fix is
65
+ * very informative; a 30-day-to-fix is *more* informative but not 30×
66
+ * more. Logarithmic growth captures diminishing returns.
67
+ */
68
+ export function regretBoost(signal, opts = {}) {
69
+ const maxBoost = opts.maxBoost ?? 2.5;
70
+ const severity = (opts.severityMap ?? DEFAULT_SEVERITY)[signal.kind] ?? 0;
71
+ if (severity === 0)
72
+ return 1;
73
+ const raw = 1 + Math.log(1 + signal.daysToFollowup * severity);
74
+ return Math.min(maxBoost, raw);
75
+ }
76
+ export function applyRacb(results, signals, opts = {}) {
77
+ const sigByHash = new Map();
78
+ for (const s of signals)
79
+ sigByHash.set(s.commitHash, s);
80
+ return results
81
+ .map((r) => {
82
+ const sig = sigByHash.get(r.commit.hash);
83
+ const boost = sig ? regretBoost(sig, opts) : 1;
84
+ return { ...r, score: r.score * boost };
85
+ })
86
+ .sort((a, b) => b.score - a.score);
87
+ }
88
+ /**
89
+ * Re-rank results to penalize over-representation of any single author.
90
+ *
91
+ * For each result at position i:
92
+ * sameAuthorAbove = count of (j < i) where author(j) == author(i)
93
+ * penalty = α × (sameAuthorAbove / total)
94
+ * final = base × (1 - penalty)
95
+ *
96
+ * Then re-sort by final.
97
+ *
98
+ * Why this matters: when one author dominates a topic, retrieval becomes
99
+ * monocultural. Diversity surfaces the second-most-knowledgeable
100
+ * contributor, which the user often actually needs.
101
+ */
102
+ export function applyAds(results, opts = {}) {
103
+ const alpha = opts.alpha ?? 0.4;
104
+ const topK = opts.topK ?? results.length;
105
+ const slice = results.slice(0, topK);
106
+ const total = Math.max(1, slice.length);
107
+ const adjusted = slice.map((r, i) => {
108
+ let sameAuthorAbove = 0;
109
+ for (let j = 0; j < i; j++) {
110
+ if (slice[j].commit.authorEmail === r.commit.authorEmail) {
111
+ sameAuthorAbove += 1;
112
+ }
113
+ }
114
+ const penalty = alpha * (sameAuthorAbove / total);
115
+ const finalScore = r.score * (1 - penalty);
116
+ return { ...r, score: finalScore };
117
+ });
118
+ adjusted.sort((a, b) => b.score - a.score);
119
+ return adjusted;
120
+ }
121
+ /**
122
+ * Build adjacency of "causal references" between commits using:
123
+ * - PR numbers in body / subject
124
+ * - Issue refs (closes #N, fixes #N, etc.)
125
+ * - Reverts that name a commit hash
126
+ *
127
+ * Returns a map: commitHash → Set<referenced commit hash>
128
+ */
129
+ export function buildCausalGraph(commits) {
130
+ const graph = new Map();
131
+ const byPrNumber = new Map();
132
+ for (const c of commits) {
133
+ if (c.prNumber)
134
+ byPrNumber.set(c.prNumber, c.hash);
135
+ }
136
+ const PR_REF = /(?:pr|pull request|merge\s+pull\s+request)\s*#?\s*(\d+)|#(\d+)/gi;
137
+ const HASH_REF = /\b([0-9a-f]{7,40})\b/gi;
138
+ const REVERT_HASH = /revert(?:s|ed)?\s+(?:commit\s+)?["`]?([0-9a-f]{7,40})/i;
139
+ for (const c of commits) {
140
+ const text = `${c.subject}\n${c.body || ""}`;
141
+ const refs = new Set();
142
+ // PR/issue references
143
+ let m;
144
+ PR_REF.lastIndex = 0;
145
+ while ((m = PR_REF.exec(text)) !== null) {
146
+ const num = Number(m[1] ?? m[2]);
147
+ const target = byPrNumber.get(num);
148
+ if (target && target !== c.hash)
149
+ refs.add(target);
150
+ }
151
+ // Direct hash references — match against commits in the index
152
+ HASH_REF.lastIndex = 0;
153
+ while ((m = HASH_REF.exec(text)) !== null) {
154
+ const candidate = m[1].toLowerCase();
155
+ if (candidate.length < 7)
156
+ continue;
157
+ // Find any commit whose hash starts with this candidate
158
+ for (const target of commits) {
159
+ if (target.hash === c.hash)
160
+ continue;
161
+ if (target.hash.toLowerCase().startsWith(candidate)) {
162
+ refs.add(target.hash);
163
+ break;
164
+ }
165
+ }
166
+ }
167
+ // Revert markers
168
+ const rev = REVERT_HASH.exec(c.subject + " " + (c.body || ""));
169
+ if (rev) {
170
+ const candidate = rev[1].toLowerCase();
171
+ for (const target of commits) {
172
+ if (target.hash === c.hash)
173
+ continue;
174
+ if (target.hash.toLowerCase().startsWith(candidate)) {
175
+ refs.add(target.hash);
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ if (refs.size > 0)
181
+ graph.set(c.hash, refs);
182
+ }
183
+ return graph;
184
+ }
185
+ /**
186
+ * Boost search results by causal proximity to the top hit.
187
+ *
188
+ * If a chunk's commit is referenced (directly or transitively up to
189
+ * maxHops) by another result's commit, its score gets boosted.
190
+ */
191
+ export function applyCgar(results, commits, opts = {}) {
192
+ const maxHops = opts.maxHops ?? 2;
193
+ const decay = opts.hopDecay ?? 0.85;
194
+ const initial = opts.initialBoost ?? 1.3;
195
+ if (results.length === 0)
196
+ return results;
197
+ const graph = buildCausalGraph(commits);
198
+ // Compute distance (in hops) from any result-commit to every commit it
199
+ // references transitively.
200
+ const seedHashes = new Set(results.map((r) => r.commit.hash));
201
+ const distance = new Map();
202
+ // BFS from each seed
203
+ for (const seed of seedHashes) {
204
+ const queue = [[seed, 0]];
205
+ const seen = new Set([seed]);
206
+ while (queue.length > 0) {
207
+ const [node, hops] = queue.shift();
208
+ if (hops >= maxHops)
209
+ continue;
210
+ const neighbors = graph.get(node);
211
+ if (!neighbors)
212
+ continue;
213
+ for (const nb of neighbors) {
214
+ if (seen.has(nb))
215
+ continue;
216
+ seen.add(nb);
217
+ const cur = distance.get(nb);
218
+ if (cur === undefined || hops + 1 < cur) {
219
+ distance.set(nb, hops + 1);
220
+ }
221
+ queue.push([nb, hops + 1]);
222
+ }
223
+ }
224
+ }
225
+ return results
226
+ .map((r) => {
227
+ const hops = distance.get(r.commit.hash);
228
+ if (hops === undefined || hops === 0)
229
+ return r;
230
+ const boost = initial * Math.pow(decay, hops - 1);
231
+ return { ...r, score: r.score * boost };
232
+ })
233
+ .sort((a, b) => b.score - a.score);
234
+ }
235
+ /**
236
+ * Apply all four novel scoring layers in sequence:
237
+ * 1. TDWE — temporal relevance
238
+ * 2. RACB — regret-derived wisdom boost
239
+ * 3. CGAR — causal graph propagation
240
+ * 4. ADS — diversity re-ranking (last, after scoring is final)
241
+ */
242
+ export function applyNovelScoring(results, ensemble) {
243
+ let r = results;
244
+ if (ensemble.tdwe !== false)
245
+ r = applyTdwe(r, ensemble.tdwe || {});
246
+ if (ensemble.racb !== false && ensemble.racb) {
247
+ r = applyRacb(r, ensemble.racb.signals, ensemble.racb.opts);
248
+ }
249
+ if (ensemble.cgar !== false && ensemble.cgar) {
250
+ r = applyCgar(r, ensemble.cgar.commits, ensemble.cgar.opts);
251
+ }
252
+ if (ensemble.ads !== false)
253
+ r = applyAds(r, ensemble.ads || {});
254
+ return r;
255
+ }
256
+ //# sourceMappingURL=novel-scoring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"novel-scoring.js","sourceRoot":"","sources":["../../src/retrieve/novel-scoring.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=novel-scoring.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"novel-scoring.test.d.ts","sourceRoot":"","sources":["../../src/retrieve/novel-scoring.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,221 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { timeDecayWeight, applyTdwe, regretBoost, applyRacb, applyAds, buildCausalGraph, applyCgar, applyNovelScoring, } from "./novel-scoring.js";
3
+ const NOW = new Date("2026-05-06").getTime();
4
+ function mkCommit(p) {
5
+ return {
6
+ hash: p.hash,
7
+ shortHash: p.hash.slice(0, 7),
8
+ authorName: p.author ?? "Alice",
9
+ authorEmail: (p.author ?? "alice").toLowerCase() + "@x.com",
10
+ authorDate: p.date,
11
+ committerDate: p.date,
12
+ subject: p.subject ?? "x",
13
+ body: p.body ?? "",
14
+ files: [],
15
+ parents: [],
16
+ prNumber: p.pr,
17
+ };
18
+ }
19
+ function mkResult(c, score) {
20
+ return { commit: c, score, matchedChunks: [] };
21
+ }
22
+ // ─── TDWE ──────────────────────────────────────────────────────────────
23
+ describe("timeDecayWeight", () => {
24
+ it("returns ≈ 1 for today's commit", () => {
25
+ const w = timeDecayWeight(new Date(NOW).toISOString(), { nowMs: NOW });
26
+ expect(w).toBeGreaterThan(0.99);
27
+ });
28
+ it("returns ≈ 0.5 at half-life age", () => {
29
+ const halfLifeAgo = new Date(NOW - 365 * 86_400_000).toISOString();
30
+ const w = timeDecayWeight(halfLifeAgo, { nowMs: NOW, halfLifeDays: 365 });
31
+ expect(w).toBeCloseTo(0.5, 1);
32
+ });
33
+ it("returns ≈ 0.25 at 2× half-life age", () => {
34
+ const twiceHalfLife = new Date(NOW - 730 * 86_400_000).toISOString();
35
+ const w = timeDecayWeight(twiceHalfLife, { nowMs: NOW, halfLifeDays: 365 });
36
+ expect(w).toBeCloseTo(0.25, 1);
37
+ });
38
+ it("clamps negative ages to 0 (future commits = weight 1)", () => {
39
+ const future = new Date(NOW + 86_400_000).toISOString();
40
+ const w = timeDecayWeight(future, { nowMs: NOW });
41
+ expect(w).toBe(1);
42
+ });
43
+ });
44
+ describe("applyTdwe", () => {
45
+ it("boosts recent commits over old ones with same base score", () => {
46
+ const recent = mkCommit({ hash: "r1", date: "2026-04-01" });
47
+ const old = mkCommit({ hash: "o1", date: "2024-04-01" });
48
+ const results = [mkResult(old, 0.5), mkResult(recent, 0.5)];
49
+ const out = applyTdwe(results, { nowMs: NOW });
50
+ expect(out[0].commit.hash).toBe("r1");
51
+ });
52
+ it("preserves score ordering when ages are equal", () => {
53
+ const c1 = mkCommit({ hash: "a", date: "2026-04-01" });
54
+ const c2 = mkCommit({ hash: "b", date: "2026-04-01" });
55
+ const out = applyTdwe([mkResult(c1, 0.3), mkResult(c2, 0.5)], { nowMs: NOW });
56
+ expect(out[0].commit.hash).toBe("b");
57
+ });
58
+ });
59
+ // ─── RACB ──────────────────────────────────────────────────────────────
60
+ describe("regretBoost", () => {
61
+ it("returns 1 when severity = 0 (no regret)", () => {
62
+ const b = regretBoost({ commitHash: "x", kind: "none", daysToFollowup: 5 });
63
+ expect(b).toBe(1);
64
+ });
65
+ it("scales boost with daysToFollowup logarithmically", () => {
66
+ const day1 = regretBoost({ commitHash: "x", kind: "fix", daysToFollowup: 1 });
67
+ const day7 = regretBoost({ commitHash: "x", kind: "fix", daysToFollowup: 7 });
68
+ expect(day7).toBeGreaterThan(day1);
69
+ expect(day7).toBeLessThan(day1 * 7); // sub-linear
70
+ });
71
+ it("revert > hotfix > fix for same days (using small enough days to avoid cap)", () => {
72
+ // Use days=0.5 so all three stay below the maxBoost cap
73
+ const r = regretBoost({ commitHash: "x", kind: "revert", daysToFollowup: 0.5 });
74
+ const h = regretBoost({ commitHash: "x", kind: "hotfix", daysToFollowup: 0.5 });
75
+ const f = regretBoost({ commitHash: "x", kind: "fix", daysToFollowup: 0.5 });
76
+ expect(r).toBeGreaterThan(h);
77
+ expect(h).toBeGreaterThan(f);
78
+ });
79
+ it("respects maxBoost cap", () => {
80
+ const b = regretBoost({ commitHash: "x", kind: "revert", daysToFollowup: 1000 }, { maxBoost: 1.5 });
81
+ expect(b).toBeLessThanOrEqual(1.5);
82
+ });
83
+ });
84
+ describe("applyRacb", () => {
85
+ it("boosts commits with regret signals", () => {
86
+ const commits = [
87
+ mkCommit({ hash: "regretted", date: "2024-01-01" }),
88
+ mkCommit({ hash: "clean", date: "2024-01-01" }),
89
+ ];
90
+ const results = [mkResult(commits[1], 0.6), mkResult(commits[0], 0.5)];
91
+ const signals = [
92
+ { commitHash: "regretted", kind: "revert", daysToFollowup: 2 },
93
+ ];
94
+ const out = applyRacb(results, signals);
95
+ // The regretted commit should now beat the clean one despite lower base score
96
+ expect(out[0].commit.hash).toBe("regretted");
97
+ });
98
+ });
99
+ // ─── ADS ──────────────────────────────────────────────────────────────
100
+ describe("applyAds", () => {
101
+ it("penalizes results from the same author appearing repeatedly", () => {
102
+ const commits = [
103
+ mkCommit({ hash: "a1", date: "2024-01-01", author: "Alice" }),
104
+ mkCommit({ hash: "a2", date: "2024-01-02", author: "Alice" }),
105
+ mkCommit({ hash: "a3", date: "2024-01-03", author: "Alice" }),
106
+ mkCommit({ hash: "b1", date: "2024-01-04", author: "Bob" }),
107
+ ];
108
+ // Alice dominates with high scores; Bob has a lower score
109
+ const results = [
110
+ mkResult(commits[0], 0.50),
111
+ mkResult(commits[1], 0.49),
112
+ mkResult(commits[2], 0.48),
113
+ mkResult(commits[3], 0.40),
114
+ ];
115
+ const out = applyAds(results, { alpha: 0.4 });
116
+ // Bob should rise above some Alice entries despite lower base score
117
+ const bobIdx = out.findIndex((r) => r.commit.authorEmail === "bob@x.com");
118
+ expect(bobIdx).toBeLessThanOrEqual(2);
119
+ });
120
+ it("preserves order when all results are different authors", () => {
121
+ const commits = [
122
+ mkCommit({ hash: "a", date: "2024-01-01", author: "Alice" }),
123
+ mkCommit({ hash: "b", date: "2024-01-01", author: "Bob" }),
124
+ mkCommit({ hash: "c", date: "2024-01-01", author: "Carol" }),
125
+ ];
126
+ const out = applyAds([
127
+ mkResult(commits[0], 0.5),
128
+ mkResult(commits[1], 0.4),
129
+ mkResult(commits[2], 0.3),
130
+ ]);
131
+ expect(out.map((r) => r.commit.hash)).toEqual(["a", "b", "c"]);
132
+ });
133
+ });
134
+ // ─── CGAR ──────────────────────────────────────────────────────────────
135
+ describe("buildCausalGraph", () => {
136
+ it("links commits via PR number references", () => {
137
+ const commits = [
138
+ mkCommit({ hash: "old", date: "2024-01-01", pr: 482 }),
139
+ mkCommit({ hash: "new", date: "2024-02-01", subject: "follow-up to PR #482" }),
140
+ ];
141
+ const g = buildCausalGraph(commits);
142
+ expect(g.get("new")?.has("old")).toBe(true);
143
+ });
144
+ it("links commits via direct hash references", () => {
145
+ const commits = [
146
+ mkCommit({ hash: "abc1234defabcdef", date: "2024-01-01" }),
147
+ mkCommit({ hash: "new", date: "2024-02-01", subject: "fix regression in abc1234" }),
148
+ ];
149
+ const g = buildCausalGraph(commits);
150
+ expect(g.get("new")?.has("abc1234defabcdef")).toBe(true);
151
+ });
152
+ it("links revert commits to their target", () => {
153
+ const commits = [
154
+ mkCommit({ hash: "abc1234defabcdef", date: "2024-01-01", subject: "add caching" }),
155
+ mkCommit({ hash: "rev1", date: "2024-02-01", subject: "Revert \"add caching\"", body: "Reverts commit abc1234" }),
156
+ ];
157
+ const g = buildCausalGraph(commits);
158
+ expect(g.get("rev1")?.has("abc1234defabcdef")).toBe(true);
159
+ });
160
+ });
161
+ describe("applyCgar", () => {
162
+ it("boosts results that are causally linked to other results", () => {
163
+ const commits = [
164
+ mkCommit({ hash: "abcd1234efghij5678", date: "2024-01-01", subject: "feat: caching" }),
165
+ mkCommit({ hash: "linked", date: "2024-02-01", subject: "follow-up to abcd1234" }),
166
+ mkCommit({ hash: "unrelated", date: "2024-02-15", subject: "docs: typo" }),
167
+ ];
168
+ const results = [
169
+ mkResult(commits[0], 0.7),
170
+ mkResult(commits[1], 0.4),
171
+ mkResult(commits[2], 0.4),
172
+ ];
173
+ const out = applyCgar(results, commits);
174
+ // "linked" should rise above "unrelated" because it's causally connected
175
+ const linkedIdx = out.findIndex((r) => r.commit.hash === "linked");
176
+ const unrelatedIdx = out.findIndex((r) => r.commit.hash === "unrelated");
177
+ expect(linkedIdx).toBeLessThan(unrelatedIdx);
178
+ });
179
+ it("respects maxHops bound", () => {
180
+ const commits = [
181
+ mkCommit({ hash: "a", date: "2024-01-01" }),
182
+ mkCommit({ hash: "b", date: "2024-02-01", subject: "ref a" }),
183
+ mkCommit({ hash: "c", date: "2024-03-01", subject: "ref b" }),
184
+ ];
185
+ // With maxHops=1, c shouldn't get a boost (it's 2 hops from a)
186
+ const out1 = applyCgar([mkResult(commits[0], 0.5), mkResult(commits[2], 0.3)], commits, { maxHops: 1 });
187
+ expect(out1[0].commit.hash).toBe("a");
188
+ });
189
+ });
190
+ // ─── ENSEMBLE ──────────────────────────────────────────────────────────
191
+ describe("applyNovelScoring (ensemble)", () => {
192
+ it("composes all four algorithms without errors", () => {
193
+ const commits = [
194
+ mkCommit({ hash: "a1", date: "2026-04-01", author: "Alice", pr: 1 }),
195
+ mkCommit({ hash: "a2", date: "2024-04-01", author: "Alice", subject: "ref PR #1" }),
196
+ mkCommit({ hash: "b1", date: "2025-04-01", author: "Bob" }),
197
+ ];
198
+ const results = [
199
+ mkResult(commits[0], 0.5),
200
+ mkResult(commits[1], 0.4),
201
+ mkResult(commits[2], 0.45),
202
+ ];
203
+ const out = applyNovelScoring(results, {
204
+ tdwe: { nowMs: new Date("2026-05-06").getTime() },
205
+ racb: {
206
+ signals: [{ commitHash: "a2", kind: "fix", daysToFollowup: 3 }],
207
+ },
208
+ ads: { alpha: 0.3 },
209
+ cgar: { commits },
210
+ });
211
+ expect(out.length).toBe(3);
212
+ // Every output should still have a valid score
213
+ for (const r of out)
214
+ expect(r.score).toBeGreaterThan(0);
215
+ });
216
+ it("returns empty array when input is empty", () => {
217
+ const out = applyNovelScoring([], {});
218
+ expect(out).toEqual([]);
219
+ });
220
+ });
221
+ //# sourceMappingURL=novel-scoring.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"novel-scoring.test.js","sourceRoot":"","sources":["../../src/retrieve/novel-scoring.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mneme-ai/core",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Core indexing, retrieval, and graph engine for Mneme",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",