UNPKG

claude-flow

Version:

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

183 lines 6.21 kB
/** * Graph Edge Writer — ADR-130 Phase 1 * * Provides a minimal interface for inserting rows into the graph_edges * sql.js table defined by MEMORY_SCHEMA_V3. * * This module is intentionally thin: it opens the shared sql.js SQLite db * (the same file used by memory-initializer storeEntry), ensures the * graph_edges table exists, and returns a better-sqlite3-compatible * prepared-statement interface. * * The module is designed for fire-and-forget callers — every public function * suppresses errors internally so callers never need try/catch. * * @module v3/cli/memory/graph-edge-writer */ import * as path from 'path'; import * as fs from 'fs'; import * as crypto from 'crypto'; import { getMemoryRoot } from './memory-initializer.js'; import { encodeEmbedding } from './embedding-quantization.js'; // ============================================================================ // Lazy-cached sql.js db handle // ============================================================================ let _db = null; let _dbPath = ''; let _dbInitializing = false; /** * Return the sql.js Database instance for graph_edges writes. * Creates the graph_edges table if it is absent (idempotent). * Returns null if sql.js is not available or db cannot be opened. */ export async function getBridgeDb(customDbPath) { const dbPath = customDbPath ?? path.join(getMemoryRoot(), 'memory.db'); if (_db && _dbPath === dbPath) return _db; if (_dbInitializing) return null; _dbInitializing = true; try { if (!fs.existsSync(dbPath)) return null; const initSqlJs = (await import('sql.js')).default; const SQL = await initSqlJs(); const fileBuffer = fs.readFileSync(dbPath); const db = new SQL.Database(fileBuffer); // Ensure graph_edges table exists (in case this is an older DB that // predates ADR-130 Phase 1 schema migration). db.run(` CREATE TABLE IF NOT EXISTS graph_edges ( id TEXT PRIMARY KEY, source_id TEXT NOT NULL, target_id TEXT NOT NULL, relation TEXT NOT NULL, weight REAL DEFAULT 1.0, confidence REAL DEFAULT 1.0, decay_rate REAL DEFAULT 0.0, last_reinforced TEXT, witness_id TEXT, embedding_ref TEXT, metadata TEXT, created_at TEXT NOT NULL ) `); db.run(`CREATE INDEX IF NOT EXISTS idx_graph_edges_source ON graph_edges (source_id)`); db.run(`CREATE INDEX IF NOT EXISTS idx_graph_edges_target ON graph_edges (target_id)`); db.run(`CREATE INDEX IF NOT EXISTS idx_graph_edges_relation ON graph_edges (relation)`); db.run(`CREATE INDEX IF NOT EXISTS idx_graph_edges_reinforced ON graph_edges (last_reinforced)`); _db = db; _dbPath = dbPath; return db; } catch { return null; } finally { _dbInitializing = false; } } /** * Persist the in-memory sql.js database back to disk. * Called after each write to keep the file consistent. */ async function flushDb(db) { try { if (!_dbPath) return; const data = db.export(); fs.writeFileSync(_dbPath, Buffer.from(data)); } catch { /* non-fatal */ } } /** * Insert a single edge into graph_edges. * Fire-and-forget — errors are suppressed. * Returns true if the write succeeded, false otherwise. */ export async function insertGraphEdge(input) { try { const db = await getBridgeDb(input.dbPath); if (!db) return false; const id = `edge-${crypto.randomUUID()}`; const createdAt = new Date().toISOString(); let embeddingRef = null; if (input.embedding && input.embedding.length > 0) { embeddingRef = encodeEmbedding(input.embedding); } const metaStr = input.metadata ? JSON.stringify(input.metadata) : null; db.run(`INSERT OR IGNORE INTO graph_edges (id, source_id, target_id, relation, weight, confidence, decay_rate, last_reinforced, witness_id, embedding_ref, metadata, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ id, input.sourceId, input.targetId, input.relation, input.weight ?? 1.0, input.confidence ?? 1.0, input.decayRate ?? 0.0, input.lastReinforced ?? null, input.witnessId ?? null, embeddingRef, metaStr, createdAt, ]); await flushDb(db); return true; } catch { return false; } } /** * Query graph_edges by source_id. * Returns rows or empty array on error. */ export async function queryEdgesBySource(sourceId, relation, dbPath) { try { const db = await getBridgeDb(dbPath); if (!db) return []; const sql = relation ? `SELECT id, source_id, target_id, relation, weight FROM graph_edges WHERE source_id = ? AND relation = ? LIMIT 1000` : `SELECT id, source_id, target_id, relation, weight FROM graph_edges WHERE source_id = ? LIMIT 1000`; const args = relation ? [sourceId, relation] : [sourceId]; const result = db.exec(sql, args); if (!result?.[0]) return []; const cols = result[0].columns; return result[0].values.map((row) => { const obj = {}; cols.forEach((c, i) => { obj[c] = row[i]; }); return obj; }); } catch { return []; } } /** * Count rows in graph_edges (for test assertions). */ export async function countGraphEdges(dbPath) { try { const db = await getBridgeDb(dbPath); if (!db) return 0; const result = db.exec(`SELECT COUNT(*) FROM graph_edges`); return result?.[0]?.values?.[0]?.[0] ?? 0; } catch { return 0; } } /** * Reset the cached db handle (for tests that need a fresh DB). */ export function _resetBridgeDb() { _db = null; _dbPath = ''; } //# sourceMappingURL=graph-edge-writer.js.map