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