@cachly-dev/mcp-server 0.10.120 → 0.10.122

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.
@@ -1,4 +1,4 @@
1
- import { type LessonCandidate, type GateOptions } from './ambient-recall.js';
1
+ import { type RecallMemory, type LessonCandidate, type GateOptions } from './ambient-recall.js';
2
2
  /** The subset of the Claude Code hook payload we care about. */
3
3
  export interface HookPayload {
4
4
  hook_event_name?: string;
@@ -47,6 +47,19 @@ export declare function truncateToTokens(text: string, maxTokens: number): strin
47
47
  * Kept to one short line — its own token cost is counted as injected.
48
48
  */
49
49
  export declare const CREDIT_FOOTER = "(If this memory just saved you from a wrong path, record it: `npx @cachly-dev/mcp-server@latest ambient-credit <tokens-saved>`)";
50
+ /**
51
+ * Der Beleg: WANN es gelernt wurde und WORAN.
52
+ *
53
+ * Ohne Beleg ist proaktive Erinnerung eine Behauptung — und Behauptungen einer
54
+ * Maschine prueft niemand nach, man glaubt sie oder ignoriert sie. Mit Datum
55
+ * und Datei wird daraus eine nachpruefbare Aussage: "am 24.07., in
56
+ * infra/backup.sh — ging damals schief". Genau das macht den Unterschied
57
+ * zwischen einem Hinweis, dem man folgt, und einem, den man wegklickt.
58
+ *
59
+ * Bewusst kurz gehalten (eine Zeile, hoechstens zwei Dateien): der Beleg wird
60
+ * bei JEDER Erinnerung mitgezahlt und darf das Token-Budget nicht sprengen.
61
+ */
62
+ export declare function formatProvenance(l: LessonCandidate): string;
50
63
  /**
51
64
  * Render the gated lessons into a context block for the agent.
52
65
  * • A single already-formatted briefing (multi-line summary — what smart_recall
@@ -63,6 +76,9 @@ export declare function formatContextBlock(lessons: LessonCandidate[], withCredi
63
76
  */
64
77
  export declare function buildHookOutput(event: string, additionalContext: string): string;
65
78
  export interface AmbientDeps {
79
+ /** Optionales Trigger-Gedaechtnis (Dedupe + Ruhe-Budget). Fehlt es, bleibt das Verhalten zustandslos. */
80
+ loadMemory?: () => RecallMemory | null | undefined;
81
+ saveMemory?: (m: RecallMemory) => void;
66
82
  /** Fetch candidate lessons for a query. Should already be scoped to the brain. */
67
83
  recall: (query: string, event: string) => Promise<LessonCandidate[]>;
68
84
  /** Overrides for the relevance gate. */
@@ -13,7 +13,7 @@
13
13
  // a crashing hook can never block or corrupt the agent's turn.
14
14
  // • Self-limited: recall runs under a hard timeout budget; a slow brain never
15
15
  // stalls the user's prompt.
16
- import { isTrivialPrompt, selectInjectable, estimateTokens, } from './ambient-recall.js';
16
+ import { decideRecall, commitInjection, emptyMemory, isTrivialPrompt, selectInjectable, estimateTokens, } from './ambient-recall.js';
17
17
  /** Parse the hook payload from stdin. Never throws — returns null on any problem. */
18
18
  export function parseHookPayload(raw) {
19
19
  const trimmed = (raw ?? '').trim();
@@ -101,6 +101,36 @@ export function truncateToTokens(text, maxTokens) {
101
101
  * Kept to one short line — its own token cost is counted as injected.
102
102
  */
103
103
  export const CREDIT_FOOTER = '(If this memory just saved you from a wrong path, record it: `npx @cachly-dev/mcp-server@latest ambient-credit <tokens-saved>`)';
104
+ /**
105
+ * Der Beleg: WANN es gelernt wurde und WORAN.
106
+ *
107
+ * Ohne Beleg ist proaktive Erinnerung eine Behauptung — und Behauptungen einer
108
+ * Maschine prueft niemand nach, man glaubt sie oder ignoriert sie. Mit Datum
109
+ * und Datei wird daraus eine nachpruefbare Aussage: "am 24.07., in
110
+ * infra/backup.sh — ging damals schief". Genau das macht den Unterschied
111
+ * zwischen einem Hinweis, dem man folgt, und einem, den man wegklickt.
112
+ *
113
+ * Bewusst kurz gehalten (eine Zeile, hoechstens zwei Dateien): der Beleg wird
114
+ * bei JEDER Erinnerung mitgezahlt und darf das Token-Budget nicht sprengen.
115
+ */
116
+ export function formatProvenance(l) {
117
+ const teile = [];
118
+ if (l.learnedAt) {
119
+ const d = new Date(l.learnedAt);
120
+ if (!Number.isNaN(d.getTime())) {
121
+ teile.push(`am ${String(d.getDate()).padStart(2, "0")}.${String(d.getMonth() + 1).padStart(2, "0")}.`);
122
+ }
123
+ }
124
+ if (l.files?.length) {
125
+ const f = l.files.slice(0, 2).join(", ");
126
+ teile.push(`in ${f}${l.files.length > 2 ? ` (+${l.files.length - 2})` : ""}`);
127
+ }
128
+ if (l.outcome === "failure")
129
+ teile.push("ging damals schief");
130
+ else if (l.outcome === "success")
131
+ teile.push("so ging es damals gut aus");
132
+ return teile.length ? ` ↳ ${teile.join(" · ")}` : "";
133
+ }
104
134
  /**
105
135
  * Render the gated lessons into a context block for the agent.
106
136
  * • A single already-formatted briefing (multi-line summary — what smart_recall
@@ -115,7 +145,12 @@ export function formatContextBlock(lessons, withCreditFooter = false) {
115
145
  const body = lessons.length === 1 && lessons[0].summary.includes('\n')
116
146
  ? lessons[0].summary.trim()
117
147
  : `🧠 Relevant memory from your cachly brain (auto-recalled):\n` +
118
- lessons.map((l) => `- ${l.summary.trim()}`).join('\n');
148
+ lessons
149
+ .map((l) => {
150
+ const beleg = formatProvenance(l);
151
+ return `- ${l.summary.trim()}` + (beleg ? '\n' + beleg : '');
152
+ })
153
+ .join('\n');
119
154
  return withCreditFooter ? `${body}\n${CREDIT_FOOTER}` : body;
120
155
  }
121
156
  /**
@@ -181,7 +216,26 @@ export async function runAmbient(raw, deps) {
181
216
  // SessionStart/PreToolUse have no user prompt, so their trivial-skip is
182
217
  // meaningless; pass the recall query itself so the gate's non-trivial branch runs.
183
218
  const gateInput = event === 'UserPromptSubmit' ? (payload.prompt ?? query) : query;
184
- const decision = selectInjectable(gateInput, candidates, deps.gate);
219
+ // Antizipation braucht Timing, nicht nur Relevanz: dieselbe Lektion in jedem
220
+ // zweiten Turn ist kein Hinweis mehr, sondern Laerm. `decideRecall` bringt
221
+ // Dedupe, Ruhe-Budget und einen risikoabhaengigen Ausloesemoment mit; ohne
222
+ // Gedaechtnis-Deps bleibt das Verhalten exakt wie bisher (reiner Gate).
223
+ const memory = deps.loadMemory?.() ?? emptyMemory();
224
+ const decision = deps.loadMemory
225
+ ? decideRecall(gateInput, candidates, memory, deps.gate)
226
+ : {
227
+ ...selectInjectable(gateInput, candidates, deps.gate),
228
+ risk: 'normal',
229
+ suppressedDuplicates: [],
230
+ };
231
+ if (deps.saveMemory) {
232
+ try {
233
+ deps.saveMemory(commitInjection(memory, decision));
234
+ }
235
+ catch {
236
+ // Gedaechtnis ist Komfort, kein Muss — ein Schreibfehler darf den Turn nie stoppen
237
+ }
238
+ }
185
239
  if (!decision.inject)
186
240
  return '';
187
241
  // Credit footer only where a recall can change a decision mid-flight.
@@ -1 +1 @@
1
- {"version":3,"file":"ambient-cli.js","sourceRoot":"","sources":["../../src/ambient-cli.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,kFAAkF;AAClF,kFAAkF;AAClF,iFAAiF;AACjF,iFAAiF;AACjF,4EAA4E;AAC5E,EAAE;AACF,qCAAqC;AACrC,6EAA6E;AAC7E,uCAAuC;AACvC,kFAAkF;AAClF,mEAAmE;AACnE,gFAAgF;AAChF,gCAAgC;AAEhC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,cAAc,GAGf,MAAM,qBAAqB,CAAC;AAiB7B,qFAAqF;AACrF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAY,CAAC;QACzC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;YAAE,OAAO,CAAgB,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,wEAAwE;AACxE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;AAE3E;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,OAAoB;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,IAAI,kBAAkB,CAAC;IAC5D,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;QAC7B,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QAC5E,OAAO,0FAA0F,CAAC;IACpG,CAAC;IACD,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QACzE,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC;QAC/C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QAClE,OAAO,kDAAkD,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;IAC7E,CAAC;IACD,IAAI,KAAK,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,MAAM,CAAC;AAChB,CAAC;AASD,4EAA4E;AAC5E,8EAA8E;AAC9E,6DAA6D;AAC7D,MAAM,aAAa,GACjB,4KAA4K,CAAC;AAE/K;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAoB;IAClD,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC,CAAC,mCAAmC;IACrE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC;IAClF,OAAO;QACL,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC/B,OAAO,EAAE,SAAS;QAClB,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC1B,QAAQ,EAAE,OAAO;KAClB,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,SAAiB;IAC9D,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACtB,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,SAAS;QAAE,OAAO,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IACjC,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,OAAO,CAAC,SAAS,GAAG,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC;AACtF,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,iIAAiI,CAAC;AAEpI;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA0B,EAAE,gBAAgB,GAAG,KAAK;IACrF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,IAAI,GACR,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QACvD,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE;QAC3B,CAAC,CAAC,8DAA8D;YAC9D,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7D,OAAO,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,iBAAyB;IACtE,IAAI,CAAC,iBAAiB;QAAE,OAAO,EAAE,CAAC;IAClC,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,kBAAkB,EAAE;YAClB,aAAa,EAAE,KAAK;YACpB,iBAAiB;SAClB;KACF,CAAC,CAAC;AACL,CAAC;AAkBD,yEAAyE;AACzE,KAAK,UAAU,WAAW,CAAI,CAAa,EAAE,EAAU,EAAE,QAAW;IAClE,IAAI,KAAgD,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAI,CAAC,GAAG,EAAE,EAAE;QACrC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1C,CAAC;YAAS,CAAC;QACT,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,IAAiB;IAC7D,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,OAAO,CAAC,eAAe,IAAI,kBAAkB,CAAC;IAE5D,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC,CAAC,8DAA8D;IAE7F,4EAA4E;IAC5E,iEAAiE;IACjE,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,EAAE,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,iDAAiD;IACnD,CAAC;IAED,IAAI,UAAU,GAAsB,EAAE,CAAC;IACvC,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC,CAAC,uDAAuD;IACpE,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAErE,wEAAwE;IACxE,mFAAmF;IACnF,MAAM,SAAS,GAAG,KAAK,KAAK,kBAAkB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACnF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACpE,IAAI,CAAC,QAAQ,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEhC,sEAAsE;IACtE,MAAM,UAAU,GAAG,KAAK,KAAK,kBAAkB,IAAI,KAAK,KAAK,YAAY,CAAC;IAC1E,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAClE,MAAM,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC;IACjF,IAAI,CAAC;QACH,IAAI,CAAC,QAAQ,EAAE,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;IACD,OAAO,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"ambient-cli.js","sourceRoot":"","sources":["../../src/ambient-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { type AmbientDeps } from './ambient-cli.js';
2
+ import { type RecallMemory } from './ambient-recall.js';
3
+ export interface BuildAmbientDepsOptions {
4
+ /** Brain instance to recall from. Missing/empty → recall is a no-op (never calls `smartRecall`). */
5
+ instanceId?: string;
6
+ /** The raw `smart_recall` call (already scoped to `instanceId` by the caller). */
7
+ smartRecall: (query: string) => Promise<string>;
8
+ /** Optional trigger memory (dedupe + quiet-budget). Stateless when omitted. */
9
+ loadMemory?: () => RecallMemory | null | undefined;
10
+ saveMemory?: (m: RecallMemory) => void;
11
+ /** Auto-backoff probe (§6.3 guardrail 3). */
12
+ backoff?: () => boolean | Promise<boolean>;
13
+ /** Called with the estimated injected tokens whenever context is emitted. */
14
+ onInject?: (tokens: number, event: string) => void;
15
+ /** Hard latency budget for the whole recall step (ms). Default 3000. */
16
+ timeoutMs?: number;
17
+ }
18
+ /**
19
+ * Builds the ONE production `AmbientDeps` used by the `ambient-recall` CLI
20
+ * entrypoint. This is the exact mapping that used to be inlined at the call
21
+ * site: a miss-detection heuristic, a token-truncated summary, and a
22
+ * content-derived candidate id — wrapped around whatever `smartRecall`
23
+ * implementation the caller passes in (production: `handleTool('smart_recall', ...)`;
24
+ * tests: a stub).
25
+ */
26
+ export declare function buildAmbientDeps(opts: BuildAmbientDepsOptions): AmbientDeps;
@@ -0,0 +1,55 @@
1
+ // Ambient Recall — the production `AmbientDeps` factory.
2
+ //
3
+ // ambient-cli.ts (`runAmbient`) is dependency-injected on purpose so it can be
4
+ // unit-tested without a network. This module is the OTHER half of that seam:
5
+ // the one place that turns a raw `smart_recall` string reply into the
6
+ // `AmbientDeps.recall` candidates the gate understands — miss-detection,
7
+ // truncation and content-based IDs (PR #241 regression: the id must come from
8
+ // the summary, never a constant like 'ambient', or "same lesson twice" and
9
+ // "different lesson" become indistinguishable in `RecallMemory`).
10
+ //
11
+ // Previously this mapping lived inline at the `ambient-recall` CLI call site
12
+ // in index.ts. Moved here so the exact production wiring — not a hand-copied
13
+ // approximation of it — is what the seam test (SDK-003) exercises across
14
+ // multiple turns.
15
+ import { truncateToTokens } from './ambient-cli.js';
16
+ import { candidateIdFor } from './ambient-recall.js';
17
+ /** A brain reply that carries no usable lesson — same heuristic as smart_recall's own callers. */
18
+ const NO_LESSONS_RE = /No lessons found|no lessons|No matches found/i;
19
+ /** Below this length a `smart_recall` reply is boilerplate, not a real lesson. */
20
+ const MIN_LESSON_CHARS = 80;
21
+ /** Hard cap on how much of one recalled lesson may be injected (§6.2 budget). */
22
+ const MAX_LESSON_TOKENS = 500;
23
+ /**
24
+ * Builds the ONE production `AmbientDeps` used by the `ambient-recall` CLI
25
+ * entrypoint. This is the exact mapping that used to be inlined at the call
26
+ * site: a miss-detection heuristic, a token-truncated summary, and a
27
+ * content-derived candidate id — wrapped around whatever `smartRecall`
28
+ * implementation the caller passes in (production: `handleTool('smart_recall', ...)`;
29
+ * tests: a stub).
30
+ */
31
+ export function buildAmbientDeps(opts) {
32
+ const { instanceId, smartRecall, loadMemory, saveMemory, backoff, onInject, timeoutMs } = opts;
33
+ return {
34
+ timeoutMs: timeoutMs ?? 3000, // §6.3 guardrail 4: never stall the prompt on a slow brain
35
+ // Ambient injection budget: one relevance-ranked briefing, hard-capped.
36
+ // smart_recall already returns its hits best-first, so the capped head is
37
+ // the high-signal part. Per-lesson structured candidates are a future slice.
38
+ gate: { topK: 1, maxTokens: MAX_LESSON_TOKENS },
39
+ loadMemory,
40
+ saveMemory,
41
+ backoff,
42
+ onInject,
43
+ recall: async (query) => {
44
+ if (!instanceId)
45
+ return [];
46
+ const text = String((await smartRecall(query)) ?? '');
47
+ const miss = text.length < MIN_LESSON_CHARS || NO_LESSONS_RE.test(text);
48
+ if (miss)
49
+ return [];
50
+ const summary = truncateToTokens(text, MAX_LESSON_TOKENS);
51
+ return [{ id: candidateIdFor(summary), summary, confidence: 0.9, score: 0.9 }];
52
+ },
53
+ };
54
+ }
55
+ //# sourceMappingURL=ambient-deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ambient-deps.js","sourceRoot":"","sources":["../../src/ambient-deps.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,sEAAsE;AACtE,yEAAyE;AACzE,8EAA8E;AAC9E,2EAA2E;AAC3E,kEAAkE;AAClE,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,yEAAyE;AACzE,kBAAkB;AAElB,OAAO,EAAE,gBAAgB,EAAoB,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAqB,MAAM,qBAAqB,CAAC;AAExE,kGAAkG;AAClG,MAAM,aAAa,GAAG,+CAA+C,CAAC;AAEtE,kFAAkF;AAClF,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,iFAAiF;AACjF,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAkB9B;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAA6B;IAC5D,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC/F,OAAO;QACL,SAAS,EAAE,SAAS,IAAI,IAAI,EAAE,2DAA2D;QACzF,wEAAwE;QACxE,0EAA0E;QAC1E,6EAA6E;QAC7E,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE;QAC/C,UAAU;QACV,UAAU;QACV,OAAO;QACP,QAAQ;QACR,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,IAAI,CAAC,UAAU;gBAAE,OAAO,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACtD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,gBAAgB,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACxE,IAAI,IAAI;gBAAE,OAAO,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;YAC1D,OAAO,CAAC,EAAE,EAAE,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACjF,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type RecallMemory } from './ambient-recall.js';
2
+ /**
3
+ * Loads the persisted {@link RecallMemory} from `<dir>/ambient-memory.json`.
4
+ * Falls back to {@link emptyMemory} when the file is missing, corrupt, larger
5
+ * than 64KB, or structurally invalid. Never throws.
6
+ */
7
+ export declare function loadAmbientMemory(dir?: string): RecallMemory;
8
+ /**
9
+ * Persists `memory` to `<dir>/ambient-memory.json` via write-then-rename so a
10
+ * crash mid-write never corrupts the file for the next read. Never throws.
11
+ */
12
+ export declare function saveAmbientMemory(memory: RecallMemory, dir?: string): void;
@@ -0,0 +1,67 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { emptyMemory } from './ambient-recall.js';
4
+ const MEMORY_FILE_NAME = 'ambient-memory.json';
5
+ const MAX_MEMORY_BYTES = 64 * 1024;
6
+ function defaultAmbientMemoryDir() {
7
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '.';
8
+ return resolve(home, '.cachly');
9
+ }
10
+ function isFiniteNumberRecord(value) {
11
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
12
+ return false;
13
+ return Object.values(value).every((n) => typeof n === 'number' && Number.isFinite(n));
14
+ }
15
+ function isValidMemory(value) {
16
+ if (typeof value !== 'object' || value === null)
17
+ return false;
18
+ const m = value;
19
+ if (typeof m.turn !== 'number' || !Number.isFinite(m.turn))
20
+ return false;
21
+ if (!isFiniteNumberRecord(m.lastInjectedTurn))
22
+ return false;
23
+ if (!Array.isArray(m.injectionTurns))
24
+ return false;
25
+ if (!m.injectionTurns.every((n) => typeof n === 'number' && Number.isFinite(n)))
26
+ return false;
27
+ return true;
28
+ }
29
+ /**
30
+ * Loads the persisted {@link RecallMemory} from `<dir>/ambient-memory.json`.
31
+ * Falls back to {@link emptyMemory} when the file is missing, corrupt, larger
32
+ * than 64KB, or structurally invalid. Never throws.
33
+ */
34
+ export function loadAmbientMemory(dir = defaultAmbientMemoryDir()) {
35
+ try {
36
+ const raw = readFileSync(resolve(dir, MEMORY_FILE_NAME), 'utf-8');
37
+ if (Buffer.byteLength(raw, 'utf-8') > MAX_MEMORY_BYTES)
38
+ return emptyMemory();
39
+ const parsed = JSON.parse(raw);
40
+ if (!isValidMemory(parsed))
41
+ return emptyMemory();
42
+ return {
43
+ turn: parsed.turn,
44
+ lastInjectedTurn: { ...parsed.lastInjectedTurn },
45
+ injectionTurns: [...parsed.injectionTurns],
46
+ };
47
+ }
48
+ catch {
49
+ return emptyMemory();
50
+ }
51
+ }
52
+ /**
53
+ * Persists `memory` to `<dir>/ambient-memory.json` via write-then-rename so a
54
+ * crash mid-write never corrupts the file for the next read. Never throws.
55
+ */
56
+ export function saveAmbientMemory(memory, dir = defaultAmbientMemoryDir()) {
57
+ try {
58
+ mkdirSync(dir, { recursive: true });
59
+ const path = resolve(dir, MEMORY_FILE_NAME);
60
+ const tmp = `${path}.tmp`;
61
+ writeFileSync(tmp, JSON.stringify(memory), 'utf-8');
62
+ renameSync(tmp, path);
63
+ }
64
+ catch {
65
+ }
66
+ }
67
+ //# sourceMappingURL=ambient-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ambient-memory.js","sourceRoot":"","sources":["../../src/ambient-memory.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,12 @@ export interface LessonCandidate {
7
7
  /** Semantic similarity of the lesson to the current prompt, [0,1]. */
8
8
  score: number;
9
9
  severity?: 'critical' | 'major' | 'minor';
10
+ /** Wann die Lektion gelernt wurde (ISO) — Grundlage des Belegs. */
11
+ learnedAt?: string;
12
+ /** Dateien, an denen sie entstand — der konkreteste Beleg. */
13
+ files?: string[];
14
+ /** Wie es damals ausging. */
15
+ outcome?: "success" | "failure" | "partial";
10
16
  }
11
17
  export interface GateOptions {
12
18
  /** Minimum brain confidence to be eligible. */
@@ -19,6 +25,12 @@ export interface GateOptions {
19
25
  maxTokens: number;
20
26
  }
21
27
  export declare const DEFAULT_GATE: GateOptions;
28
+ /**
29
+ * Derives a content-based candidate ID from a summary string. Enables
30
+ * ID-based deduplication to suppress "the same advice" while allowing
31
+ * "different advice" through. See PR #241.
32
+ */
33
+ export declare function candidateIdFor(summary: string): string;
22
34
  /** Rough token estimate (~4 chars/token) — good enough for a per-turn budget. */
23
35
  export declare function estimateTokens(text: string): number;
24
36
  /**
@@ -57,3 +69,59 @@ export declare function netBalance(records: TurnRecord[]): NetBalance;
57
69
  * there is enough signal (>= minTurns) so early noise doesn't trip it.
58
70
  */
59
71
  export declare function shouldBackoff(records: TurnRecord[], windowN?: number, minTurns?: number): boolean;
72
+ /** Wie riskant ist der Moment — bestimmt, wie eifrig erinnert werden darf. */
73
+ export type PromptRisk = 'high' | 'normal' | 'low';
74
+ /**
75
+ * Risikoeinschätzung des Auslösemoments. Sie verschiebt die Schwellen, statt
76
+ * eine zweite, konkurrierende Entscheidung zu treffen — eine Stellschraube
77
+ * weniger, und der Gate bleibt die einzige Wahrheit über Relevanz.
78
+ */
79
+ export declare function promptRisk(prompt: string): PromptRisk;
80
+ /** Schwellen-Anpassung je Risiko (klein gehalten und bewusst begründet). */
81
+ export declare function gateForRisk(risk: PromptRisk, base?: GateOptions): GateOptions;
82
+ export interface TimingOptions {
83
+ /** Dieselbe Lektion frühestens nach so vielen Turns erneut. */
84
+ repeatCooldownTurns: number;
85
+ /** Höchstzahl Einwürfe im gleitenden Fenster. */
86
+ maxInjectionsPerWindow: number;
87
+ /** Größe des gleitenden Fensters in Turns. */
88
+ windowTurns: number;
89
+ /** Mindestruhe unmittelbar nach einem Einwurf (in Turns). */
90
+ minSilenceTurns: number;
91
+ }
92
+ export declare const DEFAULT_TIMING: TimingOptions;
93
+ /** Gedächtnis über die letzten Einwürfe — vom Aufrufer gehalten und übergeben. */
94
+ export interface RecallMemory {
95
+ /** Aktueller Turn-Zähler (monoton steigend). */
96
+ turn: number;
97
+ /** Lektions-ID → Turn des letzten Einwurfs. */
98
+ lastInjectedTurn: Record<string, number>;
99
+ /** Turns, in denen etwas eingeworfen wurde (aufsteigend). */
100
+ injectionTurns: number[];
101
+ }
102
+ export declare function emptyMemory(): RecallMemory;
103
+ export type TimingReason = 'quiet-budget' | 'silence-window' | 'all-duplicates';
104
+ export interface RecallDecision extends GateDecision {
105
+ risk: PromptRisk;
106
+ /** Kandidaten, die nur wegen der Abklingzeit entfielen (Diagnose/Telemetrie). */
107
+ suppressedDuplicates: string[];
108
+ timingReason?: TimingReason;
109
+ }
110
+ /**
111
+ * Die vollständige Entscheidung: Relevanz (Gate) UND Zeitpunkt (Timing).
112
+ *
113
+ * Reihenfolge ist Absicht: erst die billigen Ausschlüsse (Ruhe, Budget), dann
114
+ * Dedupe, erst zuletzt der teure Relevanz-Gate — so kostet ein unterdrückter
115
+ * Turn praktisch nichts.
116
+ *
117
+ * Die Funktion ist seiteneffektfrei; `commitInjection` schreibt das Gedächtnis
118
+ * fort, damit Aufrufer selbst entscheiden, ob ein Vorschlag wirklich gezeigt
119
+ * wurde (z. B. bei abgebrochenem Turn).
120
+ */
121
+ export declare function decideRecall(prompt: string, candidates: LessonCandidate[], memory: RecallMemory, gateOverrides?: Partial<GateOptions>, timingOverrides?: Partial<TimingOptions>): RecallDecision;
122
+ /**
123
+ * Schreibt das Gedächtnis nach einem TATSÄCHLICH gezeigten Einwurf fort.
124
+ * Der Turn-Zähler wird immer erhöht — auch ohne Einwurf, sonst laufen
125
+ * Abklingzeit und Fenster nicht weiter.
126
+ */
127
+ export declare function commitInjection(memory: RecallMemory, decision: RecallDecision, timingOverrides?: Partial<TimingOptions>): RecallMemory;
@@ -9,6 +9,7 @@
9
9
  //
10
10
  // This module is pure and dependency-free so it is exhaustively unit-tested; the
11
11
  // hook scripts + telemetry layer build on top of it.
12
+ import { createHash } from 'node:crypto';
12
13
  // Deliberately conservative defaults: it is far cheaper to skip a marginal
13
14
  // lesson than to erode the "−60% tokens" credibility by injecting noise.
14
15
  export const DEFAULT_GATE = {
@@ -17,6 +18,15 @@ export const DEFAULT_GATE = {
17
18
  topK: 3,
18
19
  maxTokens: 240,
19
20
  };
21
+ /**
22
+ * Derives a content-based candidate ID from a summary string. Enables
23
+ * ID-based deduplication to suppress "the same advice" while allowing
24
+ * "different advice" through. See PR #241.
25
+ */
26
+ export function candidateIdFor(summary) {
27
+ const hash = createHash('sha256').update(summary).digest('hex');
28
+ return hash.slice(0, 16);
29
+ }
20
30
  /** Rough token estimate (~4 chars/token) — good enough for a per-turn budget. */
21
31
  export function estimateTokens(text) {
22
32
  return Math.ceil(text.trim().length / 4);
@@ -93,4 +103,119 @@ export function shouldBackoff(records, windowN = 20, minTurns = 8) {
93
103
  return false;
94
104
  return netBalance(recent).net < 0;
95
105
  }
106
+ // Schritte, die man nicht zurücknehmen kann: hier ist eine verpasste Lektion
107
+ // teuer, ein überflüssiger Hinweis dagegen billig.
108
+ const RISKY_RE = /\b(deploy\w*|migrat\w*|rollback|revert|drop\s+(table|database|schema)|truncate|rm\s+-rf|force[- ]push|prune|restore|rotat\w*|secret\w*|credential\w*|production|prod|live|kunde\w*|customer\w*|delete|loesch\w*|lösch\w*)\b/i;
109
+ // Reine Bestätigungen/Fortsetzungen: hier beginnt keine neue Arbeit.
110
+ const CONTINUATION_RE = /^(ok(ay)?|ja|nein|weiter|passt|danke|thanks|go|los|mach|jup|yes|no|sure|next)\b[\s.!]*$/i;
111
+ /**
112
+ * Risikoeinschätzung des Auslösemoments. Sie verschiebt die Schwellen, statt
113
+ * eine zweite, konkurrierende Entscheidung zu treffen — eine Stellschraube
114
+ * weniger, und der Gate bleibt die einzige Wahrheit über Relevanz.
115
+ */
116
+ export function promptRisk(prompt) {
117
+ const p = prompt.trim();
118
+ if (CONTINUATION_RE.test(p))
119
+ return 'low';
120
+ if (RISKY_RE.test(p))
121
+ return 'high';
122
+ return 'normal';
123
+ }
124
+ /** Schwellen-Anpassung je Risiko (klein gehalten und bewusst begründet). */
125
+ export function gateForRisk(risk, base = DEFAULT_GATE) {
126
+ if (risk === 'high') {
127
+ // Vor irreversiblen Schritten darf eine etwas schwächere Übereinstimmung
128
+ // reichen — die vermiedene Katastrophe ist jeden Token wert.
129
+ return { ...base, minScore: Math.max(0, base.minScore - 0.07), topK: base.topK, maxTokens: base.maxTokens + 120 };
130
+ }
131
+ if (risk === 'low') {
132
+ // Im Geplauder muss es sehr deutlich sein, sonst lieber schweigen.
133
+ return { ...base, minScore: Math.min(1, base.minScore + 0.1), topK: 1, maxTokens: Math.min(base.maxTokens, 120) };
134
+ }
135
+ return { ...base };
136
+ }
137
+ // Ein Einwurf pro ~3 Turns im Schnitt: oft genug, um zu helfen, selten genug,
138
+ // dass er auffällt, wenn er kommt.
139
+ export const DEFAULT_TIMING = {
140
+ repeatCooldownTurns: 12,
141
+ maxInjectionsPerWindow: 3,
142
+ windowTurns: 10,
143
+ minSilenceTurns: 1,
144
+ };
145
+ export function emptyMemory() {
146
+ return { turn: 0, lastInjectedTurn: {}, injectionTurns: [] };
147
+ }
148
+ /**
149
+ * Die vollständige Entscheidung: Relevanz (Gate) UND Zeitpunkt (Timing).
150
+ *
151
+ * Reihenfolge ist Absicht: erst die billigen Ausschlüsse (Ruhe, Budget), dann
152
+ * Dedupe, erst zuletzt der teure Relevanz-Gate — so kostet ein unterdrückter
153
+ * Turn praktisch nichts.
154
+ *
155
+ * Die Funktion ist seiteneffektfrei; `commitInjection` schreibt das Gedächtnis
156
+ * fort, damit Aufrufer selbst entscheiden, ob ein Vorschlag wirklich gezeigt
157
+ * wurde (z. B. bei abgebrochenem Turn).
158
+ */
159
+ export function decideRecall(prompt, candidates, memory, gateOverrides = {}, timingOverrides = {}) {
160
+ const t = { ...DEFAULT_TIMING, ...timingOverrides };
161
+ const risk = promptRisk(prompt);
162
+ const none = (reason, timingReason) => ({
163
+ inject: false, reason, selected: [], tokens: 0, risk,
164
+ suppressedDuplicates: [], timingReason,
165
+ });
166
+ if (isTrivialPrompt(prompt))
167
+ return none('trivial-skip');
168
+ // Mindestruhe nach dem letzten Einwurf.
169
+ const last = memory.injectionTurns[memory.injectionTurns.length - 1];
170
+ if (last !== undefined && memory.turn - last <= t.minSilenceTurns) {
171
+ return none('no-candidate-passed-gate', 'silence-window');
172
+ }
173
+ // Ruhe-Budget im gleitenden Fenster.
174
+ const inWindow = memory.injectionTurns.filter((n) => memory.turn - n < t.windowTurns).length;
175
+ if (inWindow >= t.maxInjectionsPerWindow) {
176
+ return none('no-candidate-passed-gate', 'quiet-budget');
177
+ }
178
+ // Dedupe: was gerade erst gesagt wurde, wird nicht wiederholt.
179
+ const suppressedDuplicates = [];
180
+ const fresh = candidates.filter((c) => {
181
+ const seen = memory.lastInjectedTurn[c.id];
182
+ if (seen !== undefined && memory.turn - seen < t.repeatCooldownTurns) {
183
+ suppressedDuplicates.push(c.id);
184
+ return false;
185
+ }
186
+ return true;
187
+ });
188
+ if (candidates.length > 0 && fresh.length === 0) {
189
+ return { ...none('no-candidate-passed-gate', 'all-duplicates'), suppressedDuplicates };
190
+ }
191
+ const gate = selectInjectable(prompt, fresh, { ...gateForRisk(risk), ...gateOverrides });
192
+ return { ...gate, risk, suppressedDuplicates };
193
+ }
194
+ /**
195
+ * Schreibt das Gedächtnis nach einem TATSÄCHLICH gezeigten Einwurf fort.
196
+ * Der Turn-Zähler wird immer erhöht — auch ohne Einwurf, sonst laufen
197
+ * Abklingzeit und Fenster nicht weiter.
198
+ */
199
+ export function commitInjection(memory, decision, timingOverrides = {}) {
200
+ const t = { ...DEFAULT_TIMING, ...timingOverrides };
201
+ const next = {
202
+ turn: memory.turn + 1,
203
+ lastInjectedTurn: { ...memory.lastInjectedTurn },
204
+ injectionTurns: [...memory.injectionTurns],
205
+ };
206
+ if (decision.inject) {
207
+ for (const c of decision.selected)
208
+ next.lastInjectedTurn[c.id] = memory.turn;
209
+ next.injectionTurns.push(memory.turn);
210
+ }
211
+ // Nach Ablauf der Fenster sind Eintraege bedeutungslos — sonst waechst das
212
+ // Gedaechtnis in langen Sitzungen unbegrenzt mit.
213
+ const horizon = memory.turn - Math.max(t.windowTurns, t.repeatCooldownTurns);
214
+ next.injectionTurns = next.injectionTurns.filter((n) => n >= horizon);
215
+ for (const [id, turn] of Object.entries(next.lastInjectedTurn)) {
216
+ if (turn < horizon)
217
+ delete next.lastInjectedTurn[id];
218
+ }
219
+ return next;
220
+ }
96
221
  //# sourceMappingURL=ambient-recall.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ambient-recall.js","sourceRoot":"","sources":["../../src/ambient-recall.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ambient-recall.js","sourceRoot":"","sources":["../../src/ambient-recall.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,18 @@ export const CONFIDENCE_WARN_DAYS = Number(process.env.CACHLY_CONFIDENCE_WARN_DA
4
4
  export const CONFIDENCE_STALE_DAYS = Number(process.env.CACHLY_CONFIDENCE_STALE_DAYS ?? 10);
5
5
  export const CONFIDENCE_WARN_VALUE = 0.7;
6
6
  export const CONFIDENCE_STALE_VALUE = 0.5;
7
+ // ── One of THREE deliberately-different age/decay curves. Do NOT unify them. ──
8
+ // This one (SHARP: 0.7@5d, 0.5@10d) is a user-facing *staleness warning* — it
9
+ // drives the recall_best_solution freshness badge and brain_predict, where the
10
+ // point is to shout "verify before applying" once a lesson is old.
11
+ // • Ranking uses a SEPARATE, gentle curve — search.ts `recencyBoost`
12
+ // (0.5^(age/7)+0.5, range [0.5,1.5]). It is bench-tuned. Feeding THIS sharp
13
+ // curve into the recall rerank regressed every Cachly-Bench floor (home P@1
14
+ // 92→77%) and was reverted — see PR #228. Relevant lessons are often days
15
+ // /weeks old; penalising age hard in ranking buries the right answer.
16
+ // • The knowledge_decay *report* uses a THIRD, points-based curve
17
+ // (advanced.ts) that also folds in recall_count + outcome.
18
+ // Three purposes, three curves — that divergence is intentional, not a bug.
7
19
  /** Calculate current confidence for a lesson based on how long since last verified. */
8
20
  export function calculateConfidence(lesson) {
9
21
  const ref = lesson.verified_at ?? lesson.ts;
@@ -1 +1 @@
1
- {"version":3,"file":"confidence.js","sourceRoot":"","sources":["../../src/confidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAK,CAAC,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;AAC5F,MAAM,CAAC,MAAM,qBAAqB,GAAI,GAAG,CAAC;AAC1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,MAAmE;IACrG,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;IACjC,IAAI,OAAO,IAAI,qBAAqB;QAAE,OAAO,sBAAsB,CAAC;IACpE,IAAI,OAAO,IAAI,oBAAoB;QAAG,OAAO,qBAAqB,CAAC;IACnE,OAAO,GAAG,GAAG,CAAC,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,GAAG,GAAG,qBAAqB,CAAC,CAAC;AAChF,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,OAAe;IACjE,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAClC,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;IACxI,OAAO,aAAa,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;AACnH,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,oBAAoB,GAAyD;IACxF,QAAQ,EAAG,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAiB,IAAI,EAAE,2BAA2B,EAAE;IACvF,MAAM,EAAK,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAiB,IAAI,EAAE,yBAAyB,EAAE;IACrF,OAAO,EAAI,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAiB,IAAI,EAAE,oDAAoD,EAAE;IAChH,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAA2B,IAAI,EAAE,wEAAwE,EAAE;IACpI,QAAQ,EAAG,EAAE,QAAQ,EAAE,EAAE,EAA2B,IAAI,EAAE,gDAAgD,EAAE;CAC7G,CAAC;AAEF,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"confidence.js","sourceRoot":"","sources":["../../src/confidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAK,CAAC,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;AAC5F,MAAM,CAAC,MAAM,qBAAqB,GAAI,GAAG,CAAC;AAC1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC;AAE1C,iFAAiF;AACjF,8EAA8E;AAC9E,+EAA+E;AAC/E,mEAAmE;AACnE,uEAAuE;AACvE,gFAAgF;AAChF,gFAAgF;AAChF,8EAA8E;AAC9E,0EAA0E;AAC1E,oEAAoE;AACpE,+DAA+D;AAC/D,4EAA4E;AAC5E,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,MAAmE;IACrG,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;IACjC,IAAI,OAAO,IAAI,qBAAqB;QAAE,OAAO,sBAAsB,CAAC;IACpE,IAAI,OAAO,IAAI,oBAAoB;QAAG,OAAO,qBAAqB,CAAC;IACnE,OAAO,GAAG,GAAG,CAAC,OAAO,GAAG,oBAAoB,CAAC,GAAG,CAAC,GAAG,GAAG,qBAAqB,CAAC,CAAC;AAChF,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,eAAe,CAAC,UAAkB,EAAE,OAAe;IACjE,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IAClC,IAAI,UAAU,IAAI,GAAG;QAAE,OAAO,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,CAAC;IACxI,OAAO,aAAa,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,CAAC,UAAU,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB,CAAC;AACnH,CAAC;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,oBAAoB,GAAyD;IACxF,QAAQ,EAAG,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAiB,IAAI,EAAE,2BAA2B,EAAE;IACvF,MAAM,EAAK,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAiB,IAAI,EAAE,yBAAyB,EAAE;IACrF,OAAO,EAAI,EAAE,QAAQ,EAAE,CAAC,UAAU,CAAC,EAAiB,IAAI,EAAE,oDAAoD,EAAE;IAChH,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAA2B,IAAI,EAAE,wEAAwE,EAAE;IACpI,QAAQ,EAAG,EAAE,QAAQ,EAAE,EAAE,EAA2B,IAAI,EAAE,gDAAgD,EAAE;CAC7G,CAAC;AAEF,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC"}
@@ -429,6 +429,13 @@ export async function handleAdvancedTool(name, args, getConnection, _apiFetch) {
429
429
  continue;
430
430
  const age_days = Math.floor(ageMs / 86400000);
431
431
  const recalls = l.recall_count ?? 0;
432
+ // The THIRD deliberately-different decay curve (see confidence.ts /
433
+ // search.ts recencyBoost). This one is a *report-only* health score on
434
+ // a 0–100 scale that also folds in recall_count and outcome — it is not
435
+ // a ranking signal and not the freshness badge. Kept separate on
436
+ // purpose; do not "unify" the three (a single curve regresses either
437
+ // the Cachly-Bench ranking floors — see PR #228 — or these reports).
438
+ //
432
439
  // Confidence formula:
433
440
  // base = 100 → decays by 1pt/day after 7 days, floored at 5
434
441
  // boost: +5 per recall, capped at +50