@mastra/convex 1.3.2 → 1.4.0-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.
Files changed (44) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +28 -17
  3. package/dist/{chunk-6P3LEDHY.cjs → chunk-PKCUPSKO.cjs} +315 -2
  4. package/dist/chunk-PKCUPSKO.cjs.map +1 -0
  5. package/dist/{chunk-MC75WADX.js → chunk-QWWFHN52.js} +41 -3
  6. package/dist/chunk-QWWFHN52.js.map +1 -0
  7. package/dist/{chunk-77UWNT5X.js → chunk-SQMTQ7ZD.js} +315 -2
  8. package/dist/chunk-SQMTQ7ZD.js.map +1 -0
  9. package/dist/{chunk-SFRHJGSM.cjs → chunk-ZCCKLZQH.cjs} +42 -2
  10. package/dist/chunk-ZCCKLZQH.cjs.map +1 -0
  11. package/dist/docs/SKILL.md +1 -1
  12. package/dist/docs/assets/SOURCE_MAP.json +41 -33
  13. package/dist/docs/references/reference-storage-convex.md +18 -11
  14. package/dist/index.cjs +586 -44
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +542 -4
  17. package/dist/index.js.map +1 -1
  18. package/dist/schema.cjs +38 -30
  19. package/dist/schema.d.ts +95 -1
  20. package/dist/schema.d.ts.map +1 -1
  21. package/dist/schema.js +1 -1
  22. package/dist/server/index-map.d.ts.map +1 -1
  23. package/dist/server/index.cjs +41 -33
  24. package/dist/server/index.d.ts +1 -1
  25. package/dist/server/index.d.ts.map +1 -1
  26. package/dist/server/index.js +2 -2
  27. package/dist/server/observational-memory.d.ts +52 -0
  28. package/dist/server/observational-memory.d.ts.map +1 -0
  29. package/dist/server/storage.d.ts.map +1 -1
  30. package/dist/storage/db/index.d.ts +65 -14
  31. package/dist/storage/db/index.d.ts.map +1 -1
  32. package/dist/storage/domains/memory/index.d.ts +23 -1
  33. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  34. package/dist/storage/domains/schedules/index.d.ts.map +1 -1
  35. package/dist/storage/domains/scores/index.d.ts.map +1 -1
  36. package/dist/storage/types.d.ts +133 -0
  37. package/dist/storage/types.d.ts.map +1 -1
  38. package/dist/vector/index.d.ts.map +1 -1
  39. package/dist/vector/native.d.ts.map +1 -1
  40. package/package.json +9 -9
  41. package/dist/chunk-6P3LEDHY.cjs.map +0 -1
  42. package/dist/chunk-77UWNT5X.js.map +0 -1
  43. package/dist/chunk-MC75WADX.js.map +0 -1
  44. package/dist/chunk-SFRHJGSM.cjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @mastra/convex
2
2
 
3
+ ## 1.4.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added Observational Memory support to the Convex storage adapter. Agents using `ConvexStore` can now enable `observationalMemory` in `@mastra/memory`, including async observation buffering and reflections. ([#19474](https://github.com/mastra-ai/mastra/pull/19474))
8
+
9
+ To enable it, add the new `mastraObservationalMemoryTable` to your Convex schema and redeploy:
10
+
11
+ ```ts title="convex/schema.ts"
12
+ import { defineSchema } from 'convex/server';
13
+ import {
14
+ mastraThreadsTable,
15
+ mastraMessagesTable,
16
+ mastraResourcesTable,
17
+ mastraObservationalMemoryTable,
18
+ // ...other Mastra tables
19
+ } from '@mastra/convex/schema';
20
+
21
+ export default defineSchema({
22
+ mastra_threads: mastraThreadsTable,
23
+ mastra_messages: mastraMessagesTable,
24
+ mastra_resources: mastraResourcesTable,
25
+ mastra_observational_memory: mastraObservationalMemoryTable,
26
+ // ...other Mastra tables
27
+ });
28
+ ```
29
+
30
+ Then run `npx convex deploy` (or `npx convex dev`) so the new table and storage operations are live. All observational memory writes run inside the deployed Convex storage mutation, so buffered-observation swaps and reflection generations are atomic.
31
+
32
+ ### Patch Changes
33
+
34
+ - Improved type safety of the Convex storage layer. Table names accepted by the internal ConvexDB are now a closed union (core storage tables plus the observational memory table) instead of any string, so table-name typos are caught at compile time instead of silently routing records to the mastra_documents fallback table. The internal observational memory operations no longer take a table-name argument. ([#19485](https://github.com/mastra-ai/mastra/pull/19485))
35
+
36
+ - Removed the Convex adapters' only Node builtin dependency. ConvexStore, the vector adapters, and the storage domains now use the Web Crypto API (crypto.randomUUID) instead of importing node:crypto, so @mastra/convex itself no longer requires the Node.js runtime ("use node") to bundle inside a Convex project. ([#19498](https://github.com/mastra-ai/mastra/pull/19498))
37
+
38
+ - Updated dependencies [[`8a0d145`](https://github.com/mastra-ai/mastra/commit/8a0d145aadbdf7278665aceaaec364b35dd9bd94), [`bd2f1d2`](https://github.com/mastra-ai/mastra/commit/bd2f1d274d05e60e2366f005ea0d94d5cea0d5ff), [`21a0eb8`](https://github.com/mastra-ai/mastra/commit/21a0eb86746ba0b703acea360d4f84c6a5a493f2), [`de86fd7`](https://github.com/mastra-ai/mastra/commit/de86fd7119f0438381d1a642e3d258143c0b9c29), [`2745031`](https://github.com/mastra-ai/mastra/commit/2745031d1d4a4978f037092da371428c32e2842a), [`db650ce`](https://github.com/mastra-ai/mastra/commit/db650ce490348914e85b93651d83acdf8f2a4c31), [`6354eeb`](https://github.com/mastra-ai/mastra/commit/6354eeb32efa9f5f68f51dda394e90e2ee76f1fb)]:
39
+ - @mastra/core@1.51.1-alpha.0
40
+
3
41
  ## 1.3.2
4
42
 
5
43
  ### Patch Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Convex adapters for Mastra:
4
4
 
5
- - `ConvexStore` implements the Mastra storage contract (threads, messages, workflows, scores, resources, schedules, channels, background tasks).
5
+ - `ConvexStore` implements the Mastra storage contract (threads, messages, workflows, scores, resources, schedules, channels, background tasks, observational memory).
6
6
  - `ConvexVector` stores embeddings inside Convex and performs development-scale cosine similarity search.
7
7
  - `ConvexNativeVector` uses Convex native vector search for production workloads.
8
8
  - `ConvexServerCache` stores Mastra server cache entries in Convex for durable stream replay and response caching.
@@ -33,6 +33,7 @@ import {
33
33
  mastraChannelInstallationsTable,
34
34
  mastraChannelConfigTable,
35
35
  mastraBackgroundTasksTable,
36
+ mastraObservationalMemoryTable,
36
37
  mastraVectorIndexesTable,
37
38
  mastraVectorsTable,
38
39
  mastraCacheTable,
@@ -51,6 +52,7 @@ export default defineSchema({
51
52
  mastra_channel_installations: mastraChannelInstallationsTable,
52
53
  mastra_channel_config: mastraChannelConfigTable,
53
54
  mastra_background_tasks: mastraBackgroundTasksTable,
55
+ mastra_observational_memory: mastraObservationalMemoryTable,
54
56
  mastra_vector_indexes: mastraVectorIndexesTable,
55
57
  mastra_vectors: mastraVectorsTable,
56
58
  mastra_cache: mastraCacheTable,
@@ -170,22 +172,23 @@ Native vector search uses Convex's schema-defined vector indexes and action-only
170
172
 
171
173
  This adapter uses **typed Convex tables** for each Mastra domain:
172
174
 
173
- | Domain | Convex Table | Purpose |
174
- | ---------------- | ------------------------------------------------------- | -------------------------------- |
175
- | Threads | `mastra_threads` | Conversation threads |
176
- | Messages | `mastra_messages` | Chat messages |
177
- | Resources | `mastra_resources` | User working memory |
178
- | Workflows | `mastra_workflow_snapshots` | Workflow state |
179
- | Scorers | `mastra_scorers` | Evaluation data |
180
- | Schedules | `mastra_schedules` | Workflow schedules |
181
- | Triggers | `mastra_schedule_triggers` | Schedule history |
182
- | Channels | `mastra_channel_installations`, `mastra_channel_config` | Channel installations and config |
183
- | Background Tasks | `mastra_background_tasks` | Background task state |
184
- | Vector Indexes | `mastra_vector_indexes` | Index metadata |
185
- | Vectors | `mastra_vectors` | Embeddings |
186
- | Cache | `mastra_cache` | Cache metadata |
187
- | Cache Items | `mastra_cache_list_items` | Cache list entries |
188
- | Fallback | `mastra_documents` | Unknown tables |
175
+ | Domain | Convex Table | Purpose |
176
+ | -------------------- | ------------------------------------------------------- | -------------------------------- |
177
+ | Threads | `mastra_threads` | Conversation threads |
178
+ | Messages | `mastra_messages` | Chat messages |
179
+ | Resources | `mastra_resources` | User working memory |
180
+ | Workflows | `mastra_workflow_snapshots` | Workflow state |
181
+ | Scorers | `mastra_scorers` | Evaluation data |
182
+ | Schedules | `mastra_schedules` | Workflow schedules |
183
+ | Triggers | `mastra_schedule_triggers` | Schedule history |
184
+ | Channels | `mastra_channel_installations`, `mastra_channel_config` | Channel installations and config |
185
+ | Background Tasks | `mastra_background_tasks` | Background task state |
186
+ | Observational Memory | `mastra_observational_memory` | Observational memory generations |
187
+ | Vector Indexes | `mastra_vector_indexes` | Index metadata |
188
+ | Vectors | `mastra_vectors` | Embeddings |
189
+ | Cache | `mastra_cache` | Cache metadata |
190
+ | Cache Items | `mastra_cache_list_items` | Cache list entries |
191
+ | Fallback | `mastra_documents` | Unknown tables |
189
192
 
190
193
  All typed tables include:
191
194
 
@@ -196,6 +199,14 @@ Schedule due reads and trigger-history reads use bounded Convex queries to avoid
196
199
 
197
200
  Background task reads and updates also tolerate older rows that were written to the fallback `mastra_documents` table.
198
201
 
202
+ ### Observational memory
203
+
204
+ `ConvexStore` supports Mastra's observational memory. Records are keyed by a `lookupKey` (`thread:{id}` or `resource:{id}`) and the latest generation is served through the `by_lookup_key` index. All observational memory read-modify-write operations run inside the deployed storage mutation, so swaps and counter updates are atomic.
205
+
206
+ Upgrading from a version without observational memory: add `mastraObservationalMemoryTable` to your `convex/schema.ts` (as shown in the quick start) and run `npx convex deploy` again.
207
+
208
+ Active observations and buffered chunks share one Convex document, which is subject to Convex's 1 MiB document size limit. Default observational memory thresholds stay well below this limit; extremely large custom thresholds can exceed it.
209
+
199
210
  ## Testing
200
211
 
201
212
  Set the following environment variables before running tests:
@@ -267,6 +267,10 @@ var TABLE_INDEX_MAP = {
267
267
  mastra_vector_indexes: [
268
268
  { name: "by_name", fields: ["indexName"] },
269
269
  { name: "by_record_id", fields: ["id"] }
270
+ ],
271
+ mastra_observational_memory: [
272
+ { name: "by_lookup_key", fields: ["lookupKey", "generationCount"] },
273
+ { name: "by_record_id", fields: ["id"] }
270
274
  ]
271
275
  };
272
276
  function findBestIndex(convexTable, filters) {
@@ -296,6 +300,299 @@ function findBestIndex(convexTable, filters) {
296
300
  return best ? { indexName: best.indexName, indexedFilters: best.indexedFilters } : null;
297
301
  }
298
302
 
303
+ // src/server/observational-memory.ts
304
+ var OM_QUERY_MAX_DOCS = 1e4;
305
+ function parseStoredChunks(value) {
306
+ if (typeof value !== "string" || !value) return [];
307
+ try {
308
+ const parsed = JSON.parse(value);
309
+ return Array.isArray(parsed) ? parsed : [];
310
+ } catch {
311
+ return [];
312
+ }
313
+ }
314
+ function selectActivationBoundary(chunks, opts) {
315
+ const retentionFloor = opts.messageTokensThreshold * (1 - opts.activationRatio);
316
+ const targetMessageTokens = Math.max(0, opts.currentPendingTokens - retentionFloor);
317
+ let cumulativeMessageTokens = 0;
318
+ let bestOverBoundary = 0;
319
+ let bestOverTokens = 0;
320
+ let bestUnderBoundary = 0;
321
+ let bestUnderTokens = 0;
322
+ for (let i = 0; i < chunks.length; i++) {
323
+ cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
324
+ const boundary = i + 1;
325
+ if (cumulativeMessageTokens >= targetMessageTokens) {
326
+ if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
327
+ bestOverBoundary = boundary;
328
+ bestOverTokens = cumulativeMessageTokens;
329
+ }
330
+ } else {
331
+ if (cumulativeMessageTokens > bestUnderTokens) {
332
+ bestUnderBoundary = boundary;
333
+ bestUnderTokens = cumulativeMessageTokens;
334
+ }
335
+ }
336
+ }
337
+ const maxOvershoot = retentionFloor * 0.95;
338
+ const overshoot = bestOverTokens - targetMessageTokens;
339
+ const remainingAfterOver = opts.currentPendingTokens - bestOverTokens;
340
+ const remainingAfterUnder = opts.currentPendingTokens - bestUnderTokens;
341
+ const minRemaining = Math.min(1e3, retentionFloor);
342
+ if (opts.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) {
343
+ return bestOverBoundary;
344
+ }
345
+ if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
346
+ return bestOverBoundary;
347
+ }
348
+ if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
349
+ return bestUnderBoundary;
350
+ }
351
+ if (bestOverBoundary > 0) {
352
+ return bestOverBoundary;
353
+ }
354
+ return 1;
355
+ }
356
+ function mergeReflectionWithUnreflected(activeObservations, bufferedReflection, reflectedLineCount) {
357
+ const allLines = (activeObservations || "").split("\n");
358
+ const unreflectedLines = allLines.slice(reflectedLineCount);
359
+ const unreflectedContent = unreflectedLines.join("\n").trim();
360
+ return unreflectedContent ? `${bufferedReflection}
361
+
362
+ ${unreflectedContent}` : bufferedReflection;
363
+ }
364
+ function isPlainObj(value) {
365
+ return typeof value === "object" && value !== null && !Array.isArray(value);
366
+ }
367
+ function deepMergeOMConfig(target, source) {
368
+ const output = { ...target };
369
+ for (const key of Object.keys(source)) {
370
+ const tVal = target[key];
371
+ const sVal = source[key];
372
+ if (isPlainObj(tVal) && isPlainObj(sVal)) {
373
+ output[key] = deepMergeOMConfig(tVal, sVal);
374
+ } else if (sVal !== void 0) {
375
+ output[key] = sVal;
376
+ }
377
+ }
378
+ return output;
379
+ }
380
+ function parseJsonObject(value) {
381
+ if (typeof value !== "string" || !value) return {};
382
+ try {
383
+ const parsed = JSON.parse(value);
384
+ return isPlainObj(parsed) ? parsed : {};
385
+ } catch {
386
+ return {};
387
+ }
388
+ }
389
+ async function findRecordById(ctx, convexTable, id) {
390
+ return await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", id)).unique();
391
+ }
392
+ function requireRecord(doc, id) {
393
+ if (!doc) {
394
+ throw new Error(`Observational memory record not found: ${id}`);
395
+ }
396
+ return doc;
397
+ }
398
+ var EMPTY_SWAP_RESULT = {
399
+ chunksActivated: 0,
400
+ messageTokensActivated: 0,
401
+ observationTokensActivated: 0,
402
+ messagesActivated: 0,
403
+ activatedCycleIds: [],
404
+ activatedMessageIds: []
405
+ };
406
+ async function handleObservationalMemoryOperation(ctx, convexTable, request) {
407
+ switch (request.op) {
408
+ case "omGetLatest": {
409
+ const doc = await ctx.db.query(convexTable).withIndex("by_lookup_key", (q) => q.eq("lookupKey", request.lookupKey)).order("desc").first();
410
+ return { ok: true, result: doc ?? null };
411
+ }
412
+ case "omGetHistory": {
413
+ let docs = await ctx.db.query(convexTable).withIndex("by_lookup_key", (q) => q.eq("lookupKey", request.lookupKey)).order("desc").take(OM_QUERY_MAX_DOCS);
414
+ if (request.from) {
415
+ docs = docs.filter((doc) => typeof doc.createdAt === "string" && doc.createdAt >= request.from);
416
+ }
417
+ if (request.to) {
418
+ docs = docs.filter((doc) => typeof doc.createdAt === "string" && doc.createdAt <= request.to);
419
+ }
420
+ if (request.offset != null) {
421
+ docs = docs.slice(request.offset);
422
+ }
423
+ return { ok: true, result: docs.slice(0, request.limit) };
424
+ }
425
+ case "omUpdateActive": {
426
+ const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
427
+ const safeTokenCount = Number.isFinite(request.tokenCount) && request.tokenCount >= 0 ? request.tokenCount : 0;
428
+ await ctx.db.patch(doc._id, {
429
+ activeObservations: request.observations,
430
+ lastObservedAt: request.lastObservedAt,
431
+ // Reset pending tokens since we've now observed them
432
+ pendingMessageTokens: 0,
433
+ observationTokenCount: safeTokenCount,
434
+ totalTokensObserved: Number(doc.totalTokensObserved || 0) + safeTokenCount,
435
+ observedMessageIds: request.observedMessageIds,
436
+ updatedAt: request.updatedAt
437
+ });
438
+ return { ok: true };
439
+ }
440
+ case "omAppendBufferedChunk": {
441
+ const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
442
+ const chunks = parseStoredChunks(doc.bufferedObservationChunks);
443
+ chunks.push(request.chunk);
444
+ const patch = {
445
+ bufferedObservationChunks: JSON.stringify(chunks),
446
+ updatedAt: request.updatedAt
447
+ };
448
+ if (request.lastBufferedAtTime) {
449
+ patch.lastBufferedAtTime = request.lastBufferedAtTime;
450
+ }
451
+ await ctx.db.patch(doc._id, patch);
452
+ return { ok: true };
453
+ }
454
+ case "omSwapBuffered": {
455
+ const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
456
+ const persistedChunks = parseStoredChunks(doc.bufferedObservationChunks);
457
+ if (persistedChunks.length === 0) {
458
+ return { ok: true, result: EMPTY_SWAP_RESULT };
459
+ }
460
+ const chunks = Array.isArray(request.bufferedChunks) ? request.bufferedChunks : persistedChunks;
461
+ if (chunks.length === 0) {
462
+ return { ok: true, result: EMPTY_SWAP_RESULT };
463
+ }
464
+ const chunksToActivate = selectActivationBoundary(chunks, {
465
+ activationRatio: request.activationRatio,
466
+ messageTokensThreshold: request.messageTokensThreshold,
467
+ currentPendingTokens: request.currentPendingTokens,
468
+ forceMaxActivation: request.forceMaxActivation
469
+ });
470
+ const activatedChunks = chunks.slice(0, chunksToActivate);
471
+ const remainingChunks = chunks.slice(chunksToActivate);
472
+ const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
473
+ const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
474
+ const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
475
+ const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + (c.messageIds?.length ?? 0), 0);
476
+ const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
477
+ const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
478
+ const latestChunk = activatedChunks[activatedChunks.length - 1];
479
+ const lastObservedAt = request.lastObservedAt ?? latestChunk?.lastObservedAt ?? request.now;
480
+ const existingActive = doc.activeObservations || "";
481
+ const boundary = `
482
+
483
+ --- message boundary (${lastObservedAt}) ---
484
+
485
+ `;
486
+ const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
487
+ await ctx.db.patch(doc._id, {
488
+ activeObservations: newActive,
489
+ observationTokenCount: Number(doc.observationTokenCount || 0) + activatedTokens,
490
+ // Decrement pending message tokens (clamped to zero)
491
+ pendingMessageTokens: Math.max(0, Number(doc.pendingMessageTokens || 0) - activatedMessageTokens),
492
+ bufferedObservationChunks: remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
493
+ lastObservedAt,
494
+ updatedAt: request.now
495
+ });
496
+ const latestChunkHints = activatedChunks[activatedChunks.length - 1];
497
+ return {
498
+ ok: true,
499
+ result: {
500
+ chunksActivated: activatedChunks.length,
501
+ messageTokensActivated: activatedMessageTokens,
502
+ observationTokensActivated: activatedTokens,
503
+ messagesActivated: activatedMessageCount,
504
+ activatedCycleIds,
505
+ activatedMessageIds,
506
+ observations: activatedContent,
507
+ perChunk: activatedChunks.map((c) => ({
508
+ cycleId: c.cycleId ?? "",
509
+ messageTokens: c.messageTokens ?? 0,
510
+ observationTokens: c.tokenCount,
511
+ messageCount: c.messageIds?.length ?? 0,
512
+ observations: c.observations
513
+ })),
514
+ suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
515
+ currentTask: latestChunkHints?.currentTask ?? void 0
516
+ }
517
+ };
518
+ }
519
+ case "omUpdateBufferedReflection": {
520
+ const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
521
+ const existingContent = doc.bufferedReflection || "";
522
+ await ctx.db.patch(doc._id, {
523
+ bufferedReflection: existingContent ? `${existingContent}
524
+
525
+ ${request.reflection}` : request.reflection,
526
+ bufferedReflectionTokens: Number(doc.bufferedReflectionTokens || 0) + request.tokenCount,
527
+ bufferedReflectionInputTokens: Number(doc.bufferedReflectionInputTokens || 0) + request.inputTokenCount,
528
+ reflectedObservationLineCount: request.reflectedObservationLineCount,
529
+ updatedAt: request.updatedAt
530
+ });
531
+ return { ok: true };
532
+ }
533
+ case "omSwapBufferedReflection": {
534
+ const { currentRecord, newId, tokenCount, now } = request;
535
+ const doc = requireRecord(await findRecordById(ctx, convexTable, currentRecord.id), currentRecord.id);
536
+ const bufferedReflection = doc.bufferedReflection || "";
537
+ if (!bufferedReflection) {
538
+ throw new Error("No buffered reflection to swap");
539
+ }
540
+ const newObservations = mergeReflectionWithUnreflected(
541
+ doc.activeObservations || "",
542
+ bufferedReflection,
543
+ Number(doc.reflectedObservationLineCount || 0)
544
+ );
545
+ const newRecord = {
546
+ id: newId,
547
+ lookupKey: currentRecord.lookupKey,
548
+ scope: currentRecord.scope,
549
+ resourceId: currentRecord.resourceId,
550
+ threadId: currentRecord.threadId,
551
+ activeObservations: newObservations,
552
+ activeObservationsPendingUpdate: null,
553
+ originType: "reflection",
554
+ config: currentRecord.config,
555
+ generationCount: currentRecord.generationCount + 1,
556
+ lastObservedAt: currentRecord.lastObservedAt,
557
+ lastReflectionAt: now,
558
+ pendingMessageTokens: 0,
559
+ totalTokensObserved: currentRecord.totalTokensObserved,
560
+ observationTokenCount: tokenCount,
561
+ isObserving: false,
562
+ isReflecting: false,
563
+ isBufferingObservation: false,
564
+ isBufferingReflection: false,
565
+ lastBufferedAtTokens: 0,
566
+ lastBufferedAtTime: null,
567
+ observedTimezone: currentRecord.observedTimezone,
568
+ metadata: currentRecord.metadata,
569
+ createdAt: now,
570
+ updatedAt: now
571
+ };
572
+ await ctx.db.insert(convexTable, newRecord);
573
+ await ctx.db.patch(doc._id, {
574
+ bufferedReflection: null,
575
+ bufferedReflectionTokens: null,
576
+ bufferedReflectionInputTokens: null,
577
+ reflectedObservationLineCount: null,
578
+ updatedAt: now
579
+ });
580
+ return { ok: true, result: newRecord };
581
+ }
582
+ case "omUpdateConfig": {
583
+ const doc = requireRecord(await findRecordById(ctx, convexTable, request.id), request.id);
584
+ const existing = parseJsonObject(doc.config);
585
+ const incoming = parseJsonObject(request.config);
586
+ const merged = deepMergeOMConfig(existing, incoming);
587
+ await ctx.db.patch(doc._id, {
588
+ config: JSON.stringify(merged),
589
+ updatedAt: request.updatedAt
590
+ });
591
+ return { ok: true };
592
+ }
593
+ }
594
+ }
595
+
299
596
  // src/server/workflow-snapshot.ts
300
597
  var PENDING_MARKER_KEY = "__mastra_pending__";
301
598
  function isPendingMarker(val) {
@@ -375,6 +672,7 @@ var VECTOR_TABLE_PREFIX = "mastra_vector_";
375
672
  var CONVEX_TABLE_WORKFLOW_SNAPSHOTS = "mastra_workflow_snapshots";
376
673
  var CONVEX_TABLE_BACKGROUND_TASKS = "mastra_background_tasks";
377
674
  var CONVEX_TABLE_DOCUMENTS = "mastra_documents";
675
+ var CONVEX_TABLE_OBSERVATIONAL_MEMORY = "mastra_observational_memory";
378
676
  var STORAGE_MUTATION_BATCH_SIZE = 25;
379
677
  var LOAD_MANY_MAX_IDS_PER_REQUEST = 10;
380
678
  var DEFAULT_SCHEDULE_QUERY_LIMIT = 100;
@@ -527,6 +825,8 @@ function resolveTable(tableName) {
527
825
  return { convexTable: CONVEX_TABLE_BACKGROUND_TASKS, isTyped: true };
528
826
  case TABLE_VECTOR_INDEXES:
529
827
  return { convexTable: "mastra_vector_indexes", isTyped: true };
828
+ case CONVEX_TABLE_OBSERVATIONAL_MEMORY:
829
+ return { convexTable: CONVEX_TABLE_OBSERVATIONAL_MEMORY, isTyped: true };
530
830
  default:
531
831
  if (tableName.startsWith(VECTOR_TABLE_PREFIX)) {
532
832
  return { convexTable: "mastra_vectors", isTyped: true };
@@ -587,6 +887,19 @@ function mergeMetadata(existing, update) {
587
887
  }
588
888
  async function handleTypedOperation(ctx, convexTable, request) {
589
889
  switch (request.op) {
890
+ case "omGetLatest":
891
+ case "omGetHistory":
892
+ case "omUpdateActive":
893
+ case "omAppendBufferedChunk":
894
+ case "omSwapBuffered":
895
+ case "omUpdateBufferedReflection":
896
+ case "omSwapBufferedReflection":
897
+ case "omUpdateConfig": {
898
+ if (convexTable !== CONVEX_TABLE_OBSERVATIONAL_MEMORY) {
899
+ throw new Error(`${request.op} is only supported for ${CONVEX_TABLE_OBSERVATIONAL_MEMORY}`);
900
+ }
901
+ return handleObservationalMemoryOperation(ctx, convexTable, request);
902
+ }
590
903
  case "createSchedule": {
591
904
  if (convexTable !== "mastra_schedules") {
592
905
  throw new Error(`createSchedule is only supported for mastra_schedules`);
@@ -1459,5 +1772,5 @@ exports.mastraNativeVectorAction = mastraNativeVectorAction;
1459
1772
  exports.mastraNativeVectorMutation = mastraNativeVectorMutation;
1460
1773
  exports.mastraNativeVectorQuery = mastraNativeVectorQuery;
1461
1774
  exports.mastraStorage = mastraStorage;
1462
- //# sourceMappingURL=chunk-6P3LEDHY.cjs.map
1463
- //# sourceMappingURL=chunk-6P3LEDHY.cjs.map
1775
+ //# sourceMappingURL=chunk-PKCUPSKO.cjs.map
1776
+ //# sourceMappingURL=chunk-PKCUPSKO.cjs.map