@mastra/mongodb 1.9.0 → 1.9.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @mastra/mongodb
2
2
 
3
+ ## 1.9.1-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Added notification inbox storage support for MongoDB stores. ([#17241](https://github.com/mastra-ai/mastra/pull/17241))
8
+
9
+ ```ts
10
+ import { MongoDBStore } from '@mastra/mongodb';
11
+
12
+ const storage = new MongoDBStore({ url: process.env.MONGODB_URI!, dbName: 'mastra' });
13
+ ```
14
+
15
+ Agents using this store can persist thread-scoped notification inbox records for notification signals.
16
+
17
+ - Updated dependencies [[`c973db4`](https://github.com/mastra-ai/mastra/commit/c973db428df1b564ff0c35d4b2a90e8f4f1e13fd), [`552285e`](https://github.com/mastra-ai/mastra/commit/552285e5af43cfc680a0972032cab8de8776c6a0), [`77e686c`](https://github.com/mastra-ai/mastra/commit/77e686c264e493e99ae5024e4dfe3ea5d5a09718), [`ece8dba`](https://github.com/mastra-ai/mastra/commit/ece8dba7ec1a5089eee8c33167cd762bfa91e509), [`e751af2`](https://github.com/mastra-ai/mastra/commit/e751af219433fbf4c7035b2d771b4c9ec8813b05), [`e2a8380`](https://github.com/mastra-ai/mastra/commit/e2a838017a7657850404c1e94c70d79ffdc6f14a), [`be3f1cd`](https://github.com/mastra-ai/mastra/commit/be3f1cd81f0e2a649e8eac15a024d542d814aef8), [`a34d9db`](https://github.com/mastra-ai/mastra/commit/a34d9dbc39fedb722f271318e9355ecee70489ab)]:
18
+ - @mastra/core@1.39.0-alpha.0
19
+
3
20
  ## 1.9.0
4
21
 
5
22
  ### Minor 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.9.0"
6
+ version: "1.9.1-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.9.0",
2
+ "version": "1.9.1-alpha.0",
3
3
  "package": "@mastra/mongodb",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -26,7 +26,7 @@ const agent = new Agent({
26
26
  id: 'personal-assistant',
27
27
  name: 'PersonalAssistant',
28
28
  instructions: 'You are a helpful personal assistant.',
29
- model: 'openai/gpt-5.4',
29
+ model: 'openai/gpt-5.5',
30
30
  memory: new Memory({
31
31
  options: {
32
32
  workingMemory: {
@@ -130,7 +130,7 @@ Resource-scoped working memory requires specific storage adapters that support t
130
130
 
131
131
  ## Custom templates
132
132
 
133
- Templates guide the agent on what information to track and update in working memory. While a default template is used if none is provided, you'll typically want to define a custom template tailored to your agent's specific use case to ensure it remembers the most relevant information.
133
+ Templates guide the agent on what information to track and update in working memory. While a default template is used if none is provided, you'll typically want to define a custom template tailored to your agent's specific use case to ensure it remembers the most relevant information. For threads shared by multiple users, see [Multi-user threads](https://mastra.ai/docs/memory/multi-user-threads).
134
134
 
135
135
  Here's an example of a custom template. In this example the agent will store the users name, location, timezone, etc as soon as the user sends a message containing any of the info:
136
136
 
@@ -393,6 +393,33 @@ const response = await agent.generate('What do you know about me?', {
393
393
  })
394
394
  ```
395
395
 
396
+ ## Opt in to state signals (experimental)
397
+
398
+ By default, working memory reaches the model as part of the system message. You can opt into delivering it as a [state signal](https://mastra.ai/docs/agents/signals) instead by setting `useStateSignals: true`:
399
+
400
+ ```typescript
401
+ const memory = new Memory({
402
+ storage: new LibSQLStore({ url: 'file:./mastra.db' }),
403
+ options: {
404
+ workingMemory: {
405
+ enabled: true,
406
+ template: '# User\n- name:\n- location:',
407
+ useStateSignals: true, // experimental: deliver as state signal
408
+ },
409
+ },
410
+ })
411
+ ```
412
+
413
+ What changes:
414
+
415
+ - **Storage is identical.** The same resource/thread `workingMemory` field is read and written.
416
+ - **The tool is the same shape, exposed under a new name.** Writes still flow through the same underlying tool; on this path it is registered as `setWorkingMemory` (instead of `updateWorkingMemory`). The rename keeps legacy strip filters from removing tool-call parts so they persist as a normal audit trail and the next model step picks the new value up automatically.
417
+ - **Delivery only.** Instead of folding into the system prompt, `Memory` auto-attaches a `WorkingMemoryStateProcessor` that emits the current working memory as a `state` signal with `stateId: 'working-memory'`.
418
+
419
+ You inherit the standard state-signal benefits: thread-scoped tracking metadata, `cacheKey` dedup so identical snapshots are only emitted once, and `contextWindow.hasSnapshot` re-injection when an older snapshot rolls out of the window.
420
+
421
+ The default (`useStateSignals: false`) keeps the existing system-message behavior unchanged. `useStateSignals` is not supported with template working memory `version: 'vnext'`.
422
+
396
423
  ## Examples
397
424
 
398
425
  - [Working memory with template](https://github.com/mastra-ai/mastra/tree/main/examples/memory-with-template)
@@ -272,7 +272,7 @@ import { PGVECTOR_PROMPT } from '@mastra/pg'
272
272
  export const ragAgent = new Agent({
273
273
  id: 'rag-agent',
274
274
  name: 'RAG Agent',
275
- model: 'openai/gpt-5.4',
275
+ model: 'openai/gpt-5.5',
276
276
  instructions: `
277
277
  Process queries using the provided context. Structure responses to be concise and relevant.
278
278
  ${PGVECTOR_PROMPT}
@@ -289,7 +289,7 @@ import { PINECONE_PROMPT } from '@mastra/pinecone'
289
289
  export const ragAgent = new Agent({
290
290
  id: 'rag-agent',
291
291
  name: 'RAG Agent',
292
- model: 'openai/gpt-5.4',
292
+ model: 'openai/gpt-5.5',
293
293
  instructions: `
294
294
  Process queries using the provided context. Structure responses to be concise and relevant.
295
295
  ${PINECONE_PROMPT}
@@ -306,7 +306,7 @@ import { QDRANT_PROMPT } from '@mastra/qdrant'
306
306
  export const ragAgent = new Agent({
307
307
  id: 'rag-agent',
308
308
  name: 'RAG Agent',
309
- model: 'openai/gpt-5.4',
309
+ model: 'openai/gpt-5.5',
310
310
  instructions: `
311
311
  Process queries using the provided context. Structure responses to be concise and relevant.
312
312
  ${QDRANT_PROMPT}
@@ -323,7 +323,7 @@ import { CHROMA_PROMPT } from '@mastra/chroma'
323
323
  export const ragAgent = new Agent({
324
324
  id: 'rag-agent',
325
325
  name: 'RAG Agent',
326
- model: 'openai/gpt-5.4',
326
+ model: 'openai/gpt-5.5',
327
327
  instructions: `
328
328
  Process queries using the provided context. Structure responses to be concise and relevant.
329
329
  ${CHROMA_PROMPT}
@@ -340,7 +340,7 @@ import { ASTRA_PROMPT } from '@mastra/astra'
340
340
  export const ragAgent = new Agent({
341
341
  id: 'rag-agent',
342
342
  name: 'RAG Agent',
343
- model: 'openai/gpt-5.4',
343
+ model: 'openai/gpt-5.5',
344
344
  instructions: `
345
345
  Process queries using the provided context. Structure responses to be concise and relevant.
346
346
  ${ASTRA_PROMPT}
@@ -357,7 +357,7 @@ import { LIBSQL_PROMPT } from '@mastra/libsql'
357
357
  export const ragAgent = new Agent({
358
358
  id: 'rag-agent',
359
359
  name: 'RAG Agent',
360
- model: 'openai/gpt-5.4',
360
+ model: 'openai/gpt-5.5',
361
361
  instructions: `
362
362
  Process queries using the provided context. Structure responses to be concise and relevant.
363
363
  ${LIBSQL_PROMPT}
@@ -374,7 +374,7 @@ import { UPSTASH_PROMPT } from '@mastra/upstash'
374
374
  export const ragAgent = new Agent({
375
375
  id: 'rag-agent',
376
376
  name: 'RAG Agent',
377
- model: 'openai/gpt-5.4',
377
+ model: 'openai/gpt-5.5',
378
378
  instructions: `
379
379
  Process queries using the provided context. Structure responses to be concise and relevant.
380
380
  ${UPSTASH_PROMPT}
@@ -391,7 +391,7 @@ import { VECTORIZE_PROMPT } from '@mastra/vectorize'
391
391
  export const ragAgent = new Agent({
392
392
  id: 'rag-agent',
393
393
  name: 'RAG Agent',
394
- model: 'openai/gpt-5.4',
394
+ model: 'openai/gpt-5.5',
395
395
  instructions: `
396
396
  Process queries using the provided context. Structure responses to be concise and relevant.
397
397
  ${VECTORIZE_PROMPT}
@@ -408,7 +408,7 @@ import { MONGODB_PROMPT } from '@mastra/mongodb'
408
408
  export const ragAgent = new Agent({
409
409
  id: 'rag-agent',
410
410
  name: 'RAG Agent',
411
- model: 'openai/gpt-5.4',
411
+ model: 'openai/gpt-5.5',
412
412
  instructions: `
413
413
  Process queries using the provided context. Structure responses to be concise and relevant.
414
414
  ${MONGODB_PROMPT}
@@ -425,7 +425,7 @@ import { OPENSEARCH_PROMPT } from '@mastra/opensearch'
425
425
  export const ragAgent = new Agent({
426
426
  id: 'rag-agent',
427
427
  name: 'RAG Agent',
428
- model: 'openai/gpt-5.4',
428
+ model: 'openai/gpt-5.5',
429
429
  instructions: `
430
430
  Process queries using the provided context. Structure responses to be concise and relevant.
431
431
  ${OPENSEARCH_PROMPT}
@@ -442,7 +442,7 @@ import { S3VECTORS_PROMPT } from '@mastra/s3vectors'
442
442
  export const ragAgent = new Agent({
443
443
  id: 'rag-agent',
444
444
  name: 'RAG Agent',
445
- model: 'openai/gpt-5.4',
445
+ model: 'openai/gpt-5.5',
446
446
  instructions: `
447
447
  Process queries using the provided context. Structure responses to be concise and relevant.
448
448
  ${S3VECTORS_PROMPT}
@@ -472,7 +472,10 @@ const initialResults = await pgVector.query({
472
472
  })
473
473
 
474
474
  // Create a relevance scorer
475
- const relevanceProvider = new MastraAgentRelevanceScorer('relevance-scorer', 'openai/gpt-5.4')
475
+ const relevanceProvider = new MastraAgentRelevanceScorer(
476
+ 'relevance-scorer',
477
+ 'openai/gpt-5.5',
478
+ )
476
479
 
477
480
  // Re-rank the results
478
481
  const rerankedResults = await rerank({
@@ -97,6 +97,9 @@ The storage implementation handles collection creation and management automatica
97
97
  - `mastra_traces`: Stores telemetry and tracing data
98
98
  - `mastra_scorers`: Stores scoring and evaluation data
99
99
  - `mastra_resources`: Stores resource working memory data
100
+ - `mastra_notifications`: Stores notification inbox records and delivery metadata
101
+
102
+ `MongoDBStore` exposes notification storage through `getStore('notifications')`.
100
103
 
101
104
  ### Initialization
102
105
 
@@ -206,7 +209,7 @@ export const mongodbAgent = new Agent({
206
209
  name: 'mongodb-agent',
207
210
  instructions:
208
211
  'You are an AI agent with the ability to automatically recall memories from previous interactions.',
209
- model: 'openai/gpt-5.4',
212
+ model: 'openai/gpt-5.5',
210
213
  memory: new Memory({
211
214
  storage: new MongoDBStore({
212
215
  uri: process.env.MONGODB_URI!,
@@ -263,7 +263,7 @@ export const mongodbAgent = new Agent({
263
263
  name: 'mongodb-agent',
264
264
  instructions:
265
265
  'You are an AI agent with the ability to automatically recall memories from previous interactions.',
266
- model: 'openai/gpt-5.4',
266
+ model: 'openai/gpt-5.5',
267
267
  memory: new Memory({
268
268
  storage: new MongoDBStore({
269
269
  id: 'mongodb-storage',
package/dist/index.cjs CHANGED
@@ -14,7 +14,7 @@ var evals = require('@mastra/core/evals');
14
14
 
15
15
  // package.json
16
16
  var package_default = {
17
- version: "1.9.0"};
17
+ version: "1.9.1-alpha.0"};
18
18
  var MongoDBFilterTranslator = class extends filter.BaseFilterTranslator {
19
19
  getSupportedOperators() {
20
20
  return {
@@ -6287,6 +6287,294 @@ ${unreflectedContent}` : bufferedReflection;
6287
6287
  }
6288
6288
  }
6289
6289
  };
6290
+ var statusTimestamp = (status, now) => {
6291
+ if (status === "delivered") return { deliveredAt: now };
6292
+ if (status === "seen") return { seenAt: now };
6293
+ if (status === "dismissed") return { dismissedAt: now };
6294
+ if (status === "archived") return { archivedAt: now };
6295
+ if (status === "discarded") return { discardedAt: now };
6296
+ return {};
6297
+ };
6298
+ var cloneDate = (value) => value ? new Date(value) : void 0;
6299
+ var cloneValue = (value) => value === void 0 ? void 0 : structuredClone(value);
6300
+ var cloneRecord = (record) => ({
6301
+ ...record,
6302
+ createdAt: new Date(record.createdAt),
6303
+ updatedAt: new Date(record.updatedAt),
6304
+ deliveredAt: cloneDate(record.deliveredAt),
6305
+ seenAt: cloneDate(record.seenAt),
6306
+ dismissedAt: cloneDate(record.dismissedAt),
6307
+ archivedAt: cloneDate(record.archivedAt),
6308
+ discardedAt: cloneDate(record.discardedAt),
6309
+ deliverAt: cloneDate(record.deliverAt),
6310
+ summaryAt: cloneDate(record.summaryAt),
6311
+ lastDeliveryAttemptAt: cloneDate(record.lastDeliveryAttemptAt),
6312
+ payload: cloneValue(record.payload),
6313
+ attributes: cloneValue(record.attributes),
6314
+ metadata: cloneValue(record.metadata)
6315
+ });
6316
+ function rowToNotification(row) {
6317
+ return {
6318
+ id: row.id,
6319
+ threadId: row.threadId,
6320
+ source: row.source,
6321
+ kind: row.kind,
6322
+ priority: row.priority,
6323
+ status: row.status,
6324
+ summary: row.summary,
6325
+ payload: row.payload ?? void 0,
6326
+ resourceId: row.resourceId ?? void 0,
6327
+ agentId: row.agentId ?? void 0,
6328
+ sourceId: row.sourceId ?? void 0,
6329
+ dedupeKey: row.dedupeKey ?? void 0,
6330
+ coalesceKey: row.coalesceKey ?? void 0,
6331
+ coalescedCount: Number(row.coalescedCount ?? 1),
6332
+ attributes: row.attributes,
6333
+ createdAt: row.createdAt instanceof Date ? row.createdAt : new Date(row.createdAt),
6334
+ updatedAt: row.updatedAt instanceof Date ? row.updatedAt : new Date(row.updatedAt),
6335
+ deliveredAt: row.deliveredAt ? row.deliveredAt instanceof Date ? row.deliveredAt : new Date(row.deliveredAt) : void 0,
6336
+ seenAt: row.seenAt ? row.seenAt instanceof Date ? row.seenAt : new Date(row.seenAt) : void 0,
6337
+ dismissedAt: row.dismissedAt ? row.dismissedAt instanceof Date ? row.dismissedAt : new Date(row.dismissedAt) : void 0,
6338
+ archivedAt: row.archivedAt ? row.archivedAt instanceof Date ? row.archivedAt : new Date(row.archivedAt) : void 0,
6339
+ discardedAt: row.discardedAt ? row.discardedAt instanceof Date ? row.discardedAt : new Date(row.discardedAt) : void 0,
6340
+ deliverAt: row.deliverAt ? row.deliverAt instanceof Date ? row.deliverAt : new Date(row.deliverAt) : void 0,
6341
+ summaryAt: row.summaryAt ? row.summaryAt instanceof Date ? row.summaryAt : new Date(row.summaryAt) : void 0,
6342
+ deliveryReason: row.deliveryReason ?? void 0,
6343
+ deliveryAttempts: Number(row.deliveryAttempts ?? 0),
6344
+ lastDeliveryAttemptAt: row.lastDeliveryAttemptAt ? row.lastDeliveryAttemptAt instanceof Date ? row.lastDeliveryAttemptAt : new Date(row.lastDeliveryAttemptAt) : void 0,
6345
+ lastDeliveryError: row.lastDeliveryError ?? void 0,
6346
+ deliveredSignalId: row.deliveredSignalId ?? void 0,
6347
+ summarySignalId: row.summarySignalId ?? void 0,
6348
+ metadata: row.metadata ?? void 0
6349
+ };
6350
+ }
6351
+ function recordToDocument(record) {
6352
+ return {
6353
+ ...record,
6354
+ payload: record.payload ?? null,
6355
+ resourceId: record.resourceId ?? null,
6356
+ agentId: record.agentId ?? null,
6357
+ sourceId: record.sourceId ?? null,
6358
+ dedupeKey: record.dedupeKey ?? null,
6359
+ coalesceKey: record.coalesceKey ?? null,
6360
+ attributes: record.attributes ?? null,
6361
+ deliveredAt: record.deliveredAt ?? null,
6362
+ seenAt: record.seenAt ?? null,
6363
+ dismissedAt: record.dismissedAt ?? null,
6364
+ archivedAt: record.archivedAt ?? null,
6365
+ discardedAt: record.discardedAt ?? null,
6366
+ deliverAt: record.deliverAt ?? null,
6367
+ summaryAt: record.summaryAt ?? null,
6368
+ deliveryReason: record.deliveryReason ?? null,
6369
+ lastDeliveryAttemptAt: record.lastDeliveryAttemptAt ?? null,
6370
+ lastDeliveryError: record.lastDeliveryError ?? null,
6371
+ deliveredSignalId: record.deliveredSignalId ?? null,
6372
+ summarySignalId: record.summarySignalId ?? null,
6373
+ metadata: record.metadata ?? null
6374
+ };
6375
+ }
6376
+ var NotificationsMongoDB = class _NotificationsMongoDB extends storage.NotificationsStorage {
6377
+ #connector;
6378
+ #skipDefaultIndexes;
6379
+ #indexes;
6380
+ static MANAGED_COLLECTIONS = [storage.TABLE_NOTIFICATIONS];
6381
+ constructor(config) {
6382
+ super();
6383
+ this.#connector = resolveMongoDBConfig(config);
6384
+ this.#skipDefaultIndexes = config.skipDefaultIndexes;
6385
+ this.#indexes = config.indexes?.filter(
6386
+ (idx) => _NotificationsMongoDB.MANAGED_COLLECTIONS.includes(idx.collection)
6387
+ );
6388
+ }
6389
+ async getCollection() {
6390
+ return this.#connector.getCollection(storage.TABLE_NOTIFICATIONS);
6391
+ }
6392
+ async init() {
6393
+ await this.createDefaultIndexes();
6394
+ await this.createCustomIndexes();
6395
+ }
6396
+ getDefaultIndexDefinitions() {
6397
+ return [
6398
+ { collection: storage.TABLE_NOTIFICATIONS, keys: { threadId: 1, id: 1 }, options: { unique: true } },
6399
+ { collection: storage.TABLE_NOTIFICATIONS, keys: { threadId: 1, status: 1, updatedAt: -1 } },
6400
+ {
6401
+ collection: storage.TABLE_NOTIFICATIONS,
6402
+ keys: { threadId: 1, source: 1, kind: 1, status: 1, agentId: 1, resourceId: 1, dedupeKey: 1, coalesceKey: 1 }
6403
+ },
6404
+ { collection: storage.TABLE_NOTIFICATIONS, keys: { status: 1, deliverAt: 1, summaryAt: 1 } }
6405
+ ];
6406
+ }
6407
+ async createDefaultIndexes() {
6408
+ if (this.#skipDefaultIndexes) return;
6409
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
6410
+ try {
6411
+ const collection = await this.#connector.getCollection(indexDef.collection);
6412
+ await collection.createIndex(indexDef.keys, indexDef.options);
6413
+ } catch (error) {
6414
+ this.logger?.warn?.(`Failed to create index on ${indexDef.collection}:`, error);
6415
+ }
6416
+ }
6417
+ }
6418
+ async createCustomIndexes() {
6419
+ if (!this.#indexes || this.#indexes.length === 0) return;
6420
+ for (const indexDef of this.#indexes) {
6421
+ try {
6422
+ const collection = await this.#connector.getCollection(indexDef.collection);
6423
+ await collection.createIndex(indexDef.keys, indexDef.options);
6424
+ } catch (error) {
6425
+ this.logger?.warn?.(`Failed to create custom index on ${indexDef.collection}:`, error);
6426
+ }
6427
+ }
6428
+ }
6429
+ async dangerouslyClearAll() {
6430
+ const collection = await this.getCollection();
6431
+ await collection.deleteMany({});
6432
+ }
6433
+ async createNotification(input) {
6434
+ const existing = await this.findCoalescable(input);
6435
+ const collection = await this.getCollection();
6436
+ if (existing) {
6437
+ const now2 = /* @__PURE__ */ new Date();
6438
+ const attributes = input.attributes ? { ...cloneValue(existing.attributes), ...cloneValue(input.attributes) } : cloneValue(existing.attributes);
6439
+ const metadata = input.metadata ? { ...cloneValue(existing.metadata), ...cloneValue(input.metadata) } : cloneValue(existing.metadata);
6440
+ await collection.updateOne(
6441
+ { threadId: existing.threadId, id: existing.id },
6442
+ {
6443
+ $set: {
6444
+ summary: input.summary,
6445
+ payload: cloneValue(input.payload ?? existing.payload) ?? null,
6446
+ priority: input.priority ?? existing.priority,
6447
+ attributes: attributes ?? null,
6448
+ updatedAt: now2,
6449
+ deliverAt: input.deliverAt ?? existing.deliverAt ?? null,
6450
+ summaryAt: input.summaryAt ?? existing.summaryAt ?? null,
6451
+ deliveryReason: input.deliveryReason ?? existing.deliveryReason ?? null,
6452
+ metadata: metadata ?? null
6453
+ },
6454
+ $inc: { coalescedCount: 1 }
6455
+ }
6456
+ );
6457
+ const updated = await this.getNotification({ threadId: existing.threadId, id: existing.id });
6458
+ if (!updated) throw new Error(`Notification ${existing.id} was not found for thread ${existing.threadId}`);
6459
+ return updated;
6460
+ }
6461
+ const now = input.createdAt ?? /* @__PURE__ */ new Date();
6462
+ const record = {
6463
+ id: input.id ?? crypto$1.randomUUID(),
6464
+ threadId: input.threadId,
6465
+ source: input.source,
6466
+ kind: input.kind,
6467
+ priority: input.priority ?? "medium",
6468
+ status: "pending",
6469
+ summary: input.summary,
6470
+ payload: cloneValue(input.payload),
6471
+ resourceId: input.resourceId,
6472
+ agentId: input.agentId,
6473
+ sourceId: input.sourceId,
6474
+ dedupeKey: input.dedupeKey,
6475
+ coalesceKey: input.coalesceKey,
6476
+ coalescedCount: 1,
6477
+ attributes: cloneValue(input.attributes),
6478
+ createdAt: now,
6479
+ updatedAt: now,
6480
+ deliverAt: input.deliverAt,
6481
+ summaryAt: input.summaryAt,
6482
+ deliveryReason: input.deliveryReason,
6483
+ deliveryAttempts: 0,
6484
+ metadata: cloneValue(input.metadata)
6485
+ };
6486
+ await collection.insertOne(recordToDocument(record));
6487
+ return cloneRecord(record);
6488
+ }
6489
+ async listNotifications(input) {
6490
+ const filter = { threadId: input.threadId };
6491
+ if (input.status) filter.status = Array.isArray(input.status) ? { $in: input.status } : input.status;
6492
+ if (input.priority) filter.priority = Array.isArray(input.priority) ? { $in: input.priority } : input.priority;
6493
+ if (input.source) filter.source = input.source;
6494
+ if (input.resourceId) filter.resourceId = input.resourceId;
6495
+ if (input.agentId) filter.agentId = input.agentId;
6496
+ if (input.search) {
6497
+ const search = input.search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6498
+ filter.$or = [{ summary: { $regex: search, $options: "i" } }, { kind: { $regex: search, $options: "i" } }];
6499
+ }
6500
+ const collection = await this.getCollection();
6501
+ const cursor = collection.find(filter).sort({ updatedAt: -1 });
6502
+ if (input.limit) cursor.limit(input.limit);
6503
+ const rows = await cursor.toArray();
6504
+ return rows.map((row) => rowToNotification(row));
6505
+ }
6506
+ async listDueNotifications(input) {
6507
+ const filter = {
6508
+ status: "pending",
6509
+ $or: [{ deliverAt: { $ne: null, $lte: input.now } }, { summaryAt: { $ne: null, $lte: input.now } }]
6510
+ };
6511
+ if (input.agentId) filter.agentId = input.agentId;
6512
+ if (input.resourceId) filter.resourceId = input.resourceId;
6513
+ const collection = await this.getCollection();
6514
+ const rows = await collection.find(filter).sort({ updatedAt: 1 }).toArray();
6515
+ const due = rows.map((row) => rowToNotification(row)).sort((a, b) => dueTime(a) - dueTime(b) || a.updatedAt.getTime() - b.updatedAt.getTime());
6516
+ return due.slice(0, input.limit ?? due.length).map(cloneRecord);
6517
+ }
6518
+ async getNotification(input) {
6519
+ const collection = await this.getCollection();
6520
+ const row = await collection.findOne({ threadId: input.threadId, id: input.id });
6521
+ return row ? rowToNotification(row) : null;
6522
+ }
6523
+ async updateNotification(input) {
6524
+ const existing = await this.getNotification({ threadId: input.threadId, id: input.id });
6525
+ if (!existing) {
6526
+ throw new Error(`Notification ${input.id} was not found for thread ${input.threadId}`);
6527
+ }
6528
+ const now = /* @__PURE__ */ new Date();
6529
+ const set = {
6530
+ ...input.status ? { status: input.status, ...statusTimestamp(input.status, now) } : {},
6531
+ ...input.summary !== void 0 ? { summary: input.summary } : {},
6532
+ ...input.payload !== void 0 ? { payload: cloneValue(input.payload) } : {},
6533
+ ...input.attributes !== void 0 ? { attributes: cloneValue(input.attributes) } : {},
6534
+ ...input.metadata !== void 0 ? { metadata: cloneValue(input.metadata) } : {},
6535
+ ...input.deliverAt !== void 0 ? { deliverAt: input.deliverAt } : {},
6536
+ ...input.summaryAt !== void 0 ? { summaryAt: input.summaryAt } : {},
6537
+ ...input.deliveryReason !== void 0 ? { deliveryReason: input.deliveryReason } : {},
6538
+ ...input.deliveryAttempts !== void 0 ? { deliveryAttempts: input.deliveryAttempts } : {},
6539
+ ...input.lastDeliveryAttemptAt !== void 0 ? { lastDeliveryAttemptAt: input.lastDeliveryAttemptAt } : {},
6540
+ ...input.lastDeliveryError !== void 0 ? { lastDeliveryError: input.lastDeliveryError } : {},
6541
+ ...input.deliveredSignalId !== void 0 ? { deliveredSignalId: input.deliveredSignalId } : {},
6542
+ ...input.summarySignalId !== void 0 ? { summarySignalId: input.summarySignalId } : {},
6543
+ updatedAt: now
6544
+ };
6545
+ const collection = await this.getCollection();
6546
+ await collection.updateOne({ threadId: input.threadId, id: input.id }, { $set: set });
6547
+ const updated = await this.getNotification({ threadId: input.threadId, id: input.id });
6548
+ if (!updated) throw new Error(`Notification ${input.id} was not found for thread ${input.threadId}`);
6549
+ return updated;
6550
+ }
6551
+ async findCoalescable(input) {
6552
+ if (!input.dedupeKey && !input.coalesceKey) return void 0;
6553
+ const collection = await this.getCollection();
6554
+ const row = await collection.findOne(
6555
+ {
6556
+ threadId: input.threadId,
6557
+ source: input.source,
6558
+ kind: input.kind,
6559
+ status: "pending",
6560
+ agentId: input.agentId ?? null,
6561
+ resourceId: input.resourceId ?? null,
6562
+ $or: [
6563
+ ...input.dedupeKey ? [{ dedupeKey: input.dedupeKey }] : [],
6564
+ ...input.coalesceKey ? [{ coalesceKey: input.coalesceKey }] : []
6565
+ ]
6566
+ },
6567
+ { sort: { updatedAt: -1 } }
6568
+ );
6569
+ return row ? rowToNotification(row) : void 0;
6570
+ }
6571
+ };
6572
+ var dueTime = (record) => {
6573
+ const deliverAt = record.deliverAt?.getTime();
6574
+ const summaryAt = record.summaryAt?.getTime();
6575
+ if (deliverAt !== void 0 && summaryAt !== void 0) return Math.min(deliverAt, summaryAt);
6576
+ return deliverAt ?? summaryAt ?? Number.POSITIVE_INFINITY;
6577
+ };
6290
6578
  var ObservabilityMongoDB = class _ObservabilityMongoDB extends storage.ObservabilityStorage {
6291
6579
  #connector;
6292
6580
  #skipDefaultIndexes;
@@ -10351,6 +10639,7 @@ var MongoDBStore = class extends storage.MastraCompositeStore {
10351
10639
  indexes: config.indexes
10352
10640
  };
10353
10641
  const memory = new MemoryStorageMongoDB(domainConfig);
10642
+ const notifications = new NotificationsMongoDB(domainConfig);
10354
10643
  const scores = new ScoresStorageMongoDB(domainConfig);
10355
10644
  const workflows = new WorkflowsStorageMongoDB(domainConfig);
10356
10645
  const observability = new ObservabilityMongoDB(domainConfig);
@@ -10368,6 +10657,7 @@ var MongoDBStore = class extends storage.MastraCompositeStore {
10368
10657
  const schedules = new SchedulesMongoDB(domainConfig);
10369
10658
  this.stores = {
10370
10659
  memory,
10660
+ notifications,
10371
10661
  scores,
10372
10662
  workflows,
10373
10663
  observability,
@@ -10516,6 +10806,7 @@ exports.MongoDBSkillsStorage = MongoDBSkillsStorage;
10516
10806
  exports.MongoDBStore = MongoDBStore;
10517
10807
  exports.MongoDBVector = MongoDBVector;
10518
10808
  exports.MongoDBWorkspacesStorage = MongoDBWorkspacesStorage;
10809
+ exports.NotificationsMongoDB = NotificationsMongoDB;
10519
10810
  exports.ObservabilityMongoDB = ObservabilityMongoDB;
10520
10811
  exports.SchedulesMongoDB = SchedulesMongoDB;
10521
10812
  exports.ScoresStorageMongoDB = ScoresStorageMongoDB;