@mastra/mongodb 1.25.0 → 1.26.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -5,12 +5,13 @@ let _mastra_core_storage = require("@mastra/core/storage");
5
5
  let _mastra_core_vector = require("@mastra/core/vector");
6
6
  let mongodb = require("mongodb");
7
7
  let _mastra_core_vector_filter = require("@mastra/core/vector/filter");
8
+ let _mastra_core_features = require("@mastra/core/features");
8
9
  let crypto$1 = require("crypto");
9
10
  let _mastra_core_agent = require("@mastra/core/agent");
10
11
  let _mastra_core_evals = require("@mastra/core/evals");
11
12
  let _mastra_core_storage_domains_skills = require("@mastra/core/storage/domains/skills");
12
13
  // package.json
13
- var version = "1.25.0";
14
+ var version = "1.26.0-alpha.0";
14
15
  // src/vector/filter.ts
15
16
  /**
16
17
  * Translator for MongoDB filter queries.
@@ -4769,845 +4770,1953 @@ var MongoDBExperimentsStorage = class MongoDBExperimentsStorage extends _mastra_
4769
4770
  } catch {}
4770
4771
  }
4771
4772
  };
4772
- // src/storage/domains/knowledge/index.ts
4773
- let assertDescriptionWithinBound;
4774
- async function assertKnowledgeDescriptionWithinBoundCompat(description) {
4775
- let assertWithinBound = assertDescriptionWithinBound;
4776
- if (!assertWithinBound) {
4777
- const resolvedAssert = (await import("@mastra/core/storage")).assertKnowledgeDescriptionWithinBound ?? ((value) => {
4778
- if (value !== void 0 && value.length > 400) throw new Error("Knowledge node description exceeds the 400 UTF-16 code unit limit");
4773
+ const KNOWLEDGE_TABLE_NAMES = [
4774
+ "mastra_knowledge_nodes",
4775
+ "mastra_knowledge_records",
4776
+ "mastra_knowledge_mentions",
4777
+ "mastra_knowledge_activity",
4778
+ "mastra_knowledge_semantic_outbox",
4779
+ "mastra_knowledge_node_scopes",
4780
+ "mastra_knowledge_record_scopes",
4781
+ "mastra_knowledge_scope_grants",
4782
+ "mastra_knowledge_access_state",
4783
+ "mastra_knowledge_scope_addresses",
4784
+ "mastra_knowledge_node_addresses",
4785
+ "mastra_knowledge_import_state",
4786
+ "mastra_knowledge_import_runs",
4787
+ "mastra_knowledge_proposals"
4788
+ ];
4789
+ /** Knowledge v1 tables with no v2 equivalent; an explicit schema reset drops them. */
4790
+ const RETIRED_KNOWLEDGE_TABLE_NAMES = ["mastra_knowledge_cursors"];
4791
+ const KNOWLEDGE_PAGES_CORE_FEATURE = "knowledge-pages";
4792
+ const KNOWLEDGE_PAGES_TABLE_NAMES = [
4793
+ "mastra_knowledge_pages",
4794
+ "mastra_knowledge_notes",
4795
+ "mastra_knowledge_note_stamps",
4796
+ "mastra_knowledge_mentions",
4797
+ "mastra_knowledge_pins",
4798
+ "mastra_knowledge_activity",
4799
+ "mastra_knowledge_access_state"
4800
+ ];
4801
+ /** Every Knowledge table any schema version created. Reset drops these and nothing else. */
4802
+ const KNOWLEDGE_RESETTABLE_TABLE_NAMES = [.../* @__PURE__ */ new Set([
4803
+ ...KNOWLEDGE_PAGES_TABLE_NAMES,
4804
+ ...KNOWLEDGE_TABLE_NAMES,
4805
+ ...RETIRED_KNOWLEDGE_TABLE_NAMES
4806
+ ])];
4807
+ /** Schema-1/2 tables whose presence means the store needs a reset before schema 4 can run. */
4808
+ const LEGACY_KNOWLEDGE_TABLE_NAMES = new Set([...KNOWLEDGE_TABLE_NAMES, ...RETIRED_KNOWLEDGE_TABLE_NAMES].filter((name) => !KNOWLEDGE_PAGES_TABLE_NAMES.includes(name)));
4809
+ /**
4810
+ * Classifies what an adapter found before creating schema-4 tables. `tableNames` is every
4811
+ * `mastra_knowledge_*` table present; `schemaVersion` is the state row's version, if any.
4812
+ */
4813
+ function inspectKnowledgePagesSchema(snapshot) {
4814
+ const legacy = snapshot.tableNames.filter((name) => LEGACY_KNOWLEDGE_TABLE_NAMES.has(name));
4815
+ if (legacy.length > 0) return {
4816
+ status: "incompatible-reset-required",
4817
+ schemaVersion: snapshot.schemaVersion ?? null,
4818
+ reason: `found Knowledge tables from an earlier schema (${legacy.join(", ")})`
4819
+ };
4820
+ if (snapshot.schemaVersion === 4) return {
4821
+ status: "compatible",
4822
+ schemaVersion: 4
4823
+ };
4824
+ if (snapshot.schemaVersion === void 0) {
4825
+ const present = snapshot.tableNames.filter((name) => KNOWLEDGE_PAGES_TABLE_NAMES.includes(name));
4826
+ if (present.length === 0) return {
4827
+ status: "uninitialized",
4828
+ schemaVersion: null
4829
+ };
4830
+ return {
4831
+ status: "incompatible-reset-required",
4832
+ schemaVersion: null,
4833
+ reason: `found Knowledge tables (${present.join(", ")}) without a schema version`
4834
+ };
4835
+ }
4836
+ return {
4837
+ status: "incompatible-reset-required",
4838
+ schemaVersion: snapshot.schemaVersion,
4839
+ reason: `Knowledge schema version ${snapshot.schemaVersion} is not 4`
4840
+ };
4841
+ }
4842
+ /** Schema-2 table names schema 4 reuses with a different shape. */
4843
+ const REUSED_KNOWLEDGE_TABLE_NAMES = KNOWLEDGE_TABLE_NAMES.filter((name) => KNOWLEDGE_PAGES_TABLE_NAMES.includes(name));
4844
+ /**
4845
+ * The old Knowledge tables an adapter drops on init, rows included: Knowledge before schema 4 was an
4846
+ * experimental store with no migration. Every schema-1/2-only table present is dropped. The table
4847
+ * names schema 4 reuses (mentions, activity, the state table) are dropped too, but only when an old
4848
+ * table is present and no schema-4 state row exists, so they are old-shaped. Schema-4-only tables
4849
+ * are never listed. On a schema-4 store this lists only tables an older process recreated during a
4850
+ * rolling deploy. Drop order is safe for adapters that enforce references.
4851
+ */
4852
+ function legacyKnowledgeTablesToDrop(snapshot) {
4853
+ const present = new Set(snapshot.tableNames);
4854
+ const legacy = [...LEGACY_KNOWLEDGE_TABLE_NAMES].filter((name) => present.has(name));
4855
+ if (legacy.length === 0) return [];
4856
+ const reused = snapshot.schemaVersion === 4 ? [] : REUSED_KNOWLEDGE_TABLE_NAMES.filter((name) => present.has(name));
4857
+ const drop = /* @__PURE__ */ new Set([...legacy, ...reused]);
4858
+ return [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()].filter((name) => drop.has(name));
4859
+ }
4860
+ /** Message for the error an adapter throws on `incompatible-reset-required`. */
4861
+ function knowledgePagesResetMessage(reason) {
4862
+ return `Knowledge storage needs a reset before this version can use it: ${reason}. Knowledge data has no migration; run \`await storage.stores?.knowledge?.dangerouslyReset()\` to drop the Knowledge tables (Memory tables are untouched).`;
4863
+ }
4864
+ const KNOWLEDGE_PAGES_CORE_FUNCTIONS = [
4865
+ "assertKnowledgePageDescriptionWithinBound",
4866
+ "assertKnowledgeNoteTextWithinBound",
4867
+ "canonicalKnowledgeName",
4868
+ "knowledgePositionBetween",
4869
+ "KnowledgeMoveTooLargeError",
4870
+ "KnowledgePageDepthError",
4871
+ "KnowledgePageHasChildrenError",
4872
+ "KnowledgePageKeyConflictError"
4873
+ ];
4874
+ const KNOWLEDGE_PAGES_CORE_NUMBERS = [
4875
+ "MAX_KNOWLEDGE_MOVE_PAGES",
4876
+ "MAX_KNOWLEDGE_PAGE_DEPTH",
4877
+ "MAX_KNOWLEDGE_PAGE_LIST_LIMIT"
4878
+ ];
4879
+ function resolveKnowledgePagesCore(storageModule) {
4880
+ const module = typeof storageModule === "object" && storageModule !== null ? storageModule : {};
4881
+ const missing = [...KNOWLEDGE_PAGES_CORE_FUNCTIONS.filter((name) => typeof module[name] !== "function"), ...KNOWLEDGE_PAGES_CORE_NUMBERS.filter((name) => typeof module[name] !== "number")];
4882
+ if (missing.length > 0) throw new Error(`@mastra/core advertises Knowledge pages without the required storage API (missing ${missing.join(", ")})`);
4883
+ return module;
4884
+ }
4885
+ /** Loads core's page-store runtime once, after checking core advertises `knowledge-pages`. */
4886
+ function createKnowledgePagesCoreLoader(features, loadStorageModule) {
4887
+ let core;
4888
+ return async () => {
4889
+ if (!features.has("knowledge-pages")) throw new Error(`Knowledge requires a @mastra/core release with the "${KNOWLEDGE_PAGES_CORE_FEATURE}" feature. Upgrade @mastra/core to use Knowledge; other storage domains keep working on this version.`);
4890
+ core ??= loadStorageModule().then(resolveKnowledgePagesCore).catch((error) => {
4891
+ core = void 0;
4892
+ throw error;
4779
4893
  });
4780
- assertDescriptionWithinBound = resolvedAssert;
4781
- assertWithinBound = resolvedAssert;
4894
+ return core;
4895
+ };
4896
+ }
4897
+ /**
4898
+ * Keyword-search words: the query split on whitespace, lowercased, deduplicated. Every word must
4899
+ * appear (as a case-insensitive substring) in a hit; an empty list means the search returns nothing.
4900
+ */
4901
+ function knowledgeSearchWords(query) {
4902
+ return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
4903
+ }
4904
+ /**
4905
+ * Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
4906
+ * non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
4907
+ */
4908
+ function knowledgePageKeyProblem(key) {
4909
+ if (!key) return "Knowledge page key must not be empty";
4910
+ if (/[/#%]/.test(key)) return `Knowledge page key "${key}" must not contain "/", "#" or "%"`;
4911
+ }
4912
+ // src/storage/domains/knowledge/pages.ts
4913
+ const loadPagesCore = createKnowledgePagesCoreLoader(_mastra_core_features.coreFeatures, () => import("@mastra/core/storage"));
4914
+ const DEFAULT_LIST_LIMIT = 50;
4915
+ const MONGO_DUPLICATE_KEY = 11e3;
4916
+ const MONGO_KNOWLEDGE_PAGES_COLLECTIONS = {
4917
+ pages: "mastra_knowledge_pages",
4918
+ notes: "mastra_knowledge_notes",
4919
+ pins: "mastra_knowledge_pins",
4920
+ activity: "mastra_knowledge_activity",
4921
+ state: "mastra_knowledge_access_state"
4922
+ };
4923
+ /** Thrown when existing Knowledge collections can't be used as schema 4 without a reset. */
4924
+ var MongoKnowledgeResetRequiredError = class extends Error {
4925
+ inspection;
4926
+ constructor(inspection) {
4927
+ super(knowledgePagesResetMessage(inspection.reason));
4928
+ this.name = "KnowledgeSchemaResetRequiredError";
4929
+ this.inspection = inspection;
4782
4930
  }
4783
- assertWithinBound(description);
4931
+ };
4932
+ function escapeRegex(value) {
4933
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4784
4934
  }
4785
- let assertRecordTextWithinBound;
4786
- async function assertKnowledgeRecordTextWithinBoundCompat(text) {
4787
- if (!assertRecordTextWithinBound) assertRecordTextWithinBound = (await import("@mastra/core/storage")).assertKnowledgeRecordTextWithinBound ?? ((value) => {
4788
- if (value.length > 1e3) throw new Error("Knowledge record text exceeds the 1000 UTF-16 code unit limit; split it into separate facts or summarize it");
4789
- });
4790
- assertRecordTextWithinBound(text);
4935
+ function isDuplicateKey(error) {
4936
+ return typeof error === "object" && error !== null && error.code === MONGO_DUPLICATE_KEY;
4937
+ }
4938
+ function encodeCursor(parts) {
4939
+ return Buffer.from(JSON.stringify(parts)).toString("base64url");
4791
4940
  }
4792
- const cloneScope = (scope) => [...scope];
4793
- const canonicalName = (name) => name.trim().toLocaleLowerCase();
4794
- const nodeReferenceId = (node) => typeof node === "string" ? node : node.id;
4795
- const sessionOptions = (session) => session ? { session } : {};
4796
- function visibleScopeKeys(scope) {
4797
- const canonical = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(scope);
4798
- const subsets = [[]];
4799
- for (const entry of canonical) subsets.push(...subsets.map((subset) => [...subset, entry]));
4800
- const keys = /* @__PURE__ */ new Set();
4801
- for (const subset of subsets.slice(1)) try {
4802
- keys.add((0, _mastra_core_storage.knowledgeScopeKey)(subset));
4941
+ function decodeCursor(cursor) {
4942
+ try {
4943
+ const parts = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
4944
+ if (Array.isArray(parts) && parts.length === 2 && parts.every((part) => typeof part === "string")) return parts;
4803
4945
  } catch {}
4804
- return [...keys];
4946
+ throw new Error("Invalid Knowledge cursor");
4947
+ }
4948
+ function clampLimit(limit, max) {
4949
+ const value = limit ?? DEFAULT_LIST_LIMIT;
4950
+ if (!Number.isInteger(value) || value < 1) throw new Error("Knowledge list limit must be a positive integer");
4951
+ return Math.min(value, max);
4805
4952
  }
4806
- function recordCursorFilter(cursor, expected) {
4807
- const parsed = (0, _mastra_core_storage.parseKnowledgeNodeCursor)(cursor, expected);
4808
- return { $or: [{ updatedAt: { $lt: parsed.updatedAt } }, {
4809
- updatedAt: parsed.updatedAt,
4810
- $or: [{ name: { $gt: parsed.name } }, {
4811
- name: parsed.name,
4812
- id: { $gt: parsed.id }
4813
- }]
4814
- }] };
4953
+ const live = { deletedAt: { $exists: false } };
4954
+ const notScope = { isScope: { $ne: true } };
4955
+ /** Unique-while-live page key per parent; unset on delete so the key can be reused. */
4956
+ function activeKeyOf(parentId, key) {
4957
+ return `${parentId}\u0000${key}`;
4815
4958
  }
4816
- function nodeFromDocument(row) {
4959
+ /** Pipeline stage recomputing `activeKey` for pages that have a live key after their parent changed. */
4960
+ function recomputeActiveKey(parentId) {
4961
+ return { $set: { activeKey: { $cond: [
4962
+ { $and: [{ $eq: [{ $type: "$key" }, "string"] }, { $eq: [{ $type: "$deletedAt" }, "missing"] }] },
4963
+ { $concat: [
4964
+ parentId,
4965
+ "\0",
4966
+ "$key"
4967
+ ] },
4968
+ "$$REMOVE"
4969
+ ] } } };
4970
+ }
4971
+ function toPage(row) {
4817
4972
  return {
4818
4973
  id: String(row.id),
4819
- type: "node",
4974
+ scopeId: String(row.scopeId),
4975
+ parentId: row.parentId == null ? null : String(row.parentId),
4976
+ isScope: row.isScope === true,
4977
+ ...row.address == null ? {} : { address: String(row.address) },
4978
+ ...row.declaration == null ? {} : { declaration: String(row.declaration) },
4979
+ ...row.key == null ? {} : { key: String(row.key) },
4980
+ position: String(row.position),
4820
4981
  name: String(row.name),
4821
- kind: String(row.kind),
4822
- content: row.content == null ? void 0 : String(row.content),
4823
- description: row.description == null ? void 0 : String(row.description),
4824
- scope: cloneScope(row.scope),
4982
+ ...row.description == null ? {} : { description: String(row.description) },
4983
+ ...row.body == null ? {} : { body: String(row.body) },
4825
4984
  version: Number(row.version),
4826
- mergedInto: row.mergedInto ?? void 0,
4985
+ ...row.mergedInto == null ? {} : { mergedInto: String(row.mergedInto) },
4986
+ createdBy: row.createdBy,
4987
+ updatedBy: row.updatedBy,
4827
4988
  createdAt: new Date(row.createdAt),
4828
- updatedAt: new Date(row.updatedAt)
4989
+ updatedAt: new Date(row.updatedAt),
4990
+ ...row.deletedAt == null ? {} : { deletedAt: new Date(row.deletedAt) },
4991
+ ...row.deletedBy == null ? {} : { deletedBy: String(row.deletedBy) }
4829
4992
  };
4830
4993
  }
4831
- function recordFromDocument(row) {
4994
+ function toNote(row) {
4832
4995
  return {
4833
4996
  id: String(row.id),
4834
- node: String(row.node),
4997
+ pageId: String(row.pageId),
4835
4998
  text: String(row.text),
4836
- scope: cloneScope(row.scope),
4837
- sourceThreadId: String(row.sourceThreadId),
4838
- capturedAt: new Date(row.capturedAt),
4839
- when: row.when ? new Date(row.when) : void 0,
4840
- metadata: row.metadata ?? void 0,
4841
- deletedAt: row.deletedAt ? new Date(row.deletedAt) : void 0,
4842
- deletedBy: row.deletedBy ?? void 0
4999
+ ...row.when == null ? {} : { when: new Date(row.when) },
5000
+ stampScopeIds: [...row.stampScopeIds ?? []].sort(),
5001
+ mentions: [...row.mentions ?? []].sort(),
5002
+ provenance: row.provenance,
5003
+ version: Number(row.version),
5004
+ createdAt: new Date(row.createdAt),
5005
+ updatedAt: new Date(row.updatedAt),
5006
+ ...row.deletedAt == null ? {} : { deletedAt: new Date(row.deletedAt) },
5007
+ ...row.deletedBy == null ? {} : { deletedBy: String(row.deletedBy) }
4843
5008
  };
4844
5009
  }
4845
- function outboxFromDocument(row) {
5010
+ function toListedPin(row) {
5011
+ return {
5012
+ ...toPin(row),
5013
+ pageName: String(row.pageName)
5014
+ };
5015
+ }
5016
+ function toPin(row) {
4846
5017
  return {
4847
5018
  id: String(row.id),
4848
- idempotencyKey: String(row.idempotencyKey),
4849
- documentId: String(row.documentId),
4850
- documentType: row.documentType,
4851
- operation: row.operation,
4852
- scope: cloneScope(row.scope),
4853
- status: row.status,
4854
- attempts: Number(row.attempts),
4855
- availableAt: new Date(row.availableAt),
4856
- claimedAt: row.claimedAt ? new Date(row.claimedAt) : void 0,
4857
- claimedBy: row.claimedBy ?? void 0,
5019
+ pageId: String(row.pageId),
5020
+ audience: [...row.audience ?? []],
5021
+ createdBy: String(row.createdBy),
4858
5022
  createdAt: new Date(row.createdAt),
4859
- completedAt: row.completedAt ? new Date(row.completedAt) : void 0
5023
+ updatedAt: new Date(row.updatedAt)
4860
5024
  };
4861
5025
  }
4862
- const KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE = "Knowledge curation cursors were removed: observation-time curate is the only Knowledge writer and needs no cursor.";
4863
- var KnowledgeMongoDB = class KnowledgeMongoDB extends _mastra_core_storage.KnowledgeStorage {
4864
- static MANAGED_COLLECTIONS = [
4865
- _mastra_core_storage.TABLE_KNOWLEDGE_NODES,
4866
- _mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
4867
- _mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS,
4868
- _mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
4869
- _mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
4870
- ];
5026
+ const noProjection = { projection: { _id: 0 } };
5027
+ /**
5028
+ * MongoDB implementation of the schema-4 {@link KnowledgePageStore}.
5029
+ *
5030
+ * Notes embed their stamps and mentions and carry two denormalized copies of page scope
5031
+ * (`pageScopeId`, `mentionScopeIds`); activity events carry `pageScopeId`. Moves and merges rewrite
5032
+ * those copies in the same transaction as the page rows, so a read is one indexed filter against the
5033
+ * set of readable scope ids resolved from the read scope (never a per-address query).
5034
+ */
5035
+ var MongoKnowledgePages = class {
4871
5036
  #connector;
4872
- constructor(config) {
4873
- super();
4874
- this.#connector = resolveMongoDBConfig(config);
5037
+ #bounds;
5038
+ constructor(options) {
5039
+ this.#connector = options.connector;
5040
+ this.#bounds = options.bounds;
5041
+ }
5042
+ #c(name) {
5043
+ return this.#connector.getCollection(MONGO_KNOWLEDGE_PAGES_COLLECTIONS[name]);
5044
+ }
5045
+ #tx(operation) {
5046
+ return this.#connector.withTransaction(operation);
5047
+ }
5048
+ async inspectSchema() {
5049
+ return inspectKnowledgePagesSchema(await this.#snapshot());
5050
+ }
5051
+ async #snapshot() {
5052
+ const tableNames = [];
5053
+ for (const name of KNOWLEDGE_RESETTABLE_TABLE_NAMES) if (await this.#connector.collectionExists(name)) tableNames.push(name);
5054
+ let schemaVersion;
5055
+ if (tableNames.includes(MONGO_KNOWLEDGE_PAGES_COLLECTIONS.state)) {
5056
+ const state = await (await this.#c("state")).findOne({ id: "knowledge" });
5057
+ schemaVersion = typeof state?.schemaVersion === "number" ? state.schemaVersion : void 0;
5058
+ }
5059
+ return {
5060
+ tableNames,
5061
+ schemaVersion
5062
+ };
5063
+ }
5064
+ async #drop(name) {
5065
+ const collection = await this.#connector.getCollection(name);
5066
+ try {
5067
+ await collection.drop();
5068
+ } catch (error) {
5069
+ if (error.codeName !== "NamespaceNotFound") throw error;
5070
+ }
4875
5071
  }
4876
5072
  async init() {
4877
- const nodes = await this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_NODES);
4878
- const knowledge = await this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS);
4879
- const mentions = await this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS);
4880
- const activity = await this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY);
4881
- const outbox = await this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX);
5073
+ const stateName = MONGO_KNOWLEDGE_PAGES_COLLECTIONS.state;
5074
+ for (const name of legacyKnowledgeTablesToDrop(await this.#snapshot())) if (name === stateName) await (await this.#c("state")).deleteMany({ schemaVersion: { $ne: 4 } });
5075
+ else await this.#drop(name);
5076
+ await (await this.#c("state")).updateOne({ id: "knowledge" }, { $setOnInsert: {
5077
+ id: "knowledge",
5078
+ schemaVersion: 4
5079
+ } }, { upsert: true });
5080
+ const inspection = await this.inspectSchema();
5081
+ if (inspection.status === "incompatible-reset-required") throw new MongoKnowledgeResetRequiredError(inspection);
5082
+ const [pages, notes, pins, activity] = await Promise.all([
5083
+ this.#c("pages"),
5084
+ this.#c("notes"),
5085
+ this.#c("pins"),
5086
+ this.#c("activity")
5087
+ ]);
4882
5088
  await Promise.all([
4883
- nodes.createIndex({
4884
- type: 1,
4885
- scopeKey: 1,
4886
- canonicalName: 1
4887
- }, { unique: true }),
4888
- nodes.createIndex({
4889
- scopeKey: 1,
4890
- type: 1
5089
+ pages.createIndex({ id: 1 }, { unique: true }),
5090
+ pages.createIndex({ address: 1 }, {
5091
+ unique: true,
5092
+ partialFilterExpression: { isScope: true }
4891
5093
  }),
4892
- nodes.createIndex({
4893
- type: 1,
4894
- canonicalName: 1
5094
+ pages.createIndex({ activeKey: 1 }, {
5095
+ unique: true,
5096
+ partialFilterExpression: { activeKey: { $type: "string" } }
4895
5097
  }),
4896
- knowledge.createIndex({
4897
- node: 1,
4898
- id: -1
5098
+ pages.createIndex({
5099
+ parentId: 1,
5100
+ position: 1,
5101
+ id: 1
4899
5102
  }),
4900
- knowledge.createIndex({
4901
- sourceThreadId: 1,
4902
- id: -1
5103
+ pages.createIndex({
5104
+ scopeId: 1,
5105
+ name: 1,
5106
+ id: 1
4903
5107
  }),
4904
- knowledge.createIndex({
4905
- scopeKey: 1,
4906
- id: -1
5108
+ pages.createIndex({
5109
+ canonicalName: 1,
5110
+ scopeId: 1
4907
5111
  }),
4908
- mentions.createIndex({
4909
- sourceType: 1,
4910
- sourceId: 1,
4911
- recordId: 1
4912
- }, { unique: true }),
4913
- mentions.createIndex({
4914
- recordId: 1,
4915
- sourceType: 1,
4916
- sourceId: 1
5112
+ pages.createIndex({
5113
+ isScope: 1,
5114
+ declaration: 1,
5115
+ parentId: 1
4917
5116
  }),
4918
- activity.createIndex({ id: -1 }),
5117
+ notes.createIndex({ id: 1 }, { unique: true }),
5118
+ notes.createIndex({
5119
+ pageId: 1,
5120
+ createdAt: 1,
5121
+ id: 1
5122
+ }),
5123
+ notes.createIndex({ mentions: 1 }),
5124
+ notes.createIndex({ pageScopeId: 1 }),
5125
+ notes.createIndex({ stampScopeIds: 1 }),
5126
+ pins.createIndex({ id: 1 }, { unique: true }),
5127
+ pins.createIndex({ pageId: 1 }, { unique: true }),
5128
+ pins.createIndex({ audience: 1 }),
5129
+ activity.createIndex({ id: 1 }, { unique: true }),
4919
5130
  activity.createIndex({
4920
- scopeKey: 1,
5131
+ pageScopeId: 1,
5132
+ createdAt: -1,
4921
5133
  id: -1
4922
5134
  }),
4923
- outbox.createIndex({ idempotencyKey: 1 }, { unique: true }),
4924
- outbox.createIndex({
4925
- status: 1,
4926
- availableAt: 1,
4927
- createdAt: 1
5135
+ activity.createIndex({
5136
+ pageId: 1,
5137
+ createdAt: -1,
5138
+ id: -1
4928
5139
  })
4929
5140
  ]);
4930
5141
  }
4931
- async dangerouslyClearAll() {
4932
- await this.#connector.withTransaction(async (session) => {
4933
- for (const name of KnowledgeMongoDB.MANAGED_COLLECTIONS) await (await this.#collection(name)).deleteMany({}, sessionOptions(session));
5142
+ /** Drops exactly the Knowledge collections (never Memory's) and recreates schema 4. */
5143
+ async reset() {
5144
+ for (const name of KNOWLEDGE_RESETTABLE_TABLE_NAMES) await this.#drop(name);
5145
+ await this.init();
5146
+ }
5147
+ /** The ids of every scope page the read scope can see; `null` means everything. */
5148
+ async #readable(read, session) {
5149
+ if (read === "all") return null;
5150
+ const any = this.#readableClauses(read);
5151
+ if (!any.length) return [];
5152
+ return (await (await this.#c("pages")).find({
5153
+ isScope: true,
5154
+ $or: any
5155
+ }, {
5156
+ projection: {
5157
+ _id: 0,
5158
+ id: 1
5159
+ },
5160
+ session
5161
+ }).toArray()).map((row) => String(row.id));
5162
+ }
5163
+ #readableClauses(read) {
5164
+ const any = [];
5165
+ if (read.addresses.length) any.push({ address: { $in: read.addresses } });
5166
+ for (const entry of read.prefixes) any.push({
5167
+ declaration: entry.declaration,
5168
+ address: { $regex: `^${escapeRegex(entry.prefix)}` }
4934
5169
  });
5170
+ for (const entry of read.children) any.push({
5171
+ declaration: entry.declaration,
5172
+ parentId: entry.parentId
5173
+ });
5174
+ return any;
4935
5175
  }
4936
- async createNode(input) {
4937
- await assertKnowledgeDescriptionWithinBoundCompat(input.description);
4938
- if (input.scopeAddresses?.length) {
4939
- const error = /* @__PURE__ */ new Error("This Knowledge storage adapter does not expose structural scope placement.");
4940
- error.name = "KnowledgeUnsupportedCapabilityError";
4941
- throw error;
5176
+ /**
5177
+ * Whether one scope page is readable, answered by a single indexed lookup instead of resolving the
5178
+ * whole frontier. Use it when a call targets one scope; the frontier can be thousands of scopes.
5179
+ */
5180
+ async #scopeReadable(read, scopeId) {
5181
+ if (read === "all") return true;
5182
+ const any = this.#readableClauses(read);
5183
+ if (!any.length) return false;
5184
+ return await (await this.#c("pages")).countDocuments({
5185
+ id: scopeId,
5186
+ isScope: true,
5187
+ $or: any
5188
+ }, { limit: 1 }) > 0;
5189
+ }
5190
+ #inScopes(field, ids) {
5191
+ return ids === null ? {} : { [field]: { $in: ids } };
5192
+ }
5193
+ #visibleNotes(ids) {
5194
+ if (ids === null) return {};
5195
+ return {
5196
+ pageScopeId: { $in: ids },
5197
+ stampScopeIds: { $in: ids },
5198
+ mentionScopeIds: { $not: { $elemMatch: { $nin: ids } } }
5199
+ };
5200
+ }
5201
+ async #page(id, session) {
5202
+ const row = await (await this.#c("pages")).findOne({ id }, {
5203
+ ...noProjection,
5204
+ session
5205
+ });
5206
+ return row ? toPage(row) : null;
5207
+ }
5208
+ async #requireLivePage(id, session) {
5209
+ const page = await this.#page(id, session);
5210
+ if (!page || page.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("page", id);
5211
+ return page;
5212
+ }
5213
+ async #log(session, event) {
5214
+ await (await this.#c("activity")).insertOne({
5215
+ id: (0, _mastra_core_storage.createKnowledgeUlid)(),
5216
+ action: event.action,
5217
+ scopeId: event.scopeId,
5218
+ pageScopeId: event.pageScopeId ?? event.scopeId,
5219
+ pageId: event.pageId,
5220
+ ...event.noteId ? { noteId: event.noteId } : {},
5221
+ provenance: event.provenance,
5222
+ createdAt: event.at
5223
+ }, { session });
5224
+ }
5225
+ async #lastPosition(parentId, session) {
5226
+ const row = await (await this.#c("pages")).find({
5227
+ parentId,
5228
+ ...live
5229
+ }, {
5230
+ projection: {
5231
+ _id: 0,
5232
+ position: 1
5233
+ },
5234
+ session
5235
+ }).sort({ position: -1 }).limit(1).next();
5236
+ return row ? String(row.position) : void 0;
5237
+ }
5238
+ /**
5239
+ * Ordinary descendants of `rootId`, level by level. Stops counting once the live total passes the
5240
+ * move cap, so an oversize subtree is reported without being walked to the end.
5241
+ */
5242
+ async #subtree(rootId, session, find) {
5243
+ const core = await loadPagesCore();
5244
+ const pages = await this.#c("pages");
5245
+ const ids = [];
5246
+ let liveCount = 0;
5247
+ let maxDepth = 0;
5248
+ let frontier = [rootId];
5249
+ while (frontier.length) {
5250
+ const rows = await pages.find({
5251
+ parentId: { $in: frontier },
5252
+ ...notScope
5253
+ }, {
5254
+ projection: {
5255
+ _id: 0,
5256
+ id: 1,
5257
+ depth: 1,
5258
+ deletedAt: 1
5259
+ },
5260
+ session
5261
+ }).toArray();
5262
+ frontier = [];
5263
+ for (const row of rows) {
5264
+ const id = String(row.id);
5265
+ if (find !== void 0 && id === find) return {
5266
+ ids,
5267
+ live: liveCount,
5268
+ maxDepth,
5269
+ found: true,
5270
+ truncated: false
5271
+ };
5272
+ ids.push(id);
5273
+ frontier.push(id);
5274
+ if (row.deletedAt == null) {
5275
+ liveCount++;
5276
+ maxDepth = Math.max(maxDepth, Number(row.depth));
5277
+ }
5278
+ }
5279
+ if (liveCount > core.MAX_KNOWLEDGE_MOVE_PAGES) return {
5280
+ ids,
5281
+ live: liveCount,
5282
+ maxDepth,
5283
+ found: false,
5284
+ truncated: true
5285
+ };
4942
5286
  }
4943
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
4944
- return this.#connector.withTransaction(async (session) => {
4945
- const existing = await this.#getNodeByName(input.name, scope, session);
4946
- if (existing) {
4947
- const terminal = await this.#resolveTerminalNode(existing.id, session);
4948
- if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(terminal.scope, scope)) throw new Error(`Merged knowledge node is not visible from scope: ${input.name}`);
4949
- return terminal;
5287
+ return {
5288
+ ids,
5289
+ live: liveCount,
5290
+ maxDepth,
5291
+ found: false,
5292
+ truncated: false
5293
+ };
5294
+ }
5295
+ /** Refuses before any write when a live arriving page's key is already live under `parentId`. */
5296
+ async #assertNoKeyConflicts(keys, parentId, session, exceptId) {
5297
+ const core = await loadPagesCore();
5298
+ if (!keys.length) return;
5299
+ const clash = await (await this.#c("pages")).findOne({
5300
+ ...live,
5301
+ parentId,
5302
+ key: { $in: keys },
5303
+ ...exceptId ? { id: { $ne: exceptId } } : {}
5304
+ }, {
5305
+ projection: {
5306
+ _id: 0,
5307
+ key: 1
5308
+ },
5309
+ session
5310
+ });
5311
+ if (clash) throw new core.KnowledgePageKeyConflictError(String(clash.key), parentId);
5312
+ }
5313
+ /** Recomputes the denormalized scope of every page a note mentions, for notes touching `pageIds`. */
5314
+ async #refreshMentionScopes(pageIds, session) {
5315
+ if (!pageIds.length) return;
5316
+ const notes = await this.#c("notes");
5317
+ const pages = await this.#c("pages");
5318
+ const affected = await notes.find({ mentions: { $in: pageIds } }, {
5319
+ projection: {
5320
+ _id: 0,
5321
+ id: 1,
5322
+ mentions: 1
5323
+ },
5324
+ session
5325
+ }).toArray();
5326
+ for (const note of affected) {
5327
+ const mentioned = await pages.find({ id: { $in: note.mentions } }, {
5328
+ projection: {
5329
+ _id: 0,
5330
+ scopeId: 1
5331
+ },
5332
+ session
5333
+ }).toArray();
5334
+ await notes.updateOne({ id: note.id }, { $set: { mentionScopeIds: [...new Set(mentioned.map((page) => String(page.scopeId)))] } }, { session });
5335
+ }
5336
+ }
5337
+ async materializeScope(input) {
5338
+ for (let attempt = 0;; attempt++) try {
5339
+ return await this.#materializeScopeOnce(input);
5340
+ } catch (error) {
5341
+ if (!(error instanceof (await loadPagesCore()).KnowledgePageKeyConflictError || error instanceof _mastra_core_storage.KnowledgeConflictError || isDuplicateKey(error) || error?.hasErrorLabel?.("TransientTransactionError")) || attempt >= 4) throw error;
5342
+ }
5343
+ }
5344
+ async #materializeScopeOnce(input) {
5345
+ const bounds = await this.#bounds();
5346
+ bounds.assertKnowledgePageDescriptionWithinBound(input.description);
5347
+ for (const child of input.children ?? []) bounds.assertKnowledgePageDescriptionWithinBound(child.description);
5348
+ const checkTemplates = (templates) => {
5349
+ for (const template of templates) {
5350
+ bounds.assertKnowledgePageDescriptionWithinBound(template.description);
5351
+ checkTemplates(template.pages ?? []);
4950
5352
  }
5353
+ };
5354
+ checkTemplates(input.pages ?? []);
5355
+ return this.#tx(async (session) => {
4951
5356
  const now = /* @__PURE__ */ new Date();
4952
- const node = {
4953
- id: input.id ?? crypto.randomUUID(),
4954
- type: "node",
4955
- name: input.name.trim(),
4956
- kind: input.kind,
4957
- content: input.content,
4958
- description: input.description,
5357
+ let scope = await this.#scopeByAddress(input.address, session);
5358
+ let created = false;
5359
+ let parentConflict;
5360
+ if (!scope) {
5361
+ if (input.parentId) await this.#requireLivePage(input.parentId, session);
5362
+ scope = await this.#insertScope(session, {
5363
+ address: input.address,
5364
+ declaration: input.declaration,
5365
+ name: input.name,
5366
+ description: input.description,
5367
+ parentId: input.parentId ?? null,
5368
+ provenance: input.provenance,
5369
+ at: now
5370
+ });
5371
+ created = true;
5372
+ } else if (input.parentId && scope.parentId === null) {
5373
+ await this.#requireLivePage(input.parentId, session);
5374
+ const linked = await (await this.#c("pages")).findOneAndUpdate({
5375
+ id: scope.id,
5376
+ parentId: null
5377
+ }, { $set: { parentId: input.parentId } }, {
5378
+ returnDocument: "after",
5379
+ projection: { _id: 0 },
5380
+ session
5381
+ });
5382
+ scope = toPage(linked ?? await this.#scopeByAddress(input.address, session));
5383
+ if (!linked && input.parentId !== scope.parentId) parentConflict = {
5384
+ existingParentId: scope.parentId,
5385
+ requestedParentId: input.parentId
5386
+ };
5387
+ } else if (input.parentId !== void 0 && (input.parentId ?? null) !== scope.parentId) parentConflict = {
5388
+ existingParentId: scope.parentId,
5389
+ requestedParentId: input.parentId ?? null
5390
+ };
5391
+ for (const child of input.children ?? []) {
5392
+ if (await this.#scopeByAddress(child.address, session)) continue;
5393
+ await this.#insertScope(session, {
5394
+ address: child.address,
5395
+ declaration: child.declaration,
5396
+ name: child.name,
5397
+ description: child.description,
5398
+ parentId: scope.id,
5399
+ provenance: input.provenance,
5400
+ at: now
5401
+ });
5402
+ }
5403
+ await this.#materializeTemplates(session, scope, input.pages ?? [], input.provenance, now);
5404
+ return {
4959
5405
  scope,
4960
- version: 1,
4961
- createdAt: now,
4962
- updatedAt: now
5406
+ created,
5407
+ ...parentConflict ? { parentConflict } : {}
4963
5408
  };
4964
- try {
4965
- await (await this.#nodes()).insertOne({
4966
- ...node,
4967
- canonicalName: canonicalName(node.name),
4968
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
4969
- mergedInto: null
4970
- }, sessionOptions(session));
4971
- } catch (error) {
4972
- if (error.code !== 11e3) throw error;
4973
- const concurrent = await this.#getNodeByName(input.name, scope, session);
4974
- if (!concurrent) throw error;
4975
- return await this.#resolveTerminalNode(concurrent.id, session);
4976
- }
4977
- await this.#replaceMentions("node", node.id, node.content ?? "", input.resolutionScope ?? scope, scope, session);
4978
- await this.#activity("node-created", "node", node.id, scope, void 0, session);
4979
- await this.#outbox("node", node.id, "upsert", 1, scope, session);
4980
- return node;
4981
5409
  });
4982
5410
  }
4983
- async getNode(id) {
4984
- return this.#getNode(id);
5411
+ async #materializeTemplates(session, parent, templates, provenance, at) {
5412
+ const pages = await this.#c("pages");
5413
+ for (const template of templates) {
5414
+ const problem = knowledgePageKeyProblem(template.key);
5415
+ if (problem) throw new Error(problem);
5416
+ const existing = await pages.findOne({
5417
+ parentId: parent.id,
5418
+ key: template.key,
5419
+ ...live
5420
+ }, {
5421
+ ...noProjection,
5422
+ session
5423
+ });
5424
+ const page = existing ? toPage(existing) : await this.#insertPage(session, {
5425
+ parent,
5426
+ key: template.key,
5427
+ name: template.name,
5428
+ description: template.description,
5429
+ provenance,
5430
+ at
5431
+ });
5432
+ if (template.pages?.length) await this.#materializeTemplates(session, page, template.pages, provenance, at);
5433
+ }
5434
+ }
5435
+ async #scopeByAddress(address, session) {
5436
+ const row = await (await this.#c("pages")).findOne({
5437
+ isScope: true,
5438
+ address
5439
+ }, {
5440
+ ...noProjection,
5441
+ session
5442
+ });
5443
+ return row ? toPage(row) : null;
5444
+ }
5445
+ async #insertScope(session, input) {
5446
+ const core = await loadPagesCore();
5447
+ const id = (0, _mastra_core_storage.createKnowledgeUlid)();
5448
+ const doc = {
5449
+ id,
5450
+ scopeId: id,
5451
+ parentId: input.parentId,
5452
+ isScope: true,
5453
+ address: input.address,
5454
+ declaration: input.declaration,
5455
+ position: core.knowledgePositionBetween(await this.#lastPosition(input.parentId, session)),
5456
+ name: input.name,
5457
+ canonicalName: core.canonicalKnowledgeName(input.name),
5458
+ ...input.description === void 0 ? {} : { description: input.description },
5459
+ depth: 0,
5460
+ version: 1,
5461
+ createdBy: input.provenance,
5462
+ updatedBy: input.provenance,
5463
+ createdAt: input.at,
5464
+ updatedAt: input.at
5465
+ };
5466
+ try {
5467
+ await (await this.#c("pages")).insertOne({ ...doc }, { session });
5468
+ } catch (error) {
5469
+ if (isDuplicateKey(error)) throw new _mastra_core_storage.KnowledgeConflictError(input.address);
5470
+ throw error;
5471
+ }
5472
+ await this.#log(session, {
5473
+ action: "scope-created",
5474
+ scopeId: id,
5475
+ pageId: id,
5476
+ provenance: input.provenance,
5477
+ at: input.at
5478
+ });
5479
+ return toPage(doc);
5480
+ }
5481
+ async getScopesByAddress(addresses) {
5482
+ if (addresses.length === 0) return [];
5483
+ const rows = await (await this.#c("pages")).find({
5484
+ isScope: true,
5485
+ address: { $in: addresses }
5486
+ }, noProjection).toArray();
5487
+ const byAddress = new Map(rows.map((row) => [String(row.address), toPage(row)]));
5488
+ return [...new Set(addresses)].flatMap((address) => byAddress.get(address) ?? []);
5489
+ }
5490
+ async listScopeDeclarations() {
5491
+ return (await (await this.#c("pages")).aggregate([{ $match: {
5492
+ isScope: true,
5493
+ declaration: { $type: "string" }
5494
+ } }, { $group: {
5495
+ _id: "$declaration",
5496
+ scopes: { $sum: 1 }
5497
+ } }]).toArray()).map((row) => ({
5498
+ declaration: String(row._id),
5499
+ scopes: Number(row.scopes)
5500
+ })).sort((a, b) => a.declaration < b.declaration ? -1 : a.declaration > b.declaration ? 1 : 0);
5501
+ }
5502
+ async renameScopeDeclaration(input) {
5503
+ return this.#tx(async (session) => {
5504
+ const pages = await this.#c("pages");
5505
+ const rows = await pages.find({
5506
+ isScope: true,
5507
+ $or: [{ declaration: input.from }, { declaration: { $regex: `^${escapeRegex(`${input.from}/`)}` } }]
5508
+ }, {
5509
+ projection: {
5510
+ _id: 0,
5511
+ id: 1,
5512
+ address: 1,
5513
+ declaration: 1
5514
+ },
5515
+ session
5516
+ }).toArray();
5517
+ if (rows.length === 0) return 0;
5518
+ const renames = rows.map((row) => ({
5519
+ address: String(row.address),
5520
+ from: String(row.declaration),
5521
+ to: input.to + String(row.declaration).slice(input.from.length)
5522
+ }));
5523
+ input.check(renames);
5524
+ if ((await pages.bulkWrite(rows.map((row, index) => ({ updateOne: {
5525
+ filter: {
5526
+ id: row.id,
5527
+ isScope: true
5528
+ },
5529
+ update: { $set: { declaration: renames[index].to } }
5530
+ } })), { session })).matchedCount < rows.length) throw new _mastra_core_storage.KnowledgeConflictError("Knowledge scope declarations changed while renaming; retry the rename");
5531
+ return rows.length;
5532
+ });
5533
+ }
5534
+ async listChildScopes(input) {
5535
+ const core = await loadPagesCore();
5536
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
5537
+ const filter = {
5538
+ isScope: true,
5539
+ parentId: input.parentId,
5540
+ ...live
5541
+ };
5542
+ if (input.cursor) {
5543
+ const [position, id] = decodeCursor(input.cursor);
5544
+ filter.$or = [{ position: { $gt: position } }, {
5545
+ position,
5546
+ id: { $gt: id }
5547
+ }];
5548
+ }
5549
+ const rows = await (await this.#c("pages")).find(filter, noProjection).sort({
5550
+ position: 1,
5551
+ id: 1
5552
+ }).limit(limit + 1).toArray();
5553
+ return this.#pageOutput(rows, limit, (page) => [page.position, page.id]);
5554
+ }
5555
+ #pageOutput(rows, limit, cursorOf) {
5556
+ const pages = rows.slice(0, limit).map(toPage);
5557
+ const last = pages[pages.length - 1];
5558
+ return {
5559
+ pages,
5560
+ ...rows.length > limit && last ? { nextCursor: encodeCursor(cursorOf(last)) } : {}
5561
+ };
5562
+ }
5563
+ async getPage(input) {
5564
+ const row = await (await this.#c("pages")).findOne({ id: input.id }, noProjection);
5565
+ return row && await this.#scopeReadable(input.read, String(row.scopeId)) ? toPage(row) : null;
5566
+ }
5567
+ async getPageByPath(input) {
5568
+ if (!input.path.length) return null;
5569
+ if (!await this.#scopeReadable(input.read, input.scopeId)) return null;
5570
+ const pages = await this.#c("pages");
5571
+ let page;
5572
+ let parentId = input.scopeId;
5573
+ for (const key of input.path) {
5574
+ const row = await pages.findOne({
5575
+ parentId,
5576
+ key,
5577
+ scopeId: input.scopeId,
5578
+ ...live,
5579
+ ...notScope
5580
+ }, noProjection);
5581
+ if (!row) return null;
5582
+ page = toPage(row);
5583
+ parentId = page.id;
5584
+ }
5585
+ return page ?? null;
5586
+ }
5587
+ async findPagesByName(input) {
5588
+ const core = await loadPagesCore();
5589
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
5590
+ const filter = {
5591
+ canonicalName: core.canonicalKnowledgeName(input.name),
5592
+ ...live
5593
+ };
5594
+ if (!await this.#scopeFilter(filter, input.read, input.scopeId)) return [];
5595
+ return (await (await this.#c("pages")).find(filter, noProjection).sort({
5596
+ createdAt: -1,
5597
+ id: -1
5598
+ }).limit(limit).toArray()).map(toPage);
4985
5599
  }
4986
- async getNodeByName(input) {
4987
- return this.#getNodeByName(input.name, (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
5600
+ /**
5601
+ * Adds the home-scope constraint: the explicit scope when given (one indexed readability check),
5602
+ * otherwise the readable frontier. Returns false when nothing can match.
5603
+ */
5604
+ async #scopeFilter(filter, read, scopeId) {
5605
+ if (scopeId !== void 0) {
5606
+ if (!await this.#scopeReadable(read, scopeId)) return false;
5607
+ filter.scopeId = scopeId;
5608
+ return true;
5609
+ }
5610
+ const ids = await this.#readable(read);
5611
+ if (ids !== null) filter.scopeId = { $in: ids };
5612
+ return true;
4988
5613
  }
4989
- async resolveNode(input) {
4990
- return this.#resolveNode(input.name, (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope));
5614
+ async listChildPages(input) {
5615
+ const core = await loadPagesCore();
5616
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
5617
+ const ids = await this.#readable(input.read);
5618
+ const filter = {
5619
+ parentId: input.parentId,
5620
+ ...notScope,
5621
+ ...live,
5622
+ ...this.#inScopes("scopeId", ids)
5623
+ };
5624
+ if (input.cursor) {
5625
+ const [position, id] = decodeCursor(input.cursor);
5626
+ filter.$or = [{ position: { $gt: position } }, {
5627
+ position,
5628
+ id: { $gt: id }
5629
+ }];
5630
+ }
5631
+ const rows = await (await this.#c("pages")).find(filter, noProjection).sort({
5632
+ position: 1,
5633
+ id: 1
5634
+ }).limit(limit + 1).toArray();
5635
+ return this.#pageOutput(rows, limit, (page) => [page.position, page.id]);
4991
5636
  }
4992
- async listNodes(input) {
5637
+ async listPages(input) {
5638
+ const core = await loadPagesCore();
5639
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
4993
5640
  const filter = {
4994
- type: "node",
4995
- mergedInto: null,
4996
- scopeKey: { $in: visibleScopeKeys((0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope)) },
4997
- ...input.namePrefix ? { canonicalName: { $regex: `^${this.#escapeRegex(canonicalName(input.namePrefix))}` } } : {},
4998
- ...input.kind ? { kind: input.kind } : {},
4999
- ...input.hasContent === void 0 ? {} : input.hasContent ? { content: {
5000
- $exists: true,
5001
- $nin: [null, ""]
5002
- } } : { $or: [
5003
- { content: { $exists: false } },
5004
- { content: null },
5005
- { content: "" }
5006
- ] },
5007
- ...input.cursor ? recordCursorFilter(input.cursor, {
5008
- namePrefix: input.namePrefix,
5009
- kind: input.kind,
5010
- hasContent: input.hasContent
5011
- }) : {}
5641
+ ...notScope,
5642
+ ...live
5012
5643
  };
5013
- return (await (await this.#nodes()).find(filter).sort({
5014
- updatedAt: -1,
5644
+ if (!await this.#scopeFilter(filter, input.read, input.scopeId)) return { pages: [] };
5645
+ if (input.namePrefix) filter.canonicalName = { $regex: `^${escapeRegex(core.canonicalKnowledgeName(input.namePrefix))}` };
5646
+ if (input.cursor) {
5647
+ const [name, id] = decodeCursor(input.cursor);
5648
+ filter.$or = [{ name: { $gt: name } }, {
5649
+ name,
5650
+ id: { $gt: id }
5651
+ }];
5652
+ }
5653
+ const rows = await (await this.#c("pages")).find(filter, noProjection).sort({
5015
5654
  name: 1,
5016
5655
  id: 1
5017
- }).limit(input.limit ?? 100).toArray()).map(nodeFromDocument);
5656
+ }).limit(limit + 1).toArray();
5657
+ return this.#pageOutput(rows, limit, (page) => [page.name, page.id]);
5658
+ }
5659
+ async createPage(input) {
5660
+ (await this.#bounds()).assertKnowledgePageDescriptionWithinBound(input.description);
5661
+ if (input.key !== void 0) {
5662
+ const problem = knowledgePageKeyProblem(input.key);
5663
+ if (problem) throw new Error(problem);
5664
+ }
5665
+ return this.#tx(async (session) => {
5666
+ const parent = await this.#requireLivePage(input.parentId, session);
5667
+ return this.#insertPage(session, {
5668
+ id: input.id,
5669
+ parent,
5670
+ key: input.key,
5671
+ name: input.name,
5672
+ description: input.description,
5673
+ body: input.body,
5674
+ position: input.position,
5675
+ provenance: input.provenance,
5676
+ at: /* @__PURE__ */ new Date()
5677
+ });
5678
+ });
5018
5679
  }
5019
- async updateNode(input) {
5020
- await assertKnowledgeDescriptionWithinBoundCompat(input.description);
5021
- return this.#connector.withTransaction(async (session) => {
5022
- const existing = await this.#getNode(input.id, session);
5023
- if (!existing) throw new _mastra_core_storage.KnowledgeNotFoundError("node", input.id);
5024
- if (existing.mergedInto) throw new Error(`Cannot update merged knowledge node: ${input.id}`);
5025
- const scope = input.scope ? (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope) : existing.scope;
5026
- const name = input.name?.trim() ?? existing.name;
5027
- const content = input.content ?? existing.content;
5028
- const description = input.description ?? existing.description;
5680
+ async #insertPage(session, input) {
5681
+ const core = await loadPagesCore();
5682
+ const id = input.id ?? (0, _mastra_core_storage.createKnowledgeUlid)();
5683
+ const scopeId = input.parent.isScope ? input.parent.id : input.parent.scopeId;
5684
+ const depth = input.parent.isScope ? 1 : await this.#depthOf(input.parent.id, session) + 1;
5685
+ if (depth > core.MAX_KNOWLEDGE_PAGE_DEPTH) throw new core.KnowledgePageDepthError(id);
5686
+ const doc = {
5687
+ id,
5688
+ scopeId,
5689
+ parentId: input.parent.id,
5690
+ isScope: false,
5691
+ ...input.key === void 0 ? {} : {
5692
+ key: input.key,
5693
+ activeKey: activeKeyOf(input.parent.id, input.key)
5694
+ },
5695
+ position: input.position ?? core.knowledgePositionBetween(await this.#lastPosition(input.parent.id, session)),
5696
+ name: input.name,
5697
+ canonicalName: core.canonicalKnowledgeName(input.name),
5698
+ ...input.description === void 0 ? {} : { description: input.description },
5699
+ ...input.body === void 0 ? {} : { body: input.body },
5700
+ depth,
5701
+ version: 1,
5702
+ createdBy: input.provenance,
5703
+ updatedBy: input.provenance,
5704
+ createdAt: input.at,
5705
+ updatedAt: input.at
5706
+ };
5707
+ try {
5708
+ await (await this.#c("pages")).insertOne({ ...doc }, { session });
5709
+ } catch (error) {
5710
+ if (isDuplicateKey(error)) {
5711
+ if (input.key) throw new core.KnowledgePageKeyConflictError(input.key, input.parent.id);
5712
+ throw new _mastra_core_storage.KnowledgeConflictError(id);
5713
+ }
5714
+ throw error;
5715
+ }
5716
+ await this.#log(session, {
5717
+ action: "page-created",
5718
+ scopeId,
5719
+ pageId: id,
5720
+ provenance: input.provenance,
5721
+ at: input.at
5722
+ });
5723
+ return toPage(doc);
5724
+ }
5725
+ async #depthOf(id, session) {
5726
+ const row = await (await this.#c("pages")).findOne({ id }, {
5727
+ projection: {
5728
+ _id: 0,
5729
+ depth: 1
5730
+ },
5731
+ session
5732
+ });
5733
+ return Number(row?.depth ?? 0);
5734
+ }
5735
+ async updatePage(input) {
5736
+ const core = await loadPagesCore();
5737
+ (await this.#bounds()).assertKnowledgePageDescriptionWithinBound(input.description);
5738
+ return this.#tx(async (session) => {
5739
+ const page = await this.#requireLivePage(input.id, session);
5740
+ if (page.version !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5029
5741
  const now = /* @__PURE__ */ new Date();
5030
- const result = await (await this.#nodes()).findOneAndUpdate({
5742
+ const set = {
5743
+ updatedAt: now,
5744
+ updatedBy: input.provenance
5745
+ };
5746
+ const unset = {};
5747
+ if (input.name !== void 0) {
5748
+ set.name = input.name;
5749
+ set.canonicalName = core.canonicalKnowledgeName(input.name);
5750
+ }
5751
+ if (input.description !== void 0) {
5752
+ if (input.description === "") unset.description = "";
5753
+ else set.description = input.description;
5754
+ }
5755
+ if (input.body !== void 0) set.body = input.body;
5756
+ const updated = await (await this.#c("pages")).findOneAndUpdate({
5031
5757
  id: input.id,
5032
- type: "node",
5033
- version: input.version
5758
+ version: input.expectedVersion,
5759
+ ...live
5034
5760
  }, {
5035
- $set: {
5036
- name,
5037
- canonicalName: canonicalName(name),
5038
- kind: input.kind ?? existing.kind,
5039
- content: content ?? null,
5040
- description: description ?? null,
5041
- scope,
5042
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
5043
- updatedAt: now
5044
- },
5045
- $inc: { version: 1 }
5761
+ $set: set,
5762
+ $inc: { version: 1 },
5763
+ ...Object.keys(unset).length ? { $unset: unset } : {}
5046
5764
  }, {
5047
- ...sessionOptions(session),
5048
- returnDocument: "after"
5765
+ returnDocument: "after",
5766
+ projection: { _id: 0 },
5767
+ session
5049
5768
  });
5050
- if (!result) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5051
- if (input.content !== void 0 || input.name !== void 0 || input.scope !== void 0) await this.#replaceMentions("node", input.id, content ?? "", input.resolutionScope ?? scope, scope, session);
5052
- if ((0, _mastra_core_storage.knowledgeScopeKey)(scope) !== (0, _mastra_core_storage.knowledgeScopeKey)(existing.scope)) {
5053
- await this.#outbox("node", input.id, "delete", (0, _mastra_core_storage.createKnowledgeUlid)(), existing.scope, session);
5054
- const records = await (await this.#knowledge()).find({ node: input.id }, sessionOptions(session)).toArray();
5055
- for (const record of records) {
5056
- await this.#outbox("record", record.id, "delete", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope, session);
5057
- if (!record.deletedAt) await this.#outbox("record", record.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope, session);
5058
- }
5769
+ if (!updated) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5770
+ await this.#log(session, {
5771
+ action: "page-updated",
5772
+ scopeId: page.scopeId,
5773
+ pageId: page.id,
5774
+ provenance: input.provenance,
5775
+ at: now
5776
+ });
5777
+ return toPage(updated);
5778
+ });
5779
+ }
5780
+ /** Rewrites everything that follows a page's home scope to `scopeId`, for the pages in `moved`. */
5781
+ async #rehome(moved, scopeId, depthDelta, session) {
5782
+ const pages = await this.#c("pages");
5783
+ const descendants = moved.slice(1);
5784
+ if (descendants.length) await pages.updateMany({ id: { $in: descendants } }, [{ $set: {
5785
+ scopeId,
5786
+ depth: { $add: ["$depth", depthDelta] }
5787
+ } }], { session });
5788
+ await (await this.#c("notes")).updateMany({ pageId: { $in: moved } }, { $set: { pageScopeId: scopeId } }, { session });
5789
+ await (await this.#c("activity")).updateMany({ pageId: { $in: moved } }, { $set: { pageScopeId: scopeId } }, { session });
5790
+ }
5791
+ async movePage(input) {
5792
+ const core = await loadPagesCore();
5793
+ return this.#tx(async (session) => {
5794
+ const page = await this.#requireLivePage(input.id, session);
5795
+ if (page.version !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5796
+ if (page.isScope) throw new Error(`Knowledge scope page ${input.id} can't be moved`);
5797
+ if (input.parentId === input.id) throw new Error(`Knowledge page ${input.id} can't move under itself or its own subtree`);
5798
+ const tree = await this.#subtree(input.id, session, input.parentId);
5799
+ if (tree.found) throw new Error(`Knowledge page ${input.id} can't move under itself or its own subtree`);
5800
+ const parent = await this.#requireLivePage(input.parentId, session);
5801
+ if (tree.live + 1 > core.MAX_KNOWLEDGE_MOVE_PAGES) throw new core.KnowledgeMoveTooLargeError(input.id, tree.live + 1);
5802
+ const newScopeId = parent.isScope ? parent.id : parent.scopeId;
5803
+ const newDepth = parent.isScope ? 1 : await this.#depthOf(parent.id, session) + 1;
5804
+ const delta = newDepth - await this.#depthOf(page.id, session);
5805
+ if (page.key) await this.#assertNoKeyConflicts([page.key], parent.id, session, page.id);
5806
+ if (tree.maxDepth + delta > core.MAX_KNOWLEDGE_PAGE_DEPTH || newDepth > core.MAX_KNOWLEDGE_PAGE_DEPTH) throw new core.KnowledgePageDepthError(input.id);
5807
+ const position = input.position ?? core.knowledgePositionBetween(await this.#lastPosition(parent.id, session));
5808
+ const now = /* @__PURE__ */ new Date();
5809
+ let updated;
5810
+ try {
5811
+ await this.#rehome([page.id, ...tree.ids], newScopeId, delta, session);
5812
+ updated = await (await this.#c("pages")).findOneAndUpdate({
5813
+ id: input.id,
5814
+ version: input.expectedVersion
5815
+ }, {
5816
+ $set: {
5817
+ parentId: parent.id,
5818
+ scopeId: newScopeId,
5819
+ ...page.key ? { activeKey: activeKeyOf(parent.id, page.key) } : {},
5820
+ depth: newDepth,
5821
+ position,
5822
+ updatedAt: now,
5823
+ updatedBy: input.provenance
5824
+ },
5825
+ $inc: { version: 1 }
5826
+ }, {
5827
+ returnDocument: "after",
5828
+ projection: { _id: 0 },
5829
+ session
5830
+ });
5831
+ } catch (error) {
5832
+ if (isDuplicateKey(error)) throw new _mastra_core_storage.KnowledgeConflictError(page.key ? `${parent.id}#${page.key}` : input.id);
5833
+ throw error;
5059
5834
  }
5060
- await this.#activity("node-updated", "node", input.id, scope, void 0, session);
5061
- await this.#outbox("node", input.id, "upsert", Number(result.version), scope, session);
5062
- return nodeFromDocument(result);
5835
+ if (!updated) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5836
+ await this.#refreshMentionScopes([page.id, ...tree.ids], session);
5837
+ await this.#log(session, {
5838
+ action: "page-moved",
5839
+ scopeId: newScopeId,
5840
+ pageId: input.id,
5841
+ provenance: input.provenance,
5842
+ at: now
5843
+ });
5844
+ return toPage(updated);
5063
5845
  });
5064
5846
  }
5065
- async mergeNodes(input) {
5066
- if (input.sourceId === input.targetId) throw new Error("Cannot merge a knowledge node into itself");
5067
- return this.#connector.withTransaction(async (session) => {
5068
- const source = await this.#getNode(input.sourceId, session);
5069
- if (!source) throw new _mastra_core_storage.KnowledgeNotFoundError("node", input.sourceId);
5070
- const target = await this.#resolveTerminalNode(input.targetId, session);
5071
- if (!target) throw new _mastra_core_storage.KnowledgeNotFoundError("node", input.targetId);
5072
- if (target.id === source.id) throw new Error("Cannot create a knowledge merge cycle");
5073
- if (!(0, _mastra_core_storage.isKnowledgeScopeVisible)(target.scope, source.scope)) throw new Error("Cannot merge a knowledge node into a target that is narrower than its source scope");
5074
- const mentions = await this.#mentions();
5075
- const affected = await mentions.find({ recordId: source.id }, sessionOptions(session)).toArray();
5076
- const movedFacts = await (await this.#knowledge()).find({ node: source.id }, sessionOptions(session)).toArray();
5077
- if ((await (await this.#nodes()).updateOne({
5847
+ async mergePages(input) {
5848
+ const core = await loadPagesCore();
5849
+ return this.#tx(async (session) => {
5850
+ if (input.sourceId === input.targetId) throw new Error(`Knowledge page ${input.sourceId} can't merge into itself or its own subtree`);
5851
+ const source = await this.#requireLivePage(input.sourceId, session);
5852
+ const target = await this.#requireLivePage(input.targetId, session);
5853
+ if (source.version !== input.sourceVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.sourceId);
5854
+ if (source.isScope || target.isScope) throw new Error("Knowledge scope pages can't be merged");
5855
+ const tree = await this.#subtree(source.id, session, target.id);
5856
+ if (tree.found) throw new Error(`Knowledge page ${source.id} can't merge into itself or its own subtree`);
5857
+ if (tree.live > core.MAX_KNOWLEDGE_MOVE_PAGES) throw new core.KnowledgeMoveTooLargeError(source.id, tree.live);
5858
+ const delta = await this.#depthOf(target.id, session) - await this.#depthOf(source.id, session);
5859
+ const arriving = await (await this.#c("pages")).find({
5860
+ parentId: source.id,
5861
+ ...live,
5862
+ ...notScope,
5863
+ key: { $type: "string" }
5864
+ }, {
5865
+ projection: {
5866
+ _id: 0,
5867
+ key: 1
5868
+ },
5869
+ session
5870
+ }).toArray();
5871
+ await this.#assertNoKeyConflicts(arriving.map((row) => String(row.key)), target.id, session, source.id);
5872
+ if (tree.maxDepth > 0 && tree.maxDepth + delta > core.MAX_KNOWLEDGE_PAGE_DEPTH) throw new core.KnowledgePageDepthError(source.id);
5873
+ const now = /* @__PURE__ */ new Date();
5874
+ const pages = await this.#c("pages");
5875
+ const notes = await this.#c("notes");
5876
+ if ((await pages.updateOne({
5078
5877
  id: source.id,
5079
- type: "node",
5080
- version: input.sourceVersion,
5081
- mergedInto: null
5878
+ version: input.sourceVersion
5082
5879
  }, {
5083
5880
  $set: {
5881
+ deletedAt: now,
5882
+ deletedBy: input.provenance.author,
5084
5883
  mergedInto: target.id,
5085
- updatedAt: /* @__PURE__ */ new Date()
5884
+ scopeId: target.scopeId,
5885
+ updatedAt: now,
5886
+ updatedBy: input.provenance
5086
5887
  },
5888
+ $unset: { activeKey: "" },
5087
5889
  $inc: { version: 1 }
5088
- }, sessionOptions(session))).modifiedCount === 0) throw new _mastra_core_storage.KnowledgeConflictError(source.id);
5089
- await (await this.#knowledge()).updateMany({ node: source.id }, { $set: { node: target.id } }, sessionOptions(session));
5090
- for (const mention of affected) if (await mentions.findOne({
5091
- sourceType: mention.sourceType,
5092
- sourceId: mention.sourceId,
5093
- recordId: target.id
5094
- }, sessionOptions(session))) await mentions.deleteOne({ _id: mention._id }, sessionOptions(session));
5095
- else await mentions.updateOne({ _id: mention._id }, { $set: { recordId: target.id } }, sessionOptions(session));
5096
- for (const record of movedFacts) if (!record.deletedAt) await this.#outbox("record", record.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope, session);
5097
- for (const mention of affected) {
5098
- const scope = mention.sourceType === "record" ? (await (await this.#knowledge()).findOne({ id: mention.sourceId }, sessionOptions(session)))?.scope : (await (await this.#nodes()).findOne({
5099
- id: mention.sourceId,
5100
- type: "node"
5101
- }, sessionOptions(session)))?.scope;
5102
- if (scope) await this.#outbox(mention.sourceType, mention.sourceId, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), scope, session);
5890
+ }, { session })).matchedCount === 0) throw new _mastra_core_storage.KnowledgeConflictError(input.sourceId);
5891
+ try {
5892
+ if (tree.ids.length) {
5893
+ await pages.updateMany({ id: { $in: tree.ids } }, [{ $set: {
5894
+ scopeId: target.scopeId,
5895
+ depth: { $add: ["$depth", delta] }
5896
+ } }], { session });
5897
+ await notes.updateMany({ pageId: { $in: tree.ids } }, { $set: { pageScopeId: target.scopeId } }, { session });
5898
+ await (await this.#c("activity")).updateMany({ pageId: { $in: tree.ids } }, { $set: { pageScopeId: target.scopeId } }, { session });
5899
+ }
5900
+ await pages.updateMany({
5901
+ parentId: source.id,
5902
+ ...notScope
5903
+ }, [{ $set: { parentId: target.id } }, recomputeActiveKey(target.id)], { session });
5904
+ } catch (error) {
5905
+ if (isDuplicateKey(error)) throw new _mastra_core_storage.KnowledgeConflictError(source.id);
5906
+ throw error;
5103
5907
  }
5104
- let mergedTarget = target;
5105
- if (target.description === void 0 && source.description) {
5106
- const adoptedAt = /* @__PURE__ */ new Date();
5107
- if ((await (await this.#nodes()).updateOne({
5108
- id: target.id,
5109
- type: "node",
5110
- version: target.version,
5111
- description: null
5112
- }, {
5113
- $set: {
5114
- description: source.description,
5115
- updatedAt: adoptedAt
5116
- },
5117
- $inc: { version: 1 }
5118
- }, sessionOptions(session))).modifiedCount > 0) {
5119
- mergedTarget = {
5120
- ...target,
5121
- description: source.description,
5122
- version: target.version + 1,
5123
- updatedAt: adoptedAt
5124
- };
5125
- await this.#activity("node-updated", "node", target.id, target.scope, void 0, session);
5126
- }
5908
+ await notes.updateMany({ pageId: source.id }, { $set: {
5909
+ pageId: target.id,
5910
+ pageScopeId: target.scopeId
5911
+ } }, { session });
5912
+ await notes.updateMany({ mentions: source.id }, [{ $set: { mentions: { $setUnion: [{ $map: {
5913
+ input: "$mentions",
5914
+ in: { $cond: [
5915
+ { $eq: ["$$this", source.id] },
5916
+ target.id,
5917
+ "$$this"
5918
+ ] }
5919
+ } }] } } }], { session });
5920
+ await this.#refreshMentionScopes([target.id, ...tree.ids], session);
5921
+ const pins = await this.#c("pins");
5922
+ const sourcePin = await pins.findOne({ pageId: source.id }, {
5923
+ ...noProjection,
5924
+ session
5925
+ });
5926
+ if (sourcePin) {
5927
+ await this.#upsertPinAudience(session, target.id, sourcePin.audience ?? [], String(sourcePin.createdBy), now);
5928
+ await pins.deleteOne({ id: sourcePin.id }, { session });
5127
5929
  }
5128
- await this.#activity("node-merged", "node", source.id, source.scope, void 0, session);
5129
- await this.#outbox("node", source.id, "delete", input.sourceVersion + 1, source.scope, session);
5130
- await this.#outbox("node", target.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), mergedTarget.scope, session);
5131
- return mergedTarget;
5930
+ await (await this.#c("activity")).updateMany({ pageId: source.id }, { $set: { pageScopeId: target.scopeId } }, { session });
5931
+ await this.#log(session, {
5932
+ action: "page-merged",
5933
+ scopeId: target.scopeId,
5934
+ pageScopeId: target.scopeId,
5935
+ pageId: source.id,
5936
+ provenance: input.provenance,
5937
+ at: now
5938
+ });
5939
+ return await this.#page(target.id, session);
5132
5940
  });
5133
5941
  }
5134
- async appendKnowledge(input) {
5135
- await assertKnowledgeRecordTextWithinBoundCompat(input.text);
5136
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
5137
- const defaultScope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.defaultScope);
5138
- return this.#connector.withTransaction(async (session) => {
5139
- const parent = await this.#resolveTerminalNode(nodeReferenceId(input.node), session);
5140
- if (!parent) throw new _mastra_core_storage.KnowledgeNotFoundError("node", nodeReferenceId(input.node));
5141
- const id = input.id ?? (0, _mastra_core_storage.createKnowledgeUlid)();
5142
- const existing = await (await this.#knowledge()).findOne({ id }, sessionOptions(session));
5143
- if (existing) return recordFromDocument(existing);
5144
- const record = {
5145
- id,
5146
- node: parent.id,
5147
- text: input.text,
5148
- scope,
5149
- sourceThreadId: input.sourceThreadId,
5150
- capturedAt: /* @__PURE__ */ new Date(),
5151
- when: input.when,
5152
- metadata: input.metadata
5153
- };
5154
- await (await this.#knowledge()).insertOne({
5155
- ...record,
5156
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
5157
- when: record.when ?? null,
5158
- deletedAt: null,
5159
- deletedBy: null
5160
- }, sessionOptions(session));
5161
- await this.#replaceMentions("record", id, record.text, input.resolutionScope, defaultScope, session);
5162
- await this.#activity("record-created", "record", id, scope, input.sourceThreadId, session);
5163
- await this.#outbox("record", id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), scope, session);
5164
- return record;
5942
+ async deletePage(input) {
5943
+ const core = await loadPagesCore();
5944
+ return this.#tx(async (session) => {
5945
+ const page = await this.#requireLivePage(input.id, session);
5946
+ if (page.version !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5947
+ if (page.isScope) throw new Error(`Knowledge scope page ${input.id} can't be deleted`);
5948
+ const pages = await this.#c("pages");
5949
+ if (await pages.findOne({
5950
+ parentId: input.id,
5951
+ ...live
5952
+ }, {
5953
+ projection: { id: 1 },
5954
+ session
5955
+ })) throw new core.KnowledgePageHasChildrenError(input.id);
5956
+ const now = /* @__PURE__ */ new Date();
5957
+ const updated = await pages.findOneAndUpdate({
5958
+ id: input.id,
5959
+ version: input.expectedVersion,
5960
+ ...live
5961
+ }, {
5962
+ $set: {
5963
+ deletedAt: now,
5964
+ deletedBy: input.deletedBy,
5965
+ updatedAt: now,
5966
+ updatedBy: { author: input.deletedBy }
5967
+ },
5968
+ $unset: { activeKey: "" },
5969
+ $inc: { version: 1 }
5970
+ }, {
5971
+ returnDocument: "after",
5972
+ projection: { _id: 0 },
5973
+ session
5974
+ });
5975
+ if (!updated) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
5976
+ await (await this.#c("pins")).deleteOne({ pageId: input.id }, { session });
5977
+ await this.#log(session, {
5978
+ action: "page-deleted",
5979
+ scopeId: page.scopeId,
5980
+ pageId: page.id,
5981
+ provenance: { author: input.deletedBy },
5982
+ at: now
5983
+ });
5984
+ return toPage(updated);
5165
5985
  });
5166
5986
  }
5167
- async getKnowledge(input) {
5168
- const row = await (await this.#knowledge()).findOne({
5987
+ async getNote(input) {
5988
+ const ids = await this.#readable(input.read);
5989
+ const filter = {
5169
5990
  id: input.id,
5170
- ...input.includeDeleted ? {} : { deletedAt: null }
5171
- });
5172
- return row ? recordFromDocument(row) : null;
5173
- }
5174
- async listKnowledgeAbout(input) {
5175
- return this.#queryKnowledge(input, "about");
5176
- }
5177
- async listKnowledgeMentioning(input) {
5178
- return this.#queryKnowledge(input, "mentioning");
5179
- }
5180
- async listKnowledgeRelatedTo(input) {
5181
- return this.#queryKnowledge(input, "related");
5182
- }
5183
- async knowledgeBySource(input) {
5184
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
5185
- const limit = input.limit ?? 100;
5186
- const rows = await (await this.#knowledge()).find({
5187
- sourceThreadId: input.sourceThreadId,
5188
- scopeKey: { $in: visibleScopeKeys(scope) },
5189
- ...input.includeDeleted ? {} : { deletedAt: null },
5190
- ...input.after ? { id: { $gt: input.after } } : {}
5191
- }).sort({ id: 1 }).limit(limit + 1).toArray();
5991
+ ...this.#visibleNotes(ids)
5992
+ };
5993
+ if (!input.includeDeleted) Object.assign(filter, live);
5994
+ const row = await (await this.#c("notes")).findOne(filter, noProjection);
5995
+ return row ? toNote(row) : null;
5996
+ }
5997
+ async listNotes(input) {
5998
+ const core = await loadPagesCore();
5999
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
6000
+ const ids = await this.#readable(input.read);
6001
+ const filter = {
6002
+ pageId: input.pageId,
6003
+ ...this.#visibleNotes(ids)
6004
+ };
6005
+ if (!input.includeDeleted) Object.assign(filter, live);
6006
+ if (input.cursor) {
6007
+ const [createdAt, id] = decodeCursor(input.cursor);
6008
+ const at = new Date(createdAt);
6009
+ filter.$or = [{ createdAt: { $gt: at } }, {
6010
+ createdAt: at,
6011
+ id: { $gt: id }
6012
+ }];
6013
+ }
6014
+ const rows = await (await this.#c("notes")).find(filter, noProjection).sort({
6015
+ createdAt: 1,
6016
+ id: 1
6017
+ }).limit(limit + 1).toArray();
6018
+ return this.#noteOutput(rows, limit);
6019
+ }
6020
+ async countNotes(input) {
6021
+ const ids = [...new Set(input.pageIds)];
6022
+ const counts = Object.fromEntries(ids.map((id) => [id, 0]));
6023
+ if (!ids.length) return counts;
6024
+ const readable = await this.#readable(input.read);
6025
+ const rows = await (await this.#c("notes")).aggregate([{ $match: {
6026
+ pageId: { $in: ids },
6027
+ ...live,
6028
+ ...this.#visibleNotes(readable)
6029
+ } }, { $group: {
6030
+ _id: "$pageId",
6031
+ count: { $sum: 1 }
6032
+ } }]).toArray();
6033
+ for (const row of rows) counts[row._id] = row.count;
6034
+ return counts;
6035
+ }
6036
+ async listNotesMentioning(input) {
6037
+ const core = await loadPagesCore();
6038
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
6039
+ const ids = await this.#readable(input.read);
6040
+ const match = {
6041
+ mentions: input.pageId,
6042
+ pageId: { $ne: input.pageId },
6043
+ ...live,
6044
+ ...this.#visibleNotes(ids)
6045
+ };
6046
+ if (input.cursor) {
6047
+ const [createdAt, id] = decodeCursor(input.cursor);
6048
+ const at = new Date(createdAt);
6049
+ match.$or = [{ createdAt: { $lt: at } }, {
6050
+ createdAt: at,
6051
+ id: { $lt: id }
6052
+ }];
6053
+ }
6054
+ const rows = await (await this.#c("notes")).aggregate([
6055
+ { $match: match },
6056
+ { $lookup: {
6057
+ from: MONGO_KNOWLEDGE_PAGES_COLLECTIONS.pages,
6058
+ localField: "pageId",
6059
+ foreignField: "id",
6060
+ as: "page"
6061
+ } },
6062
+ { $match: { "page.deletedAt": { $exists: false } } },
6063
+ { $project: {
6064
+ _id: 0,
6065
+ page: 0
6066
+ } },
6067
+ { $sort: {
6068
+ createdAt: -1,
6069
+ id: -1
6070
+ } },
6071
+ { $limit: limit + 1 }
6072
+ ]).toArray();
6073
+ return this.#noteOutput(rows, limit);
6074
+ }
6075
+ #noteOutput(rows, limit) {
6076
+ const notes = rows.slice(0, limit).map(toNote);
6077
+ const last = notes[notes.length - 1];
5192
6078
  return {
5193
- records: rows.slice(0, limit).map(recordFromDocument),
5194
- nextCursor: rows.length > limit ? rows[limit - 1]?.id : void 0
6079
+ notes,
6080
+ ...rows.length > limit && last ? { nextCursor: encodeCursor([last.createdAt.toISOString(), last.id]) } : {}
5195
6081
  };
5196
6082
  }
5197
- async removeKnowledge(input) {
5198
- return this.#connector.withTransaction(async (session) => {
5199
- const record = await this.#getKnowledge(input.id, true, session);
5200
- if (!record) throw new _mastra_core_storage.KnowledgeNotFoundError("record", input.id);
5201
- if (record.deletedAt) return record;
5202
- const deletedAt = /* @__PURE__ */ new Date();
5203
- await (await this.#knowledge()).updateOne({
5204
- id: input.id,
5205
- deletedAt: null
5206
- }, { $set: {
5207
- deletedAt,
5208
- deletedBy: input.deletedBy
5209
- } }, sessionOptions(session));
5210
- await this.#activity("record-deleted", "record", input.id, record.scope, record.sourceThreadId, session);
5211
- await this.#outbox("record", input.id, "delete", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope, session);
5212
- return {
5213
- ...record,
5214
- deletedAt,
5215
- deletedBy: input.deletedBy
6083
+ async #assertScopePages(ids, session) {
6084
+ const rows = await (await this.#c("pages")).find({
6085
+ id: { $in: ids },
6086
+ isScope: true
6087
+ }, {
6088
+ projection: {
6089
+ _id: 0,
6090
+ id: 1
6091
+ },
6092
+ session
6093
+ }).toArray();
6094
+ const found = new Set(rows.map((row) => String(row.id)));
6095
+ const missing = ids.find((id) => !found.has(id));
6096
+ if (missing) throw new Error(`Knowledge note stamp ${missing} is not a scope page`);
6097
+ }
6098
+ async addNote(input) {
6099
+ (await this.#bounds()).assertKnowledgeNoteTextWithinBound(input.text);
6100
+ if (input.stampScopeIds.length === 0) throw new Error("A Knowledge note needs at least one stamp scope");
6101
+ return this.#tx(async (session) => {
6102
+ const page = await this.#requireLivePage(input.pageId, session);
6103
+ const stampIds = [...new Set(input.stampScopeIds)];
6104
+ await this.#assertScopePages(stampIds, session);
6105
+ const mentionIds = [...new Set(input.mentions)];
6106
+ let mentionScopeIds = [];
6107
+ if (mentionIds.length) {
6108
+ const found = await (await this.#c("pages")).find({ id: { $in: mentionIds } }, {
6109
+ projection: {
6110
+ _id: 0,
6111
+ id: 1,
6112
+ scopeId: 1
6113
+ },
6114
+ session
6115
+ }).toArray();
6116
+ const known = new Set(found.map((row) => String(row.id)));
6117
+ const missing = mentionIds.find((id) => !known.has(id));
6118
+ if (missing) throw new _mastra_core_storage.KnowledgeNotFoundError("page", missing);
6119
+ mentionScopeIds = [...new Set(found.map((row) => String(row.scopeId)))];
6120
+ }
6121
+ const id = input.id ?? (0, _mastra_core_storage.createKnowledgeUlid)();
6122
+ const now = /* @__PURE__ */ new Date();
6123
+ const doc = {
6124
+ id,
6125
+ pageId: page.id,
6126
+ pageScopeId: page.scopeId,
6127
+ text: input.text,
6128
+ ...input.when ? { when: input.when } : {},
6129
+ stampScopeIds: stampIds,
6130
+ mentions: mentionIds,
6131
+ mentionScopeIds,
6132
+ provenance: input.provenance,
6133
+ version: 1,
6134
+ createdAt: now,
6135
+ updatedAt: now
5216
6136
  };
6137
+ try {
6138
+ await (await this.#c("notes")).insertOne({ ...doc }, { session });
6139
+ } catch (error) {
6140
+ if (isDuplicateKey(error)) throw new _mastra_core_storage.KnowledgeConflictError(id);
6141
+ throw error;
6142
+ }
6143
+ await this.#log(session, {
6144
+ action: "note-added",
6145
+ scopeId: page.scopeId,
6146
+ pageId: page.id,
6147
+ noteId: id,
6148
+ provenance: input.provenance,
6149
+ at: now
6150
+ });
6151
+ return toNote(doc);
5217
6152
  });
5218
6153
  }
5219
- async restoreKnowledge(input) {
5220
- return this.#connector.withTransaction(async (session) => {
5221
- const record = await this.#getKnowledge(input.id, true, session);
5222
- if (!record) throw new _mastra_core_storage.KnowledgeNotFoundError("record", input.id);
5223
- if (!record.deletedAt) return record;
5224
- await (await this.#knowledge()).updateOne({ id: input.id }, { $set: {
5225
- deletedAt: null,
5226
- deletedBy: null
5227
- } }, sessionOptions(session));
5228
- await this.#activity("record-restored", "record", input.id, record.scope, record.sourceThreadId, session);
5229
- await this.#outbox("record", input.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope, session);
5230
- return {
5231
- ...record,
5232
- deletedAt: void 0,
5233
- deletedBy: void 0
5234
- };
6154
+ async #requireNote(id, session) {
6155
+ const row = await (await this.#c("notes")).findOne({ id }, {
6156
+ ...noProjection,
6157
+ session
6158
+ });
6159
+ if (!row) throw new _mastra_core_storage.KnowledgeNotFoundError("note", id);
6160
+ return row;
6161
+ }
6162
+ async restampNote(input) {
6163
+ if (input.stampScopeIds.length === 0) throw new Error("A Knowledge note needs at least one stamp scope");
6164
+ return this.#tx(async (session) => {
6165
+ const note = await this.#requireNote(input.id, session);
6166
+ if (note.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("note", input.id);
6167
+ if (Number(note.version) !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
6168
+ const stampIds = [...new Set(input.stampScopeIds)];
6169
+ await this.#assertScopePages(stampIds, session);
6170
+ const now = /* @__PURE__ */ new Date();
6171
+ const updated = await (await this.#c("notes")).findOneAndUpdate({
6172
+ id: input.id,
6173
+ version: input.expectedVersion
6174
+ }, {
6175
+ $set: {
6176
+ stampScopeIds: stampIds,
6177
+ updatedAt: now
6178
+ },
6179
+ $inc: { version: 1 }
6180
+ }, {
6181
+ returnDocument: "after",
6182
+ projection: { _id: 0 },
6183
+ session
6184
+ });
6185
+ if (!updated) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
6186
+ await this.#log(session, {
6187
+ action: "note-restamped",
6188
+ scopeId: String(note.pageScopeId),
6189
+ pageId: String(note.pageId),
6190
+ noteId: input.id,
6191
+ provenance: input.provenance,
6192
+ at: now
6193
+ });
6194
+ return toNote(updated);
5235
6195
  });
5236
6196
  }
5237
- async rescopeKnowledge(input) {
5238
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
5239
- return this.#connector.withTransaction(async (session) => {
5240
- const record = await this.#getKnowledge(input.id, true, session);
5241
- if (!record) throw new _mastra_core_storage.KnowledgeNotFoundError("record", input.id);
5242
- await (await this.#knowledge()).updateOne({ id: input.id }, { $set: {
5243
- scope,
5244
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope)
5245
- } }, sessionOptions(session));
5246
- await this.#activity("record-rescoped", "record", input.id, scope, record.sourceThreadId, session);
5247
- await this.#outbox("record", input.id, "delete", (0, _mastra_core_storage.createKnowledgeUlid)(), record.scope, session);
5248
- if (!record.deletedAt) await this.#outbox("record", input.id, "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), scope, session);
5249
- return {
5250
- ...record,
5251
- scope
5252
- };
6197
+ async removeNote(input) {
6198
+ return this.#tx(async (session) => {
6199
+ const note = await this.#requireNote(input.id, session);
6200
+ if (note.deletedAt) return toNote(note);
6201
+ const now = /* @__PURE__ */ new Date();
6202
+ const updated = await (await this.#c("notes")).findOneAndUpdate({ id: input.id }, {
6203
+ $set: {
6204
+ deletedAt: now,
6205
+ deletedBy: input.deletedBy,
6206
+ updatedAt: now
6207
+ },
6208
+ $inc: { version: 1 }
6209
+ }, {
6210
+ returnDocument: "after",
6211
+ projection: { _id: 0 },
6212
+ session
6213
+ });
6214
+ await this.#log(session, {
6215
+ action: "note-removed",
6216
+ scopeId: String(note.pageScopeId),
6217
+ pageId: String(note.pageId),
6218
+ noteId: input.id,
6219
+ provenance: input.provenance,
6220
+ at: now
6221
+ });
6222
+ return toNote(updated);
5253
6223
  });
5254
6224
  }
5255
- async search(input) {
5256
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
5257
- const query = input.query.trim();
5258
- if (!query) return [];
5259
- const regex = new RegExp(this.#escapeRegex(query), "i");
5260
- const limit = input.limit ?? 20;
5261
- const results = (await (await this.#nodes()).find({
5262
- mergedInto: null,
5263
- scopeKey: { $in: visibleScopeKeys(scope) },
5264
- $or: [
5265
- { name: regex },
5266
- { kind: regex },
5267
- { content: regex },
5268
- { description: regex }
5269
- ]
5270
- }).sort({ updatedAt: -1 }).limit(limit).toArray()).map((row) => ({
5271
- type: "node",
5272
- id: row.id,
5273
- recordId: row.id,
5274
- name: row.name,
5275
- text: [
5276
- row.name,
5277
- ...row.description ? [row.description] : [],
5278
- ...row.content ? [row.content] : []
5279
- ].join("\n"),
5280
- scope: cloneScope(row.scope)
6225
+ async restoreNote(input) {
6226
+ return this.#tx(async (session) => {
6227
+ const note = await this.#requireNote(input.id, session);
6228
+ if (!note.deletedAt) return toNote(note);
6229
+ const now = /* @__PURE__ */ new Date();
6230
+ const updated = await (await this.#c("notes")).findOneAndUpdate({ id: input.id }, {
6231
+ $set: { updatedAt: now },
6232
+ $unset: {
6233
+ deletedAt: "",
6234
+ deletedBy: ""
6235
+ },
6236
+ $inc: { version: 1 }
6237
+ }, {
6238
+ returnDocument: "after",
6239
+ projection: { _id: 0 },
6240
+ session
6241
+ });
6242
+ await this.#log(session, {
6243
+ action: "note-restored",
6244
+ scopeId: String(note.pageScopeId),
6245
+ pageId: String(note.pageId),
6246
+ noteId: input.id,
6247
+ provenance: input.provenance,
6248
+ at: now
6249
+ });
6250
+ return toNote(updated);
6251
+ });
6252
+ }
6253
+ async searchPages(input) {
6254
+ const core = await loadPagesCore();
6255
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
6256
+ const words = knowledgeSearchWords(input.query);
6257
+ if (!words.length) return [];
6258
+ const phrase = input.query.trim().toLowerCase().replace(/\s+/g, " ");
6259
+ const ids = await this.#readable(input.read);
6260
+ const scopes = input.scopeIds?.length ? input.scopeIds : void 0;
6261
+ const inScope = (field) => {
6262
+ if (ids === null) return scopes ? { [field]: { $in: scopes } } : {};
6263
+ const allowed = scopes ? scopes.filter((id) => ids.includes(id)) : ids;
6264
+ return { [field]: { $in: allowed } };
6265
+ };
6266
+ const matcher = (word) => ({
6267
+ $regex: escapeRegex(word),
6268
+ $options: "i"
6269
+ });
6270
+ const pages = await this.#c("pages");
6271
+ const pageFilter = {
6272
+ ...live,
6273
+ ...notScope,
6274
+ ...inScope("scopeId"),
6275
+ $and: words.map((word) => ({ $or: [
6276
+ { name: matcher(word) },
6277
+ { description: matcher(word) },
6278
+ { body: matcher(word) }
6279
+ ] }))
6280
+ };
6281
+ const hits = (await pages.aggregate([
6282
+ { $match: pageFilter },
6283
+ { $addFields: { searchTier: { $cond: [
6284
+ { $regexMatch: {
6285
+ input: "$canonicalName",
6286
+ regex: escapeRegex(phrase)
6287
+ } },
6288
+ 0,
6289
+ { $cond: [
6290
+ { $and: words.map((word) => ({ $regexMatch: {
6291
+ input: "$name",
6292
+ regex: escapeRegex(word),
6293
+ options: "i"
6294
+ } })) },
6295
+ 1,
6296
+ 2
6297
+ ] }
6298
+ ] } } },
6299
+ { $sort: {
6300
+ searchTier: 1,
6301
+ updatedAt: -1,
6302
+ id: 1
6303
+ } },
6304
+ { $limit: limit },
6305
+ { $project: {
6306
+ searchTier: 0,
6307
+ _id: 0
6308
+ } }
6309
+ ]).toArray()).map((row) => ({
6310
+ type: "page",
6311
+ page: toPage(row)
5281
6312
  }));
5282
- if (results.length < limit) {
5283
- const records = await (await this.#knowledge()).find({
5284
- deletedAt: null,
5285
- scopeKey: { $in: visibleScopeKeys(scope) },
5286
- text: regex
5287
- }).sort({ id: -1 }).limit(limit - results.length).toArray();
5288
- for (const record of records) {
5289
- const parent = await this.#resolveTerminalNode(record.node);
5290
- const parentVisible = parent && (0, _mastra_core_storage.isKnowledgeScopeVisible)(parent.scope, scope);
5291
- results.push({
5292
- type: "record",
5293
- id: record.id,
5294
- recordId: parentVisible ? record.node : record.id,
5295
- name: parentVisible ? parent.name : "(private node)",
5296
- text: record.text,
5297
- scope: cloneScope(record.scope)
5298
- });
5299
- }
6313
+ if (hits.length >= limit) return hits;
6314
+ const noteRows = await (await this.#c("notes")).find({
6315
+ ...live,
6316
+ ...this.#visibleNotes(ids),
6317
+ ...scopes ? { pageScopeId: { $in: ids === null ? scopes : scopes.filter((id) => ids.includes(id)) } } : {},
6318
+ $and: words.map((word) => ({ text: matcher(word) }))
6319
+ }, noProjection).sort({
6320
+ updatedAt: -1,
6321
+ id: -1
6322
+ }).limit(limit - hits.length).toArray();
6323
+ const notePages = new Map((await pages.find({
6324
+ id: { $in: [...new Set(noteRows.map((row) => String(row.pageId)))] },
6325
+ ...live
6326
+ }, noProjection).toArray()).map((row) => [String(row.id), toPage(row)]));
6327
+ for (const row of noteRows) {
6328
+ const page = notePages.get(String(row.pageId));
6329
+ if (page) hits.push({
6330
+ type: "note",
6331
+ note: toNote(row),
6332
+ page
6333
+ });
5300
6334
  }
5301
- return results.slice(0, limit);
5302
- }
5303
- /**
5304
- * @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
5305
- * cursor. Always throws.
5306
- */
5307
- async getCurationCursor(_input) {
5308
- throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
6335
+ return hits;
5309
6336
  }
5310
- /**
5311
- * @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
5312
- * cursor. Always throws.
5313
- */
5314
- async advanceCurationCursor(_input) {
5315
- throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
5316
- }
5317
- async listActivity(input) {
5318
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
5319
- return (await (await this.#activityCollection()).find({
5320
- scopeKey: { $in: visibleScopeKeys(scope) },
5321
- ...input.after ? { id: { $lt: input.after } } : {}
5322
- }).sort({ id: -1 }).limit(input.limit ?? 100).toArray()).map((row) => ({
5323
- id: row.id,
5324
- action: row.action,
5325
- recordType: row.recordType,
5326
- recordId: row.recordId,
5327
- scope: cloneScope(row.scope),
5328
- sourceThreadId: row.sourceThreadId ?? void 0,
6337
+ async listPageActivity(input) {
6338
+ const core = await loadPagesCore();
6339
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
6340
+ const ids = await this.#readable(input.read);
6341
+ const activity = await this.#c("activity");
6342
+ const notes = await this.#c("notes");
6343
+ const filter = {};
6344
+ if (ids !== null) filter.pageScopeId = { $in: ids };
6345
+ delete filter.scopeId;
6346
+ if (input.scopeId) {
6347
+ if (ids !== null && !ids.includes(input.scopeId)) filter.pageScopeId = { $in: [] };
6348
+ else filter.pageScopeId = input.scopeId;
6349
+ }
6350
+ if (input.pageId) filter.pageId = input.pageId;
6351
+ if (input.cursor) {
6352
+ const [createdAt, id] = decodeCursor(input.cursor);
6353
+ const at = new Date(createdAt);
6354
+ filter.$or = [{ createdAt: { $lt: at } }, {
6355
+ createdAt: at,
6356
+ id: { $lt: id }
6357
+ }];
6358
+ }
6359
+ const events = [];
6360
+ let cursorFilter = filter;
6361
+ for (;;) {
6362
+ const batch = await activity.find(cursorFilter, noProjection).sort({
6363
+ createdAt: -1,
6364
+ id: -1
6365
+ }).limit(limit * 4 + 1).toArray();
6366
+ const noteIds = [...new Set(batch.flatMap((row) => row.noteId ? [String(row.noteId)] : []))];
6367
+ const visible = new Set(noteIds.length ? (await notes.find({
6368
+ id: { $in: noteIds },
6369
+ ...this.#visibleNotes(ids)
6370
+ }, { projection: {
6371
+ _id: 0,
6372
+ id: 1
6373
+ } }).toArray()).map((row) => String(row.id)) : []);
6374
+ for (const row of batch) {
6375
+ if (row.noteId && !visible.has(String(row.noteId))) continue;
6376
+ events.push(row);
6377
+ if (events.length > limit) break;
6378
+ }
6379
+ if (events.length > limit || batch.length <= limit * 4) break;
6380
+ const last = batch[batch.length - 1];
6381
+ cursorFilter = {
6382
+ ...filter,
6383
+ $or: [{ createdAt: { $lt: last.createdAt } }, {
6384
+ createdAt: last.createdAt,
6385
+ id: { $lt: last.id }
6386
+ }]
6387
+ };
6388
+ }
6389
+ const page = events.slice(0, limit).map((row) => ({
6390
+ id: String(row.id),
6391
+ action: String(row.action),
6392
+ scopeId: String(row.scopeId),
6393
+ pageId: String(row.pageId),
6394
+ ...row.noteId == null ? {} : { noteId: String(row.noteId) },
6395
+ provenance: row.provenance,
5329
6396
  createdAt: new Date(row.createdAt)
5330
6397
  }));
5331
- }
5332
- async listSemanticOutbox(input = {}) {
5333
- const filter = {
5334
- ...input.status ? { status: input.status } : {},
5335
- ...input.scope ? { scopeKey: { $in: visibleScopeKeys(input.scope) } } : {}
6398
+ const last = page[page.length - 1];
6399
+ return {
6400
+ events: page,
6401
+ ...events.length > limit && last ? { nextCursor: encodeCursor([last.createdAt.toISOString(), last.id]) } : {}
5336
6402
  };
5337
- return (await (await this.#outboxCollection()).find(filter).sort({
5338
- createdAt: 1,
5339
- id: 1
5340
- }).limit(input.limit ?? 100).toArray()).map(outboxFromDocument);
5341
6403
  }
5342
- async claimSemanticOutbox(input) {
5343
- return this.#connector.withTransaction(async (session) => {
5344
- const collection = await this.#outboxCollection();
5345
- const now = input.now ?? /* @__PURE__ */ new Date();
5346
- const staleBefore = new Date(now.getTime() - (input.claimTimeoutMs ?? 6e4));
5347
- const filter = {
5348
- $or: [{
5349
- status: "pending",
5350
- availableAt: { $lte: now }
5351
- }, {
5352
- status: "processing",
5353
- claimedAt: { $lte: staleBefore }
5354
- }],
5355
- ...input.scope ? { scopeKey: { $in: visibleScopeKeys(input.scope) } } : {}
6404
+ /** Deletes every document but keeps the collections, indexes and schema-version marker. */
6405
+ async clearAll() {
6406
+ await this.#tx(async (session) => {
6407
+ for (const name of [
6408
+ "activity",
6409
+ "pins",
6410
+ "notes",
6411
+ "pages"
6412
+ ]) await (await this.#c(name)).deleteMany({}, { session });
6413
+ });
6414
+ }
6415
+ async addPinAudience(input) {
6416
+ return this.#tx(async (session) => {
6417
+ const page = await this.#requireLivePage(input.pageId, session);
6418
+ const now = /* @__PURE__ */ new Date();
6419
+ const { pin, changed } = await this.#upsertPinAudience(session, page.id, input.audience, input.createdBy, now);
6420
+ if (changed) await this.#log(session, {
6421
+ action: "pin-added",
6422
+ scopeId: page.scopeId,
6423
+ pageId: page.id,
6424
+ provenance: { author: input.createdBy },
6425
+ at: now
6426
+ });
6427
+ return pin;
6428
+ });
6429
+ }
6430
+ async #upsertPinAudience(session, pageId, audience, createdBy, now) {
6431
+ const pins = await this.#c("pins");
6432
+ const existing = await pins.findOne({ pageId }, {
6433
+ ...noProjection,
6434
+ session
6435
+ });
6436
+ if (!existing) {
6437
+ const doc = {
6438
+ id: (0, _mastra_core_storage.createKnowledgeUlid)(),
6439
+ pageId,
6440
+ audience: [...new Set(audience)],
6441
+ createdBy,
6442
+ createdAt: now,
6443
+ updatedAt: now
5356
6444
  };
5357
- const limit = input.limit ?? 100;
5358
- const candidates = await collection.find(filter, sessionOptions(session)).sort({
5359
- createdAt: 1,
6445
+ try {
6446
+ await pins.insertOne({ ...doc }, { session });
6447
+ } catch (error) {
6448
+ if (isDuplicateKey(error)) throw new _mastra_core_storage.KnowledgeConflictError(pageId);
6449
+ throw error;
6450
+ }
6451
+ return {
6452
+ pin: toPin(doc),
6453
+ changed: true
6454
+ };
6455
+ }
6456
+ const current = existing.audience ?? [];
6457
+ const merged = [.../* @__PURE__ */ new Set([...current, ...audience])];
6458
+ if (merged.length === current.length) return {
6459
+ pin: toPin(existing),
6460
+ changed: false
6461
+ };
6462
+ return {
6463
+ pin: toPin(await pins.findOneAndUpdate({ id: existing.id }, { $set: {
6464
+ audience: merged,
6465
+ updatedAt: now
6466
+ } }, {
6467
+ returnDocument: "after",
6468
+ projection: { _id: 0 },
6469
+ session
6470
+ })),
6471
+ changed: true
6472
+ };
6473
+ }
6474
+ async removePinAudience(input) {
6475
+ return this.#tx(async (session) => {
6476
+ const pins = await this.#c("pins");
6477
+ const existing = await pins.findOne({ pageId: input.pageId }, {
6478
+ ...noProjection,
6479
+ session
6480
+ });
6481
+ if (!existing) return null;
6482
+ const current = existing.audience ?? [];
6483
+ const remaining = current.filter((entry) => !input.audience.includes(entry));
6484
+ if (remaining.length === current.length) return toPin(existing);
6485
+ const now = /* @__PURE__ */ new Date();
6486
+ let pin = null;
6487
+ if (remaining.length === 0) await pins.deleteOne({ id: existing.id }, { session });
6488
+ else pin = toPin(await pins.findOneAndUpdate({ id: existing.id }, { $set: {
6489
+ audience: remaining,
6490
+ updatedAt: now
6491
+ } }, {
6492
+ returnDocument: "after",
6493
+ projection: { _id: 0 },
6494
+ session
6495
+ }));
6496
+ const page = await this.#page(input.pageId, session);
6497
+ await this.#log(session, {
6498
+ action: "pin-removed",
6499
+ scopeId: page?.scopeId ?? input.pageId,
6500
+ pageId: input.pageId,
6501
+ provenance: { author: input.removedBy },
6502
+ at: now
6503
+ });
6504
+ return pin;
6505
+ });
6506
+ }
6507
+ async getPages(input) {
6508
+ if (!input.ids.length) return [];
6509
+ const rows = await (await this.#c("pages")).find({
6510
+ id: { $in: [...new Set(input.ids)] },
6511
+ ...live
6512
+ }, noProjection).toArray();
6513
+ if (input.read === "all") return rows.map(toPage);
6514
+ const read = input.read;
6515
+ const scopeIds = [...new Set(rows.map((row) => String(row.scopeId)))];
6516
+ const readable = /* @__PURE__ */ new Set();
6517
+ const any = this.#readableClauses(read);
6518
+ if (scopeIds.length && any.length) {
6519
+ const scopes = await (await this.#c("pages")).find({
6520
+ id: { $in: scopeIds },
6521
+ isScope: true,
6522
+ $or: any
6523
+ }, { projection: {
6524
+ _id: 0,
5360
6525
  id: 1
5361
- }).limit(Math.max(limit * 10, 100)).toArray();
5362
- const claimed = [];
5363
- for (const candidate of candidates) {
5364
- if (claimed.length >= limit) break;
5365
- if (await collection.findOne({
5366
- documentId: candidate.documentId,
5367
- status: { $ne: "completed" },
5368
- $or: [{ createdAt: { $lt: candidate.createdAt } }, {
5369
- createdAt: candidate.createdAt,
5370
- id: { $lt: candidate.id }
5371
- }]
5372
- }, sessionOptions(session))) continue;
5373
- const result = await collection.findOneAndUpdate({
5374
- id: candidate.id,
5375
- $or: [{
5376
- status: "pending",
5377
- availableAt: { $lte: now }
5378
- }, {
5379
- status: "processing",
5380
- claimedAt: { $lte: staleBefore }
5381
- }]
5382
- }, {
5383
- $set: {
5384
- status: "processing",
5385
- claimedAt: now,
5386
- claimedBy: input.workerId
5387
- },
5388
- $inc: { attempts: 1 }
5389
- }, {
5390
- ...sessionOptions(session),
5391
- returnDocument: "after"
5392
- });
5393
- if (result) claimed.push(result);
6526
+ } }).toArray();
6527
+ for (const scope of scopes) readable.add(String(scope.id));
6528
+ }
6529
+ return rows.filter((row) => readable.has(String(row.scopeId))).map(toPage);
6530
+ }
6531
+ async listPins(input) {
6532
+ const core = await loadPagesCore();
6533
+ const limit = clampLimit(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
6534
+ const ids = await this.#readable(input.read);
6535
+ const match = {};
6536
+ if (input.audience !== "all") match.audience = { $in: input.audience };
6537
+ const pageId = {};
6538
+ if (input.pageIds) pageId.$in = input.pageIds;
6539
+ if (input.excludePageIds?.length) pageId.$nin = input.excludePageIds;
6540
+ if (Object.keys(pageId).length) match.pageId = pageId;
6541
+ const pins = await this.#c("pins");
6542
+ const base = [
6543
+ { $match: match },
6544
+ { $lookup: {
6545
+ from: MONGO_KNOWLEDGE_PAGES_COLLECTIONS.pages,
6546
+ localField: "pageId",
6547
+ foreignField: "id",
6548
+ as: "page"
6549
+ } },
6550
+ { $match: {
6551
+ "page.deletedAt": { $exists: false },
6552
+ ...ids === null ? {} : { "page.scopeId": { $in: ids } },
6553
+ ...input.withBody ? { "page.body": { $regex: "\\S" } } : {}
6554
+ } }
6555
+ ];
6556
+ const pageMatch = [];
6557
+ if (input.cursor) {
6558
+ const [createdAt, id] = decodeCursor(input.cursor);
6559
+ const at = new Date(createdAt);
6560
+ pageMatch.push({ $match: { $or: [{ createdAt: { $lt: at } }, {
6561
+ createdAt: at,
6562
+ id: { $lt: id }
6563
+ }] } });
6564
+ }
6565
+ const rows = await pins.aggregate([
6566
+ ...base,
6567
+ ...pageMatch,
6568
+ { $addFields: { pageName: { $arrayElemAt: ["$page.name", 0] } } },
6569
+ { $project: {
6570
+ _id: 0,
6571
+ page: 0
6572
+ } },
6573
+ { $sort: {
6574
+ createdAt: -1,
6575
+ id: -1
6576
+ } },
6577
+ { $limit: limit + 1 }
6578
+ ]).toArray();
6579
+ let total;
6580
+ if (input.count) {
6581
+ const counted = await pins.aggregate([...base, { $count: "total" }]).toArray();
6582
+ total = Number(counted[0]?.total ?? 0);
6583
+ }
6584
+ const listed = rows.slice(0, limit).map(toListedPin);
6585
+ const last = listed[listed.length - 1];
6586
+ return {
6587
+ pins: listed,
6588
+ ...rows.length > limit && last ? { nextCursor: encodeCursor([last.createdAt.toISOString(), last.id]) } : {},
6589
+ ...total === void 0 ? {} : { total }
6590
+ };
6591
+ }
6592
+ };
6593
+ // src/storage/domains/knowledge/index.ts
6594
+ const loadKnowledgePagesCore = createKnowledgePagesCoreLoader(_mastra_core_features.coreFeatures, () => import("@mastra/core/storage"));
6595
+ /** @experimental Knowledge APIs are experimental and may change without notice. */
6596
+ var KnowledgeMongoDB = class extends _mastra_core_storage.KnowledgeStorage {
6597
+ static MANAGED_COLLECTIONS = Object.values(MONGO_KNOWLEDGE_PAGES_COLLECTIONS);
6598
+ #connector;
6599
+ #pages;
6600
+ constructor(config) {
6601
+ super();
6602
+ this.#connector = resolveMongoDBConfig(config);
6603
+ this.#pages = new MongoKnowledgePages({
6604
+ connector: this.#connector,
6605
+ bounds: async () => {
6606
+ const core = await loadKnowledgePagesCore();
6607
+ return {
6608
+ assertKnowledgePageDescriptionWithinBound: core.assertKnowledgePageDescriptionWithinBound,
6609
+ assertKnowledgeNoteTextWithinBound: core.assertKnowledgeNoteTextWithinBound
6610
+ };
5394
6611
  }
5395
- return claimed.map(outboxFromDocument);
5396
6612
  });
5397
6613
  }
5398
- async completeSemanticOutbox(input) {
5399
- if (!input.ids.length) return;
5400
- await (await this.#outboxCollection()).updateMany({
5401
- id: { $in: input.ids },
5402
- status: "processing",
5403
- claimedBy: input.workerId
5404
- }, { $set: {
5405
- status: "completed",
5406
- completedAt: /* @__PURE__ */ new Date(),
5407
- claimedAt: null,
5408
- claimedBy: null
5409
- } });
5410
- }
5411
- async releaseSemanticOutbox(input) {
5412
- if (!input.ids.length) return;
5413
- await (await this.#outboxCollection()).updateMany({
5414
- id: { $in: input.ids },
5415
- status: "processing",
5416
- claimedBy: input.workerId
5417
- }, { $set: {
5418
- status: "pending",
5419
- availableAt: input.retryAt ?? /* @__PURE__ */ new Date(),
5420
- claimedAt: null,
5421
- claimedBy: null
5422
- } });
6614
+ getCapabilities() {
6615
+ return {
6616
+ ...super.getCapabilities(),
6617
+ schemaVersion: 4,
6618
+ supportsSchemaInspection: true,
6619
+ supportsExplicitReset: true
6620
+ };
5423
6621
  }
5424
- async #collection(name) {
5425
- return this.#connector.getCollection(name);
6622
+ async init() {
6623
+ await this.#pages.init();
5426
6624
  }
5427
- #nodes() {
5428
- return this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_NODES);
6625
+ async inspectSchema() {
6626
+ return this.#pages.inspectSchema();
5429
6627
  }
5430
- #knowledge() {
5431
- return this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS);
6628
+ async dangerouslyReset() {
6629
+ await this.#pages.reset();
5432
6630
  }
5433
- #mentions() {
5434
- return this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS);
6631
+ async dangerouslyClearAll() {
6632
+ await this.#pages.clearAll();
5435
6633
  }
5436
- #activityCollection() {
5437
- return this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY);
6634
+ materializeScope(input) {
6635
+ return this.#pages.materializeScope(input);
5438
6636
  }
5439
- #outboxCollection() {
5440
- return this.#collection(_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX);
6637
+ getScopesByAddress(input) {
6638
+ return this.#pages.getScopesByAddress(input);
5441
6639
  }
5442
- async #getNode(id, session) {
5443
- const row = await (await this.#nodes()).findOne({
5444
- id,
5445
- type: "node"
5446
- }, sessionOptions(session));
5447
- return row ? nodeFromDocument(row) : null;
5448
- }
5449
- async #getNodeByName(name, scope, session) {
5450
- const row = await (await this.#nodes()).findOne({
5451
- type: "node",
5452
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
5453
- canonicalName: canonicalName(name)
5454
- }, sessionOptions(session));
5455
- return row ? nodeFromDocument(row) : null;
5456
- }
5457
- async #resolveNode(name, scope, session) {
5458
- const candidates = (await (await this.#nodes()).find({
5459
- type: "node",
5460
- canonicalName: canonicalName(name),
5461
- $or: [{ scopeKey: { $in: visibleScopeKeys(scope) } }, { mergedInto: { $ne: null } }]
5462
- }, sessionOptions(session)).toArray()).map(nodeFromDocument).sort((left, right) => right.scope.length - left.scope.length);
5463
- for (const candidate of candidates) {
5464
- const terminal = await this.#resolveTerminalNode(candidate.id, session);
5465
- if (terminal && (0, _mastra_core_storage.isKnowledgeScopeVisible)(terminal.scope, scope)) return terminal;
5466
- }
5467
- return null;
5468
- }
5469
- async #resolveTerminalNode(id, session) {
5470
- let node = await this.#getNode(id, session);
5471
- const seen = /* @__PURE__ */ new Set();
5472
- while (node?.mergedInto) {
5473
- if (seen.has(node.id)) throw new Error(`Knowledge merge cycle detected at ${node.id}`);
5474
- seen.add(node.id);
5475
- node = await this.#getNode(node.mergedInto, session);
5476
- }
5477
- return node;
6640
+ listScopeDeclarations() {
6641
+ return this.#pages.listScopeDeclarations();
5478
6642
  }
5479
- async #getPageByExactName(name, scope, session) {
5480
- const row = await (await this.#nodes()).findOne({
5481
- type: "page",
5482
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
5483
- canonicalName: canonicalName(name)
5484
- }, sessionOptions(session));
5485
- return row ? nodeFromDocument(row) : null;
6643
+ renameScopeDeclaration(input) {
6644
+ return this.#pages.renameScopeDeclaration(input);
5486
6645
  }
5487
- async #getKnowledge(id, includeDeleted, session) {
5488
- const row = await (await this.#knowledge()).findOne({
5489
- id,
5490
- ...includeDeleted ? {} : { deletedAt: null }
5491
- }, sessionOptions(session));
5492
- return row ? recordFromDocument(row) : null;
5493
- }
5494
- async #queryKnowledge(input, relationship) {
5495
- const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
5496
- const node = await this.#resolveTerminalNode(nodeReferenceId(input.node));
5497
- if (!node) return { records: [] };
5498
- const nodeIds = [node.id];
5499
- if (relationship !== "about") {
5500
- const mentions = await (await this.#mentions()).find({
5501
- recordId: node.id,
5502
- sourceType: "record"
5503
- }).toArray();
5504
- nodeIds.push(...mentions.map((row) => row.sourceId));
5505
- }
5506
- const limit = input.limit ?? 100;
5507
- const filter = relationship === "about" ? { node: node.id } : relationship === "mentioning" ? { id: { $in: nodeIds.slice(1) } } : { $or: [{ node: node.id }, { id: { $in: nodeIds.slice(1) } }] };
5508
- Object.assign(filter, {
5509
- scopeKey: { $in: visibleScopeKeys(scope) },
5510
- ...input.includeDeleted ? {} : { deletedAt: null },
5511
- ...input.after ? { id: { $lt: input.after } } : {}
5512
- });
5513
- const rows = await (await this.#knowledge()).find(filter).sort({ id: -1 }).limit(limit + 1).toArray();
5514
- return {
5515
- records: rows.slice(0, limit).map(recordFromDocument),
5516
- nextCursor: rows.length > limit ? rows[limit - 1]?.id : void 0
5517
- };
6646
+ listChildScopes(input) {
6647
+ return this.#pages.listChildScopes(input);
5518
6648
  }
5519
- async #replaceMentions(sourceType, sourceId, text, resolutionScope, defaultScope, session) {
5520
- const mentions = await this.#mentions();
5521
- await mentions.deleteMany({
5522
- sourceType,
5523
- sourceId
5524
- }, sessionOptions(session));
5525
- for (const name of (0, _mastra_core_storage.parseKnowledgeWikilinks)(text)) {
5526
- let node = await this.#resolveNode(name, resolutionScope, session);
5527
- if (!node) {
5528
- const existing = await this.#getNodeByName(name, defaultScope, session);
5529
- node = existing ? await this.#resolveTerminalNode(existing.id, session) : null;
5530
- }
5531
- if (!node) {
5532
- const now = /* @__PURE__ */ new Date();
5533
- node = {
5534
- id: crypto.randomUUID(),
5535
- type: "node",
5536
- name,
5537
- kind: "node",
5538
- scope: defaultScope,
5539
- version: 1,
5540
- createdAt: now,
5541
- updatedAt: now
5542
- };
5543
- try {
5544
- await (await this.#nodes()).insertOne({
5545
- ...node,
5546
- canonicalName: canonicalName(name),
5547
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(defaultScope),
5548
- mergedInto: null
5549
- }, sessionOptions(session));
5550
- await this.#activity("node-created", "node", node.id, defaultScope, void 0, session);
5551
- await this.#outbox("node", node.id, "upsert", 1, defaultScope, session);
5552
- } catch (error) {
5553
- if (error.code !== 11e3) throw error;
5554
- node = await this.#getNodeByName(name, defaultScope, session);
5555
- if (!node) throw error;
5556
- }
5557
- }
5558
- await mentions.updateOne({
5559
- sourceType,
5560
- sourceId,
5561
- recordId: node.id
5562
- }, { $setOnInsert: {
5563
- sourceType,
5564
- sourceId,
5565
- recordId: node.id
5566
- } }, {
5567
- ...sessionOptions(session),
5568
- upsert: true
5569
- });
5570
- }
6649
+ getPages(input) {
6650
+ return this.#pages.getPages(input);
5571
6651
  }
5572
- async #activity(action, recordType, recordId, scope, sourceThreadId, session) {
5573
- await (await this.#activityCollection()).insertOne({
5574
- id: (0, _mastra_core_storage.createKnowledgeUlid)(),
5575
- action,
5576
- recordType,
5577
- recordId,
5578
- scope,
5579
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
5580
- sourceThreadId: sourceThreadId ?? null,
5581
- createdAt: /* @__PURE__ */ new Date()
5582
- }, sessionOptions(session));
5583
- }
5584
- async #outbox(documentType, id, operation, version, scope, session) {
5585
- const documentId = (0, _mastra_core_storage.knowledgeSemanticDocumentId)(documentType, id);
5586
- const idempotencyKey = (0, _mastra_core_storage.knowledgeSemanticIdempotencyKey)(documentId, operation, version);
5587
- const now = /* @__PURE__ */ new Date();
5588
- try {
5589
- await (await this.#outboxCollection()).insertOne({
5590
- id: (0, _mastra_core_storage.createKnowledgeUlid)(),
5591
- idempotencyKey,
5592
- documentId,
5593
- documentType,
5594
- operation,
5595
- scope,
5596
- scopeKey: (0, _mastra_core_storage.knowledgeScopeKey)(scope),
5597
- status: "pending",
5598
- attempts: 0,
5599
- availableAt: now,
5600
- claimedAt: null,
5601
- claimedBy: null,
5602
- createdAt: now,
5603
- completedAt: null
5604
- }, sessionOptions(session));
5605
- } catch (error) {
5606
- if (error.code !== 11e3) throw error;
5607
- }
6652
+ getPage(input) {
6653
+ return this.#pages.getPage(input);
6654
+ }
6655
+ getPageByPath(input) {
6656
+ return this.#pages.getPageByPath(input);
6657
+ }
6658
+ findPagesByName(input) {
6659
+ return this.#pages.findPagesByName(input);
6660
+ }
6661
+ listChildPages(input) {
6662
+ return this.#pages.listChildPages(input);
6663
+ }
6664
+ listPages(input) {
6665
+ return this.#pages.listPages(input);
6666
+ }
6667
+ createPage(input) {
6668
+ return this.#pages.createPage(input);
6669
+ }
6670
+ updatePage(input) {
6671
+ return this.#pages.updatePage(input);
6672
+ }
6673
+ movePage(input) {
6674
+ return this.#pages.movePage(input);
6675
+ }
6676
+ mergePages(input) {
6677
+ return this.#pages.mergePages(input);
6678
+ }
6679
+ deletePage(input) {
6680
+ return this.#pages.deletePage(input);
6681
+ }
6682
+ getNote(input) {
6683
+ return this.#pages.getNote(input);
6684
+ }
6685
+ listNotes(input) {
6686
+ return this.#pages.listNotes(input);
6687
+ }
6688
+ countNotes(input) {
6689
+ return this.#pages.countNotes(input);
6690
+ }
6691
+ listNotesMentioning(input) {
6692
+ return this.#pages.listNotesMentioning(input);
6693
+ }
6694
+ addNote(input) {
6695
+ return this.#pages.addNote(input);
6696
+ }
6697
+ restampNote(input) {
6698
+ return this.#pages.restampNote(input);
6699
+ }
6700
+ removeNote(input) {
6701
+ return this.#pages.removeNote(input);
6702
+ }
6703
+ restoreNote(input) {
6704
+ return this.#pages.restoreNote(input);
6705
+ }
6706
+ searchPages(input) {
6707
+ return this.#pages.searchPages(input);
6708
+ }
6709
+ listPageActivity(input) {
6710
+ return this.#pages.listPageActivity(input);
6711
+ }
6712
+ addPinAudience(input) {
6713
+ return this.#pages.addPinAudience(input);
6714
+ }
6715
+ removePinAudience(input) {
6716
+ return this.#pages.removePinAudience(input);
5608
6717
  }
5609
- #escapeRegex(value) {
5610
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6718
+ listPins(input) {
6719
+ return this.#pages.listPins(input);
5611
6720
  }
5612
6721
  };
5613
6722
  // src/storage/domains/mcp-clients/index.ts