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