vecmindb 1.0.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1205 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AuthManager: () => AuthManager,
34
+ AuthenticationError: () => AuthenticationError,
35
+ NotFoundError: () => NotFoundError,
36
+ PaymentRequiredError: () => PaymentRequiredError,
37
+ PermissionError: () => PermissionError,
38
+ RateLimitError: () => RateLimitError,
39
+ ServerError: () => ServerError,
40
+ VecminClient: () => VecminClient,
41
+ VecminDBVectorStore: () => VecminDBVectorStore,
42
+ VecminError: () => VecminError,
43
+ VecminMCPClient: () => VecminMCPClient,
44
+ createErrorFromStatus: () => createErrorFromStatus,
45
+ fetchJson: () => fetchJson,
46
+ resolveRetryOptions: () => resolveRetryOptions,
47
+ withRetry: () => withRetry
48
+ });
49
+ module.exports = __toCommonJS(index_exports);
50
+
51
+ // src/errors.ts
52
+ var VecminError = class extends Error {
53
+ /** HTTP-style status code from the server response. */
54
+ code;
55
+ constructor(message, code) {
56
+ super(message);
57
+ this.name = "VecminError";
58
+ this.code = code;
59
+ Object.setPrototypeOf(this, new.target.prototype);
60
+ }
61
+ };
62
+ var AuthenticationError = class extends VecminError {
63
+ constructor(message = "Authentication failed or missing credentials") {
64
+ super(message, 401);
65
+ this.name = "AuthenticationError";
66
+ Object.setPrototypeOf(this, new.target.prototype);
67
+ }
68
+ };
69
+ var PermissionError = class extends VecminError {
70
+ constructor(message = "Insufficient permissions") {
71
+ super(message, 403);
72
+ this.name = "PermissionError";
73
+ Object.setPrototypeOf(this, new.target.prototype);
74
+ }
75
+ };
76
+ var PaymentRequiredError = class extends VecminError {
77
+ constructor(message = "Subscription payment or valid license key required. Please register/activate your license at https://lingxinmind.com") {
78
+ super(message, 402);
79
+ this.name = "PaymentRequiredError";
80
+ Object.setPrototypeOf(this, new.target.prototype);
81
+ }
82
+ };
83
+ var NotFoundError = class extends VecminError {
84
+ constructor(message = "Resource not found") {
85
+ super(message, 404);
86
+ this.name = "NotFoundError";
87
+ Object.setPrototypeOf(this, new.target.prototype);
88
+ }
89
+ };
90
+ var RateLimitError = class extends VecminError {
91
+ constructor(message = "Rate limit exceeded") {
92
+ super(message, 429);
93
+ this.name = "RateLimitError";
94
+ Object.setPrototypeOf(this, new.target.prototype);
95
+ }
96
+ };
97
+ var ServerError = class extends VecminError {
98
+ constructor(message = "Internal server error", code = 500) {
99
+ super(message, code);
100
+ this.name = "ServerError";
101
+ Object.setPrototypeOf(this, new.target.prototype);
102
+ }
103
+ };
104
+ function createErrorFromStatus(status, message) {
105
+ switch (status) {
106
+ case 401:
107
+ return new AuthenticationError(message);
108
+ case 402:
109
+ return new PaymentRequiredError(message);
110
+ case 403:
111
+ return new PermissionError(message);
112
+ case 404:
113
+ return new NotFoundError(message);
114
+ case 429:
115
+ return new RateLimitError(message);
116
+ default:
117
+ if (status >= 500 && status < 600) {
118
+ return new ServerError(message, status);
119
+ }
120
+ return new VecminError(message, status);
121
+ }
122
+ }
123
+
124
+ // src/retry.ts
125
+ var DEFAULTS = {
126
+ maxRetries: 3,
127
+ backoffFactor: 0.5,
128
+ retryableStatusCodes: [429, 500, 502, 503, 504]
129
+ };
130
+ function resolveRetryOptions(opts) {
131
+ return {
132
+ maxRetries: opts?.maxRetries ?? DEFAULTS.maxRetries,
133
+ backoffFactor: opts?.backoffFactor ?? DEFAULTS.backoffFactor,
134
+ retryableStatusCodes: opts?.retryableStatusCodes ?? DEFAULTS.retryableStatusCodes
135
+ };
136
+ }
137
+ function sleep(ms) {
138
+ return new Promise((resolve) => setTimeout(resolve, ms));
139
+ }
140
+ function isRetryable(status, retryableCodes) {
141
+ return retryableCodes.includes(status);
142
+ }
143
+ async function withRetry(fn, options) {
144
+ const opts = resolveRetryOptions(options);
145
+ let lastError;
146
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
147
+ try {
148
+ return await fn();
149
+ } catch (err) {
150
+ if (err instanceof VecminError && isRetryable(err.code, opts.retryableStatusCodes)) {
151
+ lastError = err;
152
+ let delayMs;
153
+ if (err instanceof RateLimitError && err.retryAfter) {
154
+ delayMs = (err.retryAfter ?? 1) * 1e3;
155
+ } else {
156
+ delayMs = opts.backoffFactor * Math.pow(2, attempt) * 1e3;
157
+ }
158
+ const jitter = Math.random() * 200 - 100;
159
+ await sleep(Math.max(0, delayMs + jitter));
160
+ continue;
161
+ }
162
+ throw err;
163
+ }
164
+ }
165
+ throw lastError ?? new VecminError("All retry attempts exhausted", 0);
166
+ }
167
+ async function fetchJson(url, init, retryOpts) {
168
+ return withRetry(async () => {
169
+ const res = await fetch(url, init);
170
+ const body = await res.json();
171
+ if (!res.ok) {
172
+ const message = body?.error ?? body?.message ?? `HTTP ${res.status}`;
173
+ const code = body?.code ?? res.status;
174
+ throw createErrorFromStatus(code, message);
175
+ }
176
+ return body?.data !== void 0 ? body.data : body;
177
+ }, retryOpts);
178
+ }
179
+
180
+ // src/auth.ts
181
+ function decodeJwtPayload(token) {
182
+ try {
183
+ const parts = token.split(".");
184
+ if (parts.length < 2) return null;
185
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
186
+ const json = decodeURIComponent(
187
+ atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
188
+ );
189
+ return JSON.parse(json);
190
+ } catch {
191
+ return null;
192
+ }
193
+ }
194
+ function isJwtExpiringSoon(token, bufferMs = 5 * 60 * 1e3) {
195
+ const payload = decodeJwtPayload(token);
196
+ if (!payload) return true;
197
+ const exp = payload["exp"];
198
+ if (typeof exp !== "number") return true;
199
+ return Date.now() >= exp * 1e3 - bufferMs;
200
+ }
201
+ var AuthManager = class {
202
+ apiKey;
203
+ jwt;
204
+ loginParams;
205
+ baseUrl;
206
+ refreshPromise = null;
207
+ constructor(baseUrl, options) {
208
+ this.baseUrl = baseUrl;
209
+ this.apiKey = options.apiKey;
210
+ this.jwt = options.jwt;
211
+ }
212
+ /**
213
+ * Store login credentials so the manager can automatically refresh the JWT.
214
+ * Called internally by {@link VecminClient.login}.
215
+ */
216
+ setLoginCredentials(params, jwt) {
217
+ this.loginParams = params;
218
+ this.jwt = jwt;
219
+ }
220
+ /**
221
+ * Return the HTTP headers needed for the next request.
222
+ *
223
+ * If a JWT is present and not expiring soon, uses `Authorization: Bearer`.
224
+ * Otherwise falls back to `x-api-key` (if configured).
225
+ *
226
+ * When both are configured, JWT takes precedence; if the JWT is expiring
227
+ * the method triggers a background refresh and falls back to the API key
228
+ * for the current request.
229
+ */
230
+ async getHeaders() {
231
+ const headers = {};
232
+ if (this.jwt) {
233
+ if (isJwtExpiringSoon(this.jwt)) {
234
+ const freshJwt = await this.refreshJwt();
235
+ if (freshJwt) {
236
+ headers["Authorization"] = `Bearer ${freshJwt}`;
237
+ } else if (this.apiKey) {
238
+ headers["x-api-key"] = this.apiKey;
239
+ }
240
+ } else {
241
+ headers["Authorization"] = `Bearer ${this.jwt}`;
242
+ }
243
+ } else if (this.apiKey) {
244
+ headers["x-api-key"] = this.apiKey;
245
+ }
246
+ return headers;
247
+ }
248
+ /**
249
+ * Attempt to refresh the JWT by re-posting the stored login credentials.
250
+ * Uses a singleton promise so concurrent calls don't trigger multiple logins.
251
+ */
252
+ async refreshJwt() {
253
+ if (!this.loginParams) return null;
254
+ if (this.refreshPromise) return this.refreshPromise;
255
+ this.refreshPromise = (async () => {
256
+ try {
257
+ const result = await fetchJson(
258
+ `${this.baseUrl}/api/v1/cluster/login`,
259
+ {
260
+ method: "POST",
261
+ headers: { "Content-Type": "application/json" },
262
+ body: JSON.stringify(this.loginParams)
263
+ },
264
+ { maxRetries: 1 }
265
+ // Don't retry aggressively on auth failures.
266
+ );
267
+ this.jwt = result;
268
+ return result;
269
+ } catch {
270
+ return null;
271
+ } finally {
272
+ this.refreshPromise = null;
273
+ }
274
+ })();
275
+ return this.refreshPromise;
276
+ }
277
+ };
278
+
279
+ // src/client.ts
280
+ var VecminClient = class {
281
+ /** Resolved base URL (trailing slash stripped). */
282
+ baseUrl;
283
+ /** API path prefix. */
284
+ apiUrl;
285
+ /** MCP path prefix. */
286
+ mcpUrl;
287
+ /** Authentication manager. */
288
+ auth;
289
+ /** Request timeout in milliseconds. */
290
+ timeout;
291
+ /** Retry configuration propagated to every request. */
292
+ retryOptions;
293
+ /** Default headers merged into every request. */
294
+ defaultHeaders;
295
+ /** Abort controllers for in-flight requests (used by `close()`). */
296
+ controllers = /* @__PURE__ */ new Set();
297
+ /** Whether the client has been closed. */
298
+ closed = false;
299
+ constructor(options) {
300
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
301
+ this.apiUrl = `${this.baseUrl}/api/v1`;
302
+ this.mcpUrl = `${this.baseUrl}/mcp`;
303
+ this.auth = new AuthManager(this.baseUrl, options);
304
+ this.timeout = options.timeout ?? 3e4;
305
+ this.retryOptions = {
306
+ maxRetries: options.maxRetries ?? 3,
307
+ backoffFactor: options.backoffFactor ?? 0.5
308
+ };
309
+ this.defaultHeaders = {
310
+ "Content-Type": "application/json",
311
+ ...options.defaultHeaders
312
+ };
313
+ if (options.agentId) this.defaultHeaders["x-agent-id"] = options.agentId;
314
+ if (options.modelId) this.defaultHeaders["x-model-id"] = options.modelId;
315
+ }
316
+ // -----------------------------------------------------------------------
317
+ // Internal helpers
318
+ // -----------------------------------------------------------------------
319
+ /** Build the full headers map for an outgoing request. */
320
+ async buildHeaders() {
321
+ const authHeaders = await this.auth.getHeaders();
322
+ return { ...this.defaultHeaders, ...authHeaders };
323
+ }
324
+ /** Create an AbortController with the configured timeout and track it. */
325
+ createController() {
326
+ const controller = new AbortController();
327
+ this.controllers.add(controller);
328
+ const timer = setTimeout(() => controller.abort(), this.timeout);
329
+ if (timer.unref) timer.unref();
330
+ controller.signal.addEventListener("abort", () => {
331
+ this.controllers.delete(controller);
332
+ });
333
+ return controller;
334
+ }
335
+ /** Perform a JSON GET request. */
336
+ async get(path) {
337
+ this.assertOpen();
338
+ const controller = this.createController();
339
+ try {
340
+ return await fetchJson(
341
+ `${this.apiUrl}${path}`,
342
+ {
343
+ method: "GET",
344
+ headers: await this.buildHeaders(),
345
+ signal: controller.signal
346
+ },
347
+ this.retryOptions
348
+ );
349
+ } finally {
350
+ this.controllers.delete(controller);
351
+ }
352
+ }
353
+ /** Perform a JSON POST request. */
354
+ async post(path, body) {
355
+ this.assertOpen();
356
+ const controller = this.createController();
357
+ try {
358
+ return await fetchJson(
359
+ `${this.apiUrl}${path}`,
360
+ {
361
+ method: "POST",
362
+ headers: await this.buildHeaders(),
363
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
364
+ signal: controller.signal
365
+ },
366
+ this.retryOptions
367
+ );
368
+ } finally {
369
+ this.controllers.delete(controller);
370
+ }
371
+ }
372
+ /** Perform a JSON DELETE request. */
373
+ async del(path) {
374
+ this.assertOpen();
375
+ const controller = this.createController();
376
+ try {
377
+ return await fetchJson(
378
+ `${this.apiUrl}${path}`,
379
+ {
380
+ method: "DELETE",
381
+ headers: await this.buildHeaders(),
382
+ signal: controller.signal
383
+ },
384
+ this.retryOptions
385
+ );
386
+ } finally {
387
+ this.controllers.delete(controller);
388
+ }
389
+ }
390
+ /** Throw if the client has been closed. */
391
+ assertOpen() {
392
+ if (this.closed) {
393
+ throw new Error("VecminClient has been closed");
394
+ }
395
+ }
396
+ // -----------------------------------------------------------------------
397
+ // Collection Management
398
+ // -----------------------------------------------------------------------
399
+ /**
400
+ * Create a new collection.
401
+ *
402
+ * @example
403
+ * ```ts
404
+ * const col = await client.createCollection({
405
+ * name: "docs",
406
+ * dimension: 1536,
407
+ * metric_type: "Cosine",
408
+ * index_type: "HNSW",
409
+ * });
410
+ * ```
411
+ */
412
+ async createCollection(params) {
413
+ return this.post("/collections", params);
414
+ }
415
+ /**
416
+ * List all collections.
417
+ *
418
+ * @example
419
+ * ```ts
420
+ * const collections = await client.listCollections();
421
+ * ```
422
+ */
423
+ async listCollections() {
424
+ return this.get("/collections");
425
+ }
426
+ /**
427
+ * Get details for a specific collection.
428
+ *
429
+ * @example
430
+ * ```ts
431
+ * const col = await client.getCollection("docs");
432
+ * ```
433
+ */
434
+ async getCollection(name) {
435
+ return this.get(`/collections/${encodeURIComponent(name)}`);
436
+ }
437
+ /**
438
+ * Delete a collection and all its data.
439
+ *
440
+ * @example
441
+ * ```ts
442
+ * await client.deleteCollection("docs");
443
+ * ```
444
+ */
445
+ async deleteCollection(name) {
446
+ await this.del(`/collections/${encodeURIComponent(name)}`);
447
+ }
448
+ /**
449
+ * Get statistics for a collection.
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const stats = await client.getCollectionStats("docs");
454
+ * console.log(`Vectors: ${stats.vector_count}`);
455
+ * ```
456
+ */
457
+ async getCollectionStats(name) {
458
+ return this.get(`/collections/${encodeURIComponent(name)}/stats`);
459
+ }
460
+ // -----------------------------------------------------------------------
461
+ // Vector Operations
462
+ // -----------------------------------------------------------------------
463
+ /**
464
+ * Insert a single vector into a collection.
465
+ *
466
+ * @returns The vector ID (either the caller-supplied ID or a server-generated one).
467
+ *
468
+ * @example
469
+ * ```ts
470
+ * const id = await client.insert("docs", {
471
+ * vector: [0.1, 0.2, /* ... *\/],
472
+ * metadata: { title: "Hello" },
473
+ * });
474
+ * ```
475
+ */
476
+ async insert(collection, params) {
477
+ return this.post(`/collections/${encodeURIComponent(collection)}/insert`, params);
478
+ }
479
+ /**
480
+ * Insert multiple vectors in a single request.
481
+ *
482
+ * @returns An array of vector IDs in the same order as the input.
483
+ *
484
+ * @example
485
+ * ```ts
486
+ * const ids = await client.batchInsert("docs", [
487
+ * { vector: [0.1, 0.2], metadata: { title: "A" } },
488
+ * { vector: [0.3, 0.4], metadata: { title: "B" } },
489
+ * ]);
490
+ * ```
491
+ */
492
+ async batchInsert(collection, vectors) {
493
+ return this.post(`/collections/${encodeURIComponent(collection)}/batch`, vectors);
494
+ }
495
+ /**
496
+ * Search for the nearest neighbours of a query vector.
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * const results = await client.search("docs", {
501
+ * query: [0.1, 0.2, /* ... *\/],
502
+ * k: 5,
503
+ * ef_search: 100,
504
+ * });
505
+ * for (const r of results) {
506
+ * console.log(r.id, r.score);
507
+ * }
508
+ * ```
509
+ */
510
+ async search(collection, params) {
511
+ return this.post(`/collections/${encodeURIComponent(collection)}/search`, params);
512
+ }
513
+ /**
514
+ * Retrieve a single vector by its ID.
515
+ *
516
+ * @example
517
+ * ```ts
518
+ * const vec = await client.getVector("docs", "abc-123");
519
+ * ```
520
+ */
521
+ async getVector(collection, id) {
522
+ return this.get(`/collections/${encodeURIComponent(collection)}/vectors/${encodeURIComponent(id)}`);
523
+ }
524
+ /**
525
+ * Delete a single vector by its ID.
526
+ *
527
+ * @example
528
+ * ```ts
529
+ * await client.deleteVector("docs", "abc-123");
530
+ * ```
531
+ */
532
+ async deleteVector(collection, id) {
533
+ await this.del(`/collections/${encodeURIComponent(collection)}/vectors/${encodeURIComponent(id)}`);
534
+ }
535
+ // -----------------------------------------------------------------------
536
+ // Index Management
537
+ // -----------------------------------------------------------------------
538
+ /**
539
+ * Trigger an index rebuild for a collection.
540
+ *
541
+ * @example
542
+ * ```ts
543
+ * await client.rebuildIndex("docs");
544
+ * ```
545
+ */
546
+ async rebuildIndex(collection) {
547
+ await this.post(`/collections/${encodeURIComponent(collection)}/rebuild_index`);
548
+ }
549
+ /**
550
+ * List all indexes in the cluster.
551
+ *
552
+ * @example
553
+ * ```ts
554
+ * const indexes = await client.listIndexes();
555
+ * ```
556
+ */
557
+ async listIndexes() {
558
+ return this.get("/indexes");
559
+ }
560
+ /**
561
+ * Rebuild a named index.
562
+ *
563
+ * @example
564
+ * ```ts
565
+ * await client.rebuildNamedIndex("docs_hnsw");
566
+ * ```
567
+ */
568
+ async rebuildNamedIndex(name) {
569
+ await this.post(`/indexes/${encodeURIComponent(name)}/rebuild`);
570
+ }
571
+ /**
572
+ * Optimize a named index.
573
+ *
574
+ * @example
575
+ * ```ts
576
+ * await client.optimizeIndex("docs_hnsw");
577
+ * ```
578
+ */
579
+ async optimizeIndex(name) {
580
+ await this.post(`/indexes/${encodeURIComponent(name)}/optimize`);
581
+ }
582
+ // -----------------------------------------------------------------------
583
+ // Cluster Management
584
+ // -----------------------------------------------------------------------
585
+ /**
586
+ * Authenticate and obtain a JWT token.
587
+ * The token is cached and automatically refreshed before expiry.
588
+ *
589
+ * @returns The JWT string.
590
+ *
591
+ * @example
592
+ * ```ts
593
+ * const jwt = await client.login({ username: "admin", password: "secret" });
594
+ * ```
595
+ */
596
+ async login(credentials) {
597
+ const jwt = await this.post("/cluster/login", credentials);
598
+ this.auth.setLoginCredentials(credentials, jwt);
599
+ return jwt;
600
+ }
601
+ /**
602
+ * List all nodes in the cluster.
603
+ *
604
+ * @example
605
+ * ```ts
606
+ * const nodes = await client.listNodes();
607
+ * ```
608
+ */
609
+ async listNodes() {
610
+ return this.get("/cluster/nodes");
611
+ }
612
+ /**
613
+ * Get the overall cluster status.
614
+ *
615
+ * @example
616
+ * ```ts
617
+ * const status = await client.clusterStatus();
618
+ * console.log(status.status, status.leader_id);
619
+ * ```
620
+ */
621
+ async clusterStatus() {
622
+ return this.get("/cluster/status");
623
+ }
624
+ /**
625
+ * Create a cluster snapshot.
626
+ *
627
+ * @example
628
+ * ```ts
629
+ * await client.createSnapshot();
630
+ * ```
631
+ */
632
+ async createSnapshot() {
633
+ await this.post("/cluster/snapshot");
634
+ }
635
+ // -----------------------------------------------------------------------
636
+ // MCP Convenience Methods
637
+ // -----------------------------------------------------------------------
638
+ /**
639
+ * Store a memory via the MCP `store_memory` tool.
640
+ *
641
+ * This is a convenience wrapper that calls the MCP JSON-RPC endpoint.
642
+ *
643
+ * @example
644
+ * ```ts
645
+ * await client.mcpStoreMemory("User prefers dark mode", "agent-1");
646
+ * ```
647
+ */
648
+ async mcpStoreMemory(text, agentId, options) {
649
+ const controller = this.createController();
650
+ try {
651
+ await fetchJson(
652
+ `${this.mcpUrl}/messages`,
653
+ {
654
+ method: "POST",
655
+ headers: await this.buildHeaders(),
656
+ body: JSON.stringify({
657
+ jsonrpc: "2.0",
658
+ id: Date.now(),
659
+ method: "tools/call",
660
+ params: {
661
+ name: "store_memory",
662
+ arguments: {
663
+ agent_id: agentId,
664
+ content: text,
665
+ source: options?.source ?? "typescript-sdk",
666
+ collection: options?.collection ?? "agent_memory_mcp"
667
+ }
668
+ }
669
+ }),
670
+ signal: controller.signal
671
+ },
672
+ this.retryOptions
673
+ );
674
+ } finally {
675
+ this.controllers.delete(controller);
676
+ }
677
+ }
678
+ /**
679
+ * Search memories via the MCP `search_memory` tool.
680
+ *
681
+ * @example
682
+ * ```ts
683
+ * const results = await client.mcpSearchMemory("user preferences", "agent-1", 5);
684
+ * ```
685
+ */
686
+ async mcpSearchMemory(query, agentId, topK = 5) {
687
+ const controller = this.createController();
688
+ try {
689
+ const result = await fetchJson(
690
+ `${this.mcpUrl}/messages`,
691
+ {
692
+ method: "POST",
693
+ headers: await this.buildHeaders(),
694
+ body: JSON.stringify({
695
+ jsonrpc: "2.0",
696
+ id: Date.now(),
697
+ method: "tools/call",
698
+ params: {
699
+ name: "search_memory",
700
+ arguments: {
701
+ agent_id: agentId,
702
+ query,
703
+ top_k: topK
704
+ }
705
+ }
706
+ }),
707
+ signal: controller.signal
708
+ },
709
+ this.retryOptions
710
+ );
711
+ return result;
712
+ } finally {
713
+ this.controllers.delete(controller);
714
+ }
715
+ }
716
+ // -----------------------------------------------------------------------
717
+ // Lifecycle
718
+ // -----------------------------------------------------------------------
719
+ /**
720
+ * Gracefully shut down the client.
721
+ * Aborts all in-flight requests and releases resources.
722
+ *
723
+ * @example
724
+ * ```ts
725
+ * await client.close();
726
+ * ```
727
+ */
728
+ async close() {
729
+ this.closed = true;
730
+ for (const controller of this.controllers) {
731
+ controller.abort();
732
+ }
733
+ this.controllers.clear();
734
+ }
735
+ };
736
+
737
+ // src/mcp.ts
738
+ var VecminMCPClient = class {
739
+ baseUrl;
740
+ mcpUrl;
741
+ options;
742
+ handlers = /* @__PURE__ */ new Map();
743
+ rpcId = 0;
744
+ pendingRpc = /* @__PURE__ */ new Map();
745
+ abortController = null;
746
+ reader = null;
747
+ connected = false;
748
+ constructor(baseUrl, options) {
749
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
750
+ this.mcpUrl = `${this.baseUrl}/mcp`;
751
+ this.options = options ?? {};
752
+ }
753
+ // -----------------------------------------------------------------------
754
+ // Event emitter
755
+ // -----------------------------------------------------------------------
756
+ /**
757
+ * Register an event handler.
758
+ *
759
+ * @param event - One of the {@link MCPEventMap} keys.
760
+ * @param handler - Callback invoked when the event fires.
761
+ */
762
+ on(event, handler) {
763
+ if (!this.handlers.has(event)) {
764
+ this.handlers.set(event, /* @__PURE__ */ new Set());
765
+ }
766
+ this.handlers.get(event).add(handler);
767
+ }
768
+ /**
769
+ * Remove a previously registered event handler.
770
+ */
771
+ off(event, handler) {
772
+ this.handlers.get(event)?.delete(handler);
773
+ }
774
+ emit(event, data) {
775
+ for (const handler of this.handlers.get(event) ?? []) {
776
+ try {
777
+ handler(data);
778
+ } catch {
779
+ }
780
+ }
781
+ }
782
+ // -----------------------------------------------------------------------
783
+ // SSE Connection
784
+ // -----------------------------------------------------------------------
785
+ /**
786
+ * Open the SSE event-stream connection to `GET /mcp/sse`.
787
+ *
788
+ * The connection remains open until {@link disconnect} is called.
789
+ * Events are parsed and emitted via the `on("event", ...)` interface.
790
+ */
791
+ async connect() {
792
+ if (this.connected) return;
793
+ this.abortController = new AbortController();
794
+ const headers = {
795
+ Accept: "text/event-stream"
796
+ };
797
+ if (this.options.apiKey) headers["x-api-key"] = this.options.apiKey;
798
+ if (this.options.jwt) headers["Authorization"] = `Bearer ${this.options.jwt}`;
799
+ if (this.options.agentId) headers["x-agent-id"] = this.options.agentId;
800
+ if (this.options.modelId) headers["x-model-id"] = this.options.modelId;
801
+ const response = await fetch(`${this.mcpUrl}/sse`, {
802
+ method: "GET",
803
+ headers,
804
+ signal: this.abortController.signal
805
+ });
806
+ if (!response.ok) {
807
+ throw createErrorFromStatus(
808
+ response.status,
809
+ `SSE connection failed: HTTP ${response.status}`
810
+ );
811
+ }
812
+ const body = response.body;
813
+ if (!body) {
814
+ throw new VecminError("SSE connection returned no body", 0);
815
+ }
816
+ this.connected = true;
817
+ this.emit("connected", void 0);
818
+ this.readSSEStream(body).catch((err) => {
819
+ if (this.connected) {
820
+ this.emit("error", err instanceof Error ? err : new Error(String(err)));
821
+ }
822
+ });
823
+ }
824
+ /**
825
+ * Read and parse an SSE stream using `eventsource-parser`.
826
+ */
827
+ async readSSEStream(stream) {
828
+ const { createParser } = await import("eventsource-parser");
829
+ const parser = createParser((event) => {
830
+ if (!("data" in event)) return;
831
+ this.emit("event", { event: event.event ?? "message", data: event.data });
832
+ if (event.data) {
833
+ try {
834
+ const json = JSON.parse(event.data);
835
+ if (json.id !== void 0 && json.id !== null) {
836
+ const pending = this.pendingRpc.get(json.id);
837
+ if (pending) {
838
+ this.pendingRpc.delete(json.id);
839
+ if (json.error) {
840
+ pending.reject(
841
+ new VecminError(json.error.message ?? "JSON-RPC error", json.error.code)
842
+ );
843
+ } else {
844
+ pending.resolve(json.result);
845
+ }
846
+ this.emit("result", { id: json.id, result: json.result });
847
+ }
848
+ }
849
+ } catch {
850
+ }
851
+ }
852
+ });
853
+ this.reader = stream.getReader();
854
+ const decoder = new TextDecoder();
855
+ try {
856
+ while (true) {
857
+ const { done, value } = await this.reader.read();
858
+ if (done) break;
859
+ parser.feed(decoder.decode(value, { stream: true }));
860
+ }
861
+ } finally {
862
+ this.connected = false;
863
+ this.emit("disconnected", void 0);
864
+ }
865
+ }
866
+ // -----------------------------------------------------------------------
867
+ // JSON-RPC Calls
868
+ // -----------------------------------------------------------------------
869
+ /**
870
+ * Send a JSON-RPC 2.0 request to `POST /mcp/messages`.
871
+ *
872
+ * @returns The `result` field of the JSON-RPC response.
873
+ */
874
+ async rpc(method, params) {
875
+ const id = ++this.rpcId;
876
+ const request = {
877
+ jsonrpc: "2.0",
878
+ id,
879
+ method,
880
+ params
881
+ };
882
+ const headers = {
883
+ "Content-Type": "application/json"
884
+ };
885
+ if (this.options.apiKey) headers["x-api-key"] = this.options.apiKey;
886
+ if (this.options.jwt) headers["Authorization"] = `Bearer ${this.options.jwt}`;
887
+ if (this.options.agentId) headers["x-agent-id"] = this.options.agentId;
888
+ if (this.options.modelId) headers["x-model-id"] = this.options.modelId;
889
+ const rpcPromise = new Promise((resolve, reject) => {
890
+ this.pendingRpc.set(id, { resolve, reject });
891
+ });
892
+ const res = await fetch(`${this.mcpUrl}/messages`, {
893
+ method: "POST",
894
+ headers,
895
+ body: JSON.stringify(request)
896
+ });
897
+ if (!res.ok) {
898
+ this.pendingRpc.delete(id);
899
+ throw createErrorFromStatus(res.status, `MCP RPC call failed: HTTP ${res.status}`);
900
+ }
901
+ const text = await res.text();
902
+ if (text) {
903
+ try {
904
+ const json = JSON.parse(text);
905
+ this.pendingRpc.delete(id);
906
+ if (json.error) {
907
+ throw new VecminError(json.error.message ?? "JSON-RPC error", json.error.code);
908
+ }
909
+ return json.result ?? json;
910
+ } catch (err) {
911
+ if (err instanceof VecminError) throw err;
912
+ }
913
+ }
914
+ return rpcPromise;
915
+ }
916
+ // -----------------------------------------------------------------------
917
+ // MCP Tool Methods
918
+ // -----------------------------------------------------------------------
919
+ /**
920
+ * Call the `store_memory` MCP tool.
921
+ *
922
+ * @example
923
+ * ```ts
924
+ * await mcp.storeMemory({ text: "User prefers dark mode", agent_id: "agent-1" });
925
+ * ```
926
+ */
927
+ async storeMemory(params) {
928
+ await this.rpc("tools/call", {
929
+ name: "store_memory",
930
+ arguments: {
931
+ agent_id: params.agent_id,
932
+ text: params.text,
933
+ source: params.source ?? "typescript-sdk",
934
+ collection: params.collection ?? "agent_memory_mcp"
935
+ }
936
+ });
937
+ }
938
+ /**
939
+ * Call the `search_memory` MCP tool.
940
+ *
941
+ * @example
942
+ * ```ts
943
+ * const results = await mcp.searchMemory({ query: "preferences", agent_id: "agent-1", top_k: 5 });
944
+ * ```
945
+ */
946
+ async searchMemory(params) {
947
+ const result = await this.rpc("tools/call", {
948
+ name: "search_memory",
949
+ arguments: {
950
+ agent_id: params.agent_id,
951
+ query: params.query,
952
+ top_k: params.top_k ?? 5,
953
+ collection: params.collection ?? "agent_memory_mcp"
954
+ }
955
+ });
956
+ return result;
957
+ }
958
+ // -----------------------------------------------------------------------
959
+ // Lifecycle
960
+ // -----------------------------------------------------------------------
961
+ /**
962
+ * Close the SSE connection and release all resources.
963
+ */
964
+ async disconnect() {
965
+ this.connected = false;
966
+ this.abortController?.abort();
967
+ this.abortController = null;
968
+ try {
969
+ await this.reader?.cancel();
970
+ } catch {
971
+ }
972
+ this.reader = null;
973
+ for (const [id, { reject }] of this.pendingRpc) {
974
+ reject(new VecminError("MCP client disconnected", 0));
975
+ this.pendingRpc.delete(id);
976
+ }
977
+ }
978
+ };
979
+
980
+ // src/langchain.ts
981
+ var LangChain = null;
982
+ var LangChainDocs = null;
983
+ async function ensureLangChain() {
984
+ if (LangChain && LangChainDocs) {
985
+ return {
986
+ VectorStore: LangChain.VectorStore,
987
+ Document: LangChainDocs.Document,
988
+ Embeddings: (await import("@langchain/core/embeddings")).Embeddings
989
+ };
990
+ }
991
+ try {
992
+ const vc = await import("@langchain/core/vectorstores");
993
+ const docs = await import("@langchain/core/documents");
994
+ const emb = await import("@langchain/core/embeddings");
995
+ LangChain = vc;
996
+ LangChainDocs = docs;
997
+ return {
998
+ VectorStore: vc.VectorStore,
999
+ Document: docs.Document,
1000
+ Embeddings: emb.Embeddings
1001
+ };
1002
+ } catch {
1003
+ throw new Error(
1004
+ "@langchain/core is required for VecminDBVectorStore. Install it with: npm install @langchain/core"
1005
+ );
1006
+ }
1007
+ }
1008
+ var VecminDBVectorStore = class _VecminDBVectorStore {
1009
+ /** Underlying VecminDB client used for all I/O. */
1010
+ client;
1011
+ /** LangChain embeddings instance. */
1012
+ _embeddings;
1013
+ // typed as unknown to avoid requiring @langchain/core at import time
1014
+ /** Collection name in VecminDB. */
1015
+ collectionName;
1016
+ /** Vector dimensionality. */
1017
+ dimension;
1018
+ /** Whether the collection has been ensured to exist. */
1019
+ ensured = false;
1020
+ // LangChain VectorStore interface fields
1021
+ /** @internal */
1022
+ lc_namespace = ["vecmindb", "vectorstores"];
1023
+ /** @internal */
1024
+ lc_serializable = true;
1025
+ constructor(embeddings, options) {
1026
+ this._embeddings = embeddings;
1027
+ this.collectionName = options.collectionName ?? "langchain_memory";
1028
+ this.dimension = options.dimension ?? 1536;
1029
+ this.client = new VecminClient({
1030
+ baseUrl: options.baseUrl,
1031
+ apiKey: options.apiKey,
1032
+ timeout: options.timeout
1033
+ });
1034
+ }
1035
+ // -----------------------------------------------------------------------
1036
+ // Collection bootstrap
1037
+ // -----------------------------------------------------------------------
1038
+ /**
1039
+ * Ensure the target collection exists in VecminDB.
1040
+ * Called lazily before the first write operation.
1041
+ */
1042
+ async ensureCollection() {
1043
+ if (this.ensured) return;
1044
+ try {
1045
+ await this.client.createCollection({
1046
+ name: this.collectionName,
1047
+ dimension: this.dimension,
1048
+ metric_type: "Cosine",
1049
+ index_type: "HNSW",
1050
+ index_params: { m: 16, ef_construction: 100 }
1051
+ });
1052
+ } catch (err) {
1053
+ if (err instanceof Error && err.message?.includes("already exists")) {
1054
+ } else if (err instanceof Error && (err.message?.includes("409") || err.message?.includes("Conflict"))) {
1055
+ } else if (err instanceof Error && err.message?.includes("404") === false) {
1056
+ throw err;
1057
+ }
1058
+ }
1059
+ this.ensured = true;
1060
+ }
1061
+ // -----------------------------------------------------------------------
1062
+ // LangChain VectorStore interface
1063
+ // -----------------------------------------------------------------------
1064
+ /**
1065
+ * Add documents to the vector store.
1066
+ * Embeds the document texts and inserts them into VecminDB.
1067
+ */
1068
+ async addDocuments(documents) {
1069
+ const { Embeddings } = await ensureLangChain();
1070
+ const emb = this._embeddings;
1071
+ const texts = documents.map((d) => d.pageContent);
1072
+ const metadatas = documents.map((d) => d.metadata ?? {});
1073
+ const vectors = await emb.embedDocuments(texts);
1074
+ return this.addVectors(vectors, documents, metadatas);
1075
+ }
1076
+ /**
1077
+ * Add pre-computed vectors to the vector store.
1078
+ */
1079
+ async addVectors(vectors, documents, metadatas) {
1080
+ await this.ensureCollection();
1081
+ const ids = [];
1082
+ for (let i = 0; i < vectors.length; i++) {
1083
+ const doc = documents[i];
1084
+ const meta = {
1085
+ ...metadatas?.[i] ?? doc.metadata ?? {},
1086
+ text: doc.pageContent
1087
+ // Store the text in metadata for retrieval.
1088
+ };
1089
+ const id = await this.client.insert(this.collectionName, {
1090
+ vector: vectors[i],
1091
+ metadata: meta
1092
+ });
1093
+ ids.push(id);
1094
+ }
1095
+ return ids;
1096
+ }
1097
+ /**
1098
+ * Search for the most similar vectors and return results with scores.
1099
+ *
1100
+ * @param query - Query vector.
1101
+ * @param k - Number of results. @default 4
1102
+ */
1103
+ async similaritySearchVectorWithScore(query, k = 4) {
1104
+ const results = await this.client.search(this.collectionName, {
1105
+ query,
1106
+ k
1107
+ });
1108
+ return results.map((r) => {
1109
+ const meta = r.metadata ?? {};
1110
+ const text = typeof meta["text"] === "string" ? meta["text"] : "";
1111
+ const cleanMeta = { ...meta };
1112
+ delete cleanMeta["text"];
1113
+ return [
1114
+ { pageContent: text, metadata: cleanMeta },
1115
+ r.score
1116
+ ];
1117
+ });
1118
+ }
1119
+ /**
1120
+ * Convenience method: search for similar documents (without scores).
1121
+ */
1122
+ async similaritySearch(query, k = 4) {
1123
+ const { Embeddings } = await ensureLangChain();
1124
+ const emb = this._embeddings;
1125
+ const queryVector = await emb.embedQuery(query);
1126
+ const results = await this.similaritySearchVectorWithScore(queryVector, k);
1127
+ return results.map(([doc]) => doc);
1128
+ }
1129
+ /**
1130
+ * Convenience method: search for similar documents with scores.
1131
+ */
1132
+ async similaritySearchWithScore(query, k = 4) {
1133
+ const { Embeddings } = await ensureLangChain();
1134
+ const emb = this._embeddings;
1135
+ const queryVector = await emb.embedQuery(query);
1136
+ return this.similaritySearchVectorWithScore(queryVector, k);
1137
+ }
1138
+ // -----------------------------------------------------------------------
1139
+ // Static factory methods
1140
+ // -----------------------------------------------------------------------
1141
+ /**
1142
+ * Create a VecminDBVectorStore from an array of Document objects.
1143
+ *
1144
+ * @example
1145
+ * ```ts
1146
+ * const store = await VecminDBVectorStore.fromDocuments(
1147
+ * [{ pageContent: "Hello", metadata: {} }],
1148
+ * embeddings,
1149
+ * { baseUrl: "http://localhost:8080" },
1150
+ * );
1151
+ * ```
1152
+ */
1153
+ static async fromDocuments(documents, embeddings, options) {
1154
+ const store = new _VecminDBVectorStore(embeddings, options);
1155
+ await store.addDocuments(documents);
1156
+ return store;
1157
+ }
1158
+ /**
1159
+ * Create a VecminDBVectorStore from an array of texts.
1160
+ *
1161
+ * @example
1162
+ * ```ts
1163
+ * const store = await VecminDBVectorStore.fromTexts(
1164
+ * ["Hello world", "Goodbye world"],
1165
+ * [{}, {}],
1166
+ * embeddings,
1167
+ * { baseUrl: "http://localhost:8080" },
1168
+ * );
1169
+ * ```
1170
+ */
1171
+ static async fromTexts(texts, metadatas, embeddings, options) {
1172
+ const documents = texts.map((text, i) => ({
1173
+ pageContent: text,
1174
+ metadata: metadatas[i] ?? {}
1175
+ }));
1176
+ return _VecminDBVectorStore.fromDocuments(documents, embeddings, options);
1177
+ }
1178
+ // -----------------------------------------------------------------------
1179
+ // Cleanup
1180
+ // -----------------------------------------------------------------------
1181
+ /**
1182
+ * Close the underlying client connection.
1183
+ */
1184
+ async close() {
1185
+ await this.client.close();
1186
+ }
1187
+ };
1188
+ // Annotate the CommonJS export names for ESM import in node:
1189
+ 0 && (module.exports = {
1190
+ AuthManager,
1191
+ AuthenticationError,
1192
+ NotFoundError,
1193
+ PaymentRequiredError,
1194
+ PermissionError,
1195
+ RateLimitError,
1196
+ ServerError,
1197
+ VecminClient,
1198
+ VecminDBVectorStore,
1199
+ VecminError,
1200
+ VecminMCPClient,
1201
+ createErrorFromStatus,
1202
+ fetchJson,
1203
+ resolveRetryOptions,
1204
+ withRetry
1205
+ });