@mastra/pg 1.35.0-alpha.1 → 1.35.0-alpha.3

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 (43) hide show
  1. package/dist/_types/{@internal_core → @internal_core@0.1.4}/dist/knowledge-compat/index.d.ts +17 -2
  2. package/dist/index.cjs +252 -39
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.js +252 -39
  5. package/dist/index.js.map +1 -1
  6. package/dist/storage/domains/agents/index.d.ts.map +1 -1
  7. package/dist/storage/domains/knowledge/index.d.ts +8 -1
  8. package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
  9. package/dist/storage/domains/knowledge/pages-schema.d.ts +1 -1
  10. package/dist/storage/domains/knowledge/pages-schema.d.ts.map +1 -1
  11. package/dist/storage/domains/knowledge/pages.d.ts +34 -11
  12. package/dist/storage/domains/knowledge/pages.d.ts.map +1 -1
  13. package/dist/storage/domains/memory/index.d.ts +1 -1
  14. package/dist/storage/domains/observability/v-next/index.d.ts +1 -1
  15. package/dist/storage/domains/observability/v-next/index.d.ts.map +1 -1
  16. package/dist/storage/domains/observability/v-next/trace-query.d.ts +2 -0
  17. package/dist/storage/domains/observability/v-next/trace-query.d.ts.map +1 -1
  18. package/package.json +7 -7
  19. package/dist/docs/SKILL.md +0 -48
  20. package/dist/docs/assets/SOURCE_MAP.json +0 -6
  21. package/dist/docs/references/docs-deployment-workers.md +0 -404
  22. package/dist/docs/references/docs-memory-semantic-recall.md +0 -485
  23. package/dist/docs/references/docs-memory-working-memory.md +0 -450
  24. package/dist/docs/references/docs-storage.md +0 -224
  25. package/dist/docs/references/integrations-databases-clickhouse-managed-postgres.md +0 -82
  26. package/dist/docs/references/integrations-databases-dynamodb.md +0 -286
  27. package/dist/docs/references/integrations-databases-neon.md +0 -222
  28. package/dist/docs/references/integrations-databases-postgresql.md +0 -592
  29. package/dist/docs/references/integrations-vectors-pg.md +0 -451
  30. package/dist/docs/references/reference-memory-memory-class.md +0 -237
  31. package/dist/docs/references/reference-processors-message-history-processor.md +0 -89
  32. package/dist/docs/references/reference-processors-semantic-recall-processor.md +0 -119
  33. package/dist/docs/references/reference-processors-working-memory-processor.md +0 -154
  34. package/dist/docs/references/reference-rag-chunking-and-embedding.md +0 -184
  35. package/dist/docs/references/reference-rag-metadata-filters.md +0 -238
  36. package/dist/docs/references/reference-rag-overview.md +0 -76
  37. package/dist/docs/references/reference-rag-retrieval.md +0 -672
  38. package/dist/docs/references/reference-rag-vector-databases.md +0 -808
  39. package/dist/docs/references/reference-storage-composite.md +0 -433
  40. package/dist/docs/references/reference-storage-retention.md +0 -310
  41. package/dist/docs/references/reference-tools-vector-query-tool.md +0 -500
  42. /package/dist/_types/{@internal_core → @internal_core@0.1.4}/dist/memory/index.d.ts +0 -0
  43. /package/dist/_types/{@internal_core → @internal_core@0.1.4}/package.json +0 -0
@@ -318,6 +318,7 @@ export interface KnowledgePagesCore {
318
318
  MAX_KNOWLEDGE_MOVE_PAGES: number;
319
319
  MAX_KNOWLEDGE_PAGE_DEPTH: number;
320
320
  MAX_KNOWLEDGE_PAGE_LIST_LIMIT: number;
321
+ MAX_KNOWLEDGE_PAGE_LINKS: number;
321
322
  KnowledgeMoveTooLargeError: new (pageId: string, pages: number) => Error;
322
323
  KnowledgePageDepthError: new (pageId: string) => Error;
323
324
  KnowledgePageHasChildrenError: new (pageId: string) => Error;
@@ -325,11 +326,25 @@ export interface KnowledgePagesCore {
325
326
  }
326
327
  /** Loads core's page-store runtime once, after checking core advertises `knowledge-pages`. */
327
328
  export declare function createKnowledgePagesCoreLoader(features: ReadonlySet<string>, loadStorageModule: () => Promise<unknown>): () => Promise<KnowledgePagesCore>;
329
+ /** Most words one keyword search uses; every word adds a substring check per page in each store. */
330
+ export declare const KNOWLEDGE_SEARCH_MAX_WORDS = 16;
328
331
  /**
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.
332
+ * Keyword-search words: the query split on whitespace, lowercased, deduplicated, keeping the first
333
+ * {@link KNOWLEDGE_SEARCH_MAX_WORDS}. A hit needs at least one of them (as a case-insensitive
334
+ * substring); an empty list means the search returns nothing.
331
335
  */
332
336
  export declare function knowledgeSearchWords(query: string): string[];
337
+ /**
338
+ * Points one matched search word earns on a page, by the best field it appears in: name, then
339
+ * description, then body. A page whose name contains the whole query ranks first; below that, pages
340
+ * rank by how many query words they match, and these points only break ties between pages that
341
+ * match the same number of words.
342
+ */
343
+ export declare const KNOWLEDGE_SEARCH_WEIGHTS: {
344
+ readonly name: 3;
345
+ readonly description: 2;
346
+ readonly body: 1;
347
+ };
333
348
  /**
334
349
  * Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
335
350
  * non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
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"),
@@ -8645,7 +8649,8 @@ const KNOWLEDGE_PAGES_CORE_FUNCTIONS = [
8645
8649
  const KNOWLEDGE_PAGES_CORE_NUMBERS = [
8646
8650
  "MAX_KNOWLEDGE_MOVE_PAGES",
8647
8651
  "MAX_KNOWLEDGE_PAGE_DEPTH",
8648
- "MAX_KNOWLEDGE_PAGE_LIST_LIMIT"
8652
+ "MAX_KNOWLEDGE_PAGE_LIST_LIMIT",
8653
+ "MAX_KNOWLEDGE_PAGE_LINKS"
8649
8654
  ];
8650
8655
  function resolveKnowledgePagesCore(storageModule) {
8651
8656
  const module = typeof storageModule === "object" && storageModule !== null ? storageModule : {};
@@ -8666,13 +8671,25 @@ function createKnowledgePagesCoreLoader(features, loadStorageModule) {
8666
8671
  };
8667
8672
  }
8668
8673
  /**
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.
8674
+ * Keyword-search words: the query split on whitespace, lowercased, deduplicated, keeping the first
8675
+ * {@link KNOWLEDGE_SEARCH_MAX_WORDS}. A hit needs at least one of them (as a case-insensitive
8676
+ * substring); an empty list means the search returns nothing.
8671
8677
  */
8672
8678
  function knowledgeSearchWords(query) {
8673
- return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
8679
+ return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))].slice(0, 16);
8674
8680
  }
8675
8681
  /**
8682
+ * Points one matched search word earns on a page, by the best field it appears in: name, then
8683
+ * description, then body. A page whose name contains the whole query ranks first; below that, pages
8684
+ * rank by how many query words they match, and these points only break ties between pages that
8685
+ * match the same number of words.
8686
+ */
8687
+ const KNOWLEDGE_SEARCH_WEIGHTS = {
8688
+ name: 3,
8689
+ description: 2,
8690
+ body: 1
8691
+ };
8692
+ /**
8676
8693
  * Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
8677
8694
  * non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
8678
8695
  */
@@ -8691,7 +8708,8 @@ const PG_KNOWLEDGE_PAGES_TABLES = {
8691
8708
  mentions: "mastra_knowledge_mentions",
8692
8709
  pins: "mastra_knowledge_pins",
8693
8710
  activity: "mastra_knowledge_activity",
8694
- state: "mastra_knowledge_access_state"
8711
+ state: "mastra_knowledge_access_state",
8712
+ links: "mastra_knowledge_page_links"
8695
8713
  };
8696
8714
  /**
8697
8715
  * Schema-4 DDL. `COLLATE "C"` on every column that is ordered, prefix-matched or compared for
@@ -8750,7 +8768,9 @@ function pgKnowledgePagesDDL(schemaName) {
8750
8768
  )`,
8751
8769
  `CREATE TABLE IF NOT EXISTS ${q(t.pins)} (
8752
8770
  "id" text PRIMARY KEY,
8753
- "pageId" text NOT NULL UNIQUE REFERENCES ${q(t.pages)} ("id"),
8771
+ "pageId" text NOT NULL REFERENCES ${q(t.pages)} ("id"),
8772
+ "kind" text NOT NULL DEFAULT 'page',
8773
+ "depth" integer,
8754
8774
  "audience" text[] NOT NULL,
8755
8775
  "createdBy" text NOT NULL,
8756
8776
  "createdAt" timestamptz NOT NULL,
@@ -8782,6 +8802,19 @@ function pgKnowledgePagesDDL(schemaName) {
8782
8802
  index("mastra_knowledge_mentions_page_idx", t.mentions, "\"pageId\""),
8783
8803
  `CREATE INDEX IF NOT EXISTS "mastra_knowledge_pins_audience_gin" ON ${q(t.pins)} USING gin ("audience")`,
8784
8804
  index("mastra_knowledge_pins_created_idx", t.pins, "\"createdAt\",\"id\""),
8805
+ `ALTER TABLE ${q(t.pins)} ADD COLUMN IF NOT EXISTS "kind" text NOT NULL DEFAULT 'page'`,
8806
+ `ALTER TABLE ${q(t.pins)} ADD COLUMN IF NOT EXISTS "depth" integer`,
8807
+ `ALTER TABLE ${q(t.pins)} DROP CONSTRAINT IF EXISTS "${t.pins}_pageId_key"`,
8808
+ index("mastra_knowledge_pins_page_kind_idx", t.pins, "\"pageId\",\"kind\"", "", true),
8809
+ `CREATE TABLE IF NOT EXISTS ${q(t.links)} (
8810
+ "fromPageId" text NOT NULL,
8811
+ "ref" text COLLATE "C" NOT NULL,
8812
+ "toPageId" text,
8813
+ "count" integer NOT NULL,
8814
+ PRIMARY KEY ("fromPageId", "ref")
8815
+ )`,
8816
+ index("mastra_knowledge_page_links_to_idx", t.links, "\"toPageId\"", "\"toPageId\" IS NOT NULL"),
8817
+ index("mastra_knowledge_page_links_ref_idx", t.links, "\"ref\"", "\"toPageId\" IS NULL"),
8785
8818
  index("mastra_knowledge_activity_page_idx", t.activity, "\"pageId\",\"createdAt\",\"id\""),
8786
8819
  index("mastra_knowledge_activity_created_idx", t.activity, "\"createdAt\",\"id\"")
8787
8820
  ];
@@ -8926,6 +8959,8 @@ function toPin(row) {
8926
8959
  return {
8927
8960
  id: String(row.id),
8928
8961
  pageId: String(row.pageId),
8962
+ kind: String(row.kind),
8963
+ ...row.depth == null ? {} : { depth: Number(row.depth) },
8929
8964
  audience: row.audience ?? [],
8930
8965
  createdBy: String(row.createdBy),
8931
8966
  createdAt: new Date(row.createdAt),
@@ -8977,6 +9012,9 @@ var PgKnowledgePages = class {
8977
9012
  get #pins() {
8978
9013
  return this.#q(PG_KNOWLEDGE_PAGES_TABLES.pins);
8979
9014
  }
9015
+ get #links() {
9016
+ return this.#q(PG_KNOWLEDGE_PAGES_TABLES.links);
9017
+ }
8980
9018
  get #activity() {
8981
9019
  return this.#q(PG_KNOWLEDGE_PAGES_TABLES.activity);
8982
9020
  }
@@ -9213,6 +9251,7 @@ var PgKnowledgePages = class {
9213
9251
  const params = new Params();
9214
9252
  const where = [`"isScope"`, `"deletedAt" IS NULL`];
9215
9253
  where.push(input.parentId === null ? `"parentId" IS NULL` : `"parentId" = ${params.add(input.parentId)}`);
9254
+ if (input.read && input.read !== "all") where.push(`"id" IN (${new ReadFilter(input.read, params, this.#pages).readableIds()})`);
9216
9255
  if (input.cursor) {
9217
9256
  const [position, id] = decodeCursor(input.cursor, 2);
9218
9257
  where.push(`("position", "id") > (${params.add(position)}, ${params.add(id)})`);
@@ -9488,9 +9527,17 @@ var PgKnowledgePages = class {
9488
9527
  await tx.query(`UPDATE ${this.#notes} SET "pageId" = $2 WHERE "pageId" = $1`, [source.id, target.id]);
9489
9528
  await tx.query(`DELETE FROM ${this.#mentions} m WHERE m."pageId" = $1 AND EXISTS (SELECT 1 FROM ${this.#mentions} t WHERE t."noteId" = m."noteId" AND t."pageId" = $2)`, [source.id, target.id]);
9490
9529
  await tx.query(`UPDATE ${this.#mentions} SET "pageId" = $2 WHERE "pageId" = $1`, [source.id, target.id]);
9491
- const sourcePin = (await this.#rows(tx, `SELECT * FROM ${this.#pins} WHERE "pageId" = $1 FOR UPDATE`, [source.id]))[0];
9492
- if (sourcePin) {
9493
- await this.#upsertPinAudience(tx, target.id, sourcePin.audience ?? [], String(sourcePin.createdBy), now);
9530
+ const sourcePins = await this.#rows(tx, `SELECT * FROM ${this.#pins} WHERE "pageId" = $1 ORDER BY "kind" FOR UPDATE`, [source.id]);
9531
+ for (const sourcePin of sourcePins) {
9532
+ await this.#upsertPinAudience(tx, {
9533
+ pageId: target.id,
9534
+ kind: String(sourcePin.kind),
9535
+ depth: sourcePin.depth == null ? null : Number(sourcePin.depth),
9536
+ audience: sourcePin.audience ?? [],
9537
+ createdBy: String(sourcePin.createdBy),
9538
+ now,
9539
+ keepDepth: true
9540
+ });
9494
9541
  await tx.query(`DELETE FROM ${this.#pins} WHERE "id" = $1`, [String(sourcePin.id)]);
9495
9542
  }
9496
9543
  const rows = await this.#rows(tx, `SELECT * FROM ${this.#pages} WHERE "id" = $1`, [target.id]);
@@ -9550,6 +9597,118 @@ var PgKnowledgePages = class {
9550
9597
  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
9598
  return this.#noteOutput(rows, limit);
9552
9599
  }
9600
+ async countChildScopes(input) {
9601
+ const ids = [...new Set(input.parentIds)];
9602
+ const counts = Object.fromEntries(ids.map((id) => [id, 0]));
9603
+ if (!ids.length) return counts;
9604
+ const params = new Params();
9605
+ const filter = new ReadFilter(input.read, params, this.#pages);
9606
+ const rows = await this.#rows(this.#client, `SELECT s."parentId" AS "parentId", count(*)::int AS "count" FROM ${this.#pages} s
9607
+ WHERE s."parentId" = ANY(${params.add(ids)}::text[]) AND s."isScope" AND s."deletedAt" IS NULL AND ${filter.readable("s")}
9608
+ GROUP BY s."parentId"`, params.values);
9609
+ for (const row of rows) counts[row.parentId] = Number(row.count);
9610
+ return counts;
9611
+ }
9612
+ async countChildPages(input) {
9613
+ const ids = [...new Set(input.parentIds)];
9614
+ const counts = Object.fromEntries(ids.map((id) => [id, 0]));
9615
+ if (!ids.length) return counts;
9616
+ const params = new Params();
9617
+ const filter = new ReadFilter(input.read, params, this.#pages);
9618
+ 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"
9619
+ WHERE p."parentId" = ANY(${params.add(ids)}::text[]) AND NOT p."isScope" AND p."deletedAt" IS NULL AND ${filter.readable("s")}
9620
+ GROUP BY p."parentId"`, params.values);
9621
+ for (const row of rows) counts[row.parentId] = Number(row.count);
9622
+ return counts;
9623
+ }
9624
+ async replacePageBodyLinks(input) {
9625
+ await this.#tx(async (tx) => {
9626
+ await tx.query(`DELETE FROM ${this.#links} WHERE "fromPageId" = $1`, [input.pageId]);
9627
+ if (!input.links.length) return;
9628
+ await tx.query(`INSERT INTO ${this.#links} ("fromPageId","ref","toPageId","count") SELECT $1, r, t, c FROM unnest($2::text[], $3::text[], $4::int[]) AS x(r, t, c)`, [
9629
+ input.pageId,
9630
+ input.links.map((l) => l.ref),
9631
+ input.links.map((l) => l.toPageId),
9632
+ input.links.map((l) => l.count)
9633
+ ]);
9634
+ });
9635
+ }
9636
+ async listPageBodyLinkSources(input) {
9637
+ const refs = [...new Set(input.refs ?? [])];
9638
+ const targets = [...new Set(input.toPageIds ?? [])];
9639
+ if (!refs.length && !targets.length) return [];
9640
+ const parts = [];
9641
+ const params = new Params();
9642
+ if (refs.length) parts.push(`("toPageId" IS NULL AND "ref" = ANY(${params.add(refs)}::text[]))`);
9643
+ if (targets.length) parts.push(`"toPageId" = ANY(${params.add(targets)}::text[])`);
9644
+ return (await this.#rows(this.#client, `SELECT DISTINCT "fromPageId" FROM ${this.#links} WHERE ${parts.join(" OR ")}`, params.values)).map((row) => String(row.fromPageId));
9645
+ }
9646
+ /**
9647
+ * Every resolved link touching `ids` whose both ends are live and readable, one row per
9648
+ * (from, to, kind); no self-links. Note links go through the same visible-note rule as listNotes.
9649
+ */
9650
+ #visibleLinkEdges(filter, params, ids, direction, kinds) {
9651
+ const wanted = new Set(kinds ?? ["body", "note"]);
9652
+ const idsParam = `${params.add(ids)}::text[]`;
9653
+ const touch = (from, to) => direction === "out" ? `${from} = ANY(${idsParam})` : direction === "in" ? `${to} = ANY(${idsParam})` : `(${from} = ANY(${idsParam}) OR ${to} = ANY(${idsParam}))`;
9654
+ const branches = [];
9655
+ if (wanted.has("body")) branches.push(`SELECT l."fromPageId" AS "fromPageId", l."toPageId" AS "toPageId", 'body'::text AS "kind", sum(l."count")::int AS "count"
9656
+ FROM ${this.#links} l
9657
+ JOIN ${this.#pages} fp ON fp."id" = l."fromPageId" AND fp."deletedAt" IS NULL
9658
+ JOIN ${this.#pages} fs ON fs."id" = fp."scopeId"
9659
+ JOIN ${this.#pages} tp ON tp."id" = l."toPageId" AND tp."deletedAt" IS NULL
9660
+ JOIN ${this.#pages} ts ON ts."id" = tp."scopeId"
9661
+ WHERE l."toPageId" IS NOT NULL AND l."fromPageId" <> l."toPageId" AND ${touch("l.\"fromPageId\"", "l.\"toPageId\"")}
9662
+ AND ${filter.readable("fs")} AND ${filter.readable("ts")}
9663
+ GROUP BY l."fromPageId", l."toPageId"`);
9664
+ if (wanted.has("note")) branches.push(`SELECT vn."pageId" AS "fromPageId", mn."pageId" AS "toPageId", 'note'::text AS "kind", count(*)::int AS "count"
9665
+ FROM (SELECT n."id", n."pageId" FROM ${this.#visibleNotes(filter)}
9666
+ AND n."deletedAt" IS NULL AND np."deletedAt" IS NULL
9667
+ AND (n."pageId" = ANY(${idsParam}) OR n."id" IN (SELECT mm."noteId" FROM ${this.#mentions} mm WHERE mm."pageId" = ANY(${idsParam})))) vn
9668
+ JOIN ${this.#mentions} mn ON mn."noteId" = vn."id"
9669
+ JOIN ${this.#pages} tp ON tp."id" = mn."pageId" AND tp."deletedAt" IS NULL
9670
+ JOIN ${this.#pages} ts ON ts."id" = tp."scopeId"
9671
+ WHERE mn."pageId" <> vn."pageId" AND ${touch("vn.\"pageId\"", "mn.\"pageId\"")} AND ${filter.readable("ts")}
9672
+ GROUP BY vn."pageId", mn."pageId"`);
9673
+ return branches.length ? branches.join(" UNION ALL ") : `SELECT NULL::text AS "fromPageId", NULL::text AS "toPageId", NULL::text AS "kind", NULL::int AS "count" WHERE FALSE`;
9674
+ }
9675
+ async countPageLinks(input) {
9676
+ const ids = [...new Set(input.pageIds)];
9677
+ const counts = Object.fromEntries(ids.map((id) => [id, {
9678
+ in: 0,
9679
+ out: 0
9680
+ }]));
9681
+ if (!ids.length) return counts;
9682
+ const params = new Params();
9683
+ const filter = new ReadFilter(input.read, params, this.#pages);
9684
+ const edges = this.#visibleLinkEdges(filter, params, ids, "both", input.kinds);
9685
+ const rows = await this.#rows(this.#client, `WITH e AS (${edges}), r AS (
9686
+ SELECT "fromPageId" AS "id", count(DISTINCT "toPageId")::int AS "out", 0 AS "in" FROM e WHERE "fromPageId" = ANY(${params.add(ids)}::text[]) GROUP BY "fromPageId"
9687
+ UNION ALL
9688
+ SELECT "toPageId", 0, count(DISTINCT "fromPageId")::int FROM e WHERE "toPageId" = ANY(${params.add(ids)}::text[]) GROUP BY "toPageId")
9689
+ SELECT "id", sum("out")::int AS "out", sum("in")::int AS "in" FROM r GROUP BY "id"`, params.values);
9690
+ for (const row of rows) counts[String(row.id)] = {
9691
+ in: Number(row.in),
9692
+ out: Number(row.out)
9693
+ };
9694
+ return counts;
9695
+ }
9696
+ async listPageLinks(input) {
9697
+ const limit = input.limit ?? 1e3;
9698
+ if (!Number.isInteger(limit) || limit < 1) throw new RangeError("Knowledge link limit must be a positive integer");
9699
+ const core = await loadPagesCore();
9700
+ const ids = [...new Set(input.pageIds)];
9701
+ if (!ids.length) return [];
9702
+ const params = new Params();
9703
+ const filter = new ReadFilter(input.read, params, this.#pages);
9704
+ const edges = this.#visibleLinkEdges(filter, params, ids, input.direction ?? "both", input.kinds);
9705
+ return (await this.#rows(this.#client, `SELECT * FROM (${edges}) e ORDER BY "fromPageId" COLLATE "C", "toPageId" COLLATE "C", CASE "kind" WHEN 'body' THEN 0 ELSE 1 END LIMIT ${Math.min(limit, core.MAX_KNOWLEDGE_PAGE_LINKS)}`, params.values)).map((row) => ({
9706
+ fromPageId: String(row.fromPageId),
9707
+ toPageId: String(row.toPageId),
9708
+ kind: row.kind,
9709
+ count: Number(row.count)
9710
+ }));
9711
+ }
9553
9712
  async countNotes(input) {
9554
9713
  const ids = [...new Set(input.pageIds)];
9555
9714
  const counts = Object.fromEntries(ids.map((id) => [id, 0]));
@@ -9664,7 +9823,7 @@ var PgKnowledgePages = class {
9664
9823
  async removeNote(input) {
9665
9824
  return this.#tx(async (tx) => {
9666
9825
  const note = await this.#noteById(tx, input.id, true);
9667
- if (note.deletedAt) return note;
9826
+ if (note.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("note", input.id);
9668
9827
  const now = /* @__PURE__ */ new Date();
9669
9828
  await tx.query(`UPDATE ${this.#notes} SET "deletedAt" = $2, "deletedBy" = $3, "version" = "version" + 1, "updatedAt" = $2 WHERE "id" = $1`, [
9670
9829
  input.id,
@@ -9717,9 +9876,12 @@ var PgKnowledgePages = class {
9717
9876
  filter.readable("s")
9718
9877
  ];
9719
9878
  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);
9879
+ const anyField = patterns.map((pattern) => `(p."name" ILIKE ${pattern} OR p."description" ILIKE ${pattern} OR p."body" ILIKE ${pattern})`).join(" OR ");
9880
+ where.push(`(${anyField})`);
9881
+ 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(" + ");
9882
+ 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(" + ");
9883
+ const tier = `CASE WHEN p."canonicalName" LIKE ${phrasePattern} THEN 0 ELSE 1 END`;
9884
+ 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
9885
  for (const row of rows) hits.push({
9724
9886
  type: "page",
9725
9887
  page: toPage(row)
@@ -9731,8 +9893,10 @@ var PgKnowledgePages = class {
9731
9893
  const filter = new ReadFilter(input.read, params, this.#pages);
9732
9894
  const extra = [`n."deletedAt" IS NULL`, `np."deletedAt" IS NULL`];
9733
9895
  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);
9896
+ const noteHas = words.map((word) => `n."text" ILIKE ${params.add(`%${escapeLike(word)}%`)}`);
9897
+ extra.push(`(${noteHas.join(" OR ")})`);
9898
+ const noteScore = noteHas.map((has) => `(CASE WHEN ${has} THEN 1 ELSE 0 END)`).join(" + ");
9899
+ 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
9900
  for (const row of rows) hits.push({
9737
9901
  type: "note",
9738
9902
  note: toNote(row),
@@ -9771,10 +9935,18 @@ var PgKnowledgePages = class {
9771
9935
  };
9772
9936
  }
9773
9937
  async addPinAudience(input) {
9938
+ const kind = input.kind ?? "page";
9774
9939
  return this.#tx(async (tx) => {
9775
9940
  const page = await this.#requireLivePage(tx, input.pageId, false);
9776
9941
  const now = /* @__PURE__ */ new Date();
9777
- const { pin, changed } = await this.#upsertPinAudience(tx, page.id, input.audience, input.createdBy, now);
9942
+ const { pin, changed } = await this.#upsertPinAudience(tx, {
9943
+ pageId: page.id,
9944
+ kind,
9945
+ depth: kind === "list" ? input.depth ?? 1 : null,
9946
+ audience: input.audience,
9947
+ createdBy: input.createdBy,
9948
+ now
9949
+ });
9778
9950
  if (changed) await this.#logActivity(tx, {
9779
9951
  action: "pin-added",
9780
9952
  scopeId: page.scopeId,
@@ -9785,28 +9957,35 @@ var PgKnowledgePages = class {
9785
9957
  return pin;
9786
9958
  });
9787
9959
  }
9788
- async #upsertPinAudience(db, pageId, audience, createdBy, now) {
9789
- const existing = (await this.#rows(db, `SELECT * FROM ${this.#pins} WHERE "pageId" = $1 FOR UPDATE`, [pageId]))[0];
9960
+ /** `keepDepth` (merges) leaves an existing pin's depth as it is instead of setting `depth`. */
9961
+ async #upsertPinAudience(db, input) {
9962
+ const { pageId, kind, audience, now } = input;
9963
+ const existing = (await this.#rows(db, `SELECT * FROM ${this.#pins} WHERE "pageId" = $1 AND "kind" = $2 FOR UPDATE`, [pageId, kind]))[0];
9790
9964
  if (!existing) return {
9791
- pin: toPin((await this.#rows(db, `INSERT INTO ${this.#pins} ("id","pageId","audience","createdBy","createdAt","updatedAt") VALUES ($1,$2,$3::text[],$4,$5,$5) RETURNING *`, [
9965
+ pin: toPin((await this.#rows(db, `INSERT INTO ${this.#pins} ("id","pageId","kind","depth","audience","createdBy","createdAt","updatedAt") VALUES ($1,$2,$3,$4,$5::text[],$6,$7,$7) RETURNING *`, [
9792
9966
  (0, _mastra_core_storage.createKnowledgeUlid)(),
9793
9967
  pageId,
9968
+ kind,
9969
+ input.depth,
9794
9970
  [...new Set(audience)],
9795
- createdBy,
9971
+ input.createdBy,
9796
9972
  iso(now)
9797
9973
  ]))[0]),
9798
9974
  changed: true
9799
9975
  };
9800
9976
  const current = existing.audience ?? [];
9801
9977
  const merged = [.../* @__PURE__ */ new Set([...current, ...audience])];
9802
- if (merged.length === current.length) return {
9978
+ const currentDepth = existing.depth == null ? null : Number(existing.depth);
9979
+ const depth = input.keepDepth ? currentDepth : input.depth;
9980
+ if (merged.length === current.length && depth === currentDepth) return {
9803
9981
  pin: toPin(existing),
9804
9982
  changed: false
9805
9983
  };
9806
9984
  return {
9807
- pin: toPin((await this.#rows(db, `UPDATE ${this.#pins} SET "audience" = $2::text[], "updatedAt" = $3 WHERE "id" = $1 RETURNING *`, [
9985
+ pin: toPin((await this.#rows(db, `UPDATE ${this.#pins} SET "audience" = $2::text[], "depth" = $3, "updatedAt" = $4 WHERE "id" = $1 RETURNING *`, [
9808
9986
  String(existing.id),
9809
9987
  merged,
9988
+ depth,
9810
9989
  iso(now)
9811
9990
  ]))[0]),
9812
9991
  changed: true
@@ -9814,7 +9993,7 @@ var PgKnowledgePages = class {
9814
9993
  }
9815
9994
  async removePinAudience(input) {
9816
9995
  return this.#tx(async (tx) => {
9817
- const existing = (await this.#rows(tx, `SELECT * FROM ${this.#pins} WHERE "pageId" = $1 FOR UPDATE`, [input.pageId]))[0];
9996
+ const existing = (await this.#rows(tx, `SELECT * FROM ${this.#pins} WHERE "pageId" = $1 AND "kind" = $2 FOR UPDATE`, [input.pageId, input.kind ?? "page"]))[0];
9818
9997
  if (!existing) return null;
9819
9998
  const current = existing.audience ?? [];
9820
9999
  const remaining = current.filter((entry) => !input.audience.includes(entry));
@@ -9846,6 +10025,7 @@ var PgKnowledgePages = class {
9846
10025
  t.mentions,
9847
10026
  t.stamps,
9848
10027
  t.notes,
10028
+ t.links,
9849
10029
  t.pages
9850
10030
  ].map((name) => this.#q(name)).join(", ");
9851
10031
  await this.#client.query(`TRUNCATE ${tables}`);
@@ -9864,9 +10044,10 @@ var PgKnowledgePages = class {
9864
10044
  const params = new Params();
9865
10045
  const where = [`p."deletedAt" IS NULL`, new ReadFilter(input.read, params, this.#pages).readable("s")];
9866
10046
  if (input.audience !== "all") where.push(`pn."audience" && ${params.add(input.audience)}::text[]`);
10047
+ if (input.kind) where.push(`pn."kind" = ${params.add(input.kind)}`);
9867
10048
  if (input.pageIds) where.push(`pn."pageId" = ANY(${params.add(input.pageIds)}::text[])`);
9868
- if (input.excludePageIds?.length) where.push(`pn."pageId" <> ALL(${params.add(input.excludePageIds)}::text[])`);
9869
- if (input.withBody) where.push(`p."body" ~ '\\S'`);
10049
+ if (input.excludePageIds?.length) where.push(`(pn."kind" <> 'page' OR pn."pageId" <> ALL(${params.add(input.excludePageIds)}::text[]))`);
10050
+ if (input.withBody) where.push(`(pn."kind" <> 'page' OR p."body" ~ '\\S')`);
9870
10051
  const from = `FROM ${this.#pins} pn JOIN ${this.#pages} p ON p."id" = pn."pageId" JOIN ${this.#pages} s ON s."id" = p."scopeId"`;
9871
10052
  const totalWhere = where.join(" AND ");
9872
10053
  if (input.cursor) {
@@ -9932,7 +10113,7 @@ async function pgKnowledgeSnapshot(db, schemaName) {
9932
10113
  }
9933
10114
  /** Drops exactly the Knowledge tables (never Memory's) and recreates schema 4. */
9934
10115
  async function resetPgKnowledgePagesSchema(client, schemaName) {
9935
- const tables = KNOWLEDGE_RESETTABLE_TABLE_NAMES.map((table) => qualify(schemaName, table)).join(", ");
10116
+ const tables = [...KNOWLEDGE_RESETTABLE_TABLE_NAMES, PG_KNOWLEDGE_PAGES_TABLES.links].map((table) => qualify(schemaName, table)).join(", ");
9936
10117
  await client.query(`DROP TABLE IF EXISTS ${tables} CASCADE`);
9937
10118
  await initPgKnowledgePagesSchema(client, schemaName);
9938
10119
  }
@@ -10007,7 +10188,8 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
10007
10188
  ...super.getCapabilities(),
10008
10189
  schemaVersion: 4,
10009
10190
  supportsSchemaInspection: true,
10010
- supportsExplicitReset: true
10191
+ supportsExplicitReset: true,
10192
+ supportsPinKinds: true
10011
10193
  };
10012
10194
  }
10013
10195
  async init() {
@@ -10037,6 +10219,9 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
10037
10219
  listChildScopes(input) {
10038
10220
  return this.#pages.listChildScopes(input);
10039
10221
  }
10222
+ countChildScopes(input) {
10223
+ return this.#pages.countChildScopes(input);
10224
+ }
10040
10225
  getPages(input) {
10041
10226
  return this.#pages.getPages(input);
10042
10227
  }
@@ -10076,6 +10261,21 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
10076
10261
  listNotes(input) {
10077
10262
  return this.#pages.listNotes(input);
10078
10263
  }
10264
+ countChildPages(input) {
10265
+ return this.#pages.countChildPages(input);
10266
+ }
10267
+ replacePageBodyLinks(input) {
10268
+ return this.#pages.replacePageBodyLinks(input);
10269
+ }
10270
+ listPageBodyLinkSources(input) {
10271
+ return this.#pages.listPageBodyLinkSources(input);
10272
+ }
10273
+ countPageLinks(input) {
10274
+ return this.#pages.countPageLinks(input);
10275
+ }
10276
+ listPageLinks(input) {
10277
+ return this.#pages.listPageLinks(input);
10278
+ }
10079
10279
  countNotes(input) {
10080
10280
  return this.#pages.countNotes(input);
10081
10281
  }
@@ -19025,6 +19225,8 @@ const timeToFirstTokenSortSupported = _mastra_core_storage.TRACE_QUERY_ORDER_FIE
19025
19225
  const spanQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.SPAN_QUERY_FIELD_REGISTRY ?? {});
19026
19226
  const traceQueryOutcomeFieldsSupported = "outcome" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {});
19027
19227
  const outcomeFeatures = [...spanQueryOutcomeFieldsSupported ? ["span-query-outcome-fields"] : [], ...traceQueryOutcomeFieldsSupported ? ["trace-query-outcome-fields"] : []];
19228
+ /** Core before the fields rejects their trace and span predicates, so the feature is only declared when core plans them. */
19229
+ const contextFieldFeatures = "requestId" in (_mastra_core_storage.TRACE_QUERY_FIELD_REGISTRY?.trace ?? {}) ? ["trace-query-context-fields"] : [];
19028
19230
  const timingFeatures = [
19029
19231
  ...timeToFirstChunkSupported ? ["trace-query-time-to-first-chunk"] : [],
19030
19232
  ...timeToFirstChunkSortSupported ? ["trace-query-time-to-first-chunk-sort"] : [],
@@ -19059,6 +19261,9 @@ const TRACE_FIELDS = {
19059
19261
  sessionId: "r.\"sessionId\"",
19060
19262
  userId: "r.\"userId\"",
19061
19263
  organizationId: "r.\"organizationId\"",
19264
+ requestId: "r.\"requestId\"",
19265
+ serviceName: "r.\"serviceName\"",
19266
+ experimentId: "r.\"experimentId\"",
19062
19267
  startedAt: "r.\"startedAt\"",
19063
19268
  endedAt: "r.\"endedAt\"",
19064
19269
  durationMs: durationMsSql("r.\"startedAt\"", "r.\"endedAt\""),
@@ -19098,7 +19303,10 @@ const SPAN_FIELDS = {
19098
19303
  runId: "s.\"runId\"",
19099
19304
  sessionId: "s.\"sessionId\"",
19100
19305
  userId: "s.\"userId\"",
19101
- organizationId: "s.\"organizationId\""
19306
+ organizationId: "s.\"organizationId\"",
19307
+ requestId: "s.\"requestId\"",
19308
+ serviceName: "s.\"serviceName\"",
19309
+ experimentId: "s.\"experimentId\""
19102
19310
  };
19103
19311
  const SPAN_QUERY_FIELDS = {
19104
19312
  ...SPAN_FIELDS,
@@ -19107,9 +19315,6 @@ const SPAN_QUERY_FIELDS = {
19107
19315
  parentSpanId: "s.\"parentSpanId\"",
19108
19316
  threadId: "s.\"threadId\"",
19109
19317
  resourceId: "s.\"resourceId\"",
19110
- requestId: "s.\"requestId\"",
19111
- serviceName: "s.\"serviceName\"",
19112
- experimentId: "s.\"experimentId\"",
19113
19318
  environment: "s.\"environment\"",
19114
19319
  executionSource: "s.\"executionSource\"",
19115
19320
  parentEntityType: "s.\"parentEntityType\"",
@@ -19373,6 +19578,9 @@ const ROOT_PUSHDOWN_FIELDS = /* @__PURE__ */ new Set([
19373
19578
  "sessionId",
19374
19579
  "userId",
19375
19580
  "organizationId",
19581
+ "requestId",
19582
+ "serviceName",
19583
+ "experimentId",
19376
19584
  "startedAt",
19377
19585
  "endedAt",
19378
19586
  "entityId",
@@ -19544,7 +19752,10 @@ function compilePostgresTraceScope(schema, selection, relationCollections, scope
19544
19752
  s."runId",
19545
19753
  s."sessionId",
19546
19754
  s."userId",
19547
- s."organizationId"
19755
+ s."organizationId",
19756
+ s."requestId",
19757
+ s."serviceName",
19758
+ s."experimentId"
19548
19759
  FROM ${spanTable} s
19549
19760
  WHERE s."traceId" IS NOT NULL
19550
19761
  AND s."traceId" IN (SELECT "traceId" FROM root_scope)
@@ -21811,6 +22022,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21811
22022
  "trace-query-tenant-scope",
21812
22023
  "feedback",
21813
22024
  "trace-query-context-ids",
22025
+ ...contextFieldFeatures,
21814
22026
  ...timingFeatures,
21815
22027
  ...outcomeFeatures,
21816
22028
  "trace-query-root-span-fields",
@@ -21838,6 +22050,7 @@ var ObservabilityStoragePostgresVNext = class ObservabilityStoragePostgresVNext
21838
22050
  "trace-query-tenant-scope",
21839
22051
  "feedback",
21840
22052
  "trace-query-context-ids",
22053
+ ...contextFieldFeatures,
21841
22054
  ...timingFeatures,
21842
22055
  ...outcomeFeatures,
21843
22056
  "trace-query-root-span-fields",