@mastra/mongodb 1.10.1-alpha.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,157 @@
1
1
  # @mastra/mongodb
2
2
 
3
+ ## 1.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Fixed new observability span writes in MongoDB so `startedAt`, `endedAt`, `createdAt`, and `updatedAt` are stored as native BSON Date objects. Existing string-typed span dates remain readable and date filters support both old string values and new Date values. ([#18366](https://github.com/mastra-ai/mastra/pull/18366))
8
+
9
+ ### Patch Changes
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
+ - 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))
76
+
77
+ - 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)]:
78
+ - @mastra/core@1.46.0
79
+
80
+ ## 1.11.0-alpha.1
81
+
82
+ ### Minor Changes
83
+
84
+ - Fixed new observability span writes in MongoDB so `startedAt`, `endedAt`, `createdAt`, and `updatedAt` are stored as native BSON Date objects. Existing string-typed span dates remain readable and date filters support both old string values and new Date values. ([#18366](https://github.com/mastra-ai/mastra/pull/18366))
85
+
86
+ ### Patch Changes
87
+
88
+ - 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))
89
+
90
+ `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.
91
+
92
+ 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.
93
+
94
+ This release also clarifies the `targetType` contract via JSDoc:
95
+ - `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.
96
+ - `Experiment.targetType` / `CreateExperimentInput.targetType` stay required. An experiment by definition replays inputs against a specific target.
97
+
98
+ No behavior change for existing OSS-created experiments; the new fields are additive and optional.
99
+
100
+ Example:
101
+
102
+ ```ts
103
+ // Create an experiment scoped to a tenancy bucket. When the parent dataset
104
+ // already carries `organizationId` / `projectId`, `runExperiment` hydrates
105
+ // these fields automatically from the dataset record.
106
+ const experiment = await storage.createExperiment({
107
+ name: 'qa-regression',
108
+ datasetId: 'ds_123',
109
+ datasetVersion: 1,
110
+ targetType: 'agent',
111
+ targetId: 'agent_qa',
112
+ totalItems: 10,
113
+ organizationId: 'org_123',
114
+ projectId: 'proj_123',
115
+ });
116
+
117
+ // List experiments within a tenancy bucket.
118
+ const experiments = await storage.listExperiments({
119
+ pagination: { page: 0, perPage: 20 },
120
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
121
+ });
122
+
123
+ // List per-item results within the same bucket.
124
+ const results = await storage.listExperimentResults({
125
+ experimentId: experiment.id,
126
+ pagination: { page: 0, perPage: 50 },
127
+ filters: { organizationId: 'org_123', projectId: 'proj_123' },
128
+ });
129
+ ```
130
+
131
+ - Persist and filter dataset tenancy + candidate identity in storage adapters. ([#18314](https://github.com/mastra-ai/mastra/pull/18314))
132
+
133
+ `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).
134
+
135
+ 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.
136
+
137
+ ```ts
138
+ await storage.createDataset({
139
+ name: 'candidates/missing-tool-call/incident-123',
140
+ organizationId: 'org_abc',
141
+ projectId: 'project_xyz',
142
+ candidateKey: 'missing-tool-call',
143
+ candidateId: 'incident-123',
144
+ });
145
+
146
+ await storage.listDatasets({
147
+ pagination: { page: 0, perPage: 20 },
148
+ filters: { organizationId: 'org_abc', projectId: 'project_xyz' },
149
+ });
150
+ ```
151
+
152
+ - 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)]:
153
+ - @mastra/core@1.46.0-alpha.4
154
+
3
155
  ## 1.10.1-alpha.0
4
156
 
5
157
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-mongodb
3
3
  description: Documentation for @mastra/mongodb. Use when working with @mastra/mongodb APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/mongodb"
6
- version: "1.10.1-alpha.0"
6
+ version: "1.11.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.10.1-alpha.0",
2
+ "version": "1.11.0",
3
3
  "package": "@mastra/mongodb",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -15,7 +15,7 @@ var skills = require('@mastra/core/storage/domains/skills');
15
15
 
16
16
  // package.json
17
17
  var package_default = {
18
- version: "1.10.1-alpha.0"};
18
+ version: "1.11.0"};
19
19
  var MongoDBFilterTranslator = class extends filter.BaseFilterTranslator {
20
20
  getSupportedOperators() {
21
21
  return {
@@ -1930,7 +1930,22 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
1930
1930
  return [
1931
1931
  { collection: storage.TABLE_DATASETS, keys: { id: 1 }, options: { name: "idx_datasets_id", unique: true } },
1932
1932
  { collection: storage.TABLE_DATASETS, keys: { createdAt: -1, id: 1 }, options: { name: "idx_datasets_createdat_id" } },
1933
+ {
1934
+ collection: storage.TABLE_DATASETS,
1935
+ keys: { organizationId: 1, projectId: 1, createdAt: -1, id: 1 },
1936
+ options: { name: "idx_datasets_tenancy_createdat_id" }
1937
+ },
1938
+ {
1939
+ collection: storage.TABLE_DATASETS,
1940
+ keys: { organizationId: 1, projectId: 1, candidateKey: 1, candidateId: 1 },
1941
+ options: { name: "idx_datasets_tenancy_candidate" }
1942
+ },
1933
1943
  { collection: storage.TABLE_DATASET_ITEMS, keys: { datasetId: 1 }, options: { name: "idx_dataset_items_datasetid" } },
1944
+ {
1945
+ collection: storage.TABLE_DATASET_ITEMS,
1946
+ keys: { organizationId: 1, projectId: 1, datasetId: 1, validTo: 1, isDeleted: 1 },
1947
+ options: { name: "idx_dataset_items_tenancy_list" }
1948
+ },
1934
1949
  {
1935
1950
  collection: storage.TABLE_DATASET_ITEMS,
1936
1951
  keys: { datasetId: 1, validTo: 1 },
@@ -2008,6 +2023,10 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2008
2023
  targetIds: typeof row.targetIds === "string" ? storage.safelyParseJSON(row.targetIds) : row.targetIds ?? void 0,
2009
2024
  scorerIds: typeof row.scorerIds === "string" ? storage.safelyParseJSON(row.scorerIds) : row.scorerIds ?? void 0,
2010
2025
  version: row.version ?? 0,
2026
+ organizationId: row.organizationId ?? null,
2027
+ projectId: row.projectId ?? null,
2028
+ candidateKey: row.candidateKey ?? null,
2029
+ candidateId: row.candidateId ?? null,
2011
2030
  createdAt: storage.ensureDate(row.createdAt),
2012
2031
  updatedAt: storage.ensureDate(row.updatedAt)
2013
2032
  };
@@ -2017,6 +2036,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2017
2036
  id: row.id,
2018
2037
  datasetId: row.datasetId,
2019
2038
  datasetVersion: row.datasetVersion,
2039
+ organizationId: row.organizationId ?? null,
2040
+ projectId: row.projectId ?? null,
2020
2041
  input: typeof row.input === "string" ? storage.safelyParseJSON(row.input) : row.input,
2021
2042
  groundTruth: typeof row.groundTruth === "string" ? storage.safelyParseJSON(row.groundTruth) : row.groundTruth,
2022
2043
  expectedTrajectory: typeof row.expectedTrajectory === "string" ? storage.safelyParseJSON(row.expectedTrajectory) : row.expectedTrajectory,
@@ -2061,6 +2082,10 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2061
2082
  targetIds: input.targetIds ?? null,
2062
2083
  scorerIds: input.scorerIds ?? null,
2063
2084
  version: 0,
2085
+ organizationId: input.organizationId ?? null,
2086
+ projectId: input.projectId ?? null,
2087
+ candidateKey: input.candidateKey ?? null,
2088
+ candidateId: input.candidateId ?? null,
2064
2089
  createdAt: now,
2065
2090
  updatedAt: now
2066
2091
  };
@@ -2171,7 +2196,12 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2171
2196
  try {
2172
2197
  const { page, perPage: perPageInput } = args.pagination;
2173
2198
  const collection = await this.getCollection(storage.TABLE_DATASETS);
2174
- const total = await collection.countDocuments({});
2199
+ const filter = {};
2200
+ if (args.filters?.organizationId !== void 0) filter.organizationId = args.filters.organizationId;
2201
+ if (args.filters?.projectId !== void 0) filter.projectId = args.filters.projectId;
2202
+ if (args.filters?.candidateKey !== void 0) filter.candidateKey = args.filters.candidateKey;
2203
+ if (args.filters?.candidateId !== void 0) filter.candidateId = args.filters.candidateId;
2204
+ const total = await collection.countDocuments(filter);
2175
2205
  if (total === 0) {
2176
2206
  return { datasets: [], pagination: { total: 0, page, perPage: perPageInput, hasMore: false } };
2177
2207
  }
@@ -2181,7 +2211,7 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2181
2211
  return { datasets: [], pagination: { total, page, perPage: perPageForResponse, hasMore: total > 0 } };
2182
2212
  }
2183
2213
  const limitValue = perPageInput === false ? total : perPage;
2184
- const rows = await collection.find({}).sort({ createdAt: -1, id: 1 }).skip(offset).limit(limitValue).toArray();
2214
+ const rows = await collection.find(filter).sort({ createdAt: -1, id: 1 }).skip(offset).limit(limitValue).toArray();
2185
2215
  return {
2186
2216
  datasets: rows.map((row) => this.transformDatasetRow(row)),
2187
2217
  pagination: {
@@ -2225,10 +2255,14 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2225
2255
  });
2226
2256
  }
2227
2257
  const newVersion = result.version;
2258
+ const organizationId = result.organizationId ?? null;
2259
+ const projectId = result.projectId ?? null;
2228
2260
  await itemsCollection.insertOne({
2229
2261
  id,
2230
2262
  datasetId: args.datasetId,
2231
2263
  datasetVersion: newVersion,
2264
+ organizationId,
2265
+ projectId,
2232
2266
  validTo: null,
2233
2267
  isDeleted: false,
2234
2268
  input: args.input,
@@ -2251,6 +2285,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2251
2285
  id,
2252
2286
  datasetId: args.datasetId,
2253
2287
  datasetVersion: newVersion,
2288
+ organizationId,
2289
+ projectId,
2254
2290
  input: args.input,
2255
2291
  groundTruth: args.groundTruth,
2256
2292
  expectedTrajectory: args.expectedTrajectory,
@@ -2322,6 +2358,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2322
2358
  });
2323
2359
  }
2324
2360
  const newVersion = result.version;
2361
+ const parentOrganizationId = result.organizationId ?? null;
2362
+ const parentProjectId = result.projectId ?? null;
2325
2363
  await itemsCollection.updateOne(
2326
2364
  { id: args.id, validTo: null, isDeleted: false },
2327
2365
  { $set: { validTo: newVersion } }
@@ -2330,6 +2368,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2330
2368
  id: args.id,
2331
2369
  datasetId: args.datasetId,
2332
2370
  datasetVersion: newVersion,
2371
+ organizationId: parentOrganizationId,
2372
+ projectId: parentProjectId,
2333
2373
  validTo: null,
2334
2374
  isDeleted: false,
2335
2375
  input: mergedInput,
@@ -2351,6 +2391,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2351
2391
  return {
2352
2392
  ...existing,
2353
2393
  datasetVersion: newVersion,
2394
+ organizationId: parentOrganizationId,
2395
+ projectId: parentProjectId,
2354
2396
  input: mergedInput,
2355
2397
  groundTruth: mergedGroundTruth,
2356
2398
  expectedTrajectory: mergedExpectedTrajectory,
@@ -2403,15 +2445,21 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2403
2445
  });
2404
2446
  }
2405
2447
  const newVersion = result.version;
2448
+ const parentOrganizationId = result.organizationId ?? null;
2449
+ const parentProjectId = result.projectId ?? null;
2406
2450
  await itemsCollection.updateOne({ id, validTo: null, isDeleted: false }, { $set: { validTo: newVersion } });
2407
2451
  await itemsCollection.insertOne({
2408
2452
  id,
2409
2453
  datasetId,
2410
2454
  datasetVersion: newVersion,
2455
+ organizationId: parentOrganizationId,
2456
+ projectId: parentProjectId,
2411
2457
  validTo: null,
2412
2458
  isDeleted: true,
2413
2459
  input: existing.input,
2414
2460
  groundTruth: existing.groundTruth,
2461
+ expectedTrajectory: existing.expectedTrajectory ?? null,
2462
+ toolMocks: existing.toolMocks ?? null,
2415
2463
  requestContext: existing.requestContext,
2416
2464
  metadata: existing.metadata,
2417
2465
  source: existing.source,
@@ -2474,11 +2522,15 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2474
2522
  });
2475
2523
  }
2476
2524
  const newVersion = result.version;
2525
+ const organizationId = result.organizationId ?? null;
2526
+ const projectId = result.projectId ?? null;
2477
2527
  if (itemsWithIds.length > 0) {
2478
2528
  const docs = itemsWithIds.map(({ generatedId, itemInput }) => ({
2479
2529
  id: generatedId,
2480
2530
  datasetId: input.datasetId,
2481
2531
  datasetVersion: newVersion,
2532
+ organizationId,
2533
+ projectId,
2482
2534
  validTo: null,
2483
2535
  isDeleted: false,
2484
2536
  input: itemInput.input,
@@ -2503,6 +2555,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2503
2555
  id: generatedId,
2504
2556
  datasetId: input.datasetId,
2505
2557
  datasetVersion: newVersion,
2558
+ organizationId,
2559
+ projectId,
2506
2560
  input: itemInput.input,
2507
2561
  groundTruth: itemInput.groundTruth,
2508
2562
  expectedTrajectory: itemInput.expectedTrajectory,
@@ -2563,6 +2617,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2563
2617
  });
2564
2618
  }
2565
2619
  const newVersion = result.version;
2620
+ const parentOrganizationId = result.organizationId ?? null;
2621
+ const parentProjectId = result.projectId ?? null;
2566
2622
  const currentIds = currentItems.map((i) => i.id);
2567
2623
  await itemsCollection.updateMany(
2568
2624
  { id: { $in: currentIds }, validTo: null, isDeleted: false },
@@ -2572,10 +2628,14 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2572
2628
  id: item.id,
2573
2629
  datasetId: input.datasetId,
2574
2630
  datasetVersion: newVersion,
2631
+ organizationId: parentOrganizationId,
2632
+ projectId: parentProjectId,
2575
2633
  validTo: null,
2576
2634
  isDeleted: true,
2577
2635
  input: item.input,
2578
2636
  groundTruth: item.groundTruth,
2637
+ expectedTrajectory: item.expectedTrajectory ?? null,
2638
+ toolMocks: item.toolMocks ?? null,
2579
2639
  requestContext: item.requestContext,
2580
2640
  metadata: item.metadata,
2581
2641
  source: item.source,
@@ -2677,6 +2737,8 @@ var MongoDBDatasetsStorage = class extends storage.DatasetsStorage {
2677
2737
  const { page, perPage: perPageInput } = args.pagination;
2678
2738
  const collection = await this.getCollection(storage.TABLE_DATASET_ITEMS);
2679
2739
  const filter = { datasetId: args.datasetId };
2740
+ if (args.filters?.organizationId !== void 0) filter.organizationId = args.filters.organizationId;
2741
+ if (args.filters?.projectId !== void 0) filter.projectId = args.filters.projectId;
2680
2742
  if (args.version !== void 0) {
2681
2743
  filter.datasetVersion = { $lte: args.version };
2682
2744
  filter.$or = [{ validTo: null }, { validTo: { $gt: args.version } }];
@@ -2831,6 +2893,8 @@ function transformExperimentRow(row) {
2831
2893
  metadata: parseJsonField(row.metadata) ?? void 0,
2832
2894
  datasetId: row.datasetId ?? null,
2833
2895
  datasetVersion: row.datasetVersion != null ? Number(row.datasetVersion) : null,
2896
+ organizationId: row.organizationId ?? null,
2897
+ projectId: row.projectId ?? null,
2834
2898
  targetType: row.targetType,
2835
2899
  targetId: row.targetId,
2836
2900
  status: row.status,
@@ -2851,6 +2915,8 @@ function transformExperimentResultRow(row) {
2851
2915
  experimentId: row.experimentId,
2852
2916
  itemId: row.itemId,
2853
2917
  itemDatasetVersion: row.itemDatasetVersion != null ? Number(row.itemDatasetVersion) : null,
2918
+ organizationId: row.organizationId ?? null,
2919
+ projectId: row.projectId ?? null,
2854
2920
  input: parseJsonField(row.input),
2855
2921
  output: parseJsonField(row.output) ?? null,
2856
2922
  groundTruth: parseJsonField(row.groundTruth) ?? null,
@@ -2889,11 +2955,14 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
2889
2955
  { collection: storage.TABLE_EXPERIMENTS, keys: { id: 1 }, options: { unique: true } },
2890
2956
  { collection: storage.TABLE_EXPERIMENTS, keys: { datasetId: 1 } },
2891
2957
  { collection: storage.TABLE_EXPERIMENTS, keys: { createdAt: -1, id: 1 } },
2958
+ // Tenancy: leading-tenant indexes for multi-tenant scans (parity with datasets domain).
2959
+ { collection: storage.TABLE_EXPERIMENTS, keys: { organizationId: 1, projectId: 1 } },
2892
2960
  { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { id: 1 }, options: { unique: true } },
2893
2961
  { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { experimentId: 1 } },
2894
2962
  { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { experimentId: 1, itemId: 1 }, options: { unique: true } },
2895
2963
  { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { createdAt: -1 } },
2896
- { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { experimentId: 1, startedAt: 1, id: 1 } }
2964
+ { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { experimentId: 1, startedAt: 1, id: 1 } },
2965
+ { collection: storage.TABLE_EXPERIMENT_RESULTS, keys: { organizationId: 1, projectId: 1 } }
2897
2966
  ];
2898
2967
  }
2899
2968
  async createDefaultIndexes() {
@@ -2935,6 +3004,8 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
2935
3004
  metadata: input.metadata ?? null,
2936
3005
  datasetId: input.datasetId ?? null,
2937
3006
  datasetVersion: input.datasetVersion ?? null,
3007
+ organizationId: input.organizationId ?? null,
3008
+ projectId: input.projectId ?? null,
2938
3009
  targetType: input.targetType,
2939
3010
  targetId: input.targetId,
2940
3011
  status: "pending",
@@ -2958,6 +3029,8 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
2958
3029
  metadata: input.metadata,
2959
3030
  datasetId: input.datasetId ?? null,
2960
3031
  datasetVersion: input.datasetVersion ?? null,
3032
+ organizationId: input.organizationId ?? null,
3033
+ projectId: input.projectId ?? null,
2961
3034
  targetType: input.targetType,
2962
3035
  targetId: input.targetId,
2963
3036
  status: "pending",
@@ -3059,6 +3132,15 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3059
3132
  if (args.status) {
3060
3133
  filter.status = args.status;
3061
3134
  }
3135
+ if (args.filters) {
3136
+ const { organizationId, projectId } = args.filters;
3137
+ if (organizationId !== void 0) {
3138
+ filter.organizationId = organizationId;
3139
+ }
3140
+ if (projectId !== void 0) {
3141
+ filter.projectId = projectId;
3142
+ }
3143
+ }
3062
3144
  const total = await collection.countDocuments(filter);
3063
3145
  if (total === 0) {
3064
3146
  return { experiments: [], pagination: { total: 0, page, perPage: perPageInput, hasMore: false } };
@@ -3119,6 +3201,8 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3119
3201
  experimentId: input.experimentId,
3120
3202
  itemId: input.itemId,
3121
3203
  itemDatasetVersion: input.itemDatasetVersion ?? null,
3204
+ organizationId: input.organizationId ?? null,
3205
+ projectId: input.projectId ?? null,
3122
3206
  input: input.input,
3123
3207
  output: input.output ?? null,
3124
3208
  groundTruth: input.groundTruth ?? null,
@@ -3140,6 +3224,8 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3140
3224
  experimentId: input.experimentId,
3141
3225
  itemId: input.itemId,
3142
3226
  itemDatasetVersion: input.itemDatasetVersion ?? null,
3227
+ organizationId: input.organizationId ?? null,
3228
+ projectId: input.projectId ?? null,
3143
3229
  input: input.input,
3144
3230
  output: input.output ?? null,
3145
3231
  groundTruth: input.groundTruth ?? null,
@@ -3239,6 +3325,15 @@ var MongoDBExperimentsStorage = class _MongoDBExperimentsStorage extends storage
3239
3325
  if (args.status) {
3240
3326
  filter.status = args.status;
3241
3327
  }
3328
+ if (args.filters) {
3329
+ const { organizationId, projectId } = args.filters;
3330
+ if (organizationId !== void 0) {
3331
+ filter.organizationId = organizationId;
3332
+ }
3333
+ if (projectId !== void 0) {
3334
+ filter.projectId = projectId;
3335
+ }
3336
+ }
3242
3337
  const total = await collection.countDocuments(filter);
3243
3338
  if (total === 0) {
3244
3339
  return { results: [], pagination: { total: 0, page, perPage: perPageInput, hasMore: false } };
@@ -6876,17 +6971,39 @@ Note: This migration may take some time for large collections.
6876
6971
  supported: ["batch-with-updates", "insert-only"]
6877
6972
  };
6878
6973
  }
6974
+ createDateRangeCondition(field, range) {
6975
+ const dateFilter = {};
6976
+ const stringFilter = {};
6977
+ if (range.start) {
6978
+ const operator = range.startExclusive ? "$gt" : "$gte";
6979
+ const start = range.start instanceof Date ? range.start : new Date(range.start);
6980
+ dateFilter[operator] = start;
6981
+ stringFilter[operator] = start.toISOString();
6982
+ }
6983
+ if (range.end) {
6984
+ const operator = range.endExclusive ? "$lt" : "$lte";
6985
+ const end = range.end instanceof Date ? range.end : new Date(range.end);
6986
+ dateFilter[operator] = end;
6987
+ stringFilter[operator] = end.toISOString();
6988
+ }
6989
+ if (Object.keys(dateFilter).length === 0) {
6990
+ return null;
6991
+ }
6992
+ return {
6993
+ $or: [{ [field]: dateFilter }, { [field]: stringFilter }]
6994
+ };
6995
+ }
6879
6996
  async createSpan(args) {
6880
6997
  const { span } = args;
6881
6998
  try {
6882
- const startedAt = span.startedAt instanceof Date ? span.startedAt.toISOString() : span.startedAt;
6883
- const endedAt = span.endedAt instanceof Date ? span.endedAt.toISOString() : span.endedAt;
6999
+ const startedAt = span.startedAt instanceof Date ? span.startedAt : new Date(span.startedAt);
7000
+ const endedAt = span.endedAt == null ? span.endedAt : span.endedAt instanceof Date ? span.endedAt : new Date(span.endedAt);
6884
7001
  const record = {
6885
7002
  ...span,
6886
7003
  startedAt,
6887
7004
  endedAt,
6888
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
6889
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7005
+ createdAt: /* @__PURE__ */ new Date(),
7006
+ updatedAt: /* @__PURE__ */ new Date()
6890
7007
  };
6891
7008
  const collection = await this.getCollection(storage.TABLE_SPANS);
6892
7009
  await collection.insertOne(record);
@@ -7021,15 +7138,15 @@ Note: This migration may take some time for large collections.
7021
7138
  const { traceId, spanId, updates } = args;
7022
7139
  try {
7023
7140
  const data = { ...updates };
7024
- if (data.endedAt instanceof Date) {
7025
- data.endedAt = data.endedAt.toISOString();
7141
+ if (data.endedAt != null && !(data.endedAt instanceof Date)) {
7142
+ data.endedAt = new Date(data.endedAt);
7026
7143
  }
7027
- if (data.startedAt instanceof Date) {
7028
- data.startedAt = data.startedAt.toISOString();
7144
+ if (data.startedAt != null && !(data.startedAt instanceof Date)) {
7145
+ data.startedAt = new Date(data.startedAt);
7029
7146
  }
7030
7147
  const updateData = {
7031
7148
  ...data,
7032
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7149
+ updatedAt: /* @__PURE__ */ new Date()
7033
7150
  };
7034
7151
  const collection = await this.getCollection(storage.TABLE_SPANS);
7035
7152
  await collection.updateOne({ spanId, traceId }, { $set: updateData });
@@ -7061,27 +7178,15 @@ Note: This migration may take some time for large collections.
7061
7178
  const andConditions = [];
7062
7179
  if (filters) {
7063
7180
  if (filters.startedAt) {
7064
- const startedAtFilter = {};
7065
- if (filters.startedAt.start) {
7066
- startedAtFilter.$gte = filters.startedAt.start.toISOString();
7067
- }
7068
- if (filters.startedAt.end) {
7069
- startedAtFilter.$lte = filters.startedAt.end.toISOString();
7070
- }
7071
- if (Object.keys(startedAtFilter).length > 0) {
7072
- mongoFilter.startedAt = startedAtFilter;
7181
+ const startedAtCondition = this.createDateRangeCondition("startedAt", filters.startedAt);
7182
+ if (startedAtCondition) {
7183
+ andConditions.push(startedAtCondition);
7073
7184
  }
7074
7185
  }
7075
7186
  if (filters.endedAt) {
7076
- const endedAtFilter = {};
7077
- if (filters.endedAt.start) {
7078
- endedAtFilter.$gte = filters.endedAt.start.toISOString();
7079
- }
7080
- if (filters.endedAt.end) {
7081
- endedAtFilter.$lte = filters.endedAt.end.toISOString();
7082
- }
7083
- if (Object.keys(endedAtFilter).length > 0) {
7084
- andConditions.push({ endedAt: endedAtFilter });
7187
+ const endedAtCondition = this.createDateRangeCondition("endedAt", filters.endedAt);
7188
+ if (endedAtCondition) {
7189
+ andConditions.push(endedAtCondition);
7085
7190
  }
7086
7191
  }
7087
7192
  if (filters.spanType !== void 0) {
@@ -7289,14 +7394,14 @@ Note: This migration may take some time for large collections.
7289
7394
  async batchCreateSpans(args) {
7290
7395
  try {
7291
7396
  const records = args.records.map((record) => {
7292
- const startedAt = record.startedAt instanceof Date ? record.startedAt.toISOString() : record.startedAt;
7293
- const endedAt = record.endedAt instanceof Date ? record.endedAt.toISOString() : record.endedAt;
7397
+ const startedAt = record.startedAt instanceof Date ? record.startedAt : new Date(record.startedAt);
7398
+ const endedAt = record.endedAt == null ? record.endedAt : record.endedAt instanceof Date ? record.endedAt : new Date(record.endedAt);
7294
7399
  return {
7295
7400
  ...record,
7296
7401
  startedAt,
7297
7402
  endedAt,
7298
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
7299
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7403
+ createdAt: /* @__PURE__ */ new Date(),
7404
+ updatedAt: /* @__PURE__ */ new Date()
7300
7405
  };
7301
7406
  });
7302
7407
  if (records.length > 0) {
@@ -7321,15 +7426,15 @@ Note: This migration may take some time for large collections.
7321
7426
  }
7322
7427
  const bulkOps = args.records.map((record) => {
7323
7428
  const data = { ...record.updates };
7324
- if (data.endedAt instanceof Date) {
7325
- data.endedAt = data.endedAt.toISOString();
7429
+ if (data.endedAt != null && !(data.endedAt instanceof Date)) {
7430
+ data.endedAt = new Date(data.endedAt);
7326
7431
  }
7327
- if (data.startedAt instanceof Date) {
7328
- data.startedAt = data.startedAt.toISOString();
7432
+ if (data.startedAt != null && !(data.startedAt instanceof Date)) {
7433
+ data.startedAt = new Date(data.startedAt);
7329
7434
  }
7330
7435
  const updateData = {
7331
7436
  ...data,
7332
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
7437
+ updatedAt: /* @__PURE__ */ new Date()
7333
7438
  };
7334
7439
  return {
7335
7440
  updateOne: {