@claude-flow/cli 3.1.0-alpha.51 → 3.1.0-alpha.53
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/README.md +137 -60
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +17 -13
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/neural.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +11 -1
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/mcp-generator.d.ts +0 -7
- package/dist/src/init/mcp-generator.d.ts.map +1 -1
- package/dist/src/init/mcp-generator.js +9 -29
- package/dist/src/init/mcp-generator.js.map +1 -1
- package/dist/src/init/settings-generator.d.ts.map +1 -1
- package/dist/src/init/settings-generator.js +4 -4
- package/dist/src/init/settings-generator.js.map +1 -1
- package/dist/src/init/statusline-generator.d.ts.map +1 -1
- package/dist/src/init/statusline-generator.js +85 -2
- package/dist/src/init/statusline-generator.js.map +1 -1
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +4 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/agentdb-tools.d.ts +27 -0
- package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/agentdb-tools.js +450 -0
- package/dist/src/mcp-tools/agentdb-tools.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +184 -32
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/memory/memory-bridge.d.ts +183 -4
- package/dist/src/memory/memory-bridge.d.ts.map +1 -1
- package/dist/src/memory/memory-bridge.js +707 -26
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/services/ruvector-training.d.ts +2 -1
- package/dist/src/services/ruvector-training.d.ts.map +1 -1
- package/dist/src/services/ruvector-training.js +1 -2
- package/dist/src/services/ruvector-training.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory Bridge — Routes CLI memory operations through ControllerRegistry + AgentDB v3
|
|
3
3
|
*
|
|
4
|
-
* Per ADR-053
|
|
5
|
-
*
|
|
4
|
+
* Per ADR-053 Phases 1-6: Full controller activation pipeline.
|
|
5
|
+
* CLI → ControllerRegistry → AgentDB v3 controllers.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Phase 1: Core CRUD + embeddings + HNSW + controller access (complete)
|
|
8
|
+
* Phase 2: BM25 hybrid search, TieredCache read/write, MutationGuard validation
|
|
9
|
+
* Phase 3: ReasoningBank pattern store, recordFeedback, CausalMemoryGraph edges
|
|
10
|
+
* Phase 4: SkillLibrary promotion, ExplainableRecall provenance, AttestationLog
|
|
11
|
+
* Phase 5: ReflexionMemory session lifecycle, WitnessChain attestation
|
|
12
|
+
* Phase 6: AgentDB MCP tools (separate file), COW branching
|
|
9
13
|
*
|
|
10
14
|
* Uses better-sqlite3 API (synchronous .all()/.get()/.run()) since that's
|
|
11
15
|
* what AgentDB v3 uses internally.
|
|
@@ -55,6 +59,8 @@ async function getRegistry(dbPath) {
|
|
|
55
59
|
reasoningBank: true,
|
|
56
60
|
learningBridge: false,
|
|
57
61
|
tieredCache: true,
|
|
62
|
+
hierarchicalMemory: true,
|
|
63
|
+
memoryConsolidation: true,
|
|
58
64
|
},
|
|
59
65
|
});
|
|
60
66
|
}
|
|
@@ -74,6 +80,130 @@ async function getRegistry(dbPath) {
|
|
|
74
80
|
}
|
|
75
81
|
return registryPromise;
|
|
76
82
|
}
|
|
83
|
+
// ===== Phase 2: BM25 hybrid scoring =====
|
|
84
|
+
/**
|
|
85
|
+
* BM25 scoring for keyword-based search.
|
|
86
|
+
* Replaces naive String.includes() with proper information retrieval scoring.
|
|
87
|
+
* Parameters tuned for short memory entries (k1=1.2, b=0.75).
|
|
88
|
+
*/
|
|
89
|
+
function bm25Score(queryTerms, docContent, avgDocLength, docCount, termDocFreqs) {
|
|
90
|
+
const k1 = 1.2;
|
|
91
|
+
const b = 0.75;
|
|
92
|
+
const docWords = docContent.toLowerCase().split(/\s+/);
|
|
93
|
+
const docLength = docWords.length;
|
|
94
|
+
let score = 0;
|
|
95
|
+
for (const term of queryTerms) {
|
|
96
|
+
const tf = docWords.filter(w => w === term || w.includes(term)).length;
|
|
97
|
+
if (tf === 0)
|
|
98
|
+
continue;
|
|
99
|
+
const df = termDocFreqs.get(term) || 1;
|
|
100
|
+
const idf = Math.log((docCount - df + 0.5) / (df + 0.5) + 1);
|
|
101
|
+
const tfNorm = (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (docLength / Math.max(1, avgDocLength))));
|
|
102
|
+
score += idf * tfNorm;
|
|
103
|
+
}
|
|
104
|
+
return score;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Compute BM25 term document frequencies for a set of rows.
|
|
108
|
+
*/
|
|
109
|
+
function computeTermDocFreqs(queryTerms, rows) {
|
|
110
|
+
const termDocFreqs = new Map();
|
|
111
|
+
let totalLength = 0;
|
|
112
|
+
for (const row of rows) {
|
|
113
|
+
const content = (row.content || '').toLowerCase();
|
|
114
|
+
const words = content.split(/\s+/);
|
|
115
|
+
totalLength += words.length;
|
|
116
|
+
for (const term of queryTerms) {
|
|
117
|
+
if (content.includes(term)) {
|
|
118
|
+
termDocFreqs.set(term, (termDocFreqs.get(term) || 0) + 1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { termDocFreqs, avgDocLength: rows.length > 0 ? totalLength / rows.length : 1 };
|
|
123
|
+
}
|
|
124
|
+
// ===== Phase 2: TieredCache helpers =====
|
|
125
|
+
/**
|
|
126
|
+
* Try to read from TieredCache before hitting DB.
|
|
127
|
+
* Returns cached value or null if cache miss.
|
|
128
|
+
*/
|
|
129
|
+
async function cacheGet(registry, cacheKey) {
|
|
130
|
+
try {
|
|
131
|
+
const cache = registry.get('tieredCache');
|
|
132
|
+
if (!cache || typeof cache.get !== 'function')
|
|
133
|
+
return null;
|
|
134
|
+
return cache.get(cacheKey) ?? null;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Write to TieredCache after DB write.
|
|
142
|
+
*/
|
|
143
|
+
async function cacheSet(registry, cacheKey, value) {
|
|
144
|
+
try {
|
|
145
|
+
const cache = registry.get('tieredCache');
|
|
146
|
+
if (cache && typeof cache.set === 'function') {
|
|
147
|
+
cache.set(cacheKey, value);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Non-fatal
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Invalidate a cache key after mutation.
|
|
156
|
+
*/
|
|
157
|
+
async function cacheInvalidate(registry, cacheKey) {
|
|
158
|
+
try {
|
|
159
|
+
const cache = registry.get('tieredCache');
|
|
160
|
+
if (cache && typeof cache.delete === 'function') {
|
|
161
|
+
cache.delete(cacheKey);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// Non-fatal
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ===== Phase 2: MutationGuard helpers =====
|
|
169
|
+
/**
|
|
170
|
+
* Validate a mutation through MutationGuard before executing.
|
|
171
|
+
* Returns true if the mutation is allowed, false if rejected.
|
|
172
|
+
* Falls back to allowing if guard is unavailable.
|
|
173
|
+
*/
|
|
174
|
+
async function guardValidate(registry, operation, params) {
|
|
175
|
+
try {
|
|
176
|
+
const guard = registry.get('mutationGuard');
|
|
177
|
+
if (!guard || typeof guard.validate !== 'function') {
|
|
178
|
+
return { allowed: true }; // No guard = allow
|
|
179
|
+
}
|
|
180
|
+
const result = guard.validate({ operation, params, timestamp: Date.now() });
|
|
181
|
+
return { allowed: result?.allowed !== false, reason: result?.reason };
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return { allowed: true }; // Guard error = allow (graceful degradation)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// ===== Phase 3: AttestationLog helpers =====
|
|
188
|
+
/**
|
|
189
|
+
* Log a write operation to AttestationLog/WitnessChain.
|
|
190
|
+
*/
|
|
191
|
+
async function logAttestation(registry, operation, entryId, metadata) {
|
|
192
|
+
try {
|
|
193
|
+
const attestation = registry.get('attestationLog');
|
|
194
|
+
if (!attestation)
|
|
195
|
+
return;
|
|
196
|
+
if (typeof attestation.record === 'function') {
|
|
197
|
+
attestation.record({ operation, entryId, timestamp: Date.now(), ...metadata });
|
|
198
|
+
}
|
|
199
|
+
else if (typeof attestation.log === 'function') {
|
|
200
|
+
attestation.log(operation, entryId, metadata);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Non-fatal — attestation is observability, not correctness
|
|
205
|
+
}
|
|
206
|
+
}
|
|
77
207
|
/**
|
|
78
208
|
* Get the AgentDB database handle and ensure memory_entries table exists.
|
|
79
209
|
* Returns null if not available.
|
|
@@ -118,6 +248,7 @@ function getDb(registry) {
|
|
|
118
248
|
// ===== Bridge functions — match memory-initializer.ts signatures =====
|
|
119
249
|
/**
|
|
120
250
|
* Store an entry via AgentDB v3.
|
|
251
|
+
* Phase 2-5: Routes through MutationGuard → TieredCache → DB → AttestationLog.
|
|
121
252
|
* Returns null to signal fallback to sql.js.
|
|
122
253
|
*/
|
|
123
254
|
export async function bridgeStoreEntry(options) {
|
|
@@ -131,6 +262,11 @@ export async function bridgeStoreEntry(options) {
|
|
|
131
262
|
const { key, value, namespace = 'default', tags = [], ttl } = options;
|
|
132
263
|
const id = `entry_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
133
264
|
const now = Date.now();
|
|
265
|
+
// Phase 5: MutationGuard validation before write
|
|
266
|
+
const guardResult = await guardValidate(registry, 'store', { key, namespace, size: value.length });
|
|
267
|
+
if (!guardResult.allowed) {
|
|
268
|
+
return { success: false, id, error: `MutationGuard rejected: ${guardResult.reason}` };
|
|
269
|
+
}
|
|
134
270
|
// Generate embedding via AgentDB's embedder
|
|
135
271
|
let embeddingJson = null;
|
|
136
272
|
let dimensions = 0;
|
|
@@ -165,10 +301,18 @@ export async function bridgeStoreEntry(options) {
|
|
|
165
301
|
) VALUES (?, ?, ?, ?, 'semantic', ?, ?, ?, ?, ?, ?, ?, ?, 'active')`;
|
|
166
302
|
const stmt = ctx.db.prepare(insertSql);
|
|
167
303
|
stmt.run(id, key, namespace, value, embeddingJson, dimensions || null, model, tags.length > 0 ? JSON.stringify(tags) : null, '{}', now, now, ttl ? now + (ttl * 1000) : null);
|
|
304
|
+
// Phase 2: Write-through to TieredCache
|
|
305
|
+
const cacheKey = `entry:${namespace}:${key}`;
|
|
306
|
+
await cacheSet(registry, cacheKey, { id, key, namespace, content: value, embedding: embeddingJson });
|
|
307
|
+
// Phase 4: AttestationLog write audit
|
|
308
|
+
await logAttestation(registry, 'store', id, { key, namespace, hasEmbedding: !!embeddingJson });
|
|
168
309
|
return {
|
|
169
310
|
success: true,
|
|
170
311
|
id,
|
|
171
312
|
embedding: embeddingJson ? { dimensions, model } : undefined,
|
|
313
|
+
guarded: true,
|
|
314
|
+
cached: true,
|
|
315
|
+
attested: true,
|
|
172
316
|
};
|
|
173
317
|
}
|
|
174
318
|
catch {
|
|
@@ -177,6 +321,8 @@ export async function bridgeStoreEntry(options) {
|
|
|
177
321
|
}
|
|
178
322
|
/**
|
|
179
323
|
* Search entries via AgentDB v3.
|
|
324
|
+
* Phase 2: BM25 hybrid scoring replaces naive String.includes() keyword fallback.
|
|
325
|
+
* Combines cosine similarity (semantic) with BM25 (lexical) via reciprocal rank fusion.
|
|
180
326
|
*/
|
|
181
327
|
export async function bridgeSearchEntries(options) {
|
|
182
328
|
const registry = await getRegistry(options.dbPath);
|
|
@@ -217,32 +363,47 @@ export async function bridgeSearchEntries(options) {
|
|
|
217
363
|
catch {
|
|
218
364
|
return null;
|
|
219
365
|
}
|
|
366
|
+
// Phase 2: Compute BM25 term stats for the corpus
|
|
367
|
+
const queryTerms = queryStr.toLowerCase().split(/\s+/).filter(t => t.length > 1);
|
|
368
|
+
const { termDocFreqs, avgDocLength } = computeTermDocFreqs(queryTerms, rows);
|
|
369
|
+
const docCount = rows.length;
|
|
220
370
|
const results = [];
|
|
221
371
|
for (const row of rows) {
|
|
222
|
-
let
|
|
372
|
+
let semanticScore = 0;
|
|
373
|
+
let bm25ScoreVal = 0;
|
|
374
|
+
// Semantic scoring via cosine similarity
|
|
223
375
|
if (queryEmbedding && row.embedding) {
|
|
224
376
|
try {
|
|
225
377
|
const embedding = JSON.parse(row.embedding);
|
|
226
|
-
|
|
378
|
+
semanticScore = cosineSim(queryEmbedding, embedding);
|
|
227
379
|
}
|
|
228
380
|
catch {
|
|
229
381
|
// Invalid embedding
|
|
230
382
|
}
|
|
231
383
|
}
|
|
232
|
-
//
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
score = Math.max(score, (matchCount / words.length) * 0.5);
|
|
384
|
+
// Phase 2: BM25 keyword scoring (replaces String.includes fallback)
|
|
385
|
+
if (queryTerms.length > 0 && row.content) {
|
|
386
|
+
bm25ScoreVal = bm25Score(queryTerms, row.content, avgDocLength, docCount, termDocFreqs);
|
|
387
|
+
// Normalize BM25 to 0-1 range (cap at 10 for normalization)
|
|
388
|
+
bm25ScoreVal = Math.min(bm25ScoreVal / 10, 1.0);
|
|
238
389
|
}
|
|
390
|
+
// Reciprocal rank fusion: combine semantic and BM25
|
|
391
|
+
// Weight: 0.7 semantic + 0.3 BM25 (semantic preferred when embeddings available)
|
|
392
|
+
const score = queryEmbedding
|
|
393
|
+
? (0.7 * semanticScore + 0.3 * bm25ScoreVal)
|
|
394
|
+
: bm25ScoreVal; // BM25-only when no embeddings
|
|
239
395
|
if (score >= threshold) {
|
|
396
|
+
// Phase 4: ExplainableRecall provenance
|
|
397
|
+
const provenance = queryEmbedding
|
|
398
|
+
? `semantic:${semanticScore.toFixed(3)}+bm25:${bm25ScoreVal.toFixed(3)}`
|
|
399
|
+
: `bm25:${bm25ScoreVal.toFixed(3)}`;
|
|
240
400
|
results.push({
|
|
241
401
|
id: String(row.id).substring(0, 12),
|
|
242
402
|
key: row.key || String(row.id).substring(0, 15),
|
|
243
403
|
content: (row.content || '').substring(0, 60) + ((row.content || '').length > 60 ? '...' : ''),
|
|
244
404
|
score,
|
|
245
405
|
namespace: row.namespace || 'default',
|
|
406
|
+
provenance,
|
|
246
407
|
});
|
|
247
408
|
}
|
|
248
409
|
}
|
|
@@ -251,6 +412,7 @@ export async function bridgeSearchEntries(options) {
|
|
|
251
412
|
success: true,
|
|
252
413
|
results: results.slice(0, limit),
|
|
253
414
|
searchTime: Date.now() - startTime,
|
|
415
|
+
searchMethod: queryEmbedding ? 'hybrid-bm25-semantic' : 'bm25-only',
|
|
254
416
|
};
|
|
255
417
|
}
|
|
256
418
|
catch {
|
|
@@ -316,6 +478,7 @@ export async function bridgeListEntries(options) {
|
|
|
316
478
|
}
|
|
317
479
|
/**
|
|
318
480
|
* Get a specific entry via AgentDB v3.
|
|
481
|
+
* Phase 2: TieredCache consulted before DB hit.
|
|
319
482
|
*/
|
|
320
483
|
export async function bridgeGetEntry(options) {
|
|
321
484
|
const registry = await getRegistry(options.dbPath);
|
|
@@ -326,6 +489,27 @@ export async function bridgeGetEntry(options) {
|
|
|
326
489
|
return null;
|
|
327
490
|
try {
|
|
328
491
|
const { key, namespace = 'default' } = options;
|
|
492
|
+
// Phase 2: Check TieredCache first
|
|
493
|
+
const cacheKey = `entry:${namespace}:${key}`;
|
|
494
|
+
const cached = await cacheGet(registry, cacheKey);
|
|
495
|
+
if (cached && cached.content) {
|
|
496
|
+
return {
|
|
497
|
+
success: true,
|
|
498
|
+
found: true,
|
|
499
|
+
cacheHit: true,
|
|
500
|
+
entry: {
|
|
501
|
+
id: String(cached.id || ''),
|
|
502
|
+
key: cached.key || key,
|
|
503
|
+
namespace: cached.namespace || namespace,
|
|
504
|
+
content: cached.content || '',
|
|
505
|
+
accessCount: cached.accessCount || 0,
|
|
506
|
+
createdAt: cached.createdAt || new Date().toISOString(),
|
|
507
|
+
updatedAt: cached.updatedAt || new Date().toISOString(),
|
|
508
|
+
hasEmbedding: !!cached.embedding,
|
|
509
|
+
tags: cached.tags || [],
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
}
|
|
329
513
|
let row;
|
|
330
514
|
try {
|
|
331
515
|
const stmt = ctx.db.prepare(`
|
|
@@ -356,21 +540,20 @@ export async function bridgeGetEntry(options) {
|
|
|
356
540
|
}
|
|
357
541
|
catch { /* invalid */ }
|
|
358
542
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
updatedAt: row.updated_at || new Date().toISOString(),
|
|
370
|
-
hasEmbedding: !!(row.embedding && String(row.embedding).length > 10),
|
|
371
|
-
tags,
|
|
372
|
-
},
|
|
543
|
+
const entry = {
|
|
544
|
+
id: String(row.id),
|
|
545
|
+
key: row.key || String(row.id),
|
|
546
|
+
namespace: row.namespace || 'default',
|
|
547
|
+
content: row.content || '',
|
|
548
|
+
accessCount: (row.access_count || 0) + 1,
|
|
549
|
+
createdAt: row.created_at || new Date().toISOString(),
|
|
550
|
+
updatedAt: row.updated_at || new Date().toISOString(),
|
|
551
|
+
hasEmbedding: !!(row.embedding && String(row.embedding).length > 10),
|
|
552
|
+
tags,
|
|
373
553
|
};
|
|
554
|
+
// Phase 2: Populate cache for next read
|
|
555
|
+
await cacheSet(registry, cacheKey, entry);
|
|
556
|
+
return { success: true, found: true, cacheHit: false, entry };
|
|
374
557
|
}
|
|
375
558
|
catch {
|
|
376
559
|
return null;
|
|
@@ -378,6 +561,7 @@ export async function bridgeGetEntry(options) {
|
|
|
378
561
|
}
|
|
379
562
|
/**
|
|
380
563
|
* Delete an entry via AgentDB v3.
|
|
564
|
+
* Phase 5: MutationGuard validation, cache invalidation, attestation logging.
|
|
381
565
|
*/
|
|
382
566
|
export async function bridgeDeleteEntry(options) {
|
|
383
567
|
const registry = await getRegistry(options.dbPath);
|
|
@@ -388,6 +572,11 @@ export async function bridgeDeleteEntry(options) {
|
|
|
388
572
|
return null;
|
|
389
573
|
try {
|
|
390
574
|
const { key, namespace = 'default' } = options;
|
|
575
|
+
// Phase 5: MutationGuard validation before delete
|
|
576
|
+
const guardResult = await guardValidate(registry, 'delete', { key, namespace });
|
|
577
|
+
if (!guardResult.allowed) {
|
|
578
|
+
return { success: false, deleted: false, key, namespace, remainingEntries: 0, error: `MutationGuard rejected: ${guardResult.reason}` };
|
|
579
|
+
}
|
|
391
580
|
// Soft delete using parameterized query
|
|
392
581
|
let changes = 0;
|
|
393
582
|
try {
|
|
@@ -401,6 +590,12 @@ export async function bridgeDeleteEntry(options) {
|
|
|
401
590
|
catch {
|
|
402
591
|
return null;
|
|
403
592
|
}
|
|
593
|
+
// Phase 2: Invalidate cache
|
|
594
|
+
await cacheInvalidate(registry, `entry:${namespace}:${key}`);
|
|
595
|
+
// Phase 4: AttestationLog delete audit
|
|
596
|
+
if (changes > 0) {
|
|
597
|
+
await logAttestation(registry, 'delete', key, { namespace });
|
|
598
|
+
}
|
|
404
599
|
let remaining = 0;
|
|
405
600
|
try {
|
|
406
601
|
const row = ctx.db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE status = 'active'`).get();
|
|
@@ -415,6 +610,7 @@ export async function bridgeDeleteEntry(options) {
|
|
|
415
610
|
key,
|
|
416
611
|
namespace,
|
|
417
612
|
remainingEntries: remaining,
|
|
613
|
+
guarded: true,
|
|
418
614
|
};
|
|
419
615
|
}
|
|
420
616
|
catch {
|
|
@@ -675,6 +871,491 @@ export async function shutdownBridge() {
|
|
|
675
871
|
bridgeAvailable = null;
|
|
676
872
|
}
|
|
677
873
|
}
|
|
874
|
+
// ===== Phase 3: ReasoningBank pattern operations =====
|
|
875
|
+
/**
|
|
876
|
+
* Store a pattern via ReasoningBank controller.
|
|
877
|
+
* Falls back to raw SQL if ReasoningBank unavailable.
|
|
878
|
+
*/
|
|
879
|
+
export async function bridgeStorePattern(options) {
|
|
880
|
+
const registry = await getRegistry(options.dbPath);
|
|
881
|
+
if (!registry)
|
|
882
|
+
return null;
|
|
883
|
+
try {
|
|
884
|
+
const reasoningBank = registry.get('reasoningBank');
|
|
885
|
+
const patternId = `pattern-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
886
|
+
if (reasoningBank && typeof reasoningBank.store === 'function') {
|
|
887
|
+
await reasoningBank.store({
|
|
888
|
+
id: patternId,
|
|
889
|
+
content: options.pattern,
|
|
890
|
+
type: options.type,
|
|
891
|
+
confidence: options.confidence,
|
|
892
|
+
metadata: options.metadata,
|
|
893
|
+
timestamp: Date.now(),
|
|
894
|
+
});
|
|
895
|
+
return { success: true, patternId, controller: 'reasoningBank' };
|
|
896
|
+
}
|
|
897
|
+
// Fallback: store via bridge SQL
|
|
898
|
+
const result = await bridgeStoreEntry({
|
|
899
|
+
key: patternId,
|
|
900
|
+
value: JSON.stringify({ pattern: options.pattern, type: options.type, confidence: options.confidence, metadata: options.metadata }),
|
|
901
|
+
namespace: 'pattern',
|
|
902
|
+
generateEmbeddingFlag: true,
|
|
903
|
+
tags: [options.type, 'reasoning-pattern'],
|
|
904
|
+
dbPath: options.dbPath,
|
|
905
|
+
});
|
|
906
|
+
return result ? { success: true, patternId: result.id, controller: 'bridge-fallback' } : null;
|
|
907
|
+
}
|
|
908
|
+
catch {
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Search patterns via ReasoningBank controller.
|
|
914
|
+
*/
|
|
915
|
+
export async function bridgeSearchPatterns(options) {
|
|
916
|
+
const registry = await getRegistry(options.dbPath);
|
|
917
|
+
if (!registry)
|
|
918
|
+
return null;
|
|
919
|
+
try {
|
|
920
|
+
const reasoningBank = registry.get('reasoningBank');
|
|
921
|
+
if (reasoningBank && typeof reasoningBank.search === 'function') {
|
|
922
|
+
const results = await reasoningBank.search(options.query, {
|
|
923
|
+
topK: options.topK || 5,
|
|
924
|
+
minScore: options.minConfidence || 0.3,
|
|
925
|
+
});
|
|
926
|
+
return {
|
|
927
|
+
results: Array.isArray(results) ? results.map((r) => ({
|
|
928
|
+
id: r.id || r.patternId || '',
|
|
929
|
+
content: r.content || r.pattern || '',
|
|
930
|
+
score: r.score || r.confidence || 0,
|
|
931
|
+
})) : [],
|
|
932
|
+
controller: 'reasoningBank',
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
// Fallback: search via bridge
|
|
936
|
+
const result = await bridgeSearchEntries({
|
|
937
|
+
query: options.query,
|
|
938
|
+
namespace: 'pattern',
|
|
939
|
+
limit: options.topK || 5,
|
|
940
|
+
threshold: options.minConfidence || 0.3,
|
|
941
|
+
dbPath: options.dbPath,
|
|
942
|
+
});
|
|
943
|
+
return result ? {
|
|
944
|
+
results: result.results.map(r => ({ id: r.id, content: r.content, score: r.score })),
|
|
945
|
+
controller: 'bridge-fallback',
|
|
946
|
+
} : null;
|
|
947
|
+
}
|
|
948
|
+
catch {
|
|
949
|
+
return null;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
// ===== Phase 3: Feedback recording =====
|
|
953
|
+
/**
|
|
954
|
+
* Record task feedback for learning via ReasoningBank or LearningSystem.
|
|
955
|
+
* Wired into hooks_post-task handler.
|
|
956
|
+
*/
|
|
957
|
+
export async function bridgeRecordFeedback(options) {
|
|
958
|
+
const registry = await getRegistry(options.dbPath);
|
|
959
|
+
if (!registry)
|
|
960
|
+
return null;
|
|
961
|
+
try {
|
|
962
|
+
let controller = 'none';
|
|
963
|
+
let updated = 0;
|
|
964
|
+
// Try LearningSystem first (Phase 4)
|
|
965
|
+
const learningSystem = registry.get('learningSystem');
|
|
966
|
+
if (learningSystem) {
|
|
967
|
+
try {
|
|
968
|
+
if (typeof learningSystem.recordFeedback === 'function') {
|
|
969
|
+
await learningSystem.recordFeedback({
|
|
970
|
+
taskId: options.taskId, success: options.success, quality: options.quality,
|
|
971
|
+
agent: options.agent, duration: options.duration, timestamp: Date.now(),
|
|
972
|
+
});
|
|
973
|
+
controller = 'learningSystem';
|
|
974
|
+
updated++;
|
|
975
|
+
}
|
|
976
|
+
else if (typeof learningSystem.record === 'function') {
|
|
977
|
+
await learningSystem.record(options.taskId, options.quality, options.success ? 'success' : 'failure');
|
|
978
|
+
controller = 'learningSystem';
|
|
979
|
+
updated++;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
catch { /* API mismatch — skip */ }
|
|
983
|
+
}
|
|
984
|
+
// Also record in ReasoningBank for pattern reinforcement
|
|
985
|
+
const reasoningBank = registry.get('reasoningBank');
|
|
986
|
+
if (reasoningBank) {
|
|
987
|
+
try {
|
|
988
|
+
if (typeof reasoningBank.recordOutcome === 'function') {
|
|
989
|
+
await reasoningBank.recordOutcome({
|
|
990
|
+
taskId: options.taskId, verdict: options.success ? 'success' : 'failure',
|
|
991
|
+
score: options.quality, timestamp: Date.now(),
|
|
992
|
+
});
|
|
993
|
+
controller = controller === 'none' ? 'reasoningBank' : `${controller}+reasoningBank`;
|
|
994
|
+
updated++;
|
|
995
|
+
}
|
|
996
|
+
else if (typeof reasoningBank.record === 'function') {
|
|
997
|
+
await reasoningBank.record(options.taskId, options.quality);
|
|
998
|
+
controller = controller === 'none' ? 'reasoningBank' : `${controller}+reasoningBank`;
|
|
999
|
+
updated++;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
catch { /* API mismatch — skip */ }
|
|
1003
|
+
}
|
|
1004
|
+
// Phase 4: SkillLibrary promotion for high-quality patterns
|
|
1005
|
+
if (options.success && options.quality >= 0.9 && options.patterns?.length) {
|
|
1006
|
+
const skills = registry.get('skills');
|
|
1007
|
+
if (skills && typeof skills.promote === 'function') {
|
|
1008
|
+
for (const pattern of options.patterns) {
|
|
1009
|
+
try {
|
|
1010
|
+
await skills.promote(pattern, options.quality);
|
|
1011
|
+
updated++;
|
|
1012
|
+
}
|
|
1013
|
+
catch { /* skip */ }
|
|
1014
|
+
}
|
|
1015
|
+
controller += '+skills';
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
// Always store feedback as a memory entry for retrieval (ensures it persists)
|
|
1019
|
+
const storeResult = await bridgeStoreEntry({
|
|
1020
|
+
key: `feedback-${options.taskId}`,
|
|
1021
|
+
value: JSON.stringify(options),
|
|
1022
|
+
namespace: 'feedback',
|
|
1023
|
+
tags: [options.success ? 'success' : 'failure', options.agent || 'unknown'],
|
|
1024
|
+
dbPath: options.dbPath,
|
|
1025
|
+
});
|
|
1026
|
+
if (storeResult?.success) {
|
|
1027
|
+
controller = controller === 'none' ? 'bridge-store' : `${controller}+bridge-store`;
|
|
1028
|
+
updated++;
|
|
1029
|
+
}
|
|
1030
|
+
return { success: true, controller, updated };
|
|
1031
|
+
}
|
|
1032
|
+
catch {
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
// ===== Phase 3: CausalMemoryGraph =====
|
|
1037
|
+
/**
|
|
1038
|
+
* Record a causal edge between two entries (e.g., task → result).
|
|
1039
|
+
*/
|
|
1040
|
+
export async function bridgeRecordCausalEdge(options) {
|
|
1041
|
+
const registry = await getRegistry(options.dbPath);
|
|
1042
|
+
if (!registry)
|
|
1043
|
+
return null;
|
|
1044
|
+
try {
|
|
1045
|
+
const causalGraph = registry.get('causalGraph');
|
|
1046
|
+
if (causalGraph && typeof causalGraph.addEdge === 'function') {
|
|
1047
|
+
causalGraph.addEdge(options.sourceId, options.targetId, {
|
|
1048
|
+
relation: options.relation,
|
|
1049
|
+
weight: options.weight || 1.0,
|
|
1050
|
+
timestamp: Date.now(),
|
|
1051
|
+
});
|
|
1052
|
+
return { success: true, controller: 'causalGraph' };
|
|
1053
|
+
}
|
|
1054
|
+
// Fallback: store edge as metadata
|
|
1055
|
+
const ctx = getDb(registry);
|
|
1056
|
+
if (ctx) {
|
|
1057
|
+
try {
|
|
1058
|
+
ctx.db.prepare(`
|
|
1059
|
+
INSERT OR REPLACE INTO memory_entries (id, key, namespace, content, type, created_at, updated_at, status)
|
|
1060
|
+
VALUES (?, ?, 'causal-edges', ?, 'procedural', ?, ?, 'active')
|
|
1061
|
+
`).run(`edge-${Date.now()}`, `${options.sourceId}→${options.targetId}`, JSON.stringify(options), Date.now(), Date.now());
|
|
1062
|
+
return { success: true, controller: 'bridge-fallback' };
|
|
1063
|
+
}
|
|
1064
|
+
catch { /* skip */ }
|
|
1065
|
+
}
|
|
1066
|
+
return null;
|
|
1067
|
+
}
|
|
1068
|
+
catch {
|
|
1069
|
+
return null;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
// ===== Phase 5: ReflexionMemory session lifecycle =====
|
|
1073
|
+
/**
|
|
1074
|
+
* Start a session with ReflexionMemory episodic replay.
|
|
1075
|
+
* Loads relevant past session patterns for the new session.
|
|
1076
|
+
*/
|
|
1077
|
+
export async function bridgeSessionStart(options) {
|
|
1078
|
+
const registry = await getRegistry(options.dbPath);
|
|
1079
|
+
if (!registry)
|
|
1080
|
+
return null;
|
|
1081
|
+
try {
|
|
1082
|
+
let restoredPatterns = 0;
|
|
1083
|
+
let controller = 'none';
|
|
1084
|
+
// Try ReflexionMemory for episodic session replay
|
|
1085
|
+
const reflexion = registry.get('reflexion');
|
|
1086
|
+
if (reflexion && typeof reflexion.startEpisode === 'function') {
|
|
1087
|
+
await reflexion.startEpisode(options.sessionId, { context: options.context });
|
|
1088
|
+
controller = 'reflexion';
|
|
1089
|
+
}
|
|
1090
|
+
// Load recent patterns from past sessions
|
|
1091
|
+
const searchResult = await bridgeSearchEntries({
|
|
1092
|
+
query: options.context || 'session patterns',
|
|
1093
|
+
namespace: 'session',
|
|
1094
|
+
limit: 10,
|
|
1095
|
+
threshold: 0.2,
|
|
1096
|
+
dbPath: options.dbPath,
|
|
1097
|
+
});
|
|
1098
|
+
if (searchResult?.results) {
|
|
1099
|
+
restoredPatterns = searchResult.results.length;
|
|
1100
|
+
}
|
|
1101
|
+
return {
|
|
1102
|
+
success: true,
|
|
1103
|
+
controller: controller === 'none' ? 'bridge-search' : controller,
|
|
1104
|
+
restoredPatterns,
|
|
1105
|
+
sessionId: options.sessionId,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
catch {
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* End a session and persist episodic summary to ReflexionMemory.
|
|
1114
|
+
*/
|
|
1115
|
+
export async function bridgeSessionEnd(options) {
|
|
1116
|
+
const registry = await getRegistry(options.dbPath);
|
|
1117
|
+
if (!registry)
|
|
1118
|
+
return null;
|
|
1119
|
+
try {
|
|
1120
|
+
let controller = 'none';
|
|
1121
|
+
let persisted = false;
|
|
1122
|
+
// End episode in ReflexionMemory
|
|
1123
|
+
const reflexion = registry.get('reflexion');
|
|
1124
|
+
if (reflexion && typeof reflexion.endEpisode === 'function') {
|
|
1125
|
+
await reflexion.endEpisode(options.sessionId, {
|
|
1126
|
+
summary: options.summary,
|
|
1127
|
+
tasksCompleted: options.tasksCompleted,
|
|
1128
|
+
patternsLearned: options.patternsLearned,
|
|
1129
|
+
});
|
|
1130
|
+
controller = 'reflexion';
|
|
1131
|
+
persisted = true;
|
|
1132
|
+
}
|
|
1133
|
+
// Persist session summary as memory entry
|
|
1134
|
+
await bridgeStoreEntry({
|
|
1135
|
+
key: `session-${options.sessionId}`,
|
|
1136
|
+
value: JSON.stringify({
|
|
1137
|
+
sessionId: options.sessionId,
|
|
1138
|
+
summary: options.summary || 'Session ended',
|
|
1139
|
+
tasksCompleted: options.tasksCompleted || 0,
|
|
1140
|
+
patternsLearned: options.patternsLearned || 0,
|
|
1141
|
+
endedAt: new Date().toISOString(),
|
|
1142
|
+
}),
|
|
1143
|
+
namespace: 'session',
|
|
1144
|
+
tags: ['session-end'],
|
|
1145
|
+
upsert: true,
|
|
1146
|
+
dbPath: options.dbPath,
|
|
1147
|
+
});
|
|
1148
|
+
if (controller === 'none')
|
|
1149
|
+
controller = 'bridge-store';
|
|
1150
|
+
persisted = true;
|
|
1151
|
+
// Phase 3: Trigger NightlyLearner consolidation if available
|
|
1152
|
+
const nightlyLearner = registry.get('nightlyLearner');
|
|
1153
|
+
if (nightlyLearner && typeof nightlyLearner.consolidate === 'function') {
|
|
1154
|
+
try {
|
|
1155
|
+
await nightlyLearner.consolidate({ sessionId: options.sessionId });
|
|
1156
|
+
controller += '+nightlyLearner';
|
|
1157
|
+
}
|
|
1158
|
+
catch { /* non-fatal */ }
|
|
1159
|
+
}
|
|
1160
|
+
return { success: true, controller, persisted };
|
|
1161
|
+
}
|
|
1162
|
+
catch {
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
// ===== Phase 5: SemanticRouter bridge =====
|
|
1167
|
+
/**
|
|
1168
|
+
* Route a task via AgentDB's SemanticRouter.
|
|
1169
|
+
* Returns null to fall back to local ruvector router.
|
|
1170
|
+
*/
|
|
1171
|
+
export async function bridgeRouteTask(options) {
|
|
1172
|
+
const registry = await getRegistry(options.dbPath);
|
|
1173
|
+
if (!registry)
|
|
1174
|
+
return null;
|
|
1175
|
+
try {
|
|
1176
|
+
// Try AgentDB's SemanticRouter
|
|
1177
|
+
const semanticRouter = registry.get('semanticRouter');
|
|
1178
|
+
if (semanticRouter && typeof semanticRouter.route === 'function') {
|
|
1179
|
+
const result = await semanticRouter.route(options.task, { context: options.context });
|
|
1180
|
+
if (result) {
|
|
1181
|
+
return {
|
|
1182
|
+
route: result.route || result.category || 'general',
|
|
1183
|
+
confidence: result.confidence || result.score || 0.5,
|
|
1184
|
+
agents: result.agents || result.suggestedAgents || [],
|
|
1185
|
+
controller: 'semanticRouter',
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
// Try LearningSystem recommendAlgorithm (Phase 4)
|
|
1190
|
+
const learningSystem = registry.get('learningSystem');
|
|
1191
|
+
if (learningSystem && typeof learningSystem.recommendAlgorithm === 'function') {
|
|
1192
|
+
const rec = await learningSystem.recommendAlgorithm(options.task);
|
|
1193
|
+
if (rec) {
|
|
1194
|
+
return {
|
|
1195
|
+
route: rec.algorithm || rec.route || 'general',
|
|
1196
|
+
confidence: rec.confidence || 0.5,
|
|
1197
|
+
agents: rec.agents || [],
|
|
1198
|
+
controller: 'learningSystem',
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
return null; // Fall back to local router
|
|
1203
|
+
}
|
|
1204
|
+
catch {
|
|
1205
|
+
return null;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
// ===== Phase 4: Health check with attestation =====
|
|
1209
|
+
/**
|
|
1210
|
+
* Get comprehensive bridge health including all controller statuses.
|
|
1211
|
+
*/
|
|
1212
|
+
export async function bridgeHealthCheck(dbPath) {
|
|
1213
|
+
const registry = await getRegistry(dbPath);
|
|
1214
|
+
if (!registry)
|
|
1215
|
+
return null;
|
|
1216
|
+
try {
|
|
1217
|
+
const controllers = registry.listControllers();
|
|
1218
|
+
// Phase 4: AttestationLog stats
|
|
1219
|
+
let attestationCount = 0;
|
|
1220
|
+
const attestation = registry.get('attestationLog');
|
|
1221
|
+
if (attestation && typeof attestation.count === 'function') {
|
|
1222
|
+
attestationCount = attestation.count();
|
|
1223
|
+
}
|
|
1224
|
+
// Phase 2: TieredCache stats
|
|
1225
|
+
let cacheStats = { size: 0, hits: 0, misses: 0 };
|
|
1226
|
+
const cache = registry.get('tieredCache');
|
|
1227
|
+
if (cache && typeof cache.stats === 'function') {
|
|
1228
|
+
const s = cache.stats();
|
|
1229
|
+
cacheStats = { size: s.size || 0, hits: s.hits || 0, misses: s.misses || 0 };
|
|
1230
|
+
}
|
|
1231
|
+
return { available: true, controllers, attestationCount, cacheStats };
|
|
1232
|
+
}
|
|
1233
|
+
catch {
|
|
1234
|
+
return null;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
// ===== Phase 7: Hierarchical memory, consolidation, batch, context, semantic route =====
|
|
1238
|
+
/**
|
|
1239
|
+
* Store to hierarchical memory with tier.
|
|
1240
|
+
*/
|
|
1241
|
+
export async function bridgeHierarchicalStore(params) {
|
|
1242
|
+
const registry = await getRegistry();
|
|
1243
|
+
if (!registry)
|
|
1244
|
+
return null;
|
|
1245
|
+
try {
|
|
1246
|
+
const hm = registry.getController('hierarchicalMemory');
|
|
1247
|
+
if (!hm)
|
|
1248
|
+
return { success: false, error: 'HierarchicalMemory not available' };
|
|
1249
|
+
await hm.store(params.key, params.value, params.tier || 'working');
|
|
1250
|
+
return { success: true, key: params.key, tier: params.tier || 'working' };
|
|
1251
|
+
}
|
|
1252
|
+
catch (e) {
|
|
1253
|
+
return { success: false, error: e.message };
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Recall from hierarchical memory.
|
|
1258
|
+
*/
|
|
1259
|
+
export async function bridgeHierarchicalRecall(params) {
|
|
1260
|
+
const registry = await getRegistry();
|
|
1261
|
+
if (!registry)
|
|
1262
|
+
return null;
|
|
1263
|
+
try {
|
|
1264
|
+
const hm = registry.getController('hierarchicalMemory');
|
|
1265
|
+
if (!hm)
|
|
1266
|
+
return { results: [], error: 'HierarchicalMemory not available' };
|
|
1267
|
+
const results = await hm.recall(params.query, params.topK || 5);
|
|
1268
|
+
return { results, controller: 'hierarchicalMemory' };
|
|
1269
|
+
}
|
|
1270
|
+
catch (e) {
|
|
1271
|
+
return { results: [], error: e.message };
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* Run memory consolidation.
|
|
1276
|
+
*/
|
|
1277
|
+
export async function bridgeConsolidate(params) {
|
|
1278
|
+
const registry = await getRegistry();
|
|
1279
|
+
if (!registry)
|
|
1280
|
+
return null;
|
|
1281
|
+
try {
|
|
1282
|
+
const mc = registry.getController('memoryConsolidation');
|
|
1283
|
+
if (!mc)
|
|
1284
|
+
return { success: false, error: 'MemoryConsolidation not available' };
|
|
1285
|
+
const result = await mc.consolidate();
|
|
1286
|
+
return { success: true, consolidated: result };
|
|
1287
|
+
}
|
|
1288
|
+
catch (e) {
|
|
1289
|
+
return { success: false, error: e.message };
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Batch operations (insert, update, delete).
|
|
1294
|
+
*/
|
|
1295
|
+
export async function bridgeBatchOperation(params) {
|
|
1296
|
+
const registry = await getRegistry();
|
|
1297
|
+
if (!registry)
|
|
1298
|
+
return null;
|
|
1299
|
+
try {
|
|
1300
|
+
const batch = registry.getController('batchOperations');
|
|
1301
|
+
if (!batch)
|
|
1302
|
+
return { success: false, error: 'BatchOperations not available' };
|
|
1303
|
+
let result;
|
|
1304
|
+
switch (params.operation) {
|
|
1305
|
+
case 'insert':
|
|
1306
|
+
result = await batch.insertEpisodes(params.entries);
|
|
1307
|
+
break;
|
|
1308
|
+
case 'delete':
|
|
1309
|
+
result = await batch.bulkDelete(params.entries.map((e) => e.key));
|
|
1310
|
+
break;
|
|
1311
|
+
case 'update':
|
|
1312
|
+
result = await batch.bulkUpdate(params.entries);
|
|
1313
|
+
break;
|
|
1314
|
+
default: return { success: false, error: `Unknown operation: ${params.operation}` };
|
|
1315
|
+
}
|
|
1316
|
+
return { success: true, operation: params.operation, count: params.entries.length, result };
|
|
1317
|
+
}
|
|
1318
|
+
catch (e) {
|
|
1319
|
+
return { success: false, error: e.message };
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Synthesize context from memories.
|
|
1324
|
+
*/
|
|
1325
|
+
export async function bridgeContextSynthesize(params) {
|
|
1326
|
+
const registry = await getRegistry();
|
|
1327
|
+
if (!registry)
|
|
1328
|
+
return null;
|
|
1329
|
+
try {
|
|
1330
|
+
const agentdbModule = await import('agentdb');
|
|
1331
|
+
const ContextSynthesizer = agentdbModule.ContextSynthesizer;
|
|
1332
|
+
if (!ContextSynthesizer)
|
|
1333
|
+
return { success: false, error: 'ContextSynthesizer not available' };
|
|
1334
|
+
const result = ContextSynthesizer.synthesize(params.query, []);
|
|
1335
|
+
return { success: true, synthesis: result };
|
|
1336
|
+
}
|
|
1337
|
+
catch (e) {
|
|
1338
|
+
return { success: false, error: e.message };
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Route via SemanticRouter.
|
|
1343
|
+
*/
|
|
1344
|
+
export async function bridgeSemanticRoute(params) {
|
|
1345
|
+
const registry = await getRegistry();
|
|
1346
|
+
if (!registry)
|
|
1347
|
+
return null;
|
|
1348
|
+
try {
|
|
1349
|
+
const router = registry.getController('semanticRouter');
|
|
1350
|
+
if (!router)
|
|
1351
|
+
return { route: null, error: 'SemanticRouter not available' };
|
|
1352
|
+
const result = await router.route(params.input);
|
|
1353
|
+
return { route: result, controller: 'semanticRouter' };
|
|
1354
|
+
}
|
|
1355
|
+
catch (e) {
|
|
1356
|
+
return { route: null, error: e.message };
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
678
1359
|
// ===== Utility =====
|
|
679
1360
|
function cosineSim(a, b) {
|
|
680
1361
|
if (!a || !b || a.length === 0 || b.length === 0)
|