@reverbia/sdk 1.0.0-next.20251125084024 → 1.0.0-next.20251125205812
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/react/index.cjs +201 -14
- package/dist/react/index.d.mts +27 -1
- package/dist/react/index.d.ts +27 -1
- package/dist/react/index.mjs +195 -12
- package/package.json +3 -1
package/dist/react/index.cjs
CHANGED
|
@@ -31,9 +31,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
createMemoryContextSystemMessage: () => createMemoryContextSystemMessage,
|
|
34
|
+
decryptData: () => decryptData,
|
|
35
|
+
decryptDataBytes: () => decryptDataBytes,
|
|
36
|
+
encryptData: () => encryptData,
|
|
34
37
|
extractConversationContext: () => extractConversationContext,
|
|
35
38
|
formatMemoriesForChat: () => formatMemoriesForChat,
|
|
36
39
|
useChat: () => useChat,
|
|
40
|
+
useEncryption: () => useEncryption,
|
|
37
41
|
useMemory: () => useMemory
|
|
38
42
|
});
|
|
39
43
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -857,7 +861,13 @@ var client = createClient(createClientConfig(createConfig()));
|
|
|
857
861
|
|
|
858
862
|
// src/react/useChat.ts
|
|
859
863
|
function useChat(options) {
|
|
860
|
-
const {
|
|
864
|
+
const {
|
|
865
|
+
getToken,
|
|
866
|
+
baseUrl,
|
|
867
|
+
onData: globalOnData,
|
|
868
|
+
onFinish,
|
|
869
|
+
onError
|
|
870
|
+
} = options || {};
|
|
861
871
|
const [isLoading, setIsLoading] = (0, import_react.useState)(false);
|
|
862
872
|
const abortControllerRef = (0, import_react.useRef)(null);
|
|
863
873
|
const stop = (0, import_react.useCallback)(() => {
|
|
@@ -998,7 +1008,7 @@ function useChat(options) {
|
|
|
998
1008
|
}
|
|
999
1009
|
}
|
|
1000
1010
|
},
|
|
1001
|
-
[getToken, globalOnData, onFinish, onError]
|
|
1011
|
+
[getToken, baseUrl, globalOnData, onFinish, onError]
|
|
1002
1012
|
);
|
|
1003
1013
|
return {
|
|
1004
1014
|
isLoading,
|
|
@@ -1007,8 +1017,166 @@ function useChat(options) {
|
|
|
1007
1017
|
};
|
|
1008
1018
|
}
|
|
1009
1019
|
|
|
1010
|
-
// src/react/
|
|
1020
|
+
// src/react/useEncryption.ts
|
|
1011
1021
|
var import_react2 = require("react");
|
|
1022
|
+
var import_react_auth = require("@privy-io/react-auth");
|
|
1023
|
+
var SIGN_MESSAGE = "The app is asking you to sign this message to generate a key, which will be used to encrypt data.";
|
|
1024
|
+
var SIGNATURE_STORAGE_KEY = "privy_encryption_key";
|
|
1025
|
+
function getStorageItem(key) {
|
|
1026
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
try {
|
|
1030
|
+
return localStorage.getItem(key);
|
|
1031
|
+
} catch {
|
|
1032
|
+
return null;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
function setStorageItem(key, value) {
|
|
1036
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
1037
|
+
return false;
|
|
1038
|
+
}
|
|
1039
|
+
try {
|
|
1040
|
+
localStorage.setItem(key, value);
|
|
1041
|
+
return true;
|
|
1042
|
+
} catch {
|
|
1043
|
+
return false;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
function hexToBytes(hex) {
|
|
1047
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1048
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
1049
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1050
|
+
bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1051
|
+
}
|
|
1052
|
+
return bytes;
|
|
1053
|
+
}
|
|
1054
|
+
function bytesToHex(bytes) {
|
|
1055
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1056
|
+
}
|
|
1057
|
+
async function deriveKeyFromSignature(signature) {
|
|
1058
|
+
const sigBytes = hexToBytes(signature);
|
|
1059
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
1060
|
+
"SHA-256",
|
|
1061
|
+
sigBytes.buffer
|
|
1062
|
+
);
|
|
1063
|
+
const hashBytes = new Uint8Array(hashBuffer);
|
|
1064
|
+
return bytesToHex(hashBytes);
|
|
1065
|
+
}
|
|
1066
|
+
async function getEncryptionKey() {
|
|
1067
|
+
const keyHex = getStorageItem(SIGNATURE_STORAGE_KEY);
|
|
1068
|
+
if (!keyHex) {
|
|
1069
|
+
throw new Error("Encryption key not found. Please sign in first.");
|
|
1070
|
+
}
|
|
1071
|
+
const keyBytes = hexToBytes(keyHex);
|
|
1072
|
+
return crypto.subtle.importKey(
|
|
1073
|
+
"raw",
|
|
1074
|
+
keyBytes.buffer,
|
|
1075
|
+
{ name: "AES-GCM" },
|
|
1076
|
+
false,
|
|
1077
|
+
["encrypt", "decrypt"]
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
async function encryptData(plaintext) {
|
|
1081
|
+
const key = await getEncryptionKey();
|
|
1082
|
+
const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
|
|
1083
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1084
|
+
const encryptedData = await crypto.subtle.encrypt(
|
|
1085
|
+
{
|
|
1086
|
+
name: "AES-GCM",
|
|
1087
|
+
iv
|
|
1088
|
+
},
|
|
1089
|
+
key,
|
|
1090
|
+
plaintextBytes.buffer
|
|
1091
|
+
);
|
|
1092
|
+
const encryptedBytes = new Uint8Array(encryptedData);
|
|
1093
|
+
const combined = new Uint8Array(iv.length + encryptedBytes.length);
|
|
1094
|
+
combined.set(iv, 0);
|
|
1095
|
+
combined.set(encryptedBytes, iv.length);
|
|
1096
|
+
return bytesToHex(combined);
|
|
1097
|
+
}
|
|
1098
|
+
async function decryptData(encryptedHex) {
|
|
1099
|
+
const key = await getEncryptionKey();
|
|
1100
|
+
const combined = hexToBytes(encryptedHex);
|
|
1101
|
+
const iv = combined.slice(0, 12);
|
|
1102
|
+
const encryptedData = combined.slice(12);
|
|
1103
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
1104
|
+
{
|
|
1105
|
+
name: "AES-GCM",
|
|
1106
|
+
iv
|
|
1107
|
+
},
|
|
1108
|
+
key,
|
|
1109
|
+
encryptedData
|
|
1110
|
+
);
|
|
1111
|
+
return new TextDecoder().decode(decryptedData);
|
|
1112
|
+
}
|
|
1113
|
+
async function decryptDataBytes(encryptedHex) {
|
|
1114
|
+
const key = await getEncryptionKey();
|
|
1115
|
+
const combined = hexToBytes(encryptedHex);
|
|
1116
|
+
const iv = combined.slice(0, 12);
|
|
1117
|
+
const encryptedData = combined.slice(12);
|
|
1118
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
1119
|
+
{
|
|
1120
|
+
name: "AES-GCM",
|
|
1121
|
+
iv
|
|
1122
|
+
},
|
|
1123
|
+
key,
|
|
1124
|
+
encryptedData
|
|
1125
|
+
);
|
|
1126
|
+
return new Uint8Array(decryptedData);
|
|
1127
|
+
}
|
|
1128
|
+
function useEncryption(authenticated) {
|
|
1129
|
+
const { signMessage } = (0, import_react_auth.useSignMessage)();
|
|
1130
|
+
const { wallets } = (0, import_react_auth.useWallets)();
|
|
1131
|
+
const hasRequestedSignature = (0, import_react2.useRef)(false);
|
|
1132
|
+
const hasCheckedStorage = (0, import_react2.useRef)(false);
|
|
1133
|
+
(0, import_react2.useEffect)(() => {
|
|
1134
|
+
if (!authenticated || wallets.length === 0) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const existingKey = getStorageItem(SIGNATURE_STORAGE_KEY);
|
|
1138
|
+
if (existingKey) {
|
|
1139
|
+
if (!hasCheckedStorage.current) {
|
|
1140
|
+
hasCheckedStorage.current = true;
|
|
1141
|
+
}
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
const requestSignature = async () => {
|
|
1145
|
+
if (!hasRequestedSignature.current) {
|
|
1146
|
+
hasRequestedSignature.current = true;
|
|
1147
|
+
try {
|
|
1148
|
+
const { signature } = await signMessage(
|
|
1149
|
+
{ message: SIGN_MESSAGE },
|
|
1150
|
+
{
|
|
1151
|
+
address: wallets[0].address
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
1154
|
+
const encryptionKey = await deriveKeyFromSignature(signature);
|
|
1155
|
+
const stored = setStorageItem(SIGNATURE_STORAGE_KEY, encryptionKey);
|
|
1156
|
+
if (!stored) {
|
|
1157
|
+
throw new Error("Failed to store encryption key in localStorage");
|
|
1158
|
+
}
|
|
1159
|
+
} catch (error) {
|
|
1160
|
+
hasRequestedSignature.current = false;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
requestSignature();
|
|
1165
|
+
}, [
|
|
1166
|
+
authenticated,
|
|
1167
|
+
wallets.length > 0 ? wallets[0]?.address : null,
|
|
1168
|
+
signMessage
|
|
1169
|
+
]);
|
|
1170
|
+
(0, import_react2.useEffect)(() => {
|
|
1171
|
+
if (!authenticated) {
|
|
1172
|
+
hasRequestedSignature.current = false;
|
|
1173
|
+
hasCheckedStorage.current = false;
|
|
1174
|
+
}
|
|
1175
|
+
}, [authenticated]);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
// src/react/useMemory.ts
|
|
1179
|
+
var import_react3 = require("react");
|
|
1012
1180
|
var import_client6 = require("@reverbia/sdk");
|
|
1013
1181
|
|
|
1014
1182
|
// src/lib/memory/service.ts
|
|
@@ -1239,7 +1407,11 @@ var postApiV1Embeddings = (options) => {
|
|
|
1239
1407
|
|
|
1240
1408
|
// src/lib/memory/embeddings.ts
|
|
1241
1409
|
var generateEmbeddingForText = async (text, options = {}) => {
|
|
1242
|
-
const {
|
|
1410
|
+
const {
|
|
1411
|
+
model = "openai/text-embedding-3-small",
|
|
1412
|
+
getToken,
|
|
1413
|
+
baseUrl
|
|
1414
|
+
} = options;
|
|
1243
1415
|
try {
|
|
1244
1416
|
const token = getToken ? await getToken() : null;
|
|
1245
1417
|
const headers = {};
|
|
@@ -1247,6 +1419,7 @@ var generateEmbeddingForText = async (text, options = {}) => {
|
|
|
1247
1419
|
headers.Authorization = `Bearer ${token}`;
|
|
1248
1420
|
}
|
|
1249
1421
|
const response = await postApiV1Embeddings({
|
|
1422
|
+
baseUrl,
|
|
1250
1423
|
body: {
|
|
1251
1424
|
input: text,
|
|
1252
1425
|
model
|
|
@@ -1279,14 +1452,19 @@ var generateEmbeddingForMemory = async (memory, options = {}) => {
|
|
|
1279
1452
|
return generateEmbeddingForText(text, options);
|
|
1280
1453
|
};
|
|
1281
1454
|
var generateEmbeddingsForMemories = async (memories, options = {}) => {
|
|
1282
|
-
const {
|
|
1455
|
+
const {
|
|
1456
|
+
model = "openai/text-embedding-3-small",
|
|
1457
|
+
getToken,
|
|
1458
|
+
baseUrl
|
|
1459
|
+
} = options;
|
|
1283
1460
|
const embeddings = /* @__PURE__ */ new Map();
|
|
1284
1461
|
for (const memory of memories) {
|
|
1285
1462
|
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
1286
1463
|
try {
|
|
1287
1464
|
const embedding = await generateEmbeddingForMemory(memory, {
|
|
1288
1465
|
model,
|
|
1289
|
-
getToken
|
|
1466
|
+
getToken,
|
|
1467
|
+
baseUrl
|
|
1290
1468
|
});
|
|
1291
1469
|
embeddings.set(uniqueKey, embedding);
|
|
1292
1470
|
} catch (error) {
|
|
@@ -1339,10 +1517,11 @@ function useMemory(options = {}) {
|
|
|
1339
1517
|
embeddingModel = "openai/text-embedding-3-small",
|
|
1340
1518
|
generateEmbeddings = true,
|
|
1341
1519
|
onFactsExtracted,
|
|
1342
|
-
getToken
|
|
1520
|
+
getToken,
|
|
1521
|
+
baseUrl
|
|
1343
1522
|
} = options;
|
|
1344
|
-
const extractionInProgressRef = (0,
|
|
1345
|
-
const extractMemoriesFromMessage = (0,
|
|
1523
|
+
const extractionInProgressRef = (0, import_react3.useRef)(false);
|
|
1524
|
+
const extractMemoriesFromMessage = (0, import_react3.useCallback)(
|
|
1346
1525
|
async (options2) => {
|
|
1347
1526
|
const { messages, model } = options2;
|
|
1348
1527
|
if (!getToken || extractionInProgressRef.current) {
|
|
@@ -1356,6 +1535,7 @@ function useMemory(options = {}) {
|
|
|
1356
1535
|
return null;
|
|
1357
1536
|
}
|
|
1358
1537
|
const completion = await (0, import_client6.postApiV1ChatCompletions)({
|
|
1538
|
+
baseUrl,
|
|
1359
1539
|
body: {
|
|
1360
1540
|
messages: [
|
|
1361
1541
|
{
|
|
@@ -1479,7 +1659,8 @@ function useMemory(options = {}) {
|
|
|
1479
1659
|
try {
|
|
1480
1660
|
await generateAndStoreEmbeddings(result.items, {
|
|
1481
1661
|
model: embeddingModel,
|
|
1482
|
-
getToken: getToken || void 0
|
|
1662
|
+
getToken: getToken || void 0,
|
|
1663
|
+
baseUrl
|
|
1483
1664
|
});
|
|
1484
1665
|
console.log(
|
|
1485
1666
|
`Generated embeddings for ${result.items.length} memories`
|
|
@@ -1508,10 +1689,11 @@ function useMemory(options = {}) {
|
|
|
1508
1689
|
embeddingModel,
|
|
1509
1690
|
generateEmbeddings,
|
|
1510
1691
|
getToken,
|
|
1511
|
-
onFactsExtracted
|
|
1692
|
+
onFactsExtracted,
|
|
1693
|
+
baseUrl
|
|
1512
1694
|
]
|
|
1513
1695
|
);
|
|
1514
|
-
const searchMemories = (0,
|
|
1696
|
+
const searchMemories = (0, import_react3.useCallback)(
|
|
1515
1697
|
async (query, limit = 10, minSimilarity = 0.6) => {
|
|
1516
1698
|
if (!getToken || !embeddingModel) {
|
|
1517
1699
|
console.warn(
|
|
@@ -1523,7 +1705,8 @@ function useMemory(options = {}) {
|
|
|
1523
1705
|
console.log(`[Memory Search] Searching for: "${query}"`);
|
|
1524
1706
|
const queryEmbedding = await generateQueryEmbedding(query, {
|
|
1525
1707
|
model: embeddingModel,
|
|
1526
|
-
getToken
|
|
1708
|
+
getToken,
|
|
1709
|
+
baseUrl
|
|
1527
1710
|
});
|
|
1528
1711
|
console.log(
|
|
1529
1712
|
`[Memory Search] Generated query embedding (${queryEmbedding.length} dimensions)`
|
|
@@ -1548,7 +1731,7 @@ function useMemory(options = {}) {
|
|
|
1548
1731
|
return [];
|
|
1549
1732
|
}
|
|
1550
1733
|
},
|
|
1551
|
-
[embeddingModel, getToken]
|
|
1734
|
+
[embeddingModel, getToken, baseUrl]
|
|
1552
1735
|
);
|
|
1553
1736
|
return {
|
|
1554
1737
|
extractMemoriesFromMessage,
|
|
@@ -1613,8 +1796,12 @@ var extractConversationContext = (messages, maxMessages = 3) => {
|
|
|
1613
1796
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1614
1797
|
0 && (module.exports = {
|
|
1615
1798
|
createMemoryContextSystemMessage,
|
|
1799
|
+
decryptData,
|
|
1800
|
+
decryptDataBytes,
|
|
1801
|
+
encryptData,
|
|
1616
1802
|
extractConversationContext,
|
|
1617
1803
|
formatMemoriesForChat,
|
|
1618
1804
|
useChat,
|
|
1805
|
+
useEncryption,
|
|
1619
1806
|
useMemory
|
|
1620
1807
|
});
|
package/dist/react/index.d.mts
CHANGED
|
@@ -98,6 +98,7 @@ type SendMessageResult = {
|
|
|
98
98
|
};
|
|
99
99
|
type UseChatOptions = {
|
|
100
100
|
getToken?: () => Promise<string | null>;
|
|
101
|
+
baseUrl?: string;
|
|
101
102
|
/**
|
|
102
103
|
* Callback function to be called when a new data chunk is received.
|
|
103
104
|
*/
|
|
@@ -141,6 +142,7 @@ type UseChatResult = {
|
|
|
141
142
|
* @param options.getToken - An async function that returns an authentication token.
|
|
142
143
|
* This token will be used as a Bearer token in the Authorization header.
|
|
143
144
|
* If not provided, `sendMessage` will return an error.
|
|
145
|
+
* @param options.baseUrl - Optional base URL for the API requests.
|
|
144
146
|
* @param options.onData - Callback function to be called when a new data chunk is received.
|
|
145
147
|
* @param options.onFinish - Callback function to be called when the chat completion finishes successfully.
|
|
146
148
|
* @param options.onError - Callback function to be called when an unexpected error
|
|
@@ -190,6 +192,26 @@ type UseChatResult = {
|
|
|
190
192
|
*/
|
|
191
193
|
declare function useChat(options?: UseChatOptions): UseChatResult;
|
|
192
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Encrypts data using AES-GCM with the stored encryption key
|
|
197
|
+
* @param plaintext - The data to encrypt (string or Uint8Array)
|
|
198
|
+
* @returns Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
199
|
+
*/
|
|
200
|
+
declare function encryptData(plaintext: string | Uint8Array): Promise<string>;
|
|
201
|
+
/**
|
|
202
|
+
* Decrypts data using AES-GCM with the stored encryption key
|
|
203
|
+
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
204
|
+
* @returns Decrypted data as string
|
|
205
|
+
*/
|
|
206
|
+
declare function decryptData(encryptedHex: string): Promise<string>;
|
|
207
|
+
/**
|
|
208
|
+
* Decrypts data and returns as Uint8Array (for binary data)
|
|
209
|
+
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
210
|
+
* @returns Decrypted data as Uint8Array
|
|
211
|
+
*/
|
|
212
|
+
declare function decryptDataBytes(encryptedHex: string): Promise<Uint8Array>;
|
|
213
|
+
declare function useEncryption(authenticated: boolean): void;
|
|
214
|
+
|
|
193
215
|
interface MemoryItem {
|
|
194
216
|
type: "identity" | "preference" | "project" | "skill" | "constraint";
|
|
195
217
|
namespace: string;
|
|
@@ -238,6 +260,10 @@ type UseMemoryOptions = {
|
|
|
238
260
|
* Custom function to get auth token for API calls
|
|
239
261
|
*/
|
|
240
262
|
getToken?: () => Promise<string | null>;
|
|
263
|
+
/**
|
|
264
|
+
* Optional base URL for the API requests.
|
|
265
|
+
*/
|
|
266
|
+
baseUrl?: string;
|
|
241
267
|
};
|
|
242
268
|
type UseMemoryResult = {
|
|
243
269
|
extractMemoriesFromMessage: (options: {
|
|
@@ -295,4 +321,4 @@ declare const extractConversationContext: (messages: Array<{
|
|
|
295
321
|
content: string;
|
|
296
322
|
}>, maxMessages?: number) => string;
|
|
297
323
|
|
|
298
|
-
export { createMemoryContextSystemMessage, extractConversationContext, formatMemoriesForChat, useChat, useMemory };
|
|
324
|
+
export { createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, extractConversationContext, formatMemoriesForChat, useChat, useEncryption, useMemory };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -98,6 +98,7 @@ type SendMessageResult = {
|
|
|
98
98
|
};
|
|
99
99
|
type UseChatOptions = {
|
|
100
100
|
getToken?: () => Promise<string | null>;
|
|
101
|
+
baseUrl?: string;
|
|
101
102
|
/**
|
|
102
103
|
* Callback function to be called when a new data chunk is received.
|
|
103
104
|
*/
|
|
@@ -141,6 +142,7 @@ type UseChatResult = {
|
|
|
141
142
|
* @param options.getToken - An async function that returns an authentication token.
|
|
142
143
|
* This token will be used as a Bearer token in the Authorization header.
|
|
143
144
|
* If not provided, `sendMessage` will return an error.
|
|
145
|
+
* @param options.baseUrl - Optional base URL for the API requests.
|
|
144
146
|
* @param options.onData - Callback function to be called when a new data chunk is received.
|
|
145
147
|
* @param options.onFinish - Callback function to be called when the chat completion finishes successfully.
|
|
146
148
|
* @param options.onError - Callback function to be called when an unexpected error
|
|
@@ -190,6 +192,26 @@ type UseChatResult = {
|
|
|
190
192
|
*/
|
|
191
193
|
declare function useChat(options?: UseChatOptions): UseChatResult;
|
|
192
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Encrypts data using AES-GCM with the stored encryption key
|
|
197
|
+
* @param plaintext - The data to encrypt (string or Uint8Array)
|
|
198
|
+
* @returns Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
199
|
+
*/
|
|
200
|
+
declare function encryptData(plaintext: string | Uint8Array): Promise<string>;
|
|
201
|
+
/**
|
|
202
|
+
* Decrypts data using AES-GCM with the stored encryption key
|
|
203
|
+
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
204
|
+
* @returns Decrypted data as string
|
|
205
|
+
*/
|
|
206
|
+
declare function decryptData(encryptedHex: string): Promise<string>;
|
|
207
|
+
/**
|
|
208
|
+
* Decrypts data and returns as Uint8Array (for binary data)
|
|
209
|
+
* @param encryptedHex - Encrypted data as hex string (IV + ciphertext + auth tag)
|
|
210
|
+
* @returns Decrypted data as Uint8Array
|
|
211
|
+
*/
|
|
212
|
+
declare function decryptDataBytes(encryptedHex: string): Promise<Uint8Array>;
|
|
213
|
+
declare function useEncryption(authenticated: boolean): void;
|
|
214
|
+
|
|
193
215
|
interface MemoryItem {
|
|
194
216
|
type: "identity" | "preference" | "project" | "skill" | "constraint";
|
|
195
217
|
namespace: string;
|
|
@@ -238,6 +260,10 @@ type UseMemoryOptions = {
|
|
|
238
260
|
* Custom function to get auth token for API calls
|
|
239
261
|
*/
|
|
240
262
|
getToken?: () => Promise<string | null>;
|
|
263
|
+
/**
|
|
264
|
+
* Optional base URL for the API requests.
|
|
265
|
+
*/
|
|
266
|
+
baseUrl?: string;
|
|
241
267
|
};
|
|
242
268
|
type UseMemoryResult = {
|
|
243
269
|
extractMemoriesFromMessage: (options: {
|
|
@@ -295,4 +321,4 @@ declare const extractConversationContext: (messages: Array<{
|
|
|
295
321
|
content: string;
|
|
296
322
|
}>, maxMessages?: number) => string;
|
|
297
323
|
|
|
298
|
-
export { createMemoryContextSystemMessage, extractConversationContext, formatMemoriesForChat, useChat, useMemory };
|
|
324
|
+
export { createMemoryContextSystemMessage, decryptData, decryptDataBytes, encryptData, extractConversationContext, formatMemoriesForChat, useChat, useEncryption, useMemory };
|
package/dist/react/index.mjs
CHANGED
|
@@ -817,7 +817,13 @@ var client = createClient(createClientConfig(createConfig()));
|
|
|
817
817
|
|
|
818
818
|
// src/react/useChat.ts
|
|
819
819
|
function useChat(options) {
|
|
820
|
-
const {
|
|
820
|
+
const {
|
|
821
|
+
getToken,
|
|
822
|
+
baseUrl,
|
|
823
|
+
onData: globalOnData,
|
|
824
|
+
onFinish,
|
|
825
|
+
onError
|
|
826
|
+
} = options || {};
|
|
821
827
|
const [isLoading, setIsLoading] = useState(false);
|
|
822
828
|
const abortControllerRef = useRef(null);
|
|
823
829
|
const stop = useCallback(() => {
|
|
@@ -958,7 +964,7 @@ function useChat(options) {
|
|
|
958
964
|
}
|
|
959
965
|
}
|
|
960
966
|
},
|
|
961
|
-
[getToken, globalOnData, onFinish, onError]
|
|
967
|
+
[getToken, baseUrl, globalOnData, onFinish, onError]
|
|
962
968
|
);
|
|
963
969
|
return {
|
|
964
970
|
isLoading,
|
|
@@ -967,8 +973,166 @@ function useChat(options) {
|
|
|
967
973
|
};
|
|
968
974
|
}
|
|
969
975
|
|
|
976
|
+
// src/react/useEncryption.ts
|
|
977
|
+
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
978
|
+
import { useSignMessage, useWallets } from "@privy-io/react-auth";
|
|
979
|
+
var SIGN_MESSAGE = "The app is asking you to sign this message to generate a key, which will be used to encrypt data.";
|
|
980
|
+
var SIGNATURE_STORAGE_KEY = "privy_encryption_key";
|
|
981
|
+
function getStorageItem(key) {
|
|
982
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
985
|
+
try {
|
|
986
|
+
return localStorage.getItem(key);
|
|
987
|
+
} catch {
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
function setStorageItem(key, value) {
|
|
992
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
try {
|
|
996
|
+
localStorage.setItem(key, value);
|
|
997
|
+
return true;
|
|
998
|
+
} catch {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
function hexToBytes(hex) {
|
|
1003
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1004
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
1005
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1006
|
+
bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1007
|
+
}
|
|
1008
|
+
return bytes;
|
|
1009
|
+
}
|
|
1010
|
+
function bytesToHex(bytes) {
|
|
1011
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1012
|
+
}
|
|
1013
|
+
async function deriveKeyFromSignature(signature) {
|
|
1014
|
+
const sigBytes = hexToBytes(signature);
|
|
1015
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
1016
|
+
"SHA-256",
|
|
1017
|
+
sigBytes.buffer
|
|
1018
|
+
);
|
|
1019
|
+
const hashBytes = new Uint8Array(hashBuffer);
|
|
1020
|
+
return bytesToHex(hashBytes);
|
|
1021
|
+
}
|
|
1022
|
+
async function getEncryptionKey() {
|
|
1023
|
+
const keyHex = getStorageItem(SIGNATURE_STORAGE_KEY);
|
|
1024
|
+
if (!keyHex) {
|
|
1025
|
+
throw new Error("Encryption key not found. Please sign in first.");
|
|
1026
|
+
}
|
|
1027
|
+
const keyBytes = hexToBytes(keyHex);
|
|
1028
|
+
return crypto.subtle.importKey(
|
|
1029
|
+
"raw",
|
|
1030
|
+
keyBytes.buffer,
|
|
1031
|
+
{ name: "AES-GCM" },
|
|
1032
|
+
false,
|
|
1033
|
+
["encrypt", "decrypt"]
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
async function encryptData(plaintext) {
|
|
1037
|
+
const key = await getEncryptionKey();
|
|
1038
|
+
const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
|
|
1039
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1040
|
+
const encryptedData = await crypto.subtle.encrypt(
|
|
1041
|
+
{
|
|
1042
|
+
name: "AES-GCM",
|
|
1043
|
+
iv
|
|
1044
|
+
},
|
|
1045
|
+
key,
|
|
1046
|
+
plaintextBytes.buffer
|
|
1047
|
+
);
|
|
1048
|
+
const encryptedBytes = new Uint8Array(encryptedData);
|
|
1049
|
+
const combined = new Uint8Array(iv.length + encryptedBytes.length);
|
|
1050
|
+
combined.set(iv, 0);
|
|
1051
|
+
combined.set(encryptedBytes, iv.length);
|
|
1052
|
+
return bytesToHex(combined);
|
|
1053
|
+
}
|
|
1054
|
+
async function decryptData(encryptedHex) {
|
|
1055
|
+
const key = await getEncryptionKey();
|
|
1056
|
+
const combined = hexToBytes(encryptedHex);
|
|
1057
|
+
const iv = combined.slice(0, 12);
|
|
1058
|
+
const encryptedData = combined.slice(12);
|
|
1059
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
1060
|
+
{
|
|
1061
|
+
name: "AES-GCM",
|
|
1062
|
+
iv
|
|
1063
|
+
},
|
|
1064
|
+
key,
|
|
1065
|
+
encryptedData
|
|
1066
|
+
);
|
|
1067
|
+
return new TextDecoder().decode(decryptedData);
|
|
1068
|
+
}
|
|
1069
|
+
async function decryptDataBytes(encryptedHex) {
|
|
1070
|
+
const key = await getEncryptionKey();
|
|
1071
|
+
const combined = hexToBytes(encryptedHex);
|
|
1072
|
+
const iv = combined.slice(0, 12);
|
|
1073
|
+
const encryptedData = combined.slice(12);
|
|
1074
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
1075
|
+
{
|
|
1076
|
+
name: "AES-GCM",
|
|
1077
|
+
iv
|
|
1078
|
+
},
|
|
1079
|
+
key,
|
|
1080
|
+
encryptedData
|
|
1081
|
+
);
|
|
1082
|
+
return new Uint8Array(decryptedData);
|
|
1083
|
+
}
|
|
1084
|
+
function useEncryption(authenticated) {
|
|
1085
|
+
const { signMessage } = useSignMessage();
|
|
1086
|
+
const { wallets } = useWallets();
|
|
1087
|
+
const hasRequestedSignature = useRef2(false);
|
|
1088
|
+
const hasCheckedStorage = useRef2(false);
|
|
1089
|
+
useEffect2(() => {
|
|
1090
|
+
if (!authenticated || wallets.length === 0) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
const existingKey = getStorageItem(SIGNATURE_STORAGE_KEY);
|
|
1094
|
+
if (existingKey) {
|
|
1095
|
+
if (!hasCheckedStorage.current) {
|
|
1096
|
+
hasCheckedStorage.current = true;
|
|
1097
|
+
}
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
const requestSignature = async () => {
|
|
1101
|
+
if (!hasRequestedSignature.current) {
|
|
1102
|
+
hasRequestedSignature.current = true;
|
|
1103
|
+
try {
|
|
1104
|
+
const { signature } = await signMessage(
|
|
1105
|
+
{ message: SIGN_MESSAGE },
|
|
1106
|
+
{
|
|
1107
|
+
address: wallets[0].address
|
|
1108
|
+
}
|
|
1109
|
+
);
|
|
1110
|
+
const encryptionKey = await deriveKeyFromSignature(signature);
|
|
1111
|
+
const stored = setStorageItem(SIGNATURE_STORAGE_KEY, encryptionKey);
|
|
1112
|
+
if (!stored) {
|
|
1113
|
+
throw new Error("Failed to store encryption key in localStorage");
|
|
1114
|
+
}
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
hasRequestedSignature.current = false;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
requestSignature();
|
|
1121
|
+
}, [
|
|
1122
|
+
authenticated,
|
|
1123
|
+
wallets.length > 0 ? wallets[0]?.address : null,
|
|
1124
|
+
signMessage
|
|
1125
|
+
]);
|
|
1126
|
+
useEffect2(() => {
|
|
1127
|
+
if (!authenticated) {
|
|
1128
|
+
hasRequestedSignature.current = false;
|
|
1129
|
+
hasCheckedStorage.current = false;
|
|
1130
|
+
}
|
|
1131
|
+
}, [authenticated]);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
970
1134
|
// src/react/useMemory.ts
|
|
971
|
-
import { useCallback as useCallback2, useRef as
|
|
1135
|
+
import { useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
972
1136
|
import { postApiV1ChatCompletions } from "@reverbia/sdk";
|
|
973
1137
|
|
|
974
1138
|
// src/lib/memory/service.ts
|
|
@@ -1199,7 +1363,11 @@ var postApiV1Embeddings = (options) => {
|
|
|
1199
1363
|
|
|
1200
1364
|
// src/lib/memory/embeddings.ts
|
|
1201
1365
|
var generateEmbeddingForText = async (text, options = {}) => {
|
|
1202
|
-
const {
|
|
1366
|
+
const {
|
|
1367
|
+
model = "openai/text-embedding-3-small",
|
|
1368
|
+
getToken,
|
|
1369
|
+
baseUrl
|
|
1370
|
+
} = options;
|
|
1203
1371
|
try {
|
|
1204
1372
|
const token = getToken ? await getToken() : null;
|
|
1205
1373
|
const headers = {};
|
|
@@ -1207,6 +1375,7 @@ var generateEmbeddingForText = async (text, options = {}) => {
|
|
|
1207
1375
|
headers.Authorization = `Bearer ${token}`;
|
|
1208
1376
|
}
|
|
1209
1377
|
const response = await postApiV1Embeddings({
|
|
1378
|
+
baseUrl,
|
|
1210
1379
|
body: {
|
|
1211
1380
|
input: text,
|
|
1212
1381
|
model
|
|
@@ -1239,14 +1408,19 @@ var generateEmbeddingForMemory = async (memory, options = {}) => {
|
|
|
1239
1408
|
return generateEmbeddingForText(text, options);
|
|
1240
1409
|
};
|
|
1241
1410
|
var generateEmbeddingsForMemories = async (memories, options = {}) => {
|
|
1242
|
-
const {
|
|
1411
|
+
const {
|
|
1412
|
+
model = "openai/text-embedding-3-small",
|
|
1413
|
+
getToken,
|
|
1414
|
+
baseUrl
|
|
1415
|
+
} = options;
|
|
1243
1416
|
const embeddings = /* @__PURE__ */ new Map();
|
|
1244
1417
|
for (const memory of memories) {
|
|
1245
1418
|
const uniqueKey = `${memory.namespace}:${memory.key}:${memory.value}`;
|
|
1246
1419
|
try {
|
|
1247
1420
|
const embedding = await generateEmbeddingForMemory(memory, {
|
|
1248
1421
|
model,
|
|
1249
|
-
getToken
|
|
1422
|
+
getToken,
|
|
1423
|
+
baseUrl
|
|
1250
1424
|
});
|
|
1251
1425
|
embeddings.set(uniqueKey, embedding);
|
|
1252
1426
|
} catch (error) {
|
|
@@ -1299,9 +1473,10 @@ function useMemory(options = {}) {
|
|
|
1299
1473
|
embeddingModel = "openai/text-embedding-3-small",
|
|
1300
1474
|
generateEmbeddings = true,
|
|
1301
1475
|
onFactsExtracted,
|
|
1302
|
-
getToken
|
|
1476
|
+
getToken,
|
|
1477
|
+
baseUrl
|
|
1303
1478
|
} = options;
|
|
1304
|
-
const extractionInProgressRef =
|
|
1479
|
+
const extractionInProgressRef = useRef3(false);
|
|
1305
1480
|
const extractMemoriesFromMessage = useCallback2(
|
|
1306
1481
|
async (options2) => {
|
|
1307
1482
|
const { messages, model } = options2;
|
|
@@ -1316,6 +1491,7 @@ function useMemory(options = {}) {
|
|
|
1316
1491
|
return null;
|
|
1317
1492
|
}
|
|
1318
1493
|
const completion = await postApiV1ChatCompletions({
|
|
1494
|
+
baseUrl,
|
|
1319
1495
|
body: {
|
|
1320
1496
|
messages: [
|
|
1321
1497
|
{
|
|
@@ -1439,7 +1615,8 @@ function useMemory(options = {}) {
|
|
|
1439
1615
|
try {
|
|
1440
1616
|
await generateAndStoreEmbeddings(result.items, {
|
|
1441
1617
|
model: embeddingModel,
|
|
1442
|
-
getToken: getToken || void 0
|
|
1618
|
+
getToken: getToken || void 0,
|
|
1619
|
+
baseUrl
|
|
1443
1620
|
});
|
|
1444
1621
|
console.log(
|
|
1445
1622
|
`Generated embeddings for ${result.items.length} memories`
|
|
@@ -1468,7 +1645,8 @@ function useMemory(options = {}) {
|
|
|
1468
1645
|
embeddingModel,
|
|
1469
1646
|
generateEmbeddings,
|
|
1470
1647
|
getToken,
|
|
1471
|
-
onFactsExtracted
|
|
1648
|
+
onFactsExtracted,
|
|
1649
|
+
baseUrl
|
|
1472
1650
|
]
|
|
1473
1651
|
);
|
|
1474
1652
|
const searchMemories = useCallback2(
|
|
@@ -1483,7 +1661,8 @@ function useMemory(options = {}) {
|
|
|
1483
1661
|
console.log(`[Memory Search] Searching for: "${query}"`);
|
|
1484
1662
|
const queryEmbedding = await generateQueryEmbedding(query, {
|
|
1485
1663
|
model: embeddingModel,
|
|
1486
|
-
getToken
|
|
1664
|
+
getToken,
|
|
1665
|
+
baseUrl
|
|
1487
1666
|
});
|
|
1488
1667
|
console.log(
|
|
1489
1668
|
`[Memory Search] Generated query embedding (${queryEmbedding.length} dimensions)`
|
|
@@ -1508,7 +1687,7 @@ function useMemory(options = {}) {
|
|
|
1508
1687
|
return [];
|
|
1509
1688
|
}
|
|
1510
1689
|
},
|
|
1511
|
-
[embeddingModel, getToken]
|
|
1690
|
+
[embeddingModel, getToken, baseUrl]
|
|
1512
1691
|
);
|
|
1513
1692
|
return {
|
|
1514
1693
|
extractMemoriesFromMessage,
|
|
@@ -1572,8 +1751,12 @@ var extractConversationContext = (messages, maxMessages = 3) => {
|
|
|
1572
1751
|
};
|
|
1573
1752
|
export {
|
|
1574
1753
|
createMemoryContextSystemMessage,
|
|
1754
|
+
decryptData,
|
|
1755
|
+
decryptDataBytes,
|
|
1756
|
+
encryptData,
|
|
1575
1757
|
extractConversationContext,
|
|
1576
1758
|
formatMemoriesForChat,
|
|
1577
1759
|
useChat,
|
|
1760
|
+
useEncryption,
|
|
1578
1761
|
useMemory
|
|
1579
1762
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reverbia/sdk",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.20251125205812",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@hey-api/openapi-ts": "0.87.2",
|
|
58
|
+
"@privy-io/react-auth": "^3.7.0",
|
|
58
59
|
"@types/react": "^19.2.6",
|
|
59
60
|
"tsup": "^8.5.1",
|
|
60
61
|
"typedoc": "^0.28.14",
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
"typescript": "^5.9.3"
|
|
64
65
|
},
|
|
65
66
|
"peerDependencies": {
|
|
67
|
+
"@privy-io/react-auth": "^3.7.0",
|
|
66
68
|
"dexie": "^4.2.1",
|
|
67
69
|
"react": "^18.0.0 || ^19.0.0"
|
|
68
70
|
}
|