@nebula-ai/sdk 0.0.24 → 0.0.29

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 CHANGED
@@ -1,12 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  // src/types.ts
4
- var RetrievalType = /* @__PURE__ */ ((RetrievalType2) => {
5
- RetrievalType2["BASIC"] = "basic";
6
- RetrievalType2["ADVANCED"] = "advanced";
7
- RetrievalType2["CUSTOM"] = "custom";
8
- return RetrievalType2;
9
- })(RetrievalType || {});
10
4
  var GraphSearchResultType = /* @__PURE__ */ ((GraphSearchResultType2) => {
11
5
  GraphSearchResultType2["ENTITY"] = "entity";
12
6
  GraphSearchResultType2["RELATIONSHIP"] = "relationship";
@@ -47,10 +41,16 @@ var NebulaValidationException = class extends NebulaException {
47
41
  this.name = "NebulaValidationException";
48
42
  }
49
43
  };
50
- var NebulaClusterNotFoundException = class extends NebulaException {
51
- constructor(message = "Cluster not found") {
44
+ var NebulaCollectionNotFoundException = class extends NebulaException {
45
+ constructor(message = "Collection not found") {
52
46
  super(message, 404);
53
- this.name = "NebulaClusterNotFoundException";
47
+ this.name = "NebulaCollectionNotFoundException";
48
+ }
49
+ };
50
+ var NebulaNotFoundException = class extends NebulaException {
51
+ constructor(resourceId, resourceType = "Resource") {
52
+ super(`${resourceType} not found: ${resourceId}`, 404);
53
+ this.name = "NebulaNotFoundException";
54
54
  }
55
55
  };
56
56
 
@@ -143,6 +143,17 @@ var NebulaClient = class {
143
143
  } else if (response.status === 400) {
144
144
  const errorData = await response.json().catch(() => ({}));
145
145
  throw new NebulaValidationException(errorData.message || "Validation error", errorData.details);
146
+ } else if (response.status === 422) {
147
+ const errorData = await response.json().catch(() => ({}));
148
+ console.error("[SDK] 422 Validation error - Full details:");
149
+ console.error(" Status:", response.status);
150
+ console.error(" Error data:", JSON.stringify(errorData, null, 2));
151
+ console.error(" Message:", errorData.message);
152
+ console.error(" Detail:", errorData.detail);
153
+ throw new NebulaValidationException(
154
+ errorData.message || (typeof errorData.detail === "string" ? errorData.detail : JSON.stringify(errorData.detail)) || "Validation error",
155
+ errorData
156
+ );
146
157
  } else {
147
158
  const errorData = await response.json().catch(() => ({}));
148
159
  throw new NebulaException(errorData.message || `API error: ${response.status}`, response.status, errorData);
@@ -161,48 +172,85 @@ var NebulaClient = class {
161
172
  throw new NebulaClientException(`Request failed: ${String(error)}`);
162
173
  }
163
174
  }
164
- // Cluster Management Methods
165
- /** Create a new cluster */
166
- async createCluster(name, description, metadata) {
167
- const data = { name };
168
- if (description) data.description = description;
169
- if (metadata) data.metadata = metadata;
175
+ // Collection Management Methods
176
+ /** Create a new collection */
177
+ async createCollection(options) {
178
+ const data = { name: options.name };
179
+ if (options.description) data.description = options.description;
180
+ if (options.metadata) data.metadata = options.metadata;
170
181
  const response = await this._makeRequest("POST", "/v1/collections", data);
171
182
  const result = response.results || response;
172
- return this._clusterFromDict(result);
183
+ return this._collectionFromDict(result);
173
184
  }
174
- /** Get a specific cluster by ID */
175
- async getCluster(clusterId) {
176
- const response = await this._makeRequest("GET", `/v1/collections/${clusterId}`);
185
+ /** Get a specific collection by ID */
186
+ async getCollection(collectionId) {
187
+ const response = await this._makeRequest("GET", `/v1/collections/${collectionId}`);
177
188
  const result = response.results || response;
178
- return this._clusterFromDict(result);
189
+ return this._collectionFromDict(result);
179
190
  }
180
- /** Get a specific cluster by name */
181
- async getClusterByName(name) {
191
+ /** Get a specific collection by name */
192
+ async getCollectionByName(name) {
182
193
  const response = await this._makeRequest("GET", `/v1/collections/name/${name}`);
183
194
  const result = response.results || response;
184
- return this._clusterFromDict(result);
195
+ return this._collectionFromDict(result);
185
196
  }
186
- /** Get all clusters */
187
- async listClusters(limit = 100, offset = 0) {
188
- const params = { limit, offset };
197
+ /** Get all collections */
198
+ async listCollections(options) {
199
+ const params = {
200
+ limit: options?.limit ?? 100,
201
+ offset: options?.offset ?? 0
202
+ };
189
203
  const response = await this._makeRequest("GET", "/v1/collections", void 0, params);
190
- let clusters;
204
+ let collections;
191
205
  if (response.results) {
192
- clusters = response.results;
206
+ collections = response.results;
193
207
  } else if (Array.isArray(response)) {
194
- clusters = response;
208
+ collections = response;
195
209
  } else {
196
- clusters = [response];
210
+ collections = [response];
197
211
  }
198
- return clusters.map((cluster) => this._clusterFromDict(cluster));
212
+ return collections.map((collection) => this._collectionFromDict(collection));
199
213
  }
200
214
  // Conversations Methods
201
- /** List conversations for the authenticated user */
202
- async listConversations(limit = 100, offset = 0, cluster_ids) {
203
- const params = { limit, offset };
204
- if (cluster_ids && cluster_ids.length > 0) {
205
- params.collection_ids = cluster_ids;
215
+ /**
216
+ * List conversations for the authenticated user with optional metadata filtering
217
+ *
218
+ * @param options - Configuration for listing conversations
219
+ * @param options.limit - Maximum number of conversations to return (default: 100)
220
+ * @param options.offset - Number of conversations to skip for pagination (default: 0)
221
+ * @param options.collection_ids - Optional list of collection IDs to filter conversations by
222
+ * @param options.metadata_filters - Optional metadata filters using MongoDB-like operators.
223
+ * Supported operators: $eq, $ne, $in, $nin, $exists, $and, $or
224
+ *
225
+ * @returns Promise resolving to array of conversation objects with fields: id, created_at, user_id, name, collection_ids
226
+ *
227
+ * @example
228
+ * // Get all playground conversations
229
+ * const conversations = await client.listConversations({
230
+ * collection_ids: ['collection-id'],
231
+ * metadata_filters: {
232
+ * 'metadata.playground': { $eq: true }
233
+ * }
234
+ * });
235
+ *
236
+ * @example
237
+ * // Filter by session ID
238
+ * const conversations = await client.listConversations({
239
+ * metadata_filters: {
240
+ * 'metadata.session_id': { $eq: 'session-123' }
241
+ * }
242
+ * });
243
+ */
244
+ async listConversations(options) {
245
+ const params = {
246
+ limit: options?.limit ?? 100,
247
+ offset: options?.offset ?? 0
248
+ };
249
+ if (options?.collection_ids && options.collection_ids.length > 0) {
250
+ params.collection_ids = options.collection_ids;
251
+ }
252
+ if (options?.metadata_filters) {
253
+ params.metadata_filters = JSON.stringify(options.metadata_filters);
206
254
  }
207
255
  const response = await this._makeRequest("GET", "/v1/conversations", void 0, params);
208
256
  let conversations;
@@ -283,30 +331,30 @@ var NebulaClient = class {
283
331
  content: text,
284
332
  metadata: combinedMetadata,
285
333
  created_at: msgResp.created_at,
286
- cluster_ids: msgResp.collection_ids || []
334
+ collection_ids: msgResp.collection_ids || []
287
335
  };
288
336
  });
289
337
  }
290
- /** Update a cluster */
291
- async updateCluster(clusterId, name, description, metadata) {
338
+ /** Update a collection */
339
+ async updateCollection(options) {
292
340
  const data = {};
293
- if (name !== void 0) data.name = name;
294
- if (description !== void 0) data.description = description;
295
- if (metadata !== void 0) data.metadata = metadata;
296
- const response = await this._makeRequest("POST", `/v1/collections/${clusterId}`, data);
341
+ if (options.name !== void 0) data.name = options.name;
342
+ if (options.description !== void 0) data.description = options.description;
343
+ if (options.metadata !== void 0) data.metadata = options.metadata;
344
+ const response = await this._makeRequest("POST", `/v1/collections/${options.collectionId}`, data);
297
345
  const result = response.results || response;
298
- return this._clusterFromDict(result);
346
+ return this._collectionFromDict(result);
299
347
  }
300
- /** Delete a cluster */
301
- async deleteCluster(clusterId) {
302
- await this._makeRequest("DELETE", `/v1/collections/${clusterId}`);
348
+ /** Delete a collection */
349
+ async deleteCollection(collectionId) {
350
+ await this._makeRequest("DELETE", `/v1/collections/${collectionId}`);
303
351
  return true;
304
352
  }
305
353
  // Memory Management Methods
306
354
  /**
307
- * Legacy convenience: store raw text content into a cluster as a document
355
+ * Legacy convenience: store raw text content into a collection as a document
308
356
  */
309
- async store(content, clusterId, metadata = {}) {
357
+ async store(content, collectionId, metadata = {}) {
310
358
  const docMetadata = {
311
359
  ...metadata,
312
360
  memory_type: "memory",
@@ -315,10 +363,10 @@ var NebulaClient = class {
315
363
  const data = {
316
364
  metadata: JSON.stringify(docMetadata),
317
365
  ingestion_mode: "fast",
318
- collection_ids: JSON.stringify([clusterId]),
366
+ collection_ids: JSON.stringify([collectionId]),
319
367
  raw_text: String(content || "")
320
368
  };
321
- const url = `${this.baseUrl}/v1/documents`;
369
+ const url = `${this.baseUrl}/v1/memories`;
322
370
  const headers = this._buildAuthHeaders(false);
323
371
  const response = await fetch(url, {
324
372
  method: "POST",
@@ -328,72 +376,117 @@ var NebulaClient = class {
328
376
  if (!response.ok) {
329
377
  const errorData = await response.json().catch(() => ({}));
330
378
  throw new NebulaException(
331
- errorData.message || `Failed to create document: ${response.status}`,
379
+ errorData.message || `Failed to create engram: ${response.status}`,
332
380
  response.status,
333
381
  errorData
334
382
  );
335
383
  }
336
384
  const respData = await response.json();
337
- const id = respData?.results?.document_id || respData?.results?.id || respData?.id || "";
385
+ const id = respData?.results?.engram_id || respData?.results?.id || respData?.id || "";
338
386
  const result = {
339
387
  id: String(id),
340
388
  content: String(content || ""),
341
389
  metadata: docMetadata,
342
- cluster_ids: [clusterId],
390
+ collection_ids: [collectionId],
343
391
  created_at: docMetadata.timestamp,
344
392
  updated_at: docMetadata.timestamp
345
393
  };
346
394
  return result;
347
395
  }
348
- /** Store a single memory */
349
- async storeMemory(memory) {
396
+ /**
397
+ * Store a single memory using the unified engrams API.
398
+ *
399
+ * Automatically infers memory type:
400
+ * - If role is present, creates a conversation
401
+ * - Otherwise, creates a document
402
+ */
403
+ async storeMemory(memory, name) {
350
404
  let mem;
351
- if ("cluster_id" in memory) {
405
+ if ("collection_id" in memory) {
352
406
  mem = memory;
353
407
  } else {
354
408
  mem = {
355
- cluster_id: memory.cluster_id,
356
- content: memory.content,
409
+ collection_id: memory.collection_id,
410
+ content: memory.content || "",
357
411
  role: memory.role,
358
- parent_id: memory.parent_id,
412
+ memory_id: memory.memory_id,
359
413
  metadata: memory.metadata || {}
360
414
  };
361
415
  }
362
- if (mem.role) {
363
- let convId = mem.parent_id;
364
- if (!convId) {
365
- const created = await this._makeRequest("POST", "/v1/conversations", {});
366
- const conv = created.results || created;
367
- convId = conv.id;
416
+ if (mem.memory_id) {
417
+ return await this._appendToMemory(mem.memory_id, mem);
418
+ }
419
+ const memoryType = mem.role ? "conversation" : "document";
420
+ if (memoryType === "conversation") {
421
+ const docMetadata2 = { ...mem.metadata };
422
+ const data2 = {
423
+ engram_type: "conversation",
424
+ name: name || "Conversation",
425
+ metadata: JSON.stringify(docMetadata2),
426
+ collection_ids: JSON.stringify([mem.collection_id])
427
+ };
428
+ const url2 = `${this.baseUrl}/v1/memories`;
429
+ const headers2 = this._buildAuthHeaders(false);
430
+ const response2 = await fetch(url2, {
431
+ method: "POST",
432
+ headers: headers2,
433
+ body: this._formDataFromObject(data2)
434
+ });
435
+ if (!response2.ok) {
436
+ const errorData = await response2.json().catch(() => ({}));
437
+ throw new NebulaException(
438
+ errorData.message || `Failed to create conversation: ${response2.status}`,
439
+ response2.status,
440
+ errorData
441
+ );
442
+ }
443
+ const respData2 = await response2.json();
444
+ if (respData2.results) {
445
+ const convId = respData2.results.engram_id || respData2.results.id;
368
446
  if (!convId) {
369
447
  throw new NebulaClientException("Failed to create conversation: no id returned");
370
448
  }
449
+ if (mem.content && mem.role) {
450
+ const appendMem = {
451
+ collection_id: mem.collection_id,
452
+ content: [
453
+ {
454
+ content: String(mem.content),
455
+ role: mem.role,
456
+ metadata: mem.metadata,
457
+ ...typeof mem.authority === "number" ? { authority: Number(mem.authority) } : {}
458
+ }
459
+ ],
460
+ memory_id: convId,
461
+ metadata: {}
462
+ };
463
+ await this._appendToMemory(convId, appendMem);
464
+ }
465
+ return String(convId);
371
466
  }
372
- const payload = {
373
- messages: [
374
- {
375
- content: String(mem.content),
376
- role: mem.role,
377
- metadata: mem.metadata
378
- }
379
- ],
380
- collection_id: mem.cluster_id
381
- };
382
- await this._makeRequest("POST", `/v1/conversations/${convId}/messages`, payload);
383
- return String(convId);
467
+ throw new NebulaClientException("Failed to create conversation: invalid response format");
384
468
  }
385
469
  const contentText = String(mem.content || "");
470
+ if (!contentText) {
471
+ throw new NebulaClientException("Content is required for document memories");
472
+ }
386
473
  const contentHash = await this._sha256(contentText);
387
474
  const docMetadata = { ...mem.metadata };
388
475
  docMetadata.memory_type = "memory";
389
476
  docMetadata.content_hash = contentHash;
477
+ if (typeof mem.authority === "number") {
478
+ const v = Number(mem.authority);
479
+ if (!Number.isNaN(v) && v >= 0 && v <= 1) {
480
+ docMetadata.authority = v;
481
+ }
482
+ }
390
483
  const data = {
391
484
  metadata: JSON.stringify(docMetadata),
392
485
  ingestion_mode: "fast",
393
- collection_ids: JSON.stringify([mem.cluster_id]),
486
+ collection_ids: JSON.stringify([mem.collection_id]),
394
487
  raw_text: contentText
395
488
  };
396
- const url = `${this.baseUrl}/v1/documents`;
489
+ const url = `${this.baseUrl}/v1/memories`;
397
490
  const headers = this._buildAuthHeaders(false);
398
491
  const response = await fetch(url, {
399
492
  method: "POST",
@@ -403,26 +496,67 @@ var NebulaClient = class {
403
496
  if (!response.ok) {
404
497
  const errorData = await response.json().catch(() => ({}));
405
498
  throw new NebulaException(
406
- errorData.message || `Failed to create document: ${response.status}`,
499
+ errorData.message || `Failed to create engram: ${response.status}`,
407
500
  response.status,
408
501
  errorData
409
502
  );
410
503
  }
411
504
  const respData = await response.json();
412
505
  if (respData.results) {
413
- if (respData.results.document_id) return String(respData.results.document_id);
506
+ if (respData.results.engram_id) return String(respData.results.engram_id);
414
507
  if (respData.results.id) return String(respData.results.id);
415
508
  }
416
509
  return "";
417
510
  }
418
- /** Store multiple memories */
511
+ /**
512
+ * Internal method to append content to an existing engram
513
+ *
514
+ * @throws NebulaNotFoundException if engram_id doesn't exist
515
+ */
516
+ async _appendToMemory(memoryId, memory) {
517
+ const collectionId = memory.collection_id;
518
+ const content = memory.content;
519
+ const metadata = memory.metadata;
520
+ if (!collectionId) {
521
+ throw new NebulaClientException("collection_id is required");
522
+ }
523
+ const payload = {
524
+ collection_id: collectionId
525
+ };
526
+ if (Array.isArray(content)) {
527
+ if (content.length > 0 && typeof content[0] === "object" && "content" in content[0]) {
528
+ payload.messages = content;
529
+ } else {
530
+ payload.chunks = content;
531
+ }
532
+ } else if (typeof content === "string") {
533
+ payload.raw_text = content;
534
+ } else {
535
+ throw new NebulaClientException(
536
+ "content must be a string, array of strings, or array of message objects"
537
+ );
538
+ }
539
+ if (metadata) {
540
+ payload.metadata = metadata;
541
+ }
542
+ try {
543
+ await this._makeRequest("POST", `/v1/memories/${memoryId}/append`, payload);
544
+ return memoryId;
545
+ } catch (error) {
546
+ if (error instanceof NebulaException && error.statusCode === 404) {
547
+ throw new NebulaNotFoundException(memoryId, "Memory");
548
+ }
549
+ throw error;
550
+ }
551
+ }
552
+ /** Store multiple memories using the unified engrams API */
419
553
  async storeMemories(memories) {
420
554
  const results = [];
421
555
  const convGroups = {};
422
556
  const others = [];
423
557
  for (const m of memories) {
424
558
  if (m.role) {
425
- const key = m.parent_id || `__new__::${m.cluster_id}`;
559
+ const key = m.memory_id || `__new__::${m.collection_id}`;
426
560
  if (!convGroups[key]) convGroups[key] = [];
427
561
  convGroups[key].push(m);
428
562
  } else {
@@ -430,15 +564,19 @@ var NebulaClient = class {
430
564
  }
431
565
  }
432
566
  for (const [key, group] of Object.entries(convGroups)) {
433
- const clusterId = group[0].cluster_id;
567
+ const collectionId = group[0].collection_id;
434
568
  let convId;
435
569
  if (key.startsWith("__new__::")) {
436
- const created = await this._makeRequest("POST", "/v1/conversations", {});
437
- const conv = created.results || created;
438
- convId = conv.id;
439
- if (!convId) {
440
- throw new NebulaClientException("Failed to create conversation: no id returned");
441
- }
570
+ convId = await this.storeMemory(
571
+ {
572
+ collection_id: collectionId,
573
+ content: "",
574
+ role: "assistant",
575
+ // Placeholder role to infer conversation type
576
+ metadata: {}
577
+ },
578
+ "Conversation"
579
+ );
442
580
  } else {
443
581
  convId = key;
444
582
  }
@@ -447,8 +585,13 @@ var NebulaClient = class {
447
585
  role: m.role,
448
586
  metadata: m.metadata || {}
449
587
  }));
450
- const payload = { messages, collection_id: clusterId };
451
- await this._makeRequest("POST", `/v1/conversations/${convId}/messages`, payload);
588
+ const appendMem = {
589
+ collection_id: collectionId,
590
+ content: messages,
591
+ memory_id: convId,
592
+ metadata: {}
593
+ };
594
+ await this._appendToMemory(convId, appendMem);
452
595
  results.push(...Array(group.length).fill(String(convId)));
453
596
  }
454
597
  for (const m of others) {
@@ -456,18 +599,116 @@ var NebulaClient = class {
456
599
  }
457
600
  return results;
458
601
  }
459
- /** Delete a specific memory */
460
- async delete(memoryId) {
602
+ /** Delete one or more memories */
603
+ async delete(memoryIds) {
461
604
  try {
462
- await this._makeRequest("DELETE", `/v1/documents/${memoryId}`);
463
- return true;
605
+ console.log("[SDK] delete() called with:", { memoryIds, type: typeof memoryIds, isArray: Array.isArray(memoryIds) });
606
+ if (typeof memoryIds === "string") {
607
+ console.log("[SDK] Single deletion path for ID:", memoryIds);
608
+ try {
609
+ await this._makeRequest("DELETE", `/v1/memories/${memoryIds}`);
610
+ return true;
611
+ } catch {
612
+ try {
613
+ console.log("[SDK] Falling back to POST /v1/memories/delete with single ID");
614
+ const response = await this._makeRequest("POST", "/v1/memories/delete", memoryIds);
615
+ return typeof response === "object" && response.success !== void 0 ? response.success : true;
616
+ } catch (error) {
617
+ throw error;
618
+ }
619
+ }
620
+ } else {
621
+ console.log("[SDK] Batch deletion path for IDs:", memoryIds);
622
+ console.log("[SDK] Sending POST request with body:", memoryIds);
623
+ const response = await this._makeRequest("POST", "/v1/memories/delete", memoryIds);
624
+ console.log("[SDK] Batch deletion response:", response);
625
+ return response;
626
+ }
464
627
  } catch (error) {
628
+ console.error("[SDK] Delete error:", error);
465
629
  if (error instanceof Error) {
466
630
  throw error;
467
631
  }
468
632
  throw new NebulaClientException(`Unknown error: ${String(error)}`);
469
633
  }
470
634
  }
635
+ /** Delete a specific chunk or message within a memory */
636
+ async deleteChunk(chunkId) {
637
+ try {
638
+ await this._makeRequest("DELETE", `/v1/chunks/${chunkId}`);
639
+ return true;
640
+ } catch (error) {
641
+ if (error instanceof NebulaException && error.statusCode === 404) {
642
+ throw new NebulaNotFoundException(chunkId, "Chunk");
643
+ }
644
+ throw error;
645
+ }
646
+ }
647
+ /** Update a specific chunk or message within a memory */
648
+ async updateChunk(chunkId, content, metadata) {
649
+ const payload = { content };
650
+ if (metadata !== void 0) {
651
+ payload.metadata = metadata;
652
+ }
653
+ try {
654
+ await this._makeRequest("PATCH", `/v1/chunks/${chunkId}`, payload);
655
+ return true;
656
+ } catch (error) {
657
+ if (error instanceof NebulaException && error.statusCode === 404) {
658
+ throw new NebulaNotFoundException(chunkId, "Chunk");
659
+ }
660
+ throw error;
661
+ }
662
+ }
663
+ /**
664
+ * Update memory-level properties including name, metadata, and collection associations.
665
+ *
666
+ * This method allows updating properties of an entire memory (document or conversation)
667
+ * without modifying its content. For updating individual chunks or messages within a memory,
668
+ * use updateChunk(). For updating content, use storeMemory() to append.
669
+ *
670
+ * @param options - Update configuration
671
+ * @param options.memoryId - The ID of the memory to update
672
+ * @param options.name - New name for the memory (useful for conversations and documents)
673
+ * @param options.metadata - Metadata to set. By default, replaces existing metadata.
674
+ * Set mergeMetadata=true to merge with existing metadata instead.
675
+ * @param options.collectionIds - New collection associations. Must specify at least one valid collection.
676
+ * @param options.mergeMetadata - If true, merges provided metadata with existing metadata.
677
+ * If false (default), replaces existing metadata entirely.
678
+ *
679
+ * @returns Promise resolving to true if successful
680
+ *
681
+ * @throws NebulaNotFoundException if memory_id doesn't exist
682
+ * @throws NebulaValidationException if validation fails (e.g., no fields provided)
683
+ * @throws NebulaAuthenticationException if user doesn't have permission to update this memory
684
+ */
685
+ async updateMemory(options) {
686
+ const payload = {};
687
+ if (options.name !== void 0) {
688
+ payload.name = options.name;
689
+ }
690
+ if (options.metadata !== void 0) {
691
+ payload.metadata = options.metadata;
692
+ payload.merge_metadata = options.mergeMetadata ?? false;
693
+ }
694
+ if (options.collectionIds !== void 0) {
695
+ payload.collection_ids = options.collectionIds;
696
+ }
697
+ if (Object.keys(payload).length === 0) {
698
+ throw new NebulaValidationException(
699
+ "At least one field (name, metadata, or collectionIds) must be provided to update"
700
+ );
701
+ }
702
+ try {
703
+ await this._makeRequest("PATCH", `/v1/memories/${options.memoryId}`, payload);
704
+ return true;
705
+ } catch (error) {
706
+ if (error instanceof NebulaException && error.statusCode === 404) {
707
+ throw new NebulaNotFoundException(options.memoryId, "Memory");
708
+ }
709
+ throw error;
710
+ }
711
+ }
471
712
  /** Delete a conversation and all its messages */
472
713
  async deleteConversation(conversationId) {
473
714
  try {
@@ -480,14 +721,53 @@ var NebulaClient = class {
480
721
  throw new NebulaClientException(`Unknown error: ${String(error)}`);
481
722
  }
482
723
  }
483
- /** Get all memories from specific clusters */
484
- async listMemories(clusterIds, limit = 100, offset = 0) {
485
- const ids = Array.isArray(clusterIds) ? clusterIds : [clusterIds];
724
+ /**
725
+ * Get all memories from specific collections with optional metadata filtering
726
+ *
727
+ * @param options - Configuration for listing memories
728
+ * @param options.collection_ids - One or more collection IDs to retrieve memories from
729
+ * @param options.limit - Maximum number of memories to return (default: 100)
730
+ * @param options.offset - Number of memories to skip for pagination (default: 0)
731
+ * @param options.metadata_filters - Optional metadata filters using MongoDB-like operators.
732
+ * Supported operators: $eq, $ne, $in, $nin, $exists, $and, $or
733
+ *
734
+ * @returns Promise resolving to array of MemoryResponse objects
735
+ *
736
+ * @example
737
+ * // Get all playground memories excluding conversations
738
+ * const memories = await client.listMemories({
739
+ * collection_ids: ['collection-id'],
740
+ * metadata_filters: {
741
+ * 'metadata.content_type': { $ne: 'conversation' }
742
+ * }
743
+ * });
744
+ *
745
+ * @example
746
+ * // Complex filter with multiple conditions
747
+ * const memories = await client.listMemories({
748
+ * collection_ids: ['collection-id'],
749
+ * metadata_filters: {
750
+ * $and: [
751
+ * { 'metadata.playground': { $eq: true } },
752
+ * { 'metadata.session_id': { $exists: true } }
753
+ * ]
754
+ * }
755
+ * });
756
+ */
757
+ async listMemories(options) {
758
+ const ids = Array.isArray(options.collection_ids) ? options.collection_ids : [options.collection_ids];
486
759
  if (!ids.length) {
487
- throw new NebulaClientException("cluster_ids must be provided to list_memories().");
760
+ throw new NebulaClientException("collection_ids must be provided to list_memories().");
761
+ }
762
+ const params = {
763
+ limit: options.limit ?? 100,
764
+ offset: options.offset ?? 0,
765
+ collection_ids: ids
766
+ };
767
+ if (options.metadata_filters) {
768
+ params.metadata_filters = JSON.stringify(options.metadata_filters);
488
769
  }
489
- const params = { limit, offset, collection_ids: ids };
490
- const response = await this._makeRequest("GET", "/v1/documents", void 0, params);
770
+ const response = await this._makeRequest("GET", "/v1/memories", void 0, params);
491
771
  let documents;
492
772
  if (response.results) {
493
773
  documents = response.results;
@@ -498,9 +778,9 @@ var NebulaClient = class {
498
778
  }
499
779
  return documents.map((doc) => this._memoryResponseFromDict(doc, ids));
500
780
  }
501
- /** Get a specific memory by ID */
781
+ /** Get a specific memory by engram ID */
502
782
  async getMemory(memoryId) {
503
- const response = await this._makeRequest("GET", `/v1/documents/${memoryId}`);
783
+ const response = await this._makeRequest("GET", `/v1/memories/${memoryId}`);
504
784
  const content = response.text || response.content;
505
785
  const chunks = Array.isArray(response.chunks) ? response.chunks : void 0;
506
786
  const memoryData = {
@@ -513,32 +793,123 @@ var NebulaClient = class {
513
793
  return this._memoryResponseFromDict(memoryData, []);
514
794
  }
515
795
  // Search Methods
516
- /** Search within specific clusters */
517
- async search(query, clusters, limitOrOptions, retrievalType = "advanced" /* ADVANCED */, filters, searchSettings) {
518
- const clusterIds = Array.isArray(clusters) ? clusters : [clusters];
519
- if (!clusterIds.length) {
520
- throw new NebulaClientException("cluster_ids must be provided to search().");
521
- }
522
- let limit = 10;
523
- if (typeof limitOrOptions === "number") {
524
- limit = limitOrOptions;
525
- } else if (typeof limitOrOptions === "object" && limitOrOptions) {
526
- if (typeof limitOrOptions.limit === "number") limit = limitOrOptions.limit;
527
- }
528
- if (typeof retrievalType === "string") {
529
- retrievalType = RetrievalType[retrievalType.toUpperCase()] || "advanced" /* ADVANCED */;
530
- }
531
- const effectiveSettings = { ...searchSettings };
796
+ /**
797
+ * Search within specific collections with optional metadata filtering.
798
+ *
799
+ * @param options - Search configuration
800
+ * @param options.query - Search query string
801
+ * @param options.collection_ids - One or more collection IDs to search within
802
+ * @param options.limit - Maximum number of results to return (default: 10)
803
+ * @param options.retrieval_type - Retrieval strategy (default: ADVANCED)
804
+ * @param options.filters - Optional filters to apply to the search. Supports comprehensive metadata filtering
805
+ * with MongoDB-like operators for both vector/chunk search and graph search.
806
+ * @param options.searchSettings - Optional search configuration
807
+ *
808
+ * @returns Promise resolving to array of SearchResult objects containing both vector/chunk and graph search results
809
+ *
810
+ * @example
811
+ * // Basic equality filter
812
+ * await client.search({
813
+ * query: "machine learning",
814
+ * collection_ids: ["research-collection"],
815
+ * filters: {
816
+ * "metadata.category": { $eq: "research" },
817
+ * "metadata.verified": true // Shorthand for $eq
818
+ * }
819
+ * });
820
+ *
821
+ * @example
822
+ * // Numeric comparisons
823
+ * await client.search({
824
+ * query: "high priority",
825
+ * collection_ids: ["tasks"],
826
+ * filters: {
827
+ * "metadata.priority": { $gte: 8 },
828
+ * "metadata.score": { $lt: 100 }
829
+ * }
830
+ * });
831
+ *
832
+ * @example
833
+ * // String matching
834
+ * await client.search({
835
+ * query: "employees",
836
+ * collection_ids: ["team"],
837
+ * filters: {
838
+ * "metadata.email": { $ilike: "%@company.com" } // Case-insensitive
839
+ * }
840
+ * });
841
+ *
842
+ * @example
843
+ * // Array operations
844
+ * await client.search({
845
+ * query: "developers",
846
+ * collection_ids: ["team"],
847
+ * filters: {
848
+ * "metadata.skills": { $overlap: ["python", "typescript"] } // Has any
849
+ * }
850
+ * });
851
+ *
852
+ * @example
853
+ * // Nested paths
854
+ * await client.search({
855
+ * query: "users",
856
+ * collection_ids: ["profiles"],
857
+ * filters: {
858
+ * "metadata.user.preferences.theme": { $eq: "dark" }
859
+ * }
860
+ * });
861
+ *
862
+ * @example
863
+ * // Complex logical combinations
864
+ * await client.search({
865
+ * query: "candidates",
866
+ * collection_ids: ["hiring"],
867
+ * filters: {
868
+ * $and: [
869
+ * { "metadata.verified": true },
870
+ * { "metadata.level": { $gte: 5 } },
871
+ * {
872
+ * $or: [
873
+ * { "metadata.skills": { $overlap: ["python", "go"] } },
874
+ * { "metadata.years_experience": { $gte: 8 } }
875
+ * ]
876
+ * }
877
+ * ]
878
+ * }
879
+ * });
880
+ *
881
+ * @remarks
882
+ * Supported Operators:
883
+ * - Comparison: $eq, $ne, $lt, $lte, $gt, $gte
884
+ * - String: $like (case-sensitive), $ilike (case-insensitive)
885
+ * - Array: $in, $nin, $overlap, $contains
886
+ * - JSONB: $json_contains
887
+ * - Logical: $and, $or
888
+ *
889
+ * For comprehensive filtering documentation, see the Metadata Filtering Guide:
890
+ * https://docs.nebulacloud.app/guides/metadata-filtering
891
+ */
892
+ async search(options) {
893
+ const collectionIds = Array.isArray(options.collection_ids) ? options.collection_ids : [options.collection_ids];
894
+ const validCollectionIds = collectionIds.filter((id) => id && id.trim() !== "");
895
+ if (!validCollectionIds.length) {
896
+ throw new NebulaClientException("collection_ids must be provided to search().");
897
+ }
898
+ const limit = options.limit ?? 10;
899
+ const searchMode = options.search_mode ?? "super";
900
+ const effectiveSettings = {
901
+ ...options.searchSettings
902
+ };
532
903
  effectiveSettings.limit = limit;
533
904
  const userFilters = { ...effectiveSettings.filters };
534
- if (filters) {
535
- Object.assign(userFilters, filters);
905
+ if (options.filters) {
906
+ Object.assign(userFilters, options.filters);
536
907
  }
537
- userFilters.collection_ids = { $overlap: clusterIds };
908
+ userFilters.collection_ids = { $overlap: validCollectionIds };
538
909
  effectiveSettings.filters = userFilters;
539
910
  const data = {
540
- query,
541
- search_mode: "custom",
911
+ query: options.query,
912
+ search_mode: searchMode,
542
913
  search_settings: effectiveSettings
543
914
  };
544
915
  const response = await this._makeRequest("POST", "/v1/retrieval/search", data);
@@ -558,28 +929,33 @@ var NebulaClient = class {
558
929
  /**
559
930
  * Legacy wrapper: store a two-message conversation turn as a document
560
931
  */
561
- async storeConversation(userMessage, assistantMessage, clusterId, sessionId) {
932
+ async storeConversation(userMessage, assistantMessage, collectionId, sessionId) {
562
933
  const content = `User: ${String(userMessage || "")}
563
934
  Assistant: ${String(assistantMessage || "")}`;
564
935
  const metadata = { session_id: sessionId, content_type: "conversation" };
565
- return this.store(content, clusterId, metadata);
936
+ return this.store(content, collectionId, metadata);
566
937
  }
567
938
  /**
568
939
  * Legacy wrapper: search conversations optionally scoped by session
569
940
  */
570
- async searchConversations(query, clusterId, sessionId, includeAllSessions = true) {
941
+ async searchConversations(query, collectionId, sessionId, includeAllSessions = true) {
571
942
  const filters = { "metadata.content_type": "conversation" };
572
943
  if (sessionId && !includeAllSessions) {
573
944
  filters["metadata.session_id"] = sessionId;
574
945
  }
575
- return this.search(query, [clusterId], { limit: 10 }, "advanced" /* ADVANCED */, filters);
946
+ return this.search({
947
+ query,
948
+ collection_ids: [collectionId],
949
+ limit: 10,
950
+ filters
951
+ });
576
952
  }
577
953
  // Health Check
578
954
  async healthCheck() {
579
955
  return this._makeRequest("GET", "/health");
580
956
  }
581
957
  // Helpers
582
- _clusterFromDict(data) {
958
+ _collectionFromDict(data) {
583
959
  let createdAt;
584
960
  if (data.created_at) {
585
961
  createdAt = typeof data.created_at === "string" ? data.created_at : data.created_at.toISOString();
@@ -588,29 +964,29 @@ Assistant: ${String(assistantMessage || "")}`;
588
964
  if (data.updated_at) {
589
965
  updatedAt = typeof data.updated_at === "string" ? data.updated_at : data.updated_at.toISOString();
590
966
  }
591
- const clusterId = String(data.id || "");
592
- const clusterName = data.name || "";
593
- const clusterDescription = data.description;
594
- const clusterOwnerId = data.owner_id ? String(data.owner_id) : void 0;
967
+ const collectionId = String(data.id || "");
968
+ const collectionName = data.name || "";
969
+ const collectionDescription = data.description;
970
+ const collectionOwnerId = data.owner_id ? String(data.owner_id) : void 0;
595
971
  const memoryCount = data.document_count || 0;
596
972
  const metadata = {
597
- graph_cluster_status: data.graph_cluster_status || "",
973
+ graph_collection_status: data.graph_collection_status || "",
598
974
  graph_sync_status: data.graph_sync_status || "",
599
975
  user_count: data.user_count || 0,
600
976
  document_count: data.document_count || 0
601
977
  };
602
978
  return {
603
- id: clusterId,
604
- name: clusterName,
605
- description: clusterDescription,
979
+ id: collectionId,
980
+ name: collectionName,
981
+ description: collectionDescription,
606
982
  metadata,
607
983
  created_at: createdAt,
608
984
  updated_at: updatedAt,
609
985
  memory_count: memoryCount,
610
- owner_id: clusterOwnerId
986
+ owner_id: collectionOwnerId
611
987
  };
612
988
  }
613
- _memoryResponseFromDict(data, clusterIds) {
989
+ _memoryResponseFromDict(data, collectionIds) {
614
990
  let createdAt;
615
991
  if (data.created_at) {
616
992
  createdAt = typeof data.created_at === "string" ? data.created_at : data.created_at.toISOString();
@@ -619,7 +995,7 @@ Assistant: ${String(assistantMessage || "")}`;
619
995
  if (data.updated_at) {
620
996
  updatedAt = typeof data.updated_at === "string" ? data.updated_at : data.updated_at.toISOString();
621
997
  }
622
- const memoryId = String(data.id || "");
998
+ const engramId = String(data.id || "");
623
999
  const content = data.content || data.text;
624
1000
  let chunks;
625
1001
  if (data.chunks && Array.isArray(data.chunks)) {
@@ -630,12 +1006,12 @@ Assistant: ${String(assistantMessage || "")}`;
630
1006
  }
631
1007
  }
632
1008
  const metadata = { ...data.metadata };
633
- if (data.document_id) {
634
- metadata.document_id = data.document_id;
1009
+ if (data.engram_id) {
1010
+ metadata.engram_id = data.engram_id;
635
1011
  }
636
- let finalId = memoryId;
637
- if (data.document_id && !memoryId) {
638
- finalId = data.document_id;
1012
+ let finalId = engramId;
1013
+ if (data.engram_id && !engramId) {
1014
+ finalId = data.engram_id;
639
1015
  }
640
1016
  if (data.document_metadata) {
641
1017
  Object.assign(metadata, data.document_metadata);
@@ -645,7 +1021,7 @@ Assistant: ${String(assistantMessage || "")}`;
645
1021
  content,
646
1022
  chunks,
647
1023
  metadata,
648
- cluster_ids: data.collection_ids || clusterIds,
1024
+ collection_ids: data.collection_ids || collectionIds,
649
1025
  created_at: createdAt,
650
1026
  updated_at: updatedAt
651
1027
  };
@@ -668,6 +1044,23 @@ Assistant: ${String(assistantMessage || "")}`;
668
1044
  const score = data.score !== void 0 ? Number(data.score) : 0;
669
1045
  const metadata = data.metadata || {};
670
1046
  const chunkIds = Array.isArray(data.chunk_ids) ? data.chunk_ids : void 0;
1047
+ let timestamp;
1048
+ if (data.timestamp) {
1049
+ if (typeof data.timestamp === "string") {
1050
+ timestamp = data.timestamp;
1051
+ } else if (data.timestamp instanceof Date) {
1052
+ timestamp = data.timestamp.toISOString();
1053
+ } else {
1054
+ const parsed = new Date(data.timestamp);
1055
+ if (!Number.isNaN(parsed.valueOf())) {
1056
+ timestamp = parsed.toISOString();
1057
+ }
1058
+ }
1059
+ }
1060
+ const displayName = typeof data.display_name === "string" ? data.display_name : void 0;
1061
+ const sourceRole = typeof data.source_role === "string" ? data.source_role : void 0;
1062
+ const engramId = data.engram_id ? String(data.engram_id) : void 0;
1063
+ const ownerId = data.owner_id ? String(data.owner_id) : void 0;
671
1064
  let entity;
672
1065
  let rel;
673
1066
  let comm;
@@ -707,7 +1100,12 @@ Assistant: ${String(assistantMessage || "")}`;
707
1100
  graph_entity: entity,
708
1101
  graph_relationship: rel,
709
1102
  graph_community: comm,
710
- chunk_ids: chunkIds
1103
+ chunk_ids: chunkIds,
1104
+ timestamp,
1105
+ display_name: displayName,
1106
+ source_role: sourceRole,
1107
+ engram_id: engramId,
1108
+ owner_id: ownerId
711
1109
  };
712
1110
  }
713
1111
  async _sha256(message) {
@@ -730,10 +1128,10 @@ exports.GraphSearchResultType = GraphSearchResultType;
730
1128
  exports.NebulaAuthenticationException = NebulaAuthenticationException;
731
1129
  exports.NebulaClient = NebulaClient;
732
1130
  exports.NebulaClientException = NebulaClientException;
733
- exports.NebulaClusterNotFoundException = NebulaClusterNotFoundException;
1131
+ exports.NebulaCollectionNotFoundException = NebulaCollectionNotFoundException;
734
1132
  exports.NebulaException = NebulaException;
1133
+ exports.NebulaNotFoundException = NebulaNotFoundException;
735
1134
  exports.NebulaRateLimitException = NebulaRateLimitException;
736
1135
  exports.NebulaValidationException = NebulaValidationException;
737
- exports.RetrievalType = RetrievalType;
738
1136
  //# sourceMappingURL=index.js.map
739
1137
  //# sourceMappingURL=index.js.map