@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.
@@ -1226,7 +1226,8 @@ function useChat(options) {
1226
1226
  model,
1227
1227
  onData,
1228
1228
  runTools = true,
1229
- headers
1229
+ headers,
1230
+ memoryContext
1230
1231
  }) => {
1231
1232
  const messagesValidation = validateMessages(messages);
1232
1233
  if (!messagesValidation.valid) {
@@ -1239,7 +1240,20 @@ function useChat(options) {
1239
1240
  abortControllerRef.current = abortController;
1240
1241
  setIsLoading(true);
1241
1242
  let toolExecutionResult;
1242
- let messagesWithToolContext = messages;
1243
+ let messagesWithContext = messages;
1244
+ if (memoryContext) {
1245
+ const memorySystemMessage = {
1246
+ role: "system",
1247
+ content: [
1248
+ {
1249
+ type: "text",
1250
+ text: memoryContext
1251
+ }
1252
+ ]
1253
+ };
1254
+ messagesWithContext = [memorySystemMessage, ...messages];
1255
+ }
1256
+ let messagesWithToolContext = messagesWithContext;
1243
1257
  const shouldRunTools = runTools && tools && tools.length > 0;
1244
1258
  if (shouldRunTools) {
1245
1259
  const lastUserMessage = [...messages].reverse().find((m) => m.role === "user");
@@ -1622,10 +1636,920 @@ function useEncryption(signMessage) {
1622
1636
  };
1623
1637
  }
1624
1638
 
1625
- // src/react/useMemory.ts
1626
- import { useCallback as useCallback2, useRef as useRef2 } from "react";
1639
+ // src/react/useChatStorage.ts
1640
+ import { useCallback as useCallback2, useState as useState2, useMemo } from "react";
1641
+
1642
+ // src/lib/chatStorage/types.ts
1643
+ function convertUsageToStored(usage) {
1644
+ if (!usage) return void 0;
1645
+ return {
1646
+ promptTokens: usage.prompt_tokens,
1647
+ completionTokens: usage.completion_tokens,
1648
+ totalTokens: usage.total_tokens,
1649
+ costMicroUsd: usage.cost_micro_usd
1650
+ };
1651
+ }
1652
+ function generateConversationId() {
1653
+ return `conv_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1654
+ }
1655
+
1656
+ // src/lib/chatStorage/operations.ts
1657
+ import { Q } from "@nozbe/watermelondb";
1658
+ function messageToStored(message) {
1659
+ return {
1660
+ uniqueId: message.id,
1661
+ messageId: message.messageId,
1662
+ conversationId: message.conversationId,
1663
+ role: message.role,
1664
+ content: message.content,
1665
+ model: message.model,
1666
+ files: message.files,
1667
+ createdAt: message.createdAt,
1668
+ updatedAt: message.updatedAt,
1669
+ vector: message.vector,
1670
+ embeddingModel: message.embeddingModel,
1671
+ usage: message.usage,
1672
+ sources: message.sources,
1673
+ responseDuration: message.responseDuration
1674
+ };
1675
+ }
1676
+ function conversationToStored(conversation) {
1677
+ return {
1678
+ uniqueId: conversation.id,
1679
+ conversationId: conversation.conversationId,
1680
+ title: conversation.title,
1681
+ createdAt: conversation.createdAt,
1682
+ updatedAt: conversation.updatedAt,
1683
+ isDeleted: conversation.isDeleted
1684
+ };
1685
+ }
1686
+ async function createConversationOp(ctx, opts, defaultTitle = "New Conversation") {
1687
+ const convId = opts?.conversationId || generateConversationId();
1688
+ const title = opts?.title || defaultTitle;
1689
+ const created = await ctx.database.write(async () => {
1690
+ return await ctx.conversationsCollection.create((conv) => {
1691
+ conv._setRaw("conversation_id", convId);
1692
+ conv._setRaw("title", title);
1693
+ conv._setRaw("is_deleted", false);
1694
+ });
1695
+ });
1696
+ return conversationToStored(created);
1697
+ }
1698
+ async function getConversationOp(ctx, id) {
1699
+ const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
1700
+ return results.length > 0 ? conversationToStored(results[0]) : null;
1701
+ }
1702
+ async function getConversationsOp(ctx) {
1703
+ const results = await ctx.conversationsCollection.query(Q.where("is_deleted", false), Q.sortBy("created_at", Q.desc)).fetch();
1704
+ return results.map(conversationToStored);
1705
+ }
1706
+ async function updateConversationTitleOp(ctx, id, title) {
1707
+ const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
1708
+ if (results.length > 0) {
1709
+ await ctx.database.write(async () => {
1710
+ await results[0].update((conv) => {
1711
+ conv._setRaw("title", title);
1712
+ });
1713
+ });
1714
+ return true;
1715
+ }
1716
+ return false;
1717
+ }
1718
+ async function deleteConversationOp(ctx, id) {
1719
+ const results = await ctx.conversationsCollection.query(Q.where("conversation_id", id), Q.where("is_deleted", false)).fetch();
1720
+ if (results.length > 0) {
1721
+ await ctx.database.write(async () => {
1722
+ await results[0].update((conv) => {
1723
+ conv._setRaw("is_deleted", true);
1724
+ });
1725
+ });
1726
+ return true;
1727
+ }
1728
+ return false;
1729
+ }
1730
+ async function getMessagesOp(ctx, convId) {
1731
+ const results = await ctx.messagesCollection.query(Q.where("conversation_id", convId), Q.sortBy("message_id", Q.asc)).fetch();
1732
+ return results.map(messageToStored);
1733
+ }
1734
+ async function getMessageCountOp(ctx, convId) {
1735
+ return await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetchCount();
1736
+ }
1737
+ async function clearMessagesOp(ctx, convId) {
1738
+ const messages = await ctx.messagesCollection.query(Q.where("conversation_id", convId)).fetch();
1739
+ await ctx.database.write(async () => {
1740
+ for (const message of messages) {
1741
+ await message.destroyPermanently();
1742
+ }
1743
+ });
1744
+ }
1745
+ async function createMessageOp(ctx, opts) {
1746
+ const existingCount = await getMessageCountOp(ctx, opts.conversationId);
1747
+ const messageId = existingCount + 1;
1748
+ const created = await ctx.database.write(async () => {
1749
+ return await ctx.messagesCollection.create((msg) => {
1750
+ msg._setRaw("message_id", messageId);
1751
+ msg._setRaw("conversation_id", opts.conversationId);
1752
+ msg._setRaw("role", opts.role);
1753
+ msg._setRaw("content", opts.content);
1754
+ if (opts.model) msg._setRaw("model", opts.model);
1755
+ if (opts.files) msg._setRaw("files", JSON.stringify(opts.files));
1756
+ if (opts.usage) msg._setRaw("usage", JSON.stringify(opts.usage));
1757
+ if (opts.sources) msg._setRaw("sources", JSON.stringify(opts.sources));
1758
+ if (opts.responseDuration !== void 0)
1759
+ msg._setRaw("response_duration", opts.responseDuration);
1760
+ if (opts.vector) msg._setRaw("vector", JSON.stringify(opts.vector));
1761
+ if (opts.embeddingModel) msg._setRaw("embedding_model", opts.embeddingModel);
1762
+ });
1763
+ });
1764
+ return messageToStored(created);
1765
+ }
1766
+ async function updateMessageEmbeddingOp(ctx, uniqueId, vector, embeddingModel) {
1767
+ let message;
1768
+ try {
1769
+ message = await ctx.messagesCollection.find(uniqueId);
1770
+ } catch {
1771
+ return null;
1772
+ }
1773
+ await ctx.database.write(async () => {
1774
+ await message.update((msg) => {
1775
+ msg._setRaw("vector", JSON.stringify(vector));
1776
+ msg._setRaw("embedding_model", embeddingModel);
1777
+ });
1778
+ });
1779
+ return messageToStored(message);
1780
+ }
1781
+ function cosineSimilarity(a, b) {
1782
+ if (a.length !== b.length) return 0;
1783
+ let dotProduct = 0;
1784
+ let normA = 0;
1785
+ let normB = 0;
1786
+ for (let i = 0; i < a.length; i++) {
1787
+ dotProduct += a[i] * b[i];
1788
+ normA += a[i] * a[i];
1789
+ normB += b[i] * b[i];
1790
+ }
1791
+ const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
1792
+ return magnitude === 0 ? 0 : dotProduct / magnitude;
1793
+ }
1794
+ async function searchMessagesOp(ctx, queryVector, options) {
1795
+ const { limit = 10, minSimilarity = 0.5, conversationId } = options || {};
1796
+ const activeConversations = await ctx.conversationsCollection.query(Q.where("is_deleted", false)).fetch();
1797
+ const activeConversationIds = new Set(
1798
+ activeConversations.map((c) => c.conversationId)
1799
+ );
1800
+ const queryConditions = conversationId ? [Q.where("conversation_id", conversationId)] : [];
1801
+ const messages = await ctx.messagesCollection.query(...queryConditions).fetch();
1802
+ const resultsWithSimilarity = [];
1803
+ for (const message of messages) {
1804
+ if (!activeConversationIds.has(message.conversationId)) continue;
1805
+ const messageVector = message.vector;
1806
+ if (!messageVector || messageVector.length === 0) continue;
1807
+ const similarity = cosineSimilarity(queryVector, messageVector);
1808
+ if (similarity >= minSimilarity) {
1809
+ resultsWithSimilarity.push({
1810
+ ...messageToStored(message),
1811
+ similarity
1812
+ });
1813
+ }
1814
+ }
1815
+ return resultsWithSimilarity.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
1816
+ }
1817
+
1818
+ // src/react/useChatStorage.ts
1819
+ function storedToLlmapiMessage(stored) {
1820
+ const content = [
1821
+ { type: "text", text: stored.content }
1822
+ ];
1823
+ if (stored.files?.length) {
1824
+ for (const file of stored.files) {
1825
+ if (file.url) {
1826
+ content.push({
1827
+ type: "image_url",
1828
+ image_url: { url: file.url }
1829
+ });
1830
+ }
1831
+ }
1832
+ }
1833
+ return {
1834
+ role: stored.role,
1835
+ content
1836
+ };
1837
+ }
1838
+ function useChatStorage(options) {
1839
+ const {
1840
+ database,
1841
+ conversationId: initialConversationId,
1842
+ autoCreateConversation = true,
1843
+ defaultConversationTitle = "New Conversation",
1844
+ getToken,
1845
+ baseUrl,
1846
+ onData,
1847
+ onFinish,
1848
+ onError,
1849
+ chatProvider,
1850
+ localModel,
1851
+ tools,
1852
+ toolSelectorModel,
1853
+ onToolExecution
1854
+ } = options;
1855
+ const [currentConversationId, setCurrentConversationId] = useState2(initialConversationId || null);
1856
+ const messagesCollection = useMemo(
1857
+ () => database.get("history"),
1858
+ [database]
1859
+ );
1860
+ const conversationsCollection = useMemo(
1861
+ () => database.get("conversations"),
1862
+ [database]
1863
+ );
1864
+ const storageCtx = useMemo(
1865
+ () => ({
1866
+ database,
1867
+ messagesCollection,
1868
+ conversationsCollection
1869
+ }),
1870
+ [database, messagesCollection, conversationsCollection]
1871
+ );
1872
+ const {
1873
+ isLoading,
1874
+ isSelectingTool,
1875
+ sendMessage: baseSendMessage,
1876
+ stop
1877
+ } = useChat({
1878
+ getToken,
1879
+ baseUrl,
1880
+ onData,
1881
+ onFinish,
1882
+ onError,
1883
+ chatProvider,
1884
+ localModel,
1885
+ tools,
1886
+ toolSelectorModel,
1887
+ onToolExecution
1888
+ });
1889
+ const createConversation = useCallback2(
1890
+ async (opts) => {
1891
+ const created = await createConversationOp(
1892
+ storageCtx,
1893
+ opts,
1894
+ defaultConversationTitle
1895
+ );
1896
+ setCurrentConversationId(created.conversationId);
1897
+ return created;
1898
+ },
1899
+ [storageCtx, defaultConversationTitle]
1900
+ );
1901
+ const getConversation = useCallback2(
1902
+ async (id) => {
1903
+ return getConversationOp(storageCtx, id);
1904
+ },
1905
+ [storageCtx]
1906
+ );
1907
+ const getConversations = useCallback2(async () => {
1908
+ return getConversationsOp(storageCtx);
1909
+ }, [storageCtx]);
1910
+ const updateConversationTitle = useCallback2(
1911
+ async (id, title) => {
1912
+ return updateConversationTitleOp(storageCtx, id, title);
1913
+ },
1914
+ [storageCtx]
1915
+ );
1916
+ const deleteConversation = useCallback2(
1917
+ async (id) => {
1918
+ const deleted = await deleteConversationOp(storageCtx, id);
1919
+ if (deleted && currentConversationId === id) {
1920
+ setCurrentConversationId(null);
1921
+ }
1922
+ return deleted;
1923
+ },
1924
+ [storageCtx, currentConversationId]
1925
+ );
1926
+ const getMessages = useCallback2(
1927
+ async (convId) => {
1928
+ return getMessagesOp(storageCtx, convId);
1929
+ },
1930
+ [storageCtx]
1931
+ );
1932
+ const getMessageCount = useCallback2(
1933
+ async (convId) => {
1934
+ return getMessageCountOp(storageCtx, convId);
1935
+ },
1936
+ [storageCtx]
1937
+ );
1938
+ const clearMessages = useCallback2(
1939
+ async (convId) => {
1940
+ return clearMessagesOp(storageCtx, convId);
1941
+ },
1942
+ [storageCtx]
1943
+ );
1944
+ const ensureConversation = useCallback2(async () => {
1945
+ if (currentConversationId) {
1946
+ const existing = await getConversation(currentConversationId);
1947
+ if (existing) {
1948
+ return currentConversationId;
1949
+ }
1950
+ if (autoCreateConversation) {
1951
+ const newConv = await createConversation({
1952
+ conversationId: currentConversationId
1953
+ });
1954
+ return newConv.conversationId;
1955
+ }
1956
+ }
1957
+ if (autoCreateConversation) {
1958
+ const newConv = await createConversation();
1959
+ return newConv.conversationId;
1960
+ }
1961
+ throw new Error(
1962
+ "No conversation ID provided and autoCreateConversation is disabled"
1963
+ );
1964
+ }, [
1965
+ currentConversationId,
1966
+ getConversation,
1967
+ autoCreateConversation,
1968
+ createConversation
1969
+ ]);
1970
+ const sendMessage = useCallback2(
1971
+ async (args) => {
1972
+ const {
1973
+ content,
1974
+ model,
1975
+ messages: providedMessages,
1976
+ includeHistory = true,
1977
+ maxHistoryMessages = 50,
1978
+ files,
1979
+ onData: perRequestOnData,
1980
+ runTools,
1981
+ headers,
1982
+ memoryContext
1983
+ } = args;
1984
+ let convId;
1985
+ try {
1986
+ convId = await ensureConversation();
1987
+ } catch (err) {
1988
+ return {
1989
+ data: null,
1990
+ error: err instanceof Error ? err.message : "Failed to ensure conversation"
1991
+ };
1992
+ }
1993
+ let messagesToSend = [];
1994
+ if (includeHistory && !providedMessages) {
1995
+ const storedMessages = await getMessages(convId);
1996
+ const limitedMessages = storedMessages.slice(-maxHistoryMessages);
1997
+ messagesToSend = limitedMessages.map(storedToLlmapiMessage);
1998
+ } else if (providedMessages) {
1999
+ messagesToSend = providedMessages;
2000
+ }
2001
+ const userMessageContent = [
2002
+ { type: "text", text: content }
2003
+ ];
2004
+ if (files && files.length > 0) {
2005
+ for (const file of files) {
2006
+ if (file.url && file.type.startsWith("image/")) {
2007
+ userMessageContent.push({
2008
+ type: "image_url",
2009
+ image_url: { url: file.url }
2010
+ });
2011
+ }
2012
+ }
2013
+ }
2014
+ const userMessage = {
2015
+ role: "user",
2016
+ content: userMessageContent
2017
+ };
2018
+ messagesToSend.push(userMessage);
2019
+ const sanitizedFiles = files?.map((file) => ({
2020
+ id: file.id,
2021
+ name: file.name,
2022
+ type: file.type,
2023
+ size: file.size,
2024
+ // Only keep URL if it's not a data URI (e.g., external URLs)
2025
+ url: file.url && !file.url.startsWith("data:") ? file.url : void 0
2026
+ }));
2027
+ let storedUserMessage;
2028
+ try {
2029
+ storedUserMessage = await createMessageOp(storageCtx, {
2030
+ conversationId: convId,
2031
+ role: "user",
2032
+ content,
2033
+ files: sanitizedFiles
2034
+ });
2035
+ } catch (err) {
2036
+ return {
2037
+ data: null,
2038
+ error: err instanceof Error ? err.message : "Failed to store user message"
2039
+ };
2040
+ }
2041
+ const startTime = Date.now();
2042
+ const result = await baseSendMessage({
2043
+ messages: messagesToSend,
2044
+ model,
2045
+ onData: perRequestOnData,
2046
+ runTools,
2047
+ headers,
2048
+ memoryContext
2049
+ });
2050
+ const responseDuration = (Date.now() - startTime) / 1e3;
2051
+ if (result.error || !result.data) {
2052
+ return {
2053
+ data: null,
2054
+ error: result.error || "No response data received",
2055
+ toolExecution: result.toolExecution,
2056
+ userMessage: storedUserMessage
2057
+ };
2058
+ }
2059
+ const responseData = result.data;
2060
+ const assistantContent = responseData.choices?.[0]?.message?.content?.map((part) => part.text || "").join("") || "";
2061
+ let storedAssistantMessage;
2062
+ try {
2063
+ storedAssistantMessage = await createMessageOp(storageCtx, {
2064
+ conversationId: convId,
2065
+ role: "assistant",
2066
+ content: assistantContent,
2067
+ model: responseData.model,
2068
+ usage: convertUsageToStored(responseData.usage),
2069
+ responseDuration
2070
+ });
2071
+ } catch (err) {
2072
+ return {
2073
+ data: null,
2074
+ error: err instanceof Error ? err.message : "Failed to store assistant message",
2075
+ toolExecution: result.toolExecution,
2076
+ userMessage: storedUserMessage
2077
+ };
2078
+ }
2079
+ return {
2080
+ data: responseData,
2081
+ error: null,
2082
+ toolExecution: result.toolExecution,
2083
+ userMessage: storedUserMessage,
2084
+ assistantMessage: storedAssistantMessage
2085
+ };
2086
+ },
2087
+ [ensureConversation, getMessages, storageCtx, baseSendMessage]
2088
+ );
2089
+ const searchMessages = useCallback2(
2090
+ async (queryVector, options2) => {
2091
+ return searchMessagesOp(storageCtx, queryVector, options2);
2092
+ },
2093
+ [storageCtx]
2094
+ );
2095
+ const updateMessageEmbedding = useCallback2(
2096
+ async (uniqueId, vector, embeddingModel) => {
2097
+ return updateMessageEmbeddingOp(storageCtx, uniqueId, vector, embeddingModel);
2098
+ },
2099
+ [storageCtx]
2100
+ );
2101
+ return {
2102
+ isLoading,
2103
+ isSelectingTool,
2104
+ sendMessage,
2105
+ stop,
2106
+ conversationId: currentConversationId,
2107
+ setConversationId: setCurrentConversationId,
2108
+ createConversation,
2109
+ getConversation,
2110
+ getConversations,
2111
+ updateConversationTitle,
2112
+ deleteConversation,
2113
+ getMessages,
2114
+ getMessageCount,
2115
+ clearMessages,
2116
+ searchMessages,
2117
+ updateMessageEmbedding
2118
+ };
2119
+ }
2120
+
2121
+ // src/lib/chatStorage/schema.ts
2122
+ import { appSchema, tableSchema } from "@nozbe/watermelondb";
2123
+ var chatStorageSchema = appSchema({
2124
+ version: 1,
2125
+ tables: [
2126
+ tableSchema({
2127
+ name: "history",
2128
+ columns: [
2129
+ { name: "message_id", type: "number" },
2130
+ // Sequential ID within conversation
2131
+ { name: "conversation_id", type: "string", isIndexed: true },
2132
+ { name: "role", type: "string", isIndexed: true },
2133
+ // 'user' | 'assistant' | 'system'
2134
+ { name: "content", type: "string" },
2135
+ { name: "model", type: "string", isOptional: true },
2136
+ { name: "files", type: "string", isOptional: true },
2137
+ // JSON stringified FileMetadata[]
2138
+ { name: "created_at", type: "number", isIndexed: true },
2139
+ { name: "updated_at", type: "number" },
2140
+ { name: "vector", type: "string", isOptional: true },
2141
+ // JSON stringified number[]
2142
+ { name: "embedding_model", type: "string", isOptional: true },
2143
+ { name: "usage", type: "string", isOptional: true },
2144
+ // JSON stringified ChatCompletionUsage
2145
+ { name: "sources", type: "string", isOptional: true },
2146
+ // JSON stringified SearchSource[]
2147
+ { name: "response_duration", type: "number", isOptional: true }
2148
+ ]
2149
+ }),
2150
+ tableSchema({
2151
+ name: "conversations",
2152
+ columns: [
2153
+ { name: "conversation_id", type: "string", isIndexed: true },
2154
+ { name: "title", type: "string" },
2155
+ { name: "created_at", type: "number" },
2156
+ { name: "updated_at", type: "number" },
2157
+ { name: "is_deleted", type: "boolean", isIndexed: true }
2158
+ ]
2159
+ })
2160
+ ]
2161
+ });
2162
+
2163
+ // src/lib/chatStorage/models.ts
2164
+ import { Model } from "@nozbe/watermelondb";
2165
+ var Message = class extends Model {
2166
+ /** Sequential message ID within conversation */
2167
+ get messageId() {
2168
+ return this._getRaw("message_id");
2169
+ }
2170
+ /** Links message to its conversation */
2171
+ get conversationId() {
2172
+ return this._getRaw("conversation_id");
2173
+ }
2174
+ /** Who sent the message: 'user' | 'assistant' | 'system' */
2175
+ get role() {
2176
+ return this._getRaw("role");
2177
+ }
2178
+ /** The message text content */
2179
+ get content() {
2180
+ return this._getRaw("content");
2181
+ }
2182
+ /** LLM model used (e.g., GPT-4, Claude) */
2183
+ get model() {
2184
+ const value = this._getRaw("model");
2185
+ return value ? value : void 0;
2186
+ }
2187
+ /** Optional attached files */
2188
+ get files() {
2189
+ const raw = this._getRaw("files");
2190
+ if (!raw) return void 0;
2191
+ try {
2192
+ return JSON.parse(raw);
2193
+ } catch {
2194
+ return void 0;
2195
+ }
2196
+ }
2197
+ /** Created timestamp */
2198
+ get createdAt() {
2199
+ return new Date(this._getRaw("created_at"));
2200
+ }
2201
+ /** Updated timestamp */
2202
+ get updatedAt() {
2203
+ return new Date(this._getRaw("updated_at"));
2204
+ }
2205
+ /** Embedding vector for semantic search */
2206
+ get vector() {
2207
+ const raw = this._getRaw("vector");
2208
+ if (!raw) return void 0;
2209
+ try {
2210
+ return JSON.parse(raw);
2211
+ } catch {
2212
+ return void 0;
2213
+ }
2214
+ }
2215
+ /** Model used to generate embedding */
2216
+ get embeddingModel() {
2217
+ const value = this._getRaw("embedding_model");
2218
+ return value ? value : void 0;
2219
+ }
2220
+ /** Token counts and cost */
2221
+ get usage() {
2222
+ const raw = this._getRaw("usage");
2223
+ if (!raw) return void 0;
2224
+ try {
2225
+ return JSON.parse(raw);
2226
+ } catch {
2227
+ return void 0;
2228
+ }
2229
+ }
2230
+ /** Web search sources */
2231
+ get sources() {
2232
+ const raw = this._getRaw("sources");
2233
+ if (!raw) return void 0;
2234
+ try {
2235
+ return JSON.parse(raw);
2236
+ } catch {
2237
+ return void 0;
2238
+ }
2239
+ }
2240
+ /** Response time in seconds */
2241
+ get responseDuration() {
2242
+ const value = this._getRaw("response_duration");
2243
+ return value !== null && value !== void 0 ? value : void 0;
2244
+ }
2245
+ };
2246
+ Message.table = "history";
2247
+ Message.associations = {
2248
+ conversations: { type: "belongs_to", key: "conversation_id" }
2249
+ };
2250
+ var Conversation = class extends Model {
2251
+ /** Unique conversation identifier */
2252
+ get conversationId() {
2253
+ return this._getRaw("conversation_id");
2254
+ }
2255
+ /** Conversation title */
2256
+ get title() {
2257
+ return this._getRaw("title");
2258
+ }
2259
+ /** Created timestamp */
2260
+ get createdAt() {
2261
+ return new Date(this._getRaw("created_at"));
2262
+ }
2263
+ /** Updated timestamp */
2264
+ get updatedAt() {
2265
+ return new Date(this._getRaw("updated_at"));
2266
+ }
2267
+ /** Soft delete flag */
2268
+ get isDeleted() {
2269
+ return this._getRaw("is_deleted");
2270
+ }
2271
+ };
2272
+ Conversation.table = "conversations";
2273
+ Conversation.associations = {
2274
+ history: { type: "has_many", foreignKey: "conversation_id" }
2275
+ };
2276
+
2277
+ // src/react/useMemoryStorage.ts
2278
+ import { useCallback as useCallback3, useState as useState3, useMemo as useMemo2, useRef as useRef2 } from "react";
1627
2279
  import { postApiV1ChatCompletions } from "@reverbia/sdk";
1628
2280
 
2281
+ // src/lib/memoryStorage/operations.ts
2282
+ import { Q as Q2 } from "@nozbe/watermelondb";
2283
+
2284
+ // src/lib/memoryStorage/types.ts
2285
+ function generateCompositeKey(namespace, key) {
2286
+ return `${namespace}:${key}`;
2287
+ }
2288
+ function generateUniqueKey(namespace, key, value) {
2289
+ return `${namespace}:${key}:${value}`;
2290
+ }
2291
+ function cosineSimilarity2(a, b) {
2292
+ if (a.length !== b.length) {
2293
+ throw new Error("Vectors must have the same length");
2294
+ }
2295
+ let dotProduct = 0;
2296
+ let normA = 0;
2297
+ let normB = 0;
2298
+ for (let i = 0; i < a.length; i++) {
2299
+ dotProduct += a[i] * b[i];
2300
+ normA += a[i] * a[i];
2301
+ normB += b[i] * b[i];
2302
+ }
2303
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
2304
+ if (denominator === 0) {
2305
+ return 0;
2306
+ }
2307
+ return dotProduct / denominator;
2308
+ }
2309
+
2310
+ // src/lib/memoryStorage/operations.ts
2311
+ function memoryToStored(memory) {
2312
+ return {
2313
+ uniqueId: memory.id,
2314
+ type: memory.type,
2315
+ namespace: memory.namespace,
2316
+ key: memory.key,
2317
+ value: memory.value,
2318
+ rawEvidence: memory.rawEvidence,
2319
+ confidence: memory.confidence,
2320
+ pii: memory.pii,
2321
+ compositeKey: memory.compositeKey,
2322
+ uniqueKey: memory.uniqueKey,
2323
+ createdAt: memory.createdAt,
2324
+ updatedAt: memory.updatedAt,
2325
+ embedding: memory.embedding,
2326
+ embeddingModel: memory.embeddingModel,
2327
+ isDeleted: memory.isDeleted
2328
+ };
2329
+ }
2330
+ async function getAllMemoriesOp(ctx) {
2331
+ const results = await ctx.memoriesCollection.query(Q2.where("is_deleted", false), Q2.sortBy("created_at", Q2.desc)).fetch();
2332
+ return results.map(memoryToStored);
2333
+ }
2334
+ async function getMemoryByIdOp(ctx, id) {
2335
+ try {
2336
+ const memory = await ctx.memoriesCollection.find(id);
2337
+ if (memory.isDeleted) return null;
2338
+ return memoryToStored(memory);
2339
+ } catch {
2340
+ return null;
2341
+ }
2342
+ }
2343
+ async function getMemoriesByNamespaceOp(ctx, namespace) {
2344
+ const results = await ctx.memoriesCollection.query(
2345
+ Q2.where("namespace", namespace),
2346
+ Q2.where("is_deleted", false),
2347
+ Q2.sortBy("created_at", Q2.desc)
2348
+ ).fetch();
2349
+ return results.map(memoryToStored);
2350
+ }
2351
+ async function getMemoriesByKeyOp(ctx, namespace, key) {
2352
+ const compositeKey = generateCompositeKey(namespace, key);
2353
+ const results = await ctx.memoriesCollection.query(
2354
+ Q2.where("composite_key", compositeKey),
2355
+ Q2.where("is_deleted", false),
2356
+ Q2.sortBy("created_at", Q2.desc)
2357
+ ).fetch();
2358
+ return results.map(memoryToStored);
2359
+ }
2360
+ async function saveMemoryOp(ctx, opts) {
2361
+ const compositeKey = generateCompositeKey(opts.namespace, opts.key);
2362
+ const uniqueKey = generateUniqueKey(opts.namespace, opts.key, opts.value);
2363
+ const result = await ctx.database.write(async () => {
2364
+ const existing = await ctx.memoriesCollection.query(Q2.where("unique_key", uniqueKey)).fetch();
2365
+ if (existing.length > 0) {
2366
+ const existingMemory = existing[0];
2367
+ 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;
2368
+ await existingMemory.update((mem) => {
2369
+ mem._setRaw("type", opts.type);
2370
+ mem._setRaw("namespace", opts.namespace);
2371
+ mem._setRaw("key", opts.key);
2372
+ mem._setRaw("value", opts.value);
2373
+ mem._setRaw("raw_evidence", opts.rawEvidence);
2374
+ mem._setRaw("confidence", opts.confidence);
2375
+ mem._setRaw("pii", opts.pii);
2376
+ mem._setRaw("composite_key", compositeKey);
2377
+ mem._setRaw("unique_key", uniqueKey);
2378
+ mem._setRaw("is_deleted", false);
2379
+ if (shouldPreserveEmbedding) {
2380
+ } else if (opts.embedding) {
2381
+ mem._setRaw("embedding", JSON.stringify(opts.embedding));
2382
+ if (opts.embeddingModel) {
2383
+ mem._setRaw("embedding_model", opts.embeddingModel);
2384
+ }
2385
+ } else {
2386
+ mem._setRaw("embedding", null);
2387
+ mem._setRaw("embedding_model", null);
2388
+ }
2389
+ });
2390
+ return existingMemory;
2391
+ }
2392
+ return await ctx.memoriesCollection.create((mem) => {
2393
+ mem._setRaw("type", opts.type);
2394
+ mem._setRaw("namespace", opts.namespace);
2395
+ mem._setRaw("key", opts.key);
2396
+ mem._setRaw("value", opts.value);
2397
+ mem._setRaw("raw_evidence", opts.rawEvidence);
2398
+ mem._setRaw("confidence", opts.confidence);
2399
+ mem._setRaw("pii", opts.pii);
2400
+ mem._setRaw("composite_key", compositeKey);
2401
+ mem._setRaw("unique_key", uniqueKey);
2402
+ mem._setRaw("is_deleted", false);
2403
+ if (opts.embedding) {
2404
+ mem._setRaw("embedding", JSON.stringify(opts.embedding));
2405
+ if (opts.embeddingModel) {
2406
+ mem._setRaw("embedding_model", opts.embeddingModel);
2407
+ }
2408
+ }
2409
+ });
2410
+ });
2411
+ return memoryToStored(result);
2412
+ }
2413
+ async function saveMemoriesOp(ctx, memories) {
2414
+ const results = [];
2415
+ for (const memory of memories) {
2416
+ const saved = await saveMemoryOp(ctx, memory);
2417
+ results.push(saved);
2418
+ }
2419
+ return results;
2420
+ }
2421
+ async function updateMemoryOp(ctx, id, updates) {
2422
+ let memory;
2423
+ try {
2424
+ memory = await ctx.memoriesCollection.find(id);
2425
+ } catch {
2426
+ return { ok: false, reason: "not_found" };
2427
+ }
2428
+ if (memory.isDeleted) {
2429
+ return { ok: false, reason: "not_found" };
2430
+ }
2431
+ const newNamespace = updates.namespace ?? memory.namespace;
2432
+ const newKey = updates.key ?? memory.key;
2433
+ const newValue = updates.value ?? memory.value;
2434
+ const newCompositeKey = generateCompositeKey(newNamespace, newKey);
2435
+ const newUniqueKey = generateUniqueKey(newNamespace, newKey, newValue);
2436
+ if (newUniqueKey !== memory.uniqueKey) {
2437
+ const existing = await ctx.memoriesCollection.query(Q2.where("unique_key", newUniqueKey), Q2.where("is_deleted", false)).fetch();
2438
+ if (existing.length > 0) {
2439
+ return { ok: false, reason: "conflict", conflictingKey: newUniqueKey };
2440
+ }
2441
+ }
2442
+ try {
2443
+ const updated = await ctx.database.write(async () => {
2444
+ await memory.update((mem) => {
2445
+ if (updates.type !== void 0) mem._setRaw("type", updates.type);
2446
+ if (updates.namespace !== void 0)
2447
+ mem._setRaw("namespace", updates.namespace);
2448
+ if (updates.key !== void 0) mem._setRaw("key", updates.key);
2449
+ if (updates.value !== void 0) mem._setRaw("value", updates.value);
2450
+ if (updates.rawEvidence !== void 0)
2451
+ mem._setRaw("raw_evidence", updates.rawEvidence);
2452
+ if (updates.confidence !== void 0)
2453
+ mem._setRaw("confidence", updates.confidence);
2454
+ if (updates.pii !== void 0) mem._setRaw("pii", updates.pii);
2455
+ if (updates.namespace !== void 0 || updates.key !== void 0 || updates.value !== void 0) {
2456
+ mem._setRaw("composite_key", newCompositeKey);
2457
+ mem._setRaw("unique_key", newUniqueKey);
2458
+ }
2459
+ if (updates.embedding !== void 0) {
2460
+ mem._setRaw(
2461
+ "embedding",
2462
+ updates.embedding ? JSON.stringify(updates.embedding) : null
2463
+ );
2464
+ }
2465
+ if (updates.embeddingModel !== void 0) {
2466
+ mem._setRaw("embedding_model", updates.embeddingModel || null);
2467
+ }
2468
+ });
2469
+ return memory;
2470
+ });
2471
+ return { ok: true, memory: memoryToStored(updated) };
2472
+ } catch (err) {
2473
+ return {
2474
+ ok: false,
2475
+ reason: "error",
2476
+ error: err instanceof Error ? err : new Error(String(err))
2477
+ };
2478
+ }
2479
+ }
2480
+ async function deleteMemoryByIdOp(ctx, id) {
2481
+ try {
2482
+ const memory = await ctx.memoriesCollection.find(id);
2483
+ await ctx.database.write(async () => {
2484
+ await memory.update((mem) => {
2485
+ mem._setRaw("is_deleted", true);
2486
+ });
2487
+ });
2488
+ } catch {
2489
+ }
2490
+ }
2491
+ async function deleteMemoryOp(ctx, namespace, key, value) {
2492
+ const uniqueKey = generateUniqueKey(namespace, key, value);
2493
+ const results = await ctx.memoriesCollection.query(Q2.where("unique_key", uniqueKey)).fetch();
2494
+ if (results.length > 0) {
2495
+ await ctx.database.write(async () => {
2496
+ await results[0].update((mem) => {
2497
+ mem._setRaw("is_deleted", true);
2498
+ });
2499
+ });
2500
+ }
2501
+ }
2502
+ async function deleteMemoriesByKeyOp(ctx, namespace, key) {
2503
+ const compositeKey = generateCompositeKey(namespace, key);
2504
+ const results = await ctx.memoriesCollection.query(Q2.where("composite_key", compositeKey), Q2.where("is_deleted", false)).fetch();
2505
+ await ctx.database.write(async () => {
2506
+ for (const memory of results) {
2507
+ await memory.update((mem) => {
2508
+ mem._setRaw("is_deleted", true);
2509
+ });
2510
+ }
2511
+ });
2512
+ }
2513
+ async function clearAllMemoriesOp(ctx) {
2514
+ const results = await ctx.memoriesCollection.query(Q2.where("is_deleted", false)).fetch();
2515
+ await ctx.database.write(async () => {
2516
+ for (const memory of results) {
2517
+ await memory.update((mem) => {
2518
+ mem._setRaw("is_deleted", true);
2519
+ });
2520
+ }
2521
+ });
2522
+ }
2523
+ async function searchSimilarMemoriesOp(ctx, queryEmbedding, limit = 10, minSimilarity = 0.6) {
2524
+ const allMemories = await ctx.memoriesCollection.query(Q2.where("is_deleted", false)).fetch();
2525
+ const memoriesWithEmbeddings = allMemories.filter(
2526
+ (m) => m.embedding && m.embedding.length > 0
2527
+ );
2528
+ if (memoriesWithEmbeddings.length === 0) {
2529
+ return [];
2530
+ }
2531
+ const results = memoriesWithEmbeddings.map((memory) => {
2532
+ const similarity = cosineSimilarity2(queryEmbedding, memory.embedding);
2533
+ return {
2534
+ ...memoryToStored(memory),
2535
+ similarity
2536
+ };
2537
+ }).filter((result) => result.similarity >= minSimilarity).sort((a, b) => b.similarity - a.similarity).slice(0, limit);
2538
+ return results;
2539
+ }
2540
+ async function updateMemoryEmbeddingOp(ctx, id, embedding, embeddingModel) {
2541
+ try {
2542
+ const memory = await ctx.memoriesCollection.find(id);
2543
+ await ctx.database.write(async () => {
2544
+ await memory.update((mem) => {
2545
+ mem._setRaw("embedding", JSON.stringify(embedding));
2546
+ mem._setRaw("embedding_model", embeddingModel);
2547
+ });
2548
+ });
2549
+ } catch {
2550
+ }
2551
+ }
2552
+
1629
2553
  // src/lib/memory/service.ts
1630
2554
  var FACT_EXTRACTION_PROMPT = `You are a memory extraction system. Extract durable user memories from chat messages.
1631
2555
 
@@ -1755,180 +2679,6 @@ var preprocessMemories = (items, minConfidence = 0.6) => {
1755
2679
  return Array.from(deduplicatedMap.values());
1756
2680
  };
1757
2681
 
1758
- // src/lib/memory/db.ts
1759
- import Dexie from "dexie";
1760
- var MemoryDatabase = class extends Dexie {
1761
- constructor() {
1762
- super("MemoryDatabase");
1763
- this.version(2).stores({
1764
- memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
1765
- });
1766
- this.version(3).stores({
1767
- memories: "++id, uniqueKey, compositeKey, namespace, key, type, createdAt, updatedAt"
1768
- });
1769
- }
1770
- };
1771
- var memoryDb = new MemoryDatabase();
1772
- var saveMemory = async (memory) => {
1773
- const compositeKey = `${memory.namespace}:${memory.key}`;
1774
- const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
1775
- const now = Date.now();
1776
- const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
1777
- if (existing) {
1778
- const shouldPreserveEmbedding = existing.value === memory.value && existing.rawEvidence === memory.rawEvidence && existing.type === memory.type && existing.namespace === memory.namespace && existing.key === memory.key && existing.embedding !== void 0 && existing.embedding.length > 0;
1779
- const updateData = {
1780
- ...memory,
1781
- compositeKey,
1782
- uniqueKey,
1783
- updatedAt: now,
1784
- createdAt: existing.createdAt
1785
- };
1786
- if (shouldPreserveEmbedding) {
1787
- updateData.embedding = existing.embedding;
1788
- updateData.embeddingModel = existing.embeddingModel;
1789
- } else {
1790
- updateData.embedding = [];
1791
- updateData.embeddingModel = void 0;
1792
- }
1793
- await memoryDb.memories.update(existing.id, updateData);
1794
- } else {
1795
- await memoryDb.memories.add({
1796
- ...memory,
1797
- compositeKey,
1798
- uniqueKey,
1799
- createdAt: now,
1800
- updatedAt: now
1801
- });
1802
- }
1803
- };
1804
- var saveMemories = async (memories) => {
1805
- await Promise.all(memories.map((memory) => saveMemory(memory)));
1806
- };
1807
- var updateMemoryById = async (id, updates, existingMemory, embedding, embeddingModel) => {
1808
- const now = Date.now();
1809
- const updatedMemory = {
1810
- ...updates,
1811
- updatedAt: now
1812
- };
1813
- if ("namespace" in updates || "key" in updates || "value" in updates) {
1814
- const namespace = updates.namespace ?? existingMemory.namespace;
1815
- const key = updates.key ?? existingMemory.key;
1816
- const value = updates.value ?? existingMemory.value;
1817
- const newUniqueKey = `${namespace}:${key}:${value}`;
1818
- if (newUniqueKey !== existingMemory.uniqueKey) {
1819
- const conflicting = await getMemory(namespace, key, value);
1820
- if (conflicting) {
1821
- throw new Error(
1822
- `A memory with uniqueKey "${newUniqueKey}" already exists (id: ${conflicting.id})`
1823
- );
1824
- }
1825
- }
1826
- updatedMemory.compositeKey = `${namespace}:${key}`;
1827
- updatedMemory.uniqueKey = newUniqueKey;
1828
- }
1829
- updatedMemory.embedding = embedding;
1830
- updatedMemory.embeddingModel = embeddingModel;
1831
- return await memoryDb.transaction("rw", memoryDb.memories, async () => {
1832
- await memoryDb.memories.update(id, updatedMemory);
1833
- return await memoryDb.memories.get(id);
1834
- });
1835
- };
1836
- var getAllMemories = async () => {
1837
- return memoryDb.memories.toArray();
1838
- };
1839
- var getMemoryById = async (id) => {
1840
- return memoryDb.memories.get(id);
1841
- };
1842
- var getMemoriesByNamespace = async (namespace) => {
1843
- return memoryDb.memories.where("namespace").equals(namespace).toArray();
1844
- };
1845
- var getMemories = async (namespace, key) => {
1846
- const compositeKey = `${namespace}:${key}`;
1847
- return memoryDb.memories.where("compositeKey").equals(compositeKey).toArray();
1848
- };
1849
- var getMemory = async (namespace, key, value) => {
1850
- const uniqueKey = `${namespace}:${key}:${value}`;
1851
- return memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
1852
- };
1853
- var deleteMemories = async (namespace, key) => {
1854
- const compositeKey = `${namespace}:${key}`;
1855
- await memoryDb.memories.where("compositeKey").equals(compositeKey).delete();
1856
- };
1857
- var deleteMemory = async (namespace, key, value) => {
1858
- const uniqueKey = `${namespace}:${key}:${value}`;
1859
- const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
1860
- if (existing?.id) {
1861
- await memoryDb.memories.delete(existing.id);
1862
- }
1863
- };
1864
- var deleteMemoryById = async (id) => {
1865
- await memoryDb.memories.delete(id);
1866
- };
1867
- var clearAllMemories = async () => {
1868
- await memoryDb.memories.clear();
1869
- };
1870
- var cosineSimilarity = (a, b) => {
1871
- if (a.length !== b.length) {
1872
- throw new Error("Vectors must have the same length");
1873
- }
1874
- let dotProduct = 0;
1875
- let normA = 0;
1876
- let normB = 0;
1877
- for (let i = 0; i < a.length; i++) {
1878
- dotProduct += a[i] * b[i];
1879
- normA += a[i] * a[i];
1880
- normB += b[i] * b[i];
1881
- }
1882
- const denominator = Math.sqrt(normA) * Math.sqrt(normB);
1883
- if (denominator === 0) {
1884
- return 0;
1885
- }
1886
- return dotProduct / denominator;
1887
- };
1888
- var searchSimilarMemories = async (queryEmbedding, limit = 10, minSimilarity = 0.6) => {
1889
- const allMemories = await getAllMemories();
1890
- const memoriesWithEmbeddings = allMemories.filter(
1891
- (m) => m.embedding && m.embedding.length > 0
1892
- );
1893
- console.log(
1894
- `[Memory Search] Total memories: ${allMemories.length}, memories with embeddings: ${memoriesWithEmbeddings.length}`
1895
- );
1896
- if (memoriesWithEmbeddings.length === 0) {
1897
- console.warn(
1898
- "[Memory Search] No memories with embeddings found. Memories may need embeddings generated. Use generateAndStoreEmbeddings() to generate embeddings for existing memories."
1899
- );
1900
- return [];
1901
- }
1902
- const allResults = memoriesWithEmbeddings.map((memory) => {
1903
- const similarity = cosineSimilarity(queryEmbedding, memory.embedding);
1904
- return {
1905
- ...memory,
1906
- similarity
1907
- };
1908
- }).sort((a, b) => b.similarity - a.similarity);
1909
- console.log(
1910
- `[Memory Search] All similarity scores:`,
1911
- allResults.map((r) => ({
1912
- key: `${r.namespace}:${r.key}`,
1913
- value: r.value,
1914
- similarity: r.similarity.toFixed(4)
1915
- }))
1916
- );
1917
- const results = allResults.filter((result) => result.similarity >= minSimilarity).slice(0, limit);
1918
- if (results.length === 0 && allResults.length > 0) {
1919
- const topSimilarity = allResults[0].similarity;
1920
- const suggestedThreshold = Math.max(0.3, topSimilarity - 0.1);
1921
- console.warn(
1922
- `[Memory Search] No memories above threshold ${minSimilarity}. Highest similarity was ${topSimilarity.toFixed(4)}. Consider lowering the threshold to ${suggestedThreshold.toFixed(2)}`
1923
- );
1924
- } else {
1925
- console.log(
1926
- `[Memory Search] Found ${results.length} memories above similarity threshold ${minSimilarity}. Top similarity: ${results[0]?.similarity.toFixed(4) || "N/A"}`
1927
- );
1928
- }
1929
- return results;
1930
- };
1931
-
1932
2682
  // src/client/sdk.gen.ts
1933
2683
  var postApiV1Embeddings = (options) => {
1934
2684
  return (options.client ?? client).post({
@@ -2037,70 +2787,11 @@ var generateEmbeddingForMemory = async (memory, options = {}) => {
2037
2787
  ].filter(Boolean).join(" ");
2038
2788
  return generateEmbeddingForText(text, options);
2039
2789
  };
2040
- var generateEmbeddingsForMemories = async (memories, options = {}) => {
2041
- const embeddings = /* @__PURE__ */ new Map();
2042
- for (const memory of memories) {
2043
- const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
2044
- try {
2045
- const embedding = await generateEmbeddingForMemory(memory, options);
2046
- embeddings.set(uniqueKey, embedding);
2047
- } catch (error) {
2048
- console.error(
2049
- `Failed to generate embedding for memory ${uniqueKey}:`,
2050
- error
2051
- );
2052
- }
2053
- }
2054
- return embeddings;
2055
- };
2056
- var updateMemoriesWithEmbeddings = async (embeddings, embeddingModel) => {
2057
- const updates = Array.from(embeddings.entries()).map(
2058
- async ([uniqueKey, embedding]) => {
2059
- const existing = await memoryDb.memories.where("uniqueKey").equals(uniqueKey).first();
2060
- if (existing?.id) {
2061
- await memoryDb.memories.update(existing.id, {
2062
- embedding,
2063
- embeddingModel,
2064
- updatedAt: Date.now(),
2065
- createdAt: existing.createdAt
2066
- });
2067
- } else {
2068
- console.warn(
2069
- `[Embeddings] Memory with uniqueKey ${uniqueKey} not found. It may have been updated or deleted before embedding was generated.`
2070
- );
2071
- }
2072
- }
2073
- );
2074
- await Promise.all(updates);
2075
- };
2076
- var generateAndStoreEmbeddings = async (memories, options = {}) => {
2077
- let { model } = options;
2078
- const { provider = "local" } = options;
2079
- if (!model) {
2080
- if (provider === "local") {
2081
- model = DEFAULT_LOCAL_EMBEDDING_MODEL;
2082
- } else {
2083
- model = DEFAULT_API_EMBEDDING_MODEL;
2084
- }
2085
- }
2086
- if (provider === "local" && model === DEFAULT_API_EMBEDDING_MODEL) {
2087
- model = DEFAULT_LOCAL_EMBEDDING_MODEL;
2088
- }
2089
- if (memories.length === 0) {
2090
- return;
2091
- }
2092
- console.log(`Generating embeddings for ${memories.length} memories...`);
2093
- const embeddings = await generateEmbeddingsForMemories(memories, {
2094
- ...options,
2095
- model
2096
- });
2097
- await updateMemoriesWithEmbeddings(embeddings, model);
2098
- console.log(`Generated and stored ${embeddings.size} embeddings`);
2099
- };
2100
2790
 
2101
- // src/react/useMemory.ts
2102
- function useMemory(options = {}) {
2791
+ // src/react/useMemoryStorage.ts
2792
+ function useMemoryStorage(options) {
2103
2793
  const {
2794
+ database,
2104
2795
  completionsModel = DEFAULT_COMPLETION_MODEL,
2105
2796
  embeddingModel: userEmbeddingModel,
2106
2797
  embeddingProvider = "local",
@@ -2110,10 +2801,39 @@ function useMemory(options = {}) {
2110
2801
  baseUrl = BASE_URL
2111
2802
  } = options;
2112
2803
  const embeddingModel = userEmbeddingModel === void 0 ? embeddingProvider === "local" ? DEFAULT_LOCAL_EMBEDDING_MODEL : DEFAULT_API_EMBEDDING_MODEL : userEmbeddingModel;
2804
+ const [memories, setMemories] = useState3([]);
2113
2805
  const extractionInProgressRef = useRef2(false);
2114
- const extractMemoriesFromMessage = useCallback2(
2115
- async (options2) => {
2116
- const { messages, model } = options2;
2806
+ const memoriesCollection = useMemo2(
2807
+ () => database.get("memories"),
2808
+ [database]
2809
+ );
2810
+ const storageCtx = useMemo2(
2811
+ () => ({
2812
+ database,
2813
+ memoriesCollection
2814
+ }),
2815
+ [database, memoriesCollection]
2816
+ );
2817
+ const effectiveEmbeddingModel = useMemo2(
2818
+ () => embeddingModel ?? (embeddingProvider === "api" ? DEFAULT_API_EMBEDDING_MODEL : DEFAULT_LOCAL_EMBEDDING_MODEL),
2819
+ [embeddingModel, embeddingProvider]
2820
+ );
2821
+ const embeddingOptions = useMemo2(
2822
+ () => ({
2823
+ model: effectiveEmbeddingModel,
2824
+ provider: embeddingProvider,
2825
+ getToken: getToken || void 0,
2826
+ baseUrl
2827
+ }),
2828
+ [effectiveEmbeddingModel, embeddingProvider, getToken, baseUrl]
2829
+ );
2830
+ const refreshMemories = useCallback3(async () => {
2831
+ const storedMemories = await getAllMemoriesOp(storageCtx);
2832
+ setMemories(storedMemories);
2833
+ }, [storageCtx]);
2834
+ const extractMemoriesFromMessage = useCallback3(
2835
+ async (opts) => {
2836
+ const { messages, model } = opts;
2117
2837
  if (!getToken || extractionInProgressRef.current) {
2118
2838
  return null;
2119
2839
  }
@@ -2199,11 +2919,7 @@ function useMemory(options = {}) {
2199
2919
  if (jsonObjectMatch && jsonObjectMatch[0]) {
2200
2920
  jsonContent = jsonObjectMatch[0];
2201
2921
  } else {
2202
- console.warn(
2203
- "Memory extraction returned non-JSON response. The model may not have found any memories to extract, or it returned natural language instead of JSON.",
2204
- "\nFirst 200 chars of response:",
2205
- content.substring(0, 200)
2206
- );
2922
+ console.warn("Memory extraction returned non-JSON response");
2207
2923
  return { items: [] };
2208
2924
  }
2209
2925
  }
@@ -2211,9 +2927,7 @@ function useMemory(options = {}) {
2211
2927
  const trimmedJson = jsonContent.trim();
2212
2928
  if (!trimmedJson.startsWith("{") || !trimmedJson.includes("items")) {
2213
2929
  console.warn(
2214
- "Memory extraction response doesn't appear to be valid JSON. The model may not have found any memories to extract, or returned natural language instead of JSON.",
2215
- "\nResponse preview:",
2216
- content.substring(0, 200)
2930
+ "Memory extraction response doesn't appear to be valid JSON"
2217
2931
  );
2218
2932
  return { items: [] };
2219
2933
  }
@@ -2224,10 +2938,7 @@ function useMemory(options = {}) {
2224
2938
  throw new Error("Invalid JSON structure: not an object");
2225
2939
  }
2226
2940
  if (!Array.isArray(result.items)) {
2227
- console.warn(
2228
- "Memory extraction result missing 'items' array. Result:",
2229
- result
2230
- );
2941
+ console.warn("Memory extraction result missing 'items' array");
2231
2942
  return { items: [] };
2232
2943
  }
2233
2944
  } catch (parseError) {
@@ -2235,42 +2946,61 @@ function useMemory(options = {}) {
2235
2946
  "Failed to parse memory extraction JSON:",
2236
2947
  parseError instanceof Error ? parseError.message : parseError
2237
2948
  );
2238
- console.error("Attempted to parse:", jsonContent.substring(0, 200));
2239
- console.error("Full raw content:", content.substring(0, 500));
2240
2949
  return { items: [] };
2241
2950
  }
2242
2951
  if (result.items && Array.isArray(result.items)) {
2243
- const originalCount = result.items.length;
2244
2952
  result.items = preprocessMemories(result.items);
2245
- const filteredCount = result.items.length;
2246
- if (originalCount !== filteredCount) {
2247
- console.log(
2248
- `Preprocessed memories: ${originalCount} -> ${filteredCount} (dropped ${originalCount - filteredCount} entries)`
2249
- );
2250
- }
2251
2953
  }
2252
- console.log("Extracted memories:", JSON.stringify(result, null, 2));
2253
2954
  if (result.items && result.items.length > 0) {
2254
2955
  try {
2255
- await saveMemories(result.items);
2256
- console.log(`Saved ${result.items.length} memories to IndexedDB`);
2956
+ const createOptions = result.items.map(
2957
+ (item) => ({
2958
+ type: item.type,
2959
+ namespace: item.namespace,
2960
+ key: item.key,
2961
+ value: item.value,
2962
+ rawEvidence: item.rawEvidence,
2963
+ confidence: item.confidence,
2964
+ pii: item.pii
2965
+ })
2966
+ );
2967
+ const savedMemories = await saveMemoriesOp(storageCtx, createOptions);
2968
+ console.log(
2969
+ `Saved ${savedMemories.length} memories to WatermelonDB`
2970
+ );
2257
2971
  if (generateEmbeddings && embeddingModel) {
2258
2972
  try {
2259
- await generateAndStoreEmbeddings(result.items, {
2260
- model: embeddingModel,
2261
- provider: embeddingProvider,
2262
- getToken: getToken || void 0,
2263
- baseUrl
2264
- });
2973
+ for (const saved of savedMemories) {
2974
+ const memoryItem = {
2975
+ type: saved.type,
2976
+ namespace: saved.namespace,
2977
+ key: saved.key,
2978
+ value: saved.value,
2979
+ rawEvidence: saved.rawEvidence,
2980
+ confidence: saved.confidence,
2981
+ pii: saved.pii
2982
+ };
2983
+ const embedding = await generateEmbeddingForMemory(
2984
+ memoryItem,
2985
+ embeddingOptions
2986
+ );
2987
+ await updateMemoryEmbeddingOp(
2988
+ storageCtx,
2989
+ saved.uniqueId,
2990
+ embedding,
2991
+ effectiveEmbeddingModel
2992
+ );
2993
+ }
2265
2994
  console.log(
2266
- `Generated embeddings for ${result.items.length} memories`
2995
+ `Generated embeddings for ${savedMemories.length} memories`
2267
2996
  );
2268
2997
  } catch (error) {
2269
2998
  console.error("Failed to generate embeddings:", error);
2270
2999
  }
2271
3000
  }
3001
+ await refreshMemories();
2272
3002
  } catch (error) {
2273
- console.error("Failed to save memories to IndexedDB:", error);
3003
+ console.error("Failed to save memories to WatermelonDB:", error);
2274
3004
  }
2275
3005
  }
2276
3006
  if (onFactsExtracted) {
@@ -2287,38 +3017,35 @@ function useMemory(options = {}) {
2287
3017
  [
2288
3018
  completionsModel,
2289
3019
  embeddingModel,
2290
- embeddingProvider,
3020
+ embeddingOptions,
2291
3021
  generateEmbeddings,
2292
3022
  getToken,
2293
3023
  onFactsExtracted,
2294
- baseUrl
3024
+ baseUrl,
3025
+ storageCtx,
3026
+ refreshMemories
2295
3027
  ]
2296
3028
  );
2297
- const searchMemories = useCallback2(
3029
+ const searchMemories = useCallback3(
2298
3030
  async (query, limit = 10, minSimilarity = 0.6) => {
2299
3031
  if (!embeddingModel) {
2300
3032
  console.warn("Cannot search memories: embeddingModel not provided");
2301
3033
  return [];
2302
3034
  }
2303
3035
  try {
2304
- console.log(`[Memory Search] Searching for: "${query}"`);
2305
- const queryEmbedding = await generateEmbeddingForText(query, {
2306
- model: embeddingModel,
2307
- provider: embeddingProvider,
2308
- getToken,
2309
- baseUrl
2310
- });
2311
- console.log(
2312
- `[Memory Search] Generated query embedding (${queryEmbedding.length} dimensions)`
3036
+ const queryEmbedding = await generateEmbeddingForText(
3037
+ query,
3038
+ embeddingOptions
2313
3039
  );
2314
- const results = await searchSimilarMemories(
3040
+ const results = await searchSimilarMemoriesOp(
3041
+ storageCtx,
2315
3042
  queryEmbedding,
2316
3043
  limit,
2317
3044
  minSimilarity
2318
3045
  );
2319
3046
  if (results.length === 0) {
2320
3047
  console.warn(
2321
- `[Memory Search] No memories found above similarity threshold ${minSimilarity}. Try lowering the threshold or ensure memories have embeddings generated.`
3048
+ `[Memory Search] No memories found above similarity threshold ${minSimilarity}.`
2322
3049
  );
2323
3050
  } else {
2324
3051
  console.log(
@@ -2326,175 +3053,282 @@ function useMemory(options = {}) {
2326
3053
  );
2327
3054
  }
2328
3055
  return results;
2329
- } catch (error) {
2330
- console.error("Failed to search memories:", error);
3056
+ } catch {
2331
3057
  return [];
2332
3058
  }
2333
3059
  },
2334
- [embeddingModel, embeddingProvider, getToken, baseUrl]
3060
+ [embeddingModel, embeddingOptions, storageCtx]
2335
3061
  );
2336
- const fetchAllMemories = useCallback2(async () => {
3062
+ const fetchAllMemories = useCallback3(async () => {
2337
3063
  try {
2338
- return await getAllMemories();
3064
+ return await getAllMemoriesOp(storageCtx);
2339
3065
  } catch (error) {
2340
3066
  throw new Error(
2341
3067
  "Failed to fetch all memories: " + (error instanceof Error ? error.message : String(error))
2342
3068
  );
2343
3069
  }
2344
- }, []);
2345
- const fetchMemoriesByNamespace = useCallback2(
3070
+ }, [storageCtx]);
3071
+ const fetchMemoriesByNamespace = useCallback3(
2346
3072
  async (namespace) => {
2347
3073
  if (!namespace) {
2348
3074
  throw new Error("Missing required field: namespace");
2349
3075
  }
2350
3076
  try {
2351
- return await getMemoriesByNamespace(namespace);
3077
+ return await getMemoriesByNamespaceOp(storageCtx, namespace);
2352
3078
  } catch (error) {
2353
3079
  throw new Error(
2354
3080
  `Failed to fetch memories for namespace "${namespace}": ` + (error instanceof Error ? error.message : String(error))
2355
3081
  );
2356
3082
  }
2357
3083
  },
2358
- []
3084
+ [storageCtx]
2359
3085
  );
2360
- const fetchMemoriesByKey = useCallback2(
3086
+ const fetchMemoriesByKey = useCallback3(
2361
3087
  async (namespace, key) => {
2362
3088
  if (!namespace || !key) {
2363
3089
  throw new Error("Missing required fields: namespace, key");
2364
3090
  }
2365
3091
  try {
2366
- return await getMemories(namespace, key);
3092
+ return await getMemoriesByKeyOp(storageCtx, namespace, key);
2367
3093
  } catch (error) {
2368
3094
  throw new Error(
2369
3095
  `Failed to fetch memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
2370
3096
  );
2371
3097
  }
2372
3098
  },
2373
- []
3099
+ [storageCtx]
2374
3100
  );
2375
- const updateMemory = useCallback2(
2376
- async (id, updates) => {
2377
- if (!Number.isInteger(id) || id <= 0) {
2378
- throw new Error("id must be a non-negative integer");
3101
+ const getMemoryById = useCallback3(
3102
+ async (id) => {
3103
+ try {
3104
+ return await getMemoryByIdOp(storageCtx, id);
3105
+ } catch (error) {
3106
+ throw new Error(
3107
+ `Failed to get memory ${id}: ` + (error instanceof Error ? error.message : String(error))
3108
+ );
2379
3109
  }
3110
+ },
3111
+ [storageCtx]
3112
+ );
3113
+ const saveMemory = useCallback3(
3114
+ async (memory) => {
2380
3115
  try {
2381
- const embeddingModelToUse = embeddingProvider === "api" ? embeddingModel ?? DEFAULT_API_EMBEDDING_MODEL : embeddingModel && embeddingModel !== DEFAULT_API_EMBEDDING_MODEL ? embeddingModel : DEFAULT_LOCAL_EMBEDDING_MODEL;
2382
- const embeddingOptions = {
2383
- model: embeddingModelToUse,
2384
- provider: embeddingProvider,
2385
- getToken: getToken || void 0,
2386
- baseUrl
2387
- };
2388
- if (!updates.type || !updates.namespace || !updates.key || !updates.value || !updates.rawEvidence || updates.confidence === void 0 || updates.confidence === null || updates.pii === void 0 || updates.pii === null) {
2389
- throw new Error(
2390
- "Missing required fields: type, namespace, key, value, rawEvidence, confidence, pii"
2391
- );
2392
- }
2393
- const existingMemory = await getMemoryById(id);
2394
- if (!existingMemory) {
2395
- throw new Error(`Memory with id ${id} not found`);
2396
- }
2397
- const embeddingFieldsChanged = updates.value !== void 0 && updates.value !== existingMemory.value || updates.rawEvidence !== void 0 && updates.rawEvidence !== existingMemory.rawEvidence || updates.type !== void 0 && updates.type !== existingMemory.type || updates.namespace !== void 0 && updates.namespace !== existingMemory.namespace || updates.key !== void 0 && updates.key !== existingMemory.key;
2398
- if (!embeddingFieldsChanged) {
2399
- return existingMemory;
2400
- }
2401
- const memory = {
2402
- type: updates.type,
2403
- namespace: updates.namespace,
2404
- key: updates.key,
2405
- value: updates.value,
2406
- rawEvidence: updates.rawEvidence,
2407
- confidence: updates.confidence,
2408
- pii: updates.pii
2409
- };
2410
- let embedding = existingMemory.embedding ?? [];
2411
- let embeddingModelToStore = existingMemory.embeddingModel ?? "";
2412
- if (generateEmbeddings && embeddingModelToUse) {
3116
+ const saved = await saveMemoryOp(storageCtx, memory);
3117
+ if (generateEmbeddings && embeddingModel && !memory.embedding) {
2413
3118
  try {
2414
- embedding = await generateEmbeddingForMemory(
2415
- memory,
3119
+ const memoryItem = {
3120
+ type: memory.type,
3121
+ namespace: memory.namespace,
3122
+ key: memory.key,
3123
+ value: memory.value,
3124
+ rawEvidence: memory.rawEvidence,
3125
+ confidence: memory.confidence,
3126
+ pii: memory.pii
3127
+ };
3128
+ const embedding = await generateEmbeddingForMemory(
3129
+ memoryItem,
2416
3130
  embeddingOptions
2417
3131
  );
2418
- embeddingModelToStore = embeddingModelToUse;
2419
- } catch (embeddingError) {
2420
- console.error(
2421
- "Failed to generate embedding, keeping existing:",
2422
- embeddingError
3132
+ await updateMemoryEmbeddingOp(
3133
+ storageCtx,
3134
+ saved.uniqueId,
3135
+ embedding,
3136
+ effectiveEmbeddingModel
2423
3137
  );
3138
+ } catch (error) {
3139
+ console.error("Failed to generate embedding:", error);
2424
3140
  }
2425
3141
  }
2426
- return await updateMemoryById(
2427
- id,
2428
- updates,
2429
- existingMemory,
2430
- embedding,
2431
- embeddingModelToStore
3142
+ setMemories((prev) => {
3143
+ const existing = prev.find((m) => m.uniqueId === saved.uniqueId);
3144
+ if (existing) {
3145
+ return prev.map((m) => m.uniqueId === saved.uniqueId ? saved : m);
3146
+ }
3147
+ return [saved, ...prev];
3148
+ });
3149
+ return saved;
3150
+ } catch (error) {
3151
+ throw new Error(
3152
+ "Failed to save memory: " + (error instanceof Error ? error.message : String(error))
2432
3153
  );
3154
+ }
3155
+ },
3156
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
3157
+ );
3158
+ const saveMemories = useCallback3(
3159
+ async (memoriesToSave) => {
3160
+ try {
3161
+ const saved = await saveMemoriesOp(storageCtx, memoriesToSave);
3162
+ if (generateEmbeddings && embeddingModel) {
3163
+ for (let i = 0; i < saved.length; i++) {
3164
+ const memory = memoriesToSave[i];
3165
+ if (!memory.embedding) {
3166
+ try {
3167
+ const memoryItem = {
3168
+ type: memory.type,
3169
+ namespace: memory.namespace,
3170
+ key: memory.key,
3171
+ value: memory.value,
3172
+ rawEvidence: memory.rawEvidence,
3173
+ confidence: memory.confidence,
3174
+ pii: memory.pii
3175
+ };
3176
+ const embedding = await generateEmbeddingForMemory(
3177
+ memoryItem,
3178
+ embeddingOptions
3179
+ );
3180
+ await updateMemoryEmbeddingOp(
3181
+ storageCtx,
3182
+ saved[i].uniqueId,
3183
+ embedding,
3184
+ effectiveEmbeddingModel
3185
+ );
3186
+ } catch (error) {
3187
+ console.error("Failed to generate embedding:", error);
3188
+ }
3189
+ }
3190
+ }
3191
+ }
3192
+ await refreshMemories();
3193
+ return saved;
2433
3194
  } catch (error) {
2434
3195
  throw new Error(
2435
- `Failed to update memory ${id}: ` + (error instanceof Error ? error.message : String(error))
3196
+ "Failed to save memories: " + (error instanceof Error ? error.message : String(error))
3197
+ );
3198
+ }
3199
+ },
3200
+ [
3201
+ storageCtx,
3202
+ generateEmbeddings,
3203
+ embeddingModel,
3204
+ embeddingOptions,
3205
+ refreshMemories
3206
+ ]
3207
+ );
3208
+ const updateMemory = useCallback3(
3209
+ async (id, updates) => {
3210
+ const result = await updateMemoryOp(storageCtx, id, updates);
3211
+ if (!result.ok) {
3212
+ if (result.reason === "not_found") {
3213
+ return null;
3214
+ }
3215
+ if (result.reason === "conflict") {
3216
+ throw new Error(
3217
+ `Cannot update memory: a memory with key "${result.conflictingKey}" already exists`
3218
+ );
3219
+ }
3220
+ throw new Error(
3221
+ `Failed to update memory ${id}: ${result.error.message}`
2436
3222
  );
2437
3223
  }
3224
+ const updated = result.memory;
3225
+ const contentChanged = updates.value !== void 0 || updates.rawEvidence !== void 0 || updates.type !== void 0 || updates.namespace !== void 0 || updates.key !== void 0;
3226
+ if (contentChanged && generateEmbeddings && embeddingModel && !updates.embedding) {
3227
+ try {
3228
+ const memoryItem = {
3229
+ type: updated.type,
3230
+ namespace: updated.namespace,
3231
+ key: updated.key,
3232
+ value: updated.value,
3233
+ rawEvidence: updated.rawEvidence,
3234
+ confidence: updated.confidence,
3235
+ pii: updated.pii
3236
+ };
3237
+ const embedding = await generateEmbeddingForMemory(
3238
+ memoryItem,
3239
+ embeddingOptions
3240
+ );
3241
+ await updateMemoryEmbeddingOp(
3242
+ storageCtx,
3243
+ id,
3244
+ embedding,
3245
+ effectiveEmbeddingModel
3246
+ );
3247
+ } catch (error) {
3248
+ console.error("Failed to regenerate embedding:", error);
3249
+ }
3250
+ }
3251
+ setMemories(
3252
+ (prev) => prev.map((m) => m.uniqueId === id ? updated : m)
3253
+ );
3254
+ return updated;
2438
3255
  },
2439
- [embeddingModel, embeddingProvider, generateEmbeddings, getToken, baseUrl]
3256
+ [storageCtx, generateEmbeddings, embeddingModel, embeddingOptions]
2440
3257
  );
2441
- const removeMemory = useCallback2(
3258
+ const removeMemory = useCallback3(
2442
3259
  async (namespace, key, value) => {
2443
3260
  if (!namespace || !key || !value) {
2444
3261
  throw new Error("Missing required fields: namespace, key, value");
2445
3262
  }
2446
3263
  try {
2447
- await deleteMemory(namespace, key, value);
3264
+ await deleteMemoryOp(storageCtx, namespace, key, value);
3265
+ setMemories(
3266
+ (prev) => prev.filter(
3267
+ (m) => !(m.namespace === namespace && m.key === key && m.value === value)
3268
+ )
3269
+ );
2448
3270
  } catch (error) {
2449
3271
  throw new Error(
2450
3272
  `Failed to delete memory "${namespace}:${key}:${value}": ` + (error instanceof Error ? error.message : String(error))
2451
3273
  );
2452
3274
  }
2453
3275
  },
2454
- []
3276
+ [storageCtx]
2455
3277
  );
2456
- const removeMemoryById = useCallback2(async (id) => {
2457
- if (!Number.isInteger(id) || id <= 0) {
2458
- throw new Error("id must be a non-negative integer");
2459
- }
2460
- try {
2461
- await deleteMemoryById(id);
2462
- } catch (error) {
2463
- throw new Error(
2464
- `Failed to delete memory with id ${id}: ` + (error instanceof Error ? error.message : String(error))
2465
- );
2466
- }
2467
- }, []);
2468
- const removeMemories = useCallback2(
3278
+ const removeMemoryById = useCallback3(
3279
+ async (id) => {
3280
+ try {
3281
+ await deleteMemoryByIdOp(storageCtx, id);
3282
+ setMemories((prev) => prev.filter((m) => m.uniqueId !== id));
3283
+ } catch (error) {
3284
+ throw new Error(
3285
+ `Failed to delete memory with id ${id}: ` + (error instanceof Error ? error.message : String(error))
3286
+ );
3287
+ }
3288
+ },
3289
+ [storageCtx]
3290
+ );
3291
+ const removeMemories = useCallback3(
2469
3292
  async (namespace, key) => {
2470
3293
  if (!namespace || !key) {
2471
3294
  throw new Error("Missing required fields: namespace, key");
2472
3295
  }
2473
3296
  try {
2474
- await deleteMemories(namespace, key);
3297
+ await deleteMemoriesByKeyOp(storageCtx, namespace, key);
3298
+ setMemories(
3299
+ (prev) => prev.filter(
3300
+ (m) => !(m.namespace === namespace && m.key === key)
3301
+ )
3302
+ );
2475
3303
  } catch (error) {
2476
3304
  throw new Error(
2477
3305
  `Failed to delete memories for "${namespace}:${key}": ` + (error instanceof Error ? error.message : String(error))
2478
3306
  );
2479
3307
  }
2480
3308
  },
2481
- []
3309
+ [storageCtx]
2482
3310
  );
2483
- const clearMemories = useCallback2(async () => {
3311
+ const clearMemories = useCallback3(async () => {
2484
3312
  try {
2485
- await clearAllMemories();
3313
+ await clearAllMemoriesOp(storageCtx);
3314
+ setMemories([]);
2486
3315
  } catch (error) {
2487
3316
  throw new Error(
2488
3317
  "Failed to clear all memories: " + (error instanceof Error ? error.message : String(error))
2489
3318
  );
2490
3319
  }
2491
- }, []);
3320
+ }, [storageCtx]);
2492
3321
  return {
3322
+ memories,
3323
+ refreshMemories,
2493
3324
  extractMemoriesFromMessage,
2494
3325
  searchMemories,
2495
3326
  fetchAllMemories,
2496
3327
  fetchMemoriesByNamespace,
2497
3328
  fetchMemoriesByKey,
3329
+ getMemoryById,
3330
+ saveMemory,
3331
+ saveMemories,
2498
3332
  updateMemory,
2499
3333
  removeMemory,
2500
3334
  removeMemoryById,
@@ -2503,8 +3337,115 @@ function useMemory(options = {}) {
2503
3337
  };
2504
3338
  }
2505
3339
 
3340
+ // src/lib/memoryStorage/schema.ts
3341
+ import { appSchema as appSchema2, tableSchema as tableSchema2 } from "@nozbe/watermelondb";
3342
+ var memoryStorageSchema = appSchema2({
3343
+ version: 1,
3344
+ tables: [
3345
+ tableSchema2({
3346
+ name: "memories",
3347
+ columns: [
3348
+ // Memory type classification
3349
+ { name: "type", type: "string", isIndexed: true },
3350
+ // 'identity' | 'preference' | 'project' | 'skill' | 'constraint'
3351
+ // Hierarchical key structure
3352
+ { name: "namespace", type: "string", isIndexed: true },
3353
+ { name: "key", type: "string", isIndexed: true },
3354
+ { name: "value", type: "string" },
3355
+ // Evidence and confidence
3356
+ { name: "raw_evidence", type: "string" },
3357
+ { name: "confidence", type: "number" },
3358
+ { name: "pii", type: "boolean", isIndexed: true },
3359
+ // Composite keys for efficient lookups
3360
+ { name: "composite_key", type: "string", isIndexed: true },
3361
+ // namespace:key
3362
+ { name: "unique_key", type: "string", isIndexed: true },
3363
+ // namespace:key:value
3364
+ // Timestamps
3365
+ { name: "created_at", type: "number", isIndexed: true },
3366
+ { name: "updated_at", type: "number" },
3367
+ // Vector embeddings for semantic search
3368
+ { name: "embedding", type: "string", isOptional: true },
3369
+ // JSON stringified number[]
3370
+ { name: "embedding_model", type: "string", isOptional: true },
3371
+ // Soft delete flag
3372
+ { name: "is_deleted", type: "boolean", isIndexed: true }
3373
+ ]
3374
+ })
3375
+ ]
3376
+ });
3377
+
3378
+ // src/lib/memoryStorage/models.ts
3379
+ import { Model as Model2 } from "@nozbe/watermelondb";
3380
+ var Memory = class extends Model2 {
3381
+ /** Memory type classification */
3382
+ get type() {
3383
+ return this._getRaw("type");
3384
+ }
3385
+ /** Namespace for grouping related memories */
3386
+ get namespace() {
3387
+ return this._getRaw("namespace");
3388
+ }
3389
+ /** Key within the namespace */
3390
+ get key() {
3391
+ return this._getRaw("key");
3392
+ }
3393
+ /** The memory value/content */
3394
+ get value() {
3395
+ return this._getRaw("value");
3396
+ }
3397
+ /** Raw evidence from which this memory was extracted */
3398
+ get rawEvidence() {
3399
+ return this._getRaw("raw_evidence");
3400
+ }
3401
+ /** Confidence score (0-1) */
3402
+ get confidence() {
3403
+ return this._getRaw("confidence");
3404
+ }
3405
+ /** Whether this memory contains PII */
3406
+ get pii() {
3407
+ return this._getRaw("pii");
3408
+ }
3409
+ /** Composite key (namespace:key) for efficient lookups */
3410
+ get compositeKey() {
3411
+ return this._getRaw("composite_key");
3412
+ }
3413
+ /** Unique key (namespace:key:value) for deduplication */
3414
+ get uniqueKey() {
3415
+ return this._getRaw("unique_key");
3416
+ }
3417
+ /** Created timestamp */
3418
+ get createdAt() {
3419
+ return new Date(this._getRaw("created_at"));
3420
+ }
3421
+ /** Updated timestamp */
3422
+ get updatedAt() {
3423
+ return new Date(this._getRaw("updated_at"));
3424
+ }
3425
+ /** Embedding vector for semantic search */
3426
+ get embedding() {
3427
+ const raw = this._getRaw("embedding");
3428
+ if (!raw) return void 0;
3429
+ try {
3430
+ return JSON.parse(raw);
3431
+ } catch {
3432
+ return void 0;
3433
+ }
3434
+ }
3435
+ /** Model used to generate embedding */
3436
+ get embeddingModel() {
3437
+ const value = this._getRaw("embedding_model");
3438
+ return value ? value : void 0;
3439
+ }
3440
+ /** Soft delete flag */
3441
+ get isDeleted() {
3442
+ return this._getRaw("is_deleted");
3443
+ }
3444
+ };
3445
+ Memory.table = "memories";
3446
+
2506
3447
  // src/react/usePdf.ts
2507
- import { useCallback as useCallback3, useState as useState2 } from "react";
3448
+ import { useCallback as useCallback4, useState as useState4 } from "react";
2508
3449
 
2509
3450
  // src/lib/pdf.ts
2510
3451
  import * as pdfjs from "pdfjs-dist";
@@ -2557,9 +3498,9 @@ async function convertPdfToImages(pdfDataUrl) {
2557
3498
  // src/react/usePdf.ts
2558
3499
  var PDF_MIME_TYPE = "application/pdf";
2559
3500
  function usePdf() {
2560
- const [isProcessing, setIsProcessing] = useState2(false);
2561
- const [error, setError] = useState2(null);
2562
- const extractPdfContext = useCallback3(
3501
+ const [isProcessing, setIsProcessing] = useState4(false);
3502
+ const [error, setError] = useState4(null);
3503
+ const extractPdfContext = useCallback4(
2563
3504
  async (files) => {
2564
3505
  setIsProcessing(true);
2565
3506
  setError(null);
@@ -2606,12 +3547,12 @@ ${text}`;
2606
3547
  }
2607
3548
 
2608
3549
  // src/react/useOCR.ts
2609
- import { useCallback as useCallback4, useState as useState3 } from "react";
3550
+ import { useCallback as useCallback5, useState as useState5 } from "react";
2610
3551
  import Tesseract from "tesseract.js";
2611
3552
  function useOCR() {
2612
- const [isProcessing, setIsProcessing] = useState3(false);
2613
- const [error, setError] = useState3(null);
2614
- const extractOCRContext = useCallback4(
3553
+ const [isProcessing, setIsProcessing] = useState5(false);
3554
+ const [error, setError] = useState5(null);
3555
+ const extractOCRContext = useCallback5(
2615
3556
  async (files) => {
2616
3557
  setIsProcessing(true);
2617
3558
  setError(null);
@@ -2697,12 +3638,12 @@ ${text}`;
2697
3638
  }
2698
3639
 
2699
3640
  // src/react/useModels.ts
2700
- import { useCallback as useCallback5, useEffect as useEffect2, useRef as useRef3, useState as useState4 } from "react";
3641
+ import { useCallback as useCallback6, useEffect as useEffect2, useRef as useRef3, useState as useState6 } from "react";
2701
3642
  function useModels(options = {}) {
2702
3643
  const { getToken, baseUrl = BASE_URL, provider, autoFetch = true } = options;
2703
- const [models, setModels] = useState4([]);
2704
- const [isLoading, setIsLoading] = useState4(false);
2705
- const [error, setError] = useState4(null);
3644
+ const [models, setModels] = useState6([]);
3645
+ const [isLoading, setIsLoading] = useState6(false);
3646
+ const [error, setError] = useState6(null);
2706
3647
  const getTokenRef = useRef3(getToken);
2707
3648
  const baseUrlRef = useRef3(baseUrl);
2708
3649
  const providerRef = useRef3(provider);
@@ -2720,7 +3661,7 @@ function useModels(options = {}) {
2720
3661
  }
2721
3662
  };
2722
3663
  }, []);
2723
- const fetchModels = useCallback5(async () => {
3664
+ const fetchModels = useCallback6(async () => {
2724
3665
  if (abortControllerRef.current) {
2725
3666
  abortControllerRef.current.abort();
2726
3667
  }
@@ -2778,7 +3719,7 @@ function useModels(options = {}) {
2778
3719
  }
2779
3720
  }
2780
3721
  }, []);
2781
- const refetch = useCallback5(async () => {
3722
+ const refetch = useCallback6(async () => {
2782
3723
  setModels([]);
2783
3724
  await fetchModels();
2784
3725
  }, [fetchModels]);
@@ -2801,13 +3742,13 @@ function useModels(options = {}) {
2801
3742
  }
2802
3743
 
2803
3744
  // src/react/useSearch.ts
2804
- import { useCallback as useCallback6, useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
3745
+ import { useCallback as useCallback7, useEffect as useEffect3, useRef as useRef4, useState as useState7 } from "react";
2805
3746
  function useSearch(options = {}) {
2806
3747
  const { getToken, baseUrl = BASE_URL, onError } = options;
2807
- const [isLoading, setIsLoading] = useState5(false);
2808
- const [results, setResults] = useState5(null);
2809
- const [response, setResponse] = useState5(null);
2810
- const [error, setError] = useState5(null);
3748
+ const [isLoading, setIsLoading] = useState7(false);
3749
+ const [results, setResults] = useState7(null);
3750
+ const [response, setResponse] = useState7(null);
3751
+ const [error, setError] = useState7(null);
2811
3752
  const abortControllerRef = useRef4(null);
2812
3753
  useEffect3(() => {
2813
3754
  return () => {
@@ -2817,7 +3758,7 @@ function useSearch(options = {}) {
2817
3758
  }
2818
3759
  };
2819
3760
  }, []);
2820
- const search = useCallback6(
3761
+ const search = useCallback7(
2821
3762
  async (query, searchOptions = {}) => {
2822
3763
  if (abortControllerRef.current) {
2823
3764
  abortControllerRef.current.abort();
@@ -2885,10 +3826,10 @@ function useSearch(options = {}) {
2885
3826
  }
2886
3827
 
2887
3828
  // src/react/useImageGeneration.ts
2888
- import { useCallback as useCallback7, useEffect as useEffect4, useRef as useRef5, useState as useState6 } from "react";
3829
+ import { useCallback as useCallback8, useEffect as useEffect4, useRef as useRef5, useState as useState8 } from "react";
2889
3830
  function useImageGeneration(options = {}) {
2890
3831
  const { getToken, baseUrl = BASE_URL, onFinish, onError } = options;
2891
- const [isLoading, setIsLoading] = useState6(false);
3832
+ const [isLoading, setIsLoading] = useState8(false);
2892
3833
  const abortControllerRef = useRef5(null);
2893
3834
  useEffect4(() => {
2894
3835
  return () => {
@@ -2898,13 +3839,13 @@ function useImageGeneration(options = {}) {
2898
3839
  }
2899
3840
  };
2900
3841
  }, []);
2901
- const stop = useCallback7(() => {
3842
+ const stop = useCallback8(() => {
2902
3843
  if (abortControllerRef.current) {
2903
3844
  abortControllerRef.current.abort();
2904
3845
  abortControllerRef.current = null;
2905
3846
  }
2906
3847
  }, []);
2907
- const generateImage = useCallback7(
3848
+ const generateImage = useCallback8(
2908
3849
  async (args) => {
2909
3850
  if (abortControllerRef.current) {
2910
3851
  abortControllerRef.current.abort();
@@ -3021,7 +3962,11 @@ var extractConversationContext = (messages, maxMessages = 3) => {
3021
3962
  return userMessages.trim();
3022
3963
  };
3023
3964
  export {
3965
+ Conversation as ChatConversation,
3966
+ Message as ChatMessage,
3024
3967
  DEFAULT_TOOL_SELECTOR_MODEL,
3968
+ Memory as StoredMemoryModel,
3969
+ chatStorageSchema,
3025
3970
  createMemoryContextSystemMessage,
3026
3971
  decryptData,
3027
3972
  decryptDataBytes,
@@ -3029,13 +3974,18 @@ export {
3029
3974
  executeTool,
3030
3975
  extractConversationContext,
3031
3976
  formatMemoriesForChat,
3977
+ generateCompositeKey,
3978
+ generateConversationId,
3979
+ generateUniqueKey,
3032
3980
  hasEncryptionKey,
3981
+ memoryStorageSchema,
3033
3982
  requestEncryptionKey,
3034
3983
  selectTool,
3035
3984
  useChat,
3985
+ useChatStorage,
3036
3986
  useEncryption,
3037
3987
  useImageGeneration,
3038
- useMemory,
3988
+ useMemoryStorage,
3039
3989
  useModels,
3040
3990
  useOCR,
3041
3991
  usePdf,