@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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +32 -32
- package/dist/docs/references/reference-processors-semantic-recall-processor.md +2 -0
- package/dist/index.cjs +256 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +256 -96
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +12 -1
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/feedback.d.ts +1 -0
- package/dist/storage/domains/observability/v-next/feedback.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/index.d.ts +2 -1
- package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
- package/dist/storage/domains/observability/v-next/sql.d.ts +2 -0
- package/dist/storage/domains/observability/v-next/sql.d.ts.map +1 -1
- package/dist/storage/domains/schedules/index.d.ts.map +1 -1
- package/dist/vector/sql-builder.d.ts.map +1 -1
- package/package.json +2 -2
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
|
|
278
|
-
|
|
279
|
-
|
|
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)
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
created.
|
|
3436
|
-
|
|
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
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
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.
|
|
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
|
-
|
|
12276
|
-
|
|
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
|
-
|
|
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(
|
|
12295
|
-
const
|
|
12296
|
-
|
|
12297
|
-
|
|
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
|
-
|
|
12307
|
-
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
18338
|
-
|
|
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
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
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.#
|
|
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);
|