@mastra/pg 1.20.0-alpha.1 → 1.20.0-alpha.3
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 +48 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +2 -1
- package/dist/docs/references/docs-memory-working-memory.md +1 -0
- package/dist/docs/references/docs-storage-overview.md +1 -0
- package/dist/docs/references/guides-rag-overview.md +1 -1
- package/dist/docs/references/guides-rag-retrieval.md +17 -0
- package/dist/docs/references/guides-rag-vector-databases.md +41 -0
- package/dist/docs/references/reference-rag-metadata-filters.md +13 -4
- package/dist/docs/references/reference-storage-retention.md +1 -1
- package/dist/index.cjs +207 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +208 -17
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +8 -2
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/thread-state/index.d.ts +42 -0
- package/dist/storage/domains/thread-state/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +2 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @mastra/pg
|
|
2
2
|
|
|
3
|
+
## 1.20.0-alpha.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed generated thread titles being clobbered during a turn ([#21041](https://github.com/mastra-ai/mastra/pull/21041))
|
|
8
|
+
|
|
9
|
+
`updateThread` required both `title` and `metadata`, so callers that only needed to
|
|
10
|
+
change metadata (message persistence, working memory, observational memory, channel
|
|
11
|
+
subscriptions) had to read the thread and pass its title back. When title generation
|
|
12
|
+
finished between that read and the write, the freshly generated title was overwritten
|
|
13
|
+
with the stale one.
|
|
14
|
+
|
|
15
|
+
`title` and `metadata` are now independently optional: omitting one leaves that column
|
|
16
|
+
untouched. Callers that only change metadata no longer send a title, and message
|
|
17
|
+
persistence no longer rewrites a thread row it just read.
|
|
18
|
+
|
|
19
|
+
- Updated dependencies [[`1c75e32`](https://github.com/mastra-ai/mastra/commit/1c75e32f7fc0b9fb6f548b4407feaec8a1440212), [`c47165c`](https://github.com/mastra-ai/mastra/commit/c47165c983c87594c6952f1fd2fa51a90205034c), [`e08e789`](https://github.com/mastra-ai/mastra/commit/e08e789c1bf4cd2fe46363f7a4728536ceccc9bd), [`35cc901`](https://github.com/mastra-ai/mastra/commit/35cc90102cf834a84827acaf9eee0b6d6d1e2a3b), [`a8b4cf0`](https://github.com/mastra-ai/mastra/commit/a8b4cf02823cffebc4751a53337dfacf097c1ae1), [`f33264f`](https://github.com/mastra-ai/mastra/commit/f33264f517ae603279afd5c4251e2b40f6dd3618), [`689f2c4`](https://github.com/mastra-ai/mastra/commit/689f2c4b6c0835fe455702b01d21daa8abcd9331), [`eeae63e`](https://github.com/mastra-ai/mastra/commit/eeae63e7fbe8e1f237adc69bca6e2ac13c5ca907), [`4c186a0`](https://github.com/mastra-ai/mastra/commit/4c186a017275f45e6ed4c09de0f89550e2d09e8c), [`b0fa077`](https://github.com/mastra-ai/mastra/commit/b0fa077bcbc9b08551846fe372a0d3d15b71ed72)]:
|
|
20
|
+
- @mastra/core@1.58.0-alpha.8
|
|
21
|
+
|
|
22
|
+
## 1.20.0-alpha.2
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Fixed `include` in `listMessages` and `listMessagesByResourceId` so it can no longer return a message that belongs to a different resource. When you pass a `resourceId`, the target message and its surrounding context messages now stay inside that resource. Includes that cross threads inside the same resource keep working, so semantic recall with `scope: 'resource'` is unchanged. ([#20984](https://github.com/mastra-ai/mastra/pull/20984))
|
|
27
|
+
|
|
28
|
+
**Behaviour change in the in-memory store**
|
|
29
|
+
|
|
30
|
+
The in-memory store read the context window from the thread you queried. It now reads the window from the thread that owns the target message, which is what the SQL stores already did. This only changes the result when an `include` entry names a message from another thread.
|
|
31
|
+
|
|
32
|
+
The in-memory store also ignored `include` in `listMessagesByResourceId`. It now returns the included messages, like `@mastra/libsql` and `@mastra/pg` do.
|
|
33
|
+
|
|
34
|
+
Fixes #20604.
|
|
35
|
+
|
|
36
|
+
- Add a persistent `threadState` domain to `PostgresStore` ([#20994](https://github.com/mastra-ai/mastra/pull/20994))
|
|
37
|
+
|
|
38
|
+
`PostgresStore` did not register a `threadState` domain, so durable task and goal
|
|
39
|
+
state fell back to in-memory storage and was lost on restart. Anything relying on
|
|
40
|
+
thread state (task tools, long-running agent goals) could not be backed by Postgres
|
|
41
|
+
alone, and required composing a second adapter through `MastraCompositeStore`.
|
|
42
|
+
|
|
43
|
+
`ThreadStatePG` now stores state as JSONB keyed by `(threadId, type)` with an atomic
|
|
44
|
+
upsert that preserves `createdAt`, participates in `exportSchemas()` and `init()`, and
|
|
45
|
+
supports retention pruning anchored on `updatedAtZ` so state for still-active threads
|
|
46
|
+
survives age-based pruning.
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [[`6445eba`](https://github.com/mastra-ai/mastra/commit/6445eba6020abac681aba1cc9289f446cb400cbe), [`df31eb0`](https://github.com/mastra-ai/mastra/commit/df31eb0c7087d782a0d9346e467f9a4af4b0eef6), [`fcd0667`](https://github.com/mastra-ai/mastra/commit/fcd0667a4e378be35c9a1b1eb19cce78fbfd7282), [`bab06b1`](https://github.com/mastra-ai/mastra/commit/bab06b18923873a584bdfc71a6b4ec7fb4727fb7)]:
|
|
49
|
+
- @mastra/core@1.58.0-alpha.5
|
|
50
|
+
|
|
3
51
|
## 1.20.0-alpha.1
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -148,6 +148,7 @@ Each vector store page below includes installation instructions, configuration p
|
|
|
148
148
|
- [libSQL](https://mastra.ai/reference/vectors/libsql)
|
|
149
149
|
- [MongoDB](https://mastra.ai/reference/vectors/mongodb)
|
|
150
150
|
- [OpenSearch](https://mastra.ai/reference/vectors/opensearch)
|
|
151
|
+
- [OracleDB](https://mastra.ai/reference/vectors/oracledb)
|
|
151
152
|
- [Pinecone](https://mastra.ai/reference/vectors/pinecone)
|
|
152
153
|
- [PostgreSQL](https://mastra.ai/reference/vectors/pg)
|
|
153
154
|
- [Qdrant](https://mastra.ai/reference/vectors/qdrant)
|
|
@@ -180,7 +181,7 @@ const agent = new Agent({
|
|
|
180
181
|
})
|
|
181
182
|
```
|
|
182
183
|
|
|
183
|
-
> **Note:** `scope: 'resource'` is supported by the LibSQL, PostgreSQL, MongoDB, and Upstash storage adapters.
|
|
184
|
+
> **Note:** `scope: 'resource'` is supported by the LibSQL, OracleDB, PostgreSQL, MongoDB, and Upstash storage adapters.
|
|
184
185
|
|
|
185
186
|
### Metadata filtering
|
|
186
187
|
|
|
@@ -129,6 +129,7 @@ Resource-scoped working memory requires specific storage adapters that support t
|
|
|
129
129
|
|
|
130
130
|
- **libSQL** (`@mastra/libsql`)
|
|
131
131
|
- **PostgreSQL** (`@mastra/pg`)
|
|
132
|
+
- **OracleDB** (`@mastra/oracledb`)
|
|
132
133
|
- **Upstash** (`@mastra/upstash`)
|
|
133
134
|
- **MongoDB** (`@mastra/mongodb`)
|
|
134
135
|
|
|
@@ -193,6 +193,7 @@ Each provider page includes installation instructions, configuration parameters,
|
|
|
193
193
|
- [libSQL](https://mastra.ai/reference/storage/libsql)
|
|
194
194
|
- [PostgreSQL](https://mastra.ai/reference/storage/postgresql)
|
|
195
195
|
- [MongoDB](https://mastra.ai/reference/storage/mongodb)
|
|
196
|
+
- [OracleDB](https://mastra.ai/reference/storage/oracledb)
|
|
196
197
|
- [Upstash](https://mastra.ai/reference/storage/upstash)
|
|
197
198
|
- [Redis](https://mastra.ai/reference/storage/redis)
|
|
198
199
|
- [Cloudflare D1](https://mastra.ai/reference/storage/cloudflare-d1)
|
|
@@ -67,7 +67,7 @@ The basic building block of RAG is document processing. Documents can be chunked
|
|
|
67
67
|
|
|
68
68
|
## Vector storage
|
|
69
69
|
|
|
70
|
-
Mastra supports multiple vector stores for embedding persistence and similarity search, including pgvector, Pinecone, Qdrant, and MongoDB. See the [vector database doc](https://mastra.ai/guides/rag/vector-databases).
|
|
70
|
+
Mastra supports multiple vector stores for embedding persistence and similarity search, including pgvector, OracleDB, Pinecone, Qdrant, and MongoDB. See the [vector database doc](https://mastra.ai/guides/rag/vector-databases).
|
|
71
71
|
|
|
72
72
|
## More resources
|
|
73
73
|
|
|
@@ -436,6 +436,23 @@ export const ragAgent = new Agent({
|
|
|
436
436
|
})
|
|
437
437
|
```
|
|
438
438
|
|
|
439
|
+
**OracleDB**:
|
|
440
|
+
|
|
441
|
+
```ts
|
|
442
|
+
import { ORACLEDB_PROMPT } from '@mastra/oracledb'
|
|
443
|
+
|
|
444
|
+
export const ragAgent = new Agent({
|
|
445
|
+
id: 'rag-agent',
|
|
446
|
+
name: 'RAG Agent',
|
|
447
|
+
model: 'openai/gpt-5.6-sol',
|
|
448
|
+
instructions: `
|
|
449
|
+
Process queries using the provided context. Structure responses to be concise and relevant.
|
|
450
|
+
${ORACLEDB_PROMPT}
|
|
451
|
+
`,
|
|
452
|
+
tools: { vectorQueryTool },
|
|
453
|
+
})
|
|
454
|
+
```
|
|
455
|
+
|
|
439
456
|
**S3Vectors**:
|
|
440
457
|
|
|
441
458
|
```ts
|
|
@@ -78,6 +78,35 @@ await store.upsert({
|
|
|
78
78
|
|
|
79
79
|
PostgreSQL with the pgvector extension is a good solution for teams already using PostgreSQL who want to minimize infrastructure complexity. For detailed setup instructions and best practices, see the [official pgvector repository](https://github.com/pgvector/pgvector).
|
|
80
80
|
|
|
81
|
+
**OracleDB**:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { OracleVector } from '@mastra/oracledb'
|
|
85
|
+
|
|
86
|
+
const store = new OracleVector({
|
|
87
|
+
id: 'oracle-vector',
|
|
88
|
+
user: process.env.ORACLE_DATABASE_USER,
|
|
89
|
+
password: process.env.ORACLE_DATABASE_PASSWORD,
|
|
90
|
+
connectString: process.env.ORACLE_DATABASE_CONNECT_STRING,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
await store.createIndex({
|
|
94
|
+
indexName: 'myCollection',
|
|
95
|
+
dimension: 1536,
|
|
96
|
+
indexConfig: { type: 'none' },
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
await store.upsert({
|
|
100
|
+
indexName: 'myCollection',
|
|
101
|
+
vectors: embeddings,
|
|
102
|
+
metadata: chunks.map(chunk => ({ text: chunk.text })),
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Using Oracle Database Vector Search
|
|
107
|
+
|
|
108
|
+
OracleDB stores embeddings in native `VECTOR` columns and metadata in Oracle JSON. Exact search is the default; HNSW and IVF indexes can be configured for tuned deployments.
|
|
109
|
+
|
|
81
110
|
**Pinecone**:
|
|
82
111
|
|
|
83
112
|
```ts
|
|
@@ -410,6 +439,18 @@ Index names must:
|
|
|
410
439
|
- Example: `my_index_123` is valid
|
|
411
440
|
- Example: `my-index` is not valid (contains hyphen)
|
|
412
441
|
|
|
442
|
+
**OracleDB**:
|
|
443
|
+
|
|
444
|
+
Index names are logical Mastra names. OracleDB maps each logical index to a physical Oracle table internally.
|
|
445
|
+
|
|
446
|
+
Logical index names must:
|
|
447
|
+
|
|
448
|
+
- Be non-empty
|
|
449
|
+
- Be 512 characters or fewer
|
|
450
|
+
- Be stable for the lifetime of the vector index
|
|
451
|
+
- Example: `my_collection_123` is valid
|
|
452
|
+
- Example: `customer-support/docs:v1` is valid and is mapped to a safe Oracle table name
|
|
453
|
+
|
|
413
454
|
**Pinecone**:
|
|
414
455
|
|
|
415
456
|
Index names must:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Metadata filters
|
|
4
4
|
|
|
5
|
-
Mastra provides a unified metadata filtering syntax across all vector stores, based on MongoDB/Sift query syntax. Each vector store translates these filters into
|
|
5
|
+
Mastra provides a unified metadata filtering syntax across all vector stores, based on MongoDB/Sift query syntax. Each vector store translates these filters into its native query format. For example, PgVector uses PostgreSQL JSONB predicates, while OracleDB stores metadata as Oracle JSON and compiles filters to `JSON_VALUE`, `JSON_EXISTS`, `REGEXP_LIKE`, and `LIKE` predicates with bound values.
|
|
6
6
|
|
|
7
7
|
## Basic example
|
|
8
8
|
|
|
@@ -34,11 +34,11 @@ const results = await store.query({
|
|
|
34
34
|
|
|
35
35
|
### Array Operators
|
|
36
36
|
|
|
37
|
-
`$in`Matches any value in array{ category: { $in: \["A", "B"] } }Supported by: All except Couchbase`$nin`Matches none of the values{ status: { $nin: \["deleted", "archived"] } }Supported by: All except Couchbase`$all`Matches arrays containing all elements{ tags: { $all: \["urgent", "high"] } }Supported by: Astra, Pinecone, Upstash, MongoDB`$elemMatch`Matches array elements meeting criteria{ scores: { $elemMatch: { $gt: 80 } } }Supported by: libSQL, PgVector, MongoDB
|
|
37
|
+
`$in`Matches any value in array{ category: { $in: \["A", "B"] } }Supported by: All except Couchbase`$nin`Matches none of the values{ status: { $nin: \["deleted", "archived"] } }Supported by: All except Couchbase`$all`Matches arrays containing all elements{ tags: { $all: \["urgent", "high"] } }Supported by: Astra, Pinecone, Upstash, MongoDB, OracleDB`$elemMatch`Matches array elements meeting criteria{ scores: { $elemMatch: { $gt: 80 } } }Supported by: libSQL, PgVector, MongoDB, OracleDB
|
|
38
38
|
|
|
39
39
|
### Logical Operators
|
|
40
40
|
|
|
41
|
-
`$and`Logical AND{ $and: \[{ price: { $gt: 100 } }, { stock: { $gt: 0 } }] }Supported by: All except Vectorize, Couchbase`$or`Logical OR{ $or: \[{ status: "active" }, { priority: "high" }] }Supported by: All except Vectorize, Couchbase`$not`Logical NOT{ price: { $not: { $lt: 100 } } }Supported by: Astra, Qdrant, Upstash, PgVector, libSQL, MongoDB`$nor`Logical NOR{ $nor: \[{ status: "deleted" }, { archived: true }] }Supported by: Qdrant, Upstash, PgVector, libSQL, MongoDB
|
|
41
|
+
`$and`Logical AND{ $and: \[{ price: { $gt: 100 } }, { stock: { $gt: 0 } }] }Supported by: All except Vectorize, Couchbase`$or`Logical OR{ $or: \[{ status: "active" }, { priority: "high" }] }Supported by: All except Vectorize, Couchbase`$not`Logical NOT{ price: { $not: { $lt: 100 } } }Supported by: Astra, Qdrant, Upstash, PgVector, libSQL, MongoDB, OracleDB`$nor`Logical NOR{ $nor: \[{ status: "deleted" }, { archived: true }] }Supported by: Qdrant, Upstash, PgVector, libSQL, MongoDB, OracleDB
|
|
42
42
|
|
|
43
43
|
### Element Operators
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ const results = await store.query({
|
|
|
46
46
|
|
|
47
47
|
### Custom Operators
|
|
48
48
|
|
|
49
|
-
`$contains`Text contains substring{ description: { $contains: "sale" } }Supported by: Upstash, libSQL, PgVector`$regex`Regular expression match{ name: { $regex: "^test" } }Supported by: Qdrant, PgVector, Upstash, MongoDB`$size`Array length check{ tags: { $size:
|
|
49
|
+
`$contains`Text contains substring{ description: { $contains: "sale" } }Supported by: Upstash, libSQL, PgVector, OracleDB`$regex`Regular expression match{ name: { $regex: "^test" } }Supported by: Qdrant, PgVector, Upstash, MongoDB, OracleDB`$size`Array length check{ tags: { $size: 3 } }Supported by: Astra, libSQL, PgVector, MongoDB, OracleDB`$geo`Geospatial query{ location: { $geo: { type: "radius", ... } } }Supported by: Qdrant`$datetime`Datetime range query{ created: { $datetime: { range: { gt: "2024-01-01" } } } }Supported by: Qdrant`$hasId`Vector ID existence check{ $hasId: \["id1", "id2"] }Supported by: Qdrant`$hasVector`Vector existence check{ $hasVector: true }Supported by: Qdrant
|
|
50
50
|
|
|
51
51
|
## Common rules and restrictions
|
|
52
52
|
|
|
@@ -124,6 +124,14 @@ const results = await store.query({
|
|
|
124
124
|
- Empty arrays in conditions are handled gracefully
|
|
125
125
|
- Metadata is stored in a JSONB column for efficient querying
|
|
126
126
|
|
|
127
|
+
### OracleDB
|
|
128
|
+
|
|
129
|
+
- Metadata is stored as Oracle JSON alongside each `VECTOR` row
|
|
130
|
+
- Scalar comparisons use `JSON_VALUE`, while array, existence, and element-match checks use `JSON_EXISTS`
|
|
131
|
+
- `$regex` uses Oracle `REGEXP_LIKE`; string `$contains` uses case-insensitive `LIKE`
|
|
132
|
+
- Nested fields are supported with dot notation and are converted to quoted Oracle JSON paths
|
|
133
|
+
- User-provided metadata values are bound as parameters instead of interpolated into SQL
|
|
134
|
+
|
|
127
135
|
### PgVector
|
|
128
136
|
|
|
129
137
|
- Full support for PostgreSQL's native JSON querying capabilities
|
|
@@ -211,6 +219,7 @@ const results = await store.query({
|
|
|
211
219
|
- [Cloudflare Vectorize](https://mastra.ai/reference/vectors/vectorize)
|
|
212
220
|
- [libSQL](https://mastra.ai/reference/vectors/libsql)
|
|
213
221
|
- [MongoDB](https://mastra.ai/reference/vectors/mongodb)
|
|
222
|
+
- [OracleDB](https://mastra.ai/reference/vectors/oracledb)
|
|
214
223
|
- [PgStore](https://mastra.ai/reference/vectors/pg)
|
|
215
224
|
- [Pinecone](https://mastra.ai/reference/vectors/pinecone)
|
|
216
225
|
- [Qdrant](https://mastra.ai/reference/vectors/qdrant)
|
|
@@ -92,7 +92,7 @@ Each domain declares which of its tables can be age-pruned and which timestamp c
|
|
|
92
92
|
> - Experiments prune as whole units: an aged experiment's result rows are deleted together with it (results cascade with their parent), so a run is never left partially deleted. Retention doesn't have a separate `results` key.
|
|
93
93
|
> - For `schedules`, the growth table is the fire history (`schedule_triggers`, one row per fire): schedule definitions are config and aren't pruned.
|
|
94
94
|
> - On PostgreSQL, timestamp anchors use the timezone-aware mirror columns (for example `createdAtZ`, `completedAtZ`).
|
|
95
|
-
> - LibSQL
|
|
95
|
+
> - LibSQL and PostgreSQL support all domains above except `harness`, which PostgreSQL doesn't implement. MongoDB supports all except `threadState` and `harness`.
|
|
96
96
|
> - The v-next PostgreSQL observability domain stores signal events in day-partitioned tables (`spans`, `metrics`, `logs`, `scores`, `feedback`). For it, `prune()` drops whole day partitions (or TimescaleDB chunks) that are entirely older than the cutoff instead of deleting rows: effective level of detail is one day, and a partition is only dropped once its entire day is past `maxAge`. `PruneResult.deleted` reports the number of rows in the dropped partitions.
|
|
97
97
|
|
|
98
98
|
## Methods
|
package/dist/index.cjs
CHANGED
|
@@ -9200,7 +9200,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9200
9200
|
text: `Thread ${id} not found`,
|
|
9201
9201
|
details: {
|
|
9202
9202
|
threadId: id,
|
|
9203
|
-
title
|
|
9203
|
+
title: title ?? null
|
|
9204
9204
|
}
|
|
9205
9205
|
});
|
|
9206
9206
|
const mergedMetadata = {
|
|
@@ -9211,14 +9211,14 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9211
9211
|
const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
|
|
9212
9212
|
const thread = await this.#db.client.one(`UPDATE ${threadTableName}
|
|
9213
9213
|
SET
|
|
9214
|
-
title = $1,
|
|
9214
|
+
title = COALESCE($1, title),
|
|
9215
9215
|
metadata = $2,
|
|
9216
9216
|
"updatedAt" = $3,
|
|
9217
9217
|
"updatedAtZ" = $4
|
|
9218
9218
|
WHERE id = $5
|
|
9219
9219
|
RETURNING *
|
|
9220
9220
|
`, [
|
|
9221
|
-
title,
|
|
9221
|
+
title ?? null,
|
|
9222
9222
|
mergedMetadata,
|
|
9223
9223
|
nowStr,
|
|
9224
9224
|
nowStr,
|
|
@@ -9239,7 +9239,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9239
9239
|
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
9240
9240
|
details: {
|
|
9241
9241
|
threadId: id,
|
|
9242
|
-
title
|
|
9242
|
+
title: title ?? null
|
|
9243
9243
|
}
|
|
9244
9244
|
}, error);
|
|
9245
9245
|
}
|
|
@@ -9307,7 +9307,13 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9307
9307
|
return direction === "ASC" ? String(aValue).localeCompare(String(bValue)) : String(bValue).localeCompare(String(aValue));
|
|
9308
9308
|
});
|
|
9309
9309
|
}
|
|
9310
|
-
|
|
9310
|
+
/**
|
|
9311
|
+
* Fetches included messages by ID, discovering their thread automatically.
|
|
9312
|
+
* This handles cross-thread includes where the include item doesn't specify a threadId.
|
|
9313
|
+
* When a resourceId is given, both the target lookup and the surrounding window stay
|
|
9314
|
+
* inside that resource, so an include never leaks another resource's messages.
|
|
9315
|
+
*/
|
|
9316
|
+
async _getIncludedMessages({ include, resourceId }) {
|
|
9311
9317
|
if (!include || include.length === 0) return null;
|
|
9312
9318
|
const tableName = getTableName$3({
|
|
9313
9319
|
indexName: _mastra_core_storage.TABLE_MESSAGES,
|
|
@@ -9317,7 +9323,8 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9317
9323
|
const targetIds = include.map((inc) => inc.id).filter(Boolean);
|
|
9318
9324
|
if (targetIds.length === 0) return null;
|
|
9319
9325
|
const idPlaceholders = targetIds.map((_, i) => "$" + (i + 1)).join(", ");
|
|
9320
|
-
const
|
|
9326
|
+
const targetResourceCondition = resourceId ? ` AND "resourceId" = $${targetIds.length + 1}` : "";
|
|
9327
|
+
const targetRows = await this.#db.client.manyOrNone(`SELECT id, thread_id, "createdAt" FROM ${tableName} WHERE id IN (${idPlaceholders})${targetResourceCondition}`, resourceId ? [...targetIds, resourceId] : targetIds);
|
|
9321
9328
|
if (targetRows.length === 0) return null;
|
|
9322
9329
|
const targetMap = new Map(targetRows.map((r) => [r.id, {
|
|
9323
9330
|
threadId: r.thread_id,
|
|
@@ -9325,7 +9332,12 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9325
9332
|
}]));
|
|
9326
9333
|
const unionQueries = [];
|
|
9327
9334
|
const params = [];
|
|
9328
|
-
let
|
|
9335
|
+
let resourceCondition = "";
|
|
9336
|
+
if (resourceId) {
|
|
9337
|
+
params.push(resourceId);
|
|
9338
|
+
resourceCondition = ` AND m."resourceId" = $1`;
|
|
9339
|
+
}
|
|
9340
|
+
let paramIdx = params.length + 1;
|
|
9329
9341
|
for (const inc of include) {
|
|
9330
9342
|
const { id, withPreviousMessages = 0, withNextMessages = 0 } = inc;
|
|
9331
9343
|
const target = targetMap.get(id);
|
|
@@ -9337,7 +9349,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9337
9349
|
SELECT ${selectColumns}
|
|
9338
9350
|
FROM ${tableName} m
|
|
9339
9351
|
WHERE m.thread_id = ${p1}
|
|
9340
|
-
AND m."createdAt" <= ${p2}
|
|
9352
|
+
AND m."createdAt" <= ${p2}${resourceCondition}
|
|
9341
9353
|
ORDER BY m."createdAt" DESC, m.id DESC
|
|
9342
9354
|
LIMIT ${p3}
|
|
9343
9355
|
)`);
|
|
@@ -9351,7 +9363,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9351
9363
|
SELECT ${selectColumns}
|
|
9352
9364
|
FROM ${tableName} m
|
|
9353
9365
|
WHERE m.thread_id = ${p4}
|
|
9354
|
-
AND m."createdAt" > ${p5}
|
|
9366
|
+
AND m."createdAt" > ${p5}${resourceCondition}
|
|
9355
9367
|
ORDER BY m."createdAt" ASC, m.id ASC
|
|
9356
9368
|
LIMIT ${p6}
|
|
9357
9369
|
)`);
|
|
@@ -9501,7 +9513,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9501
9513
|
hasMore: false
|
|
9502
9514
|
};
|
|
9503
9515
|
if (perPage === 0 && include && include.length > 0) {
|
|
9504
|
-
const includeMessages = await this._getIncludedMessages({
|
|
9516
|
+
const includeMessages = await this._getIncludedMessages({
|
|
9517
|
+
include,
|
|
9518
|
+
resourceId
|
|
9519
|
+
});
|
|
9505
9520
|
if (!includeMessages || includeMessages.length === 0) return {
|
|
9506
9521
|
messages: [],
|
|
9507
9522
|
total: 0,
|
|
@@ -9520,7 +9535,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9520
9535
|
};
|
|
9521
9536
|
}
|
|
9522
9537
|
let includeFailure;
|
|
9523
|
-
const includePromise = include && include.length > 0 ? this._getIncludedMessages({
|
|
9538
|
+
const includePromise = include && include.length > 0 ? this._getIncludedMessages({
|
|
9539
|
+
include,
|
|
9540
|
+
resourceId
|
|
9541
|
+
}).catch((error) => {
|
|
9524
9542
|
includeFailure = error;
|
|
9525
9543
|
return null;
|
|
9526
9544
|
}) : null;
|
|
@@ -9641,7 +9659,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9641
9659
|
hasMore: false
|
|
9642
9660
|
};
|
|
9643
9661
|
if (perPage === 0 && include && include.length > 0) {
|
|
9644
|
-
const includeMessages = await this._getIncludedMessages({
|
|
9662
|
+
const includeMessages = await this._getIncludedMessages({
|
|
9663
|
+
include,
|
|
9664
|
+
resourceId
|
|
9665
|
+
});
|
|
9645
9666
|
if (!includeMessages || includeMessages.length === 0) return {
|
|
9646
9667
|
messages: [],
|
|
9647
9668
|
total: 0,
|
|
@@ -9660,7 +9681,10 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
|
|
|
9660
9681
|
};
|
|
9661
9682
|
}
|
|
9662
9683
|
let includeFailure;
|
|
9663
|
-
const includePromise = include && include.length > 0 ? this._getIncludedMessages({
|
|
9684
|
+
const includePromise = include && include.length > 0 ? this._getIncludedMessages({
|
|
9685
|
+
include,
|
|
9686
|
+
resourceId
|
|
9687
|
+
}).catch((error) => {
|
|
9664
9688
|
includeFailure = error;
|
|
9665
9689
|
return null;
|
|
9666
9690
|
}) : null;
|
|
@@ -18688,6 +18712,171 @@ var SkillsPG = class SkillsPG extends _mastra_core_storage.SkillsStorage {
|
|
|
18688
18712
|
}
|
|
18689
18713
|
};
|
|
18690
18714
|
//#endregion
|
|
18715
|
+
//#region src/storage/domains/thread-state/index.ts
|
|
18716
|
+
const COMPOSITE_PRIMARY_KEY = ["threadId", "type"];
|
|
18717
|
+
/**
|
|
18718
|
+
* PostgreSQL implementation of {@link ThreadStateStorage}.
|
|
18719
|
+
*
|
|
18720
|
+
* Stores per-thread, per-type state in `mastra_thread_state`, keyed by the
|
|
18721
|
+
* composite primary key `(threadId, type)`. The `value` column is `jsonb`, so
|
|
18722
|
+
* payloads (the task list for `type = 'task'`, the goal objective for
|
|
18723
|
+
* `type = 'goal'`) come back already parsed.
|
|
18724
|
+
*/
|
|
18725
|
+
var ThreadStatePG = class ThreadStatePG extends _mastra_core_storage.ThreadStateStorage {
|
|
18726
|
+
#db;
|
|
18727
|
+
#schema;
|
|
18728
|
+
static MANAGED_TABLES = [_mastra_core_storage.TABLE_THREAD_STATE];
|
|
18729
|
+
/**
|
|
18730
|
+
* `thread_state` grows as a side effect of thread activity (one row per
|
|
18731
|
+
* thread per state type). It anchors on `updatedAtZ` (last activity), so
|
|
18732
|
+
* state for a thread that is still being written to is not pruned by
|
|
18733
|
+
* creation age.
|
|
18734
|
+
*/
|
|
18735
|
+
static retentionTables = { threadState: {
|
|
18736
|
+
table: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18737
|
+
column: "updatedAtZ",
|
|
18738
|
+
indexed: true
|
|
18739
|
+
} };
|
|
18740
|
+
constructor(config) {
|
|
18741
|
+
super();
|
|
18742
|
+
const { client, schemaName, skipDefaultIndexes } = resolvePgConfig(config);
|
|
18743
|
+
this.#db = new PgDB({
|
|
18744
|
+
client,
|
|
18745
|
+
schemaName,
|
|
18746
|
+
skipDefaultIndexes
|
|
18747
|
+
});
|
|
18748
|
+
this.#schema = schemaName || "public";
|
|
18749
|
+
}
|
|
18750
|
+
static getExportDDL(schemaName) {
|
|
18751
|
+
return [generateTableSQL({
|
|
18752
|
+
tableName: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18753
|
+
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_THREAD_STATE],
|
|
18754
|
+
schemaName,
|
|
18755
|
+
compositePrimaryKey: COMPOSITE_PRIMARY_KEY,
|
|
18756
|
+
includeAllConstraints: true
|
|
18757
|
+
})];
|
|
18758
|
+
}
|
|
18759
|
+
async init() {
|
|
18760
|
+
await this.#db.createTable({
|
|
18761
|
+
tableName: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18762
|
+
schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_THREAD_STATE],
|
|
18763
|
+
compositePrimaryKey: COMPOSITE_PRIMARY_KEY
|
|
18764
|
+
});
|
|
18765
|
+
}
|
|
18766
|
+
get #table() {
|
|
18767
|
+
return getTableName$5({
|
|
18768
|
+
indexName: _mastra_core_storage.TABLE_THREAD_STATE,
|
|
18769
|
+
schemaName: getSchemaName$5(this.#schema)
|
|
18770
|
+
});
|
|
18771
|
+
}
|
|
18772
|
+
/**
|
|
18773
|
+
* Create the retention index on demand, mirroring the other PG domains: only
|
|
18774
|
+
* deployments that configure retention pay for the extra index. Best-effort —
|
|
18775
|
+
* a failure here leaves pruning correct, just slower.
|
|
18776
|
+
*/
|
|
18777
|
+
async #ensureRetentionIndexes(policies) {
|
|
18778
|
+
const prefix = this.#schema && this.#schema !== "public" ? `${this.#schema}_` : "";
|
|
18779
|
+
for (const [key, entry] of Object.entries(ThreadStatePG.retentionTables)) {
|
|
18780
|
+
if (!entry.indexed || !policies[key]) continue;
|
|
18781
|
+
try {
|
|
18782
|
+
await this.#db.ensureIndex({
|
|
18783
|
+
indexName: `${prefix}mastra_${key}_retention_idx`,
|
|
18784
|
+
tableName: entry.table,
|
|
18785
|
+
column: entry.column
|
|
18786
|
+
});
|
|
18787
|
+
} catch (error) {
|
|
18788
|
+
this.logger?.warn?.(`Failed to create retention index for ${entry.table}:`, error);
|
|
18789
|
+
}
|
|
18790
|
+
}
|
|
18791
|
+
}
|
|
18792
|
+
/** Delete thread state older than the `threadState` policy's `maxAge`, batched. */
|
|
18793
|
+
async prune(policies, options) {
|
|
18794
|
+
await this.#ensureRetentionIndexes(policies);
|
|
18795
|
+
const targets = resolveTargets({
|
|
18796
|
+
policies,
|
|
18797
|
+
descriptor: ThreadStatePG.retentionTables,
|
|
18798
|
+
order: ["threadState"]
|
|
18799
|
+
});
|
|
18800
|
+
return runPrune({
|
|
18801
|
+
db: this.#db,
|
|
18802
|
+
domain: "threadState",
|
|
18803
|
+
targets,
|
|
18804
|
+
options
|
|
18805
|
+
});
|
|
18806
|
+
}
|
|
18807
|
+
async dangerouslyClearAll() {
|
|
18808
|
+
try {
|
|
18809
|
+
await this.#db.client.none(`DELETE FROM ${this.#table}`);
|
|
18810
|
+
} catch (error) {
|
|
18811
|
+
throw new _mastra_core_error.MastraError({
|
|
18812
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_CLEAR_ALL", "FAILED"),
|
|
18813
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18814
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY
|
|
18815
|
+
}, error);
|
|
18816
|
+
}
|
|
18817
|
+
}
|
|
18818
|
+
async getState({ threadId, type }) {
|
|
18819
|
+
try {
|
|
18820
|
+
const row = await this.#db.client.oneOrNone(`SELECT "value" FROM ${this.#table} WHERE "threadId" = $1 AND "type" = $2 LIMIT 1`, [threadId, type]);
|
|
18821
|
+
if (!row || row.value === null || row.value === void 0) return void 0;
|
|
18822
|
+
return typeof row.value === "string" ? JSON.parse(row.value) : row.value;
|
|
18823
|
+
} catch (error) {
|
|
18824
|
+
throw new _mastra_core_error.MastraError({
|
|
18825
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_GET", "FAILED"),
|
|
18826
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18827
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
18828
|
+
details: {
|
|
18829
|
+
threadId,
|
|
18830
|
+
type
|
|
18831
|
+
}
|
|
18832
|
+
}, error);
|
|
18833
|
+
}
|
|
18834
|
+
}
|
|
18835
|
+
async setState({ threadId, type, value }) {
|
|
18836
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18837
|
+
const serialized = JSON.stringify(value ?? null);
|
|
18838
|
+
try {
|
|
18839
|
+
await this.#db.client.none(`INSERT INTO ${this.#table} ("threadId", "type", "value", "createdAt", "createdAtZ", "updatedAt", "updatedAtZ")
|
|
18840
|
+
VALUES ($1, $2, $3::jsonb, $4::timestamp, $5::timestamptz, $4::timestamp, $5::timestamptz)
|
|
18841
|
+
ON CONFLICT ("threadId", "type")
|
|
18842
|
+
DO UPDATE SET "value" = EXCLUDED."value",
|
|
18843
|
+
"updatedAt" = EXCLUDED."updatedAt",
|
|
18844
|
+
"updatedAtZ" = EXCLUDED."updatedAtZ"`, [
|
|
18845
|
+
threadId,
|
|
18846
|
+
type,
|
|
18847
|
+
serialized,
|
|
18848
|
+
now,
|
|
18849
|
+
now
|
|
18850
|
+
]);
|
|
18851
|
+
} catch (error) {
|
|
18852
|
+
throw new _mastra_core_error.MastraError({
|
|
18853
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_SET", "FAILED"),
|
|
18854
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18855
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
18856
|
+
details: {
|
|
18857
|
+
threadId,
|
|
18858
|
+
type
|
|
18859
|
+
}
|
|
18860
|
+
}, error);
|
|
18861
|
+
}
|
|
18862
|
+
}
|
|
18863
|
+
async deleteState({ threadId, type }) {
|
|
18864
|
+
try {
|
|
18865
|
+
await this.#db.client.none(`DELETE FROM ${this.#table} WHERE "threadId" = $1 AND "type" = $2`, [threadId, type]);
|
|
18866
|
+
} catch (error) {
|
|
18867
|
+
throw new _mastra_core_error.MastraError({
|
|
18868
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "THREAD_STATE_DELETE", "FAILED"),
|
|
18869
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
18870
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
18871
|
+
details: {
|
|
18872
|
+
threadId,
|
|
18873
|
+
type
|
|
18874
|
+
}
|
|
18875
|
+
}, error);
|
|
18876
|
+
}
|
|
18877
|
+
}
|
|
18878
|
+
};
|
|
18879
|
+
//#endregion
|
|
18691
18880
|
//#region src/storage/domains/tool-provider-connections/index.ts
|
|
18692
18881
|
function normaliseScope(raw) {
|
|
18693
18882
|
const value = raw == null ? "per-author" : String(raw);
|
|
@@ -20622,7 +20811,8 @@ const ALL_DOMAINS = [
|
|
|
20622
20811
|
BackgroundTasksPG,
|
|
20623
20812
|
FavoritesPG,
|
|
20624
20813
|
ChannelsPG,
|
|
20625
|
-
SchedulesPG
|
|
20814
|
+
SchedulesPG,
|
|
20815
|
+
ThreadStatePG
|
|
20626
20816
|
];
|
|
20627
20817
|
/**
|
|
20628
20818
|
* Exports the Mastra database schema as SQL DDL statements, including tables, indexes, and triggers.
|
|
@@ -20716,7 +20906,8 @@ var PostgresStore = class extends _mastra_core_storage.MastraCompositeStore {
|
|
|
20716
20906
|
experiments: new ExperimentsPG(domainConfig),
|
|
20717
20907
|
backgroundTasks: new BackgroundTasksPG(domainConfig),
|
|
20718
20908
|
channels: new ChannelsPG(domainConfig),
|
|
20719
|
-
schedules: new SchedulesPG(domainConfig)
|
|
20909
|
+
schedules: new SchedulesPG(domainConfig),
|
|
20910
|
+
threadState: new ThreadStatePG(domainConfig)
|
|
20720
20911
|
};
|
|
20721
20912
|
} catch (e) {
|
|
20722
20913
|
throw new _mastra_core_error.MastraError({
|
|
@@ -21047,6 +21238,7 @@ exports.SchedulesPG = SchedulesPG;
|
|
|
21047
21238
|
exports.ScorerDefinitionsPG = ScorerDefinitionsPG;
|
|
21048
21239
|
exports.ScoresPG = ScoresPG;
|
|
21049
21240
|
exports.SkillsPG = SkillsPG;
|
|
21241
|
+
exports.ThreadStatePG = ThreadStatePG;
|
|
21050
21242
|
exports.ToolProviderConnectionsPG = ToolProviderConnectionsPG;
|
|
21051
21243
|
exports.WorkflowDefinitionsPG = WorkflowDefinitionsPG;
|
|
21052
21244
|
exports.WorkflowsPG = WorkflowsPG;
|