@mastra/pg 1.15.0-alpha.1 → 1.15.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 +182 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,187 @@
|
|
|
1
1
|
# @mastra/pg
|
|
2
2
|
|
|
3
|
+
## 1.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added storage retention support to PostgreSQL. When you set a `retention` config, `PostgresStore` can prune old rows from every growth-table domain it implements: `memory` (threads, messages, resources by `createdAtZ`), `observability` (spans by `startedAtZ`), `scores` (by `createdAtZ`), `workflows` (run snapshots by `updatedAtZ`), `backgroundTasks` (by `completedAtZ`, so in-flight tasks are never pruned), `experiments` (whole runs by `completedAtZ`, results cascade with their parent), `notifications` (by `createdAtZ`), and `schedules` fire history (by `actual_fire_at`). ([#18733](https://github.com/mastra-ai/mastra/pull/18733))
|
|
8
|
+
|
|
9
|
+
Deletes run in batches via `ctid` subqueries (bounded, resumable, and cancellable) so they stay safe on large tables. Anchor-column indexes are created lazily on the first `prune()` call — never at init — so deployments that don't configure retention pay no extra index overhead. `prune()` only deletes rows; PostgreSQL's autovacuum reclaims the dead tuples for reuse.
|
|
10
|
+
|
|
11
|
+
The v-next observability domain (day-partitioned signal event tables: `spans`, `metrics`, `logs`, `scores`, `feedback`) is also covered: `prune()` drops whole day partitions — TimescaleDB chunks via `drop_chunks()`, pg_partman children and native partitions via detach + drop — that are entirely older than the cutoff, so aging out event data is a metadata operation instead of a row-by-row delete.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
const storage = new PostgresStore({
|
|
15
|
+
id: 'mastra-storage',
|
|
16
|
+
connectionString: process.env.DATABASE_URL,
|
|
17
|
+
retention: {
|
|
18
|
+
memory: { messages: { maxAge: '30d' } },
|
|
19
|
+
observability: { spans: { maxAge: '7d' } },
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await storage.prune();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Added optional tenancy arguments to `getDataset`, `updateDataset`, and `deleteDataset`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
29
|
+
|
|
30
|
+
You can now pass `organizationId` and `projectId` to scope dataset reads, updates, and deletes to a specific tenant. Reads and updates against a dataset in a different tenant throw `DATASET_NOT_FOUND` (surfaced as a 404 over HTTP). Deletes silently no-op on a tenancy mismatch — matching the existing "delete non-existent id is a no-op" semantics so cross-tenant existence is never leaked via error timing or status.
|
|
31
|
+
|
|
32
|
+
**Example**
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// Before
|
|
36
|
+
await client.getDataset('abc123');
|
|
37
|
+
await client.deleteDataset('abc123');
|
|
38
|
+
await client.updateDataset({ id: 'abc123', name: 'renamed' });
|
|
39
|
+
|
|
40
|
+
// After — scope to a tenant
|
|
41
|
+
await client.getDataset('abc123', { organizationId: 'org_a', projectId: 'proj_1' });
|
|
42
|
+
await client.deleteDataset('abc123', { organizationId: 'org_a' });
|
|
43
|
+
await client.updateDataset({ id: 'abc123', name: 'renamed', organizationId: 'org_a' });
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Pushed remaining dataset read filters and pagination down to storage. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
|
|
47
|
+
|
|
48
|
+
`DatasetsManager.list({ filters })` now accepts `targetType`, `targetIds` (overlap/union semantics), and `name` (substring, case-insensitive) in addition to the existing tenancy and candidate filters. Filtering is pushed down to the storage layer so callers no longer have to post-filter results.
|
|
49
|
+
|
|
50
|
+
Storage adapters must also be upgraded to the versions listed below to honor the new filters. If a caller is on this version of `@mastra/core` but on an older storage adapter, the new `targetType`/`targetIds`/`name` filter keys are silently ignored by the adapter — no runtime error, but the filter has no effect and every dataset in the tenancy is returned.
|
|
51
|
+
|
|
52
|
+
`Dataset.listItems({ version, search, page, perPage })` now applies `search` and pagination at the storage layer when `version` is provided alongside any of those. Previously they were silently dropped whenever `version` was set. The return shape is unchanged: passing only `version` still returns a bare `DatasetItem[]` snapshot; passing `search`, `page`, or `perPage` (with or without `version`) returns the paginated `{ items, pagination }` shape. The bare-array branch is marked `@deprecated`; prefer passing `page` / `perPage` to always receive the paginated shape.
|
|
53
|
+
|
|
54
|
+
- Tenancy-scope experiments `getById` and `delete*` on `ExperimentsStorage`. ([#18770](https://github.com/mastra-ai/mastra/pull/18770))
|
|
55
|
+
|
|
56
|
+
`ExperimentsStorage.getExperimentById`, `getExperimentResultById`, `deleteExperiment`, and `deleteExperimentResults` used to key on the primary id alone, so any caller who knew the id could read or delete the row regardless of tenant. All four now accept an optional `filters: { organizationId?, projectId? }` argument that is enforced on every adapter (inmemory, libsql, pg, mysql, mongodb, spanner):
|
|
57
|
+
- On tenancy mismatch, `get*` returns `null` at the storage layer.
|
|
58
|
+
- On tenancy mismatch, `delete*` is a silent no-op.
|
|
59
|
+
- The tenancy predicate is folded into the destructive DML itself (scoped `WHERE` on the DELETE, an atomic gate + delete inside a transaction, or a scoped subquery for the results cascade). A concurrent tenant swap of the same id between a pre-check and the DELETE cannot let a scoped delete hit another tenant's row.
|
|
60
|
+
|
|
61
|
+
Both behaviors match how a missing id already responds, so existence does not leak through error timing or messages.
|
|
62
|
+
|
|
63
|
+
The same atomic-DML pattern is also applied to `DatasetsStorage.deleteDataset` across all 5 store adapters, closing a TOCTOU window between the pre-check and the parent DELETE that was introduced when tenancy filters were originally added.
|
|
64
|
+
|
|
65
|
+
`Dataset.getExperiment` and the shared experiment-ownership gate on `Dataset` now forward the dataset's tenancy scope to storage, so experiment reads and downstream mutations (list results, update result, delete experiment) reached through a dataset handle are automatically scoped to the owning tenant.
|
|
66
|
+
|
|
67
|
+
Legacy calls that omit `filters` are unchanged, so this is fully backwards-compatible.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Before: any caller who knew the id could read/delete across tenants.
|
|
71
|
+
await store.experiments.getExperimentById({ id: experimentId });
|
|
72
|
+
await store.experiments.deleteExperiment({ id: experimentId });
|
|
73
|
+
|
|
74
|
+
// After: pass the caller's scope; wrong tenant gets null / silent no-op.
|
|
75
|
+
await store.experiments.getExperimentById({
|
|
76
|
+
id: experimentId,
|
|
77
|
+
filters: { organizationId, projectId },
|
|
78
|
+
});
|
|
79
|
+
await store.experiments.deleteExperiment({
|
|
80
|
+
id: experimentId,
|
|
81
|
+
filters: { organizationId, projectId },
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- Fixed a double-encoding bug where `createDataset` and `updateDataset` stored `targetIds` and `scorerIds` as JSON-encoded strings into the `JSONB` columns instead of arrays. This caused the new `listDatasets({ filters: { targetIds } })` overlap query (`targetIds ?| array[...]`) to never match. ([#18710](https://github.com/mastra-ai/mastra/pull/18710))
|
|
86
|
+
|
|
87
|
+
Existing rows written before this fix are still double-encoded and will not be matched by the new `targetIds` filter. They self-heal on the next `updateDataset` call. Deployments with a long tail of pre-existing datasets should run a one-time backfill (re-encoding `targetIds` and `scorerIds` on affected rows) rather than rely on incidental writes; this can be tracked as a follow-up if needed.
|
|
88
|
+
|
|
89
|
+
- Fixed a cross-tenant data-access issue on datasets by scoping `DatasetsManager.get` and `DatasetsManager.delete` to tenancy filters. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
90
|
+
|
|
91
|
+
Previously `get({ id })` and `delete({ id })` looked up a dataset by its primary key alone. Any caller who knew a dataset id could read or delete it regardless of which `organizationId` / `projectId` it belonged to. This is now closed at the storage layer via a scoped SQL predicate (option (a) — no fetch-then-assert).
|
|
92
|
+
|
|
93
|
+
**What changed**
|
|
94
|
+
- `DatasetsManager.get` and `DatasetsManager.delete` accept optional `organizationId` and `projectId`.
|
|
95
|
+
- The tenancy is stashed on the returned `Dataset` handle and forwarded to every downstream storage call (`getDetails`, `update`, `addItem`, item batch ops, `startExperimentAsync`).
|
|
96
|
+
- The abstract storage contract (`getDatasetById`, `deleteDataset`) gained an optional `filters?: DatasetTenancyFilters` arg.
|
|
97
|
+
- Item-mutation inputs (`AddDatasetItemInput`, `UpdateDatasetItemInput`, `BatchInsertItemsInput`, `BatchDeleteItemsInput`) and `UpdateDatasetInput` accept optional `filters` for the internal existence check.
|
|
98
|
+
|
|
99
|
+
**Behavior**
|
|
100
|
+
- Omitting tenancy preserves the existing behavior (no predicate added) — fully backwards compatible.
|
|
101
|
+
- On tenancy mismatch, `get` throws NOT_FOUND (returns null at the storage layer) and `delete` is a silent no-op — matching how a missing id already behaves, so existence does not leak through error timing or messages.
|
|
102
|
+
|
|
103
|
+
**Example**
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// Before
|
|
107
|
+
const ds = await mastra.datasets.get({ id });
|
|
108
|
+
await mastra.datasets.delete({ id });
|
|
109
|
+
|
|
110
|
+
// After — scope to a tenant
|
|
111
|
+
const ds = await mastra.datasets.get({ id, organizationId, projectId });
|
|
112
|
+
await mastra.datasets.delete({ id, organizationId, projectId });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- Add optional `batchId`, `datasetId`, and `datasetItemId` fields to persisted scores so saved baseline scores can be grouped as one scoring pass and joined back to the dataset items they came from. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
|
|
116
|
+
- `scoreTrace()` accepts top-level `batchId`, `datasetId`, and `datasetItemId` when persisting a score for a stored trace.
|
|
117
|
+
- `ScoreRowData` and score save payloads now include nullable `batchId`, `datasetId`, and `datasetItemId`.
|
|
118
|
+
- Built-in stores with explicit score schema or attribute mappings now persist these provenance fields on saved scores.
|
|
119
|
+
- D1, DSQL, MSSQL, and Upstash score stores now apply additive provenance migrations or deterministic score ordering for persisted score reads.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
await scoreTrace({
|
|
123
|
+
storage,
|
|
124
|
+
scorer,
|
|
125
|
+
target: { traceId },
|
|
126
|
+
batchId: 'baseline-batch-1',
|
|
127
|
+
datasetId,
|
|
128
|
+
datasetItemId,
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- Added multi-tenant scoping to stored scorer definitions. Stored scorers now persist optional `organizationId` and `projectId` on the definition record, and `list`/`listResolved` accept matching filters to scope results by tenant. The Postgres adapter backfills the new columns and applies the scoped filters; tenancy lives on the record while version snapshots stay pure config. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
await storage.create({
|
|
136
|
+
scorerDefinition: { id, organizationId: 'org-a', projectId: 'proj-1', ...config },
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const { scorerDefinitions } = await storage.list({
|
|
140
|
+
status: 'draft',
|
|
141
|
+
organizationId: 'org-a',
|
|
142
|
+
projectId: 'proj-1',
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- Added optional `organizationId` and `projectId` fields to scores for multi-tenant isolation. Scores can now be saved with tenancy metadata and the `listScoresBy*` methods accept a `filters` option to scope results by organization and project. ([#18331](https://github.com/mastra-ai/mastra/pull/18331))
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
await storage.saveScore({ ...score, organizationId: 'org-a', projectId: 'proj-1' });
|
|
150
|
+
|
|
151
|
+
const result = await storage.listScoresByScorerId({
|
|
152
|
+
scorerId,
|
|
153
|
+
filters: { organizationId: 'org-a', projectId: 'proj-1' },
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`projectId` identifies the project scope, separate from `resourceId` which continues to mean the agent memory resource.
|
|
158
|
+
|
|
159
|
+
- Raise `@mastra/core` peer floor to `>=1.49.0-0` on all storage adapters so the tenancy-related named exports the adapters now consume are guaranteed to exist at install time. ([#18861](https://github.com/mastra-ai/mastra/pull/18861))
|
|
160
|
+
|
|
161
|
+
- Scoped `getDatasetById` and `deleteDataset` to tenancy filters when the caller passes `organizationId` / `projectId`. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
162
|
+
|
|
163
|
+
The adapters now push the tenancy predicate into the SQL/query when the new optional `filters` argument is present. Legacy calls that omit tenancy are unchanged. On mismatch, `getDatasetById` returns `null` and `deleteDataset` is a silent no-op — the cascade delete (dataset items and versions) is gated by a scoped parent pre-check, so cross-tenant data is never touched.
|
|
164
|
+
|
|
165
|
+
- Added optional `organizationId` and `projectId` query parameters to the dataset routes. ([#18750](https://github.com/mastra-ai/mastra/pull/18750))
|
|
166
|
+
|
|
167
|
+
`GET /datasets/:datasetId`, `PATCH /datasets/:datasetId`, and `DELETE /datasets/:datasetId` now accept optional tenancy query parameters. When provided, they are forwarded to `mastra.datasets.get` / `.delete` and the operation returns 404 if the dataset does not belong to the requested tenant. Requests that omit the query parameters keep their existing behavior.
|
|
168
|
+
|
|
169
|
+
**Example**
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
GET /datasets/abc123?organizationId=org_a&projectId=proj_1
|
|
173
|
+
DELETE /datasets/abc123?organizationId=org_a
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- Updated dependencies [[`700619b`](https://github.com/mastra-ai/mastra/commit/700619b61d572e592cbaaf758121d168844ca4d2), [`0f69865`](https://github.com/mastra-ai/mastra/commit/0f69865aced225d98eac812e22699dc445ee18cb), [`9250acd`](https://github.com/mastra-ai/mastra/commit/9250acd1357f0f1f33d0dcca16f9655084c58eca), [`0c3d4bc`](https://github.com/mastra-ai/mastra/commit/0c3d4bcae13ea3699d379403e6f350d5cf4efe9f), [`cc440a3`](https://github.com/mastra-ai/mastra/commit/cc440a39400d8ce06655462b26c1666a1b3d4320), [`6a61846`](https://github.com/mastra-ai/mastra/commit/6a61846eeda29fb714549b70f1bee2bf6b141c44), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`17369b2`](https://github.com/mastra-ai/mastra/commit/17369b25250561e9ed994ae509be1d15bfb33bcb), [`c64c2a8`](https://github.com/mastra-ai/mastra/commit/c64c2a8503a50252f9ca6b8e8c54cadee31b92a2), [`bcae929`](https://github.com/mastra-ai/mastra/commit/bcae929945cbf265bd9f327cc715ecafa072b5b9), [`ea6327b`](https://github.com/mastra-ai/mastra/commit/ea6327ba2d63ca647804bc97b347e03a58617162), [`3439fa8`](https://github.com/mastra-ai/mastra/commit/3439fa836ecfcaa257b40c20b30ac2a8be22e9ea), [`85107f2`](https://github.com/mastra-ai/mastra/commit/85107f2758b527147fccbedff962961927c2d3b8), [`b33822e`](https://github.com/mastra-ai/mastra/commit/b33822e8d470884954b02f7b0745407ee4ef74b1), [`06e2680`](https://github.com/mastra-ai/mastra/commit/06e26806b51d2cbd858afdc66daa2b86ff3ba64a), [`06ff9e0`](https://github.com/mastra-ai/mastra/commit/06ff9e0befd1d642ab87ff749285ee4091205c7e), [`d5c11e3`](https://github.com/mastra-ai/mastra/commit/d5c11e3ba5045969caa7272a7bd1fd141c93ab6c), [`7f5e1ff`](https://github.com/mastra-ai/mastra/commit/7f5e1ff695a92f672bb3976363925d1e9136b54a), [`ff80671`](https://github.com/mastra-ai/mastra/commit/ff8067185e208b27198b4e5b71803013175c3643), [`b8375c1`](https://github.com/mastra-ai/mastra/commit/b8375c1f8fe905df8ae2ae9a893bb365f17aec4e), [`dab1257`](https://github.com/mastra-ai/mastra/commit/dab1257b64e4ed576dc5038bb7a3f7072338bc9f), [`1240f05`](https://github.com/mastra-ai/mastra/commit/1240f051c8e5371f1c014448bf37b1a1b9a05e47), [`705ff39`](https://github.com/mastra-ai/mastra/commit/705ff3969e57214ff2fdaf3815d751dd558886ed), [`e6fbd5b`](https://github.com/mastra-ai/mastra/commit/e6fbd5bfdc28e92c0c0433f29aa1bc152d3430f6), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`6f2026c`](https://github.com/mastra-ai/mastra/commit/6f2026cdf114ff1e21e49133ca774ec7d5085059), [`24c10d3`](https://github.com/mastra-ai/mastra/commit/24c10d333e6649ac06075903aeeee13a933db3b3), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`215f9b0`](https://github.com/mastra-ai/mastra/commit/215f9b0f3f3f6fc165edad360582dd4d3d7ea748), [`003f35d`](https://github.com/mastra-ai/mastra/commit/003f35d19e07b23b4bacc591c8bc0c59b42124ae), [`f890eda`](https://github.com/mastra-ai/mastra/commit/f890eda2c8a2ae83d9b30bc6d85842f93b6c266b), [`1340fb7`](https://github.com/mastra-ai/mastra/commit/1340fb76262a3ca062130aa71859f07257a0a5a4)]:
|
|
177
|
+
- @mastra/core@1.49.0
|
|
178
|
+
|
|
179
|
+
## 1.15.0-alpha.2
|
|
180
|
+
|
|
181
|
+
### Patch Changes
|
|
182
|
+
|
|
183
|
+
- Raise `@mastra/core` peer floor to `>=1.49.0-0` on all storage adapters so the tenancy-related named exports the adapters now consume are guaranteed to exist at install time. ([#18861](https://github.com/mastra-ai/mastra/pull/18861))
|
|
184
|
+
|
|
3
185
|
## 1.15.0-alpha.1
|
|
4
186
|
|
|
5
187
|
### Patch Changes
|
package/dist/docs/SKILL.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/pg",
|
|
3
|
-
"version": "1.15.0
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -35,13 +35,13 @@
|
|
|
35
35
|
"tsx": "^4.22.4",
|
|
36
36
|
"typescript": "^6.0.3",
|
|
37
37
|
"vitest": "4.1.8",
|
|
38
|
-
"@internal/
|
|
39
|
-
"@
|
|
40
|
-
"@internal/
|
|
41
|
-
"@
|
|
38
|
+
"@internal/lint": "0.0.111",
|
|
39
|
+
"@internal/storage-test-utils": "0.0.107",
|
|
40
|
+
"@internal/types-builder": "0.0.86",
|
|
41
|
+
"@mastra/core": "1.49.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@mastra/core": ">=1.
|
|
44
|
+
"@mastra/core": ">=1.49.0-0 <2.0.0-0"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"dist",
|