@mastra/pg 1.32.0 → 1.33.0-alpha.1

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.
Files changed (31) hide show
  1. package/dist/_types/@internal_core/dist/knowledge-compat/index.d.ts +48 -48
  2. package/dist/_types/@internal_core/dist/memory/index.d.ts +12 -13
  3. package/dist/docs/SKILL.md +1 -1
  4. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  5. package/dist/docs/references/docs-memory-semantic-recall.md +2 -2
  6. package/dist/docs/references/docs-memory-working-memory.md +2 -2
  7. package/dist/docs/references/docs-storage.md +1 -1
  8. package/dist/docs/references/integrations-databases-postgresql.md +1 -1
  9. package/dist/docs/references/reference-memory-memory-class.md +3 -3
  10. package/dist/docs/references/reference-processors-message-history-processor.md +2 -2
  11. package/dist/docs/references/reference-processors-semantic-recall-processor.md +2 -2
  12. package/dist/docs/references/reference-processors-working-memory-processor.md +2 -2
  13. package/dist/docs/references/reference-rag-chunking-and-embedding.md +1 -1
  14. package/dist/docs/references/reference-rag-metadata-filters.md +1 -1
  15. package/dist/docs/references/reference-rag-retrieval.md +13 -13
  16. package/dist/docs/references/reference-storage-retention.md +1 -1
  17. package/dist/docs/references/reference-tools-vector-query-tool.md +1 -1
  18. package/dist/docs/references/reference-vectors-pg.md +1 -1
  19. package/dist/index.cjs +151 -34
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.js +152 -35
  22. package/dist/index.js.map +1 -1
  23. package/dist/storage/domains/observability/v-next/index.d.ts +3 -2
  24. package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
  25. package/dist/storage/domains/observability/v-next/span-query.d.ts +9 -0
  26. package/dist/storage/domains/observability/v-next/span-query.d.ts.map +1 -1
  27. package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts +7 -1
  28. package/dist/storage/domains/observability/v-next/trace-aggregate.d.ts.map +1 -1
  29. package/dist/storage/domains/workflows/index.d.ts +5 -2
  30. package/dist/storage/domains/workflows/index.d.ts.map +1 -1
  31. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
2
2
  import * as coreStorage from "@mastra/core/storage";
3
- import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, RUN_FENCING_TABLE_SCHEMAS, RunFenceConflictError, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MEMORY_RUN_FENCES, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, canonicalizeKnowledgeScope, computeTraceStatus, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, isRunFenceConflictError, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, matchesRunFence, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, resolveRunFence, retentionCutoffMs, runRetentionBatches, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
3
+ import { AgentsStorage, BRANCH_SPAN_TYPES, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, EntityType, ExperimentsStorage, FactoryStorage, FavoritesStorage, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, METRIC_DISTINCT_COLUMNS, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PromptBlocksStorage, RUN_FENCING_TABLE_SCHEMAS, RunFenceConflictError, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_CONFIGS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MEMORY_RUN_FENCES, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_RUN_OWNERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, assertWorkflowStepGuard, calculatePagination, canonicalizeKnowledgeScope, computeTraceStatus, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getDefaultValue, getSqlType, hasErrorCode, isKnowledgeScopeVisible, isRunFenceConflictError, isWorkflowStepConflictError, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, matchesExpectedWorkflowStatus, matchesRunFence, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, resolveRunFence, retentionCutoffMs, runRetentionBatches, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
4
4
  import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
5
5
  import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
6
6
  import { Mutex } from "async-mutex";
@@ -20256,7 +20256,12 @@ function afterCursor(plan, values) {
20256
20256
  }
20257
20257
  return `(${terms.join(" OR ")})`;
20258
20258
  }
20259
- function compilePostgresSpanQuery(schema, plan) {
20259
+ /**
20260
+ * The spans `querySpans()` pages through, as a `selected` CTE: the latest completed copy of each
20261
+ * span that starts in the window and matches `where`. `aggregateSpans()` groups the same CTE.
20262
+ * `values` holds the CTEs' parameters; callers append their own.
20263
+ */
20264
+ function compilePostgresSpanSelection(schema, plan) {
20260
20265
  const values = [plan.timeRange.from, plan.timeRange.to];
20261
20266
  const scope = [];
20262
20267
  if (plan.scope) {
@@ -20272,17 +20277,17 @@ function compilePostgresSpanQuery(schema, plan) {
20272
20277
  values: []
20273
20278
  };
20274
20279
  values.push(...predicate.values);
20275
- const after = afterCursor(plan, values);
20276
- values.push(plan.limit + 1);
20277
20280
  const table = qualifiedTable(schema, TABLE_SPAN_EVENTS);
20278
20281
  const legacyModelFields = coreStorage.SPAN_QUERY_FIELD_REGISTRY ? "" : `CASE WHEN jsonb_typeof(attributes -> 'model') = 'string' THEN attributes ->> 'model' END AS model,
20279
20282
  CASE WHEN jsonb_typeof(attributes -> 'provider') = 'string' THEN attributes ->> 'provider' END AS provider,`;
20280
20283
  return {
20281
- text: `WITH candidate_ids AS MATERIALIZED (
20284
+ ctes: [
20285
+ `candidate_ids AS MATERIALIZED (
20282
20286
  SELECT DISTINCT ${identities} FROM ${table}
20283
20287
  WHERE "startedAt" >= $1 AND "startedAt" < $2 AND "endedAt" >= $1 AND NOT "isPending"
20284
20288
  ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
20285
- ), current_spans AS MATERIALIZED (
20289
+ )`,
20290
+ `current_spans AS MATERIALIZED (
20286
20291
  SELECT DISTINCT ON (${identities})
20287
20292
  ${fields.map((field) => `"${field}"`).join(", ")}, "startedAt", "endedAt", "cursorId"::text AS version,
20288
20293
  ${legacyModelFields}
@@ -20296,9 +20301,23 @@ function compilePostgresSpanQuery(schema, plan) {
20296
20301
  SELECT "organizationId" IS NULL, COALESCE("organizationId", ''), "resourceId" IS NULL, COALESCE("resourceId", ''), "traceId", "spanId" FROM candidate_ids
20297
20302
  ) ${scope.length ? `AND ${scope.join(" AND ")}` : ""}
20298
20303
  ORDER BY ${identities}, "endedAt" DESC, "cursorId" DESC
20299
- ), candidates AS (
20304
+ )`,
20305
+ `candidates AS (
20300
20306
  SELECT *, CASE WHEN "hasError" THEN 'error' ELSE NULL END AS error FROM current_spans
20301
- )
20307
+ )`,
20308
+ `selected AS (
20309
+ SELECT * FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql})
20310
+ )`
20311
+ ],
20312
+ values
20313
+ };
20314
+ }
20315
+ function compilePostgresSpanQuery(schema, plan) {
20316
+ const { ctes, values } = compilePostgresSpanSelection(schema, plan);
20317
+ const after = afterCursor(plan, values);
20318
+ values.push(plan.limit + 1);
20319
+ return {
20320
+ text: `WITH ${ctes.join(", ")}
20302
20321
  SELECT ${[
20303
20322
  ...fields,
20304
20323
  "startedAt",
@@ -20307,7 +20326,7 @@ function compilePostgresSpanQuery(schema, plan) {
20307
20326
  "status",
20308
20327
  "version"
20309
20328
  ].map((field) => `s."${field}"`).join(", ")}
20310
- FROM candidates s WHERE s."startedAt" >= $1 AND s."startedAt" < $2 AND (${predicate.sql}) AND ${after}
20329
+ FROM selected s WHERE ${after}
20311
20330
  ORDER BY s."${plan.orderBy.field}" ${plan.orderBy.direction === "asc" ? "ASC" : "DESC"} NULLS LAST, ${identityOrder}
20312
20331
  LIMIT $${values.length}`,
20313
20332
  values
@@ -20641,6 +20660,28 @@ const DIMENSION_SQL = {
20641
20660
  organizationId: "r.\"organizationId\"",
20642
20661
  experimentId: "r.\"experimentId\""
20643
20662
  };
20663
+ /** Span column expressions (alias `r`, a `selected` span row) for each groupable span dimension. */
20664
+ const SPAN_DIMENSION_SQL = Object.fromEntries([
20665
+ "name",
20666
+ "spanType",
20667
+ "entityType",
20668
+ "entityId",
20669
+ "entityName",
20670
+ "parentEntityType",
20671
+ "parentEntityName",
20672
+ "rootEntityType",
20673
+ "rootEntityName",
20674
+ "environment",
20675
+ "status",
20676
+ "serviceName",
20677
+ "executionSource",
20678
+ "threadId",
20679
+ "resourceId",
20680
+ "userId",
20681
+ "sessionId",
20682
+ "organizationId",
20683
+ "experimentId"
20684
+ ].map((field) => [field, `r."${field}"`]));
20644
20685
  const PERCENTILES = {
20645
20686
  "duration.p50": .5,
20646
20687
  "duration.p90": .9,
@@ -20666,16 +20707,43 @@ const COMPARISON_SQL = {
20666
20707
  */
20667
20708
  function compilePostgresTraceAggregate(schema, plan) {
20668
20709
  const { ctes, values } = compilePostgresTraceCandidates(schema, plan, "r.*");
20710
+ return compilePostgresAggregate(schema, plan, {
20711
+ ctes,
20712
+ values,
20713
+ from: "candidates",
20714
+ dimensionSql: (field, param) => {
20715
+ if (field.startsWith("metadata.")) return traceMetadataValueSql(param(field.slice(9)));
20716
+ return lookupDimensionSql(DIMENSION_SQL, field);
20717
+ },
20718
+ usageKey: ["traceId"]
20719
+ });
20720
+ }
20721
+ /**
20722
+ * Compiles a trusted span aggregate plan over the spans `querySpans()` selects. Usage is
20723
+ * attributed by `(traceId, spanId)`, so only the span that recorded a metric row carries it.
20724
+ */
20725
+ function compilePostgresSpanAggregate(schema, plan) {
20726
+ const { ctes, values } = compilePostgresSpanSelection(schema, plan);
20727
+ return compilePostgresAggregate(schema, plan, {
20728
+ ctes,
20729
+ values,
20730
+ from: "selected",
20731
+ dimensionSql: (field) => lookupDimensionSql(SPAN_DIMENSION_SQL, field),
20732
+ usageKey: ["traceId", "spanId"]
20733
+ });
20734
+ }
20735
+ function lookupDimensionSql(columns, field) {
20736
+ const sql = Object.hasOwn(columns, field) ? columns[field] : void 0;
20737
+ if (sql === void 0) throw new Error(`Unsupported trusted aggregate field: ${field}`);
20738
+ return sql;
20739
+ }
20740
+ function compilePostgresAggregate(schema, plan, source) {
20741
+ const { ctes, values } = source;
20669
20742
  const param = (value) => {
20670
20743
  values.push(value);
20671
20744
  return `$${values.length}`;
20672
20745
  };
20673
- const dimensionSql = (field) => {
20674
- if (field.startsWith("metadata.")) return traceMetadataValueSql(param(field.slice(9)));
20675
- const sql = Object.hasOwn(DIMENSION_SQL, field) ? DIMENSION_SQL[field] : void 0;
20676
- if (sql === void 0) throw new Error(`Unsupported trusted trace-aggregate field: ${field}`);
20677
- return sql;
20678
- };
20746
+ const dimensionSql = (field) => source.dimensionSql(field, param);
20679
20747
  const distinctSql = (field) => field === "traceId" ? "r.\"traceId\"" : dimensionSql(field);
20680
20748
  const dimensionColumns = plan.dimensions.map((_, index) => `"d${index}"`);
20681
20749
  const distinctColumns = /* @__PURE__ */ new Map();
@@ -20696,14 +20764,14 @@ function compilePostgresTraceAggregate(schema, plan) {
20696
20764
  const hasCost = measureKinds.has("cost");
20697
20765
  let usageJoin = "";
20698
20766
  if (hasCost || measureKinds.has("tokens")) {
20699
- ctes.push(...compileUsageCtes(schema, plan, param));
20700
- usageJoin = "\n LEFT JOIN usage u ON u.\"traceId\" = r.\"traceId\"";
20767
+ ctes.push(...compileUsageCtes(schema, plan, source, param));
20768
+ usageJoin = `\n LEFT JOIN usage u ON ${source.usageKey.map((key) => `u."${key}" = r."${key}"`).join(" AND ")}`;
20701
20769
  USAGE_METRIC_NAMES.forEach((_, index) => factColumns.push(`CASE WHEN u."traceId" IS NOT NULL THEN COALESCE(u."t${index}", 0) END AS "t${index}"`));
20702
20770
  factColumns.push(`u."traceId" IS NOT NULL AS "usageBearing"`, `CASE WHEN u."priced" THEN u."cost" END AS "traceCost"`, `COALESCE(u."priced" AND NOT u."pricingFailure", FALSE) AS "covered"`, `u."unitMin"`, `u."unitMax"`);
20703
20771
  }
20704
20772
  ctes.push(`facts AS (
20705
20773
  SELECT ${factColumns.join(",\n ")}
20706
- FROM candidates r${usageJoin}
20774
+ FROM ${source.from} r${usageJoin}
20707
20775
  )`);
20708
20776
  const mixedUnitsSql = (alias) => `MIN(${alias}"unitMin" COLLATE "C") <> MAX(${alias}"unitMax" COLLATE "C")`;
20709
20777
  const measureSql = (name, alias = "") => {
@@ -20819,36 +20887,39 @@ function usageMetricIndex(name) {
20819
20887
  return index;
20820
20888
  }
20821
20889
  /**
20822
- * Builds `usage`: one row per candidate trace that has at least one token metric row, holding
20823
- * per-name token sums (`t<i>`, indexed by `TRACE_AGGREGATE_USAGE_METRIC_NAMES`) and the trace's
20824
- * pricing state. Metric rows are matched by traceId, usage metric name, tenant scope, and
20825
- * `timestamp >= from` only: a trace that starts in the window can emit metrics after `to`, and
20826
- * the candidates semi-join already bounds the scan.
20890
+ * Builds `usage`: one row per population row (trace, or span) that has at least one token metric
20891
+ * row, holding per-name token sums (`t<i>`, indexed by `TRACE_AGGREGATE_USAGE_METRIC_NAMES`) and
20892
+ * its pricing state. Metric rows are matched by the source's usage key, usage metric name, tenant
20893
+ * scope, and `timestamp >= from` only: a trace or span that starts in the window can emit metrics
20894
+ * after `to`, and the population semi-join already bounds the scan.
20827
20895
  *
20828
20896
  * Rows are deduplicated on `metricId` alone. The primary key is (`metricId`, `timestamp`)
20829
20897
  * because the table is partitioned by timestamp, so retried copies with different timestamps
20830
20898
  * are both stored.
20831
20899
  */
20832
- function compileUsageCtes(schema, plan, param) {
20900
+ function compileUsageCtes(schema, plan, source, param) {
20901
+ const key = source.usageKey.map((column) => `"${column}"`).join(", ");
20902
+ const metricKey = source.usageKey.map((column) => `m."${column}"`).join(", ");
20833
20903
  const scopeConditions = plan.scope ? [`m."organizationId" = ${param(plan.scope.organizationId)}`, ...plan.scope.resourceId === void 0 ? [] : [`m."resourceId" = ${param(plan.scope.resourceId)}`]] : [];
20834
20904
  const rowConditions = [
20835
- `m."traceId" IN (SELECT "traceId" FROM candidates)`,
20905
+ `${source.usageKey.length === 1 ? metricKey : `(${metricKey})`} IN (SELECT ${key} FROM ${source.from})`,
20836
20906
  `m."name" = ANY(${param(USAGE_METRIC_NAMES)}::text[])`,
20837
20907
  `m."timestamp" >= ${param(plan.timeRange.from)}::timestamptz`,
20838
20908
  ...scopeConditions
20839
20909
  ];
20840
20910
  const costRow = `"name" = ANY(${param(coreStorage.TRACE_AGGREGATE_COST_METRIC_NAMES)}::text[])`;
20841
- const priced = `${costRow} AND "estimatedCost" IS NOT NULL AND "costUnit" IS NOT NULL AND NOT "hasError"`;
20911
+ const priced = `${costRow} AND "estimatedCost" IS NOT NULL AND "costUnit" IS NOT NULL AND (NOT "hasError" OR "partialCost")`;
20842
20912
  const failed = `${costRow} AND ("hasError" OR ("estimatedCost" IS NOT NULL AND "costUnit" IS NULL))`;
20843
20913
  const tokenColumns = USAGE_METRIC_NAMES.map((name, index) => `SUM("value") FILTER (WHERE "name" = ${param(name)}) AS "t${index}"`);
20844
20914
  return [`usage_rows AS (
20845
- SELECT DISTINCT ON (m."metricId") m."traceId", m."name", m."value", m."estimatedCost", m."costUnit",
20846
- COALESCE(jsonb_typeof(m."costMetadata" -> 'error'), 'null') <> 'null' AS "hasError"
20915
+ SELECT DISTINCT ON (m."metricId") ${metricKey}, m."name", m."value", m."estimatedCost", m."costUnit",
20916
+ COALESCE(jsonb_typeof(m."costMetadata" -> 'error'), 'null') <> 'null' AS "hasError",
20917
+ COALESCE(m."costMetadata" ->> 'error' = 'partial_cost', FALSE) AS "partialCost"
20847
20918
  FROM ${qualifiedTable(schema, TABLE_METRIC_EVENTS)} m
20848
20919
  WHERE ${rowConditions.join("\n AND ")}
20849
20920
  ORDER BY m."metricId", m."timestamp"
20850
20921
  )`, `usage AS (
20851
- SELECT "traceId",
20922
+ SELECT ${key},
20852
20923
  ${tokenColumns.join(",\n ")},
20853
20924
  SUM("estimatedCost") FILTER (WHERE ${priced}) AS "cost",
20854
20925
  bool_or(${priced}) AS "priced",
@@ -20856,7 +20927,7 @@ function compileUsageCtes(schema, plan, param) {
20856
20927
  MIN("costUnit" COLLATE "C") FILTER (WHERE ${priced}) AS "unitMin",
20857
20928
  MAX("costUnit" COLLATE "C") FILTER (WHERE ${priced}) AS "unitMax"
20858
20929
  FROM usage_rows
20859
- GROUP BY "traceId"
20930
+ GROUP BY ${key}
20860
20931
  )`];
20861
20932
  }
20862
20933
  function nullableNumber(value) {
@@ -20866,7 +20937,12 @@ function toIsoTimestamp(value) {
20866
20937
  return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
20867
20938
  }
20868
20939
  async function aggregateTraces(client, schema, plan, timeoutMs) {
20869
- const compiled = compilePostgresTraceAggregate(schema, plan);
20940
+ return runAggregate(client, plan, compilePostgresTraceAggregate(schema, plan), timeoutMs);
20941
+ }
20942
+ async function aggregateSpans(client, schema, plan, timeoutMs) {
20943
+ return runAggregate(client, plan, compilePostgresSpanAggregate(schema, plan), timeoutMs);
20944
+ }
20945
+ async function runAggregate(client, plan, compiled, timeoutMs) {
20870
20946
  const result = await runWithPostgresTraceQueryTimeout(client, timeoutMs, (transaction) => transaction.any(compiled.text, compiled.values));
20871
20947
  let truncated;
20872
20948
  let resultRows;
@@ -21662,6 +21738,7 @@ async function dangerouslyClearTracing(client, schema) {
21662
21738
  * Use it through `MastraCompositeStore` with a dedicated Postgres connection.
21663
21739
  */
21664
21740
  const spanQueryFeatures = typeof coreStorage.planSpanQuery === "function" ? ["span-query", "span-query-context-fields"] : [];
21741
+ const spanAggregateFeatures = typeof coreStorage.planSpanAggregate === "function" ? ["span-aggregate"] : [];
21665
21742
  function wrapError(op, error, details) {
21666
21743
  if (error instanceof MastraError || error instanceof coreStorage.TraceQueryCursorError || error instanceof coreStorage.TraceQueryExecutionError || error instanceof coreStorage.TraceQueryResourceLimitError) throw error;
21667
21744
  throw new MastraError({
@@ -21852,6 +21929,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21852
21929
  "trace-query",
21853
21930
  "trace-aggregate",
21854
21931
  ...spanQueryFeatures,
21932
+ ...spanAggregateFeatures,
21855
21933
  ...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
21856
21934
  "trace-query-root-duration",
21857
21935
  "trace-query-discovery",
@@ -21876,6 +21954,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21876
21954
  "trace-query",
21877
21955
  "trace-aggregate",
21878
21956
  ...spanQueryFeatures,
21957
+ ...spanAggregateFeatures,
21879
21958
  ...typeof coreStorage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
21880
21959
  "trace-query-root-duration",
21881
21960
  "trace-query-discovery",
@@ -21940,6 +22019,9 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21940
22019
  async aggregateTraces(plan) {
21941
22020
  return this.#run("AGGREGATE_TRACES", () => aggregateTraces(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
21942
22021
  }
22022
+ async aggregateSpans(plan) {
22023
+ return this.#run("AGGREGATE_SPANS", () => aggregateSpans(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
22024
+ }
21943
22025
  async getTraceQueryObservedFields(plan) {
21944
22026
  return this.#run("GET_TRACE_QUERY_OBSERVED_FIELDS", () => getTraceQueryObservedFields(this.#readClient, this.#schema, plan, this.#traceQueryTimeoutMs));
21945
22027
  }
@@ -25802,7 +25884,10 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
25802
25884
  throw this.#ownershipError("GET_RUN_OWNERSHIP", runId, error);
25803
25885
  }
25804
25886
  }
25805
- async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence }) {
25887
+ supportsWorkflowStepGuards() {
25888
+ return true;
25889
+ }
25890
+ async updateWorkflowResults({ workflowName, runId, stepId, result, requestContext, state, fence: explicitFence, startGuard, completionGuard }) {
25806
25891
  const fence = resolveRunFence(this, explicitFence, runId);
25807
25892
  try {
25808
25893
  return await this.#db.client.tx(async (t) => {
@@ -25812,16 +25897,48 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
25812
25897
  schemaName: getSchemaName(this.#schema)
25813
25898
  });
25814
25899
  const existingSnapshotResult = await t.oneOrNone(`SELECT snapshot FROM ${tableName} WHERE workflow_name = $1 AND run_id = $2 FOR UPDATE`, [workflowName, runId]);
25815
- if (!existingSnapshotResult) return {};
25900
+ if (!existingSnapshotResult) {
25901
+ assertWorkflowStepGuard({
25902
+ snapshot: null,
25903
+ stepId,
25904
+ startGuard,
25905
+ completionGuard
25906
+ });
25907
+ return {};
25908
+ }
25816
25909
  const existingSnapshot = existingSnapshotResult.snapshot;
25817
25910
  const snapshot = typeof existingSnapshot === "string" ? JSON.parse(existingSnapshot) : existingSnapshot;
25818
- mergeWorkflowStepResult({
25911
+ assertWorkflowStepGuard({
25912
+ snapshot,
25913
+ stepId,
25914
+ startGuard,
25915
+ completionGuard
25916
+ });
25917
+ if (completionGuard?.pruneOnly) snapshot.context[stepId] = {
25918
+ ...result,
25919
+ metadata: {
25920
+ ...snapshot.context[stepId]?.metadata,
25921
+ ...result.metadata,
25922
+ __continuationStarted: true
25923
+ }
25924
+ };
25925
+ else mergeWorkflowStepResult({
25819
25926
  snapshot,
25820
25927
  stepId,
25821
25928
  result,
25822
25929
  requestContext,
25823
25930
  state
25824
25931
  });
25932
+ const previous = startGuard?.previousStep;
25933
+ const previousResult = previous && snapshot.context[previous.stepId];
25934
+ if (previous && previous.stepId !== stepId && previousResult?.status === "success" && previousResult.metadata?.startedBy === previous.eventId) snapshot.context[previous.stepId] = {
25935
+ ...previous.result,
25936
+ metadata: {
25937
+ ...previousResult.metadata,
25938
+ ...previous.result.metadata,
25939
+ __continuationStarted: true
25940
+ }
25941
+ };
25825
25942
  const now = /* @__PURE__ */ new Date();
25826
25943
  const sanitizedSnapshot = toPgJson(snapshot);
25827
25944
  await t.none(`INSERT INTO ${tableName}
@@ -25840,7 +25957,7 @@ var WorkflowsPG = class WorkflowsPG extends WorkflowsStorage {
25840
25957
  return snapshot.context;
25841
25958
  });
25842
25959
  } catch (error) {
25843
- if (isRunFenceConflictError(error)) throw error;
25960
+ if (isRunFenceConflictError(error) || isWorkflowStepConflictError(error)) throw error;
25844
25961
  throw new MastraError({
25845
25962
  id: createStorageErrorId("PG", "UPDATE_WORKFLOW_RESULTS", "FAILED"),
25846
25963
  domain: ErrorDomain.STORAGE,