@mastra/pg 1.35.0-alpha.0 → 1.35.0-alpha.2

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.
@@ -326,10 +326,20 @@ export interface KnowledgePagesCore {
326
326
  /** Loads core's page-store runtime once, after checking core advertises `knowledge-pages`. */
327
327
  export declare function createKnowledgePagesCoreLoader(features: ReadonlySet<string>, loadStorageModule: () => Promise<unknown>): () => Promise<KnowledgePagesCore>;
328
328
  /**
329
- * Keyword-search words: the query split on whitespace, lowercased, deduplicated. Every word must
330
- * appear (as a case-insensitive substring) in a hit; an empty list means the search returns nothing.
329
+ * Keyword-search words: the query split on whitespace, lowercased, deduplicated. A hit needs at
330
+ * least one of them (as a case-insensitive substring); an empty list means the search returns nothing.
331
331
  */
332
332
  export declare function knowledgeSearchWords(query: string): string[];
333
+ /**
334
+ * Points one matched search word earns on a page, by the best field it appears in: name, then
335
+ * description, then body. Pages rank by how many query words they match first; these points only
336
+ * break ties between pages that match the same number of words.
337
+ */
338
+ export declare const KNOWLEDGE_SEARCH_WEIGHTS: {
339
+ readonly name: 3;
340
+ readonly description: 2;
341
+ readonly body: 1;
342
+ };
333
343
  /**
334
344
  * Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
335
345
  * non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
@@ -3,7 +3,7 @@ name: mastra-pg
3
3
  description: Documentation for @mastra/pg. Use when working with @mastra/pg APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/pg"
6
- version: "1.35.0-alpha.0"
6
+ version: "1.35.0-alpha.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.35.0-alpha.0",
2
+ "version": "1.35.0-alpha.2",
3
3
  "package": "@mastra/pg",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -4466,7 +4466,8 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4466
4466
  "skillsFormat",
4467
4467
  "durable",
4468
4468
  "browser",
4469
- "toolProviders"
4469
+ "toolProviders",
4470
+ "modelList"
4470
4471
  ]
4471
4472
  });
4472
4473
  await this.#migrateToolsToJsonbFormat();
@@ -4524,10 +4525,10 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4524
4525
  row.updatedAt ?? now
4525
4526
  ]);
4526
4527
  await this.#db.client.none(`INSERT INTO ${fullVersionsTableName}
4527
- (id, "agentId", "versionNumber", name, description, instructions, model, tools,
4528
+ (id, "agentId", "versionNumber", name, description, instructions, model, "modelList", tools,
4528
4529
  "defaultOptions", workflows, agents, "integrationTools", "toolProviders", "inputProcessors",
4529
4530
  "outputProcessors", memory, scorers, "changedFields", "changeMessage", "createdAt")
4530
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20)
4531
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21)
4531
4532
  ON CONFLICT (id) DO NOTHING`, [
4532
4533
  versionId,
4533
4534
  agentId,
@@ -4536,6 +4537,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4536
4537
  row.description ?? null,
4537
4538
  this.serializeInstructions(row.instructions ?? ""),
4538
4539
  row.model ? toPgJson(row.model) : "{}",
4540
+ row.modelList ? toPgJson(row.modelList) : null,
4539
4541
  row.tools ? toPgJson(row.tools) : null,
4540
4542
  row.defaultOptions ? toPgJson(row.defaultOptions) : null,
4541
4543
  row.workflows ? toPgJson(row.workflows) : null,
@@ -4949,21 +4951,22 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
4949
4951
  const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4950
4952
  const row = await this.#db.client.one(`INSERT INTO ${tableName} (
4951
4953
  id, "agentId", "versionNumber",
4952
- name, description, instructions, model, tools,
4954
+ name, description, instructions, model, "modelList", tools,
4953
4955
  "defaultOptions", workflows, agents, "integrationTools", "toolProviders",
4954
4956
  "inputProcessors", "outputProcessors", memory, scorers,
4955
4957
  "mcpClients", "requestContextSchema", workspace, skills, "skillsFormat",
4956
4958
  durable, browser,
4957
4959
  "changedFields", "changeMessage",
4958
4960
  "createdAt", "createdAtZ"
4959
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28) RETURNING *`, [
4961
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29) RETURNING *`, [
4960
4962
  input.id,
4961
4963
  input.agentId,
4962
4964
  input.versionNumber,
4963
4965
  input.name,
4964
4966
  input.description ?? null,
4965
- this.serializeInstructions(input.instructions),
4966
- toPgJson(input.model),
4967
+ this.serializeInstructions(input.instructions) ?? "",
4968
+ toPgJson(input.model ?? {}),
4969
+ input.modelList ? toPgJson(input.modelList) : null,
4967
4970
  input.tools ? toPgJson(input.tools) : null,
4968
4971
  input.defaultOptions ? toPgJson(input.defaultOptions) : null,
4969
4972
  input.workflows ? toPgJson(input.workflows) : null,
@@ -5209,6 +5212,7 @@ var AgentsPG = class AgentsPG extends _mastra_core_storage.AgentsStorage {
5209
5212
  description: row.description,
5210
5213
  instructions: this.deserializeInstructions(row.instructions),
5211
5214
  model: parseJsonResilient(row.model, "model"),
5215
+ modelList: parseJsonResilient(row.modelList, "modelList"),
5212
5216
  tools: parseJsonResilient(row.tools, "tools"),
5213
5217
  defaultOptions: parseJsonResilient(row.defaultOptions, "defaultOptions"),
5214
5218
  workflows: parseJsonResilient(row.workflows, "workflows"),
@@ -8666,13 +8670,23 @@ function createKnowledgePagesCoreLoader(features, loadStorageModule) {
8666
8670
  };
8667
8671
  }
8668
8672
  /**
8669
- * Keyword-search words: the query split on whitespace, lowercased, deduplicated. Every word must
8670
- * appear (as a case-insensitive substring) in a hit; an empty list means the search returns nothing.
8673
+ * Keyword-search words: the query split on whitespace, lowercased, deduplicated. A hit needs at
8674
+ * least one of them (as a case-insensitive substring); an empty list means the search returns nothing.
8671
8675
  */
8672
8676
  function knowledgeSearchWords(query) {
8673
8677
  return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
8674
8678
  }
8675
8679
  /**
8680
+ * Points one matched search word earns on a page, by the best field it appears in: name, then
8681
+ * description, then body. Pages rank by how many query words they match first; these points only
8682
+ * break ties between pages that match the same number of words.
8683
+ */
8684
+ const KNOWLEDGE_SEARCH_WEIGHTS = {
8685
+ name: 3,
8686
+ description: 2,
8687
+ body: 1
8688
+ };
8689
+ /**
8676
8690
  * Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
8677
8691
  * non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
8678
8692
  */
@@ -9550,6 +9564,18 @@ var PgKnowledgePages = class {
9550
9564
  const rows = await this.#rows(this.#client, `SELECT ${this.#noteColumns()} FROM ${this.#visibleNotes(filter)} AND ${extra.join(" AND ")} ORDER BY n."createdAt", n."id" LIMIT ${limit + 1}`, params.values);
9551
9565
  return this.#noteOutput(rows, limit);
9552
9566
  }
9567
+ async countChildPages(input) {
9568
+ const ids = [...new Set(input.parentIds)];
9569
+ const counts = Object.fromEntries(ids.map((id) => [id, 0]));
9570
+ if (!ids.length) return counts;
9571
+ const params = new Params();
9572
+ const filter = new ReadFilter(input.read, params, this.#pages);
9573
+ const rows = await this.#rows(this.#client, `SELECT p."parentId" AS "parentId", count(*)::int AS "count" FROM ${this.#pages} p JOIN ${this.#pages} s ON s."id" = p."scopeId"
9574
+ WHERE p."parentId" = ANY(${params.add(ids)}::text[]) AND NOT p."isScope" AND p."deletedAt" IS NULL AND ${filter.readable("s")}
9575
+ GROUP BY p."parentId"`, params.values);
9576
+ for (const row of rows) counts[row.parentId] = Number(row.count);
9577
+ return counts;
9578
+ }
9553
9579
  async countNotes(input) {
9554
9580
  const ids = [...new Set(input.pageIds)];
9555
9581
  const counts = Object.fromEntries(ids.map((id) => [id, 0]));
@@ -9717,9 +9743,12 @@ var PgKnowledgePages = class {
9717
9743
  filter.readable("s")
9718
9744
  ];
9719
9745
  if (input.scopeIds?.length) where.push(`p."scopeId" = ANY(${params.add(input.scopeIds)}::text[])`);
9720
- for (const pattern of patterns) where.push(`(p."name" ILIKE ${pattern} OR p."description" ILIKE ${pattern} OR p."body" ILIKE ${pattern})`);
9721
- const tier = `CASE WHEN p."canonicalName" LIKE ${phrasePattern} THEN 0 WHEN ${patterns.map((pattern) => `p."name" ILIKE ${pattern}`).join(" AND ")} THEN 1 ELSE 2 END`;
9722
- const rows = await this.#rows(this.#client, `SELECT p.* FROM ${this.#pages} p JOIN ${this.#pages} s ON s."id" = p."scopeId" WHERE ${where.join(" AND ")} ORDER BY ${tier}, p."updatedAt" DESC, p."id" ASC LIMIT ${limit}`, params.values);
9746
+ const anyField = patterns.map((pattern) => `(p."name" ILIKE ${pattern} OR p."description" ILIKE ${pattern} OR p."body" ILIKE ${pattern})`).join(" OR ");
9747
+ where.push(`(${anyField})`);
9748
+ const score = patterns.map((pattern) => `(CASE WHEN p."name" ILIKE ${pattern} THEN ${KNOWLEDGE_SEARCH_WEIGHTS.name} WHEN p."description" ILIKE ${pattern} THEN ${KNOWLEDGE_SEARCH_WEIGHTS.description} WHEN p."body" ILIKE ${pattern} THEN ${KNOWLEDGE_SEARCH_WEIGHTS.body} ELSE 0 END)`).join(" + ");
9749
+ const count = patterns.map((pattern) => `(CASE WHEN p."name" ILIKE ${pattern} OR p."description" ILIKE ${pattern} OR p."body" ILIKE ${pattern} THEN 1 ELSE 0 END)`).join(" + ");
9750
+ const tier = `CASE WHEN p."canonicalName" LIKE ${phrasePattern} THEN 0 ELSE 1 END`;
9751
+ const rows = await this.#rows(this.#client, `SELECT p.* FROM ${this.#pages} p JOIN ${this.#pages} s ON s."id" = p."scopeId" WHERE ${where.join(" AND ")} ORDER BY ${tier}, (${count}) DESC, (${score}) DESC, p."updatedAt" DESC, p."id" ASC LIMIT ${limit}`, params.values);
9723
9752
  for (const row of rows) hits.push({
9724
9753
  type: "page",
9725
9754
  page: toPage(row)
@@ -9731,8 +9760,10 @@ var PgKnowledgePages = class {
9731
9760
  const filter = new ReadFilter(input.read, params, this.#pages);
9732
9761
  const extra = [`n."deletedAt" IS NULL`, `np."deletedAt" IS NULL`];
9733
9762
  if (input.scopeIds?.length) extra.push(`np."scopeId" = ANY(${params.add(input.scopeIds)}::text[])`);
9734
- for (const word of words) extra.push(`n."text" ILIKE ${params.add(`%${escapeLike(word)}%`)}`);
9735
- const rows = await this.#rows(this.#client, `SELECT ${this.#noteColumns()}, row_to_json(np) AS "pageJson" FROM ${this.#visibleNotes(filter)} AND ${extra.join(" AND ")} ORDER BY n."updatedAt" DESC, n."id" DESC LIMIT ${limit - hits.length}`, params.values);
9763
+ const noteHas = words.map((word) => `n."text" ILIKE ${params.add(`%${escapeLike(word)}%`)}`);
9764
+ extra.push(`(${noteHas.join(" OR ")})`);
9765
+ const noteScore = noteHas.map((has) => `(CASE WHEN ${has} THEN 1 ELSE 0 END)`).join(" + ");
9766
+ const rows = await this.#rows(this.#client, `SELECT ${this.#noteColumns()}, row_to_json(np) AS "pageJson" FROM ${this.#visibleNotes(filter)} AND ${extra.join(" AND ")} ORDER BY (${noteScore}) DESC, n."updatedAt" DESC, n."id" DESC LIMIT ${limit - hits.length}`, params.values);
9736
9767
  for (const row of rows) hits.push({
9737
9768
  type: "note",
9738
9769
  note: toNote(row),
@@ -10076,6 +10107,9 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
10076
10107
  listNotes(input) {
10077
10108
  return this.#pages.listNotes(input);
10078
10109
  }
10110
+ countChildPages(input) {
10111
+ return this.#pages.countChildPages(input);
10112
+ }
10079
10113
  countNotes(input) {
10080
10114
  return this.#pages.countNotes(input);
10081
10115
  }
@@ -19025,6 +19059,8 @@ const timeToFirstTokenSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIE
19025
19059
  const spanQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {});
19026
19060
  const traceQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
19027
19061
  const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
19062
+ /** Core before the fields rejects their trace and span predicates, so the feature is only declared when core plans them. */
19063
+ const contextFieldFeatures = "requestId" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {}) ? ["trace-query-context-fields"] : [];
19028
19064
  const timingFeatures = [
19029
19065
  ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
19030
19066
  ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
@@ -19059,6 +19095,9 @@ const TRACE_FIELDS = {
19059
19095
  sessionId: "r.\"sessionId\"",
19060
19096
  userId: "r.\"userId\"",
19061
19097
  organizationId: "r.\"organizationId\"",
19098
+ requestId: "r.\"requestId\"",
19099
+ serviceName: "r.\"serviceName\"",
19100
+ experimentId: "r.\"experimentId\"",
19062
19101
  startedAt: "r.\"startedAt\"",
19063
19102
  endedAt: "r.\"endedAt\"",
19064
19103
  durationMs: durationMsSql("r.\"startedAt\"", "r.\"endedAt\""),
@@ -19098,7 +19137,10 @@ const SPAN_FIELDS = {
19098
19137
  runId: "s.\"runId\"",
19099
19138
  sessionId: "s.\"sessionId\"",
19100
19139
  userId: "s.\"userId\"",
19101
- organizationId: "s.\"organizationId\""
19140
+ organizationId: "s.\"organizationId\"",
19141
+ requestId: "s.\"requestId\"",
19142
+ serviceName: "s.\"serviceName\"",
19143
+ experimentId: "s.\"experimentId\""
19102
19144
  };
19103
19145
  const SPAN_QUERY_FIELDS = {
19104
19146
  ...SPAN_FIELDS,
@@ -19107,9 +19149,6 @@ const SPAN_QUERY_FIELDS = {
19107
19149
  parentSpanId: "s.\"parentSpanId\"",
19108
19150
  threadId: "s.\"threadId\"",
19109
19151
  resourceId: "s.\"resourceId\"",
19110
- requestId: "s.\"requestId\"",
19111
- serviceName: "s.\"serviceName\"",
19112
- experimentId: "s.\"experimentId\"",
19113
19152
  environment: "s.\"environment\"",
19114
19153
  executionSource: "s.\"executionSource\"",
19115
19154
  parentEntityType: "s.\"parentEntityType\"",
@@ -19373,6 +19412,9 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
19373
19412
  "sessionId",
19374
19413
  "userId",
19375
19414
  "organizationId",
19415
+ "requestId",
19416
+ "serviceName",
19417
+ "experimentId",
19376
19418
  "startedAt",
19377
19419
  "endedAt",
19378
19420
  "entityId",
@@ -19544,7 +19586,10 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
19544
19586
  s."runId",
19545
19587
  s."sessionId",
19546
19588
  s."userId",
19547
- s."organizationId"
19589
+ s."organizationId",
19590
+ s."requestId",
19591
+ s."serviceName",
19592
+ s."experimentId"
19548
19593
  FROM ${spanTable} s
19549
19594
  WHERE s."traceId" IS NOT NULL
19550
19595
  AND s."traceId" IN (SELECT "traceId" FROM root_scope)
@@ -20526,6 +20571,18 @@ const PERCENTILES = {
20526
20571
  "duration.p95": .95,
20527
20572
  "duration.p99": .99
20528
20573
  };
20574
+ /** Quantile for each percentile statistic of a time-to-first measure. */
20575
+ const STATISTIC_PERCENTILES = {
20576
+ p50: .5,
20577
+ p90: .9,
20578
+ p95: .95,
20579
+ p99: .99
20580
+ };
20581
+ /**
20582
+ * Store features this compiler supports beyond `trace-aggregate` / `span-aggregate`. Core before
20583
+ * the time-to-first measures rejects them, so they are only declared when core plans them.
20584
+ */
20585
+ const aggregateTimeToFirstFeatures = "timeToFirstToken.p50" in _mastra_core_storage.TRACE_AGGREGATE_MEASURE_REGISTRY ? ["aggregate-time-to-first-measures"] : [];
20529
20586
  const COMPARISON_SQL = {
20530
20587
  eq: "=",
20531
20588
  ne: "<>",
@@ -20593,6 +20650,8 @@ function compilePostgresAggregate(schema, plan, source) {
20593
20650
  factColumns.push(`(${distinctSql(measure.field)})::text AS ${column}`);
20594
20651
  }
20595
20652
  factColumns.push(`(${durationMsSql("r.\"startedAt\"", "r.\"endedAt\"")})::float8 AS "durationMs"`, `r."error" IS NOT NULL AS "isError"`);
20653
+ const timeToFirstFields = new Set(plan.measures.map((measure) => measureRule(measure.name)?.field));
20654
+ for (const field of timeToFirstFields) if (field) factColumns.push(`r."${field}"::float8 AS "${field}"`);
20596
20655
  if (plan.interval !== void 0) {
20597
20656
  const intervalMs = _mastra_core_storage.TRACE_AGGREGATE_INTERVAL_MS[plan.interval];
20598
20657
  if (intervalMs === void 0) throw new Error(`Unsupported trusted trace-aggregate interval: ${plan.interval}`);
@@ -20624,6 +20683,15 @@ function compilePostgresAggregate(schema, plan, source) {
20624
20683
  const aggregate = rule.statistic === "avg" ? "AVG" : "SUM";
20625
20684
  return `(CASE WHEN ${mixedUnitsSql(alias)} THEN NULL ELSE ${aggregate}(${column("traceCost")}) END)::float8`;
20626
20685
  }
20686
+ if (rule?.field) {
20687
+ const value = column(rule.field);
20688
+ if (rule.statistic === "count") return `COUNT(${value})::float8`;
20689
+ if (rule.statistic === "avg") return `AVG(${value})`;
20690
+ if (rule.statistic === "min") return `MIN(${value})`;
20691
+ if (rule.statistic === "max") return `MAX(${value})`;
20692
+ const quantile = rule.statistic && STATISTIC_PERCENTILES[rule.statistic];
20693
+ if (quantile !== void 0) return `percentile_cont(${quantile}) WITHIN GROUP (ORDER BY ${value})`;
20694
+ }
20627
20695
  if (name === "errorCount") return `(COUNT(*) FILTER (WHERE ${column("isError")}))::float8`;
20628
20696
  if (name === "errorRate") return `(COUNT(*) FILTER (WHERE ${column("isError")}))::float8 / COUNT(*)`;
20629
20697
  if (name === "duration.avg") return `AVG(${column("durationMs")})`;
@@ -21778,6 +21846,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21778
21846
  "metric-discovery",
21779
21847
  "trace-query",
21780
21848
  "trace-aggregate",
21849
+ ...aggregateTimeToFirstFeatures,
21781
21850
  ...spanQueryFeatures,
21782
21851
  ...spanAggregateFeatures,
21783
21852
  ...typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
@@ -21787,6 +21856,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21787
21856
  "trace-query-tenant-scope",
21788
21857
  "feedback",
21789
21858
  "trace-query-context-ids",
21859
+ ...contextFieldFeatures,
21790
21860
  ...timingFeatures,
21791
21861
  ...outcomeFeatures,
21792
21862
  "trace-query-root-span-fields",
@@ -21804,6 +21874,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21804
21874
  "delta-polling",
21805
21875
  "trace-query",
21806
21876
  "trace-aggregate",
21877
+ ...aggregateTimeToFirstFeatures,
21807
21878
  ...spanQueryFeatures,
21808
21879
  ...spanAggregateFeatures,
21809
21880
  ...typeof _mastra_core_storage.planThreadRowsQuery === "function" ? ["thread-query-rows"] : [],
@@ -21813,6 +21884,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21813
21884
  "trace-query-tenant-scope",
21814
21885
  "feedback",
21815
21886
  "trace-query-context-ids",
21887
+ ...contextFieldFeatures,
21816
21888
  ...timingFeatures,
21817
21889
  ...outcomeFeatures,
21818
21890
  "trace-query-root-span-fields",