@cogitator-ai/memory 0.10.0 → 0.11.1

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