@mastra/pg 1.22.3-alpha.2 → 1.22.3-alpha.4

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/README.md CHANGED
@@ -8,11 +8,6 @@ PostgreSQL implementation for Mastra, providing both vector similarity search (u
8
8
  npm install @mastra/pg
9
9
  ```
10
10
 
11
- ## Prerequisites
12
-
13
- - PostgreSQL server with pgvector extension installed (if using vector store)
14
- - PostgreSQL 11 or higher
15
-
16
11
  ## Usage
17
12
 
18
13
  ### Vector Store
@@ -31,487 +26,14 @@ const vectorStore = new PgVector({
31
26
  });
32
27
  ```
33
28
 
34
- **2. Host/Port/Database Configuration**
35
-
36
- ```typescript
37
- const vectorStore = new PgVector({
38
- host: 'localhost',
39
- port: 5432,
40
- database: 'mydb',
41
- user: 'postgres',
42
- password: 'password',
43
- });
44
- ```
45
-
46
- > **Note:** PgVector also supports advanced configurations like Google Cloud SQL Connector via `pg.ClientConfig`.
47
-
48
- #### Advanced Options
49
-
50
- ```typescript
51
- const vectorStore = new PgVector({
52
- connectionString: 'postgresql://user:pass@localhost:5432/db',
53
- schemaName: 'custom_schema', // Use custom schema (default: public)
54
- max: 30, // Max pool connections (default: 20)
55
- idleTimeoutMillis: 60000, // Idle timeout (default: 30000)
56
- pgPoolOptions: {
57
- // Additional pg pool options
58
- connectionTimeoutMillis: 5000,
59
- allowExitOnIdle: true,
60
- },
61
- });
62
- ```
63
-
64
- #### Usage Example
65
-
66
- ```typescript
67
- // Create a new table with vector support
68
- await vectorStore.createIndex({
69
- indexName: 'my_vectors',
70
- dimension: 1536,
71
- metric: 'cosine',
72
- // Optional: Configure index type and parameters
73
- indexConfig: {
74
- type: 'hnsw', // 'ivfflat' (default), 'hnsw', or 'flat'
75
- hnsw: {
76
- m: 16, // Number of connections per layer (default: 8)
77
- efConstruction: 64 // Size of dynamic list (default: 32)
78
- }
79
- }
80
- });
81
-
82
- // Add vectors
83
- const ids = await vectorStore.upsert({
84
- indexName: 'my_vectors',
85
- vectors: [[0.1, 0.2, ...], [0.3, 0.4, ...]],
86
- metadata: [{ text: 'doc1' }, { text: 'doc2' }],
87
- });
88
-
89
- // Query vectors
90
- const results = await vectorStore.query({
91
- indexName: 'my_vectors',
92
- queryVector: [0.1, 0.2, ...],
93
- topK: 10, // topK
94
- filter: { text: 'doc1' }, // filter
95
- includeVector: false, // includeVector
96
- minScore: 0.5, // minScore
97
- });
98
-
99
- // Clean up
100
- await vectorStore.disconnect();
101
- ```
102
-
103
- ### Storage
104
-
105
- ```typescript
106
- import { PostgresStore } from '@mastra/pg';
107
-
108
- const store = new PostgresStore({
109
- host: 'localhost',
110
- port: 5432,
111
- database: 'mastra',
112
- user: 'postgres',
113
- password: 'postgres',
114
- });
115
-
116
- // Create a thread
117
- await store.saveThread({
118
- thread: {
119
- id: 'thread-123',
120
- resourceId: 'resource-456',
121
- title: 'My Thread',
122
- metadata: { key: 'value' },
123
- createdAt: new Date(),
124
- },
125
- });
126
-
127
- // Add messages to thread
128
- await store.saveMessages({
129
- messages: [
130
- {
131
- id: 'msg-789',
132
- threadId: 'thread-123',
133
- role: 'user',
134
- content: { content: 'Hello' },
135
- resourceId: 'resource-456',
136
- createdAt: new Date(),
137
- },
138
- ],
139
- });
140
-
141
- // Query threads and messages
142
- const savedThread = await store.getThreadById({ threadId: 'thread-123' });
143
- const messages = await store.listMessages({ threadId: 'thread-123' });
144
- ```
145
-
146
- ## Configuration
147
-
148
- ### Connection Methods
149
-
150
- Both `PgVector` and `PostgresStore` support multiple connection methods:
151
-
152
- 1. **Connection String**
153
-
154
- ```typescript
155
- {
156
- connectionString: 'postgresql://user:pass@localhost:5432/db';
157
- }
158
- ```
159
-
160
- 2. **Host/Port/Database**
161
- ```typescript
162
- {
163
- host: 'localhost',
164
- port: 5432,
165
- database: 'mydb',
166
- user: 'postgres',
167
- password: 'password'
168
- }
169
- ```
170
-
171
- > **Advanced:** Also supports `pg.ClientConfig` for use cases like Google Cloud SQL Connector with IAM authentication.
172
-
173
- ### Optional Configuration
174
-
175
- - `schemaName`: Custom PostgreSQL schema (default: `public`)
176
- - `ssl`: Enable SSL or provide custom SSL options (`true` | `false` | `ConnectionOptions`)
177
- - `max`: Maximum pool connections (default: `20`)
178
- - `idleTimeoutMillis`: Idle connection timeout (default: `30000`)
179
- - `pgPoolOptions`: Additional pg pool options (PgVector only)
180
-
181
- ### Default Connection Pool Settings
182
-
183
- - Maximum connections: 20
184
- - Idle timeout: 30 seconds
185
- - Connection timeout: 2 seconds
186
-
187
- ## Features
188
-
189
- ### Vector Store Features
190
-
191
- - Vector similarity search with cosine, euclidean, and dot product (inner) metrics
192
- - Advanced metadata filtering with MongoDB-like query syntax
193
- - Minimum score threshold for queries
194
- - Automatic UUID generation for vectors
195
- - Table management (create, list, describe, delete, truncate)
196
- - Configurable vector index types:
197
- - **IVFFlat** (default): Balanced speed/accuracy, auto-calculates optimal lists parameter
198
- - **HNSW**: Fastest queries, higher memory usage, best for large datasets
199
- - **Flat**: No index, 100% accuracy, best for small datasets (<1000 vectors)
200
-
201
- ### Storage Features
202
-
203
- - Thread and message storage with JSON support
204
- - Atomic transactions for data consistency
205
- - Efficient batch operations
206
- - Rich metadata support
207
- - Timestamp tracking
208
- - Cascading deletes
209
-
210
- ## Supported Filter Operators
211
-
212
- The following filter operators are supported for metadata queries:
213
-
214
- - Comparison: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`
215
- - Logical: `$and`, `$or`
216
- - Array: `$in`, `$nin`
217
- - Text: `$regex`, `$like`
218
-
219
- Example filter:
220
-
221
- ```typescript
222
- {
223
- $and: [{ age: { $gt: 25 } }, { tags: { $in: ['tag1', 'tag2'] } }];
224
- }
225
- ```
226
-
227
- ## Vector Index Configuration
228
-
229
- pgvector supports three index types, each with different performance characteristics:
230
-
231
- ### IVFFlat Index (Default)
232
-
233
- IVFFlat groups vectors into clusters for efficient searching:
234
-
235
- ```typescript
236
- await vectorStore.createIndex({
237
- indexName: 'my_vectors',
238
- dimension: 1536,
239
- metric: 'cosine',
240
- indexConfig: {
241
- type: 'ivfflat',
242
- ivf: {
243
- lists: 1000, // Number of clusters (default: auto-calculated as sqrt(rows) * 2)
244
- },
245
- },
246
- });
247
- ```
248
-
249
- - **Best for:** Medium to large datasets (10K-1M vectors)
250
- - **Build time:** Minutes for millions of vectors
251
- - **Query speed:** Fast (tens of milliseconds)
252
- - **Memory:** Moderate
253
- - **Accuracy:** ~95-99%
254
-
255
- ### HNSW Index
256
-
257
- HNSW builds a graph structure for extremely fast searches:
258
-
259
- ```typescript
260
- await vectorStore.createIndex({
261
- indexName: 'my_vectors',
262
- dimension: 1536,
263
- metric: 'dotproduct', // Recommended for normalized embeddings (OpenAI, etc.)
264
- indexConfig: {
265
- type: 'hnsw',
266
- hnsw: {
267
- m: 16, // Connections per layer (default: 8, range: 2-100)
268
- efConstruction: 64, // Dynamic list size (default: 32, range: 4-1000)
269
- },
270
- },
271
- });
272
- ```
273
-
274
- - **Best for:** Large datasets (100K+ vectors) requiring fastest searches
275
- - **Build time:** Can take hours for large datasets
276
- - **Query speed:** Very fast (milliseconds even for millions)
277
- - **Memory:** High (can be 2-3x vector size)
278
- - **Accuracy:** ~99%
279
-
280
- **Tuning HNSW:**
281
-
282
- - Higher `m`: Better accuracy, more memory (16-32 for high accuracy)
283
- - Higher `efConstruction`: Better index quality, slower builds (64-200 for quality)
284
-
285
- ### Flat Index (No Index)
286
-
287
- Uses sequential scan for 100% accuracy:
288
-
289
- ```typescript
290
- await vectorStore.createIndex({
291
- indexName: 'my_vectors',
292
- dimension: 1536,
293
- metric: 'cosine',
294
- indexConfig: {
295
- type: 'flat',
296
- },
297
- });
298
- ```
299
-
300
- - **Best for:** Small datasets (<1000 vectors) or when 100% accuracy is required
301
- - **Build time:** None
302
- - **Query speed:** Slow for large datasets (linear scan)
303
- - **Memory:** Minimal (just vectors)
304
- - **Accuracy:** 100%
305
-
306
- ### Distance Metrics
307
-
308
- Choose the appropriate metric for your embeddings:
309
-
310
- - **`cosine`** (default): Angular similarity, good for text embeddings
311
- - **`euclidean`**: L2 distance, for unnormalized embeddings
312
- - **`dotproduct`**: Dot product, optimal for normalized embeddings (OpenAI, Cohere)
313
-
314
- ### Index Recreation
315
-
316
- The system automatically detects configuration changes and only rebuilds indexes when necessary, preventing the performance issues from unnecessary recreations.
317
-
318
- **Important behaviors:**
319
-
320
- - If no `indexConfig` is provided, existing indexes are preserved as-is
321
- - If `indexConfig` is provided, indexes are only rebuilt if the configuration differs
322
- - New indexes default to IVFFlat with cosine distance when no config is specified
323
-
324
- ## Vector Store Methods
325
-
326
- - `createIndex({indexName, dimension, metric?, indexConfig?, buildIndex?})`: Create a new table with vector support
327
- - `buildIndex({indexName, metric?, indexConfig?})`: Build or rebuild vector index
328
- - `upsert({indexName, vectors, metadata?, ids?})`: Add or update vectors
329
- - `query({indexName, queryVector, topK?, filter?, includeVector?, minScore?})`: Search for similar vectors
330
- - `updateVector({ indexName, id?, filter?, update })`: Update a single vector by ID or metadata filter
331
- - `deleteVector({ indexName, id })`: Delete a single vector by ID
332
- - `deleteVectors({ indexName, ids?, filter? })`: Delete multiple vectors by IDs or metadata filter
333
- - `listIndexes()`: List all vector-enabled tables
334
- - `describeIndex(indexName)`: Get table statistics and index configuration
335
- - `deleteIndex(indexName)`: Delete a table
336
- - `truncateIndex(indexName)`: Remove all data from a table
337
- - `disconnect()`: Close all database connections
338
-
339
- ## Storage Methods
340
-
341
- ### Thread Operations
342
-
343
- - `saveThread({ thread })`: Create or update a thread
344
- - `getThreadById({ threadId })`: Get a thread by ID
345
- - `updateThread({ id, title, metadata })`: Update thread title and/or metadata
346
- - `deleteThread({ threadId })`: Delete a thread and its messages
347
- - `listThreadsByResourceId({ resourceId, offset, limit, orderBy? })`: List paginated threads for a resource
348
-
349
- ### Message Operations
350
-
351
- - `saveMessages({ messages })`: Save multiple messages in a transaction
352
- - `listMessages({ threadId, resourceId?, perPage?, page?, orderBy?, filter? })`: Get messages for a thread with pagination
353
- - `listMessagesById({ messageIds })`: Get specific messages by their IDs
354
- - `updateMessages({ messages })`: Update existing messages
355
- - `deleteMessages(messageIds)`: Delete specific messages
356
-
357
- ### Resource Operations
358
-
359
- - `getResourceById({ resourceId })`: Get a resource by ID
360
- - `saveResource({ resource })`: Create or save a resource
361
- - `updateResource({ resourceId, workingMemory })`: Update resource working memory
362
-
363
- ### Workflow Operations
364
-
365
- - `persistWorkflowSnapshot({ workflowName, runId, snapshot })`: Save workflow state
366
- - `loadWorkflowSnapshot({ workflowName, runId })`: Load workflow state
367
- - `listWorkflowRuns({ workflowName, pagination })`: List workflow runs with pagination
368
- - `getWorkflowRunById({ workflowName, runId })`: Get a specific workflow run
369
- - `updateWorkflowState({ workflowName, runId, state })`: Update workflow state
370
- - `updateWorkflowResults({ workflowName, runId, results })`: Update workflow results
371
-
372
- ### AI Observability Operations
373
-
374
- - `createSpan(span)`: Create a single AI span
375
- - `batchCreateSpans({ records })`: Create multiple AI spans
376
- - `updateSpan({ traceId, spanId, updates })`: Update an AI span
377
- - `batchUpdateSpans({ updates })`: Update multiple AI spans
378
- - `getTrace(traceId)`: Get an trace by ID
379
- - `getTracesPaginated({ ...filters, pagination })`: Get paginated traces with filtering
380
- - `batchDeleteTraces({ traceIds })`: Delete multiple traces
29
+ ## Documentation
381
30
 
382
- ### Evaluation/Scoring Operations
31
+ - [@mastra/pg documentation](https://mastra.ai/reference/vectors/pg)
383
32
 
384
- - `getScoreById({ id })`: Get a score by ID
385
- - `saveScore(score)`: Save an evaluation score
386
- - `listScoresByScorerId({ scorerId, pagination })`: List scores by scorer with pagination
387
- - `listScoresByRunId({ runId, pagination })`: List scores by run with pagination
388
- - `listScoresByEntityId({ entityId, entityType, pagination })`: List scores by entity with pagination
389
- - `listScoresBySpan({ traceId, spanId, pagination })`: List scores by span with pagination
33
+ ## Changelog
390
34
 
391
- ## Index Management
392
-
393
- The PostgreSQL store provides comprehensive index management capabilities to optimize query performance.
394
-
395
- ### Automatic Performance Indexes
396
-
397
- PostgreSQL storage automatically creates composite indexes during initialization for common query patterns:
398
-
399
- - `mastra_threads_resourceid_createdat_idx`: (resourceId, createdAt DESC)
400
- - `mastra_messages_thread_id_createdat_idx`: (thread_id, createdAt DESC)
401
- - `mastra_traces_name_starttime_idx`: (name, startTime DESC)
402
- - `mastra_evals_agent_name_created_at_idx`: (agent_name, created_at DESC)
403
-
404
- These indexes significantly improve performance for filtered queries with sorting.
405
-
406
- ### Creating Custom Indexes
407
-
408
- Create additional indexes to optimize specific query patterns:
409
-
410
- ```typescript
411
- // Basic index for common queries
412
- await store.createIndex({
413
- name: 'idx_threads_resource',
414
- table: 'mastra_threads',
415
- columns: ['resourceId'],
416
- });
417
-
418
- // Composite index with sort order for filtering + sorting
419
- await store.createIndex({
420
- name: 'idx_messages_composite',
421
- table: 'mastra_messages',
422
- columns: ['thread_id', 'createdAt DESC'],
423
- });
424
-
425
- // GIN index for JSONB columns (fast JSON queries)
426
- await store.createIndex({
427
- name: 'idx_traces_attributes',
428
- table: 'mastra_traces',
429
- columns: ['attributes'],
430
- method: 'gin',
431
- });
432
- ```
433
-
434
- For more advanced use cases, you can also use:
435
-
436
- - `unique: true` for unique constraints
437
- - `where: 'condition'` for partial indexes
438
- - `method: 'brin'` for time-series data
439
- - `storage: { fillfactor: 90 }` for update-heavy tables
440
- - `concurrent: true` for non-blocking creation (default)
441
-
442
- ### Managing Indexes
443
-
444
- ```typescript
445
- // List all indexes
446
- const allIndexes = await store.listIndexes();
447
-
448
- // List indexes for specific table
449
- const threadIndexes = await store.listIndexes('mastra_threads');
450
-
451
- // Get detailed statistics for an index
452
- const stats = await store.describeIndex('idx_threads_resource');
453
- console.log(stats);
454
- // {
455
- // name: 'idx_threads_resource',
456
- // table: 'mastra_threads',
457
- // columns: ['resourceId', 'createdAt'],
458
- // unique: false,
459
- // size: '128 KB',
460
- // definition: 'CREATE INDEX idx_threads_resource...',
461
- // method: 'btree',
462
- // scans: 1542, // Number of index scans
463
- // tuples_read: 45230, // Tuples read via index
464
- // tuples_fetched: 12050 // Tuples fetched via index
465
- // }
466
-
467
- // Drop an index
468
- await store.dropIndex('idx_threads_status');
469
- ```
470
-
471
- ### Index Types and Use Cases
472
-
473
- | Index Type | Best For | Storage | Speed |
474
- | ------------------- | --------------------------------------- | ---------- | -------------------------- |
475
- | **btree** (default) | Range queries, sorting, general purpose | Moderate | Fast |
476
- | **hash** | Equality comparisons only | Small | Very fast for `=` |
477
- | **gin** | JSONB, arrays, full-text search | Large | Fast for contains |
478
- | **gist** | Geometric data, full-text search | Moderate | Fast for nearest-neighbor |
479
- | **spgist** | Non-balanced data, text patterns | Small | Fast for specific patterns |
480
- | **brin** | Large tables with natural ordering | Very small | Fast for ranges |
481
-
482
- ### Index Options
483
-
484
- - `name` (required): Index name
485
- - `table` (required): Table name
486
- - `columns` (required): Array of column names (can include DESC/ASC)
487
- - `unique`: Create unique index (default: false)
488
- - `concurrent`: Non-blocking index creation (default: true)
489
- - `where`: Partial index condition
490
- - `method`: Index type ('btree' | 'hash' | 'gin' | 'gist' | 'spgist' | 'brin')
491
- - `opclass`: Operator class for GIN/GIST indexes
492
- - `storage`: Storage parameters (e.g., { fillfactor: 90 })
493
- - `tablespace`: Tablespace name for index placement
494
-
495
- ### Monitoring Index Performance
496
-
497
- ```typescript
498
- // Check index usage statistics
499
- const stats = await store.describeIndex('idx_threads_resource');
500
-
501
- // Identify unused indexes
502
- if (stats.scans === 0) {
503
- console.log(`Index ${stats.name} is unused - consider removing`);
504
- await store.dropIndex(stats.name);
505
- }
506
-
507
- // Monitor index efficiency
508
- const efficiency = stats.tuples_fetched / stats.tuples_read;
509
- if (efficiency < 0.5) {
510
- console.log(`Index ${stats.name} has low efficiency: ${efficiency}`);
511
- }
512
- ```
35
+ See the [package changelog](https://github.com/mastra-ai/mastra/blob/main/stores/pg/CHANGELOG.md) for version history and release notes.
513
36
 
514
- ## Related Links
37
+ ## Support
515
38
 
516
- - [pgvector Documentation](https://github.com/pgvector/pgvector)
517
- - [PostgreSQL Documentation](https://www.postgresql.org/docs/)
39
+ We have an [open community Discord](https://discord.gg/mastra-ai). Come and say hello and let us know if you have any questions or need any help getting things running.
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.22.3-alpha.2"
6
+ version: "1.22.3-alpha.4"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.22.3-alpha.2",
2
+ "version": "1.22.3-alpha.4",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -39,6 +39,12 @@ Polls storage for due cron schedules and publishes `workflow.start` events. It's
39
39
 
40
40
  The scheduler reads declarative `schedule` fields from your workflow definitions automatically. See [Scheduled workflows](https://mastra.ai/docs/workflows/scheduled-workflows) for how to declare schedules.
41
41
 
42
+ The scheduler only polls storage once a schedule exists. At boot, a process with no declared schedules runs a single `listSchedules()` check. If that check finds no rows, the poll loop never starts, so idle deployments issue no recurring scheduler queries and can scale to zero.
43
+
44
+ When a schedule is created at runtime, Mastra publishes a wake event on the PubSub backend. Any process running the default worker set starts its scheduler in response, which is how a standalone worker discovers schedules created by the API process. This requires both processes to share the same PubSub backend.
45
+
46
+ To poll from startup regardless of whether schedules exist (for example, when your processes don't share a PubSub backend), set `scheduler: { enabled: true }` on the worker or run it with `MASTRA_WORKERS=scheduler`.
47
+
42
48
  **Don't run more than one scheduler instance.** Multiple schedulers polling the same storage would fire duplicate events for the same schedule.
43
49
 
44
50
  ### Background task worker
package/dist/index.cjs CHANGED
@@ -14073,6 +14073,11 @@ const FEEDBACK_EVENT_COLUMNS = [
14073
14073
  type: "text",
14074
14074
  nullable: true
14075
14075
  },
14076
+ {
14077
+ name: "reviewStatus",
14078
+ type: "text",
14079
+ defaultSql: "'needs-review'"
14080
+ },
14076
14081
  ...COMMON_CONTEXT_COLUMNS,
14077
14082
  {
14078
14083
  name: "tags",
@@ -14513,6 +14518,10 @@ function additiveColumns(schema) {
14513
14518
  table: TABLE_SPAN_EVENTS,
14514
14519
  column: "isPending",
14515
14520
  ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_SPAN_EVENTS)} ADD COLUMN IF NOT EXISTS "isPending" boolean NOT NULL DEFAULT false`
14521
+ }, {
14522
+ table: TABLE_FEEDBACK_EVENTS,
14523
+ column: "reviewStatus",
14524
+ ddl: `ALTER TABLE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)} ADD COLUMN IF NOT EXISTS "reviewStatus" text NOT NULL DEFAULT 'needs-review'`
14516
14525
  }];
14517
14526
  }
14518
14527
  /** Existence probe for an additive column. Params: schema, table, column. */
@@ -14835,6 +14844,31 @@ function whereOrEmpty(acc) {
14835
14844
  return acc.conditions.length ? `WHERE ${acc.conditions.join(" AND ")}` : "";
14836
14845
  }
14837
14846
  //#endregion
14847
+ //#region src/storage/domains/observability/v-next/review-status.ts
14848
+ const FEEDBACK_REVIEW_STATUSES = ["needs-review", "reviewed"];
14849
+ function isFeedbackReviewStatus(value) {
14850
+ return FEEDBACK_REVIEW_STATUSES.some((status) => status === value);
14851
+ }
14852
+ /** Normalize a stored value to a review status, defaulting legacy/unknown values to `needs-review`. */
14853
+ function coerceFeedbackReviewStatus(value) {
14854
+ return isFeedbackReviewStatus(value) ? value : "needs-review";
14855
+ }
14856
+ function parseUpdateFeedbackReviewStatusArgs(args) {
14857
+ const invalid = (text) => new _mastra_core_error.MastraError({
14858
+ id: "OBSERVABILITY_UPDATE_FEEDBACK_REVIEW_STATUS_INVALID_ARGS",
14859
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
14860
+ category: _mastra_core_error.ErrorCategory.USER,
14861
+ text
14862
+ });
14863
+ if (typeof args !== "object" || args === null) throw invalid("args must be an object");
14864
+ if (typeof args.feedbackId !== "string" || args.feedbackId.length === 0) throw invalid("feedbackId is required");
14865
+ if (!isFeedbackReviewStatus(args.reviewStatus)) throw invalid(`reviewStatus must be one of: ${FEEDBACK_REVIEW_STATUSES.join(", ")}`);
14866
+ return {
14867
+ feedbackId: args.feedbackId,
14868
+ reviewStatus: args.reviewStatus
14869
+ };
14870
+ }
14871
+ //#endregion
14838
14872
  //#region src/storage/domains/observability/v-next/helpers.ts
14839
14873
  const PROMOTED_KEYS = /* @__PURE__ */ new Set([
14840
14874
  "experimentId",
@@ -15199,6 +15233,7 @@ function feedbackRecordToRow(feedback) {
15199
15233
  spanId: feedback.spanId ?? null,
15200
15234
  feedbackUserId: feedback.feedbackUserId ?? null,
15201
15235
  sourceId: feedback.sourceId ?? null,
15236
+ reviewStatus: feedback.reviewStatus ?? "needs-review",
15202
15237
  feedbackSource,
15203
15238
  feedbackType: feedback.feedbackType,
15204
15239
  valueString: typeof feedback.value === "string" ? feedback.value : null,
@@ -15220,6 +15255,7 @@ function rowToFeedbackRecord(row) {
15220
15255
  spanId: nullableString(row.spanId),
15221
15256
  feedbackUserId: nullableString(row.feedbackUserId),
15222
15257
  sourceId: nullableString(row.sourceId),
15258
+ reviewStatus: coerceFeedbackReviewStatus(row.reviewStatus),
15223
15259
  feedbackSource,
15224
15260
  feedbackType: row.feedbackType,
15225
15261
  value: hasNumber ? Number(row.valueNumber) : nullableString(row.valueString) ?? "",
@@ -15739,6 +15775,10 @@ function applyFeedbackFilters(acc, filters) {
15739
15775
  acc.conditions.push(`"feedbackUserId" = $${acc.next++}`);
15740
15776
  acc.params.push(filters.feedbackUserId);
15741
15777
  }
15778
+ if (filters?.reviewStatus) {
15779
+ acc.conditions.push(`"reviewStatus" = $${acc.next++}`);
15780
+ acc.params.push(filters.reviewStatus);
15781
+ }
15742
15782
  }
15743
15783
  /**
15744
15784
  * OLAP queries take an explicit feedbackType / feedbackSource pair as
@@ -15762,6 +15802,21 @@ async function batchCreateFeedback(client, schema, args) {
15762
15802
  const insert = buildInsert(schema, TABLE_FEEDBACK_EVENTS, args.feedbacks.map(feedbackRecordToRow));
15763
15803
  if (insert) await client.query(insert.text, insert.values);
15764
15804
  }
15805
+ async function updateFeedbackReviewStatus(client, schema, args) {
15806
+ const { feedbackId, reviewStatus } = parseUpdateFeedbackReviewStatusArgs(args);
15807
+ const row = await client.oneOrNone(`UPDATE ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
15808
+ SET "reviewStatus" = $2
15809
+ WHERE "feedbackId" = $1
15810
+ RETURNING ${FEEDBACK_SELECT_COLUMNS}`, [feedbackId, reviewStatus]);
15811
+ if (!row) throw new _mastra_core_error.MastraError({
15812
+ id: "OBSERVABILITY_UPDATE_FEEDBACK_REVIEW_STATUS_NOT_FOUND",
15813
+ domain: _mastra_core_error.ErrorDomain.MASTRA_OBSERVABILITY,
15814
+ category: _mastra_core_error.ErrorCategory.USER,
15815
+ text: "Feedback record not found",
15816
+ details: { feedbackId }
15817
+ });
15818
+ return rowToFeedbackRecord(row);
15819
+ }
15765
15820
  async function listFeedback(client, schema, args) {
15766
15821
  const { mode, filters, pagination, orderBy, after, limit } = _mastra_core_storage.listFeedbackArgsSchema.parse(args);
15767
15822
  const table = qualifiedTable(schema, TABLE_FEEDBACK_EVENTS);
@@ -17625,6 +17680,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
17625
17680
  async listFeedback(args) {
17626
17681
  return this.#run("LIST_FEEDBACK", () => listFeedback(this.#client, this.#schema, args));
17627
17682
  }
17683
+ async updateFeedbackReviewStatus(args) {
17684
+ return this.#run("UPDATE_FEEDBACK_REVIEW_STATUS", () => updateFeedbackReviewStatus(this.#client, this.#schema, args), { feedbackId: args.feedbackId });
17685
+ }
17628
17686
  async getMetricAggregate(args) {
17629
17687
  return this.#run("GET_METRIC_AGGREGATE", () => getMetricAggregate(this.#client, this.#schema, args));
17630
17688
  }