@soleri/core 2.0.0 → 2.0.2

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 (68) hide show
  1. package/dist/brain/brain.d.ts +3 -12
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +13 -245
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/curator/curator.d.ts +28 -0
  6. package/dist/curator/curator.d.ts.map +1 -0
  7. package/dist/curator/curator.js +523 -0
  8. package/dist/curator/curator.js.map +1 -0
  9. package/dist/curator/types.d.ts +87 -0
  10. package/dist/curator/types.d.ts.map +1 -0
  11. package/dist/curator/types.js +3 -0
  12. package/dist/curator/types.js.map +1 -0
  13. package/dist/facades/types.d.ts +1 -1
  14. package/dist/index.d.ts +9 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +10 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/llm/llm-client.d.ts +28 -0
  19. package/dist/llm/llm-client.d.ts.map +1 -0
  20. package/dist/llm/llm-client.js +219 -0
  21. package/dist/llm/llm-client.js.map +1 -0
  22. package/dist/runtime/core-ops.d.ts +17 -0
  23. package/dist/runtime/core-ops.d.ts.map +1 -0
  24. package/dist/runtime/core-ops.js +448 -0
  25. package/dist/runtime/core-ops.js.map +1 -0
  26. package/dist/runtime/domain-ops.d.ts +25 -0
  27. package/dist/runtime/domain-ops.d.ts.map +1 -0
  28. package/dist/runtime/domain-ops.js +130 -0
  29. package/dist/runtime/domain-ops.js.map +1 -0
  30. package/dist/runtime/runtime.d.ts +19 -0
  31. package/dist/runtime/runtime.d.ts.map +1 -0
  32. package/dist/runtime/runtime.js +62 -0
  33. package/dist/runtime/runtime.js.map +1 -0
  34. package/dist/runtime/types.d.ts +39 -0
  35. package/dist/runtime/types.d.ts.map +1 -0
  36. package/dist/runtime/types.js +2 -0
  37. package/dist/{cognee → runtime}/types.js.map +1 -1
  38. package/dist/text/similarity.d.ts +8 -0
  39. package/dist/text/similarity.d.ts.map +1 -0
  40. package/dist/text/similarity.js +161 -0
  41. package/dist/text/similarity.js.map +1 -0
  42. package/package.json +6 -2
  43. package/src/__tests__/brain.test.ts +27 -222
  44. package/src/__tests__/core-ops.test.ts +190 -0
  45. package/src/__tests__/curator.test.ts +479 -0
  46. package/src/__tests__/domain-ops.test.ts +124 -0
  47. package/src/__tests__/llm-client.test.ts +69 -0
  48. package/src/__tests__/runtime.test.ts +93 -0
  49. package/src/brain/brain.ts +19 -275
  50. package/src/curator/curator.ts +662 -0
  51. package/src/curator/types.ts +114 -0
  52. package/src/index.ts +40 -11
  53. package/src/llm/llm-client.ts +316 -0
  54. package/src/runtime/core-ops.ts +472 -0
  55. package/src/runtime/domain-ops.ts +144 -0
  56. package/src/runtime/runtime.ts +71 -0
  57. package/src/runtime/types.ts +37 -0
  58. package/src/text/similarity.ts +168 -0
  59. package/dist/cognee/client.d.ts +0 -35
  60. package/dist/cognee/client.d.ts.map +0 -1
  61. package/dist/cognee/client.js +0 -289
  62. package/dist/cognee/client.js.map +0 -1
  63. package/dist/cognee/types.d.ts +0 -46
  64. package/dist/cognee/types.d.ts.map +0 -1
  65. package/dist/cognee/types.js +0 -3
  66. package/src/__tests__/cognee-client.test.ts +0 -524
  67. package/src/cognee/client.ts +0 -350
  68. package/src/cognee/types.ts +0 -62
@@ -1,9 +1,7 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
1
+ import { describe, it, expect, beforeEach, afterEach } 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';
7
5
 
8
6
  function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntry {
9
7
  return {
@@ -17,36 +15,6 @@ function makeEntry(overrides: Partial<IntelligenceEntry> = {}): IntelligenceEntr
17
15
  };
18
16
  }
19
17
 
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
-
50
18
  describe('Brain', () => {
51
19
  let vault: Vault;
52
20
  let brain: Brain;
@@ -85,12 +53,6 @@ describe('Brain', () => {
85
53
  const brain2 = new Brain(vault);
86
54
  expect(brain2.getVocabularySize()).toBeGreaterThan(0);
87
55
  });
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
- });
94
56
  });
95
57
 
96
58
  // ─── Intelligent Search ──────────────────────────────────────
@@ -127,52 +89,49 @@ describe('Brain', () => {
127
89
  brain = new Brain(vault);
128
90
  });
129
91
 
130
- it('should return ranked results', async () => {
131
- const results = await brain.intelligentSearch('validation input');
92
+ it('should return ranked results', () => {
93
+ const results = brain.intelligentSearch('validation input');
132
94
  expect(results.length).toBeGreaterThan(0);
133
95
  expect(results[0].entry.id).toBe('is-1');
134
96
  });
135
97
 
136
- it('should include score breakdown with vector field', async () => {
137
- const results = await brain.intelligentSearch('validation');
98
+ it('should include score breakdown', () => {
99
+ const results = brain.intelligentSearch('validation');
138
100
  expect(results.length).toBeGreaterThan(0);
139
101
  const breakdown = results[0].breakdown;
140
102
  expect(breakdown).toHaveProperty('semantic');
141
- expect(breakdown).toHaveProperty('vector');
142
103
  expect(breakdown).toHaveProperty('severity');
143
104
  expect(breakdown).toHaveProperty('recency');
144
105
  expect(breakdown).toHaveProperty('tagOverlap');
145
106
  expect(breakdown).toHaveProperty('domainMatch');
146
107
  expect(breakdown).toHaveProperty('total');
147
108
  expect(breakdown.total).toBe(results[0].score);
148
- // Without cognee, vector should be 0
149
- expect(breakdown.vector).toBe(0);
150
109
  });
151
110
 
152
- it('should return empty array for no matches', async () => {
153
- const results = await brain.intelligentSearch('xyznonexistent');
111
+ it('should return empty array for no matches', () => {
112
+ const results = brain.intelligentSearch('xyznonexistent');
154
113
  expect(results).toEqual([]);
155
114
  });
156
115
 
157
- it('should respect limit', async () => {
158
- const results = await brain.intelligentSearch('pattern', { limit: 1 });
116
+ it('should respect limit', () => {
117
+ const results = brain.intelligentSearch('pattern', { limit: 1 });
159
118
  expect(results.length).toBeLessThanOrEqual(1);
160
119
  });
161
120
 
162
- it('should filter by domain', async () => {
163
- const results = await brain.intelligentSearch('pattern', { domain: 'security' });
121
+ it('should filter by domain', () => {
122
+ const results = brain.intelligentSearch('pattern', { domain: 'security' });
164
123
  expect(results.every((r) => r.entry.domain === 'security')).toBe(true);
165
124
  });
166
125
 
167
- it('should boost domain matches when domain is specified', async () => {
168
- const withDomain = await brain.intelligentSearch('pattern', { domain: 'security' });
126
+ it('should boost domain matches when domain is specified', () => {
127
+ const withDomain = brain.intelligentSearch('pattern', { domain: 'security' });
169
128
  if (withDomain.length > 0) {
170
129
  expect(withDomain[0].breakdown.domainMatch).toBe(1.0);
171
130
  }
172
131
  });
173
132
 
174
- it('should boost severity in scoring', async () => {
175
- const results = await brain.intelligentSearch('pattern');
133
+ it('should boost severity in scoring', () => {
134
+ const results = brain.intelligentSearch('pattern');
176
135
  if (results.length >= 2) {
177
136
  const critical = results.find((r) => r.entry.severity === 'critical');
178
137
  const suggestion = results.find((r) => r.entry.severity === 'suggestion');
@@ -182,10 +141,8 @@ describe('Brain', () => {
182
141
  }
183
142
  });
184
143
 
185
- it('should boost tag overlap when tags provided', async () => {
186
- const results = await brain.intelligentSearch('pattern', {
187
- tags: ['validation', 'security'],
188
- });
144
+ it('should boost tag overlap when tags provided', () => {
145
+ const results = brain.intelligentSearch('pattern', { tags: ['validation', 'security'] });
189
146
  if (results.length > 0) {
190
147
  const secEntry = results.find((r) => r.entry.id === 'is-1');
191
148
  if (secEntry) {
@@ -194,142 +151,15 @@ describe('Brain', () => {
194
151
  }
195
152
  });
196
153
 
197
- it('should handle search on empty vault gracefully', async () => {
154
+ it('should handle search on empty vault gracefully', () => {
198
155
  const emptyVault = new Vault(':memory:');
199
156
  const emptyBrain = new Brain(emptyVault);
200
- const results = await emptyBrain.intelligentSearch('anything');
157
+ const results = emptyBrain.intelligentSearch('anything');
201
158
  expect(results).toEqual([]);
202
159
  emptyVault.close();
203
160
  });
204
161
  });
205
162
 
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 include vector scores from Cognee in breakdown', async () => {
231
- const cognee = makeMockCognee({
232
- searchResults: [{ id: 'hs-1', score: 0.92, text: 'Auth flow', searchType: 'INSIGHTS' }],
233
- });
234
- const hybridBrain = new Brain(vault, cognee);
235
- const results = await hybridBrain.intelligentSearch('authentication');
236
- expect(results.length).toBeGreaterThan(0);
237
- const authResult = results.find((r) => r.entry.id === 'hs-1');
238
- expect(authResult).toBeDefined();
239
- expect(authResult!.breakdown.vector).toBe(0.92);
240
- });
241
-
242
- it('should use cognee-aware weights when vector results are present', async () => {
243
- const cognee = makeMockCognee({
244
- searchResults: [{ id: 'hs-1', score: 0.9, text: 'Auth', searchType: 'INSIGHTS' }],
245
- });
246
- const hybridBrain = new Brain(vault, cognee);
247
- const results = await hybridBrain.intelligentSearch('authentication');
248
- // With cognee weights, vector contributes significantly
249
- const authResult = results.find((r) => r.entry.id === 'hs-1');
250
- expect(authResult).toBeDefined();
251
- // vector=0.9 * weight=0.35 = 0.315 contribution
252
- expect(authResult!.breakdown.vector).toBe(0.9);
253
- });
254
-
255
- it('should merge cognee-only entries into results', async () => {
256
- // hs-2 may not match FTS5 for "authentication" but Cognee finds it via semantic similarity
257
- const cognee = makeMockCognee({
258
- searchResults: [
259
- { id: 'hs-1', score: 0.95, text: 'Auth', searchType: 'INSIGHTS' },
260
- { id: 'hs-2', score: 0.6, text: 'Logging', searchType: 'INSIGHTS' },
261
- ],
262
- });
263
- const hybridBrain = new Brain(vault, cognee);
264
- const results = await hybridBrain.intelligentSearch('authentication');
265
- // Both entries should be in results (hs-2 merged from Cognee even if not in FTS5)
266
- const ids = results.map((r) => r.entry.id);
267
- expect(ids).toContain('hs-1');
268
- expect(ids).toContain('hs-2');
269
- const loggingResult = results.find((r) => r.entry.id === 'hs-2');
270
- expect(loggingResult).toBeDefined();
271
- expect(loggingResult!.breakdown.vector).toBe(0.6);
272
- });
273
-
274
- it('should fall back to FTS5-only on Cognee search error', async () => {
275
- const cognee = makeMockCognee({ searchError: true });
276
- const hybridBrain = new Brain(vault, cognee);
277
- const results = await hybridBrain.intelligentSearch('authentication');
278
- // Should still work, just without vector scores
279
- for (const r of results) {
280
- expect(r.breakdown.vector).toBe(0);
281
- }
282
- });
283
-
284
- it('should work without Cognee (backward compatible)', async () => {
285
- const noCogneeBrain = new Brain(vault);
286
- const results = await noCogneeBrain.intelligentSearch('authentication');
287
- for (const r of results) {
288
- expect(r.breakdown.vector).toBe(0);
289
- }
290
- });
291
-
292
- it('should handle unavailable Cognee gracefully', async () => {
293
- const cognee = makeMockCognee({ available: false });
294
- const hybridBrain = new Brain(vault, cognee);
295
- const results = await hybridBrain.intelligentSearch('authentication');
296
- for (const r of results) {
297
- expect(r.breakdown.vector).toBe(0);
298
- }
299
- // search should not have been called
300
- expect(cognee.search).not.toHaveBeenCalled();
301
- });
302
- });
303
-
304
- // ─── syncToCognee ──────────────────────────────────────────────
305
-
306
- describe('syncToCognee', () => {
307
- it('should return 0 when Cognee not available', async () => {
308
- const result = await brain.syncToCognee();
309
- expect(result).toEqual({ synced: 0, cognified: false });
310
- });
311
-
312
- it('should sync all entries and cognify', async () => {
313
- vault.seed([makeEntry({ id: 'sync-1' }), makeEntry({ id: 'sync-2' })]);
314
- const cognee = makeMockCognee();
315
- (cognee.addEntries as ReturnType<typeof vi.fn>).mockResolvedValue({ added: 2 });
316
- const hybridBrain = new Brain(vault, cognee);
317
- const result = await hybridBrain.syncToCognee();
318
- expect(result.synced).toBe(2);
319
- expect(result.cognified).toBe(true);
320
- expect(cognee.addEntries).toHaveBeenCalledTimes(1);
321
- expect(cognee.cognify).toHaveBeenCalledTimes(1);
322
- });
323
-
324
- it('should skip cognify when no entries added', async () => {
325
- const cognee = makeMockCognee();
326
- const hybridBrain = new Brain(vault, cognee);
327
- const result = await hybridBrain.syncToCognee();
328
- expect(result.synced).toBe(0);
329
- expect(result.cognified).toBe(false);
330
- });
331
- });
332
-
333
163
  // ─── Enrich and Capture ─────────────────────────────────────
334
164
 
335
165
  describe('enrichAndCapture', () => {
@@ -413,21 +243,6 @@ describe('Brain', () => {
413
243
  const entry = vault.get('cap-5');
414
244
  expect(entry!.tags.length).toBeGreaterThan(0);
415
245
  });
416
-
417
- it('should fire-and-forget sync to Cognee on capture', () => {
418
- const cognee = makeMockCognee();
419
- const hybridBrain = new Brain(vault, cognee);
420
- hybridBrain.enrichAndCapture({
421
- id: 'cap-cognee-1',
422
- type: 'pattern',
423
- domain: 'testing',
424
- title: 'Cognee sync test',
425
- severity: 'warning',
426
- description: 'Testing fire-and-forget Cognee sync.',
427
- tags: [],
428
- });
429
- expect(cognee.addEntries).toHaveBeenCalledTimes(1);
430
- });
431
246
  });
432
247
 
433
248
  // ─── Duplicate Detection ────────────────────────────────────
@@ -539,7 +354,6 @@ describe('Brain', () => {
539
354
  const stats = brain.getStats();
540
355
  const sum =
541
356
  stats.weights.semantic +
542
- stats.weights.vector +
543
357
  stats.weights.severity +
544
358
  stats.weights.recency +
545
359
  stats.weights.tagOverlap +
@@ -557,11 +371,6 @@ describe('Brain', () => {
557
371
  const stats = brain.getStats();
558
372
  expect(stats.weights.semantic).toBeCloseTo(0.4, 1);
559
373
  });
560
-
561
- it('should keep vector weight at 0 in base weights', () => {
562
- const stats = brain.getStats();
563
- expect(stats.weights.vector).toBe(0);
564
- });
565
374
  });
566
375
 
567
376
  // ─── Vocabulary ─────────────────────────────────────────────
@@ -633,7 +442,6 @@ describe('Brain', () => {
633
442
  expect(stats.vocabularySize).toBe(0);
634
443
  expect(stats.feedbackCount).toBe(0);
635
444
  expect(stats.weights.semantic).toBeCloseTo(0.4, 2);
636
- expect(stats.weights.vector).toBe(0);
637
445
  });
638
446
 
639
447
  it('should return correct vocabulary size after seeding', () => {
@@ -667,7 +475,7 @@ describe('Brain', () => {
667
475
  // ─── Get Relevant Patterns ──────────────────────────────────
668
476
 
669
477
  describe('getRelevantPatterns', () => {
670
- it('should return ranked results for query context', async () => {
478
+ it('should return ranked results for query context', () => {
671
479
  vault.seed([
672
480
  makeEntry({
673
481
  id: 'rel-1',
@@ -685,15 +493,12 @@ describe('Brain', () => {
685
493
  }),
686
494
  ]);
687
495
  brain = new Brain(vault);
688
- const results = await brain.getRelevantPatterns({
689
- query: 'authentication',
690
- domain: 'security',
691
- });
496
+ const results = brain.getRelevantPatterns({ query: 'authentication', domain: 'security' });
692
497
  expect(results.length).toBeGreaterThan(0);
693
498
  });
694
499
 
695
- it('should return empty for no context matches', async () => {
696
- const results = await brain.getRelevantPatterns({ query: 'nonexistent' });
500
+ it('should return empty for no context matches', () => {
501
+ const results = brain.getRelevantPatterns({ query: 'nonexistent' });
697
502
  expect(results).toEqual([]);
698
503
  });
699
504
  });
@@ -701,13 +506,13 @@ describe('Brain', () => {
701
506
  // ─── Graceful Degradation ───────────────────────────────────
702
507
 
703
508
  describe('graceful degradation', () => {
704
- it('should work without vocabulary (empty vault)', async () => {
509
+ it('should work without vocabulary (empty vault)', () => {
705
510
  expect(brain.getVocabularySize()).toBe(0);
706
- const results = await brain.intelligentSearch('anything');
511
+ const results = brain.intelligentSearch('anything');
707
512
  expect(results).toEqual([]);
708
513
  });
709
514
 
710
- it('should fall back to severity + recency scoring when vocabulary is empty', async () => {
515
+ it('should fall back to severity + recency scoring when vocabulary is empty', () => {
711
516
  vault.seed([
712
517
  makeEntry({
713
518
  id: 'gd-1',
@@ -718,7 +523,7 @@ describe('Brain', () => {
718
523
  }),
719
524
  ]);
720
525
  brain = new Brain(vault);
721
- const results = await brain.intelligentSearch('fallback test');
526
+ const results = brain.intelligentSearch('fallback test');
722
527
  expect(results.length).toBeGreaterThan(0);
723
528
  expect(results[0].score).toBeGreaterThan(0);
724
529
  });
@@ -0,0 +1,190 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdirSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { createAgentRuntime } from '../runtime/runtime.js';
6
+ import { createCoreOps } from '../runtime/core-ops.js';
7
+ import type { AgentRuntime } from '../runtime/types.js';
8
+ import type { OpDefinition } from '../facades/types.js';
9
+
10
+ describe('createCoreOps', () => {
11
+ let runtime: AgentRuntime;
12
+ let ops: OpDefinition[];
13
+ let plannerDir: string;
14
+
15
+ beforeEach(() => {
16
+ plannerDir = join(tmpdir(), 'core-ops-test-' + Date.now());
17
+ mkdirSync(plannerDir, { recursive: true });
18
+ runtime = createAgentRuntime({
19
+ agentId: 'test-core-ops',
20
+ vaultPath: ':memory:',
21
+ plansPath: join(plannerDir, 'plans.json'),
22
+ });
23
+ ops = createCoreOps(runtime);
24
+ });
25
+
26
+ afterEach(() => {
27
+ runtime.close();
28
+ rmSync(plannerDir, { recursive: true, force: true });
29
+ });
30
+
31
+ function findOp(name: string): OpDefinition {
32
+ const op = ops.find((o) => o.name === name);
33
+ if (!op) throw new Error(`Op "${name}" not found`);
34
+ return op;
35
+ }
36
+
37
+ it('should return 26 ops', () => {
38
+ expect(ops.length).toBe(26);
39
+ });
40
+
41
+ it('should have all expected op names', () => {
42
+ const names = ops.map((o) => o.name);
43
+ // Search/Vault
44
+ expect(names).toContain('search');
45
+ expect(names).toContain('vault_stats');
46
+ expect(names).toContain('list_all');
47
+ expect(names).toContain('register');
48
+ // Memory
49
+ expect(names).toContain('memory_search');
50
+ expect(names).toContain('memory_capture');
51
+ expect(names).toContain('memory_list');
52
+ expect(names).toContain('session_capture');
53
+ // Export
54
+ expect(names).toContain('export');
55
+ // Planning
56
+ expect(names).toContain('create_plan');
57
+ expect(names).toContain('get_plan');
58
+ expect(names).toContain('approve_plan');
59
+ expect(names).toContain('update_task');
60
+ expect(names).toContain('complete_plan');
61
+ // Brain
62
+ expect(names).toContain('record_feedback');
63
+ expect(names).toContain('rebuild_vocabulary');
64
+ expect(names).toContain('brain_stats');
65
+ expect(names).toContain('llm_status');
66
+ // Curator
67
+ expect(names).toContain('curator_status');
68
+ expect(names).toContain('curator_detect_duplicates');
69
+ expect(names).toContain('curator_contradictions');
70
+ expect(names).toContain('curator_resolve_contradiction');
71
+ expect(names).toContain('curator_groom');
72
+ expect(names).toContain('curator_groom_all');
73
+ expect(names).toContain('curator_consolidate');
74
+ expect(names).toContain('curator_health_audit');
75
+ });
76
+
77
+ it('search should query vault via brain', async () => {
78
+ runtime.vault.seed([{
79
+ id: 'co-1',
80
+ type: 'pattern',
81
+ domain: 'testing',
82
+ title: 'Test pattern for core ops',
83
+ severity: 'warning',
84
+ description: 'Core ops test.',
85
+ tags: ['test'],
86
+ }]);
87
+ runtime.brain.rebuildVocabulary();
88
+
89
+ // Re-create ops since brain state changed
90
+ ops = createCoreOps(runtime);
91
+ const results = await findOp('search').handler({ query: 'core ops test' }) as unknown[];
92
+ expect(results.length).toBeGreaterThan(0);
93
+ });
94
+
95
+ it('vault_stats should return counts', async () => {
96
+ runtime.vault.seed([{
97
+ id: 'vs-1',
98
+ type: 'pattern',
99
+ domain: 'd1',
100
+ title: 'T',
101
+ severity: 'warning',
102
+ description: 'D',
103
+ tags: ['t'],
104
+ }]);
105
+ const stats = await findOp('vault_stats').handler({}) as { totalEntries: number };
106
+ expect(stats.totalEntries).toBe(1);
107
+ });
108
+
109
+ it('create_plan + get_plan should work', async () => {
110
+ const created = await findOp('create_plan').handler({
111
+ objective: 'Test plan',
112
+ scope: 'core-ops test',
113
+ tasks: [{ title: 'Task 1', description: 'Do something' }],
114
+ }) as { created: boolean; plan: { id: string; status: string } };
115
+ expect(created.created).toBe(true);
116
+ expect(created.plan.status).toBe('draft');
117
+
118
+ const plan = await findOp('get_plan').handler({ planId: created.plan.id }) as { id: string };
119
+ expect(plan.id).toBe(created.plan.id);
120
+ });
121
+
122
+ it('brain_stats should return stats', async () => {
123
+ const stats = await findOp('brain_stats').handler({}) as { vocabularySize: number; feedbackCount: number };
124
+ expect(stats.vocabularySize).toBe(0);
125
+ expect(stats.feedbackCount).toBe(0);
126
+ });
127
+
128
+ it('llm_status should return provider info', async () => {
129
+ const status = await findOp('llm_status').handler({}) as {
130
+ providers: {
131
+ openai: { available: boolean };
132
+ anthropic: { available: boolean };
133
+ };
134
+ routes: unknown[];
135
+ };
136
+ expect(typeof status.providers.openai.available).toBe('boolean');
137
+ expect(typeof status.providers.anthropic.available).toBe('boolean');
138
+ expect(Array.isArray(status.routes)).toBe(true);
139
+ });
140
+
141
+ it('curator_status should return initialized', async () => {
142
+ const status = await findOp('curator_status').handler({}) as { initialized: boolean };
143
+ expect(status.initialized).toBe(true);
144
+ });
145
+
146
+ it('curator_health_audit should return score', async () => {
147
+ runtime.vault.seed([{
148
+ id: 'ha-1',
149
+ type: 'pattern',
150
+ domain: 'testing',
151
+ title: 'Health pattern',
152
+ severity: 'warning',
153
+ description: 'Testing health.',
154
+ tags: ['health'],
155
+ }]);
156
+ runtime.curator.groomAll();
157
+ const result = await findOp('curator_health_audit').handler({}) as { score: number };
158
+ expect(result.score).toBeGreaterThan(0);
159
+ });
160
+
161
+ it('memory_capture + memory_search should work', async () => {
162
+ await findOp('memory_capture').handler({
163
+ projectPath: '/test',
164
+ type: 'lesson',
165
+ context: 'Testing core ops',
166
+ summary: 'Core ops memory test works',
167
+ topics: ['testing'],
168
+ filesModified: [],
169
+ toolsUsed: [],
170
+ });
171
+
172
+ const results = await findOp('memory_search').handler({ query: 'core ops memory' }) as unknown[];
173
+ expect(results.length).toBeGreaterThan(0);
174
+ });
175
+
176
+ it('export should return bundles', async () => {
177
+ runtime.vault.seed([{
178
+ id: 'exp-1',
179
+ type: 'pattern',
180
+ domain: 'security',
181
+ title: 'Export test',
182
+ severity: 'warning',
183
+ description: 'Testing export.',
184
+ tags: ['export'],
185
+ }]);
186
+ const result = await findOp('export').handler({}) as { exported: boolean; totalEntries: number };
187
+ expect(result.exported).toBe(true);
188
+ expect(result.totalEntries).toBe(1);
189
+ });
190
+ });