@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
|
@@ -1,104 +0,0 @@
|
|
|
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 { CogneeSyncManager } from '../cognee/sync-manager.js';
|
|
7
|
-
import type { AgentRuntime } from '../runtime/types.js';
|
|
8
|
-
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
9
|
-
|
|
10
|
-
const testEntry: IntelligenceEntry = {
|
|
11
|
-
id: 'test-1',
|
|
12
|
-
type: 'pattern' as const,
|
|
13
|
-
domain: 'test',
|
|
14
|
-
title: 'Test Pattern',
|
|
15
|
-
severity: 'suggestion' as const,
|
|
16
|
-
description: 'A test pattern',
|
|
17
|
-
tags: ['test'],
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
describe('CogneeSyncManager', () => {
|
|
21
|
-
let runtime: AgentRuntime;
|
|
22
|
-
let plannerDir: string;
|
|
23
|
-
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
plannerDir = join(tmpdir(), 'sync-mgr-test-' + Date.now());
|
|
26
|
-
mkdirSync(plannerDir, { recursive: true });
|
|
27
|
-
runtime = createAgentRuntime({
|
|
28
|
-
agentId: 'test-sync-mgr',
|
|
29
|
-
vaultPath: ':memory:',
|
|
30
|
-
plansPath: join(plannerDir, 'plans.json'),
|
|
31
|
-
cognee: true,
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
afterEach(() => {
|
|
36
|
-
runtime.close();
|
|
37
|
-
rmSync(plannerDir, { recursive: true, force: true });
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should have syncManager on runtime', () => {
|
|
41
|
-
expect(runtime.syncManager).toBeDefined();
|
|
42
|
-
expect(runtime.syncManager).not.toBeNull();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should enqueue on vault seed', () => {
|
|
46
|
-
runtime.vault.seed([testEntry]);
|
|
47
|
-
const stats = runtime.syncManager.getStats();
|
|
48
|
-
expect(stats.pending).toBeGreaterThanOrEqual(1);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should enqueue delete on vault remove', () => {
|
|
52
|
-
runtime.vault.seed([testEntry]);
|
|
53
|
-
runtime.vault.remove(testEntry.id);
|
|
54
|
-
const stats = runtime.syncManager.getStats();
|
|
55
|
-
// At least 2 items: one ingest from seed + one delete from remove
|
|
56
|
-
expect(stats.pending).toBeGreaterThanOrEqual(2);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('getStats should return valid structure', () => {
|
|
60
|
-
const stats = runtime.syncManager.getStats();
|
|
61
|
-
expect(stats).toHaveProperty('pending');
|
|
62
|
-
expect(stats).toHaveProperty('processing');
|
|
63
|
-
expect(stats).toHaveProperty('completed');
|
|
64
|
-
expect(stats).toHaveProperty('failed');
|
|
65
|
-
expect(stats).toHaveProperty('lastDrainAt');
|
|
66
|
-
expect(stats).toHaveProperty('queueSize');
|
|
67
|
-
expect(typeof stats.pending).toBe('number');
|
|
68
|
-
expect(typeof stats.processing).toBe('number');
|
|
69
|
-
expect(typeof stats.completed).toBe('number');
|
|
70
|
-
expect(typeof stats.failed).toBe('number');
|
|
71
|
-
expect(typeof stats.queueSize).toBe('number');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('reconcile should enqueue unsynced entries', () => {
|
|
75
|
-
// Seed an entry (which auto-enqueues), then clear the queue manually
|
|
76
|
-
runtime.vault.seed([testEntry]);
|
|
77
|
-
runtime.vault.getProvider().run('DELETE FROM cognee_sync_queue');
|
|
78
|
-
|
|
79
|
-
// Verify queue is empty
|
|
80
|
-
const statsBefore = runtime.syncManager.getStats();
|
|
81
|
-
expect(statsBefore.pending).toBe(0);
|
|
82
|
-
|
|
83
|
-
// Reconcile should detect the unsynced entry and enqueue it
|
|
84
|
-
const enqueued = runtime.syncManager.reconcile();
|
|
85
|
-
expect(enqueued).toBeGreaterThanOrEqual(1);
|
|
86
|
-
|
|
87
|
-
const statsAfter = runtime.syncManager.getStats();
|
|
88
|
-
expect(statsAfter.pending).toBeGreaterThanOrEqual(1);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('drain should return 0 when Cognee is unavailable', async () => {
|
|
92
|
-
runtime.vault.seed([testEntry]);
|
|
93
|
-
// In test env, Cognee is always unavailable (no server running)
|
|
94
|
-
const processed = await runtime.syncManager.drain();
|
|
95
|
-
expect(processed).toBe(0);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('contentHash should be deterministic', () => {
|
|
99
|
-
const hash1 = CogneeSyncManager.contentHash(testEntry);
|
|
100
|
-
const hash2 = CogneeSyncManager.contentHash(testEntry);
|
|
101
|
-
expect(hash1).toBe(hash2);
|
|
102
|
-
expect(hash1).toHaveLength(16);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { translateSql, PostgresPersistenceProvider } from '../persistence/postgres-provider.js';
|
|
3
|
-
|
|
4
|
-
describe('translateSql', () => {
|
|
5
|
-
it('converts positional ? to $N', () => {
|
|
6
|
-
const result = translateSql('SELECT * FROM t WHERE a = ? AND b = ?', ['x', 'y']);
|
|
7
|
-
expect(result.sql).toBe('SELECT * FROM t WHERE a = $1 AND b = $2');
|
|
8
|
-
expect(result.values).toEqual(['x', 'y']);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('converts named @params to $N', () => {
|
|
12
|
-
const result = translateSql('INSERT INTO t (a, b) VALUES (@name, @age)', {
|
|
13
|
-
name: 'Alice',
|
|
14
|
-
age: 30,
|
|
15
|
-
});
|
|
16
|
-
expect(result.sql).toBe('INSERT INTO t (a, b) VALUES ($1, $2)');
|
|
17
|
-
expect(result.values).toEqual(['Alice', 30]);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('deduplicates repeated named params', () => {
|
|
21
|
-
const result = translateSql('SELECT * FROM t WHERE a = @x OR b = @x', { x: 42 });
|
|
22
|
-
expect(result.sql).toBe('SELECT * FROM t WHERE a = $1 OR b = $1');
|
|
23
|
-
expect(result.values).toEqual([42]);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('replaces unixepoch()', () => {
|
|
27
|
-
const result = translateSql('INSERT INTO t (ts) VALUES (unixepoch())');
|
|
28
|
-
expect(result.sql).toBe('INSERT INTO t (ts) VALUES (EXTRACT(EPOCH FROM NOW())::integer)');
|
|
29
|
-
expect(result.values).toEqual([]);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('handles no params', () => {
|
|
33
|
-
const result = translateSql('SELECT 1');
|
|
34
|
-
expect(result.sql).toBe('SELECT 1');
|
|
35
|
-
expect(result.values).toEqual([]);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('handles mixed: ? params + unixepoch()', () => {
|
|
39
|
-
const result = translateSql('UPDATE t SET name = ?, updated_at = unixepoch() WHERE id = ?', [
|
|
40
|
-
'Bob',
|
|
41
|
-
'id-1',
|
|
42
|
-
]);
|
|
43
|
-
expect(result.sql).toBe(
|
|
44
|
-
'UPDATE t SET name = $1, updated_at = EXTRACT(EPOCH FROM NOW())::integer WHERE id = $2',
|
|
45
|
-
);
|
|
46
|
-
expect(result.values).toEqual(['Bob', 'id-1']);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('replaces INTEGER PRIMARY KEY AUTOINCREMENT with SERIAL PRIMARY KEY', () => {
|
|
50
|
-
const result = translateSql('CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)');
|
|
51
|
-
expect(result.sql).toBe('CREATE TABLE t (id SERIAL PRIMARY KEY, name TEXT)');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('strips PRAGMA statements', () => {
|
|
55
|
-
const result = translateSql('PRAGMA journal_mode = WAL');
|
|
56
|
-
expect(result.sql).toBe('-- pragma removed');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('replaces INSERT OR IGNORE with INSERT', () => {
|
|
60
|
-
const result = translateSql('INSERT OR IGNORE INTO t (a) VALUES (?)', ['x']);
|
|
61
|
-
expect(result.sql).toBe('INSERT INTO t (a) VALUES ($1)');
|
|
62
|
-
expect(result.values).toEqual(['x']);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('replaces INSERT OR REPLACE with INSERT', () => {
|
|
66
|
-
const result = translateSql('INSERT OR REPLACE INTO t (a) VALUES (?)', ['x']);
|
|
67
|
-
expect(result.sql).toBe('INSERT INTO t (a) VALUES ($1)');
|
|
68
|
-
expect(result.values).toEqual(['x']);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('handles complex vault-style query with multiple named params', () => {
|
|
72
|
-
const result = translateSql(
|
|
73
|
-
'SELECT e.* FROM entries e WHERE e.domain = @domain AND e.type = @type ORDER BY e.title LIMIT @limit OFFSET @offset',
|
|
74
|
-
{ domain: 'design', type: 'pattern', limit: 10, offset: 0 },
|
|
75
|
-
);
|
|
76
|
-
expect(result.sql).toBe(
|
|
77
|
-
'SELECT e.* FROM entries e WHERE e.domain = $1 AND e.type = $2 ORDER BY e.title LIMIT $3 OFFSET $4',
|
|
78
|
-
);
|
|
79
|
-
expect(result.values).toEqual(['design', 'pattern', 10, 0]);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('handles vault seed query with many named params', () => {
|
|
83
|
-
const result = translateSql(
|
|
84
|
-
'INSERT INTO entries (id, type, domain) VALUES (@id, @type, @domain) ON CONFLICT(id) DO UPDATE SET type=excluded.type',
|
|
85
|
-
{ id: 'e1', type: 'pattern', domain: 'general' },
|
|
86
|
-
);
|
|
87
|
-
expect(result.sql).toContain('VALUES ($1, $2, $3)');
|
|
88
|
-
expect(result.values).toEqual(['e1', 'pattern', 'general']);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('PostgresPersistenceProvider', () => {
|
|
93
|
-
it('class exists with correct backend', () => {
|
|
94
|
-
expect(PostgresPersistenceProvider).toBeDefined();
|
|
95
|
-
expect(typeof PostgresPersistenceProvider.create).toBe('function');
|
|
96
|
-
expect(typeof PostgresPersistenceProvider.createSync).toBe('function');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('createSync returns a provider instance', () => {
|
|
100
|
-
const provider = PostgresPersistenceProvider.createSync('postgresql://localhost/test');
|
|
101
|
-
expect(provider.backend).toBe('postgres');
|
|
102
|
-
expect(provider.getConnectionString()).toBe('postgresql://localhost/test');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('ftsRebuild is a no-op (PostgreSQL GIN auto-maintains)', () => {
|
|
106
|
-
const provider = PostgresPersistenceProvider.createSync('postgresql://localhost/test');
|
|
107
|
-
// Should not throw
|
|
108
|
-
provider.ftsRebuild('entries');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('close is safe to call multiple times', () => {
|
|
112
|
-
const provider = PostgresPersistenceProvider.createSync('postgresql://localhost/test');
|
|
113
|
-
provider.close();
|
|
114
|
-
provider.close(); // should not throw
|
|
115
|
-
});
|
|
116
|
-
});
|
package/src/cognee/client.ts
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CogneeConfig,
|
|
3
|
-
CogneeSearchResult,
|
|
4
|
-
CogneeSearchType,
|
|
5
|
-
CogneeStatus,
|
|
6
|
-
CogneeAddResult,
|
|
7
|
-
CogneeCognifyResult,
|
|
8
|
-
} from './types.js';
|
|
9
|
-
import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
10
|
-
|
|
11
|
-
// ─── Defaults ──────────────────────────────────────────────────────
|
|
12
|
-
// Aligned with Salvador MCP's battle-tested Cognee integration.
|
|
13
|
-
|
|
14
|
-
const DEFAULT_SERVICE_EMAIL = 'soleri-agent@cognee.dev';
|
|
15
|
-
const DEFAULT_SERVICE_PASSWORD = 'soleri-cognee-local';
|
|
16
|
-
|
|
17
|
-
/** Only allow default service credentials for local endpoints. */
|
|
18
|
-
function isLocalUrl(url: string): boolean {
|
|
19
|
-
try {
|
|
20
|
-
const { hostname } = new URL(url);
|
|
21
|
-
return (
|
|
22
|
-
hostname === 'localhost' ||
|
|
23
|
-
hostname === '127.0.0.1' ||
|
|
24
|
-
hostname === '::1' ||
|
|
25
|
-
hostname === '0.0.0.0'
|
|
26
|
-
);
|
|
27
|
-
} catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const DEFAULT_CONFIG: CogneeConfig = {
|
|
33
|
-
baseUrl: 'http://localhost:8000',
|
|
34
|
-
dataset: 'vault',
|
|
35
|
-
timeoutMs: 30_000,
|
|
36
|
-
searchTimeoutMs: 120_000, // Ollama cold start can take 90s
|
|
37
|
-
healthTimeoutMs: 5_000,
|
|
38
|
-
healthCacheTtlMs: 60_000,
|
|
39
|
-
cognifyDebounceMs: 30_000,
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// ─── CogneeClient ──────────────────────────────────────────────────
|
|
43
|
-
|
|
44
|
-
export class CogneeClient {
|
|
45
|
-
private config: CogneeConfig;
|
|
46
|
-
private healthCache: { status: CogneeStatus; cachedAt: number } | null = null;
|
|
47
|
-
private accessToken: string | null = null;
|
|
48
|
-
private cognifyTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();
|
|
49
|
-
private pendingDatasets: Set<string> = new Set();
|
|
50
|
-
|
|
51
|
-
constructor(config?: Partial<CogneeConfig>) {
|
|
52
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
53
|
-
// Strip trailing slash
|
|
54
|
-
this.config.baseUrl = this.config.baseUrl.replace(/\/+$/, '');
|
|
55
|
-
// Pre-set token if provided
|
|
56
|
-
if (this.config.apiToken) {
|
|
57
|
-
this.accessToken = this.config.apiToken;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// ─── Health ────────────────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
get isAvailable(): boolean {
|
|
64
|
-
if (!this.healthCache) return false;
|
|
65
|
-
const age = Date.now() - this.healthCache.cachedAt;
|
|
66
|
-
if (age > this.config.healthCacheTtlMs) return false;
|
|
67
|
-
return this.healthCache.status.available;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async healthCheck(): Promise<CogneeStatus> {
|
|
71
|
-
const start = Date.now();
|
|
72
|
-
try {
|
|
73
|
-
// Cognee health endpoint is GET / (returns {"message":"Hello, World, I am alive!"})
|
|
74
|
-
const res = await globalThis.fetch(`${this.config.baseUrl}/`, {
|
|
75
|
-
signal: AbortSignal.timeout(this.config.healthTimeoutMs),
|
|
76
|
-
});
|
|
77
|
-
const latencyMs = Date.now() - start;
|
|
78
|
-
if (res.ok) {
|
|
79
|
-
const status: CogneeStatus = { available: true, url: this.config.baseUrl, latencyMs };
|
|
80
|
-
this.healthCache = { status, cachedAt: Date.now() };
|
|
81
|
-
return status;
|
|
82
|
-
}
|
|
83
|
-
const status: CogneeStatus = {
|
|
84
|
-
available: false,
|
|
85
|
-
url: this.config.baseUrl,
|
|
86
|
-
latencyMs,
|
|
87
|
-
error: `HTTP ${res.status}`,
|
|
88
|
-
};
|
|
89
|
-
this.healthCache = { status, cachedAt: Date.now() };
|
|
90
|
-
return status;
|
|
91
|
-
} catch (err) {
|
|
92
|
-
const latencyMs = Date.now() - start;
|
|
93
|
-
const status: CogneeStatus = {
|
|
94
|
-
available: false,
|
|
95
|
-
url: this.config.baseUrl,
|
|
96
|
-
latencyMs,
|
|
97
|
-
error: err instanceof Error ? err.message : String(err),
|
|
98
|
-
};
|
|
99
|
-
this.healthCache = { status, cachedAt: Date.now() };
|
|
100
|
-
return status;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// ─── Ingest ────────────────────────────────────────────────────
|
|
105
|
-
|
|
106
|
-
async addEntries(entries: IntelligenceEntry[]): Promise<CogneeAddResult> {
|
|
107
|
-
if (!this.isAvailable || entries.length === 0) return { added: 0 };
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const token = await this.ensureAuth().catch(() => null);
|
|
111
|
-
|
|
112
|
-
// Cognee /add expects multipart/form-data with files + datasetName
|
|
113
|
-
const formData = new FormData();
|
|
114
|
-
formData.append('datasetName', this.config.dataset);
|
|
115
|
-
|
|
116
|
-
for (const entry of entries) {
|
|
117
|
-
const text = this.serializeEntry(entry);
|
|
118
|
-
const blob = new Blob([text], { type: 'text/plain' });
|
|
119
|
-
formData.append('data', blob, `${entry.id}.txt`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const headers: Record<string, string> = {};
|
|
123
|
-
if (token) headers.Authorization = `Bearer ${token}`;
|
|
124
|
-
|
|
125
|
-
const res = await globalThis.fetch(`${this.config.baseUrl}/api/v1/add`, {
|
|
126
|
-
method: 'POST',
|
|
127
|
-
headers,
|
|
128
|
-
body: formData,
|
|
129
|
-
signal: AbortSignal.timeout(this.config.timeoutMs),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
if (!res.ok) return { added: 0 };
|
|
133
|
-
|
|
134
|
-
// Schedule debounced cognify (multiple rapid ingests coalesce)
|
|
135
|
-
this.scheduleCognify(this.config.dataset);
|
|
136
|
-
|
|
137
|
-
return { added: entries.length };
|
|
138
|
-
} catch {
|
|
139
|
-
return { added: 0 };
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
async cognify(dataset?: string): Promise<CogneeCognifyResult> {
|
|
144
|
-
if (!this.isAvailable) return { status: 'unavailable' };
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
const res = await this.post('/api/v1/cognify', {
|
|
148
|
-
datasets: [dataset ?? this.config.dataset],
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (!res.ok) return { status: `error: HTTP ${res.status}` };
|
|
152
|
-
return { status: 'ok' };
|
|
153
|
-
} catch (err) {
|
|
154
|
-
return { status: `error: ${err instanceof Error ? err.message : String(err)}` };
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ─── Cognify debounce ───────────────────────────────────────────
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Schedule a debounced cognify for a dataset.
|
|
162
|
-
* Sliding window: each call resets the timer. When it expires,
|
|
163
|
-
* cognify fires once. Prevents pipeline dedup on rapid ingests.
|
|
164
|
-
*/
|
|
165
|
-
private scheduleCognify(dataset: string): void {
|
|
166
|
-
const existing = this.cognifyTimers.get(dataset);
|
|
167
|
-
if (existing) clearTimeout(existing);
|
|
168
|
-
|
|
169
|
-
this.pendingDatasets.add(dataset);
|
|
170
|
-
|
|
171
|
-
const timer = setTimeout(() => {
|
|
172
|
-
this.cognifyTimers.delete(dataset);
|
|
173
|
-
this.pendingDatasets.delete(dataset);
|
|
174
|
-
this.post('/api/v1/cognify', { datasets: [dataset] }).catch(() => {});
|
|
175
|
-
}, this.config.cognifyDebounceMs);
|
|
176
|
-
|
|
177
|
-
// Unref so the timer doesn't keep the process alive during shutdown
|
|
178
|
-
if (typeof timer === 'object' && 'unref' in timer) (timer as NodeJS.Timeout).unref();
|
|
179
|
-
|
|
180
|
-
this.cognifyTimers.set(dataset, timer);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** Flush all pending debounced cognify calls immediately. */
|
|
184
|
-
async flushPendingCognify(): Promise<void> {
|
|
185
|
-
const datasets = [...this.pendingDatasets];
|
|
186
|
-
for (const timer of this.cognifyTimers.values()) clearTimeout(timer);
|
|
187
|
-
this.cognifyTimers.clear();
|
|
188
|
-
this.pendingDatasets.clear();
|
|
189
|
-
|
|
190
|
-
if (datasets.length === 0) return;
|
|
191
|
-
|
|
192
|
-
await Promise.allSettled(
|
|
193
|
-
datasets.map((ds) => this.post('/api/v1/cognify', { datasets: [ds] }).catch(() => {})),
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Cancel all pending cognify calls without firing them. For test teardown. */
|
|
198
|
-
resetPendingCognify(): void {
|
|
199
|
-
for (const timer of this.cognifyTimers.values()) clearTimeout(timer);
|
|
200
|
-
this.cognifyTimers.clear();
|
|
201
|
-
this.pendingDatasets.clear();
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ─── Search ────────────────────────────────────────────────────
|
|
205
|
-
|
|
206
|
-
async search(
|
|
207
|
-
query: string,
|
|
208
|
-
opts?: { searchType?: CogneeSearchType; limit?: number },
|
|
209
|
-
): Promise<CogneeSearchResult[]> {
|
|
210
|
-
if (!this.isAvailable) return [];
|
|
211
|
-
|
|
212
|
-
// Default to CHUNKS (pure vector similarity) — GRAPH_COMPLETION requires
|
|
213
|
-
// the LLM to produce instructor-compatible JSON which small local models
|
|
214
|
-
// (llama3.2) can't do reliably, causing infinite retries and timeouts.
|
|
215
|
-
const searchType = opts?.searchType ?? 'CHUNKS';
|
|
216
|
-
const topK = opts?.limit ?? 10;
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
const res = await this.post(
|
|
220
|
-
'/api/v1/search',
|
|
221
|
-
{ query, search_type: searchType, datasets: [this.config.dataset], topK },
|
|
222
|
-
this.config.searchTimeoutMs,
|
|
223
|
-
);
|
|
224
|
-
|
|
225
|
-
if (!res.ok) return [];
|
|
226
|
-
|
|
227
|
-
const data = (await res.json()) as Array<{
|
|
228
|
-
id?: string;
|
|
229
|
-
text?: string;
|
|
230
|
-
score?: number;
|
|
231
|
-
payload?: { id?: string };
|
|
232
|
-
}>;
|
|
233
|
-
|
|
234
|
-
// Position-based scoring when Cognee omits scores.
|
|
235
|
-
// Cognee returns results ordered by relevance but may not include numeric scores.
|
|
236
|
-
return data.slice(0, topK).map((item, idx) => ({
|
|
237
|
-
id: item.payload?.id ?? item.id ?? '',
|
|
238
|
-
score: item.score ?? positionScore(idx, data.length),
|
|
239
|
-
text: typeof item.text === 'string' ? item.text : String(item.text ?? ''),
|
|
240
|
-
searchType,
|
|
241
|
-
}));
|
|
242
|
-
} catch {
|
|
243
|
-
return [];
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// ─── Config access ─────────────────────────────────────────────
|
|
248
|
-
|
|
249
|
-
getConfig(): Readonly<CogneeConfig> {
|
|
250
|
-
return { ...this.config };
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
getStatus(): CogneeStatus | null {
|
|
254
|
-
return this.healthCache?.status ?? null;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ─── Delete ──────────────────────────────────────────────────────
|
|
258
|
-
|
|
259
|
-
async deleteEntries(entryIds: string[]): Promise<{ deleted: number }> {
|
|
260
|
-
if (!this.isAvailable || entryIds.length === 0) return { deleted: 0 };
|
|
261
|
-
|
|
262
|
-
try {
|
|
263
|
-
const res = await this.post('/api/v1/delete', {
|
|
264
|
-
datasetName: this.config.dataset,
|
|
265
|
-
entryIds,
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
if (!res.ok) return { deleted: 0 };
|
|
269
|
-
return { deleted: entryIds.length };
|
|
270
|
-
} catch {
|
|
271
|
-
return { deleted: 0 };
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// ─── Auth ──────────────────────────────────────────────────────
|
|
276
|
-
// Auto-register + login pattern from Salvador MCP.
|
|
277
|
-
// Tries login first (account may already exist), falls back to register.
|
|
278
|
-
|
|
279
|
-
private async ensureAuth(): Promise<string> {
|
|
280
|
-
if (this.accessToken) return this.accessToken;
|
|
281
|
-
|
|
282
|
-
const email = this.config.serviceEmail ?? DEFAULT_SERVICE_EMAIL;
|
|
283
|
-
const password = this.config.servicePassword ?? DEFAULT_SERVICE_PASSWORD;
|
|
284
|
-
|
|
285
|
-
// Refuse default credentials for non-local endpoints
|
|
286
|
-
if (
|
|
287
|
-
!isLocalUrl(this.config.baseUrl) &&
|
|
288
|
-
email === DEFAULT_SERVICE_EMAIL &&
|
|
289
|
-
password === DEFAULT_SERVICE_PASSWORD
|
|
290
|
-
) {
|
|
291
|
-
throw new Error('Explicit Cognee credentials are required for non-local endpoints');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Try login first
|
|
295
|
-
const loginResp = await globalThis.fetch(`${this.config.baseUrl}/api/v1/auth/login`, {
|
|
296
|
-
method: 'POST',
|
|
297
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
298
|
-
body: `username=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`,
|
|
299
|
-
signal: AbortSignal.timeout(this.config.healthTimeoutMs),
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
if (loginResp.ok) {
|
|
303
|
-
const data = (await loginResp.json()) as { access_token: string };
|
|
304
|
-
this.accessToken = data.access_token;
|
|
305
|
-
return this.accessToken;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Register, then retry login
|
|
309
|
-
await globalThis.fetch(`${this.config.baseUrl}/api/v1/auth/register`, {
|
|
310
|
-
method: 'POST',
|
|
311
|
-
headers: { 'Content-Type': 'application/json' },
|
|
312
|
-
body: JSON.stringify({ email, password }),
|
|
313
|
-
signal: AbortSignal.timeout(this.config.healthTimeoutMs),
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
const retryLogin = await globalThis.fetch(`${this.config.baseUrl}/api/v1/auth/login`, {
|
|
317
|
-
method: 'POST',
|
|
318
|
-
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
319
|
-
body: `username=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`,
|
|
320
|
-
signal: AbortSignal.timeout(this.config.healthTimeoutMs),
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
if (!retryLogin.ok) {
|
|
324
|
-
throw new Error(`Cognee auth failed: HTTP ${retryLogin.status}`);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const retryData = (await retryLogin.json()) as { access_token: string };
|
|
328
|
-
this.accessToken = retryData.access_token;
|
|
329
|
-
return this.accessToken;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
private async authHeaders(): Promise<Record<string, string>> {
|
|
333
|
-
try {
|
|
334
|
-
const token = await this.ensureAuth();
|
|
335
|
-
return { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` };
|
|
336
|
-
} catch {
|
|
337
|
-
// Fall back to no auth (works if AUTH_REQUIRED=false)
|
|
338
|
-
return { 'Content-Type': 'application/json' };
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// ─── Private helpers ───────────────────────────────────────────
|
|
343
|
-
|
|
344
|
-
private serializeEntry(entry: IntelligenceEntry): string {
|
|
345
|
-
// Prefix with vault ID so we can cross-reference search results back to vault entries.
|
|
346
|
-
// Cognee assigns its own UUIDs to chunks — the vault ID would otherwise be lost.
|
|
347
|
-
const parts = [`[vault-id:${entry.id}]`, entry.title, entry.description];
|
|
348
|
-
if (entry.context) parts.push(entry.context);
|
|
349
|
-
if (entry.tags.length > 0) parts.push(`Tags: ${entry.tags.join(', ')}`);
|
|
350
|
-
return parts.join('\n');
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private async post(path: string, body: unknown, timeoutMs?: number): Promise<Response> {
|
|
354
|
-
const headers = await this.authHeaders();
|
|
355
|
-
return globalThis.fetch(`${this.config.baseUrl}${path}`, {
|
|
356
|
-
method: 'POST',
|
|
357
|
-
headers,
|
|
358
|
-
body: JSON.stringify(body),
|
|
359
|
-
signal: AbortSignal.timeout(timeoutMs ?? this.config.timeoutMs),
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// ─── Helpers ──────────────────────────────────────────────────────
|
|
365
|
-
|
|
366
|
-
/** Position-based score: first result gets ~1.0, last gets ~0.05. */
|
|
367
|
-
function positionScore(index: number, total: number): number {
|
|
368
|
-
if (total <= 1) return 1.0;
|
|
369
|
-
return Math.max(0.05, 1.0 - (index / (total - 1)) * 0.95);
|
|
370
|
-
}
|