@cachly-dev/mcp-server 0.9.17 → 0.9.18

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.
@@ -0,0 +1,504 @@
1
+ /**
2
+ * Use-case tests for the Cachly Brain flow.
3
+ *
4
+ * Tests the core Brain operations end-to-end using an in-memory Redis mock:
5
+ * learn_from_attempts → CKG update → recall_best_solution (BM25)
6
+ * session_start → lessons summary
7
+ * CKG: Bayesian confidence, contradiction detection
8
+ * Embeddings: provider detection, config sync
9
+ *
10
+ * Run: npx vitest run src/__tests__/brain-flow.test.ts
11
+ */
12
+ import { describe, it, expect, beforeEach } from 'vitest';
13
+ import { EventEmitter } from 'node:events';
14
+ // ── Modules under test ────────────────────────────────────────────────────────
15
+ import { ckgSlug, extractProblemConcept, ckgUpsertNode, ckgUpdateEdge } from '../ckg.js';
16
+ import { EMBED_PROVIDER, hasEmbedProvider, embedProviderHint, embedConfig, setEmbedJwt } from '../embeddings.js';
17
+ import { keywordSearch } from '../search.js';
18
+ // ── In-memory Redis mock ──────────────────────────────────────────────────────
19
+ class MockRedis {
20
+ store = new Map();
21
+ lists = new Map();
22
+ sets = new Map();
23
+ async get(key) {
24
+ return this.store.get(key) ?? null;
25
+ }
26
+ async set(key, value) {
27
+ this.store.set(key, value);
28
+ return 'OK';
29
+ }
30
+ async rpush(key, ...values) {
31
+ const list = this.lists.get(key) ?? [];
32
+ list.push(...values);
33
+ this.lists.set(key, list);
34
+ return list.length;
35
+ }
36
+ async lrange(key, start, stop) {
37
+ const list = this.lists.get(key) ?? [];
38
+ const end = stop < 0 ? list.length + stop + 1 : stop + 1;
39
+ return list.slice(start < 0 ? list.length + start : start, end);
40
+ }
41
+ async ltrim(key, start, stop) {
42
+ const list = this.lists.get(key) ?? [];
43
+ const end = stop < 0 ? list.length + stop + 1 : stop + 1;
44
+ this.lists.set(key, list.slice(start < 0 ? list.length + start : start, end));
45
+ return 'OK';
46
+ }
47
+ async sadd(key, ...members) {
48
+ const s = this.sets.get(key) ?? new Set();
49
+ let added = 0;
50
+ for (const m of members) {
51
+ if (!s.has(m)) {
52
+ s.add(m);
53
+ added++;
54
+ }
55
+ }
56
+ this.sets.set(key, s);
57
+ return added;
58
+ }
59
+ async smembers(key) {
60
+ return [...(this.sets.get(key) ?? [])];
61
+ }
62
+ /** Simplified scanStream: returns all matching keys in a single 'data' event. */
63
+ scanStream(opts) {
64
+ const emitter = new EventEmitter();
65
+ const pattern = opts.match.replace(/\*/g, '.*').replace(/\?/g, '.');
66
+ const regex = new RegExp(`^${pattern}$`);
67
+ const matches = [...this.store.keys()].filter(k => regex.test(k));
68
+ setImmediate(() => {
69
+ emitter.emit('data', matches);
70
+ emitter.emit('end');
71
+ });
72
+ return emitter;
73
+ }
74
+ /** Minimal pipeline: batches GET calls, returns [err, value][] */
75
+ pipeline() {
76
+ const commands = [];
77
+ const self = this;
78
+ return {
79
+ get(key) { commands.push({ cmd: 'get', key }); return this; },
80
+ async exec() {
81
+ const out = [];
82
+ for (const c of commands) {
83
+ out.push([null, self.store.get(c.key) ?? null]);
84
+ }
85
+ return out;
86
+ },
87
+ };
88
+ }
89
+ // Expose raw store for assertions
90
+ _getStore() { return this.store; }
91
+ _getLists() { return this.lists; }
92
+ _getSets() { return this.sets; }
93
+ }
94
+ /** Minimal implementation of the learn_from_attempts Redis storage logic */
95
+ async function storeLessonInRedis(redis, lesson) {
96
+ const ts = new Date().toISOString();
97
+ const existingRaw = await redis.get(`cachly:lesson:best:${lesson.topic}`);
98
+ const auditTrail = existingRaw
99
+ ? [{ ts, action: 'updated', prev_outcome: JSON.parse(existingRaw).outcome }]
100
+ : [{ ts, action: 'created' }];
101
+ const obj = {
102
+ ...lesson,
103
+ recall_count: 0,
104
+ ts,
105
+ verified_at: lesson.outcome !== 'failure' ? ts : undefined,
106
+ confidence: 1.0,
107
+ audit_trail: auditTrail,
108
+ version: 3,
109
+ };
110
+ const serialized = JSON.stringify(obj);
111
+ await redis.rpush(`cachly:lessons:${lesson.topic}`, serialized);
112
+ if (lesson.outcome === 'success' || lesson.outcome === 'partial' || !existingRaw) {
113
+ await redis.set(`cachly:lesson:best:${lesson.topic}`, serialized);
114
+ }
115
+ return obj;
116
+ }
117
+ // ── Tests ─────────────────────────────────────────────────────────────────────
118
+ describe('CKG helpers (ckg.ts)', () => {
119
+ let redis;
120
+ beforeEach(() => { redis = new MockRedis(); });
121
+ // ── ckgSlug ─────────────────────────────────────────────────────────────────
122
+ describe('ckgSlug', () => {
123
+ it('lowercases and replaces non-alnum with hyphens', () => {
124
+ // trailing '!' becomes '-' which is then stripped by the trailing-hyphen rule
125
+ expect(ckgSlug('Deploy API!')).toBe('deploy-api');
126
+ });
127
+ it('collapses multiple hyphens', () => {
128
+ expect(ckgSlug('foo -- bar')).toBe('foo-bar');
129
+ });
130
+ it('strips leading/trailing hyphens', () => {
131
+ expect(ckgSlug('--hello--')).toBe('hello');
132
+ });
133
+ it('allows colon for namespaced slugs', () => {
134
+ expect(ckgSlug('deploy:api')).toBe('deploy:api');
135
+ });
136
+ it('truncates at 80 chars', () => {
137
+ expect(ckgSlug('a'.repeat(100))).toHaveLength(80);
138
+ });
139
+ });
140
+ // ── extractProblemConcept ────────────────────────────────────────────────────
141
+ describe('extractProblemConcept', () => {
142
+ it('extracts 2 keywords from text', () => {
143
+ const result = extractProblemConcept('docker container fails to start');
144
+ expect(result).toBe('docker-container');
145
+ });
146
+ it('filters short words', () => {
147
+ const result = extractProblemConcept('the key issue');
148
+ // 'the' is stopword, 'key' is 3 chars (filtered), 'issue' passes
149
+ expect(result).toBe('issue');
150
+ });
151
+ it('returns null for empty/all-stopword text', () => {
152
+ expect(extractProblemConcept('')).toBeNull();
153
+ });
154
+ });
155
+ // ── ckgUpsertNode ────────────────────────────────────────────────────────────
156
+ describe('ckgUpsertNode', () => {
157
+ it('creates a new node with count=1', async () => {
158
+ await ckgUpsertNode(redis, 'deploy-api', 'backend', 'concept');
159
+ const raw = await redis.get('cachly:ckg:node:deploy-api');
160
+ expect(raw).not.toBeNull();
161
+ const node = JSON.parse(raw);
162
+ expect(node.id).toBe('deploy-api');
163
+ expect(node.domain).toBe('backend');
164
+ expect(node.type).toBe('concept');
165
+ expect(node.count).toBe(1);
166
+ });
167
+ it('increments count on repeated upsert', async () => {
168
+ await ckgUpsertNode(redis, 'deploy-api', 'backend', 'concept');
169
+ await ckgUpsertNode(redis, 'deploy-api', 'backend', 'concept');
170
+ const node = JSON.parse((await redis.get('cachly:ckg:node:deploy-api')));
171
+ expect(node.count).toBe(2);
172
+ });
173
+ });
174
+ // ── ckgUpdateEdge ────────────────────────────────────────────────────────────
175
+ describe('ckgUpdateEdge', () => {
176
+ it('creates edge with Bayesian smoothed confidence', async () => {
177
+ await ckgUpdateEdge(redis, 'problem-A', 'solved_by', 'solution-B', true);
178
+ const edgeKey = 'cachly:ckg:edge:problem-A:solved_by:solution-B';
179
+ const edge = JSON.parse((await redis.get(edgeKey)));
180
+ expect(edge.successes).toBe(1);
181
+ expect(edge.trials).toBe(1);
182
+ // Beta(successes+1, trials+2) = 2/3 ≈ 0.667
183
+ expect(edge.confidence).toBeCloseTo(2 / 3, 5);
184
+ });
185
+ it('increases confidence after multiple successes', async () => {
186
+ const r = redis;
187
+ await ckgUpdateEdge(r, 'A', 'leads_to', 'B', true);
188
+ await ckgUpdateEdge(r, 'A', 'leads_to', 'B', true);
189
+ await ckgUpdateEdge(r, 'A', 'leads_to', 'B', true);
190
+ const edge = JSON.parse((await redis.get('cachly:ckg:edge:A:leads_to:B')));
191
+ // 3 successes, 3 trials → (3+1)/(3+2) = 0.8
192
+ expect(edge.confidence).toBeCloseTo(0.8, 5);
193
+ });
194
+ it('partial success adds 0.5 to successes', async () => {
195
+ const r = redis;
196
+ await ckgUpdateEdge(r, 'A', 'leads_to', 'B', false, true); // partial
197
+ const edge = JSON.parse((await redis.get('cachly:ckg:edge:A:leads_to:B')));
198
+ expect(edge.successes).toBe(0.5);
199
+ // (0.5+1)/(1+2) = 1.5/3 = 0.5
200
+ expect(edge.confidence).toBeCloseTo(0.5, 5);
201
+ });
202
+ it('decreases confidence on failure', async () => {
203
+ const r = redis;
204
+ await ckgUpdateEdge(r, 'A', 'leads_to', 'B', true);
205
+ const before = JSON.parse((await redis.get('cachly:ckg:edge:A:leads_to:B'))).confidence;
206
+ await ckgUpdateEdge(r, 'A', 'leads_to', 'B', false);
207
+ const after = JSON.parse((await redis.get('cachly:ckg:edge:A:leads_to:B'))).confidence;
208
+ expect(after).toBeLessThan(before);
209
+ });
210
+ it('indexes edge in from/to index sets', async () => {
211
+ const r = redis;
212
+ await ckgUpdateEdge(r, 'prob', 'solved_by', 'sol', true);
213
+ const fromIdx = await redis.smembers('cachly:ckg:idx:from:prob');
214
+ const toIdx = await redis.smembers('cachly:ckg:idx:to:sol');
215
+ expect(fromIdx).toContain('cachly:ckg:edge:prob:solved_by:sol');
216
+ expect(toIdx).toContain('cachly:ckg:edge:prob:solved_by:sol');
217
+ });
218
+ });
219
+ });
220
+ // ── Embeddings (embeddings.ts) ────────────────────────────────────────────────
221
+ describe('Embeddings config (embeddings.ts)', () => {
222
+ it('EMBED_PROVIDER is a string', () => {
223
+ expect(typeof EMBED_PROVIDER).toBe('string');
224
+ });
225
+ it('hasEmbedProvider returns false when no provider keys are set', () => {
226
+ // In test environment (no API keys), EMBED_PROVIDER is 'none'
227
+ // hasEmbedProvider() depends on env — just verify it returns a boolean
228
+ expect(typeof hasEmbedProvider()).toBe('boolean');
229
+ });
230
+ it('embedProviderHint returns a non-empty string', () => {
231
+ const hint = embedProviderHint();
232
+ expect(typeof hint).toBe('string');
233
+ expect(hint.length).toBeGreaterThan(0);
234
+ });
235
+ it('setEmbedJwt updates embedConfig.jwt', () => {
236
+ const prev = embedConfig.jwt;
237
+ setEmbedJwt('test-jwt-token-xyz');
238
+ expect(embedConfig.jwt).toBe('test-jwt-token-xyz');
239
+ // Restore
240
+ setEmbedJwt(prev);
241
+ });
242
+ it('embedConfig.apiUrl defaults to cachly.dev', () => {
243
+ expect(embedConfig.apiUrl).toContain('cachly.dev');
244
+ });
245
+ });
246
+ // ── Brain Lesson Flow (learn → store → recall) ───────────────────────────────
247
+ describe('Brain lesson flow', () => {
248
+ let redis;
249
+ beforeEach(() => { redis = new MockRedis(); });
250
+ describe('storeLessonInRedis (learn_from_attempts logic)', () => {
251
+ it('stores lesson at cachly:lesson:best:{topic}', async () => {
252
+ await storeLessonInRedis(redis, {
253
+ topic: 'deploy:api',
254
+ outcome: 'success',
255
+ what_worked: 'docker compose up -d --build api',
256
+ what_failed: 'docker compose up (hangs on SSH timeout)',
257
+ severity: 'critical',
258
+ commands: ['docker compose up -d --build api'],
259
+ tags: ['docker', 'deploy'],
260
+ file_paths: [],
261
+ });
262
+ const raw = await redis.get('cachly:lesson:best:deploy:api');
263
+ expect(raw).not.toBeNull();
264
+ const lesson = JSON.parse(raw);
265
+ expect(lesson.topic).toBe('deploy:api');
266
+ expect(lesson.outcome).toBe('success');
267
+ expect(lesson.what_worked).toBe('docker compose up -d --build api');
268
+ expect(lesson.commands).toContain('docker compose up -d --build api');
269
+ expect(lesson.confidence).toBe(1.0);
270
+ expect(lesson.version).toBe(3);
271
+ });
272
+ it('appends to history list cachly:lessons:{topic}', async () => {
273
+ await storeLessonInRedis(redis, {
274
+ topic: 'fix:redis-timeout',
275
+ outcome: 'success',
276
+ what_worked: 'increase timeout to 30s',
277
+ severity: 'major',
278
+ commands: [],
279
+ tags: [],
280
+ file_paths: [],
281
+ });
282
+ await storeLessonInRedis(redis, {
283
+ topic: 'fix:redis-timeout',
284
+ outcome: 'partial',
285
+ what_worked: 'add retry logic',
286
+ severity: 'minor',
287
+ commands: [],
288
+ tags: [],
289
+ file_paths: [],
290
+ });
291
+ const history = await redis.lrange('cachly:lessons:fix:redis-timeout', 0, -1);
292
+ expect(history).toHaveLength(2);
293
+ });
294
+ it('failure does not overwrite an existing success lesson', async () => {
295
+ await storeLessonInRedis(redis, {
296
+ topic: 'infra:wireguard',
297
+ outcome: 'success',
298
+ what_worked: 'add WireGuard IP to TLS-SAN',
299
+ severity: 'critical',
300
+ commands: [],
301
+ tags: [],
302
+ file_paths: [],
303
+ });
304
+ await storeLessonInRedis(redis, {
305
+ topic: 'infra:wireguard',
306
+ outcome: 'failure',
307
+ what_worked: '',
308
+ severity: 'major',
309
+ commands: [],
310
+ tags: [],
311
+ file_paths: [],
312
+ });
313
+ const best = JSON.parse((await redis.get('cachly:lesson:best:infra:wireguard')));
314
+ // The success lesson must win
315
+ expect(best.outcome).toBe('success');
316
+ });
317
+ it('audit trail records creation and update', async () => {
318
+ await storeLessonInRedis(redis, {
319
+ topic: 'debug:clickhouse',
320
+ outcome: 'failure',
321
+ what_worked: '',
322
+ severity: 'major',
323
+ commands: [],
324
+ tags: [],
325
+ file_paths: [],
326
+ });
327
+ await storeLessonInRedis(redis, {
328
+ topic: 'debug:clickhouse',
329
+ outcome: 'success',
330
+ what_worked: 'use 127.0.0.1 not localhost',
331
+ severity: 'major',
332
+ commands: [],
333
+ tags: [],
334
+ file_paths: [],
335
+ });
336
+ const best = JSON.parse((await redis.get('cachly:lesson:best:debug:clickhouse')));
337
+ expect(best.audit_trail.length).toBe(1);
338
+ expect(best.audit_trail[0].action).toBe('updated');
339
+ expect(best.audit_trail[0].prev_outcome).toBe('failure');
340
+ });
341
+ });
342
+ // ── BM25 recall ─────────────────────────────────────────────────────────────
343
+ describe('BM25 recall (recall_best_solution keyword search)', () => {
344
+ beforeEach(async () => {
345
+ // Seed lessons
346
+ const lessons = [
347
+ { topic: 'deploy:api', what_worked: 'nohup docker compose up -d --build api' },
348
+ { topic: 'infra:clickhouse-ipv6', what_worked: 'use 127.0.0.1 not localhost in ClickHouse healthcheck' },
349
+ { topic: 'fix:keycloak-auth', what_worked: 'set redirect_uri to https not http' },
350
+ { topic: 'bash:macos-lowercase', what_worked: 'use tr instead of ${var,,} on macOS' },
351
+ { topic: 'deploy:web', what_worked: 'rsync --checksum --delete with ssh port 2222' },
352
+ ];
353
+ for (const l of lessons) {
354
+ await storeLessonInRedis(redis, {
355
+ ...l,
356
+ outcome: 'success',
357
+ severity: 'critical',
358
+ commands: [],
359
+ tags: [],
360
+ file_paths: [],
361
+ });
362
+ }
363
+ });
364
+ it('finds deploy lesson by keyword', async () => {
365
+ const results = await keywordSearch(redis, ['cachly:lesson:best:*'], 'docker deploy api', 5);
366
+ expect(results.length).toBeGreaterThan(0);
367
+ expect(results[0].content).toContain('docker');
368
+ });
369
+ it('finds clickhouse lesson by infrastructure topic', async () => {
370
+ const results = await keywordSearch(redis, ['cachly:lesson:best:*'], 'clickhouse localhost healthcheck', 5);
371
+ expect(results.length).toBeGreaterThan(0);
372
+ const topResult = results[0];
373
+ expect(topResult.content.toLowerCase()).toContain('clickhouse');
374
+ });
375
+ it('ranks more specific matches higher', async () => {
376
+ const results = await keywordSearch(redis, ['cachly:lesson:best:*'], 'macos bash lowercase variable', 5);
377
+ expect(results.length).toBeGreaterThan(0);
378
+ // macOS bash lesson should rank highest since it matches all terms
379
+ expect(results[0].content.toLowerCase()).toContain('macos');
380
+ });
381
+ it('returns empty array for completely unrelated query', async () => {
382
+ const results = await keywordSearch(redis, ['cachly:lesson:best:*'], 'xyzzy quantum entanglement unobtanium', 5);
383
+ // Might return 0 or very low-score results; score should be minimal
384
+ // Either empty or score near 0 is acceptable
385
+ if (results.length > 0) {
386
+ expect(results[0].score).toBeLessThan(0.1);
387
+ }
388
+ });
389
+ });
390
+ // ── session_start context ────────────────────────────────────────────────────
391
+ describe('session_start lesson aggregation', () => {
392
+ it('correctly sorts lessons by timestamp (newest first)', async () => {
393
+ const topics = ['old:lesson', 'mid:lesson', 'new:lesson'];
394
+ for (let i = 0; i < topics.length; i++) {
395
+ await storeLessonInRedis(redis, {
396
+ topic: topics[i],
397
+ outcome: 'success',
398
+ what_worked: `fix ${i}`,
399
+ severity: 'minor',
400
+ commands: [],
401
+ tags: [],
402
+ file_paths: [],
403
+ });
404
+ // Tiny delay to ensure different timestamps
405
+ await new Promise(r => setTimeout(r, 5));
406
+ }
407
+ // Simulate what session_start does: scan all lesson keys, sort by ts descending
408
+ const allKeys = [];
409
+ await new Promise((resolve, reject) => {
410
+ const stream = redis.scanStream({ match: 'cachly:lesson:best:*', count: 200 });
411
+ stream.on('data', (batch) => allKeys.push(...batch));
412
+ stream.on('end', resolve);
413
+ stream.on('error', reject);
414
+ });
415
+ const lessons = [];
416
+ for (const k of allKeys) {
417
+ const raw = await redis.get(k);
418
+ if (raw)
419
+ lessons.push(JSON.parse(raw));
420
+ }
421
+ lessons.sort((a, b) => new Date(b.ts).getTime() - new Date(a.ts).getTime());
422
+ expect(lessons[0].topic).toBe('new:lesson');
423
+ expect(lessons[lessons.length - 1].topic).toBe('old:lesson');
424
+ });
425
+ it('scanStream returns all stored lesson keys', async () => {
426
+ await storeLessonInRedis(redis, { topic: 'alpha', outcome: 'success', what_worked: 'x', severity: 'minor', commands: [], tags: [], file_paths: [] });
427
+ await storeLessonInRedis(redis, { topic: 'beta', outcome: 'success', what_worked: 'y', severity: 'minor', commands: [], tags: [], file_paths: [] });
428
+ await storeLessonInRedis(redis, { topic: 'gamma', outcome: 'success', what_worked: 'z', severity: 'minor', commands: [], tags: [], file_paths: [] });
429
+ const keys = [];
430
+ await new Promise((resolve, reject) => {
431
+ const stream = redis.scanStream({ match: 'cachly:lesson:best:*', count: 100 });
432
+ stream.on('data', (batch) => keys.push(...batch));
433
+ stream.on('end', resolve);
434
+ stream.on('error', reject);
435
+ });
436
+ expect(keys).toHaveLength(3);
437
+ expect(keys.some(k => k.includes('alpha'))).toBe(true);
438
+ expect(keys.some(k => k.includes('beta'))).toBe(true);
439
+ expect(keys.some(k => k.includes('gamma'))).toBe(true);
440
+ });
441
+ });
442
+ });
443
+ // ── End-to-end Brain flow integration ────────────────────────────────────────
444
+ describe('Brain E2E: learn → CKG → recall', () => {
445
+ let redis;
446
+ beforeEach(() => { redis = new MockRedis(); });
447
+ it('full cycle: store lesson + update CKG + retrieve via BM25', async () => {
448
+ const r = redis;
449
+ // 1. Learn: store a deploy lesson
450
+ await storeLessonInRedis(redis, {
451
+ topic: 'deploy:api',
452
+ outcome: 'success',
453
+ what_worked: 'nohup docker compose up -d --build api avoids SSH timeout',
454
+ what_failed: 'plain docker compose up hangs when SSH connection drops',
455
+ severity: 'critical',
456
+ commands: ['nohup docker compose up -d --build api'],
457
+ tags: ['docker', 'deploy', 'ssh'],
458
+ file_paths: ['docker-compose.yml'],
459
+ });
460
+ // 2. CKG: record the causal connection
461
+ await ckgUpsertNode(r, 'deploy-api', 'infra', 'concept');
462
+ await ckgUpdateEdge(r, 'ssh-timeout', 'solved_by', 'nohup-docker', true);
463
+ // 3. Recall via BM25
464
+ const results = await keywordSearch(r, ['cachly:lesson:best:*'], 'docker compose ssh timeout deploy', 5);
465
+ expect(results.length).toBeGreaterThan(0);
466
+ expect(results[0].content).toContain('nohup');
467
+ // 4. Verify CKG state
468
+ const edge = JSON.parse((await redis.get('cachly:ckg:edge:ssh-timeout:solved_by:nohup-docker')));
469
+ expect(edge.confidence).toBeCloseTo(2 / 3, 5); // Beta(1+1, 1+2)
470
+ // 5. Verify lesson persisted correctly
471
+ const lesson = JSON.parse((await redis.get('cachly:lesson:best:deploy:api')));
472
+ expect(lesson.outcome).toBe('success');
473
+ expect(lesson.commands).toContain('nohup docker compose up -d --build api');
474
+ });
475
+ it('contradiction: failure after success is detected', async () => {
476
+ await storeLessonInRedis(redis, {
477
+ topic: 'infra:k3s',
478
+ outcome: 'success',
479
+ what_worked: 'add WireGuard IP to k3s TLS-SAN',
480
+ severity: 'critical',
481
+ commands: [],
482
+ tags: [],
483
+ file_paths: [],
484
+ });
485
+ await storeLessonInRedis(redis, {
486
+ topic: 'infra:k3s',
487
+ outcome: 'failure',
488
+ what_worked: '',
489
+ severity: 'major',
490
+ commands: [],
491
+ tags: [],
492
+ file_paths: [],
493
+ });
494
+ // Success lesson should still be the "best" lesson
495
+ const best = JSON.parse((await redis.get('cachly:lesson:best:infra:k3s')));
496
+ expect(best.outcome).toBe('success');
497
+ // But failure is logged in history
498
+ const history = await redis.lrange('cachly:lessons:infra:k3s', 0, -1);
499
+ expect(history).toHaveLength(2);
500
+ const lastAttempt = JSON.parse(history[1]);
501
+ expect(lastAttempt.outcome).toBe('failure');
502
+ });
503
+ });
504
+ //# sourceMappingURL=brain-flow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-flow.test.js","sourceRoot":"","sources":["../../src/__tests__/brain-flow.test.ts"],"names":[],"mappings":"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"}
package/dist/ckg.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { Redis } from 'ioredis';
2
+ export type CKGEdge = {
3
+ from: string;
4
+ to: string;
5
+ edgeType: string;
6
+ successes: number;
7
+ trials: number;
8
+ confidence: number;
9
+ last_updated: string;
10
+ };
11
+ export type CKGNode = {
12
+ id: string;
13
+ domain: string;
14
+ type: string;
15
+ count: number;
16
+ ts: string;
17
+ };
18
+ export declare function ckgSlug(text: string): string;
19
+ /** Extract 1-3 significant keywords from free text for a problem concept */
20
+ export declare function extractProblemConcept(text: string): string | null;
21
+ export declare function ckgUpsertNode(redis: Redis, id: string, domain: string, type: string): Promise<void>;
22
+ export declare function ckgUpdateEdge(redis: Redis, from: string, edgeType: string, to: string, success: boolean, partial?: boolean): Promise<void>;
package/dist/ckg.js ADDED
@@ -0,0 +1,40 @@
1
+ const STOPWORDS_CKG = new Set(['that', 'this', 'with', 'from', 'when', 'then', 'also', 'have', 'been', 'will', 'were', 'they', 'them', 'than', 'more', 'some', 'into', 'over', 'only', 'just', 'where', 'while', 'which', 'there', 'their', 'would', 'could', 'should', 'after', 'before', 'about']);
2
+ export function ckgSlug(text) {
3
+ return text.toLowerCase().replace(/[^a-z0-9\-:]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '').slice(0, 80);
4
+ }
5
+ /** Extract 1-3 significant keywords from free text for a problem concept */
6
+ export function extractProblemConcept(text) {
7
+ const words = text.toLowerCase()
8
+ .replace(/[^a-z0-9\s\-_]/g, ' ').split(/\s+/)
9
+ .filter(w => w.length > 3 && !STOPWORDS_CKG.has(w))
10
+ .slice(0, 5);
11
+ if (words.length === 0)
12
+ return null;
13
+ return words.slice(0, 2).join('-');
14
+ }
15
+ export async function ckgUpsertNode(redis, id, domain, type) {
16
+ const key = `cachly:ckg:node:${id}`;
17
+ const raw = await redis.get(key);
18
+ const node = raw ? JSON.parse(raw) : { id, domain, type, count: 0, ts: new Date().toISOString() };
19
+ node.count = (node.count || 0) + 1;
20
+ node.ts = new Date().toISOString();
21
+ await redis.set(key, JSON.stringify(node));
22
+ }
23
+ export async function ckgUpdateEdge(redis, from, edgeType, to, success, partial = false) {
24
+ const key = `cachly:ckg:edge:${from}:${edgeType}:${to}`;
25
+ const raw = await redis.get(key);
26
+ const edge = raw ? JSON.parse(raw) : { from, to, edgeType, successes: 0, trials: 0, confidence: 0, last_updated: '' };
27
+ edge.trials = (edge.trials || 0) + 1;
28
+ if (success)
29
+ edge.successes = (edge.successes || 0) + 1;
30
+ else if (partial)
31
+ edge.successes = (edge.successes || 0) + 0.5;
32
+ // Beta distribution smoothed confidence: (successes+1) / (trials+2)
33
+ edge.confidence = (edge.successes + 1) / (edge.trials + 2);
34
+ edge.last_updated = new Date().toISOString();
35
+ await redis.set(key, JSON.stringify(edge));
36
+ // Index: set of edge keys per source node (for fast traversal)
37
+ await redis.sadd(`cachly:ckg:idx:from:${from}`, key);
38
+ await redis.sadd(`cachly:ckg:idx:to:${to}`, key);
39
+ }
40
+ //# sourceMappingURL=ckg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ckg.js","sourceRoot":"","sources":["../src/ckg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export declare const CONFIDENCE_WARN_DAYS: number;
2
+ export declare const CONFIDENCE_STALE_DAYS: number;
3
+ export declare const CONFIDENCE_WARN_VALUE = 0.7;
4
+ export declare const CONFIDENCE_STALE_VALUE = 0.5;
5
+ /** Calculate current confidence for a lesson based on how long since last verified. */
6
+ export declare function calculateConfidence(lesson: {
7
+ verified_at?: string;
8
+ ts: string;
9
+ recall_count?: number;
10
+ }): number;
11
+ /** Render a confidence badge string. */
12
+ export declare function confidenceBadge(confidence: number, ageDays: number): string;
13
+ /** Category-specific required fields for structured lessons. */
14
+ export declare const STRUCTURED_TEMPLATES: Record<string, {
15
+ required: string[];
16
+ hint: string;
17
+ }>;
18
+ /** Fast content hash for index invalidation (not cryptographic, just change detection) */
19
+ export declare function simpleHash(text: string): string;