@cogitator-ai/memory 0.10.0 → 0.11.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.
Files changed (37) hide show
  1. package/README.md +204 -88
  2. package/dist/adapters/postgres.d.ts +1 -0
  3. package/dist/adapters/postgres.d.ts.map +1 -1
  4. package/dist/adapters/postgres.js +78 -38
  5. package/dist/adapters/postgres.js.map +1 -1
  6. package/dist/adapters/qdrant.d.ts.map +1 -1
  7. package/dist/adapters/qdrant.js +16 -2
  8. package/dist/adapters/qdrant.js.map +1 -1
  9. package/dist/adapters/sqlite.d.ts +1 -0
  10. package/dist/adapters/sqlite.d.ts.map +1 -1
  11. package/dist/adapters/sqlite.js +17 -16
  12. package/dist/adapters/sqlite.js.map +1 -1
  13. package/dist/compaction.d.ts +14 -1
  14. package/dist/compaction.d.ts.map +1 -1
  15. package/dist/compaction.js +4 -1
  16. package/dist/compaction.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/knowledge-graph/entity-extractor.d.ts +24 -11
  22. package/dist/knowledge-graph/entity-extractor.d.ts.map +1 -1
  23. package/dist/knowledge-graph/entity-extractor.js +15 -3
  24. package/dist/knowledge-graph/entity-extractor.js.map +1 -1
  25. package/dist/knowledge-graph/graph-adapter.d.ts +1 -0
  26. package/dist/knowledge-graph/graph-adapter.d.ts.map +1 -1
  27. package/dist/knowledge-graph/graph-adapter.js +470 -448
  28. package/dist/knowledge-graph/graph-adapter.js.map +1 -1
  29. package/dist/knowledge-graph/sqlite-graph-adapter.d.ts +1 -0
  30. package/dist/knowledge-graph/sqlite-graph-adapter.d.ts.map +1 -1
  31. package/dist/knowledge-graph/sqlite-graph-adapter.js +459 -435
  32. package/dist/knowledge-graph/sqlite-graph-adapter.js.map +1 -1
  33. package/dist/schema.d.ts +4 -0
  34. package/dist/schema.d.ts.map +1 -1
  35. package/dist/schema.js +2 -0
  36. package/dist/schema.js.map +1 -1
  37. package/package.json +3 -3
@@ -1,4 +1,5 @@
1
1
  import { nanoid } from 'nanoid';
2
+ import { unwrap } from '../result.js';
2
3
  export class SQLiteGraphAdapter {
3
4
  db = null;
4
5
  path;
@@ -87,6 +88,15 @@ export class SQLiteGraphAdapter {
87
88
  failure(error) {
88
89
  return { success: false, error };
89
90
  }
91
+ async run(operation) {
92
+ try {
93
+ await this.initialize();
94
+ return await operation();
95
+ }
96
+ catch (err) {
97
+ return this.failure(err instanceof Error ? err.message : String(err));
98
+ }
99
+ }
90
100
  generateId(prefix) {
91
101
  return `${prefix}_${nanoid(12)}`;
92
102
  }
@@ -164,300 +174,313 @@ export class SQLiteGraphAdapter {
164
174
  return dot / denom;
165
175
  }
166
176
  async addNode(node) {
167
- await this.initialize();
168
- const db = this.ensureDb();
169
- const id = this.generateId('node');
170
- const now = new Date();
171
- const nowStr = now.toISOString();
172
- db.prepare(`INSERT INTO graph_nodes
173
- (id, agent_id, type, name, aliases, description, properties, embedding, confidence, source, metadata, access_count, created_at, updated_at, last_accessed_at)
174
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`).run(id, node.agentId, node.type, node.name, JSON.stringify(node.aliases), node.description ?? null, JSON.stringify(node.properties), node.embedding ? JSON.stringify(node.embedding) : null, node.confidence, node.source, JSON.stringify(node.metadata ?? {}), nowStr, nowStr, nowStr);
175
- return this.success({
176
- ...node,
177
- id,
178
- createdAt: now,
179
- updatedAt: now,
180
- lastAccessedAt: now,
181
- accessCount: 0,
177
+ return this.run(async () => {
178
+ const db = this.ensureDb();
179
+ const id = this.generateId('node');
180
+ const now = new Date();
181
+ const nowStr = now.toISOString();
182
+ db.prepare(`INSERT INTO graph_nodes
183
+ (id, agent_id, type, name, aliases, description, properties, embedding, confidence, source, metadata, access_count, created_at, updated_at, last_accessed_at)
184
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)`).run(id, node.agentId, node.type, node.name, JSON.stringify(node.aliases), node.description ?? null, JSON.stringify(node.properties), node.embedding ? JSON.stringify(node.embedding) : null, node.confidence, node.source, JSON.stringify(node.metadata ?? {}), nowStr, nowStr, nowStr);
185
+ return this.success({
186
+ ...node,
187
+ id,
188
+ createdAt: now,
189
+ updatedAt: now,
190
+ lastAccessedAt: now,
191
+ accessCount: 0,
192
+ });
182
193
  });
183
194
  }
184
195
  async getNode(nodeId) {
185
- await this.initialize();
186
- const db = this.ensureDb();
187
- db.prepare(`UPDATE graph_nodes SET last_accessed_at = ?, access_count = access_count + 1 WHERE id = ?`).run(new Date().toISOString(), nodeId);
188
- const row = db.prepare(`SELECT * FROM graph_nodes WHERE id = ?`).get(nodeId);
189
- if (!row)
190
- return this.success(null);
191
- return this.success(this.rowToNode(row));
196
+ return this.run(async () => {
197
+ const db = this.ensureDb();
198
+ db.prepare(`UPDATE graph_nodes SET last_accessed_at = ?, access_count = access_count + 1 WHERE id = ?`).run(new Date().toISOString(), nodeId);
199
+ const row = db.prepare(`SELECT * FROM graph_nodes WHERE id = ?`).get(nodeId);
200
+ if (!row)
201
+ return this.success(null);
202
+ return this.success(this.rowToNode(row));
203
+ });
192
204
  }
193
205
  async getNodeByName(agentId, name) {
194
- await this.initialize();
195
- const db = this.ensureDb();
196
- const row = db
197
- .prepare(`SELECT * FROM graph_nodes WHERE agent_id = ? AND name = ?`)
198
- .get(agentId, name);
199
- if (row)
200
- return this.success(this.rowToNode(row));
201
- const allRows = db
202
- .prepare(`SELECT * FROM graph_nodes WHERE agent_id = ?`)
203
- .all(agentId);
204
- for (const r of allRows) {
205
- const aliases = JSON.parse(r.aliases);
206
- if (aliases.includes(name)) {
207
- return this.success(this.rowToNode(r));
206
+ return this.run(async () => {
207
+ const db = this.ensureDb();
208
+ const row = db
209
+ .prepare(`SELECT * FROM graph_nodes WHERE agent_id = ? AND name = ?`)
210
+ .get(agentId, name);
211
+ if (row)
212
+ return this.success(this.rowToNode(row));
213
+ const allRows = db
214
+ .prepare(`SELECT * FROM graph_nodes WHERE agent_id = ?`)
215
+ .all(agentId);
216
+ for (const r of allRows) {
217
+ const aliases = JSON.parse(r.aliases);
218
+ if (aliases.includes(name)) {
219
+ return this.success(this.rowToNode(r));
220
+ }
208
221
  }
209
- }
210
- return this.success(null);
222
+ return this.success(null);
223
+ });
211
224
  }
212
225
  async updateNode(nodeId, updates) {
213
- await this.initialize();
214
- const db = this.ensureDb();
215
- const setClauses = ['updated_at = ?'];
216
- const params = [new Date().toISOString()];
217
- if (updates.name !== undefined) {
218
- setClauses.push('name = ?');
219
- params.push(updates.name);
220
- }
221
- if (updates.aliases !== undefined) {
222
- setClauses.push('aliases = ?');
223
- params.push(JSON.stringify(updates.aliases));
224
- }
225
- if (updates.description !== undefined) {
226
- setClauses.push('description = ?');
227
- params.push(updates.description);
228
- }
229
- if (updates.properties !== undefined) {
230
- setClauses.push('properties = ?');
231
- params.push(JSON.stringify(updates.properties));
232
- }
233
- if (updates.confidence !== undefined) {
234
- setClauses.push('confidence = ?');
235
- params.push(updates.confidence);
236
- }
237
- if (updates.metadata !== undefined) {
238
- setClauses.push('metadata = ?');
239
- params.push(JSON.stringify(updates.metadata));
240
- }
241
- if (updates.embedding !== undefined) {
242
- setClauses.push('embedding = ?');
243
- params.push(JSON.stringify(updates.embedding));
244
- }
245
- params.push(nodeId);
246
- const result = db
247
- .prepare(`UPDATE graph_nodes SET ${setClauses.join(', ')} WHERE id = ?`)
248
- .run(...params);
249
- if (result.changes === 0) {
250
- return this.failure(`Node not found: ${nodeId}`);
251
- }
252
- const row = db.prepare(`SELECT * FROM graph_nodes WHERE id = ?`).get(nodeId);
253
- return this.success(this.rowToNode(row));
226
+ return this.run(async () => {
227
+ const db = this.ensureDb();
228
+ const setClauses = ['updated_at = ?'];
229
+ const params = [new Date().toISOString()];
230
+ if (updates.name !== undefined) {
231
+ setClauses.push('name = ?');
232
+ params.push(updates.name);
233
+ }
234
+ if (updates.aliases !== undefined) {
235
+ setClauses.push('aliases = ?');
236
+ params.push(JSON.stringify(updates.aliases));
237
+ }
238
+ if (updates.description !== undefined) {
239
+ setClauses.push('description = ?');
240
+ params.push(updates.description);
241
+ }
242
+ if (updates.properties !== undefined) {
243
+ setClauses.push('properties = ?');
244
+ params.push(JSON.stringify(updates.properties));
245
+ }
246
+ if (updates.confidence !== undefined) {
247
+ setClauses.push('confidence = ?');
248
+ params.push(updates.confidence);
249
+ }
250
+ if (updates.metadata !== undefined) {
251
+ setClauses.push('metadata = ?');
252
+ params.push(JSON.stringify(updates.metadata));
253
+ }
254
+ if (updates.embedding !== undefined) {
255
+ setClauses.push('embedding = ?');
256
+ params.push(JSON.stringify(updates.embedding));
257
+ }
258
+ params.push(nodeId);
259
+ const result = db
260
+ .prepare(`UPDATE graph_nodes SET ${setClauses.join(', ')} WHERE id = ?`)
261
+ .run(...params);
262
+ if (result.changes === 0) {
263
+ return this.failure(`Node not found: ${nodeId}`);
264
+ }
265
+ const row = db.prepare(`SELECT * FROM graph_nodes WHERE id = ?`).get(nodeId);
266
+ return this.success(this.rowToNode(row));
267
+ });
254
268
  }
255
269
  async deleteNode(nodeId) {
256
- await this.initialize();
257
- const db = this.ensureDb();
258
- db.prepare(`DELETE FROM graph_nodes WHERE id = ?`).run(nodeId);
259
- return this.success(undefined);
270
+ return this.run(async () => {
271
+ const db = this.ensureDb();
272
+ db.prepare(`DELETE FROM graph_nodes WHERE id = ?`).run(nodeId);
273
+ return this.success(undefined);
274
+ });
260
275
  }
261
276
  async queryNodes(query) {
262
- await this.initialize();
263
- const db = this.ensureDb();
264
- let sql = `SELECT * FROM graph_nodes WHERE agent_id = ?`;
265
- const params = [query.agentId];
266
- if (query.types && query.types.length > 0) {
267
- const placeholders = query.types.map(() => '?').join(', ');
268
- sql += ` AND type IN (${placeholders})`;
269
- params.push(...query.types);
270
- }
271
- if (query.namePattern) {
272
- sql += ` AND name LIKE ? ESCAPE '\\'`;
273
- params.push(`%${query.namePattern.replace(/[%_\\]/g, '\\$&')}%`);
274
- }
275
- if (query.minConfidence !== undefined) {
276
- sql += ` AND confidence >= ?`;
277
- params.push(query.minConfidence);
278
- }
279
- sql += ' ORDER BY access_count DESC, updated_at DESC';
280
- if (query.limit) {
281
- sql += ` LIMIT ?`;
282
- params.push(query.limit);
283
- }
284
- const rows = db.prepare(sql).all(...params);
285
- return this.success(rows.map((row) => this.rowToNode(row, query.includeEmbedding)));
277
+ return this.run(async () => {
278
+ const db = this.ensureDb();
279
+ let sql = `SELECT * FROM graph_nodes WHERE agent_id = ?`;
280
+ const params = [query.agentId];
281
+ if (query.types && query.types.length > 0) {
282
+ const placeholders = query.types.map(() => '?').join(', ');
283
+ sql += ` AND type IN (${placeholders})`;
284
+ params.push(...query.types);
285
+ }
286
+ if (query.namePattern) {
287
+ sql += ` AND name LIKE ? ESCAPE '\\'`;
288
+ params.push(`%${query.namePattern.replace(/[%_\\]/g, '\\$&')}%`);
289
+ }
290
+ if (query.minConfidence !== undefined) {
291
+ sql += ` AND confidence >= ?`;
292
+ params.push(query.minConfidence);
293
+ }
294
+ sql += ' ORDER BY access_count DESC, updated_at DESC';
295
+ if (query.limit) {
296
+ sql += ` LIMIT ?`;
297
+ params.push(query.limit);
298
+ }
299
+ const rows = db.prepare(sql).all(...params);
300
+ return this.success(rows.map((row) => this.rowToNode(row, query.includeEmbedding)));
301
+ });
286
302
  }
287
303
  async searchNodesSemantic(options) {
288
- await this.initialize();
289
- const db = this.ensureDb();
290
- if (!options.vector) {
291
- return this.failure('searchNodesSemantic requires vector');
292
- }
293
- const limit = options.limit ?? 10;
294
- const threshold = options.threshold ?? 0.7;
295
- let sql = `SELECT * FROM graph_nodes WHERE agent_id = ? AND embedding IS NOT NULL`;
296
- const params = [options.agentId];
297
- if (options.entityTypes && options.entityTypes.length > 0) {
298
- const placeholders = options.entityTypes.map(() => '?').join(', ');
299
- sql += ` AND type IN (${placeholders})`;
300
- params.push(...options.entityTypes);
301
- }
302
- const rows = db.prepare(sql).all(...params);
303
- const scored = [];
304
- for (const row of rows) {
305
- if (!row.embedding)
306
- continue;
307
- const emb = JSON.parse(row.embedding);
308
- const score = this.cosineSimilarity(options.vector, emb);
309
- if (score >= threshold) {
310
- scored.push({ ...this.rowToNode(row), score });
304
+ return this.run(async () => {
305
+ const db = this.ensureDb();
306
+ if (!options.vector) {
307
+ return this.failure('searchNodesSemantic requires vector');
311
308
  }
312
- }
313
- scored.sort((a, b) => b.score - a.score);
314
- return this.success(scored.slice(0, limit));
309
+ const limit = options.limit ?? 10;
310
+ const threshold = options.threshold ?? 0.7;
311
+ let sql = `SELECT * FROM graph_nodes WHERE agent_id = ? AND embedding IS NOT NULL`;
312
+ const params = [options.agentId];
313
+ if (options.entityTypes && options.entityTypes.length > 0) {
314
+ const placeholders = options.entityTypes.map(() => '?').join(', ');
315
+ sql += ` AND type IN (${placeholders})`;
316
+ params.push(...options.entityTypes);
317
+ }
318
+ const rows = db.prepare(sql).all(...params);
319
+ const scored = [];
320
+ for (const row of rows) {
321
+ if (!row.embedding)
322
+ continue;
323
+ const emb = JSON.parse(row.embedding);
324
+ const score = this.cosineSimilarity(options.vector, emb);
325
+ if (score >= threshold) {
326
+ scored.push({ ...this.rowToNode(row), score });
327
+ }
328
+ }
329
+ scored.sort((a, b) => b.score - a.score);
330
+ return this.success(scored.slice(0, limit));
331
+ });
315
332
  }
316
333
  async addEdge(edge) {
317
- await this.initialize();
318
- const db = this.ensureDb();
319
- const id = this.generateId('edge');
320
- const now = new Date();
321
- const nowStr = now.toISOString();
322
- db.prepare(`INSERT INTO graph_edges
323
- (id, agent_id, source_node_id, target_node_id, type, label, weight, bidirectional, properties, confidence, source, valid_from, valid_until, metadata, created_at, updated_at)
324
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, edge.agentId, edge.sourceNodeId, edge.targetNodeId, edge.type, edge.label ?? null, edge.weight, edge.bidirectional ? 1 : 0, JSON.stringify(edge.properties), edge.confidence, edge.source, edge.validFrom?.toISOString() ?? null, edge.validUntil?.toISOString() ?? null, JSON.stringify(edge.metadata ?? {}), nowStr, nowStr);
325
- return this.success({
326
- ...edge,
327
- id,
328
- createdAt: now,
329
- updatedAt: now,
334
+ return this.run(async () => {
335
+ const db = this.ensureDb();
336
+ const id = this.generateId('edge');
337
+ const now = new Date();
338
+ const nowStr = now.toISOString();
339
+ db.prepare(`INSERT INTO graph_edges
340
+ (id, agent_id, source_node_id, target_node_id, type, label, weight, bidirectional, properties, confidence, source, valid_from, valid_until, metadata, created_at, updated_at)
341
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, edge.agentId, edge.sourceNodeId, edge.targetNodeId, edge.type, edge.label ?? null, edge.weight, edge.bidirectional ? 1 : 0, JSON.stringify(edge.properties), edge.confidence, edge.source, edge.validFrom?.toISOString() ?? null, edge.validUntil?.toISOString() ?? null, JSON.stringify(edge.metadata ?? {}), nowStr, nowStr);
342
+ return this.success({
343
+ ...edge,
344
+ id,
345
+ createdAt: now,
346
+ updatedAt: now,
347
+ });
330
348
  });
331
349
  }
332
350
  async getEdge(edgeId) {
333
- await this.initialize();
334
- const db = this.ensureDb();
335
- const row = db.prepare(`SELECT * FROM graph_edges WHERE id = ?`).get(edgeId);
336
- if (!row)
337
- return this.success(null);
338
- return this.success(this.rowToEdge(row));
351
+ return this.run(async () => {
352
+ const db = this.ensureDb();
353
+ const row = db.prepare(`SELECT * FROM graph_edges WHERE id = ?`).get(edgeId);
354
+ if (!row)
355
+ return this.success(null);
356
+ return this.success(this.rowToEdge(row));
357
+ });
339
358
  }
340
359
  async getEdgesBetween(sourceNodeId, targetNodeId) {
341
- await this.initialize();
342
- const db = this.ensureDb();
343
- const rows = db
344
- .prepare(`SELECT * FROM graph_edges
345
- WHERE (source_node_id = ? AND target_node_id = ?)
346
- OR (bidirectional = 1 AND source_node_id = ? AND target_node_id = ?)`)
347
- .all(sourceNodeId, targetNodeId, targetNodeId, sourceNodeId);
348
- return this.success(rows.map((row) => this.rowToEdge(row)));
360
+ return this.run(async () => {
361
+ const db = this.ensureDb();
362
+ const rows = db
363
+ .prepare(`SELECT * FROM graph_edges
364
+ WHERE (source_node_id = ? AND target_node_id = ?)
365
+ OR (bidirectional = 1 AND source_node_id = ? AND target_node_id = ?)`)
366
+ .all(sourceNodeId, targetNodeId, targetNodeId, sourceNodeId);
367
+ return this.success(rows.map((row) => this.rowToEdge(row)));
368
+ });
349
369
  }
350
370
  async updateEdge(edgeId, updates) {
351
- await this.initialize();
352
- const db = this.ensureDb();
353
- const setClauses = ['updated_at = ?'];
354
- const params = [new Date().toISOString()];
355
- if (updates.weight !== undefined) {
356
- setClauses.push('weight = ?');
357
- params.push(updates.weight);
358
- }
359
- if (updates.label !== undefined) {
360
- setClauses.push('label = ?');
361
- params.push(updates.label);
362
- }
363
- if (updates.properties !== undefined) {
364
- setClauses.push('properties = ?');
365
- params.push(JSON.stringify(updates.properties));
366
- }
367
- if (updates.confidence !== undefined) {
368
- setClauses.push('confidence = ?');
369
- params.push(updates.confidence);
370
- }
371
- if (updates.validFrom !== undefined) {
372
- setClauses.push('valid_from = ?');
373
- params.push(updates.validFrom.toISOString());
374
- }
375
- if (updates.validUntil !== undefined) {
376
- setClauses.push('valid_until = ?');
377
- params.push(updates.validUntil.toISOString());
378
- }
379
- if (updates.metadata !== undefined) {
380
- setClauses.push('metadata = ?');
381
- params.push(JSON.stringify(updates.metadata));
382
- }
383
- params.push(edgeId);
384
- const result = db
385
- .prepare(`UPDATE graph_edges SET ${setClauses.join(', ')} WHERE id = ?`)
386
- .run(...params);
387
- if (result.changes === 0) {
388
- return this.failure(`Edge not found: ${edgeId}`);
389
- }
390
- const row = db.prepare(`SELECT * FROM graph_edges WHERE id = ?`).get(edgeId);
391
- return this.success(this.rowToEdge(row));
371
+ return this.run(async () => {
372
+ const db = this.ensureDb();
373
+ const setClauses = ['updated_at = ?'];
374
+ const params = [new Date().toISOString()];
375
+ if (updates.weight !== undefined) {
376
+ setClauses.push('weight = ?');
377
+ params.push(updates.weight);
378
+ }
379
+ if (updates.label !== undefined) {
380
+ setClauses.push('label = ?');
381
+ params.push(updates.label);
382
+ }
383
+ if (updates.properties !== undefined) {
384
+ setClauses.push('properties = ?');
385
+ params.push(JSON.stringify(updates.properties));
386
+ }
387
+ if (updates.confidence !== undefined) {
388
+ setClauses.push('confidence = ?');
389
+ params.push(updates.confidence);
390
+ }
391
+ if (updates.validFrom !== undefined) {
392
+ setClauses.push('valid_from = ?');
393
+ params.push(updates.validFrom.toISOString());
394
+ }
395
+ if (updates.validUntil !== undefined) {
396
+ setClauses.push('valid_until = ?');
397
+ params.push(updates.validUntil.toISOString());
398
+ }
399
+ if (updates.metadata !== undefined) {
400
+ setClauses.push('metadata = ?');
401
+ params.push(JSON.stringify(updates.metadata));
402
+ }
403
+ params.push(edgeId);
404
+ const result = db
405
+ .prepare(`UPDATE graph_edges SET ${setClauses.join(', ')} WHERE id = ?`)
406
+ .run(...params);
407
+ if (result.changes === 0) {
408
+ return this.failure(`Edge not found: ${edgeId}`);
409
+ }
410
+ const row = db.prepare(`SELECT * FROM graph_edges WHERE id = ?`).get(edgeId);
411
+ return this.success(this.rowToEdge(row));
412
+ });
392
413
  }
393
414
  async deleteEdge(edgeId) {
394
- await this.initialize();
395
- const db = this.ensureDb();
396
- db.prepare(`DELETE FROM graph_edges WHERE id = ?`).run(edgeId);
397
- return this.success(undefined);
415
+ return this.run(async () => {
416
+ const db = this.ensureDb();
417
+ db.prepare(`DELETE FROM graph_edges WHERE id = ?`).run(edgeId);
418
+ return this.success(undefined);
419
+ });
398
420
  }
399
421
  async queryEdges(query) {
400
- await this.initialize();
401
- const db = this.ensureDb();
402
- let sql = `SELECT * FROM graph_edges WHERE agent_id = ?`;
403
- const params = [query.agentId];
404
- if (query.sourceNodeId) {
405
- sql += ` AND source_node_id = ?`;
406
- params.push(query.sourceNodeId);
407
- }
408
- if (query.targetNodeId) {
409
- sql += ` AND target_node_id = ?`;
410
- params.push(query.targetNodeId);
411
- }
412
- if (query.types && query.types.length > 0) {
413
- const placeholders = query.types.map(() => '?').join(', ');
414
- sql += ` AND type IN (${placeholders})`;
415
- params.push(...query.types);
416
- }
417
- if (query.minWeight !== undefined) {
418
- sql += ` AND weight >= ?`;
419
- params.push(query.minWeight);
420
- }
421
- if (query.minConfidence !== undefined) {
422
- sql += ` AND confidence >= ?`;
423
- params.push(query.minConfidence);
424
- }
425
- if (query.bidirectionalOnly) {
426
- sql += ' AND bidirectional = 1';
427
- }
428
- sql += ' ORDER BY weight DESC, confidence DESC';
429
- if (query.limit) {
430
- sql += ` LIMIT ?`;
431
- params.push(query.limit);
432
- }
433
- const rows = db.prepare(sql).all(...params);
434
- return this.success(rows.map((row) => this.rowToEdge(row)));
422
+ return this.run(async () => {
423
+ const db = this.ensureDb();
424
+ let sql = `SELECT * FROM graph_edges WHERE agent_id = ?`;
425
+ const params = [query.agentId];
426
+ if (query.sourceNodeId) {
427
+ sql += ` AND source_node_id = ?`;
428
+ params.push(query.sourceNodeId);
429
+ }
430
+ if (query.targetNodeId) {
431
+ sql += ` AND target_node_id = ?`;
432
+ params.push(query.targetNodeId);
433
+ }
434
+ if (query.types && query.types.length > 0) {
435
+ const placeholders = query.types.map(() => '?').join(', ');
436
+ sql += ` AND type IN (${placeholders})`;
437
+ params.push(...query.types);
438
+ }
439
+ if (query.minWeight !== undefined) {
440
+ sql += ` AND weight >= ?`;
441
+ params.push(query.minWeight);
442
+ }
443
+ if (query.minConfidence !== undefined) {
444
+ sql += ` AND confidence >= ?`;
445
+ params.push(query.minConfidence);
446
+ }
447
+ if (query.bidirectionalOnly) {
448
+ sql += ' AND bidirectional = 1';
449
+ }
450
+ sql += ' ORDER BY weight DESC, confidence DESC';
451
+ if (query.limit) {
452
+ sql += ` LIMIT ?`;
453
+ params.push(query.limit);
454
+ }
455
+ const rows = db.prepare(sql).all(...params);
456
+ return this.success(rows.map((row) => this.rowToEdge(row)));
457
+ });
435
458
  }
436
459
  async traverse(options) {
437
- await this.initialize();
438
- const visited = new Set();
439
- const visitedEdges = new Set();
440
- const paths = [];
441
- const allNodes = [];
442
- const allEdges = [];
443
- const startNodeResult = await this.getNode(options.startNodeId);
444
- if (!startNodeResult.success || !startNodeResult.data) {
445
- return this.failure(`Start node not found: ${options.startNodeId}`);
446
- }
447
- const startNode = startNodeResult.data;
448
- visited.add(startNode.id);
449
- allNodes.push(startNode);
450
- await this.traverseRecursive(startNode, [], [], 0, options, visited, visitedEdges, paths, allNodes, allEdges);
451
- let actualMaxDepth = 0;
452
- for (const p of paths) {
453
- if (p.length > actualMaxDepth)
454
- actualMaxDepth = p.length;
455
- }
456
- return this.success({
457
- paths,
458
- visitedNodes: allNodes,
459
- visitedEdges: allEdges,
460
- depth: actualMaxDepth,
460
+ return this.run(async () => {
461
+ const visited = new Set();
462
+ const visitedEdges = new Set();
463
+ const paths = [];
464
+ const allNodes = [];
465
+ const allEdges = [];
466
+ const startNode = unwrap(await this.getNode(options.startNodeId));
467
+ if (!startNode) {
468
+ return this.failure(`Start node not found: ${options.startNodeId}`);
469
+ }
470
+ visited.add(startNode.id);
471
+ allNodes.push(startNode);
472
+ await this.traverseRecursive(startNode, [], [], 0, options, visited, visitedEdges, paths, allNodes, allEdges);
473
+ let actualMaxDepth = 0;
474
+ for (const p of paths) {
475
+ if (p.length > actualMaxDepth)
476
+ actualMaxDepth = p.length;
477
+ }
478
+ return this.success({
479
+ paths,
480
+ visitedNodes: allNodes,
481
+ visitedEdges: allEdges,
482
+ depth: actualMaxDepth,
483
+ });
461
484
  });
462
485
  }
463
486
  async traverseRecursive(currentNode, pathNodes, pathEdges, currentDepth, options, visited, visitedEdges, paths, allNodes, allEdges) {
@@ -474,11 +497,9 @@ export class SQLiteGraphAdapter {
474
497
  }
475
498
  if (options.limit && paths.length >= options.limit)
476
499
  return;
477
- const neighborsResult = await this.getNeighbors(currentNode.id, options.direction);
478
- if (!neighborsResult.success)
479
- return;
500
+ const neighbors = unwrap(await this.getNeighbors(currentNode.id, options.direction));
480
501
  let hasUnvisitedNeighbors = false;
481
- for (const { node, edge } of neighborsResult.data) {
502
+ for (const { node, edge } of neighbors) {
482
503
  if (options.edgeTypes && !options.edgeTypes.includes(edge.type))
483
504
  continue;
484
505
  if (options.minEdgeWeight !== undefined && edge.weight < options.minEdgeWeight)
@@ -506,188 +527,191 @@ export class SQLiteGraphAdapter {
506
527
  }
507
528
  }
508
529
  async findShortestPath(_agentId, startNodeId, endNodeId, maxDepth = 5) {
509
- await this.initialize();
510
- if (startNodeId === endNodeId) {
511
- const nodeResult = await this.getNode(startNodeId);
512
- if (!nodeResult.success || !nodeResult.data)
513
- return this.success(null);
514
- return this.success({
515
- nodes: [nodeResult.data],
516
- edges: [],
517
- totalWeight: 0,
518
- length: 0,
519
- });
520
- }
521
- const queue = [{ nodeId: startNodeId, pathNodeIds: [startNodeId], pathEdgeIds: [], totalWeight: 0 }];
522
- const visited = new Set([startNodeId]);
523
- while (queue.length > 0) {
524
- const current = queue.shift();
525
- if (current.pathEdgeIds.length >= maxDepth)
526
- continue;
527
- const neighborsResult = await this.getNeighbors(current.nodeId, 'both');
528
- if (!neighborsResult.success)
529
- continue;
530
- for (const { node, edge } of neighborsResult.data) {
531
- if (visited.has(node.id))
530
+ return this.run(async () => {
531
+ if (startNodeId === endNodeId) {
532
+ const node = unwrap(await this.getNode(startNodeId));
533
+ if (!node)
534
+ return this.success(null);
535
+ return this.success({
536
+ nodes: [node],
537
+ edges: [],
538
+ totalWeight: 0,
539
+ length: 0,
540
+ });
541
+ }
542
+ const queue = [{ nodeId: startNodeId, pathNodeIds: [startNodeId], pathEdgeIds: [], totalWeight: 0 }];
543
+ const visited = new Set([startNodeId]);
544
+ while (queue.length > 0) {
545
+ const current = queue.shift();
546
+ if (current.pathEdgeIds.length >= maxDepth)
532
547
  continue;
533
- const newPath = {
534
- nodeId: node.id,
535
- pathNodeIds: [...current.pathNodeIds, node.id],
536
- pathEdgeIds: [...current.pathEdgeIds, edge.id],
537
- totalWeight: current.totalWeight + edge.weight,
538
- };
539
- if (node.id === endNodeId) {
540
- const nodes = [];
541
- for (const nid of newPath.pathNodeIds) {
542
- const nr = await this.getNode(nid);
543
- if (nr.success && nr.data)
544
- nodes.push(nr.data);
548
+ const neighbors = unwrap(await this.getNeighbors(current.nodeId, 'both'));
549
+ for (const { node, edge } of neighbors) {
550
+ if (visited.has(node.id))
551
+ continue;
552
+ const newPath = {
553
+ nodeId: node.id,
554
+ pathNodeIds: [...current.pathNodeIds, node.id],
555
+ pathEdgeIds: [...current.pathEdgeIds, edge.id],
556
+ totalWeight: current.totalWeight + edge.weight,
557
+ };
558
+ if (node.id === endNodeId) {
559
+ const nodes = [];
560
+ for (const nid of newPath.pathNodeIds) {
561
+ const pathNode = unwrap(await this.getNode(nid));
562
+ if (pathNode)
563
+ nodes.push(pathNode);
564
+ }
565
+ const edges = [];
566
+ for (const eid of newPath.pathEdgeIds) {
567
+ const pathEdge = unwrap(await this.getEdge(eid));
568
+ if (pathEdge)
569
+ edges.push(pathEdge);
570
+ }
571
+ return this.success({
572
+ nodes,
573
+ edges,
574
+ totalWeight: newPath.totalWeight,
575
+ length: edges.length,
576
+ });
545
577
  }
546
- const edges = [];
547
- for (const eid of newPath.pathEdgeIds) {
548
- const er = await this.getEdge(eid);
549
- if (er.success && er.data)
550
- edges.push(er.data);
551
- }
552
- return this.success({
553
- nodes,
554
- edges,
555
- totalWeight: newPath.totalWeight,
556
- length: edges.length,
557
- });
578
+ visited.add(node.id);
579
+ queue.push(newPath);
558
580
  }
559
- visited.add(node.id);
560
- queue.push(newPath);
561
581
  }
562
- }
563
- return this.success(null);
582
+ return this.success(null);
583
+ });
564
584
  }
565
585
  async getNeighbors(nodeId, direction = 'both') {
566
- await this.initialize();
567
- const db = this.ensureDb();
568
- const selectCols = `
569
- n.id, n.agent_id, n.type, n.name, n.aliases, n.description,
570
- n.properties, n.embedding, n.confidence, n.source, n.metadata,
571
- n.created_at, n.updated_at, n.last_accessed_at, n.access_count,
572
- e.id as edge_id, e.agent_id as edge_agent_id, e.type as edge_type,
573
- e.source_node_id as edge_source_node_id, e.target_node_id as edge_target_node_id,
574
- e.label as edge_label, e.weight as edge_weight, e.bidirectional as edge_bidirectional,
575
- e.properties as edge_properties, e.confidence as edge_confidence,
576
- e.source as edge_source, e.valid_from as edge_valid_from, e.valid_until as edge_valid_until,
577
- e.metadata as edge_metadata, e.created_at as edge_created_at, e.updated_at as edge_updated_at
578
- `;
579
- let sql;
580
- if (direction === 'outgoing') {
581
- sql = `
582
- SELECT ${selectCols}
583
- FROM graph_edges e
584
- JOIN graph_nodes n ON n.id = e.target_node_id
585
- WHERE e.source_node_id = ?
586
- UNION ALL
587
- SELECT ${selectCols}
588
- FROM graph_edges e
589
- JOIN graph_nodes n ON n.id = e.source_node_id
590
- WHERE e.target_node_id = ? AND e.bidirectional = 1
591
- `;
592
- }
593
- else if (direction === 'incoming') {
594
- sql = `
595
- SELECT ${selectCols}
596
- FROM graph_edges e
597
- JOIN graph_nodes n ON n.id = e.source_node_id
598
- WHERE e.target_node_id = ?
599
- UNION ALL
600
- SELECT ${selectCols}
601
- FROM graph_edges e
602
- JOIN graph_nodes n ON n.id = e.target_node_id
603
- WHERE e.source_node_id = ? AND e.bidirectional = 1
604
- `;
605
- }
606
- else {
607
- sql = `
608
- SELECT ${selectCols}
609
- FROM graph_edges e
610
- JOIN graph_nodes n ON n.id = e.target_node_id
611
- WHERE e.source_node_id = ?
612
- UNION ALL
613
- SELECT ${selectCols}
614
- FROM graph_edges e
615
- JOIN graph_nodes n ON n.id = e.source_node_id
616
- WHERE e.target_node_id = ?
586
+ return this.run(async () => {
587
+ const db = this.ensureDb();
588
+ const selectCols = `
589
+ n.id, n.agent_id, n.type, n.name, n.aliases, n.description,
590
+ n.properties, n.embedding, n.confidence, n.source, n.metadata,
591
+ n.created_at, n.updated_at, n.last_accessed_at, n.access_count,
592
+ e.id as edge_id, e.agent_id as edge_agent_id, e.type as edge_type,
593
+ e.source_node_id as edge_source_node_id, e.target_node_id as edge_target_node_id,
594
+ e.label as edge_label, e.weight as edge_weight, e.bidirectional as edge_bidirectional,
595
+ e.properties as edge_properties, e.confidence as edge_confidence,
596
+ e.source as edge_source, e.valid_from as edge_valid_from, e.valid_until as edge_valid_until,
597
+ e.metadata as edge_metadata, e.created_at as edge_created_at, e.updated_at as edge_updated_at
617
598
  `;
618
- }
619
- const rows = db.prepare(sql).all(nodeId, nodeId);
620
- return this.success(rows.map((row) => ({
621
- node: this.rowToNode(row),
622
- edge: this.neighborRowToEdge(row),
623
- })));
599
+ let sql;
600
+ if (direction === 'outgoing') {
601
+ sql = `
602
+ SELECT ${selectCols}
603
+ FROM graph_edges e
604
+ JOIN graph_nodes n ON n.id = e.target_node_id
605
+ WHERE e.source_node_id = ?
606
+ UNION ALL
607
+ SELECT ${selectCols}
608
+ FROM graph_edges e
609
+ JOIN graph_nodes n ON n.id = e.source_node_id
610
+ WHERE e.target_node_id = ? AND e.bidirectional = 1
611
+ `;
612
+ }
613
+ else if (direction === 'incoming') {
614
+ sql = `
615
+ SELECT ${selectCols}
616
+ FROM graph_edges e
617
+ JOIN graph_nodes n ON n.id = e.source_node_id
618
+ WHERE e.target_node_id = ?
619
+ UNION ALL
620
+ SELECT ${selectCols}
621
+ FROM graph_edges e
622
+ JOIN graph_nodes n ON n.id = e.target_node_id
623
+ WHERE e.source_node_id = ? AND e.bidirectional = 1
624
+ `;
625
+ }
626
+ else {
627
+ sql = `
628
+ SELECT ${selectCols}
629
+ FROM graph_edges e
630
+ JOIN graph_nodes n ON n.id = e.target_node_id
631
+ WHERE e.source_node_id = ?
632
+ UNION ALL
633
+ SELECT ${selectCols}
634
+ FROM graph_edges e
635
+ JOIN graph_nodes n ON n.id = e.source_node_id
636
+ WHERE e.target_node_id = ?
637
+ `;
638
+ }
639
+ const rows = db.prepare(sql).all(nodeId, nodeId);
640
+ return this.success(rows.map((row) => ({
641
+ node: this.rowToNode(row),
642
+ edge: this.neighborRowToEdge(row),
643
+ })));
644
+ });
624
645
  }
625
646
  async mergeNodes(targetNodeId, sourceNodeIds) {
626
- await this.initialize();
627
- const db = this.ensureDb();
628
- for (const sourceId of sourceNodeIds) {
629
- const sourceNode = await this.getNode(sourceId);
630
- db.prepare(`UPDATE graph_edges SET source_node_id = ? WHERE source_node_id = ?`).run(targetNodeId, sourceId);
631
- db.prepare(`UPDATE graph_edges SET target_node_id = ? WHERE target_node_id = ?`).run(targetNodeId, sourceId);
632
- db.prepare(`DELETE FROM graph_edges WHERE source_node_id = ? AND target_node_id = ?`).run(targetNodeId, targetNodeId);
633
- if (sourceNode.success && sourceNode.data) {
634
- const targetRow = db
635
- .prepare(`SELECT aliases FROM graph_nodes WHERE id = ?`)
636
- .get(targetNodeId);
637
- if (targetRow) {
638
- const currentAliases = JSON.parse(targetRow.aliases);
639
- const newAliases = [...currentAliases, sourceNode.data.name, ...sourceNode.data.aliases];
640
- const uniqueAliases = [...new Set(newAliases)];
641
- db.prepare(`UPDATE graph_nodes SET aliases = ? WHERE id = ?`).run(JSON.stringify(uniqueAliases), targetNodeId);
647
+ return this.run(async () => {
648
+ const db = this.ensureDb();
649
+ for (const sourceId of sourceNodeIds) {
650
+ const sourceNode = unwrap(await this.getNode(sourceId));
651
+ db.prepare(`UPDATE graph_edges SET source_node_id = ? WHERE source_node_id = ?`).run(targetNodeId, sourceId);
652
+ db.prepare(`UPDATE graph_edges SET target_node_id = ? WHERE target_node_id = ?`).run(targetNodeId, sourceId);
653
+ db.prepare(`DELETE FROM graph_edges WHERE source_node_id = ? AND target_node_id = ?`).run(targetNodeId, targetNodeId);
654
+ if (sourceNode) {
655
+ const targetRow = db
656
+ .prepare(`SELECT aliases FROM graph_nodes WHERE id = ?`)
657
+ .get(targetNodeId);
658
+ if (targetRow) {
659
+ const currentAliases = JSON.parse(targetRow.aliases);
660
+ const newAliases = [...currentAliases, sourceNode.name, ...sourceNode.aliases];
661
+ const uniqueAliases = [...new Set(newAliases)];
662
+ db.prepare(`UPDATE graph_nodes SET aliases = ? WHERE id = ?`).run(JSON.stringify(uniqueAliases), targetNodeId);
663
+ }
642
664
  }
665
+ db.prepare(`DELETE FROM graph_nodes WHERE id = ?`).run(sourceId);
643
666
  }
644
- db.prepare(`DELETE FROM graph_nodes WHERE id = ?`).run(sourceId);
645
- }
646
- const mergedNode = await this.getNode(targetNodeId);
647
- if (!mergedNode.success || !mergedNode.data) {
648
- return this.failure(`Target node not found: ${targetNodeId}`);
649
- }
650
- return this.success(mergedNode.data);
667
+ const mergedNode = unwrap(await this.getNode(targetNodeId));
668
+ if (!mergedNode) {
669
+ return this.failure(`Target node not found: ${targetNodeId}`);
670
+ }
671
+ return this.success(mergedNode);
672
+ });
651
673
  }
652
674
  async clearGraph(agentId) {
653
- await this.initialize();
654
- const db = this.ensureDb();
655
- db.prepare(`DELETE FROM graph_edges WHERE agent_id = ?`).run(agentId);
656
- db.prepare(`DELETE FROM graph_nodes WHERE agent_id = ?`).run(agentId);
657
- return this.success(undefined);
675
+ return this.run(async () => {
676
+ const db = this.ensureDb();
677
+ db.prepare(`DELETE FROM graph_edges WHERE agent_id = ?`).run(agentId);
678
+ db.prepare(`DELETE FROM graph_nodes WHERE agent_id = ?`).run(agentId);
679
+ return this.success(undefined);
680
+ });
658
681
  }
659
682
  async getGraphStats(agentId) {
660
- await this.initialize();
661
- const db = this.ensureDb();
662
- const nodeCountRow = db
663
- .prepare(`SELECT COUNT(*) as count FROM graph_nodes WHERE agent_id = ?`)
664
- .get(agentId);
665
- const edgeCountRow = db
666
- .prepare(`SELECT COUNT(*) as count FROM graph_edges WHERE agent_id = ?`)
667
- .get(agentId);
668
- const nodesByTypeRows = db
669
- .prepare(`SELECT type, COUNT(*) as count FROM graph_nodes WHERE agent_id = ? GROUP BY type`)
670
- .all(agentId);
671
- const edgesByTypeRows = db
672
- .prepare(`SELECT type, COUNT(*) as count FROM graph_edges WHERE agent_id = ? GROUP BY type`)
673
- .all(agentId);
674
- const nodeCount = nodeCountRow.count;
675
- const edgeCount = edgeCountRow.count;
676
- const nodesByType = {};
677
- for (const row of nodesByTypeRows) {
678
- nodesByType[row.type] = row.count;
679
- }
680
- const edgesByType = {};
681
- for (const row of edgesByTypeRows) {
682
- edgesByType[row.type] = row.count;
683
- }
684
- return this.success({
685
- nodeCount,
686
- edgeCount,
687
- nodesByType: nodesByType,
688
- edgesByType: edgesByType,
689
- averageEdgesPerNode: nodeCount > 0 ? edgeCount / nodeCount : 0,
690
- maxDepth: 0,
683
+ return this.run(async () => {
684
+ const db = this.ensureDb();
685
+ const nodeCountRow = db
686
+ .prepare(`SELECT COUNT(*) as count FROM graph_nodes WHERE agent_id = ?`)
687
+ .get(agentId);
688
+ const edgeCountRow = db
689
+ .prepare(`SELECT COUNT(*) as count FROM graph_edges WHERE agent_id = ?`)
690
+ .get(agentId);
691
+ const nodesByTypeRows = db
692
+ .prepare(`SELECT type, COUNT(*) as count FROM graph_nodes WHERE agent_id = ? GROUP BY type`)
693
+ .all(agentId);
694
+ const edgesByTypeRows = db
695
+ .prepare(`SELECT type, COUNT(*) as count FROM graph_edges WHERE agent_id = ? GROUP BY type`)
696
+ .all(agentId);
697
+ const nodeCount = nodeCountRow.count;
698
+ const edgeCount = edgeCountRow.count;
699
+ const nodesByType = {};
700
+ for (const row of nodesByTypeRows) {
701
+ nodesByType[row.type] = row.count;
702
+ }
703
+ const edgesByType = {};
704
+ for (const row of edgesByTypeRows) {
705
+ edgesByType[row.type] = row.count;
706
+ }
707
+ return this.success({
708
+ nodeCount,
709
+ edgeCount,
710
+ nodesByType: nodesByType,
711
+ edgesByType: edgesByType,
712
+ averageEdgesPerNode: nodeCount > 0 ? edgeCount / nodeCount : 0,
713
+ maxDepth: 0,
714
+ });
691
715
  });
692
716
  }
693
717
  }