@mastra/libsql 1.26.0 → 1.26.1-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.
package/dist/index.js CHANGED
@@ -373,7 +373,7 @@ function isSingleConnectionDatabase({ url, syncUrl }) {
373
373
  * transactions rather than failing with `TRANSACTION_ACTIVE`.
374
374
  *
375
375
  * `transaction()` takes the gate and releases it when the transaction commits,
376
- * rolls back, or closes. `execute`, `batch`, `executeMultiple`, and `migrate`
376
+ * rolls back, or closes. `execute`, `batch`, `executeMultiple`, `migrate`, and `sync`
377
377
  * wait for the gate to be free before running but do not hold it — the driver
378
378
  * executes them synchronously on the connection, so they cannot interleave
379
379
  * with each other. Every other member passes through untouched.
@@ -424,7 +424,8 @@ function gateSingleConnectionClient(client) {
424
424
  case "execute":
425
425
  case "batch":
426
426
  case "executeMultiple":
427
- case "migrate": return (...args) => waitForGate(() => target[prop].apply(target, args));
427
+ case "migrate":
428
+ case "sync": return (...args) => waitForGate(() => target[prop].apply(target, args));
428
429
  default: {
429
430
  const value = Reflect.get(target, prop);
430
431
  return typeof value === "function" ? value.bind(target) : value;
@@ -9287,6 +9288,92 @@ var MCPServersLibSQL = class extends MCPServersStorage {
9287
9288
  }
9288
9289
  };
9289
9290
  //#endregion
9291
+ //#region ../../packages/_internal-core/dist/memory/index.js
9292
+ /**
9293
+ * Shared rules for observational memory lifecycle writes. Every storage adapter applies
9294
+ * these inside its own atomic section (lock, transaction, or conditional update), so the
9295
+ * decisions are identical across adapters.
9296
+ */
9297
+ /**
9298
+ * Observational memory columns that lifecycle writes depend on. Storage adapters merge these
9299
+ * into the table schema they get from `@mastra/core`, so the columns exist even when the
9300
+ * installed core predates them.
9301
+ */
9302
+ const OBSERVATIONAL_MEMORY_LIFECYCLE_COLUMNS = { supersededBy: {
9303
+ type: "text",
9304
+ nullable: true
9305
+ } };
9306
+ /** `schema` plus {@link OBSERVATIONAL_MEMORY_LIFECYCLE_COLUMNS}; `undefined` when `schema` is. */
9307
+ function withObservationalMemoryLifecycleColumns(schema) {
9308
+ return schema ? {
9309
+ ...schema,
9310
+ ...OBSERVATIONAL_MEMORY_LIFECYCLE_COLUMNS
9311
+ } : void 0;
9312
+ }
9313
+ /**
9314
+ * A buffered chunk stores `lastObservedAt = max message time + 1ms`. It is wholly covered
9315
+ * (every message it observed is already at or before the cursor) iff
9316
+ * `cursor >= chunk.lastObservedAt - 1ms`.
9317
+ */
9318
+ function isBufferedChunkCoveredByCursor(chunkLastObservedAt, cursor) {
9319
+ if (!cursor) return false;
9320
+ return new Date(cursor).getTime() >= new Date(chunkLastObservedAt).getTime() - 1;
9321
+ }
9322
+ /** The later of two cursors; `undefined` only when both are absent. */
9323
+ function maxObservationCursor(a, b) {
9324
+ if (!a) return b ? new Date(b) : void 0;
9325
+ if (!b) return new Date(a);
9326
+ return new Date(Math.max(new Date(a).getTime(), new Date(b).getTime()));
9327
+ }
9328
+ /**
9329
+ * Decide the new generation's text when a reflection built from `snapshot` commits against
9330
+ * the stored record.
9331
+ *
9332
+ * - Stored text equals the snapshot: the reflection replaces it.
9333
+ * - Stored text extends the snapshot (activation only appends): the appended tail is kept
9334
+ * after the reflection, and its tokens are added.
9335
+ * - Anything else (a non-append rewrite): `null` — the reflection must not be applied.
9336
+ */
9337
+ function planReflectionGenerationText(input) {
9338
+ const stored = input.storedObservations ?? "";
9339
+ const snapshot = input.snapshotObservations ?? "";
9340
+ if (stored === snapshot) return {
9341
+ mode: "equal",
9342
+ observations: input.reflection,
9343
+ tokenCount: input.tokenCount
9344
+ };
9345
+ if (!stored.startsWith(snapshot)) return null;
9346
+ const tail = stored.slice(snapshot.length).trimStart();
9347
+ if (!tail.trim()) return {
9348
+ mode: "equal",
9349
+ observations: input.reflection,
9350
+ tokenCount: input.tokenCount
9351
+ };
9352
+ return {
9353
+ mode: "append",
9354
+ observations: input.reflection ? `${input.reflection}\n\n${tail}` : tail,
9355
+ tokenCount: input.tokenCount + Math.max(0, (input.storedObservationTokenCount ?? 0) - (input.snapshotObservationTokenCount ?? 0))
9356
+ };
9357
+ }
9358
+ /**
9359
+ * Backs `updateActiveObservations` in adapters that implement `commitActiveObservations`.
9360
+ * Callers of `updateActiveObservations` (which resolves `void`) can't see a commit result, so a
9361
+ * write that was not applied throws instead of resolving as if it had committed.
9362
+ */
9363
+ function assertActiveObservationsApplied(result, id) {
9364
+ if (result.applied) return;
9365
+ 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`);
9366
+ }
9367
+ /**
9368
+ * Whether the stored text can still accept a reflection built from `snapshot`
9369
+ * (equal, or only appended to since).
9370
+ */
9371
+ function isAppendOnlySince(storedObservations, snapshotObservations) {
9372
+ const stored = storedObservations ?? "";
9373
+ const snapshot = snapshotObservations ?? "";
9374
+ return stored === snapshot || stored.startsWith(snapshot);
9375
+ }
9376
+ //#endregion
9290
9377
  //#region src/storage/domains/memory/index.ts
9291
9378
  /**
9292
9379
  * Local constant for the observational memory table name.
@@ -9294,6 +9381,13 @@ var MCPServersLibSQL = class extends MCPServersStorage {
9294
9381
  * versions that don't export TABLE_OBSERVATIONAL_MEMORY.
9295
9382
  */
9296
9383
  const OM_TABLE = "mastra_observational_memory";
9384
+ /** Canonical head order: newest generation, then earliest createdAt, then lowest id. */
9385
+ const OM_HEAD_ORDER = `"generationCount" DESC, "createdAt" ASC, id ASC`;
9386
+ /** Lifecycle writes aimed at a retired record follow `supersededBy` to the head at most this many times. */
9387
+ const OM_MAX_HEAD_HOPS = 3;
9388
+ /** Optimistic lifecycle writes rerun at most this many times when another process changes the row. */
9389
+ const OM_MAX_CAS_ATTEMPTS = 10;
9390
+ const OM_CAS_CONFLICT = Symbol("om-cas-conflict");
9297
9391
  function addSqliteMetadataFilter(conditions, params, metadataFilter) {
9298
9392
  if (!metadataFilter) return;
9299
9393
  for (const [key, value] of Object.entries(metadataFilter)) {
@@ -9359,11 +9453,13 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
9359
9453
  }
9360
9454
  };
9361
9455
  #client;
9456
+ #embeddedReplica;
9362
9457
  #db;
9363
9458
  constructor(config) {
9364
9459
  super();
9365
9460
  const client = resolveClient(config);
9366
9461
  this.#client = client;
9462
+ this.#embeddedReplica = config.embeddedReplica === true;
9367
9463
  this.#db = new LibSQLDB({
9368
9464
  client,
9369
9465
  maxRetries: config.maxRetries,
@@ -9402,7 +9498,7 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
9402
9498
  tableName: TABLE_MEMORY_RUN_FENCES,
9403
9499
  schema: RUN_FENCING_TABLE_SCHEMAS[TABLE_MEMORY_RUN_FENCES]
9404
9500
  });
9405
- const omSchema = OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE];
9501
+ const omSchema = withObservationalMemoryLifecycleColumns(OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE]);
9406
9502
  if (omSchema) {
9407
9503
  await this.#db.createTable({
9408
9504
  tableName: OM_TABLE,
@@ -9426,7 +9522,8 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
9426
9522
  "isBufferingReflection",
9427
9523
  "lastBufferedAtTokens",
9428
9524
  "lastBufferedAtTime",
9429
- "metadata"
9525
+ "metadata",
9526
+ "supersededBy"
9430
9527
  ]
9431
9528
  });
9432
9529
  }
@@ -9442,10 +9539,13 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
9442
9539
  sql: `CREATE INDEX IF NOT EXISTS idx_messages_thread_resource_created_at ON ${TABLE_MESSAGES} (thread_id, "resourceId", "createdAt")`,
9443
9540
  args: []
9444
9541
  }], "write");
9445
- if (omSchema) await this.#client.execute({
9446
- sql: `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON "${OM_TABLE}" ("lookupKey")`,
9447
- args: []
9448
- });
9542
+ if (omSchema) {
9543
+ await this.#client.execute({
9544
+ sql: `CREATE INDEX IF NOT EXISTS idx_om_lookup_key ON "${OM_TABLE}" ("lookupKey")`,
9545
+ args: []
9546
+ });
9547
+ await this.#backfillSupersededBy();
9548
+ }
9449
9549
  }
9450
9550
  async dangerouslyClearAll() {
9451
9551
  await this.init();
@@ -10579,18 +10679,218 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10579
10679
  config: row.config ? JSON.parse(row.config) : {},
10580
10680
  metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
10581
10681
  observedMessageIds: row.observedMessageIds ? JSON.parse(row.observedMessageIds) : void 0,
10582
- observedTimezone: row.observedTimezone || void 0
10682
+ observedTimezone: row.observedTimezone || void 0,
10683
+ supersededBy: row.supersededBy ?? null
10583
10684
  };
10584
10685
  }
10686
+ /**
10687
+ * Runs one optimistic lifecycle write under the client write lock (in-process ordering).
10688
+ * `attempt` reads the rows it needs, then writes with statements guarded by those reads
10689
+ * (`#readOMRow`'s guard); it returns `OM_CAS_CONFLICT` when a guard matched no row because
10690
+ * another process changed the row in between, and the attempt reruns on fresh state.
10691
+ *
10692
+ * No interactive transaction is held across `await`s: the `libsql` driver is synchronous, so
10693
+ * an open write transaction makes any other connection's write in this process block the
10694
+ * event loop until `busy_timeout` (SQLITE_BUSY). Each statement or batch here runs in one call.
10695
+ *
10696
+ * With `fencedOperation`, a write made inside a run fence scope is fenced: each attempt first
10697
+ * checks the run's claim, and the attempt's reads put the claim into their guards, so a claim
10698
+ * taken over between the check and the write turns the write into a conflict and the next
10699
+ * attempt's check rejects it.
10700
+ */
10701
+ async #withOMCas(operation, attempt, fencedOperation) {
10702
+ const check = fencedOperation ? this.#runFenceCheck(void 0, fencedOperation) : void 0;
10703
+ return this.#write(operation, async () => {
10704
+ for (let i = 0; i < OM_MAX_CAS_ATTEMPTS; i++) {
10705
+ if (check) await assertRunFence(this.#client, check);
10706
+ const result = await attempt(i, check);
10707
+ if (result !== OM_CAS_CONFLICT) return result;
10708
+ if (this.#embeddedReplica) await this.#client.sync?.();
10709
+ }
10710
+ throw new MastraError({
10711
+ id: createStorageErrorId("LIBSQL", operation, "CONFLICT"),
10712
+ text: `Observational memory write kept conflicting with concurrent writers (${OM_MAX_CAS_ATTEMPTS} attempts)`,
10713
+ domain: ErrorDomain.STORAGE,
10714
+ category: ErrorCategory.THIRD_PARTY
10715
+ });
10716
+ });
10717
+ }
10718
+ /**
10719
+ * Whether a missing row may only be missing from this embedded replica's local copy: the first
10720
+ * attempt reads before any sync, so it returns a conflict to sync and read again instead of
10721
+ * acting on the absence.
10722
+ */
10723
+ #mayBeUnsynced(attemptIndex) {
10724
+ return this.#embeddedReplica && attemptIndex === 0;
10725
+ }
10726
+ /**
10727
+ * With `check`, the guard also requires the run's claim to still match it, so a guarded write
10728
+ * made after the claim was taken over matches no row.
10729
+ */
10730
+ #toOMRow(result, check) {
10731
+ const row = result.rows?.[0];
10732
+ if (!row) return null;
10733
+ const columns = result.columns;
10734
+ const guard = {
10735
+ sql: columns.map((c) => `"${c}" IS ?`).join(" AND "),
10736
+ args: columns.map((c) => row[c])
10737
+ };
10738
+ if (check) {
10739
+ guard.sql += ` AND EXISTS (SELECT 1 FROM ${check.claimsTable} WHERE runId = ? AND generation = ? AND ownerId = ?)`;
10740
+ guard.args.push(check.fence.runId, check.fence.generation, check.fence.ownerId);
10741
+ }
10742
+ return {
10743
+ record: this.parseOMRow(row),
10744
+ guard
10745
+ };
10746
+ }
10747
+ async #readOMRow(id, check) {
10748
+ return this.#toOMRow(await this.#client.execute({
10749
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
10750
+ args: [id]
10751
+ }), check);
10752
+ }
10753
+ async #readOMHeadRow(lookupKey, check) {
10754
+ return this.#toOMRow(await this.#client.execute({
10755
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE "lookupKey" = ? ORDER BY ${OM_HEAD_ORDER} LIMIT 1`,
10756
+ args: [lookupKey]
10757
+ }), check);
10758
+ }
10759
+ /** The live row a lifecycle write aimed at `row` lands on: the row itself while live, otherwise the head. */
10760
+ async #resolveLiveOMRow(row, check) {
10761
+ let current = row;
10762
+ for (let hop = 0; current.record.supersededBy; hop++) {
10763
+ const head = hop < OM_MAX_HEAD_HOPS ? await this.#readOMHeadRow(this.getOMKey(current.record.threadId, current.record.resourceId), check) : null;
10764
+ if (!head || head.record.id === current.record.id) throw new MastraError({
10765
+ id: createStorageErrorId("LIBSQL", "RESOLVE_OBSERVATIONAL_MEMORY_HEAD", "FAILED"),
10766
+ text: `Observational memory record ${row.record.id} is superseded but no live head was found`,
10767
+ domain: ErrorDomain.STORAGE,
10768
+ category: ErrorCategory.THIRD_PARTY,
10769
+ details: { id: row.record.id }
10770
+ });
10771
+ current = head;
10772
+ }
10773
+ return current;
10774
+ }
10775
+ /**
10776
+ * Retire every live row of a lookup key that sorts after the canonical head, marking it
10777
+ * superseded by the head. Rows retired by older adapter versions (which never set
10778
+ * `supersededBy`) become frozen. One statement: the head it computes and the rows it retires
10779
+ * are read under the same SQLite write lock, so a concurrent rollover cannot leave a key
10780
+ * without a live row.
10781
+ */
10782
+ async #backfillSupersededBy() {
10783
+ const headOf = `(SELECT h.id FROM "${OM_TABLE}" h WHERE h."lookupKey" = "${OM_TABLE}"."lookupKey" ORDER BY ${OM_HEAD_ORDER} LIMIT 1)`;
10784
+ const duplicated = `SELECT "lookupKey" FROM "${OM_TABLE}" WHERE "supersededBy" IS NULL GROUP BY "lookupKey" HAVING COUNT(*) > 1`;
10785
+ if (!(await this.#client.execute({
10786
+ sql: duplicated,
10787
+ args: []
10788
+ })).rows?.length) return;
10789
+ await this.#write("backfillSupersededBy", () => this.#client.execute({
10790
+ sql: `UPDATE "${OM_TABLE}" SET "supersededBy" = ${headOf}
10791
+ WHERE "supersededBy" IS NULL AND "lookupKey" IN (${duplicated}) AND id <> ${headOf}`,
10792
+ args: []
10793
+ }));
10794
+ }
10795
+ /**
10796
+ * Create the next generation from the stored (live) row and retire it in one atomic batch,
10797
+ * guarded by the row as read. Buffered chunks move to the new generation (in order); the
10798
+ * cursor, buffering markers, flags, counters, config, and metadata carry over; buffered
10799
+ * reflection state does not (it is cleared on the retired row). Returns `OM_CAS_CONFLICT`
10800
+ * when the row changed since it was read.
10801
+ */
10802
+ async #rollOverObservationalMemory(storedRow, observations, tokenCount, newRecordId) {
10803
+ const stored = storedRow.record;
10804
+ const id = newRecordId ?? crypto.randomUUID();
10805
+ const now = /* @__PURE__ */ new Date();
10806
+ const chunks = Array.isArray(stored.bufferedObservationChunks) ? stored.bufferedObservationChunks : [];
10807
+ const lookupKey = this.getOMKey(stored.threadId, stored.resourceId);
10808
+ const record = {
10809
+ id,
10810
+ scope: stored.scope,
10811
+ threadId: stored.threadId,
10812
+ resourceId: stored.resourceId,
10813
+ createdAt: now,
10814
+ updatedAt: now,
10815
+ lastObservedAt: stored.lastObservedAt,
10816
+ originType: "reflection",
10817
+ generationCount: stored.generationCount + 1,
10818
+ activeObservations: observations,
10819
+ bufferedObservationChunks: chunks.length > 0 ? chunks : void 0,
10820
+ totalTokensObserved: stored.totalTokensObserved,
10821
+ observationTokenCount: tokenCount,
10822
+ pendingMessageTokens: stored.pendingMessageTokens,
10823
+ isReflecting: false,
10824
+ isObserving: false,
10825
+ isBufferingObservation: stored.isBufferingObservation,
10826
+ isBufferingReflection: false,
10827
+ lastBufferedAtTokens: stored.lastBufferedAtTokens,
10828
+ lastBufferedAtTime: stored.lastBufferedAtTime,
10829
+ config: stored.config,
10830
+ metadata: stored.metadata,
10831
+ observedTimezone: stored.observedTimezone,
10832
+ supersededBy: null
10833
+ };
10834
+ const [retired] = await this.#client.batch([{
10835
+ sql: `UPDATE "${OM_TABLE}" SET "supersededBy" = ?, "bufferedObservationChunks" = NULL,
10836
+ "bufferedReflection" = NULL, "bufferedReflectionTokens" = NULL,
10837
+ "bufferedReflectionInputTokens" = NULL, "reflectedObservationLineCount" = NULL, "updatedAt" = ?
10838
+ WHERE id = ? AND ${storedRow.guard.sql}`,
10839
+ args: [
10840
+ id,
10841
+ now.toISOString(),
10842
+ stored.id,
10843
+ ...storedRow.guard.args
10844
+ ]
10845
+ }, {
10846
+ sql: `INSERT INTO "${OM_TABLE}" (
10847
+ id, "lookupKey", scope, "resourceId", "threadId",
10848
+ "activeObservations", "activeObservationsPendingUpdate",
10849
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
10850
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
10851
+ "bufferedObservationChunks",
10852
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
10853
+ "observedTimezone", metadata, "supersededBy", "createdAt", "updatedAt"
10854
+ ) SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
10855
+ WHERE EXISTS (SELECT 1 FROM "${OM_TABLE}" WHERE id = ? AND "supersededBy" = ?)`,
10856
+ args: [
10857
+ id,
10858
+ lookupKey,
10859
+ record.scope,
10860
+ record.resourceId,
10861
+ record.threadId || null,
10862
+ observations,
10863
+ null,
10864
+ "reflection",
10865
+ JSON.stringify(record.config),
10866
+ record.generationCount,
10867
+ record.lastObservedAt ? record.lastObservedAt.toISOString() : null,
10868
+ now.toISOString(),
10869
+ record.pendingMessageTokens,
10870
+ record.totalTokensObserved,
10871
+ record.observationTokenCount,
10872
+ chunks.length > 0 ? JSON.stringify(chunks) : null,
10873
+ false,
10874
+ false,
10875
+ record.isBufferingObservation,
10876
+ false,
10877
+ record.lastBufferedAtTokens,
10878
+ record.lastBufferedAtTime ? new Date(record.lastBufferedAtTime).toISOString() : null,
10879
+ record.observedTimezone || null,
10880
+ record.metadata ? JSON.stringify(record.metadata) : null,
10881
+ null,
10882
+ now.toISOString(),
10883
+ now.toISOString(),
10884
+ stored.id,
10885
+ id
10886
+ ]
10887
+ }], "write");
10888
+ return retired.rowsAffected === 1 ? record : OM_CAS_CONFLICT;
10889
+ }
10585
10890
  async getObservationalMemory(threadId, resourceId) {
10586
10891
  try {
10587
10892
  const lookupKey = this.getOMKey(threadId, resourceId);
10588
- const result = await this.#client.execute({
10589
- sql: `SELECT * FROM "${OM_TABLE}" WHERE "lookupKey" = ? ORDER BY "generationCount" DESC LIMIT 1`,
10590
- args: [lookupKey]
10591
- });
10592
- if (!result.rows || result.rows.length === 0) return null;
10593
- return this.parseOMRow(result.rows[0]);
10893
+ return (await this.#readOMHeadRow(lookupKey))?.record ?? null;
10594
10894
  } catch (error) {
10595
10895
  throw new MastraError({
10596
10896
  id: createStorageErrorId("LIBSQL", "GET_OBSERVATIONAL_MEMORY", "FAILED"),
@@ -10664,9 +10964,9 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10664
10964
  }
10665
10965
  async initializeObservationalMemory(input) {
10666
10966
  try {
10967
+ const lookupKey = this.getOMKey(input.threadId, input.resourceId);
10667
10968
  const id = crypto.randomUUID();
10668
10969
  const now = /* @__PURE__ */ new Date();
10669
- const lookupKey = this.getOMKey(input.threadId, input.resourceId);
10670
10970
  const record = {
10671
10971
  id,
10672
10972
  scope: input.scope,
@@ -10688,45 +10988,52 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10688
10988
  lastBufferedAtTokens: 0,
10689
10989
  lastBufferedAtTime: null,
10690
10990
  config: input.config,
10691
- observedTimezone: input.observedTimezone
10991
+ observedTimezone: input.observedTimezone,
10992
+ supersededBy: null
10692
10993
  };
10693
- await this.#write("initializeObservationalMemory", () => this.#client.execute({
10694
- sql: `INSERT INTO "${OM_TABLE}" (
10994
+ return await this.#withOMCas("INITIALIZE_OBSERVATIONAL_MEMORY", async () => {
10995
+ const existing = (await this.#readOMHeadRow(lookupKey))?.record ?? null;
10996
+ if (existing) return existing;
10997
+ if ((await this.#client.execute({
10998
+ sql: `INSERT INTO "${OM_TABLE}" (
10695
10999
  id, "lookupKey", scope, "resourceId", "threadId",
10696
11000
  "activeObservations", "activeObservationsPendingUpdate",
10697
11001
  "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
10698
11002
  "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
10699
11003
  "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
10700
11004
  "observedTimezone", "createdAt", "updatedAt"
10701
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10702
- args: [
10703
- id,
10704
- lookupKey,
10705
- input.scope,
10706
- input.resourceId,
10707
- input.threadId || null,
10708
- "",
10709
- null,
10710
- "initial",
10711
- JSON.stringify(input.config),
10712
- 0,
10713
- null,
10714
- null,
10715
- 0,
10716
- 0,
10717
- 0,
10718
- false,
10719
- false,
10720
- false,
10721
- false,
10722
- 0,
10723
- null,
10724
- input.observedTimezone || null,
10725
- now.toISOString(),
10726
- now.toISOString()
10727
- ]
10728
- }));
10729
- return record;
11005
+ ) SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
11006
+ WHERE NOT EXISTS (SELECT 1 FROM "${OM_TABLE}" WHERE "lookupKey" = ? AND "supersededBy" IS NULL)`,
11007
+ args: [
11008
+ id,
11009
+ lookupKey,
11010
+ input.scope,
11011
+ input.resourceId,
11012
+ input.threadId || null,
11013
+ "",
11014
+ null,
11015
+ "initial",
11016
+ JSON.stringify(input.config),
11017
+ 0,
11018
+ null,
11019
+ null,
11020
+ 0,
11021
+ 0,
11022
+ 0,
11023
+ false,
11024
+ false,
11025
+ false,
11026
+ false,
11027
+ 0,
11028
+ null,
11029
+ input.observedTimezone || null,
11030
+ now.toISOString(),
11031
+ now.toISOString(),
11032
+ lookupKey
11033
+ ]
11034
+ })).rowsAffected === 1) return record;
11035
+ return (await this.#readOMHeadRow(lookupKey))?.record ?? OM_CAS_CONFLICT;
11036
+ });
10730
11037
  } catch (error) {
10731
11038
  throw new MastraError({
10732
11039
  id: createStorageErrorId("LIBSQL", "INITIALIZE_OBSERVATIONAL_MEMORY", "FAILED"),
@@ -10753,8 +11060,8 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10753
11060
  "reflectedObservationLineCount",
10754
11061
  "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection",
10755
11062
  "lastBufferedAtTokens", "lastBufferedAtTime",
10756
- "observedTimezone", metadata, "createdAt", "updatedAt"
10757
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
11063
+ "observedTimezone", metadata, "supersededBy", "createdAt", "updatedAt"
11064
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10758
11065
  args: [
10759
11066
  record.id,
10760
11067
  lookupKey,
@@ -10785,6 +11092,7 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10785
11092
  record.lastBufferedAtTime ? record.lastBufferedAtTime.toISOString() : null,
10786
11093
  record.observedTimezone || null,
10787
11094
  record.metadata ? JSON.stringify(record.metadata) : null,
11095
+ record.supersededBy ?? null,
10788
11096
  record.createdAt.toISOString(),
10789
11097
  record.updatedAt.toISOString()
10790
11098
  ]
@@ -10803,11 +11111,36 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10803
11111
  }
10804
11112
  }
10805
11113
  async updateActiveObservations(input) {
11114
+ assertActiveObservationsApplied(await this.commitActiveObservations(input), input.id);
11115
+ }
11116
+ async commitActiveObservations(input) {
10806
11117
  try {
10807
11118
  const now = /* @__PURE__ */ new Date();
10808
11119
  const observedMessageIdsJson = input.observedMessageIds ? JSON.stringify(input.observedMessageIds) : null;
10809
- if ((await this.#fencedWrite("updateActiveObservations", this.#runFenceCheck(void 0, "updateActiveObservations"), (writer) => writer.execute({
10810
- sql: `UPDATE "${OM_TABLE}" SET
11120
+ return await this.#withOMCas("UPDATE_ACTIVE_OBSERVATIONS", async (attemptIndex, check) => {
11121
+ const row = await this.#readOMRow(input.id, check);
11122
+ if (!row) {
11123
+ if (this.#mayBeUnsynced(attemptIndex)) return OM_CAS_CONFLICT;
11124
+ throw new MastraError({
11125
+ id: createStorageErrorId("LIBSQL", "UPDATE_ACTIVE_OBSERVATIONS", "NOT_FOUND"),
11126
+ text: `Observational memory record not found: ${input.id}`,
11127
+ domain: ErrorDomain.STORAGE,
11128
+ category: ErrorCategory.THIRD_PARTY,
11129
+ details: { id: input.id }
11130
+ });
11131
+ }
11132
+ const stored = row.record;
11133
+ if (stored.supersededBy) return {
11134
+ applied: false,
11135
+ reason: "retired"
11136
+ };
11137
+ if (input.expectedActiveObservations !== void 0 && input.expectedActiveObservations !== stored.activeObservations) return {
11138
+ applied: false,
11139
+ reason: "conflict"
11140
+ };
11141
+ const lastObservedAt = maxObservationCursor(stored.lastObservedAt, input.lastObservedAt);
11142
+ return (await this.#client.execute({
11143
+ sql: `UPDATE "${OM_TABLE}" SET
10811
11144
  "activeObservations" = ?,
10812
11145
  "lastObservedAt" = ?,
10813
11146
  "pendingMessageTokens" = 0,
@@ -10815,23 +11148,19 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10815
11148
  "totalTokensObserved" = "totalTokensObserved" + ?,
10816
11149
  "observedMessageIds" = ?,
10817
11150
  "updatedAt" = ?
10818
- WHERE id = ?`,
10819
- args: [
10820
- input.observations,
10821
- input.lastObservedAt.toISOString(),
10822
- input.tokenCount,
10823
- input.tokenCount,
10824
- observedMessageIdsJson,
10825
- now.toISOString(),
10826
- input.id
10827
- ]
10828
- }))).rowsAffected === 0) throw new MastraError({
10829
- id: createStorageErrorId("LIBSQL", "UPDATE_ACTIVE_OBSERVATIONS", "NOT_FOUND"),
10830
- text: `Observational memory record not found: ${input.id}`,
10831
- domain: ErrorDomain.STORAGE,
10832
- category: ErrorCategory.THIRD_PARTY,
10833
- details: { id: input.id }
10834
- });
11151
+ WHERE id = ? AND ${row.guard.sql}`,
11152
+ args: [
11153
+ input.observations,
11154
+ lastObservedAt.toISOString(),
11155
+ input.tokenCount,
11156
+ input.tokenCount,
11157
+ observedMessageIdsJson,
11158
+ now.toISOString(),
11159
+ input.id,
11160
+ ...row.guard.args
11161
+ ]
11162
+ })).rowsAffected === 1 ? { applied: true } : OM_CAS_CONFLICT;
11163
+ }, "commitActiveObservations");
10835
11164
  } catch (error) {
10836
11165
  if (error instanceof MastraError) throw error;
10837
11166
  throw new MastraError({
@@ -10844,19 +11173,23 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10844
11173
  }
10845
11174
  async createReflectionGeneration(input) {
10846
11175
  try {
10847
- const check = this.#runFenceCheck(void 0, "createReflectionGeneration");
10848
- return await this.#write("createReflectionGeneration", async () => {
10849
- const tx = await this.#client.transaction("write");
10850
- try {
10851
- if (check) await assertRunFence(tx, check);
10852
- const record = await this.#insertReflectionGeneration(tx, input);
10853
- await tx.commit();
10854
- return record;
10855
- } catch (error) {
10856
- if (!tx.closed) await tx.rollback();
10857
- throw error;
10858
- }
10859
- });
11176
+ return await this.#withOMCas("CREATE_REFLECTION_GENERATION", async (attemptIndex, check) => {
11177
+ const { currentRecord } = input;
11178
+ const row = await this.#readOMRow(currentRecord.id, check);
11179
+ if (!row) return this.#mayBeUnsynced(attemptIndex) ? OM_CAS_CONFLICT : currentRecord;
11180
+ const stored = row.record;
11181
+ if (stored.supersededBy) return (await this.#resolveLiveOMRow(row, check)).record;
11182
+ const plan = planReflectionGenerationText({
11183
+ storedObservations: stored.activeObservations,
11184
+ storedObservationTokenCount: stored.observationTokenCount,
11185
+ snapshotObservations: currentRecord.activeObservations,
11186
+ snapshotObservationTokenCount: currentRecord.observationTokenCount,
11187
+ reflection: input.reflection,
11188
+ tokenCount: input.tokenCount
11189
+ });
11190
+ if (!plan) return stored;
11191
+ return this.#rollOverObservationalMemory(row, plan.observations, plan.tokenCount, input.newRecordId);
11192
+ }, "createReflectionGeneration");
10860
11193
  } catch (error) {
10861
11194
  if (error instanceof MastraError) throw error;
10862
11195
  throw new MastraError({
@@ -10867,79 +11200,6 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
10867
11200
  }, error);
10868
11201
  }
10869
11202
  }
10870
- /**
10871
- * Inserts a new reflection-generation row. Called inside a locked write
10872
- * transaction — either from {@link createReflectionGeneration} (standalone)
10873
- * or from {@link swapBufferedReflectionToActive} (as part of a compound
10874
- * swap). Must not acquire the write lock itself; the caller owns it.
10875
- */
10876
- async #insertReflectionGeneration(tx, input) {
10877
- const id = crypto.randomUUID();
10878
- const now = /* @__PURE__ */ new Date();
10879
- const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
10880
- const record = {
10881
- id,
10882
- scope: input.currentRecord.scope,
10883
- threadId: input.currentRecord.threadId,
10884
- resourceId: input.currentRecord.resourceId,
10885
- createdAt: now,
10886
- updatedAt: now,
10887
- lastObservedAt: input.currentRecord.lastObservedAt,
10888
- originType: "reflection",
10889
- generationCount: input.currentRecord.generationCount + 1,
10890
- activeObservations: input.reflection,
10891
- totalTokensObserved: input.currentRecord.totalTokensObserved,
10892
- observationTokenCount: input.tokenCount,
10893
- pendingMessageTokens: 0,
10894
- isReflecting: false,
10895
- isObserving: false,
10896
- isBufferingObservation: false,
10897
- isBufferingReflection: false,
10898
- lastBufferedAtTokens: 0,
10899
- lastBufferedAtTime: null,
10900
- config: input.currentRecord.config,
10901
- metadata: input.currentRecord.metadata,
10902
- observedTimezone: input.currentRecord.observedTimezone
10903
- };
10904
- await tx.execute({
10905
- sql: `INSERT INTO "${OM_TABLE}" (
10906
- id, "lookupKey", scope, "resourceId", "threadId",
10907
- "activeObservations", "activeObservationsPendingUpdate",
10908
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
10909
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
10910
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
10911
- "observedTimezone", metadata, "createdAt", "updatedAt"
10912
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
10913
- args: [
10914
- id,
10915
- lookupKey,
10916
- record.scope,
10917
- record.resourceId,
10918
- record.threadId || null,
10919
- input.reflection,
10920
- null,
10921
- "reflection",
10922
- JSON.stringify(record.config),
10923
- input.currentRecord.generationCount + 1,
10924
- record.lastObservedAt?.toISOString() || null,
10925
- now.toISOString(),
10926
- record.pendingMessageTokens,
10927
- record.totalTokensObserved,
10928
- record.observationTokenCount,
10929
- false,
10930
- false,
10931
- false,
10932
- false,
10933
- 0,
10934
- null,
10935
- record.observedTimezone || null,
10936
- record.metadata ? JSON.stringify(record.metadata) : null,
10937
- now.toISOString(),
10938
- now.toISOString()
10939
- ]
10940
- });
10941
- return record;
10942
- }
10943
11203
  async setReflectingFlag(id, isReflecting) {
10944
11204
  try {
10945
11205
  if ((await this.#write("setReflectingFlag", () => this.#client.execute({
@@ -11006,38 +11266,40 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
11006
11266
  }
11007
11267
  async setBufferingObservationFlag(id, isBuffering, lastBufferedAtTokens) {
11008
11268
  try {
11009
- const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11010
- let sql;
11011
- let args;
11012
- if (lastBufferedAtTokens !== void 0) {
11013
- sql = `UPDATE "${OM_TABLE}" SET "isBufferingObservation" = ?, "lastBufferedAtTokens" = ?, "updatedAt" = ? WHERE id = ?`;
11014
- args = [
11015
- isBuffering,
11016
- lastBufferedAtTokens,
11017
- nowStr,
11018
- id
11019
- ];
11020
- } else {
11021
- sql = `UPDATE "${OM_TABLE}" SET "isBufferingObservation" = ?, "updatedAt" = ? WHERE id = ?`;
11022
- args = [
11023
- isBuffering,
11024
- nowStr,
11025
- id
11026
- ];
11027
- }
11028
- if ((await this.#write("setBufferingObservationFlag", () => this.#client.execute({
11029
- sql,
11030
- args
11031
- }))).rowsAffected === 0) throw new MastraError({
11032
- id: createStorageErrorId("LIBSQL", "SET_BUFFERING_OBSERVATION_FLAG", "NOT_FOUND"),
11033
- text: `Observational memory record not found: ${id}`,
11034
- domain: ErrorDomain.STORAGE,
11035
- category: ErrorCategory.THIRD_PARTY,
11036
- details: {
11037
- id,
11038
- isBuffering,
11039
- lastBufferedAtTokens: lastBufferedAtTokens ?? null
11269
+ await this.#withOMCas("SET_BUFFERING_OBSERVATION_FLAG", async (attemptIndex) => {
11270
+ const row = await this.#readOMRow(id);
11271
+ if (!row) {
11272
+ if (this.#mayBeUnsynced(attemptIndex)) return OM_CAS_CONFLICT;
11273
+ throw new MastraError({
11274
+ id: createStorageErrorId("LIBSQL", "SET_BUFFERING_OBSERVATION_FLAG", "NOT_FOUND"),
11275
+ text: `Observational memory record not found: ${id}`,
11276
+ domain: ErrorDomain.STORAGE,
11277
+ category: ErrorCategory.THIRD_PARTY,
11278
+ details: {
11279
+ id,
11280
+ isBuffering,
11281
+ lastBufferedAtTokens: lastBufferedAtTokens ?? null
11282
+ }
11283
+ });
11040
11284
  }
11285
+ const target = (await this.#resolveLiveOMRow(row)).record;
11286
+ const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11287
+ return (lastBufferedAtTokens !== void 0 ? await this.#client.execute({
11288
+ sql: `UPDATE "${OM_TABLE}" SET "isBufferingObservation" = ?, "lastBufferedAtTokens" = ?, "updatedAt" = ? WHERE id = ? AND "supersededBy" IS NULL`,
11289
+ args: [
11290
+ isBuffering,
11291
+ lastBufferedAtTokens,
11292
+ nowStr,
11293
+ target.id
11294
+ ]
11295
+ }) : await this.#client.execute({
11296
+ sql: `UPDATE "${OM_TABLE}" SET "isBufferingObservation" = ?, "updatedAt" = ? WHERE id = ? AND "supersededBy" IS NULL`,
11297
+ args: [
11298
+ isBuffering,
11299
+ nowStr,
11300
+ target.id
11301
+ ]
11302
+ })).rowsAffected === 1 ? void 0 : OM_CAS_CONFLICT;
11041
11303
  });
11042
11304
  } catch (error) {
11043
11305
  if (error instanceof MastraError) throw error;
@@ -11106,25 +11368,30 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
11106
11368
  }
11107
11369
  async setPendingMessageTokens(id, tokenCount) {
11108
11370
  try {
11109
- if ((await this.#write("setPendingMessageTokens", () => this.#client.execute({
11110
- sql: `UPDATE "${OM_TABLE}" SET
11111
- "pendingMessageTokens" = ?,
11112
- "updatedAt" = ?
11113
- WHERE id = ?`,
11114
- args: [
11115
- tokenCount,
11116
- (/* @__PURE__ */ new Date()).toISOString(),
11117
- id
11118
- ]
11119
- }))).rowsAffected === 0) throw new MastraError({
11120
- id: createStorageErrorId("LIBSQL", "SET_PENDING_MESSAGE_TOKENS", "NOT_FOUND"),
11121
- text: `Observational memory record not found: ${id}`,
11122
- domain: ErrorDomain.STORAGE,
11123
- category: ErrorCategory.THIRD_PARTY,
11124
- details: {
11125
- id,
11126
- tokenCount
11371
+ await this.#withOMCas("SET_PENDING_MESSAGE_TOKENS", async (attemptIndex) => {
11372
+ const row = await this.#readOMRow(id);
11373
+ if (!row) {
11374
+ if (this.#mayBeUnsynced(attemptIndex)) return OM_CAS_CONFLICT;
11375
+ throw new MastraError({
11376
+ id: createStorageErrorId("LIBSQL", "SET_PENDING_MESSAGE_TOKENS", "NOT_FOUND"),
11377
+ text: `Observational memory record not found: ${id}`,
11378
+ domain: ErrorDomain.STORAGE,
11379
+ category: ErrorCategory.THIRD_PARTY,
11380
+ details: {
11381
+ id,
11382
+ tokenCount
11383
+ }
11384
+ });
11127
11385
  }
11386
+ const target = (await this.#resolveLiveOMRow(row)).record;
11387
+ return (await this.#client.execute({
11388
+ sql: `UPDATE "${OM_TABLE}" SET "pendingMessageTokens" = ?, "updatedAt" = ? WHERE id = ? AND "supersededBy" IS NULL`,
11389
+ args: [
11390
+ tokenCount,
11391
+ (/* @__PURE__ */ new Date()).toISOString(),
11392
+ target.id
11393
+ ]
11394
+ })).rowsAffected === 1 ? void 0 : OM_CAS_CONFLICT;
11128
11395
  });
11129
11396
  } catch (error) {
11130
11397
  if (error instanceof MastraError) throw error;
@@ -11141,36 +11408,33 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
11141
11408
  }
11142
11409
  async updateObservationalMemoryConfig(input) {
11143
11410
  try {
11144
- await this.#write("updateObservationalMemoryConfig", async () => {
11145
- const tx = await this.#client.transaction("write");
11146
- try {
11147
- const selectResult = await tx.execute({
11148
- sql: `SELECT config FROM "${OM_TABLE}" WHERE id = ?`,
11149
- args: [input.id]
11150
- });
11151
- if (selectResult.rows.length === 0) throw new MastraError({
11411
+ await this.#withOMCas("UPDATE_OM_CONFIG", async (attemptIndex) => {
11412
+ const selectResult = await this.#client.execute({
11413
+ sql: `SELECT config FROM "${OM_TABLE}" WHERE id = ?`,
11414
+ args: [input.id]
11415
+ });
11416
+ if (selectResult.rows.length === 0) {
11417
+ if (this.#mayBeUnsynced(attemptIndex)) return OM_CAS_CONFLICT;
11418
+ throw new MastraError({
11152
11419
  id: createStorageErrorId("LIBSQL", "UPDATE_OM_CONFIG", "NOT_FOUND"),
11153
11420
  text: `Observational memory record not found: ${input.id}`,
11154
11421
  domain: ErrorDomain.STORAGE,
11155
11422
  category: ErrorCategory.THIRD_PARTY,
11156
11423
  details: { id: input.id }
11157
11424
  });
11158
- const row = selectResult.rows[0];
11159
- const existing = row.config ? JSON.parse(row.config) : {};
11160
- const merged = this.deepMergeConfig(existing, input.config);
11161
- await tx.execute({
11162
- sql: `UPDATE "${OM_TABLE}" SET config = ?, "updatedAt" = ? WHERE id = ?`,
11163
- args: [
11164
- JSON.stringify(merged),
11165
- (/* @__PURE__ */ new Date()).toISOString(),
11166
- input.id
11167
- ]
11168
- });
11169
- await tx.commit();
11170
- } catch (error) {
11171
- if (!tx.closed) await tx.rollback();
11172
- throw error;
11173
11425
  }
11426
+ const row = selectResult.rows[0];
11427
+ const existing = row.config ? JSON.parse(row.config) : {};
11428
+ const merged = this.deepMergeConfig(existing, input.config);
11429
+ return (await this.#client.execute({
11430
+ sql: `UPDATE "${OM_TABLE}" SET config = ?, "updatedAt" = ? WHERE id = ? AND config IS ?`,
11431
+ args: [
11432
+ JSON.stringify(merged),
11433
+ (/* @__PURE__ */ new Date()).toISOString(),
11434
+ input.id,
11435
+ row.config ?? null
11436
+ ]
11437
+ })).rowsAffected === 1 ? void 0 : OM_CAS_CONFLICT;
11174
11438
  });
11175
11439
  } catch (error) {
11176
11440
  if (error instanceof MastraError) throw error;
@@ -11183,78 +11447,64 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
11183
11447
  }
11184
11448
  }
11185
11449
  async updateBufferedObservations(input) {
11450
+ await this.appendBufferedObservations(input);
11451
+ }
11452
+ async appendBufferedObservations(input) {
11186
11453
  try {
11187
11454
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11188
- const check = this.#runFenceCheck(void 0, "updateBufferedObservations");
11189
- await this.#write("updateBufferedObservations", async () => {
11190
- const tx = await this.#client.transaction("write");
11191
- try {
11192
- if (check) await assertRunFence(tx, check);
11193
- const current = await tx.execute({
11194
- sql: `SELECT "bufferedObservationChunks" FROM "${OM_TABLE}" WHERE id = ?`,
11195
- args: [input.id]
11196
- });
11197
- if (!current.rows || current.rows.length === 0) throw new MastraError({
11198
- id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
11199
- text: `Observational memory record not found: ${input.id}`,
11200
- domain: ErrorDomain.STORAGE,
11201
- category: ErrorCategory.THIRD_PARTY,
11202
- details: { id: input.id }
11203
- });
11204
- const row = current.rows[0];
11205
- let existingChunks = [];
11206
- if (row.bufferedObservationChunks) try {
11207
- const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
11208
- existingChunks = Array.isArray(parsed) ? parsed : [];
11209
- } catch {
11210
- existingChunks = [];
11211
- }
11212
- if (existingChunks.some((existing) => existing.cycleId === input.chunk.cycleId)) {
11213
- await tx.commit();
11214
- return;
11215
- }
11216
- const newChunk = {
11217
- id: `ombuf-${globalThis.crypto.randomUUID()}`,
11218
- cycleId: input.chunk.cycleId,
11219
- observations: input.chunk.observations,
11220
- tokenCount: input.chunk.tokenCount,
11221
- messageIds: input.chunk.messageIds,
11222
- messageTokens: input.chunk.messageTokens,
11223
- lastObservedAt: input.chunk.lastObservedAt,
11224
- createdAt: /* @__PURE__ */ new Date(),
11225
- suggestedContinuation: input.chunk.suggestedContinuation,
11226
- currentTask: input.chunk.currentTask,
11227
- threadTitle: input.chunk.threadTitle,
11228
- extractedValues: input.chunk.extractedValues,
11229
- extractionFailures: input.chunk.extractionFailures
11230
- };
11231
- const newChunks = [...existingChunks, newChunk];
11232
- const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
11233
- if ((await tx.execute({
11234
- sql: `UPDATE "${OM_TABLE}" SET
11235
- "bufferedObservationChunks" = ?,
11236
- "lastBufferedAtTime" = COALESCE(?, "lastBufferedAtTime"),
11237
- "updatedAt" = ?
11238
- WHERE id = ?`,
11239
- args: [
11240
- JSON.stringify(newChunks),
11241
- lastBufferedAtTime,
11242
- nowStr,
11243
- input.id
11244
- ]
11245
- })).rowsAffected === 0) throw new MastraError({
11455
+ return await this.#withOMCas("UPDATE_BUFFERED_OBSERVATIONS", async (attemptIndex, check) => {
11456
+ const row = await this.#readOMRow(input.id, check);
11457
+ if (!row) {
11458
+ if (this.#mayBeUnsynced(attemptIndex)) return OM_CAS_CONFLICT;
11459
+ throw new MastraError({
11246
11460
  id: createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
11247
11461
  text: `Observational memory record not found: ${input.id}`,
11248
11462
  domain: ErrorDomain.STORAGE,
11249
11463
  category: ErrorCategory.THIRD_PARTY,
11250
11464
  details: { id: input.id }
11251
11465
  });
11252
- await tx.commit();
11253
- } catch (error) {
11254
- if (!tx.closed) await tx.rollback();
11255
- throw error;
11256
11466
  }
11257
- });
11467
+ const targetRow = await this.#resolveLiveOMRow(row, check);
11468
+ const target = targetRow.record;
11469
+ const existingChunks = Array.isArray(target.bufferedObservationChunks) ? target.bufferedObservationChunks : [];
11470
+ if (existingChunks.some((existing) => existing.cycleId === input.chunk.cycleId) || isBufferedChunkCoveredByCursor(input.chunk.lastObservedAt, target.lastObservedAt)) return {
11471
+ persisted: false,
11472
+ recordId: target.id
11473
+ };
11474
+ const newChunk = {
11475
+ id: `ombuf-${globalThis.crypto.randomUUID()}`,
11476
+ cycleId: input.chunk.cycleId,
11477
+ observations: input.chunk.observations,
11478
+ tokenCount: input.chunk.tokenCount,
11479
+ messageIds: input.chunk.messageIds,
11480
+ messageTokens: input.chunk.messageTokens,
11481
+ lastObservedAt: input.chunk.lastObservedAt,
11482
+ createdAt: /* @__PURE__ */ new Date(),
11483
+ suggestedContinuation: input.chunk.suggestedContinuation,
11484
+ currentTask: input.chunk.currentTask,
11485
+ threadTitle: input.chunk.threadTitle,
11486
+ extractedValues: input.chunk.extractedValues,
11487
+ extractionFailures: input.chunk.extractionFailures
11488
+ };
11489
+ const lastBufferedAtTime = maxObservationCursor(target.lastBufferedAtTime, input.lastBufferedAtTime);
11490
+ return (await this.#client.execute({
11491
+ sql: `UPDATE "${OM_TABLE}" SET
11492
+ "bufferedObservationChunks" = ?,
11493
+ "lastBufferedAtTime" = ?,
11494
+ "updatedAt" = ?
11495
+ WHERE id = ? AND ${targetRow.guard.sql}`,
11496
+ args: [
11497
+ JSON.stringify([...existingChunks, newChunk]),
11498
+ lastBufferedAtTime ? lastBufferedAtTime.toISOString() : null,
11499
+ nowStr,
11500
+ target.id,
11501
+ ...targetRow.guard.args
11502
+ ]
11503
+ })).rowsAffected === 1 ? {
11504
+ persisted: true,
11505
+ recordId: target.id
11506
+ } : OM_CAS_CONFLICT;
11507
+ }, "appendBufferedObservations");
11258
11508
  } catch (error) {
11259
11509
  if (error instanceof MastraError) throw error;
11260
11510
  throw new MastraError({
@@ -11268,144 +11518,137 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
11268
11518
  async swapBufferedToActive(input) {
11269
11519
  try {
11270
11520
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11271
- const check = this.#runFenceCheck(void 0, "swapBufferedToActive");
11272
- return await this.#write("swapBufferedToActive", async () => {
11273
- const tx = await this.#client.transaction("write");
11274
- try {
11275
- if (check) await assertRunFence(tx, check);
11276
- const current = await tx.execute({
11277
- sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
11278
- args: [input.id]
11279
- });
11280
- if (!current.rows || current.rows.length === 0) throw new MastraError({
11521
+ return await this.#withOMCas("SWAP_BUFFERED_TO_ACTIVE", async (attemptIndex, check) => {
11522
+ const current = await this.#client.execute({
11523
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
11524
+ args: [input.id]
11525
+ });
11526
+ if (!current.rows || current.rows.length === 0) {
11527
+ if (this.#mayBeUnsynced(attemptIndex)) return OM_CAS_CONFLICT;
11528
+ throw new MastraError({
11281
11529
  id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_TO_ACTIVE", "NOT_FOUND"),
11282
11530
  text: `Observational memory record not found: ${input.id}`,
11283
11531
  domain: ErrorDomain.STORAGE,
11284
11532
  category: ErrorCategory.THIRD_PARTY,
11285
11533
  details: { id: input.id }
11286
11534
  });
11287
- const row = current.rows[0];
11288
- let chunks = [];
11289
- if (row.bufferedObservationChunks) try {
11290
- const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
11291
- chunks = Array.isArray(parsed) ? parsed : [];
11292
- } catch {
11293
- chunks = [];
11294
- }
11295
- if (chunks.length === 0) {
11296
- await tx.commit();
11297
- return {
11298
- chunksActivated: 0,
11299
- messageTokensActivated: 0,
11300
- observationTokensActivated: 0,
11301
- messagesActivated: 0,
11302
- activatedCycleIds: [],
11303
- activatedMessageIds: []
11304
- };
11305
- }
11306
- const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
11307
- const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
11308
- let cumulativeMessageTokens = 0;
11309
- let bestOverBoundary = 0;
11310
- let bestOverTokens = 0;
11311
- let bestUnderBoundary = 0;
11312
- let bestUnderTokens = 0;
11313
- for (let i = 0; i < chunks.length; i++) {
11314
- cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
11315
- const boundary = i + 1;
11316
- if (cumulativeMessageTokens >= targetMessageTokens) {
11317
- if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
11318
- bestOverBoundary = boundary;
11319
- bestOverTokens = cumulativeMessageTokens;
11320
- }
11321
- } else if (cumulativeMessageTokens > bestUnderTokens) {
11322
- bestUnderBoundary = boundary;
11323
- bestUnderTokens = cumulativeMessageTokens;
11535
+ }
11536
+ const row = current.rows[0];
11537
+ const guard = this.#toOMRow(current, check).guard;
11538
+ const emptyResult = {
11539
+ chunksActivated: 0,
11540
+ messageTokensActivated: 0,
11541
+ observationTokensActivated: 0,
11542
+ messagesActivated: 0,
11543
+ activatedCycleIds: [],
11544
+ activatedMessageIds: []
11545
+ };
11546
+ if (row.supersededBy) return {
11547
+ ...emptyResult,
11548
+ retired: true
11549
+ };
11550
+ let persistedChunks = [];
11551
+ if (row.bufferedObservationChunks) try {
11552
+ const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
11553
+ persistedChunks = Array.isArray(parsed) ? parsed : [];
11554
+ } catch {
11555
+ persistedChunks = [];
11556
+ }
11557
+ const refreshedWeights = new Map((Array.isArray(input.bufferedChunks) ? input.bufferedChunks : []).map((c) => [c.id, c.messageTokens]));
11558
+ const chunks = persistedChunks.map((c) => refreshedWeights.has(c.id) ? {
11559
+ ...c,
11560
+ messageTokens: refreshedWeights.get(c.id)
11561
+ } : c);
11562
+ if (chunks.length === 0) return emptyResult;
11563
+ const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
11564
+ const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
11565
+ let cumulativeMessageTokens = 0;
11566
+ let bestOverBoundary = 0;
11567
+ let bestOverTokens = 0;
11568
+ let bestUnderBoundary = 0;
11569
+ let bestUnderTokens = 0;
11570
+ for (let i = 0; i < chunks.length; i++) {
11571
+ cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
11572
+ const boundary = i + 1;
11573
+ if (cumulativeMessageTokens >= targetMessageTokens) {
11574
+ if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
11575
+ bestOverBoundary = boundary;
11576
+ bestOverTokens = cumulativeMessageTokens;
11324
11577
  }
11578
+ } else if (cumulativeMessageTokens > bestUnderTokens) {
11579
+ bestUnderBoundary = boundary;
11580
+ bestUnderTokens = cumulativeMessageTokens;
11325
11581
  }
11326
- const maxOvershoot = retentionFloor * .95;
11327
- const overshoot = bestOverTokens - targetMessageTokens;
11328
- const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
11329
- const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
11330
- const minRemaining = Math.min(1e3, retentionFloor);
11331
- let chunksToActivate;
11332
- if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) chunksToActivate = bestOverBoundary;
11333
- else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) chunksToActivate = bestOverBoundary;
11334
- else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) chunksToActivate = bestUnderBoundary;
11335
- else if (bestOverBoundary > 0) chunksToActivate = bestOverBoundary;
11336
- else chunksToActivate = 1;
11337
- const activatedChunks = chunks.slice(0, chunksToActivate);
11338
- const remainingChunks = chunks.slice(chunksToActivate);
11339
- const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
11340
- const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
11341
- const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
11342
- const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
11343
- const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
11344
- const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
11345
- const latestChunk = activatedChunks[activatedChunks.length - 1];
11346
- const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
11347
- const lastObservedAtStr = lastObservedAt.toISOString();
11348
- const existingActive = row.activeObservations || "";
11349
- const existingTokenCount = Number(row.observationTokenCount || 0);
11350
- const boundary = `\n\n--- message boundary (${lastObservedAt.toISOString()}) ---\n\n`;
11351
- const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
11352
- const newTokenCount = existingTokenCount + activatedTokens;
11353
- const existingPending = Number(row.pendingMessageTokens || 0);
11354
- const newPending = Math.max(0, existingPending - activatedMessageTokens);
11355
- const updateResult = await tx.execute({
11356
- sql: `UPDATE "${OM_TABLE}" SET
11582
+ }
11583
+ const maxOvershoot = retentionFloor * .95;
11584
+ const overshoot = bestOverTokens - targetMessageTokens;
11585
+ const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
11586
+ const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
11587
+ const minRemaining = Math.min(1e3, retentionFloor);
11588
+ let chunksToActivate;
11589
+ if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) chunksToActivate = bestOverBoundary;
11590
+ else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) chunksToActivate = bestOverBoundary;
11591
+ else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) chunksToActivate = bestUnderBoundary;
11592
+ else if (bestOverBoundary > 0) chunksToActivate = bestOverBoundary;
11593
+ else chunksToActivate = 1;
11594
+ const activatedChunks = chunks.slice(0, chunksToActivate);
11595
+ const remainingChunks = persistedChunks.slice(chunksToActivate);
11596
+ const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
11597
+ const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
11598
+ const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
11599
+ const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
11600
+ const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
11601
+ const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
11602
+ const latestChunk = activatedChunks[activatedChunks.length - 1];
11603
+ const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
11604
+ const lastObservedAtStr = maxObservationCursor(row.lastObservedAt, lastObservedAt).toISOString();
11605
+ const existingActive = row.activeObservations || "";
11606
+ const existingTokenCount = Number(row.observationTokenCount || 0);
11607
+ const boundary = `\n\n--- message boundary (${lastObservedAt.toISOString()}) ---\n\n`;
11608
+ const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
11609
+ const newTokenCount = existingTokenCount + activatedTokens;
11610
+ const existingPending = Number(row.pendingMessageTokens || 0);
11611
+ const newPending = Math.max(0, existingPending - activatedMessageTokens);
11612
+ if ((await this.#client.execute({
11613
+ sql: `UPDATE "${OM_TABLE}" SET
11357
11614
  "activeObservations" = ?,
11358
11615
  "observationTokenCount" = ?,
11359
11616
  "pendingMessageTokens" = ?,
11360
11617
  "bufferedObservationChunks" = ?,
11361
11618
  "lastObservedAt" = ?,
11362
11619
  "updatedAt" = ?
11363
- WHERE id = ?
11364
- AND "bufferedObservationChunks" IS NOT NULL
11365
- AND "bufferedObservationChunks" != '[]'`,
11366
- args: [
11367
- newActive,
11368
- newTokenCount,
11369
- newPending,
11370
- remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
11371
- lastObservedAtStr,
11372
- nowStr,
11373
- input.id
11374
- ]
11375
- });
11376
- await tx.commit();
11377
- if (updateResult.rowsAffected === 0) return {
11378
- chunksActivated: 0,
11379
- messageTokensActivated: 0,
11380
- observationTokensActivated: 0,
11381
- messagesActivated: 0,
11382
- activatedCycleIds: [],
11383
- activatedMessageIds: []
11384
- };
11385
- const latestChunkHints = activatedChunks[activatedChunks.length - 1];
11386
- return {
11387
- chunksActivated: activatedChunks.length,
11388
- messageTokensActivated: activatedMessageTokens,
11389
- observationTokensActivated: activatedTokens,
11390
- messagesActivated: activatedMessageCount,
11391
- activatedCycleIds,
11392
- activatedMessageIds,
11393
- observations: activatedContent,
11394
- perChunk: activatedChunks.map((c) => ({
11395
- cycleId: c.cycleId ?? "",
11396
- messageTokens: c.messageTokens ?? 0,
11397
- observationTokens: c.tokenCount,
11398
- messageCount: c.messageIds.length,
11399
- observations: c.observations
11400
- })),
11401
- suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
11402
- currentTask: latestChunkHints?.currentTask ?? void 0
11403
- };
11404
- } catch (error) {
11405
- if (!tx.closed) await tx.rollback();
11406
- throw error;
11407
- }
11408
- });
11620
+ WHERE id = ? AND ${guard.sql}`,
11621
+ args: [
11622
+ newActive,
11623
+ newTokenCount,
11624
+ newPending,
11625
+ remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
11626
+ lastObservedAtStr,
11627
+ nowStr,
11628
+ input.id,
11629
+ ...guard.args
11630
+ ]
11631
+ })).rowsAffected === 0) return OM_CAS_CONFLICT;
11632
+ const latestChunkHints = activatedChunks[activatedChunks.length - 1];
11633
+ return {
11634
+ chunksActivated: activatedChunks.length,
11635
+ messageTokensActivated: activatedMessageTokens,
11636
+ observationTokensActivated: activatedTokens,
11637
+ messagesActivated: activatedMessageCount,
11638
+ activatedCycleIds,
11639
+ activatedMessageIds,
11640
+ observations: activatedContent,
11641
+ perChunk: activatedChunks.map((c) => ({
11642
+ cycleId: c.cycleId ?? "",
11643
+ messageTokens: c.messageTokens ?? 0,
11644
+ observationTokens: c.tokenCount,
11645
+ messageCount: c.messageIds.length,
11646
+ observations: c.observations
11647
+ })),
11648
+ suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
11649
+ currentTask: latestChunkHints?.currentTask ?? void 0
11650
+ };
11651
+ }, "swapBufferedToActive");
11409
11652
  } catch (error) {
11410
11653
  if (error instanceof MastraError) throw error;
11411
11654
  throw new MastraError({
@@ -11459,57 +11702,27 @@ var MemoryLibSQL = class MemoryLibSQL extends MemoryStorage {
11459
11702
  }
11460
11703
  async swapBufferedReflectionToActive(input) {
11461
11704
  try {
11462
- const check = this.#runFenceCheck(void 0, "swapBufferedReflectionToActive");
11463
- return await this.#write("swapBufferedReflectionToActive", async () => {
11464
- const tx = await this.#client.transaction("write");
11465
- try {
11466
- if (check) await assertRunFence(tx, check);
11467
- const current = await tx.execute({
11468
- sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
11469
- args: [input.currentRecord.id]
11470
- });
11471
- if (!current.rows || current.rows.length === 0) throw new MastraError({
11472
- id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NOT_FOUND"),
11473
- text: `Observational memory record not found: ${input.currentRecord.id}`,
11474
- domain: ErrorDomain.STORAGE,
11475
- category: ErrorCategory.THIRD_PARTY,
11476
- details: { id: input.currentRecord.id }
11477
- });
11478
- const row = current.rows[0];
11479
- const bufferedReflection = row.bufferedReflection || "";
11480
- const reflectedLineCount = Number(row.reflectedObservationLineCount || 0);
11481
- if (!bufferedReflection) throw new MastraError({
11482
- id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
11483
- text: "No buffered reflection to swap",
11484
- domain: ErrorDomain.STORAGE,
11485
- category: ErrorCategory.USER,
11486
- details: { id: input.currentRecord.id }
11487
- });
11488
- const unreflectedContent = (row.activeObservations || "").split("\n").slice(reflectedLineCount).join("\n").trim();
11489
- const newObservations = unreflectedContent ? `${bufferedReflection}\n\n${unreflectedContent}` : bufferedReflection;
11490
- const newRecord = await this.#insertReflectionGeneration(tx, {
11491
- currentRecord: input.currentRecord,
11492
- reflection: newObservations,
11493
- tokenCount: input.tokenCount
11494
- });
11495
- const nowStr = (/* @__PURE__ */ new Date()).toISOString();
11496
- await tx.execute({
11497
- sql: `UPDATE "${OM_TABLE}" SET
11498
- "bufferedReflection" = NULL,
11499
- "bufferedReflectionTokens" = NULL,
11500
- "bufferedReflectionInputTokens" = NULL,
11501
- "reflectedObservationLineCount" = NULL,
11502
- "updatedAt" = ?
11503
- WHERE id = ?`,
11504
- args: [nowStr, input.currentRecord.id]
11505
- });
11506
- await tx.commit();
11507
- return newRecord;
11508
- } catch (error) {
11509
- if (!tx.closed) await tx.rollback();
11510
- throw error;
11511
- }
11512
- });
11705
+ return await this.#withOMCas("SWAP_BUFFERED_REFLECTION_TO_ACTIVE", async (attemptIndex, check) => {
11706
+ const { currentRecord } = input;
11707
+ const row = await this.#readOMRow(currentRecord.id, check);
11708
+ if (!row) return this.#mayBeUnsynced(attemptIndex) ? OM_CAS_CONFLICT : currentRecord;
11709
+ const stored = row.record;
11710
+ if (stored.supersededBy) return (await this.#resolveLiveOMRow(row, check)).record;
11711
+ const bufferedReflection = stored.bufferedReflection || "";
11712
+ if (!bufferedReflection) throw new MastraError({
11713
+ id: createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
11714
+ text: "No buffered reflection to swap",
11715
+ domain: ErrorDomain.STORAGE,
11716
+ category: ErrorCategory.USER,
11717
+ details: { id: currentRecord.id }
11718
+ });
11719
+ if (!isAppendOnlySince(stored.activeObservations, currentRecord.activeObservations)) return stored;
11720
+ const reflectedLineCount = stored.reflectedObservationLineCount ?? 0;
11721
+ const unreflectedContent = (stored.activeObservations || "").split("\n").slice(reflectedLineCount).join("\n").trim();
11722
+ const newObservations = unreflectedContent ? `${bufferedReflection}\n\n${unreflectedContent}` : bufferedReflection;
11723
+ const tokenCount = input.tokenCount + Math.max(0, (stored.observationTokenCount ?? 0) - (currentRecord.observationTokenCount ?? 0));
11724
+ return this.#rollOverObservationalMemory(row, newObservations, tokenCount, input.newRecordId);
11725
+ }, "swapBufferedReflectionToActive");
11513
11726
  } catch (error) {
11514
11727
  if (error instanceof MastraError) throw error;
11515
11728
  throw new MastraError({
@@ -15212,25 +15425,12 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends WorkflowsStorage {
15212
15425
  sql: `SELECT json(snapshot) as snapshot FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
15213
15426
  args: [workflowName, runId]
15214
15427
  });
15215
- let snapshot;
15216
- if (!existingSnapshotResult.rows?.[0]) snapshot = {
15217
- context: {},
15218
- activePaths: [],
15219
- timestamp: Date.now(),
15220
- suspendedPaths: {},
15221
- activeStepsPath: {},
15222
- resumeLabels: {},
15223
- serializedStepGraph: [],
15224
- status: "pending",
15225
- value: {},
15226
- waitingPaths: {},
15227
- runId,
15228
- requestContext: {}
15229
- };
15230
- else {
15231
- const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
15232
- snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
15428
+ if (!existingSnapshotResult.rows?.[0]) {
15429
+ await tx.rollback();
15430
+ return {};
15233
15431
  }
15432
+ const existingSnapshot = existingSnapshotResult.rows[0].snapshot;
15433
+ const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
15234
15434
  mergeWorkflowStepResult({
15235
15435
  snapshot,
15236
15436
  stepId,
@@ -16266,7 +16466,6 @@ var LibSQLFactoryStorage = class extends FactoryStorage {
16266
16466
  }
16267
16467
  async withTransaction(fn) {
16268
16468
  return withClientWriteLock(this.#client, async () => {
16269
- if (this.#config.url.includes(":memory:")) return fn(new LibSQLFactoryStorageOps(this.#client, this.#schemas, runWithoutWriteLock));
16270
16469
  const transaction = await this.#client.transaction("write");
16271
16470
  try {
16272
16471
  const result = await fn(new LibSQLFactoryStorageOps(transaction, this.#schemas, runWithoutWriteLock));
@@ -16428,6 +16627,7 @@ var LibSQLStore = class extends MastraCompositeStore {
16428
16627
  }
16429
16628
  const domainConfig = {
16430
16629
  client: this.client,
16630
+ embeddedReplica: "url" in config && !!config.syncUrl,
16431
16631
  maxRetries: this.maxRetries,
16432
16632
  initialBackoffMs: this.initialBackoffMs
16433
16633
  };