@mneme-ai/core 0.22.1 → 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.
- package/dist/retrieve/ddtree.d.ts +67 -0
- package/dist/retrieve/ddtree.d.ts.map +1 -0
- package/dist/retrieve/ddtree.js +156 -0
- package/dist/retrieve/ddtree.js.map +1 -0
- package/dist/retrieve/ddtree.test.d.ts +2 -0
- package/dist/retrieve/ddtree.test.d.ts.map +1 -0
- package/dist/retrieve/ddtree.test.js +181 -0
- package/dist/retrieve/ddtree.test.js.map +1 -0
- package/dist/retrieve/index.d.ts +3 -0
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/index.js +3 -0
- package/dist/retrieve/index.js.map +1 -1
- package/dist/retrieve/leviathan.d.ts +68 -0
- package/dist/retrieve/leviathan.d.ts.map +1 -0
- package/dist/retrieve/leviathan.js +192 -0
- package/dist/retrieve/leviathan.js.map +1 -0
- package/dist/retrieve/leviathan.test.d.ts +2 -0
- package/dist/retrieve/leviathan.test.d.ts.map +1 -0
- package/dist/retrieve/leviathan.test.js +124 -0
- package/dist/retrieve/leviathan.test.js.map +1 -0
- package/dist/retrieve/search.d.ts +7 -0
- package/dist/retrieve/search.d.ts.map +1 -1
- package/dist/retrieve/search.js +31 -3
- package/dist/retrieve/search.js.map +1 -1
- package/dist/retrieve/stream.d.ts +93 -0
- package/dist/retrieve/stream.d.ts.map +1 -0
- package/dist/retrieve/stream.js +52 -0
- package/dist/retrieve/stream.js.map +1 -0
- package/dist/retrieve/stream.test.d.ts +2 -0
- package/dist/retrieve/stream.test.d.ts.map +1 -0
- package/dist/retrieve/stream.test.js +118 -0
- package/dist/retrieve/stream.test.js.map +1 -0
- package/dist/retrieve/synthesize.d.ts.map +1 -1
- package/dist/retrieve/synthesize.js +17 -1
- package/dist/retrieve/synthesize.js.map +1 -1
- package/dist/util/constraint-pruner.d.ts +76 -0
- package/dist/util/constraint-pruner.d.ts.map +1 -0
- package/dist/util/constraint-pruner.js +89 -0
- package/dist/util/constraint-pruner.js.map +1 -0
- package/dist/util/constraint-pruner.test.d.ts +2 -0
- package/dist/util/constraint-pruner.test.d.ts.map +1 -0
- package/dist/util/constraint-pruner.test.js +101 -0
- package/dist/util/constraint-pruner.test.js.map +1 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +1 -0
- package/dist/util/index.js.map +1 -1
- package/dist/wisdom/index.d.ts +2 -0
- package/dist/wisdom/index.d.ts.map +1 -1
- package/dist/wisdom/index.js +2 -0
- package/dist/wisdom/index.js.map +1 -1
- package/dist/wisdom/mutant-adapt.d.ts +69 -0
- package/dist/wisdom/mutant-adapt.d.ts.map +1 -0
- package/dist/wisdom/mutant-adapt.js +216 -0
- package/dist/wisdom/mutant-adapt.js.map +1 -0
- package/dist/wisdom/mutant-adapt.test.d.ts +2 -0
- package/dist/wisdom/mutant-adapt.test.d.ts.map +1 -0
- package/dist/wisdom/mutant-adapt.test.js +184 -0
- package/dist/wisdom/mutant-adapt.test.js.map +1 -0
- package/dist/wisdom/session.d.ts +60 -0
- package/dist/wisdom/session.d.ts.map +1 -0
- package/dist/wisdom/session.js +193 -0
- package/dist/wisdom/session.js.map +1 -0
- package/dist/wisdom/session.test.d.ts +2 -0
- package/dist/wisdom/session.test.d.ts.map +1 -0
- package/dist/wisdom/session.test.js +161 -0
- package/dist/wisdom/session.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mutant-adapt — auto-adapt Mneme's behavior based on what's worked.
|
|
3
|
+
*
|
|
4
|
+
* Where the calibrator tunes a fixed set of search knobs against feedback,
|
|
5
|
+
* this module tracks success/failure across *any* labeled axis: provider,
|
|
6
|
+
* model, scoring config, intent classifier — anything caller wants to tag.
|
|
7
|
+
* Over time, `recommend()` returns whichever axis in a given group has the
|
|
8
|
+
* best success rate weighted by recency.
|
|
9
|
+
*
|
|
10
|
+
* Design choices:
|
|
11
|
+
* - Axis names are free-form strings. Callers convene on prefix conventions
|
|
12
|
+
* ("provider:groq", "model:qwen2.5:3b", "weights:rrf-k=60"). The grouping
|
|
13
|
+
* is just substring-startsWith, so naming is the API.
|
|
14
|
+
* - `decayState` is *pure* — it returns a new state. The caller chooses when
|
|
15
|
+
* to persist. This keeps the file write batched at most once per command.
|
|
16
|
+
* - Stats older than 7 days get halved by decay. This isn't a half-life; it's
|
|
17
|
+
* a one-shot demotion. Callers can run decay before every recommendation
|
|
18
|
+
* for a softer running average, or once per day for a sharper recency bias.
|
|
19
|
+
*/
|
|
20
|
+
export interface AxisStat {
|
|
21
|
+
axis: string;
|
|
22
|
+
successCount: number;
|
|
23
|
+
failureCount: number;
|
|
24
|
+
/** Last success ISO timestamp. */
|
|
25
|
+
lastSuccessAt?: string;
|
|
26
|
+
/** Avg latency (ms). */
|
|
27
|
+
avgLatencyMs?: number;
|
|
28
|
+
/** Most recent failure reason — useful for diagnostics. */
|
|
29
|
+
lastFailureReason?: string;
|
|
30
|
+
/** Last update ISO timestamp — used by decayState. */
|
|
31
|
+
lastUpdatedAt?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface MutantState {
|
|
34
|
+
axes: Record<string, AxisStat>;
|
|
35
|
+
/** Last time mutant adapted recommendations. */
|
|
36
|
+
lastAdaptedAt?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface AxisRecommendation {
|
|
39
|
+
bestAxis: string;
|
|
40
|
+
successRate: number;
|
|
41
|
+
reason: string;
|
|
42
|
+
}
|
|
43
|
+
/** Read state from .mneme/mutant.json. Returns empty state if missing/corrupt. */
|
|
44
|
+
export declare function readMutantState(repoRoot: string): MutantState;
|
|
45
|
+
/** Record a success for an axis. Bumps successCount, updates timestamps + latency. */
|
|
46
|
+
export declare function recordSuccess(repoRoot: string, axis: string, latencyMs?: number): void;
|
|
47
|
+
/** Record a failure for an axis. Bumps failureCount + records reason. */
|
|
48
|
+
export declare function recordFailure(repoRoot: string, axis: string, reason: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Compute a recommendation: which axis (within a group) has the best
|
|
51
|
+
* success rate × recency? Returns null if no data.
|
|
52
|
+
*
|
|
53
|
+
* Tiebreakers (in order):
|
|
54
|
+
* 1. Highest success rate.
|
|
55
|
+
* 2. Highest absolute success count (more samples = more confidence).
|
|
56
|
+
* 3. Most recent success.
|
|
57
|
+
*
|
|
58
|
+
* Pure function — does not read or write files.
|
|
59
|
+
*/
|
|
60
|
+
export declare function recommend(state: MutantState, axisGroupPrefix: string): AxisRecommendation | null;
|
|
61
|
+
/**
|
|
62
|
+
* Decay old stats so the system can adapt to current realities.
|
|
63
|
+
* Halves counts on axes whose lastUpdatedAt is older than 7 days.
|
|
64
|
+
* Pure function — caller decides when to persist.
|
|
65
|
+
*/
|
|
66
|
+
export declare function decayState(state: MutantState, nowMs?: number): MutantState;
|
|
67
|
+
/** Persist a state object (e.g. after a caller-driven decayState call). */
|
|
68
|
+
export declare function writeMutantState(repoRoot: string, state: MutantState): void;
|
|
69
|
+
//# sourceMappingURL=mutant-adapt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutant-adapt.d.ts","sourceRoot":"","sources":["../../src/wisdom/mutant-adapt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC/B,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAaD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAsC7D;AAmCD,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAkBtF;AAED,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAOlF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAuChG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,GAAE,MAAmB,GAAG,WAAW,CAkBtF;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAE3E"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mutant-adapt — auto-adapt Mneme's behavior based on what's worked.
|
|
3
|
+
*
|
|
4
|
+
* Where the calibrator tunes a fixed set of search knobs against feedback,
|
|
5
|
+
* this module tracks success/failure across *any* labeled axis: provider,
|
|
6
|
+
* model, scoring config, intent classifier — anything caller wants to tag.
|
|
7
|
+
* Over time, `recommend()` returns whichever axis in a given group has the
|
|
8
|
+
* best success rate weighted by recency.
|
|
9
|
+
*
|
|
10
|
+
* Design choices:
|
|
11
|
+
* - Axis names are free-form strings. Callers convene on prefix conventions
|
|
12
|
+
* ("provider:groq", "model:qwen2.5:3b", "weights:rrf-k=60"). The grouping
|
|
13
|
+
* is just substring-startsWith, so naming is the API.
|
|
14
|
+
* - `decayState` is *pure* — it returns a new state. The caller chooses when
|
|
15
|
+
* to persist. This keeps the file write batched at most once per command.
|
|
16
|
+
* - Stats older than 7 days get halved by decay. This isn't a half-life; it's
|
|
17
|
+
* a one-shot demotion. Callers can run decay before every recommendation
|
|
18
|
+
* for a softer running average, or once per day for a sharper recency bias.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { dirname, join } from "node:path";
|
|
22
|
+
/** ms in 7 days — anything older than this gets halved by decayState. */
|
|
23
|
+
const DECAY_AFTER_MS = 7 * 24 * 60 * 60 * 1000;
|
|
24
|
+
function statePath(repoRoot) {
|
|
25
|
+
return join(repoRoot, ".mneme", "mutant.json");
|
|
26
|
+
}
|
|
27
|
+
function emptyState() {
|
|
28
|
+
return { axes: {} };
|
|
29
|
+
}
|
|
30
|
+
/** Read state from .mneme/mutant.json. Returns empty state if missing/corrupt. */
|
|
31
|
+
export function readMutantState(repoRoot) {
|
|
32
|
+
const path = statePath(repoRoot);
|
|
33
|
+
if (!existsSync(path))
|
|
34
|
+
return emptyState();
|
|
35
|
+
let raw;
|
|
36
|
+
try {
|
|
37
|
+
raw = readFileSync(path, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return emptyState();
|
|
41
|
+
}
|
|
42
|
+
let parsed;
|
|
43
|
+
try {
|
|
44
|
+
parsed = JSON.parse(raw);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return emptyState();
|
|
48
|
+
}
|
|
49
|
+
if (!parsed || typeof parsed !== "object")
|
|
50
|
+
return emptyState();
|
|
51
|
+
const s = parsed;
|
|
52
|
+
if (!s.axes || typeof s.axes !== "object")
|
|
53
|
+
return emptyState();
|
|
54
|
+
// Defensive: drop malformed axis records rather than throw.
|
|
55
|
+
const axes = {};
|
|
56
|
+
for (const [key, val] of Object.entries(s.axes)) {
|
|
57
|
+
if (!val || typeof val !== "object")
|
|
58
|
+
continue;
|
|
59
|
+
const a = val;
|
|
60
|
+
if (typeof a.axis !== "string")
|
|
61
|
+
continue;
|
|
62
|
+
axes[key] = {
|
|
63
|
+
axis: a.axis,
|
|
64
|
+
successCount: typeof a.successCount === "number" ? a.successCount : 0,
|
|
65
|
+
failureCount: typeof a.failureCount === "number" ? a.failureCount : 0,
|
|
66
|
+
lastSuccessAt: typeof a.lastSuccessAt === "string" ? a.lastSuccessAt : undefined,
|
|
67
|
+
avgLatencyMs: typeof a.avgLatencyMs === "number" ? a.avgLatencyMs : undefined,
|
|
68
|
+
lastFailureReason: typeof a.lastFailureReason === "string" ? a.lastFailureReason : undefined,
|
|
69
|
+
lastUpdatedAt: typeof a.lastUpdatedAt === "string" ? a.lastUpdatedAt : undefined,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
axes,
|
|
74
|
+
lastAdaptedAt: typeof s.lastAdaptedAt === "string" ? s.lastAdaptedAt : undefined,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function atomicWrite(path, contents) {
|
|
78
|
+
const dir = dirname(path);
|
|
79
|
+
if (!existsSync(dir)) {
|
|
80
|
+
try {
|
|
81
|
+
mkdirSync(dir, { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const tmp = `${path}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
88
|
+
try {
|
|
89
|
+
writeFileSync(tmp, contents, "utf8");
|
|
90
|
+
renameSync(tmp, path);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
try {
|
|
94
|
+
rmSync(tmp, { force: true });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* ignore */
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function persist(repoRoot, state) {
|
|
102
|
+
atomicWrite(statePath(repoRoot), JSON.stringify(state, null, 2));
|
|
103
|
+
}
|
|
104
|
+
function getOrInit(state, axis) {
|
|
105
|
+
if (!state.axes[axis]) {
|
|
106
|
+
state.axes[axis] = { axis, successCount: 0, failureCount: 0 };
|
|
107
|
+
}
|
|
108
|
+
return state.axes[axis];
|
|
109
|
+
}
|
|
110
|
+
/** Record a success for an axis. Bumps successCount, updates timestamps + latency. */
|
|
111
|
+
export function recordSuccess(repoRoot, axis, latencyMs) {
|
|
112
|
+
const state = readMutantState(repoRoot);
|
|
113
|
+
const stat = getOrInit(state, axis);
|
|
114
|
+
stat.successCount += 1;
|
|
115
|
+
const nowIso = new Date().toISOString();
|
|
116
|
+
stat.lastSuccessAt = nowIso;
|
|
117
|
+
stat.lastUpdatedAt = nowIso;
|
|
118
|
+
if (typeof latencyMs === "number" && latencyMs >= 0) {
|
|
119
|
+
// Running average — weighted by total success count so a single outlier
|
|
120
|
+
// doesn't wreck the signal once we have a sample.
|
|
121
|
+
const prev = stat.avgLatencyMs;
|
|
122
|
+
if (prev === undefined) {
|
|
123
|
+
stat.avgLatencyMs = latencyMs;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
stat.avgLatencyMs = prev + (latencyMs - prev) / stat.successCount;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
persist(repoRoot, state);
|
|
130
|
+
}
|
|
131
|
+
/** Record a failure for an axis. Bumps failureCount + records reason. */
|
|
132
|
+
export function recordFailure(repoRoot, axis, reason) {
|
|
133
|
+
const state = readMutantState(repoRoot);
|
|
134
|
+
const stat = getOrInit(state, axis);
|
|
135
|
+
stat.failureCount += 1;
|
|
136
|
+
stat.lastFailureReason = reason;
|
|
137
|
+
stat.lastUpdatedAt = new Date().toISOString();
|
|
138
|
+
persist(repoRoot, state);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Compute a recommendation: which axis (within a group) has the best
|
|
142
|
+
* success rate × recency? Returns null if no data.
|
|
143
|
+
*
|
|
144
|
+
* Tiebreakers (in order):
|
|
145
|
+
* 1. Highest success rate.
|
|
146
|
+
* 2. Highest absolute success count (more samples = more confidence).
|
|
147
|
+
* 3. Most recent success.
|
|
148
|
+
*
|
|
149
|
+
* Pure function — does not read or write files.
|
|
150
|
+
*/
|
|
151
|
+
export function recommend(state, axisGroupPrefix) {
|
|
152
|
+
const candidates = Object.values(state.axes).filter((a) => a.axis.startsWith(axisGroupPrefix) && a.successCount + a.failureCount > 0);
|
|
153
|
+
if (candidates.length === 0)
|
|
154
|
+
return null;
|
|
155
|
+
let best = null;
|
|
156
|
+
let bestRate = -1;
|
|
157
|
+
for (const c of candidates) {
|
|
158
|
+
const total = c.successCount + c.failureCount;
|
|
159
|
+
const rate = c.successCount / total;
|
|
160
|
+
if (best === null) {
|
|
161
|
+
best = c;
|
|
162
|
+
bestRate = rate;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const bestTotal = best.successCount + best.failureCount;
|
|
166
|
+
if (rate > bestRate ||
|
|
167
|
+
(rate === bestRate && c.successCount > best.successCount) ||
|
|
168
|
+
(rate === bestRate &&
|
|
169
|
+
c.successCount === best.successCount &&
|
|
170
|
+
(c.lastSuccessAt ?? "") > (best.lastSuccessAt ?? ""))) {
|
|
171
|
+
best = c;
|
|
172
|
+
bestRate = rate;
|
|
173
|
+
// bestTotal is computed for symmetry but unused — left explicit to
|
|
174
|
+
// document the comparison contract.
|
|
175
|
+
void bestTotal;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!best)
|
|
179
|
+
return null;
|
|
180
|
+
const total = best.successCount + best.failureCount;
|
|
181
|
+
return {
|
|
182
|
+
bestAxis: best.axis,
|
|
183
|
+
successRate: bestRate,
|
|
184
|
+
reason: `${best.successCount}/${total} successful${best.lastSuccessAt ? `, last success ${best.lastSuccessAt}` : ""}`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Decay old stats so the system can adapt to current realities.
|
|
189
|
+
* Halves counts on axes whose lastUpdatedAt is older than 7 days.
|
|
190
|
+
* Pure function — caller decides when to persist.
|
|
191
|
+
*/
|
|
192
|
+
export function decayState(state, nowMs = Date.now()) {
|
|
193
|
+
const next = { axes: {}, lastAdaptedAt: state.lastAdaptedAt };
|
|
194
|
+
for (const [key, stat] of Object.entries(state.axes)) {
|
|
195
|
+
const updatedAt = stat.lastUpdatedAt ? Date.parse(stat.lastUpdatedAt) : NaN;
|
|
196
|
+
const isStale = !Number.isNaN(updatedAt) && nowMs - updatedAt > DECAY_AFTER_MS;
|
|
197
|
+
if (isStale) {
|
|
198
|
+
next.axes[key] = {
|
|
199
|
+
...stat,
|
|
200
|
+
// Math.floor preserves the "halve and round down" contract — 1 success
|
|
201
|
+
// decays to 0, not 0.5, so a never-revisited axis eventually drops out.
|
|
202
|
+
successCount: Math.floor(stat.successCount / 2),
|
|
203
|
+
failureCount: Math.floor(stat.failureCount / 2),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
next.axes[key] = { ...stat };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return next;
|
|
211
|
+
}
|
|
212
|
+
/** Persist a state object (e.g. after a caller-driven decayState call). */
|
|
213
|
+
export function writeMutantState(repoRoot, state) {
|
|
214
|
+
persist(repoRoot, state);
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=mutant-adapt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutant-adapt.js","sourceRoot":"","sources":["../../src/wisdom/mutant-adapt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutant-adapt.test.d.ts","sourceRoot":"","sources":["../../src/wisdom/mutant-adapt.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { decayState, readMutantState, recommend, recordFailure, recordSuccess, writeMutantState, } from "./mutant-adapt.js";
|
|
6
|
+
let tmpDir;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
tmpDir = mkdtempSync(join(tmpdir(), "mneme-mutant-test-"));
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
12
|
+
});
|
|
13
|
+
describe("mutant-adapt — round-trip", () => {
|
|
14
|
+
it("returns an empty state when nothing is stored", () => {
|
|
15
|
+
const s = readMutantState(tmpDir);
|
|
16
|
+
expect(s.axes).toEqual({});
|
|
17
|
+
});
|
|
18
|
+
it("persists across recordSuccess + readMutantState", () => {
|
|
19
|
+
recordSuccess(tmpDir, "provider:groq", 120);
|
|
20
|
+
const s = readMutantState(tmpDir);
|
|
21
|
+
expect(s.axes["provider:groq"]).toBeDefined();
|
|
22
|
+
expect(s.axes["provider:groq"].successCount).toBe(1);
|
|
23
|
+
expect(s.axes["provider:groq"].lastSuccessAt).toBeDefined();
|
|
24
|
+
expect(s.axes["provider:groq"].avgLatencyMs).toBe(120);
|
|
25
|
+
expect(existsSync(join(tmpDir, ".mneme", "mutant.json"))).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
it("recordSuccess running-average converges over many samples", () => {
|
|
28
|
+
for (let i = 0; i < 10; i++)
|
|
29
|
+
recordSuccess(tmpDir, "provider:groq", 100);
|
|
30
|
+
const stat = readMutantState(tmpDir).axes["provider:groq"];
|
|
31
|
+
expect(stat.successCount).toBe(10);
|
|
32
|
+
expect(stat.avgLatencyMs).toBeCloseTo(100, 4);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("mutant-adapt — recordFailure", () => {
|
|
36
|
+
it("increments failureCount and stores last reason", () => {
|
|
37
|
+
recordFailure(tmpDir, "provider:groq", "AllProvidersFailedError");
|
|
38
|
+
const s = readMutantState(tmpDir);
|
|
39
|
+
expect(s.axes["provider:groq"].failureCount).toBe(1);
|
|
40
|
+
expect(s.axes["provider:groq"].lastFailureReason).toBe("AllProvidersFailedError");
|
|
41
|
+
expect(s.axes["provider:groq"].successCount).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("mutant-adapt — recommend", () => {
|
|
45
|
+
it("returns null when no axes match the prefix", () => {
|
|
46
|
+
recordSuccess(tmpDir, "provider:groq");
|
|
47
|
+
const s = readMutantState(tmpDir);
|
|
48
|
+
expect(recommend(s, "model:")).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
it("returns null when state is empty", () => {
|
|
51
|
+
expect(recommend({ axes: {} }, "provider:")).toBeNull();
|
|
52
|
+
});
|
|
53
|
+
it("picks the highest success-rate axis within a prefix group", () => {
|
|
54
|
+
// groq: 9 wins / 1 loss = 0.9
|
|
55
|
+
for (let i = 0; i < 9; i++)
|
|
56
|
+
recordSuccess(tmpDir, "provider:groq");
|
|
57
|
+
recordFailure(tmpDir, "provider:groq", "rate-limit");
|
|
58
|
+
// openai: 5 wins / 5 losses = 0.5
|
|
59
|
+
for (let i = 0; i < 5; i++)
|
|
60
|
+
recordSuccess(tmpDir, "provider:openai");
|
|
61
|
+
for (let i = 0; i < 5; i++)
|
|
62
|
+
recordFailure(tmpDir, "provider:openai", "auth");
|
|
63
|
+
// anthropic: untouched.
|
|
64
|
+
const rec = recommend(readMutantState(tmpDir), "provider:");
|
|
65
|
+
expect(rec.bestAxis).toBe("provider:groq");
|
|
66
|
+
expect(rec.successRate).toBeCloseTo(0.9, 4);
|
|
67
|
+
expect(rec.reason).toContain("9/10");
|
|
68
|
+
});
|
|
69
|
+
it("ignores axes with no samples even if they exist in state", () => {
|
|
70
|
+
// Manually craft a zero-sample axis.
|
|
71
|
+
const state = {
|
|
72
|
+
axes: {
|
|
73
|
+
"provider:zero": { axis: "provider:zero", successCount: 0, failureCount: 0 },
|
|
74
|
+
"provider:real": {
|
|
75
|
+
axis: "provider:real",
|
|
76
|
+
successCount: 3,
|
|
77
|
+
failureCount: 1,
|
|
78
|
+
lastSuccessAt: new Date().toISOString(),
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
expect(recommend(state, "provider:").bestAxis).toBe("provider:real");
|
|
83
|
+
});
|
|
84
|
+
it("respects prefix boundaries — provider:groq does not match model:", () => {
|
|
85
|
+
recordSuccess(tmpDir, "provider:groq");
|
|
86
|
+
recordSuccess(tmpDir, "model:qwen");
|
|
87
|
+
const s = readMutantState(tmpDir);
|
|
88
|
+
expect(recommend(s, "provider:").bestAxis).toBe("provider:groq");
|
|
89
|
+
expect(recommend(s, "model:").bestAxis).toBe("model:qwen");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe("mutant-adapt — decayState", () => {
|
|
93
|
+
it("halves counts on axes older than 7 days", () => {
|
|
94
|
+
const old = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString();
|
|
95
|
+
const fresh = new Date().toISOString();
|
|
96
|
+
const state = {
|
|
97
|
+
axes: {
|
|
98
|
+
"provider:stale": {
|
|
99
|
+
axis: "provider:stale",
|
|
100
|
+
successCount: 8,
|
|
101
|
+
failureCount: 4,
|
|
102
|
+
lastUpdatedAt: old,
|
|
103
|
+
},
|
|
104
|
+
"provider:fresh": {
|
|
105
|
+
axis: "provider:fresh",
|
|
106
|
+
successCount: 8,
|
|
107
|
+
failureCount: 4,
|
|
108
|
+
lastUpdatedAt: fresh,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const decayed = decayState(state);
|
|
113
|
+
expect(decayed.axes["provider:stale"].successCount).toBe(4);
|
|
114
|
+
expect(decayed.axes["provider:stale"].failureCount).toBe(2);
|
|
115
|
+
// Fresh axis is untouched.
|
|
116
|
+
expect(decayed.axes["provider:fresh"].successCount).toBe(8);
|
|
117
|
+
expect(decayed.axes["provider:fresh"].failureCount).toBe(4);
|
|
118
|
+
});
|
|
119
|
+
it("uses the supplied nowMs for testability", () => {
|
|
120
|
+
const baseMs = Date.parse("2026-01-01T00:00:00Z");
|
|
121
|
+
const updatedAt = new Date(baseMs).toISOString();
|
|
122
|
+
const state = {
|
|
123
|
+
axes: {
|
|
124
|
+
"p:a": { axis: "p:a", successCount: 10, failureCount: 0, lastUpdatedAt: updatedAt },
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
// 6 days later — not yet stale.
|
|
128
|
+
const at6d = baseMs + 6 * 24 * 60 * 60 * 1000;
|
|
129
|
+
expect(decayState(state, at6d).axes["p:a"].successCount).toBe(10);
|
|
130
|
+
// 8 days later — stale.
|
|
131
|
+
const at8d = baseMs + 8 * 24 * 60 * 60 * 1000;
|
|
132
|
+
expect(decayState(state, at8d).axes["p:a"].successCount).toBe(5);
|
|
133
|
+
});
|
|
134
|
+
it("is pure — does not mutate input state", () => {
|
|
135
|
+
const old = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString();
|
|
136
|
+
const state = {
|
|
137
|
+
axes: { "p:x": { axis: "p:x", successCount: 6, failureCount: 0, lastUpdatedAt: old } },
|
|
138
|
+
};
|
|
139
|
+
decayState(state);
|
|
140
|
+
expect(state.axes["p:x"].successCount).toBe(6);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
describe("mutant-adapt — sample evolution trace", () => {
|
|
144
|
+
it("after 10 successes, recommend reliably picks provider X over a 50/50 baseline", () => {
|
|
145
|
+
// Bootstrap: provider X starts 50/50 against provider Y.
|
|
146
|
+
for (let i = 0; i < 5; i++) {
|
|
147
|
+
recordSuccess(tmpDir, "provider:x");
|
|
148
|
+
recordFailure(tmpDir, "provider:x", "boot");
|
|
149
|
+
}
|
|
150
|
+
for (let i = 0; i < 5; i++) {
|
|
151
|
+
recordSuccess(tmpDir, "provider:y");
|
|
152
|
+
recordFailure(tmpDir, "provider:y", "boot");
|
|
153
|
+
}
|
|
154
|
+
// Initially indistinguishable — both 50%. recommend picks either; we just
|
|
155
|
+
// verify it isn't null.
|
|
156
|
+
expect(recommend(readMutantState(tmpDir), "provider:")).not.toBeNull();
|
|
157
|
+
// 10 fresh wins for X.
|
|
158
|
+
for (let i = 0; i < 10; i++)
|
|
159
|
+
recordSuccess(tmpDir, "provider:x");
|
|
160
|
+
const rec = recommend(readMutantState(tmpDir), "provider:");
|
|
161
|
+
expect(rec.bestAxis).toBe("provider:x");
|
|
162
|
+
expect(rec.successRate).toBeCloseTo(15 / 20, 4);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe("mutant-adapt — writeMutantState", () => {
|
|
166
|
+
it("round-trips a hand-crafted state through write + read", () => {
|
|
167
|
+
const state = {
|
|
168
|
+
axes: {
|
|
169
|
+
"scoring:rrf-k=60": {
|
|
170
|
+
axis: "scoring:rrf-k=60",
|
|
171
|
+
successCount: 7,
|
|
172
|
+
failureCount: 1,
|
|
173
|
+
lastSuccessAt: new Date().toISOString(),
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
lastAdaptedAt: new Date().toISOString(),
|
|
177
|
+
};
|
|
178
|
+
writeMutantState(tmpDir, state);
|
|
179
|
+
const read = readMutantState(tmpDir);
|
|
180
|
+
expect(read.axes["scoring:rrf-k=60"].successCount).toBe(7);
|
|
181
|
+
expect(read.lastAdaptedAt).toBe(state.lastAdaptedAt);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
//# sourceMappingURL=mutant-adapt.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutant-adapt.test.js","sourceRoot":"","sources":["../../src/wisdom/mutant-adapt.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversational session memory — path-aware ask context across invocations.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: when a user runs `mneme ask` twice in a row, the second
|
|
5
|
+
* question almost always builds on the first. Without session memory, every
|
|
6
|
+
* ask is a cold start; with it, Q2 search is constrained by the commits and
|
|
7
|
+
* files surfaced in Q1, and topic vocabulary accumulates.
|
|
8
|
+
*
|
|
9
|
+
* Storage is a small JSON file at `<repoRoot>/.mneme/session.json`. We keep
|
|
10
|
+
* it deliberately separate from the SQLite store: sessions are short-lived
|
|
11
|
+
* (1-hour idle expiry), human-readable, and cheap to nuke. SQLite is the
|
|
12
|
+
* permanent record; this is the scratch pad.
|
|
13
|
+
*
|
|
14
|
+
* Concurrency: writes go through a temp-file rename so a crashed/half-written
|
|
15
|
+
* write never corrupts the file readers see.
|
|
16
|
+
*/
|
|
17
|
+
export interface AskTurn {
|
|
18
|
+
/** ISO timestamp. */
|
|
19
|
+
at: string;
|
|
20
|
+
/** Question text. */
|
|
21
|
+
question: string;
|
|
22
|
+
/** Top commits surfaced in the answer (hashes). */
|
|
23
|
+
topHashes: string[];
|
|
24
|
+
/** Top files mentioned (paths). */
|
|
25
|
+
topFiles: string[];
|
|
26
|
+
/** Confidence label of the answer. */
|
|
27
|
+
confidence: "high" | "medium" | "low" | "none";
|
|
28
|
+
}
|
|
29
|
+
export interface Session {
|
|
30
|
+
/** ISO timestamp of session start. */
|
|
31
|
+
startedAt: string;
|
|
32
|
+
/** Last activity. Sessions auto-expire after 1 hour of idle. */
|
|
33
|
+
lastActivityAt: string;
|
|
34
|
+
/** History of ask turns, oldest first. Cap at 20 turns. */
|
|
35
|
+
turns: AskTurn[];
|
|
36
|
+
}
|
|
37
|
+
export interface SessionContext {
|
|
38
|
+
/** Hashes seen in the last N turns (recency-weighted). */
|
|
39
|
+
recentHashes: Set<string>;
|
|
40
|
+
/** Files seen in the last N turns. */
|
|
41
|
+
recentFiles: Set<string>;
|
|
42
|
+
/** Topics — extracted noun phrases from recent questions, frequency-weighted. */
|
|
43
|
+
recentTopics: Map<string, number>;
|
|
44
|
+
}
|
|
45
|
+
/** Maximum turns retained in a single session. */
|
|
46
|
+
export declare const MAX_TURNS = 20;
|
|
47
|
+
/** Idle timeout in milliseconds (1 hour). */
|
|
48
|
+
export declare const SESSION_IDLE_MS: number;
|
|
49
|
+
/** Read session from .mneme/session.json — returns null if expired/missing. */
|
|
50
|
+
export declare function readSession(repoRoot: string, nowMs?: number): Session | null;
|
|
51
|
+
/** Append a turn + write back. Auto-rotates when >MAX_TURNS turns. */
|
|
52
|
+
export declare function appendTurn(repoRoot: string, turn: AskTurn): void;
|
|
53
|
+
/** Reset session (e.g. user runs `mneme reset-session`). */
|
|
54
|
+
export declare function clearSession(repoRoot: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* Build a "session context" for retrieval — accumulates hashes/files/topics
|
|
57
|
+
* from recent turns to bias search. Used by the next ask.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildSessionContext(session: Session | null): SessionContext;
|
|
60
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/wisdom/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,MAAM,WAAW,OAAO;IACtB,qBAAqB;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,sCAAsC;IACtC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,OAAO;IACtB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,sCAAsC;IACtC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,iFAAiF;IACjF,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,kDAAkD;AAClD,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,6CAA6C;AAC7C,eAAO,MAAM,eAAe,QAAiB,CAAC;AAQ9C,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,OAAO,GAAG,IAAI,CA+CxF;AA4BD,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAkBhE;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAQnD;AA8BD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,cAAc,CAgB3E"}
|