@mastra/pg 1.36.0-alpha.2 → 1.36.0-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/dist/index.cjs CHANGED
@@ -273,31 +273,29 @@ function buildElemMatchConditions(value, paramIndex) {
273
273
  if (typeof value !== "object" || Array.isArray(value)) throw new Error("$elemMatch requires an object with conditions");
274
274
  const conditions = [];
275
275
  const values = [];
276
+ function buildFieldCondition(field, operator, operatorValue) {
277
+ if (operator === "$not") {
278
+ const nested = Object.entries(operatorValue);
279
+ if (nested.length === 0) throw new Error("$not requires at least one operator");
280
+ return `NOT (COALESCE(${nested.map(([nestedOp, nestedValue]) => buildFieldCondition(field, nestedOp, nestedValue)).join(" AND ")}, FALSE))`;
281
+ }
282
+ const operatorFn = FILTER_OPERATORS[operator];
283
+ if (!operatorFn) throw new Error(`Invalid operator: ${operator}`);
284
+ const scalarElement = field.startsWith("$");
285
+ const result = operatorFn(scalarElement ? "" : field, paramIndex + values.length, operatorValue);
286
+ const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>").replaceAll("metadata ?", "elem ?");
287
+ const elementSql = scalarElement ? sql.replaceAll("elem->>''", "elem #>> '{}'").replaceAll("elem->''", "elem") : sql;
288
+ if (result.needsValue) {
289
+ const transformedValue = result.transformValue ? result.transformValue() : operatorValue;
290
+ if (operator === "$elemMatch") values.push(...transformedValue);
291
+ else values.push(transformedValue);
292
+ }
293
+ return elementSql;
294
+ }
276
295
  Object.entries(value).forEach(([field, val]) => {
277
- const nextParamIndex = paramIndex + values.length;
278
- let paramOperator;
279
- let paramKey;
280
- let paramValue;
281
- if (field.startsWith("$")) {
282
- paramOperator = field;
283
- paramKey = "";
284
- paramValue = val;
285
- } else if (typeof val === "object" && !Array.isArray(val)) {
286
- const [op, opValue] = Object.entries(val || {})[0] || [];
287
- paramOperator = op;
288
- paramKey = field;
289
- paramValue = opValue;
290
- } else {
291
- paramOperator = "$eq";
292
- paramKey = field;
293
- paramValue = val;
294
- }
295
- const operatorFn = FILTER_OPERATORS[paramOperator];
296
- if (!operatorFn) throw new Error(`Invalid operator: ${paramOperator}`);
297
- const result = operatorFn(paramKey, nextParamIndex, paramValue);
298
- const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>");
299
- conditions.push(sql);
300
- if (result.needsValue) values.push(paramValue);
296
+ const operators = field.startsWith("$") ? [[field, val]] : typeof val === "object" && val !== null && !Array.isArray(val) ? Object.entries(val) : [["$eq", val]];
297
+ if (operators.length === 0) throw new Error(`$elemMatch field ${field} requires at least one operator`);
298
+ for (const [operator, operatorValue] of operators) conditions.push(buildFieldCondition(field, operator, operatorValue));
301
299
  });
302
300
  return {
303
301
  sql: conditions.join(" AND "),
@@ -3428,12 +3426,16 @@ var PgDB = class extends _mastra_core_base.MastraBase {
3428
3426
  }
3429
3427
  if (snapshot) {
3430
3428
  snapshot.tables.add(tableName);
3431
- if (isNewTable) snapshot.createdTables.add(tableName);
3432
- const created = this.snapshotColumns(snapshot, tableName);
3433
- for (const [name, def] of Object.entries(schema)) {
3434
- const parsedName = (0, _mastra_core_utils.parseSqlIdentifier)(name, "column name");
3435
- created.add(parsedName);
3436
- if (def.type === "timestamp") created.add(`${parsedName}Z`);
3429
+ if (isNewTable) {
3430
+ snapshot.createdTables.add(tableName);
3431
+ snapshot.columns.set(tableName, new Set(await this.getTableColumns(tableName)));
3432
+ } else {
3433
+ const created = this.snapshotColumns(snapshot, tableName);
3434
+ for (const [name, def] of Object.entries(schema)) {
3435
+ const parsedName = (0, _mastra_core_utils.parseSqlIdentifier)(name, "column name");
3436
+ created.add(parsedName);
3437
+ if (def.type === "timestamp") created.add(`${parsedName}Z`);
3438
+ }
3437
3439
  }
3438
3440
  }
3439
3441
  }
@@ -11678,6 +11680,13 @@ function dedupeMessagesForSave(messages) {
11678
11680
  }
11679
11681
  return Array.from(deduped.values());
11680
11682
  }
11683
+ /** Shallow merge; keys patched to `undefined` are dropped when serialized. Non-object metadata is replaced. */
11684
+ function mergeMetadata(existing, patch) {
11685
+ return {
11686
+ ...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {},
11687
+ ...patch
11688
+ };
11689
+ }
11681
11690
  var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11682
11691
  supportsPartialThreadUpdate = true;
11683
11692
  supportsObservationalMemory = true;
@@ -11815,15 +11824,25 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
11815
11824
  * @param schemaPrefix - Prefix for index names (e.g. "my_schema_" or "")
11816
11825
  */
11817
11826
  static getDefaultIndexDefs(schemaPrefix) {
11818
- return [{
11819
- name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
11820
- table: _mastra_core_storage.TABLE_THREADS,
11821
- columns: ["resourceId", "createdAt DESC"]
11822
- }, {
11823
- name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
11824
- table: _mastra_core_storage.TABLE_MESSAGES,
11825
- columns: ["thread_id", "createdAt DESC"]
11826
- }];
11827
+ return [
11828
+ {
11829
+ name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
11830
+ table: _mastra_core_storage.TABLE_THREADS,
11831
+ columns: ["resourceId", "createdAt DESC"]
11832
+ },
11833
+ {
11834
+ name: `${schemaPrefix}mastra_threads_metadata_gin_idx`,
11835
+ table: _mastra_core_storage.TABLE_THREADS,
11836
+ columns: ["metadata"],
11837
+ method: "gin",
11838
+ opclass: "jsonb_path_ops"
11839
+ },
11840
+ {
11841
+ name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
11842
+ table: _mastra_core_storage.TABLE_MESSAGES,
11843
+ columns: ["thread_id", "createdAt DESC"]
11844
+ }
11845
+ ];
11827
11846
  }
11828
11847
  /**
11829
11848
  * Returns all DDL statements for this domain: tables (threads, messages, resources, OM), indexes.
@@ -12244,7 +12263,7 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12244
12263
  const createdAt = toUtcISOString(thread.createdAt);
12245
12264
  const updatedAt = toUtcISOString(thread.updatedAt);
12246
12265
  const metadataJson = thread.metadata ? toPgJson(thread.metadata) : null;
12247
- await this.#fenced(fence, "saveThread", (q) => q.none(`INSERT INTO ${tableName} (
12266
+ const saved = await this.#fenced(fence, "saveThread", (q) => q.oneOrNone(`INSERT INTO ${tableName} AS t (
12248
12267
  id,
12249
12268
  "resourceId",
12250
12269
  title,
@@ -12255,13 +12274,12 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12255
12274
  "updatedAtZ"
12256
12275
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
12257
12276
  ON CONFLICT (id) DO UPDATE SET
12258
- "resourceId" = EXCLUDED."resourceId",
12259
12277
  title = EXCLUDED.title,
12260
12278
  metadata = EXCLUDED.metadata,
12261
- "createdAt" = EXCLUDED."createdAt",
12262
- "createdAtZ" = EXCLUDED."createdAtZ",
12263
12279
  "updatedAt" = EXCLUDED."updatedAt",
12264
- "updatedAtZ" = EXCLUDED."updatedAtZ"`, [
12280
+ "updatedAtZ" = EXCLUDED."updatedAtZ"
12281
+ WHERE t."resourceId" = EXCLUDED."resourceId"
12282
+ RETURNING *`, [
12265
12283
  thread.id,
12266
12284
  thread.resourceId,
12267
12285
  thread.title,
@@ -12271,12 +12289,26 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12271
12289
  updatedAt,
12272
12290
  updatedAt
12273
12291
  ]));
12292
+ if (!saved) throw new _mastra_core_error.MastraError({
12293
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SAVE_THREAD", "RESOURCE_ID_MISMATCH"),
12294
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
12295
+ category: _mastra_core_error.ErrorCategory.USER,
12296
+ text: `Thread ${thread.id} already exists and belongs to a different resource. Use updateThreadResourceId to move a thread and its messages to another resource.`,
12297
+ details: {
12298
+ threadId: thread.id,
12299
+ resourceId: thread.resourceId
12300
+ }
12301
+ });
12274
12302
  return {
12275
- ...thread,
12276
- metadata: metadataJson ? JSON.parse(metadataJson) : thread.metadata
12303
+ id: saved.id,
12304
+ resourceId: saved.resourceId,
12305
+ title: saved.title,
12306
+ metadata: saved.metadata == null ? void 0 : typeof saved.metadata === "string" ? JSON.parse(saved.metadata) : saved.metadata,
12307
+ createdAt: saved.createdAtZ || saved.createdAt,
12308
+ updatedAt: saved.updatedAtZ || saved.updatedAt
12277
12309
  };
12278
12310
  } catch (error) {
12279
- if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
12311
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
12280
12312
  throw new _mastra_core_error.MastraError({
12281
12313
  id: (0, _mastra_core_storage.createStorageErrorId)("PG", "SAVE_THREAD", "FAILED"),
12282
12314
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -12285,52 +12317,125 @@ var MemoryPG = class MemoryPG extends _mastra_core_storage.MemoryStorage {
12285
12317
  }, error);
12286
12318
  }
12287
12319
  }
12288
- async updateThread({ id, title, metadata, fence }) {
12320
+ #toThread(row) {
12321
+ return {
12322
+ id: row.id,
12323
+ resourceId: row.resourceId,
12324
+ title: row.title,
12325
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata,
12326
+ createdAt: row.createdAtZ || row.createdAt,
12327
+ updatedAt: row.updatedAtZ || row.updatedAt
12328
+ };
12329
+ }
12330
+ async #lockThread(t, tableName, { id, resourceId }) {
12331
+ const params = [id];
12332
+ let query = `SELECT * FROM ${tableName} WHERE id = $1`;
12333
+ if (resourceId !== void 0) {
12334
+ query += ` AND "resourceId" = $2`;
12335
+ params.push(resourceId);
12336
+ }
12337
+ const row = await t.oneOrNone(`${query} FOR UPDATE`, params);
12338
+ return row ? this.#toThread(row) : null;
12339
+ }
12340
+ /**
12341
+ * Writes `title` (when given) and the full `metadata` value (when given).
12342
+ * Callers that merge metadata must hold the row lock from `#lockThread`.
12343
+ */
12344
+ async #writeThread(q, tableName, { id, title, metadata }) {
12345
+ const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12346
+ const params = [
12347
+ title ?? null,
12348
+ nowStr,
12349
+ nowStr,
12350
+ id
12351
+ ];
12352
+ let metadataSet = "";
12353
+ if (metadata !== void 0) {
12354
+ params.push(toPgJson(metadata));
12355
+ metadataSet = "metadata = $5,";
12356
+ }
12357
+ const row = await q.oneOrNone(`UPDATE ${tableName}
12358
+ SET
12359
+ title = COALESCE($1, title),
12360
+ ${metadataSet}
12361
+ "updatedAt" = $2,
12362
+ "updatedAtZ" = $3
12363
+ WHERE id = $4
12364
+ RETURNING *`, params);
12365
+ return row ? this.#toThread(row) : null;
12366
+ }
12367
+ /**
12368
+ * Cross-process atomic metadata read-modify-write: the thread row stays locked
12369
+ * (`SELECT ... FOR UPDATE`) from the read through the write, so concurrent
12370
+ * writers from any process serialize instead of overwriting each other.
12371
+ */
12372
+ async updateThreadMetadata({ id, resourceId, update, fence: explicitFence }) {
12373
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence);
12374
+ const threadsTable = getTableName$3({
12375
+ indexName: _mastra_core_storage.TABLE_THREADS,
12376
+ schemaName: getSchemaName$3(this.#schema)
12377
+ });
12378
+ try {
12379
+ return await this.#db.client.tx(async (t) => {
12380
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateThreadMetadata");
12381
+ const thread = await this.#lockThread(t, threadsTable, {
12382
+ id,
12383
+ resourceId
12384
+ });
12385
+ if (!thread) return null;
12386
+ const metadata = update(thread);
12387
+ if (!metadata) return thread;
12388
+ return this.#writeThread(t, threadsTable, {
12389
+ id,
12390
+ metadata: mergeMetadata(thread.metadata, metadata)
12391
+ });
12392
+ });
12393
+ } catch (error) {
12394
+ if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
12395
+ throw new _mastra_core_error.MastraError({
12396
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD_METADATA", "FAILED"),
12397
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
12398
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
12399
+ details: { threadId: id }
12400
+ }, error);
12401
+ }
12402
+ }
12403
+ async updateThread({ id, title, metadata, fence: explicitFence }) {
12289
12404
  const threadTableName = getTableName$3({
12290
12405
  indexName: _mastra_core_storage.TABLE_THREADS,
12291
12406
  schemaName: getSchemaName$3(this.#schema)
12292
12407
  });
12408
+ const notFound = () => new _mastra_core_error.MastraError({
12409
+ id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD", "FAILED"),
12410
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
12411
+ category: _mastra_core_error.ErrorCategory.USER,
12412
+ text: `Thread ${id} not found`,
12413
+ details: {
12414
+ threadId: id,
12415
+ title: title ?? null
12416
+ }
12417
+ });
12293
12418
  try {
12294
- return await this.#fenced(fence, "updateThread", async (q) => {
12295
- const existingThread = await this.#getThreadById(q, { threadId: id });
12296
- if (!existingThread) throw new _mastra_core_error.MastraError({
12297
- id: (0, _mastra_core_storage.createStorageErrorId)("PG", "UPDATE_THREAD", "FAILED"),
12298
- domain: _mastra_core_error.ErrorDomain.STORAGE,
12299
- category: _mastra_core_error.ErrorCategory.USER,
12300
- text: `Thread ${id} not found`,
12301
- details: {
12302
- threadId: id,
12303
- title: title ?? null
12304
- }
12419
+ if (metadata === void 0) return await this.#fenced(explicitFence, "updateThread", async (q) => {
12420
+ const thread = await this.#writeThread(q, threadTableName, {
12421
+ id,
12422
+ title
12305
12423
  });
12306
- const mergedMetadata = {
12307
- ...existingThread.metadata,
12308
- ...metadata
12309
- };
12310
- const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12311
- const thread = await q.one(`UPDATE ${threadTableName}
12312
- SET
12313
- title = COALESCE($1, title),
12314
- metadata = $2,
12315
- "updatedAt" = $3,
12316
- "updatedAtZ" = $4
12317
- WHERE id = $5
12318
- RETURNING *
12319
- `, [
12320
- title ?? null,
12321
- mergedMetadata,
12322
- nowStr,
12323
- nowStr,
12324
- id
12325
- ]);
12326
- return {
12327
- id: thread.id,
12328
- resourceId: thread.resourceId,
12329
- title: thread.title,
12330
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
12331
- createdAt: thread.createdAtZ || thread.createdAt,
12332
- updatedAt: thread.updatedAtZ || thread.updatedAt
12333
- };
12424
+ if (!thread) throw notFound();
12425
+ return thread;
12426
+ });
12427
+ const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence);
12428
+ return await this.#db.client.tx(async (t) => {
12429
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateThread");
12430
+ const existing = await this.#lockThread(t, threadTableName, { id });
12431
+ if (!existing) throw notFound();
12432
+ const thread = await this.#writeThread(t, threadTableName, {
12433
+ id,
12434
+ title,
12435
+ metadata: mergeMetadata(existing.metadata, metadata)
12436
+ });
12437
+ if (!thread) throw notFound();
12438
+ return thread;
12334
12439
  });
12335
12440
  } catch (error) {
12336
12441
  if (error instanceof _mastra_core_error.MastraError || (0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
@@ -17386,8 +17491,12 @@ ${onConflict}`,
17386
17491
  async function insertRows(client, schema, table, records, partitioning, onConflict) {
17387
17492
  const insert = buildInsert(schema, table, records, onConflict);
17388
17493
  if (!insert) return;
17494
+ await withPartitionRetry(client, schema, table, records, partitioning, () => client.query(insert.text, insert.values));
17495
+ }
17496
+ /** Retry a complete write after creating a missing native partition, outside any failed transaction. */
17497
+ async function withPartitionRetry(client, schema, table, records, partitioning, write) {
17389
17498
  try {
17390
- await client.query(insert.text, insert.values);
17499
+ return await write();
17391
17500
  } catch (error) {
17392
17501
  if (!isMissingPartitionError(error) || await resolveMode(client, partitioning) !== "native") throw error;
17393
17502
  const days = withinPartitionWindow(recordDays(records, SIGNAL_TIME_COLUMN[table]), partitioning);
@@ -17400,7 +17509,7 @@ async function insertRows(client, schema, table, records, partitioning, onConfli
17400
17509
  cause
17401
17510
  }), { cause });
17402
17511
  });
17403
- await client.query(insert.text, insert.values);
17512
+ return await write();
17404
17513
  }
17405
17514
  }
17406
17515
  /** `42501`: the role may not create the child, or does not own the parent it must attach to. */
@@ -18334,13 +18443,47 @@ function pushFeedbackIdentity(acc, feedbackType, feedbackSource) {
18334
18443
  acc.conditions.push(`"valueNumber" IS NOT NULL`);
18335
18444
  }
18336
18445
  async function createFeedback(client, schema, args, partitioning) {
18337
- const row = feedbackRecordToRow(args.feedback);
18338
- await insertRows(client, schema, TABLE_FEEDBACK_EVENTS, [row], partitioning, FEEDBACK_UPSERT_CLAUSE);
18446
+ await writeFeedback(client, schema, [args.feedback], args.onConflict, partitioning);
18447
+ }
18448
+ async function replaceFeedback(client, schema, args, partitioning) {
18449
+ await writeFeedback(client, schema, [args.feedback], "replace", partitioning, true);
18339
18450
  }
18340
18451
  async function batchCreateFeedback(client, schema, args, partitioning) {
18341
- if (args.feedbacks.length === 0) return;
18342
- const rows = [...new Map(args.feedbacks.map((feedback) => [feedback.feedbackId, feedback])).values()].map(feedbackRecordToRow);
18343
- await insertRows(client, schema, TABLE_FEEDBACK_EVENTS, rows, partitioning, FEEDBACK_UPSERT_CLAUSE);
18452
+ await writeFeedback(client, schema, args.feedbacks, args.onConflict, partitioning);
18453
+ }
18454
+ /** Serialize feedback writes by ID, including absent IDs and retries with a different timestamp. */
18455
+ async function writeFeedback(client, schema, feedbacks, onConflict, partitioning, preserveIdentity = false) {
18456
+ if (!feedbacks.length) return;
18457
+ const currentFeedback = new Map((onConflict === "ignore" ? [...feedbacks].reverse() : feedbacks).map((feedback) => [feedback.feedbackId, feedback]));
18458
+ const rows = [...currentFeedback.values()].map(feedbackRecordToRow);
18459
+ const ids = [...currentFeedback.keys()].sort();
18460
+ await withPartitionRetry(client, schema, TABLE_FEEDBACK_EVENTS, rows, partitioning, () => client.tx(async (tx) => {
18461
+ await tx.query(`SELECT pg_advisory_xact_lock(hashtextextended(lock_key, 0))
18462
+ FROM unnest($1::text[]) WITH ORDINALITY AS locks(lock_key, position)
18463
+ ORDER BY position`, [ids.map((id) => JSON.stringify([
18464
+ schema,
18465
+ TABLE_FEEDBACK_EVENTS,
18466
+ id
18467
+ ]))]);
18468
+ const existing = await tx.any(`SELECT DISTINCT ON ("feedbackId") "feedbackId", "traceId", "timestamp", "organizationId", "resourceId"
18469
+ FROM ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
18470
+ WHERE "feedbackId" = ANY($1::text[]) ORDER BY "feedbackId", "cursorId" DESC`, [ids]);
18471
+ const byId = new Map(existing.map((row) => [row.feedbackId, row]));
18472
+ const accepted = rows.filter((row) => {
18473
+ const previous = byId.get(row.feedbackId);
18474
+ if (!previous) return true;
18475
+ if (onConflict === "ignore") return false;
18476
+ if (preserveIdentity && (previous.traceId !== row.traceId || new Date(previous.timestamp).getTime() !== new Date(row.timestamp).getTime() || (previous.organizationId ?? null) !== (row.organizationId ?? null) || (previous.resourceId ?? null) !== (row.resourceId ?? null))) throw new _mastra_core_error.MastraError({
18477
+ id: "OBSERVABILITY_FEEDBACK_IDENTITY_CONFLICT",
18478
+ domain: _mastra_core_error.ErrorDomain.MASTRA_OBSERVABILITY,
18479
+ category: _mastra_core_error.ErrorCategory.USER,
18480
+ text: "Feedback replacement must preserve its trace, timestamp, and tenant"
18481
+ });
18482
+ return true;
18483
+ });
18484
+ const insert = buildInsert(schema, TABLE_FEEDBACK_EVENTS, accepted, FEEDBACK_UPSERT_CLAUSE);
18485
+ if (insert) await tx.query(insert.text, insert.values);
18486
+ }));
18344
18487
  }
18345
18488
  async function getFeedbackById(client, schema, feedbackId) {
18346
18489
  const row = await client.oneOrNone(`SELECT ${FEEDBACK_SELECT_COLUMNS} FROM ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
@@ -21818,6 +21961,7 @@ async function dangerouslyClearTracing(client, schema) {
21818
21961
  const spanQueryFeatures = typeof _mastra_core_storage.planSpanQuery === "function" ? [
21819
21962
  "span-query",
21820
21963
  "span-query-context-fields",
21964
+ ..._mastra_core_storage.SPAN_QUERY_ORDER_FIELDS?.includes("durationMs") ? ["span-query-duration-sort"] : [],
21821
21965
  ..."model" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
21822
21966
  ] : [];
21823
21967
  const spanAggregateFeatures = typeof _mastra_core_storage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (_mastra_core_storage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
@@ -22031,6 +22175,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22031
22175
  "thread-query",
22032
22176
  "trace-query-tenant-scope",
22033
22177
  "feedback",
22178
+ "feedback-replacement",
22034
22179
  "trace-query-context-ids",
22035
22180
  ...contextFieldFeatures,
22036
22181
  ...timingFeatures,
@@ -22059,6 +22204,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22059
22204
  "thread-query",
22060
22205
  "trace-query-tenant-scope",
22061
22206
  "feedback",
22207
+ "feedback-replacement",
22062
22208
  "trace-query-context-ids",
22063
22209
  ...contextFieldFeatures,
22064
22210
  ...timingFeatures,
@@ -22158,6 +22304,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22158
22304
  async batchCreateScores(args) {
22159
22305
  await this.#run("BATCH_CREATE_SCORES", () => batchCreateScores(this.#client, this.#schema, args, this.#writePartitioning()), { count: args.scores.length });
22160
22306
  }
22307
+ async writeFeedbackReplacement(args) {
22308
+ await this.#run("REPLACE_FEEDBACK", () => replaceFeedback(this.#client, this.#schema, args, this.#writePartitioning()));
22309
+ }
22161
22310
  async createFeedback(args) {
22162
22311
  await this.#run("CREATE_FEEDBACK", () => createFeedback(this.#client, this.#schema, args, this.#writePartitioning()));
22163
22312
  }
@@ -22183,7 +22332,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22183
22332
  return this.#run("GET_SCORE_BY_ID", () => getScoreById(this.#readClient, this.#schema, scoreId), { scoreId });
22184
22333
  }
22185
22334
  async getFeedbackById(feedbackId) {
22186
- return this.#run("GET_FEEDBACK_BY_ID", () => getFeedbackById(this.#readClient, this.#schema, feedbackId), { feedbackId });
22335
+ return this.#run("GET_FEEDBACK_BY_ID", () => getFeedbackById(this.#client, this.#schema, feedbackId), { feedbackId });
22187
22336
  }
22188
22337
  async listFeedback(args) {
22189
22338
  return this.#run("LIST_FEEDBACK", () => listFeedback(this.#readClient, this.#schema, args));
@@ -22884,6 +23033,8 @@ function rowToSchedule(row) {
22884
23033
  if (metadata !== void 0) schedule.metadata = metadata;
22885
23034
  if (row.owner_type != null) schedule.ownerType = String(row.owner_type);
22886
23035
  if (row.owner_id != null) schedule.ownerId = String(row.owner_id);
23036
+ if (row.run_at != null) schedule.runAt = Number(row.run_at);
23037
+ if (row.end_at != null) schedule.endAt = Number(row.end_at);
22887
23038
  return schedule;
22888
23039
  }
22889
23040
  function rowToTrigger(row) {
@@ -22943,6 +23094,11 @@ var SchedulesPG = class SchedulesPG extends _mastra_core_storage.SchedulesStorag
22943
23094
  tableName: _mastra_core_storage.TABLE_SCHEDULES,
22944
23095
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_SCHEDULES]
22945
23096
  });
23097
+ await this.#db.alterTable({
23098
+ tableName: _mastra_core_storage.TABLE_SCHEDULES,
23099
+ schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_SCHEDULES],
23100
+ ifNotExists: ["run_at", "end_at"]
23101
+ });
22946
23102
  await this.#db.createTable({
22947
23103
  tableName: _mastra_core_storage.TABLE_SCHEDULE_TRIGGERS,
22948
23104
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_SCHEDULE_TRIGGERS]
@@ -23070,7 +23226,9 @@ var SchedulesPG = class SchedulesPG extends _mastra_core_storage.SchedulesStorag
23070
23226
  updated_at: schedule.updatedAt,
23071
23227
  metadata: schedule.metadata ?? null,
23072
23228
  owner_type: schedule.ownerType ?? null,
23073
- owner_id: schedule.ownerId ?? null
23229
+ owner_id: schedule.ownerId ?? null,
23230
+ run_at: schedule.runAt ?? null,
23231
+ end_at: schedule.endAt ?? null
23074
23232
  }
23075
23233
  });
23076
23234
  return schedule;
@@ -23140,6 +23298,8 @@ var SchedulesPG = class SchedulesPG extends _mastra_core_storage.SchedulesStorag
23140
23298
  if ("metadata" in patch) push("metadata = ?::jsonb", patch.metadata != null ? toPgJson(patch.metadata) : null);
23141
23299
  if ("ownerType" in patch) push("owner_type = ?", patch.ownerType ?? null);
23142
23300
  if ("ownerId" in patch) push("owner_id = ?", patch.ownerId ?? null);
23301
+ if ("runAt" in patch) push("run_at = ?", patch.runAt ?? null);
23302
+ if ("endAt" in patch) push("end_at = ?", patch.endAt ?? null);
23143
23303
  push("updated_at = ?", Date.now());
23144
23304
  if (setClauses.length === 1) {
23145
23305
  const existing = await this.#getSchedule(this.#client, id);