@mastra/libsql 1.16.0-alpha.0 → 1.16.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @mastra/libsql
2
2
 
3
+ ## 1.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added atomic caller-defined dataset IDs for idempotent dataset creation across built-in storage adapters. Supplying `id` to `mastra.datasets.create()` now creates the dataset once and resolves compatible retries to the persisted record; incompatible immutable identity fields throw `DATASET_ID_CONFLICT`. ([#19370](https://github.com/mastra-ai/mastra/pull/19370))
8
+
9
+ - Added `LibSQLVector.close()` to release the underlying libsql client. For local file databases it checkpoints the WAL and switches back to `journal_mode=DELETE` before closing (mirroring `LibSQLStore.close()`), so the `-wal`/`-shm` sidecar files and OS handles are released promptly. Safe to call more than once. ([#19059](https://github.com/mastra-ai/mastra/pull/19059))
10
+
11
+ - Added caller-defined dataset item identities for safe retries across all dataset storage adapters. ([#19384](https://github.com/mastra-ai/mastra/pull/19384))
12
+
13
+ Dataset items can now include an `externalId` when calling `addItem` or `addItems`:
14
+
15
+ ```ts
16
+ await dataset.addItem({
17
+ externalId: 'source-item-123',
18
+ input: { prompt: 'Hello' },
19
+ });
20
+ ```
21
+
22
+ Retrying with the same identity and payload returns the existing item. Reusing an identity with different content returns a typed conflict, including during concurrent writes. Updates and deletes preserve the identity, Spanner retries transactions without changing the outcome, and MySQL batch writes now preserve every supported dataset item field.
23
+
24
+ ### Patch Changes
25
+
26
+ - Raised the `@mastra/core` peer dependency floor to `>=1.51.0-0` so the dataset item identity helpers used by the storage adapters are available at runtime. ([#19384](https://github.com/mastra-ai/mastra/pull/19384))
27
+
28
+ - Updated dependencies [[`bd6d240`](https://github.com/mastra-ai/mastra/commit/bd6d2402db93dddaef0721667e7e8a030e7c6e16), [`0111486`](https://github.com/mastra-ai/mastra/commit/01114867612593eef5cfa2fda6a1194dfedda841), [`96a3749`](https://github.com/mastra-ai/mastra/commit/96a37492235f5b8076b3e3177d83ed5a5e44a640), [`fe1bda0`](https://github.com/mastra-ai/mastra/commit/fe1bda06f6af92a694a51712db747cda1e7185f0), [`25e7c12`](https://github.com/mastra-ai/mastra/commit/25e7c126a770069ae7fb7ecf1d2adb40e017b009), [`1ce5121`](https://github.com/mastra-ai/mastra/commit/1ce512155d122bb21f47d98383e82ffbf84b39e8), [`fb8aea3`](https://github.com/mastra-ai/mastra/commit/fb8aea384291e77311be3a64ee1717320d5c3c73), [`4adc391`](https://github.com/mastra-ai/mastra/commit/4adc3911075249c352bb4832d2471922826344de), [`a5c6337`](https://github.com/mastra-ai/mastra/commit/a5c6337d23c7686c81a32ce62f550f610543a240), [`3cfc47a`](https://github.com/mastra-ai/mastra/commit/3cfc47a6b89940aadd0f46fb01ae9624a73a865d), [`2bb7817`](https://github.com/mastra-ai/mastra/commit/2bb78176112fde628483de2830528f7eee911e56), [`51d9870`](https://github.com/mastra-ai/mastra/commit/51d987032c689c2855374d0f244f5d654da809d1), [`5cab274`](https://github.com/mastra-ai/mastra/commit/5cab2744250e22d12fefa7b32637dce224233cee), [`7fa27d3`](https://github.com/mastra-ai/mastra/commit/7fa27d3b6f5ed68cd34e454a4d3ad9c482a0cfbc), [`8b97958`](https://github.com/mastra-ai/mastra/commit/8b979589f9aa59ba67cac565949475f2ffeb4ac3), [`8410541`](https://github.com/mastra-ai/mastra/commit/84105412c60ecd3bb33a9838146f59c4b588228f), [`a58dcbb`](https://github.com/mastra-ai/mastra/commit/a58dcbb546d7e1d65ebdc1f39e55f0908fcd9391), [`aa38805`](https://github.com/mastra-ai/mastra/commit/aa38805b878b827403be785eb90688d7172f5a40), [`153bd3b`](https://github.com/mastra-ai/mastra/commit/153bd3b396bdfed6b74cf43de12db8fd2d83c04a), [`45a8e65`](https://github.com/mastra-ai/mastra/commit/45a8e65e1556d1362cb3f25187023c36de26661d), [`e955965`](https://github.com/mastra-ai/mastra/commit/e955965dce575a903e37cf054d28ea99aa48785e), [`2d22570`](https://github.com/mastra-ai/mastra/commit/2d22570c7dfdd02123d0ecc529efb05ccba2d9fc), [`07bb863`](https://github.com/mastra-ai/mastra/commit/07bb8631919c6f7cf377dccd45b096e0f17fbed0), [`c8ed116`](https://github.com/mastra-ai/mastra/commit/c8ed11699f62bcac70102ab4ec84d80d20541da6), [`01b338c`](https://github.com/mastra-ai/mastra/commit/01b338c56271f0219606710e3e8b26dee27ac6c2), [`a99eae8`](https://github.com/mastra-ai/mastra/commit/a99eae8908e500c1b2d12f9d277be616b98617a5), [`860ef7e`](https://github.com/mastra-ai/mastra/commit/860ef7e77d92b63469cbe5857aa1e626197e43e9), [`17e818c`](https://github.com/mastra-ai/mastra/commit/17e818c51a958ba90641b1a959dc38faf8c034e9), [`edce8d2`](https://github.com/mastra-ai/mastra/commit/edce8d2769f19e27a05737c627af2d765472a4f8), [`8a586ec`](https://github.com/mastra-ai/mastra/commit/8a586eca9a4914f31dff6140d0d45ac375b00669), [`4451dfe`](https://github.com/mastra-ai/mastra/commit/4451dfe857428e7abcc0261a507a2e186dae6d47), [`8b7361d`](https://github.com/mastra-ai/mastra/commit/8b7361d35de68b80d05d30a74e0c69e7218fd612), [`1d39058`](https://github.com/mastra-ai/mastra/commit/1d39058e548efd691799985d5c8af2737f1c3bd2), [`3927473`](https://github.com/mastra-ai/mastra/commit/392747323ddb10c643d12be7b9ae913159dfaeed), [`dce50dc`](https://github.com/mastra-ai/mastra/commit/dce50dc9a1c1fcd0f427bb5f6250ec74910cb04b), [`fd13f8e`](https://github.com/mastra-ai/mastra/commit/fd13f8e21990f9904c3eedba3a626bb4a929cdb8), [`634caff`](https://github.com/mastra-ai/mastra/commit/634caff29a9200ad058b67d53f96d9e5832fb8a2), [`f703f87`](https://github.com/mastra-ai/mastra/commit/f703f878de072d51fda557f9c50867d8252bef05), [`3e26c87`](https://github.com/mastra-ai/mastra/commit/3e26c87de0c5bc2583b795ce6ca5889b6b161acb), [`33f2b88`](https://github.com/mastra-ai/mastra/commit/33f2b88842c09a567f906fac4cb61cd5277ced59), [`177010f`](https://github.com/mastra-ai/mastra/commit/177010ff096d2e4b28d89803be5b1a4cad2a0d6b), [`0ad646f`](https://github.com/mastra-ai/mastra/commit/0ad646f71a530f2454664299e5e01bfd13fa12e5), [`b486abf`](https://github.com/mastra-ai/mastra/commit/b486abfa2a7528c6f527e4015c819ea9fa54aaad), [`54a51e0`](https://github.com/mastra-ai/mastra/commit/54a51e0a484fe1ebad3fb1f7ef5282a075709eb7), [`c43f3a9`](https://github.com/mastra-ai/mastra/commit/c43f3a9d1efde99b38789364ba4d0ba670f430e3), [`a5008f2`](https://github.com/mastra-ai/mastra/commit/a5008f22ae710ad9402ea9f2547d8c02f74d384b), [`e2d5f37`](https://github.com/mastra-ai/mastra/commit/e2d5f373bd289be534d5f8694d34465010533df6), [`4ce0163`](https://github.com/mastra-ai/mastra/commit/4ce0163dc86e675a86809685c8ce6c49f1aeb87e), [`4378341`](https://github.com/mastra-ai/mastra/commit/43783412df5ea3dd35f5b1f6e4851e79c346fc89)]:
29
+ - @mastra/core@1.51.0
30
+
31
+ ## 1.16.0-alpha.1
32
+
33
+ ### Minor Changes
34
+
35
+ - Added atomic caller-defined dataset IDs for idempotent dataset creation across built-in storage adapters. Supplying `id` to `mastra.datasets.create()` now creates the dataset once and resolves compatible retries to the persisted record; incompatible immutable identity fields throw `DATASET_ID_CONFLICT`. ([#19370](https://github.com/mastra-ai/mastra/pull/19370))
36
+
37
+ - Added caller-defined dataset item identities for safe retries across all dataset storage adapters. ([#19384](https://github.com/mastra-ai/mastra/pull/19384))
38
+
39
+ Dataset items can now include an `externalId` when calling `addItem` or `addItems`:
40
+
41
+ ```ts
42
+ await dataset.addItem({
43
+ externalId: 'source-item-123',
44
+ input: { prompt: 'Hello' },
45
+ });
46
+ ```
47
+
48
+ Retrying with the same identity and payload returns the existing item. Reusing an identity with different content returns a typed conflict, including during concurrent writes. Updates and deletes preserve the identity, Spanner retries transactions without changing the outcome, and MySQL batch writes now preserve every supported dataset item field.
49
+
50
+ ### Patch Changes
51
+
52
+ - Raised the `@mastra/core` peer dependency floor to `>=1.51.0-0` so the dataset item identity helpers used by the storage adapters are available at runtime. ([#19384](https://github.com/mastra-ai/mastra/pull/19384))
53
+
54
+ - Updated dependencies [[`a99eae8`](https://github.com/mastra-ai/mastra/commit/a99eae8908e500c1b2d12f9d277be616b98617a5), [`fd13f8e`](https://github.com/mastra-ai/mastra/commit/fd13f8e21990f9904c3eedba3a626bb4a929cdb8), [`f703f87`](https://github.com/mastra-ai/mastra/commit/f703f878de072d51fda557f9c50867d8252bef05), [`0ad646f`](https://github.com/mastra-ai/mastra/commit/0ad646f71a530f2454664299e5e01bfd13fa12e5)]:
55
+ - @mastra/core@1.51.0-alpha.13
56
+
3
57
  ## 1.16.0-alpha.0
4
58
 
5
59
  ### Minor Changes
@@ -3,7 +3,7 @@ name: mastra-libsql
3
3
  description: Documentation for @mastra/libsql. Use when working with @mastra/libsql APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/libsql"
6
- version: "1.16.0-alpha.0"
6
+ version: "1.16.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.16.0-alpha.0",
2
+ "version": "1.16.0",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -77,7 +77,7 @@ Use memory when your agent needs to maintain multi-turn conversations that refer
77
77
  bun add @mastra/libsql@latest
78
78
  ```
79
79
 
80
- > **Note:** For more details on available providers and how storage works in Mastra, visit the [storage](https://mastra.ai/docs/storage/overview) documentation.
80
+ For more details on available providers and how storage works in Mastra, visit the [storage](https://mastra.ai/docs/storage/overview) documentation.
81
81
 
82
82
  3. Add the storage provider to your main Mastra instance to enable memory across all configured agents.
83
83
 
@@ -110,7 +110,7 @@ Use memory when your agent needs to maintain multi-turn conversations that refer
110
110
  })
111
111
  ```
112
112
 
113
- > **Note:** Visit [Memory Class](https://mastra.ai/reference/memory/memory-class) for a full list of configuration options.
113
+ Visit [Memory Class](https://mastra.ai/reference/memory/memory-class) for a full list of configuration options.
114
114
 
115
115
  5. Call your agent, for example in [Studio](https://mastra.ai/docs/studio/overview). Inside Studio, start a new chat with your agent and take a look at the right sidebar. It'll now display various memory-related information.
116
116
 
@@ -166,7 +166,7 @@ export const memoryAgent = new Agent({
166
166
  })
167
167
  ```
168
168
 
169
- > **Note:** See [Observational Memory](https://mastra.ai/docs/memory/observational-memory) for details on how observations and reflections work, and [the reference](https://mastra.ai/reference/memory/observational-memory) for all configuration options.
169
+ See [Observational Memory](https://mastra.ai/docs/memory/observational-memory) for details on how observations and reflections work, and [the reference](https://mastra.ai/reference/memory/observational-memory) for all configuration options.
170
170
 
171
171
  ## Memory in multi-agent systems
172
172
 
@@ -238,7 +238,7 @@ export const memoryAgent = new Agent({
238
238
  })
239
239
  ```
240
240
 
241
- > **Note:** Visit [Request Context](https://mastra.ai/docs/server/request-context) for more information.
241
+ Visit [Request Context](https://mastra.ai/docs/server/request-context) for more information.
242
242
 
243
243
  ## Related
244
244
 
package/dist/index.cjs CHANGED
@@ -1597,6 +1597,19 @@ var LibSQLDB = class extends base.MastraBase {
1597
1597
  insert(args) {
1598
1598
  return this.executeWriteOperationWithRetry(() => this.doInsert(args), `insert into table ${args.tableName}`);
1599
1599
  }
1600
+ /** Inserts a record without replacing an existing primary key. */
1601
+ insertOnly(args) {
1602
+ return this.executeWriteOperationWithRetry(async () => {
1603
+ const filteredRecord = await this.filterRecordToKnownColumns(args.tableName, args.record);
1604
+ if (Object.keys(filteredRecord).length === 0) return;
1605
+ const statement = prepareStatement({ tableName: args.tableName, record: filteredRecord });
1606
+ if (!statement.sql.startsWith("INSERT OR REPLACE")) {
1607
+ throw new Error(`Unexpected insert statement generated for table ${args.tableName}`);
1608
+ }
1609
+ statement.sql = statement.sql.replace("INSERT OR REPLACE", "INSERT");
1610
+ await withClientWriteLock(this.client, () => this.client.execute(statement));
1611
+ }, `insert into table ${args.tableName}`);
1612
+ }
1600
1613
  /**
1601
1614
  * Internal update implementation without retry logic.
1602
1615
  */
@@ -3906,6 +3919,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3906
3919
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "organizationId", "TEXT");
3907
3920
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "projectId", "TEXT");
3908
3921
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "toolMocks", "TEXT");
3922
+ await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "externalId", "TEXT");
3909
3923
  await this.#client.batch(
3910
3924
  [
3911
3925
  {
@@ -3916,6 +3930,10 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3916
3930
  sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_version ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "datasetVersion")`,
3917
3931
  args: []
3918
3932
  },
3933
+ {
3934
+ sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_externalid_version ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "externalId", "datasetVersion")`,
3935
+ args: []
3936
+ },
3919
3937
  {
3920
3938
  sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_dataset_validto_deleted ON "${storage.TABLE_DATASET_ITEMS}" ("datasetId", "validTo", "isDeleted")`,
3921
3939
  args: []
@@ -3995,6 +4013,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3995
4013
  id: row.id,
3996
4014
  datasetId: row.datasetId,
3997
4015
  datasetVersion: row.datasetVersion,
4016
+ externalId: row.externalId ?? null,
3998
4017
  organizationId: row.organizationId ?? null,
3999
4018
  projectId: row.projectId ?? null,
4000
4019
  input: storage.safelyParseJSON(row.input),
@@ -4013,6 +4032,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4013
4032
  id: row.id,
4014
4033
  datasetId: row.datasetId,
4015
4034
  datasetVersion: row.datasetVersion,
4035
+ externalId: row.externalId ?? null,
4016
4036
  organizationId: row.organizationId ?? null,
4017
4037
  projectId: row.projectId ?? null,
4018
4038
  validTo: row.validTo,
@@ -4039,10 +4059,11 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4039
4059
  // --- Dataset CRUD ---
4040
4060
  async createDataset(input) {
4041
4061
  try {
4042
- const id = crypto.randomUUID();
4062
+ const id = input.id ?? crypto.randomUUID();
4063
+ if (input.id !== void 0) this.validateCallerDefinedDatasetId(input.id);
4043
4064
  const now = /* @__PURE__ */ new Date();
4044
4065
  const nowIso = now.toISOString();
4045
- await this.#db.insert({
4066
+ await this.#db.insertOnly({
4046
4067
  tableName: storage.TABLE_DATASETS,
4047
4068
  record: {
4048
4069
  id,
@@ -4084,6 +4105,11 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4084
4105
  updatedAt: now
4085
4106
  };
4086
4107
  } catch (error$1) {
4108
+ if (input.id !== void 0 && storage.hasErrorCode(error$1, /* @__PURE__ */ new Set(["SQLITE_CONSTRAINT", "SQLITE_CONSTRAINT_PRIMARYKEY", "SQLITE_CONSTRAINT_UNIQUE"]))) {
4109
+ const existing = await this.getDatasetById({ id: input.id });
4110
+ if (existing) return this.resolveExistingDataset(existing, { ...input, id: input.id });
4111
+ }
4112
+ if (error$1 instanceof error.MastraError) throw error$1;
4087
4113
  throw new error.MastraError(
4088
4114
  {
4089
4115
  id: storage.createStorageErrorId("LIBSQL", "CREATE_DATASET", "FAILED"),
@@ -4329,11 +4355,12 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4329
4355
  args: [args.datasetId]
4330
4356
  },
4331
4357
  {
4332
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT organizationId FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 0, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4358
+ sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, externalId, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?, (SELECT organizationId FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 0, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4333
4359
  args: [
4334
4360
  id,
4335
4361
  args.datasetId,
4336
4362
  args.datasetId,
4363
+ args.externalId ?? null,
4337
4364
  args.datasetId,
4338
4365
  args.datasetId,
4339
4366
  jsonbArg(args.input),
@@ -4359,6 +4386,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4359
4386
  id,
4360
4387
  datasetId: args.datasetId,
4361
4388
  datasetVersion: newVersion,
4389
+ externalId: args.externalId ?? null,
4362
4390
  organizationId: dataset?.organizationId ?? null,
4363
4391
  projectId: dataset?.projectId ?? null,
4364
4392
  input: args.input,
@@ -4424,11 +4452,12 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4424
4452
  args: [args.datasetId, args.id]
4425
4453
  },
4426
4454
  {
4427
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT organizationId FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 0, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4455
+ sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, externalId, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?, (SELECT organizationId FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 0, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4428
4456
  args: [
4429
4457
  args.id,
4430
4458
  args.datasetId,
4431
4459
  args.datasetId,
4460
+ existing.externalId ?? null,
4432
4461
  args.datasetId,
4433
4462
  args.datasetId,
4434
4463
  jsonbArg(mergedInput),
@@ -4501,11 +4530,12 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4501
4530
  args: [datasetId, id]
4502
4531
  },
4503
4532
  {
4504
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT organizationId FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 1, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4533
+ sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, externalId, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?, (SELECT organizationId FROM ${storage.TABLE_DATASETS} WHERE id = ?), (SELECT projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 1, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4505
4534
  args: [
4506
4535
  id,
4507
4536
  datasetId,
4508
4537
  datasetId,
4538
+ existing.externalId ?? null,
4509
4539
  datasetId,
4510
4540
  datasetId,
4511
4541
  jsonbArg(existing.input),
@@ -4787,70 +4817,94 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4787
4817
  // --- Bulk operations (SCD-2 internally) ---
4788
4818
  async _doBatchInsertItems(input) {
4789
4819
  try {
4790
- const dataset = await this.getDatasetById({ id: input.datasetId });
4791
- if (!dataset) {
4792
- throw new error.MastraError({
4793
- id: storage.createStorageErrorId("LIBSQL", "BULK_ADD_ITEMS", "DATASET_NOT_FOUND"),
4794
- domain: error.ErrorDomain.STORAGE,
4795
- category: error.ErrorCategory.USER,
4796
- details: { datasetId: input.datasetId }
4797
- });
4798
- }
4799
- const now = /* @__PURE__ */ new Date();
4800
- const nowIso = now.toISOString();
4801
- const versionId = crypto.randomUUID();
4802
- const statements = [
4803
- {
4804
- sql: `UPDATE ${storage.TABLE_DATASETS} SET version = version + 1 WHERE id = ? RETURNING version`,
4805
- args: [input.datasetId]
4820
+ if (input.items.length === 0) return [];
4821
+ return await withClientWriteLock(this.#client, async () => {
4822
+ const tx = await this.#client.transaction("write");
4823
+ try {
4824
+ const datasetResult = await tx.execute({
4825
+ sql: `SELECT version, organizationId, projectId FROM ${storage.TABLE_DATASETS} WHERE id = ?`,
4826
+ args: [input.datasetId]
4827
+ });
4828
+ const dataset = datasetResult.rows[0];
4829
+ if (!dataset)
4830
+ throw new error.MastraError({
4831
+ id: storage.createStorageErrorId("LIBSQL", "BULK_ADD_ITEMS", "DATASET_NOT_FOUND"),
4832
+ domain: error.ErrorDomain.STORAGE,
4833
+ category: error.ErrorCategory.USER,
4834
+ details: { datasetId: input.datasetId }
4835
+ });
4836
+ const externalIds = [...new Set(input.items.flatMap((item) => item.externalId ? [item.externalId] : []))];
4837
+ const historyResult = externalIds.length ? await tx.execute({
4838
+ sql: `SELECT id, datasetId, datasetVersion, externalId, organizationId, projectId, validTo, isDeleted, json(input) AS input, json(groundTruth) AS groundTruth, json(expectedTrajectory) AS expectedTrajectory, json(toolMocks) AS toolMocks, json(requestContext) AS requestContext, json(metadata) AS metadata, json(source) AS source, createdAt, updatedAt FROM ${storage.TABLE_DATASET_ITEMS} WHERE datasetId = ? AND externalId IN (${externalIds.map(() => "?").join(",")}) ORDER BY datasetVersion`,
4839
+ args: [input.datasetId, ...externalIds]
4840
+ }) : { rows: [] };
4841
+ const plan = this.planDatasetItemBatch(
4842
+ input.items,
4843
+ historyResult.rows.map((row) => this.transformItemRowFull(row)),
4844
+ () => crypto.randomUUID()
4845
+ );
4846
+ const resolved = new Map(
4847
+ [...plan.existingCurrentItems].map(([id, row]) => [id, this.datasetItemFromRow(row)])
4848
+ );
4849
+ if (plan.inserts.length > 0) {
4850
+ const newVersion = Number(dataset.version) + 1;
4851
+ const now = /* @__PURE__ */ new Date();
4852
+ const nowIso = now.toISOString();
4853
+ await tx.execute({
4854
+ sql: `UPDATE ${storage.TABLE_DATASETS} SET version = ? WHERE id = ?`,
4855
+ args: [newVersion, input.datasetId]
4856
+ });
4857
+ for (const { id, item } of plan.inserts) {
4858
+ await tx.execute({
4859
+ sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, externalId, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, NULL, 0, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4860
+ args: [
4861
+ id,
4862
+ input.datasetId,
4863
+ newVersion,
4864
+ item.externalId ?? null,
4865
+ dataset.organizationId ?? null,
4866
+ dataset.projectId ?? null,
4867
+ jsonbArg(item.input),
4868
+ jsonbArg(item.groundTruth),
4869
+ jsonbArg(item.expectedTrajectory),
4870
+ jsonbArg(item.toolMocks),
4871
+ jsonbArg(item.requestContext),
4872
+ jsonbArg(item.metadata),
4873
+ jsonbArg(item.source),
4874
+ nowIso,
4875
+ nowIso
4876
+ ]
4877
+ });
4878
+ resolved.set(id, {
4879
+ id,
4880
+ datasetId: input.datasetId,
4881
+ datasetVersion: newVersion,
4882
+ externalId: item.externalId ?? null,
4883
+ organizationId: dataset.organizationId,
4884
+ projectId: dataset.projectId,
4885
+ input: item.input,
4886
+ groundTruth: item.groundTruth,
4887
+ expectedTrajectory: item.expectedTrajectory,
4888
+ toolMocks: item.toolMocks,
4889
+ requestContext: item.requestContext,
4890
+ metadata: item.metadata,
4891
+ source: item.source,
4892
+ createdAt: now,
4893
+ updatedAt: now
4894
+ });
4895
+ }
4896
+ await tx.execute({
4897
+ sql: `INSERT INTO ${storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, ?, ?)`,
4898
+ args: [crypto.randomUUID(), input.datasetId, newVersion, nowIso]
4899
+ });
4900
+ }
4901
+ await tx.commit();
4902
+ return plan.resolvedIds.map((id) => resolved.get(id));
4903
+ } catch (error) {
4904
+ if (!tx.closed) await tx.rollback();
4905
+ throw error;
4806
4906
  }
4807
- ];
4808
- const items = [];
4809
- for (const itemInput of input.items) {
4810
- const id = crypto.randomUUID();
4811
- items.push({ id, input: itemInput });
4812
- statements.push({
4813
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?, ?, NULL, 0, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4814
- args: [
4815
- id,
4816
- input.datasetId,
4817
- input.datasetId,
4818
- dataset.organizationId ?? null,
4819
- dataset.projectId ?? null,
4820
- jsonbArg(itemInput.input),
4821
- jsonbArg(itemInput.groundTruth),
4822
- jsonbArg(itemInput.expectedTrajectory),
4823
- jsonbArg(itemInput.toolMocks),
4824
- jsonbArg(itemInput.requestContext),
4825
- jsonbArg(itemInput.metadata),
4826
- jsonbArg(itemInput.source),
4827
- nowIso,
4828
- nowIso
4829
- ]
4830
- });
4831
- }
4832
- statements.push({
4833
- sql: `INSERT INTO ${storage.TABLE_DATASET_VERSIONS} (id, datasetId, version, createdAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?)`,
4834
- args: [versionId, input.datasetId, input.datasetId, nowIso]
4835
4907
  });
4836
- const results = await this.#client.batch(statements, "write");
4837
- const newVersion = Number(results[0].rows[0].version);
4838
- return items.map(({ id, input: itemInput }) => ({
4839
- id,
4840
- datasetId: input.datasetId,
4841
- datasetVersion: newVersion,
4842
- organizationId: dataset.organizationId ?? null,
4843
- projectId: dataset.projectId ?? null,
4844
- input: itemInput.input,
4845
- groundTruth: itemInput.groundTruth,
4846
- expectedTrajectory: itemInput.expectedTrajectory,
4847
- toolMocks: itemInput.toolMocks,
4848
- requestContext: itemInput.requestContext,
4849
- metadata: itemInput.metadata,
4850
- source: itemInput.source,
4851
- createdAt: now,
4852
- updatedAt: now
4853
- }));
4854
4908
  } catch (error$1) {
4855
4909
  if (error$1 instanceof error.MastraError) throw error$1;
4856
4910
  throw new error.MastraError(
@@ -4896,11 +4950,12 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4896
4950
  args: [input.datasetId, item.id]
4897
4951
  });
4898
4952
  statements.push({
4899
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?, ?, NULL, 1, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4953
+ sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, externalId, organizationId, projectId, validTo, isDeleted, input, groundTruth, expectedTrajectory, toolMocks, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), ?, ?, ?, NULL, 1, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4900
4954
  args: [
4901
4955
  item.id,
4902
4956
  input.datasetId,
4903
4957
  input.datasetId,
4958
+ item.externalId ?? null,
4904
4959
  dataset.organizationId ?? null,
4905
4960
  dataset.projectId ?? null,
4906
4961
  jsonbArg(item.input),