@mastra/libsql 1.22.5 → 1.23.0-alpha.2

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
@@ -1850,6 +1850,9 @@ MIGRATION REQUIRED: Duplicate spans detected in ${_mastra_core_storage.TABLE_SPA
1850
1850
  this.logger.debug(`LibSQLDB: Created unique index on (spanId, traceId) for ${_mastra_core_storage.TABLE_SPANS}`);
1851
1851
  }
1852
1852
  }
1853
+ await this.client.execute(`CREATE INDEX IF NOT EXISTS "mastra_ai_spans_roots_started_at_idx" ON "${_mastra_core_storage.TABLE_SPANS}" ("startedAt" DESC) WHERE "parentSpanId" IS NULL`);
1854
+ await this.client.execute(`CREATE INDEX IF NOT EXISTS "mastra_ai_spans_trace_started_at_idx" ON "${_mastra_core_storage.TABLE_SPANS}" ("traceId", "startedAt")`);
1855
+ this.logger.debug(`LibSQLDB: Ensured trace read indexes for ${_mastra_core_storage.TABLE_SPANS}`);
1853
1856
  this.logger.info(`LibSQLDB: Migration completed for ${_mastra_core_storage.TABLE_SPANS}`);
1854
1857
  } catch (error) {
1855
1858
  if (error instanceof _mastra_core_error.MastraError) throw error;
@@ -3021,8 +3024,15 @@ function resolveTargets({ policies, descriptor, order }) {
3021
3024
  //#endregion
3022
3025
  //#region src/storage/domains/background-tasks/index.ts
3023
3026
  function serializeJson(v) {
3024
- if (typeof v === "object" && v != null) return JSON.stringify(v);
3025
- return v ?? null;
3027
+ if (v === void 0) return null;
3028
+ let serialized;
3029
+ try {
3030
+ serialized = JSON.stringify(v);
3031
+ } catch (error) {
3032
+ throw new Error("Failed to serialize background task value as JSON", { cause: error });
3033
+ }
3034
+ if (serialized === void 0) throw new Error("Failed to serialize background task value as JSON");
3035
+ return serialized;
3026
3036
  }
3027
3037
  function parseJson$4(val) {
3028
3038
  if (val == null) return void 0;
@@ -4141,94 +4151,122 @@ var DatasetsLibSQL = class extends _mastra_core_storage.DatasetsStorage {
4141
4151
  }
4142
4152
  async _doUpdateItem(args) {
4143
4153
  try {
4144
- const existing = await this.getItemById({ id: args.id });
4145
- if (!existing) throw new _mastra_core_error.MastraError({
4146
- id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_ITEM", "NOT_FOUND"),
4147
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4148
- category: _mastra_core_error.ErrorCategory.USER,
4149
- details: { itemId: args.id }
4150
- });
4151
- if (existing.datasetId !== args.datasetId) throw new _mastra_core_error.MastraError({
4152
- id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_ITEM", "DATASET_MISMATCH"),
4153
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4154
- category: _mastra_core_error.ErrorCategory.USER,
4155
- details: {
4156
- itemId: args.id,
4157
- expectedDatasetId: args.datasetId,
4158
- actualDatasetId: existing.datasetId
4154
+ return await withClientWriteLock(this.#client, async () => {
4155
+ const tx = await this.#client.transaction("write");
4156
+ try {
4157
+ const itemResult = await tx.execute({
4158
+ sql: `SELECT ${buildSelectColumns(_mastra_core_storage.TABLE_DATASET_ITEMS)} FROM ${_mastra_core_storage.TABLE_DATASET_ITEMS} WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4159
+ args: [args.id]
4160
+ });
4161
+ const existing = itemResult.rows[0] ? this.transformItemRow(itemResult.rows[0]) : null;
4162
+ if (!existing) throw new _mastra_core_error.MastraError({
4163
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_ITEM", "NOT_FOUND"),
4164
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
4165
+ category: _mastra_core_error.ErrorCategory.USER,
4166
+ details: { itemId: args.id }
4167
+ });
4168
+ if (existing.datasetId !== args.datasetId) throw new _mastra_core_error.MastraError({
4169
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_ITEM", "DATASET_MISMATCH"),
4170
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
4171
+ category: _mastra_core_error.ErrorCategory.USER,
4172
+ details: {
4173
+ itemId: args.id,
4174
+ expectedDatasetId: args.datasetId,
4175
+ actualDatasetId: existing.datasetId
4176
+ }
4177
+ });
4178
+ if (existing.metadata?.__purged === true) throw new _mastra_core_error.MastraError({
4179
+ id: "DATASET_ITEM_PURGED",
4180
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
4181
+ category: _mastra_core_error.ErrorCategory.USER,
4182
+ details: {
4183
+ datasetId: args.datasetId,
4184
+ itemId: args.id
4185
+ },
4186
+ text: `Purged dataset item cannot be updated: ${args.id}`
4187
+ });
4188
+ const dataset = (await tx.execute({
4189
+ sql: `SELECT organizationId, projectId FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?`,
4190
+ args: [args.datasetId]
4191
+ })).rows[0];
4192
+ const organizationId = typeof dataset?.organizationId === "string" ? dataset.organizationId : null;
4193
+ const projectId = typeof dataset?.projectId === "string" ? dataset.projectId : null;
4194
+ const versionId = crypto.randomUUID();
4195
+ const now = /* @__PURE__ */ new Date();
4196
+ const nowIso = now.toISOString();
4197
+ const mergedInput = args.input !== void 0 ? args.input : existing.input;
4198
+ const mergedGroundTruth = args.groundTruth !== void 0 ? args.groundTruth : existing.groundTruth;
4199
+ const mergedExpectedTrajectory = args.expectedTrajectory !== void 0 ? args.expectedTrajectory : existing.expectedTrajectory;
4200
+ const mergedToolMocks = args.toolMocks !== void 0 ? args.toolMocks : existing.toolMocks;
4201
+ const mergedUnmockedToolPolicy = args.unmockedToolPolicy !== void 0 ? args.unmockedToolPolicy : existing.unmockedToolPolicy;
4202
+ const mergedScorerIds = args.scorerIds !== void 0 ? args.scorerIds ?? void 0 : existing.scorerIds;
4203
+ const mergedRequestContext = args.requestContext !== void 0 ? args.requestContext : existing.requestContext;
4204
+ const mergedMetadata = args.metadata !== void 0 ? args.metadata : existing.metadata;
4205
+ const mergedSource = args.source !== void 0 ? args.source : existing.source;
4206
+ const versionResult = await tx.execute({
4207
+ sql: `UPDATE ${_mastra_core_storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version`,
4208
+ args: [args.datasetId]
4209
+ });
4210
+ const newVersion = Number(versionResult.rows[0].version);
4211
+ await tx.execute({
4212
+ sql: `UPDATE ${_mastra_core_storage.TABLE_DATASET_ITEMS} SET validTo = ? WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4213
+ args: [newVersion, args.id]
4214
+ });
4215
+ await tx.execute({
4216
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_ITEMS} (id,datasetId,datasetVersion,externalId,organizationId,projectId,validTo,isDeleted,input,groundTruth,expectedTrajectory,toolMocks,unmockedToolPolicy,scorerIds,requestContext,metadata,source,createdAt,updatedAt) VALUES (?,?,?,?,?,?,NULL,0,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,?)`,
4217
+ args: [
4218
+ args.id,
4219
+ args.datasetId,
4220
+ newVersion,
4221
+ existing.externalId ?? null,
4222
+ organizationId,
4223
+ projectId,
4224
+ jsonbArg(mergedInput),
4225
+ jsonbArg(mergedGroundTruth),
4226
+ jsonbArg(mergedExpectedTrajectory),
4227
+ jsonbArg(mergedToolMocks),
4228
+ mergedUnmockedToolPolicy ?? null,
4229
+ jsonbArg(mergedScorerIds),
4230
+ jsonbArg(mergedRequestContext),
4231
+ jsonbArg(mergedMetadata),
4232
+ jsonbArg(mergedSource),
4233
+ existing.createdAt.toISOString(),
4234
+ nowIso
4235
+ ]
4236
+ });
4237
+ await tx.execute({
4238
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, ?, ?)`,
4239
+ args: [
4240
+ versionId,
4241
+ args.datasetId,
4242
+ newVersion,
4243
+ nowIso
4244
+ ]
4245
+ });
4246
+ await tx.commit();
4247
+ return {
4248
+ ...existing,
4249
+ datasetVersion: newVersion,
4250
+ organizationId,
4251
+ projectId,
4252
+ input: mergedInput,
4253
+ groundTruth: mergedGroundTruth,
4254
+ expectedTrajectory: mergedExpectedTrajectory,
4255
+ toolMocks: mergedToolMocks,
4256
+ unmockedToolPolicy: mergedUnmockedToolPolicy,
4257
+ scorerIds: mergedScorerIds,
4258
+ requestContext: mergedRequestContext,
4259
+ metadata: mergedMetadata,
4260
+ source: mergedSource,
4261
+ updatedAt: now
4262
+ };
4263
+ } catch (error) {
4264
+ if (!tx.closed) await tx.rollback().catch((rollbackError) => {
4265
+ throw new AggregateError([error, rollbackError], "Transaction and rollback both failed");
4266
+ });
4267
+ throw error;
4159
4268
  }
4160
4269
  });
4161
- const dataset = await this.getDatasetById({ id: args.datasetId });
4162
- const versionId = crypto.randomUUID();
4163
- const now = /* @__PURE__ */ new Date();
4164
- const nowIso = now.toISOString();
4165
- const mergedInput = args.input !== void 0 ? args.input : existing.input;
4166
- const mergedGroundTruth = args.groundTruth !== void 0 ? args.groundTruth : existing.groundTruth;
4167
- const mergedExpectedTrajectory = args.expectedTrajectory !== void 0 ? args.expectedTrajectory : existing.expectedTrajectory;
4168
- const mergedToolMocks = args.toolMocks !== void 0 ? args.toolMocks : existing.toolMocks;
4169
- const mergedUnmockedToolPolicy = args.unmockedToolPolicy !== void 0 ? args.unmockedToolPolicy : existing.unmockedToolPolicy;
4170
- const mergedScorerIds = args.scorerIds !== void 0 ? args.scorerIds ?? void 0 : existing.scorerIds;
4171
- const mergedRequestContext = args.requestContext !== void 0 ? args.requestContext : existing.requestContext;
4172
- const mergedMetadata = args.metadata !== void 0 ? args.metadata : existing.metadata;
4173
- const mergedSource = args.source !== void 0 ? args.source : existing.source;
4174
- const results = await this.#client.batch([
4175
- {
4176
- sql: `UPDATE ${_mastra_core_storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version`,
4177
- args: [args.datasetId]
4178
- },
4179
- {
4180
- sql: `UPDATE ${_mastra_core_storage.TABLE_DATASET_ITEMS} SET validTo = (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?) WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4181
- args: [args.datasetId, args.id]
4182
- },
4183
- {
4184
- sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_ITEMS} (id,datasetId,datasetVersion,externalId,organizationId,projectId,validTo,isDeleted,input,groundTruth,expectedTrajectory,toolMocks,unmockedToolPolicy,scorerIds,requestContext,metadata,source,createdAt,updatedAt) VALUES (?,?,(SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),?,(SELECT organizationId FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),(SELECT projectId FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),NULL,0,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,?)`,
4185
- args: [
4186
- args.id,
4187
- args.datasetId,
4188
- args.datasetId,
4189
- existing.externalId ?? null,
4190
- args.datasetId,
4191
- args.datasetId,
4192
- jsonbArg(mergedInput),
4193
- jsonbArg(mergedGroundTruth),
4194
- jsonbArg(mergedExpectedTrajectory),
4195
- jsonbArg(mergedToolMocks),
4196
- mergedUnmockedToolPolicy ?? null,
4197
- jsonbArg(mergedScorerIds),
4198
- jsonbArg(mergedRequestContext),
4199
- jsonbArg(mergedMetadata),
4200
- jsonbArg(mergedSource),
4201
- existing.createdAt.toISOString(),
4202
- nowIso
4203
- ]
4204
- },
4205
- {
4206
- sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?), ?)`,
4207
- args: [
4208
- versionId,
4209
- args.datasetId,
4210
- args.datasetId,
4211
- nowIso
4212
- ]
4213
- }
4214
- ], "write");
4215
- const newVersion = Number(results[0].rows[0].version);
4216
- return {
4217
- ...existing,
4218
- datasetVersion: newVersion,
4219
- organizationId: dataset?.organizationId ?? null,
4220
- projectId: dataset?.projectId ?? null,
4221
- input: mergedInput,
4222
- groundTruth: mergedGroundTruth,
4223
- expectedTrajectory: mergedExpectedTrajectory,
4224
- toolMocks: mergedToolMocks,
4225
- unmockedToolPolicy: mergedUnmockedToolPolicy,
4226
- scorerIds: mergedScorerIds,
4227
- requestContext: mergedRequestContext,
4228
- metadata: mergedMetadata,
4229
- source: mergedSource,
4230
- updatedAt: now
4231
- };
4232
4270
  } catch (error) {
4233
4271
  if (error instanceof _mastra_core_error.MastraError) throw error;
4234
4272
  throw new _mastra_core_error.MastraError({
@@ -4240,61 +4278,78 @@ var DatasetsLibSQL = class extends _mastra_core_storage.DatasetsStorage {
4240
4278
  }
4241
4279
  async _doDeleteItem({ id, datasetId }) {
4242
4280
  try {
4243
- const existing = await this.getItemById({ id });
4244
- if (!existing) return;
4245
- if (existing.datasetId !== datasetId) throw new _mastra_core_error.MastraError({
4246
- id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "DELETE_ITEM", "DATASET_MISMATCH"),
4247
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4248
- category: _mastra_core_error.ErrorCategory.USER,
4249
- details: {
4250
- itemId: id,
4251
- expectedDatasetId: datasetId,
4252
- actualDatasetId: existing.datasetId
4281
+ await withClientWriteLock(this.#client, async () => {
4282
+ const tx = await this.#client.transaction("write");
4283
+ try {
4284
+ const itemResult = await tx.execute({
4285
+ sql: `SELECT ${buildSelectColumns(_mastra_core_storage.TABLE_DATASET_ITEMS)} FROM ${_mastra_core_storage.TABLE_DATASET_ITEMS} WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4286
+ args: [id]
4287
+ });
4288
+ const existing = itemResult.rows[0] ? this.transformItemRow(itemResult.rows[0]) : null;
4289
+ if (!existing) {
4290
+ await tx.commit();
4291
+ return;
4292
+ }
4293
+ if (existing.datasetId !== datasetId) throw new _mastra_core_error.MastraError({
4294
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "DELETE_ITEM", "DATASET_MISMATCH"),
4295
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
4296
+ category: _mastra_core_error.ErrorCategory.USER,
4297
+ details: {
4298
+ itemId: id,
4299
+ expectedDatasetId: datasetId,
4300
+ actualDatasetId: existing.datasetId
4301
+ }
4302
+ });
4303
+ const versionId = crypto.randomUUID();
4304
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4305
+ const dataset = (await tx.execute({
4306
+ sql: `UPDATE ${_mastra_core_storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version, organizationId, projectId`,
4307
+ args: [datasetId]
4308
+ })).rows[0];
4309
+ const newVersion = Number(dataset.version);
4310
+ await tx.execute({
4311
+ sql: `UPDATE ${_mastra_core_storage.TABLE_DATASET_ITEMS} SET validTo = ? WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4312
+ args: [newVersion, id]
4313
+ });
4314
+ await tx.execute({
4315
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_ITEMS} (id,datasetId,datasetVersion,externalId,organizationId,projectId,validTo,isDeleted,input,groundTruth,expectedTrajectory,toolMocks,unmockedToolPolicy,scorerIds,requestContext,metadata,source,createdAt,updatedAt) VALUES (?,?,?,?,?,?,NULL,1,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,?)`,
4316
+ args: [
4317
+ id,
4318
+ datasetId,
4319
+ newVersion,
4320
+ existing.externalId ?? null,
4321
+ dataset.organizationId ?? null,
4322
+ dataset.projectId ?? null,
4323
+ jsonbArg(existing.input),
4324
+ jsonbArg(existing.groundTruth),
4325
+ jsonbArg(existing.expectedTrajectory),
4326
+ jsonbArg(existing.toolMocks),
4327
+ existing.unmockedToolPolicy ?? null,
4328
+ jsonbArg(existing.scorerIds),
4329
+ jsonbArg(existing.requestContext),
4330
+ jsonbArg(existing.metadata),
4331
+ jsonbArg(existing.source),
4332
+ existing.createdAt.toISOString(),
4333
+ nowIso
4334
+ ]
4335
+ });
4336
+ await tx.execute({
4337
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, ?, ?)`,
4338
+ args: [
4339
+ versionId,
4340
+ datasetId,
4341
+ newVersion,
4342
+ nowIso
4343
+ ]
4344
+ });
4345
+ await tx.commit();
4346
+ } catch (error) {
4347
+ if (!tx.closed) await tx.rollback().catch((rollbackError) => {
4348
+ throw new AggregateError([error, rollbackError], "Transaction and rollback both failed");
4349
+ });
4350
+ throw error;
4253
4351
  }
4254
4352
  });
4255
- const versionId = crypto.randomUUID();
4256
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4257
- await this.#client.batch([
4258
- {
4259
- sql: `UPDATE ${_mastra_core_storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version`,
4260
- args: [datasetId]
4261
- },
4262
- {
4263
- sql: `UPDATE ${_mastra_core_storage.TABLE_DATASET_ITEMS} SET validTo = (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?) WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4264
- args: [datasetId, id]
4265
- },
4266
- {
4267
- sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_ITEMS} (id,datasetId,datasetVersion,externalId,organizationId,projectId,validTo,isDeleted,input,groundTruth,expectedTrajectory,toolMocks,unmockedToolPolicy,scorerIds,requestContext,metadata,source,createdAt,updatedAt) VALUES (?,?,(SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),?,(SELECT organizationId FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),(SELECT projectId FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),NULL,1,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,?)`,
4268
- args: [
4269
- id,
4270
- datasetId,
4271
- datasetId,
4272
- existing.externalId ?? null,
4273
- datasetId,
4274
- datasetId,
4275
- jsonbArg(existing.input),
4276
- jsonbArg(existing.groundTruth),
4277
- jsonbArg(existing.expectedTrajectory),
4278
- jsonbArg(existing.toolMocks),
4279
- existing.unmockedToolPolicy ?? null,
4280
- jsonbArg(existing.scorerIds),
4281
- jsonbArg(existing.requestContext),
4282
- jsonbArg(existing.metadata),
4283
- jsonbArg(existing.source),
4284
- existing.createdAt.toISOString(),
4285
- nowIso
4286
- ]
4287
- },
4288
- {
4289
- sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?), ?)`,
4290
- args: [
4291
- versionId,
4292
- datasetId,
4293
- datasetId,
4294
- nowIso
4295
- ]
4296
- }
4297
- ], "write");
4298
4353
  } catch (error) {
4299
4354
  if (error instanceof _mastra_core_error.MastraError) throw error;
4300
4355
  throw new _mastra_core_error.MastraError({
@@ -4712,63 +4767,84 @@ var DatasetsLibSQL = class extends _mastra_core_storage.DatasetsStorage {
4712
4767
  }
4713
4768
  async _doBatchDeleteItems(input) {
4714
4769
  try {
4715
- const dataset = await this.getDatasetById({ id: input.datasetId });
4716
- if (!dataset) throw new _mastra_core_error.MastraError({
4717
- id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "BULK_DELETE_ITEMS", "DATASET_NOT_FOUND"),
4718
- domain: _mastra_core_error.ErrorDomain.STORAGE,
4719
- category: _mastra_core_error.ErrorCategory.USER,
4720
- details: { datasetId: input.datasetId }
4721
- });
4722
- const currentItems = [];
4723
- for (const itemId of input.itemIds) {
4724
- const item = await this.getItemById({ id: itemId });
4725
- if (item && item.datasetId === input.datasetId) currentItems.push(item);
4726
- }
4727
- if (currentItems.length === 0) return;
4728
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4729
- const versionId = crypto.randomUUID();
4730
- const statements = [{
4731
- sql: `UPDATE ${_mastra_core_storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version`,
4732
- args: [input.datasetId]
4733
- }];
4734
- for (const item of currentItems) {
4735
- statements.push({
4736
- sql: `UPDATE ${_mastra_core_storage.TABLE_DATASET_ITEMS} SET validTo = (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?) WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4737
- args: [input.datasetId, item.id]
4738
- });
4739
- statements.push({
4740
- sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_ITEMS} (id,datasetId,datasetVersion,externalId,organizationId,projectId,validTo,isDeleted,input,groundTruth,expectedTrajectory,toolMocks,unmockedToolPolicy,scorerIds,requestContext,metadata,source,createdAt,updatedAt) VALUES (?,?,(SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),?,?,?,NULL,1,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,?)`,
4741
- args: [
4742
- item.id,
4743
- input.datasetId,
4744
- input.datasetId,
4745
- item.externalId ?? null,
4746
- dataset.organizationId ?? null,
4747
- dataset.projectId ?? null,
4748
- jsonbArg(item.input),
4749
- jsonbArg(item.groundTruth),
4750
- jsonbArg(item.expectedTrajectory),
4751
- jsonbArg(item.toolMocks),
4752
- item.unmockedToolPolicy ?? null,
4753
- jsonbArg(item.scorerIds),
4754
- jsonbArg(item.requestContext),
4755
- jsonbArg(item.metadata),
4756
- jsonbArg(item.source),
4757
- item.createdAt.toISOString(),
4758
- nowIso
4759
- ]
4760
- });
4761
- }
4762
- statements.push({
4763
- sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?), ?)`,
4764
- args: [
4765
- versionId,
4766
- input.datasetId,
4767
- input.datasetId,
4768
- nowIso
4769
- ]
4770
+ await withClientWriteLock(this.#client, async () => {
4771
+ const tx = await this.#client.transaction("write");
4772
+ try {
4773
+ const dataset = (await tx.execute({
4774
+ sql: `SELECT organizationId, projectId FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?`,
4775
+ args: [input.datasetId]
4776
+ })).rows[0];
4777
+ if (!dataset) throw new _mastra_core_error.MastraError({
4778
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "BULK_DELETE_ITEMS", "DATASET_NOT_FOUND"),
4779
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
4780
+ category: _mastra_core_error.ErrorCategory.USER,
4781
+ details: { datasetId: input.datasetId }
4782
+ });
4783
+ const currentItems = [];
4784
+ for (const itemId of input.itemIds) {
4785
+ const itemResult = await tx.execute({
4786
+ sql: `SELECT ${buildSelectColumns(_mastra_core_storage.TABLE_DATASET_ITEMS)} FROM ${_mastra_core_storage.TABLE_DATASET_ITEMS} WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4787
+ args: [itemId]
4788
+ });
4789
+ const item = itemResult.rows[0] ? this.transformItemRow(itemResult.rows[0]) : null;
4790
+ if (item && item.datasetId === input.datasetId) currentItems.push(item);
4791
+ }
4792
+ if (currentItems.length === 0) {
4793
+ await tx.commit();
4794
+ return;
4795
+ }
4796
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4797
+ const versionId = crypto.randomUUID();
4798
+ const statements = [{
4799
+ sql: `UPDATE ${_mastra_core_storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version`,
4800
+ args: [input.datasetId]
4801
+ }];
4802
+ for (const item of currentItems) {
4803
+ statements.push({
4804
+ sql: `UPDATE ${_mastra_core_storage.TABLE_DATASET_ITEMS} SET validTo = (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?) WHERE id = ? AND validTo IS NULL AND isDeleted = 0`,
4805
+ args: [input.datasetId, item.id]
4806
+ });
4807
+ statements.push({
4808
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_ITEMS} (id,datasetId,datasetVersion,externalId,organizationId,projectId,validTo,isDeleted,input,groundTruth,expectedTrajectory,toolMocks,unmockedToolPolicy,scorerIds,requestContext,metadata,source,createdAt,updatedAt) VALUES (?,?,(SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?),?,?,?,NULL,1,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,jsonb(?),jsonb(?),jsonb(?),jsonb(?),?,?)`,
4809
+ args: [
4810
+ item.id,
4811
+ input.datasetId,
4812
+ input.datasetId,
4813
+ item.externalId ?? null,
4814
+ dataset.organizationId ?? null,
4815
+ dataset.projectId ?? null,
4816
+ jsonbArg(item.input),
4817
+ jsonbArg(item.groundTruth),
4818
+ jsonbArg(item.expectedTrajectory),
4819
+ jsonbArg(item.toolMocks),
4820
+ item.unmockedToolPolicy ?? null,
4821
+ jsonbArg(item.scorerIds),
4822
+ jsonbArg(item.requestContext),
4823
+ jsonbArg(item.metadata),
4824
+ jsonbArg(item.source),
4825
+ item.createdAt.toISOString(),
4826
+ nowIso
4827
+ ]
4828
+ });
4829
+ }
4830
+ statements.push({
4831
+ sql: `INSERT INTO ${_mastra_core_storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, (SELECT version FROM ${_mastra_core_storage.TABLE_DATASETS} WHERE id = ?), ?)`,
4832
+ args: [
4833
+ versionId,
4834
+ input.datasetId,
4835
+ input.datasetId,
4836
+ nowIso
4837
+ ]
4838
+ });
4839
+ for (const statement of statements) await tx.execute(statement);
4840
+ await tx.commit();
4841
+ } catch (error) {
4842
+ if (!tx.closed) await tx.rollback().catch((rollbackError) => {
4843
+ throw new AggregateError([error, rollbackError], "Transaction and rollback both failed");
4844
+ });
4845
+ throw error;
4846
+ }
4770
4847
  });
4771
- await this.#client.batch(statements, "write");
4772
4848
  } catch (error) {
4773
4849
  if (error instanceof _mastra_core_error.MastraError) throw error;
4774
4850
  throw new _mastra_core_error.MastraError({
@@ -8541,6 +8617,71 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8541
8617
  }, error);
8542
8618
  }
8543
8619
  }
8620
+ /**
8621
+ * Atomically reassign a thread and all of its messages to a different resource.
8622
+ *
8623
+ * Runs inside a single write transaction. SQLite serializes write transactions, so overlapping
8624
+ * transfers of the same thread cannot interleave the thread update with the message update:
8625
+ * either both the thread and every message move to the new resource, or neither does. The
8626
+ * thread's `createdAt` is preserved. Callers are responsible for authorizing the reassignment.
8627
+ */
8628
+ async updateThreadResourceId({ threadId, resourceId }) {
8629
+ try {
8630
+ const tx = await this.#client.transaction("write");
8631
+ try {
8632
+ const row = (await tx.execute({
8633
+ sql: `SELECT * FROM "${_mastra_core_storage.TABLE_THREADS}" WHERE id = ?`,
8634
+ args: [threadId]
8635
+ })).rows?.[0];
8636
+ if (!row) throw new Error(`Thread "${threadId}" not found`);
8637
+ const currentResourceId = row.resourceId;
8638
+ const normalized = {
8639
+ id: row.id,
8640
+ resourceId: currentResourceId,
8641
+ title: row.title,
8642
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata,
8643
+ createdAt: new Date(row.createdAt),
8644
+ updatedAt: new Date(row.updatedAt)
8645
+ };
8646
+ if (currentResourceId === resourceId) {
8647
+ await tx.commit();
8648
+ return normalized;
8649
+ }
8650
+ const now = /* @__PURE__ */ new Date();
8651
+ await tx.execute({
8652
+ sql: `UPDATE "${_mastra_core_storage.TABLE_THREADS}" SET "resourceId" = ?, "updatedAt" = ? WHERE id = ?`,
8653
+ args: [
8654
+ resourceId,
8655
+ now.toISOString(),
8656
+ threadId
8657
+ ]
8658
+ });
8659
+ await tx.execute({
8660
+ sql: `UPDATE "${_mastra_core_storage.TABLE_MESSAGES}" SET "resourceId" = ? WHERE thread_id = ?`,
8661
+ args: [resourceId, threadId]
8662
+ });
8663
+ await tx.commit();
8664
+ return {
8665
+ ...normalized,
8666
+ resourceId,
8667
+ updatedAt: now
8668
+ };
8669
+ } catch (error) {
8670
+ await tx.rollback();
8671
+ throw error;
8672
+ }
8673
+ } catch (error) {
8674
+ throw new _mastra_core_error.MastraError({
8675
+ id: (0, _mastra_core_storage.createStorageErrorId)("LIBSQL", "UPDATE_THREAD_RESOURCE_ID", "FAILED"),
8676
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
8677
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
8678
+ details: {
8679
+ threadId,
8680
+ resourceId
8681
+ }
8682
+ }, error);
8683
+ }
8684
+ }
8544
8685
  async listThreads(args) {
8545
8686
  const { page = 0, perPage: perPageInput, orderBy, filter } = args;
8546
8687
  try {
@@ -8729,7 +8870,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8729
8870
  }, error);
8730
8871
  }
8731
8872
  }
8732
- async cloneThread(args) {
8873
+ async copyThread(args) {
8733
8874
  const { sourceThreadId, newThreadId: providedThreadId, resourceId, title, metadata, options } = args;
8734
8875
  const sourceThread = await this.getThreadById({ threadId: sourceThreadId });
8735
8876
  if (!sourceThread) throw new _mastra_core_error.MastraError({
@@ -8748,7 +8889,7 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8748
8889
  details: { newThreadId }
8749
8890
  });
8750
8891
  try {
8751
- let messageQuery = `SELECT id, content, role, type, "createdAt", thread_id, "resourceId"
8892
+ let messageQuery = `SELECT id, "createdAt"
8752
8893
  FROM "${_mastra_core_storage.TABLE_MESSAGES}" WHERE thread_id = ?`;
8753
8894
  const messageParams = [sourceThreadId];
8754
8895
  if (options?.messageFilter?.startDate) {
@@ -8806,52 +8947,28 @@ var MemoryLibSQL = class MemoryLibSQL extends _mastra_core_storage.MemoryStorage
8806
8947
  nowStr
8807
8948
  ]
8808
8949
  });
8809
- const clonedMessages = [];
8810
8950
  const messageIdMap = {};
8811
8951
  const targetResourceId = resourceId || sourceThread.resourceId;
8812
8952
  for (const sourceMsg of sourceMessages) {
8813
8953
  const newMessageId = crypto.randomUUID();
8814
- messageIdMap[sourceMsg.id] = newMessageId;
8815
- const contentStr = sourceMsg.content;
8816
- let parsedContent;
8817
- try {
8818
- parsedContent = JSON.parse(contentStr);
8819
- } catch {
8820
- parsedContent = {
8821
- format: 2,
8822
- parts: [{
8823
- type: "text",
8824
- text: contentStr
8825
- }]
8826
- };
8827
- }
8828
- await tx.execute({
8954
+ const sourceMsgId = sourceMsg.id;
8955
+ messageIdMap[sourceMsgId] = newMessageId;
8956
+ const insertResult = await tx.execute({
8829
8957
  sql: `INSERT INTO "${_mastra_core_storage.TABLE_MESSAGES}" (id, thread_id, content, role, type, "createdAt", "resourceId")
8830
- VALUES (?, ?, ?, ?, ?, ?, ?)`,
8958
+ SELECT ?, ?, content, role, type, "createdAt", ?
8959
+ FROM "${_mastra_core_storage.TABLE_MESSAGES}" WHERE id = ?`,
8831
8960
  args: [
8832
8961
  newMessageId,
8833
8962
  newThreadId,
8834
- contentStr,
8835
- sourceMsg.role,
8836
- sourceMsg.type || "v2",
8837
- sourceMsg.createdAt,
8838
- targetResourceId
8963
+ targetResourceId,
8964
+ sourceMsgId
8839
8965
  ]
8840
8966
  });
8841
- clonedMessages.push({
8842
- id: newMessageId,
8843
- threadId: newThreadId,
8844
- content: parsedContent,
8845
- role: sourceMsg.role,
8846
- type: sourceMsg.type || void 0,
8847
- createdAt: new Date(sourceMsg.createdAt),
8848
- resourceId: targetResourceId
8849
- });
8967
+ if (insertResult.rowsAffected !== 1) throw new Error(`Failed to copy message ${sourceMsgId}: expected 1 row copied but got ${insertResult.rowsAffected}`);
8850
8968
  }
8851
8969
  await tx.commit();
8852
8970
  return {
8853
8971
  thread: newThread,
8854
- clonedMessages,
8855
8972
  messageIdMap
8856
8973
  };
8857
8974
  } catch (error) {