@memstack/core 0.7.3 → 0.8.1

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.cjs CHANGED
@@ -726,6 +726,7 @@ var MemoryStore = class {
726
726
 
727
727
  // src/adapters/storage/memory.ts
728
728
  var InMemoryStorageAdapter = class {
729
+ capabilities = { multiProcess: false };
729
730
  memories = /* @__PURE__ */ new Map();
730
731
  async initialize() {
731
732
  }
@@ -821,8 +822,10 @@ var InMemoryStorageAdapter = class {
821
822
  results.sort((a, b) => b.importance - a.importance || b._touchedAt.getTime() - a._touchedAt.getTime());
822
823
  break;
823
824
  }
824
- for (const r of results.slice(0, query.limit ?? 10)) {
825
- r._touchedAt = /* @__PURE__ */ new Date();
825
+ if (query.touch !== false) {
826
+ for (const r of results.slice(0, query.limit ?? 10)) {
827
+ r._touchedAt = /* @__PURE__ */ new Date();
828
+ }
826
829
  }
827
830
  const limit = query.limit ?? 10;
828
831
  return results.slice(0, limit);
@@ -854,6 +857,25 @@ var InMemoryStorageAdapter = class {
854
857
  }
855
858
  };
856
859
 
860
+ // src/enrichment.ts
861
+ var ENRICHMENT_MAX_TOKENS = 1024;
862
+ function extractJson(text) {
863
+ let cleaned = text.trim();
864
+ const fenceMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/);
865
+ if (fenceMatch) cleaned = fenceMatch[1].trim();
866
+ return cleaned;
867
+ }
868
+ function parseTags(text, max = 5) {
869
+ try {
870
+ const parsed = JSON.parse(extractJson(text));
871
+ if (Array.isArray(parsed) && parsed.every((t) => typeof t === "string")) {
872
+ return parsed.map((t) => t.toLowerCase().trim()).slice(0, max);
873
+ }
874
+ } catch {
875
+ }
876
+ return text.split(",").map((t) => t.replace(/[\[\]"]/g, "").trim().toLowerCase()).filter((t) => t.length > 0).slice(0, max);
877
+ }
878
+
857
879
  // src/client.ts
858
880
  var MemStack = class {
859
881
  memory;
@@ -886,7 +908,7 @@ var MemStack = class {
886
908
  const result = await this.config.llm.complete({
887
909
  system: 'Analyze the memory and respond with a JSON object: {"importance": <0.0-1.0>, "tags": [<array of 1-5 lowercase single-word tags>]}. Return ONLY the JSON, nothing else.',
888
910
  user: input.content,
889
- maxTokens: 100,
911
+ maxTokens: ENRICHMENT_MAX_TOKENS,
890
912
  temperature: 0
891
913
  });
892
914
  const parsed = this._parseEnrichmentJson(result.text);
@@ -896,7 +918,7 @@ var MemStack = class {
896
918
  const result = await this.config.llm.complete({
897
919
  system: "Rate the importance of the following memory for an AI agent on a scale of 0.0 to 1.0, where 0.0 is trivial and 1.0 is critical. Return ONLY a single float number, nothing else.",
898
920
  user: input.content,
899
- maxTokens: 10,
921
+ maxTokens: ENRICHMENT_MAX_TOKENS,
900
922
  temperature: 0
901
923
  });
902
924
  const parsed = parseFloat(result.text.trim());
@@ -905,10 +927,10 @@ var MemStack = class {
905
927
  const result = await this.config.llm.complete({
906
928
  system: 'Extract 1-5 concise, lowercase, single-word tags from the following memory. Return ONLY a JSON array of strings, nothing else. Example: ["combat", "goblin", "forest"]',
907
929
  user: input.content,
908
- maxTokens: 80,
930
+ maxTokens: ENRICHMENT_MAX_TOKENS,
909
931
  temperature: 0
910
932
  });
911
- tags = this._parseTagsJson(result.text);
933
+ tags = parseTags(result.text);
912
934
  }
913
935
  } catch (err) {
914
936
  this.config.hooks?.onError?.(
@@ -1026,7 +1048,7 @@ var MemStack = class {
1026
1048
  }
1027
1049
  _parseEnrichmentJson(text) {
1028
1050
  try {
1029
- const json = JSON.parse(this._extractJson(text));
1051
+ const json = JSON.parse(extractJson(text));
1030
1052
  const result = {};
1031
1053
  if (typeof json.importance === "number" && Number.isFinite(json.importance)) {
1032
1054
  result.importance = Math.max(0, Math.min(1, json.importance));
@@ -1039,25 +1061,448 @@ var MemStack = class {
1039
1061
  return {};
1040
1062
  }
1041
1063
  }
1042
- _parseTagsJson(text) {
1043
- try {
1044
- const parsed = JSON.parse(this._extractJson(text));
1045
- if (Array.isArray(parsed) && parsed.every((t) => typeof t === "string")) {
1046
- return parsed.map((t) => t.toLowerCase().trim()).slice(0, 5);
1064
+ };
1065
+
1066
+ // src/retrieval/text.ts
1067
+ var STOPWORDS = /* @__PURE__ */ new Set([
1068
+ "a",
1069
+ "an",
1070
+ "and",
1071
+ "are",
1072
+ "as",
1073
+ "at",
1074
+ "be",
1075
+ "by",
1076
+ "did",
1077
+ "do",
1078
+ "does",
1079
+ "for",
1080
+ "from",
1081
+ "how",
1082
+ "i",
1083
+ "in",
1084
+ "is",
1085
+ "it",
1086
+ "its",
1087
+ "me",
1088
+ "my",
1089
+ "of",
1090
+ "on",
1091
+ "or",
1092
+ "our",
1093
+ "that",
1094
+ "the",
1095
+ "this",
1096
+ "to",
1097
+ "was",
1098
+ "we",
1099
+ "were",
1100
+ "what",
1101
+ "when",
1102
+ "where",
1103
+ "which",
1104
+ "who",
1105
+ "why",
1106
+ "with",
1107
+ "you",
1108
+ "your",
1109
+ // Verbs that frame a question ("what do we use?") without saying what it is about.
1110
+ "can",
1111
+ "could",
1112
+ "has",
1113
+ "have",
1114
+ "should",
1115
+ "use",
1116
+ "used",
1117
+ "uses",
1118
+ "using",
1119
+ "would"
1120
+ ]);
1121
+ function tokenize(text) {
1122
+ return text.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
1123
+ }
1124
+ function queryTerms(text) {
1125
+ const words = [...new Set(tokenize(text))];
1126
+ const meaningful = words.filter((w) => !STOPWORDS.has(w));
1127
+ return [...new Set((meaningful.length > 0 ? meaningful : words).map(stem))];
1128
+ }
1129
+ function documentTerms(text) {
1130
+ return tokenize(text).map(stem);
1131
+ }
1132
+ var STEP2 = {
1133
+ ational: "ate",
1134
+ tional: "tion",
1135
+ enci: "ence",
1136
+ anci: "ance",
1137
+ izer: "ize",
1138
+ bli: "ble",
1139
+ alli: "al",
1140
+ entli: "ent",
1141
+ eli: "e",
1142
+ ousli: "ous",
1143
+ ization: "ize",
1144
+ ation: "ate",
1145
+ ator: "ate",
1146
+ alism: "al",
1147
+ iveness: "ive",
1148
+ fulness: "ful",
1149
+ ousness: "ous",
1150
+ aliti: "al",
1151
+ iviti: "ive",
1152
+ biliti: "ble",
1153
+ logi: "log"
1154
+ };
1155
+ var STEP3 = {
1156
+ icate: "ic",
1157
+ ative: "",
1158
+ alize: "al",
1159
+ iciti: "ic",
1160
+ ical: "ic",
1161
+ ful: "",
1162
+ ness: ""
1163
+ };
1164
+ var STEP4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
1165
+ var C = "[^aeiou]";
1166
+ var V = "[aeiouy]";
1167
+ var CS = `${C}[^aeiouy]*`;
1168
+ var VS = `${V}[aeiou]*`;
1169
+ var MGR0 = new RegExp(`^(${CS})?${VS}${CS}`);
1170
+ var MEQ1 = new RegExp(`^(${CS})?${VS}${CS}(${VS})?$`);
1171
+ var MGR1 = new RegExp(`^(${CS})?${VS}${CS}${VS}${CS}`);
1172
+ var HAS_VOWEL = new RegExp(`^(${CS})?${V}`);
1173
+ var CVC = new RegExp(`^${CS}${V}[^aeiouwxy]$`);
1174
+ function stem(word) {
1175
+ if (word.length < 3 || !/^[a-z]+$/.test(word)) return word;
1176
+ let w = word;
1177
+ const firstY = w[0] === "y";
1178
+ if (firstY) w = "Y" + w.slice(1);
1179
+ if (/(ss|i)es$/.test(w)) w = w.slice(0, -2);
1180
+ else if (/[^s]s$/.test(w)) w = w.slice(0, -1);
1181
+ let m;
1182
+ if (m = w.match(/^(.+?)eed$/)) {
1183
+ if (MGR0.test(m[1])) w = w.slice(0, -1);
1184
+ } else if (m = w.match(/^(.+?)(ed|ing)$/)) {
1185
+ const base = m[1];
1186
+ if (HAS_VOWEL.test(base)) {
1187
+ w = base;
1188
+ if (/(at|bl|iz)$/.test(w)) w += "e";
1189
+ else if (/([^aeiouylsz])\1$/.test(w)) w = w.slice(0, -1);
1190
+ else if (CVC.test(w)) w += "e";
1191
+ }
1192
+ }
1193
+ if ((m = w.match(/^(.+?)y$/)) && HAS_VOWEL.test(m[1])) w = m[1] + "i";
1194
+ if (m = w.match(/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/)) {
1195
+ if (MGR0.test(m[1])) w = m[1] + STEP2[m[2]];
1196
+ }
1197
+ if (m = w.match(/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/)) {
1198
+ if (MGR0.test(m[1])) w = m[1] + STEP3[m[2]];
1199
+ }
1200
+ if (m = w.match(STEP4)) {
1201
+ if (MGR1.test(m[1])) w = m[1];
1202
+ } else if (m = w.match(/^(.+?)(s|t)(ion)$/)) {
1203
+ const base = m[1] + m[2];
1204
+ if (MGR1.test(base)) w = base;
1205
+ }
1206
+ if (m = w.match(/^(.+?)e$/)) {
1207
+ const base = m[1];
1208
+ if (MGR1.test(base) || MEQ1.test(base) && !CVC.test(base)) w = base;
1209
+ }
1210
+ if (/ll$/.test(w) && MGR1.test(w)) w = w.slice(0, -1);
1211
+ if (firstY) w = "y" + w.slice(1);
1212
+ return w;
1213
+ }
1214
+
1215
+ // src/retrieval/LexicalRetriever.ts
1216
+ var K1 = 1.2;
1217
+ var B = 0.75;
1218
+ var PREFIX_WEIGHT = 0.5;
1219
+ var MIN_PREFIX_LENGTH = 3;
1220
+ var LexicalRetriever = class {
1221
+ constructor(storage, config = {}) {
1222
+ this.storage = storage;
1223
+ this.candidateLimit = config.candidateLimit ?? 2e3;
1224
+ this.touch = config.touch ?? true;
1225
+ }
1226
+ storage;
1227
+ candidateLimit;
1228
+ touch;
1229
+ async recall(query) {
1230
+ const limit = query.limit ?? 10;
1231
+ const maxChars = query.maxChars ?? 8e3;
1232
+ const terms = queryTerms(query.query ?? "");
1233
+ let ranked = [];
1234
+ let candidates;
1235
+ if (terms.length > 0 && this.storage.search && this.storage.capabilities?.textSearch) {
1236
+ ranked = (await this.storage.search({
1237
+ actorIds: query.actorIds,
1238
+ query: query.query,
1239
+ memoryTypes: query.memoryTypes,
1240
+ limit: this.candidateLimit
1241
+ })).map(({ memory, score }) => ({ memory, score }));
1242
+ } else if (terms.length > 0) {
1243
+ candidates = await this.loadCandidates(query);
1244
+ ranked = rank(candidates, terms);
1245
+ }
1246
+ const fallback = ranked.length === 0;
1247
+ if (fallback) {
1248
+ candidates ??= await this.loadCandidates(query);
1249
+ ranked = [...candidates].sort(byImportanceThenRecency).map((memory) => ({ memory, score: 0 }));
1250
+ }
1251
+ const hits = withinBudget(ranked, limit, maxChars);
1252
+ if (this.touch) await this.touchAll(hits);
1253
+ return { hits, fallback };
1254
+ }
1255
+ async loadCandidates(query) {
1256
+ const byId = /* @__PURE__ */ new Map();
1257
+ for (const actorId of new Set(query.actorIds)) {
1258
+ const memories = await this.storage.retrieve({
1259
+ actorId,
1260
+ memoryTypes: query.memoryTypes,
1261
+ limit: this.candidateLimit,
1262
+ strategy: "important",
1263
+ touch: false
1264
+ });
1265
+ for (const memory of memories) byId.set(memory.id, memory);
1266
+ }
1267
+ return [...byId.values()];
1268
+ }
1269
+ async touchAll(hits) {
1270
+ if (!this.storage.touch) return;
1271
+ await Promise.all(
1272
+ hits.map(
1273
+ ({ memory }) => this.storage.touch(memory.id).catch(() => {
1274
+ })
1275
+ )
1276
+ );
1277
+ }
1278
+ };
1279
+ function rank(memories, terms) {
1280
+ if (memories.length === 0) return [];
1281
+ const candidates = memories.map((memory) => {
1282
+ const tokens = documentTerms(`${memory.content} ${memory.tags.join(" ")}`);
1283
+ const counts = /* @__PURE__ */ new Map();
1284
+ for (const token of tokens) counts.set(token, (counts.get(token) ?? 0) + 1);
1285
+ return { memory, terms: counts, length: tokens.length };
1286
+ });
1287
+ const avgLength = candidates.reduce((sum, c) => sum + c.length, 0) / candidates.length || 1;
1288
+ const n = candidates.length;
1289
+ const frequencies = terms.map(
1290
+ (term) => candidates.map((c) => {
1291
+ let tf = c.terms.get(term) ?? 0;
1292
+ if (term.length >= MIN_PREFIX_LENGTH) {
1293
+ for (const [docTerm, count] of c.terms) {
1294
+ if (docTerm !== term && docTerm.startsWith(term)) tf += count * PREFIX_WEIGHT;
1295
+ }
1047
1296
  }
1048
- } catch {
1297
+ return tf;
1298
+ })
1299
+ );
1300
+ const hits = [];
1301
+ candidates.forEach((c, i) => {
1302
+ let score = 0;
1303
+ frequencies.forEach((perCandidate) => {
1304
+ const tf = perCandidate[i];
1305
+ if (tf === 0) return;
1306
+ const df = perCandidate.filter((f) => f > 0).length;
1307
+ const idf = Math.log(1 + (n - df + 0.5) / (df + 0.5));
1308
+ score += idf * (tf * (K1 + 1) / (tf + K1 * (1 - B + B * c.length / avgLength)));
1309
+ });
1310
+ if (score > 0) hits.push({ memory: c.memory, score });
1311
+ });
1312
+ return hits.sort((a, b) => b.score - a.score || byImportanceThenRecency(a.memory, b.memory));
1313
+ }
1314
+ function byImportanceThenRecency(a, b) {
1315
+ return b.importance - a.importance || b.createdAt.getTime() - a.createdAt.getTime();
1316
+ }
1317
+ function withinBudget(ranked, limit, maxChars) {
1318
+ const hits = [];
1319
+ let chars = 0;
1320
+ for (const hit of ranked) {
1321
+ if (hits.length >= limit) break;
1322
+ const size = hit.memory.content.length;
1323
+ if (hits.length > 0 && chars + size > maxChars) continue;
1324
+ hits.push(hit);
1325
+ chars += size;
1326
+ }
1327
+ return hits;
1328
+ }
1329
+
1330
+ // src/harness/namespaces.ts
1331
+ var GLOBAL_NAMESPACE = "global";
1332
+ function projectNamespace(projectId) {
1333
+ if (!projectId) throw new Error("projectId is required");
1334
+ return `project:${projectId}`;
1335
+ }
1336
+ function sessionNamespace(projectId, sessionId) {
1337
+ if (!projectId || !sessionId) throw new Error("projectId and sessionId are required");
1338
+ return `session:${projectId}:${sessionId}`;
1339
+ }
1340
+ function defaultRecallNamespaces(projectId) {
1341
+ return [projectNamespace(projectId), GLOBAL_NAMESPACE];
1342
+ }
1343
+
1344
+ // src/harness/HarnessMemory.ts
1345
+ var MAX_TAGS = 8;
1346
+ var TAG_PROMPT = 'You tag memories so they can be found later by keyword search. Return 3-5 lowercase, single-word topic tags as a JSON array of strings, and nothing else. Include broader category words a person might search for, not only words already in the text. Example: for "This project uses Hono" return ["framework", "backend", "web", "server", "hono"].';
1347
+ var HarnessMemory = class {
1348
+ storage;
1349
+ llm;
1350
+ retriever;
1351
+ autoTags;
1352
+ tagTimeoutMs;
1353
+ maxContentChars;
1354
+ onError;
1355
+ initialized;
1356
+ constructor(config) {
1357
+ this.storage = config.storage;
1358
+ this.llm = config.llm;
1359
+ this.retriever = new LexicalRetriever(config.storage, { candidateLimit: config.candidateLimit });
1360
+ this.autoTags = config.autoTags ?? true;
1361
+ this.tagTimeoutMs = config.tagTimeoutMs ?? 1e4;
1362
+ this.maxContentChars = config.maxContentChars ?? 8e3;
1363
+ this.onError = config.onError;
1364
+ }
1365
+ async remember(input) {
1366
+ const content = input.content.trim();
1367
+ if (!content) throw validationError("Memory content is empty");
1368
+ if (content.length > this.maxContentChars) {
1369
+ throw validationError(`Memory content is longer than ${this.maxContentChars} characters`, {
1370
+ length: content.length
1371
+ });
1372
+ }
1373
+ if (!input.namespace) throw validationError("Namespace is required");
1374
+ if (input.importance !== void 0 && !(input.importance >= 0 && input.importance <= 1)) {
1375
+ throw validationError("Importance must be between 0 and 1", { importance: input.importance });
1376
+ }
1377
+ await this.ensureInit();
1378
+ const llmTags = this.autoTags ? await this.suggestTags(content) : [];
1379
+ const tags = [...new Set([...input.tags ?? [], ...llmTags].map((t) => t.toLowerCase().trim()).filter(Boolean))];
1380
+ return this.storage.store({
1381
+ actorId: input.namespace,
1382
+ content,
1383
+ memoryType: input.kind ?? "observation",
1384
+ importance: input.importance ?? 0.5,
1385
+ tags: tags.slice(0, MAX_TAGS),
1386
+ metadata: { source: input.source }
1387
+ });
1388
+ }
1389
+ async recall(input) {
1390
+ await this.ensureInit();
1391
+ return this.retriever.recall({
1392
+ actorIds: input.namespaces,
1393
+ query: input.query,
1394
+ memoryTypes: input.kinds,
1395
+ limit: input.limit,
1396
+ maxChars: input.maxChars
1397
+ });
1398
+ }
1399
+ /** The memory with this ID, or null when it does not exist or is outside `namespaces`. */
1400
+ async get(id, namespaces) {
1401
+ await this.ensureInit();
1402
+ const memory = await this.storage.get(id);
1403
+ return memory && namespaces.includes(memory.actorId) ? memory : null;
1404
+ }
1405
+ /** Delete a memory. Memories outside `namespaces` are reported as not found. */
1406
+ async forget(id, namespaces) {
1407
+ const memory = await this.get(id, namespaces);
1408
+ if (!memory) throw notFound("Memory", id);
1409
+ await this.storage.delete(id);
1410
+ }
1411
+ /** Number of memories in each namespace. */
1412
+ async stats(namespaces) {
1413
+ await this.ensureInit();
1414
+ const counts = {};
1415
+ for (const namespace of new Set(namespaces)) {
1416
+ counts[namespace] = await this.storage.count({ actorId: namespace });
1417
+ }
1418
+ return counts;
1419
+ }
1420
+ /**
1421
+ * Moves every memory from one namespace to another, keeping content,
1422
+ * kind, importance, tags, provenance, and creation time. Memories get new
1423
+ * IDs. Used when a project's ID changes, e.g. after its first commit.
1424
+ * Safe to run from two processes at once: a copy whose original was
1425
+ * already moved by the other process is removed again.
1426
+ */
1427
+ async moveNamespace(from, to) {
1428
+ if (!from || !to) throw validationError("Both namespaces are required");
1429
+ if (from === to) return 0;
1430
+ await this.ensureInit();
1431
+ let moved = 0;
1432
+ for (; ; ) {
1433
+ const batch = await this.storage.retrieve({ actorId: from, limit: 500, strategy: "recent", touch: false });
1434
+ if (batch.length === 0) return moved;
1435
+ let progressed = false;
1436
+ for (const memory of batch) {
1437
+ const copy = await this.storage.store({
1438
+ actorId: to,
1439
+ content: memory.content,
1440
+ memoryType: memory.memoryType,
1441
+ importance: memory.importance,
1442
+ emotionalValence: memory.emotionalValence,
1443
+ tags: memory.tags,
1444
+ metadata: memory.metadata,
1445
+ createdAt: memory.createdAt,
1446
+ expiresAt: memory.expiresAt
1447
+ });
1448
+ try {
1449
+ await this.storage.delete(memory.id);
1450
+ moved++;
1451
+ progressed = true;
1452
+ } catch (error) {
1453
+ await this.storage.delete(copy.id);
1454
+ if (!(error instanceof MemStackError && error.code === "NOT_FOUND")) throw error;
1455
+ }
1456
+ }
1457
+ if (!progressed) return moved;
1049
1458
  }
1050
- return text.split(",").map((t) => t.replace(/[\[\]"]/g, "").trim().toLowerCase()).filter((t) => t.length > 0).slice(0, 5);
1051
1459
  }
1052
- /** Strip markdown code fences and extract the JSON payload from LLM output. */
1053
- _extractJson(text) {
1054
- let cleaned = text.trim();
1055
- const fenceMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/);
1056
- if (fenceMatch) cleaned = fenceMatch[1].trim();
1057
- return cleaned;
1460
+ /**
1461
+ * Adopts memories a project stored under IDs it had before, such as
1462
+ * before its first commit. Costs one count per previous ID when there is
1463
+ * nothing to move. Returns the number of memories moved.
1464
+ */
1465
+ async adoptProjects(previousIds, projectId) {
1466
+ const target = projectNamespace(projectId);
1467
+ let moved = 0;
1468
+ for (const id of new Set(previousIds)) {
1469
+ const from = projectNamespace(id);
1470
+ if (from === target) continue;
1471
+ await this.ensureInit();
1472
+ if (await this.storage.count({ actorId: from }) === 0) continue;
1473
+ moved += await this.moveNamespace(from, target);
1474
+ }
1475
+ return moved;
1476
+ }
1477
+ ensureInit() {
1478
+ this.initialized ??= this.storage.initialize().catch((error) => {
1479
+ this.initialized = void 0;
1480
+ throw error;
1481
+ });
1482
+ return this.initialized;
1483
+ }
1484
+ async suggestTags(content) {
1485
+ let timer;
1486
+ try {
1487
+ const timeout = new Promise((_, reject) => {
1488
+ timer = setTimeout(() => reject(new Error(`Tagging timed out after ${this.tagTimeoutMs} ms`)), this.tagTimeoutMs);
1489
+ });
1490
+ const result = await Promise.race([
1491
+ this.llm.complete({ system: TAG_PROMPT, user: content, maxTokens: ENRICHMENT_MAX_TOKENS, temperature: 0 }),
1492
+ timeout
1493
+ ]);
1494
+ if (!result.text.trim()) throw new Error("Tagging returned an empty reply");
1495
+ return parseTags(result.text).filter((t) => /^[\p{L}\p{N}-]+$/u.test(t));
1496
+ } catch (error) {
1497
+ this.onError?.(error instanceof Error ? error : new Error(String(error)), "harness-tagging");
1498
+ return [];
1499
+ } finally {
1500
+ clearTimeout(timer);
1501
+ }
1058
1502
  }
1059
1503
  };
1060
1504
  var DiskStorageAdapter = class {
1505
+ capabilities = { multiProcess: false };
1061
1506
  dir;
1062
1507
  _writeLocks = /* @__PURE__ */ new Map();
1063
1508
  _idIndex = /* @__PURE__ */ new Map();
@@ -1248,26 +1693,28 @@ var DiskStorageAdapter = class {
1248
1693
  );
1249
1694
  break;
1250
1695
  }
1251
- const nowStr = (/* @__PURE__ */ new Date()).toISOString();
1252
- const toTouch = results.slice(0, query.limit ?? 10);
1253
- const touchedActors = /* @__PURE__ */ new Set();
1254
- for (const r of toTouch) {
1255
- r._touchedAt = nowStr;
1256
- touchedActors.add(r.actorId);
1257
- }
1258
- for (const actorId of touchedActors) {
1259
- await this._withWriteLock(actorId, async () => {
1260
- const records = await this._readFile(actorId);
1261
- let changed = false;
1262
- for (const r of records) {
1263
- const touched = toTouch.find((t) => t.id === r.id);
1264
- if (touched) {
1265
- r._touchedAt = nowStr;
1266
- changed = true;
1696
+ if (query.touch !== false) {
1697
+ const nowStr = (/* @__PURE__ */ new Date()).toISOString();
1698
+ const toTouch = results.slice(0, query.limit ?? 10);
1699
+ const touchedActors = /* @__PURE__ */ new Set();
1700
+ for (const r of toTouch) {
1701
+ r._touchedAt = nowStr;
1702
+ touchedActors.add(r.actorId);
1703
+ }
1704
+ for (const actorId of touchedActors) {
1705
+ await this._withWriteLock(actorId, async () => {
1706
+ const records = await this._readFile(actorId);
1707
+ let changed = false;
1708
+ for (const r of records) {
1709
+ const touched = toTouch.find((t) => t.id === r.id);
1710
+ if (touched) {
1711
+ r._touchedAt = nowStr;
1712
+ changed = true;
1713
+ }
1267
1714
  }
1268
- }
1269
- if (changed) await this._writeFile(actorId, records);
1270
- });
1715
+ if (changed) await this._writeFile(actorId, records);
1716
+ });
1717
+ }
1271
1718
  }
1272
1719
  const limit = query.limit ?? 10;
1273
1720
  return results.slice(0, limit).map((r) => this._toExternal(r));
@@ -1418,6 +1865,7 @@ var DiskStorageAdapter = class {
1418
1865
  }
1419
1866
  };
1420
1867
  var MarkdownStorageAdapter = class {
1868
+ capabilities = { multiProcess: false };
1421
1869
  dir;
1422
1870
  oneFilePerActor;
1423
1871
  _cache = null;
@@ -1934,6 +2382,7 @@ var HybridStorageAdapter = class {
1934
2382
 
1935
2383
  // src/adapters/storage/postgres.ts
1936
2384
  var PostgresStorageAdapter = class {
2385
+ capabilities = { multiProcess: true };
1937
2386
  pool;
1938
2387
  table;
1939
2388
  vectorDimensions;
@@ -1969,7 +2418,8 @@ var PostgresStorageAdapter = class {
1969
2418
  const connStr = this._connectionString ?? this._buildConnString();
1970
2419
  try {
1971
2420
  const { default: createPostgres } = await import('postgres');
1972
- const options = {};
2421
+ const options = { onnotice: () => {
2422
+ } };
1973
2423
  if (this._ssl) {
1974
2424
  options.ssl = typeof this._ssl === "object" ? this._ssl : "require";
1975
2425
  }
@@ -2002,35 +2452,50 @@ var PostgresStorageAdapter = class {
2002
2452
  }
2003
2453
  return connStr;
2004
2454
  }
2455
+ // One DO block, so it runs in a single transaction on one connection: the
2456
+ // advisory lock makes processes that start together (Claude Code and Codex)
2457
+ // migrate one at a time instead of racing on CREATE ... IF NOT EXISTS,
2458
+ // which Postgres reports as a duplicate key in pg_type.
2005
2459
  async _runMigration() {
2006
- try {
2007
- await this.pool.query("CREATE EXTENSION IF NOT EXISTS vector");
2008
- } catch {
2009
- }
2010
2460
  await this.pool.query(`
2011
- CREATE TABLE IF NOT EXISTS ${this.table} (
2012
- id TEXT PRIMARY KEY,
2013
- actor_id TEXT NOT NULL,
2014
- memory_type TEXT NOT NULL DEFAULT 'interaction',
2015
- content TEXT NOT NULL,
2016
- importance REAL NOT NULL DEFAULT 0.5,
2017
- emotional_valence REAL NOT NULL DEFAULT 0,
2018
- tags JSONB NOT NULL DEFAULT '[]',
2019
- embedding vector(${this.vectorDimensions}),
2020
- source_id TEXT,
2021
- metadata JSONB NOT NULL DEFAULT '{}',
2022
- expires_at TIMESTAMPTZ,
2023
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
2024
- touched_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
2025
- )
2461
+ DO $memstack$
2462
+ BEGIN
2463
+ PERFORM pg_advisory_xact_lock(hashtext('memstack:migrate:${this.table}'));
2464
+ BEGIN
2465
+ CREATE EXTENSION IF NOT EXISTS vector;
2466
+ EXCEPTION WHEN OTHERS THEN
2467
+ NULL; -- pgvector may not be available
2468
+ END;
2469
+ CREATE TABLE IF NOT EXISTS ${this.table} (
2470
+ id TEXT PRIMARY KEY,
2471
+ actor_id TEXT NOT NULL,
2472
+ memory_type TEXT NOT NULL DEFAULT 'interaction',
2473
+ content TEXT NOT NULL,
2474
+ importance REAL NOT NULL DEFAULT 0.5,
2475
+ emotional_valence REAL NOT NULL DEFAULT 0,
2476
+ tags JSONB NOT NULL DEFAULT '[]',
2477
+ embedding vector(${this.vectorDimensions}),
2478
+ source_id TEXT,
2479
+ metadata JSONB NOT NULL DEFAULT '{}',
2480
+ expires_at TIMESTAMPTZ,
2481
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
2482
+ touched_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
2483
+ );
2484
+ BEGIN
2485
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_actor ON ${this.table} (actor_id);
2486
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_created ON ${this.table} (created_at DESC);
2487
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_importance ON ${this.table} (importance DESC);
2488
+ EXCEPTION WHEN OTHERS THEN
2489
+ NULL; -- index creation may fail; non-critical
2490
+ END;
2491
+ BEGIN
2492
+ CREATE INDEX IF NOT EXISTS idx_${this.table}_embedding ON ${this.table} USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);
2493
+ EXCEPTION WHEN OTHERS THEN
2494
+ NULL; -- index creation may fail; non-critical
2495
+ END;
2496
+ END
2497
+ $memstack$
2026
2498
  `);
2027
- try {
2028
- await this.pool.query(`CREATE INDEX IF NOT EXISTS idx_${this.table}_actor ON ${this.table} (actor_id)`);
2029
- await this.pool.query(`CREATE INDEX IF NOT EXISTS idx_${this.table}_created ON ${this.table} (created_at DESC)`);
2030
- await this.pool.query(`CREATE INDEX IF NOT EXISTS idx_${this.table}_importance ON ${this.table} (importance DESC)`);
2031
- await this.pool.query(`CREATE INDEX IF NOT EXISTS idx_${this.table}_embedding ON ${this.table} USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64)`);
2032
- } catch {
2033
- }
2034
2499
  }
2035
2500
  generateId() {
2036
2501
  return `mem_${Date.now().toString(36)}_${crypto.randomUUID().slice(0, 8)}`;
@@ -2165,9 +2630,11 @@ var PostgresStorageAdapter = class {
2165
2630
  const sql = `SELECT * FROM ${this.table} ${where} ORDER BY ${orderBy} LIMIT $${paramIdx}`;
2166
2631
  params.push(limit);
2167
2632
  const { rows } = await this.pool.query(sql, params);
2168
- for (const row of rows.slice(0, limit)) {
2169
- const r = row;
2170
- await this.pool.query(`UPDATE ${this.table} SET touched_at = NOW() WHERE id = $1`, [r.id]);
2633
+ if (query.touch !== false) {
2634
+ for (const row of rows.slice(0, limit)) {
2635
+ const r = row;
2636
+ await this.pool.query(`UPDATE ${this.table} SET touched_at = NOW() WHERE id = $1`, [r.id]);
2637
+ }
2171
2638
  }
2172
2639
  return rows.map((r) => this._rowToMemory(r));
2173
2640
  }
@@ -2231,9 +2698,11 @@ var PostgresStorageAdapter = class {
2231
2698
  params.push(embedding);
2232
2699
  params.push(limit);
2233
2700
  const { rows } = await this.pool.query(sql, params);
2234
- for (const row of rows.slice(0, limit)) {
2235
- const r = row;
2236
- await this.pool.query(`UPDATE ${this.table} SET touched_at = NOW() WHERE id = $1`, [r.id]);
2701
+ if (query.touch !== false) {
2702
+ for (const row of rows.slice(0, limit)) {
2703
+ const r = row;
2704
+ await this.pool.query(`UPDATE ${this.table} SET touched_at = NOW() WHERE id = $1`, [r.id]);
2705
+ }
2237
2706
  }
2238
2707
  return rows.map((r) => this._rowToMemory(r));
2239
2708
  }
@@ -2257,6 +2726,7 @@ var PostgresStorageAdapter = class {
2257
2726
 
2258
2727
  // src/adapters/storage/redis.ts
2259
2728
  var RedisStorageAdapter = class {
2729
+ capabilities = { multiProcess: true };
2260
2730
  redis;
2261
2731
  prefix;
2262
2732
  constructor(config) {
@@ -2394,11 +2864,13 @@ var RedisStorageAdapter = class {
2394
2864
  );
2395
2865
  break;
2396
2866
  }
2397
- const nowStr = (/* @__PURE__ */ new Date()).toISOString();
2398
- const toTouch = results.slice(0, query.limit ?? 10);
2399
- for (const r of toTouch) {
2400
- r._touchedAt = nowStr;
2401
- await this.redis.set(this._memKey(r.id), JSON.stringify(r));
2867
+ if (query.touch !== false) {
2868
+ const nowStr = (/* @__PURE__ */ new Date()).toISOString();
2869
+ const toTouch = results.slice(0, query.limit ?? 10);
2870
+ for (const r of toTouch) {
2871
+ r._touchedAt = nowStr;
2872
+ await this.redis.set(this._memKey(r.id), JSON.stringify(r));
2873
+ }
2402
2874
  }
2403
2875
  const limit = query.limit ?? 10;
2404
2876
  return results.slice(0, limit).map((r) => this._toExternal(r));
@@ -3332,6 +3804,7 @@ var LanceDBStorageAdapter = class {
3332
3804
 
3333
3805
  // src/adapters/storage/mongodb.ts
3334
3806
  var MongoDBStorageAdapter = class {
3807
+ capabilities = { multiProcess: true };
3335
3808
  collection;
3336
3809
  vectorDimensions;
3337
3810
  constructor(config) {
@@ -3380,7 +3853,16 @@ var MongoDBStorageAdapter = class {
3380
3853
  expiresAt: input.expiresAt,
3381
3854
  createdAt: input.createdAt ?? now
3382
3855
  };
3383
- await this.collection.insertOne(this.toDoc(memory));
3856
+ if (input.id) {
3857
+ const { _id, actorId, memoryType, createdAt, ...fields } = this.toDoc(memory);
3858
+ await this.collection.updateOne(
3859
+ { _id },
3860
+ { $set: fields, $setOnInsert: { actorId, memoryType, createdAt } },
3861
+ { upsert: true }
3862
+ );
3863
+ } else {
3864
+ await this.collection.insertOne(this.toDoc(memory));
3865
+ }
3384
3866
  return memory;
3385
3867
  }
3386
3868
  async storeBatch(inputs) {
@@ -3510,6 +3992,33 @@ var MongoDBStorageAdapter = class {
3510
3992
  };
3511
3993
 
3512
3994
  // src/adapters/storage/sqlite.ts
3995
+ var MIGRATIONS_TABLE = "memstack_schema_migrations";
3996
+ var MIGRATIONS = [
3997
+ {
3998
+ version: 1,
3999
+ sql: (t) => `
4000
+ CREATE TABLE IF NOT EXISTS ${t} (
4001
+ id TEXT PRIMARY KEY,
4002
+ actor_id TEXT NOT NULL,
4003
+ memory_type TEXT NOT NULL DEFAULT 'interaction',
4004
+ content TEXT NOT NULL,
4005
+ importance REAL NOT NULL DEFAULT 0.5,
4006
+ emotional_valence REAL NOT NULL DEFAULT 0,
4007
+ tags TEXT NOT NULL DEFAULT '[]',
4008
+ embedding TEXT,
4009
+ source_id TEXT,
4010
+ metadata TEXT NOT NULL DEFAULT '{}',
4011
+ expires_at TEXT,
4012
+ created_at TEXT NOT NULL,
4013
+ touched_at TEXT NOT NULL
4014
+ );
4015
+ CREATE INDEX IF NOT EXISTS idx_${t}_actor ON ${t}(actor_id);
4016
+ CREATE INDEX IF NOT EXISTS idx_${t}_created ON ${t}(created_at DESC);
4017
+ CREATE INDEX IF NOT EXISTS idx_${t}_importance ON ${t}(importance DESC);
4018
+ `
4019
+ }
4020
+ ];
4021
+ MIGRATIONS[MIGRATIONS.length - 1].version;
3513
4022
  function cosineSimilarity(a, b) {
3514
4023
  if (a.length === 0 || b.length === 0) return 0;
3515
4024
  const dims = Math.min(a.length, b.length);
@@ -3525,9 +4034,12 @@ function cosineSimilarity(a, b) {
3525
4034
  return mag === 0 ? 0 : dot / mag;
3526
4035
  }
3527
4036
  var SQLiteStorageAdapter = class {
4037
+ capabilities = { multiProcess: true };
3528
4038
  db;
3529
4039
  table;
3530
4040
  vectorDimensions;
4041
+ busyTimeoutMs;
4042
+ walMode;
3531
4043
  constructor(config) {
3532
4044
  if (!config.db) {
3533
4045
  throw configError("SQLiteStorageAdapter requires a better-sqlite3 database instance");
@@ -3538,33 +4050,67 @@ var SQLiteStorageAdapter = class {
3538
4050
  throw configError(`Invalid table name: "${this.table}". Use only alphanumeric characters and underscores.`);
3539
4051
  }
3540
4052
  this.vectorDimensions = config.vectorDimensions ?? 1536;
4053
+ this.busyTimeoutMs = config.busyTimeoutMs ?? 5e3;
4054
+ if (!Number.isInteger(this.busyTimeoutMs) || this.busyTimeoutMs < 0) {
4055
+ throw configError(`Invalid busyTimeoutMs: ${config.busyTimeoutMs}. Use a non-negative integer.`);
4056
+ }
4057
+ this.walMode = config.walMode ?? true;
3541
4058
  }
3542
4059
  async initialize() {
3543
- this.db.exec(`
3544
- CREATE TABLE IF NOT EXISTS ${this.table} (
3545
- id TEXT PRIMARY KEY,
3546
- actor_id TEXT NOT NULL,
3547
- memory_type TEXT NOT NULL DEFAULT 'interaction',
3548
- content TEXT NOT NULL,
3549
- importance REAL NOT NULL DEFAULT 0.5,
3550
- emotional_valence REAL NOT NULL DEFAULT 0,
3551
- tags TEXT NOT NULL DEFAULT '[]',
3552
- embedding TEXT,
3553
- source_id TEXT,
3554
- metadata TEXT NOT NULL DEFAULT '{}',
3555
- expires_at TEXT,
3556
- created_at TEXT NOT NULL,
3557
- touched_at TEXT NOT NULL
3558
- )
3559
- `);
3560
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${this.table}_actor ON ${this.table}(actor_id)`);
3561
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${this.table}_created ON ${this.table}(created_at DESC)`);
3562
- this.db.exec(`CREATE INDEX IF NOT EXISTS idx_${this.table}_importance ON ${this.table}(importance DESC)`);
4060
+ this.db.exec(`PRAGMA busy_timeout = ${this.busyTimeoutMs}`);
4061
+ if (this.walMode) this.db.exec("PRAGMA journal_mode = WAL");
4062
+ this.migrate();
4063
+ }
4064
+ /** Schema version applied to this adapter's table, 0 if none. */
4065
+ schemaVersion() {
4066
+ const row = this.db.prepare(
4067
+ `SELECT version FROM ${MIGRATIONS_TABLE} WHERE table_name = ?`
4068
+ ).get(this.table);
4069
+ return row?.version ?? 0;
4070
+ }
4071
+ // Versions are tracked per table rather than with PRAGMA user_version,
4072
+ // so adapters with different table names can share one database file.
4073
+ migrate() {
4074
+ for (const migration of MIGRATIONS) {
4075
+ this.transaction(() => {
4076
+ this.db.exec(
4077
+ `CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (table_name TEXT PRIMARY KEY, version INTEGER NOT NULL)`
4078
+ );
4079
+ if (this.schemaVersion() >= migration.version) return;
4080
+ this.db.exec(migration.sql(this.table));
4081
+ this.db.prepare(
4082
+ `INSERT INTO ${MIGRATIONS_TABLE} (table_name, version) VALUES (?, ?)
4083
+ ON CONFLICT(table_name) DO UPDATE SET version = excluded.version`
4084
+ ).run(this.table, migration.version);
4085
+ });
4086
+ }
4087
+ }
4088
+ // BEGIN IMMEDIATE takes the write lock up front, so a concurrent writer
4089
+ // waits on busy_timeout instead of failing when it upgrades a read lock.
4090
+ transaction(fn) {
4091
+ if (this.db.inTransaction) return fn();
4092
+ this.db.exec("BEGIN IMMEDIATE");
4093
+ try {
4094
+ const result = fn();
4095
+ this.db.exec("COMMIT");
4096
+ return result;
4097
+ } catch (error) {
4098
+ if (this.db.inTransaction !== false) {
4099
+ try {
4100
+ this.db.exec("ROLLBACK");
4101
+ } catch {
4102
+ }
4103
+ }
4104
+ throw error;
4105
+ }
3563
4106
  }
3564
4107
  generateId() {
3565
4108
  return `mem_${Date.now().toString(36)}_${crypto.randomUUID().slice(0, 8)}`;
3566
4109
  }
3567
4110
  async store(input) {
4111
+ return this.insert(input);
4112
+ }
4113
+ insert(input) {
3568
4114
  const now = (/* @__PURE__ */ new Date()).toISOString();
3569
4115
  const id = input.id ?? this.generateId();
3570
4116
  this.db.prepare(
@@ -3611,11 +4157,7 @@ var SQLiteStorageAdapter = class {
3611
4157
  };
3612
4158
  }
3613
4159
  async storeBatch(inputs) {
3614
- const results = [];
3615
- for (const input of inputs) {
3616
- results.push(await this.store(input));
3617
- }
3618
- return results;
4160
+ return this.transaction(() => inputs.map((input) => this.insert(input)));
3619
4161
  }
3620
4162
  async get(id) {
3621
4163
  const row = this.db.prepare(
@@ -3661,7 +4203,6 @@ var SQLiteStorageAdapter = class {
3661
4203
  conditions.push("created_at <= ?");
3662
4204
  params.push(query.createdBefore.toISOString());
3663
4205
  }
3664
- const where = `WHERE ${conditions.join(" AND ")}`;
3665
4206
  const hasSemantic = (query.strategy === "semantic" || query.strategy === "hybrid") && embedding && embedding.length > 0;
3666
4207
  let orderBy;
3667
4208
  switch (query.strategy) {
@@ -3682,9 +4223,8 @@ var SQLiteStorageAdapter = class {
3682
4223
  break;
3683
4224
  }
3684
4225
  const limit = query.limit ?? 10;
3685
- const sql = `SELECT * FROM ${this.table} ${where} ORDER BY ${orderBy}`;
3686
- const rows = this.db.prepare(sql).all(...params);
3687
- let results = rows;
4226
+ const sql = `SELECT * FROM ${this.table} WHERE ${conditions.join(" AND ")} ORDER BY ${orderBy}`;
4227
+ let results = this.db.prepare(sql).all(...params);
3688
4228
  if (query.query) {
3689
4229
  const q = query.query.toLowerCase();
3690
4230
  results = results.filter((r) => r.content.toLowerCase().includes(q));
@@ -3704,17 +4244,12 @@ var SQLiteStorageAdapter = class {
3704
4244
  if (query.strategy === "hybrid") {
3705
4245
  scored.sort((a, b) => b.score + b.row.importance - (a.score + a.row.importance));
3706
4246
  }
3707
- const topK = scored.slice(0, limit);
3708
- for (const { row } of topK) {
3709
- this.db.prepare(`UPDATE ${this.table} SET touched_at = ? WHERE id = ?`).run((/* @__PURE__ */ new Date()).toISOString(), row.id);
3710
- }
3711
- return topK.map(({ row }) => this._rowToMemory(row));
4247
+ const topK = scored.slice(0, limit).map(({ row }) => row);
4248
+ this.touchRows(topK, query.touch);
4249
+ return topK.map((row) => this._rowToMemory(row));
3712
4250
  }
3713
- const now = (/* @__PURE__ */ new Date()).toISOString();
3714
4251
  const limited = results.slice(0, limit);
3715
- for (const row of limited) {
3716
- this.db.prepare(`UPDATE ${this.table} SET touched_at = ? WHERE id = ?`).run(now, row.id);
3717
- }
4252
+ this.touchRows(limited, query.touch);
3718
4253
  return limited.map((r) => this._rowToMemory(r));
3719
4254
  }
3720
4255
  async count(filter) {
@@ -3749,6 +4284,14 @@ var SQLiteStorageAdapter = class {
3749
4284
  async close() {
3750
4285
  this.db.close();
3751
4286
  }
4287
+ touchRows(rows, touch) {
4288
+ if (touch === false || rows.length === 0) return;
4289
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4290
+ const stmt = this.db.prepare(`UPDATE ${this.table} SET touched_at = ? WHERE id = ?`);
4291
+ this.transaction(() => {
4292
+ for (const row of rows) stmt.run(now, row.id);
4293
+ });
4294
+ }
3752
4295
  _rowToMemory(row) {
3753
4296
  return {
3754
4297
  id: row.id,
@@ -4257,10 +4800,13 @@ var GroqLLMAdapter = class {
4257
4800
  exports.AnthropicLLMAdapter = AnthropicLLMAdapter;
4258
4801
  exports.CohereEmbeddingAdapter = CohereEmbeddingAdapter;
4259
4802
  exports.DiskStorageAdapter = DiskStorageAdapter;
4803
+ exports.GLOBAL_NAMESPACE = GLOBAL_NAMESPACE;
4260
4804
  exports.GroqLLMAdapter = GroqLLMAdapter;
4805
+ exports.HarnessMemory = HarnessMemory;
4261
4806
  exports.HybridStorageAdapter = HybridStorageAdapter;
4262
4807
  exports.InMemoryStorageAdapter = InMemoryStorageAdapter;
4263
4808
  exports.LanceDBStorageAdapter = LanceDBStorageAdapter;
4809
+ exports.LexicalRetriever = LexicalRetriever;
4264
4810
  exports.MarkdownStorageAdapter = MarkdownStorageAdapter;
4265
4811
  exports.MemStack = MemStack;
4266
4812
  exports.MemStackError = MemStackError;
@@ -4275,7 +4821,10 @@ exports.RedisStorageAdapter = RedisStorageAdapter;
4275
4821
  exports.SQLiteStorageAdapter = SQLiteStorageAdapter;
4276
4822
  exports.WeaviateStorageAdapter = WeaviateStorageAdapter;
4277
4823
  exports.configError = configError;
4824
+ exports.defaultRecallNamespaces = defaultRecallNamespaces;
4278
4825
  exports.notFound = notFound;
4826
+ exports.projectNamespace = projectNamespace;
4827
+ exports.sessionNamespace = sessionNamespace;
4279
4828
  exports.storageError = storageError;
4280
4829
  exports.validationError = validationError;
4281
4830
  //# sourceMappingURL=index.cjs.map