@weavelogic/knowledge-graph-agent 0.3.0 → 0.4.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 (149) hide show
  1. package/README.md +290 -3
  2. package/dist/_virtual/index10.js +2 -2
  3. package/dist/_virtual/index6.js +2 -2
  4. package/dist/_virtual/index7.js +2 -2
  5. package/dist/_virtual/index8.js +2 -2
  6. package/dist/_virtual/index9.js +2 -2
  7. package/dist/audit/config.d.ts +150 -0
  8. package/dist/audit/config.d.ts.map +1 -0
  9. package/dist/audit/config.js +111 -0
  10. package/dist/audit/config.js.map +1 -0
  11. package/dist/audit/index.d.ts +38 -0
  12. package/dist/audit/index.d.ts.map +1 -0
  13. package/dist/audit/services/audit-chain.d.ts +276 -0
  14. package/dist/audit/services/audit-chain.d.ts.map +1 -0
  15. package/dist/audit/services/audit-chain.js +502 -0
  16. package/dist/audit/services/audit-chain.js.map +1 -0
  17. package/dist/audit/services/index.d.ts +11 -0
  18. package/dist/audit/services/index.d.ts.map +1 -0
  19. package/dist/audit/services/syndication.d.ts +334 -0
  20. package/dist/audit/services/syndication.d.ts.map +1 -0
  21. package/dist/audit/services/syndication.js +589 -0
  22. package/dist/audit/services/syndication.js.map +1 -0
  23. package/dist/audit/types.d.ts +453 -0
  24. package/dist/audit/types.d.ts.map +1 -0
  25. package/dist/cli/commands/audit.d.ts +21 -0
  26. package/dist/cli/commands/audit.d.ts.map +1 -0
  27. package/dist/cli/commands/audit.js +621 -0
  28. package/dist/cli/commands/audit.js.map +1 -0
  29. package/dist/cli/commands/vector.d.ts +14 -0
  30. package/dist/cli/commands/vector.d.ts.map +1 -0
  31. package/dist/cli/commands/vector.js +429 -0
  32. package/dist/cli/commands/vector.js.map +1 -0
  33. package/dist/cli/commands/workflow.d.ts +12 -0
  34. package/dist/cli/commands/workflow.d.ts.map +1 -0
  35. package/dist/cli/commands/workflow.js +471 -0
  36. package/dist/cli/commands/workflow.js.map +1 -0
  37. package/dist/cli/index.d.ts.map +1 -1
  38. package/dist/cli/index.js +26 -0
  39. package/dist/cli/index.js.map +1 -1
  40. package/dist/database/schemas/index.d.ts +85 -0
  41. package/dist/database/schemas/index.d.ts.map +1 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +9 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/mcp-server/tools/audit/checkpoint.d.ts +58 -0
  47. package/dist/mcp-server/tools/audit/checkpoint.d.ts.map +1 -0
  48. package/dist/mcp-server/tools/audit/checkpoint.js +73 -0
  49. package/dist/mcp-server/tools/audit/checkpoint.js.map +1 -0
  50. package/dist/mcp-server/tools/audit/index.d.ts +53 -0
  51. package/dist/mcp-server/tools/audit/index.d.ts.map +1 -0
  52. package/dist/mcp-server/tools/audit/index.js +12 -0
  53. package/dist/mcp-server/tools/audit/index.js.map +1 -0
  54. package/dist/mcp-server/tools/audit/query.d.ts +58 -0
  55. package/dist/mcp-server/tools/audit/query.d.ts.map +1 -0
  56. package/dist/mcp-server/tools/audit/query.js +125 -0
  57. package/dist/mcp-server/tools/audit/query.js.map +1 -0
  58. package/dist/mcp-server/tools/audit/sync.d.ts +58 -0
  59. package/dist/mcp-server/tools/audit/sync.d.ts.map +1 -0
  60. package/dist/mcp-server/tools/audit/sync.js +126 -0
  61. package/dist/mcp-server/tools/audit/sync.js.map +1 -0
  62. package/dist/mcp-server/tools/index.d.ts +3 -0
  63. package/dist/mcp-server/tools/index.d.ts.map +1 -1
  64. package/dist/mcp-server/tools/registry.js +90 -0
  65. package/dist/mcp-server/tools/registry.js.map +1 -1
  66. package/dist/mcp-server/tools/vector/index.d.ts +12 -0
  67. package/dist/mcp-server/tools/vector/index.d.ts.map +1 -0
  68. package/dist/mcp-server/tools/vector/index.js +12 -0
  69. package/dist/mcp-server/tools/vector/index.js.map +1 -0
  70. package/dist/mcp-server/tools/vector/search.d.ts +41 -0
  71. package/dist/mcp-server/tools/vector/search.d.ts.map +1 -0
  72. package/dist/mcp-server/tools/vector/search.js +224 -0
  73. package/dist/mcp-server/tools/vector/search.js.map +1 -0
  74. package/dist/mcp-server/tools/vector/trajectory.d.ts +39 -0
  75. package/dist/mcp-server/tools/vector/trajectory.d.ts.map +1 -0
  76. package/dist/mcp-server/tools/vector/trajectory.js +170 -0
  77. package/dist/mcp-server/tools/vector/trajectory.js.map +1 -0
  78. package/dist/mcp-server/tools/vector/upsert.d.ts +44 -0
  79. package/dist/mcp-server/tools/vector/upsert.d.ts.map +1 -0
  80. package/dist/mcp-server/tools/vector/upsert.js +175 -0
  81. package/dist/mcp-server/tools/vector/upsert.js.map +1 -0
  82. package/dist/mcp-server/tools/workflow/index.d.ts +29 -0
  83. package/dist/mcp-server/tools/workflow/index.d.ts.map +1 -0
  84. package/dist/mcp-server/tools/workflow/index.js +12 -0
  85. package/dist/mcp-server/tools/workflow/index.js.map +1 -0
  86. package/dist/mcp-server/tools/workflow/list.d.ts +41 -0
  87. package/dist/mcp-server/tools/workflow/list.d.ts.map +1 -0
  88. package/dist/mcp-server/tools/workflow/list.js +195 -0
  89. package/dist/mcp-server/tools/workflow/list.js.map +1 -0
  90. package/dist/mcp-server/tools/workflow/start.d.ts +40 -0
  91. package/dist/mcp-server/tools/workflow/start.d.ts.map +1 -0
  92. package/dist/mcp-server/tools/workflow/start.js +165 -0
  93. package/dist/mcp-server/tools/workflow/start.js.map +1 -0
  94. package/dist/mcp-server/tools/workflow/status.d.ts +38 -0
  95. package/dist/mcp-server/tools/workflow/status.d.ts.map +1 -0
  96. package/dist/mcp-server/tools/workflow/status.js +97 -0
  97. package/dist/mcp-server/tools/workflow/status.js.map +1 -0
  98. package/dist/node_modules/ajv/dist/compile/index.js +1 -1
  99. package/dist/node_modules/ajv/dist/vocabularies/applicator/index.js +1 -1
  100. package/dist/node_modules/ajv/dist/vocabularies/core/index.js +1 -1
  101. package/dist/node_modules/ajv/dist/vocabularies/format/index.js +1 -1
  102. package/dist/node_modules/ajv/dist/vocabularies/validation/index.js +1 -1
  103. package/dist/vector/config.d.ts +300 -0
  104. package/dist/vector/config.d.ts.map +1 -0
  105. package/dist/vector/config.js +124 -0
  106. package/dist/vector/config.js.map +1 -0
  107. package/dist/vector/index.d.ts +50 -0
  108. package/dist/vector/index.d.ts.map +1 -0
  109. package/dist/vector/services/index.d.ts +13 -0
  110. package/dist/vector/services/index.d.ts.map +1 -0
  111. package/dist/vector/services/trajectory-tracker.d.ts +405 -0
  112. package/dist/vector/services/trajectory-tracker.d.ts.map +1 -0
  113. package/dist/vector/services/trajectory-tracker.js +445 -0
  114. package/dist/vector/services/trajectory-tracker.js.map +1 -0
  115. package/dist/vector/services/vector-store.d.ts +339 -0
  116. package/dist/vector/services/vector-store.d.ts.map +1 -0
  117. package/dist/vector/services/vector-store.js +748 -0
  118. package/dist/vector/services/vector-store.js.map +1 -0
  119. package/dist/vector/types.d.ts +677 -0
  120. package/dist/vector/types.d.ts.map +1 -0
  121. package/dist/workflow/adapters/goap-adapter.d.ts +196 -0
  122. package/dist/workflow/adapters/goap-adapter.d.ts.map +1 -0
  123. package/dist/workflow/adapters/goap-adapter.js +706 -0
  124. package/dist/workflow/adapters/goap-adapter.js.map +1 -0
  125. package/dist/workflow/adapters/index.d.ts +10 -0
  126. package/dist/workflow/adapters/index.d.ts.map +1 -0
  127. package/dist/workflow/config.d.ts +135 -0
  128. package/dist/workflow/config.d.ts.map +1 -0
  129. package/dist/workflow/config.js +92 -0
  130. package/dist/workflow/config.js.map +1 -0
  131. package/dist/workflow/handlers/index.d.ts +9 -0
  132. package/dist/workflow/handlers/index.d.ts.map +1 -0
  133. package/dist/workflow/handlers/webhook-handlers.d.ts +397 -0
  134. package/dist/workflow/handlers/webhook-handlers.d.ts.map +1 -0
  135. package/dist/workflow/handlers/webhook-handlers.js +454 -0
  136. package/dist/workflow/handlers/webhook-handlers.js.map +1 -0
  137. package/dist/workflow/index.d.ts +42 -0
  138. package/dist/workflow/index.d.ts.map +1 -0
  139. package/dist/workflow/services/index.d.ts +9 -0
  140. package/dist/workflow/services/index.d.ts.map +1 -0
  141. package/dist/workflow/services/workflow-service.d.ts +318 -0
  142. package/dist/workflow/services/workflow-service.d.ts.map +1 -0
  143. package/dist/workflow/services/workflow-service.js +577 -0
  144. package/dist/workflow/services/workflow-service.js.map +1 -0
  145. package/dist/workflow/types.d.ts +470 -0
  146. package/dist/workflow/types.d.ts.map +1 -0
  147. package/dist/workflow/workflows/realtime-collab.d.ts +245 -0
  148. package/dist/workflow/workflows/realtime-collab.d.ts.map +1 -0
  149. package/package.json +1 -1
@@ -0,0 +1,748 @@
1
+ import { createRuVectorConfig, validateRuVectorConfig } from "../config.js";
2
+ import { createLogger } from "../../utils/logger.js";
3
+ const logger = createLogger("vector-store");
4
+ class EnhancedVectorStore {
5
+ /** Store configuration */
6
+ config;
7
+ /** In-memory node storage */
8
+ nodes = /* @__PURE__ */ new Map();
9
+ /** Entry point node ID for HNSW search */
10
+ entryPoint = null;
11
+ /** Maximum level in the current index */
12
+ maxLevel = 0;
13
+ /** Level generation multiplier (1/ln(M)) */
14
+ levelMultiplier;
15
+ /** Initialization state */
16
+ isInitialized = false;
17
+ /** Event listeners */
18
+ eventListeners = /* @__PURE__ */ new Map();
19
+ /**
20
+ * Create a new EnhancedVectorStore
21
+ *
22
+ * @param config - Optional configuration overrides
23
+ */
24
+ constructor(config) {
25
+ const baseConfig = createRuVectorConfig();
26
+ this.config = { ...baseConfig, ...config };
27
+ const m = this.config.index.hnswConfig?.m || 16;
28
+ this.levelMultiplier = 1 / Math.log(m);
29
+ const validation = validateRuVectorConfig(this.config);
30
+ if (!validation.valid) {
31
+ logger.warn("Invalid configuration", { errors: validation.errors });
32
+ }
33
+ if (validation.warnings.length > 0) {
34
+ logger.debug("Configuration warnings", { warnings: validation.warnings });
35
+ }
36
+ }
37
+ /**
38
+ * Initialize the vector store
39
+ *
40
+ * Sets up the storage backend and prepares the index for operations.
41
+ * Must be called before any other operations.
42
+ *
43
+ * @throws Error if initialization fails
44
+ */
45
+ async initialize() {
46
+ if (this.isInitialized) {
47
+ return;
48
+ }
49
+ logger.info("Initializing vector store", {
50
+ backend: this.config.backend,
51
+ dimensions: this.config.index.dimensions,
52
+ indexType: this.config.index.indexType,
53
+ distanceMetric: this.config.index.distanceMetric
54
+ });
55
+ switch (this.config.backend) {
56
+ case "memory":
57
+ break;
58
+ case "postgres":
59
+ logger.warn("PostgreSQL backend not yet implemented, using memory");
60
+ break;
61
+ case "standalone":
62
+ logger.warn("Standalone backend not yet implemented, using memory");
63
+ break;
64
+ case "sqlite":
65
+ logger.warn("SQLite backend not yet implemented, using memory");
66
+ break;
67
+ }
68
+ this.isInitialized = true;
69
+ logger.info("Vector store initialized");
70
+ }
71
+ /**
72
+ * Calculate distance between two vectors
73
+ *
74
+ * @param a - First vector
75
+ * @param b - Second vector
76
+ * @returns Distance value (lower = more similar for most metrics)
77
+ * @throws Error if vectors have different dimensions
78
+ */
79
+ calculateDistance(a, b) {
80
+ if (a.length !== b.length) {
81
+ throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
82
+ }
83
+ switch (this.config.index.distanceMetric) {
84
+ case "cosine":
85
+ return this.cosineDistance(a, b);
86
+ case "euclidean":
87
+ return this.euclideanDistance(a, b);
88
+ case "dotProduct":
89
+ return this.dotProductDistance(a, b);
90
+ case "manhattan":
91
+ return this.manhattanDistance(a, b);
92
+ default:
93
+ return this.cosineDistance(a, b);
94
+ }
95
+ }
96
+ /**
97
+ * Calculate cosine distance (1 - cosine similarity)
98
+ *
99
+ * @param a - First vector
100
+ * @param b - Second vector
101
+ * @returns Cosine distance (0 = identical, 2 = opposite)
102
+ */
103
+ cosineDistance(a, b) {
104
+ let dotProduct = 0;
105
+ let normA = 0;
106
+ let normB = 0;
107
+ for (let i = 0; i < a.length; i++) {
108
+ dotProduct += a[i] * b[i];
109
+ normA += a[i] * a[i];
110
+ normB += b[i] * b[i];
111
+ }
112
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
113
+ if (denominator === 0) {
114
+ return 1;
115
+ }
116
+ const similarity = dotProduct / denominator;
117
+ return 1 - similarity;
118
+ }
119
+ /**
120
+ * Calculate Euclidean (L2) distance
121
+ *
122
+ * @param a - First vector
123
+ * @param b - Second vector
124
+ * @returns Euclidean distance
125
+ */
126
+ euclideanDistance(a, b) {
127
+ let sum = 0;
128
+ for (let i = 0; i < a.length; i++) {
129
+ const diff = a[i] - b[i];
130
+ sum += diff * diff;
131
+ }
132
+ return Math.sqrt(sum);
133
+ }
134
+ /**
135
+ * Calculate negative dot product distance
136
+ *
137
+ * Higher dot product means more similar, so we negate for distance.
138
+ *
139
+ * @param a - First vector
140
+ * @param b - Second vector
141
+ * @returns Negative dot product
142
+ */
143
+ dotProductDistance(a, b) {
144
+ let dotProduct = 0;
145
+ for (let i = 0; i < a.length; i++) {
146
+ dotProduct += a[i] * b[i];
147
+ }
148
+ return -dotProduct;
149
+ }
150
+ /**
151
+ * Calculate Manhattan (L1) distance
152
+ *
153
+ * @param a - First vector
154
+ * @param b - Second vector
155
+ * @returns Manhattan distance
156
+ */
157
+ manhattanDistance(a, b) {
158
+ let sum = 0;
159
+ for (let i = 0; i < a.length; i++) {
160
+ sum += Math.abs(a[i] - b[i]);
161
+ }
162
+ return sum;
163
+ }
164
+ /**
165
+ * Generate random level for new node (HNSW algorithm)
166
+ *
167
+ * Uses exponential distribution to generate levels.
168
+ * Most nodes will be at level 0, with exponentially fewer at higher levels.
169
+ *
170
+ * @returns Generated level (0 or higher)
171
+ */
172
+ generateLevel() {
173
+ const random = Math.random();
174
+ return Math.floor(-Math.log(random) * this.levelMultiplier);
175
+ }
176
+ /**
177
+ * Insert a vector into the index
178
+ *
179
+ * Implements the HNSW insertion algorithm:
180
+ * 1. Generate random level for the new node
181
+ * 2. If first node, make it the entry point
182
+ * 3. Otherwise, traverse from entry point to find insertion position
183
+ * 4. Connect to nearest neighbors at each level
184
+ *
185
+ * @param entry - Vector entry to insert
186
+ * @throws Error if not initialized or dimensions mismatch
187
+ */
188
+ async insert(entry) {
189
+ if (!this.isInitialized) {
190
+ await this.initialize();
191
+ }
192
+ if (entry.vector.length !== this.config.index.dimensions) {
193
+ throw new Error(
194
+ `Vector dimension mismatch: expected ${this.config.index.dimensions}, got ${entry.vector.length}`
195
+ );
196
+ }
197
+ const level = this.generateLevel();
198
+ const node = {
199
+ id: entry.id,
200
+ vector: entry.vector,
201
+ neighbors: /* @__PURE__ */ new Map(),
202
+ metadata: entry.metadata || {},
203
+ level,
204
+ createdAt: /* @__PURE__ */ new Date()
205
+ };
206
+ for (let l = 0; l <= level; l++) {
207
+ node.neighbors.set(l, []);
208
+ }
209
+ if (!this.entryPoint) {
210
+ this.entryPoint = entry.id;
211
+ this.maxLevel = level;
212
+ this.nodes.set(entry.id, node);
213
+ this.emitEvent({ type: "insert", id: entry.id, timestamp: /* @__PURE__ */ new Date() });
214
+ return;
215
+ }
216
+ let currentId = this.entryPoint;
217
+ const m = this.config.index.hnswConfig?.m || 16;
218
+ const efConstruction = this.config.index.hnswConfig?.efConstruction || 200;
219
+ for (let l = this.maxLevel; l > level; l--) {
220
+ currentId = this.greedySearch(entry.vector, currentId, l);
221
+ }
222
+ for (let l = Math.min(level, this.maxLevel); l >= 0; l--) {
223
+ const neighbors = this.searchLayer(entry.vector, currentId, efConstruction, l);
224
+ const selected = neighbors.slice(0, m);
225
+ node.neighbors.set(l, selected.map((n) => n.id));
226
+ for (const neighbor of selected) {
227
+ const neighborNode = this.nodes.get(neighbor.id);
228
+ if (neighborNode) {
229
+ const neighborList = neighborNode.neighbors.get(l) || [];
230
+ neighborList.push(entry.id);
231
+ const maxConnections = l === 0 ? m * 2 : m;
232
+ if (neighborList.length > maxConnections) {
233
+ const pruned = this.pruneNeighbors(neighborNode.vector, neighborList, m);
234
+ neighborNode.neighbors.set(l, pruned);
235
+ } else {
236
+ neighborNode.neighbors.set(l, neighborList);
237
+ }
238
+ }
239
+ }
240
+ if (neighbors.length > 0) {
241
+ currentId = neighbors[0].id;
242
+ }
243
+ }
244
+ if (level > this.maxLevel) {
245
+ this.entryPoint = entry.id;
246
+ this.maxLevel = level;
247
+ }
248
+ this.nodes.set(entry.id, node);
249
+ this.emitEvent({ type: "insert", id: entry.id, timestamp: /* @__PURE__ */ new Date() });
250
+ logger.debug("Inserted vector", { id: entry.id, level });
251
+ }
252
+ /**
253
+ * Greedy search to find nearest neighbor at a level
254
+ *
255
+ * Traverses the graph at the specified level, always moving
256
+ * toward the nearest neighbor until no improvement is found.
257
+ *
258
+ * @param query - Query vector
259
+ * @param startId - Starting node ID
260
+ * @param level - Level to search at
261
+ * @returns ID of nearest node found
262
+ */
263
+ greedySearch(query, startId, level) {
264
+ let currentId = startId;
265
+ const currentNode = this.nodes.get(currentId);
266
+ if (!currentNode) {
267
+ return startId;
268
+ }
269
+ let currentDist = this.calculateDistance(query, currentNode.vector);
270
+ let improved = true;
271
+ while (improved) {
272
+ improved = false;
273
+ const node = this.nodes.get(currentId);
274
+ if (!node) break;
275
+ const neighbors = node.neighbors.get(level) || [];
276
+ for (const neighborId of neighbors) {
277
+ const neighborNode = this.nodes.get(neighborId);
278
+ if (!neighborNode) continue;
279
+ const dist = this.calculateDistance(query, neighborNode.vector);
280
+ if (dist < currentDist) {
281
+ currentId = neighborId;
282
+ currentDist = dist;
283
+ improved = true;
284
+ }
285
+ }
286
+ }
287
+ return currentId;
288
+ }
289
+ /**
290
+ * Search a layer for nearest neighbors
291
+ *
292
+ * Implements beam search at a specific level using a priority queue.
293
+ *
294
+ * @param query - Query vector
295
+ * @param startId - Starting node ID
296
+ * @param ef - Size of dynamic candidate list
297
+ * @param level - Level to search at
298
+ * @returns Array of nearest neighbors with distances
299
+ */
300
+ searchLayer(query, startId, ef, level) {
301
+ const visited = /* @__PURE__ */ new Set();
302
+ const candidates = [];
303
+ const results = [];
304
+ const startNode = this.nodes.get(startId);
305
+ if (!startNode) return [];
306
+ const startDist = this.calculateDistance(query, startNode.vector);
307
+ candidates.push({ id: startId, distance: startDist });
308
+ results.push({ id: startId, distance: startDist });
309
+ visited.add(startId);
310
+ while (candidates.length > 0) {
311
+ candidates.sort((a, b) => a.distance - b.distance);
312
+ const current = candidates.shift();
313
+ results.sort((a, b) => a.distance - b.distance);
314
+ const furthestResult = results[results.length - 1];
315
+ if (current.distance > furthestResult.distance && results.length >= ef) {
316
+ break;
317
+ }
318
+ const node = this.nodes.get(current.id);
319
+ if (!node) continue;
320
+ const neighbors = node.neighbors.get(level) || [];
321
+ for (const neighborId of neighbors) {
322
+ if (visited.has(neighborId)) continue;
323
+ visited.add(neighborId);
324
+ const neighborNode = this.nodes.get(neighborId);
325
+ if (!neighborNode) continue;
326
+ const dist = this.calculateDistance(query, neighborNode.vector);
327
+ if (results.length < ef || dist < furthestResult.distance) {
328
+ candidates.push({ id: neighborId, distance: dist });
329
+ results.push({ id: neighborId, distance: dist });
330
+ results.sort((a, b) => a.distance - b.distance);
331
+ if (results.length > ef) {
332
+ results.pop();
333
+ }
334
+ }
335
+ }
336
+ }
337
+ return results;
338
+ }
339
+ /**
340
+ * Prune neighbors to keep only the best M
341
+ *
342
+ * Uses a simple distance-based pruning strategy.
343
+ *
344
+ * @param nodeVector - Vector of the node being pruned
345
+ * @param neighborIds - Current neighbor IDs
346
+ * @param m - Maximum neighbors to keep
347
+ * @returns Pruned list of neighbor IDs
348
+ */
349
+ pruneNeighbors(nodeVector, neighborIds, m) {
350
+ const distances = neighborIds.map((id) => {
351
+ const node = this.nodes.get(id);
352
+ if (!node) return { id, distance: Infinity };
353
+ return { id, distance: this.calculateDistance(nodeVector, node.vector) };
354
+ });
355
+ distances.sort((a, b) => a.distance - b.distance);
356
+ return distances.slice(0, m).map((d) => d.id);
357
+ }
358
+ /**
359
+ * Search for similar vectors
360
+ *
361
+ * Implements HNSW search algorithm:
362
+ * 1. Start from entry point at top level
363
+ * 2. Greedy search down to level 1
364
+ * 3. Beam search at level 0 with efSearch candidates
365
+ * 4. Return top-k results
366
+ *
367
+ * @param query - Search query with vector and options
368
+ * @returns Array of search results sorted by similarity
369
+ */
370
+ async search(query) {
371
+ const startTime = Date.now();
372
+ if (!this.isInitialized) {
373
+ await this.initialize();
374
+ }
375
+ if (!this.entryPoint) {
376
+ return [];
377
+ }
378
+ const k = query.k || 10;
379
+ const efSearch = this.config.index.hnswConfig?.efSearch || 100;
380
+ let currentId = this.entryPoint;
381
+ for (let l = this.maxLevel; l > 0; l--) {
382
+ currentId = this.greedySearch(query.vector, currentId, l);
383
+ }
384
+ const candidates = this.searchLayer(query.vector, currentId, Math.max(efSearch, k), 0);
385
+ const results = candidates.slice(0, k * 2).map((c) => {
386
+ const node = this.nodes.get(c.id);
387
+ const score = this.distanceToScore(c.distance);
388
+ return {
389
+ id: c.id,
390
+ score,
391
+ metadata: node.metadata,
392
+ distance: c.distance
393
+ };
394
+ }).filter((r) => {
395
+ if (query.minScore !== void 0 && r.score < query.minScore) {
396
+ return false;
397
+ }
398
+ if (query.filter) {
399
+ for (const [key, value] of Object.entries(query.filter)) {
400
+ if (r.metadata[key] !== value) {
401
+ return false;
402
+ }
403
+ }
404
+ }
405
+ return true;
406
+ }).slice(0, k);
407
+ const durationMs = Date.now() - startTime;
408
+ this.emitEvent({
409
+ type: "search",
410
+ queryId: crypto.randomUUID?.() || `search-${Date.now()}`,
411
+ resultCount: results.length,
412
+ durationMs,
413
+ timestamp: /* @__PURE__ */ new Date()
414
+ });
415
+ return results;
416
+ }
417
+ /**
418
+ * Convert distance to similarity score
419
+ *
420
+ * @param distance - Distance value
421
+ * @returns Similarity score between 0 and 1
422
+ */
423
+ distanceToScore(distance) {
424
+ switch (this.config.index.distanceMetric) {
425
+ case "cosine":
426
+ return Math.max(0, Math.min(1, 1 - distance));
427
+ case "euclidean":
428
+ return Math.exp(-distance);
429
+ case "dotProduct":
430
+ return 1 / (1 + Math.exp(distance));
431
+ case "manhattan":
432
+ return Math.exp(-distance / 10);
433
+ default:
434
+ return Math.max(0, Math.min(1, 1 - distance));
435
+ }
436
+ }
437
+ /**
438
+ * Hybrid search combining vectors and graph queries
439
+ *
440
+ * Performs vector similarity search and optionally enriches
441
+ * results with data from graph queries (Cypher).
442
+ *
443
+ * @param query - Hybrid search query
444
+ * @returns Array of hybrid search results
445
+ */
446
+ async hybridSearch(query) {
447
+ if (!this.isInitialized) {
448
+ await this.initialize();
449
+ }
450
+ const limit = query.limit || 10;
451
+ const vectorResults = await this.search({
452
+ vector: query.embedding,
453
+ k: limit * 2,
454
+ // Get more candidates for merging
455
+ filter: query.filters,
456
+ minScore: query.minScore
457
+ });
458
+ if (!query.cypher) {
459
+ return vectorResults.map((r) => ({
460
+ ...r,
461
+ source: "vector"
462
+ }));
463
+ }
464
+ logger.debug("Hybrid search with Cypher query", {
465
+ cypher: query.cypher,
466
+ vectorResultCount: vectorResults.length
467
+ });
468
+ const hybridResults = vectorResults.slice(0, limit).map((r) => ({
469
+ ...r,
470
+ source: "merged",
471
+ graphData: {
472
+ cypherQuery: query.cypher,
473
+ cypherParams: query.cypherParams,
474
+ note: "Graph query execution pending integration"
475
+ }
476
+ }));
477
+ return hybridResults;
478
+ }
479
+ /**
480
+ * Batch insert vectors
481
+ *
482
+ * Efficiently inserts multiple vectors in a single operation.
483
+ * Supports progress callbacks and duplicate handling.
484
+ *
485
+ * @param operation - Batch insert operation configuration
486
+ * @returns Result with counts of inserted, skipped, and errors
487
+ */
488
+ async batchInsert(operation) {
489
+ const startTime = Date.now();
490
+ const result = {
491
+ inserted: 0,
492
+ skipped: 0,
493
+ errors: []
494
+ };
495
+ const total = operation.entries.length;
496
+ for (let i = 0; i < operation.entries.length; i++) {
497
+ const entry = operation.entries[i];
498
+ try {
499
+ if (this.nodes.has(entry.id)) {
500
+ if (operation.skipDuplicates) {
501
+ result.skipped++;
502
+ continue;
503
+ }
504
+ throw new Error(`Duplicate ID: ${entry.id}`);
505
+ }
506
+ await this.insert(entry);
507
+ result.inserted++;
508
+ if (operation.onProgress) {
509
+ operation.onProgress(result.inserted + result.skipped, total);
510
+ }
511
+ } catch (error) {
512
+ result.errors.push({
513
+ id: entry.id,
514
+ error: error instanceof Error ? error.message : String(error)
515
+ });
516
+ }
517
+ }
518
+ result.durationMs = Date.now() - startTime;
519
+ logger.info("Batch insert completed", {
520
+ inserted: result.inserted,
521
+ skipped: result.skipped,
522
+ errors: result.errors.length,
523
+ durationMs: result.durationMs
524
+ });
525
+ return result;
526
+ }
527
+ /**
528
+ * Get a vector by ID
529
+ *
530
+ * @param id - Vector ID to retrieve
531
+ * @returns Vector entry or null if not found
532
+ */
533
+ async get(id) {
534
+ const node = this.nodes.get(id);
535
+ if (!node) return null;
536
+ return {
537
+ id: node.id,
538
+ vector: node.vector,
539
+ metadata: node.metadata,
540
+ createdAt: node.createdAt
541
+ };
542
+ }
543
+ /**
544
+ * Delete a vector by ID
545
+ *
546
+ * Removes the vector from the index and updates neighbor connections.
547
+ *
548
+ * @param id - Vector ID to delete
549
+ * @returns True if deleted, false if not found
550
+ */
551
+ async delete(id) {
552
+ const node = this.nodes.get(id);
553
+ if (!node) return false;
554
+ for (const [level, neighbors] of node.neighbors) {
555
+ for (const neighborId of neighbors) {
556
+ const neighborNode = this.nodes.get(neighborId);
557
+ if (neighborNode) {
558
+ const neighborList = neighborNode.neighbors.get(level) || [];
559
+ const idx = neighborList.indexOf(id);
560
+ if (idx !== -1) {
561
+ neighborList.splice(idx, 1);
562
+ }
563
+ }
564
+ }
565
+ }
566
+ if (this.entryPoint === id) {
567
+ const nodeIterator = this.nodes.keys();
568
+ let nextEntry = nodeIterator.next();
569
+ while (!nextEntry.done && nextEntry.value === id) {
570
+ nextEntry = nodeIterator.next();
571
+ }
572
+ this.entryPoint = nextEntry.done ? null : nextEntry.value;
573
+ if (this.entryPoint) {
574
+ const newEntry = this.nodes.get(this.entryPoint);
575
+ this.maxLevel = newEntry?.level || 0;
576
+ } else {
577
+ this.maxLevel = 0;
578
+ }
579
+ }
580
+ this.nodes.delete(id);
581
+ this.emitEvent({ type: "delete", id, timestamp: /* @__PURE__ */ new Date() });
582
+ logger.debug("Deleted vector", { id });
583
+ return true;
584
+ }
585
+ /**
586
+ * Get index statistics
587
+ *
588
+ * @returns Current statistics about the vector index
589
+ */
590
+ getStats() {
591
+ return {
592
+ totalVectors: this.nodes.size,
593
+ dimensions: this.config.index.dimensions,
594
+ indexType: this.config.index.indexType,
595
+ memoryUsage: this.estimateMemoryUsage(),
596
+ lastUpdated: /* @__PURE__ */ new Date(),
597
+ indexStats: {
598
+ levels: this.maxLevel + 1,
599
+ entryPoint: this.entryPoint || void 0,
600
+ avgConnections: this.calculateAverageConnections()
601
+ }
602
+ };
603
+ }
604
+ /**
605
+ * Estimate memory usage in bytes
606
+ *
607
+ * Provides rough estimate of memory consumption.
608
+ *
609
+ * @returns Estimated memory usage in bytes
610
+ */
611
+ estimateMemoryUsage() {
612
+ const vectorSize = this.config.index.dimensions * 4;
613
+ const metadataSize = 100;
614
+ const neighborsSize = 50 * 8;
615
+ const nodeOverhead = 64;
616
+ return this.nodes.size * (vectorSize + metadataSize + neighborsSize + nodeOverhead);
617
+ }
618
+ /**
619
+ * Calculate average number of connections per node
620
+ *
621
+ * @returns Average connection count
622
+ */
623
+ calculateAverageConnections() {
624
+ if (this.nodes.size === 0) return 0;
625
+ let totalConnections = 0;
626
+ for (const node of this.nodes.values()) {
627
+ for (const neighbors of node.neighbors.values()) {
628
+ totalConnections += neighbors.length;
629
+ }
630
+ }
631
+ return totalConnections / this.nodes.size;
632
+ }
633
+ /**
634
+ * Clear all vectors
635
+ *
636
+ * Removes all vectors from the index and resets state.
637
+ */
638
+ async clear() {
639
+ this.nodes.clear();
640
+ this.entryPoint = null;
641
+ this.maxLevel = 0;
642
+ logger.info("Vector store cleared");
643
+ }
644
+ /**
645
+ * Get configuration
646
+ *
647
+ * @returns Copy of current configuration
648
+ */
649
+ getConfig() {
650
+ return { ...this.config };
651
+ }
652
+ /**
653
+ * Check if store is initialized
654
+ *
655
+ * @returns True if initialized
656
+ */
657
+ isReady() {
658
+ return this.isInitialized;
659
+ }
660
+ /**
661
+ * Get total vector count
662
+ *
663
+ * @returns Number of vectors in the store
664
+ */
665
+ size() {
666
+ return this.nodes.size;
667
+ }
668
+ /**
669
+ * Check if a vector exists
670
+ *
671
+ * @param id - Vector ID to check
672
+ * @returns True if exists
673
+ */
674
+ has(id) {
675
+ return this.nodes.has(id);
676
+ }
677
+ /**
678
+ * Get all vector IDs
679
+ *
680
+ * @returns Array of all vector IDs
681
+ */
682
+ getAllIds() {
683
+ return Array.from(this.nodes.keys());
684
+ }
685
+ /**
686
+ * Add event listener
687
+ *
688
+ * @param event - Event type to listen for
689
+ * @param listener - Callback function
690
+ */
691
+ on(event, listener) {
692
+ if (!this.eventListeners.has(event)) {
693
+ this.eventListeners.set(event, /* @__PURE__ */ new Set());
694
+ }
695
+ this.eventListeners.get(event).add(listener);
696
+ }
697
+ /**
698
+ * Remove event listener
699
+ *
700
+ * @param event - Event type
701
+ * @param listener - Callback function to remove
702
+ */
703
+ off(event, listener) {
704
+ const listeners = this.eventListeners.get(event);
705
+ if (listeners) {
706
+ listeners.delete(listener);
707
+ }
708
+ }
709
+ /**
710
+ * Emit an event to all registered listeners
711
+ *
712
+ * @param event - Event to emit
713
+ */
714
+ emitEvent(event) {
715
+ const listeners = this.eventListeners.get(event.type);
716
+ if (listeners) {
717
+ for (const listener of listeners) {
718
+ try {
719
+ listener(event);
720
+ } catch (error) {
721
+ logger.error("Event listener error", error instanceof Error ? error : void 0, {
722
+ eventType: event.type
723
+ });
724
+ }
725
+ }
726
+ }
727
+ const wildcardListeners = this.eventListeners.get("*");
728
+ if (wildcardListeners) {
729
+ for (const listener of wildcardListeners) {
730
+ try {
731
+ listener(event);
732
+ } catch (error) {
733
+ logger.error("Wildcard listener error", error instanceof Error ? error : void 0, {
734
+ eventType: event.type
735
+ });
736
+ }
737
+ }
738
+ }
739
+ }
740
+ }
741
+ function createVectorStore(config) {
742
+ return new EnhancedVectorStore(config);
743
+ }
744
+ export {
745
+ EnhancedVectorStore,
746
+ createVectorStore
747
+ };
748
+ //# sourceMappingURL=vector-store.js.map