@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.js CHANGED
@@ -250,31 +250,29 @@ function buildElemMatchConditions(value, paramIndex) {
250
250
  if (typeof value !== "object" || Array.isArray(value)) throw new Error("$elemMatch requires an object with conditions");
251
251
  const conditions = [];
252
252
  const values = [];
253
+ function buildFieldCondition(field, operator, operatorValue) {
254
+ if (operator === "$not") {
255
+ const nested = Object.entries(operatorValue);
256
+ if (nested.length === 0) throw new Error("$not requires at least one operator");
257
+ return `NOT (COALESCE(${nested.map(([nestedOp, nestedValue]) => buildFieldCondition(field, nestedOp, nestedValue)).join(" AND ")}, FALSE))`;
258
+ }
259
+ const operatorFn = FILTER_OPERATORS[operator];
260
+ if (!operatorFn) throw new Error(`Invalid operator: ${operator}`);
261
+ const scalarElement = field.startsWith("$");
262
+ const result = operatorFn(scalarElement ? "" : field, paramIndex + values.length, operatorValue);
263
+ const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>").replaceAll("metadata ?", "elem ?");
264
+ const elementSql = scalarElement ? sql.replaceAll("elem->>''", "elem #>> '{}'").replaceAll("elem->''", "elem") : sql;
265
+ if (result.needsValue) {
266
+ const transformedValue = result.transformValue ? result.transformValue() : operatorValue;
267
+ if (operator === "$elemMatch") values.push(...transformedValue);
268
+ else values.push(transformedValue);
269
+ }
270
+ return elementSql;
271
+ }
253
272
  Object.entries(value).forEach(([field, val]) => {
254
- const nextParamIndex = paramIndex + values.length;
255
- let paramOperator;
256
- let paramKey;
257
- let paramValue;
258
- if (field.startsWith("$")) {
259
- paramOperator = field;
260
- paramKey = "";
261
- paramValue = val;
262
- } else if (typeof val === "object" && !Array.isArray(val)) {
263
- const [op, opValue] = Object.entries(val || {})[0] || [];
264
- paramOperator = op;
265
- paramKey = field;
266
- paramValue = opValue;
267
- } else {
268
- paramOperator = "$eq";
269
- paramKey = field;
270
- paramValue = val;
271
- }
272
- const operatorFn = FILTER_OPERATORS[paramOperator];
273
- if (!operatorFn) throw new Error(`Invalid operator: ${paramOperator}`);
274
- const result = operatorFn(paramKey, nextParamIndex, paramValue);
275
- const sql = result.sql.replaceAll("metadata->>", "elem->>").replaceAll("metadata->", "elem->").replaceAll("metadata#>>", "elem#>>").replaceAll("metadata#>", "elem#>");
276
- conditions.push(sql);
277
- if (result.needsValue) values.push(paramValue);
273
+ const operators = field.startsWith("$") ? [[field, val]] : typeof val === "object" && val !== null && !Array.isArray(val) ? Object.entries(val) : [["$eq", val]];
274
+ if (operators.length === 0) throw new Error(`$elemMatch field ${field} requires at least one operator`);
275
+ for (const [operator, operatorValue] of operators) conditions.push(buildFieldCondition(field, operator, operatorValue));
278
276
  });
279
277
  return {
280
278
  sql: conditions.join(" AND "),
@@ -3405,12 +3403,16 @@ var PgDB = class extends MastraBase {
3405
3403
  }
3406
3404
  if (snapshot) {
3407
3405
  snapshot.tables.add(tableName);
3408
- if (isNewTable) snapshot.createdTables.add(tableName);
3409
- const created = this.snapshotColumns(snapshot, tableName);
3410
- for (const [name, def] of Object.entries(schema)) {
3411
- const parsedName = parseSqlIdentifier(name, "column name");
3412
- created.add(parsedName);
3413
- if (def.type === "timestamp") created.add(`${parsedName}Z`);
3406
+ if (isNewTable) {
3407
+ snapshot.createdTables.add(tableName);
3408
+ snapshot.columns.set(tableName, new Set(await this.getTableColumns(tableName)));
3409
+ } else {
3410
+ const created = this.snapshotColumns(snapshot, tableName);
3411
+ for (const [name, def] of Object.entries(schema)) {
3412
+ const parsedName = parseSqlIdentifier(name, "column name");
3413
+ created.add(parsedName);
3414
+ if (def.type === "timestamp") created.add(`${parsedName}Z`);
3415
+ }
3414
3416
  }
3415
3417
  }
3416
3418
  }
@@ -11655,6 +11657,13 @@ function dedupeMessagesForSave(messages) {
11655
11657
  }
11656
11658
  return Array.from(deduped.values());
11657
11659
  }
11660
+ /** Shallow merge; keys patched to `undefined` are dropped when serialized. Non-object metadata is replaced. */
11661
+ function mergeMetadata(existing, patch) {
11662
+ return {
11663
+ ...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {},
11664
+ ...patch
11665
+ };
11666
+ }
11658
11667
  var MemoryPG = class MemoryPG extends MemoryStorage {
11659
11668
  supportsPartialThreadUpdate = true;
11660
11669
  supportsObservationalMemory = true;
@@ -11792,15 +11801,25 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
11792
11801
  * @param schemaPrefix - Prefix for index names (e.g. "my_schema_" or "")
11793
11802
  */
11794
11803
  static getDefaultIndexDefs(schemaPrefix) {
11795
- return [{
11796
- name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
11797
- table: TABLE_THREADS,
11798
- columns: ["resourceId", "createdAt DESC"]
11799
- }, {
11800
- name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
11801
- table: TABLE_MESSAGES,
11802
- columns: ["thread_id", "createdAt DESC"]
11803
- }];
11804
+ return [
11805
+ {
11806
+ name: `${schemaPrefix}mastra_threads_resourceid_createdat_idx`,
11807
+ table: TABLE_THREADS,
11808
+ columns: ["resourceId", "createdAt DESC"]
11809
+ },
11810
+ {
11811
+ name: `${schemaPrefix}mastra_threads_metadata_gin_idx`,
11812
+ table: TABLE_THREADS,
11813
+ columns: ["metadata"],
11814
+ method: "gin",
11815
+ opclass: "jsonb_path_ops"
11816
+ },
11817
+ {
11818
+ name: `${schemaPrefix}mastra_messages_thread_id_createdat_idx`,
11819
+ table: TABLE_MESSAGES,
11820
+ columns: ["thread_id", "createdAt DESC"]
11821
+ }
11822
+ ];
11804
11823
  }
11805
11824
  /**
11806
11825
  * Returns all DDL statements for this domain: tables (threads, messages, resources, OM), indexes.
@@ -12221,7 +12240,7 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12221
12240
  const createdAt = toUtcISOString(thread.createdAt);
12222
12241
  const updatedAt = toUtcISOString(thread.updatedAt);
12223
12242
  const metadataJson = thread.metadata ? toPgJson(thread.metadata) : null;
12224
- await this.#fenced(fence, "saveThread", (q) => q.none(`INSERT INTO ${tableName} (
12243
+ const saved = await this.#fenced(fence, "saveThread", (q) => q.oneOrNone(`INSERT INTO ${tableName} AS t (
12225
12244
  id,
12226
12245
  "resourceId",
12227
12246
  title,
@@ -12232,13 +12251,12 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12232
12251
  "updatedAtZ"
12233
12252
  ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
12234
12253
  ON CONFLICT (id) DO UPDATE SET
12235
- "resourceId" = EXCLUDED."resourceId",
12236
12254
  title = EXCLUDED.title,
12237
12255
  metadata = EXCLUDED.metadata,
12238
- "createdAt" = EXCLUDED."createdAt",
12239
- "createdAtZ" = EXCLUDED."createdAtZ",
12240
12256
  "updatedAt" = EXCLUDED."updatedAt",
12241
- "updatedAtZ" = EXCLUDED."updatedAtZ"`, [
12257
+ "updatedAtZ" = EXCLUDED."updatedAtZ"
12258
+ WHERE t."resourceId" = EXCLUDED."resourceId"
12259
+ RETURNING *`, [
12242
12260
  thread.id,
12243
12261
  thread.resourceId,
12244
12262
  thread.title,
@@ -12248,12 +12266,26 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12248
12266
  updatedAt,
12249
12267
  updatedAt
12250
12268
  ]));
12269
+ if (!saved) throw new MastraError({
12270
+ id: createStorageErrorId("PG", "SAVE_THREAD", "RESOURCE_ID_MISMATCH"),
12271
+ domain: ErrorDomain.STORAGE,
12272
+ category: ErrorCategory.USER,
12273
+ text: `Thread ${thread.id} already exists and belongs to a different resource. Use updateThreadResourceId to move a thread and its messages to another resource.`,
12274
+ details: {
12275
+ threadId: thread.id,
12276
+ resourceId: thread.resourceId
12277
+ }
12278
+ });
12251
12279
  return {
12252
- ...thread,
12253
- metadata: metadataJson ? JSON.parse(metadataJson) : thread.metadata
12280
+ id: saved.id,
12281
+ resourceId: saved.resourceId,
12282
+ title: saved.title,
12283
+ metadata: saved.metadata == null ? void 0 : typeof saved.metadata === "string" ? JSON.parse(saved.metadata) : saved.metadata,
12284
+ createdAt: saved.createdAtZ || saved.createdAt,
12285
+ updatedAt: saved.updatedAtZ || saved.updatedAt
12254
12286
  };
12255
12287
  } catch (error) {
12256
- if (isRunFenceConflictError(error)) throw error;
12288
+ if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
12257
12289
  throw new MastraError({
12258
12290
  id: createStorageErrorId("PG", "SAVE_THREAD", "FAILED"),
12259
12291
  domain: ErrorDomain.STORAGE,
@@ -12262,52 +12294,125 @@ var MemoryPG = class MemoryPG extends MemoryStorage {
12262
12294
  }, error);
12263
12295
  }
12264
12296
  }
12265
- async updateThread({ id, title, metadata, fence }) {
12297
+ #toThread(row) {
12298
+ return {
12299
+ id: row.id,
12300
+ resourceId: row.resourceId,
12301
+ title: row.title,
12302
+ metadata: typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata,
12303
+ createdAt: row.createdAtZ || row.createdAt,
12304
+ updatedAt: row.updatedAtZ || row.updatedAt
12305
+ };
12306
+ }
12307
+ async #lockThread(t, tableName, { id, resourceId }) {
12308
+ const params = [id];
12309
+ let query = `SELECT * FROM ${tableName} WHERE id = $1`;
12310
+ if (resourceId !== void 0) {
12311
+ query += ` AND "resourceId" = $2`;
12312
+ params.push(resourceId);
12313
+ }
12314
+ const row = await t.oneOrNone(`${query} FOR UPDATE`, params);
12315
+ return row ? this.#toThread(row) : null;
12316
+ }
12317
+ /**
12318
+ * Writes `title` (when given) and the full `metadata` value (when given).
12319
+ * Callers that merge metadata must hold the row lock from `#lockThread`.
12320
+ */
12321
+ async #writeThread(q, tableName, { id, title, metadata }) {
12322
+ const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12323
+ const params = [
12324
+ title ?? null,
12325
+ nowStr,
12326
+ nowStr,
12327
+ id
12328
+ ];
12329
+ let metadataSet = "";
12330
+ if (metadata !== void 0) {
12331
+ params.push(toPgJson(metadata));
12332
+ metadataSet = "metadata = $5,";
12333
+ }
12334
+ const row = await q.oneOrNone(`UPDATE ${tableName}
12335
+ SET
12336
+ title = COALESCE($1, title),
12337
+ ${metadataSet}
12338
+ "updatedAt" = $2,
12339
+ "updatedAtZ" = $3
12340
+ WHERE id = $4
12341
+ RETURNING *`, params);
12342
+ return row ? this.#toThread(row) : null;
12343
+ }
12344
+ /**
12345
+ * Cross-process atomic metadata read-modify-write: the thread row stays locked
12346
+ * (`SELECT ... FOR UPDATE`) from the read through the write, so concurrent
12347
+ * writers from any process serialize instead of overwriting each other.
12348
+ */
12349
+ async updateThreadMetadata({ id, resourceId, update, fence: explicitFence }) {
12350
+ const fence = resolveRunFence(this, explicitFence);
12351
+ const threadsTable = getTableName$3({
12352
+ indexName: TABLE_THREADS,
12353
+ schemaName: getSchemaName$3(this.#schema)
12354
+ });
12355
+ try {
12356
+ return await this.#db.client.tx(async (t) => {
12357
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateThreadMetadata");
12358
+ const thread = await this.#lockThread(t, threadsTable, {
12359
+ id,
12360
+ resourceId
12361
+ });
12362
+ if (!thread) return null;
12363
+ const metadata = update(thread);
12364
+ if (!metadata) return thread;
12365
+ return this.#writeThread(t, threadsTable, {
12366
+ id,
12367
+ metadata: mergeMetadata(thread.metadata, metadata)
12368
+ });
12369
+ });
12370
+ } catch (error) {
12371
+ if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
12372
+ throw new MastraError({
12373
+ id: createStorageErrorId("PG", "UPDATE_THREAD_METADATA", "FAILED"),
12374
+ domain: ErrorDomain.STORAGE,
12375
+ category: ErrorCategory.THIRD_PARTY,
12376
+ details: { threadId: id }
12377
+ }, error);
12378
+ }
12379
+ }
12380
+ async updateThread({ id, title, metadata, fence: explicitFence }) {
12266
12381
  const threadTableName = getTableName$3({
12267
12382
  indexName: TABLE_THREADS,
12268
12383
  schemaName: getSchemaName$3(this.#schema)
12269
12384
  });
12385
+ const notFound = () => new MastraError({
12386
+ id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
12387
+ domain: ErrorDomain.STORAGE,
12388
+ category: ErrorCategory.USER,
12389
+ text: `Thread ${id} not found`,
12390
+ details: {
12391
+ threadId: id,
12392
+ title: title ?? null
12393
+ }
12394
+ });
12270
12395
  try {
12271
- return await this.#fenced(fence, "updateThread", async (q) => {
12272
- const existingThread = await this.#getThreadById(q, { threadId: id });
12273
- if (!existingThread) throw new MastraError({
12274
- id: createStorageErrorId("PG", "UPDATE_THREAD", "FAILED"),
12275
- domain: ErrorDomain.STORAGE,
12276
- category: ErrorCategory.USER,
12277
- text: `Thread ${id} not found`,
12278
- details: {
12279
- threadId: id,
12280
- title: title ?? null
12281
- }
12396
+ if (metadata === void 0) return await this.#fenced(explicitFence, "updateThread", async (q) => {
12397
+ const thread = await this.#writeThread(q, threadTableName, {
12398
+ id,
12399
+ title
12282
12400
  });
12283
- const mergedMetadata = {
12284
- ...existingThread.metadata,
12285
- ...metadata
12286
- };
12287
- const nowStr = toUtcISOString(/* @__PURE__ */ new Date());
12288
- const thread = await q.one(`UPDATE ${threadTableName}
12289
- SET
12290
- title = COALESCE($1, title),
12291
- metadata = $2,
12292
- "updatedAt" = $3,
12293
- "updatedAtZ" = $4
12294
- WHERE id = $5
12295
- RETURNING *
12296
- `, [
12297
- title ?? null,
12298
- mergedMetadata,
12299
- nowStr,
12300
- nowStr,
12301
- id
12302
- ]);
12303
- return {
12304
- id: thread.id,
12305
- resourceId: thread.resourceId,
12306
- title: thread.title,
12307
- metadata: typeof thread.metadata === "string" ? JSON.parse(thread.metadata) : thread.metadata,
12308
- createdAt: thread.createdAtZ || thread.createdAt,
12309
- updatedAt: thread.updatedAtZ || thread.updatedAt
12310
- };
12401
+ if (!thread) throw notFound();
12402
+ return thread;
12403
+ });
12404
+ const fence = resolveRunFence(this, explicitFence);
12405
+ return await this.#db.client.tx(async (t) => {
12406
+ if (fence) await assertRunFence(t, this.#runFencesTable(), fence, "updateThread");
12407
+ const existing = await this.#lockThread(t, threadTableName, { id });
12408
+ if (!existing) throw notFound();
12409
+ const thread = await this.#writeThread(t, threadTableName, {
12410
+ id,
12411
+ title,
12412
+ metadata: mergeMetadata(existing.metadata, metadata)
12413
+ });
12414
+ if (!thread) throw notFound();
12415
+ return thread;
12311
12416
  });
12312
12417
  } catch (error) {
12313
12418
  if (error instanceof MastraError || isRunFenceConflictError(error)) throw error;
@@ -17363,8 +17468,12 @@ ${onConflict}`,
17363
17468
  async function insertRows(client, schema, table, records, partitioning, onConflict) {
17364
17469
  const insert = buildInsert(schema, table, records, onConflict);
17365
17470
  if (!insert) return;
17471
+ await withPartitionRetry(client, schema, table, records, partitioning, () => client.query(insert.text, insert.values));
17472
+ }
17473
+ /** Retry a complete write after creating a missing native partition, outside any failed transaction. */
17474
+ async function withPartitionRetry(client, schema, table, records, partitioning, write) {
17366
17475
  try {
17367
- await client.query(insert.text, insert.values);
17476
+ return await write();
17368
17477
  } catch (error) {
17369
17478
  if (!isMissingPartitionError(error) || await resolveMode(client, partitioning) !== "native") throw error;
17370
17479
  const days = withinPartitionWindow(recordDays(records, SIGNAL_TIME_COLUMN[table]), partitioning);
@@ -17377,7 +17486,7 @@ async function insertRows(client, schema, table, records, partitioning, onConfli
17377
17486
  cause
17378
17487
  }), { cause });
17379
17488
  });
17380
- await client.query(insert.text, insert.values);
17489
+ return await write();
17381
17490
  }
17382
17491
  }
17383
17492
  /** `42501`: the role may not create the child, or does not own the parent it must attach to. */
@@ -18311,13 +18420,47 @@ function pushFeedbackIdentity(acc, feedbackType, feedbackSource) {
18311
18420
  acc.conditions.push(`"valueNumber" IS NOT NULL`);
18312
18421
  }
18313
18422
  async function createFeedback(client, schema, args, partitioning) {
18314
- const row = feedbackRecordToRow(args.feedback);
18315
- await insertRows(client, schema, TABLE_FEEDBACK_EVENTS, [row], partitioning, FEEDBACK_UPSERT_CLAUSE);
18423
+ await writeFeedback(client, schema, [args.feedback], args.onConflict, partitioning);
18424
+ }
18425
+ async function replaceFeedback(client, schema, args, partitioning) {
18426
+ await writeFeedback(client, schema, [args.feedback], "replace", partitioning, true);
18316
18427
  }
18317
18428
  async function batchCreateFeedback(client, schema, args, partitioning) {
18318
- if (args.feedbacks.length === 0) return;
18319
- const rows = [...new Map(args.feedbacks.map((feedback) => [feedback.feedbackId, feedback])).values()].map(feedbackRecordToRow);
18320
- await insertRows(client, schema, TABLE_FEEDBACK_EVENTS, rows, partitioning, FEEDBACK_UPSERT_CLAUSE);
18429
+ await writeFeedback(client, schema, args.feedbacks, args.onConflict, partitioning);
18430
+ }
18431
+ /** Serialize feedback writes by ID, including absent IDs and retries with a different timestamp. */
18432
+ async function writeFeedback(client, schema, feedbacks, onConflict, partitioning, preserveIdentity = false) {
18433
+ if (!feedbacks.length) return;
18434
+ const currentFeedback = new Map((onConflict === "ignore" ? [...feedbacks].reverse() : feedbacks).map((feedback) => [feedback.feedbackId, feedback]));
18435
+ const rows = [...currentFeedback.values()].map(feedbackRecordToRow);
18436
+ const ids = [...currentFeedback.keys()].sort();
18437
+ await withPartitionRetry(client, schema, TABLE_FEEDBACK_EVENTS, rows, partitioning, () => client.tx(async (tx) => {
18438
+ await tx.query(`SELECT pg_advisory_xact_lock(hashtextextended(lock_key, 0))
18439
+ FROM unnest($1::text[]) WITH ORDINALITY AS locks(lock_key, position)
18440
+ ORDER BY position`, [ids.map((id) => JSON.stringify([
18441
+ schema,
18442
+ TABLE_FEEDBACK_EVENTS,
18443
+ id
18444
+ ]))]);
18445
+ const existing = await tx.any(`SELECT DISTINCT ON ("feedbackId") "feedbackId", "traceId", "timestamp", "organizationId", "resourceId"
18446
+ FROM ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
18447
+ WHERE "feedbackId" = ANY($1::text[]) ORDER BY "feedbackId", "cursorId" DESC`, [ids]);
18448
+ const byId = new Map(existing.map((row) => [row.feedbackId, row]));
18449
+ const accepted = rows.filter((row) => {
18450
+ const previous = byId.get(row.feedbackId);
18451
+ if (!previous) return true;
18452
+ if (onConflict === "ignore") return false;
18453
+ 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 MastraError({
18454
+ id: "OBSERVABILITY_FEEDBACK_IDENTITY_CONFLICT",
18455
+ domain: ErrorDomain.MASTRA_OBSERVABILITY,
18456
+ category: ErrorCategory.USER,
18457
+ text: "Feedback replacement must preserve its trace, timestamp, and tenant"
18458
+ });
18459
+ return true;
18460
+ });
18461
+ const insert = buildInsert(schema, TABLE_FEEDBACK_EVENTS, accepted, FEEDBACK_UPSERT_CLAUSE);
18462
+ if (insert) await tx.query(insert.text, insert.values);
18463
+ }));
18321
18464
  }
18322
18465
  async function getFeedbackById(client, schema, feedbackId) {
18323
18466
  const row = await client.oneOrNone(`SELECT ${FEEDBACK_SELECT_COLUMNS} FROM ${qualifiedTable(schema, TABLE_FEEDBACK_EVENTS)}
@@ -21795,6 +21938,7 @@ async function dangerouslyClearTracing(client, schema) {
21795
21938
  const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? [
21796
21939
  "span-query",
21797
21940
  "span-query-context-fields",
21941
+ ...coreStorage.SPAN_QUERY_ORDER_FIELDS?.includes("durationMs") ? ["span-query-duration-sort"] : [],
21798
21942
  ..."model" in (coreStorage.SPAN_QUERY_FIELD_REGISTRY ?? {}) ? ["span-query-model-fields"] : []
21799
21943
  ] : [];
21800
21944
  const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate", ..."model" in (coreStorage.SPAN_AGGREGATE_DIMENSION_REGISTRY ?? {}) ? ["span-aggregate-model-dimensions"] : []] : [];
@@ -22008,6 +22152,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22008
22152
  "thread-query",
22009
22153
  "trace-query-tenant-scope",
22010
22154
  "feedback",
22155
+ "feedback-replacement",
22011
22156
  "trace-query-context-ids",
22012
22157
  ...contextFieldFeatures,
22013
22158
  ...timingFeatures,
@@ -22036,6 +22181,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22036
22181
  "thread-query",
22037
22182
  "trace-query-tenant-scope",
22038
22183
  "feedback",
22184
+ "feedback-replacement",
22039
22185
  "trace-query-context-ids",
22040
22186
  ...contextFieldFeatures,
22041
22187
  ...timingFeatures,
@@ -22135,6 +22281,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22135
22281
  async batchCreateScores(args) {
22136
22282
  await this.#run("BATCH_CREATE_SCORES", () => batchCreateScores(this.#client, this.#schema, args, this.#writePartitioning()), { count: args.scores.length });
22137
22283
  }
22284
+ async writeFeedbackReplacement(args) {
22285
+ await this.#run("REPLACE_FEEDBACK", () => replaceFeedback(this.#client, this.#schema, args, this.#writePartitioning()));
22286
+ }
22138
22287
  async createFeedback(args) {
22139
22288
  await this.#run("CREATE_FEEDBACK", () => createFeedback(this.#client, this.#schema, args, this.#writePartitioning()));
22140
22289
  }
@@ -22160,7 +22309,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
22160
22309
  return this.#run("GET_SCORE_BY_ID", () => getScoreById(this.#readClient, this.#schema, scoreId), { scoreId });
22161
22310
  }
22162
22311
  async getFeedbackById(feedbackId) {
22163
- return this.#run("GET_FEEDBACK_BY_ID", () => getFeedbackById(this.#readClient, this.#schema, feedbackId), { feedbackId });
22312
+ return this.#run("GET_FEEDBACK_BY_ID", () => getFeedbackById(this.#client, this.#schema, feedbackId), { feedbackId });
22164
22313
  }
22165
22314
  async listFeedback(args) {
22166
22315
  return this.#run("LIST_FEEDBACK", () => listFeedback(this.#readClient, this.#schema, args));
@@ -22861,6 +23010,8 @@ function rowToSchedule(row) {
22861
23010
  if (metadata !== void 0) schedule.metadata = metadata;
22862
23011
  if (row.owner_type != null) schedule.ownerType = String(row.owner_type);
22863
23012
  if (row.owner_id != null) schedule.ownerId = String(row.owner_id);
23013
+ if (row.run_at != null) schedule.runAt = Number(row.run_at);
23014
+ if (row.end_at != null) schedule.endAt = Number(row.end_at);
22864
23015
  return schedule;
22865
23016
  }
22866
23017
  function rowToTrigger(row) {
@@ -22920,6 +23071,11 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
22920
23071
  tableName: TABLE_SCHEDULES,
22921
23072
  schema: TABLE_SCHEMAS[TABLE_SCHEDULES]
22922
23073
  });
23074
+ await this.#db.alterTable({
23075
+ tableName: TABLE_SCHEDULES,
23076
+ schema: TABLE_SCHEMAS[TABLE_SCHEDULES],
23077
+ ifNotExists: ["run_at", "end_at"]
23078
+ });
22923
23079
  await this.#db.createTable({
22924
23080
  tableName: TABLE_SCHEDULE_TRIGGERS,
22925
23081
  schema: TABLE_SCHEMAS[TABLE_SCHEDULE_TRIGGERS]
@@ -23047,7 +23203,9 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
23047
23203
  updated_at: schedule.updatedAt,
23048
23204
  metadata: schedule.metadata ?? null,
23049
23205
  owner_type: schedule.ownerType ?? null,
23050
- owner_id: schedule.ownerId ?? null
23206
+ owner_id: schedule.ownerId ?? null,
23207
+ run_at: schedule.runAt ?? null,
23208
+ end_at: schedule.endAt ?? null
23051
23209
  }
23052
23210
  });
23053
23211
  return schedule;
@@ -23117,6 +23275,8 @@ var SchedulesPG = class SchedulesPG extends SchedulesStorage {
23117
23275
  if ("metadata" in patch) push("metadata = ?::jsonb", patch.metadata != null ? toPgJson(patch.metadata) : null);
23118
23276
  if ("ownerType" in patch) push("owner_type = ?", patch.ownerType ?? null);
23119
23277
  if ("ownerId" in patch) push("owner_id = ?", patch.ownerId ?? null);
23278
+ if ("runAt" in patch) push("run_at = ?", patch.runAt ?? null);
23279
+ if ("endAt" in patch) push("end_at = ?", patch.endAt ?? null);
23120
23280
  push("updated_at = ?", Date.now());
23121
23281
  if (setClauses.length === 1) {
23122
23282
  const existing = await this.#getSchedule(this.#client, id);