@mastra/pg 1.14.1-alpha.0 → 1.14.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,155 @@
1
1
  # @mastra/pg
2
2
 
3
+ ## 1.14.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed `PostgresStore.init()` failing with "RoutingDbClient already has a pinned client" when a single store is shared across concurrent requests (for example, request-scoped Mastra instances reusing one store/pool). Concurrent `init()` calls are now coalesced into a single shared initialization instead of each pinning the client. ([#18336](https://github.com/mastra-ai/mastra/pull/18336))
8
+
9
+ Also, `init()` is now a no-op when `disableInit: true`, so apps that manage their database schema externally are no longer forced through the connect-and-pin path.
10
+
11
+ - 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))
12
+
13
+ `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.
14
+
15
+ 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.
16
+
17
+ This release also clarifies the `targetType` contract via JSDoc:
18
+ - `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.
19
+ - `Experiment.targetType` / `CreateExperimentInput.targetType` stay required. An experiment by definition replays inputs against a specific target.
20
+
21
+ No behavior change for existing OSS-created experiments; the new fields are additive and optional.
22
+
23
+ Example:
24
+
25
+ ```ts
26
+ // Create an experiment scoped to a tenancy bucket. When the parent dataset
27
+ // already carries `organizationId` / `projectId`, `runExperiment` hydrates
28
+ // these fields automatically from the dataset record.
29
+ const experiment = await storage.createExperiment({
30
+ name: 'qa-regression',
31
+ datasetId: 'ds_123',
32
+ datasetVersion: 1,
33
+ targetType: 'agent',
34
+ targetId: 'agent_qa',
35
+ totalItems: 10,
36
+ organizationId: 'org_123',
37
+ projectId: 'proj_123',
38
+ });
39
+
40
+ // List experiments within a tenancy bucket.
41
+ const experiments = await storage.listExperiments({
42
+ pagination: { page: 0, perPage: 20 },
43
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
44
+ });
45
+
46
+ // List per-item results within the same bucket.
47
+ const results = await storage.listExperimentResults({
48
+ experimentId: experiment.id,
49
+ pagination: { page: 0, perPage: 50 },
50
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
51
+ });
52
+ ```
53
+
54
+ - Persist and filter dataset tenancy + candidate identity in storage adapters. ([#18314](https://github.com/mastra-ai/mastra/pull/18314))
55
+
56
+ `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).
57
+
58
+ 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.
59
+
60
+ ```ts
61
+ await storage.createDataset({
62
+ name: 'candidates/missing-tool-call/incident-123',
63
+ organizationId: 'org_abc',
64
+ projectId: 'project_xyz',
65
+ candidateKey: 'missing-tool-call',
66
+ candidateId: 'incident-123',
67
+ });
68
+
69
+ await storage.listDatasets({
70
+ pagination: { page: 0, perPage: 20 },
71
+ filters: { organizationId: 'org_abc', projectId: 'project_xyz' },
72
+ });
73
+ ```
74
+
75
+ - 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))
76
+
77
+ - 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))
78
+
79
+ - Updated dependencies [[`5bd72d2`](https://github.com/mastra-ai/mastra/commit/5bd72d255f45b5ea8ab342643bd463814a980a24), [`1cc9ee1`](https://github.com/mastra-ai/mastra/commit/1cc9ee1ba51db53020a735626d33017a60b4b5b3), [`417baae`](https://github.com/mastra-ai/mastra/commit/417baae40b995db5819c845036947f0c27dc1c00), [`65f255a`](https://github.com/mastra-ai/mastra/commit/65f255a38667beb6ceeadabfa9eb5059bfec8298), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`30ebaf0`](https://github.com/mastra-ai/mastra/commit/30ebaf07bed5f4d30f2f257836c15d1bf7e40aae), [`5704634`](https://github.com/mastra-ai/mastra/commit/5704634b22133167dea337a942a34f57aaa3fa14), [`5c4e9a4`](https://github.com/mastra-ai/mastra/commit/5c4e9a4cfb2216bb3ea7f8988ad3727f3b92bb3a), [`4a88c6e`](https://github.com/mastra-ai/mastra/commit/4a88c6e2bdce316f8d7551b4ec3449b0b06fc71c), [`417baae`](https://github.com/mastra-ai/mastra/commit/417baae40b995db5819c845036947f0c27dc1c00), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`25961e3`](https://github.com/mastra-ai/mastra/commit/25961e3260ff3b1464637af8fcdb36210551c39f), [`6a1428a`](https://github.com/mastra-ai/mastra/commit/6a1428a23133fc070fc6c1caa08d28f3ba4fe5ff), [`87a17ef`](https://github.com/mastra-ai/mastra/commit/87a17efbd725aca6639febdc5e69e2abb3048689), [`e11ff30`](https://github.com/mastra-ai/mastra/commit/e11ff301408bf1731dca2fb7fbfcd8c819500a35), [`7794d71`](https://github.com/mastra-ai/mastra/commit/7794d71872c68733a30e028dfb7b1705daf6c5d2), [`9d2c946`](https://github.com/mastra-ai/mastra/commit/9d2c946d0859e90ae4bcec5beeb1da7398d2ad1e), [`c0eda2b`](https://github.com/mastra-ai/mastra/commit/c0eda2bcd91a228427314b12c91d8b147f3a739f), [`7b29f33`](https://github.com/mastra-ai/mastra/commit/7b29f332a357a83e555f29e718e5f2fab9979943), [`c0eda2b`](https://github.com/mastra-ai/mastra/commit/c0eda2bcd91a228427314b12c91d8b147f3a739f), [`b13925b`](https://github.com/mastra-ai/mastra/commit/b13925bfa91aa8700f56fa54a9ce707ee7e4ba62), [`f1ec385`](https://github.com/mastra-ai/mastra/commit/f1ec385386f62b1a0847ec5353ae2bb169d1c3d9), [`e14986f`](https://github.com/mastra-ai/mastra/commit/e14986f6e5478d6384d04ff9a7f9a79a46a8b529), [`24912b1`](https://github.com/mastra-ai/mastra/commit/24912b1f855d29ec36af4ef4bde1f7417e20cdf5), [`bf94ec6`](https://github.com/mastra-ai/mastra/commit/bf94ec68192d9f16e46ef7e5ac36370aeeddf35d), [`a29f371`](https://github.com/mastra-ai/mastra/commit/a29f371aef629ac8562661524a497127e93b5131), [`7686216`](https://github.com/mastra-ai/mastra/commit/7686216f37e74568feddec17cef3c3d24e10e60a), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`073f910`](https://github.com/mastra-ai/mastra/commit/073f910481e7d94b95ba3830f96531774ae95d33), [`0be490f`](https://github.com/mastra-ai/mastra/commit/0be490fabb538c5a7de796ea0aff7d04a0bea1f3), [`0be490f`](https://github.com/mastra-ai/mastra/commit/0be490fabb538c5a7de796ea0aff7d04a0bea1f3), [`ebbe1d3`](https://github.com/mastra-ai/mastra/commit/ebbe1d31a965a3adb0e728758f326b8122b4b55f), [`974f614`](https://github.com/mastra-ai/mastra/commit/974f614e083bd68278536f94453f7b320b86a3c7), [`3818814`](https://github.com/mastra-ai/mastra/commit/38188149ce454c4403fe9fcbdf73b735c68d36be), [`975c59a`](https://github.com/mastra-ai/mastra/commit/975c59ae363ee275fc55062392e1ffd2cbccbd53), [`1f97ce5`](https://github.com/mastra-ai/mastra/commit/1f97ce5695463bebb4eaacf501da6fb403e20885), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`7f51548`](https://github.com/mastra-ai/mastra/commit/7f515481213780be7047cef00640b9d35f3d545c), [`64f58c0`](https://github.com/mastra-ai/mastra/commit/64f58c04e78b40137497d47f781e897e416f22a5), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`ebbe1d3`](https://github.com/mastra-ai/mastra/commit/ebbe1d31a965a3adb0e728758f326b8122b4b55f), [`d95f394`](https://github.com/mastra-ai/mastra/commit/d95f394fd24c8411886930d727679c4d5252aa26), [`417baae`](https://github.com/mastra-ai/mastra/commit/417baae40b995db5819c845036947f0c27dc1c00), [`8e25a78`](https://github.com/mastra-ai/mastra/commit/8e25a78e0597575f0b0729bae8c5e190c84869b5), [`417baae`](https://github.com/mastra-ai/mastra/commit/417baae40b995db5819c845036947f0c27dc1c00), [`f3f0c9d`](https://github.com/mastra-ai/mastra/commit/f3f0c9d7c878db5a13177871ce3523a14f14b311), [`a5b22d3`](https://github.com/mastra-ai/mastra/commit/a5b22d314d62a68d801886a8d3d0eb6c089473db), [`31be1cf`](https://github.com/mastra-ai/mastra/commit/31be1cf5f2a7b5eef12f6123a40653b4d8115c16), [`417baae`](https://github.com/mastra-ai/mastra/commit/417baae40b995db5819c845036947f0c27dc1c00), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858), [`74955f9`](https://github.com/mastra-ai/mastra/commit/74955f9120cde8b1d8ce4399232b4033236be858)]:
80
+ - @mastra/core@1.46.0
81
+
82
+ ## 1.14.1-alpha.1
83
+
84
+ ### Patch Changes
85
+
86
+ - 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))
87
+
88
+ `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.
89
+
90
+ 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.
91
+
92
+ This release also clarifies the `targetType` contract via JSDoc:
93
+ - `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.
94
+ - `Experiment.targetType` / `CreateExperimentInput.targetType` stay required. An experiment by definition replays inputs against a specific target.
95
+
96
+ No behavior change for existing OSS-created experiments; the new fields are additive and optional.
97
+
98
+ Example:
99
+
100
+ ```ts
101
+ // Create an experiment scoped to a tenancy bucket. When the parent dataset
102
+ // already carries `organizationId` / `projectId`, `runExperiment` hydrates
103
+ // these fields automatically from the dataset record.
104
+ const experiment = await storage.createExperiment({
105
+ name: 'qa-regression',
106
+ datasetId: 'ds_123',
107
+ datasetVersion: 1,
108
+ targetType: 'agent',
109
+ targetId: 'agent_qa',
110
+ totalItems: 10,
111
+ organizationId: 'org_123',
112
+ projectId: 'proj_123',
113
+ });
114
+
115
+ // List experiments within a tenancy bucket.
116
+ const experiments = await storage.listExperiments({
117
+ pagination: { page: 0, perPage: 20 },
118
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
119
+ });
120
+
121
+ // List per-item results within the same bucket.
122
+ const results = await storage.listExperimentResults({
123
+ experimentId: experiment.id,
124
+ pagination: { page: 0, perPage: 50 },
125
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
126
+ });
127
+ ```
128
+
129
+ - Persist and filter dataset tenancy + candidate identity in storage adapters. ([#18314](https://github.com/mastra-ai/mastra/pull/18314))
130
+
131
+ `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).
132
+
133
+ 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.
134
+
135
+ ```ts
136
+ await storage.createDataset({
137
+ name: 'candidates/missing-tool-call/incident-123',
138
+ organizationId: 'org_abc',
139
+ projectId: 'project_xyz',
140
+ candidateKey: 'missing-tool-call',
141
+ candidateId: 'incident-123',
142
+ });
143
+
144
+ await storage.listDatasets({
145
+ pagination: { page: 0, perPage: 20 },
146
+ filters: { organizationId: 'org_abc', projectId: 'project_xyz' },
147
+ });
148
+ ```
149
+
150
+ - 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)]:
151
+ - @mastra/core@1.46.0-alpha.4
152
+
3
153
  ## 1.14.1-alpha.0
4
154
 
5
155
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.14.1-alpha.0"
6
+ version: "1.14.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",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -5206,9 +5206,15 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5206
5206
  await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "targetType", "TEXT");
5207
5207
  await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "targetIds", "JSONB");
5208
5208
  await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "scorerIds", "JSONB");
5209
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "organizationId", "TEXT");
5210
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "projectId", "TEXT");
5211
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "candidateKey", "TEXT");
5212
+ await this.#addColumnIfNotExists(storage.TABLE_DATASETS, "candidateId", "TEXT");
5209
5213
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "requestContext", "JSONB");
5210
5214
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "source", "JSONB");
5211
5215
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "expectedTrajectory", "JSONB");
5216
+ await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "organizationId", "TEXT");
5217
+ await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "projectId", "TEXT");
5212
5218
  await this.#addColumnIfNotExists(storage.TABLE_DATASET_ITEMS, "toolMocks", "JSONB");
5213
5219
  await this.createDefaultIndexes();
5214
5220
  await this.createCustomIndexes();
@@ -5243,6 +5249,22 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5243
5249
  table: storage.TABLE_DATASET_VERSIONS,
5244
5250
  columns: ["datasetId", "version"],
5245
5251
  unique: true
5252
+ },
5253
+ // Tenancy: leading-tenant indexes for multi-tenant scans (parity with observability storage).
5254
+ {
5255
+ name: "idx_datasets_org_project",
5256
+ table: storage.TABLE_DATASETS,
5257
+ columns: ["organizationId", "projectId"]
5258
+ },
5259
+ {
5260
+ name: "idx_datasets_candidate",
5261
+ table: storage.TABLE_DATASETS,
5262
+ columns: ["candidateKey", "candidateId"]
5263
+ },
5264
+ {
5265
+ name: "idx_dataset_items_org_project",
5266
+ table: storage.TABLE_DATASET_ITEMS,
5267
+ columns: ["organizationId", "projectId"]
5246
5268
  }
5247
5269
  ];
5248
5270
  }
@@ -5280,6 +5302,10 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5280
5302
  targetType: row.targetType || null,
5281
5303
  targetIds: row.targetIds || null,
5282
5304
  scorerIds: row.scorerIds || null,
5305
+ organizationId: row.organizationId ?? null,
5306
+ projectId: row.projectId ?? null,
5307
+ candidateKey: row.candidateKey ?? null,
5308
+ candidateId: row.candidateId ?? null,
5283
5309
  version: row.version,
5284
5310
  createdAt: storage.ensureDate(row.createdAtZ || row.createdAt),
5285
5311
  updatedAt: storage.ensureDate(row.updatedAtZ || row.updatedAt)
@@ -5290,6 +5316,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5290
5316
  id: row.id,
5291
5317
  datasetId: row.datasetId,
5292
5318
  datasetVersion: row.datasetVersion,
5319
+ organizationId: row.organizationId ?? null,
5320
+ projectId: row.projectId ?? null,
5293
5321
  input: storage.safelyParseJSON(row.input),
5294
5322
  groundTruth: row.groundTruth ? storage.safelyParseJSON(row.groundTruth) : void 0,
5295
5323
  expectedTrajectory: row.expectedTrajectory ? storage.safelyParseJSON(row.expectedTrajectory) : void 0,
@@ -5306,6 +5334,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5306
5334
  id: row.id,
5307
5335
  datasetId: row.datasetId,
5308
5336
  datasetVersion: row.datasetVersion,
5337
+ organizationId: row.organizationId ?? null,
5338
+ projectId: row.projectId ?? null,
5309
5339
  validTo: row.validTo,
5310
5340
  isDeleted: Boolean(row.isDeleted),
5311
5341
  input: storage.safelyParseJSON(row.input),
@@ -5346,6 +5376,10 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5346
5376
  targetType: input.targetType ?? null,
5347
5377
  targetIds: input.targetIds !== void 0 ? JSON.stringify(input.targetIds) : null,
5348
5378
  scorerIds: input.scorerIds ? JSON.stringify(input.scorerIds) : null,
5379
+ organizationId: input.organizationId ?? null,
5380
+ projectId: input.projectId ?? null,
5381
+ candidateKey: input.candidateKey ?? null,
5382
+ candidateId: input.candidateId ?? null,
5349
5383
  version: 0,
5350
5384
  createdAt: nowIso,
5351
5385
  updatedAt: nowIso
@@ -5362,6 +5396,10 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5362
5396
  targetType: input.targetType ?? null,
5363
5397
  targetIds: input.targetIds ?? null,
5364
5398
  scorerIds: input.scorerIds ?? null,
5399
+ organizationId: input.organizationId ?? null,
5400
+ projectId: input.projectId ?? null,
5401
+ candidateKey: input.candidateKey ?? null,
5402
+ candidateId: input.candidateId ?? null,
5365
5403
  version: 0,
5366
5404
  createdAt: now,
5367
5405
  updatedAt: now
@@ -5466,6 +5504,10 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5466
5504
  targetType: (args.targetType !== void 0 ? args.targetType : existing.targetType) ?? null,
5467
5505
  targetIds: (args.targetIds !== void 0 ? args.targetIds : existing.targetIds) ?? null,
5468
5506
  scorerIds: (args.scorerIds !== void 0 ? args.scorerIds : existing.scorerIds) ?? null,
5507
+ organizationId: existing.organizationId ?? null,
5508
+ projectId: existing.projectId ?? null,
5509
+ candidateKey: existing.candidateKey ?? null,
5510
+ candidateId: existing.candidateId ?? null,
5469
5511
  updatedAt: new Date(now)
5470
5512
  };
5471
5513
  } catch (error$1) {
@@ -5528,7 +5570,33 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5528
5570
  try {
5529
5571
  const { page, perPage: perPageInput } = args.pagination;
5530
5572
  const tableName = getTableName2({ indexName: storage.TABLE_DATASETS, schemaName: getSchemaName2(this.#schema) });
5531
- const countResult = await this.#db.client.one(`SELECT COUNT(*) as count FROM ${tableName}`);
5573
+ const conditions = [];
5574
+ const queryParams = [];
5575
+ let paramIndex = 1;
5576
+ if (args.filters) {
5577
+ const { organizationId, projectId, candidateKey, candidateId } = args.filters;
5578
+ if (organizationId !== void 0) {
5579
+ conditions.push(`"organizationId" = $${paramIndex++}`);
5580
+ queryParams.push(organizationId);
5581
+ }
5582
+ if (projectId !== void 0) {
5583
+ conditions.push(`"projectId" = $${paramIndex++}`);
5584
+ queryParams.push(projectId);
5585
+ }
5586
+ if (candidateKey !== void 0) {
5587
+ conditions.push(`"candidateKey" = $${paramIndex++}`);
5588
+ queryParams.push(candidateKey);
5589
+ }
5590
+ if (candidateId !== void 0) {
5591
+ conditions.push(`"candidateId" = $${paramIndex++}`);
5592
+ queryParams.push(candidateId);
5593
+ }
5594
+ }
5595
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
5596
+ const countResult = await this.#db.client.one(
5597
+ `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
5598
+ queryParams
5599
+ );
5532
5600
  const total = parseInt(countResult.count, 10);
5533
5601
  if (total === 0) {
5534
5602
  return { datasets: [], pagination: { total: 0, page, perPage: perPageInput, hasMore: false } };
@@ -5537,8 +5605,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5537
5605
  const { offset, perPage: perPageForResponse } = storage.calculatePagination(page, perPageInput, perPage);
5538
5606
  const limitValue = perPageInput === false ? total : perPage;
5539
5607
  const rows = await this.#db.client.manyOrNone(
5540
- `SELECT * FROM ${tableName} ORDER BY "createdAt" DESC, "id" ASC LIMIT $1 OFFSET $2`,
5541
- [limitValue, offset]
5608
+ `SELECT * FROM ${tableName} ${whereClause} ORDER BY "createdAt" DESC, "id" ASC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
5609
+ [...queryParams, limitValue, offset]
5542
5610
  );
5543
5611
  return {
5544
5612
  datasets: (rows || []).map((row) => this.transformDatasetRow(row)),
@@ -5574,18 +5642,24 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5574
5642
  const now = /* @__PURE__ */ new Date();
5575
5643
  const nowIso = now.toISOString();
5576
5644
  let newVersion;
5645
+ let parentOrganizationId = null;
5646
+ let parentProjectId = null;
5577
5647
  await this.#db.client.tx(async (t) => {
5578
5648
  const row = await t.one(
5579
- `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version"`,
5649
+ `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version", "organizationId", "projectId"`,
5580
5650
  [args.datasetId]
5581
5651
  );
5582
5652
  newVersion = row.version;
5653
+ parentOrganizationId = row.organizationId ?? null;
5654
+ parentProjectId = row.projectId ?? null;
5583
5655
  await t.none(
5584
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
5656
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","organizationId","projectId","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,$4,$5,NULL,false,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)`,
5585
5657
  [
5586
5658
  id,
5587
5659
  args.datasetId,
5588
5660
  newVersion,
5661
+ parentOrganizationId,
5662
+ parentProjectId,
5589
5663
  JSON.stringify(args.input),
5590
5664
  jsonbArg(args.groundTruth),
5591
5665
  jsonbArg(args.expectedTrajectory),
@@ -5608,6 +5682,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5608
5682
  id,
5609
5683
  datasetId: args.datasetId,
5610
5684
  datasetVersion: newVersion,
5685
+ organizationId: parentOrganizationId,
5686
+ projectId: parentProjectId,
5611
5687
  input: args.input,
5612
5688
  groundTruth: args.groundTruth,
5613
5689
  expectedTrajectory: args.expectedTrajectory,
@@ -5666,22 +5742,28 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5666
5742
  const mergedMetadata = args.metadata !== void 0 ? args.metadata : existing.metadata;
5667
5743
  const mergedSource = args.source !== void 0 ? args.source : existing.source;
5668
5744
  let newVersion;
5745
+ let parentOrganizationId = null;
5746
+ let parentProjectId = null;
5669
5747
  await this.#db.client.tx(async (t) => {
5670
5748
  const row = await t.one(
5671
- `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version"`,
5749
+ `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version", "organizationId", "projectId"`,
5672
5750
  [args.datasetId]
5673
5751
  );
5674
5752
  newVersion = row.version;
5753
+ parentOrganizationId = row.organizationId ?? null;
5754
+ parentProjectId = row.projectId ?? null;
5675
5755
  await t.none(
5676
5756
  `UPDATE ${itemsTable} SET "validTo" = $1 WHERE "id" = $2 AND "validTo" IS NULL AND "isDeleted" = false`,
5677
5757
  [newVersion, args.id]
5678
5758
  );
5679
5759
  await t.none(
5680
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
5760
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","organizationId","projectId","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,$4,$5,NULL,false,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)`,
5681
5761
  [
5682
5762
  args.id,
5683
5763
  args.datasetId,
5684
5764
  newVersion,
5765
+ parentOrganizationId,
5766
+ parentProjectId,
5685
5767
  JSON.stringify(mergedInput),
5686
5768
  jsonbArg(mergedGroundTruth),
5687
5769
  jsonbArg(mergedExpectedTrajectory),
@@ -5703,6 +5785,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5703
5785
  return {
5704
5786
  ...existing,
5705
5787
  datasetVersion: newVersion,
5788
+ organizationId: parentOrganizationId,
5789
+ projectId: parentProjectId,
5706
5790
  input: mergedInput,
5707
5791
  groundTruth: mergedGroundTruth,
5708
5792
  expectedTrajectory: mergedExpectedTrajectory,
@@ -5746,20 +5830,24 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5746
5830
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
5747
5831
  await this.#db.client.tx(async (t) => {
5748
5832
  const row = await t.one(
5749
- `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version"`,
5833
+ `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version", "organizationId", "projectId"`,
5750
5834
  [datasetId]
5751
5835
  );
5752
5836
  const newVersion = row.version;
5837
+ const parentOrganizationId = row.organizationId ?? null;
5838
+ const parentProjectId = row.projectId ?? null;
5753
5839
  await t.none(
5754
5840
  `UPDATE ${itemsTable} SET "validTo" = $1 WHERE "id" = $2 AND "validTo" IS NULL AND "isDeleted" = false`,
5755
5841
  [newVersion, id]
5756
5842
  );
5757
5843
  await t.none(
5758
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
5844
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","organizationId","projectId","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,$4,$5,NULL,true,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)`,
5759
5845
  [
5760
5846
  id,
5761
5847
  datasetId,
5762
5848
  newVersion,
5849
+ parentOrganizationId,
5850
+ parentProjectId,
5763
5851
  JSON.stringify(existing.input),
5764
5852
  jsonbArg(existing.groundTruth),
5765
5853
  jsonbArg(existing.expectedTrajectory),
@@ -5811,6 +5899,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5811
5899
  const nowIso = now.toISOString();
5812
5900
  const versionId = crypto.randomUUID();
5813
5901
  const itemsWithIds = input.items.map((itemInput) => ({ id: crypto.randomUUID(), input: itemInput }));
5902
+ const parentOrganizationId = dataset.organizationId ?? null;
5903
+ const parentProjectId = dataset.projectId ?? null;
5814
5904
  let newVersion;
5815
5905
  await this.#db.client.tx(async (t) => {
5816
5906
  const row = await t.one(
@@ -5820,11 +5910,13 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5820
5910
  newVersion = row.version;
5821
5911
  for (const { id, input: itemInput } of itemsWithIds) {
5822
5912
  await t.none(
5823
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,false,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
5913
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","organizationId","projectId","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,$4,$5,NULL,false,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)`,
5824
5914
  [
5825
5915
  id,
5826
5916
  input.datasetId,
5827
5917
  newVersion,
5918
+ parentOrganizationId,
5919
+ parentProjectId,
5828
5920
  JSON.stringify(itemInput.input),
5829
5921
  jsonbArg(itemInput.groundTruth),
5830
5922
  jsonbArg(itemInput.expectedTrajectory),
@@ -5848,6 +5940,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5848
5940
  id,
5849
5941
  datasetId: input.datasetId,
5850
5942
  datasetVersion: newVersion,
5943
+ organizationId: parentOrganizationId,
5944
+ projectId: parentProjectId,
5851
5945
  input: itemInput.input,
5852
5946
  groundTruth: itemInput.groundTruth,
5853
5947
  expectedTrajectory: itemInput.expectedTrajectory,
@@ -5897,6 +5991,8 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5897
5991
  });
5898
5992
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
5899
5993
  const versionId = crypto.randomUUID();
5994
+ const parentOrganizationId = dataset.organizationId ?? null;
5995
+ const parentProjectId = dataset.projectId ?? null;
5900
5996
  await this.#db.client.tx(async (t) => {
5901
5997
  const row = await t.one(
5902
5998
  `UPDATE ${datasetsTable} SET "version" = "version" + 1 WHERE "id" = $1 RETURNING "version"`,
@@ -5909,11 +6005,13 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
5909
6005
  [newVersion, item.id]
5910
6006
  );
5911
6007
  await t.none(
5912
- `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,NULL,true,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)`,
6008
+ `INSERT INTO ${itemsTable} ("id","datasetId","datasetVersion","organizationId","projectId","validTo","isDeleted","input","groundTruth","expectedTrajectory","toolMocks","requestContext","metadata","source","createdAt","createdAtZ","updatedAt","updatedAtZ") VALUES ($1,$2,$3,$4,$5,NULL,true,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)`,
5913
6009
  [
5914
6010
  item.id,
5915
6011
  input.datasetId,
5916
6012
  newVersion,
6013
+ parentOrganizationId,
6014
+ parentProjectId,
5917
6015
  JSON.stringify(item.input),
5918
6016
  jsonbArg(item.groundTruth),
5919
6017
  jsonbArg(item.expectedTrajectory),
@@ -6035,6 +6133,17 @@ var DatasetsPG = class _DatasetsPG extends storage.DatasetsStorage {
6035
6133
  queryParams.push(`%${args.search}%`);
6036
6134
  paramIndex++;
6037
6135
  }
6136
+ if (args.filters) {
6137
+ const { organizationId, projectId } = args.filters;
6138
+ if (organizationId !== void 0) {
6139
+ conditions.push(`"organizationId" = $${paramIndex++}`);
6140
+ queryParams.push(organizationId);
6141
+ }
6142
+ if (projectId !== void 0) {
6143
+ conditions.push(`"projectId" = $${paramIndex++}`);
6144
+ queryParams.push(projectId);
6145
+ }
6146
+ }
6038
6147
  const whereClause = `WHERE ${conditions.join(" AND ")}`;
6039
6148
  const countResult = await this.#db.client.one(
6040
6149
  `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
@@ -6173,12 +6282,12 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6173
6282
  await this.#db.alterTable({
6174
6283
  tableName: storage.TABLE_EXPERIMENTS,
6175
6284
  schema: storage.EXPERIMENTS_SCHEMA,
6176
- ifNotExists: ["agentVersion"]
6285
+ ifNotExists: ["agentVersion", "organizationId", "projectId"]
6177
6286
  });
6178
6287
  await this.#db.alterTable({
6179
6288
  tableName: storage.TABLE_EXPERIMENT_RESULTS,
6180
6289
  schema: storage.EXPERIMENT_RESULTS_SCHEMA,
6181
- ifNotExists: ["status", "tags", "toolMockReport"]
6290
+ ifNotExists: ["status", "tags", "toolMockReport", "organizationId", "projectId"]
6182
6291
  });
6183
6292
  await this.createDefaultIndexes();
6184
6293
  await this.createCustomIndexes();
@@ -6192,6 +6301,17 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6192
6301
  table: storage.TABLE_EXPERIMENT_RESULTS,
6193
6302
  columns: ["experimentId", "itemId"],
6194
6303
  unique: true
6304
+ },
6305
+ // Tenancy: leading-tenant indexes for multi-tenant scans (parity with datasets domain).
6306
+ {
6307
+ name: "idx_experiments_org_project",
6308
+ table: storage.TABLE_EXPERIMENTS,
6309
+ columns: ["organizationId", "projectId"]
6310
+ },
6311
+ {
6312
+ name: "idx_experiment_results_org_project",
6313
+ table: storage.TABLE_EXPERIMENT_RESULTS,
6314
+ columns: ["organizationId", "projectId"]
6195
6315
  }
6196
6316
  ];
6197
6317
  }
@@ -6225,6 +6345,8 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6225
6345
  datasetId: row.datasetId ?? null,
6226
6346
  datasetVersion: row.datasetVersion != null ? row.datasetVersion : null,
6227
6347
  agentVersion: row.agentVersion ?? null,
6348
+ organizationId: row.organizationId ?? null,
6349
+ projectId: row.projectId ?? null,
6228
6350
  targetType: row.targetType,
6229
6351
  targetId: row.targetId,
6230
6352
  status: row.status,
@@ -6244,6 +6366,8 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6244
6366
  experimentId: row.experimentId,
6245
6367
  itemId: row.itemId,
6246
6368
  itemDatasetVersion: row.itemDatasetVersion != null ? row.itemDatasetVersion : null,
6369
+ organizationId: row.organizationId ?? null,
6370
+ projectId: row.projectId ?? null,
6247
6371
  input: storage.safelyParseJSON(row.input),
6248
6372
  output: row.output ? storage.safelyParseJSON(row.output) : null,
6249
6373
  groundTruth: row.groundTruth ? storage.safelyParseJSON(row.groundTruth) : null,
@@ -6274,6 +6398,8 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6274
6398
  datasetId: input.datasetId ?? null,
6275
6399
  datasetVersion: input.datasetVersion ?? null,
6276
6400
  agentVersion: input.agentVersion ?? null,
6401
+ organizationId: input.organizationId ?? null,
6402
+ projectId: input.projectId ?? null,
6277
6403
  targetType: input.targetType,
6278
6404
  targetId: input.targetId,
6279
6405
  status: "pending",
@@ -6295,6 +6421,8 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6295
6421
  datasetId: input.datasetId ?? null,
6296
6422
  datasetVersion: input.datasetVersion ?? null,
6297
6423
  agentVersion: input.agentVersion ?? null,
6424
+ organizationId: input.organizationId ?? null,
6425
+ projectId: input.projectId ?? null,
6298
6426
  targetType: input.targetType,
6299
6427
  targetId: input.targetId,
6300
6428
  status: "pending",
@@ -6436,6 +6564,17 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6436
6564
  conditions.push(`"status" = $${paramIndex++}`);
6437
6565
  queryParams.push(args.status);
6438
6566
  }
6567
+ if (args.filters) {
6568
+ const { organizationId, projectId } = args.filters;
6569
+ if (organizationId !== void 0) {
6570
+ conditions.push(`"organizationId" = $${paramIndex++}`);
6571
+ queryParams.push(organizationId);
6572
+ }
6573
+ if (projectId !== void 0) {
6574
+ conditions.push(`"projectId" = $${paramIndex++}`);
6575
+ queryParams.push(projectId);
6576
+ }
6577
+ }
6439
6578
  const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
6440
6579
  const countResult = await this.#db.client.one(
6441
6580
  `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,
@@ -6505,6 +6644,8 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6505
6644
  experimentId: input.experimentId,
6506
6645
  itemId: input.itemId,
6507
6646
  itemDatasetVersion: input.itemDatasetVersion ?? null,
6647
+ organizationId: input.organizationId ?? null,
6648
+ projectId: input.projectId ?? null,
6508
6649
  input: input.input,
6509
6650
  output: input.output ?? null,
6510
6651
  groundTruth: input.groundTruth ?? null,
@@ -6524,6 +6665,8 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6524
6665
  experimentId: input.experimentId,
6525
6666
  itemId: input.itemId,
6526
6667
  itemDatasetVersion: input.itemDatasetVersion ?? null,
6668
+ organizationId: input.organizationId ?? null,
6669
+ projectId: input.projectId ?? null,
6527
6670
  input: input.input,
6528
6671
  output: input.output ?? null,
6529
6672
  groundTruth: input.groundTruth ?? null,
@@ -6637,6 +6780,17 @@ var ExperimentsPG = class _ExperimentsPG extends storage.ExperimentsStorage {
6637
6780
  conditions.push(`"status" = $${paramIndex++}`);
6638
6781
  queryParams.push(args.status);
6639
6782
  }
6783
+ if (args.filters) {
6784
+ const { organizationId, projectId } = args.filters;
6785
+ if (organizationId !== void 0) {
6786
+ conditions.push(`"organizationId" = $${paramIndex++}`);
6787
+ queryParams.push(organizationId);
6788
+ }
6789
+ if (projectId !== void 0) {
6790
+ conditions.push(`"projectId" = $${paramIndex++}`);
6791
+ queryParams.push(projectId);
6792
+ }
6793
+ }
6640
6794
  const whereClause = `WHERE ${conditions.join(" AND ")}`;
6641
6795
  const countResult = await this.#db.client.one(
6642
6796
  `SELECT COUNT(*) as count FROM ${tableName} ${whereClause}`,