@mneme-ai/core 0.8.4 → 0.10.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/entities/go-parser.d.ts +47 -0
- package/dist/entities/go-parser.d.ts.map +1 -0
- package/dist/entities/go-parser.js +315 -0
- package/dist/entities/go-parser.js.map +1 -0
- package/dist/entities/go-parser.test.d.ts +2 -0
- package/dist/entities/go-parser.test.d.ts.map +1 -0
- package/dist/entities/go-parser.test.js +147 -0
- package/dist/entities/go-parser.test.js.map +1 -0
- package/dist/entities/index.d.ts +1 -0
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +1 -0
- package/dist/entities/index.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/indexer/indexer.d.ts +12 -0
- package/dist/indexer/indexer.d.ts.map +1 -1
- package/dist/indexer/indexer.js +28 -1
- package/dist/indexer/indexer.js.map +1 -1
- package/dist/insights/bus-factor.d.ts +58 -0
- package/dist/insights/bus-factor.d.ts.map +1 -0
- package/dist/insights/bus-factor.js +117 -0
- package/dist/insights/bus-factor.js.map +1 -0
- package/dist/insights/bus-factor.test.d.ts +2 -0
- package/dist/insights/bus-factor.test.d.ts.map +1 -0
- package/dist/insights/bus-factor.test.js +149 -0
- package/dist/insights/bus-factor.test.js.map +1 -0
- package/dist/insights/commit-coach.d.ts +80 -0
- package/dist/insights/commit-coach.d.ts.map +1 -0
- package/dist/insights/commit-coach.js +230 -0
- package/dist/insights/commit-coach.js.map +1 -0
- package/dist/insights/commit-coach.test.d.ts +2 -0
- package/dist/insights/commit-coach.test.d.ts.map +1 -0
- package/dist/insights/commit-coach.test.js +163 -0
- package/dist/insights/commit-coach.test.js.map +1 -0
- package/dist/insights/crystal-ball.d.ts +76 -0
- package/dist/insights/crystal-ball.d.ts.map +1 -0
- package/dist/insights/crystal-ball.js +219 -0
- package/dist/insights/crystal-ball.js.map +1 -0
- package/dist/insights/crystal-ball.test.d.ts +2 -0
- package/dist/insights/crystal-ball.test.d.ts.map +1 -0
- package/dist/insights/crystal-ball.test.js +157 -0
- package/dist/insights/crystal-ball.test.js.map +1 -0
- package/dist/insights/decisions.d.ts +38 -0
- package/dist/insights/decisions.d.ts.map +1 -0
- package/dist/insights/decisions.js +125 -0
- package/dist/insights/decisions.js.map +1 -0
- package/dist/insights/decisions.test.d.ts +2 -0
- package/dist/insights/decisions.test.d.ts.map +1 -0
- package/dist/insights/decisions.test.js +141 -0
- package/dist/insights/decisions.test.js.map +1 -0
- package/dist/insights/dream.d.ts +71 -0
- package/dist/insights/dream.d.ts.map +1 -0
- package/dist/insights/dream.js +235 -0
- package/dist/insights/dream.js.map +1 -0
- package/dist/insights/dream.test.d.ts +2 -0
- package/dist/insights/dream.test.d.ts.map +1 -0
- package/dist/insights/dream.test.js +127 -0
- package/dist/insights/dream.test.js.map +1 -0
- package/dist/insights/index.d.ts +21 -0
- package/dist/insights/index.d.ts.map +1 -0
- package/dist/insights/index.js +21 -0
- package/dist/insights/index.js.map +1 -0
- package/dist/insights/obsidian.d.ts +42 -0
- package/dist/insights/obsidian.d.ts.map +1 -0
- package/dist/insights/obsidian.js +263 -0
- package/dist/insights/obsidian.js.map +1 -0
- package/dist/insights/obsidian.test.d.ts +2 -0
- package/dist/insights/obsidian.test.d.ts.map +1 -0
- package/dist/insights/obsidian.test.js +241 -0
- package/dist/insights/obsidian.test.js.map +1 -0
- package/dist/insights/paradox.d.ts +36 -0
- package/dist/insights/paradox.d.ts.map +1 -0
- package/dist/insights/paradox.js +201 -0
- package/dist/insights/paradox.js.map +1 -0
- package/dist/insights/paradox.test.d.ts +2 -0
- package/dist/insights/paradox.test.d.ts.map +1 -0
- package/dist/insights/paradox.test.js +88 -0
- package/dist/insights/paradox.test.js.map +1 -0
- package/dist/insights/regret.d.ts +57 -0
- package/dist/insights/regret.d.ts.map +1 -0
- package/dist/insights/regret.js +137 -0
- package/dist/insights/regret.js.map +1 -0
- package/dist/insights/regret.test.d.ts +2 -0
- package/dist/insights/regret.test.d.ts.map +1 -0
- package/dist/insights/regret.test.js +153 -0
- package/dist/insights/regret.test.js.map +1 -0
- package/dist/insights/stack-trace.d.ts +40 -0
- package/dist/insights/stack-trace.d.ts.map +1 -0
- package/dist/insights/stack-trace.js +127 -0
- package/dist/insights/stack-trace.js.map +1 -0
- package/dist/insights/stack-trace.test.d.ts +2 -0
- package/dist/insights/stack-trace.test.d.ts.map +1 -0
- package/dist/insights/stack-trace.test.js +103 -0
- package/dist/insights/stack-trace.test.js.map +1 -0
- package/dist/insights/story.d.ts +34 -0
- package/dist/insights/story.d.ts.map +1 -0
- package/dist/insights/story.js +100 -0
- package/dist/insights/story.js.map +1 -0
- package/dist/insights/story.test.d.ts +2 -0
- package/dist/insights/story.test.d.ts.map +1 -0
- package/dist/insights/story.test.js +99 -0
- package/dist/insights/story.test.js.map +1 -0
- package/dist/insights/suggest.d.ts +29 -0
- package/dist/insights/suggest.d.ts.map +1 -0
- package/dist/insights/suggest.js +93 -0
- package/dist/insights/suggest.js.map +1 -0
- package/dist/insights/suggest.test.d.ts +2 -0
- package/dist/insights/suggest.test.d.ts.map +1 -0
- package/dist/insights/suggest.test.js +71 -0
- package/dist/insights/suggest.test.js.map +1 -0
- package/dist/insights/who-knows.d.ts +66 -0
- package/dist/insights/who-knows.d.ts.map +1 -0
- package/dist/insights/who-knows.js +125 -0
- package/dist/insights/who-knows.js.map +1 -0
- package/dist/insights/who-knows.test.d.ts +2 -0
- package/dist/insights/who-knows.test.d.ts.map +1 -0
- package/dist/insights/who-knows.test.js +109 -0
- package/dist/insights/who-knows.test.js.map +1 -0
- package/dist/quant/alpha.d.ts +87 -0
- package/dist/quant/alpha.d.ts.map +1 -0
- package/dist/quant/alpha.js +103 -0
- package/dist/quant/alpha.js.map +1 -0
- package/dist/quant/alpha.test.d.ts +2 -0
- package/dist/quant/alpha.test.d.ts.map +1 -0
- package/dist/quant/alpha.test.js +147 -0
- package/dist/quant/alpha.test.js.map +1 -0
- package/dist/quant/backtest.d.ts +57 -0
- package/dist/quant/backtest.d.ts.map +1 -0
- package/dist/quant/backtest.js +90 -0
- package/dist/quant/backtest.js.map +1 -0
- package/dist/quant/backtest.test.d.ts +2 -0
- package/dist/quant/backtest.test.d.ts.map +1 -0
- package/dist/quant/backtest.test.js +133 -0
- package/dist/quant/backtest.test.js.map +1 -0
- package/dist/quant/black-swan.d.ts +45 -0
- package/dist/quant/black-swan.d.ts.map +1 -0
- package/dist/quant/black-swan.js +112 -0
- package/dist/quant/black-swan.js.map +1 -0
- package/dist/quant/black-swan.test.d.ts +2 -0
- package/dist/quant/black-swan.test.d.ts.map +1 -0
- package/dist/quant/black-swan.test.js +131 -0
- package/dist/quant/black-swan.test.js.map +1 -0
- package/dist/quant/correlation-matrix.d.ts +54 -0
- package/dist/quant/correlation-matrix.d.ts.map +1 -0
- package/dist/quant/correlation-matrix.js +103 -0
- package/dist/quant/correlation-matrix.js.map +1 -0
- package/dist/quant/correlation-matrix.test.d.ts +2 -0
- package/dist/quant/correlation-matrix.test.d.ts.map +1 -0
- package/dist/quant/correlation-matrix.test.js +118 -0
- package/dist/quant/correlation-matrix.test.js.map +1 -0
- package/dist/quant/drawdown.d.ts +51 -0
- package/dist/quant/drawdown.d.ts.map +1 -0
- package/dist/quant/drawdown.js +96 -0
- package/dist/quant/drawdown.js.map +1 -0
- package/dist/quant/drawdown.test.d.ts +2 -0
- package/dist/quant/drawdown.test.d.ts.map +1 -0
- package/dist/quant/drawdown.test.js +166 -0
- package/dist/quant/drawdown.test.js.map +1 -0
- package/dist/quant/greek.d.ts +55 -0
- package/dist/quant/greek.d.ts.map +1 -0
- package/dist/quant/greek.js +157 -0
- package/dist/quant/greek.js.map +1 -0
- package/dist/quant/greek.test.d.ts +2 -0
- package/dist/quant/greek.test.d.ts.map +1 -0
- package/dist/quant/greek.test.js +138 -0
- package/dist/quant/greek.test.js.map +1 -0
- package/dist/quant/implied-volatility.d.ts +65 -0
- package/dist/quant/implied-volatility.d.ts.map +1 -0
- package/dist/quant/implied-volatility.js +149 -0
- package/dist/quant/implied-volatility.js.map +1 -0
- package/dist/quant/implied-volatility.test.d.ts +2 -0
- package/dist/quant/implied-volatility.test.d.ts.map +1 -0
- package/dist/quant/implied-volatility.test.js +127 -0
- package/dist/quant/implied-volatility.test.js.map +1 -0
- package/dist/quant/index.d.ts +28 -0
- package/dist/quant/index.d.ts.map +1 -0
- package/dist/quant/index.js +28 -0
- package/dist/quant/index.js.map +1 -0
- package/dist/quant/insider-trading.d.ts +56 -0
- package/dist/quant/insider-trading.d.ts.map +1 -0
- package/dist/quant/insider-trading.js +129 -0
- package/dist/quant/insider-trading.js.map +1 -0
- package/dist/quant/insider-trading.test.d.ts +2 -0
- package/dist/quant/insider-trading.test.d.ts.map +1 -0
- package/dist/quant/insider-trading.test.js +130 -0
- package/dist/quant/insider-trading.test.js.map +1 -0
- package/dist/quant/moneyball.d.ts +48 -0
- package/dist/quant/moneyball.d.ts.map +1 -0
- package/dist/quant/moneyball.js +110 -0
- package/dist/quant/moneyball.js.map +1 -0
- package/dist/quant/moneyball.test.d.ts +2 -0
- package/dist/quant/moneyball.test.d.ts.map +1 -0
- package/dist/quant/moneyball.test.js +137 -0
- package/dist/quant/moneyball.test.js.map +1 -0
- package/dist/quant/tax-loss-harvest.d.ts +59 -0
- package/dist/quant/tax-loss-harvest.d.ts.map +1 -0
- package/dist/quant/tax-loss-harvest.js +126 -0
- package/dist/quant/tax-loss-harvest.js.map +1 -0
- package/dist/quant/tax-loss-harvest.test.d.ts +2 -0
- package/dist/quant/tax-loss-harvest.test.d.ts.map +1 -0
- package/dist/quant/tax-loss-harvest.test.js +126 -0
- package/dist/quant/tax-loss-harvest.test.js.map +1 -0
- package/dist/retrieve/index.d.ts +2 -0
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/index.js +2 -0
- package/dist/retrieve/index.js.map +1 -1
- package/dist/retrieve/intent.d.ts +32 -0
- package/dist/retrieve/intent.d.ts.map +1 -0
- package/dist/retrieve/intent.js +104 -0
- package/dist/retrieve/intent.js.map +1 -0
- package/dist/retrieve/intent.test.d.ts +2 -0
- package/dist/retrieve/intent.test.d.ts.map +1 -0
- package/dist/retrieve/intent.test.js +106 -0
- package/dist/retrieve/intent.test.js.map +1 -0
- package/dist/retrieve/search.d.ts +30 -0
- package/dist/retrieve/search.d.ts.map +1 -1
- package/dist/retrieve/search.js +48 -0
- package/dist/retrieve/search.js.map +1 -1
- package/dist/retrieve/search.test.js +84 -1
- package/dist/retrieve/search.test.js.map +1 -1
- package/dist/retrieve/synthesize.d.ts +57 -0
- package/dist/retrieve/synthesize.d.ts.map +1 -0
- package/dist/retrieve/synthesize.js +191 -0
- package/dist/retrieve/synthesize.js.map +1 -0
- package/dist/retrieve/synthesize.test.d.ts +2 -0
- package/dist/retrieve/synthesize.test.d.ts.map +1 -0
- package/dist/retrieve/synthesize.test.js +127 -0
- package/dist/retrieve/synthesize.test.js.map +1 -0
- package/dist/store/schema.d.ts +2 -2
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +60 -2
- package/dist/store/schema.js.map +1 -1
- package/dist/store/sqlite.d.ts +2 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +24 -0
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/sqlite.test.js +1 -1
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +2 -1
- package/dist/util/index.js.map +1 -1
- package/dist/util/redact.d.ts +58 -0
- package/dist/util/redact.d.ts.map +1 -0
- package/dist/util/redact.js +129 -0
- package/dist/util/redact.js.map +1 -0
- package/dist/util/redact.test.d.ts +2 -0
- package/dist/util/redact.test.d.ts.map +1 -0
- package/dist/util/redact.test.js +148 -0
- package/dist/util/redact.test.js.map +1 -0
- package/dist/wisdom/calibrator.d.ts +43 -0
- package/dist/wisdom/calibrator.d.ts.map +1 -0
- package/dist/wisdom/calibrator.js +120 -0
- package/dist/wisdom/calibrator.js.map +1 -0
- package/dist/wisdom/feedback.d.ts +45 -0
- package/dist/wisdom/feedback.d.ts.map +1 -0
- package/dist/wisdom/feedback.js +116 -0
- package/dist/wisdom/feedback.js.map +1 -0
- package/dist/wisdom/index.d.ts +15 -0
- package/dist/wisdom/index.d.ts.map +1 -0
- package/dist/wisdom/index.js +15 -0
- package/dist/wisdom/index.js.map +1 -0
- package/dist/wisdom/types.d.ts +67 -0
- package/dist/wisdom/types.d.ts.map +1 -0
- package/dist/wisdom/types.js +20 -0
- package/dist/wisdom/types.js.map +1 -0
- package/dist/wisdom/wisdom.test.d.ts +2 -0
- package/dist/wisdom/wisdom.test.d.ts.map +1 -0
- package/dist/wisdom/wisdom.test.js +144 -0
- package/dist/wisdom/wisdom.test.js.map +1 -0
- package/package.json +1 -1
package/dist/util/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from "./redact.js";
|
|
1
2
|
/** Load every commit from the store, oldest first, with its file list. */
|
|
2
3
|
export function loadAllCommits(s) {
|
|
3
4
|
const rows = s.db
|
|
@@ -21,7 +22,7 @@ export function loadCommitsBetween(s, sinceIso, untilIso) {
|
|
|
21
22
|
const rows = s.db.prepare(sql).all(...params);
|
|
22
23
|
return rows.map((r) => rowToCommit(r, s));
|
|
23
24
|
}
|
|
24
|
-
function rowToCommit(r, s) {
|
|
25
|
+
export function rowToCommit(r, s) {
|
|
25
26
|
const hash = String(r.hash);
|
|
26
27
|
return {
|
|
27
28
|
hash,
|
package/dist/util/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"AAOA,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAAC,CAAa;IAC1C,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE;SACd,OAAO,CAAC,gDAAgD,CAAC;SACzD,GAAG,EAAoC,CAAC;IAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,kBAAkB,CAChC,CAAa,EACb,QAAiB,EACjB,QAAiB;IAEjB,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC;IACD,MAAM,GAAG,GAAG,wBAAwB,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,2BAA2B,CAAC;IACnH,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAmC,CAAC;IAChF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/util/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redaction layer — regex scrubber for common credential formats.
|
|
3
|
+
*
|
|
4
|
+
* Used before any text leaves the machine (sent to remote embedder, written
|
|
5
|
+
* to logs, exported via `mneme ledger`). The redactor strips matches from
|
|
6
|
+
* the *text Mneme processes* — your actual git history is never modified.
|
|
7
|
+
*
|
|
8
|
+
* Why this lives in `core/util` and not in a separate package: every code
|
|
9
|
+
* path that touches a remote service (embedder, LLM provider, observability
|
|
10
|
+
* adapter) must run through it, and "import from a sibling package" is too
|
|
11
|
+
* easy to forget. Keeping it adjacent to types.ts makes it the obvious choice.
|
|
12
|
+
*
|
|
13
|
+
* Coverage rationale:
|
|
14
|
+
* - High-confidence patterns (AWS, GitHub, Stripe, OpenAI, Anthropic, Slack,
|
|
15
|
+
* GitLab, npm, Google API, private keys, JWT) — built-in, ON by default.
|
|
16
|
+
* - Lower-confidence (generic `password=` lines, hex blobs that may be hashes)
|
|
17
|
+
* — opt-in via { aggressive: true } because false-positive rate matters
|
|
18
|
+
* when redacting commit text that may legitimately contain hex commit hashes.
|
|
19
|
+
*/
|
|
20
|
+
export interface RedactionRule {
|
|
21
|
+
/** Stable name shown in audit logs and counters. */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Pattern that matches the secret. The *whole match* is replaced. */
|
|
24
|
+
pattern: RegExp;
|
|
25
|
+
/** What to put in place of the match. Default: `<REDACTED:${name}>`. */
|
|
26
|
+
replacement?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RedactOptions {
|
|
29
|
+
/** Add lower-confidence patterns (generic password=, hex blobs ≥ 40 chars). */
|
|
30
|
+
aggressive?: boolean;
|
|
31
|
+
/** Append custom rules (or override built-ins by name). */
|
|
32
|
+
extraRules?: RedactionRule[];
|
|
33
|
+
/** Disable specific built-in rules by name. */
|
|
34
|
+
disableRules?: string[];
|
|
35
|
+
}
|
|
36
|
+
export interface RedactionResult {
|
|
37
|
+
/** Text with every match replaced. */
|
|
38
|
+
text: string;
|
|
39
|
+
/** Per-rule hit count for audit reporting. Zero-count rules are omitted. */
|
|
40
|
+
hits: Record<string, number>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Redact every match of every active rule from the input text.
|
|
44
|
+
* Returns the redacted text plus per-rule hit counts.
|
|
45
|
+
*/
|
|
46
|
+
export declare function redact(text: string, opts?: RedactOptions): RedactionResult;
|
|
47
|
+
/**
|
|
48
|
+
* Returns true iff the text contains any match for any active rule. Useful
|
|
49
|
+
* when you want to short-circuit (e.g. refuse to send to remote embedder)
|
|
50
|
+
* rather than redact-and-send.
|
|
51
|
+
*/
|
|
52
|
+
export declare function containsSecret(text: string, opts?: RedactOptions): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Aggregate hit counters across many redactions — useful for audit reporting
|
|
55
|
+
* after a full index run.
|
|
56
|
+
*/
|
|
57
|
+
export declare function mergeHits(a: Record<string, number>, b: Record<string, number>): Record<string, number>;
|
|
58
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/util/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,2DAA2D;IAC3D,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAuED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,eAAe,CAsB9E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,OAAO,CAE9E;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAItG"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redaction layer — regex scrubber for common credential formats.
|
|
3
|
+
*
|
|
4
|
+
* Used before any text leaves the machine (sent to remote embedder, written
|
|
5
|
+
* to logs, exported via `mneme ledger`). The redactor strips matches from
|
|
6
|
+
* the *text Mneme processes* — your actual git history is never modified.
|
|
7
|
+
*
|
|
8
|
+
* Why this lives in `core/util` and not in a separate package: every code
|
|
9
|
+
* path that touches a remote service (embedder, LLM provider, observability
|
|
10
|
+
* adapter) must run through it, and "import from a sibling package" is too
|
|
11
|
+
* easy to forget. Keeping it adjacent to types.ts makes it the obvious choice.
|
|
12
|
+
*
|
|
13
|
+
* Coverage rationale:
|
|
14
|
+
* - High-confidence patterns (AWS, GitHub, Stripe, OpenAI, Anthropic, Slack,
|
|
15
|
+
* GitLab, npm, Google API, private keys, JWT) — built-in, ON by default.
|
|
16
|
+
* - Lower-confidence (generic `password=` lines, hex blobs that may be hashes)
|
|
17
|
+
* — opt-in via { aggressive: true } because false-positive rate matters
|
|
18
|
+
* when redacting commit text that may legitimately contain hex commit hashes.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Built-in rules. Ordered roughly by specificity (most specific first) so a
|
|
22
|
+
* GitHub PAT is recognized as a GitHub PAT and not a generic Bearer token.
|
|
23
|
+
*
|
|
24
|
+
* Each pattern uses `g` flag — `redact()` consumes the lastIndex correctly
|
|
25
|
+
* by re-creating from `source` + flags on every call (regex objects are
|
|
26
|
+
* stateful between matches; reusing them across inputs corrupts state).
|
|
27
|
+
*/
|
|
28
|
+
const BUILTIN_RULES = [
|
|
29
|
+
// AWS — keys are stable, well-formed, and unambiguous.
|
|
30
|
+
{ name: "aws-access-key-id", pattern: /\b(AKIA|ASIA)[0-9A-Z]{16}\b/g },
|
|
31
|
+
{ name: "aws-secret-access-key", pattern: /\b(?<![A-Za-z0-9+/])[A-Za-z0-9+/]{40}(?![A-Za-z0-9+/])\b/g },
|
|
32
|
+
// GitHub — modern format, all variants. github_pat_ is the new fine-grained one.
|
|
33
|
+
{ name: "github-pat", pattern: /\bgh[pousr]_[A-Za-z0-9]{36,255}\b/g },
|
|
34
|
+
{ name: "github-pat-fine-grained", pattern: /\bgithub_pat_[A-Za-z0-9_]{82,}\b/g },
|
|
35
|
+
// GitLab
|
|
36
|
+
{ name: "gitlab-pat", pattern: /\bglpat-[A-Za-z0-9_-]{20,}\b/g },
|
|
37
|
+
// OpenAI / Anthropic / generic sk-* keys
|
|
38
|
+
{ name: "anthropic-key", pattern: /\bsk-ant-[A-Za-z0-9_-]{32,}\b/g },
|
|
39
|
+
{ name: "openai-key", pattern: /\bsk-[A-Za-z0-9]{20,}\b/g },
|
|
40
|
+
// Stripe — live and test
|
|
41
|
+
{ name: "stripe-key", pattern: /\b(?:sk|pk|rk)_(?:live|test)_[0-9A-Za-z]{20,}\b/g },
|
|
42
|
+
// Slack
|
|
43
|
+
{ name: "slack-token", pattern: /\bxox[abprs]-[0-9A-Za-z-]{10,}\b/g },
|
|
44
|
+
// Google API (AIza prefix)
|
|
45
|
+
{ name: "google-api-key", pattern: /\bAIza[0-9A-Za-z_-]{35}\b/g },
|
|
46
|
+
// npm token (modern format)
|
|
47
|
+
{ name: "npm-token", pattern: /\bnpm_[A-Za-z0-9]{36}\b/g },
|
|
48
|
+
// JWT (header.payload.signature, all base64url)
|
|
49
|
+
{ name: "jwt", pattern: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g },
|
|
50
|
+
// PEM-armored private keys (multiline). These leak through commit messages
|
|
51
|
+
// and PR descriptions surprisingly often when copy/paste goes wrong.
|
|
52
|
+
{
|
|
53
|
+
name: "pem-private-key",
|
|
54
|
+
pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
55
|
+
},
|
|
56
|
+
// Generic bearer tokens (RFC 6750). Less specific — placed last so it does
|
|
57
|
+
// not eat more specific patterns above.
|
|
58
|
+
{ name: "bearer-token", pattern: /\bBearer\s+[A-Za-z0-9._~+/-]{20,}=*\b/g },
|
|
59
|
+
];
|
|
60
|
+
/** Lower-confidence rules — gated behind `aggressive: true`. */
|
|
61
|
+
const AGGRESSIVE_RULES = [
|
|
62
|
+
// password=... in commit text. Conservative bounds prevent eating commit messages.
|
|
63
|
+
// Lookbehind excludes alphanumerics (so "subpassword=..." doesn't fire) but
|
|
64
|
+
// *allows* `_` and `-` (so "DB_PASSWORD=..." and "API-KEY=..." both match —
|
|
65
|
+
// \b alone doesn't, because regex \b treats `_` as a word character).
|
|
66
|
+
{
|
|
67
|
+
name: "password-assignment",
|
|
68
|
+
pattern: /(?<![A-Za-z0-9])(?:password|passwd|pwd|api[_-]?key|secret|token)\s*[:=]\s*['"]?[^\s'"]{8,}['"]?/gi,
|
|
69
|
+
},
|
|
70
|
+
// Long hex blobs (likely a key or hash). Bounded to avoid eating short SHAs.
|
|
71
|
+
// Note: 40-char SHA1 hashes will match — false positives are expected here.
|
|
72
|
+
{
|
|
73
|
+
name: "hex-blob",
|
|
74
|
+
pattern: /\b[0-9a-fA-F]{64,}\b/g,
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
/**
|
|
78
|
+
* Redact every match of every active rule from the input text.
|
|
79
|
+
* Returns the redacted text plus per-rule hit counts.
|
|
80
|
+
*/
|
|
81
|
+
export function redact(text, opts = {}) {
|
|
82
|
+
if (!text)
|
|
83
|
+
return { text, hits: {} };
|
|
84
|
+
const disabled = new Set(opts.disableRules ?? []);
|
|
85
|
+
const rules = [];
|
|
86
|
+
for (const r of BUILTIN_RULES)
|
|
87
|
+
if (!disabled.has(r.name))
|
|
88
|
+
rules.push(r);
|
|
89
|
+
if (opts.aggressive)
|
|
90
|
+
for (const r of AGGRESSIVE_RULES)
|
|
91
|
+
if (!disabled.has(r.name))
|
|
92
|
+
rules.push(r);
|
|
93
|
+
if (opts.extraRules)
|
|
94
|
+
for (const r of opts.extraRules)
|
|
95
|
+
rules.push(r);
|
|
96
|
+
const hits = {};
|
|
97
|
+
let out = text;
|
|
98
|
+
for (const rule of rules) {
|
|
99
|
+
// Re-create regex per-call to avoid lastIndex pollution from prior runs.
|
|
100
|
+
const re = new RegExp(rule.pattern.source, rule.pattern.flags.includes("g") ? rule.pattern.flags : rule.pattern.flags + "g");
|
|
101
|
+
let count = 0;
|
|
102
|
+
out = out.replace(re, () => {
|
|
103
|
+
count += 1;
|
|
104
|
+
return rule.replacement ?? `<REDACTED:${rule.name}>`;
|
|
105
|
+
});
|
|
106
|
+
if (count > 0)
|
|
107
|
+
hits[rule.name] = count;
|
|
108
|
+
}
|
|
109
|
+
return { text: out, hits };
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Returns true iff the text contains any match for any active rule. Useful
|
|
113
|
+
* when you want to short-circuit (e.g. refuse to send to remote embedder)
|
|
114
|
+
* rather than redact-and-send.
|
|
115
|
+
*/
|
|
116
|
+
export function containsSecret(text, opts = {}) {
|
|
117
|
+
return Object.keys(redact(text, opts).hits).length > 0;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Aggregate hit counters across many redactions — useful for audit reporting
|
|
121
|
+
* after a full index run.
|
|
122
|
+
*/
|
|
123
|
+
export function mergeHits(a, b) {
|
|
124
|
+
const out = { ...a };
|
|
125
|
+
for (const [k, v] of Object.entries(b))
|
|
126
|
+
out[k] = (out[k] ?? 0) + v;
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/util/redact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.d.ts","sourceRoot":"","sources":["../../src/util/redact.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { redact, containsSecret, mergeHits } from "./redact.js";
|
|
3
|
+
describe("redact — high-confidence rules", () => {
|
|
4
|
+
it("strips AWS access key id (AKIA prefix)", () => {
|
|
5
|
+
const r = redact("token=AKIAIOSFODNN7EXAMPLE in config");
|
|
6
|
+
expect(r.text).toContain("<REDACTED:aws-access-key-id>");
|
|
7
|
+
expect(r.text).not.toContain("AKIAIOSFODNN7EXAMPLE");
|
|
8
|
+
expect(r.hits["aws-access-key-id"]).toBe(1);
|
|
9
|
+
});
|
|
10
|
+
it("strips AWS access key id (ASIA prefix — temp credentials)", () => {
|
|
11
|
+
const r = redact("ASIAIOSFODNN7EXAMPLE");
|
|
12
|
+
expect(r.text).toContain("<REDACTED:aws-access-key-id>");
|
|
13
|
+
});
|
|
14
|
+
it("strips GitHub PAT (ghp_ classic)", () => {
|
|
15
|
+
const tok = "ghp" + "_" + "A".repeat(36);
|
|
16
|
+
const r = redact(`Cloning with ${tok} failed`);
|
|
17
|
+
expect(r.text).toContain("<REDACTED:github-pat>");
|
|
18
|
+
expect(r.hits["github-pat"]).toBe(1);
|
|
19
|
+
});
|
|
20
|
+
it("strips GitHub fine-grained PAT (github_pat_ prefix)", () => {
|
|
21
|
+
const tok = "github" + "_pat_" + "A".repeat(85);
|
|
22
|
+
const r = redact(`token=${tok}`);
|
|
23
|
+
expect(r.text).not.toContain(tok);
|
|
24
|
+
});
|
|
25
|
+
it("strips Stripe live secret keys", () => {
|
|
26
|
+
// Constructed at runtime so the source file does not contain a literal
|
|
27
|
+
// string that GitHub's secret scanner flags as a real Stripe key.
|
|
28
|
+
const stripeKey = "sk" + "_live_" + "A".repeat(24);
|
|
29
|
+
const r = redact(`Stripe key: ${stripeKey}`);
|
|
30
|
+
expect(r.text).toContain("<REDACTED:stripe-key>");
|
|
31
|
+
});
|
|
32
|
+
it("strips OpenAI keys (sk- prefix)", () => {
|
|
33
|
+
const openaiKey = "sk" + "-" + "A".repeat(20);
|
|
34
|
+
const r = redact(`OPENAI_API_KEY=${openaiKey}`);
|
|
35
|
+
expect(r.text).toContain("<REDACTED:openai-key>");
|
|
36
|
+
});
|
|
37
|
+
it("strips Anthropic keys (sk-ant- prefix)", () => {
|
|
38
|
+
const anthropicKey = "sk" + "-ant-" + "A".repeat(32);
|
|
39
|
+
const r = redact(`ANTHROPIC_API_KEY=${anthropicKey}`);
|
|
40
|
+
expect(r.text).toContain("<REDACTED:anthropic-key>");
|
|
41
|
+
});
|
|
42
|
+
it("strips Slack tokens (xox prefix)", () => {
|
|
43
|
+
// Constructed at runtime to avoid GitHub secret-scanner false positives.
|
|
44
|
+
const slackToken = "xox" + "b-" + "1234567890-1234567890-" + "A".repeat(14);
|
|
45
|
+
const r = redact(`Slack: ${slackToken}`);
|
|
46
|
+
expect(r.text).toContain("<REDACTED:slack-token>");
|
|
47
|
+
});
|
|
48
|
+
it("strips Google API keys (AIza prefix)", () => {
|
|
49
|
+
// Real format: AIza + exactly 35 chars of [0-9A-Za-z_-]
|
|
50
|
+
const key = "AIza" + "B".repeat(35);
|
|
51
|
+
const r = redact(`GOOGLE_API_KEY=${key}`);
|
|
52
|
+
expect(r.text).toContain("<REDACTED:google-api-key>");
|
|
53
|
+
});
|
|
54
|
+
it("strips npm tokens", () => {
|
|
55
|
+
const r = redact("NPM_TOKEN=npm_" + "x".repeat(36));
|
|
56
|
+
expect(r.text).toContain("<REDACTED:npm-token>");
|
|
57
|
+
});
|
|
58
|
+
it("strips JWTs (3 base64url segments)", () => {
|
|
59
|
+
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
|
|
60
|
+
".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ" +
|
|
61
|
+
".SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
62
|
+
const r = redact(`auth: ${jwt}`);
|
|
63
|
+
expect(r.text).toContain("<REDACTED:jwt>");
|
|
64
|
+
});
|
|
65
|
+
it("strips multiline PEM private keys", () => {
|
|
66
|
+
const pem = [
|
|
67
|
+
"-----BEGIN RSA PRIVATE KEY-----",
|
|
68
|
+
"MIIBOQIBAAJAVhx/mmddhM4...",
|
|
69
|
+
"-----END RSA PRIVATE KEY-----",
|
|
70
|
+
].join("\n");
|
|
71
|
+
const r = redact(`config:\n${pem}\nend`);
|
|
72
|
+
expect(r.text).toContain("<REDACTED:pem-private-key>");
|
|
73
|
+
expect(r.text).not.toContain("MIIBOQIBAAJAVhx");
|
|
74
|
+
});
|
|
75
|
+
it("strips Bearer tokens", () => {
|
|
76
|
+
const r = redact("Authorization: Bearer abcDEFghiJKLmnoPQRstuVWXyz0123456");
|
|
77
|
+
expect(r.text).toContain("<REDACTED:bearer-token>");
|
|
78
|
+
});
|
|
79
|
+
it("counts multiple hits per rule", () => {
|
|
80
|
+
const r = redact("k1=AKIAIOSFODNN7EXAMPLE k2=AKIAIOSFODNN7EXAMPLF");
|
|
81
|
+
expect(r.hits["aws-access-key-id"]).toBe(2);
|
|
82
|
+
});
|
|
83
|
+
it("counts hits across rules", () => {
|
|
84
|
+
const r = redact("aws=AKIAIOSFODNN7EXAMPLE gh=ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
|
85
|
+
expect(r.hits["aws-access-key-id"]).toBe(1);
|
|
86
|
+
expect(r.hits["github-pat"]).toBe(1);
|
|
87
|
+
});
|
|
88
|
+
it("leaves clean text unchanged", () => {
|
|
89
|
+
const text = "Fix Stripe webhook crash on amount=BigInt overflow";
|
|
90
|
+
const r = redact(text);
|
|
91
|
+
expect(r.text).toBe(text);
|
|
92
|
+
expect(r.hits).toEqual({});
|
|
93
|
+
});
|
|
94
|
+
it("handles empty input", () => {
|
|
95
|
+
expect(redact("").text).toBe("");
|
|
96
|
+
expect(redact("").hits).toEqual({});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("redact — aggressive mode", () => {
|
|
100
|
+
it('catches generic password=... when { aggressive: true }', () => {
|
|
101
|
+
const r = redact("DB_PASSWORD=hunter2-very-long-password", { aggressive: true });
|
|
102
|
+
expect(r.text).toContain("<REDACTED:password-assignment>");
|
|
103
|
+
});
|
|
104
|
+
it("does NOT catch generic password=... by default (false-positive avoidance)", () => {
|
|
105
|
+
const r = redact("DB_PASSWORD=hunter2-very-long-password");
|
|
106
|
+
expect(r.text).toContain("hunter2-very-long-password");
|
|
107
|
+
});
|
|
108
|
+
it("catches long hex blobs only when aggressive", () => {
|
|
109
|
+
const hex = "a".repeat(64);
|
|
110
|
+
expect(redact(hex).text).toBe(hex);
|
|
111
|
+
expect(redact(hex, { aggressive: true }).text).toContain("<REDACTED:hex-blob>");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
describe("redact — custom rules and overrides", () => {
|
|
115
|
+
it("applies extraRules from caller", () => {
|
|
116
|
+
const r = redact("internal-secret=XYZZY", {
|
|
117
|
+
extraRules: [{ name: "internal-secret", pattern: /\binternal-secret=[A-Z]+/g }],
|
|
118
|
+
});
|
|
119
|
+
expect(r.text).toContain("<REDACTED:internal-secret>");
|
|
120
|
+
});
|
|
121
|
+
it("respects custom replacement string when built-in is disabled", () => {
|
|
122
|
+
const r = redact("k=AKIAIOSFODNN7EXAMPLE", {
|
|
123
|
+
disableRules: ["aws-access-key-id"],
|
|
124
|
+
extraRules: [{ name: "aws", pattern: /\bAKIA[0-9A-Z]{16}\b/g, replacement: "[hidden]" }],
|
|
125
|
+
});
|
|
126
|
+
expect(r.text).toBe("k=[hidden]");
|
|
127
|
+
});
|
|
128
|
+
it("disableRules removes a built-in rule", () => {
|
|
129
|
+
const r = redact("AKIAIOSFODNN7EXAMPLE", { disableRules: ["aws-access-key-id"] });
|
|
130
|
+
expect(r.text).toContain("AKIAIOSFODNN7EXAMPLE");
|
|
131
|
+
expect(r.hits["aws-access-key-id"]).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("containsSecret", () => {
|
|
135
|
+
it("returns true when any rule matches", () => {
|
|
136
|
+
expect(containsSecret("ghp_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
it("returns false on clean text", () => {
|
|
139
|
+
expect(containsSecret("Fix Stripe webhook crash on BigInt amounts")).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
describe("mergeHits", () => {
|
|
143
|
+
it("sums per-rule counters across two reports", () => {
|
|
144
|
+
const merged = mergeHits({ "aws": 2, "gh": 1 }, { "aws": 3, "stripe": 5 });
|
|
145
|
+
expect(merged).toEqual({ aws: 5, gh: 1, stripe: 5 });
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
//# sourceMappingURL=redact.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.test.js","sourceRoot":"","sources":["../../src/util/redact.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEhE,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,GAAG,MAAM,CAAC,sCAAsC,CAAC,CAAC;QACzD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACzD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACrD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,GAAG,GAAG,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,GAAG,MAAM,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;QAC/C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAClD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,GAAG,GAAG,QAAQ,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,uEAAuE;QACvE,kEAAkE;QAClE,MAAM,SAAS,GAAG,IAAI,GAAG,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,MAAM,CAAC,eAAe,SAAS,EAAE,CAAC,CAAC;QAC7C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,SAAS,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,kBAAkB,SAAS,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,YAAY,GAAG,IAAI,GAAG,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,GAAG,MAAM,CAAC,qBAAqB,YAAY,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,yEAAyE;QACzE,MAAM,UAAU,GAAG,KAAK,GAAG,IAAI,GAAG,wBAAwB,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5E,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,UAAU,EAAE,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,wDAAwD;QACxD,MAAM,GAAG,GAAG,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,GAAG,MAAM,CAAC,kBAAkB,GAAG,EAAE,CAAC,CAAC;QAC1C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,GAAG,MAAM,CAAC,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACpD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,GAAG,GAAG,sCAAsC;YAChD,iDAAiD;YACjD,8CAA8C,CAAC;QACjD,MAAM,CAAC,GAAG,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,GAAG,GAAG;YACV,iCAAiC;YACjC,4BAA4B;YAC5B,+BAA+B;SAChC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,MAAM,CAAC,GAAG,MAAM,CAAC,YAAY,GAAG,OAAO,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;QACvD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,yDAAyD,CAAC,CAAC;QAC5E,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,GAAG,MAAM,CAAC,iDAAiD,CAAC,CAAC;QACpE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,GAAG,MAAM,CAAC,sEAAsE,CAAC,CAAC;QACzF,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,IAAI,GAAG,oDAAoD,CAAC;QAClE,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,MAAM,CAAC,wCAAwC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACjF,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,gCAAgC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,CAAC,GAAG,MAAM,CAAC,wCAAwC,CAAC,CAAC;QAC3D,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC3B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,GAAG,MAAM,CAAC,uBAAuB,EAAE;YACxC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,2BAA2B,EAAE,CAAC;SAChF,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,4BAA4B,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,GAAG,MAAM,CAAC,wBAAwB,EAAE;YACzC,YAAY,EAAE,CAAC,mBAAmB,CAAC;YACnC,UAAU,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;SACzF,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,GAAG,MAAM,CAAC,sBAAsB,EAAE,EAAE,YAAY,EAAE,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC;QACjD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,cAAc,CAAC,0CAA0C,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,cAAc,CAAC,4CAA4C,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,MAAM,GAAG,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-calibrator — picks the search config that maximizes hit rate on the
|
|
3
|
+
* accumulated feedback set.
|
|
4
|
+
*
|
|
5
|
+
* Approach: small-grid search over (semanticWeight × minSemCosine × rrfK).
|
|
6
|
+
* Re-runs every query in the feedback set with each config and counts how
|
|
7
|
+
* many of the originally-helpful results stay in top-K. The config with the
|
|
8
|
+
* highest count wins.
|
|
9
|
+
*
|
|
10
|
+
* Pragmatically:
|
|
11
|
+
* - Grid is intentionally small (3 × 3 × 2 = 18 configs) to fit in seconds.
|
|
12
|
+
* - We only consider feedback where was_helpful = 1 (positive examples).
|
|
13
|
+
* - We require ≥ 10 positive examples before calibrating; below that, the
|
|
14
|
+
* defaults are statistically stronger than any tuned value.
|
|
15
|
+
*
|
|
16
|
+
* The output is written to wisdom_calibration so search() can read the
|
|
17
|
+
* latest calibrated values without re-running the calibrator.
|
|
18
|
+
*/
|
|
19
|
+
import type { MnemeStore } from "../store/sqlite.js";
|
|
20
|
+
import type { EmbeddingProvider } from "../types.js";
|
|
21
|
+
import { type CalibrationConfig, type CalibrationRow } from "./types.js";
|
|
22
|
+
export interface CalibrationResult {
|
|
23
|
+
/** The winning config. If no calibration ran, equals DEFAULT_CALIBRATION. */
|
|
24
|
+
config: CalibrationConfig;
|
|
25
|
+
/** Score on the feedback set: fraction of helpful results re-found in top-3. */
|
|
26
|
+
hitRate: number;
|
|
27
|
+
/** Number of positive examples evaluated (gating threshold: 10). */
|
|
28
|
+
positiveExamples: number;
|
|
29
|
+
/** Whether the calibrator actually ran or returned defaults. */
|
|
30
|
+
calibrated: boolean;
|
|
31
|
+
/** The full grid result, sorted desc by hitRate. */
|
|
32
|
+
grid: Array<CalibrationConfig & {
|
|
33
|
+
hitRate: number;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export declare function calibrate(store: MnemeStore, embedder?: EmbeddingProvider): Promise<CalibrationResult>;
|
|
37
|
+
export declare function persistCalibration(store: MnemeStore, key: string, config: CalibrationConfig & {
|
|
38
|
+
hitRate: number;
|
|
39
|
+
}, metricName: string, notes?: string): void;
|
|
40
|
+
/** Read back the current calibration. Returns DEFAULT_CALIBRATION if no row. */
|
|
41
|
+
export declare function readCalibration(store: MnemeStore, key?: string): CalibrationConfig;
|
|
42
|
+
export declare function listCalibrations(store: MnemeStore): CalibrationRow[];
|
|
43
|
+
//# sourceMappingURL=calibrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calibrator.d.ts","sourceRoot":"","sources":["../../src/wisdom/calibrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AAWpB,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,MAAM,EAAE,iBAAiB,CAAC;IAC1B,gFAAgF;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,gBAAgB,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,oDAAoD;IACpD,IAAI,EAAE,KAAK,CAAC,iBAAiB,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACtD;AAID,wBAAsB,SAAS,CAC7B,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAmD5B;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,EAC/C,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAmBN;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,SAAW,GAAG,iBAAiB,CAiBpF;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,EAAE,CAsBpE"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-calibrator — picks the search config that maximizes hit rate on the
|
|
3
|
+
* accumulated feedback set.
|
|
4
|
+
*
|
|
5
|
+
* Approach: small-grid search over (semanticWeight × minSemCosine × rrfK).
|
|
6
|
+
* Re-runs every query in the feedback set with each config and counts how
|
|
7
|
+
* many of the originally-helpful results stay in top-K. The config with the
|
|
8
|
+
* highest count wins.
|
|
9
|
+
*
|
|
10
|
+
* Pragmatically:
|
|
11
|
+
* - Grid is intentionally small (3 × 3 × 2 = 18 configs) to fit in seconds.
|
|
12
|
+
* - We only consider feedback where was_helpful = 1 (positive examples).
|
|
13
|
+
* - We require ≥ 10 positive examples before calibrating; below that, the
|
|
14
|
+
* defaults are statistically stronger than any tuned value.
|
|
15
|
+
*
|
|
16
|
+
* The output is written to wisdom_calibration so search() can read the
|
|
17
|
+
* latest calibrated values without re-running the calibrator.
|
|
18
|
+
*/
|
|
19
|
+
import { search } from "../retrieve/search.js";
|
|
20
|
+
import { listFeedback } from "./feedback.js";
|
|
21
|
+
import { DEFAULT_CALIBRATION, } from "./types.js";
|
|
22
|
+
const SEARCH_GRID = [];
|
|
23
|
+
for (const semanticWeight of [0.4, 0.65, 0.85]) {
|
|
24
|
+
for (const minSemCosine of [0.3, 0.4, 0.5]) {
|
|
25
|
+
for (const rrfK of [60, 30]) {
|
|
26
|
+
SEARCH_GRID.push({ semanticWeight, minSemCosine, rrfK });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const MIN_POSITIVE_EXAMPLES = 10;
|
|
31
|
+
export async function calibrate(store, embedder) {
|
|
32
|
+
const positives = listFeedback(store, { limit: 500 }).filter((f) => f.wasHelpful === 1 && f.resultHashes.length > 0);
|
|
33
|
+
if (positives.length < MIN_POSITIVE_EXAMPLES) {
|
|
34
|
+
return {
|
|
35
|
+
config: DEFAULT_CALIBRATION,
|
|
36
|
+
hitRate: 0,
|
|
37
|
+
positiveExamples: positives.length,
|
|
38
|
+
calibrated: false,
|
|
39
|
+
grid: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const config of SEARCH_GRID) {
|
|
44
|
+
let hits = 0;
|
|
45
|
+
for (const ex of positives) {
|
|
46
|
+
// The "ground truth" is the top-3 hashes from the original query —
|
|
47
|
+
// we re-run with the candidate config and check overlap.
|
|
48
|
+
const groundTruth = new Set(ex.resultHashes.slice(0, 3));
|
|
49
|
+
const re = await search(ex.query, {
|
|
50
|
+
store,
|
|
51
|
+
embedder,
|
|
52
|
+
topK: 3,
|
|
53
|
+
semanticWeight: config.semanticWeight,
|
|
54
|
+
confidenceFloor: { minFtsHits: 1, minSemCosine: config.minSemCosine },
|
|
55
|
+
});
|
|
56
|
+
const reHashes = new Set(re.map((r) => r.commit.hash));
|
|
57
|
+
// Count this query as a "hit" if at least one of the original useful
|
|
58
|
+
// results is still surfaced.
|
|
59
|
+
const overlap = [...groundTruth].some((h) => reHashes.has(h));
|
|
60
|
+
if (overlap)
|
|
61
|
+
hits += 1;
|
|
62
|
+
}
|
|
63
|
+
results.push({ ...config, hitRate: hits / positives.length });
|
|
64
|
+
}
|
|
65
|
+
results.sort((a, b) => b.hitRate - a.hitRate);
|
|
66
|
+
const best = results[0];
|
|
67
|
+
persistCalibration(store, "search", best, "hit_rate");
|
|
68
|
+
return {
|
|
69
|
+
config: { semanticWeight: best.semanticWeight, minSemCosine: best.minSemCosine, rrfK: best.rrfK },
|
|
70
|
+
hitRate: best.hitRate,
|
|
71
|
+
positiveExamples: positives.length,
|
|
72
|
+
calibrated: true,
|
|
73
|
+
grid: results,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function persistCalibration(store, key, config, metricName, notes) {
|
|
77
|
+
store.db
|
|
78
|
+
.prepare(`INSERT OR REPLACE INTO wisdom_calibration
|
|
79
|
+
(key, value, metric_value, metric_name, calibrated_at, notes)
|
|
80
|
+
VALUES (?, ?, ?, ?, ?, ?)`)
|
|
81
|
+
.run(key, JSON.stringify({
|
|
82
|
+
semanticWeight: config.semanticWeight,
|
|
83
|
+
minSemCosine: config.minSemCosine,
|
|
84
|
+
rrfK: config.rrfK,
|
|
85
|
+
}), config.hitRate, metricName, new Date().toISOString(), notes ?? null);
|
|
86
|
+
}
|
|
87
|
+
/** Read back the current calibration. Returns DEFAULT_CALIBRATION if no row. */
|
|
88
|
+
export function readCalibration(store, key = "search") {
|
|
89
|
+
const row = store.db
|
|
90
|
+
.prepare(`SELECT value FROM wisdom_calibration WHERE key = ? ORDER BY calibrated_at DESC LIMIT 1`)
|
|
91
|
+
.get(key);
|
|
92
|
+
if (!row)
|
|
93
|
+
return DEFAULT_CALIBRATION;
|
|
94
|
+
try {
|
|
95
|
+
const parsed = JSON.parse(row.value);
|
|
96
|
+
return {
|
|
97
|
+
semanticWeight: parsed.semanticWeight ?? DEFAULT_CALIBRATION.semanticWeight,
|
|
98
|
+
minSemCosine: parsed.minSemCosine ?? DEFAULT_CALIBRATION.minSemCosine,
|
|
99
|
+
rrfK: parsed.rrfK ?? DEFAULT_CALIBRATION.rrfK,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return DEFAULT_CALIBRATION;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function listCalibrations(store) {
|
|
107
|
+
const rows = store.db
|
|
108
|
+
.prepare(`SELECT key, value, metric_value, metric_name, calibrated_at, notes
|
|
109
|
+
FROM wisdom_calibration ORDER BY calibrated_at DESC`)
|
|
110
|
+
.all();
|
|
111
|
+
return rows.map((r) => ({
|
|
112
|
+
key: r.key,
|
|
113
|
+
value: JSON.parse(r.value),
|
|
114
|
+
metricValue: r.metric_value ?? undefined,
|
|
115
|
+
metricName: r.metric_name ?? undefined,
|
|
116
|
+
calibratedAt: r.calibrated_at,
|
|
117
|
+
notes: r.notes ?? undefined,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=calibrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calibrator.js","sourceRoot":"","sources":["../../src/wisdom/calibrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feedback collector — every `mneme ask` writes one row.
|
|
3
|
+
*
|
|
4
|
+
* was_helpful starts NULL and is filled by:
|
|
5
|
+
* - explicit: `mneme feedback <id> up|down`
|
|
6
|
+
* - implicit-revisit: the user ran `mneme why <commit>` on a returned commit
|
|
7
|
+
* within 5 minutes — strong positive signal.
|
|
8
|
+
* - implicit-reuse: the user did not re-ask a slightly-different version of
|
|
9
|
+
* the same query within 60s — weak positive signal.
|
|
10
|
+
*
|
|
11
|
+
* Why these heuristics: they require zero UX work from the user. The
|
|
12
|
+
* explicit path exists for power users; the implicit paths cover the 95%.
|
|
13
|
+
*/
|
|
14
|
+
import type { MnemeStore } from "../store/sqlite.js";
|
|
15
|
+
import type { FeedbackRow } from "./types.js";
|
|
16
|
+
export interface RecordFeedbackInput {
|
|
17
|
+
query: string;
|
|
18
|
+
resultHashes: string[];
|
|
19
|
+
topScore?: number;
|
|
20
|
+
semanticWeight?: number;
|
|
21
|
+
minSemCosine?: number;
|
|
22
|
+
rrfK?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Insert a pending feedback row. Returns the id so callers can update it later. */
|
|
25
|
+
export declare function recordQuery(store: MnemeStore, input: RecordFeedbackInput): string;
|
|
26
|
+
/** Set was_helpful for a feedback row. */
|
|
27
|
+
export declare function setHelpful(store: MnemeStore, id: string, wasHelpful: boolean, source?: FeedbackRow["source"]): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Implicit positive signal: the user looked at one of the returned commits
|
|
30
|
+
* via `mneme why`. We mark the most recent matching pending row as helpful.
|
|
31
|
+
*/
|
|
32
|
+
export declare function recordImplicitRevisit(store: MnemeStore, commitHash: string): number;
|
|
33
|
+
export declare function listFeedback(store: MnemeStore, opts?: {
|
|
34
|
+
since?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}): FeedbackRow[];
|
|
37
|
+
export interface FeedbackSummary {
|
|
38
|
+
total: number;
|
|
39
|
+
helpful: number;
|
|
40
|
+
unhelpful: number;
|
|
41
|
+
pending: number;
|
|
42
|
+
hitRate: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function summarizeFeedback(store: MnemeStore, since?: string): FeedbackSummary;
|
|
45
|
+
//# sourceMappingURL=feedback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.d.ts","sourceRoot":"","sources":["../../src/wisdom/feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,mBAAmB,GAAG,MAAM,CAqBjF;AAED,0CAA0C;AAC1C,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,OAAO,EACnB,MAAM,GAAE,WAAW,CAAC,QAAQ,CAAc,GACzC,OAAO,CAKT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAsBnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,WAAW,EAAE,CAwC5G;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CA0BpF"}
|