@mastra/libsql 1.17.0 → 1.17.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.cjs CHANGED
@@ -546,29 +546,14 @@ var LibSQLVector = class extends vector.MastraVector {
546
546
  this.vectorIndexes = this.isMemoryDb ? Promise.resolve(/* @__PURE__ */ new Set()) : this.discoverVectorIndexes();
547
547
  }
548
548
  /**
549
- * Closes the underlying libsql client, releasing all OS file handles.
550
- *
551
- * For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
552
- * switches back to journal_mode=DELETE so the -wal and -shm sidecar files
553
- * are released promptly (mirrors LibSQLStore.close()).
554
- *
555
- * Remote (Turso) databases skip the WAL pragmas and just close the client.
549
+ * Closes the underlying libsql client, releasing this vector store's OS file handles.
556
550
  *
557
551
  * Safe to call more than once; subsequent calls are no-ops.
558
552
  */
559
553
  async close() {
560
- if (this.turso.closed) {
561
- return;
554
+ if (!this.turso.closed) {
555
+ this.turso.close();
562
556
  }
563
- if (this.turso.protocol === "file" && !this.isMemoryDb) {
564
- try {
565
- await this.turso.execute("PRAGMA wal_checkpoint(TRUNCATE);");
566
- await this.turso.execute("PRAGMA journal_mode=DELETE;");
567
- } catch (err) {
568
- this.logger.warn("LibSQLVector: Failed to checkpoint WAL before close.", err);
569
- }
570
- }
571
- this.turso.close();
572
557
  }
573
558
  async discoverVectorIndexes() {
574
559
  try {
@@ -8306,46 +8291,49 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8306
8291
  config: input.config,
8307
8292
  observedTimezone: input.observedTimezone
8308
8293
  };
8309
- await this.#client.execute({
8310
- sql: `INSERT INTO "${OM_TABLE}" (
8311
- id, "lookupKey", scope, "resourceId", "threadId",
8312
- "activeObservations", "activeObservationsPendingUpdate",
8313
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
8314
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
8315
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
8316
- "observedTimezone", "createdAt", "updatedAt"
8317
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8318
- args: [
8319
- id,
8320
- lookupKey,
8321
- input.scope,
8322
- input.resourceId,
8323
- input.threadId || null,
8324
- "",
8325
- null,
8326
- "initial",
8327
- JSON.stringify(input.config),
8328
- 0,
8329
- null,
8330
- null,
8331
- 0,
8332
- 0,
8333
- 0,
8334
- false,
8335
- false,
8336
- false,
8337
- // isBufferingObservation
8338
- false,
8339
- // isBufferingReflection
8340
- 0,
8341
- // lastBufferedAtTokens
8342
- null,
8343
- // lastBufferedAtTime
8344
- input.observedTimezone || null,
8345
- now.toISOString(),
8346
- now.toISOString()
8347
- ]
8348
- });
8294
+ await withClientWriteLock(
8295
+ this.#client,
8296
+ () => this.#client.execute({
8297
+ sql: `INSERT INTO "${OM_TABLE}" (
8298
+ id, "lookupKey", scope, "resourceId", "threadId",
8299
+ "activeObservations", "activeObservationsPendingUpdate",
8300
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
8301
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
8302
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
8303
+ "observedTimezone", "createdAt", "updatedAt"
8304
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8305
+ args: [
8306
+ id,
8307
+ lookupKey,
8308
+ input.scope,
8309
+ input.resourceId,
8310
+ input.threadId || null,
8311
+ "",
8312
+ null,
8313
+ "initial",
8314
+ JSON.stringify(input.config),
8315
+ 0,
8316
+ null,
8317
+ null,
8318
+ 0,
8319
+ 0,
8320
+ 0,
8321
+ false,
8322
+ false,
8323
+ false,
8324
+ // isBufferingObservation
8325
+ false,
8326
+ // isBufferingReflection
8327
+ 0,
8328
+ // lastBufferedAtTokens
8329
+ null,
8330
+ // lastBufferedAtTime
8331
+ input.observedTimezone || null,
8332
+ now.toISOString(),
8333
+ now.toISOString()
8334
+ ]
8335
+ })
8336
+ );
8349
8337
  return record;
8350
8338
  } catch (error$1) {
8351
8339
  throw new error.MastraError(
@@ -8362,53 +8350,56 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8362
8350
  async insertObservationalMemoryRecord(record) {
8363
8351
  try {
8364
8352
  const lookupKey = this.getOMKey(record.threadId, record.resourceId);
8365
- await this.#client.execute({
8366
- sql: `INSERT INTO "${OM_TABLE}" (
8367
- id, "lookupKey", scope, "resourceId", "threadId",
8368
- "activeObservations", "activeObservationsPendingUpdate",
8369
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
8370
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
8371
- "observedMessageIds", "bufferedObservationChunks",
8372
- "bufferedReflection", "bufferedReflectionTokens", "bufferedReflectionInputTokens",
8373
- "reflectedObservationLineCount",
8374
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection",
8375
- "lastBufferedAtTokens", "lastBufferedAtTime",
8376
- "observedTimezone", metadata, "createdAt", "updatedAt"
8377
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8378
- args: [
8379
- record.id,
8380
- lookupKey,
8381
- record.scope,
8382
- record.resourceId,
8383
- record.threadId || null,
8384
- record.activeObservations || "",
8385
- null,
8386
- record.originType || "initial",
8387
- record.config ? JSON.stringify(record.config) : null,
8388
- record.generationCount || 0,
8389
- record.lastObservedAt ? record.lastObservedAt.toISOString() : null,
8390
- null,
8391
- record.pendingMessageTokens || 0,
8392
- record.totalTokensObserved || 0,
8393
- record.observationTokenCount || 0,
8394
- record.observedMessageIds ? JSON.stringify(record.observedMessageIds) : null,
8395
- record.bufferedObservationChunks ? JSON.stringify(record.bufferedObservationChunks) : null,
8396
- record.bufferedReflection || null,
8397
- record.bufferedReflectionTokens ?? null,
8398
- record.bufferedReflectionInputTokens ?? null,
8399
- record.reflectedObservationLineCount ?? null,
8400
- record.isObserving || false,
8401
- record.isReflecting || false,
8402
- record.isBufferingObservation || false,
8403
- record.isBufferingReflection || false,
8404
- record.lastBufferedAtTokens || 0,
8405
- record.lastBufferedAtTime ? record.lastBufferedAtTime.toISOString() : null,
8406
- record.observedTimezone || null,
8407
- record.metadata ? JSON.stringify(record.metadata) : null,
8408
- record.createdAt.toISOString(),
8409
- record.updatedAt.toISOString()
8410
- ]
8411
- });
8353
+ await withClientWriteLock(
8354
+ this.#client,
8355
+ () => this.#client.execute({
8356
+ sql: `INSERT INTO "${OM_TABLE}" (
8357
+ id, "lookupKey", scope, "resourceId", "threadId",
8358
+ "activeObservations", "activeObservationsPendingUpdate",
8359
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
8360
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
8361
+ "observedMessageIds", "bufferedObservationChunks",
8362
+ "bufferedReflection", "bufferedReflectionTokens", "bufferedReflectionInputTokens",
8363
+ "reflectedObservationLineCount",
8364
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection",
8365
+ "lastBufferedAtTokens", "lastBufferedAtTime",
8366
+ "observedTimezone", metadata, "createdAt", "updatedAt"
8367
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8368
+ args: [
8369
+ record.id,
8370
+ lookupKey,
8371
+ record.scope,
8372
+ record.resourceId,
8373
+ record.threadId || null,
8374
+ record.activeObservations || "",
8375
+ null,
8376
+ record.originType || "initial",
8377
+ record.config ? JSON.stringify(record.config) : null,
8378
+ record.generationCount || 0,
8379
+ record.lastObservedAt ? record.lastObservedAt.toISOString() : null,
8380
+ null,
8381
+ record.pendingMessageTokens || 0,
8382
+ record.totalTokensObserved || 0,
8383
+ record.observationTokenCount || 0,
8384
+ record.observedMessageIds ? JSON.stringify(record.observedMessageIds) : null,
8385
+ record.bufferedObservationChunks ? JSON.stringify(record.bufferedObservationChunks) : null,
8386
+ record.bufferedReflection || null,
8387
+ record.bufferedReflectionTokens ?? null,
8388
+ record.bufferedReflectionInputTokens ?? null,
8389
+ record.reflectedObservationLineCount ?? null,
8390
+ record.isObserving || false,
8391
+ record.isReflecting || false,
8392
+ record.isBufferingObservation || false,
8393
+ record.isBufferingReflection || false,
8394
+ record.lastBufferedAtTokens || 0,
8395
+ record.lastBufferedAtTime ? record.lastBufferedAtTime.toISOString() : null,
8396
+ record.observedTimezone || null,
8397
+ record.metadata ? JSON.stringify(record.metadata) : null,
8398
+ record.createdAt.toISOString(),
8399
+ record.updatedAt.toISOString()
8400
+ ]
8401
+ })
8402
+ );
8412
8403
  } catch (error$1) {
8413
8404
  throw new error.MastraError(
8414
8405
  {
@@ -8425,26 +8416,29 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8425
8416
  try {
8426
8417
  const now = /* @__PURE__ */ new Date();
8427
8418
  const observedMessageIdsJson = input.observedMessageIds ? JSON.stringify(input.observedMessageIds) : null;
8428
- const result = await this.#client.execute({
8429
- sql: `UPDATE "${OM_TABLE}" SET
8430
- "activeObservations" = ?,
8431
- "lastObservedAt" = ?,
8432
- "pendingMessageTokens" = 0,
8433
- "observationTokenCount" = ?,
8434
- "totalTokensObserved" = "totalTokensObserved" + ?,
8435
- "observedMessageIds" = ?,
8436
- "updatedAt" = ?
8437
- WHERE id = ?`,
8438
- args: [
8439
- input.observations,
8440
- input.lastObservedAt.toISOString(),
8441
- input.tokenCount,
8442
- input.tokenCount,
8443
- observedMessageIdsJson,
8444
- now.toISOString(),
8445
- input.id
8446
- ]
8447
- });
8419
+ const result = await withClientWriteLock(
8420
+ this.#client,
8421
+ () => this.#client.execute({
8422
+ sql: `UPDATE "${OM_TABLE}" SET
8423
+ "activeObservations" = ?,
8424
+ "lastObservedAt" = ?,
8425
+ "pendingMessageTokens" = 0,
8426
+ "observationTokenCount" = ?,
8427
+ "totalTokensObserved" = "totalTokensObserved" + ?,
8428
+ "observedMessageIds" = ?,
8429
+ "updatedAt" = ?
8430
+ WHERE id = ?`,
8431
+ args: [
8432
+ input.observations,
8433
+ input.lastObservedAt.toISOString(),
8434
+ input.tokenCount,
8435
+ input.tokenCount,
8436
+ observedMessageIdsJson,
8437
+ now.toISOString(),
8438
+ input.id
8439
+ ]
8440
+ })
8441
+ );
8448
8442
  if (result.rowsAffected === 0) {
8449
8443
  throw new error.MastraError({
8450
8444
  id: storage.createStorageErrorId("LIBSQL", "UPDATE_ACTIVE_OBSERVATIONS", "NOT_FOUND"),
@@ -8471,78 +8465,21 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8471
8465
  }
8472
8466
  async createReflectionGeneration(input) {
8473
8467
  try {
8474
- const id = crypto.randomUUID();
8475
- const now = /* @__PURE__ */ new Date();
8476
- const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
8477
- const record = {
8478
- id,
8479
- scope: input.currentRecord.scope,
8480
- threadId: input.currentRecord.threadId,
8481
- resourceId: input.currentRecord.resourceId,
8482
- createdAt: now,
8483
- updatedAt: now,
8484
- lastObservedAt: input.currentRecord.lastObservedAt,
8485
- originType: "reflection",
8486
- generationCount: input.currentRecord.generationCount + 1,
8487
- activeObservations: input.reflection,
8488
- totalTokensObserved: input.currentRecord.totalTokensObserved,
8489
- observationTokenCount: input.tokenCount,
8490
- pendingMessageTokens: 0,
8491
- isReflecting: false,
8492
- isObserving: false,
8493
- isBufferingObservation: false,
8494
- isBufferingReflection: false,
8495
- lastBufferedAtTokens: 0,
8496
- lastBufferedAtTime: null,
8497
- config: input.currentRecord.config,
8498
- metadata: input.currentRecord.metadata,
8499
- observedTimezone: input.currentRecord.observedTimezone
8500
- };
8501
- await this.#client.execute({
8502
- sql: `INSERT INTO "${OM_TABLE}" (
8503
- id, "lookupKey", scope, "resourceId", "threadId",
8504
- "activeObservations", "activeObservationsPendingUpdate",
8505
- "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
8506
- "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
8507
- "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
8508
- "observedTimezone", metadata, "createdAt", "updatedAt"
8509
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8510
- args: [
8511
- id,
8512
- lookupKey,
8513
- record.scope,
8514
- record.resourceId,
8515
- record.threadId || null,
8516
- input.reflection,
8517
- null,
8518
- "reflection",
8519
- JSON.stringify(record.config),
8520
- input.currentRecord.generationCount + 1,
8521
- record.lastObservedAt?.toISOString() || null,
8522
- now.toISOString(),
8523
- record.pendingMessageTokens,
8524
- record.totalTokensObserved,
8525
- record.observationTokenCount,
8526
- false,
8527
- // isObserving
8528
- false,
8529
- // isReflecting
8530
- false,
8531
- // isBufferingObservation
8532
- false,
8533
- // isBufferingReflection
8534
- 0,
8535
- // lastBufferedAtTokens
8536
- null,
8537
- // lastBufferedAtTime
8538
- record.observedTimezone || null,
8539
- record.metadata ? JSON.stringify(record.metadata) : null,
8540
- now.toISOString(),
8541
- now.toISOString()
8542
- ]
8468
+ return await withClientWriteLock(this.#client, async () => {
8469
+ const tx = await this.#client.transaction("write");
8470
+ try {
8471
+ const record = await this.#insertReflectionGeneration(tx, input);
8472
+ await tx.commit();
8473
+ return record;
8474
+ } catch (error) {
8475
+ if (!tx.closed) await tx.rollback();
8476
+ throw error;
8477
+ }
8543
8478
  });
8544
- return record;
8545
8479
  } catch (error$1) {
8480
+ if (error$1 instanceof error.MastraError) {
8481
+ throw error$1;
8482
+ }
8546
8483
  throw new error.MastraError(
8547
8484
  {
8548
8485
  id: storage.createStorageErrorId("LIBSQL", "CREATE_REFLECTION_GENERATION", "FAILED"),
@@ -8554,12 +8491,94 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8554
8491
  );
8555
8492
  }
8556
8493
  }
8494
+ /**
8495
+ * Inserts a new reflection-generation row. Called inside a locked write
8496
+ * transaction — either from {@link createReflectionGeneration} (standalone)
8497
+ * or from {@link swapBufferedReflectionToActive} (as part of a compound
8498
+ * swap). Must not acquire the write lock itself; the caller owns it.
8499
+ */
8500
+ async #insertReflectionGeneration(tx, input) {
8501
+ const id = crypto.randomUUID();
8502
+ const now = /* @__PURE__ */ new Date();
8503
+ const lookupKey = this.getOMKey(input.currentRecord.threadId, input.currentRecord.resourceId);
8504
+ const record = {
8505
+ id,
8506
+ scope: input.currentRecord.scope,
8507
+ threadId: input.currentRecord.threadId,
8508
+ resourceId: input.currentRecord.resourceId,
8509
+ createdAt: now,
8510
+ updatedAt: now,
8511
+ lastObservedAt: input.currentRecord.lastObservedAt,
8512
+ originType: "reflection",
8513
+ generationCount: input.currentRecord.generationCount + 1,
8514
+ activeObservations: input.reflection,
8515
+ totalTokensObserved: input.currentRecord.totalTokensObserved,
8516
+ observationTokenCount: input.tokenCount,
8517
+ pendingMessageTokens: 0,
8518
+ isReflecting: false,
8519
+ isObserving: false,
8520
+ isBufferingObservation: false,
8521
+ isBufferingReflection: false,
8522
+ lastBufferedAtTokens: 0,
8523
+ lastBufferedAtTime: null,
8524
+ config: input.currentRecord.config,
8525
+ metadata: input.currentRecord.metadata,
8526
+ observedTimezone: input.currentRecord.observedTimezone
8527
+ };
8528
+ await tx.execute({
8529
+ sql: `INSERT INTO "${OM_TABLE}" (
8530
+ id, "lookupKey", scope, "resourceId", "threadId",
8531
+ "activeObservations", "activeObservationsPendingUpdate",
8532
+ "originType", config, "generationCount", "lastObservedAt", "lastReflectionAt",
8533
+ "pendingMessageTokens", "totalTokensObserved", "observationTokenCount",
8534
+ "isObserving", "isReflecting", "isBufferingObservation", "isBufferingReflection", "lastBufferedAtTokens", "lastBufferedAtTime",
8535
+ "observedTimezone", metadata, "createdAt", "updatedAt"
8536
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
8537
+ args: [
8538
+ id,
8539
+ lookupKey,
8540
+ record.scope,
8541
+ record.resourceId,
8542
+ record.threadId || null,
8543
+ input.reflection,
8544
+ null,
8545
+ "reflection",
8546
+ JSON.stringify(record.config),
8547
+ input.currentRecord.generationCount + 1,
8548
+ record.lastObservedAt?.toISOString() || null,
8549
+ now.toISOString(),
8550
+ record.pendingMessageTokens,
8551
+ record.totalTokensObserved,
8552
+ record.observationTokenCount,
8553
+ false,
8554
+ // isObserving
8555
+ false,
8556
+ // isReflecting
8557
+ false,
8558
+ // isBufferingObservation
8559
+ false,
8560
+ // isBufferingReflection
8561
+ 0,
8562
+ // lastBufferedAtTokens
8563
+ null,
8564
+ // lastBufferedAtTime
8565
+ record.observedTimezone || null,
8566
+ record.metadata ? JSON.stringify(record.metadata) : null,
8567
+ now.toISOString(),
8568
+ now.toISOString()
8569
+ ]
8570
+ });
8571
+ return record;
8572
+ }
8557
8573
  async setReflectingFlag(id, isReflecting) {
8558
8574
  try {
8559
- const result = await this.#client.execute({
8560
- sql: `UPDATE "${OM_TABLE}" SET "isReflecting" = ?, "updatedAt" = ? WHERE id = ?`,
8561
- args: [isReflecting, (/* @__PURE__ */ new Date()).toISOString(), id]
8562
- });
8575
+ const result = await withClientWriteLock(
8576
+ this.#client,
8577
+ () => this.#client.execute({
8578
+ sql: `UPDATE "${OM_TABLE}" SET "isReflecting" = ?, "updatedAt" = ? WHERE id = ?`,
8579
+ args: [isReflecting, (/* @__PURE__ */ new Date()).toISOString(), id]
8580
+ })
8581
+ );
8563
8582
  if (result.rowsAffected === 0) {
8564
8583
  throw new error.MastraError({
8565
8584
  id: storage.createStorageErrorId("LIBSQL", "SET_REFLECTING_FLAG", "NOT_FOUND"),
@@ -8586,10 +8605,13 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8586
8605
  }
8587
8606
  async setObservingFlag(id, isObserving) {
8588
8607
  try {
8589
- const result = await this.#client.execute({
8590
- sql: `UPDATE "${OM_TABLE}" SET "isObserving" = ?, "updatedAt" = ? WHERE id = ?`,
8591
- args: [isObserving, (/* @__PURE__ */ new Date()).toISOString(), id]
8592
- });
8608
+ const result = await withClientWriteLock(
8609
+ this.#client,
8610
+ () => this.#client.execute({
8611
+ sql: `UPDATE "${OM_TABLE}" SET "isObserving" = ?, "updatedAt" = ? WHERE id = ?`,
8612
+ args: [isObserving, (/* @__PURE__ */ new Date()).toISOString(), id]
8613
+ })
8614
+ );
8593
8615
  if (result.rowsAffected === 0) {
8594
8616
  throw new error.MastraError({
8595
8617
  id: storage.createStorageErrorId("LIBSQL", "SET_OBSERVING_FLAG", "NOT_FOUND"),
@@ -8626,7 +8648,7 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8626
8648
  sql = `UPDATE "${OM_TABLE}" SET "isBufferingObservation" = ?, "updatedAt" = ? WHERE id = ?`;
8627
8649
  args = [isBuffering, nowStr, id];
8628
8650
  }
8629
- const result = await this.#client.execute({ sql, args });
8651
+ const result = await withClientWriteLock(this.#client, () => this.#client.execute({ sql, args }));
8630
8652
  if (result.rowsAffected === 0) {
8631
8653
  throw new error.MastraError({
8632
8654
  id: storage.createStorageErrorId("LIBSQL", "SET_BUFFERING_OBSERVATION_FLAG", "NOT_FOUND"),
@@ -8653,10 +8675,13 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8653
8675
  }
8654
8676
  async setBufferingReflectionFlag(id, isBuffering) {
8655
8677
  try {
8656
- const result = await this.#client.execute({
8657
- sql: `UPDATE "${OM_TABLE}" SET "isBufferingReflection" = ?, "updatedAt" = ? WHERE id = ?`,
8658
- args: [isBuffering, (/* @__PURE__ */ new Date()).toISOString(), id]
8659
- });
8678
+ const result = await withClientWriteLock(
8679
+ this.#client,
8680
+ () => this.#client.execute({
8681
+ sql: `UPDATE "${OM_TABLE}" SET "isBufferingReflection" = ?, "updatedAt" = ? WHERE id = ?`,
8682
+ args: [isBuffering, (/* @__PURE__ */ new Date()).toISOString(), id]
8683
+ })
8684
+ );
8660
8685
  if (result.rowsAffected === 0) {
8661
8686
  throw new error.MastraError({
8662
8687
  id: storage.createStorageErrorId("LIBSQL", "SET_BUFFERING_REFLECTION_FLAG", "NOT_FOUND"),
@@ -8684,10 +8709,13 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8684
8709
  async clearObservationalMemory(threadId, resourceId) {
8685
8710
  try {
8686
8711
  const lookupKey = this.getOMKey(threadId, resourceId);
8687
- await this.#client.execute({
8688
- sql: `DELETE FROM "${OM_TABLE}" WHERE "lookupKey" = ?`,
8689
- args: [lookupKey]
8690
- });
8712
+ await withClientWriteLock(
8713
+ this.#client,
8714
+ () => this.#client.execute({
8715
+ sql: `DELETE FROM "${OM_TABLE}" WHERE "lookupKey" = ?`,
8716
+ args: [lookupKey]
8717
+ })
8718
+ );
8691
8719
  } catch (error$1) {
8692
8720
  throw new error.MastraError(
8693
8721
  {
@@ -8702,13 +8730,16 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8702
8730
  }
8703
8731
  async setPendingMessageTokens(id, tokenCount) {
8704
8732
  try {
8705
- const result = await this.#client.execute({
8706
- sql: `UPDATE "${OM_TABLE}" SET
8707
- "pendingMessageTokens" = ?,
8708
- "updatedAt" = ?
8709
- WHERE id = ?`,
8710
- args: [tokenCount, (/* @__PURE__ */ new Date()).toISOString(), id]
8711
- });
8733
+ const result = await withClientWriteLock(
8734
+ this.#client,
8735
+ () => this.#client.execute({
8736
+ sql: `UPDATE "${OM_TABLE}" SET
8737
+ "pendingMessageTokens" = ?,
8738
+ "updatedAt" = ?
8739
+ WHERE id = ?`,
8740
+ args: [tokenCount, (/* @__PURE__ */ new Date()).toISOString(), id]
8741
+ })
8742
+ );
8712
8743
  if (result.rowsAffected === 0) {
8713
8744
  throw new error.MastraError({
8714
8745
  id: storage.createStorageErrorId("LIBSQL", "SET_PENDING_MESSAGE_TOKENS", "NOT_FOUND"),
@@ -8735,25 +8766,34 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8735
8766
  }
8736
8767
  async updateObservationalMemoryConfig(input) {
8737
8768
  try {
8738
- const selectResult = await this.#client.execute({
8739
- sql: `SELECT config FROM "${OM_TABLE}" WHERE id = ?`,
8740
- args: [input.id]
8741
- });
8742
- if (selectResult.rows.length === 0) {
8743
- throw new error.MastraError({
8744
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_OM_CONFIG", "NOT_FOUND"),
8745
- text: `Observational memory record not found: ${input.id}`,
8746
- domain: error.ErrorDomain.STORAGE,
8747
- category: error.ErrorCategory.THIRD_PARTY,
8748
- details: { id: input.id }
8749
- });
8750
- }
8751
- const row = selectResult.rows[0];
8752
- const existing = row.config ? JSON.parse(row.config) : {};
8753
- const merged = this.deepMergeConfig(existing, input.config);
8754
- await this.#client.execute({
8755
- sql: `UPDATE "${OM_TABLE}" SET config = ?, "updatedAt" = ? WHERE id = ?`,
8756
- args: [JSON.stringify(merged), (/* @__PURE__ */ new Date()).toISOString(), input.id]
8769
+ await withClientWriteLock(this.#client, async () => {
8770
+ const tx = await this.#client.transaction("write");
8771
+ try {
8772
+ const selectResult = await tx.execute({
8773
+ sql: `SELECT config FROM "${OM_TABLE}" WHERE id = ?`,
8774
+ args: [input.id]
8775
+ });
8776
+ if (selectResult.rows.length === 0) {
8777
+ throw new error.MastraError({
8778
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_OM_CONFIG", "NOT_FOUND"),
8779
+ text: `Observational memory record not found: ${input.id}`,
8780
+ domain: error.ErrorDomain.STORAGE,
8781
+ category: error.ErrorCategory.THIRD_PARTY,
8782
+ details: { id: input.id }
8783
+ });
8784
+ }
8785
+ const row = selectResult.rows[0];
8786
+ const existing = row.config ? JSON.parse(row.config) : {};
8787
+ const merged = this.deepMergeConfig(existing, input.config);
8788
+ await tx.execute({
8789
+ sql: `UPDATE "${OM_TABLE}" SET config = ?, "updatedAt" = ? WHERE id = ?`,
8790
+ args: [JSON.stringify(merged), (/* @__PURE__ */ new Date()).toISOString(), input.id]
8791
+ });
8792
+ await tx.commit();
8793
+ } catch (error) {
8794
+ if (!tx.closed) await tx.rollback();
8795
+ throw error;
8796
+ }
8757
8797
  });
8758
8798
  } catch (error$1) {
8759
8799
  if (error$1 instanceof error.MastraError) {
@@ -8776,63 +8816,72 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8776
8816
  async updateBufferedObservations(input) {
8777
8817
  try {
8778
8818
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
8779
- const current = await this.#client.execute({
8780
- sql: `SELECT "bufferedObservationChunks" FROM "${OM_TABLE}" WHERE id = ?`,
8781
- args: [input.id]
8782
- });
8783
- if (!current.rows || current.rows.length === 0) {
8784
- throw new error.MastraError({
8785
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
8786
- text: `Observational memory record not found: ${input.id}`,
8787
- domain: error.ErrorDomain.STORAGE,
8788
- category: error.ErrorCategory.THIRD_PARTY,
8789
- details: { id: input.id }
8790
- });
8791
- }
8792
- const row = current.rows[0];
8793
- let existingChunks = [];
8794
- if (row.bufferedObservationChunks) {
8819
+ await withClientWriteLock(this.#client, async () => {
8820
+ const tx = await this.#client.transaction("write");
8795
8821
  try {
8796
- const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
8797
- existingChunks = Array.isArray(parsed) ? parsed : [];
8798
- } catch {
8799
- existingChunks = [];
8822
+ const current = await tx.execute({
8823
+ sql: `SELECT "bufferedObservationChunks" FROM "${OM_TABLE}" WHERE id = ?`,
8824
+ args: [input.id]
8825
+ });
8826
+ if (!current.rows || current.rows.length === 0) {
8827
+ throw new error.MastraError({
8828
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
8829
+ text: `Observational memory record not found: ${input.id}`,
8830
+ domain: error.ErrorDomain.STORAGE,
8831
+ category: error.ErrorCategory.THIRD_PARTY,
8832
+ details: { id: input.id }
8833
+ });
8834
+ }
8835
+ const row = current.rows[0];
8836
+ let existingChunks = [];
8837
+ if (row.bufferedObservationChunks) {
8838
+ try {
8839
+ const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
8840
+ existingChunks = Array.isArray(parsed) ? parsed : [];
8841
+ } catch {
8842
+ existingChunks = [];
8843
+ }
8844
+ }
8845
+ const newChunk = {
8846
+ id: `ombuf-${crypto$1.randomUUID()}`,
8847
+ cycleId: input.chunk.cycleId,
8848
+ observations: input.chunk.observations,
8849
+ tokenCount: input.chunk.tokenCount,
8850
+ messageIds: input.chunk.messageIds,
8851
+ messageTokens: input.chunk.messageTokens,
8852
+ lastObservedAt: input.chunk.lastObservedAt,
8853
+ createdAt: /* @__PURE__ */ new Date(),
8854
+ suggestedContinuation: input.chunk.suggestedContinuation,
8855
+ currentTask: input.chunk.currentTask,
8856
+ threadTitle: input.chunk.threadTitle,
8857
+ extractedValues: input.chunk.extractedValues,
8858
+ extractionFailures: input.chunk.extractionFailures
8859
+ };
8860
+ const newChunks = [...existingChunks, newChunk];
8861
+ const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
8862
+ const result = await tx.execute({
8863
+ sql: `UPDATE "${OM_TABLE}" SET
8864
+ "bufferedObservationChunks" = ?,
8865
+ "lastBufferedAtTime" = COALESCE(?, "lastBufferedAtTime"),
8866
+ "updatedAt" = ?
8867
+ WHERE id = ?`,
8868
+ args: [JSON.stringify(newChunks), lastBufferedAtTime, nowStr, input.id]
8869
+ });
8870
+ if (result.rowsAffected === 0) {
8871
+ throw new error.MastraError({
8872
+ id: storage.createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
8873
+ text: `Observational memory record not found: ${input.id}`,
8874
+ domain: error.ErrorDomain.STORAGE,
8875
+ category: error.ErrorCategory.THIRD_PARTY,
8876
+ details: { id: input.id }
8877
+ });
8878
+ }
8879
+ await tx.commit();
8880
+ } catch (error) {
8881
+ if (!tx.closed) await tx.rollback();
8882
+ throw error;
8800
8883
  }
8801
- }
8802
- const newChunk = {
8803
- id: `ombuf-${crypto$1.randomUUID()}`,
8804
- cycleId: input.chunk.cycleId,
8805
- observations: input.chunk.observations,
8806
- tokenCount: input.chunk.tokenCount,
8807
- messageIds: input.chunk.messageIds,
8808
- messageTokens: input.chunk.messageTokens,
8809
- lastObservedAt: input.chunk.lastObservedAt,
8810
- createdAt: /* @__PURE__ */ new Date(),
8811
- suggestedContinuation: input.chunk.suggestedContinuation,
8812
- currentTask: input.chunk.currentTask,
8813
- threadTitle: input.chunk.threadTitle,
8814
- extractedValues: input.chunk.extractedValues,
8815
- extractionFailures: input.chunk.extractionFailures
8816
- };
8817
- const newChunks = [...existingChunks, newChunk];
8818
- const lastBufferedAtTime = input.lastBufferedAtTime ? input.lastBufferedAtTime.toISOString() : null;
8819
- const result = await this.#client.execute({
8820
- sql: `UPDATE "${OM_TABLE}" SET
8821
- "bufferedObservationChunks" = ?,
8822
- "lastBufferedAtTime" = COALESCE(?, "lastBufferedAtTime"),
8823
- "updatedAt" = ?
8824
- WHERE id = ?`,
8825
- args: [JSON.stringify(newChunks), lastBufferedAtTime, nowStr, input.id]
8826
8884
  });
8827
- if (result.rowsAffected === 0) {
8828
- throw new error.MastraError({
8829
- id: storage.createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_OBSERVATIONS", "NOT_FOUND"),
8830
- text: `Observational memory record not found: ${input.id}`,
8831
- domain: error.ErrorDomain.STORAGE,
8832
- category: error.ErrorCategory.THIRD_PARTY,
8833
- details: { id: input.id }
8834
- });
8835
- }
8836
8885
  } catch (error$1) {
8837
8886
  if (error$1 instanceof error.MastraError) {
8838
8887
  throw error$1;
@@ -8851,150 +8900,160 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
8851
8900
  async swapBufferedToActive(input) {
8852
8901
  try {
8853
8902
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
8854
- const current = await this.#client.execute({
8855
- sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
8856
- args: [input.id]
8857
- });
8858
- if (!current.rows || current.rows.length === 0) {
8859
- throw new error.MastraError({
8860
- id: storage.createStorageErrorId("LIBSQL", "SWAP_BUFFERED_TO_ACTIVE", "NOT_FOUND"),
8861
- text: `Observational memory record not found: ${input.id}`,
8862
- domain: error.ErrorDomain.STORAGE,
8863
- category: error.ErrorCategory.THIRD_PARTY,
8864
- details: { id: input.id }
8865
- });
8866
- }
8867
- const row = current.rows[0];
8868
- let chunks = [];
8869
- if (row.bufferedObservationChunks) {
8903
+ return await withClientWriteLock(this.#client, async () => {
8904
+ const tx = await this.#client.transaction("write");
8870
8905
  try {
8871
- const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
8872
- chunks = Array.isArray(parsed) ? parsed : [];
8873
- } catch {
8874
- chunks = [];
8875
- }
8876
- }
8877
- if (chunks.length === 0) {
8878
- return {
8879
- chunksActivated: 0,
8880
- messageTokensActivated: 0,
8881
- observationTokensActivated: 0,
8882
- messagesActivated: 0,
8883
- activatedCycleIds: [],
8884
- activatedMessageIds: []
8885
- };
8886
- }
8887
- const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
8888
- const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
8889
- let cumulativeMessageTokens = 0;
8890
- let bestOverBoundary = 0;
8891
- let bestOverTokens = 0;
8892
- let bestUnderBoundary = 0;
8893
- let bestUnderTokens = 0;
8894
- for (let i = 0; i < chunks.length; i++) {
8895
- cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
8896
- const boundary2 = i + 1;
8897
- if (cumulativeMessageTokens >= targetMessageTokens) {
8898
- if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
8899
- bestOverBoundary = boundary2;
8900
- bestOverTokens = cumulativeMessageTokens;
8906
+ const current = await tx.execute({
8907
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
8908
+ args: [input.id]
8909
+ });
8910
+ if (!current.rows || current.rows.length === 0) {
8911
+ throw new error.MastraError({
8912
+ id: storage.createStorageErrorId("LIBSQL", "SWAP_BUFFERED_TO_ACTIVE", "NOT_FOUND"),
8913
+ text: `Observational memory record not found: ${input.id}`,
8914
+ domain: error.ErrorDomain.STORAGE,
8915
+ category: error.ErrorCategory.THIRD_PARTY,
8916
+ details: { id: input.id }
8917
+ });
8901
8918
  }
8902
- } else {
8903
- if (cumulativeMessageTokens > bestUnderTokens) {
8904
- bestUnderBoundary = boundary2;
8905
- bestUnderTokens = cumulativeMessageTokens;
8919
+ const row = current.rows[0];
8920
+ let chunks = [];
8921
+ if (row.bufferedObservationChunks) {
8922
+ try {
8923
+ const parsed = typeof row.bufferedObservationChunks === "string" ? JSON.parse(row.bufferedObservationChunks) : row.bufferedObservationChunks;
8924
+ chunks = Array.isArray(parsed) ? parsed : [];
8925
+ } catch {
8926
+ chunks = [];
8927
+ }
8906
8928
  }
8907
- }
8908
- }
8909
- const maxOvershoot = retentionFloor * 0.95;
8910
- const overshoot = bestOverTokens - targetMessageTokens;
8911
- const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
8912
- const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
8913
- const minRemaining = Math.min(1e3, retentionFloor);
8914
- let chunksToActivate;
8915
- if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) {
8916
- chunksToActivate = bestOverBoundary;
8917
- } else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
8918
- chunksToActivate = bestOverBoundary;
8919
- } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
8920
- chunksToActivate = bestUnderBoundary;
8921
- } else if (bestOverBoundary > 0) {
8922
- chunksToActivate = bestOverBoundary;
8923
- } else {
8924
- chunksToActivate = 1;
8925
- }
8926
- const activatedChunks = chunks.slice(0, chunksToActivate);
8927
- const remainingChunks = chunks.slice(chunksToActivate);
8928
- const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
8929
- const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
8930
- const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
8931
- const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
8932
- const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
8933
- const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
8934
- const latestChunk = activatedChunks[activatedChunks.length - 1];
8935
- const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
8936
- const lastObservedAtStr = lastObservedAt.toISOString();
8937
- const existingActive = row.activeObservations || "";
8938
- const existingTokenCount = Number(row.observationTokenCount || 0);
8939
- const boundary = `
8929
+ if (chunks.length === 0) {
8930
+ await tx.commit();
8931
+ return {
8932
+ chunksActivated: 0,
8933
+ messageTokensActivated: 0,
8934
+ observationTokensActivated: 0,
8935
+ messagesActivated: 0,
8936
+ activatedCycleIds: [],
8937
+ activatedMessageIds: []
8938
+ };
8939
+ }
8940
+ const retentionFloor = input.messageTokensThreshold * (1 - input.activationRatio);
8941
+ const targetMessageTokens = Math.max(0, input.currentPendingTokens - retentionFloor);
8942
+ let cumulativeMessageTokens = 0;
8943
+ let bestOverBoundary = 0;
8944
+ let bestOverTokens = 0;
8945
+ let bestUnderBoundary = 0;
8946
+ let bestUnderTokens = 0;
8947
+ for (let i = 0; i < chunks.length; i++) {
8948
+ cumulativeMessageTokens += chunks[i].messageTokens ?? 0;
8949
+ const boundary2 = i + 1;
8950
+ if (cumulativeMessageTokens >= targetMessageTokens) {
8951
+ if (bestOverBoundary === 0 || cumulativeMessageTokens < bestOverTokens) {
8952
+ bestOverBoundary = boundary2;
8953
+ bestOverTokens = cumulativeMessageTokens;
8954
+ }
8955
+ } else {
8956
+ if (cumulativeMessageTokens > bestUnderTokens) {
8957
+ bestUnderBoundary = boundary2;
8958
+ bestUnderTokens = cumulativeMessageTokens;
8959
+ }
8960
+ }
8961
+ }
8962
+ const maxOvershoot = retentionFloor * 0.95;
8963
+ const overshoot = bestOverTokens - targetMessageTokens;
8964
+ const remainingAfterOver = input.currentPendingTokens - bestOverTokens;
8965
+ const remainingAfterUnder = input.currentPendingTokens - bestUnderTokens;
8966
+ const minRemaining = Math.min(1e3, retentionFloor);
8967
+ let chunksToActivate;
8968
+ if (input.forceMaxActivation && bestOverBoundary > 0 && remainingAfterOver >= minRemaining) {
8969
+ chunksToActivate = bestOverBoundary;
8970
+ } else if (bestOverBoundary > 0 && overshoot <= maxOvershoot && remainingAfterOver >= minRemaining) {
8971
+ chunksToActivate = bestOverBoundary;
8972
+ } else if (bestUnderBoundary > 0 && remainingAfterUnder >= minRemaining) {
8973
+ chunksToActivate = bestUnderBoundary;
8974
+ } else if (bestOverBoundary > 0) {
8975
+ chunksToActivate = bestOverBoundary;
8976
+ } else {
8977
+ chunksToActivate = 1;
8978
+ }
8979
+ const activatedChunks = chunks.slice(0, chunksToActivate);
8980
+ const remainingChunks = chunks.slice(chunksToActivate);
8981
+ const activatedContent = activatedChunks.map((c) => c.observations).join("\n\n");
8982
+ const activatedTokens = activatedChunks.reduce((sum, c) => sum + c.tokenCount, 0);
8983
+ const activatedMessageTokens = activatedChunks.reduce((sum, c) => sum + (c.messageTokens ?? 0), 0);
8984
+ const activatedMessageCount = activatedChunks.reduce((sum, c) => sum + c.messageIds.length, 0);
8985
+ const activatedCycleIds = activatedChunks.map((c) => c.cycleId).filter((id) => !!id);
8986
+ const activatedMessageIds = activatedChunks.flatMap((c) => c.messageIds ?? []);
8987
+ const latestChunk = activatedChunks[activatedChunks.length - 1];
8988
+ const lastObservedAt = input.lastObservedAt ?? (latestChunk?.lastObservedAt ? new Date(latestChunk.lastObservedAt) : /* @__PURE__ */ new Date());
8989
+ const lastObservedAtStr = lastObservedAt.toISOString();
8990
+ const existingActive = row.activeObservations || "";
8991
+ const existingTokenCount = Number(row.observationTokenCount || 0);
8992
+ const boundary = `
8940
8993
 
8941
8994
  --- message boundary (${lastObservedAt.toISOString()}) ---
8942
8995
 
8943
8996
  `;
8944
- const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
8945
- const newTokenCount = existingTokenCount + activatedTokens;
8946
- const existingPending = Number(row.pendingMessageTokens || 0);
8947
- const newPending = Math.max(0, existingPending - activatedMessageTokens);
8948
- const updateResult = await this.#client.execute({
8949
- sql: `UPDATE "${OM_TABLE}" SET
8950
- "activeObservations" = ?,
8951
- "observationTokenCount" = ?,
8952
- "pendingMessageTokens" = ?,
8953
- "bufferedObservationChunks" = ?,
8954
- "lastObservedAt" = ?,
8955
- "updatedAt" = ?
8956
- WHERE id = ?
8957
- AND "bufferedObservationChunks" IS NOT NULL
8958
- AND "bufferedObservationChunks" != '[]'`,
8959
- args: [
8960
- newActive,
8961
- newTokenCount,
8962
- newPending,
8963
- remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
8964
- lastObservedAtStr,
8965
- nowStr,
8966
- input.id
8967
- ]
8997
+ const newActive = existingActive ? `${existingActive}${boundary}${activatedContent}` : activatedContent;
8998
+ const newTokenCount = existingTokenCount + activatedTokens;
8999
+ const existingPending = Number(row.pendingMessageTokens || 0);
9000
+ const newPending = Math.max(0, existingPending - activatedMessageTokens);
9001
+ const updateResult = await tx.execute({
9002
+ sql: `UPDATE "${OM_TABLE}" SET
9003
+ "activeObservations" = ?,
9004
+ "observationTokenCount" = ?,
9005
+ "pendingMessageTokens" = ?,
9006
+ "bufferedObservationChunks" = ?,
9007
+ "lastObservedAt" = ?,
9008
+ "updatedAt" = ?
9009
+ WHERE id = ?
9010
+ AND "bufferedObservationChunks" IS NOT NULL
9011
+ AND "bufferedObservationChunks" != '[]'`,
9012
+ args: [
9013
+ newActive,
9014
+ newTokenCount,
9015
+ newPending,
9016
+ remainingChunks.length > 0 ? JSON.stringify(remainingChunks) : null,
9017
+ lastObservedAtStr,
9018
+ nowStr,
9019
+ input.id
9020
+ ]
9021
+ });
9022
+ await tx.commit();
9023
+ if (updateResult.rowsAffected === 0) {
9024
+ return {
9025
+ chunksActivated: 0,
9026
+ messageTokensActivated: 0,
9027
+ observationTokensActivated: 0,
9028
+ messagesActivated: 0,
9029
+ activatedCycleIds: [],
9030
+ activatedMessageIds: []
9031
+ };
9032
+ }
9033
+ const latestChunkHints = activatedChunks[activatedChunks.length - 1];
9034
+ return {
9035
+ chunksActivated: activatedChunks.length,
9036
+ messageTokensActivated: activatedMessageTokens,
9037
+ observationTokensActivated: activatedTokens,
9038
+ messagesActivated: activatedMessageCount,
9039
+ activatedCycleIds,
9040
+ activatedMessageIds,
9041
+ observations: activatedContent,
9042
+ perChunk: activatedChunks.map((c) => ({
9043
+ cycleId: c.cycleId ?? "",
9044
+ messageTokens: c.messageTokens ?? 0,
9045
+ observationTokens: c.tokenCount,
9046
+ messageCount: c.messageIds.length,
9047
+ observations: c.observations
9048
+ })),
9049
+ suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
9050
+ currentTask: latestChunkHints?.currentTask ?? void 0
9051
+ };
9052
+ } catch (error) {
9053
+ if (!tx.closed) await tx.rollback();
9054
+ throw error;
9055
+ }
8968
9056
  });
8969
- if (updateResult.rowsAffected === 0) {
8970
- return {
8971
- chunksActivated: 0,
8972
- messageTokensActivated: 0,
8973
- observationTokensActivated: 0,
8974
- messagesActivated: 0,
8975
- activatedCycleIds: [],
8976
- activatedMessageIds: []
8977
- };
8978
- }
8979
- const latestChunkHints = activatedChunks[activatedChunks.length - 1];
8980
- return {
8981
- chunksActivated: activatedChunks.length,
8982
- messageTokensActivated: activatedMessageTokens,
8983
- observationTokensActivated: activatedTokens,
8984
- messagesActivated: activatedMessageCount,
8985
- activatedCycleIds,
8986
- activatedMessageIds,
8987
- observations: activatedContent,
8988
- perChunk: activatedChunks.map((c) => ({
8989
- cycleId: c.cycleId ?? "",
8990
- messageTokens: c.messageTokens ?? 0,
8991
- observationTokens: c.tokenCount,
8992
- messageCount: c.messageIds.length,
8993
- observations: c.observations
8994
- })),
8995
- suggestedContinuation: latestChunkHints?.suggestedContinuation ?? void 0,
8996
- currentTask: latestChunkHints?.currentTask ?? void 0
8997
- };
8998
9057
  } catch (error$1) {
8999
9058
  if (error$1 instanceof error.MastraError) {
9000
9059
  throw error$1;
@@ -9013,28 +9072,31 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
9013
9072
  async updateBufferedReflection(input) {
9014
9073
  try {
9015
9074
  const nowStr = (/* @__PURE__ */ new Date()).toISOString();
9016
- const result = await this.#client.execute({
9017
- sql: `UPDATE "${OM_TABLE}" SET
9018
- "bufferedReflection" = CASE
9019
- WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
9020
- THEN "bufferedReflection" || char(10) || char(10) || ?
9021
- ELSE ?
9022
- END,
9023
- "bufferedReflectionTokens" = COALESCE("bufferedReflectionTokens", 0) + ?,
9024
- "bufferedReflectionInputTokens" = COALESCE("bufferedReflectionInputTokens", 0) + ?,
9025
- "reflectedObservationLineCount" = ?,
9026
- "updatedAt" = ?
9027
- WHERE id = ?`,
9028
- args: [
9029
- input.reflection,
9030
- input.reflection,
9031
- input.tokenCount,
9032
- input.inputTokenCount,
9033
- input.reflectedObservationLineCount,
9034
- nowStr,
9035
- input.id
9036
- ]
9037
- });
9075
+ const result = await withClientWriteLock(
9076
+ this.#client,
9077
+ () => this.#client.execute({
9078
+ sql: `UPDATE "${OM_TABLE}" SET
9079
+ "bufferedReflection" = CASE
9080
+ WHEN "bufferedReflection" IS NOT NULL AND "bufferedReflection" != ''
9081
+ THEN "bufferedReflection" || char(10) || char(10) || ?
9082
+ ELSE ?
9083
+ END,
9084
+ "bufferedReflectionTokens" = COALESCE("bufferedReflectionTokens", 0) + ?,
9085
+ "bufferedReflectionInputTokens" = COALESCE("bufferedReflectionInputTokens", 0) + ?,
9086
+ "reflectedObservationLineCount" = ?,
9087
+ "updatedAt" = ?
9088
+ WHERE id = ?`,
9089
+ args: [
9090
+ input.reflection,
9091
+ input.reflection,
9092
+ input.tokenCount,
9093
+ input.inputTokenCount,
9094
+ input.reflectedObservationLineCount,
9095
+ nowStr,
9096
+ input.id
9097
+ ]
9098
+ })
9099
+ );
9038
9100
  if (result.rowsAffected === 0) {
9039
9101
  throw new error.MastraError({
9040
9102
  id: storage.createStorageErrorId("LIBSQL", "UPDATE_BUFFERED_REFLECTION", "NOT_FOUND"),
@@ -9061,55 +9123,64 @@ var MemoryLibSQL = class _MemoryLibSQL extends storage.MemoryStorage {
9061
9123
  }
9062
9124
  async swapBufferedReflectionToActive(input) {
9063
9125
  try {
9064
- const current = await this.#client.execute({
9065
- sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
9066
- args: [input.currentRecord.id]
9067
- });
9068
- if (!current.rows || current.rows.length === 0) {
9069
- throw new error.MastraError({
9070
- id: storage.createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NOT_FOUND"),
9071
- text: `Observational memory record not found: ${input.currentRecord.id}`,
9072
- domain: error.ErrorDomain.STORAGE,
9073
- category: error.ErrorCategory.THIRD_PARTY,
9074
- details: { id: input.currentRecord.id }
9075
- });
9076
- }
9077
- const row = current.rows[0];
9078
- const bufferedReflection = row.bufferedReflection || "";
9079
- const reflectedLineCount = Number(row.reflectedObservationLineCount || 0);
9080
- if (!bufferedReflection) {
9081
- throw new error.MastraError({
9082
- id: storage.createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
9083
- text: "No buffered reflection to swap",
9084
- domain: error.ErrorDomain.STORAGE,
9085
- category: error.ErrorCategory.USER,
9086
- details: { id: input.currentRecord.id }
9087
- });
9088
- }
9089
- const currentObservations = row.activeObservations || "";
9090
- const allLines = currentObservations.split("\n");
9091
- const unreflectedLines = allLines.slice(reflectedLineCount);
9092
- const unreflectedContent = unreflectedLines.join("\n").trim();
9093
- const newObservations = unreflectedContent ? `${bufferedReflection}
9126
+ return await withClientWriteLock(this.#client, async () => {
9127
+ const tx = await this.#client.transaction("write");
9128
+ try {
9129
+ const current = await tx.execute({
9130
+ sql: `SELECT * FROM "${OM_TABLE}" WHERE id = ?`,
9131
+ args: [input.currentRecord.id]
9132
+ });
9133
+ if (!current.rows || current.rows.length === 0) {
9134
+ throw new error.MastraError({
9135
+ id: storage.createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NOT_FOUND"),
9136
+ text: `Observational memory record not found: ${input.currentRecord.id}`,
9137
+ domain: error.ErrorDomain.STORAGE,
9138
+ category: error.ErrorCategory.THIRD_PARTY,
9139
+ details: { id: input.currentRecord.id }
9140
+ });
9141
+ }
9142
+ const row = current.rows[0];
9143
+ const bufferedReflection = row.bufferedReflection || "";
9144
+ const reflectedLineCount = Number(row.reflectedObservationLineCount || 0);
9145
+ if (!bufferedReflection) {
9146
+ throw new error.MastraError({
9147
+ id: storage.createStorageErrorId("LIBSQL", "SWAP_BUFFERED_REFLECTION_TO_ACTIVE", "NO_CONTENT"),
9148
+ text: "No buffered reflection to swap",
9149
+ domain: error.ErrorDomain.STORAGE,
9150
+ category: error.ErrorCategory.USER,
9151
+ details: { id: input.currentRecord.id }
9152
+ });
9153
+ }
9154
+ const currentObservations = row.activeObservations || "";
9155
+ const allLines = currentObservations.split("\n");
9156
+ const unreflectedLines = allLines.slice(reflectedLineCount);
9157
+ const unreflectedContent = unreflectedLines.join("\n").trim();
9158
+ const newObservations = unreflectedContent ? `${bufferedReflection}
9094
9159
 
9095
9160
  ${unreflectedContent}` : bufferedReflection;
9096
- const newRecord = await this.createReflectionGeneration({
9097
- currentRecord: input.currentRecord,
9098
- reflection: newObservations,
9099
- tokenCount: input.tokenCount
9100
- });
9101
- const nowStr = (/* @__PURE__ */ new Date()).toISOString();
9102
- await this.#client.execute({
9103
- sql: `UPDATE "${OM_TABLE}" SET
9104
- "bufferedReflection" = NULL,
9105
- "bufferedReflectionTokens" = NULL,
9106
- "bufferedReflectionInputTokens" = NULL,
9107
- "reflectedObservationLineCount" = NULL,
9108
- "updatedAt" = ?
9109
- WHERE id = ?`,
9110
- args: [nowStr, input.currentRecord.id]
9161
+ const newRecord = await this.#insertReflectionGeneration(tx, {
9162
+ currentRecord: input.currentRecord,
9163
+ reflection: newObservations,
9164
+ tokenCount: input.tokenCount
9165
+ });
9166
+ const nowStr = (/* @__PURE__ */ new Date()).toISOString();
9167
+ await tx.execute({
9168
+ sql: `UPDATE "${OM_TABLE}" SET
9169
+ "bufferedReflection" = NULL,
9170
+ "bufferedReflectionTokens" = NULL,
9171
+ "bufferedReflectionInputTokens" = NULL,
9172
+ "reflectedObservationLineCount" = NULL,
9173
+ "updatedAt" = ?
9174
+ WHERE id = ?`,
9175
+ args: [nowStr, input.currentRecord.id]
9176
+ });
9177
+ await tx.commit();
9178
+ return newRecord;
9179
+ } catch (error) {
9180
+ if (!tx.closed) await tx.rollback();
9181
+ throw error;
9182
+ }
9111
9183
  });
9112
- return newRecord;
9113
9184
  } catch (error$1) {
9114
9185
  if (error$1 instanceof error.MastraError) {
9115
9186
  throw error$1;
@@ -13932,32 +14003,14 @@ var LibSQLStore = class extends storage.MastraCompositeStore {
13932
14003
  await this.initDomainsSequentially();
13933
14004
  }
13934
14005
  /**
13935
- * Closes the underlying libsql client, releasing all OS file handles.
13936
- *
13937
- * For local file databases, first runs PRAGMA wal_checkpoint(TRUNCATE) and
13938
- * switches back to journal_mode=DELETE so that Windows releases the -wal
13939
- * and -shm sidecar files promptly. Without this, the handles stay open
13940
- * until process exit, causing EBUSY errors when callers try to fs.rm the
13941
- * storage directory after Mastra.shutdown().
13942
- *
13943
- * Remote (Turso) databases skip the WAL pragmas and just close the client.
14006
+ * Closes the underlying libsql client, releasing this store's OS file handles.
13944
14007
  *
13945
14008
  * Safe to call more than once; subsequent calls are no-ops.
13946
14009
  */
13947
14010
  async close() {
13948
- if (this.client.closed) {
13949
- return;
13950
- }
13951
- const isLocalFileDb = this.isLocalDb || this.client.protocol === "file";
13952
- if (isLocalFileDb) {
13953
- try {
13954
- await this.client.execute("PRAGMA wal_checkpoint(TRUNCATE);");
13955
- await this.client.execute("PRAGMA journal_mode=DELETE;");
13956
- } catch (err) {
13957
- this.logger.warn("LibSQLStore: Failed to checkpoint WAL before close.", err);
13958
- }
14011
+ if (!this.client.closed) {
14012
+ this.client.close();
13959
14013
  }
13960
- this.client.close();
13961
14014
  }
13962
14015
  };
13963
14016