@agentskit/memory 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,8 @@
1
- import { serializeMessages, deserializeMessages } from '@agentskit/core';
1
+ import { __require } from './chunk-G5S2A3MJ.js';
2
+ export { createInMemoryPersonalization, renderProfileContext } from './chunk-G5S2A3MJ.js';
3
+ import { serializeMessages, deserializeMessages, MemoryError, ErrorCodes, ConfigError } from '@agentskit/core';
4
+ import { tokenize, createPIIRedactor } from '@agentskit/core/security';
2
5
 
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
6
  function fileChatMemory(path) {
10
7
  return {
11
8
  async load() {
@@ -19,7 +16,11 @@ function fileChatMemory(path) {
19
16
  },
20
17
  async save(messages) {
21
18
  const fs = await import('fs/promises');
22
- await fs.writeFile(path, JSON.stringify(serializeMessages(messages), null, 2), "utf8");
19
+ await fs.writeFile(
20
+ path,
21
+ JSON.stringify(serializeMessages(messages), null, 2),
22
+ { encoding: "utf8", mode: 384 }
23
+ );
23
24
  },
24
25
  async clear() {
25
26
  try {
@@ -47,7 +48,11 @@ async function openDatabase(path) {
47
48
  const Database = mod.default ?? mod;
48
49
  return new Database(path);
49
50
  } catch {
50
- throw new Error("Install better-sqlite3 to use sqliteChatMemory: npm install better-sqlite3");
51
+ throw new MemoryError({
52
+ code: ErrorCodes.AK_MEMORY_PEER_MISSING,
53
+ message: "Install better-sqlite3 to use sqliteChatMemory: npm install better-sqlite3",
54
+ hint: 'sqliteChatMemory uses the optional peer "better-sqlite3".'
55
+ });
51
56
  }
52
57
  }
53
58
  function sqliteChatMemory(config) {
@@ -98,7 +103,11 @@ async function loadSdk() {
98
103
  moduleId
99
104
  );
100
105
  } catch {
101
- throw new Error("Install @libsql/client to use tursoChatMemory: npm install @libsql/client");
106
+ throw new MemoryError({
107
+ code: ErrorCodes.AK_MEMORY_PEER_MISSING,
108
+ message: "Install @libsql/client to use tursoChatMemory: npm install @libsql/client",
109
+ hint: 'tursoChatMemory uses the optional peer "@libsql/client".'
110
+ });
102
111
  }
103
112
  })();
104
113
  }
@@ -159,14 +168,16 @@ function tursoChatMemory(config) {
159
168
  }
160
169
  };
161
170
  }
162
-
163
- // src/redis-client.ts
164
171
  async function createRedisClientAdapter(url) {
165
172
  let redis;
166
173
  try {
167
174
  redis = await import('redis');
168
175
  } catch {
169
- throw new Error("Install redis to use Redis memory backends: npm install redis");
176
+ throw new MemoryError({
177
+ code: ErrorCodes.AK_MEMORY_PEER_MISSING,
178
+ message: "Install redis to use Redis memory backends: npm install redis",
179
+ hint: 'redisChatMemory and redisVectorMemory use the optional peer "redis".'
180
+ });
170
181
  }
171
182
  const client = redis.createClient({ url });
172
183
  await client.connect();
@@ -185,7 +196,7 @@ async function createRedisClientAdapter(url) {
185
196
  return await client.keys(pattern);
186
197
  },
187
198
  async disconnect() {
188
- await client.disconnect();
199
+ await client.close();
189
200
  },
190
201
  async call(command, ...args) {
191
202
  return await client.sendCommand([command, ...args.map(String)]);
@@ -392,9 +403,11 @@ function requireVectra() {
392
403
  try {
393
404
  return __require("vectra");
394
405
  } catch {
395
- throw new Error(
396
- "Install vectra to use fileVectorMemory: npm install vectra"
397
- );
406
+ throw new MemoryError({
407
+ code: ErrorCodes.AK_MEMORY_PEER_MISSING,
408
+ message: "Install vectra to use fileVectorMemory: npm install vectra",
409
+ hint: 'fileVectorMemory uses the optional peer "vectra" for the on-disk index.'
410
+ });
398
411
  }
399
412
  }
400
413
  function createVectraStore(dirPath) {
@@ -411,16 +424,23 @@ function createVectraStore(dirPath) {
411
424
  return {
412
425
  async upsert(docs) {
413
426
  const idx = await getIndex();
414
- for (const doc of docs) {
415
- await idx.insertItem({
416
- vector: doc.vector,
417
- metadata: { _id: doc.id, ...doc.metadata }
418
- });
427
+ await idx.beginUpdate();
428
+ try {
429
+ for (const doc of docs) {
430
+ await idx.insertItem({
431
+ vector: doc.vector,
432
+ metadata: { _id: doc.id, ...doc.metadata }
433
+ });
434
+ }
435
+ await idx.endUpdate();
436
+ } catch (err) {
437
+ idx.cancelUpdate();
438
+ throw err;
419
439
  }
420
440
  },
421
441
  async query(vector, topK) {
422
442
  const idx = await getIndex();
423
- const results = await idx.queryItems(vector, topK);
443
+ const results = await idx.queryItems(vector, "", topK);
424
444
  return results.map((r) => ({
425
445
  id: String(r.item.metadata._id ?? ""),
426
446
  score: r.score,
@@ -471,44 +491,6 @@ function fileVectorMemory(config) {
471
491
  };
472
492
  }
473
493
 
474
- // src/personalization.ts
475
- function createInMemoryPersonalization() {
476
- const profiles = /* @__PURE__ */ new Map();
477
- return {
478
- async get(subjectId) {
479
- const hit = profiles.get(subjectId);
480
- return hit ? { ...hit, traits: { ...hit.traits } } : null;
481
- },
482
- async set(profile) {
483
- profiles.set(profile.subjectId, {
484
- ...profile,
485
- traits: { ...profile.traits },
486
- updatedAt: profile.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
487
- });
488
- },
489
- async merge(subjectId, traits) {
490
- const existing = profiles.get(subjectId);
491
- const next = {
492
- subjectId,
493
- traits: { ...existing?.traits ?? {}, ...traits },
494
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
495
- };
496
- profiles.set(subjectId, next);
497
- return { ...next, traits: { ...next.traits } };
498
- },
499
- async delete(subjectId) {
500
- profiles.delete(subjectId);
501
- }
502
- };
503
- }
504
- function renderProfileContext(profile) {
505
- if (!profile || Object.keys(profile.traits).length === 0) return "";
506
- const lines = Object.entries(profile.traits).filter(([, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => `- ${key}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
507
- if (lines.length === 0) return "";
508
- return `## User profile
509
- ${lines.join("\n")}`;
510
- }
511
-
512
494
  // src/graph.ts
513
495
  function createInMemoryGraph() {
514
496
  const nodes = /* @__PURE__ */ new Map();
@@ -632,8 +614,6 @@ function pgvector(config) {
632
614
  }
633
615
  };
634
616
  }
635
-
636
- // src/vector/pinecone.ts
637
617
  async function call(config, path, body) {
638
618
  const fetchImpl = config.fetch ?? globalThis.fetch;
639
619
  const response = await fetchImpl(`${config.indexUrl}${path}`, {
@@ -645,7 +625,13 @@ async function call(config, path, body) {
645
625
  body: JSON.stringify(body)
646
626
  });
647
627
  const text = await response.text();
648
- if (!response.ok) throw new Error(`pinecone ${response.status}: ${text.slice(0, 200)}`);
628
+ if (!response.ok) {
629
+ throw new MemoryError({
630
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
631
+ message: `pinecone ${response.status}: ${text.slice(0, 200)}`,
632
+ hint: `URL ${config.indexUrl}${path}. Check api-key + index URL/namespace.`
633
+ });
634
+ }
649
635
  return text.length > 0 ? JSON.parse(text) : {};
650
636
  }
651
637
  function pinecone(config) {
@@ -685,8 +671,6 @@ function pinecone(config) {
685
671
  }
686
672
  };
687
673
  }
688
-
689
- // src/vector/qdrant.ts
690
674
  async function call2(config, method, path, body) {
691
675
  const fetchImpl = config.fetch ?? globalThis.fetch;
692
676
  const response = await fetchImpl(`${config.url}${path}`, {
@@ -698,7 +682,13 @@ async function call2(config, method, path, body) {
698
682
  body: body === void 0 ? void 0 : JSON.stringify(body)
699
683
  });
700
684
  const text = await response.text();
701
- if (!response.ok) throw new Error(`qdrant ${response.status}: ${text.slice(0, 200)}`);
685
+ if (!response.ok) {
686
+ throw new MemoryError({
687
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
688
+ message: `qdrant ${response.status}: ${text.slice(0, 200)}`,
689
+ hint: `URL ${config.url}${path}. Check api-key + collection "${config.collection}".`
690
+ });
691
+ }
702
692
  return text.length > 0 ? JSON.parse(text) : {};
703
693
  }
704
694
  function qdrant(config) {
@@ -737,8 +727,6 @@ function qdrant(config) {
737
727
  }
738
728
  };
739
729
  }
740
-
741
- // src/vector/chroma.ts
742
730
  async function call3(config, method, path, body) {
743
731
  const fetchImpl = config.fetch ?? globalThis.fetch;
744
732
  const response = await fetchImpl(`${config.url}${path}`, {
@@ -747,7 +735,13 @@ async function call3(config, method, path, body) {
747
735
  body: body === void 0 ? void 0 : JSON.stringify(body)
748
736
  });
749
737
  const text = await response.text();
750
- if (!response.ok) throw new Error(`chroma ${response.status}: ${text.slice(0, 200)}`);
738
+ if (!response.ok) {
739
+ throw new MemoryError({
740
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
741
+ message: `chroma ${response.status}: ${text.slice(0, 200)}`,
742
+ hint: `URL ${config.url}${path}. Check Chroma server health + collection name.`
743
+ });
744
+ }
751
745
  return text.length > 0 ? JSON.parse(text) : {};
752
746
  }
753
747
  function chroma(config) {
@@ -786,8 +780,6 @@ function chroma(config) {
786
780
  }
787
781
  };
788
782
  }
789
-
790
- // src/vector/upstash.ts
791
783
  async function call4(config, path, body) {
792
784
  const fetchImpl = config.fetch ?? globalThis.fetch;
793
785
  const response = await fetchImpl(`${config.url}${path}`, {
@@ -799,7 +791,13 @@ async function call4(config, path, body) {
799
791
  body: JSON.stringify(body)
800
792
  });
801
793
  const text = await response.text();
802
- if (!response.ok) throw new Error(`upstash-vector ${response.status}: ${text.slice(0, 200)}`);
794
+ if (!response.ok) {
795
+ throw new MemoryError({
796
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
797
+ message: `upstash-vector ${response.status}: ${text.slice(0, 200)}`,
798
+ hint: `URL ${config.url}${path}. Check token + index URL.`
799
+ });
800
+ }
803
801
  return text.length > 0 ? JSON.parse(text) : {};
804
802
  }
805
803
  function upstashVector(config) {
@@ -834,8 +832,6 @@ function upstashVector(config) {
834
832
  }
835
833
  };
836
834
  }
837
-
838
- // src/vector/supabase.ts
839
835
  var cachedSdk2 = null;
840
836
  async function loadSdk2() {
841
837
  if (!cachedSdk2) {
@@ -847,9 +843,11 @@ async function loadSdk2() {
847
843
  moduleId
848
844
  );
849
845
  } catch {
850
- throw new Error(
851
- "Install @supabase/supabase-js to use supabaseVectorStore: npm install @supabase/supabase-js"
852
- );
846
+ throw new MemoryError({
847
+ code: ErrorCodes.AK_MEMORY_PEER_MISSING,
848
+ message: "Install @supabase/supabase-js to use supabaseVectorStore: npm install @supabase/supabase-js",
849
+ hint: 'supabaseVectorStore uses the optional peer "@supabase/supabase-js".'
850
+ });
853
851
  }
854
852
  })();
855
853
  }
@@ -859,7 +857,13 @@ function buildRunner(client) {
859
857
  return {
860
858
  async query(sql, params) {
861
859
  const result = await client.rpc("agentskit_execute_sql", { sql, params });
862
- if (result.error) throw new Error(`supabase: ${result.error.message}`);
860
+ if (result.error) {
861
+ throw new MemoryError({
862
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
863
+ message: `supabase: ${result.error.message}`,
864
+ hint: "Check the agentskit_execute_sql RPC + service role key permissions."
865
+ });
866
+ }
863
867
  return { rows: result.data ?? [] };
864
868
  }
865
869
  };
@@ -903,8 +907,6 @@ function supabaseVectorStore(config) {
903
907
  }
904
908
  };
905
909
  }
906
-
907
- // src/vector/weaviate.ts
908
910
  async function call5(config, method, path, body) {
909
911
  const fetchImpl = config.fetch ?? globalThis.fetch;
910
912
  const response = await fetchImpl(`${config.url}${path}`, {
@@ -916,7 +918,13 @@ async function call5(config, method, path, body) {
916
918
  body: body === void 0 ? void 0 : JSON.stringify(body)
917
919
  });
918
920
  const text = await response.text();
919
- if (!response.ok) throw new Error(`weaviate ${response.status}: ${text.slice(0, 200)}`);
921
+ if (!response.ok) {
922
+ throw new MemoryError({
923
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
924
+ message: `weaviate ${response.status}: ${text.slice(0, 200)}`,
925
+ hint: `URL ${config.url}${path}. Check API key + class name "${config.className}".`
926
+ });
927
+ }
920
928
  return text.length > 0 ? JSON.parse(text) : {};
921
929
  }
922
930
  function weaviateVectorStore(config) {
@@ -961,8 +969,6 @@ function weaviateVectorStore(config) {
961
969
  }
962
970
  };
963
971
  }
964
-
965
- // src/vector/milvus.ts
966
972
  async function call6(config, path, body) {
967
973
  const fetchImpl = config.fetch ?? globalThis.fetch;
968
974
  const response = await fetchImpl(`${config.url}${path}`, {
@@ -974,7 +980,13 @@ async function call6(config, path, body) {
974
980
  body: JSON.stringify(body)
975
981
  });
976
982
  const text = await response.text();
977
- if (!response.ok) throw new Error(`milvus ${response.status}: ${text.slice(0, 200)}`);
983
+ if (!response.ok) {
984
+ throw new MemoryError({
985
+ code: ErrorCodes.AK_MEMORY_REMOTE_HTTP,
986
+ message: `milvus ${response.status}: ${text.slice(0, 200)}`,
987
+ hint: `URL ${config.url}${path}. Check token + collection "${config.collection}".`
988
+ });
989
+ }
978
990
  return text.length > 0 ? JSON.parse(text) : {};
979
991
  }
980
992
  function milvusVectorStore(config) {
@@ -1064,8 +1076,6 @@ function mongoAtlasVectorStore(config) {
1064
1076
  }
1065
1077
  };
1066
1078
  }
1067
-
1068
- // src/encrypted.ts
1069
1079
  function toBase64(bytes) {
1070
1080
  if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
1071
1081
  let binary = "";
@@ -1083,14 +1093,24 @@ async function resolveKey(subtle, material) {
1083
1093
  if ("type" in material && material.type === "secret") return material;
1084
1094
  const raw = material;
1085
1095
  if (raw.byteLength !== 32) {
1086
- throw new Error(`createEncryptedMemory: key must be 32 bytes (got ${raw.byteLength})`);
1096
+ throw new ConfigError({
1097
+ code: ErrorCodes.AK_CONFIG_INVALID,
1098
+ message: `createEncryptedMemory: key must be 32 bytes (got ${raw.byteLength})`,
1099
+ hint: "Generate a 32-byte key, e.g. crypto.getRandomValues(new Uint8Array(32))."
1100
+ });
1087
1101
  }
1088
1102
  return subtle.importKey("raw", raw, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
1089
1103
  }
1090
1104
  async function createEncryptedMemory(options) {
1091
1105
  const subtle = options.subtle ?? globalThis.crypto?.subtle;
1092
1106
  const random = options.getRandomValues ?? ((v) => globalThis.crypto.getRandomValues(v));
1093
- if (!subtle) throw new Error("createEncryptedMemory: SubtleCrypto not available");
1107
+ if (!subtle) {
1108
+ throw new MemoryError({
1109
+ code: ErrorCodes.AK_MEMORY_LOAD_FAILED,
1110
+ message: "createEncryptedMemory: SubtleCrypto not available",
1111
+ hint: "Run on Node \u2265 20 / a modern browser, or pass options.subtle explicitly."
1112
+ });
1113
+ }
1094
1114
  const key = await resolveKey(subtle, options.key);
1095
1115
  const aad = options.aad;
1096
1116
  const encrypt = async (plain) => {
@@ -1207,6 +1227,102 @@ function createHierarchicalMemory(options) {
1207
1227
  };
1208
1228
  }
1209
1229
 
1210
- export { chroma, createEncryptedMemory, createHierarchicalMemory, createInMemoryGraph, createInMemoryPersonalization, fileChatMemory, fileVectorMemory, matchesFilter, milvusVectorStore, mongoAtlasVectorStore, pgvector, pinecone, qdrant, redisChatMemory, redisVectorMemory, renderProfileContext, sqliteChatMemory, supabaseVectorStore, tursoChatMemory, upstashVector, weaviateVectorStore };
1230
+ // src/forget.ts
1231
+ function isForgettable(value) {
1232
+ return !!value && typeof value === "object" && "forgetSubject" in value && typeof value.forgetSubject === "function";
1233
+ }
1234
+ async function hash(input) {
1235
+ const encoded = new TextEncoder().encode(input);
1236
+ const digest = await crypto.subtle.digest("SHA-256", encoded);
1237
+ return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
1238
+ }
1239
+ async function forgetSubject(memories, subjectId) {
1240
+ const reports = [];
1241
+ for (const memory of memories) {
1242
+ if (!isForgettable(memory)) continue;
1243
+ reports.push(await memory.forgetSubject(subjectId));
1244
+ }
1245
+ const totalDeleted = reports.reduce((sum, r) => sum + r.deletedCount, 0);
1246
+ const evidenceHash = await hash(
1247
+ JSON.stringify({ subjectId, reports: reports.map((r) => ({ b: r.backend, n: r.deletedCount, at: r.at })) })
1248
+ );
1249
+ return { subjectId, reports, totalDeleted, evidenceHash };
1250
+ }
1251
+ function makeForgettable(memory, options) {
1252
+ return Object.assign(memory, {
1253
+ __agentskitBackend: options.backend,
1254
+ forgetSubject: async (subjectId) => {
1255
+ const ids = await options.listIds(subjectId);
1256
+ const failures = [];
1257
+ try {
1258
+ await options.deleteIds(ids);
1259
+ } catch (err) {
1260
+ for (const id of ids) failures.push({ id, reason: err.message });
1261
+ }
1262
+ return {
1263
+ backend: options.backend,
1264
+ deletedCount: ids.length - failures.length,
1265
+ at: (/* @__PURE__ */ new Date()).toISOString(),
1266
+ failures: failures.length ? failures : void 0
1267
+ };
1268
+ }
1269
+ });
1270
+ }
1271
+ async function transform(input, opts) {
1272
+ if (opts.mode === "tokenize") {
1273
+ if (!opts.vault) {
1274
+ throw new ConfigError({
1275
+ code: ErrorCodes.AK_CONFIG_INVALID,
1276
+ message: 'wrapMemoryWithRedaction: vault is required when mode is "tokenize"'
1277
+ });
1278
+ }
1279
+ if (!opts.allowedRoles) {
1280
+ throw new ConfigError({
1281
+ code: ErrorCodes.AK_CONFIG_INVALID,
1282
+ message: 'wrapMemoryWithRedaction: allowedRoles is required when mode is "tokenize"'
1283
+ });
1284
+ }
1285
+ const result = await tokenize(input, {
1286
+ rules: opts.rules,
1287
+ vault: opts.vault,
1288
+ allowedRoles: opts.allowedRoles,
1289
+ audit: opts.audit
1290
+ });
1291
+ return result.value;
1292
+ }
1293
+ return createPIIRedactor({ rules: opts.rules }).redact(input).value;
1294
+ }
1295
+ function wrapChatMemoryWithRedaction(inner, options) {
1296
+ return {
1297
+ load: () => inner.load(),
1298
+ save: async (messages) => {
1299
+ const redacted = await Promise.all(
1300
+ messages.map(async (m) => ({
1301
+ ...m,
1302
+ content: await transform(m.content ?? "", options)
1303
+ }))
1304
+ );
1305
+ await inner.save(redacted);
1306
+ },
1307
+ clear: inner.clear ? () => inner.clear() : void 0
1308
+ };
1309
+ }
1310
+ function wrapVectorMemoryWithRedaction(inner, options) {
1311
+ return {
1312
+ store: async (docs) => {
1313
+ const redacted = await Promise.all(
1314
+ docs.map(async (d) => ({
1315
+ ...d,
1316
+ content: await transform(d.content, options)
1317
+ }))
1318
+ );
1319
+ await inner.store(redacted);
1320
+ },
1321
+ search: (embedding, opts) => inner.search(embedding, opts),
1322
+ delete: inner.delete ? (ids) => inner.delete(ids) : void 0
1323
+ };
1324
+ }
1325
+
1326
+ export { chroma, createEncryptedMemory, createHierarchicalMemory, createInMemoryGraph, fileChatMemory, fileVectorMemory, forgetSubject, makeForgettable, matchesFilter, milvusVectorStore, mongoAtlasVectorStore, pgvector, pinecone, qdrant, redisChatMemory, redisVectorMemory, sqliteChatMemory, supabaseVectorStore, tursoChatMemory, upstashVector, weaviateVectorStore, wrapChatMemoryWithRedaction, wrapVectorMemoryWithRedaction };
1211
1327
  //# sourceMappingURL=index.js.map
1212
1328
  //# sourceMappingURL=index.js.map