claude-flow 3.5.73 → 3.5.74
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/package.json +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/neural.js +54 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +6 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +14 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +73 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/ruvllm-tools.js +26 -2
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.d.ts +14 -1
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.js +69 -1
- package/v3/@claude-flow/cli/dist/src/memory/sona-optimizer.d.ts +22 -0
- package/v3/@claude-flow/cli/dist/src/memory/sona-optimizer.js +76 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/graph-backend.d.ts +79 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/graph-backend.js +220 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.d.ts +22 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/lora-adapter.js +92 -0
- package/v3/@claude-flow/cli/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.74",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -260,6 +260,17 @@ const trainCommand = {
|
|
|
260
260
|
// Flush patterns to disk
|
|
261
261
|
flushPatterns();
|
|
262
262
|
const persistence = getPersistenceStatus();
|
|
263
|
+
// Save LoRA checkpoint via ruvllm TrainingPipeline if available
|
|
264
|
+
try {
|
|
265
|
+
const { LoRAAdapter } = await import('../ruvector/lora-adapter.js');
|
|
266
|
+
const path = await import('path');
|
|
267
|
+
const cpDir = path.join(process.cwd(), '.claude-flow', 'neural');
|
|
268
|
+
const cpPath = path.join(cpDir, `lora-checkpoint-${Date.now()}.json`);
|
|
269
|
+
const adapter = new LoRAAdapter({ inputDim: dim, outputDim: dim, rank: 4 });
|
|
270
|
+
await adapter.initBackend();
|
|
271
|
+
await adapter.saveCheckpoint(cpPath);
|
|
272
|
+
}
|
|
273
|
+
catch { /* checkpoint save is best-effort */ }
|
|
263
274
|
output.writeln();
|
|
264
275
|
// Display results
|
|
265
276
|
const tableData = [
|
|
@@ -421,6 +432,43 @@ const statusCommand = {
|
|
|
421
432
|
status: output.success('Available'),
|
|
422
433
|
details: '~4x memory reduction',
|
|
423
434
|
},
|
|
435
|
+
{
|
|
436
|
+
component: 'ruvllm Coordinator',
|
|
437
|
+
status: stats._ruvllmBackend === 'active' ? output.success('Active') : output.dim('Unavailable'),
|
|
438
|
+
details: stats._ruvllmBackend === 'active'
|
|
439
|
+
? `SonaCoordinator | ${stats._ruvllmTrajectories} trajectories`
|
|
440
|
+
: 'Install @ruvector/ruvllm',
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
component: 'Contrastive Trainer',
|
|
444
|
+
status: stats._contrastiveTrainer && stats._contrastiveTrainer !== 'unavailable' ? output.success('Active') : output.dim('Unavailable'),
|
|
445
|
+
details: stats._contrastiveTrainer && stats._contrastiveTrainer !== 'unavailable'
|
|
446
|
+
? `${stats._contrastiveTrainer.triplets ?? 0} triplets, ${stats._contrastiveTrainer.agents ?? 0} agents`
|
|
447
|
+
: 'Install @ruvector/ruvllm',
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
component: 'Training Pipeline',
|
|
451
|
+
status: stats._trainingBackend === 'ruvllm' ? output.success('Active') : output.dim(stats._trainingBackend || 'Unavailable'),
|
|
452
|
+
details: stats._trainingBackend === 'ruvllm'
|
|
453
|
+
? 'ruvllm checkpoints enabled'
|
|
454
|
+
: 'JS fallback (no checkpoints)',
|
|
455
|
+
},
|
|
456
|
+
await (async () => {
|
|
457
|
+
try {
|
|
458
|
+
const { getGraphStats } = await import('../ruvector/graph-backend.js');
|
|
459
|
+
const gs = await getGraphStats();
|
|
460
|
+
return {
|
|
461
|
+
component: 'Graph Database',
|
|
462
|
+
status: gs.backend === 'graph-node' ? output.success('Active') : output.dim('Unavailable'),
|
|
463
|
+
details: gs.backend === 'graph-node'
|
|
464
|
+
? `${gs.totalNodes} nodes, ${gs.totalEdges} edges`
|
|
465
|
+
: 'Install @ruvector/graph-node',
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
return { component: 'Graph Database', status: output.dim('Unavailable'), details: 'Not loaded' };
|
|
470
|
+
}
|
|
471
|
+
})(),
|
|
424
472
|
],
|
|
425
473
|
});
|
|
426
474
|
if (verbose) {
|
|
@@ -678,6 +726,12 @@ const optimizeCommand = {
|
|
|
678
726
|
await initializeIntelligence();
|
|
679
727
|
const patterns = await getAllPatterns();
|
|
680
728
|
const stats = getIntelligenceStats();
|
|
729
|
+
// Trigger ruvllm background learning if available
|
|
730
|
+
try {
|
|
731
|
+
const { runBackgroundLearning } = await import('../memory/intelligence.js');
|
|
732
|
+
await runBackgroundLearning();
|
|
733
|
+
}
|
|
734
|
+
catch { /* background learning is best-effort */ }
|
|
681
735
|
// Get actual pattern storage size
|
|
682
736
|
const patternDir = path.join(process.cwd(), '.claude-flow', 'neural');
|
|
683
737
|
let beforeSize = 0;
|
|
@@ -184,6 +184,12 @@ export const agentTools = [
|
|
|
184
184
|
};
|
|
185
185
|
store.agents[agentId] = agent;
|
|
186
186
|
saveAgentStore(store);
|
|
187
|
+
// Record agent in graph database (ADR-087, best-effort)
|
|
188
|
+
try {
|
|
189
|
+
const { addNode } = await import('../ruvector/graph-backend.js');
|
|
190
|
+
await addNode({ id: agentId, type: 'agent', name: agentType });
|
|
191
|
+
}
|
|
192
|
+
catch { /* graph-node not available */ }
|
|
187
193
|
// Include Agent Booster routing info if applicable
|
|
188
194
|
const response = {
|
|
189
195
|
success: true,
|
|
@@ -243,6 +243,20 @@ export const agentdbCausalEdge = {
|
|
|
243
243
|
return { success: false, error: 'targetId is required (non-empty string)' };
|
|
244
244
|
if (!relation)
|
|
245
245
|
return { success: false, error: 'relation is required (non-empty string)' };
|
|
246
|
+
// Try native graph-node backend first (ADR-087)
|
|
247
|
+
try {
|
|
248
|
+
const graphBackend = await import('../ruvector/graph-backend.js');
|
|
249
|
+
if (await graphBackend.isGraphBackendAvailable()) {
|
|
250
|
+
const graphResult = await graphBackend.recordCausalEdge(sourceId, targetId, relation, typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined);
|
|
251
|
+
if (graphResult.success) {
|
|
252
|
+
// Also record in AgentDB bridge for compatibility
|
|
253
|
+
const bridge = await getBridge();
|
|
254
|
+
await bridge.bridgeRecordCausalEdge({ sourceId, targetId, relation, weight: typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined }).catch(() => { });
|
|
255
|
+
return { ...graphResult, _graphNodeBackend: true };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch { /* graph-node not available, fall through */ }
|
|
246
260
|
const bridge = await getBridge();
|
|
247
261
|
const result = await bridge.bridgeRecordCausalEdge({
|
|
248
262
|
sourceId,
|
|
@@ -2033,6 +2033,46 @@ export const hooksIntelligence = {
|
|
|
2033
2033
|
implemented: true,
|
|
2034
2034
|
note: 'Real ONNX embeddings via Xenova/all-MiniLM-L6-v2',
|
|
2035
2035
|
},
|
|
2036
|
+
ruvllmCoordinator: await (async () => {
|
|
2037
|
+
try {
|
|
2038
|
+
const { getIntelligenceStats } = await import('../memory/intelligence.js');
|
|
2039
|
+
const s = getIntelligenceStats();
|
|
2040
|
+
return { status: s._ruvllmBackend || 'unavailable', trajectories: s._ruvllmTrajectories || 0, note: s._ruvllmBackend === 'active' ? 'SonaCoordinator forwarding trajectories' : '@ruvector/ruvllm not loaded' };
|
|
2041
|
+
}
|
|
2042
|
+
catch {
|
|
2043
|
+
return { status: 'unavailable', trajectories: 0, note: 'Not initialized' };
|
|
2044
|
+
}
|
|
2045
|
+
})(),
|
|
2046
|
+
contrastiveTrainer: await (async () => {
|
|
2047
|
+
try {
|
|
2048
|
+
const { getSONAStats } = await import('../memory/sona-optimizer.js');
|
|
2049
|
+
const s = await getSONAStats();
|
|
2050
|
+
return { status: s._contrastiveTrainer !== 'unavailable' ? 'active' : 'unavailable', details: s._contrastiveTrainer, note: s._contrastiveTrainer !== 'unavailable' ? 'Agent embedding learning active' : '@ruvector/ruvllm not loaded' };
|
|
2051
|
+
}
|
|
2052
|
+
catch {
|
|
2053
|
+
return { status: 'unavailable', details: null, note: 'Not initialized' };
|
|
2054
|
+
}
|
|
2055
|
+
})(),
|
|
2056
|
+
trainingPipeline: await (async () => {
|
|
2057
|
+
try {
|
|
2058
|
+
const loraInst = await getLoRAAdapter();
|
|
2059
|
+
const s = loraInst?.getStats();
|
|
2060
|
+
return { status: s?._trainingBackend || 'unavailable', note: s?._trainingBackend === 'ruvllm' ? 'Checkpoint save/load via ruvllm' : 'JS fallback' };
|
|
2061
|
+
}
|
|
2062
|
+
catch {
|
|
2063
|
+
return { status: 'unavailable', note: 'Not initialized' };
|
|
2064
|
+
}
|
|
2065
|
+
})(),
|
|
2066
|
+
graphDatabase: await (async () => {
|
|
2067
|
+
try {
|
|
2068
|
+
const { getGraphStats } = await import('../ruvector/graph-backend.js');
|
|
2069
|
+
const gs = await getGraphStats();
|
|
2070
|
+
return { status: gs.backend, totalNodes: gs.totalNodes, totalEdges: gs.totalEdges, avgDegree: gs.avgDegree, note: gs.backend === 'graph-node' ? 'Native Rust graph with hyperedges and k-hop queries' : '@ruvector/graph-node not loaded' };
|
|
2071
|
+
}
|
|
2072
|
+
catch {
|
|
2073
|
+
return { status: 'unavailable', totalNodes: 0, totalEdges: 0, avgDegree: 0, note: 'Not initialized' };
|
|
2074
|
+
}
|
|
2075
|
+
})(),
|
|
2036
2076
|
},
|
|
2037
2077
|
realMetrics: {
|
|
2038
2078
|
trajectories: realStats.trajectories,
|
|
@@ -2044,7 +2084,7 @@ export const hooksIntelligence = {
|
|
|
2044
2084
|
working: [
|
|
2045
2085
|
'memory-store', 'embeddings', 'trajectory-recording', 'claims', 'swarm-coordination',
|
|
2046
2086
|
'hnsw-index', 'pattern-storage', 'sona-optimizer', 'ewc-consolidation', 'moe-routing',
|
|
2047
|
-
'flash-attention', 'lora-adapter'
|
|
2087
|
+
'flash-attention', 'lora-adapter', 'ruvllm-coordinator', 'contrastive-trainer', 'training-pipeline', 'graph-database'
|
|
2048
2088
|
],
|
|
2049
2089
|
partial: [],
|
|
2050
2090
|
notImplemented: [],
|
|
@@ -2307,6 +2347,12 @@ export const hooksTrajectoryEnd = {
|
|
|
2307
2347
|
// SONA learning failed, continue without it
|
|
2308
2348
|
}
|
|
2309
2349
|
}
|
|
2350
|
+
// Trigger ruvllm background learning after trajectory end
|
|
2351
|
+
try {
|
|
2352
|
+
const { runBackgroundLearning } = await import('../memory/intelligence.js');
|
|
2353
|
+
await runBackgroundLearning();
|
|
2354
|
+
}
|
|
2355
|
+
catch { /* best-effort */ }
|
|
2310
2356
|
// Try EWC++ consolidation on successful trajectories
|
|
2311
2357
|
if (success) {
|
|
2312
2358
|
const ewc = await getEWCConsolidator();
|
|
@@ -2671,12 +2717,38 @@ export const hooksIntelligenceStats = {
|
|
|
2671
2717
|
implementation: 'real-lora',
|
|
2672
2718
|
};
|
|
2673
2719
|
}
|
|
2720
|
+
// ruvllm native backend stats
|
|
2721
|
+
let ruvllmStats = { coordinator: 'unavailable', trajectories: 0, contrastiveTrainer: 'unavailable', trainingBackend: 'unavailable', graphDatabase: { backend: 'unavailable', totalNodes: 0, totalEdges: 0 } };
|
|
2722
|
+
try {
|
|
2723
|
+
const { getIntelligenceStats } = await import('../memory/intelligence.js');
|
|
2724
|
+
const iStats = getIntelligenceStats();
|
|
2725
|
+
ruvllmStats.coordinator = iStats._ruvllmBackend || 'unavailable';
|
|
2726
|
+
ruvllmStats.trajectories = iStats._ruvllmTrajectories || 0;
|
|
2727
|
+
}
|
|
2728
|
+
catch { /* not initialized */ }
|
|
2729
|
+
try {
|
|
2730
|
+
const { getSONAStats: getSONA } = await import('../memory/sona-optimizer.js');
|
|
2731
|
+
const sStats = await getSONA();
|
|
2732
|
+
ruvllmStats.contrastiveTrainer = sStats._contrastiveTrainer || 'unavailable';
|
|
2733
|
+
}
|
|
2734
|
+
catch { /* not initialized */ }
|
|
2735
|
+
if (lora) {
|
|
2736
|
+
const ls = lora.getStats();
|
|
2737
|
+
ruvllmStats.trainingBackend = ls._trainingBackend || 'unavailable';
|
|
2738
|
+
}
|
|
2739
|
+
try {
|
|
2740
|
+
const { getGraphStats } = await import('../ruvector/graph-backend.js');
|
|
2741
|
+
const gs = await getGraphStats();
|
|
2742
|
+
ruvllmStats.graphDatabase = { backend: gs.backend, totalNodes: gs.totalNodes, totalEdges: gs.totalEdges, avgDegree: gs.avgDegree };
|
|
2743
|
+
}
|
|
2744
|
+
catch { /* not available */ }
|
|
2674
2745
|
const stats = {
|
|
2675
2746
|
sona: sonaStats,
|
|
2676
2747
|
moe: moeStats,
|
|
2677
2748
|
ewc: ewcStats,
|
|
2678
2749
|
flash: flashStats,
|
|
2679
2750
|
lora: loraStats,
|
|
2751
|
+
ruvllm: ruvllmStats,
|
|
2680
2752
|
hnsw: {
|
|
2681
2753
|
indexSize: memoryStats.memory.indexSize,
|
|
2682
2754
|
avgSearchTimeMs: 0.12,
|
|
@@ -16,8 +16,32 @@ export const ruvllmWasmTools = [
|
|
|
16
16
|
handler: async () => {
|
|
17
17
|
try {
|
|
18
18
|
const mod = await loadRuvllmWasm();
|
|
19
|
-
const
|
|
20
|
-
|
|
19
|
+
const wasmStatus = await mod.getRuvllmStatus();
|
|
20
|
+
// Also include native ruvllm CJS backend status (ADR-086)
|
|
21
|
+
let nativeBackend = { available: false };
|
|
22
|
+
try {
|
|
23
|
+
const { getIntelligenceStats } = await import('../memory/intelligence.js');
|
|
24
|
+
const iStats = getIntelligenceStats();
|
|
25
|
+
const { getSONAStats } = await import('../memory/sona-optimizer.js');
|
|
26
|
+
const sStats = await getSONAStats();
|
|
27
|
+
nativeBackend = {
|
|
28
|
+
available: iStats._ruvllmBackend === 'active',
|
|
29
|
+
coordinator: iStats._ruvllmBackend || 'unavailable',
|
|
30
|
+
trajectories: iStats._ruvllmTrajectories || 0,
|
|
31
|
+
contrastiveTrainer: sStats._contrastiveTrainer !== 'unavailable' ? 'active' : 'unavailable',
|
|
32
|
+
trainingBackend: iStats._trainingBackend || 'unknown',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch { /* not initialized yet */ }
|
|
36
|
+
// Graph database status (ADR-087)
|
|
37
|
+
let graphStatus = { available: false };
|
|
38
|
+
try {
|
|
39
|
+
const { getGraphStats } = await import('../ruvector/graph-backend.js');
|
|
40
|
+
const gs = await getGraphStats();
|
|
41
|
+
graphStatus = { available: gs.backend === 'graph-node', ...gs };
|
|
42
|
+
}
|
|
43
|
+
catch { /* not loaded */ }
|
|
44
|
+
return { content: [{ type: 'text', text: JSON.stringify({ wasm: wasmStatus, native: nativeBackend, graph: graphStatus }, null, 2) }] };
|
|
21
45
|
}
|
|
22
46
|
catch (err) {
|
|
23
47
|
return { content: [{ type: 'text', text: JSON.stringify({ error: String(err) }) }], isError: true };
|
|
@@ -250,7 +250,15 @@ export declare function findSimilarPatterns(query: string, options?: {
|
|
|
250
250
|
/**
|
|
251
251
|
* Get intelligence system statistics
|
|
252
252
|
*/
|
|
253
|
-
export declare function getIntelligenceStats(): IntelligenceStats
|
|
253
|
+
export declare function getIntelligenceStats(): IntelligenceStats & {
|
|
254
|
+
_ruvllmBackend: string;
|
|
255
|
+
_ruvllmTrajectories: number;
|
|
256
|
+
_contrastiveTrainer?: {
|
|
257
|
+
triplets: number;
|
|
258
|
+
agents: number;
|
|
259
|
+
} | string;
|
|
260
|
+
_trainingBackend?: string;
|
|
261
|
+
};
|
|
254
262
|
/**
|
|
255
263
|
* Get SONA coordinator for advanced operations
|
|
256
264
|
*/
|
|
@@ -329,6 +337,11 @@ export declare function clearAllPatterns(): Promise<void>;
|
|
|
329
337
|
* Get the neural data directory path
|
|
330
338
|
*/
|
|
331
339
|
export declare function getNeuralDataDir(): string;
|
|
340
|
+
/**
|
|
341
|
+
* Trigger background learning on the @ruvector/ruvllm SonaCoordinator.
|
|
342
|
+
* No-op if ruvllm is not installed.
|
|
343
|
+
*/
|
|
344
|
+
export declare function runBackgroundLearning(): Promise<void>;
|
|
332
345
|
/**
|
|
333
346
|
* Get persistence status
|
|
334
347
|
*/
|
|
@@ -559,6 +559,26 @@ class LocalReasoningBank {
|
|
|
559
559
|
}
|
|
560
560
|
}
|
|
561
561
|
// ============================================================================
|
|
562
|
+
// @ruvector/ruvllm SonaCoordinator Integration
|
|
563
|
+
// ============================================================================
|
|
564
|
+
let ruvllmCoordinator = null;
|
|
565
|
+
let ruvllmLoaded = false;
|
|
566
|
+
async function loadRuvllmCoordinator() {
|
|
567
|
+
if (ruvllmLoaded)
|
|
568
|
+
return ruvllmCoordinator;
|
|
569
|
+
ruvllmLoaded = true;
|
|
570
|
+
try {
|
|
571
|
+
const { createRequire } = await import('module');
|
|
572
|
+
const requireCjs = createRequire(import.meta.url);
|
|
573
|
+
const ruvllm = requireCjs('@ruvector/ruvllm');
|
|
574
|
+
ruvllmCoordinator = new ruvllm.SonaCoordinator(ruvllm.DEFAULT_SONA_CONFIG);
|
|
575
|
+
return ruvllmCoordinator;
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
// ============================================================================
|
|
562
582
|
// Module State
|
|
563
583
|
// ============================================================================
|
|
564
584
|
let sonaCoordinator = null;
|
|
@@ -634,6 +654,8 @@ export async function initializeIntelligence(config) {
|
|
|
634
654
|
});
|
|
635
655
|
// Load persisted stats if available
|
|
636
656
|
loadPersistedStats();
|
|
657
|
+
// Eagerly load ruvllm coordinator so stats reflect backend status
|
|
658
|
+
await loadRuvllmCoordinator();
|
|
637
659
|
intelligenceInitialized = true;
|
|
638
660
|
return {
|
|
639
661
|
success: true,
|
|
@@ -773,6 +795,26 @@ export async function recordTrajectory(steps, verdict) {
|
|
|
773
795
|
}
|
|
774
796
|
}
|
|
775
797
|
}
|
|
798
|
+
// Forward trajectory to @ruvector/ruvllm SonaCoordinator if available
|
|
799
|
+
const ruvllmCoord = await loadRuvllmCoordinator();
|
|
800
|
+
if (ruvllmCoord) {
|
|
801
|
+
try {
|
|
802
|
+
const avgQuality = verdict === 'success' ? 1.0 : verdict === 'partial' ? 0.5 : 0.0;
|
|
803
|
+
ruvllmCoord.recordTrajectory({
|
|
804
|
+
steps: enrichedSteps.map(s => ({
|
|
805
|
+
state: s.content,
|
|
806
|
+
action: s.type,
|
|
807
|
+
reward: avgQuality,
|
|
808
|
+
embedding: s.embedding || []
|
|
809
|
+
})),
|
|
810
|
+
totalReward: avgQuality,
|
|
811
|
+
success: verdict === 'success'
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
// ruvllm recording failed silently
|
|
816
|
+
}
|
|
817
|
+
}
|
|
776
818
|
globalStats.trajectoriesRecorded++;
|
|
777
819
|
globalStats.lastAdaptation = Date.now();
|
|
778
820
|
savePersistedStats();
|
|
@@ -837,6 +879,19 @@ export async function findSimilarPatterns(query, options) {
|
|
|
837
879
|
export function getIntelligenceStats() {
|
|
838
880
|
const sonaStats = sonaCoordinator?.stats();
|
|
839
881
|
const bankStats = reasoningBank?.stats();
|
|
882
|
+
const ruvllmStats = ruvllmCoordinator?.stats?.() || null;
|
|
883
|
+
// Fetch cross-module stats for unified reporting
|
|
884
|
+
let contrastiveTrainer = 'unavailable';
|
|
885
|
+
let trainingBackend = 'unavailable';
|
|
886
|
+
try {
|
|
887
|
+
// Synchronous check — contrastiveTrainer is module-level in sona-optimizer
|
|
888
|
+
// We read it via the SONAOptimizer singleton if available
|
|
889
|
+
const sonaModule = globalThis.__claudeFlowSonaStats;
|
|
890
|
+
if (sonaModule) {
|
|
891
|
+
contrastiveTrainer = sonaModule._contrastiveTrainer || 'unavailable';
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
catch { /* not available */ }
|
|
840
895
|
return {
|
|
841
896
|
sonaEnabled: !!sonaCoordinator,
|
|
842
897
|
reasoningBankSize: bankStats?.size ?? 0,
|
|
@@ -844,7 +899,11 @@ export function getIntelligenceStats() {
|
|
|
844
899
|
signalsProcessed: globalStats.signalsProcessed,
|
|
845
900
|
trajectoriesRecorded: globalStats.trajectoriesRecorded,
|
|
846
901
|
lastAdaptation: globalStats.lastAdaptation,
|
|
847
|
-
avgAdaptationTime: sonaStats?.avgAdaptationMs ?? 0
|
|
902
|
+
avgAdaptationTime: sonaStats?.avgAdaptationMs ?? 0,
|
|
903
|
+
_ruvllmBackend: ruvllmStats ? 'active' : 'unavailable',
|
|
904
|
+
_ruvllmTrajectories: ruvllmStats?.trajectoriesBuffered || 0,
|
|
905
|
+
_contrastiveTrainer: contrastiveTrainer,
|
|
906
|
+
_trainingBackend: trainingBackend,
|
|
848
907
|
};
|
|
849
908
|
}
|
|
850
909
|
/**
|
|
@@ -1088,6 +1147,15 @@ export async function clearAllPatterns() {
|
|
|
1088
1147
|
export function getNeuralDataDir() {
|
|
1089
1148
|
return getDataDir();
|
|
1090
1149
|
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Trigger background learning on the @ruvector/ruvllm SonaCoordinator.
|
|
1152
|
+
* No-op if ruvllm is not installed.
|
|
1153
|
+
*/
|
|
1154
|
+
export async function runBackgroundLearning() {
|
|
1155
|
+
const coord = await loadRuvllmCoordinator();
|
|
1156
|
+
if (coord)
|
|
1157
|
+
coord.runBackgroundLoop();
|
|
1158
|
+
}
|
|
1091
1159
|
/**
|
|
1092
1160
|
* Get persistence status
|
|
1093
1161
|
*/
|
|
@@ -87,6 +87,11 @@ export interface SONAStats {
|
|
|
87
87
|
qLearningEnabled: boolean;
|
|
88
88
|
/** Time of last learning update */
|
|
89
89
|
lastUpdate: number | null;
|
|
90
|
+
/** Contrastive trainer status (from @ruvector/ruvllm) */
|
|
91
|
+
_contrastiveTrainer?: {
|
|
92
|
+
triplets: number;
|
|
93
|
+
agents: number;
|
|
94
|
+
} | 'unavailable';
|
|
90
95
|
}
|
|
91
96
|
/**
|
|
92
97
|
* SONA Optimizer for adaptive routing based on trajectory outcomes
|
|
@@ -149,6 +154,17 @@ export declare class SONAOptimizer {
|
|
|
149
154
|
* Get optimizer statistics
|
|
150
155
|
*/
|
|
151
156
|
getStats(): SONAStats;
|
|
157
|
+
/**
|
|
158
|
+
* Trigger contrastive training on accumulated agent embeddings.
|
|
159
|
+
* Returns training metrics or { trained: false } if insufficient data.
|
|
160
|
+
*
|
|
161
|
+
* @param _epochs - reserved for future use (epochs are set at ContrastiveTrainer construction)
|
|
162
|
+
*/
|
|
163
|
+
trainAgentEmbeddings(_epochs?: number): Promise<{
|
|
164
|
+
trained: boolean;
|
|
165
|
+
loss?: number;
|
|
166
|
+
triplets?: number;
|
|
167
|
+
}>;
|
|
152
168
|
/**
|
|
153
169
|
* Apply temporal decay to pattern confidence
|
|
154
170
|
* Reduces confidence of unused patterns
|
|
@@ -166,6 +182,12 @@ export declare class SONAOptimizer {
|
|
|
166
182
|
* Import patterns (for migration or testing)
|
|
167
183
|
*/
|
|
168
184
|
importPatterns(patterns: Record<string, LearnedPattern>): number;
|
|
185
|
+
/**
|
|
186
|
+
* Convert extracted keywords into a lightweight 384-dim embedding proxy.
|
|
187
|
+
* Uses a deterministic hash-scatter so each keyword set maps to a
|
|
188
|
+
* consistent unit-length vector compatible with ContrastiveTrainer.
|
|
189
|
+
*/
|
|
190
|
+
private keywordsToEmbedding;
|
|
169
191
|
/**
|
|
170
192
|
* Extract meaningful keywords from task description
|
|
171
193
|
*/
|
|
@@ -26,6 +26,26 @@ const CONFIDENCE_INCREMENT = 0.1;
|
|
|
26
26
|
const CONFIDENCE_DECREMENT = 0.15;
|
|
27
27
|
const DECAY_RATE = 0.01; // Per day
|
|
28
28
|
const MAX_PATTERNS = 1000;
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Contrastive Trainer (lazy-loaded from @ruvector/ruvllm)
|
|
31
|
+
// ============================================================================
|
|
32
|
+
let contrastiveTrainer = null;
|
|
33
|
+
let trainerLoaded = false;
|
|
34
|
+
async function loadContrastiveTrainer() {
|
|
35
|
+
if (trainerLoaded)
|
|
36
|
+
return contrastiveTrainer;
|
|
37
|
+
trainerLoaded = true;
|
|
38
|
+
try {
|
|
39
|
+
const { createRequire } = await import('module');
|
|
40
|
+
const requireCjs = createRequire(import.meta.url);
|
|
41
|
+
const ruvllm = requireCjs('@ruvector/ruvllm');
|
|
42
|
+
contrastiveTrainer = new ruvllm.ContrastiveTrainer({ batchSize: 32, margin: 0.5 });
|
|
43
|
+
return contrastiveTrainer;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
29
49
|
/**
|
|
30
50
|
* Common agent types for routing
|
|
31
51
|
*/
|
|
@@ -159,6 +179,8 @@ export class SONAOptimizer {
|
|
|
159
179
|
// Q-learning not available, continue without it
|
|
160
180
|
this.qLearningEnabled = false;
|
|
161
181
|
}
|
|
182
|
+
// Eagerly load ContrastiveTrainer so stats reflect backend status
|
|
183
|
+
await loadContrastiveTrainer();
|
|
162
184
|
return {
|
|
163
185
|
success: true,
|
|
164
186
|
patternsLoaded: loaded ? this.patterns.size : 0,
|
|
@@ -218,6 +240,16 @@ export class SONAOptimizer {
|
|
|
218
240
|
const reward = success ? 1.0 : -0.5;
|
|
219
241
|
this.qLearningRouter.update(task, agent, reward);
|
|
220
242
|
}
|
|
243
|
+
// Feed outcome into contrastive trainer for agent embedding learning (fire-and-forget)
|
|
244
|
+
if (success) {
|
|
245
|
+
loadContrastiveTrainer().then(trainer => {
|
|
246
|
+
if (!trainer)
|
|
247
|
+
return;
|
|
248
|
+
// Use keyword vector as a lightweight embedding proxy
|
|
249
|
+
const embedding = this.keywordsToEmbedding(keywords);
|
|
250
|
+
trainer.addAgentEmbedding(agent, embedding);
|
|
251
|
+
}).catch(() => { });
|
|
252
|
+
}
|
|
221
253
|
// Persist to disk (debounced)
|
|
222
254
|
this.saveToDisk();
|
|
223
255
|
return {
|
|
@@ -334,8 +366,25 @@ export class SONAOptimizer {
|
|
|
334
366
|
avgConfidence: this.patterns.size > 0 ? totalConfidence / this.patterns.size : 0,
|
|
335
367
|
qLearningEnabled: this.qLearningEnabled,
|
|
336
368
|
lastUpdate: this.lastUpdate,
|
|
369
|
+
_contrastiveTrainer: contrastiveTrainer
|
|
370
|
+
? { triplets: contrastiveTrainer.getTripletCount?.() ?? 0, agents: contrastiveTrainer.getAgentEmbeddings?.()?.size ?? 0 }
|
|
371
|
+
: 'unavailable',
|
|
337
372
|
};
|
|
338
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Trigger contrastive training on accumulated agent embeddings.
|
|
376
|
+
* Returns training metrics or { trained: false } if insufficient data.
|
|
377
|
+
*
|
|
378
|
+
* @param _epochs - reserved for future use (epochs are set at ContrastiveTrainer construction)
|
|
379
|
+
*/
|
|
380
|
+
async trainAgentEmbeddings(_epochs = 5) {
|
|
381
|
+
const trainer = await loadContrastiveTrainer();
|
|
382
|
+
if (!trainer || (trainer.getTripletCount?.() ?? 0) < 3) {
|
|
383
|
+
return { trained: false };
|
|
384
|
+
}
|
|
385
|
+
const result = trainer.train();
|
|
386
|
+
return { trained: true, loss: result.finalLoss, triplets: result.tripletCount };
|
|
387
|
+
}
|
|
339
388
|
/**
|
|
340
389
|
* Apply temporal decay to pattern confidence
|
|
341
390
|
* Reduces confidence of unused patterns
|
|
@@ -404,6 +453,33 @@ export class SONAOptimizer {
|
|
|
404
453
|
// ============================================================================
|
|
405
454
|
// Private Methods
|
|
406
455
|
// ============================================================================
|
|
456
|
+
/**
|
|
457
|
+
* Convert extracted keywords into a lightweight 384-dim embedding proxy.
|
|
458
|
+
* Uses a deterministic hash-scatter so each keyword set maps to a
|
|
459
|
+
* consistent unit-length vector compatible with ContrastiveTrainer.
|
|
460
|
+
*/
|
|
461
|
+
keywordsToEmbedding(keywords) {
|
|
462
|
+
const dim = 384;
|
|
463
|
+
const vec = new Float32Array(dim);
|
|
464
|
+
for (const kw of keywords) {
|
|
465
|
+
// Simple FNV-1a-like hash per character to scatter energy across dims
|
|
466
|
+
let h = 0x811c9dc5;
|
|
467
|
+
for (let i = 0; i < kw.length; i++) {
|
|
468
|
+
h ^= kw.charCodeAt(i);
|
|
469
|
+
h = Math.imul(h, 0x01000193);
|
|
470
|
+
}
|
|
471
|
+
const idx = Math.abs(h) % dim;
|
|
472
|
+
vec[idx] += (h & 1) ? 1 : -1;
|
|
473
|
+
}
|
|
474
|
+
// L2-normalize
|
|
475
|
+
let norm = 0;
|
|
476
|
+
for (let i = 0; i < dim; i++)
|
|
477
|
+
norm += vec[i] * vec[i];
|
|
478
|
+
norm = Math.sqrt(norm) || 1;
|
|
479
|
+
for (let i = 0; i < dim; i++)
|
|
480
|
+
vec[i] /= norm;
|
|
481
|
+
return vec;
|
|
482
|
+
}
|
|
407
483
|
/**
|
|
408
484
|
* Extract meaningful keywords from task description
|
|
409
485
|
*/
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ruvector/graph-node native graph database backend (ADR-087)
|
|
3
|
+
*
|
|
4
|
+
* Provides persistent graph storage for agent relationships, causal edges,
|
|
5
|
+
* task dependencies, and swarm topology using native Rust bindings.
|
|
6
|
+
*
|
|
7
|
+
* API requirements discovered via testing:
|
|
8
|
+
* - createNode: requires { id, type, embedding }
|
|
9
|
+
* - createEdge: requires { from, to, label, description, embedding, properties }
|
|
10
|
+
* - createHyperedge: requires { nodes[], label, description, embedding, properties }
|
|
11
|
+
* - kHopNeighbors(nodeId, k): returns string[] of node IDs
|
|
12
|
+
* - stats(): returns { totalNodes, totalEdges, avgDegree }
|
|
13
|
+
*/
|
|
14
|
+
export interface GraphNodeData {
|
|
15
|
+
id: string;
|
|
16
|
+
type: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
properties?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export interface GraphEdgeData {
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
label: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
weight?: number;
|
|
26
|
+
properties?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface GraphStats {
|
|
29
|
+
totalNodes: number;
|
|
30
|
+
totalEdges: number;
|
|
31
|
+
avgDegree: number;
|
|
32
|
+
backend: 'graph-node' | 'unavailable';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if graph-node backend is available
|
|
36
|
+
*/
|
|
37
|
+
export declare function isGraphBackendAvailable(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Add a node to the graph (agent, task, pattern, etc.)
|
|
40
|
+
*/
|
|
41
|
+
export declare function addNode(data: GraphNodeData): Promise<string | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Add an edge between two nodes
|
|
44
|
+
*/
|
|
45
|
+
export declare function addEdge(data: GraphEdgeData): Promise<string | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Create a hyperedge connecting multiple nodes (e.g., swarm teams)
|
|
48
|
+
*/
|
|
49
|
+
export declare function addHyperedge(nodeIds: string[], label: string, description?: string, properties?: Record<string, unknown>): Promise<string | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Get k-hop neighbors of a node
|
|
52
|
+
*/
|
|
53
|
+
export declare function getNeighbors(nodeId: string, hops?: number): Promise<string[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Get graph statistics
|
|
56
|
+
*/
|
|
57
|
+
export declare function getGraphStats(): Promise<GraphStats>;
|
|
58
|
+
/**
|
|
59
|
+
* Record a causal edge (used by agentdb_causal-edge MCP tool)
|
|
60
|
+
*/
|
|
61
|
+
export declare function recordCausalEdge(sourceId: string, targetId: string, relation: string, weight?: number): Promise<{
|
|
62
|
+
success: boolean;
|
|
63
|
+
edgeId?: string;
|
|
64
|
+
backend: string;
|
|
65
|
+
}>;
|
|
66
|
+
/**
|
|
67
|
+
* Record agent collaboration (used by swarm coordination)
|
|
68
|
+
*/
|
|
69
|
+
export declare function recordCollaboration(agentId: string, agentType: string, taskId: string, taskName: string): Promise<{
|
|
70
|
+
success: boolean;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Record swarm team as a hyperedge
|
|
74
|
+
*/
|
|
75
|
+
export declare function recordSwarmTeam(agentIds: string[], topology: string, taskDescription?: string): Promise<{
|
|
76
|
+
success: boolean;
|
|
77
|
+
hyperedgeId?: string;
|
|
78
|
+
}>;
|
|
79
|
+
//# sourceMappingURL=graph-backend.d.ts.map
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ruvector/graph-node native graph database backend (ADR-087)
|
|
3
|
+
*
|
|
4
|
+
* Provides persistent graph storage for agent relationships, causal edges,
|
|
5
|
+
* task dependencies, and swarm topology using native Rust bindings.
|
|
6
|
+
*
|
|
7
|
+
* API requirements discovered via testing:
|
|
8
|
+
* - createNode: requires { id, type, embedding }
|
|
9
|
+
* - createEdge: requires { from, to, label, description, embedding, properties }
|
|
10
|
+
* - createHyperedge: requires { nodes[], label, description, embedding, properties }
|
|
11
|
+
* - kHopNeighbors(nodeId, k): returns string[] of node IDs
|
|
12
|
+
* - stats(): returns { totalNodes, totalEdges, avgDegree }
|
|
13
|
+
*/
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
// Lazy-loaded graph-node module
|
|
16
|
+
let graphNodeModule = null;
|
|
17
|
+
let graphDb = null;
|
|
18
|
+
let graphBackendLoaded = false;
|
|
19
|
+
let graphBackendAvailable = false;
|
|
20
|
+
const DEFAULT_EMBEDDING_DIM = 8; // Minimal embedding for graph structure
|
|
21
|
+
/**
|
|
22
|
+
* Load @ruvector/graph-node via createRequire (CJS package)
|
|
23
|
+
*/
|
|
24
|
+
async function loadGraphNode() {
|
|
25
|
+
if (graphBackendLoaded)
|
|
26
|
+
return graphNodeModule;
|
|
27
|
+
graphBackendLoaded = true;
|
|
28
|
+
try {
|
|
29
|
+
const { createRequire } = await import('module');
|
|
30
|
+
const requireCjs = createRequire(import.meta.url);
|
|
31
|
+
graphNodeModule = requireCjs('@ruvector/graph-node');
|
|
32
|
+
graphBackendAvailable = true;
|
|
33
|
+
return graphNodeModule;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
graphBackendAvailable = false;
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get or create the singleton graph database instance
|
|
42
|
+
*/
|
|
43
|
+
async function getGraphDb() {
|
|
44
|
+
if (graphDb)
|
|
45
|
+
return graphDb;
|
|
46
|
+
const mod = await loadGraphNode();
|
|
47
|
+
if (!mod)
|
|
48
|
+
return null;
|
|
49
|
+
// Use persistent path if available, otherwise in-memory
|
|
50
|
+
const dataDir = join(process.cwd(), '.claude-flow', 'graph');
|
|
51
|
+
try {
|
|
52
|
+
const fs = await import('fs');
|
|
53
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
54
|
+
graphDb = new mod.GraphDatabase(join(dataDir, 'agents.db'));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Fallback to in-memory
|
|
58
|
+
graphDb = new mod.GraphDatabase();
|
|
59
|
+
}
|
|
60
|
+
return graphDb;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Create a minimal embedding for non-vector graph operations.
|
|
64
|
+
* Uses a deterministic hash of the string content.
|
|
65
|
+
*/
|
|
66
|
+
function textToMiniEmbedding(text) {
|
|
67
|
+
const emb = new Float32Array(DEFAULT_EMBEDDING_DIM);
|
|
68
|
+
for (let i = 0; i < text.length; i++) {
|
|
69
|
+
emb[i % DEFAULT_EMBEDDING_DIM] += text.charCodeAt(i) / 256;
|
|
70
|
+
}
|
|
71
|
+
// Normalize
|
|
72
|
+
let norm = 0;
|
|
73
|
+
for (let i = 0; i < emb.length; i++)
|
|
74
|
+
norm += emb[i] * emb[i];
|
|
75
|
+
norm = Math.sqrt(norm) || 1;
|
|
76
|
+
for (let i = 0; i < emb.length; i++)
|
|
77
|
+
emb[i] /= norm;
|
|
78
|
+
return emb;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if graph-node backend is available
|
|
82
|
+
*/
|
|
83
|
+
export async function isGraphBackendAvailable() {
|
|
84
|
+
await loadGraphNode();
|
|
85
|
+
return graphBackendAvailable;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Add a node to the graph (agent, task, pattern, etc.)
|
|
89
|
+
*/
|
|
90
|
+
export async function addNode(data) {
|
|
91
|
+
const db = await getGraphDb();
|
|
92
|
+
if (!db)
|
|
93
|
+
return null;
|
|
94
|
+
try {
|
|
95
|
+
const embedding = textToMiniEmbedding(`${data.type}:${data.name || data.id}`);
|
|
96
|
+
return await db.createNode({
|
|
97
|
+
id: data.id,
|
|
98
|
+
type: data.type,
|
|
99
|
+
...data.properties,
|
|
100
|
+
embedding,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Add an edge between two nodes
|
|
109
|
+
*/
|
|
110
|
+
export async function addEdge(data) {
|
|
111
|
+
const db = await getGraphDb();
|
|
112
|
+
if (!db)
|
|
113
|
+
return null;
|
|
114
|
+
try {
|
|
115
|
+
const embedding = textToMiniEmbedding(`${data.label}:${data.from}->${data.to}`);
|
|
116
|
+
return await db.createEdge({
|
|
117
|
+
from: data.from,
|
|
118
|
+
to: data.to,
|
|
119
|
+
label: data.label,
|
|
120
|
+
description: data.description || data.label,
|
|
121
|
+
embedding,
|
|
122
|
+
properties: { weight: data.weight ?? 1.0, ...data.properties },
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Create a hyperedge connecting multiple nodes (e.g., swarm teams)
|
|
131
|
+
*/
|
|
132
|
+
export async function addHyperedge(nodeIds, label, description, properties) {
|
|
133
|
+
const db = await getGraphDb();
|
|
134
|
+
if (!db)
|
|
135
|
+
return null;
|
|
136
|
+
try {
|
|
137
|
+
const embedding = textToMiniEmbedding(`${label}:${nodeIds.join(',')}`);
|
|
138
|
+
return await db.createHyperedge({
|
|
139
|
+
nodes: nodeIds,
|
|
140
|
+
label,
|
|
141
|
+
description: description || label,
|
|
142
|
+
embedding,
|
|
143
|
+
properties: properties || {},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get k-hop neighbors of a node
|
|
152
|
+
*/
|
|
153
|
+
export async function getNeighbors(nodeId, hops = 2) {
|
|
154
|
+
const db = await getGraphDb();
|
|
155
|
+
if (!db)
|
|
156
|
+
return [];
|
|
157
|
+
try {
|
|
158
|
+
return await db.kHopNeighbors(nodeId, hops);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get graph statistics
|
|
166
|
+
*/
|
|
167
|
+
export async function getGraphStats() {
|
|
168
|
+
const db = await getGraphDb();
|
|
169
|
+
if (!db)
|
|
170
|
+
return { totalNodes: 0, totalEdges: 0, avgDegree: 0, backend: 'unavailable' };
|
|
171
|
+
try {
|
|
172
|
+
const stats = await db.stats();
|
|
173
|
+
return { ...stats, backend: 'graph-node' };
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return { totalNodes: 0, totalEdges: 0, avgDegree: 0, backend: 'unavailable' };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Record a causal edge (used by agentdb_causal-edge MCP tool)
|
|
181
|
+
*/
|
|
182
|
+
export async function recordCausalEdge(sourceId, targetId, relation, weight) {
|
|
183
|
+
// Ensure both nodes exist
|
|
184
|
+
await addNode({ id: sourceId, type: 'memory-entry' });
|
|
185
|
+
await addNode({ id: targetId, type: 'memory-entry' });
|
|
186
|
+
const edgeId = await addEdge({
|
|
187
|
+
from: sourceId,
|
|
188
|
+
to: targetId,
|
|
189
|
+
label: relation,
|
|
190
|
+
description: `${relation}: ${sourceId} -> ${targetId}`,
|
|
191
|
+
weight,
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
success: edgeId !== null,
|
|
195
|
+
edgeId: edgeId ?? undefined,
|
|
196
|
+
backend: graphBackendAvailable ? 'graph-node' : 'unavailable',
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Record agent collaboration (used by swarm coordination)
|
|
201
|
+
*/
|
|
202
|
+
export async function recordCollaboration(agentId, agentType, taskId, taskName) {
|
|
203
|
+
await addNode({ id: agentId, type: 'agent', name: agentType });
|
|
204
|
+
await addNode({ id: taskId, type: 'task', name: taskName });
|
|
205
|
+
const edgeId = await addEdge({
|
|
206
|
+
from: agentId,
|
|
207
|
+
to: taskId,
|
|
208
|
+
label: 'assigned_to',
|
|
209
|
+
description: `${agentType} works on ${taskName}`,
|
|
210
|
+
});
|
|
211
|
+
return { success: edgeId !== null };
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Record swarm team as a hyperedge
|
|
215
|
+
*/
|
|
216
|
+
export async function recordSwarmTeam(agentIds, topology, taskDescription) {
|
|
217
|
+
const heId = await addHyperedge(agentIds, 'swarm-team', taskDescription || `${topology} swarm with ${agentIds.length} agents`, { topology, agentCount: agentIds.length });
|
|
218
|
+
return { success: heId !== null, hyperedgeId: heId ?? undefined };
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=graph-backend.js.map
|
|
@@ -94,6 +94,8 @@ export interface LoRAStats {
|
|
|
94
94
|
avgAdaptationNorm: number;
|
|
95
95
|
/** Last update timestamp */
|
|
96
96
|
lastUpdate: number | null;
|
|
97
|
+
/** Training backend in use ('ruvllm' | 'js-fallback') */
|
|
98
|
+
_trainingBackend?: string;
|
|
97
99
|
}
|
|
98
100
|
/**
|
|
99
101
|
* Low-Rank Adaptation module for efficient embedding fine-tuning
|
|
@@ -107,6 +109,16 @@ export declare class LoRAAdapter {
|
|
|
107
109
|
private lastUpdate;
|
|
108
110
|
private updatesSinceLastSave;
|
|
109
111
|
constructor(config?: Partial<LoRAConfig>);
|
|
112
|
+
/**
|
|
113
|
+
* Eagerly load the ruvllm TrainingPipeline backend.
|
|
114
|
+
* Called automatically during first checkpoint operation,
|
|
115
|
+
* or call explicitly to make getStats() report backend status.
|
|
116
|
+
*/
|
|
117
|
+
initBackend(): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Get a readonly copy of the adapter configuration
|
|
120
|
+
*/
|
|
121
|
+
getConfig(): Readonly<LoRAConfig>;
|
|
110
122
|
/**
|
|
111
123
|
* Initialize weights with Kaiming/He initialization
|
|
112
124
|
*/
|
|
@@ -156,6 +168,16 @@ export declare class LoRAAdapter {
|
|
|
156
168
|
* Load weights from disk
|
|
157
169
|
*/
|
|
158
170
|
loadWeights(): boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Save a training checkpoint via ruvllm TrainingPipeline.
|
|
173
|
+
* Falls back to writing our own weight JSON if ruvllm is unavailable.
|
|
174
|
+
*/
|
|
175
|
+
saveCheckpoint(path: string): Promise<boolean>;
|
|
176
|
+
/**
|
|
177
|
+
* Load a training checkpoint via ruvllm TrainingPipeline.
|
|
178
|
+
* Falls back to reading our own weight JSON if ruvllm is unavailable.
|
|
179
|
+
*/
|
|
180
|
+
loadCheckpoint(path: string): Promise<boolean>;
|
|
159
181
|
/**
|
|
160
182
|
* Export weights as JSON
|
|
161
183
|
*/
|
|
@@ -53,6 +53,33 @@ const DEFAULT_CONFIG = {
|
|
|
53
53
|
autoSaveInterval: 50,
|
|
54
54
|
};
|
|
55
55
|
// ============================================================================
|
|
56
|
+
// @ruvector/ruvllm TrainingPipeline (lazy-loaded)
|
|
57
|
+
// ============================================================================
|
|
58
|
+
let ruvllmPipeline = null;
|
|
59
|
+
let pipelineLoaded = false;
|
|
60
|
+
async function loadTrainingPipeline(adapter) {
|
|
61
|
+
if (pipelineLoaded)
|
|
62
|
+
return ruvllmPipeline;
|
|
63
|
+
pipelineLoaded = true;
|
|
64
|
+
try {
|
|
65
|
+
const { createRequire } = await import('module');
|
|
66
|
+
const requireCjs = createRequire(import.meta.url);
|
|
67
|
+
const ruvllm = requireCjs('@ruvector/ruvllm');
|
|
68
|
+
const config = adapter.getConfig();
|
|
69
|
+
// Create a ruvllm LoraAdapter that mirrors our JS adapter's config
|
|
70
|
+
const nativeAdapter = new ruvllm.LoraAdapter(config.inputDim, config.outputDim, config.rank);
|
|
71
|
+
ruvllmPipeline = new ruvllm.TrainingPipeline({
|
|
72
|
+
adapter: nativeAdapter,
|
|
73
|
+
learningRate: 0.001,
|
|
74
|
+
batchSize: 8,
|
|
75
|
+
});
|
|
76
|
+
return ruvllmPipeline;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// ============================================================================
|
|
56
83
|
// LoRA Adapter Class
|
|
57
84
|
// ============================================================================
|
|
58
85
|
/**
|
|
@@ -70,6 +97,20 @@ export class LoRAAdapter {
|
|
|
70
97
|
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
71
98
|
this.weights = this.initializeWeights();
|
|
72
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Eagerly load the ruvllm TrainingPipeline backend.
|
|
102
|
+
* Called automatically during first checkpoint operation,
|
|
103
|
+
* or call explicitly to make getStats() report backend status.
|
|
104
|
+
*/
|
|
105
|
+
async initBackend() {
|
|
106
|
+
await loadTrainingPipeline(this);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get a readonly copy of the adapter configuration
|
|
110
|
+
*/
|
|
111
|
+
getConfig() {
|
|
112
|
+
return this.config;
|
|
113
|
+
}
|
|
73
114
|
/**
|
|
74
115
|
* Initialize weights with Kaiming/He initialization
|
|
75
116
|
*/
|
|
@@ -254,6 +295,9 @@ export class LoRAAdapter {
|
|
|
254
295
|
? this.adaptationNormSum / this.totalAdaptations
|
|
255
296
|
: 0,
|
|
256
297
|
lastUpdate: this.lastUpdate,
|
|
298
|
+
_trainingBackend: pipelineLoaded
|
|
299
|
+
? (ruvllmPipeline ? 'ruvllm' : 'js-fallback')
|
|
300
|
+
: 'js-fallback',
|
|
257
301
|
};
|
|
258
302
|
}
|
|
259
303
|
/**
|
|
@@ -341,6 +385,54 @@ export class LoRAAdapter {
|
|
|
341
385
|
return false;
|
|
342
386
|
}
|
|
343
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Save a training checkpoint via ruvllm TrainingPipeline.
|
|
390
|
+
* Falls back to writing our own weight JSON if ruvllm is unavailable.
|
|
391
|
+
*/
|
|
392
|
+
async saveCheckpoint(path) {
|
|
393
|
+
const pipeline = await loadTrainingPipeline(this);
|
|
394
|
+
if (pipeline) {
|
|
395
|
+
try {
|
|
396
|
+
pipeline.saveCheckpoint(path);
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
catch { /* fall through to JS fallback */ }
|
|
400
|
+
}
|
|
401
|
+
// Fallback: save our own weights as JSON
|
|
402
|
+
try {
|
|
403
|
+
const fs = await import('fs');
|
|
404
|
+
const data = JSON.stringify(this.exportWeights());
|
|
405
|
+
fs.writeFileSync(path, data);
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Load a training checkpoint via ruvllm TrainingPipeline.
|
|
414
|
+
* Falls back to reading our own weight JSON if ruvllm is unavailable.
|
|
415
|
+
*/
|
|
416
|
+
async loadCheckpoint(path) {
|
|
417
|
+
const pipeline = await loadTrainingPipeline(this);
|
|
418
|
+
if (pipeline) {
|
|
419
|
+
try {
|
|
420
|
+
pipeline.loadCheckpoint(path);
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
catch { /* fall through to JS fallback */ }
|
|
424
|
+
}
|
|
425
|
+
// Fallback: load from our JSON format
|
|
426
|
+
try {
|
|
427
|
+
const fs = await import('fs');
|
|
428
|
+
if (fs.existsSync(path)) {
|
|
429
|
+
const data = JSON.parse(fs.readFileSync(path, 'utf-8'));
|
|
430
|
+
return this.importWeights(data);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch { /* return false below */ }
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
344
436
|
/**
|
|
345
437
|
* Export weights as JSON
|
|
346
438
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.74",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|