@soleri/core 8.0.0 → 9.0.0
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 +1 -8
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +5 -134
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/knowledge-synthesizer.d.ts.map +1 -1
- package/dist/brain/knowledge-synthesizer.js +0 -2
- package/dist/brain/knowledge-synthesizer.js.map +1 -1
- package/dist/cognee/client.d.ts +5 -0
- package/dist/cognee/client.d.ts.map +1 -1
- package/dist/cognee/client.js +83 -16
- package/dist/cognee/client.js.map +1 -1
- package/dist/cognee/sync-manager.d.ts +67 -8
- package/dist/cognee/sync-manager.d.ts.map +1 -1
- package/dist/cognee/sync-manager.js +129 -32
- package/dist/cognee/sync-manager.js.map +1 -1
- package/dist/cognee/types.d.ts +16 -0
- package/dist/cognee/types.d.ts.map +1 -1
- package/dist/context/context-engine.d.ts +2 -5
- package/dist/context/context-engine.d.ts.map +1 -1
- package/dist/context/context-engine.js +4 -31
- package/dist/context/context-engine.js.map +1 -1
- package/dist/curator/classifier.d.ts.map +1 -1
- package/dist/curator/classifier.js +0 -2
- package/dist/curator/classifier.js.map +1 -1
- package/dist/curator/curator.d.ts +2 -5
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +4 -23
- package/dist/curator/curator.js.map +1 -1
- package/dist/curator/quality-gate.d.ts.map +1 -1
- package/dist/curator/quality-gate.js +0 -2
- package/dist/curator/quality-gate.js.map +1 -1
- package/dist/domain-packs/index.d.ts +0 -3
- package/dist/domain-packs/index.d.ts.map +1 -1
- package/dist/domain-packs/index.js +0 -3
- package/dist/domain-packs/index.js.map +1 -1
- package/dist/domain-packs/loader.d.ts.map +1 -1
- package/dist/domain-packs/loader.js +20 -4
- package/dist/domain-packs/loader.js.map +1 -1
- package/dist/domain-packs/pack-runtime.d.ts +5 -5
- package/dist/domain-packs/pack-runtime.d.ts.map +1 -1
- package/dist/domain-packs/pack-runtime.js +2 -2
- package/dist/domain-packs/pack-runtime.js.map +1 -1
- package/dist/domain-packs/types.d.ts +8 -2
- package/dist/domain-packs/types.d.ts.map +1 -1
- package/dist/domain-packs/types.js.map +1 -1
- package/dist/engine/bin/soleri-engine.js +18 -7
- package/dist/engine/bin/soleri-engine.js.map +1 -1
- package/dist/engine/core-ops.d.ts.map +1 -1
- package/dist/engine/core-ops.js +11 -6
- package/dist/engine/core-ops.js.map +1 -1
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +1 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/module-manifest.d.ts +28 -0
- package/dist/engine/module-manifest.d.ts.map +1 -0
- package/dist/engine/module-manifest.js +85 -0
- package/dist/engine/module-manifest.js.map +1 -0
- package/dist/engine/register-engine.d.ts +19 -0
- package/dist/engine/register-engine.d.ts.map +1 -1
- package/dist/engine/register-engine.js +15 -9
- package/dist/engine/register-engine.js.map +1 -1
- package/dist/index.d.ts +5 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/intake/content-classifier.d.ts.map +1 -1
- package/dist/intake/content-classifier.js +0 -2
- package/dist/intake/content-classifier.js.map +1 -1
- package/dist/intelligence/types.d.ts +7 -0
- package/dist/intelligence/types.d.ts.map +1 -1
- package/dist/llm/llm-client.d.ts.map +1 -1
- package/dist/llm/llm-client.js +8 -4
- package/dist/llm/llm-client.js.map +1 -1
- package/dist/llm/oauth-discovery.d.ts +0 -8
- package/dist/llm/oauth-discovery.d.ts.map +1 -1
- package/dist/llm/oauth-discovery.js +0 -19
- package/dist/llm/oauth-discovery.js.map +1 -1
- package/dist/llm/types.d.ts +4 -2
- package/dist/llm/types.d.ts.map +1 -1
- package/dist/packs/pack-installer.d.ts +2 -1
- package/dist/packs/pack-installer.d.ts.map +1 -1
- package/dist/packs/pack-installer.js +10 -1
- package/dist/packs/pack-installer.js.map +1 -1
- package/dist/persistence/index.d.ts +0 -1
- package/dist/persistence/index.d.ts.map +1 -1
- package/dist/persistence/index.js +0 -1
- package/dist/persistence/index.js.map +1 -1
- package/dist/persistence/types.d.ts +2 -6
- package/dist/persistence/types.d.ts.map +1 -1
- package/dist/persona/defaults.d.ts +16 -0
- package/dist/persona/defaults.d.ts.map +1 -0
- package/dist/persona/defaults.js +78 -0
- package/dist/persona/defaults.js.map +1 -0
- package/dist/persona/index.d.ts +5 -0
- package/dist/persona/index.d.ts.map +1 -0
- package/dist/persona/index.js +4 -0
- package/dist/persona/index.js.map +1 -0
- package/dist/persona/loader.d.ts +11 -0
- package/dist/persona/loader.d.ts.map +1 -0
- package/dist/persona/loader.js +45 -0
- package/dist/persona/loader.js.map +1 -0
- package/dist/persona/prompt-generator.d.ts +13 -0
- package/dist/persona/prompt-generator.d.ts.map +1 -0
- package/dist/persona/prompt-generator.js +89 -0
- package/dist/persona/prompt-generator.js.map +1 -0
- package/dist/persona/types.d.ts +56 -0
- package/dist/persona/types.d.ts.map +1 -0
- package/dist/persona/types.js +9 -0
- package/dist/persona/types.js.map +1 -0
- package/dist/plugins/index.d.ts +4 -0
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +4 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/plugin-registry.d.ts +4 -0
- package/dist/plugins/plugin-registry.d.ts.map +1 -1
- package/dist/plugins/plugin-registry.js +4 -0
- package/dist/plugins/plugin-registry.js.map +1 -1
- package/dist/plugins/types.d.ts +36 -31
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/types.js +6 -3
- package/dist/plugins/types.js.map +1 -1
- package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
- package/dist/runtime/admin-extra-ops.js +5 -27
- package/dist/runtime/admin-extra-ops.js.map +1 -1
- package/dist/runtime/admin-ops.d.ts.map +1 -1
- package/dist/runtime/admin-ops.js +5 -37
- package/dist/runtime/admin-ops.js.map +1 -1
- package/dist/runtime/capture-ops.d.ts.map +1 -1
- package/dist/runtime/capture-ops.js +32 -16
- package/dist/runtime/capture-ops.js.map +1 -1
- package/dist/runtime/claude-md-helpers.d.ts +0 -9
- package/dist/runtime/claude-md-helpers.d.ts.map +1 -1
- package/dist/runtime/claude-md-helpers.js +1 -14
- package/dist/runtime/claude-md-helpers.js.map +1 -1
- package/dist/runtime/cognee-sync-ops.d.ts +2 -2
- package/dist/runtime/cognee-sync-ops.d.ts.map +1 -1
- package/dist/runtime/cognee-sync-ops.js +45 -7
- package/dist/runtime/cognee-sync-ops.js.map +1 -1
- package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
- package/dist/runtime/facades/admin-facade.js +1 -2
- package/dist/runtime/facades/admin-facade.js.map +1 -1
- package/dist/runtime/facades/index.d.ts +1 -1
- package/dist/runtime/facades/index.d.ts.map +1 -1
- package/dist/runtime/facades/index.js +1 -10
- package/dist/runtime/facades/index.js.map +1 -1
- package/dist/runtime/pack-ops.d.ts +3 -0
- package/dist/runtime/pack-ops.d.ts.map +1 -1
- package/dist/runtime/pack-ops.js +18 -1
- package/dist/runtime/pack-ops.js.map +1 -1
- package/dist/runtime/plugin-ops.d.ts.map +1 -1
- package/dist/runtime/plugin-ops.js +3 -0
- package/dist/runtime/plugin-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +14 -53
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/session-briefing.d.ts.map +1 -1
- package/dist/runtime/session-briefing.js +14 -0
- package/dist/runtime/session-briefing.js.map +1 -1
- package/dist/runtime/types.d.ts +6 -8
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
- package/dist/runtime/vault-linking-ops.js +42 -4
- package/dist/runtime/vault-linking-ops.js.map +1 -1
- package/dist/runtime/vault-sharing-ops.d.ts.map +1 -1
- package/dist/runtime/vault-sharing-ops.js +53 -3
- package/dist/runtime/vault-sharing-ops.js.map +1 -1
- package/dist/vault/linking.d.ts +37 -0
- package/dist/vault/linking.d.ts.map +1 -1
- package/dist/vault/linking.js +73 -0
- package/dist/vault/linking.js.map +1 -1
- package/dist/vault/vault.d.ts +9 -2
- package/dist/vault/vault.d.ts.map +1 -1
- package/dist/vault/vault.js +21 -12
- package/dist/vault/vault.js.map +1 -1
- package/package.json +6 -4
- package/src/__tests__/curator-pipeline-e2e.test.ts +187 -0
- package/src/__tests__/module-manifest-drift.test.ts +59 -0
- package/src/brain/brain.ts +4 -157
- package/src/brain/knowledge-synthesizer.ts +0 -2
- package/src/context/context-engine.ts +3 -31
- package/src/curator/classifier.ts +0 -2
- package/src/curator/curator.ts +5 -28
- package/src/curator/quality-gate.ts +0 -2
- package/src/domain-packs/index.ts +0 -6
- package/src/domain-packs/loader.ts +25 -5
- package/src/domain-packs/pack-runtime.ts +6 -6
- package/src/domain-packs/types.ts +8 -2
- package/src/engine/bin/soleri-engine.ts +23 -7
- package/src/engine/core-ops.ts +11 -6
- package/src/engine/index.ts +2 -0
- package/src/engine/module-manifest.ts +99 -0
- package/src/engine/register-engine.ts +21 -9
- package/src/index.ts +20 -17
- package/src/intake/content-classifier.ts +0 -2
- package/src/intelligence/types.ts +8 -0
- package/src/llm/llm-client.ts +12 -6
- package/src/llm/oauth-discovery.ts +0 -18
- package/src/llm/types.ts +4 -2
- package/src/packs/pack-installer.ts +16 -1
- package/src/persistence/index.ts +0 -1
- package/src/persistence/types.ts +2 -6
- package/src/persona/defaults.ts +96 -0
- package/src/persona/index.ts +9 -0
- package/src/persona/loader.ts +50 -0
- package/src/persona/prompt-generator.ts +109 -0
- package/src/persona/types.ts +72 -0
- package/src/plugins/index.ts +4 -0
- package/src/plugins/plugin-registry.ts +6 -1
- package/src/plugins/types.ts +10 -5
- package/src/runtime/admin-extra-ops.ts +5 -28
- package/src/runtime/admin-ops.ts +5 -38
- package/src/runtime/capture-ops.ts +33 -14
- package/src/runtime/claude-md-helpers.ts +1 -19
- package/src/runtime/facades/admin-facade.ts +1 -2
- package/src/runtime/facades/index.ts +1 -11
- package/src/runtime/pack-ops.ts +26 -1
- package/src/runtime/plugin-ops.ts +3 -0
- package/src/runtime/runtime.ts +14 -54
- package/src/runtime/session-briefing.ts +14 -0
- package/src/runtime/types.ts +6 -8
- package/src/runtime/vault-linking-ops.ts +43 -4
- package/src/runtime/vault-sharing-ops.ts +63 -4
- package/src/vault/linking.ts +94 -0
- package/src/vault/vault.ts +24 -12
- package/src/__tests__/cognee-client-gaps.test.ts +0 -474
- package/src/__tests__/cognee-client.test.ts +0 -524
- package/src/__tests__/cognee-hybrid-search.test.ts +0 -492
- package/src/__tests__/cognee-integration.test.ts +0 -80
- package/src/__tests__/cognee-sync-manager-deep.test.ts +0 -654
- package/src/__tests__/cognee-sync-manager.test.ts +0 -104
- package/src/__tests__/postgres-provider.test.ts +0 -116
- package/src/cognee/client.ts +0 -370
- package/src/cognee/sync-manager.ts +0 -389
- package/src/cognee/types.ts +0 -62
- package/src/health/doctor-checks.ts +0 -115
- package/src/persistence/postgres-provider.ts +0 -310
- package/src/runtime/cognee-sync-ops.ts +0 -63
- package/src/runtime/facades/cognee-facade.ts +0 -164
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soleri/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Shared engine for Soleri agents — vault, brain, planner, LLM utilities, and facade infrastructure.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
".": {
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
33
|
"import": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./module-manifest": {
|
|
36
|
+
"types": "./dist/engine/module-manifest.d.ts",
|
|
37
|
+
"import": "./dist/engine/module-manifest.js"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"publishConfig": {
|
|
@@ -50,7 +54,6 @@
|
|
|
50
54
|
"devDependencies": {
|
|
51
55
|
"@types/better-sqlite3": "^7.6.13",
|
|
52
56
|
"@types/pdf-parse": "^1.1.5",
|
|
53
|
-
"@types/pg": "^8.11.0",
|
|
54
57
|
"@vitest/coverage-v8": "^4.0.18"
|
|
55
58
|
},
|
|
56
59
|
"peerDependencies": {
|
|
@@ -69,7 +72,6 @@
|
|
|
69
72
|
"node": ">=18.0.0"
|
|
70
73
|
},
|
|
71
74
|
"optionalDependencies": {
|
|
72
|
-
"pdf-parse": "^1.1.4"
|
|
73
|
-
"pg": "^8.13.0"
|
|
75
|
+
"pdf-parse": "^1.1.4"
|
|
74
76
|
}
|
|
75
77
|
}
|
|
@@ -94,6 +94,56 @@ beforeAll(() => {
|
|
|
94
94
|
if (!entry) return { skipped: true, reason: 'entry not found' };
|
|
95
95
|
return classifyEntry(entry, null); // No LLM in tests
|
|
96
96
|
});
|
|
97
|
+
|
|
98
|
+
// ─── 9 additional handlers for full Salvador parity (#216) ────
|
|
99
|
+
runner.registerHandler('enrich-frontmatter', async (job) => {
|
|
100
|
+
const entry = vault.get(job.entryId ?? '');
|
|
101
|
+
if (!entry) return { skipped: true, reason: 'entry not found' };
|
|
102
|
+
return curator.enrichMetadata(entry.id);
|
|
103
|
+
});
|
|
104
|
+
runner.registerHandler('detect-staleness', async (job) => {
|
|
105
|
+
const entry = vault.get(job.entryId ?? '');
|
|
106
|
+
if (!entry) return { skipped: true, reason: 'entry not found' };
|
|
107
|
+
const entryTimestamp = (entry.validFrom ?? 0) * 1000 || Date.now();
|
|
108
|
+
const ageMs = Date.now() - entryTimestamp;
|
|
109
|
+
const isStale = ageMs > 90 * 86400000;
|
|
110
|
+
return { stale: isStale, ageDays: Math.floor(ageMs / 86400000), entryId: entry.id };
|
|
111
|
+
});
|
|
112
|
+
runner.registerHandler('detect-duplicate', async (job) => {
|
|
113
|
+
const entry = vault.get(job.entryId ?? '');
|
|
114
|
+
if (!entry) return { skipped: true, reason: 'entry not found' };
|
|
115
|
+
return curator.detectDuplicates(entry.id);
|
|
116
|
+
});
|
|
117
|
+
runner.registerHandler('detect-contradiction', async (job) => {
|
|
118
|
+
const entry = vault.get(job.entryId ?? '');
|
|
119
|
+
if (!entry) return { skipped: true, reason: 'entry not found' };
|
|
120
|
+
const contradictions = curator.detectContradictions(0.4);
|
|
121
|
+
const relevant = contradictions.filter(
|
|
122
|
+
(c) => c.patternId === job.entryId || c.antipatternId === job.entryId,
|
|
123
|
+
);
|
|
124
|
+
return { found: relevant.length, contradictions: relevant };
|
|
125
|
+
});
|
|
126
|
+
runner.registerHandler('consolidate-duplicates', async () => {
|
|
127
|
+
return curator.consolidate({ dryRun: false, staleDaysThreshold: 90 });
|
|
128
|
+
});
|
|
129
|
+
runner.registerHandler('archive-stale', async () => {
|
|
130
|
+
const result = curator.consolidate({ dryRun: false, staleDaysThreshold: 90 });
|
|
131
|
+
return { archived: result.staleEntries.length, result };
|
|
132
|
+
});
|
|
133
|
+
runner.registerHandler('cognee-ingest', async (job) => {
|
|
134
|
+
// No Cognee in tests — graceful degradation
|
|
135
|
+
return { skipped: true, reason: 'cognee not available' };
|
|
136
|
+
});
|
|
137
|
+
runner.registerHandler('cognee-cognify', async () => {
|
|
138
|
+
return { skipped: true, reason: 'cognee not available' };
|
|
139
|
+
});
|
|
140
|
+
runner.registerHandler('verify-searchable', async (job) => {
|
|
141
|
+
const entry = vault.get(job.entryId ?? '');
|
|
142
|
+
if (!entry) return { skipped: true, reason: 'entry not found' };
|
|
143
|
+
const searchResults = vault.search(entry.title, { limit: 1 });
|
|
144
|
+
const found = searchResults.some((r) => r.entry.id === entry.id);
|
|
145
|
+
return { searchable: found, entryId: entry.id };
|
|
146
|
+
});
|
|
97
147
|
});
|
|
98
148
|
|
|
99
149
|
afterAll(() => {
|
|
@@ -266,6 +316,143 @@ describe('Classifier — graceful degradation', () => {
|
|
|
266
316
|
});
|
|
267
317
|
});
|
|
268
318
|
|
|
319
|
+
// ─── Salvador Parity Handlers (#216) ─────────────────────────────────
|
|
320
|
+
|
|
321
|
+
describe('Pipeline Runner — Salvador parity handlers (#216)', () => {
|
|
322
|
+
it('enrich-frontmatter enriches entry metadata', async () => {
|
|
323
|
+
const id = queue.enqueue('enrich-frontmatter', { entryId: 'pattern-circuit-breaker' });
|
|
324
|
+
await runner.processOnce();
|
|
325
|
+
const job = queue.get(id);
|
|
326
|
+
expect(job!.status).toBe('completed');
|
|
327
|
+
expect(job!.result).toBeDefined();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('detect-staleness checks entry age', async () => {
|
|
331
|
+
const id = queue.enqueue('detect-staleness', { entryId: 'pattern-circuit-breaker' });
|
|
332
|
+
await runner.processOnce();
|
|
333
|
+
const job = queue.get(id);
|
|
334
|
+
expect(job!.status).toBe('completed');
|
|
335
|
+
const result = job!.result as { stale: boolean; ageDays: number };
|
|
336
|
+
expect(typeof result.stale).toBe('boolean');
|
|
337
|
+
expect(typeof result.ageDays).toBe('number');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('detect-duplicate runs dedup on specific entry', async () => {
|
|
341
|
+
const id = queue.enqueue('detect-duplicate', { entryId: 'pattern-circuit-breaker' });
|
|
342
|
+
await runner.processOnce();
|
|
343
|
+
const job = queue.get(id);
|
|
344
|
+
expect(job!.status).toBe('completed');
|
|
345
|
+
expect(job!.result).toBeDefined();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('detect-contradiction finds pattern/anti-pattern conflicts', async () => {
|
|
349
|
+
const id = queue.enqueue('detect-contradiction', { entryId: 'pattern-circuit-breaker' });
|
|
350
|
+
await runner.processOnce();
|
|
351
|
+
const job = queue.get(id);
|
|
352
|
+
expect(job!.status).toBe('completed');
|
|
353
|
+
const result = job!.result as { found: number };
|
|
354
|
+
expect(typeof result.found).toBe('number');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('consolidate-duplicates runs consolidation', async () => {
|
|
358
|
+
const id = queue.enqueue('consolidate-duplicates', {});
|
|
359
|
+
await runner.processOnce();
|
|
360
|
+
const job = queue.get(id);
|
|
361
|
+
expect(job!.status).toBe('completed');
|
|
362
|
+
expect(job!.result).toBeDefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('archive-stale archives old entries', async () => {
|
|
366
|
+
const id = queue.enqueue('archive-stale', {});
|
|
367
|
+
await runner.processOnce();
|
|
368
|
+
const job = queue.get(id);
|
|
369
|
+
expect(job!.status).toBe('completed');
|
|
370
|
+
const result = job!.result as { archived: number };
|
|
371
|
+
expect(typeof result.archived).toBe('number');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('cognee-ingest degrades gracefully without Cognee', async () => {
|
|
375
|
+
const id = queue.enqueue('cognee-ingest', { entryId: 'pattern-circuit-breaker' });
|
|
376
|
+
await runner.processOnce();
|
|
377
|
+
const job = queue.get(id);
|
|
378
|
+
expect(job!.status).toBe('completed');
|
|
379
|
+
expect((job!.result as Record<string, unknown>).skipped).toBe(true);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('cognee-cognify degrades gracefully without Cognee', async () => {
|
|
383
|
+
const id = queue.enqueue('cognee-cognify', {});
|
|
384
|
+
await runner.processOnce();
|
|
385
|
+
const job = queue.get(id);
|
|
386
|
+
expect(job!.status).toBe('completed');
|
|
387
|
+
expect((job!.result as Record<string, unknown>).skipped).toBe(true);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('verify-searchable confirms entry is FTS-indexed', async () => {
|
|
391
|
+
const id = queue.enqueue('verify-searchable', { entryId: 'pattern-circuit-breaker' });
|
|
392
|
+
await runner.processOnce();
|
|
393
|
+
const job = queue.get(id);
|
|
394
|
+
expect(job!.status).toBe('completed');
|
|
395
|
+
const result = job!.result as { searchable: boolean };
|
|
396
|
+
expect(result.searchable).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('all handlers handle missing entries gracefully', async () => {
|
|
400
|
+
const types = [
|
|
401
|
+
'enrich-frontmatter',
|
|
402
|
+
'detect-staleness',
|
|
403
|
+
'detect-duplicate',
|
|
404
|
+
'detect-contradiction',
|
|
405
|
+
'verify-searchable',
|
|
406
|
+
];
|
|
407
|
+
for (const type of types) {
|
|
408
|
+
const id = queue.enqueue(type, { entryId: 'nonexistent' });
|
|
409
|
+
await runner.processOnce();
|
|
410
|
+
const job = queue.get(id);
|
|
411
|
+
expect(job!.status).toBe('completed');
|
|
412
|
+
expect((job!.result as Record<string, unknown>).skipped).toBe(true);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe('Full Salvador DAG — 8-step curator pipeline', () => {
|
|
418
|
+
it('runs the complete quality pipeline in DAG order', async () => {
|
|
419
|
+
const entryId = 'pattern-semantic-tokens';
|
|
420
|
+
const pipelineId = 'full-salvador-dag';
|
|
421
|
+
|
|
422
|
+
const step1 = queue.enqueue('quality-gate', { entryId, pipelineId });
|
|
423
|
+
const step2 = queue.enqueue('enrich-frontmatter', { entryId, pipelineId, dependsOn: [step1] });
|
|
424
|
+
const step3 = queue.enqueue('tag-normalize', { entryId, pipelineId, dependsOn: [step2] });
|
|
425
|
+
const step4 = queue.enqueue('dedup-check', { entryId, pipelineId, dependsOn: [step3] });
|
|
426
|
+
const step5 = queue.enqueue('detect-contradiction', {
|
|
427
|
+
entryId,
|
|
428
|
+
pipelineId,
|
|
429
|
+
dependsOn: [step4],
|
|
430
|
+
});
|
|
431
|
+
const step6 = queue.enqueue('cognee-ingest', { entryId, pipelineId, dependsOn: [step5] });
|
|
432
|
+
const step7 = queue.enqueue('auto-link', { entryId, pipelineId, dependsOn: [step6] });
|
|
433
|
+
const step8 = queue.enqueue('verify-searchable', { entryId, pipelineId, dependsOn: [step7] });
|
|
434
|
+
|
|
435
|
+
// Drain the DAG
|
|
436
|
+
for (let i = 0; i < 15; i++) {
|
|
437
|
+
await runner.processOnce(20);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const jobs = queue.getByPipeline(pipelineId);
|
|
441
|
+
const statuses = jobs.map((j) => `${j.type}:${j.status}`);
|
|
442
|
+
|
|
443
|
+
expect(statuses).toEqual([
|
|
444
|
+
'quality-gate:completed',
|
|
445
|
+
'enrich-frontmatter:completed',
|
|
446
|
+
'tag-normalize:completed',
|
|
447
|
+
'dedup-check:completed',
|
|
448
|
+
'detect-contradiction:completed',
|
|
449
|
+
'cognee-ingest:completed',
|
|
450
|
+
'auto-link:completed',
|
|
451
|
+
'verify-searchable:completed',
|
|
452
|
+
]);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
269
456
|
// ─── Event Bus Integration ──────────────────────────────────────────
|
|
270
457
|
|
|
271
458
|
describe('Event Bus — curator events', () => {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract Drift Detection — Issue #227
|
|
3
|
+
*
|
|
4
|
+
* Ensures ENGINE_MODULE_MANIFEST (used by forge for template generation)
|
|
5
|
+
* stays in sync with ENGINE_MODULES (used by register-engine at runtime).
|
|
6
|
+
*
|
|
7
|
+
* If this test fails, someone added/removed/renamed a module in one place
|
|
8
|
+
* but not the other. Fix by updating both files to match.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { ENGINE_MODULE_MANIFEST } from '../engine/module-manifest.js';
|
|
13
|
+
import { ENGINE_MODULES } from '../engine/register-engine.js';
|
|
14
|
+
|
|
15
|
+
describe('Module manifest drift detection', () => {
|
|
16
|
+
const manifestSuffixes = ENGINE_MODULE_MANIFEST.map((m) => m.suffix);
|
|
17
|
+
const runtimeSuffixes = ENGINE_MODULES.map((m) => m.suffix);
|
|
18
|
+
|
|
19
|
+
it('manifest and runtime have the same number of modules', () => {
|
|
20
|
+
expect(manifestSuffixes).toHaveLength(runtimeSuffixes.length);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('manifest and runtime have identical suffixes in the same order', () => {
|
|
24
|
+
expect(manifestSuffixes).toEqual(runtimeSuffixes);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('conditional flags match between manifest and runtime', () => {
|
|
28
|
+
for (const manifest of ENGINE_MODULE_MANIFEST) {
|
|
29
|
+
const runtime = ENGINE_MODULES.find((m) => m.suffix === manifest.suffix);
|
|
30
|
+
expect(runtime, `module "${manifest.suffix}" missing from ENGINE_MODULES`).toBeDefined();
|
|
31
|
+
|
|
32
|
+
const runtimeConditional = runtime!.condition !== undefined;
|
|
33
|
+
const manifestConditional = manifest.conditional === true;
|
|
34
|
+
|
|
35
|
+
expect(runtimeConditional, `conditional flag mismatch for "${manifest.suffix}"`).toBe(
|
|
36
|
+
manifestConditional,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('no runtime module is missing from manifest', () => {
|
|
42
|
+
for (const runtime of ENGINE_MODULES) {
|
|
43
|
+
const manifest = ENGINE_MODULE_MANIFEST.find((m) => m.suffix === runtime.suffix);
|
|
44
|
+
expect(
|
|
45
|
+
manifest,
|
|
46
|
+
`module "${runtime.suffix}" exists in ENGINE_MODULES but missing from ENGINE_MODULE_MANIFEST`,
|
|
47
|
+
).toBeDefined();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('every manifest entry has at least one keyOp', () => {
|
|
52
|
+
for (const entry of ENGINE_MODULE_MANIFEST) {
|
|
53
|
+
expect(
|
|
54
|
+
entry.keyOps.length,
|
|
55
|
+
`module "${entry.suffix}" has empty keyOps — placeholder tables need at least one op`,
|
|
56
|
+
).toBeGreaterThan(0);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
package/src/brain/brain.ts
CHANGED
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
cosineSimilarity,
|
|
11
11
|
jaccardSimilarity,
|
|
12
12
|
} from '../text/similarity.js';
|
|
13
|
-
import type { CogneeClient } from '../cognee/client.js';
|
|
14
13
|
import type {
|
|
15
14
|
ScoringWeights,
|
|
16
15
|
ScoreBreakdown,
|
|
@@ -44,15 +43,6 @@ const DEFAULT_WEIGHTS: ScoringWeights = {
|
|
|
44
43
|
domainMatch: 0.15,
|
|
45
44
|
};
|
|
46
45
|
|
|
47
|
-
const COGNEE_WEIGHTS: ScoringWeights = {
|
|
48
|
-
semantic: 0.25,
|
|
49
|
-
vector: 0.35,
|
|
50
|
-
severity: 0.1,
|
|
51
|
-
temporalDecay: 0.1,
|
|
52
|
-
tagOverlap: 0.1,
|
|
53
|
-
domainMatch: 0.1,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
46
|
const WEIGHT_BOUND = 0.15;
|
|
57
47
|
const FEEDBACK_THRESHOLD = 30;
|
|
58
48
|
const DUPLICATE_BLOCK_THRESHOLD = 0.8;
|
|
@@ -62,14 +52,12 @@ const RECENCY_HALF_LIFE_DAYS = 365;
|
|
|
62
52
|
export class Brain {
|
|
63
53
|
private vault: Vault;
|
|
64
54
|
private vaultManager: VaultManager | undefined;
|
|
65
|
-
private cognee: CogneeClient | undefined;
|
|
66
55
|
private vocabulary: Map<string, number> = new Map();
|
|
67
56
|
private weights: ScoringWeights = { ...DEFAULT_WEIGHTS };
|
|
68
57
|
|
|
69
|
-
constructor(vault: Vault,
|
|
58
|
+
constructor(vault: Vault, vaultManager?: VaultManager) {
|
|
70
59
|
this.vault = vault;
|
|
71
60
|
this.vaultManager = vaultManager;
|
|
72
|
-
this.cognee = cognee;
|
|
73
61
|
this.rebuildVocabulary();
|
|
74
62
|
this.recomputeWeights();
|
|
75
63
|
}
|
|
@@ -102,97 +90,6 @@ export class Brain {
|
|
|
102
90
|
});
|
|
103
91
|
}
|
|
104
92
|
|
|
105
|
-
// Cognee vector search (parallel, with timeout fallback)
|
|
106
|
-
let cogneeScoreMap: Map<string, number> = new Map();
|
|
107
|
-
const cogneeAvailable = this.cognee?.isAvailable ?? false;
|
|
108
|
-
if (cogneeAvailable && this.cognee) {
|
|
109
|
-
try {
|
|
110
|
-
const cogneeResults = await this.cognee.search(query, { limit: Math.max(limit * 2, 20) });
|
|
111
|
-
|
|
112
|
-
// Build title → entryIds reverse index from FTS results for text-based matching.
|
|
113
|
-
// Cognee assigns its own UUIDs to chunks and may strip embedded metadata during
|
|
114
|
-
// chunking, so we need multiple strategies to cross-reference results.
|
|
115
|
-
// Multiple entries can share a title, so map to arrays of IDs.
|
|
116
|
-
const titleToIds = new Map<string, string[]>();
|
|
117
|
-
for (const r of rawResults) {
|
|
118
|
-
const key = r.entry.title.toLowerCase().trim();
|
|
119
|
-
const ids = titleToIds.get(key) ?? [];
|
|
120
|
-
ids.push(r.entry.id);
|
|
121
|
-
titleToIds.set(key, ids);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const vaultIdPattern = /\[vault-id:([^\]]+)\]/;
|
|
125
|
-
const unmatchedCogneeResults: Array<{ text: string; score: number }> = [];
|
|
126
|
-
|
|
127
|
-
for (const cr of cogneeResults) {
|
|
128
|
-
const text = cr.text ?? '';
|
|
129
|
-
|
|
130
|
-
// Strategy 1: Extract vault ID from [vault-id:XXX] prefix (if Cognee preserved it)
|
|
131
|
-
const vaultIdMatch = text.match(vaultIdPattern);
|
|
132
|
-
if (vaultIdMatch) {
|
|
133
|
-
const vaultId = vaultIdMatch[1];
|
|
134
|
-
cogneeScoreMap.set(vaultId, Math.max(cogneeScoreMap.get(vaultId) ?? 0, cr.score));
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Strategy 2: Match first line of chunk text against known entry titles.
|
|
139
|
-
// serializeEntry() puts the title on the first line after the [vault-id:] prefix,
|
|
140
|
-
// and Cognee's chunking typically preserves this as the chunk start.
|
|
141
|
-
const firstLine = text.split('\n')[0]?.trim().toLowerCase() ?? '';
|
|
142
|
-
const matchedIds = firstLine ? titleToIds.get(firstLine) : undefined;
|
|
143
|
-
if (matchedIds) {
|
|
144
|
-
for (const id of matchedIds) {
|
|
145
|
-
cogneeScoreMap.set(id, Math.max(cogneeScoreMap.get(id) ?? 0, cr.score));
|
|
146
|
-
}
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Strategy 3: Check if any known title appears as a substring in the chunk.
|
|
151
|
-
// Handles cases where the title isn't on the first line (mid-document chunks).
|
|
152
|
-
const textLower = text.toLowerCase();
|
|
153
|
-
let found = false;
|
|
154
|
-
for (const [title, ids] of titleToIds) {
|
|
155
|
-
if (title.length >= 8 && textLower.includes(title)) {
|
|
156
|
-
for (const id of ids) {
|
|
157
|
-
cogneeScoreMap.set(id, Math.max(cogneeScoreMap.get(id) ?? 0, cr.score));
|
|
158
|
-
}
|
|
159
|
-
found = true;
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (!found && text.length > 0) {
|
|
164
|
-
unmatchedCogneeResults.push({ text, score: cr.score });
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Strategy 4: For Cognee-only semantic matches (not in FTS results),
|
|
169
|
-
// use the first line as a vault FTS query to find the source entry.
|
|
170
|
-
// Preserve caller filters (domain/type/severity) to avoid reintroducing
|
|
171
|
-
// entries the original query excluded.
|
|
172
|
-
for (const unmatched of unmatchedCogneeResults) {
|
|
173
|
-
const searchTerm = unmatched.text.split('\n')[0]?.trim();
|
|
174
|
-
if (!searchTerm || searchTerm.length < 3) continue;
|
|
175
|
-
const vaultHits = this.vault.search(searchTerm, {
|
|
176
|
-
domain: options?.domain,
|
|
177
|
-
type: options?.type,
|
|
178
|
-
severity: options?.severity,
|
|
179
|
-
limit: 1,
|
|
180
|
-
});
|
|
181
|
-
if (vaultHits.length > 0) {
|
|
182
|
-
const id = vaultHits[0].entry.id;
|
|
183
|
-
cogneeScoreMap.set(id, Math.max(cogneeScoreMap.get(id) ?? 0, unmatched.score));
|
|
184
|
-
// Also add to FTS results pool if not already present
|
|
185
|
-
if (!rawResults.some((r) => r.entry.id === id)) {
|
|
186
|
-
rawResults.push(vaultHits[0]);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
} catch {
|
|
191
|
-
// Cognee failed — fall back to FTS5 only
|
|
192
|
-
cogneeScoreMap = new Map();
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
93
|
if (rawResults.length === 0) return [];
|
|
197
94
|
|
|
198
95
|
const seedCount = rawResults.length;
|
|
@@ -201,22 +98,9 @@ export class Brain {
|
|
|
201
98
|
const queryDomain = options?.domain;
|
|
202
99
|
const now = Math.floor(Date.now() / 1000);
|
|
203
100
|
|
|
204
|
-
// Use cognee-aware weights only if at least one ranked candidate has a vector score
|
|
205
|
-
const hasVectorCandidate = rawResults.some((r) => cogneeScoreMap.has(r.entry.id));
|
|
206
|
-
const activeWeights = hasVectorCandidate ? this.getCogneeWeights() : this.weights;
|
|
207
|
-
|
|
208
101
|
const ranked = rawResults.map((result) => {
|
|
209
102
|
const entry = result.entry;
|
|
210
|
-
const
|
|
211
|
-
const breakdown = this.scoreEntry(
|
|
212
|
-
entry,
|
|
213
|
-
queryTokens,
|
|
214
|
-
queryTags,
|
|
215
|
-
queryDomain,
|
|
216
|
-
now,
|
|
217
|
-
vectorScore,
|
|
218
|
-
activeWeights,
|
|
219
|
-
);
|
|
103
|
+
const breakdown = this.scoreEntry(entry, queryTokens, queryTags, queryDomain, now);
|
|
220
104
|
return { entry, score: breakdown.total, breakdown };
|
|
221
105
|
});
|
|
222
106
|
|
|
@@ -335,11 +219,6 @@ export class Brain {
|
|
|
335
219
|
this.vault.add(fullEntry);
|
|
336
220
|
this.updateVocabularyIncremental(fullEntry);
|
|
337
221
|
|
|
338
|
-
// Fire-and-forget Cognee sync
|
|
339
|
-
if (this.cognee?.isAvailable) {
|
|
340
|
-
this.cognee.addEntries([fullEntry]).catch(() => {});
|
|
341
|
-
}
|
|
342
|
-
|
|
343
222
|
const result: CaptureResult = {
|
|
344
223
|
captured: true,
|
|
345
224
|
id: entry.id,
|
|
@@ -465,32 +344,6 @@ export class Brain {
|
|
|
465
344
|
});
|
|
466
345
|
}
|
|
467
346
|
|
|
468
|
-
async syncToCognee(): Promise<{ synced: number; cognified: boolean }> {
|
|
469
|
-
if (!this.cognee?.isAvailable) return { synced: 0, cognified: false };
|
|
470
|
-
|
|
471
|
-
const batchSize = 1000;
|
|
472
|
-
let offset = 0;
|
|
473
|
-
let totalSynced = 0;
|
|
474
|
-
|
|
475
|
-
while (true) {
|
|
476
|
-
const batch = this.vault.list({ limit: batchSize, offset });
|
|
477
|
-
if (batch.length === 0) break;
|
|
478
|
-
|
|
479
|
-
const { added } = await this.cognee.addEntries(batch);
|
|
480
|
-
totalSynced += added;
|
|
481
|
-
offset += batch.length;
|
|
482
|
-
|
|
483
|
-
if (batch.length < batchSize) break;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (totalSynced === 0) return { synced: 0, cognified: false };
|
|
487
|
-
|
|
488
|
-
let cognified = false;
|
|
489
|
-
const cognifyResult = await this.cognee.cognify();
|
|
490
|
-
cognified = cognifyResult.status === 'ok';
|
|
491
|
-
return { synced: totalSynced, cognified };
|
|
492
|
-
}
|
|
493
|
-
|
|
494
347
|
rebuildVocabulary(): void {
|
|
495
348
|
// Collect entries from all connected sources when VaultManager is available
|
|
496
349
|
let entries: IntelligenceEntry[];
|
|
@@ -616,10 +469,8 @@ export class Brain {
|
|
|
616
469
|
queryTags: string[],
|
|
617
470
|
queryDomain: string | undefined,
|
|
618
471
|
now: number,
|
|
619
|
-
vectorScore: number = 0,
|
|
620
|
-
activeWeights?: ScoringWeights,
|
|
621
472
|
): ScoreBreakdown {
|
|
622
|
-
const w =
|
|
473
|
+
const w = this.weights;
|
|
623
474
|
|
|
624
475
|
let semantic = 0;
|
|
625
476
|
if (this.vocabulary.size > 0 && queryTokens.length > 0) {
|
|
@@ -643,7 +494,7 @@ export class Brain {
|
|
|
643
494
|
|
|
644
495
|
const domainMatch = queryDomain && entry.domain === queryDomain ? 1.0 : 0;
|
|
645
496
|
|
|
646
|
-
const vector =
|
|
497
|
+
const vector = 0;
|
|
647
498
|
|
|
648
499
|
const total =
|
|
649
500
|
w.semantic * semantic +
|
|
@@ -747,10 +598,6 @@ export class Brain {
|
|
|
747
598
|
tx();
|
|
748
599
|
}
|
|
749
600
|
|
|
750
|
-
private getCogneeWeights(): ScoringWeights {
|
|
751
|
-
return { ...COGNEE_WEIGHTS };
|
|
752
|
-
}
|
|
753
|
-
|
|
754
601
|
private recomputeWeights(): void {
|
|
755
602
|
const db = this.vault.getDb();
|
|
756
603
|
// Exclude 'failed' from weight computation — system errors don't indicate relevance
|
|
@@ -3,17 +3,15 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Orchestrates three signals to enrich intent classification:
|
|
5
5
|
* 1. Entity extraction (regex-based, domain-agnostic)
|
|
6
|
-
* 2. Knowledge retrieval (vault FTS +
|
|
6
|
+
* 2. Knowledge retrieval (vault FTS + brain recommendations)
|
|
7
7
|
* 3. Confidence scoring (combines entity + knowledge signals)
|
|
8
8
|
*
|
|
9
|
-
* Graceful degradation: if Cognee is unavailable, vault-only.
|
|
10
9
|
* If vault is empty, keyword confidence from IntentRouter is unchanged.
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
12
|
import type { Vault } from '../vault/vault.js';
|
|
14
13
|
import type { Brain } from '../brain/brain.js';
|
|
15
14
|
import type { BrainIntelligence } from '../brain/intelligence.js';
|
|
16
|
-
import type { CogneeClient } from '../cognee/client.js';
|
|
17
15
|
import type {
|
|
18
16
|
EntityType,
|
|
19
17
|
ExtractedEntity,
|
|
@@ -105,20 +103,17 @@ export class ContextEngine {
|
|
|
105
103
|
private vault: Vault;
|
|
106
104
|
private brain: Brain;
|
|
107
105
|
private brainIntelligence: BrainIntelligence;
|
|
108
|
-
private cognee: CogneeClient | null;
|
|
109
106
|
private config: Required<ContextEngineConfig>;
|
|
110
107
|
|
|
111
108
|
constructor(
|
|
112
109
|
vault: Vault,
|
|
113
110
|
brain: Brain,
|
|
114
111
|
brainIntelligence: BrainIntelligence,
|
|
115
|
-
cognee: CogneeClient | null,
|
|
116
112
|
config?: ContextEngineConfig,
|
|
117
113
|
) {
|
|
118
114
|
this.vault = vault;
|
|
119
115
|
this.brain = brain;
|
|
120
116
|
this.brainIntelligence = brainIntelligence;
|
|
121
|
-
this.cognee = cognee;
|
|
122
117
|
this.config = {
|
|
123
118
|
vaultSearchLimit: config?.vaultSearchLimit ?? 10,
|
|
124
119
|
cogneeSearchLimit: config?.cogneeSearchLimit ?? 10,
|
|
@@ -167,7 +162,6 @@ export class ContextEngine {
|
|
|
167
162
|
async retrieveKnowledge(prompt: string, domain?: string): Promise<KnowledgeRetrievalResult> {
|
|
168
163
|
const items: KnowledgeItem[] = [];
|
|
169
164
|
let vaultHits = 0;
|
|
170
|
-
let cogneeHits = 0;
|
|
171
165
|
let brainHits = 0;
|
|
172
166
|
|
|
173
167
|
// 1. Vault FTS search
|
|
@@ -208,29 +202,7 @@ export class ContextEngine {
|
|
|
208
202
|
// Vault search failed — continue with other sources
|
|
209
203
|
}
|
|
210
204
|
|
|
211
|
-
// 2.
|
|
212
|
-
if (this.cognee) {
|
|
213
|
-
try {
|
|
214
|
-
const cogneeResults = await this.cognee.search(prompt, {
|
|
215
|
-
limit: this.config.cogneeSearchLimit,
|
|
216
|
-
});
|
|
217
|
-
for (const r of cogneeResults) {
|
|
218
|
-
// Avoid duplicates from vault
|
|
219
|
-
if (items.some((i) => i.id === r.id)) continue;
|
|
220
|
-
items.push({
|
|
221
|
-
id: r.id,
|
|
222
|
-
title: r.text.slice(0, 100),
|
|
223
|
-
score: r.score,
|
|
224
|
-
source: 'cognee',
|
|
225
|
-
});
|
|
226
|
-
cogneeHits++;
|
|
227
|
-
}
|
|
228
|
-
} catch {
|
|
229
|
-
// Cognee unavailable — continue without
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// 3. Brain recommendations
|
|
205
|
+
// 2. Brain recommendations
|
|
234
206
|
try {
|
|
235
207
|
const recommendations = this.brainIntelligence.recommend({
|
|
236
208
|
domain,
|
|
@@ -255,7 +227,7 @@ export class ContextEngine {
|
|
|
255
227
|
items.sort((a, b) => b.score - a.score);
|
|
256
228
|
const filtered = items.filter((i) => i.score >= this.config.minScoreThreshold);
|
|
257
229
|
|
|
258
|
-
return { items: filtered, vaultHits, cogneeHits, brainHits };
|
|
230
|
+
return { items: filtered, vaultHits, cogneeHits: 0, brainHits };
|
|
259
231
|
}
|
|
260
232
|
|
|
261
233
|
// ─── Context Analysis ───────────────────────────────────────────
|