@cachly-dev/mcp-server 0.10.123 → 0.10.125

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +7 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/src/aussetzer.d.ts +67 -0
  5. package/dist/src/aussetzer.js +82 -0
  6. package/dist/src/aussetzer.js.map +1 -0
  7. package/dist/src/autor-abzeichen.d.ts +41 -0
  8. package/dist/src/autor-abzeichen.js +56 -0
  9. package/dist/src/autor-abzeichen.js.map +1 -0
  10. package/dist/src/autor.d.ts +101 -0
  11. package/dist/src/autor.js +136 -0
  12. package/dist/src/autor.js.map +1 -0
  13. package/dist/src/bauabdruck-daten.generiert.json +5 -0
  14. package/dist/src/bauabdruck.d.ts +38 -0
  15. package/dist/src/bauabdruck.js +93 -0
  16. package/dist/src/bauabdruck.js.map +1 -0
  17. package/dist/src/bedeutung.d.ts +175 -0
  18. package/dist/src/bedeutung.js +254 -0
  19. package/dist/src/bedeutung.js.map +1 -0
  20. package/dist/src/brain-export.d.ts +24 -0
  21. package/dist/src/brain-export.js +31 -1
  22. package/dist/src/brain-export.js.map +1 -1
  23. package/dist/src/deutsch.d.ts +98 -0
  24. package/dist/src/deutsch.js +223 -0
  25. package/dist/src/deutsch.js.map +1 -0
  26. package/dist/src/eingaenge-nachruesten.d.ts +41 -0
  27. package/dist/src/eingaenge-nachruesten.js +290 -0
  28. package/dist/src/eingaenge-nachruesten.js.map +1 -0
  29. package/dist/src/eingaenge.d.ts +135 -0
  30. package/dist/src/eingaenge.js +228 -0
  31. package/dist/src/eingaenge.js.map +1 -0
  32. package/dist/src/geliefert.d.ts +63 -0
  33. package/dist/src/geliefert.js +114 -0
  34. package/dist/src/geliefert.js.map +1 -0
  35. package/dist/src/handlers/advanced.js +7 -3
  36. package/dist/src/handlers/advanced.js.map +1 -1
  37. package/dist/src/handlers/brain.d.ts +19 -0
  38. package/dist/src/handlers/brain.js +492 -48
  39. package/dist/src/handlers/brain.js.map +1 -1
  40. package/dist/src/handlers/instances.d.ts +24 -0
  41. package/dist/src/handlers/instances.js +49 -1
  42. package/dist/src/handlers/instances.js.map +1 -1
  43. package/dist/src/handlers/team.js +105 -0
  44. package/dist/src/handlers/team.js.map +1 -1
  45. package/dist/src/index.d.ts +20 -0
  46. package/dist/src/index.js +282 -36
  47. package/dist/src/index.js.map +1 -1
  48. package/dist/src/lesson-preview.d.ts +81 -0
  49. package/dist/src/lesson-preview.js +133 -0
  50. package/dist/src/lesson-preview.js.map +1 -0
  51. package/dist/src/rangfolge.d.ts +169 -0
  52. package/dist/src/rangfolge.js +214 -0
  53. package/dist/src/rangfolge.js.map +1 -0
  54. package/dist/src/raumkorrektur.d.ts +100 -0
  55. package/dist/src/raumkorrektur.js +197 -0
  56. package/dist/src/raumkorrektur.js.map +1 -0
  57. package/dist/src/recall-tiefe.d.ts +48 -0
  58. package/dist/src/recall-tiefe.js +42 -0
  59. package/dist/src/recall-tiefe.js.map +1 -0
  60. package/dist/src/rerank.d.ts +0 -14
  61. package/dist/src/rerank.js +39 -2
  62. package/dist/src/rerank.js.map +1 -1
  63. package/dist/src/search.d.ts +56 -17
  64. package/dist/src/search.js +335 -73
  65. package/dist/src/search.js.map +1 -1
  66. package/dist/src/seltenheitsbestand.d.ts +53 -0
  67. package/dist/src/seltenheitsbestand.js +92 -0
  68. package/dist/src/seltenheitsbestand.js.map +1 -0
  69. package/dist/src/spuren.d.ts +97 -0
  70. package/dist/src/spuren.js +150 -0
  71. package/dist/src/spuren.js.map +1 -0
  72. package/dist/src/tools.js +1 -1
  73. package/dist/src/tools.js.map +1 -1
  74. package/dist/src/vorspann.d.ts +73 -0
  75. package/dist/src/vorspann.js +146 -0
  76. package/dist/src/vorspann.js.map +1 -0
  77. package/dist/src/werkzeug-nutzung.d.ts +62 -0
  78. package/dist/src/werkzeug-nutzung.js +72 -0
  79. package/dist/src/werkzeug-nutzung.js.map +1 -0
  80. package/dist/src/wertbeitrag.d.ts +57 -0
  81. package/dist/src/wertbeitrag.js +73 -0
  82. package/dist/src/wertbeitrag.js.map +1 -0
  83. package/glama.json +1 -1
  84. package/package.json +12 -4
  85. package/server.json +2 -2
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Eingänge: die zweite Tür zu einer Lektion.
3
+ *
4
+ * ── Was gemessen wurde, und was daraus folgt ────────────────────────────────
5
+ *
6
+ * Am 20.08.2026 an 499 echten Lektionen mit zwei unabhängigen 100-Fragen-Sätzen
7
+ * (`.agent/cachly/tor0-tor1-ergebnis.md`):
8
+ *
9
+ * Findequote@3, frischer Prüfsatz
10
+ * heute, nur Volltextvektor 52 %
11
+ * Eingänge in der Vorauswahl 56 %
12
+ * dazu das Merkmal "bester Eingang" im Sortierer 58 %
13
+ * alle Eingänge AUSSER den Fehlertexten 52 % ← null Gewinn
14
+ *
15
+ * Die letzte Zeile ist die wichtigste: von fünf ausprobierten Türarten trägt
16
+ * GENAU EINE. Der erste Satz von what_worked bringt nichts, die Übersetzung
17
+ * bringt nichts, der Themenname steckt längst im Sortierer. Deshalb baut
18
+ * dieses Modul nur die Fehlertexte — alles andere wäre Speicher ohne Wirkung.
19
+ *
20
+ * ── Warum ausgerechnet der Fehlertext ───────────────────────────────────────
21
+ *
22
+ * Er ist ein scharfer Schlüssel. Nutzer fügen Meldungen wörtlich ein; passt der
23
+ * Schlüssel, zieht er die Lektion nach ganz vorn. Der Preis steht in derselben
24
+ * Messung und wird hier offen genannt: dieselben Eingänge SENKEN die Decke von
25
+ * 89 auf 86 % — sie ziehen auch falsche Lektionen in die Vorauswahl. Für den
26
+ * Nutzer zählt trotzdem die Findequote: er sieht drei Lektionen, nicht 25.
27
+ *
28
+ * ── Eine Wahrheit ──────────────────────────────────────────────────────────
29
+ *
30
+ * Die Extraktion stand zuerst im Messwerkzeug (src/bench/eingaenge-b.ts). Sie
31
+ * steht jetzt HIER, und der Bench importiert sie. Zwei Fassungen desselben
32
+ * regulären Ausdrucks wären die Fehlerklasse, die dieses Haus am häufigsten
33
+ * trifft — und die Messung würde etwas anderes prüfen als das Produkt.
34
+ */
35
+ import type { Redis } from 'ioredis';
36
+ /** Wo die Eingänge einer Lektion liegen: EIN Hash je Lektion, neben ihr. */
37
+ export declare const EINGANG_PRAEFIX = "cachly:lesson:eing:";
38
+ /** Höchstens so viele Eingänge je Lektion. Mehr Türen kosten Decke. */
39
+ export declare const EINGAENGE_JE_LEKTION = 3;
40
+ /**
41
+ * Die Feld-Markierungen, die im Bestand IM Text stehen.
42
+ *
43
+ * Gemessen: `what_worked` enthält bei vielen Lektionen wörtlich
44
+ * `</what_worked>\n<what_failed>`. Wer das nicht wegnimmt, bettet Feldnamen ein
45
+ * und wundert sich über die Nähe zwischen zwei völlig fremden Lektionen.
46
+ */
47
+ export declare function ohneMarkierungen(text: string): string;
48
+ /**
49
+ * Wörtliche Fehlertexte, Codes und Kernsätze aus einer Lektion.
50
+ *
51
+ * Fünf Muster, alle an echten Lektionen belegt:
52
+ * 1. der GROSSGESCHRIEBENE Kernsatz am Anfang (Schreibregel des Hauses)
53
+ * 2. alles in Anführungszeichen oder Rückwärts-Haken
54
+ * 3. Fehlerklassen und Meldungen samt Umfeld (Error, panic, FATAL, HTTP 5xx)
55
+ * 4. Systemfehler-Kürzel MIT Umfeld — "EACCES" allein ist als Vektor blind
56
+ * 5. englische Meldungssätze mitten im deutschen Text
57
+ */
58
+ export declare function fehlertexteAus(text: string, hoechstens?: number): string[];
59
+ /** Der Text einer Lektion, aus dem Eingänge gezogen werden. */
60
+ export declare function eingangsText(lektion: Record<string, unknown>): string;
61
+ /**
62
+ * Schreibt die Eingänge einer Lektion — als EIN Hash, nicht als n Schlüssel.
63
+ *
64
+ * Warum ein Hash: die Eingänge gehören zusammen und werden zusammen gelesen.
65
+ * Drei Schlüssel je Lektion wären bei 2000 Lektionen 6000 Schlüssel im SCAN.
66
+ *
67
+ * Der Hash wird zuerst gelöscht: hat sich der Text der Lektion geändert, sind
68
+ * die alten Fehlertexte falsch. Eine Lektion neu zu lernen darf keine Reste
69
+ * hinterlassen — das wäre eine zweite Wahrheit über denselben Text.
70
+ *
71
+ * Gibt zurück, wie viele Eingänge geschrieben wurden.
72
+ */
73
+ export declare function schreibeEingaenge(redis: Redis, topic: string, lektion: Record<string, unknown>, einbetten: (text: string) => Promise<number[] | null>): Promise<number>;
74
+ /**
75
+ * Die Eingänge aller Lektionen im Arbeitsspeicher.
76
+ *
77
+ * Baugleich zu `Vektorbestand` in bedeutung.ts und aus demselben Grund: bei
78
+ * jeder Frage aus dem Speicher zu holen wäre langsamer als der Wortabgleich,
79
+ * den wir gerade ersetzen. Der MCP-Server läuft lange, also wird einmal geladen
80
+ * und danach nur nachgetragen.
81
+ */
82
+ export declare class Eingangsbestand {
83
+ private readonly frischeMs;
84
+ private eingaenge;
85
+ private geladen;
86
+ constructor(frischeMs?: number);
87
+ get groesse(): number;
88
+ get anzahlEingaenge(): number;
89
+ /**
90
+ * @param praefixe welche Hashes zum Bestand gehoeren
91
+ *
92
+ * Zwei statt einem: die aus dem Text gezogenen Fehlertexte
93
+ * (`cachly:lesson:eing:`) und die aus der Nutzung gelernten Fragen
94
+ * (`cachly:lesson:pfad:`). Sie liegen getrennt, weil `schreibeEingaenge`
95
+ * seinen Hash vor jedem Schreiben loescht — laegen die gelernten Fragen
96
+ * dort, wuerde jedes erneute Lernen die gesammelte Erfahrung wegwerfen.
97
+ * Fuer die SUCHE sind sie dasselbe: Tueren zu derselben Lektion.
98
+ */
99
+ aktualisiere(redis: Redis, jetzt?: number, praefixe?: readonly string[]): Promise<void>;
100
+ /**
101
+ * Die Nähe zum BESTEN Eingang einer Lektion. -2, wenn sie keinen hat.
102
+ *
103
+ * Maximum, nicht Mittelwert: eine Lektion wird über ihre passendste Tür
104
+ * gefunden, und die anderen dürfen schlecht passen. Der Mittelwert würde
105
+ * Lektionen mit vielen Türen bestrafen — gemessen und belegt am 20.08.
106
+ */
107
+ besteNaehe(frage: number[], topic: string): number;
108
+ /** Die ähnlichsten Lektionen, je Lektion ihr bester Eingang. */
109
+ aehnlichste(frage: number[], anzahl: number): Array<{
110
+ topic: string;
111
+ naehe: number;
112
+ }>;
113
+ }
114
+ /**
115
+ * Das Gewicht des Merkmals "bester Eingang" im Sortierer.
116
+ *
117
+ * Gemessen am 20.08.2026: 0,3 / 0,5 / 0,8 / 1,3 ergeben 57 / 58 / 57 / 57 %
118
+ * Findequote@3. Das Merkmal ist also unempfindlich gegen sein Gewicht — wer
119
+ * hier weiterdreht, misst Rauschen. 0,5 ist die Mitte des flachen Bereichs.
120
+ */
121
+ export declare const EINGANG_GEWICHT = 0.5;
122
+ /**
123
+ * Das Urteil über die Deckung des Bedeutungsabgleichs.
124
+ *
125
+ * Als eigene Funktion, damit sie prüfbar ist: `brain_doctor` steckt in einem
126
+ * großen `switch`, und ein Wächter, den man nicht einzeln aufrufen kann, ist
127
+ * ein Wächter, dessen Nein niemand je gesehen hat.
128
+ *
129
+ * Die Schwelle liegt bei 90 Prozent und nicht bei 100: eine einzelne Lektion
130
+ * ohne Vektor ist ein Aussetzer beim Einbetten (gemessen 20.08.: 7 von 507,
131
+ * alle beim zweiten Lauf nachgeholt), kein Systemfehler. Null ist etwas ganz
132
+ * anderes — dann ist der Bedeutungsabgleich schlicht aus.
133
+ */
134
+ export type Deckungsurteil = 'aus' | 'luecke' | 'gut';
135
+ export declare function beurteileDeckung(lektionen: number, vektoren: number): Deckungsurteil;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Eingänge: die zweite Tür zu einer Lektion.
3
+ *
4
+ * ── Was gemessen wurde, und was daraus folgt ────────────────────────────────
5
+ *
6
+ * Am 20.08.2026 an 499 echten Lektionen mit zwei unabhängigen 100-Fragen-Sätzen
7
+ * (`.agent/cachly/tor0-tor1-ergebnis.md`):
8
+ *
9
+ * Findequote@3, frischer Prüfsatz
10
+ * heute, nur Volltextvektor 52 %
11
+ * Eingänge in der Vorauswahl 56 %
12
+ * dazu das Merkmal "bester Eingang" im Sortierer 58 %
13
+ * alle Eingänge AUSSER den Fehlertexten 52 % ← null Gewinn
14
+ *
15
+ * Die letzte Zeile ist die wichtigste: von fünf ausprobierten Türarten trägt
16
+ * GENAU EINE. Der erste Satz von what_worked bringt nichts, die Übersetzung
17
+ * bringt nichts, der Themenname steckt längst im Sortierer. Deshalb baut
18
+ * dieses Modul nur die Fehlertexte — alles andere wäre Speicher ohne Wirkung.
19
+ *
20
+ * ── Warum ausgerechnet der Fehlertext ───────────────────────────────────────
21
+ *
22
+ * Er ist ein scharfer Schlüssel. Nutzer fügen Meldungen wörtlich ein; passt der
23
+ * Schlüssel, zieht er die Lektion nach ganz vorn. Der Preis steht in derselben
24
+ * Messung und wird hier offen genannt: dieselben Eingänge SENKEN die Decke von
25
+ * 89 auf 86 % — sie ziehen auch falsche Lektionen in die Vorauswahl. Für den
26
+ * Nutzer zählt trotzdem die Findequote: er sieht drei Lektionen, nicht 25.
27
+ *
28
+ * ── Eine Wahrheit ──────────────────────────────────────────────────────────
29
+ *
30
+ * Die Extraktion stand zuerst im Messwerkzeug (src/bench/eingaenge-b.ts). Sie
31
+ * steht jetzt HIER, und der Bench importiert sie. Zwei Fassungen desselben
32
+ * regulären Ausdrucks wären die Fehlerklasse, die dieses Haus am häufigsten
33
+ * trifft — und die Messung würde etwas anderes prüfen als das Produkt.
34
+ */
35
+ import { kosinus, packe, entpacke } from './bedeutung.js';
36
+ /** Wo die Eingänge einer Lektion liegen: EIN Hash je Lektion, neben ihr. */
37
+ export const EINGANG_PRAEFIX = 'cachly:lesson:eing:';
38
+ /** Höchstens so viele Eingänge je Lektion. Mehr Türen kosten Decke. */
39
+ export const EINGAENGE_JE_LEKTION = 3;
40
+ /**
41
+ * Die Feld-Markierungen, die im Bestand IM Text stehen.
42
+ *
43
+ * Gemessen: `what_worked` enthält bei vielen Lektionen wörtlich
44
+ * `</what_worked>\n<what_failed>`. Wer das nicht wegnimmt, bettet Feldnamen ein
45
+ * und wundert sich über die Nähe zwischen zwei völlig fremden Lektionen.
46
+ */
47
+ export function ohneMarkierungen(text) {
48
+ return text.replace(/<\/?(what_worked|what_failed|context|topic)>/g, ' ').replace(/\s+/g, ' ').trim();
49
+ }
50
+ /**
51
+ * Wörtliche Fehlertexte, Codes und Kernsätze aus einer Lektion.
52
+ *
53
+ * Fünf Muster, alle an echten Lektionen belegt:
54
+ * 1. der GROSSGESCHRIEBENE Kernsatz am Anfang (Schreibregel des Hauses)
55
+ * 2. alles in Anführungszeichen oder Rückwärts-Haken
56
+ * 3. Fehlerklassen und Meldungen samt Umfeld (Error, panic, FATAL, HTTP 5xx)
57
+ * 4. Systemfehler-Kürzel MIT Umfeld — "EACCES" allein ist als Vektor blind
58
+ * 5. englische Meldungssätze mitten im deutschen Text
59
+ */
60
+ export function fehlertexteAus(text, hoechstens = EINGAENGE_JE_LEKTION) {
61
+ const t = ohneMarkierungen(text);
62
+ const aus = [];
63
+ const nimm = (s) => {
64
+ if (!s)
65
+ return;
66
+ const k = s.replace(/\s+/g, ' ').trim().replace(/^["'`„“]|["'`„“]$/g, '').trim();
67
+ if (k.length < 8 || k.length > 160)
68
+ return;
69
+ if (aus.some((x) => x.toLowerCase() === k.toLowerCase()))
70
+ return;
71
+ aus.push(k);
72
+ };
73
+ const gross = /^[^a-zäöüß]{15,200}?(?=[.:]|\s[a-zäöüß])/.exec(t);
74
+ if (gross && /[A-ZÄÖÜ]{3,}/.test(gross[0]))
75
+ nimm(gross[0]);
76
+ for (const m of t.matchAll(/[„"'`]([^„"'`\n]{8,160})[“"'`]/g))
77
+ nimm(m[1]);
78
+ for (const m of t.matchAll(/\b((?:[A-Z][a-zA-Z]*(?:Error|Exception)|panic:|FATAL|ERROR|Traceback|exit(?:ed with)? (?:code )?\d+|HTTP [45]\d{2}|status [45]\d{2})[^.\n]{0,90})/g))
79
+ nimm(m[1]);
80
+ for (const m of t.matchAll(/\b(E[A-Z]{3,10}\b[^.\n]{0,60})/g))
81
+ nimm(m[1]);
82
+ for (const m of t.matchAll(/\b((?:no such|not found|permission denied|connection refused|no space left|too many|already exists|cannot|failed to|unable to)[^.,;\n]{0,70})/gi))
83
+ nimm(m[1]);
84
+ return aus.slice(0, hoechstens);
85
+ }
86
+ /** Der Text einer Lektion, aus dem Eingänge gezogen werden. */
87
+ export function eingangsText(lektion) {
88
+ const s = (v) => (typeof v === 'string' ? v : '');
89
+ return `${s(lektion.what_worked)}\n${s(lektion.what_failed)}`;
90
+ }
91
+ /**
92
+ * Schreibt die Eingänge einer Lektion — als EIN Hash, nicht als n Schlüssel.
93
+ *
94
+ * Warum ein Hash: die Eingänge gehören zusammen und werden zusammen gelesen.
95
+ * Drei Schlüssel je Lektion wären bei 2000 Lektionen 6000 Schlüssel im SCAN.
96
+ *
97
+ * Der Hash wird zuerst gelöscht: hat sich der Text der Lektion geändert, sind
98
+ * die alten Fehlertexte falsch. Eine Lektion neu zu lernen darf keine Reste
99
+ * hinterlassen — das wäre eine zweite Wahrheit über denselben Text.
100
+ *
101
+ * Gibt zurück, wie viele Eingänge geschrieben wurden.
102
+ */
103
+ export async function schreibeEingaenge(redis, topic, lektion, einbetten) {
104
+ const texte = fehlertexteAus(eingangsText(lektion));
105
+ const schluessel = `${EINGANG_PRAEFIX}${topic}`;
106
+ if (texte.length === 0) {
107
+ // Kein Fehlertext in dieser Lektion — dann bekommt sie keinen Eingang.
108
+ // Eine leere Tür ist besser als eine erfundene.
109
+ await redis.del(schluessel);
110
+ return 0;
111
+ }
112
+ const felder = [];
113
+ for (const text of texte) {
114
+ const v = await einbetten(text);
115
+ if (v?.length)
116
+ felder.push(text.slice(0, 200), packe(v));
117
+ }
118
+ if (felder.length === 0)
119
+ return 0;
120
+ await redis.del(schluessel);
121
+ await redis.hset(schluessel, ...felder);
122
+ return felder.length / 2;
123
+ }
124
+ /**
125
+ * Die Eingänge aller Lektionen im Arbeitsspeicher.
126
+ *
127
+ * Baugleich zu `Vektorbestand` in bedeutung.ts und aus demselben Grund: bei
128
+ * jeder Frage aus dem Speicher zu holen wäre langsamer als der Wortabgleich,
129
+ * den wir gerade ersetzen. Der MCP-Server läuft lange, also wird einmal geladen
130
+ * und danach nur nachgetragen.
131
+ */
132
+ export class Eingangsbestand {
133
+ frischeMs;
134
+ eingaenge = new Map();
135
+ geladen = 0;
136
+ constructor(frischeMs = 60_000) {
137
+ this.frischeMs = frischeMs;
138
+ }
139
+ get groesse() { return this.eingaenge.size; }
140
+ get anzahlEingaenge() {
141
+ let n = 0;
142
+ for (const vs of this.eingaenge.values())
143
+ n += vs.length;
144
+ return n;
145
+ }
146
+ /**
147
+ * @param praefixe welche Hashes zum Bestand gehoeren
148
+ *
149
+ * Zwei statt einem: die aus dem Text gezogenen Fehlertexte
150
+ * (`cachly:lesson:eing:`) und die aus der Nutzung gelernten Fragen
151
+ * (`cachly:lesson:pfad:`). Sie liegen getrennt, weil `schreibeEingaenge`
152
+ * seinen Hash vor jedem Schreiben loescht — laegen die gelernten Fragen
153
+ * dort, wuerde jedes erneute Lernen die gesammelte Erfahrung wegwerfen.
154
+ * Fuer die SUCHE sind sie dasselbe: Tueren zu derselben Lektion.
155
+ */
156
+ async aktualisiere(redis, jetzt = Date.now(), praefixe = [EINGANG_PRAEFIX, 'cachly:lesson:pfad:']) {
157
+ if (this.eingaenge.size > 0 && jetzt - this.geladen < this.frischeMs)
158
+ return;
159
+ const frisch = new Map();
160
+ for (const praefix of praefixe) {
161
+ const schluessel = [];
162
+ let cursor = '0';
163
+ do {
164
+ const [next, gefunden] = await redis.scan(cursor, 'MATCH', `${praefix}*`, 'COUNT', 500);
165
+ cursor = next;
166
+ schluessel.push(...gefunden);
167
+ } while (cursor !== '0');
168
+ for (const k of schluessel) {
169
+ const hash = await redis.hgetall(k);
170
+ const topic = k.slice(praefix.length);
171
+ const vs = frisch.get(topic) ?? [];
172
+ for (const roh of Object.values(hash)) {
173
+ const v = entpacke(roh);
174
+ if (v)
175
+ vs.push(v);
176
+ }
177
+ if (vs.length)
178
+ frisch.set(topic, vs);
179
+ }
180
+ }
181
+ this.eingaenge = frisch;
182
+ this.geladen = jetzt;
183
+ }
184
+ /**
185
+ * Die Nähe zum BESTEN Eingang einer Lektion. -2, wenn sie keinen hat.
186
+ *
187
+ * Maximum, nicht Mittelwert: eine Lektion wird über ihre passendste Tür
188
+ * gefunden, und die anderen dürfen schlecht passen. Der Mittelwert würde
189
+ * Lektionen mit vielen Türen bestrafen — gemessen und belegt am 20.08.
190
+ */
191
+ besteNaehe(frage, topic) {
192
+ const vs = this.eingaenge.get(topic);
193
+ if (!vs?.length)
194
+ return -2;
195
+ let best = -2;
196
+ for (const v of vs) {
197
+ const k = kosinus(frage, v);
198
+ if (k > best)
199
+ best = k;
200
+ }
201
+ return best;
202
+ }
203
+ /** Die ähnlichsten Lektionen, je Lektion ihr bester Eingang. */
204
+ aehnlichste(frage, anzahl) {
205
+ const aus = [];
206
+ for (const topic of this.eingaenge.keys()) {
207
+ aus.push({ topic, naehe: this.besteNaehe(frage, topic) });
208
+ }
209
+ aus.sort((a, b) => b.naehe - a.naehe);
210
+ return aus.slice(0, anzahl);
211
+ }
212
+ }
213
+ /**
214
+ * Das Gewicht des Merkmals "bester Eingang" im Sortierer.
215
+ *
216
+ * Gemessen am 20.08.2026: 0,3 / 0,5 / 0,8 / 1,3 ergeben 57 / 58 / 57 / 57 %
217
+ * Findequote@3. Das Merkmal ist also unempfindlich gegen sein Gewicht — wer
218
+ * hier weiterdreht, misst Rauschen. 0,5 ist die Mitte des flachen Bereichs.
219
+ */
220
+ export const EINGANG_GEWICHT = 0.5;
221
+ export function beurteileDeckung(lektionen, vektoren) {
222
+ if (lektionen === 0)
223
+ return 'gut'; // Ein leeres Brain ist nicht krank.
224
+ if (vektoren === 0)
225
+ return 'aus';
226
+ return (vektoren / lektionen) * 100 < 90 ? 'luecke' : 'gut';
227
+ }
228
+ //# sourceMappingURL=eingaenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eingaenge.js","sourceRoot":"","sources":["../../src/eingaenge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Wie viel Text kam aus dem Gedaechtnis statt aus dem Modell?
3
+ *
4
+ * ANLASS 19.08.2026, Heinrich: "Bei der Schaetzung der Tokenersparnis sollten
5
+ * wir schauen, dass wir nicht nur schaetzen, denn das nervt Leute sehr stark.
6
+ * Vielleicht machen wir eine weitere Metrik rein? Sowas wie Token, die bei den
7
+ * Calls aus dem Brain rausgenommen wurden und nicht aus der AI vom Vendor
8
+ * kamen?"
9
+ *
10
+ * ── Warum das die bessere Zahl ist ──────────────────────────────────────────
11
+ *
12
+ * Die bisherige Zahl "583k Token gespart" ist eine RECHNUNG: 1.200 Token je
13
+ * wiederverwendeter Lektion mal 486 Abrufe. Der Faktor 1.200 ist geraten, und
14
+ * jeder darf ihn bestreiten. Die Zahl hier ist eine SUMME: die Laenge der
15
+ * Antworten, die das Brain wirklich zurueckgegeben hat. Da gibt es nichts zu
16
+ * bestreiten — hoechstens die Frage, was sie bedeutet.
17
+ *
18
+ * ── Was sie NICHT sagt ──────────────────────────────────────────────────────
19
+ *
20
+ * Sie ist KEIN Beweis, dass das Modell diese Token sonst selbst erzeugt haette.
21
+ * Vielleicht haette es die Antwort gar nicht gefunden, vielleicht kuerzer
22
+ * geantwortet. Sie sagt genau eines, und das laesst sich nachrechnen: so viel
23
+ * Text kam als Antwort aus dem Gedaechtnis. Wer mehr behauptet, schaetzt
24
+ * wieder.
25
+ *
26
+ * Deshalb steht die Schaetzung weiter daneben — aber klein, mit ihrer Annahme
27
+ * im selben Satz, und neben einer Zahl, die keine braucht.
28
+ */
29
+ /**
30
+ * Nur ABRUFE zaehlen, nicht jede Antwort.
31
+ *
32
+ * `learn_from_attempts` gibt eine Bestaetigung zurueck, `create_instance` eine
33
+ * Kennung — das ist kein geliefertes Wissen. Gezaehlt wird, was Gespeichertes
34
+ * herausgibt. Steht ein Werkzeug nicht auf dieser Liste, zaehlt es nicht mit;
35
+ * die Zahl ist dadurch eher zu klein als zu gross, und das ist die richtige
36
+ * Richtung fuer eine Zahl, mit der man wirbt.
37
+ */
38
+ export declare const LIEFERNDE_WERKZEUGE: Set<string>;
39
+ export declare function istLieferung(werkzeugName: string): boolean;
40
+ /** Redis-Schluessel je Brain: Summe der gelieferten Token. */
41
+ export declare function lieferSchluessel(instanceId: string): string;
42
+ /** Redis-Schluessel je Brain: Abrufe gesamt und davon mit Treffer. */
43
+ export declare function trefferSchluessel(instanceId: string): string;
44
+ export declare function hatTreffer(antwort: string): boolean;
45
+ /** Die Token dieser Antwort — dieselbe Schaetzfunktion wie beim Einblende-Budget. */
46
+ export declare function tokenDerAntwort(antwort: string): number;
47
+ export interface LieferBild {
48
+ /** Summe der gelieferten Token. */
49
+ token: number;
50
+ /** Abrufe insgesamt. */
51
+ abrufe: number;
52
+ /** Davon mit mindestens einem Treffer. */
53
+ treffer: number;
54
+ /** treffer/abrufe, 0..1. Bei 0 Abrufen: 0. */
55
+ quote: number;
56
+ }
57
+ export declare function lieferBild(roh: {
58
+ token?: string | number | null;
59
+ abrufe?: string | number | null;
60
+ treffer?: string | number | null;
61
+ }): LieferBild;
62
+ /** Grosse Zahlen so, wie ein Mensch sie liest. */
63
+ export declare function inWorten(bild: LieferBild): string;
@@ -0,0 +1,114 @@
1
+ import { estimateTokens } from './ambient-recall.js';
2
+ /**
3
+ * Wie viel Text kam aus dem Gedaechtnis statt aus dem Modell?
4
+ *
5
+ * ANLASS 19.08.2026, Heinrich: "Bei der Schaetzung der Tokenersparnis sollten
6
+ * wir schauen, dass wir nicht nur schaetzen, denn das nervt Leute sehr stark.
7
+ * Vielleicht machen wir eine weitere Metrik rein? Sowas wie Token, die bei den
8
+ * Calls aus dem Brain rausgenommen wurden und nicht aus der AI vom Vendor
9
+ * kamen?"
10
+ *
11
+ * ── Warum das die bessere Zahl ist ──────────────────────────────────────────
12
+ *
13
+ * Die bisherige Zahl "583k Token gespart" ist eine RECHNUNG: 1.200 Token je
14
+ * wiederverwendeter Lektion mal 486 Abrufe. Der Faktor 1.200 ist geraten, und
15
+ * jeder darf ihn bestreiten. Die Zahl hier ist eine SUMME: die Laenge der
16
+ * Antworten, die das Brain wirklich zurueckgegeben hat. Da gibt es nichts zu
17
+ * bestreiten — hoechstens die Frage, was sie bedeutet.
18
+ *
19
+ * ── Was sie NICHT sagt ──────────────────────────────────────────────────────
20
+ *
21
+ * Sie ist KEIN Beweis, dass das Modell diese Token sonst selbst erzeugt haette.
22
+ * Vielleicht haette es die Antwort gar nicht gefunden, vielleicht kuerzer
23
+ * geantwortet. Sie sagt genau eines, und das laesst sich nachrechnen: so viel
24
+ * Text kam als Antwort aus dem Gedaechtnis. Wer mehr behauptet, schaetzt
25
+ * wieder.
26
+ *
27
+ * Deshalb steht die Schaetzung weiter daneben — aber klein, mit ihrer Annahme
28
+ * im selben Satz, und neben einer Zahl, die keine braucht.
29
+ */
30
+ /**
31
+ * Nur ABRUFE zaehlen, nicht jede Antwort.
32
+ *
33
+ * `learn_from_attempts` gibt eine Bestaetigung zurueck, `create_instance` eine
34
+ * Kennung — das ist kein geliefertes Wissen. Gezaehlt wird, was Gespeichertes
35
+ * herausgibt. Steht ein Werkzeug nicht auf dieser Liste, zaehlt es nicht mit;
36
+ * die Zahl ist dadurch eher zu klein als zu gross, und das ist die richtige
37
+ * Richtung fuer eine Zahl, mit der man wirbt.
38
+ */
39
+ export const LIEFERNDE_WERKZEUGE = new Set([
40
+ 'smart_recall',
41
+ 'recall_best_solution',
42
+ 'recall_context',
43
+ 'recall_at',
44
+ 'causal_trace',
45
+ 'semantic_search',
46
+ 'brain_search',
47
+ 'team_recall',
48
+ 'global_recall',
49
+ 'session_start',
50
+ 'session_start_summary',
51
+ 'brain_briefing',
52
+ 'compact_recover',
53
+ 'session_handoff',
54
+ 'crystal_view',
55
+ 'brain_graph',
56
+ 'trace_dependency',
57
+ 'brain_who_knows',
58
+ 'fedbrain_search',
59
+ 'syndicate_search',
60
+ ]);
61
+ export function istLieferung(werkzeugName) {
62
+ return LIEFERNDE_WERKZEUGE.has(werkzeugName);
63
+ }
64
+ /** Redis-Schluessel je Brain: Summe der gelieferten Token. */
65
+ export function lieferSchluessel(instanceId) {
66
+ return `cachly:stats:delivered_tokens:${instanceId}`;
67
+ }
68
+ /** Redis-Schluessel je Brain: Abrufe gesamt und davon mit Treffer. */
69
+ export function trefferSchluessel(instanceId) {
70
+ return `cachly:stats:recall_hits:${instanceId}`;
71
+ }
72
+ /**
73
+ * Hatte dieser Abruf einen Treffer?
74
+ *
75
+ * Erkannt an dem, was die Antwort SAGT, nicht an ihrer Laenge: eine hoefliche
76
+ * Absage ("keine passende Lektion gefunden") ist laenger als mancher Treffer.
77
+ * Die Muster stammen aus den Antworttexten der Abruf-Werkzeuge selbst.
78
+ */
79
+ const LEERE_ANTWORT = /(no (relevant |matching |cached )?(lessons?|results?|context|memory|match)|keine? (passende|relevante)|nothing (found|cached)|0 results|Results \(0\b|empty)/i;
80
+ export function hatTreffer(antwort) {
81
+ if (!antwort || antwort.trim().length === 0)
82
+ return false;
83
+ return !LEERE_ANTWORT.test(antwort);
84
+ }
85
+ /** Die Token dieser Antwort — dieselbe Schaetzfunktion wie beim Einblende-Budget. */
86
+ export function tokenDerAntwort(antwort) {
87
+ return estimateTokens(antwort);
88
+ }
89
+ export function lieferBild(roh) {
90
+ const zahl = (v) => {
91
+ const n = typeof v === 'number' ? v : Number(v ?? 0);
92
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : 0;
93
+ };
94
+ const token = zahl(roh.token);
95
+ const abrufe = zahl(roh.abrufe);
96
+ const treffer = Math.min(zahl(roh.treffer), abrufe);
97
+ return { token, abrufe, treffer, quote: abrufe > 0 ? treffer / abrufe : 0 };
98
+ }
99
+ /** Grosse Zahlen so, wie ein Mensch sie liest. */
100
+ export function inWorten(bild) {
101
+ if (bild.abrufe === 0) {
102
+ return 'Aus dem Brain geliefert: noch nichts — es gab bisher keinen Abruf.';
103
+ }
104
+ const t = bild.token >= 1_000_000
105
+ ? `${(bild.token / 1_000_000).toFixed(2)} Mio.`
106
+ : bild.token >= 1_000
107
+ ? `${Math.round(bild.token / 1_000)}k`
108
+ : `${bild.token}`;
109
+ const prozent = Math.round(bild.quote * 100);
110
+ return (`Aus dem Brain geliefert: ${t} Token in ${bild.abrufe} Abrufen. ` +
111
+ `${bild.treffer} davon fanden etwas (${prozent} %). ` +
112
+ `Gemessen, nicht gerechnet — es ist die Summe der Antwortlaengen, keine Annahme.`);
113
+ }
114
+ //# sourceMappingURL=geliefert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geliefert.js","sourceRoot":"","sources":["../../src/geliefert.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { ckgSlug } from '../ckg.js';
2
2
  import { safeJsonParse } from '../utils.js';
3
+ import { ersparteMinuten } from '../wertbeitrag.js';
3
4
  export const ADVANCED_TOOL_NAMES = new Set([
4
5
  'memory_consolidate', 'brain_diff', 'causal_trace', 'knowledge_decay', 'autopilot',
5
6
  ]);
@@ -377,9 +378,12 @@ export async function handleAdvancedTool(name, args, getConnection, _apiFetch) {
377
378
  lines.push(topSolution.what_worked.slice(0, 500));
378
379
  lines.push(`\`\`\``);
379
380
  lines.push('');
380
- // Track estimated time saved — causal_trace finding a known fix is worth 1-4h
381
- const savedMins = topSolution.severity === 'critical' ? 240 : topSolution.severity === 'major' ? 120 : 60;
382
- redis.incrbyfloat(`cachly:stats:time_saved_mins:${instance_id}`, savedMins).catch(() => { });
381
+ // Track estimated time saved — causal_trace finding a known fix is worth 1-4h.
382
+ // Starter-Lektionen zaehlen 0 — siehe wertbeitrag.ts.
383
+ const savedMins = ersparteMinuten(topSolution, 'trace');
384
+ if (savedMins > 0) {
385
+ redis.incrbyfloat(`cachly:stats:time_saved_mins:${instance_id}`, savedMins).catch(() => { });
386
+ }
383
387
  }
384
388
  }
385
389
  lines.push(`💡 After applying: \`learn_from_attempts(topic="fix:${tokens[0] ?? 'issue'}", outcome="success", ...)\``);