@reverbia/sdk 1.0.0-next.20251212012743 → 1.0.0-next.20251215143604

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.
@@ -20,8 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/expo/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ ChatConversation: () => Conversation,
24
+ ChatMessage: () => Message,
25
+ StoredMemoryModel: () => Memory,
26
+ chatStorageSchema: () => chatStorageSchema,
27
+ generateCompositeKey: () => generateCompositeKey,
28
+ generateConversationId: () => generateConversationId,
29
+ generateUniqueKey: () => generateUniqueKey,
30
+ memoryStorageSchema: () => memoryStorageSchema,
23
31
  useChat: () => useChat,
32
+ useChatStorage: () => useChatStorage,
24
33
  useImageGeneration: () => useImageGeneration,
34
+ useMemoryStorage: () => useMemoryStorage,
25
35
  useModels: () => useModels
26
36
  });
27
37
  module.exports = __toCommonJS(index_exports);
@@ -308,9 +318,405 @@ function useChat(options) {
308
318
  };
309
319
  }
310
320
 
311
- // src/react/useImageGeneration.ts
321
+ // src/expo/useChatStorage.ts
312
322
  var import_react2 = require("react");
313
323
 
324
+ // src/lib/chatStorage/types.ts
325
+ function convertUsageToStored(usage) {
326
+ if (!usage) return void 0;
327
+ return {
328
+ promptTokens: usage.prompt_tokens,
329
+ completionTokens: usage.completion_tokens,
330
+ totalTokens: usage.total_tokens,
331
+ costMicroUsd: usage.cost_micro_usd
332
+ };
333
+ }
334
+ function generateConversationId() {
335
+ return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
336
+ }
337
+
338
+ // src/lib/chatStorage/operations.ts
339
+ var import_watermelondb = require("@nozbe/watermelondb");
340
+ function messageToStored(message) {
341
+ return {
342
+ uniqueId: message.id,
343
+ messageId: message.messageId,
344
+ conversationId: message.conversationId,
345
+ role: message.role,
346
+ content: message.content,
347
+ model: message.model,
348
+ files: message.files,
349
+ createdAt: message.createdAt,
350
+ updatedAt: message.updatedAt,
351
+ vector: message.vector,
352
+ embeddingModel: message.embeddingModel,
353
+ usage: message.usage,
354
+ sources: message.sources,
355
+ responseDuration: message.responseDuration
356
+ };
357
+ }
358
+ function conversationToStored(conversation) {
359
+ return {
360
+ uniqueId: conversation.id,
361
+ conversationId: conversation.conversationId,
362
+ title: conversation.title,
363
+ createdAt: conversation.createdAt,
364
+ updatedAt: conversation.updatedAt,
365
+ isDeleted: conversation.isDeleted
366
+ };
367
+ }
368
+ async function createConversationOp(ctx, opts, defaultTitle = "New Conversation") {
369
+ const convId = opts?.conversationId || generateConversationId();
370
+ const title = opts?.title || defaultTitle;
371
+ const created = await ctx.database.write(async () => {
372
+ return await ctx.conversationsCollection.create((conv) => {
373
+ conv._setRaw("conversation_id", convId);
374
+ conv._setRaw("title", title);
375
+ conv._setRaw("is_deleted", false);
376
+ });
377
+ });
378
+ return conversationToStored(created);
379
+ }
380
+ async function getConversationOp(ctx, id) {
381
+ const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
382
+ return results.length > 0 ? conversationToStored(results[0]) : null;
383
+ }
384
+ async function getConversationsOp(ctx) {
385
+ const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("is_deleted", false), import_watermelondb.Q.sortBy("created_at", import_watermelondb.Q.desc)).fetch();
386
+ return results.map(conversationToStored);
387
+ }
388
+ async function updateConversationTitleOp(ctx, id, title) {
389
+ const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
390
+ if (results.length > 0) {
391
+ await ctx.database.write(async () => {
392
+ await results[0].update((conv) => {
393
+ conv._setRaw("title", title);
394
+ });
395
+ });
396
+ return true;
397
+ }
398
+ return false;
399
+ }
400
+ async function deleteConversationOp(ctx, id) {
401
+ const results = await ctx.conversationsCollection.query(import_watermelondb.Q.where("conversation_id", id), import_watermelondb.Q.where("is_deleted", false)).fetch();
402
+ if (results.length > 0) {
403
+ await ctx.database.write(async () => {
404
+ await results[0].update((conv) => {
405
+ conv._setRaw("is_deleted", true);
406
+ });
407
+ });
408
+ return true;
409
+ }
410
+ return false;
411
+ }
412
+ async function getMessagesOp(ctx, convId) {
413
+ const results = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId), import_watermelondb.Q.sortBy("message_id", import_watermelondb.Q.asc)).fetch();
414
+ return results.map(messageToStored);
415
+ }
416
+ async function getMessageCountOp(ctx, convId) {
417
+ return await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetchCount();
418
+ }
419
+ async function clearMessagesOp(ctx, convId) {
420
+ const messages = await ctx.messagesCollection.query(import_watermelondb.Q.where("conversation_id", convId)).fetch();
421
+ await ctx.database.write(async () => {
422
+ for (const message of messages) {
423
+ await message.destroyPermanently();
424
+ }
425
+ });
426
+ }
427
+ async function createMessageOp(ctx, opts) {
428
+ const existingCount = await getMessageCountOp(ctx, opts.conversationId);
429
+ const messageId = existingCount + 1;
430
+ const created = await ctx.database.write(async () => {
431
+ return await ctx.messagesCollection.create((msg) => {
432
+ msg._setRaw("message_id", messageId);
433
+ msg._setRaw("conversation_id", opts.conversationId);
434
+ msg._setRaw("role", opts.role);
435
+ msg._setRaw("content", opts.content);
436
+ if (opts.model) msg._setRaw("model", opts.model);
437
+ if (opts.files) msg._setRaw("files", JSON.stringify(opts.files));
438
+ if (opts.usage) msg._setRaw("usage", JSON.stringify(opts.usage));
439
+ if (opts.sources) msg._setRaw("sources", JSON.stringify(opts.sources));
440
+ if (opts.responseDuration !== void 0)
441
+ msg._setRaw("response_duration", opts.responseDuration);
442
+ if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
443
+ if (opts.embeddingModel) msg._setRaw("embedding_model", opts.embeddingModel);
444
+ });
445
+ });
446
+ return messageToStored(created);
447
+ }
448
+
449
+ // src/expo/useChatStorage.ts
450
+ function storedToLlmapiMessage(stored) {
451
+ const content = [
452
+ { type: "text", text: stored.content }
453
+ ];
454
+ if (stored.files?.length) {
455
+ for (const file of stored.files) {
456
+ if (file.url) {
457
+ content.push({
458
+ type: "image_url",
459
+ image_url: { url: file.url }
460
+ });
461
+ }
462
+ }
463
+ }
464
+ return {
465
+ role: stored.role,
466
+ content
467
+ };
468
+ }
469
+ function useChatStorage(options) {
470
+ const {
471
+ database,
472
+ conversationId: initialConversationId,
473
+ autoCreateConversation = true,
474
+ defaultConversationTitle = "New Conversation",
475
+ getToken,
476
+ baseUrl,
477
+ onData,
478
+ onFinish,
479
+ onError
480
+ } = options;
481
+ const [currentConversationId, setCurrentConversationId] = (0, import_react2.useState)(initialConversationId || null);
482
+ const messagesCollection = (0, import_react2.useMemo)(
483
+ () => database.get("history"),
484
+ [database]
485
+ );
486
+ const conversationsCollection = (0, import_react2.useMemo)(
487
+ () => database.get("conversations"),
488
+ [database]
489
+ );
490
+ const storageCtx = (0, import_react2.useMemo)(
491
+ () => ({
492
+ database,
493
+ messagesCollection,
494
+ conversationsCollection
495
+ }),
496
+ [database, messagesCollection, conversationsCollection]
497
+ );
498
+ const {
499
+ isLoading,
500
+ sendMessage: baseSendMessage,
501
+ stop
502
+ } = useChat({
503
+ getToken,
504
+ baseUrl,
505
+ onData,
506
+ onFinish,
507
+ onError
508
+ });
509
+ const createConversation = (0, import_react2.useCallback)(
510
+ async (opts) => {
511
+ const created = await createConversationOp(
512
+ storageCtx,
513
+ opts,
514
+ defaultConversationTitle
515
+ );
516
+ setCurrentConversationId(created.conversationId);
517
+ return created;
518
+ },
519
+ [storageCtx, defaultConversationTitle]
520
+ );
521
+ const getConversation = (0, import_react2.useCallback)(
522
+ async (id) => {
523
+ return getConversationOp(storageCtx, id);
524
+ },
525
+ [storageCtx]
526
+ );
527
+ const getConversations = (0, import_react2.useCallback)(async () => {
528
+ return getConversationsOp(storageCtx);
529
+ }, [storageCtx]);
530
+ const updateConversationTitle = (0, import_react2.useCallback)(
531
+ async (id, title) => {
532
+ return updateConversationTitleOp(storageCtx, id, title);
533
+ },
534
+ [storageCtx]
535
+ );
536
+ const deleteConversation = (0, import_react2.useCallback)(
537
+ async (id) => {
538
+ const deleted = await deleteConversationOp(storageCtx, id);
539
+ if (deleted && currentConversationId === id) {
540
+ setCurrentConversationId(null);
541
+ }
542
+ return deleted;
543
+ },
544
+ [storageCtx, currentConversationId]
545
+ );
546
+ const getMessages = (0, import_react2.useCallback)(
547
+ async (convId) => {
548
+ return getMessagesOp(storageCtx, convId);
549
+ },
550
+ [storageCtx]
551
+ );
552
+ const getMessageCount = (0, import_react2.useCallback)(
553
+ async (convId) => {
554
+ return getMessageCountOp(storageCtx, convId);
555
+ },
556
+ [storageCtx]
557
+ );
558
+ const clearMessages = (0, import_react2.useCallback)(
559
+ async (convId) => {
560
+ return clearMessagesOp(storageCtx, convId);
561
+ },
562
+ [storageCtx]
563
+ );
564
+ const ensureConversation = (0, import_react2.useCallback)(async () => {
565
+ if (currentConversationId) {
566
+ const existing = await getConversation(currentConversationId);
567
+ if (existing) {
568
+ return currentConversationId;
569
+ }
570
+ if (autoCreateConversation) {
571
+ const newConv = await createConversation({
572
+ conversationId: currentConversationId
573
+ });
574
+ return newConv.conversationId;
575
+ }
576
+ }
577
+ if (autoCreateConversation) {
578
+ const newConv = await createConversation();
579
+ return newConv.conversationId;
580
+ }
581
+ throw new Error(
582
+ "No conversation ID provided and autoCreateConversation is disabled"
583
+ );
584
+ }, [
585
+ currentConversationId,
586
+ getConversation,
587
+ autoCreateConversation,
588
+ createConversation
589
+ ]);
590
+ const sendMessage = (0, import_react2.useCallback)(
591
+ async (args) => {
592
+ const {
593
+ content,
594
+ model,
595
+ messages: providedMessages,
596
+ includeHistory = true,
597
+ maxHistoryMessages = 50,
598
+ files,
599
+ onData: perRequestOnData
600
+ } = args;
601
+ let convId;
602
+ try {
603
+ convId = await ensureConversation();
604
+ } catch (err) {
605
+ return {
606
+ data: null,
607
+ error: err instanceof Error ? err.message : "Failed to ensure conversation"
608
+ };
609
+ }
610
+ let messagesToSend = [];
611
+ if (includeHistory && !providedMessages) {
612
+ const storedMessages = await getMessages(convId);
613
+ const limitedMessages = storedMessages.slice(-maxHistoryMessages);
614
+ messagesToSend = limitedMessages.map(storedToLlmapiMessage);
615
+ } else if (providedMessages) {
616
+ messagesToSend = providedMessages;
617
+ }
618
+ const userMessageContent = [
619
+ { type: "text", text: content }
620
+ ];
621
+ if (files?.length) {
622
+ for (const file of files) {
623
+ if (file.url) {
624
+ userMessageContent.push({
625
+ type: "image_url",
626
+ image_url: { url: file.url }
627
+ });
628
+ }
629
+ }
630
+ }
631
+ const userMessage = {
632
+ role: "user",
633
+ content: userMessageContent
634
+ };
635
+ messagesToSend.push(userMessage);
636
+ const sanitizedFiles = files?.map((file) => ({
637
+ id: file.id,
638
+ name: file.name,
639
+ type: file.type,
640
+ size: file.size,
641
+ // Only keep URL if it's not a data URI (e.g., external URLs)
642
+ url: file.url && !file.url.startsWith("data:") ? file.url : void 0
643
+ }));
644
+ let storedUserMessage;
645
+ try {
646
+ storedUserMessage = await createMessageOp(storageCtx, {
647
+ conversationId: convId,
648
+ role: "user",
649
+ content,
650
+ files: sanitizedFiles
651
+ });
652
+ } catch (err) {
653
+ return {
654
+ data: null,
655
+ error: err instanceof Error ? err.message : "Failed to store user message"
656
+ };
657
+ }
658
+ const startTime = Date.now();
659
+ const result = await baseSendMessage({
660
+ messages: messagesToSend,
661
+ model,
662
+ onData: perRequestOnData
663
+ });
664
+ const responseDuration = (Date.now() - startTime) / 1e3;
665
+ if (result.error || !result.data) {
666
+ return {
667
+ data: null,
668
+ error: result.error || "No response data received",
669
+ userMessage: storedUserMessage
670
+ };
671
+ }
672
+ const responseData = result.data;
673
+ const assistantContent = responseData.choices?.[0]?.message?.content?.map((part) => part.text || "").join("") || "";
674
+ let storedAssistantMessage;
675
+ try {
676
+ storedAssistantMessage = await createMessageOp(storageCtx, {
677
+ conversationId: convId,
678
+ role: "assistant",
679
+ content: assistantContent,
680
+ model: responseData.model,
681
+ usage: convertUsageToStored(responseData.usage),
682
+ responseDuration
683
+ });
684
+ } catch (err) {
685
+ return {
686
+ data: null,
687
+ error: err instanceof Error ? err.message : "Failed to store assistant message",
688
+ userMessage: storedUserMessage
689
+ };
690
+ }
691
+ return {
692
+ data: responseData,
693
+ error: null,
694
+ userMessage: storedUserMessage,
695
+ assistantMessage: storedAssistantMessage
696
+ };
697
+ },
698
+ [ensureConversation, getMessages, storageCtx, baseSendMessage]
699
+ );
700
+ return {
701
+ isLoading,
702
+ sendMessage,
703
+ stop,
704
+ conversationId: currentConversationId,
705
+ setConversationId: setCurrentConversationId,
706
+ createConversation,
707
+ getConversation,
708
+ getConversations,
709
+ updateConversationTitle,
710
+ deleteConversation,
711
+ getMessages,
712
+ getMessageCount,
713
+ clearMessages
714
+ };
715
+ }
716
+
717
+ // src/react/useImageGeneration.ts
718
+ var import_react3 = require("react");
719
+
314
720
  // src/client/core/bodySerializer.gen.ts
315
721
  var jsonBodySerializer = {
316
722
  bodySerializer: (body) => JSON.stringify(
@@ -1140,9 +1546,9 @@ var getApiV1Models = (options) => {
1140
1546
  // src/react/useImageGeneration.ts
1141
1547
  function useImageGeneration(options = {}) {
1142
1548
  const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
1143
- const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
1144
- const abortControllerRef = (0, import_react2.useRef)(null);
1145
- (0, import_react2.useEffect)(() => {
1549
+ const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
1550
+ const abortControllerRef = (0, import_react3.useRef)(null);
1551
+ (0, import_react3.useEffect)(() => {
1146
1552
  return () => {
1147
1553
  if (abortControllerRef.current) {
1148
1554
  abortControllerRef.current.abort();
@@ -1150,13 +1556,13 @@ function useImageGeneration(options = {}) {
1150
1556
  }
1151
1557
  };
1152
1558
  }, []);
1153
- const stop = (0, import_react2.useCallback)(() => {
1559
+ const stop = (0, import_react3.useCallback)(() => {
1154
1560
  if (abortControllerRef.current) {
1155
1561
  abortControllerRef.current.abort();
1156
1562
  abortControllerRef.current = null;
1157
1563
  }
1158
1564
  }, []);
1159
- const generateImage = (0, import_react2.useCallback)(
1565
+ const generateImage = (0, import_react3.useCallback)(
1160
1566
  async (args) => {
1161
1567
  if (abortControllerRef.current) {
1162
1568
  abortControllerRef.current.abort();
@@ -1219,22 +1625,22 @@ function useImageGeneration(options = {}) {
1219
1625
  }
1220
1626
 
1221
1627
  // src/react/useModels.ts
1222
- var import_react3 = require("react");
1628
+ var import_react4 = require("react");
1223
1629
  function useModels(options = {}) {
1224
1630
  const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
1225
- const [models, setModels] = (0, import_react3.useState)([]);
1226
- const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
1227
- const [error, setError] = (0, import_react3.useState)(null);
1228
- const getTokenRef = (0, import_react3.useRef)(getToken);
1229
- const baseUrlRef = (0, import_react3.useRef)(baseUrl);
1230
- const providerRef = (0, import_react3.useRef)(provider);
1231
- const abortControllerRef = (0, import_react3.useRef)(null);
1232
- (0, import_react3.useEffect)(() => {
1631
+ const [models, setModels] = (0, import_react4.useState)([]);
1632
+ const [isLoading, setIsLoading] = (0, import_react4.useState)(false);
1633
+ const [error, setError] = (0, import_react4.useState)(null);
1634
+ const getTokenRef = (0, import_react4.useRef)(getToken);
1635
+ const baseUrlRef = (0, import_react4.useRef)(baseUrl);
1636
+ const providerRef = (0, import_react4.useRef)(provider);
1637
+ const abortControllerRef = (0, import_react4.useRef)(null);
1638
+ (0, import_react4.useEffect)(() => {
1233
1639
  getTokenRef.current = getToken;
1234
1640
  baseUrlRef.current = baseUrl;
1235
1641
  providerRef.current = provider;
1236
1642
  });
1237
- (0, import_react3.useEffect)(() => {
1643
+ (0, import_react4.useEffect)(() => {
1238
1644
  return () => {
1239
1645
  if (abortControllerRef.current) {
1240
1646
  abortControllerRef.current.abort();
@@ -1242,7 +1648,7 @@ function useModels(options = {}) {
1242
1648
  }
1243
1649
  };
1244
1650
  }, []);
1245
- const fetchModels = (0, import_react3.useCallback)(async () => {
1651
+ const fetchModels = (0, import_react4.useCallback)(async () => {
1246
1652
  if (abortControllerRef.current) {
1247
1653
  abortControllerRef.current.abort();
1248
1654
  }
@@ -1300,12 +1706,12 @@ function useModels(options = {}) {
1300
1706
  }
1301
1707
  }
1302
1708
  }, []);
1303
- const refetch = (0, import_react3.useCallback)(async () => {
1709
+ const refetch = (0, import_react4.useCallback)(async () => {
1304
1710
  setModels([]);
1305
1711
  await fetchModels();
1306
1712
  }, [fetchModels]);
1307
- const hasFetchedRef = (0, import_react3.useRef)(false);
1308
- (0, import_react3.useEffect)(() => {
1713
+ const hasFetchedRef = (0, import_react4.useRef)(false);
1714
+ (0, import_react4.useEffect)(() => {
1309
1715
  if (autoFetch && !hasFetchedRef.current) {
1310
1716
  hasFetchedRef.current = true;
1311
1717
  fetchModels();
@@ -1321,9 +1727,1244 @@ function useModels(options = {}) {
1321
1727
  refetch
1322
1728
  };
1323
1729
  }
1730
+
1731
+ // src/expo/useMemoryStorage.ts
1732
+ var import_react5 = require("react");
1733
+ var import_client4 = require("@reverbia/sdk");
1734
+
1735
+ // src/lib/memoryStorage/operations.ts
1736
+ var import_watermelondb2 = require("@nozbe/watermelondb");
1737
+
1738
+ // src/lib/memoryStorage/types.ts
1739
+ function generateCompositeKey(namespace, key) {
1740
+ return `${namespace}:${key}`;
1741
+ }
1742
+ function generateUniqueKey(namespace, key, value) {
1743
+ return `${namespace}:${key}:${value}`;
1744
+ }
1745
+ function cosineSimilarity(a, b) {
1746
+ if (a.length !== b.length) {
1747
+ throw new Error("Vectors must have the same length");
1748
+ }
1749
+ let dotProduct = 0;
1750
+ let normA = 0;
1751
+ let normB = 0;
1752
+ for (let i = 0; i < a.length; i++) {
1753
+ dotProduct += a[i] * b[i];
1754
+ normA += a[i] * a[i];
1755
+ normB += b[i] * b[i];
1756
+ }
1757
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
1758
+ if (denominator === 0) {
1759
+ return 0;
1760
+ }
1761
+ return dotProduct / denominator;
1762
+ }
1763
+
1764
+ // src/lib/memoryStorage/operations.ts
1765
+ function memoryToStored(memory) {
1766
+ return {
1767
+ uniqueId: memory.id,
1768
+ type: memory.type,
1769
+ namespace: memory.namespace,
1770
+ key: memory.key,
1771
+ value: memory.value,
1772
+ rawEvidence: memory.rawEvidence,
1773
+ confidence: memory.confidence,
1774
+ pii: memory.pii,
1775
+ compositeKey: memory.compositeKey,
1776
+ uniqueKey: memory.uniqueKey,
1777
+ createdAt: memory.createdAt,
1778
+ updatedAt: memory.updatedAt,
1779
+ embedding: memory.embedding,
1780
+ embeddingModel: memory.embeddingModel,
1781
+ isDeleted: memory.isDeleted
1782
+ };
1783
+ }
1784
+ async function getAllMemoriesOp(ctx) {
1785
+ const results = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("is_deleted", false), import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)).fetch();
1786
+ return results.map(memoryToStored);
1787
+ }
1788
+ async function getMemoryByIdOp(ctx, id) {
1789
+ try {
1790
+ const memory = await ctx.memoriesCollection.find(id);
1791
+ if (memory.isDeleted) return null;
1792
+ return memoryToStored(memory);
1793
+ } catch {
1794
+ return null;
1795
+ }
1796
+ }
1797
+ async function getMemoriesByNamespaceOp(ctx, namespace) {
1798
+ const results = await ctx.memoriesCollection.query(
1799
+ import_watermelondb2.Q.where("namespace", namespace),
1800
+ import_watermelondb2.Q.where("is_deleted", false),
1801
+ import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)
1802
+ ).fetch();
1803
+ return results.map(memoryToStored);
1804
+ }
1805
+ async function getMemoriesByKeyOp(ctx, namespace, key) {
1806
+ const compositeKey = generateCompositeKey(namespace, key);
1807
+ const results = await ctx.memoriesCollection.query(
1808
+ import_watermelondb2.Q.where("composite_key", compositeKey),
1809
+ import_watermelondb2.Q.where("is_deleted", false),
1810
+ import_watermelondb2.Q.sortBy("created_at", import_watermelondb2.Q.desc)
1811
+ ).fetch();
1812
+ return results.map(memoryToStored);
1813
+ }
1814
+ async function saveMemoryOp(ctx, opts) {
1815
+ const compositeKey = generateCompositeKey(opts.namespace, opts.key);
1816
+ const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
1817
+ const result = await ctx.database.write(async () => {
1818
+ const existing = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("unique_key", uniqueKey)).fetch();
1819
+ if (existing.length > 0) {
1820
+ const existingMemory = existing[0];
1821
+ const shouldPreserveEmbedding = existingMemory.value === opts.value && existingMemory.rawEvidence === opts.rawEvidence && existingMemory.type === opts.type && existingMemory.namespace === opts.namespace && existingMemory.key === opts.key && existingMemory.embedding !== void 0 && existingMemory.embedding.length > 0 && !opts.embedding;
1822
+ await existingMemory.update((mem) => {
1823
+ mem._setRaw("type", opts.type);
1824
+ mem._setRaw("namespace", opts.namespace);
1825
+ mem._setRaw("key", opts.key);
1826
+ mem._setRaw("value", opts.value);
1827
+ mem._setRaw("raw_evidence", opts.rawEvidence);
1828
+ mem._setRaw("confidence", opts.confidence);
1829
+ mem._setRaw("pii", opts.pii);
1830
+ mem._setRaw("composite_key", compositeKey);
1831
+ mem._setRaw("unique_key", uniqueKey);
1832
+ mem._setRaw("is_deleted", false);
1833
+ if (shouldPreserveEmbedding) {
1834
+ } else if (opts.embedding) {
1835
+ mem._setRaw("embedding", JSON.stringify(opts.embedding));
1836
+ if (opts.embeddingModel) {
1837
+ mem._setRaw("embedding_model", opts.embeddingModel);
1838
+ }
1839
+ } else {
1840
+ mem._setRaw("embedding", null);
1841
+ mem._setRaw("embedding_model", null);
1842
+ }
1843
+ });
1844
+ return existingMemory;
1845
+ }
1846
+ return await ctx.memoriesCollection.create((mem) => {
1847
+ mem._setRaw("type", opts.type);
1848
+ mem._setRaw("namespace", opts.namespace);
1849
+ mem._setRaw("key", opts.key);
1850
+ mem._setRaw("value", opts.value);
1851
+ mem._setRaw("raw_evidence", opts.rawEvidence);
1852
+ mem._setRaw("confidence", opts.confidence);
1853
+ mem._setRaw("pii", opts.pii);
1854
+ mem._setRaw("composite_key", compositeKey);
1855
+ mem._setRaw("unique_key", uniqueKey);
1856
+ mem._setRaw("is_deleted", false);
1857
+ if (opts.embedding) {
1858
+ mem._setRaw("embedding", JSON.stringify(opts.embedding));
1859
+ if (opts.embeddingModel) {
1860
+ mem._setRaw("embedding_model", opts.embeddingModel);
1861
+ }
1862
+ }
1863
+ });
1864
+ });
1865
+ return memoryToStored(result);
1866
+ }
1867
+ async function saveMemoriesOp(ctx, memories) {
1868
+ const results = [];
1869
+ for (const memory of memories) {
1870
+ const saved = await saveMemoryOp(ctx, memory);
1871
+ results.push(saved);
1872
+ }
1873
+ return results;
1874
+ }
1875
+ async function updateMemoryOp(ctx, id, updates) {
1876
+ let memory;
1877
+ try {
1878
+ memory = await ctx.memoriesCollection.find(id);
1879
+ } catch {
1880
+ return { ok: false, reason: "not_found" };
1881
+ }
1882
+ if (memory.isDeleted) {
1883
+ return { ok: false, reason: "not_found" };
1884
+ }
1885
+ const newNamespace = updates.namespace ?? memory.namespace;
1886
+ const newKey = updates.key ?? memory.key;
1887
+ const newValue = updates.value ?? memory.value;
1888
+ const newCompositeKey = generateCompositeKey(newNamespace, newKey);
1889
+ const newUniqueKey = generateUniqueKey(newNamespace, newKey, newValue);
1890
+ if (newUniqueKey !== memory.uniqueKey) {
1891
+ const existing = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("unique_key", newUniqueKey), import_watermelondb2.Q.where("is_deleted", false)).fetch();
1892
+ if (existing.length > 0) {
1893
+ return { ok: false, reason: "conflict", conflictingKey: newUniqueKey };
1894
+ }
1895
+ }
1896
+ try {
1897
+ const updated = await ctx.database.write(async () => {
1898
+ await memory.update((mem) => {
1899
+ if (updates.type !== void 0) mem._setRaw("type", updates.type);
1900
+ if (updates.namespace !== void 0)
1901
+ mem._setRaw("namespace", updates.namespace);
1902
+ if (updates.key !== void 0) mem._setRaw("key", updates.key);
1903
+ if (updates.value !== void 0) mem._setRaw("value", updates.value);
1904
+ if (updates.rawEvidence !== void 0)
1905
+ mem._setRaw("raw_evidence", updates.rawEvidence);
1906
+ if (updates.confidence !== void 0)
1907
+ mem._setRaw("confidence", updates.confidence);
1908
+ if (updates.pii !== void 0) mem._setRaw("pii", updates.pii);
1909
+ if (updates.namespace !== void 0 || updates.key !== void 0 || updates.value !== void 0) {
1910
+ mem._setRaw("composite_key", newCompositeKey);
1911
+ mem._setRaw("unique_key", newUniqueKey);
1912
+ }
1913
+ if (updates.embedding !== void 0) {
1914
+ mem._setRaw(
1915
+ "embedding",
1916
+ updates.embedding ? JSON.stringify(updates.embedding) : null
1917
+ );
1918
+ }
1919
+ if (updates.embeddingModel !== void 0) {
1920
+ mem._setRaw("embedding_model", updates.embeddingModel || null);
1921
+ }
1922
+ });
1923
+ return memory;
1924
+ });
1925
+ return { ok: true, memory: memoryToStored(updated) };
1926
+ } catch (err) {
1927
+ return {
1928
+ ok: false,
1929
+ reason: "error",
1930
+ error: err instanceof Error ? err : new Error(String(err))
1931
+ };
1932
+ }
1933
+ }
1934
+ async function deleteMemoryByIdOp(ctx, id) {
1935
+ try {
1936
+ const memory = await ctx.memoriesCollection.find(id);
1937
+ await ctx.database.write(async () => {
1938
+ await memory.update((mem) => {
1939
+ mem._setRaw("is_deleted", true);
1940
+ });
1941
+ });
1942
+ } catch {
1943
+ }
1944
+ }
1945
+ async function deleteMemoryOp(ctx, namespace, key, value) {
1946
+ const uniqueKey = generateUniqueKey(namespace, key, value);
1947
+ const results = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("unique_key", uniqueKey)).fetch();
1948
+ if (results.length > 0) {
1949
+ await ctx.database.write(async () => {
1950
+ await results[0].update((mem) => {
1951
+ mem._setRaw("is_deleted", true);
1952
+ });
1953
+ });
1954
+ }
1955
+ }
1956
+ async function deleteMemoriesByKeyOp(ctx, namespace, key) {
1957
+ const compositeKey = generateCompositeKey(namespace, key);
1958
+ const results = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("composite_key", compositeKey), import_watermelondb2.Q.where("is_deleted", false)).fetch();
1959
+ await ctx.database.write(async () => {
1960
+ for (const memory of results) {
1961
+ await memory.update((mem) => {
1962
+ mem._setRaw("is_deleted", true);
1963
+ });
1964
+ }
1965
+ });
1966
+ }
1967
+ async function clearAllMemoriesOp(ctx) {
1968
+ const results = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
1969
+ await ctx.database.write(async () => {
1970
+ for (const memory of results) {
1971
+ await memory.update((mem) => {
1972
+ mem._setRaw("is_deleted", true);
1973
+ });
1974
+ }
1975
+ });
1976
+ }
1977
+ async function searchSimilarMemoriesOp(ctx, queryEmbedding, limit = 10, minSimilarity = 0.6) {
1978
+ const allMemories = await ctx.memoriesCollection.query(import_watermelondb2.Q.where("is_deleted", false)).fetch();
1979
+ const memoriesWithEmbeddings = allMemories.filter(
1980
+ (m) => m.embedding && m.embedding.length > 0
1981
+ );
1982
+ if (memoriesWithEmbeddings.length === 0) {
1983
+ return [];
1984
+ }
1985
+ const results = memoriesWithEmbeddings.map((memory) => {
1986
+ const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
1987
+ return {
1988
+ ...memoryToStored(memory),
1989
+ similarity
1990
+ };
1991
+ }).filter((result) => result.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
1992
+ return results;
1993
+ }
1994
+ async function updateMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
1995
+ try {
1996
+ const memory = await ctx.memoriesCollection.find(id);
1997
+ await ctx.database.write(async () => {
1998
+ await memory.update((mem) => {
1999
+ mem._setRaw("embedding", JSON.stringify(embedding));
2000
+ mem._setRaw("embedding_model", embeddingModel);
2001
+ });
2002
+ });
2003
+ } catch {
2004
+ }
2005
+ }
2006
+
2007
+ // src/lib/memory/service.ts
2008
+ var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
2009
+
2010
+ CRITICAL: You MUST respond with ONLY valid JSON. No explanations, no markdown, no code blocks, just pure JSON.
2011
+
2012
+ Only store clear, factual statements that might be relevant for future context or reference. Extract facts that will be useful in future conversations, such as identity, stable preferences, ongoing projects, skills, locations, favorites, and constraints.
2013
+
2014
+ Do not extract sensitive attributes, temporary things, or single-use instructions.
2015
+
2016
+ You must also extract stable personal preferences, including food likes/dislikes, hobbies, favorite items, favorite genres, or other enduring tastes.
2017
+
2018
+ If there are no memories to extract, return: {"items": []}
2019
+
2020
+ Response format (JSON only, no other text):
2021
+
2022
+ {
2023
+ "items": [
2024
+ {
2025
+ "type": "identity",
2026
+ "namespace": "identity",
2027
+ "key": "name",
2028
+ "value": "Charlie",
2029
+ "rawEvidence": "I'm Charlie",
2030
+ "confidence": 0.98,
2031
+ "pii": true
2032
+ },
2033
+ {
2034
+ "type": "identity",
2035
+ "namespace": "work",
2036
+ "key": "company",
2037
+ "value": "ZetaChain",
2038
+ "rawEvidence": "called ZetaChain",
2039
+ "confidence": 0.99,
2040
+ "pii": false
2041
+ },
2042
+ {
2043
+ "type": "identity",
2044
+ "namespace": "location",
2045
+ "key": "city",
2046
+ "value": "San Francisco",
2047
+ "rawEvidence": "I live in San Francisco",
2048
+ "confidence": 0.99,
2049
+ "pii": false
2050
+ },
2051
+ {
2052
+ "type": "preference",
2053
+ "namespace": "location",
2054
+ "key": "country",
2055
+ "value": "Japan",
2056
+ "rawEvidence": "I like to travel to the Japan",
2057
+ "confidence": 0.94,
2058
+ "pii": false
2059
+ },
2060
+ {
2061
+ "type": "preference",
2062
+ "namespace": "answer_style",
2063
+ "key": "verbosity",
2064
+ "value": "concise_direct",
2065
+ "rawEvidence": "I prefer concise, direct answers",
2066
+ "confidence": 0.96,
2067
+ "pii": false
2068
+ },
2069
+ {
2070
+ "type": "identity",
2071
+ "namespace": "timezone",
2072
+ "key": "tz",
2073
+ "value": "America/Los_Angeles",
2074
+ "rawEvidence": "I'm in PST",
2075
+ "confidence": 0.9,
2076
+ "pii": false
2077
+ },
2078
+ {
2079
+ "type": "preference",
2080
+ "namespace": "food",
2081
+ "key": "likes_ice_cream",
2082
+ "value": "ice cream",
2083
+ "rawEvidence": "I like ice cream",
2084
+ "confidence": 0.95,
2085
+ "pii": false
2086
+ }
2087
+ ]
2088
+ }`;
2089
+ var preprocessMemories = (items, minConfidence = 0.6) => {
2090
+ if (!items || !Array.isArray(items)) {
2091
+ return [];
2092
+ }
2093
+ const validItems = items.filter((item) => {
2094
+ if (item.namespace == null || item.key == null || item.value == null) {
2095
+ console.warn(
2096
+ "Dropping memory item with null/undefined namespace, key, or value:",
2097
+ item
2098
+ );
2099
+ return false;
2100
+ }
2101
+ const namespace = String(item.namespace).trim();
2102
+ const key = String(item.key).trim();
2103
+ const value = String(item.value).trim();
2104
+ if (namespace === "" || key === "" || value === "") {
2105
+ console.warn(
2106
+ "Dropping memory item with empty namespace, key, or value after trimming:",
2107
+ item
2108
+ );
2109
+ return false;
2110
+ }
2111
+ if (typeof item.confidence !== "number" || item.confidence < minConfidence) {
2112
+ console.warn(
2113
+ `Dropping memory item with confidence ${item.confidence} below threshold ${minConfidence}:`,
2114
+ item
2115
+ );
2116
+ return false;
2117
+ }
2118
+ return true;
2119
+ });
2120
+ const deduplicatedMap = /* @__PURE__ */ new Map();
2121
+ for (const item of validItems) {
2122
+ const uniqueKey = `${item.namespace}:${item.key}:${item.value}`;
2123
+ const existing = deduplicatedMap.get(uniqueKey);
2124
+ if (!existing || item.confidence > existing.confidence) {
2125
+ deduplicatedMap.set(uniqueKey, item);
2126
+ } else {
2127
+ console.debug(
2128
+ `Deduplicating memory item: keeping entry with higher confidence (${existing.confidence} > ${item.confidence})`,
2129
+ { namespace: item.namespace, key: item.key, value: item.value }
2130
+ );
2131
+ }
2132
+ }
2133
+ return Array.from(deduplicatedMap.values());
2134
+ };
2135
+
2136
+ // src/lib/memory/constants.ts
2137
+ var DEFAULT_API_EMBEDDING_MODEL = "openai/text-embedding-3-small";
2138
+ var DEFAULT_COMPLETION_MODEL = "openai/gpt-4o";
2139
+
2140
+ // src/expo/useMemoryStorage.ts
2141
+ var import_client5 = require("@reverbia/sdk");
2142
+ async function generateEmbeddingForTextApi(text, options) {
2143
+ const token = options.getToken ? await options.getToken() : null;
2144
+ if (!token) {
2145
+ throw new Error("No auth token available for embedding generation");
2146
+ }
2147
+ const response = await (0, import_client5.postApiV1Embeddings)({
2148
+ baseUrl: options.baseUrl,
2149
+ body: {
2150
+ input: text,
2151
+ model: options.model
2152
+ },
2153
+ headers: {
2154
+ Authorization: `Bearer ${token}`
2155
+ }
2156
+ });
2157
+ if (!response.data || typeof response.data === "string") {
2158
+ throw new Error("Failed to generate embedding");
2159
+ }
2160
+ const embedding = response.data.data?.[0]?.embedding;
2161
+ if (!embedding) {
2162
+ throw new Error("No embedding in response");
2163
+ }
2164
+ return embedding;
2165
+ }
2166
+ async function generateEmbeddingForMemoryApi(memory, options) {
2167
+ const text = `${memory.type}: ${memory.namespace}/${memory.key} = ${memory.value}. Evidence: ${memory.rawEvidence}`;
2168
+ return generateEmbeddingForTextApi(text, options);
2169
+ }
2170
+ function useMemoryStorage(options) {
2171
+ const {
2172
+ database,
2173
+ completionsModel = DEFAULT_COMPLETION_MODEL,
2174
+ embeddingModel: userEmbeddingModel,
2175
+ embeddingProvider = "api",
2176
+ // Default to API for Expo
2177
+ generateEmbeddings = true,
2178
+ onFactsExtracted,
2179
+ getToken,
2180
+ baseUrl = BASE_URL
2181
+ } = options;
2182
+ const embeddingModel = userEmbeddingModel === void 0 ? DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
2183
+ const [memories, setMemories] = (0, import_react5.useState)([]);
2184
+ const extractionInProgressRef = (0, import_react5.useRef)(false);
2185
+ const memoriesCollection = (0, import_react5.useMemo)(
2186
+ () => database.get("memories"),
2187
+ [database]
2188
+ );
2189
+ const storageCtx = (0, import_react5.useMemo)(
2190
+ () => ({
2191
+ database,
2192
+ memoriesCollection
2193
+ }),
2194
+ [database, memoriesCollection]
2195
+ );
2196
+ const embeddingOptions = (0, import_react5.useMemo)(
2197
+ () => ({
2198
+ model: embeddingModel ?? DEFAULT_API_EMBEDDING_MODEL,
2199
+ getToken: getToken || void 0,
2200
+ baseUrl
2201
+ }),
2202
+ [embeddingModel, getToken, baseUrl]
2203
+ );
2204
+ const refreshMemories = (0, import_react5.useCallback)(async () => {
2205
+ const storedMemories = await getAllMemoriesOp(storageCtx);
2206
+ setMemories(storedMemories);
2207
+ }, [storageCtx]);
2208
+ const extractMemoriesFromMessage = (0, import_react5.useCallback)(
2209
+ async (opts) => {
2210
+ const { messages, model } = opts;
2211
+ if (!getToken || extractionInProgressRef.current) {
2212
+ return null;
2213
+ }
2214
+ extractionInProgressRef.current = true;
2215
+ try {
2216
+ const token = await getToken();
2217
+ if (!token) {
2218
+ console.error("No access token available for memory extraction");
2219
+ return null;
2220
+ }
2221
+ const completion = await (0, import_client4.postApiV1ChatCompletions)({
2222
+ baseUrl,
2223
+ body: {
2224
+ messages: [
2225
+ {
2226
+ role: "system",
2227
+ content: [{ type: "text", text: FACT_EXTRACTION_PROMPT }]
2228
+ },
2229
+ ...messages.map((m) => ({
2230
+ role: m.role,
2231
+ content: [{ type: "text", text: m.content }]
2232
+ }))
2233
+ ],
2234
+ model: model || completionsModel
2235
+ },
2236
+ headers: {
2237
+ Authorization: `Bearer ${token}`
2238
+ }
2239
+ });
2240
+ if (!completion.data) {
2241
+ console.error(
2242
+ "Memory extraction failed:",
2243
+ completion.error?.error ?? "API did not return a response"
2244
+ );
2245
+ return null;
2246
+ }
2247
+ if (typeof completion.data === "string") {
2248
+ console.error(
2249
+ "Memory extraction failed: API returned a string response"
2250
+ );
2251
+ return null;
2252
+ }
2253
+ const messageContent = completion.data.choices?.[0]?.message?.content;
2254
+ let content = "";
2255
+ if (Array.isArray(messageContent)) {
2256
+ content = messageContent.map((p) => p.text || "").join("").trim();
2257
+ } else if (typeof messageContent === "string") {
2258
+ content = messageContent.trim();
2259
+ }
2260
+ if (!content) {
2261
+ console.error("No content in memory extraction response");
2262
+ return null;
2263
+ }
2264
+ let jsonContent = content;
2265
+ jsonContent = jsonContent.replace(/^data:\s*/gm, "").trim();
2266
+ if (jsonContent.startsWith("{")) {
2267
+ let braceCount = 0;
2268
+ let jsonStart = -1;
2269
+ let jsonEnd = -1;
2270
+ for (let i = 0; i < jsonContent.length; i++) {
2271
+ if (jsonContent[i] === "{") {
2272
+ if (jsonStart === -1) jsonStart = i;
2273
+ braceCount++;
2274
+ } else if (jsonContent[i] === "}") {
2275
+ braceCount--;
2276
+ if (braceCount === 0 && jsonStart !== -1) {
2277
+ jsonEnd = i + 1;
2278
+ break;
2279
+ }
2280
+ }
2281
+ }
2282
+ if (jsonStart !== -1 && jsonEnd !== -1) {
2283
+ jsonContent = jsonContent.substring(jsonStart, jsonEnd);
2284
+ }
2285
+ } else {
2286
+ const jsonMatch = jsonContent.match(
2287
+ /```(?:json)?\s*(\{[\s\S]*?\})\s*```/
2288
+ );
2289
+ if (jsonMatch && jsonMatch[1]) {
2290
+ jsonContent = jsonMatch[1].trim();
2291
+ } else {
2292
+ const jsonObjectMatch = jsonContent.match(/\{[\s\S]*\}/);
2293
+ if (jsonObjectMatch && jsonObjectMatch[0]) {
2294
+ jsonContent = jsonObjectMatch[0];
2295
+ } else {
2296
+ console.warn("Memory extraction returned non-JSON response.");
2297
+ return { items: [] };
2298
+ }
2299
+ }
2300
+ }
2301
+ const trimmedJson = jsonContent.trim();
2302
+ if (!trimmedJson.startsWith("{") || !trimmedJson.includes("items")) {
2303
+ console.warn("Memory extraction response doesn't appear to be valid JSON.");
2304
+ return { items: [] };
2305
+ }
2306
+ let result;
2307
+ try {
2308
+ result = JSON.parse(jsonContent);
2309
+ if (!result || typeof result !== "object") {
2310
+ throw new Error("Invalid JSON structure: not an object");
2311
+ }
2312
+ if (!Array.isArray(result.items)) {
2313
+ console.warn("Memory extraction result missing 'items' array.");
2314
+ return { items: [] };
2315
+ }
2316
+ } catch (parseError) {
2317
+ console.error(
2318
+ "Failed to parse memory extraction JSON:",
2319
+ parseError instanceof Error ? parseError.message : "Unknown error"
2320
+ );
2321
+ return { items: [] };
2322
+ }
2323
+ if (result.items && Array.isArray(result.items)) {
2324
+ result.items = preprocessMemories(result.items);
2325
+ }
2326
+ if (result.items && result.items.length > 0) {
2327
+ try {
2328
+ const createOptions = result.items.map(
2329
+ (item) => ({
2330
+ type: item.type,
2331
+ namespace: item.namespace,
2332
+ key: item.key,
2333
+ value: item.value,
2334
+ rawEvidence: item.rawEvidence,
2335
+ confidence: item.confidence,
2336
+ pii: item.pii
2337
+ })
2338
+ );
2339
+ const savedMemories = await saveMemoriesOp(storageCtx, createOptions);
2340
+ console.log(`Saved ${savedMemories.length} memories to WatermelonDB`);
2341
+ if (generateEmbeddings && embeddingModel) {
2342
+ try {
2343
+ for (const saved of savedMemories) {
2344
+ const memoryItem = {
2345
+ type: saved.type,
2346
+ namespace: saved.namespace,
2347
+ key: saved.key,
2348
+ value: saved.value,
2349
+ rawEvidence: saved.rawEvidence,
2350
+ confidence: saved.confidence,
2351
+ pii: saved.pii
2352
+ };
2353
+ const embedding = await generateEmbeddingForMemoryApi(
2354
+ memoryItem,
2355
+ embeddingOptions
2356
+ );
2357
+ await updateMemoryEmbeddingOp(
2358
+ storageCtx,
2359
+ saved.uniqueId,
2360
+ embedding,
2361
+ embeddingOptions.model
2362
+ );
2363
+ }
2364
+ console.log(`Generated embeddings for ${savedMemories.length} memories`);
2365
+ } catch (error) {
2366
+ console.error("Failed to generate embeddings:", error);
2367
+ }
2368
+ }
2369
+ await refreshMemories();
2370
+ } catch (error) {
2371
+ console.error("Failed to save memories to WatermelonDB:", error);
2372
+ }
2373
+ }
2374
+ if (onFactsExtracted) {
2375
+ onFactsExtracted(result);
2376
+ }
2377
+ return result;
2378
+ } catch (error) {
2379
+ console.error("Failed to extract facts:", error);
2380
+ return null;
2381
+ } finally {
2382
+ extractionInProgressRef.current = false;
2383
+ }
2384
+ },
2385
+ [
2386
+ completionsModel,
2387
+ embeddingModel,
2388
+ embeddingOptions,
2389
+ generateEmbeddings,
2390
+ getToken,
2391
+ onFactsExtracted,
2392
+ baseUrl,
2393
+ storageCtx,
2394
+ refreshMemories
2395
+ ]
2396
+ );
2397
+ const searchMemories = (0, import_react5.useCallback)(
2398
+ async (query, limit = 10, minSimilarity = 0.6) => {
2399
+ if (!embeddingModel) {
2400
+ console.warn("Cannot search memories: embeddingModel not provided");
2401
+ return [];
2402
+ }
2403
+ try {
2404
+ const queryEmbedding = await generateEmbeddingForTextApi(
2405
+ query,
2406
+ embeddingOptions
2407
+ );
2408
+ const results = await searchSimilarMemoriesOp(
2409
+ storageCtx,
2410
+ queryEmbedding,
2411
+ limit,
2412
+ minSimilarity
2413
+ );
2414
+ if (results.length === 0) {
2415
+ console.warn(
2416
+ `[Memory Search] No memories found above similarity threshold ${minSimilarity}.`
2417
+ );
2418
+ } else {
2419
+ console.log(
2420
+ `[Memory Search] Found ${results.length} memories. Similarity scores: ${results.map((r) => r.similarity.toFixed(3)).join(", ")}`
2421
+ );
2422
+ }
2423
+ return results;
2424
+ } catch {
2425
+ return [];
2426
+ }
2427
+ },
2428
+ [embeddingModel, embeddingOptions, storageCtx]
2429
+ );
2430
+ const fetchAllMemories = (0, import_react5.useCallback)(async () => {
2431
+ try {
2432
+ return await getAllMemoriesOp(storageCtx);
2433
+ } catch (error) {
2434
+ throw new Error(
2435
+ "Failed to fetch all memories: " + (error instanceof Error ? error.message : String(error))
2436
+ );
2437
+ }
2438
+ }, [storageCtx]);
2439
+ const fetchMemoriesByNamespace = (0, import_react5.useCallback)(
2440
+ async (namespace) => {
2441
+ if (!namespace) {
2442
+ throw new Error("Missing required field: namespace");
2443
+ }
2444
+ try {
2445
+ return await getMemoriesByNamespaceOp(storageCtx, namespace);
2446
+ } catch (error) {
2447
+ throw new Error(
2448
+ `Failed to fetch memories for namespace "${namespace}": ` + (error instanceof Error ? error.message : String(error))
2449
+ );
2450
+ }
2451
+ },
2452
+ [storageCtx]
2453
+ );
2454
+ const fetchMemoriesByKey = (0, import_react5.useCallback)(
2455
+ async (namespace, key) => {
2456
+ if (!namespace || !key) {
2457
+ throw new Error("Missing required fields: namespace, key");
2458
+ }
2459
+ try {
2460
+ return await getMemoriesByKeyOp(storageCtx, namespace, key);
2461
+ } catch (error) {
2462
+ throw new Error(
2463
+ `Failed to fetch memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
2464
+ );
2465
+ }
2466
+ },
2467
+ [storageCtx]
2468
+ );
2469
+ const getMemoryById = (0, import_react5.useCallback)(
2470
+ async (id) => {
2471
+ try {
2472
+ return await getMemoryByIdOp(storageCtx, id);
2473
+ } catch (error) {
2474
+ throw new Error(
2475
+ `Failed to get memory ${id}: ` + (error instanceof Error ? error.message : String(error))
2476
+ );
2477
+ }
2478
+ },
2479
+ [storageCtx]
2480
+ );
2481
+ const saveMemory = (0, import_react5.useCallback)(
2482
+ async (memory) => {
2483
+ try {
2484
+ const saved = await saveMemoryOp(storageCtx, memory);
2485
+ if (generateEmbeddings && embeddingModel && !memory.embedding) {
2486
+ try {
2487
+ const memoryItem = {
2488
+ type: memory.type,
2489
+ namespace: memory.namespace,
2490
+ key: memory.key,
2491
+ value: memory.value,
2492
+ rawEvidence: memory.rawEvidence,
2493
+ confidence: memory.confidence,
2494
+ pii: memory.pii
2495
+ };
2496
+ const embedding = await generateEmbeddingForMemoryApi(
2497
+ memoryItem,
2498
+ embeddingOptions
2499
+ );
2500
+ await updateMemoryEmbeddingOp(
2501
+ storageCtx,
2502
+ saved.uniqueId,
2503
+ embedding,
2504
+ embeddingOptions.model
2505
+ );
2506
+ } catch (error) {
2507
+ console.error("Failed to generate embedding:", error);
2508
+ }
2509
+ }
2510
+ setMemories((prev) => {
2511
+ const existing = prev.find((m) => m.uniqueId === saved.uniqueId);
2512
+ if (existing) {
2513
+ return prev.map((m) => m.uniqueId === saved.uniqueId ? saved : m);
2514
+ }
2515
+ return [saved, ...prev];
2516
+ });
2517
+ return saved;
2518
+ } catch (error) {
2519
+ throw new Error(
2520
+ "Failed to save memory: " + (error instanceof Error ? error.message : String(error))
2521
+ );
2522
+ }
2523
+ },
2524
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
2525
+ );
2526
+ const saveMemories = (0, import_react5.useCallback)(
2527
+ async (memoriesToSave) => {
2528
+ try {
2529
+ const saved = await saveMemoriesOp(storageCtx, memoriesToSave);
2530
+ if (generateEmbeddings && embeddingModel) {
2531
+ for (let i = 0; i < saved.length; i++) {
2532
+ const memory = memoriesToSave[i];
2533
+ if (!memory.embedding) {
2534
+ try {
2535
+ const memoryItem = {
2536
+ type: memory.type,
2537
+ namespace: memory.namespace,
2538
+ key: memory.key,
2539
+ value: memory.value,
2540
+ rawEvidence: memory.rawEvidence,
2541
+ confidence: memory.confidence,
2542
+ pii: memory.pii
2543
+ };
2544
+ const embedding = await generateEmbeddingForMemoryApi(
2545
+ memoryItem,
2546
+ embeddingOptions
2547
+ );
2548
+ await updateMemoryEmbeddingOp(
2549
+ storageCtx,
2550
+ saved[i].uniqueId,
2551
+ embedding,
2552
+ embeddingOptions.model
2553
+ );
2554
+ } catch (error) {
2555
+ console.error("Failed to generate embedding:", error);
2556
+ }
2557
+ }
2558
+ }
2559
+ }
2560
+ await refreshMemories();
2561
+ return saved;
2562
+ } catch (error) {
2563
+ throw new Error(
2564
+ "Failed to save memories: " + (error instanceof Error ? error.message : String(error))
2565
+ );
2566
+ }
2567
+ },
2568
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions, refreshMemories]
2569
+ );
2570
+ const updateMemory = (0, import_react5.useCallback)(
2571
+ async (id, updates) => {
2572
+ const result = await updateMemoryOp(storageCtx, id, updates);
2573
+ if (!result.ok) {
2574
+ if (result.reason === "not_found") {
2575
+ return null;
2576
+ }
2577
+ if (result.reason === "conflict") {
2578
+ throw new Error(
2579
+ `Cannot update memory: a memory with key "${result.conflictingKey}" already exists`
2580
+ );
2581
+ }
2582
+ throw new Error(
2583
+ `Failed to update memory ${id}: ${result.error.message}`
2584
+ );
2585
+ }
2586
+ const updated = result.memory;
2587
+ const contentChanged = updates.value !== void 0 || updates.rawEvidence !== void 0 || updates.type !== void 0 || updates.namespace !== void 0 || updates.key !== void 0;
2588
+ if (contentChanged && generateEmbeddings && embeddingModel && !updates.embedding) {
2589
+ try {
2590
+ const memoryItem = {
2591
+ type: updated.type,
2592
+ namespace: updated.namespace,
2593
+ key: updated.key,
2594
+ value: updated.value,
2595
+ rawEvidence: updated.rawEvidence,
2596
+ confidence: updated.confidence,
2597
+ pii: updated.pii
2598
+ };
2599
+ const embedding = await generateEmbeddingForMemoryApi(
2600
+ memoryItem,
2601
+ embeddingOptions
2602
+ );
2603
+ await updateMemoryEmbeddingOp(storageCtx, id, embedding, embeddingOptions.model);
2604
+ } catch (error) {
2605
+ console.error("Failed to regenerate embedding:", error);
2606
+ }
2607
+ }
2608
+ setMemories((prev) => prev.map((m) => m.uniqueId === id ? updated : m));
2609
+ return updated;
2610
+ },
2611
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
2612
+ );
2613
+ const removeMemory = (0, import_react5.useCallback)(
2614
+ async (namespace, key, value) => {
2615
+ if (!namespace || !key || !value) {
2616
+ throw new Error("Missing required fields: namespace, key, value");
2617
+ }
2618
+ try {
2619
+ await deleteMemoryOp(storageCtx, namespace, key, value);
2620
+ setMemories(
2621
+ (prev) => prev.filter(
2622
+ (m) => !(m.namespace === namespace && m.key === key && m.value === value)
2623
+ )
2624
+ );
2625
+ } catch (error) {
2626
+ throw new Error(
2627
+ `Failed to delete memory "${namespace}:${key}:${value}": ` + (error instanceof Error ? error.message : String(error))
2628
+ );
2629
+ }
2630
+ },
2631
+ [storageCtx]
2632
+ );
2633
+ const removeMemoryById = (0, import_react5.useCallback)(
2634
+ async (id) => {
2635
+ try {
2636
+ await deleteMemoryByIdOp(storageCtx, id);
2637
+ setMemories((prev) => prev.filter((m) => m.uniqueId !== id));
2638
+ } catch (error) {
2639
+ throw new Error(
2640
+ `Failed to delete memory with id ${id}: ` + (error instanceof Error ? error.message : String(error))
2641
+ );
2642
+ }
2643
+ },
2644
+ [storageCtx]
2645
+ );
2646
+ const removeMemories = (0, import_react5.useCallback)(
2647
+ async (namespace, key) => {
2648
+ if (!namespace || !key) {
2649
+ throw new Error("Missing required fields: namespace, key");
2650
+ }
2651
+ try {
2652
+ await deleteMemoriesByKeyOp(storageCtx, namespace, key);
2653
+ setMemories(
2654
+ (prev) => prev.filter((m) => !(m.namespace === namespace && m.key === key))
2655
+ );
2656
+ } catch (error) {
2657
+ throw new Error(
2658
+ `Failed to delete memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
2659
+ );
2660
+ }
2661
+ },
2662
+ [storageCtx]
2663
+ );
2664
+ const clearMemories = (0, import_react5.useCallback)(async () => {
2665
+ try {
2666
+ await clearAllMemoriesOp(storageCtx);
2667
+ setMemories([]);
2668
+ } catch (error) {
2669
+ throw new Error(
2670
+ "Failed to clear all memories: " + (error instanceof Error ? error.message : String(error))
2671
+ );
2672
+ }
2673
+ }, [storageCtx]);
2674
+ return {
2675
+ memories,
2676
+ refreshMemories,
2677
+ extractMemoriesFromMessage,
2678
+ searchMemories,
2679
+ fetchAllMemories,
2680
+ fetchMemoriesByNamespace,
2681
+ fetchMemoriesByKey,
2682
+ getMemoryById,
2683
+ saveMemory,
2684
+ saveMemories,
2685
+ updateMemory,
2686
+ removeMemory,
2687
+ removeMemoryById,
2688
+ removeMemories,
2689
+ clearMemories
2690
+ };
2691
+ }
2692
+
2693
+ // src/lib/chatStorage/schema.ts
2694
+ var import_watermelondb3 = require("@nozbe/watermelondb");
2695
+ var chatStorageSchema = (0, import_watermelondb3.appSchema)({
2696
+ version: 1,
2697
+ tables: [
2698
+ (0, import_watermelondb3.tableSchema)({
2699
+ name: "history",
2700
+ columns: [
2701
+ { name: "message_id", type: "number" },
2702
+ // Sequential ID within conversation
2703
+ { name: "conversation_id", type: "string", isIndexed: true },
2704
+ { name: "role", type: "string", isIndexed: true },
2705
+ // 'user' | 'assistant' | 'system'
2706
+ { name: "content", type: "string" },
2707
+ { name: "model", type: "string", isOptional: true },
2708
+ { name: "files", type: "string", isOptional: true },
2709
+ // JSON stringified FileMetadata[]
2710
+ { name: "created_at", type: "number", isIndexed: true },
2711
+ { name: "updated_at", type: "number" },
2712
+ { name: "vector", type: "string", isOptional: true },
2713
+ // JSON stringified number[]
2714
+ { name: "embedding_model", type: "string", isOptional: true },
2715
+ { name: "usage", type: "string", isOptional: true },
2716
+ // JSON stringified ChatCompletionUsage
2717
+ { name: "sources", type: "string", isOptional: true },
2718
+ // JSON stringified SearchSource[]
2719
+ { name: "response_duration", type: "number", isOptional: true }
2720
+ ]
2721
+ }),
2722
+ (0, import_watermelondb3.tableSchema)({
2723
+ name: "conversations",
2724
+ columns: [
2725
+ { name: "conversation_id", type: "string", isIndexed: true },
2726
+ { name: "title", type: "string" },
2727
+ { name: "created_at", type: "number" },
2728
+ { name: "updated_at", type: "number" },
2729
+ { name: "is_deleted", type: "boolean", isIndexed: true }
2730
+ ]
2731
+ })
2732
+ ]
2733
+ });
2734
+
2735
+ // src/lib/chatStorage/models.ts
2736
+ var import_watermelondb4 = require("@nozbe/watermelondb");
2737
+ var Message = class extends import_watermelondb4.Model {
2738
+ /** Sequential message ID within conversation */
2739
+ get messageId() {
2740
+ return this._getRaw("message_id");
2741
+ }
2742
+ /** Links message to its conversation */
2743
+ get conversationId() {
2744
+ return this._getRaw("conversation_id");
2745
+ }
2746
+ /** Who sent the message: 'user' | 'assistant' | 'system' */
2747
+ get role() {
2748
+ return this._getRaw("role");
2749
+ }
2750
+ /** The message text content */
2751
+ get content() {
2752
+ return this._getRaw("content");
2753
+ }
2754
+ /** LLM model used (e.g., GPT-4, Claude) */
2755
+ get model() {
2756
+ const value = this._getRaw("model");
2757
+ return value ? value : void 0;
2758
+ }
2759
+ /** Optional attached files */
2760
+ get files() {
2761
+ const raw = this._getRaw("files");
2762
+ if (!raw) return void 0;
2763
+ try {
2764
+ return JSON.parse(raw);
2765
+ } catch {
2766
+ return void 0;
2767
+ }
2768
+ }
2769
+ /** Created timestamp */
2770
+ get createdAt() {
2771
+ return new Date(this._getRaw("created_at"));
2772
+ }
2773
+ /** Updated timestamp */
2774
+ get updatedAt() {
2775
+ return new Date(this._getRaw("updated_at"));
2776
+ }
2777
+ /** Embedding vector for semantic search */
2778
+ get vector() {
2779
+ const raw = this._getRaw("vector");
2780
+ if (!raw) return void 0;
2781
+ try {
2782
+ return JSON.parse(raw);
2783
+ } catch {
2784
+ return void 0;
2785
+ }
2786
+ }
2787
+ /** Model used to generate embedding */
2788
+ get embeddingModel() {
2789
+ const value = this._getRaw("embedding_model");
2790
+ return value ? value : void 0;
2791
+ }
2792
+ /** Token counts and cost */
2793
+ get usage() {
2794
+ const raw = this._getRaw("usage");
2795
+ if (!raw) return void 0;
2796
+ try {
2797
+ return JSON.parse(raw);
2798
+ } catch {
2799
+ return void 0;
2800
+ }
2801
+ }
2802
+ /** Web search sources */
2803
+ get sources() {
2804
+ const raw = this._getRaw("sources");
2805
+ if (!raw) return void 0;
2806
+ try {
2807
+ return JSON.parse(raw);
2808
+ } catch {
2809
+ return void 0;
2810
+ }
2811
+ }
2812
+ /** Response time in seconds */
2813
+ get responseDuration() {
2814
+ const value = this._getRaw("response_duration");
2815
+ return value !== null && value !== void 0 ? value : void 0;
2816
+ }
2817
+ };
2818
+ Message.table = "history";
2819
+ Message.associations = {
2820
+ conversations: { type: "belongs_to", key: "conversation_id" }
2821
+ };
2822
+ var Conversation = class extends import_watermelondb4.Model {
2823
+ /** Unique conversation identifier */
2824
+ get conversationId() {
2825
+ return this._getRaw("conversation_id");
2826
+ }
2827
+ /** Conversation title */
2828
+ get title() {
2829
+ return this._getRaw("title");
2830
+ }
2831
+ /** Created timestamp */
2832
+ get createdAt() {
2833
+ return new Date(this._getRaw("created_at"));
2834
+ }
2835
+ /** Updated timestamp */
2836
+ get updatedAt() {
2837
+ return new Date(this._getRaw("updated_at"));
2838
+ }
2839
+ /** Soft delete flag */
2840
+ get isDeleted() {
2841
+ return this._getRaw("is_deleted");
2842
+ }
2843
+ };
2844
+ Conversation.table = "conversations";
2845
+ Conversation.associations = {
2846
+ history: { type: "has_many", foreignKey: "conversation_id" }
2847
+ };
2848
+
2849
+ // src/lib/memoryStorage/schema.ts
2850
+ var import_watermelondb5 = require("@nozbe/watermelondb");
2851
+ var memoryStorageSchema = (0, import_watermelondb5.appSchema)({
2852
+ version: 1,
2853
+ tables: [
2854
+ (0, import_watermelondb5.tableSchema)({
2855
+ name: "memories",
2856
+ columns: [
2857
+ // Memory type classification
2858
+ { name: "type", type: "string", isIndexed: true },
2859
+ // 'identity' | 'preference' | 'project' | 'skill' | 'constraint'
2860
+ // Hierarchical key structure
2861
+ { name: "namespace", type: "string", isIndexed: true },
2862
+ { name: "key", type: "string", isIndexed: true },
2863
+ { name: "value", type: "string" },
2864
+ // Evidence and confidence
2865
+ { name: "raw_evidence", type: "string" },
2866
+ { name: "confidence", type: "number" },
2867
+ { name: "pii", type: "boolean", isIndexed: true },
2868
+ // Composite keys for efficient lookups
2869
+ { name: "composite_key", type: "string", isIndexed: true },
2870
+ // namespace:key
2871
+ { name: "unique_key", type: "string", isIndexed: true },
2872
+ // namespace:key:value
2873
+ // Timestamps
2874
+ { name: "created_at", type: "number", isIndexed: true },
2875
+ { name: "updated_at", type: "number" },
2876
+ // Vector embeddings for semantic search
2877
+ { name: "embedding", type: "string", isOptional: true },
2878
+ // JSON stringified number[]
2879
+ { name: "embedding_model", type: "string", isOptional: true },
2880
+ // Soft delete flag
2881
+ { name: "is_deleted", type: "boolean", isIndexed: true }
2882
+ ]
2883
+ })
2884
+ ]
2885
+ });
2886
+
2887
+ // src/lib/memoryStorage/models.ts
2888
+ var import_watermelondb6 = require("@nozbe/watermelondb");
2889
+ var Memory = class extends import_watermelondb6.Model {
2890
+ /** Memory type classification */
2891
+ get type() {
2892
+ return this._getRaw("type");
2893
+ }
2894
+ /** Namespace for grouping related memories */
2895
+ get namespace() {
2896
+ return this._getRaw("namespace");
2897
+ }
2898
+ /** Key within the namespace */
2899
+ get key() {
2900
+ return this._getRaw("key");
2901
+ }
2902
+ /** The memory value/content */
2903
+ get value() {
2904
+ return this._getRaw("value");
2905
+ }
2906
+ /** Raw evidence from which this memory was extracted */
2907
+ get rawEvidence() {
2908
+ return this._getRaw("raw_evidence");
2909
+ }
2910
+ /** Confidence score (0-1) */
2911
+ get confidence() {
2912
+ return this._getRaw("confidence");
2913
+ }
2914
+ /** Whether this memory contains PII */
2915
+ get pii() {
2916
+ return this._getRaw("pii");
2917
+ }
2918
+ /** Composite key (namespace:key) for efficient lookups */
2919
+ get compositeKey() {
2920
+ return this._getRaw("composite_key");
2921
+ }
2922
+ /** Unique key (namespace:key:value) for deduplication */
2923
+ get uniqueKey() {
2924
+ return this._getRaw("unique_key");
2925
+ }
2926
+ /** Created timestamp */
2927
+ get createdAt() {
2928
+ return new Date(this._getRaw("created_at"));
2929
+ }
2930
+ /** Updated timestamp */
2931
+ get updatedAt() {
2932
+ return new Date(this._getRaw("updated_at"));
2933
+ }
2934
+ /** Embedding vector for semantic search */
2935
+ get embedding() {
2936
+ const raw = this._getRaw("embedding");
2937
+ if (!raw) return void 0;
2938
+ try {
2939
+ return JSON.parse(raw);
2940
+ } catch {
2941
+ return void 0;
2942
+ }
2943
+ }
2944
+ /** Model used to generate embedding */
2945
+ get embeddingModel() {
2946
+ const value = this._getRaw("embedding_model");
2947
+ return value ? value : void 0;
2948
+ }
2949
+ /** Soft delete flag */
2950
+ get isDeleted() {
2951
+ return this._getRaw("is_deleted");
2952
+ }
2953
+ };
2954
+ Memory.table = "memories";
1324
2955
  // Annotate the CommonJS export names for ESM import in node:
1325
2956
  0 && (module.exports = {
2957
+ ChatConversation,
2958
+ ChatMessage,
2959
+ StoredMemoryModel,
2960
+ chatStorageSchema,
2961
+ generateCompositeKey,
2962
+ generateConversationId,
2963
+ generateUniqueKey,
2964
+ memoryStorageSchema,
1326
2965
  useChat,
2966
+ useChatStorage,
1327
2967
  useImageGeneration,
2968
+ useMemoryStorage,
1328
2969
  useModels
1329
2970
  });