@mneme-ai/core 0.14.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.
Files changed (41) hide show
  1. package/dist/guardian/guardian.d.ts +93 -0
  2. package/dist/guardian/guardian.d.ts.map +1 -0
  3. package/dist/guardian/guardian.js +170 -0
  4. package/dist/guardian/guardian.js.map +1 -0
  5. package/dist/guardian/guardian.test.d.ts +2 -0
  6. package/dist/guardian/guardian.test.d.ts.map +1 -0
  7. package/dist/guardian/guardian.test.js +154 -0
  8. package/dist/guardian/guardian.test.js.map +1 -0
  9. package/dist/guardian/index.d.ts +2 -0
  10. package/dist/guardian/index.d.ts.map +1 -0
  11. package/dist/guardian/index.js +2 -0
  12. package/dist/guardian/index.js.map +1 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/indexer/index.d.ts +1 -0
  18. package/dist/indexer/index.d.ts.map +1 -1
  19. package/dist/indexer/index.js +1 -0
  20. package/dist/indexer/index.js.map +1 -1
  21. package/dist/indexer/quality.d.ts +56 -0
  22. package/dist/indexer/quality.d.ts.map +1 -0
  23. package/dist/indexer/quality.js +181 -0
  24. package/dist/indexer/quality.js.map +1 -0
  25. package/dist/indexer/quality.test.d.ts +2 -0
  26. package/dist/indexer/quality.test.d.ts.map +1 -0
  27. package/dist/indexer/quality.test.js +120 -0
  28. package/dist/indexer/quality.test.js.map +1 -0
  29. package/dist/retrieve/index.d.ts +1 -0
  30. package/dist/retrieve/index.d.ts.map +1 -1
  31. package/dist/retrieve/index.js +1 -0
  32. package/dist/retrieve/index.js.map +1 -1
  33. package/dist/retrieve/novel-scoring.d.ts +146 -0
  34. package/dist/retrieve/novel-scoring.d.ts.map +1 -0
  35. package/dist/retrieve/novel-scoring.js +256 -0
  36. package/dist/retrieve/novel-scoring.js.map +1 -0
  37. package/dist/retrieve/novel-scoring.test.d.ts +2 -0
  38. package/dist/retrieve/novel-scoring.test.d.ts.map +1 -0
  39. package/dist/retrieve/novel-scoring.test.js +221 -0
  40. package/dist/retrieve/novel-scoring.test.js.map +1 -0
  41. package/package.json +1 -1
@@ -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":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAsB,MAAM,eAAe,CAAC;AAIhF,SAAS,QAAQ,CAAC,IAAY,EAAE,IAAI,GAAG,YAAY;IACjD,OAAO;QACL,IAAI;QACJ,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3B,UAAU,EAAE,GAAG;QACf,WAAW,EAAE,SAAS;QACtB,UAAU,EAAE,IAAI;QAChB,aAAa,EAAE,IAAI;QACnB,OAAO,EAAE,GAAG;QACZ,IAAI,EAAE,EAAE;QACR,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAE;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAa,EAAE,UAAU,GAAG,CAAC;IAC9C,OAAO;QACL,cAAc,EAAE,EAAE;QAClB,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC;QAC3C,OAAO,EAAE;YACP,YAAY,EAAE,CAAC;YACf,UAAU;YACV,cAAc,EAAE,CAAC;YACjB,SAAS,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;YACV,UAAU,EAAE,CAAC;YACb,cAAc,EAAE,CAAC;YACjB,eAAe,EAAE,CAAC;SACnB;QACD,YAAY,EAAE,KAAK;QACnB,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;QAChG,eAAe,EAAE,EAAE;KACpB,CAAC;AACJ,CAAC;AAED,MAAM,SAAS,GAAkB;IAC/B,WAAW,EAAE,EAAE;IACf,cAAc,EAAE,EAAE;IAClB,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC;IACvB,gBAAgB,EAAE,GAAG;IACrB,kBAAkB,EAAE,CAAC;IACrB,qBAAqB,EAAE,CAAC;IACxB,4BAA4B,EAAE,CAAC;CAChC,CAAC;AAEF,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;YACtE,cAAc,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;SACtC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QACzD,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,CAAC,KAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,WAAW,EAAE,IAAI;YACjB,cAAc,EAAE,EAAE;SACnB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAE,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,OAAO,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;SAC7B,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAAC,CAAC;QACxE,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC;SACxB,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;QAC7D,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC;YACvB,gBAAgB,EAAE,GAAG;SACtB,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,KAAK,aAAa;YACxB,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAC9C,CAAC;QACF,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,4BAA4B,EAAE,EAAE;SACjC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC;QACrE,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,CAAC,KAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,kBAAkB,EAAE,CAAC;YACrB,qBAAqB,EAAE,CAAC;SACzB,CAAC,CAAC;QACH,MAAM,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC;QAC7D,MAAM,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACzB,MAAM,CAAC,EAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC5B,cAAc,EAAE,EAAE;SACnB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,SAAS;YACZ,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC5B,cAAc,EAAE,EAAE;SACnB,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,MAAM,GAAG,QAAQ,CAAC;YACtB,GAAG,SAAS;YACZ,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,0BAA0B;YAC3D,cAAc,EAAE,EAAE;YAClB,kBAAkB,EAAE,CAAC,EAAE,2BAA2B;YAClD,qBAAqB,EAAE,CAAC;YACxB,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,8CAA8C;YACvE,4BAA4B,EAAE,EAAE,EAAE,yBAAyB;SAC5D,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;QAC1C,kCAAkC;QAClC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,uDAAuD;QACvD,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -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"}
@@ -1,2 +1,3 @@
1
1
  export * from "./indexer.js";
2
+ export * from "./quality.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
@@ -1,2 +1,3 @@
1
1
  export * from "./indexer.js";
2
+ export * from "./quality.js";
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/indexer/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Index quality analyzer — answers "how good is the memory I just built?"
3
+ *
4
+ * The retrieval quality of Mneme is bounded by the quality of what was
5
+ * indexed. Garbage in, garbage out. This module computes a battery of
6
+ * metrics that surface index-level problems *before* they become bad
7
+ * answers.
8
+ *
9
+ * Pure data extraction — no LLM, no external services. CLI renders.
10
+ *
11
+ * Metrics computed:
12
+ * - coverage : # commits indexed vs # commits in git history
13
+ * - chunkDensity : average chunks per commit (more = richer signal)
14
+ * - embedRatio : fraction of chunks that have a vector embedding
15
+ * - subjectQuality : fraction of commits with subjects ≥ minWords
16
+ * - bodyRatio : fraction of commits with non-trivial bodies
17
+ * - prRatio : fraction of commits linked to a PR/MR
18
+ * - issueRatio : fraction of commits with issue refs
19
+ * - duplicateRatio : fraction of commits whose subject is a duplicate
20
+ * ("fix", "wip", "merge", etc.)
21
+ * - tokenizerHealth: estimate of how well tokenization is working
22
+ * (heuristic: ratio of chunks with ≥3 distinct tokens)
23
+ *
24
+ * Returns an overall A-F score plus per-metric details + concrete
25
+ * recommendations the user can act on.
26
+ */
27
+ import type { Commit, CommitChunk } from "../types.js";
28
+ export interface IndexQualityReport {
29
+ /** Total commits in the index. */
30
+ indexedCommits: number;
31
+ /** Total chunks in the index. */
32
+ indexedChunks: number;
33
+ /** Chunks with non-empty embeddings. */
34
+ embeddedChunks: number;
35
+ /** Per-metric breakdown — values 0..1. */
36
+ metrics: {
37
+ chunkDensity: number;
38
+ embedRatio: number;
39
+ subjectQuality: number;
40
+ bodyRatio: number;
41
+ prRatio: number;
42
+ issueRatio: number;
43
+ duplicateRatio: number;
44
+ tokenizerHealth: number;
45
+ };
46
+ /** Overall 0..1 quality score (weighted average). */
47
+ overallScore: number;
48
+ /** Letter grade A-F derived from overallScore. */
49
+ grade: "A" | "B" | "C" | "D" | "F";
50
+ /** Human-actionable recommendations. */
51
+ recommendations: string[];
52
+ }
53
+ export declare function analyzeIndexQuality(commits: Commit[], chunks: CommitChunk[], opts?: {
54
+ minSubjectWords?: number;
55
+ }): IndexQualityReport;
56
+ //# sourceMappingURL=quality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/indexer/quality.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,qDAAqD;IACrD,YAAY,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,KAAK,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACnC,wCAAwC;IACxC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAQD,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,WAAW,EAAE,EACrB,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,kBAAkB,CAoHpB"}