@mastra/convex 1.7.2 → 1.7.3-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 (42) hide show
  1. package/dist/_types/@internal_core/dist/memory/index.d.ts +102 -0
  2. package/dist/_types/@internal_core/package.json +1 -0
  3. package/dist/docs/SKILL.md +1 -1
  4. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  5. package/dist/docs/references/reference-vectors-convex.md +1 -3
  6. package/dist/index.cjs +370 -161
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.js +371 -162
  9. package/dist/index.js.map +1 -1
  10. package/dist/schema.cjs +1 -0
  11. package/dist/schema.cjs.map +1 -1
  12. package/dist/schema.d.ts +3 -1
  13. package/dist/schema.d.ts.map +1 -1
  14. package/dist/schema.js +1 -0
  15. package/dist/schema.js.map +1 -1
  16. package/dist/server/index.cjs +1 -1
  17. package/dist/server/index.js +1 -1
  18. package/dist/server/observational-memory.d.ts +26 -1
  19. package/dist/server/observational-memory.d.ts.map +1 -1
  20. package/dist/server/run-fencing.d.ts +14 -0
  21. package/dist/server/run-fencing.d.ts.map +1 -0
  22. package/dist/server/storage.d.ts +2 -2
  23. package/dist/server/storage.d.ts.map +1 -1
  24. package/dist/{server-D3SG5LQf.js → server-B8RSiOmu.js} +454 -56
  25. package/dist/server-B8RSiOmu.js.map +1 -0
  26. package/dist/{server-D_scBUwi.cjs → server-Bcgr5CcA.cjs} +454 -56
  27. package/dist/server-Bcgr5CcA.cjs.map +1 -0
  28. package/dist/storage/client.d.ts +3 -3
  29. package/dist/storage/client.d.ts.map +1 -1
  30. package/dist/storage/db/index.d.ts +33 -6
  31. package/dist/storage/db/index.d.ts.map +1 -1
  32. package/dist/storage/domains/memory/index.d.ts +20 -7
  33. package/dist/storage/domains/memory/index.d.ts.map +1 -1
  34. package/dist/storage/domains/workflows/index.d.ts +16 -5
  35. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  36. package/dist/storage/run-fencing.d.ts +28 -0
  37. package/dist/storage/run-fencing.d.ts.map +1 -0
  38. package/dist/storage/types.d.ts +114 -1
  39. package/dist/storage/types.d.ts.map +1 -1
  40. package/package.json +5 -4
  41. package/dist/server-D3SG5LQf.js.map +0 -1
  42. package/dist/server-D_scBUwi.cjs.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { a as mastraCache, i as mastraStorage, n as mastraNativeVectorMutation, r as mastraNativeVectorQuery, t as mastraNativeVectorAction } from "./server-D3SG5LQf.js";
1
+ import { a as mastraCache, i as mastraStorage, n as mastraNativeVectorMutation, r as mastraNativeVectorQuery, t as mastraNativeVectorAction } from "./server-B8RSiOmu.js";
2
2
  import { TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_MESSAGES, TABLE_OBSERVATIONAL_MEMORY, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCORERS, TABLE_THREADS, TABLE_WORKFLOW_SNAPSHOT, defineMastraNativeVectorTable, mastraBackgroundTasksTable, mastraCacheListItemsTable, mastraCacheTable, mastraChannelConfigTable, mastraChannelInstallationsTable, mastraDocumentsTable, mastraMessagesTable, mastraObservationalMemoryTable, mastraResourcesTable, mastraScheduleTriggersTable, mastraSchedulesTable, mastraScoresTable, mastraThreadsTable, mastraVectorIndexesTable, mastraVectorsTable, mastraWorkflowSnapshotsTable } from "./schema.js";
3
3
  import { MastraServerCache } from "@mastra/core/cache";
4
- import { BackgroundTasksStorage, ChannelsStorage, MastraCompositeStore, MemoryStorage, SchedulesStorage, ScoresStorage, TABLE_BACKGROUND_TASKS as TABLE_BACKGROUND_TASKS$1, TABLE_CHANNEL_CONFIG as TABLE_CHANNEL_CONFIG$1, TABLE_CHANNEL_INSTALLATIONS as TABLE_CHANNEL_INSTALLATIONS$1, TABLE_MESSAGES as TABLE_MESSAGES$1, TABLE_RESOURCES as TABLE_RESOURCES$1, TABLE_SCHEDULES as TABLE_SCHEDULES$1, TABLE_SCHEDULE_TRIGGERS as TABLE_SCHEDULE_TRIGGERS$1, TABLE_SCORERS as TABLE_SCORERS$1, TABLE_THREADS as TABLE_THREADS$1, TABLE_WORKFLOW_SNAPSHOT as TABLE_WORKFLOW_SNAPSHOT$1, WorkflowsStorage, calculatePagination, createStorageErrorId, createVectorErrorId, filterByDateRange, normalizePerPage, normalizeScheduleTarget, safelyParseJSON, storageMessageMatchesMetadataFilter, validateStorageMetadataFilter } from "@mastra/core/storage";
4
+ import { BackgroundTasksStorage, ChannelsStorage, MastraCompositeStore, MemoryStorage, RUN_FENCE_CONFLICT_ERROR_ID, RunFenceConflictError, SchedulesStorage, ScoresStorage, TABLE_BACKGROUND_TASKS as TABLE_BACKGROUND_TASKS$1, TABLE_CHANNEL_CONFIG as TABLE_CHANNEL_CONFIG$1, TABLE_CHANNEL_INSTALLATIONS as TABLE_CHANNEL_INSTALLATIONS$1, TABLE_MEMORY_RUN_FENCES, TABLE_MESSAGES as TABLE_MESSAGES$1, TABLE_RESOURCES as TABLE_RESOURCES$1, TABLE_SCHEDULES as TABLE_SCHEDULES$1, TABLE_SCHEDULE_TRIGGERS as TABLE_SCHEDULE_TRIGGERS$1, TABLE_SCORERS as TABLE_SCORERS$1, TABLE_THREADS as TABLE_THREADS$1, TABLE_WORKFLOW_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT as TABLE_WORKFLOW_SNAPSHOT$1, WorkflowsStorage, calculatePagination, createStorageErrorId, createVectorErrorId, filterByDateRange, normalizePerPage, normalizeScheduleTarget, resolveRunFence, safelyParseJSON, storageMessageMatchesMetadataFilter, validateStorageMetadataFilter } from "@mastra/core/storage";
5
5
  import { MastraBase } from "@mastra/core/base";
6
6
  import { MessageList } from "@mastra/core/agent";
7
7
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
@@ -252,6 +252,154 @@ var ConvexAdminClient = class {
252
252
  }
253
253
  };
254
254
  //#endregion
255
+ //#region src/storage/run-fencing.ts
256
+ const READ_OPS = /* @__PURE__ */ new Set([
257
+ "load",
258
+ "loadMany",
259
+ "queryTable",
260
+ "omGetLatest",
261
+ "omGetHistory",
262
+ "listDueSchedules",
263
+ "listScheduleTriggers"
264
+ ]);
265
+ async function callRunClaim(client, request, operation) {
266
+ try {
267
+ return await client.callStorageRaw(request);
268
+ } catch (error) {
269
+ const { code, message } = error;
270
+ if (code === RUN_FENCE_CONFLICT_ERROR_ID && request.op === "fenced") throw new RunFenceConflictError(request.fence, operation);
271
+ if (typeof message === "string" && message.startsWith(`Unsupported operation ${request.op}`)) throw new Error(`The deployed Convex storage function does not support run fencing (${request.op}). Redeploy your Convex functions with the installed version of @mastra/convex.`, { cause: error });
272
+ throw error;
273
+ }
274
+ }
275
+ /** Sends writes inside a fenced operation, so each is applied only while `check.fence` holds the run. */
276
+ var FencedStorageCaller = class {
277
+ client;
278
+ check;
279
+ constructor(client, check) {
280
+ this.client = client;
281
+ this.check = check;
282
+ }
283
+ callStorageRaw(request) {
284
+ if (READ_OPS.has(request.op)) return this.client.callStorageRaw(request);
285
+ return callRunClaim(this.client, {
286
+ op: "fenced",
287
+ tableName: this.check.claims,
288
+ fence: toFence(this.check.fence),
289
+ request
290
+ }, this.check.operation);
291
+ }
292
+ async callStorage(request) {
293
+ return (await this.callStorageRaw(request)).result;
294
+ }
295
+ };
296
+ /** The client, or one that fences every write when `check` is set. */
297
+ function fencedCaller(client, check) {
298
+ return check ? new FencedStorageCaller(client, check) : client;
299
+ }
300
+ /**
301
+ * Rejects a fenced write that turns out to have nothing to write when its
302
+ * fence is no longer current, as the write itself would have been rejected.
303
+ */
304
+ async function assertRunFence(client, check) {
305
+ if (!check) return;
306
+ await callRunClaim(client, {
307
+ op: "fenced",
308
+ tableName: check.claims,
309
+ fence: toFence(check.fence)
310
+ }, check.operation);
311
+ }
312
+ function toFence({ runId, generation, ownerId }) {
313
+ return {
314
+ runId,
315
+ generation,
316
+ ownerId
317
+ };
318
+ }
319
+ function toRecord(claim, now) {
320
+ if (!claim) return null;
321
+ const { runId, generation, ownerId, leaseExpiresAt } = claim;
322
+ return {
323
+ runId,
324
+ generation,
325
+ ownerId,
326
+ leaseExpiresAt: leaseExpiresAt === null ? null : new Date(leaseExpiresAt),
327
+ live: leaseExpiresAt !== null && leaseExpiresAt > now
328
+ };
329
+ }
330
+ async function ownerOperation(client, request) {
331
+ const { result } = await callRunClaim(client, request, request.op);
332
+ return {
333
+ applied: result.applied,
334
+ record: toRecord(result.claim, result.now)
335
+ };
336
+ }
337
+ async function claimRunOwnership(client, { runId, ownerId, leaseMs, force, expectedGeneration }) {
338
+ const { applied, record } = await ownerOperation(client, {
339
+ op: "claimRunOwnership",
340
+ tableName: TABLE_WORKFLOW_RUN_OWNERS,
341
+ runId,
342
+ ownerId,
343
+ leaseMs,
344
+ force: force ?? false,
345
+ ...expectedGeneration !== void 0 ? { expectedGeneration } : {}
346
+ });
347
+ return applied ? {
348
+ acquired: true,
349
+ record
350
+ } : {
351
+ acquired: false,
352
+ record
353
+ };
354
+ }
355
+ async function renewRunOwnership(client, { leaseMs, ...fence }) {
356
+ const { applied, record } = await ownerOperation(client, {
357
+ op: "renewRunOwnership",
358
+ tableName: TABLE_WORKFLOW_RUN_OWNERS,
359
+ fence: toFence(fence),
360
+ leaseMs
361
+ });
362
+ return applied ? {
363
+ renewed: true,
364
+ record
365
+ } : {
366
+ renewed: false,
367
+ record
368
+ };
369
+ }
370
+ async function releaseRunOwnership(client, fence) {
371
+ const { applied } = await ownerOperation(client, {
372
+ op: "releaseRunOwnership",
373
+ tableName: TABLE_WORKFLOW_RUN_OWNERS,
374
+ fence: toFence(fence)
375
+ });
376
+ return applied;
377
+ }
378
+ async function getRunOwnership(client, runId) {
379
+ const { record } = await ownerOperation(client, {
380
+ op: "getRunOwnership",
381
+ tableName: TABLE_WORKFLOW_RUN_OWNERS,
382
+ runId
383
+ });
384
+ return record;
385
+ }
386
+ async function raiseRunFence(client, fence) {
387
+ const { applied } = await ownerOperation(client, {
388
+ op: "raiseRunFence",
389
+ tableName: TABLE_MEMORY_RUN_FENCES,
390
+ fence: toFence(fence)
391
+ });
392
+ return applied;
393
+ }
394
+ async function retireRunFence(client, fence) {
395
+ const { applied } = await ownerOperation(client, {
396
+ op: "retireRunFence",
397
+ tableName: TABLE_MEMORY_RUN_FENCES,
398
+ fence: toFence(fence)
399
+ });
400
+ return applied;
401
+ }
402
+ //#endregion
255
403
  //#region src/storage/types.ts
256
404
  /**
257
405
  * Observational memory table name. Defined locally (not value-imported from
@@ -270,11 +418,17 @@ function resolveConvexConfig(config) {
270
418
  if ("client" in config) return config.client;
271
419
  return new ConvexAdminClient(config);
272
420
  }
273
- var ConvexDB = class extends MastraBase {
421
+ var ConvexDB = class ConvexDB extends MastraBase {
422
+ adminClient;
274
423
  client;
275
- constructor(client) {
424
+ constructor(adminClient, check) {
276
425
  super({ name: "convex-db" });
277
- this.client = client;
426
+ this.adminClient = adminClient;
427
+ this.client = fencedCaller(adminClient, check);
428
+ }
429
+ /** This store with every write applied only while `check.fence` holds the run, or itself without a check. */
430
+ fenced(check) {
431
+ return check ? new ConvexDB(this.adminClient, check) : this;
278
432
  }
279
433
  async hasColumn(_table, _column) {
280
434
  return true;
@@ -487,15 +641,43 @@ var ConvexDB = class extends MastraBase {
487
641
  ...args
488
642
  });
489
643
  }
490
- async omUpdateActive(args) {
644
+ async omInitialize(record) {
645
+ return this.client.callStorage({
646
+ op: "omInitialize",
647
+ tableName: TABLE_OBSERVATIONAL_MEMORY$1,
648
+ record
649
+ });
650
+ }
651
+ async omCreateReflectionGeneration(args) {
652
+ return this.client.callStorage({
653
+ op: "omCreateReflectionGeneration",
654
+ tableName: TABLE_OBSERVATIONAL_MEMORY$1,
655
+ ...args
656
+ });
657
+ }
658
+ async omSetPendingMessageTokens(args) {
491
659
  await this.client.callStorage({
660
+ op: "omSetPendingMessageTokens",
661
+ tableName: TABLE_OBSERVATIONAL_MEMORY$1,
662
+ ...args
663
+ });
664
+ }
665
+ async omSetBufferingObservationFlag(args) {
666
+ await this.client.callStorage({
667
+ op: "omSetBufferingObservationFlag",
668
+ tableName: TABLE_OBSERVATIONAL_MEMORY$1,
669
+ ...args
670
+ });
671
+ }
672
+ async omUpdateActive(args) {
673
+ return this.client.callStorage({
492
674
  op: "omUpdateActive",
493
675
  tableName: TABLE_OBSERVATIONAL_MEMORY$1,
494
676
  ...args
495
677
  });
496
678
  }
497
679
  async omAppendBufferedChunk(args) {
498
- await this.client.callStorage({
680
+ return this.client.callStorage({
499
681
  op: "omAppendBufferedChunk",
500
682
  tableName: TABLE_OBSERVATIONAL_MEMORY$1,
501
683
  ...args
@@ -900,6 +1082,17 @@ var ChannelsConvex = class extends ChannelsStorage {
900
1082
  }
901
1083
  };
902
1084
  //#endregion
1085
+ //#region ../../packages/_internal-core/dist/memory/index.js
1086
+ /**
1087
+ * Backs `updateActiveObservations` in adapters that implement `commitActiveObservations`.
1088
+ * Callers of `updateActiveObservations` (which resolves `void`) can't see a commit result, so a
1089
+ * write that was not applied throws instead of resolving as if it had committed.
1090
+ */
1091
+ function assertActiveObservationsApplied(result, id) {
1092
+ if (result.applied) return;
1093
+ throw new Error(result.reason === "retired" ? `Observational memory record ${id} was superseded by a newer generation; observations were not written` : `Observational memory record ${id} changed since the observations were composed; observations were not written`);
1094
+ }
1095
+ //#endregion
903
1096
  //#region src/storage/domains/memory/index.ts
904
1097
  function parseStoredThread(row) {
905
1098
  return {
@@ -975,7 +1168,8 @@ function parseStoredOMRecord(doc) {
975
1168
  config: config ?? {},
976
1169
  metadata: metadata ?? void 0,
977
1170
  observedMessageIds: doc.observedMessageIds || void 0,
978
- observedTimezone: doc.observedTimezone || void 0
1171
+ observedTimezone: doc.observedTimezone || void 0,
1172
+ supersededBy: doc.supersededBy ?? null
979
1173
  };
980
1174
  }
981
1175
  var MemoryConvex = class extends MemoryStorage {
@@ -983,10 +1177,32 @@ var MemoryConvex = class extends MemoryStorage {
983
1177
  supportsObservationalMemory = true;
984
1178
  supportsObservationalMemoryHistorySearch = true;
985
1179
  #db;
1180
+ #client;
986
1181
  constructor(config) {
987
1182
  super();
988
- const client = resolveConvexConfig(config);
989
- this.#db = new ConvexDB(client);
1183
+ this.#client = resolveConvexConfig(config);
1184
+ this.#db = new ConvexDB(this.#client);
1185
+ }
1186
+ supportsRunFencing() {
1187
+ return true;
1188
+ }
1189
+ async raiseRunFence(fence) {
1190
+ return raiseRunFence(this.#client, fence);
1191
+ }
1192
+ async retireRunFence(fence) {
1193
+ return retireRunFence(this.#client, fence);
1194
+ }
1195
+ #runFenceCheck(fence, operation) {
1196
+ const resolved = resolveRunFence(this, fence);
1197
+ return resolved && {
1198
+ claims: TABLE_MEMORY_RUN_FENCES,
1199
+ fence: resolved,
1200
+ operation
1201
+ };
1202
+ }
1203
+ /** The store to write through: fenced when the write carries or inherits a fence. */
1204
+ #dbFor(fence, operation) {
1205
+ return this.#db.fenced(this.#runFenceCheck(fence, operation));
990
1206
  }
991
1207
  async init() {}
992
1208
  async dangerouslyClearAll() {
@@ -994,6 +1210,7 @@ var MemoryConvex = class extends MemoryStorage {
994
1210
  await this.#db.clearTable({ tableName: TABLE_MESSAGES$1 });
995
1211
  await this.#db.clearTable({ tableName: TABLE_RESOURCES$1 });
996
1212
  await this.#db.clearTable({ tableName: TABLE_OBSERVATIONAL_MEMORY$1 });
1213
+ await this.#db.clearTable({ tableName: TABLE_MEMORY_RUN_FENCES });
997
1214
  }
998
1215
  async getThreadById({ threadId, resourceId }) {
999
1216
  const row = await this.#db.load({
@@ -1003,8 +1220,8 @@ var MemoryConvex = class extends MemoryStorage {
1003
1220
  if (!row || resourceId !== void 0 && row.resourceId !== resourceId) return null;
1004
1221
  return parseStoredThread(row);
1005
1222
  }
1006
- async saveThread({ thread }) {
1007
- await this.#db.insert({
1223
+ async saveThread({ thread, fence }) {
1224
+ await this.#dbFor(fence, "saveThread").insert({
1008
1225
  tableName: TABLE_THREADS$1,
1009
1226
  record: {
1010
1227
  ...thread,
@@ -1013,8 +1230,8 @@ var MemoryConvex = class extends MemoryStorage {
1013
1230
  });
1014
1231
  return thread;
1015
1232
  }
1016
- async updateThread({ id, title, metadata }) {
1017
- const updated = await this.#db.updateThread({
1233
+ async updateThread({ id, title, metadata, fence }) {
1234
+ const updated = await this.#dbFor(fence, "updateThread").updateThread({
1018
1235
  id,
1019
1236
  title,
1020
1237
  metadata,
@@ -1204,8 +1421,9 @@ var MemoryConvex = class extends MemoryStorage {
1204
1421
  const filtered = (await this.#db.loadMany(TABLE_MESSAGES$1, messageIds)).map((row) => this.parseStoredMessage(row));
1205
1422
  return { messages: new MessageList().add(filtered, "memory").get.all.db() };
1206
1423
  }
1207
- async saveMessages({ messages }) {
1424
+ async saveMessages({ messages, fence }) {
1208
1425
  if (messages.length === 0) return { messages: [] };
1426
+ const db = this.#dbFor(fence, "saveMessages");
1209
1427
  const normalized = messages.map((message) => {
1210
1428
  if (!message.threadId) throw new Error("Thread ID is required");
1211
1429
  if (!message.resourceId) throw new Error("Resource ID is required");
@@ -1220,21 +1438,23 @@ var MemoryConvex = class extends MemoryStorage {
1220
1438
  resourceId: message.resourceId
1221
1439
  };
1222
1440
  });
1223
- await this.#db.batchInsert({
1441
+ await db.batchInsert({
1224
1442
  tableName: TABLE_MESSAGES$1,
1225
1443
  records: normalized
1226
1444
  });
1227
1445
  const threadIds = [...new Set(messages.map((m) => m.threadId).filter(Boolean))];
1228
1446
  const now = /* @__PURE__ */ new Date();
1229
- for (const threadId of threadIds) await this.#db.patch({
1447
+ for (const threadId of threadIds) await db.patch({
1230
1448
  tableName: TABLE_THREADS$1,
1231
1449
  id: threadId,
1232
1450
  record: { updatedAt: now.toISOString() }
1233
1451
  });
1234
1452
  return { messages: new MessageList().add(messages, "memory").get.all.db() };
1235
1453
  }
1236
- async updateMessages({ messages }) {
1454
+ async updateMessages({ messages, fence }) {
1237
1455
  if (messages.length === 0) return [];
1456
+ const check = this.#runFenceCheck(fence, "updateMessages");
1457
+ const db = this.#db.fenced(check);
1238
1458
  const existingRows = await this.#db.loadMany(TABLE_MESSAGES$1, messages.map((message) => message.id));
1239
1459
  const existing = new Map(existingRows.map((row) => [row.id, row]));
1240
1460
  const updated = [];
@@ -1262,22 +1482,26 @@ var MemoryConvex = class extends MemoryStorage {
1262
1482
  };
1263
1483
  current.content = JSON.stringify(mergedContent);
1264
1484
  }
1265
- await this.#db.insert({
1485
+ await db.insert({
1266
1486
  tableName: TABLE_MESSAGES$1,
1267
1487
  record: current
1268
1488
  });
1269
1489
  updated.push(this.parseStoredMessage(current));
1270
1490
  }
1491
+ if (updated.length === 0) {
1492
+ await assertRunFence(this.#client, check);
1493
+ return updated;
1494
+ }
1271
1495
  const now = /* @__PURE__ */ new Date();
1272
- for (const threadId of affectedThreadIds) await this.#db.patch({
1496
+ for (const threadId of affectedThreadIds) await db.patch({
1273
1497
  tableName: TABLE_THREADS$1,
1274
1498
  id: threadId,
1275
1499
  record: { updatedAt: now.toISOString() }
1276
1500
  });
1277
1501
  return updated;
1278
1502
  }
1279
- async deleteMessages(messageIds) {
1280
- await this.#db.deleteMany(TABLE_MESSAGES$1, messageIds);
1503
+ async deleteMessages(messageIds, options) {
1504
+ await this.#dbFor(options?.fence, "deleteMessages").deleteMany(TABLE_MESSAGES$1, messageIds);
1281
1505
  }
1282
1506
  async saveResource({ resource }) {
1283
1507
  const record = {
@@ -1300,9 +1524,9 @@ var MemoryConvex = class extends MemoryStorage {
1300
1524
  if (!record) return null;
1301
1525
  return parseStoredResource(record);
1302
1526
  }
1303
- async updateResource({ resourceId, workingMemory, metadata }) {
1527
+ async updateResource({ resourceId, workingMemory, metadata, fence }) {
1304
1528
  const now = /* @__PURE__ */ new Date();
1305
- return parseStoredResource(await this.#db.updateResource({
1529
+ return parseStoredResource(await this.#dbFor(fence, "updateResource").updateResource({
1306
1530
  resourceId,
1307
1531
  workingMemory,
1308
1532
  metadata,
@@ -1463,9 +1687,9 @@ var MemoryConvex = class extends MemoryStorage {
1463
1687
  })).map((doc) => parseStoredOMRecord(doc));
1464
1688
  }
1465
1689
  async initializeObservationalMemory(input) {
1466
- const id = crypto.randomUUID();
1467
1690
  const now = /* @__PURE__ */ new Date();
1468
1691
  const lookupKey = this.getOMKey(input.threadId, input.resourceId);
1692
+ const id = crypto.randomUUID();
1469
1693
  const record = {
1470
1694
  id,
1471
1695
  scope: input.scope,
@@ -1487,38 +1711,36 @@ var MemoryConvex = class extends MemoryStorage {
1487
1711
  lastBufferedAtTokens: 0,
1488
1712
  lastBufferedAtTime: null,
1489
1713
  config: input.config,
1490
- observedTimezone: input.observedTimezone
1714
+ observedTimezone: input.observedTimezone,
1715
+ supersededBy: null
1491
1716
  };
1492
- await this.#db.insert({
1493
- tableName: TABLE_OBSERVATIONAL_MEMORY$1,
1494
- record: {
1495
- id,
1496
- lookupKey,
1497
- scope: input.scope,
1498
- resourceId: input.resourceId,
1499
- threadId: input.threadId || null,
1500
- activeObservations: "",
1501
- activeObservationsPendingUpdate: null,
1502
- originType: "initial",
1503
- config: JSON.stringify(input.config ?? {}),
1504
- generationCount: 0,
1505
- lastObservedAt: null,
1506
- lastReflectionAt: null,
1507
- pendingMessageTokens: 0,
1508
- totalTokensObserved: 0,
1509
- observationTokenCount: 0,
1510
- isObserving: false,
1511
- isReflecting: false,
1512
- isBufferingObservation: false,
1513
- isBufferingReflection: false,
1514
- lastBufferedAtTokens: 0,
1515
- lastBufferedAtTime: null,
1516
- observedTimezone: input.observedTimezone || null,
1517
- createdAt: now.toISOString(),
1518
- updatedAt: now.toISOString()
1519
- }
1717
+ const doc = await this.#db.omInitialize({
1718
+ id,
1719
+ lookupKey,
1720
+ scope: input.scope,
1721
+ resourceId: input.resourceId,
1722
+ threadId: input.threadId || null,
1723
+ activeObservations: "",
1724
+ activeObservationsPendingUpdate: null,
1725
+ originType: "initial",
1726
+ config: JSON.stringify(input.config ?? {}),
1727
+ generationCount: 0,
1728
+ lastObservedAt: null,
1729
+ lastReflectionAt: null,
1730
+ pendingMessageTokens: 0,
1731
+ totalTokensObserved: 0,
1732
+ observationTokenCount: 0,
1733
+ isObserving: false,
1734
+ isReflecting: false,
1735
+ isBufferingObservation: false,
1736
+ isBufferingReflection: false,
1737
+ lastBufferedAtTokens: 0,
1738
+ lastBufferedAtTime: null,
1739
+ observedTimezone: input.observedTimezone || null,
1740
+ createdAt: now.toISOString(),
1741
+ updatedAt: now.toISOString()
1520
1742
  });
1521
- return record;
1743
+ return doc ? parseStoredOMRecord(doc) : record;
1522
1744
  }
1523
1745
  async insertObservationalMemoryRecord(record) {
1524
1746
  const lookupKey = this.getOMKey(record.threadId, record.resourceId);
@@ -1554,22 +1776,30 @@ var MemoryConvex = class extends MemoryStorage {
1554
1776
  lastBufferedAtTime: record.lastBufferedAtTime ? toISO(record.lastBufferedAtTime) : null,
1555
1777
  observedTimezone: record.observedTimezone || null,
1556
1778
  metadata: record.metadata ? JSON.stringify(record.metadata) : null,
1779
+ supersededBy: record.supersededBy ?? null,
1557
1780
  createdAt: toISO(record.createdAt),
1558
1781
  updatedAt: toISO(record.updatedAt)
1559
1782
  }
1560
1783
  });
1561
1784
  }
1562
1785
  async updateActiveObservations(input) {
1563
- await this.#db.omUpdateActive({
1786
+ assertActiveObservationsApplied(await this.commitActiveObservations(input), input.id);
1787
+ }
1788
+ async commitActiveObservations(input) {
1789
+ return await this.#dbFor(void 0, "commitActiveObservations").omUpdateActive({
1564
1790
  id: input.id,
1565
1791
  observations: input.observations,
1566
1792
  tokenCount: input.tokenCount,
1567
1793
  lastObservedAt: toISO(input.lastObservedAt),
1568
1794
  observedMessageIds: input.observedMessageIds ?? null,
1569
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1570
- });
1795
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1796
+ ...input.expectedActiveObservations !== void 0 ? { expectedActiveObservations: input.expectedActiveObservations } : {}
1797
+ }) ?? { applied: true };
1571
1798
  }
1572
1799
  async updateBufferedObservations(input) {
1800
+ await this.appendBufferedObservations(input);
1801
+ }
1802
+ async appendBufferedObservations(input) {
1573
1803
  const chunk = {
1574
1804
  id: `ombuf-${crypto.randomUUID()}`,
1575
1805
  cycleId: input.chunk.cycleId,
@@ -1585,15 +1815,18 @@ var MemoryConvex = class extends MemoryStorage {
1585
1815
  extractedValues: input.chunk.extractedValues,
1586
1816
  extractionFailures: input.chunk.extractionFailures
1587
1817
  };
1588
- await this.#db.omAppendBufferedChunk({
1818
+ return await this.#dbFor(void 0, "appendBufferedObservations").omAppendBufferedChunk({
1589
1819
  id: input.id,
1590
1820
  chunk,
1591
1821
  lastBufferedAtTime: input.lastBufferedAtTime ? toISO(input.lastBufferedAtTime) : void 0,
1592
1822
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1593
- });
1823
+ }) ?? {
1824
+ persisted: true,
1825
+ recordId: input.id
1826
+ };
1594
1827
  }
1595
1828
  async swapBufferedToActive(input) {
1596
- return this.#db.omSwapBuffered({
1829
+ return this.#dbFor(void 0, "swapBufferedToActive").omSwapBuffered({
1597
1830
  id: input.id,
1598
1831
  activationRatio: input.activationRatio,
1599
1832
  messageTokensThreshold: input.messageTokensThreshold,
@@ -1604,68 +1837,36 @@ var MemoryConvex = class extends MemoryStorage {
1604
1837
  now: (/* @__PURE__ */ new Date()).toISOString()
1605
1838
  });
1606
1839
  }
1607
- async createReflectionGeneration(input) {
1608
- const id = crypto.randomUUID();
1609
- const now = /* @__PURE__ */ new Date();
1610
- const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
1611
- const record = {
1612
- id,
1613
- scope: input.currentRecord.scope,
1614
- threadId: input.currentRecord.threadId,
1615
- resourceId: input.currentRecord.resourceId,
1616
- createdAt: now,
1617
- updatedAt: now,
1618
- lastObservedAt: input.currentRecord.lastObservedAt,
1619
- originType: "reflection",
1620
- generationCount: input.currentRecord.generationCount + 1,
1621
- activeObservations: input.reflection,
1622
- totalTokensObserved: input.currentRecord.totalTokensObserved,
1623
- observationTokenCount: input.tokenCount,
1624
- pendingMessageTokens: 0,
1625
- isReflecting: false,
1626
- isObserving: false,
1627
- isBufferingObservation: false,
1628
- isBufferingReflection: false,
1629
- lastBufferedAtTokens: 0,
1630
- lastBufferedAtTime: null,
1631
- config: input.currentRecord.config,
1632
- metadata: input.currentRecord.metadata,
1633
- observedTimezone: input.currentRecord.observedTimezone
1840
+ /** The caller's snapshot as the server's rollover ops expect it. */
1841
+ #serializeCurrentRecord(currentRecord) {
1842
+ return {
1843
+ id: currentRecord.id,
1844
+ lookupKey: this.getOMKey(currentRecord.threadId, currentRecord.resourceId),
1845
+ scope: currentRecord.scope,
1846
+ threadId: currentRecord.threadId || null,
1847
+ resourceId: currentRecord.resourceId,
1848
+ config: JSON.stringify(currentRecord.config ?? {}),
1849
+ metadata: currentRecord.metadata ? JSON.stringify(currentRecord.metadata) : null,
1850
+ observedTimezone: currentRecord.observedTimezone || null,
1851
+ lastObservedAt: currentRecord.lastObservedAt ? toISO(currentRecord.lastObservedAt) : null,
1852
+ totalTokensObserved: currentRecord.totalTokensObserved,
1853
+ generationCount: currentRecord.generationCount,
1854
+ activeObservations: currentRecord.activeObservations ?? "",
1855
+ observationTokenCount: currentRecord.observationTokenCount ?? 0
1634
1856
  };
1635
- await this.#db.insert({
1636
- tableName: TABLE_OBSERVATIONAL_MEMORY$1,
1637
- record: {
1638
- id,
1639
- lookupKey,
1640
- scope: record.scope,
1641
- resourceId: record.resourceId,
1642
- threadId: record.threadId || null,
1643
- activeObservations: input.reflection,
1644
- activeObservationsPendingUpdate: null,
1645
- originType: "reflection",
1646
- config: JSON.stringify(record.config ?? {}),
1647
- generationCount: record.generationCount,
1648
- lastObservedAt: record.lastObservedAt ? toISO(record.lastObservedAt) : null,
1649
- lastReflectionAt: now.toISOString(),
1650
- pendingMessageTokens: 0,
1651
- totalTokensObserved: record.totalTokensObserved,
1652
- observationTokenCount: record.observationTokenCount,
1653
- isObserving: false,
1654
- isReflecting: false,
1655
- isBufferingObservation: false,
1656
- isBufferingReflection: false,
1657
- lastBufferedAtTokens: 0,
1658
- lastBufferedAtTime: null,
1659
- observedTimezone: record.observedTimezone || null,
1660
- metadata: record.metadata ? JSON.stringify(record.metadata) : null,
1661
- createdAt: now.toISOString(),
1662
- updatedAt: now.toISOString()
1663
- }
1857
+ }
1858
+ async createReflectionGeneration(input) {
1859
+ const doc = await this.#dbFor(void 0, "createReflectionGeneration").omCreateReflectionGeneration({
1860
+ currentRecord: this.#serializeCurrentRecord(input.currentRecord),
1861
+ newId: input.newRecordId ?? crypto.randomUUID(),
1862
+ reflection: input.reflection,
1863
+ tokenCount: input.tokenCount,
1864
+ now: (/* @__PURE__ */ new Date()).toISOString()
1664
1865
  });
1665
- return record;
1866
+ return doc ? parseStoredOMRecord(doc) : input.currentRecord;
1666
1867
  }
1667
1868
  async updateBufferedReflection(input) {
1668
- await this.#db.omUpdateBufferedReflection({
1869
+ await this.#dbFor(void 0, "updateBufferedReflection").omUpdateBufferedReflection({
1669
1870
  id: input.id,
1670
1871
  reflection: input.reflection,
1671
1872
  tokenCount: input.tokenCount,
@@ -1675,26 +1876,13 @@ var MemoryConvex = class extends MemoryStorage {
1675
1876
  });
1676
1877
  }
1677
1878
  async swapBufferedReflectionToActive(input) {
1678
- const { currentRecord } = input;
1679
- const serializedCurrentRecord = {
1680
- id: currentRecord.id,
1681
- lookupKey: this.getOMKey(currentRecord.threadId, currentRecord.resourceId),
1682
- scope: currentRecord.scope,
1683
- threadId: currentRecord.threadId || null,
1684
- resourceId: currentRecord.resourceId,
1685
- config: JSON.stringify(currentRecord.config ?? {}),
1686
- metadata: currentRecord.metadata ? JSON.stringify(currentRecord.metadata) : null,
1687
- observedTimezone: currentRecord.observedTimezone || null,
1688
- lastObservedAt: currentRecord.lastObservedAt ? toISO(currentRecord.lastObservedAt) : null,
1689
- totalTokensObserved: currentRecord.totalTokensObserved,
1690
- generationCount: currentRecord.generationCount
1691
- };
1692
- return parseStoredOMRecord(await this.#db.omSwapBufferedReflection({
1693
- currentRecord: serializedCurrentRecord,
1694
- newId: crypto.randomUUID(),
1879
+ const doc = await this.#dbFor(void 0, "swapBufferedReflectionToActive").omSwapBufferedReflection({
1880
+ currentRecord: this.#serializeCurrentRecord(input.currentRecord),
1881
+ newId: input.newRecordId ?? crypto.randomUUID(),
1695
1882
  tokenCount: input.tokenCount,
1696
1883
  now: (/* @__PURE__ */ new Date()).toISOString()
1697
- }));
1884
+ });
1885
+ return doc ? parseStoredOMRecord(doc) : input.currentRecord;
1698
1886
  }
1699
1887
  async setReflectingFlag(id, isReflecting) {
1700
1888
  if (!await this.#db.patch({
@@ -1717,15 +1905,12 @@ var MemoryConvex = class extends MemoryStorage {
1717
1905
  })) throw this.omRecordNotFound("SET_OBSERVING_FLAG", id);
1718
1906
  }
1719
1907
  async setBufferingObservationFlag(id, isBuffering, lastBufferedAtTokens) {
1720
- if (!await this.#db.patch({
1721
- tableName: "mastra_observational_memory",
1908
+ await this.#db.omSetBufferingObservationFlag({
1722
1909
  id,
1723
- record: {
1724
- isBufferingObservation: isBuffering,
1725
- ...lastBufferedAtTokens !== void 0 ? { lastBufferedAtTokens } : {},
1726
- updatedAt: /* @__PURE__ */ new Date()
1727
- }
1728
- })) throw this.omRecordNotFound("SET_BUFFERING_OBSERVATION_FLAG", id);
1910
+ isBuffering,
1911
+ ...lastBufferedAtTokens !== void 0 ? { lastBufferedAtTokens } : {},
1912
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1913
+ });
1729
1914
  }
1730
1915
  async setBufferingReflectionFlag(id, isBuffering) {
1731
1916
  if (!await this.#db.patch({
@@ -1748,14 +1933,11 @@ var MemoryConvex = class extends MemoryStorage {
1748
1933
  tokenCount
1749
1934
  }
1750
1935
  });
1751
- if (!await this.#db.patch({
1752
- tableName: "mastra_observational_memory",
1936
+ await this.#db.omSetPendingMessageTokens({
1753
1937
  id,
1754
- record: {
1755
- pendingMessageTokens: tokenCount,
1756
- updatedAt: /* @__PURE__ */ new Date()
1757
- }
1758
- })) throw this.omRecordNotFound("SET_PENDING_MESSAGE_TOKENS", id);
1938
+ tokenCount,
1939
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1940
+ });
1759
1941
  }
1760
1942
  async clearObservationalMemory(threadId, resourceId) {
1761
1943
  const lookupKey = this.getOMKey(threadId, resourceId);
@@ -2090,25 +2272,52 @@ var ScoresConvex = class extends ScoresStorage {
2090
2272
  //#region src/storage/domains/workflows/index.ts
2091
2273
  var WorkflowsConvex = class extends WorkflowsStorage {
2092
2274
  #db;
2275
+ #client;
2093
2276
  constructor(config) {
2094
2277
  super();
2095
- const client = resolveConvexConfig(config);
2096
- this.#db = new ConvexDB(client);
2278
+ this.#client = resolveConvexConfig(config);
2279
+ this.#db = new ConvexDB(this.#client);
2097
2280
  }
2098
2281
  supportsConcurrentUpdates() {
2099
2282
  return true;
2100
2283
  }
2284
+ supportsRunFencing() {
2285
+ return true;
2286
+ }
2101
2287
  async init() {}
2102
2288
  async dangerouslyClearAll() {
2103
2289
  await this.#db.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT$1 });
2290
+ await this.#db.clearTable({ tableName: TABLE_WORKFLOW_RUN_OWNERS });
2291
+ }
2292
+ /** The store to write a run through: fenced when the write carries or inherits a fence. */
2293
+ #dbFor(fence, runId, operation) {
2294
+ const resolved = resolveRunFence(this, fence, runId);
2295
+ const check = resolved && {
2296
+ claims: TABLE_WORKFLOW_RUN_OWNERS,
2297
+ fence: resolved,
2298
+ operation
2299
+ };
2300
+ return this.#db.fenced(check);
2104
2301
  }
2105
- async updateWorkflowResults(args) {
2106
- return this.#db.mergeWorkflowStepResult(args);
2302
+ async claimRunOwnership(args) {
2303
+ return claimRunOwnership(this.#client, args);
2107
2304
  }
2108
- async updateWorkflowState(args) {
2109
- return this.#db.mergeWorkflowState(args);
2305
+ async renewRunOwnership(args) {
2306
+ return renewRunOwnership(this.#client, args);
2110
2307
  }
2111
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt }) {
2308
+ async releaseRunOwnership(fence) {
2309
+ return releaseRunOwnership(this.#client, fence);
2310
+ }
2311
+ async getRunOwnership({ runId }) {
2312
+ return getRunOwnership(this.#client, runId);
2313
+ }
2314
+ async updateWorkflowResults({ fence, ...args }) {
2315
+ return this.#dbFor(fence, args.runId, "updateWorkflowResults").mergeWorkflowStepResult(args);
2316
+ }
2317
+ async updateWorkflowState({ fence, ...args }) {
2318
+ return this.#dbFor(fence, args.runId, "updateWorkflowState").mergeWorkflowState(args);
2319
+ }
2320
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
2112
2321
  const now = /* @__PURE__ */ new Date();
2113
2322
  const existing = await this.#db.load({
2114
2323
  tableName: TABLE_WORKFLOW_SNAPSHOT$1,
@@ -2117,7 +2326,7 @@ var WorkflowsConvex = class extends WorkflowsStorage {
2117
2326
  run_id: runId
2118
2327
  }
2119
2328
  });
2120
- await this.#db.insert({
2329
+ await this.#dbFor(fence, runId, "persistWorkflowSnapshot").insert({
2121
2330
  tableName: TABLE_WORKFLOW_SNAPSHOT$1,
2122
2331
  record: {
2123
2332
  workflow_name: workflowName,
@@ -2199,8 +2408,8 @@ var WorkflowsConvex = class extends WorkflowsStorage {
2199
2408
  resourceId: match.resourceId
2200
2409
  };
2201
2410
  }
2202
- async deleteWorkflowRunById({ runId, workflowName }) {
2203
- await this.#db.deleteMany(TABLE_WORKFLOW_SNAPSHOT$1, [`${workflowName}-${runId}`]);
2411
+ async deleteWorkflowRunById({ runId, workflowName, fence }) {
2412
+ await this.#dbFor(fence, runId, "deleteWorkflowRunById").deleteMany(TABLE_WORKFLOW_SNAPSHOT$1, [`${workflowName}-${runId}`]);
2204
2413
  }
2205
2414
  async getRun(workflowName, runId) {
2206
2415
  return this.#db.load({