@mastra/spanner 1.2.0 → 1.2.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,85 @@
1
1
  # @mastra/spanner
2
2
 
3
+ ## 1.2.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
+
74
+ ## 1.2.1-alpha.0
75
+
76
+ ### Patch Changes
77
+
78
+ - Added storage for item-level tool mocks. Dataset items persist their `toolMocks` and experiment results persist their `toolMockReport`, so mocks and run diagnostics survive across sessions. ([#18036](https://github.com/mastra-ai/mastra/pull/18036))
79
+
80
+ - Updated dependencies [[`65f255a`](https://github.com/mastra-ai/mastra/commit/65f255a38667beb6ceeadabfa9eb5059bfec8298), [`4a88c6e`](https://github.com/mastra-ai/mastra/commit/4a88c6e2bdce316f8d7551b4ec3449b0b06fc71c), [`87a17ef`](https://github.com/mastra-ai/mastra/commit/87a17efbd725aca6639febdc5e69e2abb3048689), [`e11ff30`](https://github.com/mastra-ai/mastra/commit/e11ff301408bf1731dca2fb7fbfcd8c819500a35), [`9d2c946`](https://github.com/mastra-ai/mastra/commit/9d2c946d0859e90ae4bcec5beeb1da7398d2ad1e), [`f1ec385`](https://github.com/mastra-ai/mastra/commit/f1ec385386f62b1a0847ec5353ae2bb169d1c3d9), [`e14986f`](https://github.com/mastra-ai/mastra/commit/e14986f6e5478d6384d04ff9a7f9a79a46a8b529), [`0be490f`](https://github.com/mastra-ai/mastra/commit/0be490fabb538c5a7de796ea0aff7d04a0bea1f3), [`0be490f`](https://github.com/mastra-ai/mastra/commit/0be490fabb538c5a7de796ea0aff7d04a0bea1f3), [`974f614`](https://github.com/mastra-ai/mastra/commit/974f614e083bd68278536f94453f7b320b86a3c7), [`31be1cf`](https://github.com/mastra-ai/mastra/commit/31be1cf5f2a7b5eef12f6123a40653b4d8115c16)]:
81
+ - @mastra/core@1.46.0-alpha.3
82
+
3
83
  ## 1.2.0
4
84
 
5
85
  ### Minor Changes
@@ -3,7 +3,7 @@ name: mastra-spanner
3
3
  description: Documentation for @mastra/spanner. Use when working with @mastra/spanner APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/spanner"
6
- version: "1.2.0"
6
+ version: "1.2.1-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.0",
2
+ "version": "1.2.1-alpha.1",
3
3
  "package": "@mastra/spanner",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -2969,6 +2969,10 @@ function rowToDataset(row) {
2969
2969
  targetIds: t.targetIds ?? null,
2970
2970
  scorerIds: t.scorerIds ?? null,
2971
2971
  version: Number(t.version ?? 0),
2972
+ organizationId: t.organizationId ?? null,
2973
+ projectId: t.projectId ?? null,
2974
+ candidateKey: t.candidateKey ?? null,
2975
+ candidateId: t.candidateId ?? null,
2972
2976
  createdAt: toDate(t.createdAt),
2973
2977
  updatedAt: toDate(t.updatedAt)
2974
2978
  };
@@ -2979,9 +2983,12 @@ function rowToItem(row) {
2979
2983
  id: String(t.id),
2980
2984
  datasetId: String(t.datasetId),
2981
2985
  datasetVersion: Number(t.datasetVersion),
2986
+ organizationId: t.organizationId ?? null,
2987
+ projectId: t.projectId ?? null,
2982
2988
  input: t.input,
2983
2989
  groundTruth: t.groundTruth ?? void 0,
2984
2990
  expectedTrajectory: t.expectedTrajectory ?? void 0,
2991
+ toolMocks: t.toolMocks ?? void 0,
2985
2992
  requestContext: t.requestContext ?? void 0,
2986
2993
  metadata: t.metadata ?? void 0,
2987
2994
  source: t.source ?? void 0,
@@ -3024,6 +3031,16 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3024
3031
  await this.db.createTable({ tableName: storage.TABLE_DATASETS, schema: storage.TABLE_SCHEMAS[storage.TABLE_DATASETS] });
3025
3032
  await this.db.createTable({ tableName: storage.TABLE_DATASET_ITEMS, schema: storage.TABLE_SCHEMAS[storage.TABLE_DATASET_ITEMS] });
3026
3033
  await this.db.createTable({ tableName: storage.TABLE_DATASET_VERSIONS, schema: storage.TABLE_SCHEMAS[storage.TABLE_DATASET_VERSIONS] });
3034
+ await this.db.alterTable({
3035
+ tableName: storage.TABLE_DATASETS,
3036
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_DATASETS],
3037
+ ifNotExists: ["organizationId", "projectId", "candidateKey", "candidateId"]
3038
+ });
3039
+ await this.db.alterTable({
3040
+ tableName: storage.TABLE_DATASET_ITEMS,
3041
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_DATASET_ITEMS],
3042
+ ifNotExists: ["organizationId", "projectId"]
3043
+ });
3027
3044
  await this.createDefaultIndexes();
3028
3045
  await this.createCustomIndexes();
3029
3046
  }
@@ -3084,6 +3101,10 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3084
3101
  targetIds: input.targetIds ?? null,
3085
3102
  scorerIds: input.scorerIds ?? null,
3086
3103
  version: 0,
3104
+ organizationId: input.organizationId ?? null,
3105
+ projectId: input.projectId ?? null,
3106
+ candidateKey: input.candidateKey ?? null,
3107
+ candidateId: input.candidateId ?? null,
3087
3108
  createdAt: now,
3088
3109
  updatedAt: now
3089
3110
  };
@@ -3102,6 +3123,10 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3102
3123
  targetIds: record.targetIds,
3103
3124
  scorerIds: record.scorerIds,
3104
3125
  version: 0,
3126
+ organizationId: record.organizationId,
3127
+ projectId: record.projectId,
3128
+ candidateKey: record.candidateKey,
3129
+ candidateId: record.candidateId,
3105
3130
  createdAt: now,
3106
3131
  updatedAt: now
3107
3132
  }
@@ -3231,8 +3256,28 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3231
3256
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
3232
3257
  try {
3233
3258
  const tableName = quoteIdent(storage.TABLE_DATASETS, "table name");
3259
+ const filterConditions = [];
3260
+ const filterParams = {};
3261
+ if (args.filters?.organizationId !== void 0) {
3262
+ filterConditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
3263
+ filterParams.organizationId = args.filters.organizationId;
3264
+ }
3265
+ if (args.filters?.projectId !== void 0) {
3266
+ filterConditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
3267
+ filterParams.projectId = args.filters.projectId;
3268
+ }
3269
+ if (args.filters?.candidateKey !== void 0) {
3270
+ filterConditions.push(`${quoteIdent("candidateKey", "column name")} = @candidateKey`);
3271
+ filterParams.candidateKey = args.filters.candidateKey;
3272
+ }
3273
+ if (args.filters?.candidateId !== void 0) {
3274
+ filterConditions.push(`${quoteIdent("candidateId", "column name")} = @candidateId`);
3275
+ filterParams.candidateId = args.filters.candidateId;
3276
+ }
3277
+ const whereClause = filterConditions.length > 0 ? `WHERE ${filterConditions.join(" AND ")}` : "";
3234
3278
  const [countRows] = await this.database.run({
3235
- sql: `SELECT COUNT(*) AS count FROM ${tableName}`,
3279
+ sql: `SELECT COUNT(*) AS count FROM ${tableName} ${whereClause}`,
3280
+ params: filterParams,
3236
3281
  json: true
3237
3282
  });
3238
3283
  const total = Number(countRows[0]?.count ?? 0);
@@ -3241,10 +3286,10 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3241
3286
  }
3242
3287
  const limit = perPageInput === false ? total : perPage;
3243
3288
  const [rows] = await this.database.run({
3244
- sql: `SELECT * FROM ${tableName}
3289
+ sql: `SELECT * FROM ${tableName} ${whereClause}
3245
3290
  ORDER BY ${quoteIdent("createdAt", "column name")} DESC, ${quoteIdent("id", "column name")} ASC
3246
3291
  LIMIT @limit OFFSET @offset`,
3247
- params: { limit, offset },
3292
+ params: { ...filterParams, limit, offset },
3248
3293
  json: true
3249
3294
  });
3250
3295
  const datasets = rows.map(rowToDataset);
@@ -3271,10 +3316,13 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3271
3316
  // ==========================================================================
3272
3317
  // SCD-2 helpers
3273
3318
  // ==========================================================================
3274
- /** Reads and increments the dataset version inside `tx`; throws if missing. */
3319
+ /** Reads and increments the dataset version inside `tx`; throws if missing. Also returns parent tenancy. */
3275
3320
  async bumpVersion(tx, datasetId, now) {
3276
3321
  const [rows] = await tx.run({
3277
- sql: `SELECT ${quoteIdent("version", "column name")} AS version FROM ${quoteIdent(storage.TABLE_DATASETS, "table name")}
3322
+ sql: `SELECT ${quoteIdent("version", "column name")} AS version,
3323
+ ${quoteIdent("organizationId", "column name")} AS organizationId,
3324
+ ${quoteIdent("projectId", "column name")} AS projectId
3325
+ FROM ${quoteIdent(storage.TABLE_DATASETS, "table name")}
3278
3326
  WHERE ${quoteIdent("id", "column name")} = @id LIMIT 1`,
3279
3327
  params: { id: datasetId },
3280
3328
  json: true
@@ -3298,7 +3346,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3298
3346
  params: { newVersion, now: now.toISOString(), id: datasetId },
3299
3347
  types: { now: "timestamp" }
3300
3348
  });
3301
- return newVersion;
3349
+ return { version: newVersion, organizationId: row.organizationId ?? null, projectId: row.projectId ?? null };
3302
3350
  }
3303
3351
  /** Inserts a dataset version snapshot row inside `tx`. */
3304
3352
  async insertVersionRow(tx, datasetId, version, now) {
@@ -3343,18 +3391,21 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3343
3391
  await this.db.runWithAbortRetry(
3344
3392
  () => this.database.runTransactionAsync(async (tx) => {
3345
3393
  try {
3346
- const newVersion = await this.bumpVersion(tx, args.datasetId, now);
3394
+ const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, args.datasetId, now);
3347
3395
  await this.db.insert({
3348
3396
  tableName: storage.TABLE_DATASET_ITEMS,
3349
3397
  record: {
3350
3398
  id: itemId,
3351
3399
  datasetId: args.datasetId,
3352
3400
  datasetVersion: newVersion,
3401
+ organizationId,
3402
+ projectId,
3353
3403
  validTo: null,
3354
3404
  isDeleted: false,
3355
3405
  input: args.input,
3356
3406
  groundTruth: args.groundTruth ?? null,
3357
3407
  expectedTrajectory: args.expectedTrajectory ?? null,
3408
+ toolMocks: args.toolMocks ?? null,
3358
3409
  requestContext: args.requestContext ?? null,
3359
3410
  metadata: args.metadata ?? null,
3360
3411
  source: args.source ?? null,
@@ -3369,9 +3420,12 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3369
3420
  id: itemId,
3370
3421
  datasetId: args.datasetId,
3371
3422
  datasetVersion: newVersion,
3423
+ organizationId,
3424
+ projectId,
3372
3425
  input: args.input,
3373
3426
  groundTruth: args.groundTruth,
3374
3427
  expectedTrajectory: args.expectedTrajectory,
3428
+ toolMocks: args.toolMocks,
3375
3429
  requestContext: args.requestContext,
3376
3430
  metadata: args.metadata,
3377
3431
  source: args.source,
@@ -3424,6 +3478,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3424
3478
  input: args.input !== void 0 ? args.input : existing.input,
3425
3479
  groundTruth: args.groundTruth !== void 0 ? args.groundTruth : existing.groundTruth,
3426
3480
  expectedTrajectory: args.expectedTrajectory !== void 0 ? args.expectedTrajectory : existing.expectedTrajectory,
3481
+ toolMocks: args.toolMocks !== void 0 ? args.toolMocks : existing.toolMocks,
3427
3482
  requestContext: args.requestContext !== void 0 ? args.requestContext : existing.requestContext,
3428
3483
  metadata: args.metadata !== void 0 ? args.metadata : existing.metadata,
3429
3484
  source: args.source !== void 0 ? args.source : existing.source
@@ -3433,7 +3488,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3433
3488
  await this.db.runWithAbortRetry(
3434
3489
  () => this.database.runTransactionAsync(async (tx) => {
3435
3490
  try {
3436
- const newVersion = await this.bumpVersion(tx, args.datasetId, now);
3491
+ const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, args.datasetId, now);
3437
3492
  await this.closeCurrentRow(tx, args.id, newVersion);
3438
3493
  await this.db.insert({
3439
3494
  tableName: storage.TABLE_DATASET_ITEMS,
@@ -3441,11 +3496,14 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3441
3496
  id: args.id,
3442
3497
  datasetId: args.datasetId,
3443
3498
  datasetVersion: newVersion,
3499
+ organizationId,
3500
+ projectId,
3444
3501
  validTo: null,
3445
3502
  isDeleted: false,
3446
3503
  input: merged.input,
3447
3504
  groundTruth: merged.groundTruth ?? null,
3448
3505
  expectedTrajectory: merged.expectedTrajectory ?? null,
3506
+ toolMocks: merged.toolMocks ?? null,
3449
3507
  requestContext: merged.requestContext ?? null,
3450
3508
  metadata: merged.metadata ?? null,
3451
3509
  source: merged.source ?? null,
@@ -3460,9 +3518,12 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3460
3518
  id: args.id,
3461
3519
  datasetId: args.datasetId,
3462
3520
  datasetVersion: newVersion,
3521
+ organizationId,
3522
+ projectId,
3463
3523
  input: merged.input,
3464
3524
  groundTruth: merged.groundTruth,
3465
3525
  expectedTrajectory: merged.expectedTrajectory,
3526
+ toolMocks: merged.toolMocks,
3466
3527
  requestContext: merged.requestContext,
3467
3528
  metadata: merged.metadata,
3468
3529
  source: merged.source,
@@ -3507,7 +3568,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3507
3568
  await this.db.runWithAbortRetry(
3508
3569
  () => this.database.runTransactionAsync(async (tx) => {
3509
3570
  try {
3510
- const newVersion = await this.bumpVersion(tx, args.datasetId, now);
3571
+ const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, args.datasetId, now);
3511
3572
  await this.closeCurrentRow(tx, args.id, newVersion);
3512
3573
  await this.db.insert({
3513
3574
  tableName: storage.TABLE_DATASET_ITEMS,
@@ -3515,11 +3576,14 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3515
3576
  id: args.id,
3516
3577
  datasetId: args.datasetId,
3517
3578
  datasetVersion: newVersion,
3579
+ organizationId,
3580
+ projectId,
3518
3581
  validTo: null,
3519
3582
  isDeleted: true,
3520
3583
  input: existing.input,
3521
3584
  groundTruth: existing.groundTruth ?? null,
3522
3585
  expectedTrajectory: existing.expectedTrajectory ?? null,
3586
+ toolMocks: existing.toolMocks ?? null,
3523
3587
  requestContext: existing.requestContext ?? null,
3524
3588
  metadata: existing.metadata ?? null,
3525
3589
  source: existing.source ?? null,
@@ -3557,6 +3621,14 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3557
3621
  try {
3558
3622
  const conditions = [`${quoteIdent("datasetId", "column name")} = @datasetId`];
3559
3623
  const params = { datasetId: args.datasetId };
3624
+ if (args.filters?.organizationId !== void 0) {
3625
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
3626
+ params.organizationId = args.filters.organizationId;
3627
+ }
3628
+ if (args.filters?.projectId !== void 0) {
3629
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
3630
+ params.projectId = args.filters.projectId;
3631
+ }
3560
3632
  if (args.version !== void 0) {
3561
3633
  conditions.push(`${quoteIdent("datasetVersion", "column name")} <= @version`);
3562
3634
  conditions.push(
@@ -3778,7 +3850,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3778
3850
  await this.db.runWithAbortRetry(
3779
3851
  () => this.database.runTransactionAsync(async (tx) => {
3780
3852
  try {
3781
- const newVersion = await this.bumpVersion(tx, input.datasetId, now);
3853
+ const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, input.datasetId, now);
3782
3854
  for (const { id, item } of prepared) {
3783
3855
  await this.db.insert({
3784
3856
  tableName: storage.TABLE_DATASET_ITEMS,
@@ -3786,11 +3858,14 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3786
3858
  id,
3787
3859
  datasetId: input.datasetId,
3788
3860
  datasetVersion: newVersion,
3861
+ organizationId,
3862
+ projectId,
3789
3863
  validTo: null,
3790
3864
  isDeleted: false,
3791
3865
  input: item.input,
3792
3866
  groundTruth: item.groundTruth ?? null,
3793
3867
  expectedTrajectory: item.expectedTrajectory ?? null,
3868
+ toolMocks: item.toolMocks ?? null,
3794
3869
  requestContext: item.requestContext ?? null,
3795
3870
  metadata: item.metadata ?? null,
3796
3871
  source: item.source ?? null,
@@ -3806,9 +3881,12 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3806
3881
  id,
3807
3882
  datasetId: input.datasetId,
3808
3883
  datasetVersion: newVersion,
3884
+ organizationId,
3885
+ projectId,
3809
3886
  input: item.input,
3810
3887
  groundTruth: item.groundTruth,
3811
3888
  expectedTrajectory: item.expectedTrajectory,
3889
+ toolMocks: item.toolMocks,
3812
3890
  requestContext: item.requestContext,
3813
3891
  metadata: item.metadata,
3814
3892
  source: item.source,
@@ -3848,7 +3926,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3848
3926
  await this.db.runWithAbortRetry(
3849
3927
  () => this.database.runTransactionAsync(async (tx) => {
3850
3928
  try {
3851
- const newVersion = await this.bumpVersion(tx, input.datasetId, now);
3929
+ const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, input.datasetId, now);
3852
3930
  for (const existing of current) {
3853
3931
  await this.closeCurrentRow(tx, existing.id, newVersion);
3854
3932
  await this.db.insert({
@@ -3857,11 +3935,14 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
3857
3935
  id: existing.id,
3858
3936
  datasetId: input.datasetId,
3859
3937
  datasetVersion: newVersion,
3938
+ organizationId,
3939
+ projectId,
3860
3940
  validTo: null,
3861
3941
  isDeleted: true,
3862
3942
  input: existing.input,
3863
3943
  groundTruth: existing.groundTruth ?? null,
3864
3944
  expectedTrajectory: existing.expectedTrajectory ?? null,
3945
+ toolMocks: existing.toolMocks ?? null,
3865
3946
  requestContext: existing.requestContext ?? null,
3866
3947
  metadata: existing.metadata ?? null,
3867
3948
  source: existing.source ?? null,
@@ -3906,6 +3987,8 @@ function rowToExperiment(row) {
3906
3987
  metadata: t.metadata ?? void 0,
3907
3988
  datasetId: t.datasetId ?? null,
3908
3989
  datasetVersion: t.datasetVersion == null ? null : Number(t.datasetVersion),
3990
+ organizationId: t.organizationId ?? null,
3991
+ projectId: t.projectId ?? null,
3909
3992
  targetType: t.targetType,
3910
3993
  targetId: String(t.targetId),
3911
3994
  status: t.status,
@@ -3927,6 +4010,8 @@ function rowToExperimentResult(row) {
3927
4010
  experimentId: String(t.experimentId),
3928
4011
  itemId: String(t.itemId),
3929
4012
  itemDatasetVersion: t.itemDatasetVersion == null ? null : Number(t.itemDatasetVersion),
4013
+ organizationId: t.organizationId ?? null,
4014
+ projectId: t.projectId ?? null,
3930
4015
  input: t.input ?? null,
3931
4016
  output: t.output ?? null,
3932
4017
  groundTruth: t.groundTruth ?? null,
@@ -3937,6 +4022,7 @@ function rowToExperimentResult(row) {
3937
4022
  traceId: t.traceId ?? null,
3938
4023
  status: t.status ?? null,
3939
4024
  tags: t.tags ?? null,
4025
+ toolMockReport: t.toolMockReport ?? null,
3940
4026
  createdAt: toDate2(t.createdAt)
3941
4027
  };
3942
4028
  }
@@ -3957,6 +4043,16 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
3957
4043
  async init() {
3958
4044
  await this.db.createTable({ tableName: storage.TABLE_EXPERIMENTS, schema: storage.TABLE_SCHEMAS[storage.TABLE_EXPERIMENTS] });
3959
4045
  await this.db.createTable({ tableName: storage.TABLE_EXPERIMENT_RESULTS, schema: storage.TABLE_SCHEMAS[storage.TABLE_EXPERIMENT_RESULTS] });
4046
+ await this.db.alterTable({
4047
+ tableName: storage.TABLE_EXPERIMENTS,
4048
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_EXPERIMENTS],
4049
+ ifNotExists: ["organizationId", "projectId"]
4050
+ });
4051
+ await this.db.alterTable({
4052
+ tableName: storage.TABLE_EXPERIMENT_RESULTS,
4053
+ schema: storage.TABLE_SCHEMAS[storage.TABLE_EXPERIMENT_RESULTS],
4054
+ ifNotExists: ["organizationId", "projectId"]
4055
+ });
3960
4056
  await this.createDefaultIndexes();
3961
4057
  await this.createCustomIndexes();
3962
4058
  }
@@ -3978,6 +4074,17 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
3978
4074
  table: storage.TABLE_EXPERIMENT_RESULTS,
3979
4075
  columns: ["experimentId", "itemId"],
3980
4076
  unique: true
4077
+ },
4078
+ // Tenancy: leading-tenant indexes for multi-tenant scans (parity with datasets domain).
4079
+ {
4080
+ name: "mastra_experiments_org_project_idx",
4081
+ table: storage.TABLE_EXPERIMENTS,
4082
+ columns: ["organizationId", "projectId"]
4083
+ },
4084
+ {
4085
+ name: "mastra_experiment_results_org_project_idx",
4086
+ table: storage.TABLE_EXPERIMENT_RESULTS,
4087
+ columns: ["organizationId", "projectId"]
3981
4088
  }
3982
4089
  ];
3983
4090
  }
@@ -4004,6 +4111,8 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4004
4111
  metadata: input.metadata ?? void 0,
4005
4112
  datasetId: input.datasetId ?? null,
4006
4113
  datasetVersion: input.datasetVersion ?? null,
4114
+ organizationId: input.organizationId ?? null,
4115
+ projectId: input.projectId ?? null,
4007
4116
  targetType: input.targetType,
4008
4117
  targetId: input.targetId,
4009
4118
  status: "pending",
@@ -4026,6 +4135,8 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4026
4135
  metadata: experiment.metadata ?? null,
4027
4136
  datasetId: experiment.datasetId,
4028
4137
  datasetVersion: experiment.datasetVersion,
4138
+ organizationId: experiment.organizationId,
4139
+ projectId: experiment.projectId,
4029
4140
  targetType: experiment.targetType,
4030
4141
  targetId: experiment.targetId,
4031
4142
  status: experiment.status,
@@ -4144,6 +4255,17 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4144
4255
  conditions.push(`${quoteIdent("status", "column name")} = @status`);
4145
4256
  params.status = args.status;
4146
4257
  }
4258
+ if (args.filters) {
4259
+ const { organizationId, projectId } = args.filters;
4260
+ if (organizationId !== void 0) {
4261
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
4262
+ params.organizationId = organizationId;
4263
+ }
4264
+ if (projectId !== void 0) {
4265
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
4266
+ params.projectId = projectId;
4267
+ }
4268
+ }
4147
4269
  const whereSql = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
4148
4270
  const tableName = quoteIdent(storage.TABLE_EXPERIMENTS, "table name");
4149
4271
  const [countRows] = await this.database.run({
@@ -4228,6 +4350,8 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4228
4350
  experimentId: input.experimentId,
4229
4351
  itemId: input.itemId,
4230
4352
  itemDatasetVersion: input.itemDatasetVersion ?? null,
4353
+ organizationId: input.organizationId ?? null,
4354
+ projectId: input.projectId ?? null,
4231
4355
  input: input.input ?? null,
4232
4356
  output: input.output ?? null,
4233
4357
  groundTruth: input.groundTruth ?? null,
@@ -4238,6 +4362,7 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4238
4362
  traceId: input.traceId ?? null,
4239
4363
  status: input.status ?? null,
4240
4364
  tags: input.tags ?? null,
4365
+ toolMockReport: input.toolMockReport ?? null,
4241
4366
  createdAt: now
4242
4367
  };
4243
4368
  await this.db.insert({
@@ -4247,6 +4372,8 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4247
4372
  experimentId: result.experimentId,
4248
4373
  itemId: result.itemId,
4249
4374
  itemDatasetVersion: result.itemDatasetVersion,
4375
+ organizationId: result.organizationId,
4376
+ projectId: result.projectId,
4250
4377
  input: result.input,
4251
4378
  output: result.output,
4252
4379
  groundTruth: result.groundTruth,
@@ -4257,6 +4384,7 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4257
4384
  traceId: result.traceId,
4258
4385
  status: result.status,
4259
4386
  tags: result.tags,
4387
+ toolMockReport: result.toolMockReport,
4260
4388
  createdAt: now
4261
4389
  }
4262
4390
  });
@@ -4379,6 +4507,17 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends storage.ExperimentsSt
4379
4507
  conditions.push(`${quoteIdent("status", "column name")} = @status`);
4380
4508
  params.status = args.status;
4381
4509
  }
4510
+ if (args.filters) {
4511
+ const { organizationId, projectId } = args.filters;
4512
+ if (organizationId !== void 0) {
4513
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
4514
+ params.organizationId = organizationId;
4515
+ }
4516
+ if (projectId !== void 0) {
4517
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
4518
+ params.projectId = projectId;
4519
+ }
4520
+ }
4382
4521
  const whereSql = `WHERE ${conditions.join(" AND ")}`;
4383
4522
  const tableName = quoteIdent(storage.TABLE_EXPERIMENT_RESULTS, "table name");
4384
4523
  const [countRows] = await this.database.run({