@mastra/mongodb 1.11.1 → 1.12.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/docs/SKILL.md +1 -1
  3. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  4. package/dist/docs/references/docs-memory-working-memory.md +2 -0
  5. package/dist/docs/references/docs-rag-retrieval.md +2 -0
  6. package/dist/docs/references/docs-rag-vector-databases.md +2 -0
  7. package/dist/docs/references/reference-storage-mongodb.md +2 -0
  8. package/dist/docs/references/reference-vectors-mongodb.md +2 -0
  9. package/dist/index.cjs +304 -4
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +305 -5
  12. package/dist/index.js.map +1 -1
  13. package/dist/storage/domains/background-tasks/index.d.ts +13 -0
  14. package/dist/storage/domains/background-tasks/index.d.ts.map +1 -1
  15. package/dist/storage/domains/experiments/index.d.ts +22 -1
  16. package/dist/storage/domains/experiments/index.d.ts.map +1 -1
  17. package/dist/storage/domains/memory/index.d.ts +14 -1
  18. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  19. package/dist/storage/domains/notifications/index.d.ts +5 -1
  20. package/dist/storage/domains/notifications/index.d.ts.map +1 -1
  21. package/dist/storage/domains/observability/index.d.ts +5 -1
  22. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  23. package/dist/storage/domains/schedules/index.d.ts +11 -1
  24. package/dist/storage/domains/schedules/index.d.ts.map +1 -1
  25. package/dist/storage/domains/scores/index.d.ts +5 -1
  26. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  27. package/dist/storage/domains/workflows/index.d.ts +8 -1
  28. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  29. package/dist/storage/retention.d.ts +121 -0
  30. package/dist/storage/retention.d.ts.map +1 -0
  31. package/dist/storage/types.d.ts +21 -0
  32. package/dist/storage/types.d.ts.map +1 -1
  33. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @mastra/mongodb
2
2
 
3
+ ## 1.12.0-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed `createExperiment` in the MongoDB store persisting `agentVersion` as `null` regardless of the input. `listExperiments` already accepts an `agentVersion` filter, but rows created by this backend would never match it. New experiments now round-trip `agentVersion` end-to-end. ([#18769](https://github.com/mastra-ai/mastra/pull/18769))
8
+
9
+ - Updated dependencies [[`6a61846`](https://github.com/mastra-ai/mastra/commit/6a61846eeda29fb714549b70f1bee2bf6b141c44)]:
10
+ - @mastra/core@1.49.0-alpha.4
11
+
12
+ ## 1.12.0-alpha.0
13
+
14
+ ### Minor Changes
15
+
16
+ - Added storage retention support to MongoDB. When you set a `retention` config, `MongoDBStore` can prune old documents from every growth domain it implements: `memory` (threads, messages, resources by `createdAt`), `observability` (spans by `startedAt`), `scores` (by `createdAt`), `workflows` (run snapshots by `updatedAt`), `backgroundTasks` (by `completedAt`, so in-flight tasks are never pruned), `experiments` (whole runs by `completedAt`, results cascade with their parent — transactional on replica sets), `notifications` (by `createdAt`), and `schedules` fire history (by `actual_fire_at`). ([#18798](https://github.com/mastra-ai/mastra/pull/18798))
17
+
18
+ Deletes run in batches via bounded `find(_id)` + `deleteMany` pairs (bounded, resumable, and cancellable) so they stay safe on large collections. Anchor-field indexes are created lazily on the first `prune()` call — never at init — so deployments that don't configure retention pay no extra index overhead. `prune()` only deletes documents; WiredTiger reuses the freed space for subsequent writes.
19
+
20
+ ```typescript
21
+ const storage = new MongoDBStore({
22
+ id: 'mastra-storage',
23
+ uri: process.env.MONGODB_URI,
24
+ dbName: 'mastra',
25
+ retention: {
26
+ memory: { messages: { maxAge: '30d' } },
27
+ observability: { spans: { maxAge: '7d' } },
28
+ },
29
+ });
30
+
31
+ await storage.prune();
32
+ ```
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [[`700619b`](https://github.com/mastra-ai/mastra/commit/700619b61d572e592cbaaf758121d168844ca4d2), [`0c3d4bc`](https://github.com/mastra-ai/mastra/commit/0c3d4bcae13ea3699d379403e6f350d5cf4efe9f), [`17369b2`](https://github.com/mastra-ai/mastra/commit/17369b25250561e9ed994ae509be1d15bfb33bcb), [`bcae929`](https://github.com/mastra-ai/mastra/commit/bcae929945cbf265bd9f327cc715ecafa072b5b9), [`b33822e`](https://github.com/mastra-ai/mastra/commit/b33822e8d470884954b02f7b0745407ee4ef74b1), [`d5c11e3`](https://github.com/mastra-ai/mastra/commit/d5c11e3ba5045969caa7272a7bd1fd141c93ab6c), [`ff80671`](https://github.com/mastra-ai/mastra/commit/ff8067185e208b27198b4e5b71803013175c3643), [`dab1257`](https://github.com/mastra-ai/mastra/commit/dab1257b64e4ed576dc5038bb7a3f7072338bc9f), [`705ff39`](https://github.com/mastra-ai/mastra/commit/705ff3969e57214ff2fdaf3815d751dd558886ed), [`e6fbd5b`](https://github.com/mastra-ai/mastra/commit/e6fbd5bfdc28e92c0c0433f29aa1bc152d3430f6), [`6f2026c`](https://github.com/mastra-ai/mastra/commit/6f2026cdf114ff1e21e49133ca774ec7d5085059), [`f890eda`](https://github.com/mastra-ai/mastra/commit/f890eda2c8a2ae83d9b30bc6d85842f93b6c266b)]:
37
+ - @mastra/core@1.49.0-alpha.3
38
+
3
39
  ## 1.11.1
4
40
 
5
41
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-mongodb
3
3
  description: Documentation for @mastra/mongodb. Use when working with @mastra/mongodb APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/mongodb"
6
- version: "1.11.1"
6
+ version: "1.12.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.11.1",
2
+ "version": "1.12.0-alpha.1",
3
3
  "package": "@mastra/mongodb",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Working Memory
2
4
 
3
5
  While [message history](https://mastra.ai/docs/memory/message-history) and [semantic recall](https://mastra.ai/docs/memory/semantic-recall) help agents remember conversations, working memory allows them to maintain persistent information about users across interactions.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Retrieval in RAG systems
2
4
 
3
5
  After storing embeddings, you need to retrieve relevant chunks to answer user queries.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # Storing embeddings in a vector database
2
4
 
3
5
  After generating embeddings, you need to store them in a database that supports vector similarity search. Mastra provides a consistent interface for storing and querying embeddings across various vector databases.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # MongoDB storage
2
4
 
3
5
  The MongoDB storage implementation provides a scalable storage solution using MongoDB databases with support for both document storage and vector operations.
@@ -1,3 +1,5 @@
1
+ > Discover all available pages from the documentation index: https://mastra.ai/llms.txt
2
+
1
3
  # MongoDB vector store
2
4
 
3
5
  The `MongoDBVector` class provides vector search using [MongoDB Atlas Vector Search](https://www.mongodb.com/docs/atlas/atlas-vector-search/). It enables efficient similarity search and metadata filtering within your MongoDB collections.
package/dist/index.cjs CHANGED
@@ -15,7 +15,7 @@ var skills = require('@mastra/core/storage/domains/skills');
15
15
 
16
16
  // package.json
17
17
  var package_default = {
18
- version: "1.11.1"};
18
+ version: "1.12.0-alpha.1"};
19
19
  var MongoDBFilterTranslator = class extends filter.BaseFilterTranslator {
20
20
  getSupportedOperators() {
21
21
  return {
@@ -1621,6 +1621,126 @@ var MongoDBAgentsStorage = class _MongoDBAgentsStorage extends storage.AgentsSto
1621
1621
  return result;
1622
1622
  }
1623
1623
  };
1624
+ var DEFAULT_PRUNE_BATCH_SIZE = 1e3;
1625
+ async function ensureAnchorIndex(connector, target, logger) {
1626
+ if (!target.indexed) return;
1627
+ try {
1628
+ const collection = await connector.getCollection(target.table);
1629
+ await collection.createIndex({ [target.column]: -1 });
1630
+ } catch (error) {
1631
+ logger?.warn?.(`Failed to ensure retention index on ${target.table}(${target.column}):`, error);
1632
+ }
1633
+ }
1634
+ async function sleep(ms, signal) {
1635
+ if (ms <= 0 || signal?.aborted) return;
1636
+ await new Promise((resolve) => {
1637
+ const timer = setTimeout(() => {
1638
+ signal?.removeEventListener("abort", onAbort);
1639
+ resolve();
1640
+ }, ms);
1641
+ const onAbort = () => {
1642
+ clearTimeout(timer);
1643
+ resolve();
1644
+ };
1645
+ signal?.addEventListener("abort", onAbort, { once: true });
1646
+ });
1647
+ }
1648
+ function cutoffFor(policy, encoding, now = Date.now()) {
1649
+ const cutoffMs = now - storage.parseDuration(policy.maxAge);
1650
+ if (encoding === "epoch-ms") return cutoffMs;
1651
+ if (encoding === "iso") return new Date(cutoffMs).toISOString();
1652
+ return new Date(cutoffMs);
1653
+ }
1654
+ async function pruneCollectionBatch({
1655
+ connector,
1656
+ table,
1657
+ column,
1658
+ cutoff,
1659
+ limit
1660
+ }) {
1661
+ const collection = await connector.getCollection(table);
1662
+ const docs = await collection.find({ [column]: { $lt: cutoff } }).project({ _id: 1 }).limit(limit).toArray();
1663
+ if (docs.length === 0) return 0;
1664
+ const result = await collection.deleteMany({ _id: { $in: docs.map((doc) => doc._id) } });
1665
+ return result.deletedCount;
1666
+ }
1667
+ async function runBatchedDelete({
1668
+ deleteBatch,
1669
+ batchSize,
1670
+ options
1671
+ }) {
1672
+ if (!Number.isSafeInteger(batchSize) || batchSize <= 0) {
1673
+ throw new Error(`retention batchSize must be a positive integer; received ${batchSize}`);
1674
+ }
1675
+ let deleted = 0;
1676
+ let batches = 0;
1677
+ while (true) {
1678
+ if (options?.signal?.aborted) return { deleted, done: false };
1679
+ if (options?.maxBatches !== void 0 && batches >= options.maxBatches) return { deleted, done: false };
1680
+ let limit = batchSize;
1681
+ if (options?.maxRows !== void 0) {
1682
+ const remaining = options.maxRows - deleted;
1683
+ if (remaining <= 0) return { deleted, done: false };
1684
+ limit = Math.min(limit, remaining);
1685
+ }
1686
+ const affected = await deleteBatch(limit);
1687
+ deleted += affected;
1688
+ batches += 1;
1689
+ if (affected < limit) return { deleted, done: true };
1690
+ if (options?.pauseMs) {
1691
+ await sleep(options.pauseMs, options.signal);
1692
+ }
1693
+ }
1694
+ }
1695
+ async function runPrune({
1696
+ connector,
1697
+ domain,
1698
+ targets,
1699
+ options,
1700
+ logger
1701
+ }) {
1702
+ const results = [];
1703
+ const now = Date.now();
1704
+ for (const target of targets) {
1705
+ if (options?.signal?.aborted) {
1706
+ results.push({ domain, table: target.table, deleted: 0, done: false });
1707
+ continue;
1708
+ }
1709
+ await ensureAnchorIndex(connector, target, logger);
1710
+ const cutoff = cutoffFor(target.policy, target.encoding, now);
1711
+ const batchSize = target.policy.batchSize ?? DEFAULT_PRUNE_BATCH_SIZE;
1712
+ const { deleted, done } = await runBatchedDelete({
1713
+ deleteBatch: (limit) => pruneCollectionBatch({ connector, table: target.table, column: target.column, cutoff, limit }),
1714
+ batchSize,
1715
+ options
1716
+ });
1717
+ results.push({ domain, table: target.table, deleted, done });
1718
+ }
1719
+ return results;
1720
+ }
1721
+ function resolveTargets({
1722
+ policies,
1723
+ descriptor,
1724
+ order,
1725
+ encodings
1726
+ }) {
1727
+ const targets = [];
1728
+ for (const key of order) {
1729
+ const policy = policies[key];
1730
+ const entry = descriptor[key];
1731
+ if (!policy || !entry) continue;
1732
+ targets.push({
1733
+ table: entry.table,
1734
+ column: entry.column,
1735
+ encoding: encodings?.[key] ?? (entry.anchorType === "epoch-ms" ? "epoch-ms" : "date"),
1736
+ indexed: entry.indexed ?? true,
1737
+ policy
1738
+ });
1739
+ }
1740
+ return targets;
1741
+ }
1742
+
1743
+ // src/storage/domains/background-tasks/index.ts
1624
1744
  function toDoc(task) {
1625
1745
  return {
1626
1746
  id: task.id,
@@ -1672,6 +1792,14 @@ var BackgroundTasksStorageMongoDB = class _BackgroundTasksStorageMongoDB extends
1672
1792
  #skipDefaultIndexes;
1673
1793
  #indexes;
1674
1794
  static MANAGED_COLLECTIONS = [storage.TABLE_BACKGROUND_TASKS];
1795
+ /**
1796
+ * Anchor is `completedAt`, so age is measured from task completion; it stays
1797
+ * `null` while a task is in-flight, and `null $lt cutoff` never matches, so
1798
+ * pending/running tasks are never pruned regardless of age.
1799
+ */
1800
+ static retentionTables = {
1801
+ backgroundTasks: { table: storage.TABLE_BACKGROUND_TASKS, column: "completedAt", indexed: true }
1802
+ };
1675
1803
  constructor(config) {
1676
1804
  super();
1677
1805
  this.#connector = resolveMongoDBConfig(config);
@@ -1683,6 +1811,20 @@ var BackgroundTasksStorageMongoDB = class _BackgroundTasksStorageMongoDB extends
1683
1811
  async getCollection() {
1684
1812
  return this.#connector.getCollection(storage.TABLE_BACKGROUND_TASKS);
1685
1813
  }
1814
+ /**
1815
+ * Delete tasks completed longer ago than the policy's `maxAge`, batched.
1816
+ * `completedAt` is stored as an ISO-8601 string (see `toDoc`), so the cutoff
1817
+ * uses the `iso` encoding — lexicographic `$lt` on ISO strings is time-ordered.
1818
+ */
1819
+ async prune(policies, options) {
1820
+ const targets = resolveTargets({
1821
+ policies,
1822
+ descriptor: _BackgroundTasksStorageMongoDB.retentionTables,
1823
+ order: ["backgroundTasks"],
1824
+ encodings: { backgroundTasks: "iso" }
1825
+ });
1826
+ return runPrune({ connector: this.#connector, domain: "backgroundTasks", targets, options, logger: this.logger });
1827
+ }
1686
1828
  getDefaultIndexDefinitions() {
1687
1829
  return [
1688
1830
  { collection: storage.TABLE_BACKGROUND_TASKS, keys: { id: 1 }, options: { unique: true } },
@@ -3083,6 +3225,15 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3083
3225
  #skipDefaultIndexes;
3084
3226
  #indexes;
3085
3227
  static MANAGED_COLLECTIONS = [storage.TABLE_EXPERIMENTS, storage.TABLE_EXPERIMENT_RESULTS];
3228
+ /**
3229
+ * Experiments prune as whole units: an experiment and its results are only
3230
+ * deleted together, once the experiment itself is old (anchored on the
3231
+ * parent's `completedAt`, a BSON date that stays `null` while running).
3232
+ * `results` is intentionally not an independent retention key.
3233
+ */
3234
+ static retentionTables = {
3235
+ experiments: { table: storage.TABLE_EXPERIMENTS, column: "completedAt", indexed: true }
3236
+ };
3086
3237
  constructor(config) {
3087
3238
  super();
3088
3239
  this.#connector = resolveMongoDBConfig(config);
@@ -3094,6 +3245,57 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3094
3245
  async getCollection(name) {
3095
3246
  return this.#connector.getCollection(name);
3096
3247
  }
3248
+ /**
3249
+ * Prune whole experiments older than the `experiments` policy's `maxAge`.
3250
+ *
3251
+ * Each batch collects up to `batchSize` aged experiment ids
3252
+ * (`completedAt < cutoff`; BSON type bracketing means `null` — still
3253
+ * running — never matches), then deletes their `experiment_results` rows and
3254
+ * the experiment rows for exactly that id set inside
3255
+ * `connector.withTransaction()` — atomic on replica sets, sequential
3256
+ * children-first on standalone — mirroring `deleteExperiment`. Hitting
3257
+ * `maxBatches`/`maxRows` or the abort signal between batches therefore never
3258
+ * leaves a run hollow (parent kept, results gone). Bounds count whole
3259
+ * experiments, not rows.
3260
+ */
3261
+ async prune(policies, options) {
3262
+ const policy = policies["experiments"];
3263
+ if (!policy || options?.signal?.aborted) {
3264
+ return policy ? [
3265
+ { domain: "experiments", table: storage.TABLE_EXPERIMENT_RESULTS, deleted: 0, done: false },
3266
+ { domain: "experiments", table: storage.TABLE_EXPERIMENTS, deleted: 0, done: false }
3267
+ ] : [];
3268
+ }
3269
+ await ensureAnchorIndex(
3270
+ this.#connector,
3271
+ { table: storage.TABLE_EXPERIMENTS, column: "completedAt", indexed: true },
3272
+ this.logger
3273
+ );
3274
+ const cutoff = cutoffFor(policy, "date");
3275
+ const batchSize = policy.batchSize ?? DEFAULT_PRUNE_BATCH_SIZE;
3276
+ const experimentsCollection = await this.getCollection(storage.TABLE_EXPERIMENTS);
3277
+ const resultsCollection = await this.getCollection(storage.TABLE_EXPERIMENT_RESULTS);
3278
+ let childDeleted = 0;
3279
+ const parent = await runBatchedDelete({
3280
+ deleteBatch: async (limit) => {
3281
+ const docs = await experimentsCollection.find({ completedAt: { $lt: cutoff } }).project({ id: 1 }).limit(limit).toArray();
3282
+ if (docs.length === 0) return 0;
3283
+ const ids = docs.map((doc) => doc.id);
3284
+ return this.#connector.withTransaction(async (session) => {
3285
+ const children = await resultsCollection.deleteMany({ experimentId: { $in: ids } }, { session });
3286
+ childDeleted += children.deletedCount;
3287
+ const parents = await experimentsCollection.deleteMany({ id: { $in: ids } }, { session });
3288
+ return parents.deletedCount;
3289
+ });
3290
+ },
3291
+ batchSize,
3292
+ options
3293
+ });
3294
+ return [
3295
+ { domain: "experiments", table: storage.TABLE_EXPERIMENT_RESULTS, deleted: childDeleted, done: parent.done },
3296
+ { domain: "experiments", table: storage.TABLE_EXPERIMENTS, deleted: parent.deleted, done: parent.done }
3297
+ ];
3298
+ }
3097
3299
  // -------------------------------------------------------------------------
3098
3300
  // Index Management
3099
3301
  // -------------------------------------------------------------------------
@@ -3160,7 +3362,7 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3160
3362
  succeededCount: 0,
3161
3363
  failedCount: 0,
3162
3364
  skippedCount: 0,
3163
- agentVersion: null,
3365
+ agentVersion: input.agentVersion ?? null,
3164
3366
  startedAt: null,
3165
3367
  completedAt: null,
3166
3368
  createdAt: now,
@@ -3185,7 +3387,7 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3185
3387
  succeededCount: 0,
3186
3388
  failedCount: 0,
3187
3389
  skippedCount: 0,
3188
- agentVersion: null,
3390
+ agentVersion: input.agentVersion ?? null,
3189
3391
  startedAt: null,
3190
3392
  completedAt: null,
3191
3393
  createdAt: now,
@@ -4691,6 +4893,15 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
4691
4893
  #indexes;
4692
4894
  /** Collections managed by this domain */
4693
4895
  static MANAGED_COLLECTIONS = [storage.TABLE_THREADS, storage.TABLE_MESSAGES, storage.TABLE_RESOURCES, OM_TABLE];
4896
+ /**
4897
+ * Retention-eligible collections. The observational-memory collection is
4898
+ * excluded: it has no timestamp anchor to age on. All anchors are BSON dates.
4899
+ */
4900
+ static retentionTables = {
4901
+ messages: { table: storage.TABLE_MESSAGES, column: "createdAt", indexed: true },
4902
+ resources: { table: storage.TABLE_RESOURCES, column: "createdAt", indexed: true },
4903
+ threads: { table: storage.TABLE_THREADS, column: "createdAt", indexed: true }
4904
+ };
4694
4905
  constructor(config) {
4695
4906
  super();
4696
4907
  this.#connector = resolveMongoDBConfig(config);
@@ -4706,6 +4917,21 @@ var MemoryStorageMongoDB = class _MemoryStorageMongoDB extends storage.MemorySto
4706
4917
  await this.createDefaultIndexes();
4707
4918
  await this.createCustomIndexes();
4708
4919
  }
4920
+ /**
4921
+ * Delete memory rows older than each table's `maxAge`, batched. Order is
4922
+ * messages → resources → threads so child rows never outlive the delete of
4923
+ * their thread. Like `deleteThread()`, this does not sweep vector-store
4924
+ * embeddings — semantic-recall vectors live in a separate vector store the
4925
+ * memory domain cannot reach; cleaning those up is the operator's concern.
4926
+ */
4927
+ async prune(policies, options) {
4928
+ const targets = resolveTargets({
4929
+ policies,
4930
+ descriptor: _MemoryStorageMongoDB.retentionTables,
4931
+ order: ["messages", "resources", "threads"]
4932
+ });
4933
+ return runPrune({ connector: this.#connector, domain: "memory", targets, options, logger: this.logger });
4934
+ }
4709
4935
  /**
4710
4936
  * Returns default index definitions for the memory domain collections.
4711
4937
  */
@@ -6659,6 +6885,10 @@ var NotificationsMongoDB = class _NotificationsMongoDB extends storage.Notificat
6659
6885
  #skipDefaultIndexes;
6660
6886
  #indexes;
6661
6887
  static MANAGED_COLLECTIONS = [storage.TABLE_NOTIFICATIONS];
6888
+ /** Anchor is the notification's creation time, stored as a BSON date. */
6889
+ static retentionTables = {
6890
+ notifications: { table: storage.TABLE_NOTIFICATIONS, column: "createdAt", indexed: true }
6891
+ };
6662
6892
  constructor(config) {
6663
6893
  super();
6664
6894
  this.#connector = resolveMongoDBConfig(config);
@@ -6670,6 +6900,15 @@ var NotificationsMongoDB = class _NotificationsMongoDB extends storage.Notificat
6670
6900
  async getCollection() {
6671
6901
  return this.#connector.getCollection(storage.TABLE_NOTIFICATIONS);
6672
6902
  }
6903
+ /** Delete notifications older than the policy's `maxAge`, batched. */
6904
+ async prune(policies, options) {
6905
+ const targets = resolveTargets({
6906
+ policies,
6907
+ descriptor: _NotificationsMongoDB.retentionTables,
6908
+ order: ["notifications"]
6909
+ });
6910
+ return runPrune({ connector: this.#connector, domain: "notifications", targets, options, logger: this.logger });
6911
+ }
6673
6912
  async init() {
6674
6913
  await this.createDefaultIndexes();
6675
6914
  await this.createCustomIndexes();
@@ -6862,6 +7101,10 @@ var ObservabilityMongoDB = class _ObservabilityMongoDB extends storage.Observabi
6862
7101
  #indexes;
6863
7102
  /** Collections managed by this domain */
6864
7103
  static MANAGED_COLLECTIONS = [storage.TABLE_SPANS];
7104
+ /** Anchor is the span's start time, stored as a BSON date. */
7105
+ static retentionTables = {
7106
+ spans: { table: storage.TABLE_SPANS, column: "startedAt", indexed: true }
7107
+ };
6865
7108
  constructor(config) {
6866
7109
  super();
6867
7110
  this.#connector = resolveMongoDBConfig(config);
@@ -6873,6 +7116,15 @@ var ObservabilityMongoDB = class _ObservabilityMongoDB extends storage.Observabi
6873
7116
  async getCollection(name) {
6874
7117
  return this.#connector.getCollection(name);
6875
7118
  }
7119
+ /** Delete spans older than the `spans` policy's `maxAge`, batched. */
7120
+ async prune(policies, options) {
7121
+ const targets = resolveTargets({
7122
+ policies,
7123
+ descriptor: _ObservabilityMongoDB.retentionTables,
7124
+ order: ["spans"]
7125
+ });
7126
+ return runPrune({ connector: this.#connector, domain: "observability", targets, options, logger: this.logger });
7127
+ }
6876
7128
  /**
6877
7129
  * Returns default index definitions for the observability domain collections.
6878
7130
  * These indexes optimize common query patterns for span and trace lookups.
@@ -8287,6 +8539,16 @@ var SchedulesMongoDB = class _SchedulesMongoDB extends storage.SchedulesStorage
8287
8539
  #skipDefaultIndexes;
8288
8540
  #indexes;
8289
8541
  static MANAGED_COLLECTIONS = [storage.TABLE_SCHEDULES, storage.TABLE_SCHEDULE_TRIGGERS];
8542
+ /**
8543
+ * Only trigger (fire-history) rows are retention-eligible — schedule
8544
+ * definitions are config, not growth. Anchor `actual_fire_at` is stored as a
8545
+ * raw epoch-ms number, so `anchorType: 'epoch-ms'` keeps the cutoff numeric.
8546
+ * The default composite index `(schedule_id, actual_fire_at)` can't serve a
8547
+ * bare `actual_fire_at` range scan, hence the lazy single-field anchor index.
8548
+ */
8549
+ static retentionTables = {
8550
+ triggers: { table: storage.TABLE_SCHEDULE_TRIGGERS, column: "actual_fire_at", anchorType: "epoch-ms", indexed: true }
8551
+ };
8290
8552
  constructor(config) {
8291
8553
  super();
8292
8554
  this.#connector = resolveMongoDBConfig(config);
@@ -8301,6 +8563,15 @@ var SchedulesMongoDB = class _SchedulesMongoDB extends storage.SchedulesStorage
8301
8563
  getTriggersCollection() {
8302
8564
  return this.#connector.getCollection(storage.TABLE_SCHEDULE_TRIGGERS);
8303
8565
  }
8566
+ /** Delete trigger (fire-history) rows older than the `triggers` policy's `maxAge`, batched. */
8567
+ async prune(policies, options) {
8568
+ const targets = resolveTargets({
8569
+ policies,
8570
+ descriptor: _SchedulesMongoDB.retentionTables,
8571
+ order: ["triggers"]
8572
+ });
8573
+ return runPrune({ connector: this.#connector, domain: "schedules", targets, options, logger: this.logger });
8574
+ }
8304
8575
  getDefaultIndexDefinitions() {
8305
8576
  return [
8306
8577
  { collection: storage.TABLE_SCHEDULES, keys: { id: 1 }, options: { unique: true } },
@@ -9004,6 +9275,10 @@ var ScoresStorageMongoDB = class _ScoresStorageMongoDB extends storage.ScoresSto
9004
9275
  #indexes;
9005
9276
  /** Collections managed by this domain */
9006
9277
  static MANAGED_COLLECTIONS = [storage.TABLE_SCORERS];
9278
+ /** Anchor is the score row's creation time, stored as a BSON date. */
9279
+ static retentionTables = {
9280
+ scorers: { table: storage.TABLE_SCORERS, column: "createdAt", indexed: true }
9281
+ };
9007
9282
  constructor(config) {
9008
9283
  super();
9009
9284
  this.#connector = resolveMongoDBConfig(config);
@@ -9015,6 +9290,15 @@ var ScoresStorageMongoDB = class _ScoresStorageMongoDB extends storage.ScoresSto
9015
9290
  async getCollection(name) {
9016
9291
  return this.#connector.getCollection(name);
9017
9292
  }
9293
+ /** Delete scorer results older than the `scorers` policy's `maxAge`, batched. */
9294
+ async prune(policies, options) {
9295
+ const targets = resolveTargets({
9296
+ policies,
9297
+ descriptor: _ScoresStorageMongoDB.retentionTables,
9298
+ order: ["scorers"]
9299
+ });
9300
+ return runPrune({ connector: this.#connector, domain: "scores", targets, options, logger: this.logger });
9301
+ }
9018
9302
  /**
9019
9303
  * Returns default index definitions for the scores domain collections.
9020
9304
  * These indexes optimize common query patterns for score lookups.
@@ -9982,6 +10266,13 @@ var WorkflowsStorageMongoDB = class _WorkflowsStorageMongoDB extends storage.Wor
9982
10266
  #indexes;
9983
10267
  /** Collections managed by this domain */
9984
10268
  static MANAGED_COLLECTIONS = [storage.TABLE_WORKFLOW_SNAPSHOT];
10269
+ /**
10270
+ * Anchor is `updatedAt` (BSON date), so the policy reads as inactivity:
10271
+ * a run is pruned only after its snapshot has not been touched for `maxAge`.
10272
+ */
10273
+ static retentionTables = {
10274
+ workflowSnapshot: { table: storage.TABLE_WORKFLOW_SNAPSHOT, column: "updatedAt", indexed: true }
10275
+ };
9985
10276
  constructor(config) {
9986
10277
  super();
9987
10278
  this.#connector = resolveMongoDBConfig(config);
@@ -9990,6 +10281,15 @@ var WorkflowsStorageMongoDB = class _WorkflowsStorageMongoDB extends storage.Wor
9990
10281
  (idx) => _WorkflowsStorageMongoDB.MANAGED_COLLECTIONS.includes(idx.collection)
9991
10282
  );
9992
10283
  }
10284
+ /** Delete workflow snapshots idle for longer than the policy's `maxAge`, batched. */
10285
+ async prune(policies, options) {
10286
+ const targets = resolveTargets({
10287
+ policies,
10288
+ descriptor: _WorkflowsStorageMongoDB.retentionTables,
10289
+ order: ["workflowSnapshot"]
10290
+ });
10291
+ return runPrune({ connector: this.#connector, domain: "workflows", targets, options, logger: this.logger });
10292
+ }
9993
10293
  supportsConcurrentUpdates() {
9994
10294
  return true;
9995
10295
  }
@@ -10997,7 +11297,7 @@ var MongoDBStore = class extends storage.MastraCompositeStore {
10997
11297
  #connector;
10998
11298
  stores;
10999
11299
  constructor(config) {
11000
- super({ id: config.id, name: "MongoDBStore", disableInit: config.disableInit });
11300
+ super({ id: config.id, name: "MongoDBStore", disableInit: config.disableInit, retention: config.retention });
11001
11301
  this.#connector = resolveMongoDBConfig(config);
11002
11302
  const domainConfig = {
11003
11303
  connector: this.#connector,