@mastra/pg 1.35.0 → 1.36.0-alpha.0
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 +158 -2
- package/dist/docs/references/reference-processors-message-history-processor.md +1 -1
- package/dist/docs/references/reference-processors-semantic-recall-processor.md +1 -1
- package/dist/docs/references/reference-tools-vector-query-tool.md +3 -3
- package/dist/index.cjs +779 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +780 -58
- package/dist/index.js.map +1 -1
- package/dist/storage/db/index.d.ts +3 -3
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/workflows/index.d.ts +87 -7
- package/dist/storage/domains/workflows/index.d.ts.map +1 -1
- package/dist/storage/retention.d.ts +3 -1
- package/dist/storage/retention.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -5242,13 +5242,13 @@ function cutoffFor(policy, anchorType, now = Date.now()) {
|
|
|
5242
5242
|
return anchorType === "epoch-ms" ? cutoffMs : new Date(cutoffMs);
|
|
5243
5243
|
}
|
|
5244
5244
|
const runBatchedDelete = _mastra_core_storage.runRetentionBatches;
|
|
5245
|
-
function runPrune({ db, domain, targets, options }) {
|
|
5245
|
+
function runPrune({ db, domain, targets, options, deleteBatch }) {
|
|
5246
5246
|
return (0, _mastra_core_storage.executeRetentionPrune)({
|
|
5247
5247
|
domain,
|
|
5248
5248
|
targets,
|
|
5249
5249
|
options,
|
|
5250
5250
|
cutoffFor: (target, now) => cutoffFor(target.policy, target.anchorType ?? "timestamp", now),
|
|
5251
|
-
deleteBatch: (target, cutoff, limit) => db.pruneBatch({
|
|
5251
|
+
deleteBatch: (target, cutoff, limit) => deleteBatch?.(target, cutoff, limit) ?? db.pruneBatch({
|
|
5252
5252
|
tableName: target.table,
|
|
5253
5253
|
column: target.column,
|
|
5254
5254
|
cutoff,
|
|
@@ -25625,6 +25625,16 @@ function workflowSnapshotStatusIndexSQL(indexName, schemaName, concurrently = fa
|
|
|
25625
25625
|
});
|
|
25626
25626
|
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} (workflow_name, (snapshot ->> 'status'), "createdAt" DESC)`;
|
|
25627
25627
|
}
|
|
25628
|
+
/**
|
|
25629
|
+
* Storage v2 index names share the long `<schema>_mastra_workflow_runs_` prefix, so a long
|
|
25630
|
+
* schema name would collapse them to one 63-byte identifier under plain truncation; hash instead.
|
|
25631
|
+
*/
|
|
25632
|
+
function runIndexName(schemaPrefix, baseName) {
|
|
25633
|
+
return buildConstraintName({
|
|
25634
|
+
baseName: `${schemaPrefix}${baseName}`,
|
|
25635
|
+
hashWhenTruncated: true
|
|
25636
|
+
});
|
|
25637
|
+
}
|
|
25628
25638
|
/** Base name (before any schema prefix) of the expression index backing the threadId filter. */
|
|
25629
25639
|
const WORKFLOW_SNAPSHOT_THREAD_ID_INDEX = "mastra_workflow_snapshot_threadid_idx";
|
|
25630
25640
|
/**
|
|
@@ -25669,13 +25679,126 @@ function workflowSnapshotThreadIdIndexSQL(indexName, schemaName, concurrently =
|
|
|
25669
25679
|
});
|
|
25670
25680
|
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} ((${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR}))`;
|
|
25671
25681
|
}
|
|
25682
|
+
/** Base name (before any schema prefix) of the index finding the `state-base` entries built on a run. */
|
|
25683
|
+
const WORKFLOW_RUN_ENTRIES_STATE_BASE_INDEX = "mastra_workflow_run_entries_state_base_idx";
|
|
25684
|
+
/** The run a `state-base` entry builds on; `#detachStateChildren` must use these exact expressions. */
|
|
25685
|
+
const STATE_BASE_RUN_EXPR = `(payload ->> 'workflowName'), (payload ->> 'runId')`;
|
|
25686
|
+
/**
|
|
25687
|
+
* Partial expression index finding the `state-base` entries built on a run, so deleting a run whose
|
|
25688
|
+
* state others build on doesn't scan every entry. See {@link workflowSnapshotStatusIndexSQL} for
|
|
25689
|
+
* `concurrently`.
|
|
25690
|
+
*/
|
|
25691
|
+
function stateBaseIndexSQL(indexName, schemaName, concurrently = false) {
|
|
25692
|
+
const tableName = getTableName({
|
|
25693
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_RUN_ENTRIES,
|
|
25694
|
+
schemaName: getSchemaName(schemaName)
|
|
25695
|
+
});
|
|
25696
|
+
return `CREATE INDEX ${concurrently ? "CONCURRENTLY " : ""}IF NOT EXISTS "${indexName}" ON ${tableName} (${STATE_BASE_RUN_EXPR}) WHERE kind = 'state-base'`;
|
|
25697
|
+
}
|
|
25698
|
+
/**
|
|
25699
|
+
* The entries a storage v2 write adds: one per step, in order, then one per step patch, then the
|
|
25700
|
+
* state or state base (a checkpoint with neither starts the state over at `{}`), then the state
|
|
25701
|
+
* patch unless a full state was given.
|
|
25702
|
+
*/
|
|
25703
|
+
function journalEntries(steps, { state, stateBase, statePatch }, checkpoint, stepPatches = {}) {
|
|
25704
|
+
const entries = Object.entries(steps).map(([stepId, result]) => ({
|
|
25705
|
+
kind: "step",
|
|
25706
|
+
step_id: stepId,
|
|
25707
|
+
payload: result
|
|
25708
|
+
}));
|
|
25709
|
+
for (const [stepId, ops] of Object.entries(stepPatches)) entries.push({
|
|
25710
|
+
kind: "step-patch",
|
|
25711
|
+
step_id: stepId,
|
|
25712
|
+
payload: ops
|
|
25713
|
+
});
|
|
25714
|
+
if (state !== void 0) entries.push({
|
|
25715
|
+
kind: "state",
|
|
25716
|
+
step_id: null,
|
|
25717
|
+
payload: state
|
|
25718
|
+
});
|
|
25719
|
+
else if (stateBase !== void 0) entries.push({
|
|
25720
|
+
kind: "state-base",
|
|
25721
|
+
step_id: null,
|
|
25722
|
+
payload: stateBase
|
|
25723
|
+
});
|
|
25724
|
+
else if (checkpoint) entries.push({
|
|
25725
|
+
kind: "state",
|
|
25726
|
+
step_id: null,
|
|
25727
|
+
payload: {}
|
|
25728
|
+
});
|
|
25729
|
+
if (state === void 0 && statePatch !== void 0) entries.push({
|
|
25730
|
+
kind: "state-patch",
|
|
25731
|
+
step_id: null,
|
|
25732
|
+
payload: statePatch
|
|
25733
|
+
});
|
|
25734
|
+
return entries;
|
|
25735
|
+
}
|
|
25736
|
+
/**
|
|
25737
|
+
* Rows `e(elem, ord)` of the jsonb array parameter `param` of {@link EntryInput}, and the
|
|
25738
|
+
* entry columns they insert. `elem -> 'payload'` keeps a JSON null apart from an undefined
|
|
25739
|
+
* payload (dropped by `JSON.stringify`), which becomes SQL NULL.
|
|
25740
|
+
*/
|
|
25741
|
+
const entryRows = (param) => `jsonb_array_elements(${param}::jsonb) WITH ORDINALITY AS e(elem, ord)`;
|
|
25742
|
+
const ENTRY_VALUES = `e.elem ->> 'kind', e.elem ->> 'step_id', e.elem -> 'payload'`;
|
|
25743
|
+
/** The entries of {@link EntryRow} rows; an undefined payload reads back as undefined, as in v1. */
|
|
25744
|
+
function toEntries(rows) {
|
|
25745
|
+
return rows.map((row) => {
|
|
25746
|
+
const payload = row.absent ? void 0 : row.payload;
|
|
25747
|
+
switch (row.kind) {
|
|
25748
|
+
case "step": return {
|
|
25749
|
+
seq: row.seq,
|
|
25750
|
+
kind: "step",
|
|
25751
|
+
stepId: row.step_id,
|
|
25752
|
+
result: payload
|
|
25753
|
+
};
|
|
25754
|
+
case "step-patch": return {
|
|
25755
|
+
seq: row.seq,
|
|
25756
|
+
kind: "step-patch",
|
|
25757
|
+
stepId: row.step_id,
|
|
25758
|
+
ops: payload
|
|
25759
|
+
};
|
|
25760
|
+
case "state": return {
|
|
25761
|
+
seq: row.seq,
|
|
25762
|
+
kind: "state",
|
|
25763
|
+
state: payload
|
|
25764
|
+
};
|
|
25765
|
+
case "state-patch": return {
|
|
25766
|
+
seq: row.seq,
|
|
25767
|
+
kind: "state-patch",
|
|
25768
|
+
ops: payload
|
|
25769
|
+
};
|
|
25770
|
+
case "state-base": return {
|
|
25771
|
+
seq: row.seq,
|
|
25772
|
+
kind: "state-base",
|
|
25773
|
+
base: payload
|
|
25774
|
+
};
|
|
25775
|
+
case "state-draft": {
|
|
25776
|
+
const draft = payload;
|
|
25777
|
+
return "state" in draft ? {
|
|
25778
|
+
seq: row.seq,
|
|
25779
|
+
kind: "state-draft",
|
|
25780
|
+
state: draft.state
|
|
25781
|
+
} : {
|
|
25782
|
+
seq: row.seq,
|
|
25783
|
+
kind: "state-draft",
|
|
25784
|
+
on: draft.on,
|
|
25785
|
+
ops: draft.ops
|
|
25786
|
+
};
|
|
25787
|
+
}
|
|
25788
|
+
}
|
|
25789
|
+
});
|
|
25790
|
+
}
|
|
25672
25791
|
var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorage {
|
|
25673
25792
|
#db;
|
|
25674
25793
|
#schema;
|
|
25675
25794
|
#skipDefaultIndexes;
|
|
25676
25795
|
#indexes;
|
|
25677
25796
|
/** Tables managed by this domain */
|
|
25678
|
-
static MANAGED_TABLES = [
|
|
25797
|
+
static MANAGED_TABLES = [
|
|
25798
|
+
_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
25799
|
+
_mastra_core_storage.TABLE_WORKFLOW_RUNS,
|
|
25800
|
+
_mastra_core_storage.TABLE_WORKFLOW_RUN_ENTRIES
|
|
25801
|
+
];
|
|
25679
25802
|
/**
|
|
25680
25803
|
* Workflow run snapshots accumulate as runs execute. Anchored on the
|
|
25681
25804
|
* timezone-aware `updatedAtZ` mirror column (last activity) so suspended or
|
|
@@ -25738,11 +25861,43 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25738
25861
|
};
|
|
25739
25862
|
}
|
|
25740
25863
|
static getDefaultIndexDefs(schemaPrefix) {
|
|
25741
|
-
return [
|
|
25742
|
-
|
|
25743
|
-
|
|
25744
|
-
|
|
25745
|
-
|
|
25864
|
+
return [
|
|
25865
|
+
{
|
|
25866
|
+
name: `${schemaPrefix}mastra_workflow_snapshot_name_createdat_idx`,
|
|
25867
|
+
table: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
25868
|
+
columns: ["workflow_name", "createdAt DESC"]
|
|
25869
|
+
},
|
|
25870
|
+
{
|
|
25871
|
+
name: runIndexName(schemaPrefix, "mastra_workflow_runs_name_createdat_idx"),
|
|
25872
|
+
table: _mastra_core_storage.TABLE_WORKFLOW_RUNS,
|
|
25873
|
+
columns: ["workflow_name", "createdAt DESC"]
|
|
25874
|
+
},
|
|
25875
|
+
{
|
|
25876
|
+
name: runIndexName(schemaPrefix, "mastra_workflow_runs_name_status_createdat_idx"),
|
|
25877
|
+
table: _mastra_core_storage.TABLE_WORKFLOW_RUNS,
|
|
25878
|
+
columns: [
|
|
25879
|
+
"workflow_name",
|
|
25880
|
+
"status",
|
|
25881
|
+
"createdAt DESC"
|
|
25882
|
+
]
|
|
25883
|
+
},
|
|
25884
|
+
{
|
|
25885
|
+
name: runIndexName(schemaPrefix, "mastra_workflow_runs_threadid_idx"),
|
|
25886
|
+
table: _mastra_core_storage.TABLE_WORKFLOW_RUNS,
|
|
25887
|
+
columns: ["threadId"]
|
|
25888
|
+
},
|
|
25889
|
+
{
|
|
25890
|
+
name: runIndexName(schemaPrefix, "mastra_workflow_run_entries_step_idx"),
|
|
25891
|
+
table: _mastra_core_storage.TABLE_WORKFLOW_RUN_ENTRIES,
|
|
25892
|
+
columns: [
|
|
25893
|
+
"workflow_name",
|
|
25894
|
+
"run_id",
|
|
25895
|
+
"kind",
|
|
25896
|
+
"step_id",
|
|
25897
|
+
"seq"
|
|
25898
|
+
]
|
|
25899
|
+
}
|
|
25900
|
+
];
|
|
25746
25901
|
}
|
|
25747
25902
|
/**
|
|
25748
25903
|
* Returns all DDL statements for this domain: table with unique constraint.
|
|
@@ -25758,9 +25913,17 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25758
25913
|
schemaName,
|
|
25759
25914
|
includeAllConstraints: true
|
|
25760
25915
|
}));
|
|
25916
|
+
for (const [tableName, config] of Object.entries(_mastra_core_storage.WORKFLOW_RUN_JOURNAL_TABLE_CONFIGS)) statements.push(generateTableSQL({
|
|
25917
|
+
tableName,
|
|
25918
|
+
schema: config.columns,
|
|
25919
|
+
schemaName,
|
|
25920
|
+
compositePrimaryKey: config.compositePrimaryKey,
|
|
25921
|
+
includeAllConstraints: true
|
|
25922
|
+
}));
|
|
25761
25923
|
for (const idx of WorkflowsPG.getDefaultIndexDefs(schemaPrefix)) statements.push(generateIndexSQL(idx, schemaName));
|
|
25762
25924
|
statements.push(`${workflowSnapshotStatusIndexSQL(workflowSnapshotStatusIndexName(parsedSchema), schemaName)};`);
|
|
25763
25925
|
statements.push(`${workflowSnapshotThreadIdIndexSQL(workflowSnapshotThreadIdIndexName(parsedSchema), schemaName)};`);
|
|
25926
|
+
statements.push(`${stateBaseIndexSQL(runIndexName(schemaPrefix, WORKFLOW_RUN_ENTRIES_STATE_BASE_INDEX), schemaName)};`);
|
|
25764
25927
|
statements.push(generateTableSQL({
|
|
25765
25928
|
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
25766
25929
|
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS],
|
|
@@ -25786,6 +25949,12 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25786
25949
|
} catch (error) {
|
|
25787
25950
|
this.logger?.warn?.(`Failed to create index ${indexDef.name}:`, error);
|
|
25788
25951
|
}
|
|
25952
|
+
const stateBaseIndexName = runIndexName(this.#schema !== "public" ? `${schemaNamePrefix(this.#schema)}_` : "", WORKFLOW_RUN_ENTRIES_STATE_BASE_INDEX);
|
|
25953
|
+
try {
|
|
25954
|
+
await this.#db.createIndexFromStatement(stateBaseIndexName, stateBaseIndexSQL(stateBaseIndexName, this.#schema, true));
|
|
25955
|
+
} catch (error) {
|
|
25956
|
+
this.logger?.warn?.(`Failed to create index ${stateBaseIndexName}:`, error);
|
|
25957
|
+
}
|
|
25789
25958
|
if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") !== "jsonb") return;
|
|
25790
25959
|
const indexName = workflowSnapshotStatusIndexName(this.#schema);
|
|
25791
25960
|
try {
|
|
@@ -25814,6 +25983,11 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25814
25983
|
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS,
|
|
25815
25984
|
schema: _mastra_core_storage.RUN_FENCING_TABLE_SCHEMAS[_mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS]
|
|
25816
25985
|
});
|
|
25986
|
+
for (const [tableName, config] of Object.entries(_mastra_core_storage.WORKFLOW_RUN_JOURNAL_TABLE_CONFIGS)) await this.#db.createTable({
|
|
25987
|
+
tableName,
|
|
25988
|
+
schema: config.columns,
|
|
25989
|
+
compositePrimaryKey: config.compositePrimaryKey
|
|
25990
|
+
});
|
|
25817
25991
|
await this.createDefaultIndexes();
|
|
25818
25992
|
await this.createCustomIndexes();
|
|
25819
25993
|
}
|
|
@@ -25840,8 +26014,22 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25840
26014
|
this.logger?.warn?.(`Failed to create retention index for ${entry.table}:`, error);
|
|
25841
26015
|
}
|
|
25842
26016
|
}
|
|
26017
|
+
if (!policies.workflowSnapshot) return;
|
|
26018
|
+
try {
|
|
26019
|
+
await this.#db.ensureIndex({
|
|
26020
|
+
indexName: `${prefix}mastra_workflow_runs_retention_idx`,
|
|
26021
|
+
tableName: _mastra_core_storage.TABLE_WORKFLOW_RUNS,
|
|
26022
|
+
column: "updatedAtZ"
|
|
26023
|
+
});
|
|
26024
|
+
} catch (error) {
|
|
26025
|
+
this.logger?.warn?.(`Failed to create retention index for ${_mastra_core_storage.TABLE_WORKFLOW_RUNS}:`, error);
|
|
26026
|
+
}
|
|
25843
26027
|
}
|
|
25844
|
-
/**
|
|
26028
|
+
/**
|
|
26029
|
+
* Delete workflow runs and run ownership rows older than their policy's `maxAge`, batched.
|
|
26030
|
+
* The `workflowSnapshot` policy covers both storage versions: storage v2 runs are pruned by
|
|
26031
|
+
* their header's last write, together with their entries.
|
|
26032
|
+
*/
|
|
25845
26033
|
async prune(policies, options) {
|
|
25846
26034
|
await this.ensureRetentionIndexes(policies);
|
|
25847
26035
|
const targets = resolveTargets({
|
|
@@ -25849,11 +26037,40 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25849
26037
|
descriptor: WorkflowsPG.retentionTables,
|
|
25850
26038
|
order: ["workflowSnapshot", "runOwnership"]
|
|
25851
26039
|
});
|
|
26040
|
+
const snapshotTarget = targets.findIndex((target) => target.table === _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT);
|
|
26041
|
+
if (snapshotTarget !== -1) targets.splice(snapshotTarget + 1, 0, {
|
|
26042
|
+
...targets[snapshotTarget],
|
|
26043
|
+
table: _mastra_core_storage.TABLE_WORKFLOW_RUNS
|
|
26044
|
+
});
|
|
25852
26045
|
return runPrune({
|
|
25853
26046
|
db: this.#db,
|
|
25854
26047
|
domain: "workflows",
|
|
25855
26048
|
targets,
|
|
25856
|
-
options
|
|
26049
|
+
options,
|
|
26050
|
+
deleteBatch: (target, cutoff, limit) => target.table === _mastra_core_storage.TABLE_WORKFLOW_RUNS ? this.#pruneRunsBatch(cutoff, limit) : void 0
|
|
26051
|
+
});
|
|
26052
|
+
}
|
|
26053
|
+
/**
|
|
26054
|
+
* Deletes up to `limit` aged storage v2 runs, header and entries together, first detaching the
|
|
26055
|
+
* runs whose state others build on (see `#detachStateChildren`).
|
|
26056
|
+
*/
|
|
26057
|
+
async #pruneRunsBatch(cutoff, limit) {
|
|
26058
|
+
return this.#db.client.tx(async (t) => {
|
|
26059
|
+
const doomed = await t.manyOrNone(`SELECT workflow_name, run_id, "stateShared" FROM ${this.#runsTable()}
|
|
26060
|
+
WHERE "updatedAtZ" < $1
|
|
26061
|
+
ORDER BY "updatedAtZ"
|
|
26062
|
+
LIMIT $2
|
|
26063
|
+
FOR UPDATE SKIP LOCKED`, [cutoff, limit]);
|
|
26064
|
+
if (!doomed.length) return 0;
|
|
26065
|
+
for (const run of doomed) if (run.stateShared) await this.#detachStateChildren(t, run.workflow_name, run.run_id);
|
|
26066
|
+
return (await t.query(`WITH doomed AS (
|
|
26067
|
+
SELECT * FROM unnest($1::text[], $2::text[]) AS d(workflow_name, run_id)
|
|
26068
|
+
), entries AS (
|
|
26069
|
+
DELETE FROM ${this.#entriesTable()} e USING doomed d
|
|
26070
|
+
WHERE e.workflow_name = d.workflow_name AND e.run_id = d.run_id
|
|
26071
|
+
)
|
|
26072
|
+
DELETE FROM ${this.#runsTable()} r USING doomed d
|
|
26073
|
+
WHERE r.workflow_name = d.workflow_name AND r.run_id = d.run_id`, [doomed.map((run) => run.workflow_name), doomed.map((run) => run.run_id)])).rowCount ?? 0;
|
|
25857
26074
|
});
|
|
25858
26075
|
}
|
|
25859
26076
|
/**
|
|
@@ -25870,6 +26087,459 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25870
26087
|
async dangerouslyClearAll() {
|
|
25871
26088
|
await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT });
|
|
25872
26089
|
await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_OWNERS });
|
|
26090
|
+
await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUNS });
|
|
26091
|
+
await this.#db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_RUN_ENTRIES });
|
|
26092
|
+
}
|
|
26093
|
+
supportsRunJournal() {
|
|
26094
|
+
return true;
|
|
26095
|
+
}
|
|
26096
|
+
#runsTable() {
|
|
26097
|
+
return getTableName({
|
|
26098
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_RUNS,
|
|
26099
|
+
schemaName: getSchemaName(this.#schema)
|
|
26100
|
+
});
|
|
26101
|
+
}
|
|
26102
|
+
#entriesTable() {
|
|
26103
|
+
return getTableName({
|
|
26104
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_RUN_ENTRIES,
|
|
26105
|
+
schemaName: getSchemaName(this.#schema)
|
|
26106
|
+
});
|
|
26107
|
+
}
|
|
26108
|
+
/**
|
|
26109
|
+
* A jsonb array of {@link EntryRow} for the storage v2 run `alias` refers to: each step's
|
|
26110
|
+
* latest entry, ordered by when each step was first written so `context` keys keep their write
|
|
26111
|
+
* order, the step patches after it (in `seq` order, for `latestStepResults`), and the entries
|
|
26112
|
+
* `materializeState` uses for the latest state: the latest full state or state base and every
|
|
26113
|
+
* state patch after it, in `seq` order. Drafts are left out.
|
|
26114
|
+
*
|
|
26115
|
+
* With `summaryFieldsParam` (the parameter holding `STEP_RESULT_SUMMARY_FIELDS`), successful
|
|
26116
|
+
* steps other than `input` come back as their summary, built here so their data is never sent,
|
|
26117
|
+
* unless step patches follow it (the caller summarizes the patched result instead).
|
|
26118
|
+
* Like `summarizeStepResult`, it keeps a field that is JSON null and leaves out one that is absent.
|
|
26119
|
+
*/
|
|
26120
|
+
#entriesSql(alias, { summaryFieldsParam } = {}) {
|
|
26121
|
+
let payload = "x.payload";
|
|
26122
|
+
if (summaryFieldsParam) {
|
|
26123
|
+
const summarized = `(x.kind = 'step' AND x.step_id <> 'input' AND x.payload ->> 'status' = 'success'
|
|
26124
|
+
AND (x.patch_seq IS NULL OR x.patch_seq < x.seq))`;
|
|
26125
|
+
payload = `CASE WHEN ${summarized} THEN (
|
|
26126
|
+
SELECT COALESCE(jsonb_object_agg(f.key, f.value), '{}'::jsonb)
|
|
26127
|
+
FROM jsonb_each(x.payload) f WHERE f.key = ANY(${summaryFieldsParam}::text[])
|
|
26128
|
+
) ELSE x.payload END, 'summarized', ${summarized}`;
|
|
26129
|
+
}
|
|
26130
|
+
return `(SELECT COALESCE(jsonb_agg(jsonb_build_object(
|
|
26131
|
+
'seq', x.seq, 'kind', x.kind, 'step_id', x.step_id, 'absent', x.payload IS NULL, 'payload', ${payload}
|
|
26132
|
+
) ORDER BY CASE WHEN x.kind = 'step' THEN x.first_seq ELSE x.seq END), '[]'::jsonb)
|
|
26133
|
+
FROM (
|
|
26134
|
+
SELECT e.seq, e.kind, e.step_id, e.payload,
|
|
26135
|
+
ROW_NUMBER() OVER (PARTITION BY e.kind, e.step_id ORDER BY e.seq DESC) AS rn,
|
|
26136
|
+
MIN(e.seq) OVER (PARTITION BY e.kind, e.step_id) AS first_seq,
|
|
26137
|
+
MAX(CASE WHEN e.kind = 'step' THEN e.seq END) OVER (PARTITION BY e.step_id) AS step_seq,
|
|
26138
|
+
MAX(CASE WHEN e.kind = 'step-patch' THEN e.seq END) OVER (PARTITION BY e.step_id) AS patch_seq,
|
|
26139
|
+
MAX(CASE WHEN e.kind IN ('state', 'state-base') THEN e.seq END) OVER () AS base_seq
|
|
26140
|
+
FROM ${this.#entriesTable()} e
|
|
26141
|
+
WHERE e.workflow_name = ${alias}.workflow_name AND e.run_id = ${alias}.run_id AND e.kind <> 'state-draft'
|
|
26142
|
+
) x
|
|
26143
|
+
WHERE CASE x.kind
|
|
26144
|
+
WHEN 'step' THEN x.rn = 1
|
|
26145
|
+
WHEN 'step-patch' THEN x.seq > x.step_seq
|
|
26146
|
+
ELSE x.base_seq IS NULL OR x.seq >= x.base_seq
|
|
26147
|
+
END)`;
|
|
26148
|
+
}
|
|
26149
|
+
/**
|
|
26150
|
+
* A jsonb array of {@link EntryRow} with the entries `materializeState` uses for the state of the
|
|
26151
|
+
* storage v2 run `alias` refers to as of a version: `seqParam` its `seq`, `draftParam` whether
|
|
26152
|
+
* it is a draft. For a draft, every state entry up to it, as the drafts it is on can build on
|
|
26153
|
+
* any earlier version; otherwise the latest full state or state base up to it and the state
|
|
26154
|
+
* patches after it.
|
|
26155
|
+
*/
|
|
26156
|
+
#stateEntriesSql(alias, seqParam, draftParam) {
|
|
26157
|
+
return `(SELECT COALESCE(jsonb_agg(jsonb_build_object(
|
|
26158
|
+
'seq', x.seq, 'kind', x.kind, 'step_id', x.step_id, 'absent', x.payload IS NULL, 'payload', x.payload
|
|
26159
|
+
) ORDER BY x.seq), '[]'::jsonb)
|
|
26160
|
+
FROM (
|
|
26161
|
+
SELECT e.seq, e.kind, e.step_id, e.payload,
|
|
26162
|
+
MAX(CASE WHEN e.kind IN ('state', 'state-base') THEN e.seq END) OVER () AS base_seq
|
|
26163
|
+
FROM ${this.#entriesTable()} e
|
|
26164
|
+
WHERE e.workflow_name = ${alias}.workflow_name AND e.run_id = ${alias}.run_id
|
|
26165
|
+
AND e.kind NOT IN ('step', 'step-patch') AND e.seq <= ${seqParam}::int
|
|
26166
|
+
) x
|
|
26167
|
+
WHERE ${draftParam}::boolean OR (x.kind <> 'state-draft' AND (x.base_seq IS NULL OR x.seq >= x.base_seq)))`;
|
|
26168
|
+
}
|
|
26169
|
+
/**
|
|
26170
|
+
* Selects v1 snapshot rows and storage v2 runs as one result, newest first. Each version
|
|
26171
|
+
* has its own WHERE clause; `null` leaves it out. Storage v2 rows carry their header and
|
|
26172
|
+
* entries, or in summary mode only the summary snapshot. `page` is appended to the
|
|
26173
|
+
* combined, ordered rows (e.g. `LIMIT 1`).
|
|
26174
|
+
*/
|
|
26175
|
+
async #runsQuery({ v1Where, v2Where, v1Snapshot = "snapshot", summary, page = "" }) {
|
|
26176
|
+
const branches = [];
|
|
26177
|
+
if (v1Where !== null) {
|
|
26178
|
+
const optional = {
|
|
26179
|
+
resourceId: "text",
|
|
26180
|
+
createdAtZ: "timestamptz",
|
|
26181
|
+
updatedAtZ: "timestamptz"
|
|
26182
|
+
};
|
|
26183
|
+
const columns = {};
|
|
26184
|
+
for (const [column, type] of Object.entries(optional)) columns[column] = await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, column) !== null ? `"${column}"` : `NULL::${type} AS "${column}"`;
|
|
26185
|
+
branches.push(`SELECT workflow_name, run_id, ${columns.resourceId}, ${v1Snapshot} AS snapshot, NULL::jsonb AS header,
|
|
26186
|
+
"createdAt", ${columns.createdAtZ}, "updatedAt", ${columns.updatedAtZ}, 1 AS version
|
|
26187
|
+
FROM ${getTableName({
|
|
26188
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26189
|
+
schemaName: getSchemaName(this.#schema)
|
|
26190
|
+
})} ${v1Where}`);
|
|
26191
|
+
}
|
|
26192
|
+
if (v2Where !== null) {
|
|
26193
|
+
const columns = summary ? `jsonb_build_object('status', header -> 'status', 'timestamp', header -> 'timestamp') AS snapshot, NULL::jsonb AS header` : `NULL AS snapshot, header`;
|
|
26194
|
+
branches.push(`SELECT workflow_name, run_id, "resourceId", ${columns},
|
|
26195
|
+
"createdAt", "createdAtZ", "updatedAt", "updatedAtZ", 2 AS version
|
|
26196
|
+
FROM ${this.#runsTable()} ${v2Where}`);
|
|
26197
|
+
}
|
|
26198
|
+
return `SELECT p.*${summary ? "" : `, CASE WHEN p.version = 2 THEN ${this.#entriesSql("p")} END AS entries`}
|
|
26199
|
+
FROM (${branches.join(" UNION ALL ")} ORDER BY "createdAt" DESC ${page}) p
|
|
26200
|
+
ORDER BY p."createdAt" DESC`;
|
|
26201
|
+
}
|
|
26202
|
+
/**
|
|
26203
|
+
* The `base` that `materializeState` takes for a run's entries (as of `at`, or latest): the state
|
|
26204
|
+
* version of another run it builds on (`stateBaseOf`), resolved recursively, else undefined. `{}`
|
|
26205
|
+
* when that run isn't stored. One query per run in the chain.
|
|
26206
|
+
*/
|
|
26207
|
+
async #stateBase(q, entries, at) {
|
|
26208
|
+
const ref = (0, _mastra_core_storage.stateBaseOf)(entries, at);
|
|
26209
|
+
if (!ref) return void 0;
|
|
26210
|
+
return (await this.#stateAt(q, ref.workflowName, ref.runId, ref))?.state ?? {};
|
|
26211
|
+
}
|
|
26212
|
+
/** A storage v2 run's state as of the version `at`, or null when the run isn't stored. */
|
|
26213
|
+
async #stateAt(q, workflowName, runId, at) {
|
|
26214
|
+
const row = await q.oneOrNone(`SELECT ${this.#stateEntriesSql("r", "$3", "$4")} AS entries
|
|
26215
|
+
FROM ${this.#runsTable()} r WHERE r.workflow_name = $1 AND r.run_id = $2`, [
|
|
26216
|
+
workflowName,
|
|
26217
|
+
runId,
|
|
26218
|
+
at.seq,
|
|
26219
|
+
at.draft === true
|
|
26220
|
+
]);
|
|
26221
|
+
if (!row) return null;
|
|
26222
|
+
const entries = toEntries(row.entries);
|
|
26223
|
+
return (0, _mastra_core_storage.materializeState)(entries, {
|
|
26224
|
+
at,
|
|
26225
|
+
base: await this.#stateBase(q, entries, at)
|
|
26226
|
+
});
|
|
26227
|
+
}
|
|
26228
|
+
/**
|
|
26229
|
+
* Before a run is deleted, stores the full state in place of every `state-base` entry built on
|
|
26230
|
+
* it, so the runs built on it keep reading the same state. Runs inside the delete's transaction.
|
|
26231
|
+
*/
|
|
26232
|
+
async #detachStateChildren(q, workflowName, runId) {
|
|
26233
|
+
const children = await q.manyOrNone(`SELECT workflow_name, run_id, seq, payload FROM ${this.#entriesTable()}
|
|
26234
|
+
WHERE kind = 'state-base' AND (${STATE_BASE_RUN_EXPR}) = ($1, $2)`, [workflowName, runId]);
|
|
26235
|
+
const states = /* @__PURE__ */ new Map();
|
|
26236
|
+
for (const child of children) {
|
|
26237
|
+
const version = `${child.payload.seq}:${child.payload.draft === true}`;
|
|
26238
|
+
let state = states.get(version);
|
|
26239
|
+
if (!state) {
|
|
26240
|
+
state = (await this.#stateAt(q, workflowName, runId, child.payload))?.state ?? {};
|
|
26241
|
+
states.set(version, state);
|
|
26242
|
+
}
|
|
26243
|
+
await q.none(`UPDATE ${this.#entriesTable()} SET kind = 'state', payload = $4::jsonb
|
|
26244
|
+
WHERE workflow_name = $1 AND run_id = $2 AND seq = $3`, [
|
|
26245
|
+
child.workflow_name,
|
|
26246
|
+
child.run_id,
|
|
26247
|
+
child.seq,
|
|
26248
|
+
toPgJson(state)
|
|
26249
|
+
]);
|
|
26250
|
+
}
|
|
26251
|
+
}
|
|
26252
|
+
/** Rebuilds a storage v2 run's `WorkflowRunState` from its header and `#entriesSql` rows. */
|
|
26253
|
+
async #materializeRun(client, header, rows) {
|
|
26254
|
+
const entries = toEntries(rows);
|
|
26255
|
+
return (0, _mastra_core_storage.materializeWorkflowRun)(header, entries, await this.#stateBase(client, entries));
|
|
26256
|
+
}
|
|
26257
|
+
/** Parses a row of `#runsQuery`. */
|
|
26258
|
+
async #parseRun(client, row) {
|
|
26259
|
+
if (row.version !== 2) return this.parseWorkflowRun(row);
|
|
26260
|
+
const snapshot = row.entries ? await this.#materializeRun(client, row.header, row.entries) : row.snapshot;
|
|
26261
|
+
return {
|
|
26262
|
+
...this.parseWorkflowRun({
|
|
26263
|
+
...row,
|
|
26264
|
+
snapshot
|
|
26265
|
+
}),
|
|
26266
|
+
storageVersion: 2
|
|
26267
|
+
};
|
|
26268
|
+
}
|
|
26269
|
+
#runError(operation, workflowName, runId, error) {
|
|
26270
|
+
return new _mastra_core_error.MastraError({
|
|
26271
|
+
id: (0, _mastra_core_storage.createStorageErrorId)("PG", operation, "FAILED"),
|
|
26272
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
26273
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
26274
|
+
details: {
|
|
26275
|
+
workflowName,
|
|
26276
|
+
runId
|
|
26277
|
+
}
|
|
26278
|
+
}, error);
|
|
26279
|
+
}
|
|
26280
|
+
async loadWorkflowRunHeader({ workflowName, runId }) {
|
|
26281
|
+
try {
|
|
26282
|
+
return await this.#db.client.oneOrNone(`SELECT header, "lastSeq", "startStateSeq", "runToken" FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2`, [workflowName, runId]);
|
|
26283
|
+
} catch (error) {
|
|
26284
|
+
throw this.#runError("LOAD_WORKFLOW_RUN_HEADER", workflowName, runId, error);
|
|
26285
|
+
}
|
|
26286
|
+
}
|
|
26287
|
+
async loadWorkflowRun({ workflowName, runId, successfulSteps }) {
|
|
26288
|
+
try {
|
|
26289
|
+
const summary = successfulSteps === "summary";
|
|
26290
|
+
const run = await this.#db.client.oneOrNone(`SELECT r.header, r."lastSeq", r."startStateSeq", r."runToken", ${this.#entriesSql("r", { summaryFieldsParam: summary ? "$3" : void 0 })} AS entries
|
|
26291
|
+
FROM ${this.#runsTable()} r WHERE r.workflow_name = $1 AND r.run_id = $2`, summary ? [
|
|
26292
|
+
workflowName,
|
|
26293
|
+
runId,
|
|
26294
|
+
_mastra_core_storage.STEP_RESULT_SUMMARY_FIELDS
|
|
26295
|
+
] : [workflowName, runId]);
|
|
26296
|
+
if (!run) return null;
|
|
26297
|
+
const entries = toEntries(run.entries);
|
|
26298
|
+
const summarizedInQuery = new Set(run.entries.filter((row) => row.summarized).map((row) => row.step_id));
|
|
26299
|
+
const steps = {};
|
|
26300
|
+
const summarizedStepIds = [];
|
|
26301
|
+
for (const [stepId, result] of (0, _mastra_core_storage.latestStepResults)(entries)) {
|
|
26302
|
+
steps[stepId] = result;
|
|
26303
|
+
if (summary && stepId !== "input" && result?.status === "success") {
|
|
26304
|
+
if (!summarizedInQuery.has(stepId)) steps[stepId] = (0, _mastra_core_storage.summarizeStepResult)(result);
|
|
26305
|
+
summarizedStepIds.push(stepId);
|
|
26306
|
+
}
|
|
26307
|
+
}
|
|
26308
|
+
const base = await this.#stateBase(this.#db.client, entries);
|
|
26309
|
+
const { state, statePatchBytes } = (0, _mastra_core_storage.materializeState)(entries, { base });
|
|
26310
|
+
return {
|
|
26311
|
+
header: run.header,
|
|
26312
|
+
lastSeq: run.lastSeq,
|
|
26313
|
+
startStateSeq: run.startStateSeq,
|
|
26314
|
+
runToken: run.runToken,
|
|
26315
|
+
state,
|
|
26316
|
+
statePatchBytes,
|
|
26317
|
+
steps,
|
|
26318
|
+
summarizedStepIds
|
|
26319
|
+
};
|
|
26320
|
+
} catch (error) {
|
|
26321
|
+
throw this.#runError("LOAD_WORKFLOW_RUN", workflowName, runId, error);
|
|
26322
|
+
}
|
|
26323
|
+
}
|
|
26324
|
+
async loadWorkflowRunSteps({ workflowName, runId, stepIds, expectedLastSeq, expectedRunToken }) {
|
|
26325
|
+
try {
|
|
26326
|
+
const run = await this.#db.client.oneOrNone(`SELECT r."lastSeq", r."runToken", (
|
|
26327
|
+
SELECT COALESCE(jsonb_agg(jsonb_build_object(
|
|
26328
|
+
'seq', x.seq, 'kind', x.kind, 'step_id', x.step_id, 'absent', x.payload IS NULL, 'payload', x.payload
|
|
26329
|
+
) ORDER BY x.seq), '[]'::jsonb)
|
|
26330
|
+
FROM (
|
|
26331
|
+
SELECT e.seq, e.kind, e.step_id, e.payload,
|
|
26332
|
+
MAX(CASE WHEN e.kind = 'step' THEN e.seq END) OVER (PARTITION BY e.step_id) AS step_seq
|
|
26333
|
+
FROM ${this.#entriesTable()} e
|
|
26334
|
+
WHERE e.workflow_name = r.workflow_name AND e.run_id = r.run_id
|
|
26335
|
+
AND e.kind IN ('step', 'step-patch') AND e.step_id = ANY($3::text[])
|
|
26336
|
+
) x
|
|
26337
|
+
WHERE x.seq >= x.step_seq
|
|
26338
|
+
) AS entries
|
|
26339
|
+
FROM ${this.#runsTable()} r WHERE r.workflow_name = $1 AND r.run_id = $2`, [
|
|
26340
|
+
workflowName,
|
|
26341
|
+
runId,
|
|
26342
|
+
stepIds
|
|
26343
|
+
]);
|
|
26344
|
+
if (!run) return null;
|
|
26345
|
+
if (expectedLastSeq !== void 0 && run.lastSeq !== expectedLastSeq) return null;
|
|
26346
|
+
if (expectedRunToken !== void 0 && run.runToken !== expectedRunToken) return null;
|
|
26347
|
+
return Object.fromEntries((0, _mastra_core_storage.latestStepResults)(toEntries(run.entries)));
|
|
26348
|
+
} catch (error) {
|
|
26349
|
+
throw this.#runError("LOAD_WORKFLOW_RUN_STEPS", workflowName, runId, error);
|
|
26350
|
+
}
|
|
26351
|
+
}
|
|
26352
|
+
async updateWorkflowRunHeader({ workflowName, runId, opts, fence: explicitFence }) {
|
|
26353
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
26354
|
+
try {
|
|
26355
|
+
return await this.#db.client.tx(async (t) => {
|
|
26356
|
+
if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "updateWorkflowRunHeader");
|
|
26357
|
+
const run = await t.oneOrNone(`SELECT header FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2 FOR UPDATE`, [workflowName, runId]);
|
|
26358
|
+
if (!run) return void 0;
|
|
26359
|
+
const { expectedStatus, ...patch } = opts;
|
|
26360
|
+
if (!(0, _mastra_core_storage.matchesExpectedWorkflowStatus)(run.header.status, expectedStatus)) return void 0;
|
|
26361
|
+
const header = {
|
|
26362
|
+
...run.header,
|
|
26363
|
+
...patch
|
|
26364
|
+
};
|
|
26365
|
+
const now = /* @__PURE__ */ new Date();
|
|
26366
|
+
return await t.one(`UPDATE ${this.#runsTable()}
|
|
26367
|
+
SET header = $3::jsonb, status = $4, "updatedAt" = $5, "updatedAtZ" = $6
|
|
26368
|
+
WHERE workflow_name = $1 AND run_id = $2
|
|
26369
|
+
RETURNING header, "lastSeq", "startStateSeq", "runToken"`, [
|
|
26370
|
+
workflowName,
|
|
26371
|
+
runId,
|
|
26372
|
+
toPgJson(header),
|
|
26373
|
+
header.status,
|
|
26374
|
+
now,
|
|
26375
|
+
now
|
|
26376
|
+
]);
|
|
26377
|
+
});
|
|
26378
|
+
} catch (error) {
|
|
26379
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
26380
|
+
throw this.#runError("UPDATE_WORKFLOW_RUN_HEADER", workflowName, runId, error);
|
|
26381
|
+
}
|
|
26382
|
+
}
|
|
26383
|
+
/**
|
|
26384
|
+
* Upserts a storage v2 run's header from `$1`–`$12` ({@link #headerParams}), adds `$7` (the
|
|
26385
|
+
* number of entries the write adds) to `lastSeq`, and returns the new `lastSeq`, `stateShared`
|
|
26386
|
+
* and `runToken`. `$12` is the run token of a run the write creates; a stored run keeps its own.
|
|
26387
|
+
* A checkpoint's state entries come last, so its `startStateSeq` is the new `lastSeq`; an append
|
|
26388
|
+
* keeps the stored one unless given `$13`. The header's row lock is held
|
|
26389
|
+
* until commit, so the writes to one run commit in `seq` order. With `expectedParam` (a text
|
|
26390
|
+
* array of statuses), a stored run whose status isn't in it is left as is and nothing is returned.
|
|
26391
|
+
*/
|
|
26392
|
+
#upsertHeaderSql(checkpoint, expectedParam) {
|
|
26393
|
+
const startStateSeq = checkpoint ? {
|
|
26394
|
+
insert: "$7::int",
|
|
26395
|
+
update: "r.\"lastSeq\" + EXCLUDED.\"lastSeq\""
|
|
26396
|
+
} : {
|
|
26397
|
+
insert: "COALESCE($13::int, 0)",
|
|
26398
|
+
update: "COALESCE($13::int, r.\"startStateSeq\")"
|
|
26399
|
+
};
|
|
26400
|
+
return `INSERT INTO ${this.#runsTable()} AS r
|
|
26401
|
+
(workflow_name, run_id, "resourceId", "threadId", status, header, "lastSeq", "startStateSeq", "stateShared",
|
|
26402
|
+
"runToken", "createdAt", "updatedAt", "createdAtZ", "updatedAtZ")
|
|
26403
|
+
VALUES ($1, $2, $3, $4, $5, $6::jsonb, $7::int, ${startStateSeq.insert}, false, $12, $8, $9, $10, $11)
|
|
26404
|
+
ON CONFLICT (workflow_name, run_id) DO UPDATE
|
|
26405
|
+
SET "resourceId" = COALESCE(EXCLUDED."resourceId", r."resourceId"), "threadId" = EXCLUDED."threadId",
|
|
26406
|
+
status = EXCLUDED.status, header = EXCLUDED.header, "lastSeq" = r."lastSeq" + EXCLUDED."lastSeq",
|
|
26407
|
+
"startStateSeq" = ${startStateSeq.update},
|
|
26408
|
+
"updatedAt" = EXCLUDED."updatedAt", "updatedAtZ" = EXCLUDED."updatedAtZ"
|
|
26409
|
+
${expectedParam ? `WHERE r.status = ANY(${expectedParam}::text[])` : ""}
|
|
26410
|
+
RETURNING "lastSeq", "stateShared", "runToken"`;
|
|
26411
|
+
}
|
|
26412
|
+
#headerParams(run, entryCount) {
|
|
26413
|
+
const now = /* @__PURE__ */ new Date();
|
|
26414
|
+
return [
|
|
26415
|
+
run.workflowName,
|
|
26416
|
+
run.runId,
|
|
26417
|
+
run.resourceId,
|
|
26418
|
+
run.threadId ?? null,
|
|
26419
|
+
run.header.status,
|
|
26420
|
+
toPgJson(run.header),
|
|
26421
|
+
entryCount,
|
|
26422
|
+
now,
|
|
26423
|
+
now,
|
|
26424
|
+
now,
|
|
26425
|
+
now,
|
|
26426
|
+
crypto.randomUUID()
|
|
26427
|
+
];
|
|
26428
|
+
}
|
|
26429
|
+
async writeWorkflowRunCheckpoint({ fence: explicitFence, expectedStatus, steps, state, stateBase, statePatch, ...run }) {
|
|
26430
|
+
const entries = journalEntries(steps, {
|
|
26431
|
+
state,
|
|
26432
|
+
stateBase,
|
|
26433
|
+
statePatch
|
|
26434
|
+
}, true);
|
|
26435
|
+
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, run.runId);
|
|
26436
|
+
const expected = expectedStatus === void 0 ? [] : [[expectedStatus].flat()];
|
|
26437
|
+
try {
|
|
26438
|
+
return await this.#db.client.tx(async (t) => {
|
|
26439
|
+
if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "writeWorkflowRunCheckpoint");
|
|
26440
|
+
const base = state === void 0 ? stateBase : void 0;
|
|
26441
|
+
let baseLastSeq;
|
|
26442
|
+
if (base) baseLastSeq = (await t.oneOrNone(`SELECT "lastSeq" FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2 FOR KEY SHARE`, [base.workflowName, base.runId]))?.lastSeq;
|
|
26443
|
+
const header = await t.oneOrNone(this.#upsertHeaderSql(true, expected.length ? "$13" : void 0), [...this.#headerParams(run, entries.length), ...expected]);
|
|
26444
|
+
if (!header) return null;
|
|
26445
|
+
if (base) {
|
|
26446
|
+
await t.none(`UPDATE ${this.#runsTable()} SET "stateShared" = true
|
|
26447
|
+
WHERE workflow_name = $1 AND run_id = $2 AND NOT "stateShared"`, [base.workflowName, base.runId]);
|
|
26448
|
+
const { stored } = await t.one(`SELECT EXISTS (
|
|
26449
|
+
SELECT 1 FROM ${this.#entriesTable()} WHERE workflow_name = $1 AND run_id = $2
|
|
26450
|
+
AND CASE WHEN $4::boolean THEN kind = 'state-draft' AND seq = $3::int
|
|
26451
|
+
ELSE kind IN ('state', 'state-base') AND seq <= $3::int END
|
|
26452
|
+
) AS stored`, [
|
|
26453
|
+
base.workflowName,
|
|
26454
|
+
base.runId,
|
|
26455
|
+
base.seq,
|
|
26456
|
+
base.draft === true
|
|
26457
|
+
]);
|
|
26458
|
+
if (!stored || baseLastSeq === void 0 || base.seq > baseLastSeq) throw new Error(`Workflow run ${run.workflowName}/${run.runId} builds on state version ${base.seq} of ${base.workflowName}/${base.runId}, which is not stored`);
|
|
26459
|
+
}
|
|
26460
|
+
const { lastSeq, stateShared, runToken } = header;
|
|
26461
|
+
await t.none(`DELETE FROM ${this.#entriesTable()}
|
|
26462
|
+
WHERE workflow_name = $1 AND run_id = $2${stateShared ? ` AND kind IN ('step', 'step-patch')` : ""}`, [run.workflowName, run.runId]);
|
|
26463
|
+
await t.none(`INSERT INTO ${this.#entriesTable()} (workflow_name, run_id, seq, kind, step_id, payload)
|
|
26464
|
+
SELECT $1, $2, $3::int - $4::int + e.ord, ${ENTRY_VALUES}
|
|
26465
|
+
FROM ${entryRows("$5")}`, [
|
|
26466
|
+
run.workflowName,
|
|
26467
|
+
run.runId,
|
|
26468
|
+
lastSeq,
|
|
26469
|
+
entries.length,
|
|
26470
|
+
toPgJson(entries)
|
|
26471
|
+
]);
|
|
26472
|
+
return {
|
|
26473
|
+
lastSeq,
|
|
26474
|
+
runToken
|
|
26475
|
+
};
|
|
26476
|
+
});
|
|
26477
|
+
} catch (error) {
|
|
26478
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
26479
|
+
throw this.#runError("WRITE_WORKFLOW_RUN_CHECKPOINT", run.workflowName, run.runId, error);
|
|
26480
|
+
}
|
|
26481
|
+
}
|
|
26482
|
+
/** One statement: the header upsert numbers the entries inserted after it. */
|
|
26483
|
+
async appendWorkflowRunEntries({ fence, steps, stepPatches, state, statePatch, startStateSeq, expectedStatus, ...run }) {
|
|
26484
|
+
const entries = journalEntries(steps, {
|
|
26485
|
+
state,
|
|
26486
|
+
statePatch
|
|
26487
|
+
}, false, stepPatches);
|
|
26488
|
+
const expected = expectedStatus === void 0 ? [] : [[expectedStatus].flat()];
|
|
26489
|
+
try {
|
|
26490
|
+
return await withRunFence(this.#db.client, this.#runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, fence, run.runId), "appendWorkflowRunEntries", (q) => q.oneOrNone(`WITH h AS (${this.#upsertHeaderSql(false, expected.length ? "$15" : void 0)}),
|
|
26491
|
+
e AS (
|
|
26492
|
+
INSERT INTO ${this.#entriesTable()} (workflow_name, run_id, seq, kind, step_id, payload)
|
|
26493
|
+
SELECT $1, $2, h."lastSeq" - $7::int + e.ord, ${ENTRY_VALUES}
|
|
26494
|
+
FROM h, ${entryRows("$14")}
|
|
26495
|
+
)
|
|
26496
|
+
SELECT "lastSeq", "runToken" FROM h`, [
|
|
26497
|
+
...this.#headerParams(run, entries.length),
|
|
26498
|
+
startStateSeq ?? null,
|
|
26499
|
+
toPgJson(entries),
|
|
26500
|
+
...expected
|
|
26501
|
+
]));
|
|
26502
|
+
} catch (error) {
|
|
26503
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
26504
|
+
throw this.#runError("APPEND_WORKFLOW_RUN_ENTRIES", run.workflowName, run.runId, error);
|
|
26505
|
+
}
|
|
26506
|
+
}
|
|
26507
|
+
async loadWorkflowRunState({ workflowName, runId, seq }) {
|
|
26508
|
+
try {
|
|
26509
|
+
return (await this.#stateAt(this.#db.client, workflowName, runId, { seq }))?.state ?? null;
|
|
26510
|
+
} catch (error) {
|
|
26511
|
+
throw this.#runError("LOAD_WORKFLOW_RUN_STATE", workflowName, runId, error);
|
|
26512
|
+
}
|
|
26513
|
+
}
|
|
26514
|
+
async appendWorkflowRunStateDraft(args) {
|
|
26515
|
+
const { workflowName, runId } = args;
|
|
26516
|
+
const payload = "state" in args ? { state: args.state } : {
|
|
26517
|
+
on: args.on.draft ? {
|
|
26518
|
+
seq: args.on.seq,
|
|
26519
|
+
draft: true
|
|
26520
|
+
} : { seq: args.on.seq },
|
|
26521
|
+
ops: args.ops
|
|
26522
|
+
};
|
|
26523
|
+
let row;
|
|
26524
|
+
try {
|
|
26525
|
+
row = await withRunFence(this.#db.client, this.#runOwnersTable(), (0, _mastra_core_storage.resolveRunFence)(this, void 0, runId), "appendWorkflowRunStateDraft", (q) => q.oneOrNone(`WITH h AS (
|
|
26526
|
+
UPDATE ${this.#runsTable()} SET "lastSeq" = "lastSeq" + 1, "stateShared" = true
|
|
26527
|
+
WHERE workflow_name = $1 AND run_id = $2
|
|
26528
|
+
RETURNING "lastSeq"
|
|
26529
|
+
)
|
|
26530
|
+
INSERT INTO ${this.#entriesTable()} (workflow_name, run_id, seq, kind, step_id, payload)
|
|
26531
|
+
SELECT $1, $2, h."lastSeq", 'state-draft', NULL, $3::jsonb FROM h
|
|
26532
|
+
RETURNING seq`, [
|
|
26533
|
+
workflowName,
|
|
26534
|
+
runId,
|
|
26535
|
+
toPgJson(payload)
|
|
26536
|
+
]));
|
|
26537
|
+
} catch (error) {
|
|
26538
|
+
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
26539
|
+
throw this.#runError("APPEND_WORKFLOW_RUN_STATE_DRAFT", workflowName, runId, error);
|
|
26540
|
+
}
|
|
26541
|
+
if (!row) throw new Error(`Storage v2 run ${workflowName}/${runId} is not stored`);
|
|
26542
|
+
return row;
|
|
25873
26543
|
}
|
|
25874
26544
|
supportsRunFencing() {
|
|
25875
26545
|
return true;
|
|
@@ -25998,6 +26668,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
25998
26668
|
});
|
|
25999
26669
|
const existingSnapshotResult = await t.oneOrNone(`SELECT snapshot FROM ${tableName} WHERE workflow_name = $1 AND run_id = $2 FOR UPDATE`, [workflowName, runId]);
|
|
26000
26670
|
if (!existingSnapshotResult) {
|
|
26671
|
+
if (await t.oneOrNone(`SELECT 1 FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2`, [workflowName, runId])) throw new Error(`updateWorkflowResults does not support storage v2 runs (run ${runId})`);
|
|
26001
26672
|
(0, _mastra_core_storage.assertWorkflowStepGuard)({
|
|
26002
26673
|
snapshot: null,
|
|
26003
26674
|
stepId,
|
|
@@ -26070,11 +26741,36 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26070
26741
|
}, error);
|
|
26071
26742
|
}
|
|
26072
26743
|
}
|
|
26073
|
-
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence }) {
|
|
26744
|
+
async updateWorkflowState({ workflowName, runId, opts, fence: explicitFence, storageVersion }) {
|
|
26074
26745
|
const fence = (0, _mastra_core_storage.resolveRunFence)(this, explicitFence, runId);
|
|
26075
26746
|
try {
|
|
26076
26747
|
return await this.#db.client.tx(async (t) => {
|
|
26077
26748
|
if (fence) await assertRunFence(t, this.#runOwnersTable(), fence, "updateWorkflowState");
|
|
26749
|
+
if (storageVersion !== 1) {
|
|
26750
|
+
const run = await t.oneOrNone(`SELECT header FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2 FOR UPDATE`, [workflowName, runId]);
|
|
26751
|
+
if (run) {
|
|
26752
|
+
const { expectedStatus, ...patch } = opts;
|
|
26753
|
+
if (!(0, _mastra_core_storage.matchesExpectedWorkflowStatus)(run.header.status, expectedStatus)) return;
|
|
26754
|
+
const header = {
|
|
26755
|
+
...run.header,
|
|
26756
|
+
...patch
|
|
26757
|
+
};
|
|
26758
|
+
const now = /* @__PURE__ */ new Date();
|
|
26759
|
+
const { entries } = await t.one(`UPDATE ${this.#runsTable()} r
|
|
26760
|
+
SET header = $3::jsonb, status = $4, "updatedAt" = $5, "updatedAtZ" = $6
|
|
26761
|
+
WHERE r.workflow_name = $1 AND r.run_id = $2
|
|
26762
|
+
RETURNING ${this.#entriesSql("r")} AS entries`, [
|
|
26763
|
+
workflowName,
|
|
26764
|
+
runId,
|
|
26765
|
+
toPgJson(header),
|
|
26766
|
+
header.status,
|
|
26767
|
+
now,
|
|
26768
|
+
now
|
|
26769
|
+
]);
|
|
26770
|
+
return this.#materializeRun(t, header, entries);
|
|
26771
|
+
}
|
|
26772
|
+
if (storageVersion === 2) return void 0;
|
|
26773
|
+
}
|
|
26078
26774
|
const tableName = getTableName({
|
|
26079
26775
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26080
26776
|
schemaName: getSchemaName(this.#schema)
|
|
@@ -26154,10 +26850,14 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26154
26850
|
async loadWorkflowRunStatus({ workflowName, runId }) {
|
|
26155
26851
|
try {
|
|
26156
26852
|
const statusExpr = await this.#statusExpression("snapshot");
|
|
26157
|
-
return (await this.#db.client.oneOrNone(`SELECT
|
|
26853
|
+
return (await this.#db.client.oneOrNone(`SELECT COALESCE(
|
|
26854
|
+
(SELECT status FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2),
|
|
26855
|
+
(SELECT ${statusExpr} FROM ${getTableName({
|
|
26158
26856
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26159
26857
|
schemaName: getSchemaName(this.#schema)
|
|
26160
|
-
})}
|
|
26858
|
+
})}
|
|
26859
|
+
WHERE "workflow_name" = $1 AND "run_id" = $2 ORDER BY "createdAt" DESC LIMIT 1)
|
|
26860
|
+
) AS status`, [workflowName, runId]))?.status ?? null;
|
|
26161
26861
|
} catch (error) {
|
|
26162
26862
|
throw new _mastra_core_error.MastraError({
|
|
26163
26863
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "LOAD_WORKFLOW_RUN_STATUS", "FAILED"),
|
|
@@ -26170,8 +26870,14 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26170
26870
|
}, error);
|
|
26171
26871
|
}
|
|
26172
26872
|
}
|
|
26173
|
-
async loadWorkflowSnapshot({ workflowName, runId }) {
|
|
26873
|
+
async loadWorkflowSnapshot({ workflowName, runId, storageVersion }) {
|
|
26174
26874
|
try {
|
|
26875
|
+
if (storageVersion !== 1) {
|
|
26876
|
+
const run = await this.#db.client.oneOrNone(`SELECT r.header, ${this.#entriesSql("r")} AS entries
|
|
26877
|
+
FROM ${this.#runsTable()} r WHERE r.workflow_name = $1 AND r.run_id = $2`, [workflowName, runId]);
|
|
26878
|
+
if (run) return await this.#materializeRun(this.#db.client, run.header, run.entries);
|
|
26879
|
+
if (storageVersion === 2) return null;
|
|
26880
|
+
}
|
|
26175
26881
|
const result = await this.#db.load({
|
|
26176
26882
|
tableName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26177
26883
|
keys: {
|
|
@@ -26188,7 +26894,7 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26188
26894
|
}, error);
|
|
26189
26895
|
}
|
|
26190
26896
|
}
|
|
26191
|
-
async getWorkflowRunById({ runId, workflowName }) {
|
|
26897
|
+
async getWorkflowRunById({ runId, workflowName, storageVersion }) {
|
|
26192
26898
|
try {
|
|
26193
26899
|
const conditions = [];
|
|
26194
26900
|
const values = [];
|
|
@@ -26204,18 +26910,14 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26204
26910
|
paramIndex++;
|
|
26205
26911
|
}
|
|
26206
26912
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
26207
|
-
const query =
|
|
26208
|
-
|
|
26209
|
-
|
|
26210
|
-
|
|
26211
|
-
})
|
|
26212
|
-
|
|
26213
|
-
ORDER BY "createdAt" DESC LIMIT 1
|
|
26214
|
-
`;
|
|
26215
|
-
const queryValues = values;
|
|
26216
|
-
const result = await this.#db.readClient.oneOrNone(query, queryValues);
|
|
26913
|
+
const query = await this.#runsQuery({
|
|
26914
|
+
v1Where: storageVersion === 2 ? null : whereClause,
|
|
26915
|
+
v2Where: storageVersion === 1 ? null : whereClause,
|
|
26916
|
+
page: "LIMIT 1"
|
|
26917
|
+
});
|
|
26918
|
+
const result = await this.#db.readClient.oneOrNone(query, values);
|
|
26217
26919
|
if (!result) return null;
|
|
26218
|
-
return this.
|
|
26920
|
+
return await this.#parseRun(this.#db.readClient, result);
|
|
26219
26921
|
} catch (error) {
|
|
26220
26922
|
throw new _mastra_core_error.MastraError({
|
|
26221
26923
|
id: (0, _mastra_core_storage.createStorageErrorId)("PG", "GET_WORKFLOW_RUN_BY_ID", "FAILED"),
|
|
@@ -26228,12 +26930,28 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26228
26930
|
}, error);
|
|
26229
26931
|
}
|
|
26230
26932
|
}
|
|
26231
|
-
async deleteWorkflowRunById({ runId, workflowName, fence }) {
|
|
26933
|
+
async deleteWorkflowRunById({ runId, workflowName, storageVersion, fence }) {
|
|
26934
|
+
const resolvedFence = (0, _mastra_core_storage.resolveRunFence)(this, fence, runId);
|
|
26232
26935
|
try {
|
|
26233
|
-
|
|
26234
|
-
|
|
26235
|
-
|
|
26236
|
-
|
|
26936
|
+
if (storageVersion === 1) {
|
|
26937
|
+
await withRunFence(this.#db.client, this.#runOwnersTable(), resolvedFence, "deleteWorkflowRunById", (q) => q.none(`DELETE FROM ${getTableName({
|
|
26938
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26939
|
+
schemaName: getSchemaName(this.#schema)
|
|
26940
|
+
})} WHERE run_id = $1 AND workflow_name = $2`, [runId, workflowName]));
|
|
26941
|
+
return;
|
|
26942
|
+
}
|
|
26943
|
+
await this.#db.client.tx(async (t) => {
|
|
26944
|
+
if (resolvedFence) await assertRunFence(t, this.#runOwnersTable(), resolvedFence, "deleteWorkflowRunById");
|
|
26945
|
+
if ((await t.oneOrNone(`SELECT "stateShared" FROM ${this.#runsTable()} WHERE workflow_name = $1 AND run_id = $2 FOR UPDATE`, [workflowName, runId]))?.stateShared) await this.#detachStateChildren(t, workflowName, runId);
|
|
26946
|
+
for (const table of [
|
|
26947
|
+
this.#runsTable(),
|
|
26948
|
+
this.#entriesTable(),
|
|
26949
|
+
...storageVersion === 2 ? [] : [getTableName({
|
|
26950
|
+
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26951
|
+
schemaName: getSchemaName(this.#schema)
|
|
26952
|
+
})]
|
|
26953
|
+
]) await t.none(`DELETE FROM ${table} WHERE run_id = $1 AND workflow_name = $2`, [runId, workflowName]);
|
|
26954
|
+
});
|
|
26237
26955
|
} catch (error) {
|
|
26238
26956
|
if ((0, _mastra_core_storage.isRunFenceConflictError)(error)) throw error;
|
|
26239
26957
|
throw new _mastra_core_error.MastraError({
|
|
@@ -26250,77 +26968,81 @@ var WorkflowsPG = class WorkflowsPG extends _mastra_core_storage.WorkflowsStorag
|
|
|
26250
26968
|
async listWorkflowRuns({ workflowName, fromDate, toDate, perPage, page, resourceId, threadId, status, summary } = {}) {
|
|
26251
26969
|
try {
|
|
26252
26970
|
const conditions = [];
|
|
26971
|
+
const v2Conditions = [];
|
|
26253
26972
|
const values = [];
|
|
26254
26973
|
let paramIndex = 1;
|
|
26255
26974
|
if (workflowName) {
|
|
26256
26975
|
conditions.push(`workflow_name = $${paramIndex}`);
|
|
26976
|
+
v2Conditions.push(`workflow_name = $${paramIndex}`);
|
|
26257
26977
|
values.push(workflowName);
|
|
26258
26978
|
paramIndex++;
|
|
26259
26979
|
}
|
|
26260
26980
|
if (status) {
|
|
26261
26981
|
const statusExpr = await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? `snapshot ->> 'status'` : `regexp_replace(snapshot::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb ->> 'status'`;
|
|
26262
26982
|
conditions.push(`${statusExpr} = $${paramIndex}`);
|
|
26983
|
+
v2Conditions.push(`status = $${paramIndex}`);
|
|
26263
26984
|
values.push(status);
|
|
26264
26985
|
paramIndex++;
|
|
26265
26986
|
}
|
|
26266
26987
|
if (resourceId) {
|
|
26267
|
-
if (await this.#db.hasColumn(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId")) {
|
|
26268
|
-
|
|
26269
|
-
|
|
26270
|
-
|
|
26271
|
-
|
|
26988
|
+
if (await this.#db.hasColumn(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "resourceId")) conditions.push(`"resourceId" = $${paramIndex}`);
|
|
26989
|
+
else this.logger?.warn?.(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] resourceId column not found. Skipping resourceId filter.`);
|
|
26990
|
+
v2Conditions.push(`"resourceId" = $${paramIndex}`);
|
|
26991
|
+
values.push(resourceId);
|
|
26992
|
+
paramIndex++;
|
|
26272
26993
|
}
|
|
26273
26994
|
if (threadId) {
|
|
26274
|
-
if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb") {
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
|
|
26995
|
+
if (await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb") conditions.push(`${WORKFLOW_SNAPSHOT_THREAD_ID_EXPR} = $${paramIndex}`);
|
|
26996
|
+
else this.logger?.warn?.(`[${_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT}] snapshot column is not jsonb. Skipping threadId filter.`);
|
|
26997
|
+
v2Conditions.push(`"threadId" = $${paramIndex}`);
|
|
26998
|
+
values.push(threadId);
|
|
26999
|
+
paramIndex++;
|
|
26279
27000
|
}
|
|
26280
27001
|
if (fromDate) {
|
|
26281
27002
|
conditions.push(`"createdAt" >= $${paramIndex}`);
|
|
27003
|
+
v2Conditions.push(`"createdAt" >= $${paramIndex}`);
|
|
26282
27004
|
values.push(fromDate);
|
|
26283
27005
|
paramIndex++;
|
|
26284
27006
|
}
|
|
26285
27007
|
if (toDate) {
|
|
26286
27008
|
conditions.push(`"createdAt" <= $${paramIndex}`);
|
|
27009
|
+
v2Conditions.push(`"createdAt" <= $${paramIndex}`);
|
|
26287
27010
|
values.push(toDate);
|
|
26288
27011
|
paramIndex++;
|
|
26289
27012
|
}
|
|
26290
27013
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
27014
|
+
const v2WhereClause = v2Conditions.length > 0 ? `WHERE ${v2Conditions.join(" AND ")}` : "";
|
|
26291
27015
|
let total = 0;
|
|
26292
27016
|
const usePagination = typeof perPage === "number" && typeof page === "number";
|
|
26293
27017
|
if (usePagination) {
|
|
26294
|
-
const countResult = await this.#db.client.one(`SELECT COUNT(*)
|
|
27018
|
+
const countResult = await this.#db.client.one(`SELECT (SELECT COUNT(*) FROM ${getTableName({
|
|
26295
27019
|
indexName: _mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT,
|
|
26296
27020
|
schemaName: getSchemaName(this.#schema)
|
|
26297
|
-
})} ${whereClause}
|
|
27021
|
+
})} ${whereClause})
|
|
27022
|
+
+ (SELECT COUNT(*) FROM ${this.#runsTable()} ${v2WhereClause}) AS count`, values);
|
|
26298
27023
|
total = Number(countResult.count);
|
|
26299
27024
|
}
|
|
26300
27025
|
const normalizedPerPage = usePagination ? (0, _mastra_core_storage.normalizePerPage)(perPage, Number.MAX_SAFE_INTEGER) : 0;
|
|
26301
27026
|
const offset = usePagination ? page * normalizedPerPage : void 0;
|
|
26302
|
-
let
|
|
27027
|
+
let v1Snapshot = "snapshot";
|
|
26303
27028
|
if (summary) {
|
|
26304
27029
|
const snapshotJson = await this.#db.getColumnType(_mastra_core_storage.TABLE_WORKFLOW_SNAPSHOT, "snapshot") === "jsonb" ? "snapshot" : `regexp_replace(snapshot::text, '\\\\u(0000|[Dd][89A-Fa-f][0-9A-Fa-f]{2})', '', 'g')::jsonb`;
|
|
26305
|
-
|
|
27030
|
+
v1Snapshot = `jsonb_build_object('status', ${snapshotJson} -> 'status', 'timestamp', ${snapshotJson} -> 'timestamp')`;
|
|
26306
27031
|
}
|
|
26307
|
-
const query =
|
|
26308
|
-
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
${usePagination ? ` LIMIT $${paramIndex} OFFSET $${paramIndex + 1}` : ""}
|
|
26315
|
-
`;
|
|
27032
|
+
const query = await this.#runsQuery({
|
|
27033
|
+
v1Where: whereClause,
|
|
27034
|
+
v2Where: v2WhereClause,
|
|
27035
|
+
v1Snapshot,
|
|
27036
|
+
summary,
|
|
27037
|
+
page: usePagination ? `LIMIT $${paramIndex} OFFSET $${paramIndex + 1}` : ""
|
|
27038
|
+
});
|
|
26316
27039
|
const queryValues = usePagination ? [
|
|
26317
27040
|
...values,
|
|
26318
27041
|
normalizedPerPage,
|
|
26319
27042
|
offset
|
|
26320
27043
|
] : values;
|
|
26321
|
-
const
|
|
26322
|
-
|
|
26323
|
-
});
|
|
27044
|
+
const result = await this.#db.client.manyOrNone(query, queryValues);
|
|
27045
|
+
const runs = await Promise.all((result || []).map((row) => this.#parseRun(this.#db.client, row)));
|
|
26324
27046
|
return {
|
|
26325
27047
|
runs,
|
|
26326
27048
|
total: total || runs.length
|