@mastra/mysql 0.2.0 → 0.3.0-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,89 @@
1
1
  # @mastra/mysql
2
2
 
3
+ ## 0.3.0-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
+ ## 0.3.0-alpha.0
75
+
76
+ ### Minor Changes
77
+
78
+ - The MySQL store now rejects item-level tool mocks with a clear error instead of silently dropping them. Tool mock persistence is not yet supported on MySQL, so saving a dataset item with `toolMocks` (or an experiment result with a `toolMockReport`) fails fast rather than discarding the data. ([#18036](https://github.com/mastra-ai/mastra/pull/18036))
79
+
80
+ ### Patch Changes
81
+
82
+ - Fixed: `mastra build` output no longer hangs on the first storage-touching request when an app uses `LibSQLStore`, `PostgresStore`, or `MySQLStore` with observational memory. `mastra dev` was unaffected; only the bundled `mastra start` output deadlocked. No code changes or `bundler.externals` workaround required on the app side after upgrading. ([#18302](https://github.com/mastra-ai/mastra/pull/18302))
83
+
84
+ - 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)]:
85
+ - @mastra/core@1.46.0-alpha.3
86
+
3
87
  ## 0.2.0
4
88
 
5
89
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -2058,6 +2058,21 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2058
2058
  #indexes;
2059
2059
  /** Tables managed by this domain */
2060
2060
  static MANAGED_TABLES = [storage.TABLE_DATASETS, storage.TABLE_DATASET_ITEMS, storage.TABLE_DATASET_VERSIONS];
2061
+ /**
2062
+ * Item-level tool mocks are not persisted by the MySQL adapter. Reject writes
2063
+ * that carry them so the feature fails loudly here instead of silently dropping
2064
+ * the mocks and then running tools live during experiments.
2065
+ */
2066
+ #rejectToolMocks(toolMocks) {
2067
+ if (Array.isArray(toolMocks) && toolMocks.length > 0) {
2068
+ throw new error.MastraError({
2069
+ id: "MYSQL_DATASET_TOOL_MOCKS_UNSUPPORTED",
2070
+ domain: error.ErrorDomain.STORAGE,
2071
+ category: error.ErrorCategory.USER,
2072
+ text: "Tool mocks are not supported on the MySQL storage adapter. Use a supported adapter (LibSQL, PostgreSQL, MongoDB, or Spanner) to persist dataset item tool mocks."
2073
+ });
2074
+ }
2075
+ }
2061
2076
  /**
2062
2077
  * Returns default index definitions for the datasets domain tables.
2063
2078
  * Currently no default indexes are defined for datasets.
@@ -2117,6 +2132,16 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2117
2132
  await this.operations.createTable({ tableName: storage.TABLE_DATASETS, schema: storage.DATASETS_SCHEMA });
2118
2133
  await this.operations.createTable({ tableName: storage.TABLE_DATASET_ITEMS, schema: storage.DATASET_ITEMS_SCHEMA });
2119
2134
  await this.operations.createTable({ tableName: storage.TABLE_DATASET_VERSIONS, schema: storage.DATASET_VERSIONS_SCHEMA });
2135
+ await this.operations.alterTable({
2136
+ tableName: storage.TABLE_DATASETS,
2137
+ schema: storage.DATASETS_SCHEMA,
2138
+ ifNotExists: ["organizationId", "projectId", "candidateKey", "candidateId"]
2139
+ });
2140
+ await this.operations.alterTable({
2141
+ tableName: storage.TABLE_DATASET_ITEMS,
2142
+ schema: storage.DATASET_ITEMS_SCHEMA,
2143
+ ifNotExists: ["organizationId", "projectId"]
2144
+ });
2120
2145
  await this.createDefaultIndexes();
2121
2146
  await this.createCustomIndexes();
2122
2147
  }
@@ -2135,6 +2160,10 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2135
2160
  inputSchema: parseJSON(row.inputSchema),
2136
2161
  groundTruthSchema: parseJSON(row.groundTruthSchema),
2137
2162
  version: row.version,
2163
+ organizationId: row.organizationId ?? null,
2164
+ projectId: row.projectId ?? null,
2165
+ candidateKey: row.candidateKey ?? null,
2166
+ candidateId: row.candidateId ?? null,
2138
2167
  createdAt: parseDateTime(row.createdAt) ?? /* @__PURE__ */ new Date(),
2139
2168
  updatedAt: parseDateTime(row.updatedAt) ?? /* @__PURE__ */ new Date()
2140
2169
  };
@@ -2144,6 +2173,8 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2144
2173
  id: row.id,
2145
2174
  datasetId: row.datasetId,
2146
2175
  datasetVersion: row.datasetVersion,
2176
+ organizationId: row.organizationId ?? null,
2177
+ projectId: row.projectId ?? null,
2147
2178
  input: parseJSON(row.input),
2148
2179
  groundTruth: row.groundTruth ? parseJSON(row.groundTruth) : void 0,
2149
2180
  metadata: row.metadata ? parseJSON(row.metadata) : void 0,
@@ -2156,6 +2187,8 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2156
2187
  id: row.id,
2157
2188
  datasetId: row.datasetId,
2158
2189
  datasetVersion: row.datasetVersion,
2190
+ organizationId: row.organizationId ?? null,
2191
+ projectId: row.projectId ?? null,
2159
2192
  validTo: row.validTo,
2160
2193
  isDeleted: Boolean(row.isDeleted),
2161
2194
  input: parseJSON(row.input),
@@ -2188,6 +2221,10 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2188
2221
  inputSchema: jsonArg(input.inputSchema),
2189
2222
  groundTruthSchema: jsonArg(input.groundTruthSchema),
2190
2223
  version: 0,
2224
+ organizationId: input.organizationId ?? null,
2225
+ projectId: input.projectId ?? null,
2226
+ candidateKey: input.candidateKey ?? null,
2227
+ candidateId: input.candidateId ?? null,
2191
2228
  createdAt: now,
2192
2229
  updatedAt: now
2193
2230
  }
@@ -2200,6 +2237,10 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2200
2237
  inputSchema: input.inputSchema ?? void 0,
2201
2238
  groundTruthSchema: input.groundTruthSchema ?? void 0,
2202
2239
  version: 0,
2240
+ organizationId: input.organizationId ?? null,
2241
+ projectId: input.projectId ?? null,
2242
+ candidateKey: input.candidateKey ?? null,
2243
+ candidateId: input.candidateId ?? null,
2203
2244
  createdAt: now,
2204
2245
  updatedAt: now
2205
2246
  };
@@ -2322,7 +2363,28 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2322
2363
  async listDatasets(args) {
2323
2364
  try {
2324
2365
  const { page, perPage: perPageInput } = args.pagination;
2325
- const whereClause = { sql: "", args: [] };
2366
+ const filterParts = [];
2367
+ const filterArgs = [];
2368
+ if (args.filters?.organizationId !== void 0) {
2369
+ filterParts.push(`${quoteIdentifier("organizationId", "column name")} = ?`);
2370
+ filterArgs.push(args.filters.organizationId);
2371
+ }
2372
+ if (args.filters?.projectId !== void 0) {
2373
+ filterParts.push(`${quoteIdentifier("projectId", "column name")} = ?`);
2374
+ filterArgs.push(args.filters.projectId);
2375
+ }
2376
+ if (args.filters?.candidateKey !== void 0) {
2377
+ filterParts.push(`${quoteIdentifier("candidateKey", "column name")} = ?`);
2378
+ filterArgs.push(args.filters.candidateKey);
2379
+ }
2380
+ if (args.filters?.candidateId !== void 0) {
2381
+ filterParts.push(`${quoteIdentifier("candidateId", "column name")} = ?`);
2382
+ filterArgs.push(args.filters.candidateId);
2383
+ }
2384
+ const whereClause = {
2385
+ sql: filterParts.length > 0 ? `WHERE ${filterParts.join(" AND ")}` : "",
2386
+ args: filterArgs
2387
+ };
2326
2388
  const total = await this.operations.loadTotalCount({ tableName: storage.TABLE_DATASETS, whereClause });
2327
2389
  if (total === 0) {
2328
2390
  return {
@@ -2362,6 +2424,7 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2362
2424
  }
2363
2425
  // --- SCD-2 item mutations ---
2364
2426
  async _doAddItem(args) {
2427
+ this.#rejectToolMocks(args.toolMocks);
2365
2428
  const connection = await this.pool.getConnection();
2366
2429
  try {
2367
2430
  await connection.beginTransaction();
@@ -2374,17 +2437,22 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2374
2437
  await connection.execute(`UPDATE ${tableDatasetsName} SET \`version\` = \`version\` + 1 WHERE id = ?`, [
2375
2438
  args.datasetId
2376
2439
  ]);
2377
- const [versionRows] = await connection.execute(
2378
- `SELECT \`version\` FROM ${tableDatasetsName} WHERE id = ?`,
2440
+ const [datasetRows] = await connection.execute(
2441
+ `SELECT \`version\`, \`organizationId\`, \`projectId\` FROM ${tableDatasetsName} WHERE id = ?`,
2379
2442
  [args.datasetId]
2380
2443
  );
2381
- const newVersion = versionRows[0]?.version;
2444
+ const parentRow = datasetRows[0];
2445
+ const newVersion = parentRow?.version;
2446
+ const parentOrganizationId = parentRow?.organizationId ?? null;
2447
+ const parentProjectId = parentRow?.projectId ?? null;
2382
2448
  await connection.execute(
2383
- `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, NULL, 0, ?, ?, ?, ?, ?)`,
2449
+ `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`organizationId\`, \`projectId\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, ?, ?, NULL, 0, ?, ?, ?, ?, ?)`,
2384
2450
  [
2385
2451
  id,
2386
2452
  args.datasetId,
2387
2453
  newVersion,
2454
+ parentOrganizationId,
2455
+ parentProjectId,
2388
2456
  jsonArg(args.input),
2389
2457
  jsonArg(args.groundTruth),
2390
2458
  jsonArg(args.metadata),
@@ -2401,6 +2469,8 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2401
2469
  id,
2402
2470
  datasetId: args.datasetId,
2403
2471
  datasetVersion: newVersion,
2472
+ organizationId: parentOrganizationId,
2473
+ projectId: parentProjectId,
2404
2474
  input: args.input,
2405
2475
  groundTruth: args.groundTruth,
2406
2476
  metadata: args.metadata,
@@ -2423,6 +2493,7 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2423
2493
  }
2424
2494
  }
2425
2495
  async _doUpdateItem(args) {
2496
+ this.#rejectToolMocks(args.toolMocks);
2426
2497
  const existing = await this.getItemById({ id: args.id });
2427
2498
  if (!existing) {
2428
2499
  throw new error.MastraError({
@@ -2454,21 +2525,26 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2454
2525
  await connection.execute(`UPDATE ${tableDatasetsName} SET \`version\` = \`version\` + 1 WHERE id = ?`, [
2455
2526
  args.datasetId
2456
2527
  ]);
2457
- const [versionRows] = await connection.execute(
2458
- `SELECT \`version\` FROM ${tableDatasetsName} WHERE id = ?`,
2528
+ const [datasetRows] = await connection.execute(
2529
+ `SELECT \`version\`, \`organizationId\`, \`projectId\` FROM ${tableDatasetsName} WHERE id = ?`,
2459
2530
  [args.datasetId]
2460
2531
  );
2461
- const newVersion = versionRows[0]?.version;
2532
+ const parentRow = datasetRows[0];
2533
+ const newVersion = parentRow?.version;
2534
+ const parentOrganizationId = parentRow?.organizationId ?? null;
2535
+ const parentProjectId = parentRow?.projectId ?? null;
2462
2536
  await connection.execute(
2463
2537
  `UPDATE ${tableItemsName} SET \`validTo\` = ? WHERE \`id\` = ? AND \`validTo\` IS NULL AND \`isDeleted\` = 0`,
2464
2538
  [newVersion, args.id]
2465
2539
  );
2466
2540
  await connection.execute(
2467
- `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, NULL, 0, ?, ?, ?, ?, ?)`,
2541
+ `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`organizationId\`, \`projectId\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, ?, ?, NULL, 0, ?, ?, ?, ?, ?)`,
2468
2542
  [
2469
2543
  args.id,
2470
2544
  args.datasetId,
2471
2545
  newVersion,
2546
+ parentOrganizationId,
2547
+ parentProjectId,
2472
2548
  jsonArg(mergedInput),
2473
2549
  jsonArg(mergedGroundTruth),
2474
2550
  jsonArg(mergedMetadata),
@@ -2484,6 +2560,8 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2484
2560
  return {
2485
2561
  ...existing,
2486
2562
  datasetVersion: newVersion,
2563
+ organizationId: parentOrganizationId,
2564
+ projectId: parentProjectId,
2487
2565
  input: mergedInput,
2488
2566
  groundTruth: mergedGroundTruth,
2489
2567
  metadata: mergedMetadata,
@@ -2526,21 +2604,26 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2526
2604
  await connection.execute(`UPDATE ${tableDatasetsName} SET \`version\` = \`version\` + 1 WHERE id = ?`, [
2527
2605
  datasetId
2528
2606
  ]);
2529
- const [versionRows] = await connection.execute(
2530
- `SELECT \`version\` FROM ${tableDatasetsName} WHERE id = ?`,
2607
+ const [datasetRows] = await connection.execute(
2608
+ `SELECT \`version\`, \`organizationId\`, \`projectId\` FROM ${tableDatasetsName} WHERE id = ?`,
2531
2609
  [datasetId]
2532
2610
  );
2533
- const newVersion = versionRows[0]?.version;
2611
+ const parentRow = datasetRows[0];
2612
+ const newVersion = parentRow?.version;
2613
+ const parentOrganizationId = parentRow?.organizationId ?? null;
2614
+ const parentProjectId = parentRow?.projectId ?? null;
2534
2615
  await connection.execute(
2535
2616
  `UPDATE ${tableItemsName} SET \`validTo\` = ? WHERE \`id\` = ? AND \`validTo\` IS NULL AND \`isDeleted\` = 0`,
2536
2617
  [newVersion, id]
2537
2618
  );
2538
2619
  await connection.execute(
2539
- `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, NULL, 1, ?, ?, ?, ?, ?)`,
2620
+ `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`organizationId\`, \`projectId\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, ?, ?, NULL, 1, ?, ?, ?, ?, ?)`,
2540
2621
  [
2541
2622
  id,
2542
2623
  datasetId,
2543
2624
  newVersion,
2625
+ parentOrganizationId,
2626
+ parentProjectId,
2544
2627
  jsonArg(existing.input),
2545
2628
  jsonArg(existing.groundTruth),
2546
2629
  jsonArg(existing.metadata),
@@ -2649,6 +2732,14 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2649
2732
  conditions.push(`\`validTo\` IS NULL`);
2650
2733
  conditions.push(`\`isDeleted\` = 0`);
2651
2734
  }
2735
+ if (args.filters?.organizationId !== void 0) {
2736
+ conditions.push(`\`organizationId\` = ?`);
2737
+ params.push(args.filters.organizationId);
2738
+ }
2739
+ if (args.filters?.projectId !== void 0) {
2740
+ conditions.push(`\`projectId\` = ?`);
2741
+ params.push(args.filters.projectId);
2742
+ }
2652
2743
  if (args.search) {
2653
2744
  conditions.push(`(LOWER(\`input\`) LIKE ? OR LOWER(COALESCE(\`groundTruth\`, '')) LIKE ?)`);
2654
2745
  const searchPattern = `%${args.search.toLowerCase()}%`;
@@ -2765,6 +2856,9 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2765
2856
  }
2766
2857
  // --- Bulk operations (SCD-2 internally) ---
2767
2858
  async _doBatchInsertItems(input) {
2859
+ for (const item of input.items) {
2860
+ this.#rejectToolMocks(item.toolMocks);
2861
+ }
2768
2862
  const dataset = await this.getDatasetById({ id: input.datasetId });
2769
2863
  if (!dataset) {
2770
2864
  throw new error.MastraError({
@@ -2790,16 +2884,20 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2790
2884
  [input.datasetId]
2791
2885
  );
2792
2886
  const newVersion = versionRows[0]?.version;
2887
+ const parentOrganizationId = dataset.organizationId ?? null;
2888
+ const parentProjectId = dataset.projectId ?? null;
2793
2889
  const items = [];
2794
2890
  for (const itemInput of input.items) {
2795
2891
  const id = crypto$1.randomUUID();
2796
2892
  items.push({ id, itemInput });
2797
2893
  await connection.execute(
2798
- `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, NULL, 0, ?, ?, ?, ?, ?)`,
2894
+ `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`organizationId\`, \`projectId\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, ?, ?, NULL, 0, ?, ?, ?, ?, ?)`,
2799
2895
  [
2800
2896
  id,
2801
2897
  input.datasetId,
2802
2898
  newVersion,
2899
+ parentOrganizationId,
2900
+ parentProjectId,
2803
2901
  jsonArg(itemInput.input),
2804
2902
  jsonArg(itemInput.groundTruth),
2805
2903
  jsonArg(itemInput.metadata),
@@ -2817,6 +2915,8 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2817
2915
  id,
2818
2916
  datasetId: input.datasetId,
2819
2917
  datasetVersion: newVersion,
2918
+ organizationId: parentOrganizationId,
2919
+ projectId: parentProjectId,
2820
2920
  input: itemInput.input,
2821
2921
  groundTruth: itemInput.groundTruth,
2822
2922
  metadata: itemInput.metadata,
@@ -2872,17 +2972,21 @@ var DatasetsMySQL = class _DatasetsMySQL extends storage.DatasetsStorage {
2872
2972
  [input.datasetId]
2873
2973
  );
2874
2974
  const newVersion = versionRows[0]?.version;
2975
+ const parentOrganizationId = dataset.organizationId ?? null;
2976
+ const parentProjectId = dataset.projectId ?? null;
2875
2977
  for (const item of currentItems) {
2876
2978
  await connection.execute(
2877
2979
  `UPDATE ${tableItemsName} SET \`validTo\` = ? WHERE \`id\` = ? AND \`validTo\` IS NULL AND \`isDeleted\` = 0`,
2878
2980
  [newVersion, item.id]
2879
2981
  );
2880
2982
  await connection.execute(
2881
- `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, NULL, 1, ?, ?, ?, ?, ?)`,
2983
+ `INSERT INTO ${tableItemsName} (\`id\`, \`datasetId\`, \`datasetVersion\`, \`organizationId\`, \`projectId\`, \`validTo\`, \`isDeleted\`, \`input\`, \`groundTruth\`, \`metadata\`, \`createdAt\`, \`updatedAt\`) VALUES (?, ?, ?, ?, ?, NULL, 1, ?, ?, ?, ?, ?)`,
2882
2984
  [
2883
2985
  item.id,
2884
2986
  input.datasetId,
2885
2987
  newVersion,
2988
+ parentOrganizationId,
2989
+ parentProjectId,
2886
2990
  jsonArg(item.input),
2887
2991
  jsonArg(item.groundTruth),
2888
2992
  jsonArg(item.metadata),
@@ -2934,10 +3038,22 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
2934
3038
  static MANAGED_TABLES = [storage.TABLE_EXPERIMENTS, storage.TABLE_EXPERIMENT_RESULTS];
2935
3039
  /**
2936
3040
  * Returns default index definitions for the experiments domain tables.
2937
- * Currently no default indexes are defined for experiments.
2938
3041
  */
2939
3042
  static getDefaultIndexDefs(_prefix = "") {
2940
- return [];
3043
+ return [
3044
+ // Tenancy: leading-tenant indexes for multi-tenant scans (parity with
3045
+ // pg/libsql/spanner/mongodb experiments adapters).
3046
+ {
3047
+ name: "idx_experiments_org_project",
3048
+ table: storage.TABLE_EXPERIMENTS,
3049
+ columns: ["organizationId", "projectId"]
3050
+ },
3051
+ {
3052
+ name: "idx_experiment_results_org_project",
3053
+ table: storage.TABLE_EXPERIMENT_RESULTS,
3054
+ columns: ["organizationId", "projectId"]
3055
+ }
3056
+ ];
2941
3057
  }
2942
3058
  /**
2943
3059
  * Exports DDL statements for all managed tables.
@@ -2968,10 +3084,12 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
2968
3084
  }
2969
3085
  /**
2970
3086
  * Creates default indexes for optimal query performance.
2971
- * Currently no default indexes are defined for experiments.
2972
3087
  */
2973
3088
  async createDefaultIndexes() {
2974
3089
  if (this.#skipDefaultIndexes) return;
3090
+ for (const indexDef of this.getDefaultIndexDefinitions()) {
3091
+ await this.operations.createIndex(indexDef);
3092
+ }
2975
3093
  }
2976
3094
  /**
2977
3095
  * Creates custom user-defined indexes for this domain's tables.
@@ -2985,6 +3103,16 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
2985
3103
  async init() {
2986
3104
  await this.operations.createTable({ tableName: storage.TABLE_EXPERIMENTS, schema: storage.EXPERIMENTS_SCHEMA });
2987
3105
  await this.operations.createTable({ tableName: storage.TABLE_EXPERIMENT_RESULTS, schema: storage.EXPERIMENT_RESULTS_SCHEMA });
3106
+ await this.operations.alterTable({
3107
+ tableName: storage.TABLE_EXPERIMENTS,
3108
+ schema: storage.EXPERIMENTS_SCHEMA,
3109
+ ifNotExists: ["organizationId", "projectId"]
3110
+ });
3111
+ await this.operations.alterTable({
3112
+ tableName: storage.TABLE_EXPERIMENT_RESULTS,
3113
+ schema: storage.EXPERIMENT_RESULTS_SCHEMA,
3114
+ ifNotExists: ["organizationId", "projectId"]
3115
+ });
2988
3116
  await this.createDefaultIndexes();
2989
3117
  await this.createCustomIndexes();
2990
3118
  }
@@ -2997,6 +3125,8 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
2997
3125
  id: row.id,
2998
3126
  datasetId: row.datasetId ?? null,
2999
3127
  datasetVersion: row.datasetVersion ?? null,
3128
+ organizationId: row.organizationId ?? null,
3129
+ projectId: row.projectId ?? null,
3000
3130
  targetType: row.targetType,
3001
3131
  targetId: row.targetId,
3002
3132
  name: row.name ?? void 0,
@@ -3019,6 +3149,8 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3019
3149
  experimentId: row.experimentId,
3020
3150
  itemId: row.itemId,
3021
3151
  itemDatasetVersion: row.itemDatasetVersion ?? null,
3152
+ organizationId: row.organizationId ?? null,
3153
+ projectId: row.projectId ?? null,
3022
3154
  input: parseJSON2(row.input),
3023
3155
  output: row.output ? parseJSON2(row.output) : null,
3024
3156
  groundTruth: row.groundTruth ? parseJSON2(row.groundTruth) : null,
@@ -3042,6 +3174,8 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3042
3174
  id,
3043
3175
  datasetId: input.datasetId ?? null,
3044
3176
  datasetVersion: input.datasetVersion ?? null,
3177
+ organizationId: input.organizationId ?? null,
3178
+ projectId: input.projectId ?? null,
3045
3179
  targetType: input.targetType,
3046
3180
  targetId: input.targetId,
3047
3181
  name: input.name ?? null,
@@ -3062,6 +3196,8 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3062
3196
  id,
3063
3197
  datasetId: input.datasetId,
3064
3198
  datasetVersion: input.datasetVersion,
3199
+ organizationId: input.organizationId ?? null,
3200
+ projectId: input.projectId ?? null,
3065
3201
  targetType: input.targetType,
3066
3202
  targetId: input.targetId,
3067
3203
  name: input.name,
@@ -3156,6 +3292,17 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3156
3292
  conditions.push(`${quoteIdentifier("datasetId", "column name")} = ?`);
3157
3293
  params.push(args.datasetId);
3158
3294
  }
3295
+ if (args.filters) {
3296
+ const { organizationId, projectId } = args.filters;
3297
+ if (organizationId !== void 0) {
3298
+ conditions.push(`${quoteIdentifier("organizationId", "column name")} = ?`);
3299
+ params.push(organizationId);
3300
+ }
3301
+ if (projectId !== void 0) {
3302
+ conditions.push(`${quoteIdentifier("projectId", "column name")} = ?`);
3303
+ params.push(projectId);
3304
+ }
3305
+ }
3159
3306
  const whereClause = {
3160
3307
  sql: conditions.length > 0 ? ` WHERE ${conditions.join(" AND ")}` : "",
3161
3308
  args: params
@@ -3241,6 +3388,14 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3241
3388
  }
3242
3389
  }
3243
3390
  async addExperimentResult(input) {
3391
+ if (input.toolMockReport) {
3392
+ throw new error.MastraError({
3393
+ id: "MYSQL_EXPERIMENT_TOOL_MOCK_REPORT_UNSUPPORTED",
3394
+ domain: error.ErrorDomain.STORAGE,
3395
+ category: error.ErrorCategory.USER,
3396
+ text: "Tool mock reports are not supported on the MySQL storage adapter. Use a supported adapter (LibSQL, PostgreSQL, MongoDB, or Spanner) to persist experiment tool mock reports."
3397
+ });
3398
+ }
3244
3399
  try {
3245
3400
  const id = input.id ?? crypto$1.randomUUID();
3246
3401
  const now = /* @__PURE__ */ new Date();
@@ -3251,6 +3406,8 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3251
3406
  experimentId: input.experimentId,
3252
3407
  itemId: input.itemId,
3253
3408
  itemDatasetVersion: input.itemDatasetVersion ?? null,
3409
+ organizationId: input.organizationId ?? null,
3410
+ projectId: input.projectId ?? null,
3254
3411
  input: JSON.stringify(input.input),
3255
3412
  output: input.output ? JSON.stringify(input.output) : null,
3256
3413
  groundTruth: input.groundTruth ? JSON.stringify(input.groundTruth) : null,
@@ -3269,6 +3426,8 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3269
3426
  experimentId: input.experimentId,
3270
3427
  itemId: input.itemId,
3271
3428
  itemDatasetVersion: input.itemDatasetVersion,
3429
+ organizationId: input.organizationId ?? null,
3430
+ projectId: input.projectId ?? null,
3272
3431
  input: input.input,
3273
3432
  output: input.output,
3274
3433
  groundTruth: input.groundTruth,
@@ -3386,9 +3545,30 @@ var ExperimentsMySQL = class _ExperimentsMySQL extends storage.ExperimentsStorag
3386
3545
  async listExperimentResults(args) {
3387
3546
  try {
3388
3547
  const { page, perPage: perPageInput } = args.pagination;
3548
+ const conditions = [`${quoteIdentifier("experimentId", "column name")} = ?`];
3549
+ const params = [args.experimentId];
3550
+ if (args.traceId) {
3551
+ conditions.push(`${quoteIdentifier("traceId", "column name")} = ?`);
3552
+ params.push(args.traceId);
3553
+ }
3554
+ if (args.status) {
3555
+ conditions.push(`${quoteIdentifier("status", "column name")} = ?`);
3556
+ params.push(args.status);
3557
+ }
3558
+ if (args.filters) {
3559
+ const { organizationId, projectId } = args.filters;
3560
+ if (organizationId !== void 0) {
3561
+ conditions.push(`${quoteIdentifier("organizationId", "column name")} = ?`);
3562
+ params.push(organizationId);
3563
+ }
3564
+ if (projectId !== void 0) {
3565
+ conditions.push(`${quoteIdentifier("projectId", "column name")} = ?`);
3566
+ params.push(projectId);
3567
+ }
3568
+ }
3389
3569
  const whereClause = {
3390
- sql: ` WHERE ${quoteIdentifier("experimentId", "column name")} = ?`,
3391
- args: [args.experimentId]
3570
+ sql: ` WHERE ${conditions.join(" AND ")}`,
3571
+ args: params
3392
3572
  };
3393
3573
  const total = await this.operations.loadTotalCount({ tableName: storage.TABLE_EXPERIMENT_RESULTS, whereClause });
3394
3574
  if (total === 0) {
@@ -4794,12 +4974,7 @@ var MemoryMySQL = class _MemoryMySQL extends storage.MemoryStorage {
4794
4974
  await this.operations.createTable({ tableName: storage.TABLE_THREADS, schema: storage.TABLE_SCHEMAS[storage.TABLE_THREADS] });
4795
4975
  await this.operations.createTable({ tableName: storage.TABLE_MESSAGES, schema: storage.TABLE_SCHEMAS[storage.TABLE_MESSAGES] });
4796
4976
  await this.operations.createTable({ tableName: storage.TABLE_RESOURCES, schema: storage.TABLE_SCHEMAS[storage.TABLE_RESOURCES] });
4797
- let omSchema;
4798
- try {
4799
- const { OBSERVATIONAL_MEMORY_TABLE_SCHEMA } = await import('@mastra/core/storage');
4800
- omSchema = OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE];
4801
- } catch {
4802
- }
4977
+ const omSchema = storage.OBSERVATIONAL_MEMORY_TABLE_SCHEMA?.[OM_TABLE];
4803
4978
  if (omSchema) {
4804
4979
  await this.operations.createTable({
4805
4980
  tableName: OM_TABLE,