wolbarg 0.5.3 → 0.5.5

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
@@ -15,6 +15,11 @@ var WolbargError = class extends Error {
15
15
  reason;
16
16
  suggestion;
17
17
  operation;
18
+ /**
19
+ * @param message - Human-readable error message.
20
+ * @param code - Stable error code string.
21
+ * @param options - Optional cause, reason, suggestion, and operation name.
22
+ */
18
23
  constructor(message, code, options) {
19
24
  super(message, options);
20
25
  this.name = "WolbargError";
@@ -26,48 +31,80 @@ var WolbargError = class extends Error {
26
31
  }
27
32
  };
28
33
  var InitializationError = class extends WolbargError {
34
+ /**
35
+ * @param message - Description of the initialization failure.
36
+ * @param options - Optional cause and structured hints.
37
+ */
29
38
  constructor(message, options) {
30
39
  super(message, "INITIALIZATION_ERROR", options);
31
40
  this.name = "InitializationError";
32
41
  }
33
42
  };
34
43
  var ConfigurationError = class extends WolbargError {
44
+ /**
45
+ * @param message - Description of the misconfiguration.
46
+ * @param options - Optional cause, reason, suggestion, and operation name.
47
+ */
35
48
  constructor(message, options) {
36
49
  super(message, "CONFIGURATION_ERROR", options);
37
50
  this.name = "ConfigurationError";
38
51
  }
39
52
  };
40
53
  var ValidationError = class extends WolbargError {
54
+ /**
55
+ * @param message - Which argument failed and why.
56
+ * @param options - Optional cause and structured hints.
57
+ */
41
58
  constructor(message, options) {
42
59
  super(message, "VALIDATION_ERROR", options);
43
60
  this.name = "ValidationError";
44
61
  }
45
62
  };
46
63
  var DatabaseError = class extends WolbargError {
64
+ /**
65
+ * @param message - Operation-scoped failure message.
66
+ * @param options - Optional underlying `cause` and hints.
67
+ */
47
68
  constructor(message, options) {
48
69
  super(message, "DATABASE_ERROR", options);
49
70
  this.name = "DatabaseError";
50
71
  }
51
72
  };
52
73
  var StorageLockedError = class extends WolbargError {
74
+ /**
75
+ * @param message - Lock contention description.
76
+ * @param options - Typically includes suggestion to tune concurrency or use Postgres.
77
+ */
53
78
  constructor(message, options) {
54
79
  super(message, "WOLBARG_STORAGE_LOCKED", options);
55
80
  this.name = "StorageLockedError";
56
81
  }
57
82
  };
58
83
  var EmbeddingError = class extends WolbargError {
84
+ /**
85
+ * @param message - Embedding failure description.
86
+ * @param options - Optional HTTP cause and provider hints.
87
+ */
59
88
  constructor(message, options) {
60
89
  super(message, "EMBEDDING_ERROR", options);
61
90
  this.name = "EmbeddingError";
62
91
  }
63
92
  };
64
93
  var CompressionError = class extends WolbargError {
94
+ /**
95
+ * @param message - Compression failure description.
96
+ * @param options - Optional LLM cause chain.
97
+ */
65
98
  constructor(message, options) {
66
99
  super(message, "COMPRESSION_ERROR", options);
67
100
  this.name = "CompressionError";
68
101
  }
69
102
  };
70
103
  var MemoryNotFoundError = class extends WolbargError {
104
+ /**
105
+ * @param message - Which memory was not found.
106
+ * @param options - Optional operation context.
107
+ */
71
108
  constructor(message, options) {
72
109
  super(message, "MEMORY_NOT_FOUND", options);
73
110
  this.name = "MemoryNotFoundError";
@@ -75,6 +112,11 @@ var MemoryNotFoundError = class extends WolbargError {
75
112
  };
76
113
  var ProviderNotConfiguredError = class extends ConfigurationError {
77
114
  provider;
115
+ /**
116
+ * @param provider - Provider name (e.g. `"reranker"`, `"graph"`).
117
+ * @param method - Facade method that requires the provider.
118
+ * @param hint - Install or config instruction shown to the developer.
119
+ */
78
120
  constructor(provider, method, hint) {
79
121
  super(`${method} requires ${provider} \u2014 ${hint}`, {
80
122
  operation: method,
@@ -86,6 +128,10 @@ var ProviderNotConfiguredError = class extends ConfigurationError {
86
128
  }
87
129
  };
88
130
  var GraphCheckpointNotSupportedError = class extends WolbargError {
131
+ /**
132
+ * @param backend - Graph backend name (e.g. `"neo4j"`).
133
+ * @param operation - Requested operation (e.g. `"checkpoint"`).
134
+ */
89
135
  constructor(backend, operation) {
90
136
  super(
91
137
  `graph checkpoint not supported for network-backed graph providers (${backend})`,
@@ -158,9 +204,11 @@ Rules:
158
204
  var LlmCompressionProvider = class {
159
205
  name = "llm";
160
206
  llm;
207
+ /** @param llm - Configured LLM used for summarization. */
161
208
  constructor(llm) {
162
209
  this.llm = llm;
163
210
  }
211
+ /** @inheritdoc */
164
212
  async compress(memories) {
165
213
  return compressMemories(this.llm, memories);
166
214
  }
@@ -269,6 +317,7 @@ function slidingWindow(text, chunkSize, overlap) {
269
317
  }
270
318
  var FixedChunkingStrategy = class {
271
319
  name = "fixed";
320
+ /** @inheritdoc */
272
321
  chunk(text, options) {
273
322
  const { chunkSize, overlap } = clampOptions(options);
274
323
  return slidingWindow(text, chunkSize, overlap);
@@ -276,6 +325,7 @@ var FixedChunkingStrategy = class {
276
325
  };
277
326
  var SentenceChunkingStrategy = class {
278
327
  name = "sentence";
328
+ /** @inheritdoc */
279
329
  chunk(text, options) {
280
330
  const { chunkSize, overlap } = clampOptions(options);
281
331
  const sentences = text.split(/(?<=[.!?])\s+/).map((s) => s.trim()).filter(Boolean);
@@ -284,6 +334,7 @@ var SentenceChunkingStrategy = class {
284
334
  };
285
335
  var ParagraphChunkingStrategy = class {
286
336
  name = "paragraph";
337
+ /** @inheritdoc */
287
338
  chunk(text, options) {
288
339
  const { chunkSize, overlap } = clampOptions(options);
289
340
  const paragraphs = text.split(/\n\s*\n/).map((p) => p.trim()).filter(Boolean);
@@ -292,6 +343,7 @@ var ParagraphChunkingStrategy = class {
292
343
  };
293
344
  var MarkdownChunkingStrategy = class {
294
345
  name = "markdown";
346
+ /** @inheritdoc */
295
347
  chunk(text, options) {
296
348
  const { chunkSize, overlap } = clampOptions(options);
297
349
  const sections = text.split(/(?=^#{1,6}\s)/m).map((s) => s.trim()).filter(Boolean);
@@ -303,6 +355,7 @@ var MarkdownChunkingStrategy = class {
303
355
  };
304
356
  var HeadingChunkingStrategy = class {
305
357
  name = "heading";
358
+ /** @inheritdoc */
306
359
  chunk(text, options) {
307
360
  return new MarkdownChunkingStrategy().chunk(text, options);
308
361
  }
@@ -384,6 +437,12 @@ function distanceToSimilarity(distance) {
384
437
  }
385
438
  var AsyncMutex = class {
386
439
  chain = Promise.resolve();
440
+ /**
441
+ * Run `fn` exclusively — concurrent callers queue on the same mutex.
442
+ *
443
+ * @param fn - Async or sync work to serialize.
444
+ * @returns The value returned by `fn`.
445
+ */
387
446
  async runExclusive(fn) {
388
447
  let release;
389
448
  const next = new Promise((resolve) => {
@@ -407,16 +466,32 @@ function joinUrl(baseUrl, path8) {
407
466
 
408
467
  // src/embedding/index.ts
409
468
  var OpenAICompatibleEmbeddingProvider = class {
469
+ /** Model name sent in the request body. */
410
470
  model;
411
471
  baseUrl;
412
472
  apiKey;
413
473
  timeoutMs;
474
+ /**
475
+ * @param config - OpenAI-compatible embedding endpoint settings.
476
+ * @param config.baseUrl - API root, e.g. `https://api.openai.com/v1`.
477
+ * @param config.apiKey - Bearer token. Use any non-empty string for local
478
+ * servers that ignore auth.
479
+ * @param config.model - Embedding model id (e.g. `"text-embedding-3-small"`).
480
+ * @param config.timeoutMs - Abort after this many ms. Defaults to `30_000`.
481
+ */
414
482
  constructor(config) {
415
483
  this.baseUrl = config.baseUrl;
416
484
  this.apiKey = config.apiKey;
417
485
  this.model = config.model;
418
486
  this.timeoutMs = config.timeoutMs ?? 3e4;
419
487
  }
488
+ /**
489
+ * Embed a single string.
490
+ *
491
+ * @param text - Input text.
492
+ * @returns Embedding vector.
493
+ * @throws {EmbeddingError} On HTTP errors, timeouts, or empty vectors.
494
+ */
420
495
  async embed(text) {
421
496
  const response = await this.request(text);
422
497
  const vector = response.data?.[0]?.embedding;
@@ -425,6 +500,13 @@ var OpenAICompatibleEmbeddingProvider = class {
425
500
  }
426
501
  return Float32Array.from(vector);
427
502
  }
503
+ /**
504
+ * Embed multiple strings in one API call when the server supports batch input.
505
+ * Falls back to sequential {@link embed} if the response length mismatches.
506
+ *
507
+ * @param texts - Inputs to embed.
508
+ * @returns One vector per input, same order.
509
+ */
428
510
  async embedBatch(texts) {
429
511
  if (texts.length === 0) {
430
512
  return [];
@@ -451,6 +533,12 @@ var OpenAICompatibleEmbeddingProvider = class {
451
533
  return Float32Array.from(item.embedding);
452
534
  });
453
535
  }
536
+ /**
537
+ * Probe the endpoint and report vector dimensionality.
538
+ *
539
+ * @returns `{ dimensions }` from a fixed health-check string.
540
+ * @throws {EmbeddingError} When the probe fails.
541
+ */
454
542
  async validate() {
455
543
  try {
456
544
  const embedding = await this.embed("Wolbarg health check");
@@ -465,6 +553,12 @@ var OpenAICompatibleEmbeddingProvider = class {
465
553
  );
466
554
  }
467
555
  }
556
+ /**
557
+ * Low-level HTTP request to `/embeddings`.
558
+ *
559
+ * @param input - Single string or batch of strings.
560
+ * @returns Parsed OpenAI-style JSON body.
561
+ */
468
562
  async request(input) {
469
563
  const url = joinUrl(this.baseUrl, "/embeddings");
470
564
  const controller = new AbortController();
@@ -512,6 +606,7 @@ var OpenAICompatibleEmbeddingProvider = class {
512
606
  clearTimeout(timer);
513
607
  }
514
608
  }
609
+ /** @param error - Unknown thrown value. @returns Human-readable message. */
515
610
  describe(error) {
516
611
  if (error instanceof Error) {
517
612
  return error.message;
@@ -662,6 +757,45 @@ function withEmbeddingCache(provider, store, config) {
662
757
  };
663
758
  }
664
759
 
760
+ // src/utils/vector.ts
761
+ function cosineDistance(a, b) {
762
+ if (a.length !== b.length) {
763
+ throw new Error(
764
+ `cosineDistance dimension mismatch: a.length=${a.length}, b.length=${b.length}`
765
+ );
766
+ }
767
+ const len = a.length;
768
+ let dot = 0;
769
+ let normA = 0;
770
+ let normB = 0;
771
+ for (let i = 0; i < len; i += 1) {
772
+ const av = a[i] ?? 0;
773
+ const bv = b[i] ?? 0;
774
+ dot += av * bv;
775
+ normA += av * av;
776
+ normB += bv * bv;
777
+ }
778
+ if (normA === 0 || normB === 0) {
779
+ return 1;
780
+ }
781
+ const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
782
+ return 1 - similarity;
783
+ }
784
+ function bufferToEmbedding(data) {
785
+ const byteOffset = data.byteOffset ?? 0;
786
+ const byteLength = data.byteLength;
787
+ if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
788
+ return new Float32Array(
789
+ data.buffer,
790
+ byteOffset,
791
+ byteLength / Float32Array.BYTES_PER_ELEMENT
792
+ );
793
+ }
794
+ const copy = new Uint8Array(byteLength);
795
+ copy.set(data);
796
+ return new Float32Array(copy.buffer);
797
+ }
798
+
665
799
  // src/embedding/cache-store.ts
666
800
  var TOUCH_FLUSH_THRESHOLD = 64;
667
801
  var L1_DEFAULT_MAX = 5e4;
@@ -729,6 +863,10 @@ var SqliteEmbeddingCacheStore = class {
729
863
  pendingSets = /* @__PURE__ */ new Map();
730
864
  persistScheduled = false;
731
865
  stmts = null;
866
+ /**
867
+ * @param dbOrGetter - SQLite `DatabaseSync` or lazy getter (allows late bind).
868
+ * @param options.ttlMs - Optional TTL for cache entries on read.
869
+ */
732
870
  constructor(dbOrGetter, options) {
733
871
  this.ttlMs = options?.ttlMs ?? null;
734
872
  this.getDb = typeof dbOrGetter === "function" ? dbOrGetter : () => dbOrGetter;
@@ -746,7 +884,9 @@ var SqliteEmbeddingCacheStore = class {
746
884
  }
747
885
  this.stmts = {
748
886
  get: db.prepare(
749
- `SELECT vector, last_used_at, created_at FROM embedding_cache WHERE cache_key = ?`
887
+ `SELECT model, vector, last_used_at, created_at
888
+ FROM embedding_cache
889
+ WHERE cache_key = ?`
750
890
  ),
751
891
  delete: db.prepare(`DELETE FROM embedding_cache WHERE cache_key = ?`),
752
892
  set: db.prepare(
@@ -781,7 +921,29 @@ var SqliteEmbeddingCacheStore = class {
781
921
  this.l1.set(cacheKey, pending.model, pending.vector);
782
922
  return pending.vector;
783
923
  }
784
- return null;
924
+ const db = this.requireDb();
925
+ if (!db) return null;
926
+ try {
927
+ const stmts = this.ensureStatements(db);
928
+ const row = stmts.get.get(cacheKey);
929
+ if (!row) return null;
930
+ if (this.ttlMs !== null) {
931
+ const createdMs = row.created_at ? Date.parse(row.created_at) : 0;
932
+ if (createdMs && Date.now() - createdMs > this.ttlMs) {
933
+ stmts.delete.run(cacheKey);
934
+ return null;
935
+ }
936
+ }
937
+ const vector = bufferToEmbedding(row.vector);
938
+ this.l1.set(cacheKey, row.model, vector);
939
+ this.pendingTouches.add(cacheKey);
940
+ if (this.pendingTouches.size >= TOUCH_FLUSH_THRESHOLD) {
941
+ this.schedulePersist();
942
+ }
943
+ return vector;
944
+ } catch {
945
+ return null;
946
+ }
785
947
  }
786
948
  async set(cacheKey, model, vector) {
787
949
  this.l1.set(cacheKey, model, vector);
@@ -833,6 +995,16 @@ var SqliteEmbeddingCacheStore = class {
833
995
  this.pendingSets.clear();
834
996
  const touches = [...this.pendingTouches];
835
997
  this.pendingTouches.clear();
998
+ const requeue = () => {
999
+ for (const [key, value] of sets) {
1000
+ this.pendingSets.set(key, value);
1001
+ }
1002
+ for (const key of touches) {
1003
+ this.pendingTouches.add(key);
1004
+ }
1005
+ this.stmts = null;
1006
+ this.schedulePersist();
1007
+ };
836
1008
  const now = (/* @__PURE__ */ new Date()).toISOString();
837
1009
  try {
838
1010
  const stmts = this.ensureStatements(db);
@@ -867,10 +1039,10 @@ var SqliteEmbeddingCacheStore = class {
867
1039
  db.exec("ROLLBACK");
868
1040
  } catch {
869
1041
  }
870
- this.stmts = null;
1042
+ requeue();
871
1043
  }
872
1044
  } catch {
873
- this.stmts = null;
1045
+ requeue();
874
1046
  }
875
1047
  }
876
1048
  };
@@ -971,12 +1143,23 @@ var PostgresEmbeddingCacheStore = class {
971
1143
 
972
1144
  // src/llm/index.ts
973
1145
  var OpenAICompatibleLlmProvider = class {
1146
+ /** Model name sent in the request body and exposed on the provider. */
974
1147
  model;
975
1148
  baseUrl;
976
1149
  apiKey;
977
1150
  temperature;
978
1151
  maxTokens;
979
1152
  timeoutMs;
1153
+ /**
1154
+ * @param config - OpenAI-compatible endpoint settings.
1155
+ * @param config.baseUrl - API root, e.g. `https://api.openai.com/v1` (no trailing `/chat/completions`).
1156
+ * @param config.apiKey - Bearer token (`Authorization: Bearer …`). Use any non-empty
1157
+ * string for local servers that ignore auth (e.g. `"ollama"`).
1158
+ * @param config.model - Chat model id (e.g. `"gpt-4o-mini"`, `"llama3.2"`).
1159
+ * @param config.temperature - Sampling temperature. Defaults to `0.2`.
1160
+ * @param config.maxTokens - Max completion tokens. Defaults to `4096`.
1161
+ * @param config.timeoutMs - Abort after this many ms. Defaults to `60_000`.
1162
+ */
980
1163
  constructor(config) {
981
1164
  this.baseUrl = config.baseUrl;
982
1165
  this.apiKey = config.apiKey;
@@ -985,6 +1168,13 @@ var OpenAICompatibleLlmProvider = class {
985
1168
  this.maxTokens = config.maxTokens ?? 4096;
986
1169
  this.timeoutMs = config.timeoutMs ?? 6e4;
987
1170
  }
1171
+ /**
1172
+ * Call `/chat/completions` and return the first choice’s message content.
1173
+ *
1174
+ * @param messages - Chat turns in OpenAI message format.
1175
+ * @returns Trimmed assistant text.
1176
+ * @throws {CompressionError} On HTTP errors, timeouts, or empty content.
1177
+ */
988
1178
  async complete(messages) {
989
1179
  const response = await this.request(messages);
990
1180
  const content = response.choices?.[0]?.message?.content;
@@ -993,6 +1183,11 @@ var OpenAICompatibleLlmProvider = class {
993
1183
  }
994
1184
  return content.trim();
995
1185
  }
1186
+ /**
1187
+ * Probe the endpoint with a fixed `"ok"` prompt.
1188
+ *
1189
+ * @throws {CompressionError} When validation fails.
1190
+ */
996
1191
  async validate() {
997
1192
  try {
998
1193
  await this.complete([
@@ -1014,6 +1209,12 @@ var OpenAICompatibleLlmProvider = class {
1014
1209
  );
1015
1210
  }
1016
1211
  }
1212
+ /**
1213
+ * Low-level HTTP request to `/chat/completions`.
1214
+ *
1215
+ * @param messages - Chat turns to send.
1216
+ * @returns Parsed OpenAI-style JSON body.
1217
+ */
1017
1218
  async request(messages) {
1018
1219
  const url = joinUrl(this.baseUrl, "/chat/completions");
1019
1220
  const controller = new AbortController();
@@ -1062,6 +1263,7 @@ var OpenAICompatibleLlmProvider = class {
1062
1263
  clearTimeout(timer);
1063
1264
  }
1064
1265
  }
1266
+ /** @param error - Unknown thrown value. @returns Human-readable message. */
1065
1267
  describe(error) {
1066
1268
  if (error instanceof Error) {
1067
1269
  return error.message;
@@ -1179,12 +1381,27 @@ var DocxParser = class {
1179
1381
  name = "docx";
1180
1382
  extensions = [".docx"];
1181
1383
  async parse(input) {
1384
+ let mod;
1385
+ try {
1386
+ mod = await import('mammoth');
1387
+ } catch (error) {
1388
+ throw new ConfigurationError(
1389
+ 'DOCX ingest requires the optional peer package "mammoth". Install it with: npm install mammoth',
1390
+ {
1391
+ cause: error instanceof Error ? error : void 0,
1392
+ suggestion: "npm install mammoth",
1393
+ operation: "parse"
1394
+ }
1395
+ );
1396
+ }
1397
+ const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
1398
+ if (!extractRawText) {
1399
+ throw new ConfigurationError(
1400
+ "DOCX ingest could not find mammoth.extractRawText. Ensure your mammoth version is compatible.",
1401
+ { operation: "parse" }
1402
+ );
1403
+ }
1182
1404
  try {
1183
- const mod = await import('mammoth');
1184
- const extractRawText = mod.extractRawText ?? mod.default?.extractRawText;
1185
- if (!extractRawText) {
1186
- throw new Error("mammoth.extractRawText unavailable");
1187
- }
1188
1405
  const result = await extractRawText({ buffer: input.buffer });
1189
1406
  return {
1190
1407
  text: result.value ?? "",
@@ -1192,9 +1409,14 @@ var DocxParser = class {
1192
1409
  filename: input.filename,
1193
1410
  isImage: false
1194
1411
  };
1195
- } catch {
1412
+ } catch (error) {
1196
1413
  throw new ConfigurationError(
1197
- 'DOCX ingest requires the optional "mammoth" package. Install it with: npm install mammoth'
1414
+ "DOCX ingest failed to parse the document (it may be corrupt or an unsupported .docx).",
1415
+ {
1416
+ cause: error instanceof Error ? error : void 0,
1417
+ suggestion: "Try a different DOCX file or ensure it is not password-protected.",
1418
+ operation: "parse"
1419
+ }
1198
1420
  );
1199
1421
  }
1200
1422
  }
@@ -1341,6 +1563,12 @@ function compileComparison(field, op) {
1341
1563
  const extract = `json_extract(metadata_json, '${path8}')`;
1342
1564
  if ("eq" in op) {
1343
1565
  const value = op.eq;
1566
+ if (value === null) {
1567
+ return {
1568
+ expression: `json_type(metadata_json, '${path8}') = 'null'`,
1569
+ params: []
1570
+ };
1571
+ }
1344
1572
  if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
1345
1573
  return null;
1346
1574
  }
@@ -1421,6 +1649,79 @@ function compileMetadataFilterToSql(filter) {
1421
1649
  return compileComparison(filter.field, filter.op);
1422
1650
  }
1423
1651
 
1652
+ // src/telemetry/logger.ts
1653
+ var LEVEL_ORDER = {
1654
+ off: 100,
1655
+ error: 50,
1656
+ warn: 40,
1657
+ info: 30,
1658
+ debug: 20,
1659
+ trace: 10
1660
+ };
1661
+ var WolbargLogger = class {
1662
+ /** @param level - Minimum level to print (default `"info"`). */
1663
+ constructor(level = "info") {
1664
+ this.level = level;
1665
+ }
1666
+ level;
1667
+ /** Change the minimum log level at runtime. */
1668
+ setLevel(level) {
1669
+ this.level = level;
1670
+ }
1671
+ /** @returns Current minimum log level. */
1672
+ getLevel() {
1673
+ return this.level;
1674
+ }
1675
+ /** Log at error level. */
1676
+ error(message, extra) {
1677
+ this.write("error", message, extra);
1678
+ }
1679
+ /** Log at warn level.
1680
+ * @param message - Primary log line.
1681
+ * @param extra - Optional structured payload.
1682
+ */
1683
+ warn(message, extra) {
1684
+ this.write("warn", message, extra);
1685
+ }
1686
+ /** Log at info level.
1687
+ * @param message - Primary log line.
1688
+ * @param extra - Optional structured payload.
1689
+ */
1690
+ info(message, extra) {
1691
+ this.write("info", message, extra);
1692
+ }
1693
+ /** Log at debug level.
1694
+ * @param message - Primary log line.
1695
+ * @param extra - Optional structured payload.
1696
+ */
1697
+ debug(message, extra) {
1698
+ this.write("debug", message, extra);
1699
+ }
1700
+ /** Log at trace level.
1701
+ * @param message - Primary log line.
1702
+ * @param extra - Optional structured payload.
1703
+ */
1704
+ trace(message, extra) {
1705
+ this.write("trace", message, extra);
1706
+ }
1707
+ write(level, message, extra) {
1708
+ if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
1709
+ return;
1710
+ }
1711
+ if (this.level === "off") {
1712
+ return;
1713
+ }
1714
+ const line = `[wolbarg:${level}] ${message}`;
1715
+ if (level === "error") {
1716
+ console.error(line, extra ?? "");
1717
+ } else if (level === "warn") {
1718
+ console.warn(line, extra ?? "");
1719
+ } else {
1720
+ console.log(line, extra ?? "");
1721
+ }
1722
+ }
1723
+ };
1724
+
1424
1725
  // src/schema/index.ts
1425
1726
  var SCHEMA_VERSION = 4;
1426
1727
  var META_KEYS = {
@@ -1735,40 +2036,6 @@ var SQL = {
1735
2036
  `
1736
2037
  };
1737
2038
 
1738
- // src/utils/vector.ts
1739
- function cosineDistance(a, b) {
1740
- const len = Math.min(a.length, b.length);
1741
- let dot = 0;
1742
- let normA = 0;
1743
- let normB = 0;
1744
- for (let i = 0; i < len; i += 1) {
1745
- const av = a[i] ?? 0;
1746
- const bv = b[i] ?? 0;
1747
- dot += av * bv;
1748
- normA += av * av;
1749
- normB += bv * bv;
1750
- }
1751
- if (normA === 0 || normB === 0) {
1752
- return 1;
1753
- }
1754
- const similarity = dot / (Math.sqrt(normA) * Math.sqrt(normB));
1755
- return 1 - similarity;
1756
- }
1757
- function bufferToEmbedding(data) {
1758
- const byteOffset = data.byteOffset ?? 0;
1759
- const byteLength = data.byteLength;
1760
- if (byteOffset % Float32Array.BYTES_PER_ELEMENT === 0) {
1761
- return new Float32Array(
1762
- data.buffer,
1763
- byteOffset,
1764
- byteLength / Float32Array.BYTES_PER_ELEMENT
1765
- );
1766
- }
1767
- const copy = new Uint8Array(byteLength);
1768
- copy.set(data);
1769
- return new Float32Array(copy.buffer);
1770
- }
1771
-
1772
2039
  // src/utils/vector-index.ts
1773
2040
  var InMemoryVectorIndex = class {
1774
2041
  dims;
@@ -1776,18 +2043,29 @@ var InMemoryVectorIndex = class {
1776
2043
  data;
1777
2044
  count = 0;
1778
2045
  rowidToSlot = /* @__PURE__ */ new Map();
2046
+ /**
2047
+ * @param dimensions - Embedding vector length.
2048
+ * @param initialCapacity - Initial slot capacity (default `256`).
2049
+ */
1779
2050
  constructor(dimensions, initialCapacity = 256) {
1780
2051
  this.dims = dimensions;
1781
2052
  this.data = new Float32Array(initialCapacity * dimensions);
1782
2053
  }
2054
+ /** Number of indexed vectors. */
1783
2055
  get size() {
1784
2056
  return this.count;
1785
2057
  }
2058
+ /** Remove all vectors from the index. */
1786
2059
  clear() {
1787
2060
  this.rowids.length = 0;
1788
2061
  this.rowidToSlot.clear();
1789
2062
  this.count = 0;
1790
2063
  }
2064
+ /**
2065
+ * Insert or replace a vector for `rowid` (L2-normalized internally).
2066
+ * @param rowid - Memory table row identifier.
2067
+ * @param embedding - Raw embedding vector.
2068
+ */
1791
2069
  upsert(rowid, embedding) {
1792
2070
  const existing = this.rowidToSlot.get(rowid);
1793
2071
  const slot = existing !== void 0 ? existing : (() => {
@@ -1818,6 +2096,10 @@ var InMemoryVectorIndex = class {
1818
2096
  }
1819
2097
  }
1820
2098
  }
2099
+ /**
2100
+ * Remove a vector by `rowid`.
2101
+ * @param rowid - Memory table row identifier.
2102
+ */
1821
2103
  remove(rowid) {
1822
2104
  const slot = this.rowidToSlot.get(rowid);
1823
2105
  if (slot === void 0) {
@@ -1837,7 +2119,12 @@ var InMemoryVectorIndex = class {
1837
2119
  this.rowidToSlot.delete(rowid);
1838
2120
  this.count = last;
1839
2121
  }
1840
- /** Top-k by cosine distance (1 - dot) assuming query is L2-normalized. */
2122
+ /**
2123
+ * Top-k by cosine distance (1 - dot) assuming query is L2-normalized.
2124
+ *
2125
+ * @param queryNormalized - L2-normalized query vector (use {@link normalizeEmbedding}).
2126
+ * @param topK - Maximum hits to return.
2127
+ */
1841
2128
  search(queryNormalized, topK) {
1842
2129
  const n = this.count;
1843
2130
  if (n === 0 || topK <= 0) {
@@ -1861,24 +2148,33 @@ var InMemoryVectorIndex = class {
1861
2148
  dot += queryNormalized[d] * data[base + d];
1862
2149
  }
1863
2150
  const distance = 1 - dot;
2151
+ const rowid = rowids[i];
1864
2152
  if (heapSize < k) {
1865
2153
  heapDist[heapSize] = distance;
1866
- heapRow[heapSize] = rowids[i];
2154
+ heapRow[heapSize] = rowid;
1867
2155
  heapSize += 1;
1868
2156
  if (heapSize === k) {
1869
2157
  buildMaxHeap(heapDist, heapRow, k);
1870
2158
  }
1871
- } else if (distance < heapDist[0]) {
1872
- heapDist[0] = distance;
1873
- heapRow[0] = rowids[i];
1874
- siftDown(heapDist, heapRow, 0, k);
2159
+ } else {
2160
+ const worstDist = heapDist[0];
2161
+ const worstRow = heapRow[0];
2162
+ if (distance < worstDist || distance === worstDist && rowid < worstRow) {
2163
+ heapDist[0] = distance;
2164
+ heapRow[0] = rowid;
2165
+ siftDown(heapDist, heapRow, 0, k);
2166
+ }
1875
2167
  }
1876
2168
  }
1877
2169
  const hits = new Array(heapSize);
1878
2170
  for (let i = 0; i < heapSize; i += 1) {
1879
2171
  hits[i] = { memoryRowid: heapRow[i], distance: heapDist[i] };
1880
2172
  }
1881
- hits.sort((a, b) => a.distance - b.distance);
2173
+ hits.sort((a, b) => {
2174
+ const diff = a.distance - b.distance;
2175
+ if (diff !== 0) return diff;
2176
+ return a.memoryRowid - b.memoryRowid;
2177
+ });
1882
2178
  return hits;
1883
2179
  }
1884
2180
  ensureCapacity(needed) {
@@ -1924,8 +2220,8 @@ function siftDown(dist, rows, i, n) {
1924
2220
  let largest = i;
1925
2221
  const left = (i << 1) + 1;
1926
2222
  const right = left + 1;
1927
- if (left < n && dist[left] > dist[largest]) largest = left;
1928
- if (right < n && dist[right] > dist[largest]) largest = right;
2223
+ if (left < n && (dist[left] > dist[largest] || dist[left] === dist[largest] && rows[left] > rows[largest])) largest = left;
2224
+ if (right < n && (dist[right] > dist[largest] || dist[right] === dist[largest] && rows[right] > rows[largest])) largest = right;
1929
2225
  if (largest === i) return;
1930
2226
  const td = dist[i];
1931
2227
  dist[i] = dist[largest];
@@ -2067,6 +2363,9 @@ var BATCH_INSERT_CHUNK = 96;
2067
2363
  var MAX_ANN_OVERFETCH = 8192;
2068
2364
  var INSERT_COALESCE_THRESHOLD = 24;
2069
2365
  var INSERT_COALESCE_MAX = 96;
2366
+ var warnLogger = new WolbargLogger("warn");
2367
+ var warnedSqliteVecFallback = false;
2368
+ var warnedFtsKeywordSearch = false;
2070
2369
  var SqliteStorageProvider = class _SqliteStorageProvider {
2071
2370
  name = "sqlite";
2072
2371
  connectionString;
@@ -2076,6 +2375,10 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2076
2375
  vectorDimensions = null;
2077
2376
  vectorBackend = null;
2078
2377
  sqliteVecLoaded = false;
2378
+ /** Tracks nesting depth for {@link withTransaction} so we can use savepoints. */
2379
+ transactionDepth = 0;
2380
+ /** Incrementing counter for deterministic savepoint names. */
2381
+ savepointCounter = 0;
2079
2382
  /** Hot in-process ANN for blob backend (sqlite-vec unavailable platforms). */
2080
2383
  memoryIndex = null;
2081
2384
  memoryIndexDirty = false;
@@ -2097,6 +2400,9 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2097
2400
  /** Coalesce concurrent insertMemory callers into one IMMEDIATE transaction. */
2098
2401
  insertQueue = [];
2099
2402
  insertFlushScheduled = false;
2403
+ /**
2404
+ * @param options - SQLite path / `:memory:` and optional concurrency config.
2405
+ */
2100
2406
  constructor(options) {
2101
2407
  this.connectionString = options.connectionString;
2102
2408
  this.concurrency = resolveConcurrencyConfig(options.concurrency);
@@ -2109,9 +2415,11 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2109
2415
  getDatabase() {
2110
2416
  return this.db;
2111
2417
  }
2418
+ /** Register a callback invoked when SQLite busy retries occur (tests / diagnostics). */
2112
2419
  setRetryLogger(fn) {
2113
2420
  this.retryLog = fn;
2114
2421
  }
2422
+ /** Open the database, run migrations, and prepare statements. */
2115
2423
  async open() {
2116
2424
  try {
2117
2425
  const dbPath = this.resolvePath(this.connectionString);
@@ -2142,6 +2450,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2142
2450
  } else if (this.sqliteVecLoaded) {
2143
2451
  this.vectorBackend = "sqlite-vec";
2144
2452
  } else {
2453
+ if (!warnedSqliteVecFallback) {
2454
+ warnedSqliteVecFallback = true;
2455
+ warnLogger.warn(
2456
+ "sqlite-vec unavailable on this platform; using blob ANN fallback."
2457
+ );
2458
+ }
2145
2459
  this.vectorBackend = "blob";
2146
2460
  }
2147
2461
  if (dims !== null) {
@@ -2165,6 +2479,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2165
2479
  );
2166
2480
  }
2167
2481
  }
2482
+ /** Drain pending inserts, optimize, and close the SQLite connection. */
2168
2483
  async close() {
2169
2484
  const deadline = Date.now() + 2e3;
2170
2485
  while (this.insertQueue.length > 0 && Date.now() < deadline) {
@@ -2195,6 +2510,11 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2195
2510
  this.batchBlobEmbStatements.clear();
2196
2511
  }
2197
2512
  }
2513
+ /**
2514
+ * Create or validate vec0 / blob vector storage for the given dimensionality.
2515
+ *
2516
+ * @param dimensions - Embedding length from the configured model.
2517
+ */
2198
2518
  async ensureVectorSchema(dimensions) {
2199
2519
  const existing = await this.getEmbeddingDimensions();
2200
2520
  if (existing !== null && existing !== dimensions) {
@@ -2217,13 +2537,16 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2217
2537
  this.reprepareVectorStatements();
2218
2538
  this.hydrateMemoryIndex();
2219
2539
  }
2540
+ /** @inheritdoc StorageProvider.getEmbeddingDimensions */
2220
2541
  async getEmbeddingDimensions() {
2221
2542
  return this.readMetaNumber(META_KEYS.embeddingDimensions);
2222
2543
  }
2544
+ /** @inheritdoc StorageProvider.setEmbeddingDimensions */
2223
2545
  async setEmbeddingDimensions(dimensions) {
2224
2546
  await this.setMeta(META_KEYS.embeddingDimensions, String(dimensions));
2225
2547
  this.vectorDimensions = dimensions;
2226
2548
  }
2549
+ /** Insert a single memory row (coalesced under concurrent writers). */
2227
2550
  async insertMemory(input) {
2228
2551
  this.requireVectorReady();
2229
2552
  return new Promise((resolve, reject) => {
@@ -2305,6 +2628,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2305
2628
  return row;
2306
2629
  });
2307
2630
  }
2631
+ /** Batch insert memories in chunked multi-row transactions. */
2308
2632
  async insertMemoriesBatch(inputs) {
2309
2633
  if (inputs.length === 0) {
2310
2634
  return [];
@@ -2320,6 +2644,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2320
2644
  return rows;
2321
2645
  });
2322
2646
  }
2647
+ /** Update memory content, metadata, embedding, and content hash. */
2323
2648
  async updateMemory(input) {
2324
2649
  const stmts = this.requireStatements();
2325
2650
  return this.withTransaction(() => {
@@ -2363,6 +2688,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2363
2688
  return stmts.getMemoryById.get(input.id, input.organization);
2364
2689
  });
2365
2690
  }
2691
+ /** Find an active memory by org, agent, and content hash (dedupe). */
2366
2692
  async findActiveByContentHash(organization, agent, contentHash) {
2367
2693
  const stmts = this.requireStatements();
2368
2694
  const row = stmts.findActiveByContentHash.get(
@@ -2372,6 +2698,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2372
2698
  );
2373
2699
  return row ?? null;
2374
2700
  }
2701
+ /** Scan memories matching metadata filters (may paginate in SQL). */
2375
2702
  async searchByMetadata(filter, limit) {
2376
2703
  const rows = await this.listMemories(filter, limit);
2377
2704
  if (!filter.metadata) {
@@ -2382,9 +2709,16 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2382
2709
  );
2383
2710
  }
2384
2711
  /** Keyword search via FTS5 BM25. Returns memory IDs ranked by relevance. */
2712
+ /** BM25 keyword search via FTS5 (`memories_fts`). */
2385
2713
  async searchKeyword(query, organization, topK) {
2386
2714
  const stmts = this.requireStatements();
2387
2715
  if (!stmts.searchFts) {
2716
+ if (!warnedFtsKeywordSearch) {
2717
+ warnedFtsKeywordSearch = true;
2718
+ warnLogger.warn(
2719
+ "FTS keyword search is unavailable; returning empty keyword hits."
2720
+ );
2721
+ }
2388
2722
  return [];
2389
2723
  }
2390
2724
  try {
@@ -2398,20 +2732,32 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2398
2732
  // bm25 returns lower (more negative) for better matches — invert to [0, ∞)
2399
2733
  score: 1 / (1 + Math.abs(row.rank))
2400
2734
  }));
2401
- } catch {
2735
+ } catch (error) {
2736
+ if (!warnedFtsKeywordSearch) {
2737
+ warnedFtsKeywordSearch = true;
2738
+ warnLogger.warn(
2739
+ "FTS keyword search failed; returning empty keyword hits.",
2740
+ {
2741
+ cause: error instanceof Error ? error.message : String(error)
2742
+ }
2743
+ );
2744
+ }
2402
2745
  return [];
2403
2746
  }
2404
2747
  }
2748
+ /** Fetch a memory row by UUID within an organization. */
2405
2749
  async getMemoryById(id, organization) {
2406
2750
  const stmts = this.requireStatements();
2407
2751
  const row = stmts.getMemoryById.get(id, organization);
2408
2752
  return row ?? null;
2409
2753
  }
2754
+ /** Fetch a memory row by SQLite integer rowid within an organization. */
2410
2755
  async getMemoryByRowid(rowid, organization) {
2411
2756
  const stmts = this.requireStatements();
2412
2757
  const row = stmts.getMemoryByRowid.get(rowid, organization);
2413
2758
  return row ?? null;
2414
2759
  }
2760
+ /** Batch-fetch memory rows by SQLite rowids within an organization. */
2415
2761
  async getMemoriesByRowids(rowids, organization) {
2416
2762
  const out = /* @__PURE__ */ new Map();
2417
2763
  if (rowids.length === 0) {
@@ -2430,6 +2776,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2430
2776
  }
2431
2777
  return out;
2432
2778
  }
2779
+ /** List memories matching repository filters with optional limit. */
2433
2780
  async listMemories(filter, limit) {
2434
2781
  try {
2435
2782
  if (filter.metadata) {
@@ -2442,6 +2789,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2442
2789
  });
2443
2790
  }
2444
2791
  }
2792
+ /** Approximate nearest-neighbor search (vec0 or in-memory blob index). */
2445
2793
  async searchVectors(embedding, topK) {
2446
2794
  this.requireVectorReady();
2447
2795
  if (this.vectorBackend === "sqlite-vec") {
@@ -2453,6 +2801,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2453
2801
  * Org-scoped KNN + memory rows with adaptive overfetch.
2454
2802
  * Global ANN is post-filtered by org/agent/archived; underfill triggers larger k.
2455
2803
  */
2804
+ /** ANN search joined with memory rows and org/archived post-filters. */
2456
2805
  async searchVectorsWithMemories(embedding, topK, organization, options) {
2457
2806
  this.requireVectorReady();
2458
2807
  if (topK <= 0) {
@@ -2491,6 +2840,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2491
2840
  }
2492
2841
  return out;
2493
2842
  }
2843
+ /** Soft-archive memories (compression / forget paths). */
2494
2844
  async archiveMemories(ids, organization, compressedIntoId, archivedAt) {
2495
2845
  const stmts = this.requireStatements();
2496
2846
  const archived = [];
@@ -2528,6 +2878,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2528
2878
  return archived;
2529
2879
  });
2530
2880
  }
2881
+ /** Hard-delete a single memory by id; returns whether a row was removed. */
2531
2882
  async deleteMemoryById(id, organization) {
2532
2883
  const stmts = this.requireStatements();
2533
2884
  return this.withTransaction(() => {
@@ -2541,6 +2892,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2541
2892
  return Number(result.changes) > 0;
2542
2893
  });
2543
2894
  }
2895
+ /** Hard-delete memories matching org / agent / metadata filters. */
2544
2896
  async deleteMemoriesByFilter(filter) {
2545
2897
  const stmts = this.requireStatements();
2546
2898
  const agent = filter.agent;
@@ -2557,6 +2909,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2557
2909
  return Number(result.changes);
2558
2910
  });
2559
2911
  }
2912
+ /** Remove all memories (and side tables) for an organization. */
2560
2913
  async clearOrganization(organization) {
2561
2914
  const stmts = this.requireStatements();
2562
2915
  return this.withTransaction(() => {
@@ -2573,10 +2926,12 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2573
2926
  return Number(result.changes);
2574
2927
  });
2575
2928
  }
2929
+ /** List audit history events for a memory id. */
2576
2930
  async getHistory(memoryId) {
2577
2931
  const stmts = this.requireStatements();
2578
2932
  return stmts.getHistory.all(memoryId);
2579
2933
  }
2934
+ /** Append a history row (created / archived / compressed / updated). */
2580
2935
  async insertHistoryEvent(event) {
2581
2936
  const stmts = this.requireStatements();
2582
2937
  stmts.insertHistory.run(
@@ -2587,6 +2942,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2587
2942
  event.created_at
2588
2943
  );
2589
2944
  }
2945
+ /** Aggregate memory counts for an organization (single-pass SQL). */
2590
2946
  async getStats(organization) {
2591
2947
  const stmts = this.requireStatements();
2592
2948
  const row = stmts.getStats.get(organization);
@@ -2597,6 +2953,7 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2597
2953
  totalAgents: Number(row.agents)
2598
2954
  };
2599
2955
  }
2956
+ /** On-disk database file size in bytes (0 for `:memory:`). */
2600
2957
  async getDatabaseSizeBytes() {
2601
2958
  const db = this.requireDb();
2602
2959
  if (this.connectionString === ":memory:") {
@@ -2629,21 +2986,44 @@ var SqliteStorageProvider = class _SqliteStorageProvider {
2629
2986
  }
2630
2987
  async withTransaction(fn) {
2631
2988
  const db = this.requireDb();
2632
- return withImmediateTransaction(
2633
- db,
2634
- this.concurrency,
2635
- fn,
2636
- (attempt, delayMs) => {
2637
- this.retryLog?.(
2638
- `SQLITE_BUSY retry attempt=${attempt} backoffMs=${Math.round(delayMs)}`
2639
- );
2989
+ if (this.transactionDepth > 0) {
2990
+ const savepointName = `wolbarg_sp_${this.savepointCounter++}`;
2991
+ db.exec(`SAVEPOINT ${savepointName}`);
2992
+ try {
2993
+ this.transactionDepth += 1;
2994
+ const result = await fn();
2995
+ db.exec(`RELEASE SAVEPOINT ${savepointName}`);
2996
+ return result;
2997
+ } catch (error) {
2998
+ try {
2999
+ db.exec(`ROLLBACK TO SAVEPOINT ${savepointName}`);
3000
+ } catch {
3001
+ }
3002
+ throw error;
3003
+ } finally {
3004
+ this.transactionDepth -= 1;
2640
3005
  }
2641
- );
2642
- }
2643
- // ─── internals ───────────────────────────────────────────────────────────
2644
- tryLoadSqliteVec(db) {
2645
- const plat = `${process.platform}-${process.arch}`;
2646
- if (_SqliteStorageProvider.sqliteVecUnsupported.has(plat)) {
3006
+ }
3007
+ this.transactionDepth = 1;
3008
+ try {
3009
+ return await withImmediateTransaction(
3010
+ db,
3011
+ this.concurrency,
3012
+ fn,
3013
+ (attempt, delayMs) => {
3014
+ this.retryLog?.(
3015
+ `SQLITE_BUSY retry attempt=${attempt} backoffMs=${Math.round(delayMs)}`
3016
+ );
3017
+ }
3018
+ );
3019
+ } finally {
3020
+ this.transactionDepth = 0;
3021
+ }
3022
+ }
3023
+ // ─── internals ───────────────────────────────────────────────────────────
3024
+ tryLoadSqliteVec(db) {
3025
+ const plat = `${process.platform}-${process.arch}`;
3026
+ if (_SqliteStorageProvider.sqliteVecUnsupported.has(plat)) {
2647
3027
  return false;
2648
3028
  }
2649
3029
  try {
@@ -3632,11 +4012,17 @@ var PostgresSubscribeListener = class {
3632
4012
  reconnectDelayMs;
3633
4013
  connect;
3634
4014
  onError;
4015
+ /**
4016
+ * @param options.connect - Factory returning a dedicated LISTEN client.
4017
+ * @param options.reconnectDelayMs - Delay before reconnect attempts (default 1000).
4018
+ * @param options.onError - Optional error hook for connection failures.
4019
+ */
3635
4020
  constructor(options) {
3636
4021
  this.connect = options.connect;
3637
4022
  this.reconnectDelayMs = options.reconnectDelayMs ?? 1e3;
3638
4023
  this.onError = options.onError;
3639
4024
  }
4025
+ /** Open the LISTEN connection (lazy — also started on first subscribe). */
3640
4026
  async start() {
3641
4027
  if (this.closed) {
3642
4028
  return;
@@ -3733,6 +4119,13 @@ var PostgresSubscribeListener = class {
3733
4119
  }
3734
4120
  }
3735
4121
  }
4122
+ /**
4123
+ * Register a filtered callback for memory change events.
4124
+ *
4125
+ * @param filter - Organization / agent / event-type filter.
4126
+ * @param callback - Invoked synchronously on each matching NOTIFY.
4127
+ * @returns Unsubscribe function.
4128
+ */
3736
4129
  subscribe(filter, callback) {
3737
4130
  if (this.closed) {
3738
4131
  throw new Error("Subscribe backend is closed");
@@ -3754,6 +4147,7 @@ var PostgresSubscribeListener = class {
3754
4147
  */
3755
4148
  emit(_event) {
3756
4149
  }
4150
+ /** Tear down LISTEN connection and clear subscriptions. */
3757
4151
  async close() {
3758
4152
  this.closed = true;
3759
4153
  this.subscriptions.clear();
@@ -3792,9 +4186,13 @@ function createPostgresListenerFromPool(pool, onError) {
3792
4186
 
3793
4187
  // src/storage/providers/postgres.ts
3794
4188
  var txStore = new AsyncLocalStorage();
4189
+ var warnLogger2 = new WolbargLogger("warn");
4190
+ var warnedPgvectorFallback = false;
4191
+ var warnedFtsKeywordSearch2 = false;
4192
+ var warnedHnswSoftFail = false;
3795
4193
  var STMT = {
3796
4194
  insertOne: "Wolbarg_insert_one_v5",
3797
- insertBatch: "Wolbarg_insert_batch_v5"
4195
+ insertBatch: "Wolbarg_insert_batch_v6"
3798
4196
  };
3799
4197
  function toFloat4Param(embedding) {
3800
4198
  const out = new Array(embedding.length);
@@ -3865,13 +4263,13 @@ var INSERT_ONE_SQL = `WITH mem AS (
3865
4263
  var INSERT_BATCH_SQL = `WITH mem AS (
3866
4264
  INSERT INTO memories (
3867
4265
  id, organization, agent, content_text, metadata_json,
3868
- archived, compressed_into, created_at, updated_at
4266
+ archived, compressed_into, content_hash, created_at, updated_at
3869
4267
  )
3870
- SELECT id, org, agent, txt, meta::jsonb, false, NULL, c, u
4268
+ SELECT id, org, agent, txt, meta::jsonb, false, NULL, h, c, u
3871
4269
  FROM unnest(
3872
4270
  $1::text[], $2::text[], $3::text[], $4::text[],
3873
- $5::text[], $6::timestamptz[], $7::timestamptz[]
3874
- ) AS t(id, org, agent, txt, meta, c, u)
4271
+ $5::text[], $6::timestamptz[], $7::timestamptz[], $8::text[]
4272
+ ) AS t(id, org, agent, txt, meta, c, u, h)
3875
4273
  RETURNING id
3876
4274
  ),
3877
4275
  hist AS (
@@ -3887,7 +4285,7 @@ var INSERT_BATCH_SQL = `WITH mem AS (
3887
4285
  emb AS (
3888
4286
  INSERT INTO memory_embeddings (memory_id, embedding, organization, agent, archived)
3889
4287
  SELECT id, emb::vector, org, agent, false
3890
- FROM unnest($1::text[], $8::text[], $2::text[], $3::text[]) AS t(id, emb, org, agent)
4288
+ FROM unnest($1::text[], $9::text[], $2::text[], $3::text[]) AS t(id, emb, org, agent)
3891
4289
  )
3892
4290
  SELECT id FROM mem`;
3893
4291
  var COALESCE_FLUSH_MAX = 128;
@@ -3914,6 +4312,9 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
3914
4312
  insertFlushScheduled = false;
3915
4313
  insertFlushTimer = null;
3916
4314
  insertFlushInFlight = 0;
4315
+ /**
4316
+ * @param options - Connection string, pool size, and durability flags.
4317
+ */
3917
4318
  constructor(options) {
3918
4319
  this.maxPoolSize = options.maxPoolSize ?? 64;
3919
4320
  this.durableWrites = options.durableWrites !== false;
@@ -3922,6 +4323,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
3922
4323
  this.durableWrites
3923
4324
  );
3924
4325
  }
4326
+ /** Current pg pool occupancy stats (for diagnostics / subscribe setup). */
3925
4327
  getPoolStats() {
3926
4328
  const pool = this.pool;
3927
4329
  return {
@@ -3935,6 +4337,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
3935
4337
  getPool() {
3936
4338
  return this.pool;
3937
4339
  }
4340
+ /** Open the connection pool and run migrations. */
3938
4341
  async open() {
3939
4342
  let PoolCtor;
3940
4343
  try {
@@ -3983,6 +4386,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
3983
4386
  );
3984
4387
  }
3985
4388
  }
4389
+ /** Drain coalesced inserts and end the connection pool. */
3986
4390
  async close() {
3987
4391
  if (this.insertFlushTimer) {
3988
4392
  clearTimeout(this.insertFlushTimer);
@@ -4023,6 +4427,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4023
4427
  async ensureVectorIndex() {
4024
4428
  await this.ensureHnswIndex();
4025
4429
  }
4430
+ /**
4431
+ * Create pgvector tables and HNSW index for the given dimensionality.
4432
+ *
4433
+ * @param dimensions - Embedding length from the configured model.
4434
+ */
4026
4435
  async ensureVectorSchema(dimensions) {
4027
4436
  const existing = await this.getEmbeddingDimensions();
4028
4437
  if (existing !== null && existing !== dimensions) {
@@ -4087,6 +4496,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4087
4496
  }
4088
4497
  }
4089
4498
  /** Cross-process subscribe: NOTIFY after a committed write. */
4499
+ /**
4500
+ * Issue `pg_notify` for cross-process {@link subscribe} delivery.
4501
+ *
4502
+ * @param event - Memory change payload (IDs + metadata only).
4503
+ */
4090
4504
  async notifyChange(event) {
4091
4505
  await notifyMemoryChange(this.requirePool(), event);
4092
4506
  }
@@ -4094,6 +4508,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4094
4508
  * Soft reset for a single organization. Drops HNSW only when the embeddings
4095
4509
  * table is empty so other corpora on a shared bench DB stay intact.
4096
4510
  */
4511
+ /** Delete all rows for an organization (dev / test helper). */
4097
4512
  async resetOrganization(organization) {
4098
4513
  await this.query(`DELETE FROM memories WHERE organization = $1`, [
4099
4514
  organization
@@ -4111,6 +4526,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4111
4526
  /**
4112
4527
  * Wipe all Wolbarg tables (explicit opt-in). Prefer {@link resetOrganization}.
4113
4528
  */
4529
+ /** Truncate all Wolbarg tables (dev / test helper). */
4114
4530
  async wipeAllData() {
4115
4531
  await this.query(`TRUNCATE TABLE memories CASCADE`).catch(() => void 0);
4116
4532
  await this.query(
@@ -4156,6 +4572,12 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4156
4572
  return;
4157
4573
  }
4158
4574
  this.hnswCreateFailures += 1;
4575
+ if (this.hnswCreateFailures === 1 && !warnedHnswSoftFail) {
4576
+ warnedHnswSoftFail = true;
4577
+ warnLogger2.warn(
4578
+ "HNSW index creation failed; ANN will fall back to sequential scan."
4579
+ );
4580
+ }
4159
4581
  if (this.hnswCreateFailures >= 3) {
4160
4582
  throw new DatabaseError(
4161
4583
  `Failed to create HNSW index after ${this.hnswCreateFailures} attempts: ${this.describe(error)}`,
@@ -4164,6 +4586,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4164
4586
  }
4165
4587
  }
4166
4588
  }
4589
+ /** @inheritdoc StorageProvider.getEmbeddingDimensions */
4167
4590
  async getEmbeddingDimensions() {
4168
4591
  const result = await this.query(
4169
4592
  `SELECT value FROM Wolbarg_meta WHERE key = $1`,
@@ -4176,6 +4599,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4176
4599
  const parsed = Number(value);
4177
4600
  return Number.isFinite(parsed) ? parsed : null;
4178
4601
  }
4602
+ /** @inheritdoc StorageProvider.setEmbeddingDimensions */
4179
4603
  async setEmbeddingDimensions(dimensions) {
4180
4604
  await this.query(
4181
4605
  `INSERT INTO Wolbarg_meta (key, value) VALUES ($1, $2)
@@ -4184,6 +4608,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4184
4608
  );
4185
4609
  this.vectorDimensions = dimensions;
4186
4610
  }
4611
+ /** Insert a single memory row (coalesced under concurrent writers). */
4187
4612
  async insertMemory(input) {
4188
4613
  this.requireVectorReady();
4189
4614
  return new Promise((resolve, reject) => {
@@ -4270,6 +4695,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4270
4695
  }
4271
4696
  return this.insertOneBlob(input);
4272
4697
  }
4698
+ /** Batch insert via unnest / COPY paths with optional HNSW deferral. */
4273
4699
  async insertMemoriesBatch(inputs) {
4274
4700
  if (inputs.length === 0) {
4275
4701
  return [];
@@ -4300,6 +4726,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4300
4726
  const metas = new Array(inputs.length);
4301
4727
  const created = new Array(inputs.length);
4302
4728
  const updated = new Array(inputs.length);
4729
+ const contentHashes = new Array(inputs.length);
4303
4730
  const vectors = new Array(inputs.length);
4304
4731
  for (let i = 0; i < inputs.length; i += 1) {
4305
4732
  const input = inputs[i];
@@ -4310,6 +4737,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4310
4737
  metas[i] = serializeMetadata(input.metadata);
4311
4738
  created[i] = input.createdAt;
4312
4739
  updated[i] = input.updatedAt;
4740
+ contentHashes[i] = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
4313
4741
  vectors[i] = toVectorLiteral(input.embedding);
4314
4742
  }
4315
4743
  await this.queryNamed(STMT.insertBatch, INSERT_BATCH_SQL, [
@@ -4320,9 +4748,10 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4320
4748
  metas,
4321
4749
  created,
4322
4750
  updated,
4751
+ contentHashes,
4323
4752
  vectors
4324
4753
  ]);
4325
- return inputs.map((input, i) => ({
4754
+ return inputs.map((_, i) => ({
4326
4755
  id: ids[i],
4327
4756
  organization: orgs[i],
4328
4757
  agent: agents[i],
@@ -4330,7 +4759,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4330
4759
  metadata_json: metas[i],
4331
4760
  archived: 0,
4332
4761
  compressed_into: null,
4333
- content_hash: input.contentHash !== void 0 ? input.contentHash : null,
4762
+ content_hash: contentHashes[i] ?? null,
4334
4763
  created_at: created[i],
4335
4764
  updated_at: updated[i]
4336
4765
  }));
@@ -4371,81 +4800,87 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4371
4800
  return out;
4372
4801
  }
4373
4802
  async insertOneBlob(input) {
4374
- const buf = Buffer.from(
4375
- input.embedding.buffer,
4376
- input.embedding.byteOffset,
4377
- input.embedding.byteLength
4378
- );
4379
- const contentHash = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
4380
- const inserted = await this.query(
4381
- `INSERT INTO memories (
4382
- id, organization, agent, content_text, metadata_json,
4383
- archived, compressed_into, content_hash, created_at, updated_at
4384
- ) VALUES ($1,$2,$3,$4,$5::jsonb,false,NULL,$8,$6,$7)
4385
- RETURNING id, organization, agent, content_text, metadata_json,
4386
- archived::int AS archived, compressed_into, content_hash, created_at, updated_at`,
4387
- [
4388
- input.id,
4389
- input.organization,
4390
- input.agent,
4391
- input.contentText,
4392
- serializeMetadata(input.metadata),
4393
- input.createdAt,
4394
- input.updatedAt,
4395
- contentHash
4396
- ]
4397
- );
4398
- const row = this.mapRow(inserted.rows[0]);
4399
- await this.query(
4400
- `WITH mapped AS (
4401
- INSERT INTO memory_row_map (memory_id) VALUES ($1)
4402
- ON CONFLICT (memory_id) DO NOTHING
4403
- )
4404
- INSERT INTO memory_embeddings_blob (memory_id, embedding)
4405
- VALUES ($1, $2)
4406
- ON CONFLICT (memory_id) DO UPDATE SET embedding = EXCLUDED.embedding`,
4407
- [input.id, buf]
4408
- );
4409
- await this.query(
4410
- `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
4411
- VALUES ($1,$2,'created',NULL,$3)`,
4412
- [crypto.randomUUID(), input.id, input.createdAt]
4413
- );
4414
- return row;
4803
+ return this.withTransaction(async () => {
4804
+ const buf = Buffer.from(
4805
+ input.embedding.buffer,
4806
+ input.embedding.byteOffset,
4807
+ input.embedding.byteLength
4808
+ );
4809
+ const contentHash = input.contentHash !== void 0 ? input.contentHash : hashMemoryContent(input.contentText);
4810
+ const inserted = await this.query(
4811
+ `INSERT INTO memories (
4812
+ id, organization, agent, content_text, metadata_json,
4813
+ archived, compressed_into, content_hash, created_at, updated_at
4814
+ ) VALUES ($1,$2,$3,$4,$5::jsonb,false,NULL,$8,$6,$7)
4815
+ RETURNING id, organization, agent, content_text, metadata_json,
4816
+ archived::int AS archived, compressed_into, content_hash, created_at, updated_at`,
4817
+ [
4818
+ input.id,
4819
+ input.organization,
4820
+ input.agent,
4821
+ input.contentText,
4822
+ serializeMetadata(input.metadata),
4823
+ input.createdAt,
4824
+ input.updatedAt,
4825
+ contentHash
4826
+ ]
4827
+ );
4828
+ const row = this.mapRow(inserted.rows[0]);
4829
+ await this.query(
4830
+ `WITH mapped AS (
4831
+ INSERT INTO memory_row_map (memory_id) VALUES ($1)
4832
+ ON CONFLICT (memory_id) DO NOTHING
4833
+ )
4834
+ INSERT INTO memory_embeddings_blob (memory_id, embedding)
4835
+ VALUES ($1, $2)
4836
+ ON CONFLICT (memory_id) DO UPDATE SET embedding = EXCLUDED.embedding`,
4837
+ [input.id, buf]
4838
+ );
4839
+ await this.query(
4840
+ `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
4841
+ VALUES ($1,$2,'created',NULL,$3)`,
4842
+ [crypto.randomUUID(), input.id, input.createdAt]
4843
+ );
4844
+ return row;
4845
+ });
4415
4846
  }
4847
+ /** Update memory content, metadata, embedding, and content hash. */
4416
4848
  async updateMemory(input) {
4417
- const existing = await this.getMemoryById(input.id, input.organization);
4418
- if (!existing) {
4419
- return null;
4420
- }
4421
- const contentHash = input.contentHash !== void 0 ? input.contentHash : input.contentText !== void 0 ? hashMemoryContent(input.contentText) : existing.content_hash ?? null;
4422
- await this.query(
4423
- `UPDATE memories SET
4424
- content_text = COALESCE($1, content_text),
4425
- metadata_json = COALESCE($2::jsonb, metadata_json),
4426
- content_hash = COALESCE($3, content_hash),
4427
- updated_at = $4
4428
- WHERE id = $5 AND organization = $6`,
4429
- [
4430
- input.contentText ?? null,
4431
- input.metadata !== void 0 ? serializeMetadata(input.metadata) : null,
4432
- contentHash,
4433
- input.updatedAt,
4434
- input.id,
4435
- input.organization
4436
- ]
4437
- );
4438
- if (input.embedding) {
4439
- await this.deleteEmbedding(input.id);
4440
- await this.insertEmbedding(input.id, input.embedding);
4441
- }
4442
- await this.query(
4443
- `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
4444
- VALUES ($1,$2,'updated',NULL,$3)`,
4445
- [crypto.randomUUID(), input.id, input.updatedAt]
4446
- );
4447
- return this.getMemoryById(input.id, input.organization);
4849
+ return this.withTransaction(async () => {
4850
+ const existing = await this.getMemoryById(input.id, input.organization);
4851
+ if (!existing) {
4852
+ return null;
4853
+ }
4854
+ const contentHash = input.contentHash !== void 0 ? input.contentHash : input.contentText !== void 0 ? hashMemoryContent(input.contentText) : existing.content_hash ?? null;
4855
+ await this.query(
4856
+ `UPDATE memories SET
4857
+ content_text = COALESCE($1, content_text),
4858
+ metadata_json = COALESCE($2::jsonb, metadata_json),
4859
+ content_hash = COALESCE($3, content_hash),
4860
+ updated_at = $4
4861
+ WHERE id = $5 AND organization = $6`,
4862
+ [
4863
+ input.contentText ?? null,
4864
+ input.metadata !== void 0 ? serializeMetadata(input.metadata) : null,
4865
+ contentHash,
4866
+ input.updatedAt,
4867
+ input.id,
4868
+ input.organization
4869
+ ]
4870
+ );
4871
+ if (input.embedding) {
4872
+ await this.deleteEmbedding(input.id);
4873
+ await this.insertEmbedding(input.id, input.embedding);
4874
+ }
4875
+ await this.query(
4876
+ `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
4877
+ VALUES ($1,$2,'updated',NULL,$3)`,
4878
+ [crypto.randomUUID(), input.id, input.updatedAt]
4879
+ );
4880
+ return this.getMemoryById(input.id, input.organization);
4881
+ });
4448
4882
  }
4883
+ /** Find an active memory by org, agent, and content hash (dedupe). */
4449
4884
  async findActiveByContentHash(organization, agent, contentHash) {
4450
4885
  const result = await this.query(
4451
4886
  `SELECT id, organization, agent, content_text, metadata_json,
@@ -4458,6 +4893,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4458
4893
  const row = result.rows[0];
4459
4894
  return row ? this.mapRow(row) : null;
4460
4895
  }
4896
+ /** Fetch a memory row by UUID within an organization. */
4461
4897
  async getMemoryById(id, organization) {
4462
4898
  const result = await this.query(
4463
4899
  `SELECT id, organization, agent, content_text, metadata_json,
@@ -4468,6 +4904,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4468
4904
  const row = result.rows[0];
4469
4905
  return row ? this.mapRow(row) : null;
4470
4906
  }
4907
+ /** Fetch a memory row by internal rowid within an organization. */
4471
4908
  async getMemoryByRowid(rowid, organization) {
4472
4909
  const result = await this.query(
4473
4910
  `SELECT m.id, m.organization, m.agent, m.content_text, m.metadata_json,
@@ -4481,6 +4918,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4481
4918
  const row = result.rows[0];
4482
4919
  return row ? this.mapRow(row) : null;
4483
4920
  }
4921
+ /** Batch-fetch memory rows by rowids within an organization. */
4484
4922
  async getMemoriesByRowids(rowids, organization) {
4485
4923
  const out = /* @__PURE__ */ new Map();
4486
4924
  if (rowids.length === 0) {
@@ -4503,6 +4941,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4503
4941
  }
4504
4942
  return out;
4505
4943
  }
4944
+ /** List memories matching repository filters with optional limit. */
4506
4945
  async listMemories(filter, limit) {
4507
4946
  const want = limit !== void 0 ? limit : filter.metadata ? 1e4 : void 0;
4508
4947
  const compiled = filter.metadata ? compileMetadataFilterToPostgres(filter.metadata, 2) : null;
@@ -4564,9 +5003,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4564
5003
  }
4565
5004
  return rows;
4566
5005
  }
5006
+ /** Scan memories matching metadata filters. */
4567
5007
  async searchByMetadata(filter, limit) {
4568
5008
  return this.listMemories(filter, limit);
4569
5009
  }
5010
+ /** Full-text keyword search via `tsvector` / GIN index. */
4570
5011
  async searchKeyword(query, organization, topK) {
4571
5012
  const trimmed = query.trim();
4572
5013
  if (!trimmed || topK <= 0) {
@@ -4593,10 +5034,20 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4593
5034
  memoryId: String(row.memory_id),
4594
5035
  score: Number(row.rank)
4595
5036
  }));
4596
- } catch {
5037
+ } catch (error) {
5038
+ if (!warnedFtsKeywordSearch2) {
5039
+ warnedFtsKeywordSearch2 = true;
5040
+ warnLogger2.warn(
5041
+ "FTS keyword search failed; returning empty keyword hits.",
5042
+ {
5043
+ cause: error instanceof Error ? error.message : String(error)
5044
+ }
5045
+ );
5046
+ }
4597
5047
  return [];
4598
5048
  }
4599
5049
  }
5050
+ /** pgvector cosine ANN search (HNSW when index is present). */
4600
5051
  async searchVectors(embedding, topK) {
4601
5052
  this.requireVectorReady();
4602
5053
  if (this.hasPgvector) {
@@ -4646,6 +5097,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4646
5097
  scored.sort((a, b) => a.distance - b.distance);
4647
5098
  return scored.slice(0, topK);
4648
5099
  }
5100
+ /** ANN search joined with memory rows and org/archived post-filters. */
4649
5101
  async searchVectorsWithMemories(embedding, topK, organization, options) {
4650
5102
  this.requireVectorReady();
4651
5103
  if (!this.hasPgvector) {
@@ -4710,46 +5162,50 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4710
5162
  );
4711
5163
  return mapHits(result.rows);
4712
5164
  }
5165
+ /** Soft-archive memories (compression / forget paths). */
4713
5166
  async archiveMemories(ids, organization, compressedIntoId, archivedAt) {
4714
- if (ids.length === 0) {
4715
- return [];
4716
- }
4717
- const result = await this.query(
4718
- `UPDATE memories
4719
- SET archived = true, compressed_into = $1, updated_at = $2
4720
- WHERE organization = $3 AND archived = false AND id = ANY($4::text[])
4721
- RETURNING id`,
4722
- [compressedIntoId, archivedAt, organization, ids]
4723
- );
4724
- const archived = result.rows.map((r) => String(r.id));
4725
- if (archived.length === 0) {
4726
- return [];
4727
- }
4728
- await this.query(
4729
- `UPDATE memory_embeddings
4730
- SET archived = true
4731
- WHERE memory_id = ANY($1::text[])`,
4732
- [archived]
4733
- ).catch(() => void 0);
4734
- const histIds = [];
4735
- const memIds = [];
4736
- const types = [];
4737
- const related = [];
4738
- const times = [];
4739
- for (const id of archived) {
4740
- histIds.push(crypto.randomUUID(), crypto.randomUUID());
4741
- memIds.push(id, compressedIntoId);
4742
- types.push("archived", "compressed");
4743
- related.push(compressedIntoId, id);
4744
- times.push(archivedAt, archivedAt);
4745
- }
4746
- await this.query(
4747
- `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
4748
- SELECT * FROM unnest($1::text[], $2::text[], $3::text[], $4::text[], $5::timestamptz[])`,
4749
- [histIds, memIds, types, related, times]
4750
- );
4751
- return archived;
5167
+ return this.withTransaction(async () => {
5168
+ if (ids.length === 0) {
5169
+ return [];
5170
+ }
5171
+ const result = await this.query(
5172
+ `UPDATE memories
5173
+ SET archived = true, compressed_into = $1, updated_at = $2
5174
+ WHERE organization = $3 AND archived = false AND id = ANY($4::text[])
5175
+ RETURNING id`,
5176
+ [compressedIntoId, archivedAt, organization, ids]
5177
+ );
5178
+ const archived = result.rows.map((r) => String(r.id));
5179
+ if (archived.length === 0) {
5180
+ return [];
5181
+ }
5182
+ await this.query(
5183
+ `UPDATE memory_embeddings
5184
+ SET archived = true
5185
+ WHERE memory_id = ANY($1::text[])`,
5186
+ [archived]
5187
+ );
5188
+ const histIds = [];
5189
+ const memIds = [];
5190
+ const types = [];
5191
+ const related = [];
5192
+ const times = [];
5193
+ for (const id of archived) {
5194
+ histIds.push(crypto.randomUUID(), crypto.randomUUID());
5195
+ memIds.push(id, compressedIntoId);
5196
+ types.push("archived", "compressed");
5197
+ related.push(compressedIntoId, id);
5198
+ times.push(archivedAt, archivedAt);
5199
+ }
5200
+ await this.query(
5201
+ `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
5202
+ SELECT * FROM unnest($1::text[], $2::text[], $3::text[], $4::text[], $5::timestamptz[])`,
5203
+ [histIds, memIds, types, related, times]
5204
+ );
5205
+ return archived;
5206
+ });
4752
5207
  }
5208
+ /** Hard-delete a single memory by id; returns whether a row was removed. */
4753
5209
  async deleteMemoryById(id, organization) {
4754
5210
  const result = await this.query(
4755
5211
  `DELETE FROM memories WHERE id = $1 AND organization = $2`,
@@ -4757,6 +5213,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4757
5213
  );
4758
5214
  return (result.rowCount ?? 0) > 0;
4759
5215
  }
5216
+ /** Hard-delete memories matching org / agent / metadata filters. */
4760
5217
  async deleteMemoriesByFilter(filter) {
4761
5218
  if (!filter.agent) {
4762
5219
  throw new DatabaseError("deleteMemoriesByFilter requires an agent filter");
@@ -4767,6 +5224,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4767
5224
  );
4768
5225
  return result.rowCount ?? 0;
4769
5226
  }
5227
+ /** Remove all memories for an organization. */
4770
5228
  async clearOrganization(organization) {
4771
5229
  const result = await this.query(
4772
5230
  `DELETE FROM memories WHERE organization = $1`,
@@ -4774,6 +5232,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4774
5232
  );
4775
5233
  return result.rowCount ?? 0;
4776
5234
  }
5235
+ /** List audit history events for a memory id. */
4777
5236
  async getHistory(memoryId) {
4778
5237
  const result = await this.query(
4779
5238
  `SELECT id, memory_id, event_type, related_memory_id, created_at
@@ -4788,6 +5247,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4788
5247
  created_at: String(row.created_at)
4789
5248
  }));
4790
5249
  }
5250
+ /** Append a history row (created / archived / compressed / updated). */
4791
5251
  async insertHistoryEvent(event) {
4792
5252
  await this.query(
4793
5253
  `INSERT INTO memory_history (id, memory_id, event_type, related_memory_id, created_at)
@@ -4801,6 +5261,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4801
5261
  ]
4802
5262
  );
4803
5263
  }
5264
+ /** Aggregate memory counts for an organization. */
4804
5265
  async getStats(organization) {
4805
5266
  const result = await this.query(
4806
5267
  `SELECT
@@ -4818,6 +5279,7 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4818
5279
  totalAgents: Number(result.rows[0]?.agents ?? 0)
4819
5280
  };
4820
5281
  }
5282
+ /** Approximate on-disk database size in bytes (`pg_database_size`). */
4821
5283
  async getDatabaseSizeBytes() {
4822
5284
  const result = await this.query(
4823
5285
  `SELECT pg_database_size(current_database())::bigint AS size`
@@ -4859,6 +5321,11 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
4859
5321
  [META_KEYS.schemaVersion]
4860
5322
  ).catch(() => ({ rows: [] }));
4861
5323
  const current = versionRow.rows[0]?.value !== void 0 ? Number(versionRow.rows[0].value) : null;
5324
+ if (current !== null && current > SCHEMA_VERSION) {
5325
+ throw new InitializationError(
5326
+ `Database schema version ${current} is newer than this Wolbarg SDK (${SCHEMA_VERSION}). Please upgrade Wolbarg to open this database.`
5327
+ );
5328
+ }
4862
5329
  if (current === SCHEMA_VERSION) {
4863
5330
  const tsvProbe2 = await this.query(
4864
5331
  `SELECT 1 FROM information_schema.columns
@@ -5009,8 +5476,17 @@ var PostgresStorageProvider = class _PostgresStorageProvider {
5009
5476
  await this.query(`CREATE EXTENSION IF NOT EXISTS vector`);
5010
5477
  _PostgresStorageProvider.pgvectorByConn.set(this.connectionString, true);
5011
5478
  return true;
5012
- } catch {
5479
+ } catch (error) {
5013
5480
  _PostgresStorageProvider.pgvectorByConn.set(this.connectionString, false);
5481
+ if (!warnedPgvectorFallback) {
5482
+ warnedPgvectorFallback = true;
5483
+ warnLogger2.warn(
5484
+ "pgvector extension is unavailable; using blob cosine search fallback.",
5485
+ {
5486
+ cause: error instanceof Error ? error.message : `unknown error: ${String(error)}`
5487
+ }
5488
+ );
5489
+ }
5014
5490
  return false;
5015
5491
  }
5016
5492
  }
@@ -5151,11 +5627,12 @@ function createDatabaseProvider(config) {
5151
5627
  }
5152
5628
 
5153
5629
  // src/version.ts
5154
- var SDK_VERSION = "0.5.3";
5630
+ var SDK_VERSION = "0.5.5";
5155
5631
 
5156
5632
  // src/memory/transfer.ts
5633
+ var warnedMissingExportManifest = false;
5157
5634
  var SqliteMemoryTransferProvider = class {
5158
- async exportTo(exportPath, sourcePath, organization) {
5635
+ async exportTo(exportPath, sourcePath, organization, embeddingModel, embeddingDimensions) {
5159
5636
  const resolvedSource = resolvePath(sourcePath);
5160
5637
  if (resolvedSource === ":memory:") {
5161
5638
  throw new ConfigurationError(
@@ -5182,7 +5659,9 @@ Initialize Wolbarg and verify the database path.`
5182
5659
  sdkVersion: SDK_VERSION,
5183
5660
  provider: "sqlite",
5184
5661
  sourcePath: resolvedSource,
5185
- organization
5662
+ organization,
5663
+ ...embeddingModel ? { embeddingModel } : {},
5664
+ ...embeddingDimensions ? { embeddingDimensions } : {}
5186
5665
  };
5187
5666
  fs6.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
5188
5667
  return {
@@ -5191,7 +5670,7 @@ Initialize Wolbarg and verify the database path.`
5191
5670
  sizeBytes: fs6.statSync(dbExportPath).size
5192
5671
  };
5193
5672
  }
5194
- async importFrom(exportPath, targetPath) {
5673
+ async importFrom(exportPath, targetPath, expected) {
5195
5674
  const resolvedExport = resolvePath(exportPath);
5196
5675
  const dbExportPath = resolvedExport.endsWith(".db") ? resolvedExport : fs6.existsSync(`${resolvedExport}.db`) ? `${resolvedExport}.db` : resolvedExport;
5197
5676
  if (!fs6.existsSync(dbExportPath)) {
@@ -5213,26 +5692,57 @@ Pass the path returned by export().`
5213
5692
  );
5214
5693
  }
5215
5694
  } else {
5216
- manifest = {
5217
- format: "wolbarg-export-v1",
5218
- exportedAt: nowIso(),
5219
- sdkVersion: SDK_VERSION,
5220
- provider: "sqlite",
5221
- sourcePath: dbExportPath
5222
- };
5695
+ if (!warnedMissingExportManifest) {
5696
+ warnedMissingExportManifest = true;
5697
+ console.warn(
5698
+ "[wolbarg:warn] export manifest is missing; skipping manifest-based import validation."
5699
+ );
5700
+ }
5701
+ }
5702
+ if (manifest) {
5703
+ if (expected?.organization && manifest.organization && expected.organization !== manifest.organization) {
5704
+ throw new ValidationError(
5705
+ "Import failed: export organization does not match the current Wolbarg organization."
5706
+ );
5707
+ }
5708
+ if (expected?.embeddingDimensions != null && manifest.embeddingDimensions != null && expected.embeddingDimensions !== manifest.embeddingDimensions) {
5709
+ throw new ValidationError(
5710
+ `Import failed: embedding dimension mismatch (expected ${expected.embeddingDimensions}, got ${manifest.embeddingDimensions}).`
5711
+ );
5712
+ }
5713
+ if (expected?.embeddingModel && manifest.embeddingModel && expected.embeddingModel !== manifest.embeddingModel) {
5714
+ throw new ValidationError(
5715
+ `Import failed: embedding model mismatch (expected ${expected.embeddingModel}, got ${manifest.embeddingModel}).`
5716
+ );
5717
+ }
5223
5718
  }
5224
5719
  const resolvedTarget = resolvePath(targetPath);
5225
5720
  if (resolvedTarget === ":memory:") {
5226
5721
  throw new ConfigurationError("Cannot import into an in-memory database.");
5227
5722
  }
5723
+ const tmpTarget = `${resolvedTarget}.tmp-import-${Date.now()}`;
5724
+ for (const suffix of ["", "-wal", "-shm"]) {
5725
+ const side = `${tmpTarget}${suffix}`;
5726
+ if (fs6.existsSync(side)) {
5727
+ fs6.rmSync(side, { force: true });
5728
+ }
5729
+ }
5730
+ await copySqlite(dbExportPath, tmpTarget);
5228
5731
  for (const suffix of ["", "-wal", "-shm"]) {
5229
5732
  const side = `${resolvedTarget}${suffix}`;
5230
5733
  if (fs6.existsSync(side)) {
5231
5734
  fs6.rmSync(side, { force: true });
5232
5735
  }
5233
5736
  }
5234
- await copySqlite(dbExportPath, resolvedTarget);
5235
- return { path: resolvedTarget, manifest };
5737
+ fs6.renameSync(tmpTarget, resolvedTarget);
5738
+ for (const suffix of ["-wal", "-shm"]) {
5739
+ const from = `${tmpTarget}${suffix}`;
5740
+ const to = `${resolvedTarget}${suffix}`;
5741
+ if (fs6.existsSync(from)) {
5742
+ fs6.renameSync(from, to);
5743
+ }
5744
+ }
5745
+ return { path: resolvedTarget, manifest: manifest ?? void 0 };
5236
5746
  }
5237
5747
  };
5238
5748
  async function copySqlite(sourcePath, destPath) {
@@ -5461,7 +5971,8 @@ function applyMmr(candidates, topK, lambda) {
5461
5971
  );
5462
5972
  }
5463
5973
  const score = lambda * relevance - (1 - lambda) * maxSim;
5464
- if (score > bestScore) {
5974
+ const best = remaining[bestIdx];
5975
+ if (score > bestScore || score === bestScore && candidate.id < best.id) {
5465
5976
  bestScore = score;
5466
5977
  bestIdx = i;
5467
5978
  }
@@ -5585,6 +6096,9 @@ var SqliteGraphProvider = class {
5585
6096
  concurrency;
5586
6097
  db = null;
5587
6098
  opened = false;
6099
+ /**
6100
+ * @param options - Graph SQLite path and optional concurrency tuning.
6101
+ */
5588
6102
  constructor(options) {
5589
6103
  if (!options?.path || typeof options.path !== "string" || !options.path.trim()) {
5590
6104
  throw new ConfigurationError("sqlite graph requires a non-empty path");
@@ -5592,12 +6106,15 @@ var SqliteGraphProvider = class {
5592
6106
  this.dbPath = path7.resolve(options.path.trim());
5593
6107
  this.concurrency = resolveConcurrencyConfig(options.concurrency);
5594
6108
  }
6109
+ /** Whether this provider supports file snapshots for checkpoint / export. */
5595
6110
  supportsFileSnapshot() {
5596
6111
  return this.dbPath !== ":memory:";
5597
6112
  }
6113
+ /** Absolute graph database path, or `null` for `:memory:`. */
5598
6114
  getDataPath() {
5599
6115
  return this.dbPath === ":memory:" ? null : this.dbPath;
5600
6116
  }
6117
+ /** Open the graph SQLite file, apply pragmas, and create schema if needed. */
5601
6118
  async open() {
5602
6119
  if (this.opened) return;
5603
6120
  try {
@@ -5634,6 +6151,7 @@ var SqliteGraphProvider = class {
5634
6151
  );
5635
6152
  }
5636
6153
  }
6154
+ /** Close the graph database connection. */
5637
6155
  async close() {
5638
6156
  if (!this.db) {
5639
6157
  this.opened = false;
@@ -5655,6 +6173,14 @@ var SqliteGraphProvider = class {
5655
6173
  this.opened = false;
5656
6174
  }
5657
6175
  }
6176
+ /**
6177
+ * Create or replace a directed memory↔memory edge.
6178
+ *
6179
+ * @param fromId - Source memory UUID.
6180
+ * @param toId - Target memory UUID.
6181
+ * @param relation - Edge label (e.g. `"related_to"`, `"caused_by"`).
6182
+ * @param metadata - Optional JSON metadata stored on the edge.
6183
+ */
5658
6184
  async linkMemories(fromId, toId, relation, metadata) {
5659
6185
  const db = this.requireDb();
5660
6186
  await this.withTx(() => {
@@ -5677,6 +6203,13 @@ var SqliteGraphProvider = class {
5677
6203
  );
5678
6204
  });
5679
6205
  }
6206
+ /**
6207
+ * Remove edges between two memories.
6208
+ *
6209
+ * @param fromId - Source memory UUID.
6210
+ * @param toId - Target memory UUID.
6211
+ * @param relation - When set, only delete edges with this relation label.
6212
+ */
5680
6213
  async unlinkMemories(fromId, toId, relation) {
5681
6214
  const db = this.requireDb();
5682
6215
  await this.withTx(() => {
@@ -5726,6 +6259,12 @@ var SqliteGraphProvider = class {
5726
6259
  }
5727
6260
  return out;
5728
6261
  }
6262
+ /**
6263
+ * Insert or update a named entity node.
6264
+ *
6265
+ * @param entity - Entity name, type, and optional metadata.
6266
+ * @returns Stable entity id ({@link entityIdFrom}).
6267
+ */
5729
6268
  async upsertEntity(entity) {
5730
6269
  const id = entityIdFrom(entity.name, entity.type);
5731
6270
  const db = this.requireDb();
@@ -5750,6 +6289,13 @@ var SqliteGraphProvider = class {
5750
6289
  });
5751
6290
  return id;
5752
6291
  }
6292
+ /**
6293
+ * Link an entity to a memory via a `MENTIONS` edge (not traversed by {@link getRelated}).
6294
+ *
6295
+ * @param entityId - Entity id from {@link upsertEntity}.
6296
+ * @param memoryId - Target memory UUID.
6297
+ * @param role - Optional role string stored on the edge metadata.
6298
+ */
5753
6299
  async linkEntityToMemory(entityId, memoryId, role) {
5754
6300
  const db = this.requireDb();
5755
6301
  await this.withTx(() => {
@@ -5779,6 +6325,11 @@ var SqliteGraphProvider = class {
5779
6325
  );
5780
6326
  });
5781
6327
  }
6328
+ /**
6329
+ * Remove the memory node and incident edges when a memory is hard-deleted.
6330
+ *
6331
+ * @param memoryId - Wolbarg memory UUID.
6332
+ */
5782
6333
  async deleteMemory(memoryId) {
5783
6334
  const db = this.requireDb();
5784
6335
  await this.withTx(() => {
@@ -5798,6 +6349,7 @@ var SqliteGraphProvider = class {
5798
6349
  }
5799
6350
  );
5800
6351
  }
6352
+ /** Lightweight connectivity and schema statistics check. */
5801
6353
  async health() {
5802
6354
  try {
5803
6355
  if (!this.opened || !this.db) {
@@ -5943,6 +6495,9 @@ var Neo4jGraphProvider = class {
5943
6495
  database;
5944
6496
  driver = null;
5945
6497
  opened = false;
6498
+ /**
6499
+ * @param options - Bolt URL, credentials, and optional database name.
6500
+ */
5946
6501
  constructor(options) {
5947
6502
  if (!options?.url?.trim()) {
5948
6503
  throw new ConfigurationError("neo4j graph requires a non-empty url");
@@ -5958,12 +6513,15 @@ var Neo4jGraphProvider = class {
5958
6513
  this.password = options.password;
5959
6514
  this.database = options.database?.trim() || void 0;
5960
6515
  }
6516
+ /** Neo4j does not support local file snapshots for checkpoint pairing. */
5961
6517
  supportsFileSnapshot() {
5962
6518
  return false;
5963
6519
  }
6520
+ /** Always `null` — graph data lives in the remote Neo4j cluster. */
5964
6521
  getDataPath() {
5965
6522
  return null;
5966
6523
  }
6524
+ /** Connect to Neo4j, verify connectivity, and ensure uniqueness constraints. */
5967
6525
  async open() {
5968
6526
  if (this.opened) return;
5969
6527
  let mod;
@@ -5995,6 +6553,7 @@ var Neo4jGraphProvider = class {
5995
6553
  }
5996
6554
  });
5997
6555
  }
6556
+ /** Close the neo4j-driver connection. */
5998
6557
  async close() {
5999
6558
  if (this.driver) {
6000
6559
  await this.driver.close();
@@ -6021,24 +6580,26 @@ var Neo4jGraphProvider = class {
6021
6580
  }
6022
6581
  }
6023
6582
  async run(cypher, params = {}) {
6024
- return this.withSession(async (session) => {
6025
- const result = await session.run(cypher, params);
6026
- return result.records.map((rec) => {
6027
- try {
6028
- return rec.toObject();
6029
- } catch {
6030
- const obj = {};
6031
- for (const key of rec.keys) {
6032
- obj[key] = unwrapNeo4jValue(rec.get(key));
6033
- }
6034
- return obj;
6583
+ return this.withSession(
6584
+ (session) => this.runOnSession(session, cypher, params)
6585
+ );
6586
+ }
6587
+ async runOnSession(session, cypher, params = {}) {
6588
+ const result = await session.run(cypher, params);
6589
+ return result.records.map((rec) => {
6590
+ try {
6591
+ return rec.toObject();
6592
+ } catch {
6593
+ const obj = {};
6594
+ for (const key of rec.keys) {
6595
+ obj[key] = unwrapNeo4jValue(rec.get(key));
6035
6596
  }
6036
- });
6597
+ return obj;
6598
+ }
6037
6599
  });
6038
6600
  }
6039
- async ensureMemoryNode(id) {
6040
- await this.run(
6041
- `MERGE (m:Memory { id: $id })
6601
+ async ensureMemoryNode(id, session) {
6602
+ const cypher = `MERGE (m:Memory { id: $id })
6042
6603
  ON CREATE SET
6043
6604
  m.organization = '',
6044
6605
  m.agent = '',
@@ -6047,25 +6608,46 @@ var Neo4jGraphProvider = class {
6047
6608
  m.archived = false,
6048
6609
  m.compressed_into = '',
6049
6610
  m.created_at = '',
6050
- m.updated_at = ''`,
6051
- { id }
6052
- );
6611
+ m.updated_at = ''`;
6612
+ if (session) {
6613
+ await this.runOnSession(session, cypher, { id });
6614
+ return;
6615
+ }
6616
+ await this.run(cypher, { id });
6053
6617
  }
6618
+ /**
6619
+ * MERGE a directed `RELATED` relationship between two memory nodes.
6620
+ *
6621
+ * @param fromId - Source memory UUID.
6622
+ * @param toId - Target memory UUID.
6623
+ * @param relation - Relationship label stored on `RELATED.relation`.
6624
+ * @param metadata - Optional JSON metadata on the relationship.
6625
+ */
6054
6626
  async linkMemories(fromId, toId, relation, metadata) {
6055
- await this.ensureMemoryNode(fromId);
6056
- await this.ensureMemoryNode(toId);
6057
- await this.run(
6058
- `MATCH (a:Memory { id: $fromId }), (b:Memory { id: $toId })
6059
- MERGE (a)-[r:RELATED { relation: $relation }]->(b)
6060
- SET r.metadata_json = $metadata`,
6061
- {
6062
- fromId,
6063
- toId,
6064
- relation,
6065
- metadata: serializeMetadata2(metadata)
6066
- }
6067
- );
6627
+ await this.withSession(async (session) => {
6628
+ await this.ensureMemoryNode(fromId, session);
6629
+ await this.ensureMemoryNode(toId, session);
6630
+ await this.runOnSession(
6631
+ session,
6632
+ `MATCH (a:Memory { id: $fromId }), (b:Memory { id: $toId })
6633
+ MERGE (a)-[r:RELATED { relation: $relation }]->(b)
6634
+ SET r.metadata_json = $metadata`,
6635
+ {
6636
+ fromId,
6637
+ toId,
6638
+ relation,
6639
+ metadata: serializeMetadata2(metadata)
6640
+ }
6641
+ );
6642
+ });
6068
6643
  }
6644
+ /**
6645
+ * Delete `RELATED` relationship(s) between two memories.
6646
+ *
6647
+ * @param fromId - Source memory UUID.
6648
+ * @param toId - Target memory UUID.
6649
+ * @param relation - When set, only delete edges with this relation property.
6650
+ */
6069
6651
  async unlinkMemories(fromId, toId, relation) {
6070
6652
  if (relation !== void 0) {
6071
6653
  await this.run(
@@ -6111,6 +6693,12 @@ var Neo4jGraphProvider = class {
6111
6693
  (row) => rowToMemoryRecord(unwrapRow(row))
6112
6694
  );
6113
6695
  }
6696
+ /**
6697
+ * MERGE an `Entity` node keyed by deterministic id.
6698
+ *
6699
+ * @param entity - Entity name, type, and optional metadata.
6700
+ * @returns Stable entity id ({@link entityIdFrom}).
6701
+ */
6114
6702
  async upsertEntity(entity) {
6115
6703
  const id = entityIdFrom(entity.name, entity.type);
6116
6704
  await this.run(
@@ -6125,24 +6713,40 @@ var Neo4jGraphProvider = class {
6125
6713
  );
6126
6714
  return id;
6127
6715
  }
6716
+ /**
6717
+ * MERGE a `MENTIONS` relationship from entity to memory.
6718
+ *
6719
+ * @param entityId - Entity id from {@link upsertEntity}.
6720
+ * @param memoryId - Target memory UUID.
6721
+ * @param role - Optional role string on the relationship.
6722
+ */
6128
6723
  async linkEntityToMemory(entityId, memoryId, role) {
6129
- await this.ensureMemoryNode(memoryId);
6130
- const found = await this.run(
6131
- `MATCH (e:Entity { id: $id }) RETURN e.id AS id`,
6132
- { id: entityId }
6133
- );
6134
- if (found.length === 0) {
6135
- throw new DatabaseError(`Entity not found: ${entityId}`, {
6136
- operation: "linkEntityToMemory"
6137
- });
6138
- }
6139
- await this.run(
6140
- `MATCH (e:Entity { id: $entityId }), (m:Memory { id: $memoryId })
6141
- MERGE (e)-[r:MENTIONS]->(m)
6142
- SET r.role = $role`,
6143
- { entityId, memoryId, role: role ?? "" }
6144
- );
6724
+ await this.withSession(async (session) => {
6725
+ await this.ensureMemoryNode(memoryId, session);
6726
+ const found = await this.runOnSession(
6727
+ session,
6728
+ `MATCH (e:Entity { id: $id }) RETURN e.id AS id`,
6729
+ { id: entityId }
6730
+ );
6731
+ if (found.length === 0) {
6732
+ throw new DatabaseError(`Entity not found: ${entityId}`, {
6733
+ operation: "linkEntityToMemory"
6734
+ });
6735
+ }
6736
+ await this.runOnSession(
6737
+ session,
6738
+ `MATCH (e:Entity { id: $entityId }), (m:Memory { id: $memoryId })
6739
+ MERGE (e)-[r:MENTIONS]->(m)
6740
+ SET r.role = $role`,
6741
+ { entityId, memoryId, role: role ?? "" }
6742
+ );
6743
+ });
6145
6744
  }
6745
+ /**
6746
+ * DETACH DELETE the memory node and all incident relationships.
6747
+ *
6748
+ * @param memoryId - Wolbarg memory UUID.
6749
+ */
6146
6750
  async deleteMemory(memoryId) {
6147
6751
  await this.run(
6148
6752
  `MATCH (m:Memory { id: $id })
@@ -6150,9 +6754,17 @@ var Neo4jGraphProvider = class {
6150
6754
  { id: memoryId }
6151
6755
  );
6152
6756
  }
6757
+ /**
6758
+ * Run arbitrary parameterized Cypher (escape hatch for advanced graph queries).
6759
+ *
6760
+ * @param cypher - Cypher query string.
6761
+ * @param params - Bound parameters for the query.
6762
+ * @returns Raw row objects from the driver.
6763
+ */
6153
6764
  async query(cypher, params) {
6154
6765
  return this.run(cypher, params ?? {});
6155
6766
  }
6767
+ /** Verify connectivity and return server metadata when available. */
6156
6768
  async health() {
6157
6769
  try {
6158
6770
  if (!this.opened || !this.driver) {
@@ -6214,6 +6826,21 @@ function assertNonEmpty(value, fieldName) {
6214
6826
  throw new ConfigurationError(`${fieldName} must be a non-empty string`);
6215
6827
  }
6216
6828
  }
6829
+ function assertFiniteNumber2(value, fieldName) {
6830
+ if (typeof value !== "number" || !Number.isFinite(value)) {
6831
+ throw new ConfigurationError(`${fieldName} must be a finite number`);
6832
+ }
6833
+ }
6834
+ function assertNumberMin(value, fieldName, min) {
6835
+ if (!Number.isFinite(value) || value < min) {
6836
+ throw new ConfigurationError(`${fieldName} must be >= ${min}`);
6837
+ }
6838
+ }
6839
+ function assertNumberInRange(value, fieldName, min, max) {
6840
+ if (!Number.isFinite(value) || value < min || value > max) {
6841
+ throw new ConfigurationError(`${fieldName} must be between ${min} and ${max}`);
6842
+ }
6843
+ }
6217
6844
  function assertUrl(value, fieldName) {
6218
6845
  assertNonEmpty(value, fieldName);
6219
6846
  try {
@@ -6260,6 +6887,135 @@ function validateLlmConfig(config) {
6260
6887
  ...config.timeoutMs !== void 0 ? { timeoutMs: config.timeoutMs } : {}
6261
6888
  };
6262
6889
  }
6890
+ function validateRetrievalConfig(config) {
6891
+ if (config === null || typeof config !== "object") {
6892
+ throw new ConfigurationError("retrieval must be an object");
6893
+ }
6894
+ const out = {};
6895
+ if (config.overFetchFactor !== void 0) {
6896
+ assertFiniteNumber2(config.overFetchFactor, "retrieval.overFetchFactor");
6897
+ assertNumberMin(config.overFetchFactor, "retrieval.overFetchFactor", 0);
6898
+ if (config.overFetchFactor <= 0) {
6899
+ throw new ConfigurationError("retrieval.overFetchFactor must be > 0");
6900
+ }
6901
+ out.overFetchFactor = config.overFetchFactor;
6902
+ }
6903
+ if (config.hybrid !== void 0) {
6904
+ if (config.hybrid === null || typeof config.hybrid !== "object") {
6905
+ throw new ConfigurationError("retrieval.hybrid must be an object");
6906
+ }
6907
+ const hybrid = config.hybrid;
6908
+ const outHybrid = {};
6909
+ if (hybrid.semanticWeight !== void 0) {
6910
+ assertFiniteNumber2(hybrid.semanticWeight, "retrieval.hybrid.semanticWeight");
6911
+ if (hybrid.semanticWeight < 0) {
6912
+ throw new ConfigurationError(
6913
+ "retrieval.hybrid.semanticWeight must be >= 0"
6914
+ );
6915
+ }
6916
+ outHybrid.semanticWeight = hybrid.semanticWeight;
6917
+ }
6918
+ if (hybrid.keywordWeight !== void 0) {
6919
+ assertFiniteNumber2(hybrid.keywordWeight, "retrieval.hybrid.keywordWeight");
6920
+ if (hybrid.keywordWeight < 0) {
6921
+ throw new ConfigurationError(
6922
+ "retrieval.hybrid.keywordWeight must be >= 0"
6923
+ );
6924
+ }
6925
+ outHybrid.keywordWeight = hybrid.keywordWeight;
6926
+ }
6927
+ if (Object.keys(outHybrid).length > 0) {
6928
+ out.hybrid = outHybrid;
6929
+ }
6930
+ }
6931
+ if (config.mmr !== void 0) {
6932
+ if (config.mmr === null || typeof config.mmr !== "object") {
6933
+ throw new ConfigurationError("retrieval.mmr must be an object");
6934
+ }
6935
+ const mmr = config.mmr;
6936
+ const outMmr = {};
6937
+ if (mmr.lambda !== void 0) {
6938
+ assertFiniteNumber2(mmr.lambda, "retrieval.mmr.lambda");
6939
+ assertNumberInRange(mmr.lambda, "retrieval.mmr.lambda", 0, 1);
6940
+ outMmr.lambda = mmr.lambda;
6941
+ }
6942
+ if (Object.keys(outMmr).length > 0) {
6943
+ out.mmr = outMmr;
6944
+ }
6945
+ }
6946
+ return out;
6947
+ }
6948
+ function validateMemoryDedupeConfig(config) {
6949
+ if (config === null || typeof config !== "object") {
6950
+ throw new ConfigurationError("memory.dedupe must be an object");
6951
+ }
6952
+ const out = {};
6953
+ if (config.enabled !== void 0) {
6954
+ if (typeof config.enabled !== "boolean") {
6955
+ throw new ConfigurationError("memory.dedupe.enabled must be a boolean");
6956
+ }
6957
+ out.enabled = config.enabled;
6958
+ }
6959
+ if (config.strategy !== void 0) {
6960
+ const allowed = /* @__PURE__ */ new Set(["exact", "near", "exact-or-near"]);
6961
+ if (!allowed.has(config.strategy)) {
6962
+ throw new ConfigurationError(
6963
+ `memory.dedupe.strategy must be one of ${Array.from(allowed).join(", ")}`
6964
+ );
6965
+ }
6966
+ out.strategy = config.strategy;
6967
+ }
6968
+ if (config.nearThreshold !== void 0) {
6969
+ assertFiniteNumber2(config.nearThreshold, "memory.dedupe.nearThreshold");
6970
+ assertNumberInRange(
6971
+ config.nearThreshold,
6972
+ "memory.dedupe.nearThreshold",
6973
+ 0,
6974
+ 1
6975
+ );
6976
+ out.nearThreshold = config.nearThreshold;
6977
+ }
6978
+ if (config.nearCandidateLimit !== void 0) {
6979
+ assertFiniteNumber2(
6980
+ config.nearCandidateLimit,
6981
+ "memory.dedupe.nearCandidateLimit"
6982
+ );
6983
+ assertNumberMin(
6984
+ config.nearCandidateLimit,
6985
+ "memory.dedupe.nearCandidateLimit",
6986
+ 1
6987
+ );
6988
+ out.nearCandidateLimit = config.nearCandidateLimit;
6989
+ }
6990
+ return out;
6991
+ }
6992
+ function validateEmbeddingCacheConfig(config) {
6993
+ if (config === null || typeof config !== "object") {
6994
+ throw new ConfigurationError("embeddingCache must be an object");
6995
+ }
6996
+ const out = {};
6997
+ if (config.enabled !== void 0) {
6998
+ if (typeof config.enabled !== "boolean") {
6999
+ throw new ConfigurationError("embeddingCache.enabled must be a boolean");
7000
+ }
7001
+ out.enabled = config.enabled;
7002
+ }
7003
+ if (config.ttlMs !== void 0) {
7004
+ assertFiniteNumber2(config.ttlMs, "embeddingCache.ttlMs");
7005
+ if (config.ttlMs < 0) {
7006
+ throw new ConfigurationError("embeddingCache.ttlMs must be >= 0");
7007
+ }
7008
+ out.ttlMs = config.ttlMs;
7009
+ }
7010
+ if (config.maxEntries !== void 0) {
7011
+ assertFiniteNumber2(config.maxEntries, "embeddingCache.maxEntries");
7012
+ if (config.maxEntries < 0) {
7013
+ throw new ConfigurationError("embeddingCache.maxEntries must be >= 0");
7014
+ }
7015
+ out.maxEntries = config.maxEntries;
7016
+ }
7017
+ return out;
7018
+ }
6263
7019
  function normalizeDatabaseConfig(config) {
6264
7020
  const provider = config.provider;
6265
7021
  if (provider !== "sqlite" && provider !== "postgres") {
@@ -6369,11 +7125,22 @@ function validateWolbargOptions(options) {
6369
7125
  throw new ConfigurationError("Wolbarg options must be an object");
6370
7126
  }
6371
7127
  assertNonEmpty(options.organization, "organization");
7128
+ const checkpointDirectory = options.checkpointDirectory !== void 0 ? (() => {
7129
+ assertNonEmpty(options.checkpointDirectory, "checkpointDirectory");
7130
+ return options.checkpointDirectory.trim();
7131
+ })() : void 0;
6372
7132
  const storageInput = resolveStorageInput(options);
6373
7133
  let storage = storageInput;
6374
7134
  if (!isStorageProvider(storageInput)) {
6375
7135
  storage = normalizeDatabaseConfig(storageInput);
6376
7136
  }
7137
+ if (!isStorageProvider(storageInput) && storageInput.provider === "postgres" && storageInput.maxPoolSize !== void 0) {
7138
+ assertFiniteNumber2(
7139
+ storageInput.maxPoolSize,
7140
+ "database.maxPoolSize"
7141
+ );
7142
+ assertNumberMin(storageInput.maxPoolSize, "database.maxPoolSize", 1);
7143
+ }
6377
7144
  if (!options.embedding) {
6378
7145
  throw new ConfigurationError("embedding is required");
6379
7146
  }
@@ -6391,13 +7158,20 @@ function validateWolbargOptions(options) {
6391
7158
  if (graph !== void 0) {
6392
7159
  graph = resolveGraphInput(graph);
6393
7160
  }
7161
+ const retrieval = options.retrieval !== void 0 ? validateRetrievalConfig(options.retrieval) : void 0;
7162
+ const embeddingCache = options.embeddingCache !== void 0 ? validateEmbeddingCacheConfig(options.embeddingCache) : void 0;
7163
+ const memory = options.memory !== void 0 && options.memory.dedupe !== void 0 ? { ...options.memory, dedupe: validateMemoryDedupeConfig(options.memory.dedupe) } : options.memory;
6394
7164
  return {
6395
7165
  ...options,
6396
7166
  organization: options.organization.trim(),
6397
7167
  storage,
6398
7168
  database: void 0,
6399
7169
  ...telemetry ? { telemetry } : {},
6400
- ...graph !== void 0 ? { graph } : {}
7170
+ ...graph !== void 0 ? { graph } : {},
7171
+ ...retrieval !== void 0 ? { retrieval } : {},
7172
+ ...embeddingCache !== void 0 ? { embeddingCache } : {},
7173
+ ...checkpointDirectory !== void 0 ? { checkpointDirectory } : {},
7174
+ ...memory !== void 0 ? { memory } : {}
6401
7175
  };
6402
7176
  }
6403
7177
  function resolveGraphInput(input) {
@@ -6456,59 +7230,6 @@ function resolveGraphInput(input) {
6456
7230
  );
6457
7231
  }
6458
7232
 
6459
- // src/telemetry/logger.ts
6460
- var LEVEL_ORDER = {
6461
- off: 100,
6462
- error: 50,
6463
- warn: 40,
6464
- info: 30,
6465
- debug: 20,
6466
- trace: 10
6467
- };
6468
- var WolbargLogger = class {
6469
- constructor(level = "info") {
6470
- this.level = level;
6471
- }
6472
- level;
6473
- setLevel(level) {
6474
- this.level = level;
6475
- }
6476
- getLevel() {
6477
- return this.level;
6478
- }
6479
- error(message, extra) {
6480
- this.write("error", message, extra);
6481
- }
6482
- warn(message, extra) {
6483
- this.write("warn", message, extra);
6484
- }
6485
- info(message, extra) {
6486
- this.write("info", message, extra);
6487
- }
6488
- debug(message, extra) {
6489
- this.write("debug", message, extra);
6490
- }
6491
- trace(message, extra) {
6492
- this.write("trace", message, extra);
6493
- }
6494
- write(level, message, extra) {
6495
- if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) {
6496
- return;
6497
- }
6498
- if (this.level === "off") {
6499
- return;
6500
- }
6501
- const line = `[wolbarg:${level}] ${message}`;
6502
- if (level === "error") {
6503
- console.error(line, extra ?? "");
6504
- } else if (level === "warn") {
6505
- console.warn(line, extra ?? "");
6506
- } else {
6507
- console.log(line, extra ?? "");
6508
- }
6509
- }
6510
- };
6511
-
6512
7233
  // src/telemetry/trace.ts
6513
7234
  function createSessionId() {
6514
7235
  return createId();
@@ -6534,21 +7255,32 @@ function createChildTrace(parent) {
6534
7255
  // src/telemetry/emitter.ts
6535
7256
  var NoopTelemetryProvider = class {
6536
7257
  name = "noop";
7258
+ /** @inheritdoc TelemetryProvider.open */
6537
7259
  async open() {
6538
7260
  }
7261
+ /** @inheritdoc TelemetryProvider.close */
6539
7262
  async close() {
6540
7263
  }
7264
+ /** @inheritdoc TelemetryProvider.emit */
6541
7265
  emit(_event) {
6542
7266
  }
7267
+ /** @inheritdoc TelemetryProvider.flush */
6543
7268
  async flush() {
6544
7269
  }
6545
7270
  };
6546
7271
  var TelemetryEmitter = class {
7272
+ /** Stable session id for this Wolbarg instance lifetime. */
6547
7273
  sessionId;
7274
+ /** Structured logger gated by telemetry log level. */
6548
7275
  logger;
6549
7276
  provider;
6550
7277
  config;
6551
7278
  context;
7279
+ /**
7280
+ * @param provider - Telemetry backend, or `null` for {@link NoopTelemetryProvider}.
7281
+ * @param config - Capture flags and log level.
7282
+ * @param context - Default organization injected into events.
7283
+ */
6552
7284
  constructor(provider, config, context = {}) {
6553
7285
  this.sessionId = createSessionId();
6554
7286
  this.provider = provider ?? new NoopTelemetryProvider();
@@ -6564,25 +7296,37 @@ var TelemetryEmitter = class {
6564
7296
  this.context = context;
6565
7297
  this.logger = new WolbargLogger(this.config.level);
6566
7298
  }
7299
+ /** Whether telemetry is enabled and backed by a non-noop provider. */
6567
7300
  get enabled() {
6568
7301
  return this.config.enabled && this.provider.name !== "noop";
6569
7302
  }
7303
+ /** Replace the underlying telemetry provider (e.g. after lazy init). */
6570
7304
  setProvider(provider) {
6571
7305
  this.provider = provider;
6572
7306
  }
7307
+ /** Open the underlying telemetry provider when enabled. */
6573
7308
  async open() {
6574
7309
  if (!this.config.enabled) {
6575
7310
  return;
6576
7311
  }
6577
7312
  await this.provider.open();
6578
7313
  }
7314
+ /** Flush pending events and close the telemetry provider. */
6579
7315
  async close() {
6580
7316
  await this.provider.flush();
6581
7317
  await this.provider.close();
6582
7318
  }
7319
+ /** Flush pending telemetry events without closing the provider. */
6583
7320
  async flush() {
6584
7321
  await this.provider.flush();
6585
7322
  }
7323
+ /**
7324
+ * Begin tracing an operation.
7325
+ *
7326
+ * @param operation - Public telemetry operation name.
7327
+ * @param parent - Optional parent trace for nested spans.
7328
+ * @returns Handle — call `success()` or `failure()` when done.
7329
+ */
6586
7330
  start(operation, parent) {
6587
7331
  const context = parent ? createChildTrace(parent) : createRootTrace(this.sessionId);
6588
7332
  const startedAt = performance.now();
@@ -6593,14 +7337,14 @@ var TelemetryEmitter = class {
6593
7337
  const totalMs = performance.now() - startedAt;
6594
7338
  const errMessage = error instanceof Error ? error.message : error ? String(error) : fields?.error ?? null;
6595
7339
  const errStack = error instanceof Error ? error.stack ?? null : fields?.errorStack ?? null;
7340
+ if (!self.config.enabled) {
7341
+ return;
7342
+ }
6596
7343
  if (status === "error") {
6597
7344
  self.logger.error(`${operation} failed: ${errMessage ?? "unknown"}`);
6598
7345
  } else {
6599
7346
  self.logger.debug(`${operation} completed in ${totalMs.toFixed(2)}ms`);
6600
7347
  }
6601
- if (!self.config.enabled) {
6602
- return;
6603
- }
6604
7348
  const event = {
6605
7349
  id: createId(),
6606
7350
  timestamp: nowIso(),
@@ -6657,10 +7401,12 @@ var TelemetryEmitter = class {
6657
7401
  }
6658
7402
  };
6659
7403
  }
7404
+ /** Emit a startup telemetry event. */
6660
7405
  emitStartup(provider) {
6661
7406
  const trace = this.start("startup");
6662
7407
  trace.success({ provider });
6663
7408
  }
7409
+ /** Emit a shutdown telemetry event. */
6664
7410
  emitShutdown(provider) {
6665
7411
  const trace = this.start("shutdown");
6666
7412
  trace.success({ provider });
@@ -6726,10 +7472,15 @@ var SqliteEventDatabase = class {
6726
7472
  db = null;
6727
7473
  insertStmt = null;
6728
7474
  columns = /* @__PURE__ */ new Set();
7475
+ /**
7476
+ * @param options.url - Path to the telemetry SQLite file.
7477
+ * @param options.readonly - Open read-only (Studio / analytics).
7478
+ */
6729
7479
  constructor(options) {
6730
7480
  this.url = options.url;
6731
7481
  this.readonly = options.readonly ?? false;
6732
7482
  }
7483
+ /** Open the telemetry database and run schema migrations. */
6733
7484
  async open() {
6734
7485
  try {
6735
7486
  const dbPath = this.resolvePath(this.url);
@@ -6784,6 +7535,7 @@ var SqliteEventDatabase = class {
6784
7535
  );
6785
7536
  }
6786
7537
  }
7538
+ /** Close the telemetry database connection. */
6787
7539
  async close() {
6788
7540
  if (!this.db) return;
6789
7541
  try {
@@ -6794,6 +7546,7 @@ var SqliteEventDatabase = class {
6794
7546
  this.columns.clear();
6795
7547
  }
6796
7548
  }
7549
+ /** Insert one normalized telemetry event row. */
6797
7550
  async insertEvent(input) {
6798
7551
  const event = normalizeEvent(input);
6799
7552
  const stmt = this.insertStmt;
@@ -6839,6 +7592,7 @@ var SqliteEventDatabase = class {
6839
7592
  );
6840
7593
  }
6841
7594
  }
7595
+ /** Insert many events in a single transaction. */
6842
7596
  async insertEvents(inputs) {
6843
7597
  const db = this.requireDb();
6844
7598
  const out = [];
@@ -6857,6 +7611,7 @@ var SqliteEventDatabase = class {
6857
7611
  throw error;
6858
7612
  }
6859
7613
  }
7614
+ /** Query telemetry events with filters, sort, and pagination. */
6860
7615
  async query(options) {
6861
7616
  const db = this.requireDb();
6862
7617
  const clauses = [];
@@ -6902,8 +7657,10 @@ var SqliteEventDatabase = class {
6902
7657
  }
6903
7658
  }
6904
7659
  if (options.memoryId) {
6905
- clauses.push(`memory_ids_json LIKE ?`);
6906
- params.push(`%${options.memoryId}%`);
7660
+ clauses.push(
7661
+ `EXISTS (SELECT 1 FROM json_each(memory_ids_json) WHERE json_each.value = ?)`
7662
+ );
7663
+ params.push(options.memoryId);
6907
7664
  }
6908
7665
  if (options.queryText) {
6909
7666
  clauses.push(`query LIKE ?`);
@@ -6935,11 +7692,13 @@ var SqliteEventDatabase = class {
6935
7692
  offset
6936
7693
  };
6937
7694
  }
7695
+ /** Fetch one event by primary key. */
6938
7696
  async getEvent(id) {
6939
7697
  const db = this.requireDb();
6940
7698
  const row = db.prepare(`SELECT * FROM telemetry_events WHERE id = ?`).get(id);
6941
7699
  return row ? rowToEvent(row) : null;
6942
7700
  }
7701
+ /** Count events optionally filtered by time and operation. */
6943
7702
  async countEvents(filter) {
6944
7703
  const db = this.requireDb();
6945
7704
  const clauses = [];
@@ -7080,6 +7839,7 @@ function describe2(error) {
7080
7839
  }
7081
7840
 
7082
7841
  // src/providers/sqlite/sqliteTelemetryProvider.ts
7842
+ var MAX_QUEUE_SIZE = 1e4;
7083
7843
  var SqliteTelemetryProvider = class {
7084
7844
  name = "sqlite";
7085
7845
  db;
@@ -7087,9 +7847,15 @@ var SqliteTelemetryProvider = class {
7087
7847
  flushing = null;
7088
7848
  closed = false;
7089
7849
  openPromise = null;
7850
+ warnedQueueDrop = false;
7851
+ warnedFlushFailure = false;
7852
+ /**
7853
+ * @param options.url - Path to the independent telemetry SQLite database file.
7854
+ */
7090
7855
  constructor(options) {
7091
7856
  this.db = new SqliteEventDatabase({ url: options.url });
7092
7857
  }
7858
+ /** Open the telemetry EventDatabase (lazy — also invoked on first emit). */
7093
7859
  async open() {
7094
7860
  if (!this.openPromise) {
7095
7861
  this.openPromise = this.db.open();
@@ -7097,19 +7863,35 @@ var SqliteTelemetryProvider = class {
7097
7863
  await this.openPromise;
7098
7864
  this.closed = false;
7099
7865
  }
7866
+ /** Flush pending events and close the telemetry database. */
7100
7867
  async close() {
7101
7868
  this.closed = true;
7102
7869
  await this.flush();
7103
7870
  await this.db.close();
7104
7871
  this.openPromise = null;
7105
7872
  }
7873
+ /**
7874
+ * Enqueue a telemetry event for async persistence (never throws).
7875
+ *
7876
+ * @param event - Partial or complete {@link TelemetryEventInput}.
7877
+ */
7106
7878
  emit(event) {
7107
7879
  if (this.closed) {
7108
7880
  return;
7109
7881
  }
7882
+ if (this.queue.length >= MAX_QUEUE_SIZE) {
7883
+ this.queue.shift();
7884
+ if (!this.warnedQueueDrop) {
7885
+ this.warnedQueueDrop = true;
7886
+ console.warn(
7887
+ `[wolbarg telemetry] event queue capped at ${MAX_QUEUE_SIZE}; dropping oldest events`
7888
+ );
7889
+ }
7890
+ }
7110
7891
  this.queue.push(event);
7111
7892
  void this.scheduleFlush();
7112
7893
  }
7894
+ /** Block until all queued events are written (or dropped on failure). */
7113
7895
  async flush() {
7114
7896
  while (this.queue.length > 0 || this.flushing) {
7115
7897
  await this.scheduleFlush();
@@ -7118,10 +7900,12 @@ var SqliteTelemetryProvider = class {
7118
7900
  }
7119
7901
  }
7120
7902
  }
7903
+ /** Query persisted telemetry events with filters and pagination. */
7121
7904
  async query(options) {
7122
7905
  await this.open();
7123
7906
  return this.db.query(options);
7124
7907
  }
7908
+ /** Fetch a single telemetry event by id. */
7125
7909
  async getEvent(id) {
7126
7910
  await this.open();
7127
7911
  return this.db.getEvent(id);
@@ -7149,7 +7933,13 @@ var SqliteTelemetryProvider = class {
7149
7933
  await this.db.insertEvent(event);
7150
7934
  }
7151
7935
  }
7152
- } catch {
7936
+ } catch (error) {
7937
+ if (!this.warnedFlushFailure) {
7938
+ this.warnedFlushFailure = true;
7939
+ console.warn(
7940
+ `[wolbarg telemetry] flush failed; dropping batch: ${error instanceof Error ? error.message : String(error)}`
7941
+ );
7942
+ }
7153
7943
  }
7154
7944
  }
7155
7945
  };
@@ -7157,16 +7947,28 @@ var SqliteCheckpointProvider = class {
7157
7947
  name = "sqlite";
7158
7948
  directory;
7159
7949
  ready = false;
7950
+ /**
7951
+ * @param options.directory - Folder for `.json` metadata and `.db` snapshot files.
7952
+ */
7160
7953
  constructor(options) {
7161
7954
  this.directory = options?.directory ?? path7.resolve(process.cwd(), ".wolbarg", "checkpoints");
7162
7955
  }
7956
+ /** Ensure the checkpoint directory exists. */
7163
7957
  async open() {
7164
7958
  fs6.mkdirSync(this.directory, { recursive: true });
7165
7959
  this.ready = true;
7166
7960
  }
7961
+ /** Mark the provider closed (does not delete snapshots). */
7167
7962
  async close() {
7168
7963
  this.ready = false;
7169
7964
  }
7965
+ /**
7966
+ * Create an immutable SQLite backup of `sourcePath` under a unique name.
7967
+ *
7968
+ * @param name - Checkpoint label (must not already exist).
7969
+ * @param sourcePath - Live memory database path to snapshot.
7970
+ * @param options.description - Optional human-readable description stored in metadata.
7971
+ */
7170
7972
  async checkpoint(name, sourcePath, options) {
7171
7973
  this.requireReady();
7172
7974
  assertCheckpointName(name);
@@ -7192,8 +7994,20 @@ Ensure Wolbarg has been initialized and the database path is correct.`
7192
7994
  );
7193
7995
  }
7194
7996
  const snapshotPath = this.snapshotPath(name);
7195
- await safeSqliteBackup(resolvedSource, snapshotPath);
7196
- const stats = fs6.statSync(snapshotPath);
7997
+ const tmpSnapshotPath = `${snapshotPath}.tmp-${Date.now()}`;
7998
+ try {
7999
+ await safeSqliteBackup(resolvedSource, tmpSnapshotPath);
8000
+ } catch (error) {
8001
+ if (fs6.existsSync(tmpSnapshotPath)) {
8002
+ fs6.rmSync(tmpSnapshotPath, { force: true });
8003
+ }
8004
+ throw error;
8005
+ }
8006
+ const stats = fs6.statSync(tmpSnapshotPath);
8007
+ if (fs6.existsSync(snapshotPath)) {
8008
+ fs6.rmSync(snapshotPath, { force: true });
8009
+ }
8010
+ fs6.renameSync(tmpSnapshotPath, snapshotPath);
7197
8011
  const meta2 = {
7198
8012
  name,
7199
8013
  description: options?.description ?? null,
@@ -7204,9 +8018,17 @@ Ensure Wolbarg has been initialized and the database path is correct.`
7204
8018
  sourcePath: resolvedSource,
7205
8019
  sizeBytes: stats.size
7206
8020
  };
7207
- fs6.writeFileSync(metaPath, JSON.stringify(meta2, null, 2), "utf8");
8021
+ const tmpMetaPath = `${metaPath}.tmp-${Date.now()}`;
8022
+ fs6.writeFileSync(tmpMetaPath, JSON.stringify(meta2, null, 2), "utf8");
8023
+ fs6.renameSync(tmpMetaPath, metaPath);
7208
8024
  return meta2;
7209
8025
  }
8026
+ /**
8027
+ * Restore a named checkpoint over `targetPath` (replaces WAL/SHM side files).
8028
+ *
8029
+ * @param name - Existing checkpoint name.
8030
+ * @param targetPath - Live memory database path to overwrite.
8031
+ */
7210
8032
  async rollback(name, targetPath) {
7211
8033
  this.requireReady();
7212
8034
  const meta2 = await this.getCheckpoint(name);
@@ -7229,6 +8051,7 @@ Ensure Wolbarg has been initialized and the database path is correct.`
7229
8051
  await safeSqliteBackup(meta2.snapshotPath, resolvedTarget);
7230
8052
  return meta2;
7231
8053
  }
8054
+ /** Delete checkpoint metadata and snapshot files for `name`. */
7232
8055
  async deleteCheckpoint(name) {
7233
8056
  this.requireReady();
7234
8057
  const metaPath = this.metaPath(name);
@@ -7244,6 +8067,7 @@ Ensure Wolbarg has been initialized and the database path is correct.`
7244
8067
  }
7245
8068
  return removed;
7246
8069
  }
8070
+ /** List all checkpoints sorted by creation time. */
7247
8071
  async listCheckpoints() {
7248
8072
  this.requireReady();
7249
8073
  if (!fs6.existsSync(this.directory)) {
@@ -7260,6 +8084,7 @@ Ensure Wolbarg has been initialized and the database path is correct.`
7260
8084
  }
7261
8085
  return out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
7262
8086
  }
8087
+ /** Load checkpoint metadata by name, or `null` if missing / corrupt. */
7263
8088
  async getCheckpoint(name) {
7264
8089
  this.requireReady();
7265
8090
  const metaPath = this.metaPath(name);
@@ -7444,6 +8269,13 @@ var SqliteSubscribeEmitter = class {
7444
8269
  }
7445
8270
  });
7446
8271
  }
8272
+ /**
8273
+ * Register a filtered callback for in-process memory change events.
8274
+ *
8275
+ * @param filter - Organization / agent / event-type filter.
8276
+ * @param callback - Invoked synchronously on each matching emit.
8277
+ * @returns Unsubscribe function.
8278
+ */
7447
8279
  subscribe(filter, callback) {
7448
8280
  if (this.closed) {
7449
8281
  throw new Error("Subscribe backend is closed");
@@ -7454,12 +8286,14 @@ var SqliteSubscribeEmitter = class {
7454
8286
  this.subscriptions.delete(id);
7455
8287
  };
7456
8288
  }
8289
+ /** Emit a memory change event to registered subscribers. */
7457
8290
  emit(event) {
7458
8291
  if (this.closed) {
7459
8292
  return;
7460
8293
  }
7461
8294
  this.emitter.emit("change", event);
7462
8295
  }
8296
+ /** Remove all listeners and mark the emitter closed. */
7463
8297
  async close() {
7464
8298
  this.closed = true;
7465
8299
  this.subscriptions.clear();
@@ -7468,6 +8302,9 @@ var SqliteSubscribeEmitter = class {
7468
8302
  };
7469
8303
 
7470
8304
  // src/core/wolbarg.ts
8305
+ var warnLogger3 = new WolbargLogger("warn");
8306
+ var warnedHybridNoKeyword = false;
8307
+ var warnedRerankNoProvider = false;
7471
8308
  var Wolbarg = class {
7472
8309
  initialized = false;
7473
8310
  booting = null;
@@ -7494,6 +8331,20 @@ var Wolbarg = class {
7494
8331
  memoryDedupe = resolveMemoryDedupeConfig();
7495
8332
  embeddingCacheConfig = resolveEmbeddingCacheConfig();
7496
8333
  rawEmbedding = null;
8334
+ /**
8335
+ * @param options - Full {@link WolbargOptions}. Key fields:
8336
+ * - `organization` — namespace isolating memories in a shared DB
8337
+ * - `database` / `storage` — SQLite/Postgres config **or** a custom
8338
+ * {@link StorageProvider} instance
8339
+ * - `embedding` — {@link EmbeddingProvider} instance **or**
8340
+ * {@link EmbeddingConfig} / {@link openaiEmbedding} (etc.)
8341
+ * - `llm` — optional {@link LlmProvider} or {@link LlmConfig} / {@link openaiLlm};
8342
+ * required for {@link Wolbarg.compress} and extract-mode
8343
+ * {@link Wolbarg.rememberFromMessages}
8344
+ * - `graph`, `telemetry`, `reranker`, `keywordSearch`, `ocr`, `vision`,
8345
+ * `compression`, `chunking`, `retrieval`, `concurrency`, `embeddingCache`,
8346
+ * `memory.dedupe` — optional plugins / tuning
8347
+ */
7497
8348
  constructor(options) {
7498
8349
  this.telemetry = new TelemetryEmitter(null, { enabled: false, level: "off" });
7499
8350
  if (!options) {
@@ -7552,6 +8403,12 @@ var Wolbarg = class {
7552
8403
  }
7553
8404
  /**
7554
8405
  * Backwards-compatible initialization (v0.1 API).
8406
+ * Prefer constructing with options + {@link Wolbarg.ready} instead.
8407
+ *
8408
+ * @param options - Organization, database, embedding, and optional LLM config.
8409
+ * @returns Resolves when storage is open and the vector schema is ready.
8410
+ * @throws {InitializationError} If already initialized.
8411
+ * @throws {ConfigurationError} | {ValidationError} On invalid options.
7555
8412
  */
7556
8413
  async init(options) {
7557
8414
  if (this.initialized || this.storage) {
@@ -7573,6 +8430,7 @@ var Wolbarg = class {
7573
8430
  this.storage = createStorageProvider(validated.database);
7574
8431
  this.memoryDbPath = resolveDatabaseUrl(validated.database);
7575
8432
  this.embedding = createEmbeddingProvider(validated.embedding);
8433
+ this.rawEmbedding = this.embedding;
7576
8434
  if (validated.llm) {
7577
8435
  this.llm = createLlmProvider(validated.llm);
7578
8436
  this.compression = createCompressionProvider(this.llm);
@@ -7582,7 +8440,14 @@ var Wolbarg = class {
7582
8440
  }
7583
8441
  await this.ready();
7584
8442
  }
7585
- /** Ensure storage (and optional telemetry) are open. */
8443
+ /**
8444
+ * Open storage, optional telemetry / checkpoint / graph providers, wrap the
8445
+ * embedding cache, and probe embedding dimensions. Safe to call multiple times;
8446
+ * concurrent callers share one boot promise.
8447
+ *
8448
+ * @returns Resolves when the instance is ready for remember/recall.
8449
+ * @throws {InitializationError} If neither constructor options nor {@link init} were used.
8450
+ */
7586
8451
  async ready() {
7587
8452
  if (this.initialized) {
7588
8453
  return;
@@ -7598,6 +8463,7 @@ var Wolbarg = class {
7598
8463
  this.booting = null;
7599
8464
  }
7600
8465
  }
8466
+ /** Internal boot sequence invoked by {@link Wolbarg.ready}. */
7601
8467
  async boot() {
7602
8468
  if (!this.storage || !this.embedding || !this.organization) {
7603
8469
  throw new InitializationError(
@@ -7673,7 +8539,17 @@ var Wolbarg = class {
7673
8539
  );
7674
8540
  }
7675
8541
  }
7676
- /** Store a semantic memory for an agent (may upsert when dedupe is enabled). */
8542
+ /**
8543
+ * Store a semantic memory for an agent. Embeds `content.text`, writes the row,
8544
+ * and may **upsert** an existing active memory when dedupe is enabled
8545
+ * (constructor `memory.dedupe` or per-call `options.dedupe`).
8546
+ *
8547
+ * @param options - Agent id, text content, optional metadata / dedupe overrides.
8548
+ * See {@link RememberOptions}.
8549
+ * @returns The stored record plus `action` (`"created"` | `"updated"`).
8550
+ * @throws {ValidationError} On empty agent/content.
8551
+ * @throws {InitializationError} If not configured / ready.
8552
+ */
7677
8553
  async remember(options) {
7678
8554
  const trace = this.telemetry.start("remember");
7679
8555
  try {
@@ -7695,7 +8571,15 @@ var Wolbarg = class {
7695
8571
  throw wrapOperationError("remember", error);
7696
8572
  }
7697
8573
  }
7698
- /** Batch remember — sequential when dedupe enabled; otherwise one TX batch. */
8574
+ /**
8575
+ * Remember many memories in one call. Runs sequentially when any item (or the
8576
+ * global config) enables dedupe; otherwise embeds in batch and inserts in one
8577
+ * transaction for throughput.
8578
+ *
8579
+ * @param items - Non-empty list of {@link RememberOptions}.
8580
+ * @returns One {@link RememberResult} per input, same order.
8581
+ * @throws {ValidationError} If `items` is empty or an entry is invalid.
8582
+ */
7699
8583
  async rememberBatch(items) {
7700
8584
  const parent = this.telemetry.start("rememberBatch");
7701
8585
  try {
@@ -7821,7 +8705,15 @@ var Wolbarg = class {
7821
8705
  * **Experimental** until 1.0 — API shape may change.
7822
8706
  *
7823
8707
  * - `mode: "raw"` (default) — remember user message text (no LLM).
7824
- * - `mode: "extract"` — require configured `llm`; extract atomic facts then remember each.
8708
+ * - `mode: "extract"` — requires configured `llm`; extracts atomic facts then
8709
+ * remembers each. Pass a custom {@link LlmProvider} or {@link openaiLlm}
8710
+ * in the constructor.
8711
+ *
8712
+ * @param messages - Conversation turns (`role` + `content`).
8713
+ * @param options - Agent, mode, optional metadata/dedupe. See
8714
+ * {@link RememberFromMessagesOptions}.
8715
+ * @returns One {@link RememberResult} per remembered fact/message.
8716
+ * @throws {ProviderNotConfiguredError} When `mode: "extract"` but no `llm` was configured.
7825
8717
  */
7826
8718
  async rememberFromMessages(messages, options) {
7827
8719
  const parent = this.telemetry.start("rememberFromMessages");
@@ -7891,10 +8783,17 @@ var Wolbarg = class {
7891
8783
  }
7892
8784
  }
7893
8785
  /**
7894
- * Update an existing memory by id (re-embeds when content changes).
8786
+ * Update an existing memory by id. Re-embeds when `content` changes; merges
8787
+ * metadata when provided.
8788
+ *
8789
+ * @param options.id - Memory id to update.
8790
+ * @param options.content - Optional new `{ text }` (triggers re-embed + content hash).
8791
+ * @param options.metadata - Optional metadata merged onto the existing record.
8792
+ * @returns Updated record with `action: "updated"`.
8793
+ * @throws {MemoryNotFoundError} If the id is unknown in this organization.
7895
8794
  */
7896
8795
  async update(options) {
7897
- const trace = this.telemetry.start("remember");
8796
+ const trace = this.telemetry.start("update");
7898
8797
  try {
7899
8798
  const { storage, embedding, organization } = await this.requireReady();
7900
8799
  assertNonEmptyString(options.id, "id");
@@ -7961,10 +8860,18 @@ var Wolbarg = class {
7961
8860
  }
7962
8861
  }
7963
8862
  /**
7964
- * Subscribe to memory change events.
8863
+ * Subscribe to memory change events (remember / update / forget / compress / clear).
7965
8864
  *
7966
8865
  * **SQLite:** delivers events only within this Node.js process.
7967
8866
  * A second process writing the same `memory.db` will not notify subscribers here.
8867
+ *
8868
+ * **Postgres:** uses LISTEN/NOTIFY across processes (lazy connection on first subscribe).
8869
+ *
8870
+ * @param filter - Scope by `organization` (defaults to this instance’s org),
8871
+ * optional `agent`, and optional `events` allow-list. See {@link SubscribeFilter}.
8872
+ * @param callback - Invoked with each {@link MemoryChangeEvent}.
8873
+ * @returns {@link Unsubscribe} function — call to stop receiving events.
8874
+ * @throws {ValidationError} If organization cannot be resolved.
7968
8875
  */
7969
8876
  subscribe(filter, callback) {
7970
8877
  const org = filter.organization || this.organization;
@@ -7993,6 +8900,7 @@ var Wolbarg = class {
7993
8900
  }
7994
8901
  return this.subscribeBackend.subscribe(normalized, callback);
7995
8902
  }
8903
+ /** Broadcast a memory change event to in-process or Postgres NOTIFY subscribers. */
7996
8904
  emitChange(event) {
7997
8905
  try {
7998
8906
  if (this.storage instanceof PostgresStorageProvider) {
@@ -8218,6 +9126,17 @@ var Wolbarg = class {
8218
9126
  throw error;
8219
9127
  }
8220
9128
  }
9129
+ /**
9130
+ * Find the best active near-duplicate memory for upsert (cosine similarity ≥ threshold).
9131
+ *
9132
+ * @param storage - Open storage provider.
9133
+ * @param organization - Org namespace.
9134
+ * @param agent - Agent scope for the search.
9135
+ * @param vector - Embedding of the candidate text.
9136
+ * @param threshold - Minimum similarity (0–1) to treat as a duplicate.
9137
+ * @param limit - Max vector candidates to inspect.
9138
+ * @returns Matching memory row, or `null` if none qualify.
9139
+ */
8221
9140
  async findNearDuplicate(storage, organization, agent, vector, threshold, limit) {
8222
9141
  if (typeof storage.searchVectorsWithMemories === "function") {
8223
9142
  const hits2 = await storage.searchVectorsWithMemories(
@@ -8420,17 +9339,37 @@ var Wolbarg = class {
8420
9339
  }
8421
9340
  }
8422
9341
  }
9342
+ if (hybridWeights && keywordSignal === "disabled" && !warnedHybridNoKeyword) {
9343
+ warnedHybridNoKeyword = true;
9344
+ warnLogger3.warn(
9345
+ "hybrid search requested but no keyword channel is available; using semantic-only results."
9346
+ );
9347
+ }
8423
9348
  const tRank = performance.now();
8424
- let results = [...byId.values()].sort(
8425
- (a, b) => b.similarity - a.similarity
8426
- );
9349
+ let results = [...byId.values()].sort((a, b) => {
9350
+ const diff = b.similarity - a.similarity;
9351
+ if (diff !== 0) return diff;
9352
+ return a.id.localeCompare(b.id);
9353
+ });
8427
9354
  const mmrLambda = resolveMmr(options.mmr) ?? resolveMmr(
8428
9355
  this.retrievalConfig.mmr ? { lambda: this.retrievalConfig.mmr.lambda } : void 0
8429
9356
  );
8430
9357
  let rankingReason = "cosine similarity";
9358
+ let mmrApplied = false;
8431
9359
  if (mmrLambda !== null) {
8432
- results = applyMmr(results, Math.max(topK * 2, topK), mmrLambda);
8433
- rankingReason = `MMR(lambda=${mmrLambda})`;
9360
+ mmrApplied = results.length > topK;
9361
+ if (mmrApplied) {
9362
+ results = applyMmr(results, topK, mmrLambda);
9363
+ rankingReason = `MMR(lambda=${mmrLambda})`;
9364
+ } else {
9365
+ rankingReason = `MMR(lambda=${mmrLambda})(skipped:insufficient_candidates)`;
9366
+ }
9367
+ }
9368
+ if (options.rerank === true && !this.reranker && !warnedRerankNoProvider) {
9369
+ warnedRerankNoProvider = true;
9370
+ warnLogger3.warn(
9371
+ "rerank is enabled but no reranker provider is configured; using identity order."
9372
+ );
8434
9373
  }
8435
9374
  if (options.rerank && this.reranker) {
8436
9375
  const reranked = await this.reranker.rerank(
@@ -8465,14 +9404,16 @@ var Wolbarg = class {
8465
9404
  agentId: options.filter?.agent ?? commonAgent(serialized.map((result) => result.agent)),
8466
9405
  tags: commonTags(serialized.map((result) => result.metadata))
8467
9406
  };
8468
- if (!explain) {
8469
- if (options.includeGraph === true && this.graph) {
8470
- const tGraph = performance.now();
8471
- for (const hit of serialized) {
9407
+ if (options.includeGraph === true && this.graph) {
9408
+ const tGraph = performance.now();
9409
+ await Promise.all(
9410
+ serialized.map(async (hit) => {
8472
9411
  hit.related = await this.hydrateRelated(hit.id);
8473
- }
8474
- trace.mark("databaseReadMs", performance.now() - tGraph);
8475
- }
9412
+ })
9413
+ );
9414
+ trace.mark("databaseReadMs", performance.now() - tGraph);
9415
+ }
9416
+ if (!explain) {
8476
9417
  trace.success(telemetryFields);
8477
9418
  return serialized;
8478
9419
  }
@@ -8501,7 +9442,7 @@ var Wolbarg = class {
8501
9442
  semantic: "enabled",
8502
9443
  keyword: keywordSignal,
8503
9444
  reranker: options.rerank === true && this.reranker ? "enabled" : "disabled",
8504
- mmr: mmrLambda === null ? "disabled" : "enabled",
9445
+ mmr: mmrLambda === null ? "disabled" : mmrApplied ? "enabled" : "disabled",
8505
9446
  recency: "disabled"
8506
9447
  },
8507
9448
  results: explanations.map((hit) => ({
@@ -8533,7 +9474,13 @@ var Wolbarg = class {
8533
9474
  throw wrapOperationError("recall", error);
8534
9475
  }
8535
9476
  }
8536
- /** Batch recall — parent event + child traces. */
9477
+ /**
9478
+ * Run multiple recalls sequentially under one parent telemetry span.
9479
+ *
9480
+ * @param queries - Non-empty list of recall options (`explain` is forced off).
9481
+ * @returns One hit array per query, same order.
9482
+ * @throws {ValidationError} If `queries` is empty.
9483
+ */
8537
9484
  async recallBatch(queries) {
8538
9485
  const parent = this.telemetry.start("recallBatch");
8539
9486
  try {
@@ -8567,10 +9514,21 @@ var Wolbarg = class {
8567
9514
  throw wrapOperationError("recallBatch", error);
8568
9515
  }
8569
9516
  }
9517
+ /**
9518
+ * Compress the newest active memories for an agent into one summary memory
9519
+ * and archive the sources. Requires an `llm` (or custom `compression`) at
9520
+ * construction — typed only on `Wolbarg<true>`.
9521
+ *
9522
+ * @param options - `agent` required; optional `limit` (default 50, min 2).
9523
+ * See {@link CompressOptions}.
9524
+ * @returns Summary record plus archived source ids. See {@link CompressResult}.
9525
+ * @throws {ProviderNotConfiguredError} If no LLM/compression provider is set.
9526
+ * @throws {ValidationError} If fewer than 2 active memories exist for the agent.
9527
+ */
8570
9528
  async compress(options) {
8571
9529
  return this.runCompress(options);
8572
9530
  }
8573
- /** @internal */
9531
+ /** @internal Runs compress for both typed and untyped call sites. */
8574
9532
  async runCompress(options) {
8575
9533
  const trace = this.telemetry.start("compress");
8576
9534
  try {
@@ -8610,31 +9568,34 @@ var Wolbarg = class {
8610
9568
  const timestamp = nowIso();
8611
9569
  const result = await this.withWriteLock(async () => {
8612
9570
  const tWrite = performance.now();
8613
- const summaryRow = await storage.insertMemory({
8614
- id: summaryId,
8615
- organization,
8616
- agent: options.agent.trim(),
8617
- contentText: summaryText,
8618
- metadata: {
8619
- compressed: true,
8620
- sourceCount: records.length,
8621
- sourceIds: records.map((r) => r.id)
8622
- },
8623
- embedding: vector,
8624
- createdAt: timestamp,
8625
- updatedAt: timestamp
9571
+ const txResult = await storage.withTransaction(async () => {
9572
+ const summaryRow = await storage.insertMemory({
9573
+ id: summaryId,
9574
+ organization,
9575
+ agent: options.agent.trim(),
9576
+ contentText: summaryText,
9577
+ metadata: {
9578
+ compressed: true,
9579
+ sourceCount: records.length,
9580
+ sourceIds: records.map((r) => r.id)
9581
+ },
9582
+ embedding: vector,
9583
+ createdAt: timestamp,
9584
+ updatedAt: timestamp
9585
+ });
9586
+ const archivedIds = await storage.archiveMemories(
9587
+ records.map((r) => r.id),
9588
+ organization,
9589
+ summaryId,
9590
+ timestamp
9591
+ );
9592
+ return {
9593
+ summary: toMemoryRecord(summaryRow),
9594
+ archivedIds
9595
+ };
8626
9596
  });
8627
- const archivedIds = await storage.archiveMemories(
8628
- records.map((r) => r.id),
8629
- organization,
8630
- summaryId,
8631
- timestamp
8632
- );
8633
9597
  trace.mark("databaseWriteMs", performance.now() - tWrite);
8634
- return {
8635
- summary: toMemoryRecord(summaryRow),
8636
- archivedIds
8637
- };
9598
+ return txResult;
8638
9599
  });
8639
9600
  trace.success({
8640
9601
  provider: storage.name,
@@ -8656,6 +9617,16 @@ var Wolbarg = class {
8656
9617
  throw wrapOperationError("compress", error);
8657
9618
  }
8658
9619
  }
9620
+ /**
9621
+ * Ingest a document (path, URL, Buffer, or text), chunk it, embed chunks, and
9622
+ * store each as a memory. Images use optional `ocr` / `vision` providers from
9623
+ * the constructor.
9624
+ *
9625
+ * @param options - `agent`, `source`, optional chunking overrides / metadata.
9626
+ * See {@link IngestOptions}.
9627
+ * @returns Counts and created memory ids. See {@link IngestResult}.
9628
+ * @throws {ValidationError} If no text can be extracted or chunking yields zero chunks.
9629
+ */
8659
9630
  async ingest(options) {
8660
9631
  const trace = this.telemetry.start("ingest");
8661
9632
  try {
@@ -8826,8 +9797,14 @@ var Wolbarg = class {
8826
9797
  }
8827
9798
  }
8828
9799
  /**
8829
- * Link two memories in the optional graph layer.
8830
- * Throws {@link ProviderNotConfiguredError} when no graph provider is set.
9800
+ * Link two memories in the optional graph layer with a typed relation edge.
9801
+ *
9802
+ * @param fromId - Source memory id.
9803
+ * @param toId - Target memory id.
9804
+ * @param relation - Edge label (e.g. `"supports"`, `"caused_by"`).
9805
+ * @param metadata - Optional edge properties stored on the graph provider.
9806
+ * @throws {ProviderNotConfiguredError} When no `graph` was configured.
9807
+ * @throws {ValidationError} On empty ids/relation.
8831
9808
  */
8832
9809
  async linkMemories(fromId, toId, relation, metadata) {
8833
9810
  const trace = this.telemetry.start("linkMemories");
@@ -8857,8 +9834,13 @@ var Wolbarg = class {
8857
9834
  }
8858
9835
  /**
8859
9836
  * Traverse related memories via the optional graph layer.
8860
- * Throws {@link ProviderNotConfiguredError} when no graph provider is set.
8861
- * Results are re-hydrated from SQL storage when available.
9837
+ * Neighbor stubs from the graph are re-hydrated from SQL storage when available.
9838
+ *
9839
+ * @param memoryId - Seed memory id.
9840
+ * @param options - Optional `relation`, `direction`, `depth`, `limit`.
9841
+ * See {@link GetRelatedOptions}.
9842
+ * @returns Related {@link MemoryRecord} list (may be empty).
9843
+ * @throws {ProviderNotConfiguredError} When no `graph` was configured.
8862
9844
  */
8863
9845
  async getRelated(memoryId, options) {
8864
9846
  const trace = this.telemetry.start("getRelated");
@@ -8866,8 +9848,23 @@ var Wolbarg = class {
8866
9848
  await this.requireReady();
8867
9849
  const graph = this.requireGraph("getRelated");
8868
9850
  assertNonEmptyString(memoryId, "memoryId");
9851
+ let validatedOptions = options;
9852
+ if (options?.depth !== void 0) {
9853
+ if (!Number.isFinite(options.depth)) {
9854
+ throw new ConfigurationError("graph.depth must be a finite number");
9855
+ }
9856
+ if (options.depth < 1) {
9857
+ throw new ConfigurationError("graph.depth must be >= 1");
9858
+ }
9859
+ if (options.depth > 16) {
9860
+ validatedOptions = { ...options, depth: 16 };
9861
+ }
9862
+ }
8869
9863
  const tGraph = performance.now();
8870
- const related = await this.hydrateRelated(memoryId.trim(), options);
9864
+ const related = await this.hydrateRelated(
9865
+ memoryId.trim(),
9866
+ validatedOptions
9867
+ );
8871
9868
  trace.mark("databaseReadMs", performance.now() - tGraph);
8872
9869
  trace.success({
8873
9870
  provider: graph.name,
@@ -8880,6 +9877,14 @@ var Wolbarg = class {
8880
9877
  throw wrapOperationError("getRelated", error);
8881
9878
  }
8882
9879
  }
9880
+ /**
9881
+ * Delete memories by id or by agent filter. Cascades graph nodes/edges when
9882
+ * a graph provider is configured.
9883
+ *
9884
+ * @param options - Either `{ id }` or `{ filter: { agent } }`. See {@link ForgetOptions}.
9885
+ * @returns Number of memories deleted.
9886
+ * @throws {ValidationError} If neither `id` nor `filter.agent` is provided.
9887
+ */
8883
9888
  async forget(options) {
8884
9889
  const trace = this.telemetry.start("forget");
8885
9890
  try {
@@ -8944,6 +9949,13 @@ var Wolbarg = class {
8944
9949
  throw wrapOperationError("forget", error);
8945
9950
  }
8946
9951
  }
9952
+ /**
9953
+ * Return the audit/history timeline for a single memory.
9954
+ *
9955
+ * @param options - `{ id }` of the memory. See {@link HistoryOptions}.
9956
+ * @returns Memory record plus ordered {@link HistoryEvent} list.
9957
+ * @throws {MemoryNotFoundError} If the id is unknown.
9958
+ */
8947
9959
  async history(options) {
8948
9960
  const trace = this.telemetry.start("history");
8949
9961
  try {
@@ -8971,6 +9983,11 @@ var Wolbarg = class {
8971
9983
  throw wrapOperationError("history", error);
8972
9984
  }
8973
9985
  }
9986
+ /**
9987
+ * Aggregate counts for this organization (active / archived / agents / etc.).
9988
+ *
9989
+ * @returns {@link StatsResult} snapshot.
9990
+ */
8974
9991
  async stats() {
8975
9992
  const trace = this.telemetry.start("stats");
8976
9993
  try {
@@ -8998,6 +10015,13 @@ var Wolbarg = class {
8998
10015
  throw wrapOperationError("stats", error);
8999
10016
  }
9000
10017
  }
10018
+ /**
10019
+ * Delete all memories for this organization (optionally scoped by agent).
10020
+ * Cascades graph cleanup when a graph provider is configured.
10021
+ *
10022
+ * @param options - Optional `{ agent }` scope. See {@link ClearOptions}.
10023
+ * @returns Number of memories deleted.
10024
+ */
9001
10025
  async clear(options) {
9002
10026
  const trace = this.telemetry.start("clear");
9003
10027
  try {
@@ -9026,7 +10050,16 @@ var Wolbarg = class {
9026
10050
  throw wrapOperationError("clear", error);
9027
10051
  }
9028
10052
  }
9029
- /** Create an immutable named checkpoint of the memory database. */
10053
+ /**
10054
+ * Create an immutable named checkpoint of the file-backed SQLite memory DB
10055
+ * (and SQLite graph snapshot when applicable).
10056
+ *
10057
+ * @param name - Checkpoint name (unique).
10058
+ * @param options - Optional metadata. See {@link CheckpointOptions}.
10059
+ * @returns {@link CheckpointInfo} for the new checkpoint.
10060
+ * @throws {ProviderNotConfiguredError} Without a checkpoint provider / file DB.
10061
+ * @throws {GraphCheckpointNotSupportedError} For Neo4j graph backends.
10062
+ */
9030
10063
  async checkpoint(name, options) {
9031
10064
  const trace = this.telemetry.start("checkpoint");
9032
10065
  try {
@@ -9036,7 +10069,15 @@ var Wolbarg = class {
9036
10069
  this.assertGraphCheckpointSupported("checkpoint");
9037
10070
  const tCheckpoint = performance.now();
9038
10071
  const meta2 = await provider.checkpoint(name, source, options);
9039
- await this.snapshotGraphAlongside(meta2.snapshotPath, "checkpoint");
10072
+ try {
10073
+ await this.snapshotGraphAlongside(meta2.snapshotPath, "checkpoint");
10074
+ } catch (error) {
10075
+ const sidecar = this.graphSnapshotDir(meta2.snapshotPath);
10076
+ if (fs6.existsSync(sidecar)) {
10077
+ fs6.rmSync(sidecar, { recursive: true, force: true });
10078
+ }
10079
+ throw error;
10080
+ }
9040
10081
  trace.mark("databaseWriteMs", performance.now() - tCheckpoint);
9041
10082
  trace.success({
9042
10083
  provider: provider.name,
@@ -9049,7 +10090,12 @@ var Wolbarg = class {
9049
10090
  throw wrapOperationError("checkpoint", error);
9050
10091
  }
9051
10092
  }
9052
- /** Restore the memory database from a named checkpoint. */
10093
+ /**
10094
+ * Restore the memory database from a named checkpoint (replaces the live DB file).
10095
+ *
10096
+ * @param name - Checkpoint name previously created with {@link Wolbarg.checkpoint}.
10097
+ * @returns {@link CheckpointInfo} of the restored checkpoint.
10098
+ */
9053
10099
  async rollback(name) {
9054
10100
  const trace = this.telemetry.start("rollback");
9055
10101
  let storageClosed = false;
@@ -9095,13 +10141,26 @@ var Wolbarg = class {
9095
10141
  throw wrapOperationError("rollback", error);
9096
10142
  }
9097
10143
  }
10144
+ /**
10145
+ * Delete a named checkpoint from disk / the checkpoint store.
10146
+ *
10147
+ * @param name - Checkpoint name.
10148
+ * @returns `true` if a checkpoint was removed.
10149
+ */
9098
10150
  async deleteCheckpoint(name) {
9099
10151
  const trace = this.telemetry.start("deleteCheckpoint");
9100
10152
  try {
9101
10153
  await this.requireReady();
9102
10154
  const provider = this.requireCheckpointProvider();
10155
+ const existing = await provider.getCheckpoint(name);
9103
10156
  const tDelete = performance.now();
9104
10157
  const removed = await provider.deleteCheckpoint(name);
10158
+ if (removed && existing) {
10159
+ const sidecar = this.graphSnapshotDir(existing.snapshotPath);
10160
+ if (fs6.existsSync(sidecar)) {
10161
+ fs6.rmSync(sidecar, { recursive: true, force: true });
10162
+ }
10163
+ }
9105
10164
  trace.mark("databaseWriteMs", performance.now() - tDelete);
9106
10165
  trace.success({
9107
10166
  provider: provider.name,
@@ -9114,6 +10173,11 @@ var Wolbarg = class {
9114
10173
  throw wrapOperationError("deleteCheckpoint", error);
9115
10174
  }
9116
10175
  }
10176
+ /**
10177
+ * List all known checkpoints for this instance.
10178
+ *
10179
+ * @returns Array of {@link CheckpointInfo} (may be empty).
10180
+ */
9117
10181
  async listCheckpoints() {
9118
10182
  const trace = this.telemetry.start("listCheckpoints");
9119
10183
  try {
@@ -9132,6 +10196,12 @@ var Wolbarg = class {
9132
10196
  throw wrapOperationError("listCheckpoints", error);
9133
10197
  }
9134
10198
  }
10199
+ /**
10200
+ * Look up a single checkpoint by name.
10201
+ *
10202
+ * @param name - Checkpoint name.
10203
+ * @returns {@link CheckpointInfo} or `null` if missing.
10204
+ */
9135
10205
  async getCheckpoint(name) {
9136
10206
  const trace = this.telemetry.start("getCheckpoint");
9137
10207
  try {
@@ -9151,7 +10221,13 @@ var Wolbarg = class {
9151
10221
  throw wrapOperationError("getCheckpoint", error);
9152
10222
  }
9153
10223
  }
9154
- /** Export the memory database to a portable SQLite + manifest bundle. */
10224
+ /**
10225
+ * Export the memory database to a portable SQLite + manifest bundle
10226
+ * (includes SQLite graph sidecar when applicable).
10227
+ *
10228
+ * @param exportPath - Destination file path for the export bundle.
10229
+ * @returns {@link ExportResult} with path, size, and timestamp.
10230
+ */
9155
10231
  async export(exportPath) {
9156
10232
  const trace = this.telemetry.start("export");
9157
10233
  try {
@@ -9163,7 +10239,9 @@ var Wolbarg = class {
9163
10239
  const result = await this.transfer.exportTo(
9164
10240
  exportPath,
9165
10241
  source,
9166
- this.organization ?? void 0
10242
+ this.organization ?? void 0,
10243
+ this.embedding?.model,
10244
+ this.embeddingDimensions ?? void 0
9167
10245
  );
9168
10246
  await this.snapshotGraphAlongside(result.path, "export");
9169
10247
  trace.success({
@@ -9180,22 +10258,33 @@ var Wolbarg = class {
9180
10258
  throw wrapOperationError("export", error);
9181
10259
  }
9182
10260
  }
9183
- /** Import a previously exported memory database, replacing the current file. */
10261
+ /**
10262
+ * Import a previously exported memory database, replacing the current file.
10263
+ * Closes and reopens storage around the swap.
10264
+ *
10265
+ * @param exportPath - Path to a bundle produced by {@link Wolbarg.export}.
10266
+ * @returns {@link ImportResult} with restored path and timestamp.
10267
+ */
9184
10268
  async import(exportPath) {
9185
10269
  const trace = this.telemetry.start("import");
9186
10270
  let storageClosed = false;
9187
10271
  try {
9188
10272
  await this.requireReady();
10273
+ this.assertGraphCheckpointSupported("import");
9189
10274
  const target = this.requireMemoryDbPath();
9190
10275
  if (this.storage) {
9191
10276
  await this.storage.close();
9192
10277
  storageClosed = true;
9193
10278
  }
9194
10279
  const graphClosed = await this.closeGraphForSnapshot();
9195
- this.assertGraphCheckpointSupported("import");
9196
10280
  const result = await this.transfer.importFrom(
9197
10281
  exportPath,
9198
- target
10282
+ target,
10283
+ {
10284
+ organization: this.organization ?? void 0,
10285
+ embeddingModel: this.embedding?.model,
10286
+ embeddingDimensions: this.embeddingDimensions ?? void 0
10287
+ }
9199
10288
  );
9200
10289
  await this.restoreGraphAlongside(result.path, "import");
9201
10290
  if (graphClosed) {
@@ -9225,11 +10314,17 @@ var Wolbarg = class {
9225
10314
  throw wrapOperationError("import", error);
9226
10315
  }
9227
10316
  }
9228
- /** Flush pending telemetry (useful in tests). */
10317
+ /**
10318
+ * Flush pending telemetry writes (useful in tests / before process exit).
10319
+ *
10320
+ * @returns Resolves when the telemetry provider has flushed.
10321
+ */
9229
10322
  async flushTelemetry() {
9230
10323
  await this.telemetry.flush();
9231
10324
  }
9232
- /** Session id for this SDK instance (telemetry traces). */
10325
+ /**
10326
+ * Session id for this SDK instance (attached to telemetry traces and change events).
10327
+ */
9233
10328
  get sessionId() {
9234
10329
  return this.telemetry.sessionId;
9235
10330
  }
@@ -9244,6 +10339,12 @@ var Wolbarg = class {
9244
10339
  }
9245
10340
  return fn();
9246
10341
  }
10342
+ /**
10343
+ * Close storage, graph, telemetry, checkpoints, and subscribe backends.
10344
+ * The instance can be discarded afterward; construct a new one to reopen.
10345
+ *
10346
+ * @returns Resolves when all providers have closed (errors are swallowed per-provider).
10347
+ */
9247
10348
  async close() {
9248
10349
  if (this.storage) {
9249
10350
  this.telemetry.emitShutdown(this.storage.name);
@@ -9268,9 +10369,11 @@ var Wolbarg = class {
9268
10369
  this.embeddingDimensions = null;
9269
10370
  this.initialized = false;
9270
10371
  }
10372
+ /** Whether {@link Wolbarg.ready} / {@link Wolbarg.init} has completed successfully. */
9271
10373
  get isInitialized() {
9272
10374
  return this.initialized;
9273
10375
  }
10376
+ /** Ensure {@link Wolbarg.ready} completed and core providers are available. */
9274
10377
  async requireReady() {
9275
10378
  await this.ready();
9276
10379
  if (!this.initialized || !this.storage || !this.embedding || !this.organization) {
@@ -9284,6 +10387,7 @@ var Wolbarg = class {
9284
10387
  organization: this.organization
9285
10388
  };
9286
10389
  }
10390
+ /** Reject embeddings whose dimensionality differs from the initialized model. */
9287
10391
  assertEmbeddingDimensions(dimensions) {
9288
10392
  if (this.embeddingDimensions !== null && dimensions !== this.embeddingDimensions) {
9289
10393
  throw new ValidationError(
@@ -9291,6 +10395,7 @@ var Wolbarg = class {
9291
10395
  );
9292
10396
  }
9293
10397
  }
10398
+ /** Return the configured checkpoint provider or throw {@link ProviderNotConfiguredError}. */
9294
10399
  requireCheckpointProvider() {
9295
10400
  if (!this.checkpointProvider) {
9296
10401
  throw new ProviderNotConfiguredError(
@@ -9301,6 +10406,7 @@ var Wolbarg = class {
9301
10406
  }
9302
10407
  return this.checkpointProvider;
9303
10408
  }
10409
+ /** Return the configured graph provider or throw with a setup hint for `method`. */
9304
10410
  requireGraph(method) {
9305
10411
  if (!this.graph) {
9306
10412
  throw new ProviderNotConfiguredError(
@@ -9311,20 +10417,24 @@ var Wolbarg = class {
9311
10417
  }
9312
10418
  return this.graph;
9313
10419
  }
10420
+ /** Throw when graph checkpoint pairing is requested on a non-file-backed graph backend. */
9314
10421
  assertGraphCheckpointSupported(operation) {
9315
10422
  if (!this.graph) return;
9316
10423
  if (!this.graph.supportsFileSnapshot()) {
9317
10424
  throw new GraphCheckpointNotSupportedError(this.graph.name, operation);
9318
10425
  }
9319
10426
  }
10427
+ /** Directory path where a graph file snapshot is stored alongside a memory checkpoint. */
9320
10428
  graphSnapshotDir(alongsidePath) {
9321
10429
  return `${alongsidePath}.graph`;
9322
10430
  }
10431
+ /** Close the graph provider before copying files; returns whether a close occurred. */
9323
10432
  async closeGraphForSnapshot() {
9324
10433
  if (!this.graph || !this.graph.supportsFileSnapshot()) return false;
9325
10434
  await this.graph.close();
9326
10435
  return true;
9327
10436
  }
10437
+ /** Copy the SQLite graph database next to a memory checkpoint directory. */
9328
10438
  async snapshotGraphAlongside(alongsidePath, operation) {
9329
10439
  if (!this.graph) return;
9330
10440
  if (!this.graph.supportsFileSnapshot()) {
@@ -9363,6 +10473,7 @@ var Wolbarg = class {
9363
10473
  await this.graph.open();
9364
10474
  }
9365
10475
  }
10476
+ /** Restore a graph file snapshot written by {@link snapshotGraphAlongside}. */
9366
10477
  async restoreGraphAlongside(alongsidePath, operation) {
9367
10478
  if (!this.graph) return;
9368
10479
  if (!this.graph.supportsFileSnapshot()) {
@@ -9386,22 +10497,32 @@ var Wolbarg = class {
9386
10497
  fs6.cpSync(src, dataPath, { recursive: true });
9387
10498
  }
9388
10499
  }
10500
+ /** Run graph traversal then re-hydrate stub records from storage when rows exist. */
9389
10501
  async hydrateRelated(memoryId, options) {
9390
10502
  const graph = this.requireGraph("getRelated");
9391
10503
  const related = await graph.getRelated(memoryId, options);
9392
10504
  const { storage, organization } = await this.requireReady();
10505
+ const rows = await Promise.all(
10506
+ related.map((stub) => storage.getMemoryById(stub.id, organization))
10507
+ );
9393
10508
  const out = [];
9394
- for (const stub of related) {
9395
- const row = await storage.getMemoryById(stub.id, organization);
9396
- if (row) {
9397
- out.push(toMemoryRecord(row));
9398
- } else {
9399
- out.push(stub);
9400
- }
10509
+ for (let i = 0; i < related.length; i += 1) {
10510
+ const stub = related[i];
10511
+ const row = rows[i];
10512
+ out.push(row ? toMemoryRecord(row) : stub);
9401
10513
  }
9402
10514
  return out;
9403
10515
  }
10516
+ /** Resolve the on-disk SQLite memory database path (throws for Postgres / `:memory:`). */
9404
10517
  requireMemoryDbPath() {
10518
+ if (this.storage && !(this.storage instanceof SqliteStorageProvider)) {
10519
+ throw new ConfigurationError(
10520
+ "This operation requires a file-backed SQLite database (checkpoints / export-import are not supported for PostgreSQL).",
10521
+ {
10522
+ suggestion: 'Use database: { provider: "sqlite", url: "./memory.db" }'
10523
+ }
10524
+ );
10525
+ }
9405
10526
  if (!this.memoryDbPath || this.memoryDbPath === ":memory:") {
9406
10527
  throw new ConfigurationError(
9407
10528
  "This operation requires a file-backed SQLite memory database (not :memory:).",
@@ -9444,6 +10565,7 @@ function commonTags(metadata) {
9444
10565
 
9445
10566
  // src/filters/types.ts
9446
10567
  var meta = {
10568
+ /** Field equals value (strict equality). */
9447
10569
  eq: (field, value) => ({
9448
10570
  field,
9449
10571
  op: { eq: value }
@@ -9527,16 +10649,23 @@ function createTelemetryProvider(config) {
9527
10649
  function wolbarg2(options) {
9528
10650
  return new Wolbarg(options);
9529
10651
  }
10652
+ var createWolbarg = wolbarg2;
9530
10653
 
9531
10654
  // src/keyword/index.ts
9532
10655
  var Bm25KeywordSearchProvider = class {
9533
10656
  name = "bm25";
9534
10657
  k1;
9535
10658
  b;
10659
+ /**
10660
+ * @param options - BM25 tuning parameters.
10661
+ * @param options.k1 - Term frequency saturation (default `1.2`).
10662
+ * @param options.b - Length normalization (default `0.75`).
10663
+ */
9536
10664
  constructor(options) {
9537
10665
  this.k1 = options?.k1 ?? 1.2;
9538
10666
  this.b = options?.b ?? 0.75;
9539
10667
  }
10668
+ /** @inheritdoc */
9540
10669
  async search(query, documents, topK) {
9541
10670
  if (documents.length === 0 || topK <= 0) {
9542
10671
  return [];
@@ -9630,7 +10759,14 @@ var HttpRerankerProvider = class {
9630
10759
  }));
9631
10760
  }
9632
10761
  const parsed = this.options.parseResults(body, documents);
9633
- return parsed.slice(0, topK);
10762
+ const hits = parsed.slice(0, topK);
10763
+ if (hits.length === 0) {
10764
+ return documents.slice(0, topK).map((d, i) => ({
10765
+ id: d.id,
10766
+ score: 1 - i / Math.max(documents.length, 1)
10767
+ }));
10768
+ }
10769
+ return hits;
9634
10770
  } catch {
9635
10771
  return documents.slice(0, topK).map((d, i) => ({
9636
10772
  id: d.id,
@@ -9815,21 +10951,31 @@ function tesseract() {
9815
10951
  return {
9816
10952
  name: "tesseract",
9817
10953
  async recognize(image) {
10954
+ let mod;
9818
10955
  try {
9819
- const mod = await import('tesseract.js');
9820
- const createWorker = mod.createWorker ?? mod.default?.createWorker;
9821
- if (!createWorker) {
9822
- return { text: "" };
9823
- }
9824
- const worker = await createWorker("eng");
9825
- try {
9826
- const result = await worker.recognize(image);
9827
- return { text: result.data.text.trim() };
9828
- } finally {
9829
- await worker.terminate();
9830
- }
9831
- } catch {
9832
- return { text: "" };
10956
+ mod = await import('tesseract.js');
10957
+ } catch (error) {
10958
+ throw new ConfigurationError(
10959
+ 'OCR provider "tesseract" requires the optional peer package "tesseract.js". Install it with: npm install tesseract.js',
10960
+ {
10961
+ cause: error instanceof Error ? error : void 0,
10962
+ suggestion: "npm install tesseract.js",
10963
+ operation: "recognize"
10964
+ }
10965
+ );
10966
+ }
10967
+ const createWorker = mod.createWorker ?? mod.default?.createWorker;
10968
+ if (!createWorker) {
10969
+ throw new ConfigurationError(
10970
+ 'OCR provider "tesseract" could not find tesseract.js.createWorker. Ensure your tesseract.js version is compatible.'
10971
+ );
10972
+ }
10973
+ const worker = await createWorker("eng");
10974
+ try {
10975
+ const result = await worker.recognize(image);
10976
+ return { text: result.data.text.trim() };
10977
+ } finally {
10978
+ await worker.terminate();
9833
10979
  }
9834
10980
  }
9835
10981
  };
@@ -9972,6 +11118,6 @@ function percentile(sorted, p) {
9972
11118
  return sorted[idx];
9973
11119
  }
9974
11120
 
9975
- export { CompressionError, ConfigurationError, DatabaseError, EmbeddingError, FixedChunkingStrategy, GraphCheckpointNotSupportedError, HeadingChunkingStrategy, InitializationError, LlmCompressionProvider, MarkdownChunkingStrategy, MemoryNotFoundError, Neo4jGraphProvider, NoopTelemetryProvider, ParagraphChunkingStrategy, PostgresStorageProvider, ProviderNotConfiguredError, SDK_VERSION, SentenceChunkingStrategy, SqliteCheckpointProvider, SqliteDatabaseProvider, SqliteEventDatabase, SqliteGraphProvider, SqliteStorageProvider, SqliteTelemetryProvider, StorageLockedError, TelemetryEmitter, ValidationError, Wolbarg, WolbargError, WolbargLogger, bgeReranker, bm25, cohereReranker, createChunkingStrategy, createCompressionProvider, createDatabaseProvider, createEmbeddingProvider, createLlmProvider, createStorageProvider, createTelemetryProvider, wolbarg2 as createWolbarg, crossEncoder, geminiEmbedding, geminiVision, jinaReranker, lmStudioEmbedding, meta, neo4jGraph, ollamaEmbedding, ollamaLlm, openRouterEmbedding, openRouterLlm, openaiCompatibleEmbedding, openaiCompatibleLlm, openaiEmbedding, openaiLlm, openaiReranker, openaiVision, postgres, postgresConfig, runBenchmark, sqlite, sqliteCheckpoint, sqliteConfig, sqliteGraph, sqliteTelemetry, summarizeBenchmark, tesseract, togetherEmbedding, vllmEmbedding, wolbarg, wrapOperationError };
11121
+ export { CompressionError, ConfigurationError, DatabaseError, EmbeddingError, FixedChunkingStrategy, GraphCheckpointNotSupportedError, HeadingChunkingStrategy, InitializationError, LlmCompressionProvider, MarkdownChunkingStrategy, MemoryNotFoundError, Neo4jGraphProvider, NoopTelemetryProvider, ParagraphChunkingStrategy, PostgresStorageProvider, ProviderNotConfiguredError, SDK_VERSION, SentenceChunkingStrategy, SqliteCheckpointProvider, SqliteDatabaseProvider, SqliteEventDatabase, SqliteGraphProvider, SqliteStorageProvider, SqliteTelemetryProvider, StorageLockedError, TelemetryEmitter, ValidationError, Wolbarg, WolbargError, WolbargLogger, bgeReranker, bm25, cohereReranker, createChunkingStrategy, createCompressionProvider, createDatabaseProvider, createEmbeddingProvider, createLlmProvider, createStorageProvider, createTelemetryProvider, createWolbarg, crossEncoder, geminiEmbedding, geminiVision, jinaReranker, lmStudioEmbedding, meta, neo4jGraph, ollamaEmbedding, ollamaLlm, openRouterEmbedding, openRouterLlm, openaiCompatibleEmbedding, openaiCompatibleLlm, openaiEmbedding, openaiLlm, openaiReranker, openaiVision, postgres, postgresConfig, runBenchmark, sqlite, sqliteCheckpoint, sqliteConfig, sqliteGraph, sqliteTelemetry, summarizeBenchmark, tesseract, togetherEmbedding, vllmEmbedding, wolbarg, wrapOperationError };
9976
11122
  //# sourceMappingURL=index.js.map
9977
11123
  //# sourceMappingURL=index.js.map