@mastra/pg 1.35.0-alpha.2 → 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.
- package/dist/_types/@internal_core@0.1.4/dist/knowledge-compat/index.d.ts +9 -4
- package/dist/index.cjs +190 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +190 -24
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/knowledge/index.d.ts +6 -0
- package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
- package/dist/storage/domains/knowledge/pages-schema.d.ts.map +1 -1
- package/dist/storage/domains/knowledge/pages.d.ts +30 -11
- package/dist/storage/domains/knowledge/pages.d.ts.map +1 -1
- package/package.json +7 -7
- package/dist/docs/SKILL.md +0 -48
- package/dist/docs/assets/SOURCE_MAP.json +0 -6
- package/dist/docs/references/docs-deployment-workers.md +0 -404
- package/dist/docs/references/docs-memory-semantic-recall.md +0 -485
- package/dist/docs/references/docs-memory-working-memory.md +0 -450
- package/dist/docs/references/docs-storage.md +0 -224
- package/dist/docs/references/integrations-databases-clickhouse-managed-postgres.md +0 -82
- package/dist/docs/references/integrations-databases-dynamodb.md +0 -286
- package/dist/docs/references/integrations-databases-neon.md +0 -222
- package/dist/docs/references/integrations-databases-postgresql.md +0 -592
- package/dist/docs/references/integrations-vectors-pg.md +0 -451
- package/dist/docs/references/reference-memory-memory-class.md +0 -237
- package/dist/docs/references/reference-processors-message-history-processor.md +0 -89
- package/dist/docs/references/reference-processors-semantic-recall-processor.md +0 -119
- package/dist/docs/references/reference-processors-working-memory-processor.md +0 -154
- package/dist/docs/references/reference-rag-chunking-and-embedding.md +0 -184
- package/dist/docs/references/reference-rag-metadata-filters.md +0 -238
- package/dist/docs/references/reference-rag-overview.md +0 -76
- package/dist/docs/references/reference-rag-retrieval.md +0 -672
- package/dist/docs/references/reference-rag-vector-databases.md +0 -808
- package/dist/docs/references/reference-storage-composite.md +0 -433
- package/dist/docs/references/reference-storage-retention.md +0 -310
- package/dist/docs/references/reference-tools-vector-query-tool.md +0 -500
|
@@ -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,15 +326,19 @@ 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
|
|
330
|
-
* least one of them (as a case-insensitive
|
|
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[];
|
|
333
337
|
/**
|
|
334
338
|
* Points one matched search word earns on a page, by the best field it appears in: name, then
|
|
335
|
-
* description, then body.
|
|
336
|
-
*
|
|
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.
|
|
337
342
|
*/
|
|
338
343
|
export declare const KNOWLEDGE_SEARCH_WEIGHTS: {
|
|
339
344
|
readonly name: 3;
|
package/dist/index.cjs
CHANGED
|
@@ -8649,7 +8649,8 @@ const KNOWLEDGE_PAGES_CORE_FUNCTIONS = [
|
|
|
8649
8649
|
const KNOWLEDGE_PAGES_CORE_NUMBERS = [
|
|
8650
8650
|
"MAX_KNOWLEDGE_MOVE_PAGES",
|
|
8651
8651
|
"MAX_KNOWLEDGE_PAGE_DEPTH",
|
|
8652
|
-
"MAX_KNOWLEDGE_PAGE_LIST_LIMIT"
|
|
8652
|
+
"MAX_KNOWLEDGE_PAGE_LIST_LIMIT",
|
|
8653
|
+
"MAX_KNOWLEDGE_PAGE_LINKS"
|
|
8653
8654
|
];
|
|
8654
8655
|
function resolveKnowledgePagesCore(storageModule) {
|
|
8655
8656
|
const module = typeof storageModule === "object" && storageModule !== null ? storageModule : {};
|
|
@@ -8670,16 +8671,18 @@ function createKnowledgePagesCoreLoader(features, loadStorageModule) {
|
|
|
8670
8671
|
};
|
|
8671
8672
|
}
|
|
8672
8673
|
/**
|
|
8673
|
-
* Keyword-search words: the query split on whitespace, lowercased, deduplicated
|
|
8674
|
-
* least one of them (as a case-insensitive
|
|
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.
|
|
8675
8677
|
*/
|
|
8676
8678
|
function knowledgeSearchWords(query) {
|
|
8677
|
-
return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
|
|
8679
|
+
return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))].slice(0, 16);
|
|
8678
8680
|
}
|
|
8679
8681
|
/**
|
|
8680
8682
|
* Points one matched search word earns on a page, by the best field it appears in: name, then
|
|
8681
|
-
* description, then body.
|
|
8682
|
-
*
|
|
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.
|
|
8683
8686
|
*/
|
|
8684
8687
|
const KNOWLEDGE_SEARCH_WEIGHTS = {
|
|
8685
8688
|
name: 3,
|
|
@@ -8705,7 +8708,8 @@ const PG_KNOWLEDGE_PAGES_TABLES = {
|
|
|
8705
8708
|
mentions: "mastra_knowledge_mentions",
|
|
8706
8709
|
pins: "mastra_knowledge_pins",
|
|
8707
8710
|
activity: "mastra_knowledge_activity",
|
|
8708
|
-
state: "mastra_knowledge_access_state"
|
|
8711
|
+
state: "mastra_knowledge_access_state",
|
|
8712
|
+
links: "mastra_knowledge_page_links"
|
|
8709
8713
|
};
|
|
8710
8714
|
/**
|
|
8711
8715
|
* Schema-4 DDL. `COLLATE "C"` on every column that is ordered, prefix-matched or compared for
|
|
@@ -8764,7 +8768,9 @@ function pgKnowledgePagesDDL(schemaName) {
|
|
|
8764
8768
|
)`,
|
|
8765
8769
|
`CREATE TABLE IF NOT EXISTS ${q(t.pins)} (
|
|
8766
8770
|
"id" text PRIMARY KEY,
|
|
8767
|
-
"pageId" text NOT NULL
|
|
8771
|
+
"pageId" text NOT NULL REFERENCES ${q(t.pages)} ("id"),
|
|
8772
|
+
"kind" text NOT NULL DEFAULT 'page',
|
|
8773
|
+
"depth" integer,
|
|
8768
8774
|
"audience" text[] NOT NULL,
|
|
8769
8775
|
"createdBy" text NOT NULL,
|
|
8770
8776
|
"createdAt" timestamptz NOT NULL,
|
|
@@ -8796,6 +8802,19 @@ function pgKnowledgePagesDDL(schemaName) {
|
|
|
8796
8802
|
index("mastra_knowledge_mentions_page_idx", t.mentions, "\"pageId\""),
|
|
8797
8803
|
`CREATE INDEX IF NOT EXISTS "mastra_knowledge_pins_audience_gin" ON ${q(t.pins)} USING gin ("audience")`,
|
|
8798
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"),
|
|
8799
8818
|
index("mastra_knowledge_activity_page_idx", t.activity, "\"pageId\",\"createdAt\",\"id\""),
|
|
8800
8819
|
index("mastra_knowledge_activity_created_idx", t.activity, "\"createdAt\",\"id\"")
|
|
8801
8820
|
];
|
|
@@ -8940,6 +8959,8 @@ function toPin(row) {
|
|
|
8940
8959
|
return {
|
|
8941
8960
|
id: String(row.id),
|
|
8942
8961
|
pageId: String(row.pageId),
|
|
8962
|
+
kind: String(row.kind),
|
|
8963
|
+
...row.depth == null ? {} : { depth: Number(row.depth) },
|
|
8943
8964
|
audience: row.audience ?? [],
|
|
8944
8965
|
createdBy: String(row.createdBy),
|
|
8945
8966
|
createdAt: new Date(row.createdAt),
|
|
@@ -8991,6 +9012,9 @@ var PgKnowledgePages = class {
|
|
|
8991
9012
|
get #pins() {
|
|
8992
9013
|
return this.#q(PG_KNOWLEDGE_PAGES_TABLES.pins);
|
|
8993
9014
|
}
|
|
9015
|
+
get #links() {
|
|
9016
|
+
return this.#q(PG_KNOWLEDGE_PAGES_TABLES.links);
|
|
9017
|
+
}
|
|
8994
9018
|
get #activity() {
|
|
8995
9019
|
return this.#q(PG_KNOWLEDGE_PAGES_TABLES.activity);
|
|
8996
9020
|
}
|
|
@@ -9227,6 +9251,7 @@ var PgKnowledgePages = class {
|
|
|
9227
9251
|
const params = new Params();
|
|
9228
9252
|
const where = [`"isScope"`, `"deletedAt" IS NULL`];
|
|
9229
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()})`);
|
|
9230
9255
|
if (input.cursor) {
|
|
9231
9256
|
const [position, id] = decodeCursor(input.cursor, 2);
|
|
9232
9257
|
where.push(`("position", "id") > (${params.add(position)}, ${params.add(id)})`);
|
|
@@ -9502,9 +9527,17 @@ var PgKnowledgePages = class {
|
|
|
9502
9527
|
await tx.query(`UPDATE ${this.#notes} SET "pageId" = $2 WHERE "pageId" = $1`, [source.id, target.id]);
|
|
9503
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]);
|
|
9504
9529
|
await tx.query(`UPDATE ${this.#mentions} SET "pageId" = $2 WHERE "pageId" = $1`, [source.id, target.id]);
|
|
9505
|
-
const
|
|
9506
|
-
|
|
9507
|
-
await this.#upsertPinAudience(tx,
|
|
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
|
+
});
|
|
9508
9541
|
await tx.query(`DELETE FROM ${this.#pins} WHERE "id" = $1`, [String(sourcePin.id)]);
|
|
9509
9542
|
}
|
|
9510
9543
|
const rows = await this.#rows(tx, `SELECT * FROM ${this.#pages} WHERE "id" = $1`, [target.id]);
|
|
@@ -9564,6 +9597,18 @@ var PgKnowledgePages = class {
|
|
|
9564
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);
|
|
9565
9598
|
return this.#noteOutput(rows, limit);
|
|
9566
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
|
+
}
|
|
9567
9612
|
async countChildPages(input) {
|
|
9568
9613
|
const ids = [...new Set(input.parentIds)];
|
|
9569
9614
|
const counts = Object.fromEntries(ids.map((id) => [id, 0]));
|
|
@@ -9576,6 +9621,94 @@ var PgKnowledgePages = class {
|
|
|
9576
9621
|
for (const row of rows) counts[row.parentId] = Number(row.count);
|
|
9577
9622
|
return counts;
|
|
9578
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
|
+
}
|
|
9579
9712
|
async countNotes(input) {
|
|
9580
9713
|
const ids = [...new Set(input.pageIds)];
|
|
9581
9714
|
const counts = Object.fromEntries(ids.map((id) => [id, 0]));
|
|
@@ -9690,7 +9823,7 @@ var PgKnowledgePages = class {
|
|
|
9690
9823
|
async removeNote(input) {
|
|
9691
9824
|
return this.#tx(async (tx) => {
|
|
9692
9825
|
const note = await this.#noteById(tx, input.id, true);
|
|
9693
|
-
if (note.deletedAt)
|
|
9826
|
+
if (note.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("note", input.id);
|
|
9694
9827
|
const now = /* @__PURE__ */ new Date();
|
|
9695
9828
|
await tx.query(`UPDATE ${this.#notes} SET "deletedAt" = $2, "deletedBy" = $3, "version" = "version" + 1, "updatedAt" = $2 WHERE "id" = $1`, [
|
|
9696
9829
|
input.id,
|
|
@@ -9802,10 +9935,18 @@ var PgKnowledgePages = class {
|
|
|
9802
9935
|
};
|
|
9803
9936
|
}
|
|
9804
9937
|
async addPinAudience(input) {
|
|
9938
|
+
const kind = input.kind ?? "page";
|
|
9805
9939
|
return this.#tx(async (tx) => {
|
|
9806
9940
|
const page = await this.#requireLivePage(tx, input.pageId, false);
|
|
9807
9941
|
const now = /* @__PURE__ */ new Date();
|
|
9808
|
-
const { pin, changed } = await this.#upsertPinAudience(tx,
|
|
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
|
+
});
|
|
9809
9950
|
if (changed) await this.#logActivity(tx, {
|
|
9810
9951
|
action: "pin-added",
|
|
9811
9952
|
scopeId: page.scopeId,
|
|
@@ -9816,28 +9957,35 @@ var PgKnowledgePages = class {
|
|
|
9816
9957
|
return pin;
|
|
9817
9958
|
});
|
|
9818
9959
|
}
|
|
9819
|
-
|
|
9820
|
-
|
|
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];
|
|
9821
9964
|
if (!existing) return {
|
|
9822
|
-
pin: toPin((await this.#rows(db, `INSERT INTO ${this.#pins} ("id","pageId","audience","createdBy","createdAt","updatedAt") VALUES ($1,$2,$3::text[],$
|
|
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 *`, [
|
|
9823
9966
|
(0, _mastra_core_storage.createKnowledgeUlid)(),
|
|
9824
9967
|
pageId,
|
|
9968
|
+
kind,
|
|
9969
|
+
input.depth,
|
|
9825
9970
|
[...new Set(audience)],
|
|
9826
|
-
createdBy,
|
|
9971
|
+
input.createdBy,
|
|
9827
9972
|
iso(now)
|
|
9828
9973
|
]))[0]),
|
|
9829
9974
|
changed: true
|
|
9830
9975
|
};
|
|
9831
9976
|
const current = existing.audience ?? [];
|
|
9832
9977
|
const merged = [.../* @__PURE__ */ new Set([...current, ...audience])];
|
|
9833
|
-
|
|
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 {
|
|
9834
9981
|
pin: toPin(existing),
|
|
9835
9982
|
changed: false
|
|
9836
9983
|
};
|
|
9837
9984
|
return {
|
|
9838
|
-
pin: toPin((await this.#rows(db, `UPDATE ${this.#pins} SET "audience" = $2::text[], "
|
|
9985
|
+
pin: toPin((await this.#rows(db, `UPDATE ${this.#pins} SET "audience" = $2::text[], "depth" = $3, "updatedAt" = $4 WHERE "id" = $1 RETURNING *`, [
|
|
9839
9986
|
String(existing.id),
|
|
9840
9987
|
merged,
|
|
9988
|
+
depth,
|
|
9841
9989
|
iso(now)
|
|
9842
9990
|
]))[0]),
|
|
9843
9991
|
changed: true
|
|
@@ -9845,7 +9993,7 @@ var PgKnowledgePages = class {
|
|
|
9845
9993
|
}
|
|
9846
9994
|
async removePinAudience(input) {
|
|
9847
9995
|
return this.#tx(async (tx) => {
|
|
9848
|
-
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];
|
|
9849
9997
|
if (!existing) return null;
|
|
9850
9998
|
const current = existing.audience ?? [];
|
|
9851
9999
|
const remaining = current.filter((entry) => !input.audience.includes(entry));
|
|
@@ -9877,6 +10025,7 @@ var PgKnowledgePages = class {
|
|
|
9877
10025
|
t.mentions,
|
|
9878
10026
|
t.stamps,
|
|
9879
10027
|
t.notes,
|
|
10028
|
+
t.links,
|
|
9880
10029
|
t.pages
|
|
9881
10030
|
].map((name) => this.#q(name)).join(", ");
|
|
9882
10031
|
await this.#client.query(`TRUNCATE ${tables}`);
|
|
@@ -9895,9 +10044,10 @@ var PgKnowledgePages = class {
|
|
|
9895
10044
|
const params = new Params();
|
|
9896
10045
|
const where = [`p."deletedAt" IS NULL`, new ReadFilter(input.read, params, this.#pages).readable("s")];
|
|
9897
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)}`);
|
|
9898
10048
|
if (input.pageIds) where.push(`pn."pageId" = ANY(${params.add(input.pageIds)}::text[])`);
|
|
9899
|
-
if (input.excludePageIds?.length) where.push(`pn."pageId" <> ALL(${params.add(input.excludePageIds)}::text[])`);
|
|
9900
|
-
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')`);
|
|
9901
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"`;
|
|
9902
10052
|
const totalWhere = where.join(" AND ");
|
|
9903
10053
|
if (input.cursor) {
|
|
@@ -9963,7 +10113,7 @@ async function pgKnowledgeSnapshot(db, schemaName) {
|
|
|
9963
10113
|
}
|
|
9964
10114
|
/** Drops exactly the Knowledge tables (never Memory's) and recreates schema 4. */
|
|
9965
10115
|
async function resetPgKnowledgePagesSchema(client, schemaName) {
|
|
9966
|
-
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(", ");
|
|
9967
10117
|
await client.query(`DROP TABLE IF EXISTS ${tables} CASCADE`);
|
|
9968
10118
|
await initPgKnowledgePagesSchema(client, schemaName);
|
|
9969
10119
|
}
|
|
@@ -10038,7 +10188,8 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
|
|
|
10038
10188
|
...super.getCapabilities(),
|
|
10039
10189
|
schemaVersion: 4,
|
|
10040
10190
|
supportsSchemaInspection: true,
|
|
10041
|
-
supportsExplicitReset: true
|
|
10191
|
+
supportsExplicitReset: true,
|
|
10192
|
+
supportsPinKinds: true
|
|
10042
10193
|
};
|
|
10043
10194
|
}
|
|
10044
10195
|
async init() {
|
|
@@ -10068,6 +10219,9 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
|
|
|
10068
10219
|
listChildScopes(input) {
|
|
10069
10220
|
return this.#pages.listChildScopes(input);
|
|
10070
10221
|
}
|
|
10222
|
+
countChildScopes(input) {
|
|
10223
|
+
return this.#pages.countChildScopes(input);
|
|
10224
|
+
}
|
|
10071
10225
|
getPages(input) {
|
|
10072
10226
|
return this.#pages.getPages(input);
|
|
10073
10227
|
}
|
|
@@ -10110,6 +10264,18 @@ var KnowledgePG = class extends _mastra_core_storage.KnowledgeStorage {
|
|
|
10110
10264
|
countChildPages(input) {
|
|
10111
10265
|
return this.#pages.countChildPages(input);
|
|
10112
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
|
+
}
|
|
10113
10279
|
countNotes(input) {
|
|
10114
10280
|
return this.#pages.countNotes(input);
|
|
10115
10281
|
}
|