@soleri/core 1.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brain/brain.d.ts +12 -3
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +147 -12
- package/dist/brain/brain.js.map +1 -1
- package/dist/cognee/client.d.ts +35 -0
- package/dist/cognee/client.d.ts.map +1 -0
- package/dist/cognee/client.js +291 -0
- package/dist/cognee/client.js.map +1 -0
- package/dist/cognee/types.d.ts +46 -0
- package/dist/cognee/types.d.ts.map +1 -0
- package/dist/cognee/types.js +3 -0
- package/dist/cognee/types.js.map +1 -0
- package/dist/facades/types.d.ts +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/brain.test.ts +265 -27
- package/src/__tests__/cognee-client.test.ts +524 -0
- package/src/brain/brain.ts +176 -12
- package/src/cognee/client.ts +352 -0
- package/src/cognee/types.ts +62 -0
- package/src/index.ts +11 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
2
|
import { Vault } from '../vault/vault.js';
|
|
3
3
|
import { Brain } from '../brain/brain.js';
|
|
4
4
|
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
5
|
+
import type { CogneeClient } from '../cognee/client.js';
|
|
6
|
+
import type { CogneeSearchResult, CogneeStatus } from '../cognee/types.js';
|
|
5
7
|
|
|
6
8
|
function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntry {
|
|
7
9
|
return {
|
|
@@ -15,6 +17,36 @@ function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntr
|
|
|
15
17
|
};
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
function makeMockCognee(
|
|
21
|
+
overrides: {
|
|
22
|
+
available?: boolean;
|
|
23
|
+
searchResults?: CogneeSearchResult[];
|
|
24
|
+
searchError?: boolean;
|
|
25
|
+
} = {},
|
|
26
|
+
): CogneeClient {
|
|
27
|
+
const available = overrides.available ?? true;
|
|
28
|
+
return {
|
|
29
|
+
get isAvailable() {
|
|
30
|
+
return available;
|
|
31
|
+
},
|
|
32
|
+
search: overrides.searchError
|
|
33
|
+
? vi.fn().mockRejectedValue(new Error('timeout'))
|
|
34
|
+
: vi.fn().mockResolvedValue(overrides.searchResults ?? []),
|
|
35
|
+
addEntries: vi.fn().mockResolvedValue({ added: 0 }),
|
|
36
|
+
cognify: vi.fn().mockResolvedValue({ status: 'ok' }),
|
|
37
|
+
healthCheck: vi
|
|
38
|
+
.fn()
|
|
39
|
+
.mockResolvedValue({ available, url: 'http://localhost:8000', latencyMs: 1 } as CogneeStatus),
|
|
40
|
+
getConfig: vi.fn().mockReturnValue({
|
|
41
|
+
baseUrl: 'http://localhost:8000',
|
|
42
|
+
dataset: 'vault',
|
|
43
|
+
timeoutMs: 5000,
|
|
44
|
+
healthCacheTtlMs: 60000,
|
|
45
|
+
}),
|
|
46
|
+
getStatus: vi.fn().mockReturnValue(null),
|
|
47
|
+
} as unknown as CogneeClient;
|
|
48
|
+
}
|
|
49
|
+
|
|
18
50
|
describe('Brain', () => {
|
|
19
51
|
let vault: Vault;
|
|
20
52
|
let brain: Brain;
|
|
@@ -53,6 +85,12 @@ describe('Brain', () => {
|
|
|
53
85
|
const brain2 = new Brain(vault);
|
|
54
86
|
expect(brain2.getVocabularySize()).toBeGreaterThan(0);
|
|
55
87
|
});
|
|
88
|
+
|
|
89
|
+
it('should accept optional CogneeClient', () => {
|
|
90
|
+
const cognee = makeMockCognee();
|
|
91
|
+
const brain2 = new Brain(vault, cognee);
|
|
92
|
+
expect(brain2.getVocabularySize()).toBe(0);
|
|
93
|
+
});
|
|
56
94
|
});
|
|
57
95
|
|
|
58
96
|
// ─── Intelligent Search ──────────────────────────────────────
|
|
@@ -89,49 +127,52 @@ describe('Brain', () => {
|
|
|
89
127
|
brain = new Brain(vault);
|
|
90
128
|
});
|
|
91
129
|
|
|
92
|
-
it('should return ranked results', () => {
|
|
93
|
-
const results = brain.intelligentSearch('validation input');
|
|
130
|
+
it('should return ranked results', async () => {
|
|
131
|
+
const results = await brain.intelligentSearch('validation input');
|
|
94
132
|
expect(results.length).toBeGreaterThan(0);
|
|
95
133
|
expect(results[0].entry.id).toBe('is-1');
|
|
96
134
|
});
|
|
97
135
|
|
|
98
|
-
it('should include score breakdown', () => {
|
|
99
|
-
const results = brain.intelligentSearch('validation');
|
|
136
|
+
it('should include score breakdown with vector field', async () => {
|
|
137
|
+
const results = await brain.intelligentSearch('validation');
|
|
100
138
|
expect(results.length).toBeGreaterThan(0);
|
|
101
139
|
const breakdown = results[0].breakdown;
|
|
102
140
|
expect(breakdown).toHaveProperty('semantic');
|
|
141
|
+
expect(breakdown).toHaveProperty('vector');
|
|
103
142
|
expect(breakdown).toHaveProperty('severity');
|
|
104
143
|
expect(breakdown).toHaveProperty('recency');
|
|
105
144
|
expect(breakdown).toHaveProperty('tagOverlap');
|
|
106
145
|
expect(breakdown).toHaveProperty('domainMatch');
|
|
107
146
|
expect(breakdown).toHaveProperty('total');
|
|
108
147
|
expect(breakdown.total).toBe(results[0].score);
|
|
148
|
+
// Without cognee, vector should be 0
|
|
149
|
+
expect(breakdown.vector).toBe(0);
|
|
109
150
|
});
|
|
110
151
|
|
|
111
|
-
it('should return empty array for no matches', () => {
|
|
112
|
-
const results = brain.intelligentSearch('xyznonexistent');
|
|
152
|
+
it('should return empty array for no matches', async () => {
|
|
153
|
+
const results = await brain.intelligentSearch('xyznonexistent');
|
|
113
154
|
expect(results).toEqual([]);
|
|
114
155
|
});
|
|
115
156
|
|
|
116
|
-
it('should respect limit', () => {
|
|
117
|
-
const results = brain.intelligentSearch('pattern', { limit: 1 });
|
|
157
|
+
it('should respect limit', async () => {
|
|
158
|
+
const results = await brain.intelligentSearch('pattern', { limit: 1 });
|
|
118
159
|
expect(results.length).toBeLessThanOrEqual(1);
|
|
119
160
|
});
|
|
120
161
|
|
|
121
|
-
it('should filter by domain', () => {
|
|
122
|
-
const results = brain.intelligentSearch('pattern', { domain: 'security' });
|
|
162
|
+
it('should filter by domain', async () => {
|
|
163
|
+
const results = await brain.intelligentSearch('pattern', { domain: 'security' });
|
|
123
164
|
expect(results.every((r) => r.entry.domain === 'security')).toBe(true);
|
|
124
165
|
});
|
|
125
166
|
|
|
126
|
-
it('should boost domain matches when domain is specified', () => {
|
|
127
|
-
const withDomain = brain.intelligentSearch('pattern', { domain: 'security' });
|
|
167
|
+
it('should boost domain matches when domain is specified', async () => {
|
|
168
|
+
const withDomain = await brain.intelligentSearch('pattern', { domain: 'security' });
|
|
128
169
|
if (withDomain.length > 0) {
|
|
129
170
|
expect(withDomain[0].breakdown.domainMatch).toBe(1.0);
|
|
130
171
|
}
|
|
131
172
|
});
|
|
132
173
|
|
|
133
|
-
it('should boost severity in scoring', () => {
|
|
134
|
-
const results = brain.intelligentSearch('pattern');
|
|
174
|
+
it('should boost severity in scoring', async () => {
|
|
175
|
+
const results = await brain.intelligentSearch('pattern');
|
|
135
176
|
if (results.length >= 2) {
|
|
136
177
|
const critical = results.find((r) => r.entry.severity === 'critical');
|
|
137
178
|
const suggestion = results.find((r) => r.entry.severity === 'suggestion');
|
|
@@ -141,8 +182,10 @@ describe('Brain', () => {
|
|
|
141
182
|
}
|
|
142
183
|
});
|
|
143
184
|
|
|
144
|
-
it('should boost tag overlap when tags provided', () => {
|
|
145
|
-
const results = brain.intelligentSearch('pattern', {
|
|
185
|
+
it('should boost tag overlap when tags provided', async () => {
|
|
186
|
+
const results = await brain.intelligentSearch('pattern', {
|
|
187
|
+
tags: ['validation', 'security'],
|
|
188
|
+
});
|
|
146
189
|
if (results.length > 0) {
|
|
147
190
|
const secEntry = results.find((r) => r.entry.id === 'is-1');
|
|
148
191
|
if (secEntry) {
|
|
@@ -151,15 +194,185 @@ describe('Brain', () => {
|
|
|
151
194
|
}
|
|
152
195
|
});
|
|
153
196
|
|
|
154
|
-
it('should handle search on empty vault gracefully', () => {
|
|
197
|
+
it('should handle search on empty vault gracefully', async () => {
|
|
155
198
|
const emptyVault = new Vault(':memory:');
|
|
156
199
|
const emptyBrain = new Brain(emptyVault);
|
|
157
|
-
const results = emptyBrain.intelligentSearch('anything');
|
|
200
|
+
const results = await emptyBrain.intelligentSearch('anything');
|
|
158
201
|
expect(results).toEqual([]);
|
|
159
202
|
emptyVault.close();
|
|
160
203
|
});
|
|
161
204
|
});
|
|
162
205
|
|
|
206
|
+
// ─── Hybrid Search (with Cognee) ──────────────────────────────
|
|
207
|
+
|
|
208
|
+
describe('hybrid search with Cognee', () => {
|
|
209
|
+
beforeEach(() => {
|
|
210
|
+
vault.seed([
|
|
211
|
+
makeEntry({
|
|
212
|
+
id: 'hs-1',
|
|
213
|
+
title: 'Authentication flow',
|
|
214
|
+
description: 'JWT-based authentication for API endpoints.',
|
|
215
|
+
domain: 'security',
|
|
216
|
+
severity: 'critical',
|
|
217
|
+
tags: ['auth', 'jwt'],
|
|
218
|
+
}),
|
|
219
|
+
makeEntry({
|
|
220
|
+
id: 'hs-2',
|
|
221
|
+
title: 'Logging best practices',
|
|
222
|
+
description: 'Structured logging with correlation IDs for debugging.',
|
|
223
|
+
domain: 'observability',
|
|
224
|
+
severity: 'warning',
|
|
225
|
+
tags: ['logging', 'debugging'],
|
|
226
|
+
}),
|
|
227
|
+
]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should match via [vault-id:] prefix (strategy 1)', async () => {
|
|
231
|
+
const cognee = makeMockCognee({
|
|
232
|
+
searchResults: [
|
|
233
|
+
{
|
|
234
|
+
id: 'cognee-uuid-1',
|
|
235
|
+
score: 0.92,
|
|
236
|
+
text: '[vault-id:hs-1]\nAuthentication flow\nJWT-based authentication for API endpoints.',
|
|
237
|
+
searchType: 'CHUNKS',
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
});
|
|
241
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
242
|
+
const results = await hybridBrain.intelligentSearch('authentication');
|
|
243
|
+
expect(results.length).toBeGreaterThan(0);
|
|
244
|
+
const authResult = results.find((r) => r.entry.id === 'hs-1');
|
|
245
|
+
expect(authResult).toBeDefined();
|
|
246
|
+
expect(authResult!.breakdown.vector).toBe(0.92);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should match via title first-line (strategy 2)', async () => {
|
|
250
|
+
// Cognee stripped the [vault-id:] prefix during chunking — title is on first line
|
|
251
|
+
const cognee = makeMockCognee({
|
|
252
|
+
searchResults: [
|
|
253
|
+
{
|
|
254
|
+
id: 'cognee-uuid-2',
|
|
255
|
+
score: 0.9,
|
|
256
|
+
text: 'Authentication flow\nJWT-based authentication for API endpoints.',
|
|
257
|
+
searchType: 'CHUNKS',
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
});
|
|
261
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
262
|
+
const results = await hybridBrain.intelligentSearch('authentication');
|
|
263
|
+
const authResult = results.find((r) => r.entry.id === 'hs-1');
|
|
264
|
+
expect(authResult).toBeDefined();
|
|
265
|
+
expect(authResult!.breakdown.vector).toBe(0.9);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('should match via title substring (strategy 3)', async () => {
|
|
269
|
+
// Mid-document chunk where title isn't on the first line
|
|
270
|
+
const cognee = makeMockCognee({
|
|
271
|
+
searchResults: [
|
|
272
|
+
{
|
|
273
|
+
id: 'cognee-uuid-3',
|
|
274
|
+
score: 0.85,
|
|
275
|
+
text: 'Some preamble text\nAuthentication flow\nJWT-based auth...',
|
|
276
|
+
searchType: 'CHUNKS',
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
});
|
|
280
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
281
|
+
const results = await hybridBrain.intelligentSearch('authentication');
|
|
282
|
+
const authResult = results.find((r) => r.entry.id === 'hs-1');
|
|
283
|
+
expect(authResult).toBeDefined();
|
|
284
|
+
expect(authResult!.breakdown.vector).toBe(0.85);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should merge cognee-only entries via FTS fallback (strategy 4)', async () => {
|
|
288
|
+
// hs-2 may not match FTS5 for "authentication" but Cognee finds it via semantic similarity.
|
|
289
|
+
// The chunk text starts with the entry title so strategy 4's vault.search(firstLine) finds it.
|
|
290
|
+
const cognee = makeMockCognee({
|
|
291
|
+
searchResults: [
|
|
292
|
+
{
|
|
293
|
+
id: 'cognee-uuid-a',
|
|
294
|
+
score: 0.95,
|
|
295
|
+
text: 'Authentication flow\nJWT-based authentication for API endpoints.',
|
|
296
|
+
searchType: 'CHUNKS',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
id: 'cognee-uuid-b',
|
|
300
|
+
score: 0.6,
|
|
301
|
+
text: 'Logging best practices\nStructured logging with correlation IDs.',
|
|
302
|
+
searchType: 'CHUNKS',
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
});
|
|
306
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
307
|
+
const results = await hybridBrain.intelligentSearch('authentication');
|
|
308
|
+
// Both entries should be in results (hs-2 merged from Cognee even if not in FTS5)
|
|
309
|
+
const ids = results.map((r) => r.entry.id);
|
|
310
|
+
expect(ids).toContain('hs-1');
|
|
311
|
+
expect(ids).toContain('hs-2');
|
|
312
|
+
const loggingResult = results.find((r) => r.entry.id === 'hs-2');
|
|
313
|
+
expect(loggingResult).toBeDefined();
|
|
314
|
+
expect(loggingResult!.breakdown.vector).toBe(0.6);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should fall back to FTS5-only on Cognee search error', async () => {
|
|
318
|
+
const cognee = makeMockCognee({ searchError: true });
|
|
319
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
320
|
+
const results = await hybridBrain.intelligentSearch('authentication');
|
|
321
|
+
// Should still work, just without vector scores
|
|
322
|
+
for (const r of results) {
|
|
323
|
+
expect(r.breakdown.vector).toBe(0);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should work without Cognee (backward compatible)', async () => {
|
|
328
|
+
const noCogneeBrain = new Brain(vault);
|
|
329
|
+
const results = await noCogneeBrain.intelligentSearch('authentication');
|
|
330
|
+
for (const r of results) {
|
|
331
|
+
expect(r.breakdown.vector).toBe(0);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should handle unavailable Cognee gracefully', async () => {
|
|
336
|
+
const cognee = makeMockCognee({ available: false });
|
|
337
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
338
|
+
const results = await hybridBrain.intelligentSearch('authentication');
|
|
339
|
+
for (const r of results) {
|
|
340
|
+
expect(r.breakdown.vector).toBe(0);
|
|
341
|
+
}
|
|
342
|
+
// search should not have been called
|
|
343
|
+
expect(cognee.search).not.toHaveBeenCalled();
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// ─── syncToCognee ──────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
describe('syncToCognee', () => {
|
|
350
|
+
it('should return 0 when Cognee not available', async () => {
|
|
351
|
+
const result = await brain.syncToCognee();
|
|
352
|
+
expect(result).toEqual({ synced: 0, cognified: false });
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it('should sync all entries and cognify', async () => {
|
|
356
|
+
vault.seed([makeEntry({ id: 'sync-1' }), makeEntry({ id: 'sync-2' })]);
|
|
357
|
+
const cognee = makeMockCognee();
|
|
358
|
+
(cognee.addEntries as ReturnType<typeof vi.fn>).mockResolvedValue({ added: 2 });
|
|
359
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
360
|
+
const result = await hybridBrain.syncToCognee();
|
|
361
|
+
expect(result.synced).toBe(2);
|
|
362
|
+
expect(result.cognified).toBe(true);
|
|
363
|
+
expect(cognee.addEntries).toHaveBeenCalledTimes(1);
|
|
364
|
+
expect(cognee.cognify).toHaveBeenCalledTimes(1);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it('should skip cognify when no entries added', async () => {
|
|
368
|
+
const cognee = makeMockCognee();
|
|
369
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
370
|
+
const result = await hybridBrain.syncToCognee();
|
|
371
|
+
expect(result.synced).toBe(0);
|
|
372
|
+
expect(result.cognified).toBe(false);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
163
376
|
// ─── Enrich and Capture ─────────────────────────────────────
|
|
164
377
|
|
|
165
378
|
describe('enrichAndCapture', () => {
|
|
@@ -243,6 +456,21 @@ describe('Brain', () => {
|
|
|
243
456
|
const entry = vault.get('cap-5');
|
|
244
457
|
expect(entry!.tags.length).toBeGreaterThan(0);
|
|
245
458
|
});
|
|
459
|
+
|
|
460
|
+
it('should fire-and-forget sync to Cognee on capture', () => {
|
|
461
|
+
const cognee = makeMockCognee();
|
|
462
|
+
const hybridBrain = new Brain(vault, cognee);
|
|
463
|
+
hybridBrain.enrichAndCapture({
|
|
464
|
+
id: 'cap-cognee-1',
|
|
465
|
+
type: 'pattern',
|
|
466
|
+
domain: 'testing',
|
|
467
|
+
title: 'Cognee sync test',
|
|
468
|
+
severity: 'warning',
|
|
469
|
+
description: 'Testing fire-and-forget Cognee sync.',
|
|
470
|
+
tags: [],
|
|
471
|
+
});
|
|
472
|
+
expect(cognee.addEntries).toHaveBeenCalledTimes(1);
|
|
473
|
+
});
|
|
246
474
|
});
|
|
247
475
|
|
|
248
476
|
// ─── Duplicate Detection ────────────────────────────────────
|
|
@@ -354,6 +582,7 @@ describe('Brain', () => {
|
|
|
354
582
|
const stats = brain.getStats();
|
|
355
583
|
const sum =
|
|
356
584
|
stats.weights.semantic +
|
|
585
|
+
stats.weights.vector +
|
|
357
586
|
stats.weights.severity +
|
|
358
587
|
stats.weights.recency +
|
|
359
588
|
stats.weights.tagOverlap +
|
|
@@ -371,6 +600,11 @@ describe('Brain', () => {
|
|
|
371
600
|
const stats = brain.getStats();
|
|
372
601
|
expect(stats.weights.semantic).toBeCloseTo(0.4, 1);
|
|
373
602
|
});
|
|
603
|
+
|
|
604
|
+
it('should keep vector weight at 0 in base weights', () => {
|
|
605
|
+
const stats = brain.getStats();
|
|
606
|
+
expect(stats.weights.vector).toBe(0);
|
|
607
|
+
});
|
|
374
608
|
});
|
|
375
609
|
|
|
376
610
|
// ─── Vocabulary ─────────────────────────────────────────────
|
|
@@ -442,6 +676,7 @@ describe('Brain', () => {
|
|
|
442
676
|
expect(stats.vocabularySize).toBe(0);
|
|
443
677
|
expect(stats.feedbackCount).toBe(0);
|
|
444
678
|
expect(stats.weights.semantic).toBeCloseTo(0.4, 2);
|
|
679
|
+
expect(stats.weights.vector).toBe(0);
|
|
445
680
|
});
|
|
446
681
|
|
|
447
682
|
it('should return correct vocabulary size after seeding', () => {
|
|
@@ -475,7 +710,7 @@ describe('Brain', () => {
|
|
|
475
710
|
// ─── Get Relevant Patterns ──────────────────────────────────
|
|
476
711
|
|
|
477
712
|
describe('getRelevantPatterns', () => {
|
|
478
|
-
it('should return ranked results for query context', () => {
|
|
713
|
+
it('should return ranked results for query context', async () => {
|
|
479
714
|
vault.seed([
|
|
480
715
|
makeEntry({
|
|
481
716
|
id: 'rel-1',
|
|
@@ -493,12 +728,15 @@ describe('Brain', () => {
|
|
|
493
728
|
}),
|
|
494
729
|
]);
|
|
495
730
|
brain = new Brain(vault);
|
|
496
|
-
const results = brain.getRelevantPatterns({
|
|
731
|
+
const results = await brain.getRelevantPatterns({
|
|
732
|
+
query: 'authentication',
|
|
733
|
+
domain: 'security',
|
|
734
|
+
});
|
|
497
735
|
expect(results.length).toBeGreaterThan(0);
|
|
498
736
|
});
|
|
499
737
|
|
|
500
|
-
it('should return empty for no context matches', () => {
|
|
501
|
-
const results = brain.getRelevantPatterns({ query: 'nonexistent' });
|
|
738
|
+
it('should return empty for no context matches', async () => {
|
|
739
|
+
const results = await brain.getRelevantPatterns({ query: 'nonexistent' });
|
|
502
740
|
expect(results).toEqual([]);
|
|
503
741
|
});
|
|
504
742
|
});
|
|
@@ -506,13 +744,13 @@ describe('Brain', () => {
|
|
|
506
744
|
// ─── Graceful Degradation ───────────────────────────────────
|
|
507
745
|
|
|
508
746
|
describe('graceful degradation', () => {
|
|
509
|
-
it('should work without vocabulary (empty vault)', () => {
|
|
747
|
+
it('should work without vocabulary (empty vault)', async () => {
|
|
510
748
|
expect(brain.getVocabularySize()).toBe(0);
|
|
511
|
-
const results = brain.intelligentSearch('anything');
|
|
749
|
+
const results = await brain.intelligentSearch('anything');
|
|
512
750
|
expect(results).toEqual([]);
|
|
513
751
|
});
|
|
514
752
|
|
|
515
|
-
it('should fall back to severity + recency scoring when vocabulary is empty', () => {
|
|
753
|
+
it('should fall back to severity + recency scoring when vocabulary is empty', async () => {
|
|
516
754
|
vault.seed([
|
|
517
755
|
makeEntry({
|
|
518
756
|
id: 'gd-1',
|
|
@@ -523,7 +761,7 @@ describe('Brain', () => {
|
|
|
523
761
|
}),
|
|
524
762
|
]);
|
|
525
763
|
brain = new Brain(vault);
|
|
526
|
-
const results = brain.intelligentSearch('fallback test');
|
|
764
|
+
const results = await brain.intelligentSearch('fallback test');
|
|
527
765
|
expect(results.length).toBeGreaterThan(0);
|
|
528
766
|
expect(results[0].score).toBeGreaterThan(0);
|
|
529
767
|
});
|