mnemocyte 0.0.7 → 0.0.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/client.ts","../src/errors.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/client.ts","../src/errors.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { drizzle } from "drizzle-orm/postgres-js";
2
2
  import postgres from "postgres";
3
- import { and, eq, gt, inArray, isNull, lt, sql } from "drizzle-orm";
3
+ import { and, eq, gt, inArray, isNotNull, isNull, lt, or, sql } from "drizzle-orm";
4
4
  import { customType, index, integer, jsonb, pgTable, real, text, timestamp } from "drizzle-orm/pg-core";
5
5
  //#region \0rolldown/runtime.js
6
6
  var __defProp = Object.defineProperty;
@@ -153,9 +153,35 @@ async function buildContext(options) {
153
153
  }
154
154
  //#endregion
155
155
  //#region src/errors.ts
156
+ /**
157
+ * Error type thrown by every public Mnemocyte API.
158
+ *
159
+ * Use the {@link MnemocyteError.code} property — not the message — to branch
160
+ * on failure modes, or call {@link isMnemocyteError} as a type guard when
161
+ * working with `unknown` errors.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * try {
166
+ * await client.recall({ entityId: "u1", query: "hi" });
167
+ * } catch (err) {
168
+ * if (isMnemocyteError(err) && err.code === "DB") {
169
+ * // handle database failure
170
+ * }
171
+ * throw err;
172
+ * }
173
+ * ```
174
+ */
156
175
  var MnemocyteError = class extends Error {
176
+ /** Stable, machine-readable failure code. See {@link MnemocyteErrorCode}. */
157
177
  code;
178
+ /** Underlying cause, if any (e.g. an original driver or embedder error). */
158
179
  cause;
180
+ /**
181
+ * @param message - Human-readable error message.
182
+ * @param code - Stable failure code; see {@link MnemocyteErrorCode}.
183
+ * @param cause - Optional underlying cause (driver error, network error, etc.).
184
+ */
159
185
  constructor(message, code, cause) {
160
186
  super(message);
161
187
  this.name = "MnemocyteError";
@@ -163,6 +189,15 @@ var MnemocyteError = class extends Error {
163
189
  this.cause = cause;
164
190
  }
165
191
  };
192
+ /**
193
+ * Type guard for {@link MnemocyteError}.
194
+ *
195
+ * Prefer this over `instanceof` when working across module boundaries or
196
+ * bundlers where multiple copies of the class may exist.
197
+ *
198
+ * @param error - Value to test (typically a caught `unknown`).
199
+ * @returns `true` if `error` is a {@link MnemocyteError}.
200
+ */
166
201
  function isMnemocyteError(error) {
167
202
  return error instanceof MnemocyteError;
168
203
  }
@@ -216,6 +251,176 @@ async function observe(config, backend, operation, metadata, action, successMeta
216
251
  throw error;
217
252
  }
218
253
  }
254
+ //#endregion
255
+ //#region src/resilience.ts
256
+ /**
257
+ * Default per-provider-call timeout. Disabled by default so that callers
258
+ * who do not configure {@link ProviderResilienceConfig.timeoutMs} are
259
+ * unaffected.
260
+ */
261
+ const DEFAULT_TIMEOUT_MS = 0;
262
+ /** Default number of retry attempts on transient provider failures. */
263
+ const DEFAULT_RETRIES = 0;
264
+ /** Default base backoff delay used between retry attempts. */
265
+ const DEFAULT_BASE_DELAY_MS = 100;
266
+ /** Default ceiling for retry backoff delay. */
267
+ const DEFAULT_MAX_DELAY_MS = 2e3;
268
+ /**
269
+ * Throw a {@link MnemocyteError} with code `"ABORTED"` if `signal` is
270
+ * already aborted. Safe to call with `undefined`.
271
+ */
272
+ function throwIfAborted(signal) {
273
+ if (signal?.aborted) throw new MnemocyteError("Operation was aborted.", "ABORTED", signal.reason);
274
+ }
275
+ /**
276
+ * Heuristic default for whether an error should be retried.
277
+ *
278
+ * Conservative by design: only retries on common transient failure
279
+ * indicators (network errors, 5xx-ish messages, `ECONN*` etc.). Never
280
+ * retries on validation, configuration, or {@link MnemocyteError} of
281
+ * code `"VALIDATION"` / `"CONFIG"` / `"ABORTED"`.
282
+ */
283
+ function defaultShouldRetry(error) {
284
+ if (error instanceof MnemocyteError) {
285
+ if (error.code === "VALIDATION" || error.code === "CONFIG" || error.code === "ABORTED") return false;
286
+ return true;
287
+ }
288
+ if (error instanceof Error) {
289
+ const message = error.message.toLowerCase();
290
+ if (message.includes("econn") || message.includes("etimedout") || message.includes("network") || message.includes("timeout") || message.includes("temporarily") || message.includes("rate limit") || message.includes("503") || message.includes("502") || message.includes("504") || message.includes("500")) return true;
291
+ }
292
+ return false;
293
+ }
294
+ /**
295
+ * Compute the backoff delay for the given retry attempt, using exponential
296
+ * growth capped at {@link ProviderResilienceConfig.maxDelayMs}.
297
+ *
298
+ * `attempt` is 1-indexed: the delay before the first retry uses
299
+ * `attempt = 1`, before the second retry `attempt = 2`, etc.
300
+ */
301
+ function computeDelayMs(attempt, baseDelayMs, maxDelayMs) {
302
+ const exponential = baseDelayMs * 2 ** (attempt - 1);
303
+ return Math.max(0, Math.min(maxDelayMs, exponential));
304
+ }
305
+ /**
306
+ * Promise-based `setTimeout` that rejects with an `"ABORTED"`
307
+ * {@link MnemocyteError} if `signal` aborts while waiting.
308
+ */
309
+ function delay(ms, signal) {
310
+ if (ms <= 0) {
311
+ throwIfAborted(signal);
312
+ return Promise.resolve();
313
+ }
314
+ return new Promise((resolve, reject) => {
315
+ const timer = setTimeout(() => {
316
+ signal?.removeEventListener("abort", onAbort);
317
+ resolve();
318
+ }, ms);
319
+ const onAbort = () => {
320
+ clearTimeout(timer);
321
+ reject(new MnemocyteError("Operation was aborted.", "ABORTED", signal?.reason));
322
+ };
323
+ if (signal) {
324
+ if (signal.aborted) {
325
+ clearTimeout(timer);
326
+ reject(new MnemocyteError("Operation was aborted.", "ABORTED", signal.reason));
327
+ return;
328
+ }
329
+ signal.addEventListener("abort", onAbort, { once: true });
330
+ }
331
+ });
332
+ }
333
+ /**
334
+ * Race `promise` against the configured `timeoutMs` and the caller's
335
+ * `signal`. Rejects with `"TIMEOUT"` or `"ABORTED"` respectively. Resolves
336
+ * with the original promise value if it settles first.
337
+ */
338
+ function withTimeout(promise, timeoutMs, signal) {
339
+ if (timeoutMs <= 0 && !signal) return promise;
340
+ return new Promise((resolve, reject) => {
341
+ let settled = false;
342
+ const timer = timeoutMs > 0 ? setTimeout(() => {
343
+ if (settled) return;
344
+ settled = true;
345
+ signal?.removeEventListener("abort", onAbort);
346
+ reject(new MnemocyteError(`Provider call timed out after ${timeoutMs}ms.`, "TIMEOUT"));
347
+ }, timeoutMs) : void 0;
348
+ const onAbort = () => {
349
+ if (settled) return;
350
+ settled = true;
351
+ if (timer) clearTimeout(timer);
352
+ reject(new MnemocyteError("Operation was aborted.", "ABORTED", signal?.reason));
353
+ };
354
+ if (signal) {
355
+ if (signal.aborted) {
356
+ if (timer) clearTimeout(timer);
357
+ settled = true;
358
+ reject(new MnemocyteError("Operation was aborted.", "ABORTED", signal.reason));
359
+ return;
360
+ }
361
+ signal.addEventListener("abort", onAbort, { once: true });
362
+ }
363
+ promise.then((value) => {
364
+ if (settled) return;
365
+ settled = true;
366
+ if (timer) clearTimeout(timer);
367
+ signal?.removeEventListener("abort", onAbort);
368
+ resolve(value);
369
+ }, (error) => {
370
+ if (settled) return;
371
+ settled = true;
372
+ if (timer) clearTimeout(timer);
373
+ signal?.removeEventListener("abort", onAbort);
374
+ reject(error);
375
+ });
376
+ });
377
+ }
378
+ /**
379
+ * Run `action` with timeout, retry, and abort handling derived from
380
+ * `options.resilience` and `options.signal`. Each retry attempt receives
381
+ * a fresh `AbortSignal` linked to the caller's signal — so callers may
382
+ * abort mid-retry and the in-flight attempt will cancel promptly if the
383
+ * underlying provider honours signals.
384
+ *
385
+ * Errors from {@link MnemocyteError} with code `"VALIDATION"` /
386
+ * `"CONFIG"` / `"ABORTED"` are never retried.
387
+ */
388
+ async function withResilience(action, options = {}) {
389
+ const config = options.resilience ?? {};
390
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
391
+ const retries = Math.max(0, config.retries ?? DEFAULT_RETRIES);
392
+ const baseDelayMs = Math.max(0, config.baseDelayMs ?? DEFAULT_BASE_DELAY_MS);
393
+ const maxDelayMs = Math.max(baseDelayMs, config.maxDelayMs ?? DEFAULT_MAX_DELAY_MS);
394
+ const shouldRetry = config.shouldRetry ?? defaultShouldRetry;
395
+ const externalSignal = options.signal;
396
+ throwIfAborted(externalSignal);
397
+ const maxAttempts = retries + 1;
398
+ let lastError;
399
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
400
+ throwIfAborted(externalSignal);
401
+ try {
402
+ return await withTimeout(action(externalSignal), timeoutMs, externalSignal);
403
+ } catch (error) {
404
+ lastError = error;
405
+ if (error instanceof MnemocyteError && error.code === "ABORTED") throw error;
406
+ if (attempt >= maxAttempts) throw error;
407
+ if (!shouldRetry(error, attempt)) throw error;
408
+ await delay(computeDelayMs(attempt, baseDelayMs, maxDelayMs), externalSignal);
409
+ }
410
+ }
411
+ throw lastError;
412
+ }
413
+ /**
414
+ * Ordering of {@link ImportanceLevel} from least to most important. Used
415
+ * by `prune({ maxImportance })` to compare a memory's importance against
416
+ * the caller-supplied ceiling.
417
+ */
418
+ const IMPORTANCE_RANK = {
419
+ low: 0,
420
+ normal: 1,
421
+ high: 2,
422
+ critical: 3
423
+ };
219
424
  function assertNonEmptyString(value, field) {
220
425
  if (value.trim().length === 0) throw new MnemocyteError(`${field} must be a non-empty string.`, "VALIDATION");
221
426
  }
@@ -272,11 +477,15 @@ function rowToMemory(row) {
272
477
  updatedAt: row.updatedAt
273
478
  };
274
479
  }
275
- async function embedOne(embedder, text) {
480
+ async function embedOne(embedder, text, options = {}) {
276
481
  let embeddings;
277
482
  try {
278
- embeddings = await embedder.embed([text]);
483
+ embeddings = await withResilience((signal) => signal === void 0 ? embedder.embed([text]) : embedder.embed([text], { signal }), {
484
+ ...options.signal === void 0 ? {} : { signal: options.signal },
485
+ ...options.resilience === void 0 ? {} : { resilience: options.resilience }
486
+ });
279
487
  } catch (error) {
488
+ if (error instanceof MnemocyteError && (error.code === "TIMEOUT" || error.code === "ABORTED")) throw error;
280
489
  throw new MnemocyteError("Failed to embed text.", "EMBEDDING", error);
281
490
  }
282
491
  const embedding = embeddings[0];
@@ -307,6 +516,36 @@ function contextInputToRecallInput(input) {
307
516
  ...input.includeExpired === void 0 ? {} : { includeExpired: input.includeExpired }
308
517
  };
309
518
  }
519
+ /**
520
+ * Validate a {@link PruneInput} and throw a `"VALIDATION"`
521
+ * {@link MnemocyteError} when no selector is supplied. Mnemocyte
522
+ * deliberately rejects `prune({})` to avoid surprise full-table deletes.
523
+ */
524
+ function validatePruneInput(input) {
525
+ if (input.entityId !== void 0) assertNonEmptyString(input.entityId, "entityId");
526
+ if (!(input.entityId !== void 0 || input.expired === true || input.superseded === true || input.createdBefore !== void 0 || input.notAccessedSince !== void 0 || input.types !== void 0 && input.types.length > 0 || input.tags !== void 0 && input.tags.length > 0 || input.maxImportance !== void 0)) throw new MnemocyteError("prune requires at least one selector (entityId, expired, superseded, createdBefore, notAccessedSince, types, tags, or maxImportance).", "VALIDATION");
527
+ }
528
+ /**
529
+ * Test whether `memory` matches a {@link PruneInput}. All specified
530
+ * selectors are AND-combined; unspecified selectors do not restrict.
531
+ *
532
+ * `now` is supplied by the caller so callers can share a consistent
533
+ * timestamp across a batch.
534
+ */
535
+ function matchesPruneFilter(memory, input, now) {
536
+ if (input.entityId !== void 0 && memory.entityId !== input.entityId) return false;
537
+ if (input.expired === true && !isExpired(memory, now)) return false;
538
+ if (input.superseded === true && memory.supersededBy === null) return false;
539
+ if (input.createdBefore !== void 0 && memory.createdAt.getTime() >= input.createdBefore.getTime()) return false;
540
+ if (input.notAccessedSince !== void 0) {
541
+ const last = memory.lastAccessedAt;
542
+ if (last !== null && last.getTime() >= input.notAccessedSince.getTime()) return false;
543
+ }
544
+ if (input.types !== void 0 && !input.types.includes(memory.type)) return false;
545
+ if (input.tags !== void 0 && input.tags.length > 0 && !input.tags.every((tag) => memory.tags.includes(tag))) return false;
546
+ if (input.maxImportance !== void 0 && IMPORTANCE_RANK[memory.importance] > IMPORTANCE_RANK[input.maxImportance]) return false;
547
+ return true;
548
+ }
310
549
  function matchesRecallFilter(memory, input, now) {
311
550
  if (memory.entityId !== input.entityId) return false;
312
551
  if (!input.includeSuperseded && memory.supersededBy !== null) return false;
@@ -427,7 +666,10 @@ function createInMemoryClient(config) {
427
666
  return observe(config, "in-memory", "remember", { entityId: input.entityId }, async () => {
428
667
  assertOpen();
429
668
  validateRememberInput(input);
430
- const embedding = await embedOne(config.embedder, input.content);
669
+ const embedding = await embedOne(config.embedder, input.content, {
670
+ ...input.signal === void 0 ? {} : { signal: input.signal },
671
+ ...config.provider === void 0 ? {} : { resilience: config.provider }
672
+ });
431
673
  const now = /* @__PURE__ */ new Date();
432
674
  const memory = {
433
675
  id: createId(),
@@ -474,7 +716,10 @@ function createInMemoryClient(config) {
474
716
  const minScore = input.minScore ?? config.defaults?.minScore ?? 0;
475
717
  assertLimit(limit);
476
718
  assertMinScore(minScore);
477
- const queryEmbedding = await embedOne(config.embedder, input.query);
719
+ const queryEmbedding = await embedOne(config.embedder, input.query, {
720
+ ...input.signal === void 0 ? {} : { signal: input.signal },
721
+ ...config.provider === void 0 ? {} : { resilience: config.provider }
722
+ });
478
723
  const now = /* @__PURE__ */ new Date();
479
724
  const scored = Array.from(memories.values()).filter((memory) => matchesRecallFilter(memory, input, now)).map((memory) => toScoredMemory(memory, Math.max(0, cosineSimilarity(memory.embedding, queryEmbedding)), lexicalScore(memory.content, input.query), input, config.retrieval)).filter((memory) => memory.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
480
725
  for (const memory of scored) {
@@ -493,7 +738,13 @@ function createInMemoryClient(config) {
493
738
  assertOpen();
494
739
  return buildContext({
495
740
  input,
496
- recall: (contextInput) => this.recall(contextInputToRecallInput(contextInput))
741
+ recall: (contextInput) => {
742
+ const recallInput = contextInputToRecallInput(contextInput);
743
+ return this.recall(input.signal === void 0 ? recallInput : {
744
+ ...recallInput,
745
+ signal: input.signal
746
+ });
747
+ }
497
748
  });
498
749
  });
499
750
  },
@@ -522,6 +773,22 @@ function createInMemoryClient(config) {
522
773
  return deleted;
523
774
  }, (count) => ({ count })).then(() => void 0);
524
775
  },
776
+ async prune(input) {
777
+ return observe(config, "in-memory", "prune", input.entityId === void 0 ? {} : { entityId: input.entityId }, async () => {
778
+ assertOpen();
779
+ validatePruneInput(input);
780
+ const now = /* @__PURE__ */ new Date();
781
+ const matched = [];
782
+ for (const memory of memories.values()) if (matchesPruneFilter(memory, input, now)) matched.push(memory.id);
783
+ const dryRun = input.dryRun === true;
784
+ if (!dryRun) for (const id of matched) memories.delete(id);
785
+ return {
786
+ matchedCount: matched.length,
787
+ deletedCount: dryRun ? 0 : matched.length,
788
+ dryRun
789
+ };
790
+ }, (result) => ({ count: result.deletedCount }));
791
+ },
525
792
  async stats(input) {
526
793
  return observe(config, "in-memory", "stats", input?.entityId ? { entityId: input.entityId } : {}, async () => {
527
794
  assertOpen();
@@ -582,6 +849,15 @@ async function deleteMemory(db, entityId, memoryId) {
582
849
  async function deleteMemoriesForEntity(db, entityId) {
583
850
  return (await db.delete(memoriesTable).where(eq(memoriesTable.entityId, entityId)).returning({ id: memoriesTable.id })).length;
584
851
  }
852
+ function pruneConditions(filter) {
853
+ return and(filter.entityId !== void 0 ? eq(memoriesTable.entityId, filter.entityId) : void 0, filter.expired ? and(isNotNull(memoriesTable.expiresAt), sql`${memoriesTable.expiresAt} <= now()`) : void 0, filter.superseded ? isNotNull(memoriesTable.supersededBy) : void 0, filter.createdBefore ? lt(memoriesTable.createdAt, filter.createdBefore) : void 0, filter.notAccessedSince ? or(isNull(memoriesTable.lastAccessedAt), lt(memoriesTable.lastAccessedAt, filter.notAccessedSince)) : void 0, filter.types && filter.types.length > 0 ? inArray(memoriesTable.type, [...filter.types]) : void 0, filter.tags && filter.tags.length > 0 ? sql`${memoriesTable.tags} @> ${filter.tags}` : void 0, filter.maxImportanceLevels && filter.maxImportanceLevels.length > 0 ? inArray(memoriesTable.importance, [...filter.maxImportanceLevels]) : void 0);
854
+ }
855
+ async function countPruneMatches(db, filter) {
856
+ return (await db.select({ count: sql`count(*)::int` }).from(memoriesTable).where(pruneConditions(filter)))[0]?.count ?? 0;
857
+ }
858
+ async function pruneMemories(db, filter) {
859
+ return (await db.delete(memoriesTable).where(pruneConditions(filter)).returning({ id: memoriesTable.id })).length;
860
+ }
585
861
  async function markMemoryAccessed(db, memoryIds) {
586
862
  if (memoryIds.length === 0) return;
587
863
  await db.update(memoriesTable).set({
@@ -670,7 +946,10 @@ async function lexicalSearch(db, input) {
670
946
  //#endregion
671
947
  //#region src/retrieval/index.ts
672
948
  async function hybridRecall(input) {
673
- const queryEmbedding = await embedOne(input.embedder, input.input.query);
949
+ const queryEmbedding = await embedOne(input.embedder, input.input.query, {
950
+ ...input.signal === void 0 ? {} : { signal: input.signal },
951
+ ...input.resilience === void 0 ? {} : { resilience: input.resilience }
952
+ });
674
953
  const candidateMultiplier = input.retrieval?.candidateMultiplier ?? 3;
675
954
  const candidateLimit = Math.max(input.limit, input.limit * candidateMultiplier);
676
955
  const [vectorRows, lexicalRows] = await Promise.all([vectorSearch(input.db, {
@@ -703,6 +982,27 @@ async function hybridRecall(input) {
703
982
  }
704
983
  //#endregion
705
984
  //#region src/memory/postgres.ts
985
+ const IMPORTANCE_LEVELS = [
986
+ "low",
987
+ "normal",
988
+ "high",
989
+ "critical"
990
+ ];
991
+ function importanceCeilingLevels(max) {
992
+ return IMPORTANCE_LEVELS.filter((level) => IMPORTANCE_RANK[level] <= IMPORTANCE_RANK[max]);
993
+ }
994
+ function toPruneFilter(input) {
995
+ const filter = {};
996
+ if (input.entityId !== void 0) filter.entityId = input.entityId;
997
+ if (input.expired === true) filter.expired = true;
998
+ if (input.superseded === true) filter.superseded = true;
999
+ if (input.createdBefore !== void 0) filter.createdBefore = input.createdBefore;
1000
+ if (input.notAccessedSince !== void 0) filter.notAccessedSince = input.notAccessedSince;
1001
+ if (input.types !== void 0 && input.types.length > 0) filter.types = input.types;
1002
+ if (input.tags !== void 0 && input.tags.length > 0) filter.tags = input.tags;
1003
+ if (input.maxImportance !== void 0) filter.maxImportanceLevels = importanceCeilingLevels(input.maxImportance);
1004
+ return filter;
1005
+ }
706
1006
  function createPostgresClient(config, handle) {
707
1007
  let closed = false;
708
1008
  function assertOpen() {
@@ -712,7 +1012,10 @@ function createPostgresClient(config, handle) {
712
1012
  return observe(config, "postgres", "remember", { entityId: input.entityId }, async () => {
713
1013
  assertOpen();
714
1014
  validateRememberInput(input);
715
- const embedding = await embedOne(config.embedder, input.content);
1015
+ const embedding = await embedOne(config.embedder, input.content, {
1016
+ ...input.signal === void 0 ? {} : { signal: input.signal },
1017
+ ...config.provider === void 0 ? {} : { resilience: config.provider }
1018
+ });
716
1019
  const now = /* @__PURE__ */ new Date();
717
1020
  return rowToMemory(await insertMemory(handle.db, {
718
1021
  id: createId(),
@@ -764,7 +1067,9 @@ function createPostgresClient(config, handle) {
764
1067
  input,
765
1068
  limit,
766
1069
  minScore,
767
- retrieval: config.retrieval
1070
+ retrieval: config.retrieval,
1071
+ ...input.signal === void 0 ? {} : { signal: input.signal },
1072
+ ...config.provider === void 0 ? {} : { resilience: config.provider }
768
1073
  });
769
1074
  }, (result) => ({ count: result.length }));
770
1075
  },
@@ -773,7 +1078,13 @@ function createPostgresClient(config, handle) {
773
1078
  assertOpen();
774
1079
  return buildContext({
775
1080
  input,
776
- recall: (contextInput) => this.recall(contextInputToRecallInput(contextInput))
1081
+ recall: (contextInput) => {
1082
+ const recallInput = contextInputToRecallInput(contextInput);
1083
+ return this.recall(input.signal === void 0 ? recallInput : {
1084
+ ...recallInput,
1085
+ signal: input.signal
1086
+ });
1087
+ }
777
1088
  });
778
1089
  });
779
1090
  },
@@ -795,6 +1106,24 @@ function createPostgresClient(config, handle) {
795
1106
  await Promise.all([deleteMemoriesForEntity(handle.db, input.entityId), deleteEventsForEntity(handle.db, input.entityId)]);
796
1107
  });
797
1108
  },
1109
+ async prune(input) {
1110
+ return observe(config, "postgres", "prune", input.entityId === void 0 ? {} : { entityId: input.entityId }, async () => {
1111
+ assertOpen();
1112
+ validatePruneInput(input);
1113
+ const filter = toPruneFilter(input);
1114
+ if (input.dryRun === true) return {
1115
+ matchedCount: await countPruneMatches(handle.db, filter),
1116
+ deletedCount: 0,
1117
+ dryRun: true
1118
+ };
1119
+ const deletedCount = await pruneMemories(handle.db, filter);
1120
+ return {
1121
+ matchedCount: deletedCount,
1122
+ deletedCount,
1123
+ dryRun: false
1124
+ };
1125
+ }, (result) => ({ count: result.deletedCount }));
1126
+ },
798
1127
  async stats(input) {
799
1128
  return observe(config, "postgres", "stats", input?.entityId ? { entityId: input.entityId } : {}, async () => {
800
1129
  assertOpen();
@@ -833,6 +1162,31 @@ function createPostgresClient(config, handle) {
833
1162
  }
834
1163
  //#endregion
835
1164
  //#region src/client.ts
1165
+ /**
1166
+ * Create a Mnemocyte client.
1167
+ *
1168
+ * When {@link MnemocyteConfig.databaseUrl} is provided, a Postgres-backed
1169
+ * client is returned (requires the `pgvector` extension and the bundled
1170
+ * migration applied). Otherwise an in-memory client is returned, intended
1171
+ * for tests, demos, and short-lived processes.
1172
+ *
1173
+ * The returned client owns any underlying resources (e.g. the Postgres
1174
+ * connection pool). Call {@link MnemocyteClient.close} when finished to
1175
+ * release them.
1176
+ *
1177
+ * @param config - Client configuration. {@link MnemocyteConfig.embedder} is required.
1178
+ * @returns A {@link MnemocyteClient} instance.
1179
+ * @throws {MnemocyteError} With code `"CONFIG"` if `embedder` is missing or invalid,
1180
+ * or `"VALIDATION"` if `databaseUrl` is provided but empty.
1181
+ *
1182
+ * @example Postgres-backed client
1183
+ * ```ts
1184
+ * const client = createMnemocyte({
1185
+ * databaseUrl: process.env.DATABASE_URL,
1186
+ * embedder,
1187
+ * });
1188
+ * ```
1189
+ */
836
1190
  function createMnemocyte(config) {
837
1191
  assertEmbedder(config.embedder);
838
1192
  if (config.databaseUrl) {