@mastra/libsql 1.14.1-alpha.0 → 1.14.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/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # @mastra/libsql
2
2
 
3
+ ## 1.14.1-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Added multi-tenant scoping columns (`organizationId`, `projectId`) to the experiments domain so experiment records and per-item results inherit the tenancy bucket of their parent dataset. ([#18388](https://github.com/mastra-ai/mastra/pull/18388))
8
+
9
+ `Experiment`, `ExperimentResult`, `CreateExperimentInput`, and `AddExperimentResultInput` now carry optional `organizationId` / `projectId` fields. `ListExperimentsInput` and `ListExperimentResultsInput` gain a `filters: ExperimentTenancyFilters` block (mirrors `DatasetTenancyFilters`) for scoping queries within a `(organizationId, projectId)` bucket. Tenancy is hydrated from the parent dataset on `createExperiment` and denormalized onto each `ExperimentResult` for efficient tenancy-scoped queries.
10
+
11
+ The corresponding columns are also added to the `mastra_experiments` and `mastra_experiment_results` table schemas. Existing rows backfill to `null`, matching the rest of the dataset-tenancy surface.
12
+
13
+ This release also clarifies the `targetType` contract via JSDoc:
14
+ - `CreateDatasetInput.targetType` remains optional. Datasets without a `TargetType` are **not experiment-eligible** — the experiment runner requires a non-null `CreateExperimentInput.targetType` to resolve an executor.
15
+ - `Experiment.targetType` / `CreateExperimentInput.targetType` stay required. An experiment by definition replays inputs against a specific target.
16
+
17
+ No behavior change for existing OSS-created experiments; the new fields are additive and optional.
18
+
19
+ Example:
20
+
21
+ ```ts
22
+ // Create an experiment scoped to a tenancy bucket. When the parent dataset
23
+ // already carries `organizationId` / `projectId`, `runExperiment` hydrates
24
+ // these fields automatically from the dataset record.
25
+ const experiment = await storage.createExperiment({
26
+ name: 'qa-regression',
27
+ datasetId: 'ds_123',
28
+ datasetVersion: 1,
29
+ targetType: 'agent',
30
+ targetId: 'agent_qa',
31
+ totalItems: 10,
32
+ organizationId: 'org_123',
33
+ projectId: 'proj_123',
34
+ });
35
+
36
+ // List experiments within a tenancy bucket.
37
+ const experiments = await storage.listExperiments({
38
+ pagination: { page: 0, perPage: 20 },
39
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
40
+ });
41
+
42
+ // List per-item results within the same bucket.
43
+ const results = await storage.listExperimentResults({
44
+ experimentId: experiment.id,
45
+ pagination: { page: 0, perPage: 50 },
46
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
47
+ });
48
+ ```
49
+
50
+ - Persist and filter dataset tenancy + candidate identity in storage adapters. ([#18314](https://github.com/mastra-ai/mastra/pull/18314))
51
+
52
+ `createDataset` now persists `organizationId`, `projectId`, `candidateKey`, and `candidateId`. `listDatasets` and `listItems` accept matching tenancy filters. Dataset items inherit `organizationId` / `projectId` from their parent dataset on insert, update, delete, and batch insert/delete — items are never settable per call (item tenancy follows dataset tenancy).
53
+
54
+ All new columns are nullable and added retroactively via each adapter's existing column-migration path; no breaking DDL. Existing rows continue to read and write fine; new writes can choose to stamp tenancy.
55
+
56
+ ```ts
57
+ await storage.createDataset({
58
+ name: 'candidates/missing-tool-call/incident-123',
59
+ organizationId: 'org_abc',
60
+ projectId: 'project_xyz',
61
+ candidateKey: 'missing-tool-call',
62
+ candidateId: 'incident-123',
63
+ });
64
+
65
+ await storage.listDatasets({
66
+ pagination: { page: 0, perPage: 20 },
67
+ filters: { organizationId: 'org_abc', projectId: 'project_xyz' },
68
+ });
69
+ ```
70
+
71
+ - Updated dependencies [[`5c4e9a4`](https://github.com/mastra-ai/mastra/commit/5c4e9a4cfb2216bb3ea7f8988ad3727f3b92bb3a), [`25961e3`](https://github.com/mastra-ai/mastra/commit/25961e3260ff3b1464637af8fcdb36210551c39f), [`7b29f33`](https://github.com/mastra-ai/mastra/commit/7b29f332a357a83e555f29e718e5f2fab9979943), [`24912b1`](https://github.com/mastra-ai/mastra/commit/24912b1f855d29ec36af4ef4bde1f7417e20cdf5), [`7686216`](https://github.com/mastra-ai/mastra/commit/7686216f37e74568feddec17cef3c3d24e10e60a), [`975c59a`](https://github.com/mastra-ai/mastra/commit/975c59ae363ee275fc55062392e1ffd2cbccbd53), [`d95f394`](https://github.com/mastra-ai/mastra/commit/d95f394fd24c8411886930d727679c4d5252aa26), [`f3f0c9d`](https://github.com/mastra-ai/mastra/commit/f3f0c9d7c878db5a13177871ce3523a14f14b311)]:
72
+ - @mastra/core@1.46.0-alpha.4
73
+
3
74
  ## 1.14.1-alpha.0
4
75
 
5
76
  ### Patch 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.14.1-alpha.0"
6
+ version: "1.14.1-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.14.1-alpha.0",
2
+ "version": "1.14.1-alpha.1",
3
3
  "package": "@mastra/libsql",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -2,6 +2,8 @@
2
2
 
3
3
  > **Note:** The Agent Builder is part of the Mastra Enterprise Edition. Production deployments require a valid EE license. [Contact sales](https://mastra.ai/contact) for more information.
4
4
 
5
+ [YouTube video player](https://www.youtube-nocookie.com/embed/AbdgIu4Z07I)
6
+
5
7
  The Agent Builder lets you build, configure, and operate Mastra agents all within the UI. It runs inside your Mastra server, persists everything to `Mastra.storage`, and supports multi-tenant agent workflows with RBAC and channel integrations.
6
8
 
7
9
  - [**Configuration**](https://mastra.ai/docs/agent-builder/configuration): Toggle UI sections and pin admin-controlled defaults for every new agent.
package/dist/index.cjs CHANGED
@@ -3622,9 +3622,15 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3622
3622
  await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "targetType", "TEXT");
3623
3623
  await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "targetIds", "TEXT");
3624
3624
  await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "scorerIds", "TEXT");
3625
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "organizationId", "TEXT");
3626
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "projectId", "TEXT");
3627
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "candidateKey", "TEXT");
3628
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "candidateId", "TEXT");
3625
3629
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "requestContext", "TEXT");
3626
3630
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "source", "TEXT");
3627
3631
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "expectedTrajectory", "TEXT");
3632
+ await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "organizationId", "TEXT");
3633
+ await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "projectId", "TEXT");
3628
3634
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "toolMocks", "TEXT");
3629
3635
  await this.#client.batch(
3630
3636
  [
@@ -3647,6 +3653,18 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3647
3653
  {
3648
3654
  sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_dataset_versions_dataset_version_unique ON "${storage.TABLE_DATASET_VERSIONS}" ("datasetId", "version")`,
3649
3655
  args: []
3656
+ },
3657
+ {
3658
+ sql: `CREATE INDEX IF NOT EXISTS idx_datasets_tenancy_createdat ON "${storage.TABLE_DATASETS}" ("organizationId", "projectId", "createdAt", "id")`,
3659
+ args: []
3660
+ },
3661
+ {
3662
+ sql: `CREATE INDEX IF NOT EXISTS idx_datasets_tenancy_candidate ON "${storage.TABLE_DATASETS}" ("organizationId", "projectId", "candidateKey", "candidateId")`,
3663
+ args: []
3664
+ },
3665
+ {
3666
+ sql: `CREATE INDEX IF NOT EXISTS idx_dataset_items_tenancy_list ON "${storage.TABLE_DATASET_ITEMS}" ("organizationId", "projectId", "datasetId", "validTo", "isDeleted")`,
3667
+ args: []
3650
3668
  }
3651
3669
  ],
3652
3670
  "write"
@@ -3678,6 +3696,10 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3678
3696
  targetIds: row.targetIds ? storage.safelyParseJSON(row.targetIds) : void 0,
3679
3697
  scorerIds: row.scorerIds ? storage.safelyParseJSON(row.scorerIds) : void 0,
3680
3698
  version: row.version,
3699
+ organizationId: row.organizationId ?? null,
3700
+ projectId: row.projectId ?? null,
3701
+ candidateKey: row.candidateKey ?? null,
3702
+ candidateId: row.candidateId ?? null,
3681
3703
  createdAt: storage.ensureDate(row.createdAt),
3682
3704
  updatedAt: storage.ensureDate(row.updatedAt)
3683
3705
  };
@@ -3687,6 +3709,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3687
3709
  id: row.id,
3688
3710
  datasetId: row.datasetId,
3689
3711
  datasetVersion: row.datasetVersion,
3712
+ organizationId: row.organizationId ?? null,
3713
+ projectId: row.projectId ?? null,
3690
3714
  input: storage.safelyParseJSON(row.input),
3691
3715
  groundTruth: row.groundTruth ? storage.safelyParseJSON(row.groundTruth) : void 0,
3692
3716
  expectedTrajectory: row.expectedTrajectory ? storage.safelyParseJSON(row.expectedTrajectory) : void 0,
@@ -3703,6 +3727,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3703
3727
  id: row.id,
3704
3728
  datasetId: row.datasetId,
3705
3729
  datasetVersion: row.datasetVersion,
3730
+ organizationId: row.organizationId ?? null,
3731
+ projectId: row.projectId ?? null,
3706
3732
  validTo: row.validTo,
3707
3733
  isDeleted: Boolean(row.isDeleted),
3708
3734
  input: storage.safelyParseJSON(row.input),
@@ -3744,6 +3770,10 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3744
3770
  targetIds: input.targetIds ? JSON.stringify(input.targetIds) : null,
3745
3771
  scorerIds: input.scorerIds ? JSON.stringify(input.scorerIds) : null,
3746
3772
  version: 0,
3773
+ organizationId: input.organizationId ?? null,
3774
+ projectId: input.projectId ?? null,
3775
+ candidateKey: input.candidateKey ?? null,
3776
+ candidateId: input.candidateId ?? null,
3747
3777
  createdAt: nowIso,
3748
3778
  updatedAt: nowIso
3749
3779
  }
@@ -3760,6 +3790,10 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3760
3790
  targetIds: input.targetIds ?? void 0,
3761
3791
  scorerIds: input.scorerIds ?? void 0,
3762
3792
  version: 0,
3793
+ organizationId: input.organizationId ?? null,
3794
+ projectId: input.projectId ?? null,
3795
+ candidateKey: input.candidateKey ?? null,
3796
+ candidateId: input.candidateId ?? null,
3763
3797
  createdAt: now,
3764
3798
  updatedAt: now
3765
3799
  };
@@ -3915,9 +3949,28 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3915
3949
  async listDatasets(args) {
3916
3950
  try {
3917
3951
  const { page, perPage: perPageInput } = args.pagination;
3952
+ const filterConditions = [];
3953
+ const filterParams = [];
3954
+ if (args.filters?.organizationId !== void 0) {
3955
+ filterConditions.push("organizationId = ?");
3956
+ filterParams.push(args.filters.organizationId);
3957
+ }
3958
+ if (args.filters?.projectId !== void 0) {
3959
+ filterConditions.push("projectId = ?");
3960
+ filterParams.push(args.filters.projectId);
3961
+ }
3962
+ if (args.filters?.candidateKey !== void 0) {
3963
+ filterConditions.push("candidateKey = ?");
3964
+ filterParams.push(args.filters.candidateKey);
3965
+ }
3966
+ if (args.filters?.candidateId !== void 0) {
3967
+ filterConditions.push("candidateId = ?");
3968
+ filterParams.push(args.filters.candidateId);
3969
+ }
3970
+ const whereClause = filterConditions.length > 0 ? `WHERE ${filterConditions.join(" AND ")}` : "";
3918
3971
  const countResult = await this.#client.execute({
3919
- sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_DATASETS}`,
3920
- args: []
3972
+ sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_DATASETS} ${whereClause}`,
3973
+ args: filterParams
3921
3974
  });
3922
3975
  const total = Number(countResult.rows?.[0]?.count ?? 0);
3923
3976
  if (total === 0) {
@@ -3931,8 +3984,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3931
3984
  const limitValue = perPageInput === false ? total : perPage;
3932
3985
  const end = perPageInput === false ? total : start + perPage;
3933
3986
  const result = await this.#client.execute({
3934
- sql: `SELECT ${buildSelectColumns(storage.TABLE_DATASETS)} FROM ${storage.TABLE_DATASETS} ORDER BY createdAt DESC, id ASC LIMIT ? OFFSET ?`,
3935
- args: [limitValue, start]
3987
+ sql: `SELECT ${buildSelectColumns(storage.TABLE_DATASETS)} FROM ${storage.TABLE_DATASETS} ${whereClause} ORDER BY createdAt DESC, id ASC LIMIT ? OFFSET ?`,
3988
+ args: [...filterParams, limitValue, start]
3936
3989
  });
3937
3990
  return {
3938
3991
  datasets: result.rows?.map((row) => this.transformDatasetRow(row)) ?? [],
@@ -3957,6 +4010,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3957
4010
  // --- SCD-2 item mutations ---
3958
4011
  async _doAddItem(args) {
3959
4012
  try {
4013
+ const dataset = await this.getDatasetById({ id: args.datasetId });
3960
4014
  const id = crypto.randomUUID();
3961
4015
  const versionId = crypto.randomUUID();
3962
4016
  const now = /* @__PURE__ */ new Date();
@@ -3968,11 +4022,13 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3968
4022
  args: [args.datasetId]
3969
4023
  },
3970
4024
  {
3971
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, 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(?), ?, ?)`,
4025
+ 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(?), ?, ?)`,
3972
4026
  args: [
3973
4027
  id,
3974
4028
  args.datasetId,
3975
4029
  args.datasetId,
4030
+ args.datasetId,
4031
+ args.datasetId,
3976
4032
  jsonbArg(args.input),
3977
4033
  jsonbArg(args.groundTruth),
3978
4034
  jsonbArg(args.expectedTrajectory),
@@ -3996,6 +4052,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
3996
4052
  id,
3997
4053
  datasetId: args.datasetId,
3998
4054
  datasetVersion: newVersion,
4055
+ organizationId: dataset?.organizationId ?? null,
4056
+ projectId: dataset?.projectId ?? null,
3999
4057
  input: args.input,
4000
4058
  groundTruth: args.groundTruth,
4001
4059
  expectedTrajectory: args.expectedTrajectory,
@@ -4037,6 +4095,7 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4037
4095
  details: { itemId: args.id, expectedDatasetId: args.datasetId, actualDatasetId: existing.datasetId }
4038
4096
  });
4039
4097
  }
4098
+ const dataset = await this.getDatasetById({ id: args.datasetId });
4040
4099
  const versionId = crypto.randomUUID();
4041
4100
  const now = /* @__PURE__ */ new Date();
4042
4101
  const nowIso = now.toISOString();
@@ -4058,11 +4117,13 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4058
4117
  args: [args.datasetId, args.id]
4059
4118
  },
4060
4119
  {
4061
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, 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(?), ?, ?)`,
4120
+ 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(?), ?, ?)`,
4062
4121
  args: [
4063
4122
  args.id,
4064
4123
  args.datasetId,
4065
4124
  args.datasetId,
4125
+ args.datasetId,
4126
+ args.datasetId,
4066
4127
  jsonbArg(mergedInput),
4067
4128
  jsonbArg(mergedGroundTruth),
4068
4129
  jsonbArg(mergedExpectedTrajectory),
@@ -4085,6 +4146,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4085
4146
  return {
4086
4147
  ...existing,
4087
4148
  datasetVersion: newVersion,
4149
+ organizationId: dataset?.organizationId ?? null,
4150
+ projectId: dataset?.projectId ?? null,
4088
4151
  input: mergedInput,
4089
4152
  groundTruth: mergedGroundTruth,
4090
4153
  expectedTrajectory: mergedExpectedTrajectory,
@@ -4131,13 +4194,17 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4131
4194
  args: [datasetId, id]
4132
4195
  },
4133
4196
  {
4134
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, validTo, isDeleted, input, groundTruth, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 1, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4197
+ 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(?), ?, ?)`,
4135
4198
  args: [
4136
4199
  id,
4137
4200
  datasetId,
4138
4201
  datasetId,
4202
+ datasetId,
4203
+ datasetId,
4139
4204
  jsonbArg(existing.input),
4140
4205
  jsonbArg(existing.groundTruth),
4206
+ jsonbArg(existing.expectedTrajectory),
4207
+ jsonbArg(existing.toolMocks),
4141
4208
  jsonbArg(existing.requestContext),
4142
4209
  jsonbArg(existing.metadata),
4143
4210
  jsonbArg(existing.source),
@@ -4238,6 +4305,14 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4238
4305
  "isDeleted = 0"
4239
4306
  ];
4240
4307
  const queryParams2 = [args.datasetId, args.version, args.version];
4308
+ if (args.filters?.organizationId !== void 0) {
4309
+ conditions2.push("organizationId = ?");
4310
+ queryParams2.push(args.filters.organizationId);
4311
+ }
4312
+ if (args.filters?.projectId !== void 0) {
4313
+ conditions2.push("projectId = ?");
4314
+ queryParams2.push(args.filters.projectId);
4315
+ }
4241
4316
  if (args.search) {
4242
4317
  conditions2.push(`(LOWER(json(input)) LIKE ? OR LOWER(COALESCE(json(groundTruth), '')) LIKE ?)`);
4243
4318
  const searchPattern = `%${args.search.toLowerCase()}%`;
@@ -4275,6 +4350,14 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4275
4350
  }
4276
4351
  const conditions = ["datasetId = ?", "validTo IS NULL", "isDeleted = 0"];
4277
4352
  const queryParams = [args.datasetId];
4353
+ if (args.filters?.organizationId !== void 0) {
4354
+ conditions.push("organizationId = ?");
4355
+ queryParams.push(args.filters.organizationId);
4356
+ }
4357
+ if (args.filters?.projectId !== void 0) {
4358
+ conditions.push("projectId = ?");
4359
+ queryParams.push(args.filters.projectId);
4360
+ }
4278
4361
  if (args.search) {
4279
4362
  conditions.push(`(LOWER(json(input)) LIKE ? OR LOWER(COALESCE(json(groundTruth), '')) LIKE ?)`);
4280
4363
  const searchPattern = `%${args.search.toLowerCase()}%`;
@@ -4420,11 +4503,13 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4420
4503
  const id = crypto.randomUUID();
4421
4504
  items.push({ id, input: itemInput });
4422
4505
  statements.push({
4423
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, 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(?), ?, ?)`,
4506
+ 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(?), ?, ?)`,
4424
4507
  args: [
4425
4508
  id,
4426
4509
  input.datasetId,
4427
4510
  input.datasetId,
4511
+ dataset.organizationId ?? null,
4512
+ dataset.projectId ?? null,
4428
4513
  jsonbArg(itemInput.input),
4429
4514
  jsonbArg(itemInput.groundTruth),
4430
4515
  jsonbArg(itemInput.expectedTrajectory),
@@ -4447,6 +4532,8 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4447
4532
  id,
4448
4533
  datasetId: input.datasetId,
4449
4534
  datasetVersion: newVersion,
4535
+ organizationId: dataset.organizationId ?? null,
4536
+ projectId: dataset.projectId ?? null,
4450
4537
  input: itemInput.input,
4451
4538
  groundTruth: itemInput.groundTruth,
4452
4539
  expectedTrajectory: itemInput.expectedTrajectory,
@@ -4502,13 +4589,17 @@ var DatasetsLibSQL = class extends storage.DatasetsStorage {
4502
4589
  args: [input.datasetId, item.id]
4503
4590
  });
4504
4591
  statements.push({
4505
- sql: `INSERT INTO ${storage.TABLE_DATASET_ITEMS} (id, datasetId, datasetVersion, validTo, isDeleted, input, groundTruth, requestContext, metadata, source, createdAt, updatedAt) VALUES (?, ?, (SELECT version FROM ${storage.TABLE_DATASETS} WHERE id = ?), NULL, 1, jsonb(?), jsonb(?), jsonb(?), jsonb(?), jsonb(?), ?, ?)`,
4592
+ 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(?), ?, ?)`,
4506
4593
  args: [
4507
4594
  item.id,
4508
4595
  input.datasetId,
4509
4596
  input.datasetId,
4597
+ dataset.organizationId ?? null,
4598
+ dataset.projectId ?? null,
4510
4599
  jsonbArg(item.input),
4511
4600
  jsonbArg(item.groundTruth),
4601
+ jsonbArg(item.expectedTrajectory),
4602
+ jsonbArg(item.toolMocks),
4512
4603
  jsonbArg(item.requestContext),
4513
4604
  jsonbArg(item.metadata),
4514
4605
  jsonbArg(item.source),
@@ -4553,12 +4644,12 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4553
4644
  await this.#db.alterTable({
4554
4645
  tableName: storage.TABLE_EXPERIMENTS,
4555
4646
  schema: storage.EXPERIMENTS_SCHEMA,
4556
- ifNotExists: ["agentVersion"]
4647
+ ifNotExists: ["agentVersion", "organizationId", "projectId"]
4557
4648
  });
4558
4649
  await this.#db.alterTable({
4559
4650
  tableName: storage.TABLE_EXPERIMENT_RESULTS,
4560
4651
  schema: storage.EXPERIMENT_RESULTS_SCHEMA,
4561
- ifNotExists: ["status", "tags", "toolMockReport"]
4652
+ ifNotExists: ["status", "tags", "toolMockReport", "organizationId", "projectId"]
4562
4653
  });
4563
4654
  await this.#client.batch(
4564
4655
  [
@@ -4573,6 +4664,15 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4573
4664
  {
4574
4665
  sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_experiment_results_exp_item ON "${storage.TABLE_EXPERIMENT_RESULTS}" ("experimentId", "itemId")`,
4575
4666
  args: []
4667
+ },
4668
+ // Tenancy: leading-tenant indexes for multi-tenant scans (parity with datasets domain).
4669
+ {
4670
+ sql: `CREATE INDEX IF NOT EXISTS idx_experiments_org_project ON "${storage.TABLE_EXPERIMENTS}" ("organizationId", "projectId")`,
4671
+ args: []
4672
+ },
4673
+ {
4674
+ sql: `CREATE INDEX IF NOT EXISTS idx_experiment_results_org_project ON "${storage.TABLE_EXPERIMENT_RESULTS}" ("organizationId", "projectId")`,
4675
+ args: []
4576
4676
  }
4577
4677
  ],
4578
4678
  "write"
@@ -4589,6 +4689,8 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4589
4689
  datasetId: row.datasetId ?? null,
4590
4690
  datasetVersion: row.datasetVersion != null ? row.datasetVersion : null,
4591
4691
  agentVersion: row.agentVersion ?? null,
4692
+ organizationId: row.organizationId ?? null,
4693
+ projectId: row.projectId ?? null,
4592
4694
  targetType: row.targetType,
4593
4695
  targetId: row.targetId,
4594
4696
  name: row.name ?? void 0,
@@ -4612,6 +4714,8 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4612
4714
  experimentId: row.experimentId,
4613
4715
  itemId: row.itemId,
4614
4716
  itemDatasetVersion: row.itemDatasetVersion != null ? row.itemDatasetVersion : null,
4717
+ organizationId: row.organizationId ?? null,
4718
+ projectId: row.projectId ?? null,
4615
4719
  input: storage.safelyParseJSON(row.input),
4616
4720
  output: row.output ? storage.safelyParseJSON(row.output) : null,
4617
4721
  groundTruth: row.groundTruth ? storage.safelyParseJSON(row.groundTruth) : null,
@@ -4639,6 +4743,8 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4639
4743
  datasetId: input.datasetId ?? null,
4640
4744
  datasetVersion: input.datasetVersion ?? null,
4641
4745
  agentVersion: input.agentVersion ?? null,
4746
+ organizationId: input.organizationId ?? null,
4747
+ projectId: input.projectId ?? null,
4642
4748
  targetType: input.targetType,
4643
4749
  targetId: input.targetId,
4644
4750
  name: input.name ?? null,
@@ -4660,6 +4766,8 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4660
4766
  datasetId: input.datasetId,
4661
4767
  datasetVersion: input.datasetVersion,
4662
4768
  agentVersion: input.agentVersion ?? null,
4769
+ organizationId: input.organizationId ?? null,
4770
+ projectId: input.projectId ?? null,
4663
4771
  targetType: input.targetType,
4664
4772
  targetId: input.targetId,
4665
4773
  name: input.name,
@@ -4802,6 +4910,17 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4802
4910
  conditions.push("status = ?");
4803
4911
  queryParams.push(args.status);
4804
4912
  }
4913
+ if (args.filters) {
4914
+ const { organizationId, projectId } = args.filters;
4915
+ if (organizationId !== void 0) {
4916
+ conditions.push("organizationId = ?");
4917
+ queryParams.push(organizationId);
4918
+ }
4919
+ if (projectId !== void 0) {
4920
+ conditions.push("projectId = ?");
4921
+ queryParams.push(projectId);
4922
+ }
4923
+ }
4805
4924
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4806
4925
  const countResult = await this.#client.execute({
4807
4926
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_EXPERIMENTS} ${whereClause}`,
@@ -4876,6 +4995,8 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4876
4995
  experimentId: input.experimentId,
4877
4996
  itemId: input.itemId,
4878
4997
  itemDatasetVersion: input.itemDatasetVersion ?? null,
4998
+ organizationId: input.organizationId ?? null,
4999
+ projectId: input.projectId ?? null,
4879
5000
  input: input.input,
4880
5001
  output: input.output,
4881
5002
  groundTruth: input.groundTruth,
@@ -4895,6 +5016,8 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
4895
5016
  experimentId: input.experimentId,
4896
5017
  itemId: input.itemId,
4897
5018
  itemDatasetVersion: input.itemDatasetVersion,
5019
+ organizationId: input.organizationId ?? null,
5020
+ projectId: input.projectId ?? null,
4898
5021
  input: input.input,
4899
5022
  output: input.output,
4900
5023
  groundTruth: input.groundTruth,
@@ -5014,6 +5137,17 @@ var ExperimentsLibSQL = class extends storage.ExperimentsStorage {
5014
5137
  conditions.push("status = ?");
5015
5138
  queryParams.push(args.status);
5016
5139
  }
5140
+ if (args.filters) {
5141
+ const { organizationId, projectId } = args.filters;
5142
+ if (organizationId !== void 0) {
5143
+ conditions.push("organizationId = ?");
5144
+ queryParams.push(organizationId);
5145
+ }
5146
+ if (projectId !== void 0) {
5147
+ conditions.push("projectId = ?");
5148
+ queryParams.push(projectId);
5149
+ }
5150
+ }
5017
5151
  const whereClause = `WHERE ${conditions.join(" AND ")}`;
5018
5152
  const countResult = await this.#client.execute({
5019
5153
  sql: `SELECT COUNT(*) as count FROM ${storage.TABLE_EXPERIMENT_RESULTS} ${whereClause}`,