@mastra/mongodb 1.17.1-alpha.0 → 1.18.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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { v4 } from "@lukeed/uuid";
2
2
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
3
- import { AgentsStorage, BackgroundTasksStorage, BlobStore, DatasetsStorage, ExperimentsStorage, MCPClientsStorage, MCPServersStorage, MastraCompositeStore, MemoryStorage, NotificationsStorage, ObservabilityStorage, PromptBlocksStorage, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, TraceStatus, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, createStorageErrorId, createVectorErrorId, ensureDate, hasErrorCode, listTracesArgsSchema, normalizePerPage, normalizeScheduleTarget, parseDuration, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
3
+ import { AgentsStorage, BackgroundTasksStorage, BlobStore, DatasetsStorage, ExperimentsStorage, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, MastraCompositeStore, MemoryStorage, NotificationsStorage, ObservabilityStorage, PromptBlocksStorage, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_CURSORS, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, TraceStatus, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, assertKnowledgeCeilingRaised, assertKnowledgeScopeWithinCeiling, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, hasErrorCode, isKnowledgeScopeVisible, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listTracesArgsSchema, normalizePerPage, normalizeScheduleTarget, parseDuration, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, safelyParseJSON, storageMessageMatchesMetadataFilter, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
4
4
  import { MastraVector, validateUpsertInput, validateVectorValues } from "@mastra/core/vector";
5
5
  import { MongoBulkWriteError, MongoClient, ObjectId } from "mongodb";
6
6
  import { BaseFilterTranslator } from "@mastra/core/vector/filter";
@@ -9,7 +9,7 @@ import { MessageList } from "@mastra/core/agent";
9
9
  import { saveScorePayloadSchema } from "@mastra/core/evals";
10
10
  import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
11
11
  //#region package.json
12
- var version = "1.17.1-alpha.0";
12
+ var version = "1.18.0";
13
13
  //#endregion
14
14
  //#region src/vector/filter.ts
15
15
  /**
@@ -4292,6 +4292,809 @@ var MongoDBExperimentsStorage = class MongoDBExperimentsStorage extends Experime
4292
4292
  }
4293
4293
  };
4294
4294
  //#endregion
4295
+ //#region src/storage/domains/knowledge/index.ts
4296
+ const cloneScope = (scope) => [...scope];
4297
+ const canonicalName = (name) => name.trim().toLocaleLowerCase();
4298
+ const nodeReferenceId = (node) => typeof node === "string" ? node : node.id;
4299
+ const sessionOptions = (session) => session ? { session } : {};
4300
+ function visibleScopeKeys(scope) {
4301
+ const canonical = canonicalizeKnowledgeScope(scope);
4302
+ return canonical.map((_, index) => knowledgeScopeKey(canonical.slice(0, index + 1)));
4303
+ }
4304
+ function recordCursorFilter(cursor, expected) {
4305
+ const parsed = parseKnowledgeNodeCursor(cursor, expected);
4306
+ return { $or: [{ updatedAt: { $lt: parsed.updatedAt } }, {
4307
+ updatedAt: parsed.updatedAt,
4308
+ $or: [{ name: { $gt: parsed.name } }, {
4309
+ name: parsed.name,
4310
+ id: { $gt: parsed.id }
4311
+ }]
4312
+ }] };
4313
+ }
4314
+ function nodeFromDocument(row) {
4315
+ return {
4316
+ id: String(row.id),
4317
+ type: "node",
4318
+ name: String(row.name),
4319
+ kind: String(row.kind),
4320
+ content: row.content == null ? void 0 : String(row.content),
4321
+ scope: cloneScope(row.scope),
4322
+ version: Number(row.version),
4323
+ mergedInto: row.mergedInto ?? void 0,
4324
+ createdAt: new Date(row.createdAt),
4325
+ updatedAt: new Date(row.updatedAt)
4326
+ };
4327
+ }
4328
+ function recordFromDocument(row) {
4329
+ return {
4330
+ id: String(row.id),
4331
+ node: String(row.node),
4332
+ text: String(row.text),
4333
+ scope: cloneScope(row.scope),
4334
+ sourceThreadId: String(row.sourceThreadId),
4335
+ capturedAt: new Date(row.capturedAt),
4336
+ when: row.when ? new Date(row.when) : void 0,
4337
+ maxScope: row.maxScope ?? void 0,
4338
+ metadata: row.metadata ?? void 0,
4339
+ deletedAt: row.deletedAt ? new Date(row.deletedAt) : void 0,
4340
+ deletedBy: row.deletedBy ?? void 0
4341
+ };
4342
+ }
4343
+ function outboxFromDocument(row) {
4344
+ return {
4345
+ id: String(row.id),
4346
+ idempotencyKey: String(row.idempotencyKey),
4347
+ documentId: String(row.documentId),
4348
+ documentType: row.documentType,
4349
+ operation: row.operation,
4350
+ scope: cloneScope(row.scope),
4351
+ status: row.status,
4352
+ attempts: Number(row.attempts),
4353
+ availableAt: new Date(row.availableAt),
4354
+ claimedAt: row.claimedAt ? new Date(row.claimedAt) : void 0,
4355
+ claimedBy: row.claimedBy ?? void 0,
4356
+ createdAt: new Date(row.createdAt),
4357
+ completedAt: row.completedAt ? new Date(row.completedAt) : void 0
4358
+ };
4359
+ }
4360
+ var KnowledgeMongoDB = class KnowledgeMongoDB extends KnowledgeStorage {
4361
+ static MANAGED_COLLECTIONS = [
4362
+ TABLE_KNOWLEDGE_NODES,
4363
+ TABLE_KNOWLEDGE_RECORDS,
4364
+ TABLE_KNOWLEDGE_MENTIONS,
4365
+ TABLE_KNOWLEDGE_CURSORS,
4366
+ TABLE_KNOWLEDGE_ACTIVITY,
4367
+ TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
4368
+ ];
4369
+ #connector;
4370
+ constructor(config) {
4371
+ super();
4372
+ this.#connector = resolveMongoDBConfig(config);
4373
+ }
4374
+ async init() {
4375
+ const nodes = await this.#collection(TABLE_KNOWLEDGE_NODES);
4376
+ const knowledge = await this.#collection(TABLE_KNOWLEDGE_RECORDS);
4377
+ const mentions = await this.#collection(TABLE_KNOWLEDGE_MENTIONS);
4378
+ const cursors = await this.#collection(TABLE_KNOWLEDGE_CURSORS);
4379
+ const activity = await this.#collection(TABLE_KNOWLEDGE_ACTIVITY);
4380
+ const outbox = await this.#collection(TABLE_KNOWLEDGE_SEMANTIC_OUTBOX);
4381
+ await Promise.all([
4382
+ nodes.createIndex({
4383
+ type: 1,
4384
+ scopeKey: 1,
4385
+ canonicalName: 1
4386
+ }, { unique: true }),
4387
+ nodes.createIndex({
4388
+ scopeKey: 1,
4389
+ type: 1
4390
+ }),
4391
+ knowledge.createIndex({
4392
+ node: 1,
4393
+ id: -1
4394
+ }),
4395
+ knowledge.createIndex({
4396
+ sourceThreadId: 1,
4397
+ id: -1
4398
+ }),
4399
+ mentions.createIndex({
4400
+ sourceType: 1,
4401
+ sourceId: 1,
4402
+ recordId: 1
4403
+ }, { unique: true }),
4404
+ mentions.createIndex({
4405
+ recordId: 1,
4406
+ sourceType: 1,
4407
+ sourceId: 1
4408
+ }),
4409
+ cursors.createIndex({
4410
+ sourceThreadId: 1,
4411
+ agent: 1
4412
+ }, { unique: true }),
4413
+ activity.createIndex({ id: -1 }),
4414
+ outbox.createIndex({ idempotencyKey: 1 }, { unique: true }),
4415
+ outbox.createIndex({
4416
+ status: 1,
4417
+ availableAt: 1,
4418
+ createdAt: 1
4419
+ })
4420
+ ]);
4421
+ }
4422
+ async dangerouslyClearAll() {
4423
+ await this.#connector.withTransaction(async (session) => {
4424
+ for (const name of KnowledgeMongoDB.MANAGED_COLLECTIONS) await (await this.#collection(name)).deleteMany({}, sessionOptions(session));
4425
+ });
4426
+ }
4427
+ async createNode(input) {
4428
+ const scope = canonicalizeKnowledgeScope(input.scope);
4429
+ return this.#connector.withTransaction(async (session) => {
4430
+ const existing = await this.#getNodeByName(input.name, scope, session);
4431
+ if (existing) {
4432
+ const terminal = await this.#resolveTerminalNode(existing.id, session);
4433
+ if (!isKnowledgeScopeVisible(terminal.scope, scope)) throw new Error(`Merged knowledge node is not visible from scope: ${input.name}`);
4434
+ return terminal;
4435
+ }
4436
+ const now = /* @__PURE__ */ new Date();
4437
+ const node = {
4438
+ id: input.id ?? crypto.randomUUID(),
4439
+ type: "node",
4440
+ name: input.name.trim(),
4441
+ kind: input.kind,
4442
+ content: input.content,
4443
+ scope,
4444
+ version: 1,
4445
+ createdAt: now,
4446
+ updatedAt: now
4447
+ };
4448
+ try {
4449
+ await (await this.#nodes()).insertOne({
4450
+ ...node,
4451
+ canonicalName: canonicalName(node.name),
4452
+ scopeKey: knowledgeScopeKey(scope),
4453
+ mergedInto: null
4454
+ }, sessionOptions(session));
4455
+ } catch (error) {
4456
+ if (error.code !== 11e3) throw error;
4457
+ const concurrent = await this.#getNodeByName(input.name, scope, session);
4458
+ if (!concurrent) throw error;
4459
+ return await this.#resolveTerminalNode(concurrent.id, session);
4460
+ }
4461
+ await this.#replaceMentions("node", node.id, node.content ?? "", input.resolutionScope ?? scope, scope, session);
4462
+ await this.#activity("node-created", "node", node.id, scope, void 0, session);
4463
+ await this.#outbox("node", node.id, "upsert", 1, scope, session);
4464
+ return node;
4465
+ });
4466
+ }
4467
+ async getNode(id) {
4468
+ return this.#getNode(id);
4469
+ }
4470
+ async getNodeByName(input) {
4471
+ return this.#getNodeByName(input.name, canonicalizeKnowledgeScope(input.scope));
4472
+ }
4473
+ async resolveNode(input) {
4474
+ return this.#resolveNode(input.name, canonicalizeKnowledgeScope(input.scope));
4475
+ }
4476
+ async listNodes(input) {
4477
+ const filter = {
4478
+ type: "node",
4479
+ mergedInto: null,
4480
+ scopeKey: { $in: visibleScopeKeys(canonicalizeKnowledgeScope(input.scope)) },
4481
+ ...input.namePrefix ? { canonicalName: { $regex: `^${this.#escapeRegex(canonicalName(input.namePrefix))}` } } : {},
4482
+ ...input.kind ? { kind: input.kind } : {},
4483
+ ...input.hasContent === void 0 ? {} : input.hasContent ? { content: {
4484
+ $exists: true,
4485
+ $nin: [null, ""]
4486
+ } } : { $or: [
4487
+ { content: { $exists: false } },
4488
+ { content: null },
4489
+ { content: "" }
4490
+ ] },
4491
+ ...input.cursor ? recordCursorFilter(input.cursor, {
4492
+ namePrefix: input.namePrefix,
4493
+ kind: input.kind,
4494
+ hasContent: input.hasContent
4495
+ }) : {}
4496
+ };
4497
+ return (await (await this.#nodes()).find(filter).sort({
4498
+ updatedAt: -1,
4499
+ name: 1,
4500
+ id: 1
4501
+ }).limit(input.limit ?? 100).toArray()).map(nodeFromDocument);
4502
+ }
4503
+ async updateNode(input) {
4504
+ return this.#connector.withTransaction(async (session) => {
4505
+ const existing = await this.#getNode(input.id, session);
4506
+ if (!existing) throw new KnowledgeNotFoundError("node", input.id);
4507
+ if (existing.mergedInto) throw new Error(`Cannot update merged knowledge node: ${input.id}`);
4508
+ const scope = input.scope ? canonicalizeKnowledgeScope(input.scope) : existing.scope;
4509
+ const name = input.name?.trim() ?? existing.name;
4510
+ const content = input.content ?? existing.content;
4511
+ const now = /* @__PURE__ */ new Date();
4512
+ const result = await (await this.#nodes()).findOneAndUpdate({
4513
+ id: input.id,
4514
+ type: "node",
4515
+ version: input.version
4516
+ }, {
4517
+ $set: {
4518
+ name,
4519
+ canonicalName: canonicalName(name),
4520
+ kind: input.kind ?? existing.kind,
4521
+ content: content ?? null,
4522
+ scope,
4523
+ scopeKey: knowledgeScopeKey(scope),
4524
+ updatedAt: now
4525
+ },
4526
+ $inc: { version: 1 }
4527
+ }, {
4528
+ ...sessionOptions(session),
4529
+ returnDocument: "after"
4530
+ });
4531
+ if (!result) throw new KnowledgeConflictError(input.id);
4532
+ 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);
4533
+ if (knowledgeScopeKey(scope) !== knowledgeScopeKey(existing.scope)) {
4534
+ await this.#outbox("node", input.id, "delete", createKnowledgeUlid(), existing.scope, session);
4535
+ const records = await (await this.#knowledge()).find({ node: input.id }, sessionOptions(session)).toArray();
4536
+ for (const record of records) {
4537
+ await this.#outbox("record", record.id, "delete", createKnowledgeUlid(), record.scope, session);
4538
+ if (!record.deletedAt) await this.#outbox("record", record.id, "upsert", createKnowledgeUlid(), record.scope, session);
4539
+ }
4540
+ }
4541
+ await this.#activity("node-updated", "node", input.id, scope, void 0, session);
4542
+ await this.#outbox("node", input.id, "upsert", Number(result.version), scope, session);
4543
+ return nodeFromDocument(result);
4544
+ });
4545
+ }
4546
+ async mergeNodes(input) {
4547
+ if (input.sourceId === input.targetId) throw new Error("Cannot merge a knowledge node into itself");
4548
+ return this.#connector.withTransaction(async (session) => {
4549
+ const source = await this.#getNode(input.sourceId, session);
4550
+ if (!source) throw new KnowledgeNotFoundError("node", input.sourceId);
4551
+ const target = await this.#resolveTerminalNode(input.targetId, session);
4552
+ if (!target) throw new KnowledgeNotFoundError("node", input.targetId);
4553
+ if (target.id === source.id) throw new Error("Cannot create a knowledge merge cycle");
4554
+ if (!isKnowledgeScopeVisible(target.scope, source.scope)) throw new Error("Cannot merge a knowledge node into a target that is narrower than its source scope");
4555
+ const mentions = await this.#mentions();
4556
+ const affected = await mentions.find({ recordId: source.id }, sessionOptions(session)).toArray();
4557
+ const movedFacts = await (await this.#knowledge()).find({ node: source.id }, sessionOptions(session)).toArray();
4558
+ if ((await (await this.#nodes()).updateOne({
4559
+ id: source.id,
4560
+ type: "node",
4561
+ version: input.sourceVersion,
4562
+ mergedInto: null
4563
+ }, {
4564
+ $set: {
4565
+ mergedInto: target.id,
4566
+ updatedAt: /* @__PURE__ */ new Date()
4567
+ },
4568
+ $inc: { version: 1 }
4569
+ }, sessionOptions(session))).modifiedCount === 0) throw new KnowledgeConflictError(source.id);
4570
+ await (await this.#knowledge()).updateMany({ node: source.id }, { $set: { node: target.id } }, sessionOptions(session));
4571
+ for (const mention of affected) if (await mentions.findOne({
4572
+ sourceType: mention.sourceType,
4573
+ sourceId: mention.sourceId,
4574
+ recordId: target.id
4575
+ }, sessionOptions(session))) await mentions.deleteOne({ _id: mention._id }, sessionOptions(session));
4576
+ else await mentions.updateOne({ _id: mention._id }, { $set: { recordId: target.id } }, sessionOptions(session));
4577
+ for (const record of movedFacts) if (!record.deletedAt) await this.#outbox("record", record.id, "upsert", createKnowledgeUlid(), record.scope, session);
4578
+ for (const mention of affected) {
4579
+ const scope = mention.sourceType === "record" ? (await (await this.#knowledge()).findOne({ id: mention.sourceId }, sessionOptions(session)))?.scope : (await (await this.#nodes()).findOne({
4580
+ id: mention.sourceId,
4581
+ type: "node"
4582
+ }, sessionOptions(session)))?.scope;
4583
+ if (scope) await this.#outbox(mention.sourceType, mention.sourceId, "upsert", createKnowledgeUlid(), scope, session);
4584
+ }
4585
+ await this.#activity("node-merged", "node", source.id, source.scope, void 0, session);
4586
+ await this.#outbox("node", source.id, "delete", input.sourceVersion + 1, source.scope, session);
4587
+ await this.#outbox("node", target.id, "upsert", createKnowledgeUlid(), target.scope, session);
4588
+ return target;
4589
+ });
4590
+ }
4591
+ async appendKnowledge(input) {
4592
+ const scope = canonicalizeKnowledgeScope(input.scope);
4593
+ const defaultScope = canonicalizeKnowledgeScope(input.defaultScope);
4594
+ assertKnowledgeScopeWithinCeiling(scope, input.maxScope);
4595
+ return this.#connector.withTransaction(async (session) => {
4596
+ const parent = await this.#resolveTerminalNode(nodeReferenceId(input.node), session);
4597
+ if (!parent) throw new KnowledgeNotFoundError("node", nodeReferenceId(input.node));
4598
+ const id = input.id ?? createKnowledgeUlid();
4599
+ const existing = await (await this.#knowledge()).findOne({ id }, sessionOptions(session));
4600
+ if (existing) return recordFromDocument(existing);
4601
+ const record = {
4602
+ id,
4603
+ node: parent.id,
4604
+ text: input.text,
4605
+ scope,
4606
+ sourceThreadId: input.sourceThreadId,
4607
+ capturedAt: /* @__PURE__ */ new Date(),
4608
+ when: input.when,
4609
+ maxScope: input.maxScope,
4610
+ metadata: input.metadata
4611
+ };
4612
+ await (await this.#knowledge()).insertOne({
4613
+ ...record,
4614
+ scopeKey: knowledgeScopeKey(scope),
4615
+ when: record.when ?? null,
4616
+ maxScope: record.maxScope ?? null,
4617
+ deletedAt: null,
4618
+ deletedBy: null
4619
+ }, sessionOptions(session));
4620
+ await this.#replaceMentions("record", id, record.text, input.resolutionScope, defaultScope, session);
4621
+ await this.#activity("record-created", "record", id, scope, input.sourceThreadId, session);
4622
+ await this.#outbox("record", id, "upsert", createKnowledgeUlid(), scope, session);
4623
+ return record;
4624
+ });
4625
+ }
4626
+ async getKnowledge(input) {
4627
+ const row = await (await this.#knowledge()).findOne({
4628
+ id: input.id,
4629
+ ...input.includeDeleted ? {} : { deletedAt: null }
4630
+ });
4631
+ return row ? recordFromDocument(row) : null;
4632
+ }
4633
+ async listKnowledgeAbout(input) {
4634
+ return this.#queryKnowledge(input, "about");
4635
+ }
4636
+ async listKnowledgeMentioning(input) {
4637
+ return this.#queryKnowledge(input, "mentioning");
4638
+ }
4639
+ async listKnowledgeRelatedTo(input) {
4640
+ return this.#queryKnowledge(input, "related");
4641
+ }
4642
+ async knowledgeBySource(input) {
4643
+ const scope = canonicalizeKnowledgeScope(input.scope);
4644
+ const limit = input.limit ?? 100;
4645
+ const rows = await (await this.#knowledge()).find({
4646
+ sourceThreadId: input.sourceThreadId,
4647
+ scopeKey: { $in: visibleScopeKeys(scope) },
4648
+ ...input.includeDeleted ? {} : { deletedAt: null },
4649
+ ...input.after ? { id: { $gt: input.after } } : {}
4650
+ }).sort({ id: 1 }).limit(limit + 1).toArray();
4651
+ return {
4652
+ records: rows.slice(0, limit).map(recordFromDocument),
4653
+ nextCursor: rows.length > limit ? rows[limit - 1]?.id : void 0
4654
+ };
4655
+ }
4656
+ async removeKnowledge(input) {
4657
+ return this.#connector.withTransaction(async (session) => {
4658
+ const record = await this.#getKnowledge(input.id, true, session);
4659
+ if (!record) throw new KnowledgeNotFoundError("record", input.id);
4660
+ if (record.deletedAt) return record;
4661
+ const deletedAt = /* @__PURE__ */ new Date();
4662
+ await (await this.#knowledge()).updateOne({
4663
+ id: input.id,
4664
+ deletedAt: null
4665
+ }, { $set: {
4666
+ deletedAt,
4667
+ deletedBy: input.deletedBy
4668
+ } }, sessionOptions(session));
4669
+ await this.#activity("record-deleted", "record", input.id, record.scope, record.sourceThreadId, session);
4670
+ await this.#outbox("record", input.id, "delete", createKnowledgeUlid(), record.scope, session);
4671
+ return {
4672
+ ...record,
4673
+ deletedAt,
4674
+ deletedBy: input.deletedBy
4675
+ };
4676
+ });
4677
+ }
4678
+ async restoreKnowledge(input) {
4679
+ return this.#connector.withTransaction(async (session) => {
4680
+ const record = await this.#getKnowledge(input.id, true, session);
4681
+ if (!record) throw new KnowledgeNotFoundError("record", input.id);
4682
+ if (!record.deletedAt) return record;
4683
+ await (await this.#knowledge()).updateOne({ id: input.id }, { $set: {
4684
+ deletedAt: null,
4685
+ deletedBy: null
4686
+ } }, sessionOptions(session));
4687
+ await this.#activity("record-restored", "record", input.id, record.scope, record.sourceThreadId, session);
4688
+ await this.#outbox("record", input.id, "upsert", createKnowledgeUlid(), record.scope, session);
4689
+ return {
4690
+ ...record,
4691
+ deletedAt: void 0,
4692
+ deletedBy: void 0
4693
+ };
4694
+ });
4695
+ }
4696
+ async rescopeKnowledge(input) {
4697
+ const scope = canonicalizeKnowledgeScope(input.scope);
4698
+ return this.#connector.withTransaction(async (session) => {
4699
+ const record = await this.#getKnowledge(input.id, true, session);
4700
+ if (!record) throw new KnowledgeNotFoundError("record", input.id);
4701
+ assertKnowledgeScopeWithinCeiling(scope, record.maxScope);
4702
+ await (await this.#knowledge()).updateOne({ id: input.id }, { $set: {
4703
+ scope,
4704
+ scopeKey: knowledgeScopeKey(scope)
4705
+ } }, sessionOptions(session));
4706
+ await this.#activity("record-rescoped", "record", input.id, scope, record.sourceThreadId, session);
4707
+ await this.#outbox("record", input.id, "delete", createKnowledgeUlid(), record.scope, session);
4708
+ if (!record.deletedAt) await this.#outbox("record", input.id, "upsert", createKnowledgeUlid(), scope, session);
4709
+ return {
4710
+ ...record,
4711
+ scope
4712
+ };
4713
+ });
4714
+ }
4715
+ async raiseKnowledgeCeiling(input) {
4716
+ const record = await this.#getKnowledge(input.id, true);
4717
+ if (!record) throw new KnowledgeNotFoundError("record", input.id);
4718
+ assertKnowledgeScopeWithinCeiling(record.scope, input.maxScope);
4719
+ assertKnowledgeCeilingRaised(record.maxScope, input.maxScope);
4720
+ await (await this.#knowledge()).updateOne({ id: input.id }, { $set: { maxScope: input.maxScope ?? null } });
4721
+ return {
4722
+ ...record,
4723
+ maxScope: input.maxScope
4724
+ };
4725
+ }
4726
+ async search(input) {
4727
+ const scope = canonicalizeKnowledgeScope(input.scope);
4728
+ const query = input.query.trim();
4729
+ if (!query) return [];
4730
+ const regex = new RegExp(this.#escapeRegex(query), "i");
4731
+ const limit = input.limit ?? 20;
4732
+ const results = (await (await this.#nodes()).find({
4733
+ mergedInto: null,
4734
+ scopeKey: { $in: visibleScopeKeys(scope) },
4735
+ $or: [
4736
+ { name: regex },
4737
+ { kind: regex },
4738
+ { content: regex }
4739
+ ]
4740
+ }).sort({ updatedAt: -1 }).limit(limit).toArray()).map((row) => ({
4741
+ type: "node",
4742
+ id: row.id,
4743
+ recordId: row.id,
4744
+ name: row.name,
4745
+ text: row.content ? `${row.name}\n${row.content}` : row.name,
4746
+ scope: cloneScope(row.scope)
4747
+ }));
4748
+ if (results.length < limit) {
4749
+ const records = await (await this.#knowledge()).find({
4750
+ deletedAt: null,
4751
+ scopeKey: { $in: visibleScopeKeys(scope) },
4752
+ text: regex
4753
+ }).sort({ id: -1 }).limit(limit - results.length).toArray();
4754
+ for (const record of records) {
4755
+ const parent = await this.#resolveTerminalNode(record.node);
4756
+ const parentVisible = parent && isKnowledgeScopeVisible(parent.scope, scope);
4757
+ results.push({
4758
+ type: "record",
4759
+ id: record.id,
4760
+ recordId: record.node,
4761
+ name: parentVisible ? parent.name : "(private node)",
4762
+ text: record.text,
4763
+ scope: cloneScope(record.scope)
4764
+ });
4765
+ }
4766
+ }
4767
+ return results.slice(0, limit);
4768
+ }
4769
+ async getCurationCursor(input) {
4770
+ const row = await (await this.#cursors()).findOne(input);
4771
+ return row ? {
4772
+ sourceThreadId: row.sourceThreadId,
4773
+ agent: row.agent,
4774
+ lastKnowledgeId: row.lastKnowledgeId,
4775
+ updatedAt: new Date(row.updatedAt)
4776
+ } : null;
4777
+ }
4778
+ async advanceCurationCursor(input) {
4779
+ const row = await (await this.#cursors()).findOneAndUpdate({
4780
+ sourceThreadId: input.sourceThreadId,
4781
+ agent: input.agent
4782
+ }, {
4783
+ $max: { lastKnowledgeId: input.lastKnowledgeId },
4784
+ $set: { updatedAt: /* @__PURE__ */ new Date() },
4785
+ $setOnInsert: {
4786
+ sourceThreadId: input.sourceThreadId,
4787
+ agent: input.agent
4788
+ }
4789
+ }, {
4790
+ upsert: true,
4791
+ returnDocument: "after"
4792
+ });
4793
+ return {
4794
+ sourceThreadId: row.sourceThreadId,
4795
+ agent: row.agent,
4796
+ lastKnowledgeId: row.lastKnowledgeId,
4797
+ updatedAt: new Date(row.updatedAt)
4798
+ };
4799
+ }
4800
+ async listActivity(input) {
4801
+ const scope = canonicalizeKnowledgeScope(input.scope);
4802
+ return (await (await this.#activityCollection()).find({
4803
+ scopeKey: { $in: visibleScopeKeys(scope) },
4804
+ ...input.after ? { id: { $lt: input.after } } : {}
4805
+ }).sort({ id: -1 }).limit(input.limit ?? 100).toArray()).map((row) => ({
4806
+ id: row.id,
4807
+ action: row.action,
4808
+ recordType: row.recordType,
4809
+ recordId: row.recordId,
4810
+ scope: cloneScope(row.scope),
4811
+ sourceThreadId: row.sourceThreadId ?? void 0,
4812
+ createdAt: new Date(row.createdAt)
4813
+ }));
4814
+ }
4815
+ async listSemanticOutbox(input = {}) {
4816
+ const filter = {
4817
+ ...input.status ? { status: input.status } : {},
4818
+ ...input.scope ? { scopeKey: { $in: visibleScopeKeys(input.scope) } } : {}
4819
+ };
4820
+ return (await (await this.#outboxCollection()).find(filter).sort({
4821
+ createdAt: 1,
4822
+ id: 1
4823
+ }).limit(input.limit ?? 100).toArray()).map(outboxFromDocument);
4824
+ }
4825
+ async claimSemanticOutbox(input) {
4826
+ return this.#connector.withTransaction(async (session) => {
4827
+ const collection = await this.#outboxCollection();
4828
+ const now = input.now ?? /* @__PURE__ */ new Date();
4829
+ const staleBefore = new Date(now.getTime() - (input.claimTimeoutMs ?? 6e4));
4830
+ const filter = {
4831
+ $or: [{
4832
+ status: "pending",
4833
+ availableAt: { $lte: now }
4834
+ }, {
4835
+ status: "processing",
4836
+ claimedAt: { $lte: staleBefore }
4837
+ }],
4838
+ ...input.scope ? { scopeKey: { $in: visibleScopeKeys(input.scope) } } : {}
4839
+ };
4840
+ const limit = input.limit ?? 100;
4841
+ const candidates = await collection.find(filter, sessionOptions(session)).sort({
4842
+ createdAt: 1,
4843
+ id: 1
4844
+ }).limit(Math.max(limit * 10, 100)).toArray();
4845
+ const claimed = [];
4846
+ for (const candidate of candidates) {
4847
+ if (claimed.length >= limit) break;
4848
+ if (await collection.findOne({
4849
+ documentId: candidate.documentId,
4850
+ status: { $ne: "completed" },
4851
+ $or: [{ createdAt: { $lt: candidate.createdAt } }, {
4852
+ createdAt: candidate.createdAt,
4853
+ id: { $lt: candidate.id }
4854
+ }]
4855
+ }, sessionOptions(session))) continue;
4856
+ const result = await collection.findOneAndUpdate({
4857
+ id: candidate.id,
4858
+ $or: [{
4859
+ status: "pending",
4860
+ availableAt: { $lte: now }
4861
+ }, {
4862
+ status: "processing",
4863
+ claimedAt: { $lte: staleBefore }
4864
+ }]
4865
+ }, {
4866
+ $set: {
4867
+ status: "processing",
4868
+ claimedAt: now,
4869
+ claimedBy: input.workerId
4870
+ },
4871
+ $inc: { attempts: 1 }
4872
+ }, {
4873
+ ...sessionOptions(session),
4874
+ returnDocument: "after"
4875
+ });
4876
+ if (result) claimed.push(result);
4877
+ }
4878
+ return claimed.map(outboxFromDocument);
4879
+ });
4880
+ }
4881
+ async completeSemanticOutbox(input) {
4882
+ if (!input.ids.length) return;
4883
+ await (await this.#outboxCollection()).updateMany({
4884
+ id: { $in: input.ids },
4885
+ status: "processing",
4886
+ claimedBy: input.workerId
4887
+ }, { $set: {
4888
+ status: "completed",
4889
+ completedAt: /* @__PURE__ */ new Date(),
4890
+ claimedAt: null,
4891
+ claimedBy: null
4892
+ } });
4893
+ }
4894
+ async releaseSemanticOutbox(input) {
4895
+ if (!input.ids.length) return;
4896
+ await (await this.#outboxCollection()).updateMany({
4897
+ id: { $in: input.ids },
4898
+ status: "processing",
4899
+ claimedBy: input.workerId
4900
+ }, { $set: {
4901
+ status: "pending",
4902
+ availableAt: input.retryAt ?? /* @__PURE__ */ new Date(),
4903
+ claimedAt: null,
4904
+ claimedBy: null
4905
+ } });
4906
+ }
4907
+ async #collection(name) {
4908
+ return this.#connector.getCollection(name);
4909
+ }
4910
+ #nodes() {
4911
+ return this.#collection(TABLE_KNOWLEDGE_NODES);
4912
+ }
4913
+ #knowledge() {
4914
+ return this.#collection(TABLE_KNOWLEDGE_RECORDS);
4915
+ }
4916
+ #mentions() {
4917
+ return this.#collection(TABLE_KNOWLEDGE_MENTIONS);
4918
+ }
4919
+ #cursors() {
4920
+ return this.#collection(TABLE_KNOWLEDGE_CURSORS);
4921
+ }
4922
+ #activityCollection() {
4923
+ return this.#collection(TABLE_KNOWLEDGE_ACTIVITY);
4924
+ }
4925
+ #outboxCollection() {
4926
+ return this.#collection(TABLE_KNOWLEDGE_SEMANTIC_OUTBOX);
4927
+ }
4928
+ async #getNode(id, session) {
4929
+ const row = await (await this.#nodes()).findOne({
4930
+ id,
4931
+ type: "node"
4932
+ }, sessionOptions(session));
4933
+ return row ? nodeFromDocument(row) : null;
4934
+ }
4935
+ async #getNodeByName(name, scope, session) {
4936
+ const row = await (await this.#nodes()).findOne({
4937
+ type: "node",
4938
+ scopeKey: knowledgeScopeKey(scope),
4939
+ canonicalName: canonicalName(name)
4940
+ }, sessionOptions(session));
4941
+ return row ? nodeFromDocument(row) : null;
4942
+ }
4943
+ async #resolveNode(name, scope, session) {
4944
+ for (let length = scope.length; length > 0; length--) {
4945
+ const node = await this.#getNodeByName(name, scope.slice(0, length), session);
4946
+ if (node) {
4947
+ const terminal = await this.#resolveTerminalNode(node.id, session);
4948
+ if (terminal && isKnowledgeScopeVisible(terminal.scope, scope)) return terminal;
4949
+ }
4950
+ }
4951
+ return null;
4952
+ }
4953
+ async #resolveTerminalNode(id, session) {
4954
+ let node = await this.#getNode(id, session);
4955
+ const seen = /* @__PURE__ */ new Set();
4956
+ while (node?.mergedInto) {
4957
+ if (seen.has(node.id)) throw new Error(`Knowledge merge cycle detected at ${node.id}`);
4958
+ seen.add(node.id);
4959
+ node = await this.#getNode(node.mergedInto, session);
4960
+ }
4961
+ return node;
4962
+ }
4963
+ async #getPageByExactName(name, scope, session) {
4964
+ const row = await (await this.#nodes()).findOne({
4965
+ type: "page",
4966
+ scopeKey: knowledgeScopeKey(scope),
4967
+ canonicalName: canonicalName(name)
4968
+ }, sessionOptions(session));
4969
+ return row ? nodeFromDocument(row) : null;
4970
+ }
4971
+ async #getKnowledge(id, includeDeleted, session) {
4972
+ const row = await (await this.#knowledge()).findOne({
4973
+ id,
4974
+ ...includeDeleted ? {} : { deletedAt: null }
4975
+ }, sessionOptions(session));
4976
+ return row ? recordFromDocument(row) : null;
4977
+ }
4978
+ async #queryKnowledge(input, relationship) {
4979
+ const scope = canonicalizeKnowledgeScope(input.scope);
4980
+ const node = await this.#resolveTerminalNode(nodeReferenceId(input.node));
4981
+ if (!node) return { records: [] };
4982
+ const nodeIds = [node.id];
4983
+ if (relationship !== "about") {
4984
+ const mentions = await (await this.#mentions()).find({
4985
+ recordId: node.id,
4986
+ sourceType: "record"
4987
+ }).toArray();
4988
+ nodeIds.push(...mentions.map((row) => row.sourceId));
4989
+ }
4990
+ const limit = input.limit ?? 100;
4991
+ const filter = relationship === "about" ? { node: node.id } : relationship === "mentioning" ? { id: { $in: nodeIds.slice(1) } } : { $or: [{ node: node.id }, { id: { $in: nodeIds.slice(1) } }] };
4992
+ Object.assign(filter, {
4993
+ scopeKey: { $in: visibleScopeKeys(scope) },
4994
+ ...input.includeDeleted ? {} : { deletedAt: null },
4995
+ ...input.after ? { id: { $lt: input.after } } : {}
4996
+ });
4997
+ const rows = await (await this.#knowledge()).find(filter).sort({ id: -1 }).limit(limit + 1).toArray();
4998
+ return {
4999
+ records: rows.slice(0, limit).map(recordFromDocument),
5000
+ nextCursor: rows.length > limit ? rows[limit - 1]?.id : void 0
5001
+ };
5002
+ }
5003
+ async #replaceMentions(sourceType, sourceId, text, resolutionScope, defaultScope, session) {
5004
+ const mentions = await this.#mentions();
5005
+ await mentions.deleteMany({
5006
+ sourceType,
5007
+ sourceId
5008
+ }, sessionOptions(session));
5009
+ for (const name of parseKnowledgeWikilinks(text)) {
5010
+ let node = await this.#resolveNode(name, resolutionScope, session);
5011
+ if (!node) {
5012
+ const existing = await this.#getNodeByName(name, defaultScope, session);
5013
+ node = existing ? await this.#resolveTerminalNode(existing.id, session) : null;
5014
+ }
5015
+ if (!node) {
5016
+ const now = /* @__PURE__ */ new Date();
5017
+ node = {
5018
+ id: crypto.randomUUID(),
5019
+ type: "node",
5020
+ name,
5021
+ kind: "node",
5022
+ scope: defaultScope,
5023
+ version: 1,
5024
+ createdAt: now,
5025
+ updatedAt: now
5026
+ };
5027
+ try {
5028
+ await (await this.#nodes()).insertOne({
5029
+ ...node,
5030
+ canonicalName: canonicalName(name),
5031
+ scopeKey: knowledgeScopeKey(defaultScope),
5032
+ mergedInto: null
5033
+ }, sessionOptions(session));
5034
+ await this.#activity("node-created", "node", node.id, defaultScope, void 0, session);
5035
+ await this.#outbox("node", node.id, "upsert", 1, defaultScope, session);
5036
+ } catch (error) {
5037
+ if (error.code !== 11e3) throw error;
5038
+ node = await this.#getNodeByName(name, defaultScope, session);
5039
+ if (!node) throw error;
5040
+ }
5041
+ }
5042
+ await mentions.updateOne({
5043
+ sourceType,
5044
+ sourceId,
5045
+ recordId: node.id
5046
+ }, { $setOnInsert: {
5047
+ sourceType,
5048
+ sourceId,
5049
+ recordId: node.id
5050
+ } }, {
5051
+ ...sessionOptions(session),
5052
+ upsert: true
5053
+ });
5054
+ }
5055
+ }
5056
+ async #activity(action, recordType, recordId, scope, sourceThreadId, session) {
5057
+ await (await this.#activityCollection()).insertOne({
5058
+ id: createKnowledgeUlid(),
5059
+ action,
5060
+ recordType,
5061
+ recordId,
5062
+ scope,
5063
+ scopeKey: knowledgeScopeKey(scope),
5064
+ sourceThreadId: sourceThreadId ?? null,
5065
+ createdAt: /* @__PURE__ */ new Date()
5066
+ }, sessionOptions(session));
5067
+ }
5068
+ async #outbox(documentType, id, operation, version, scope, session) {
5069
+ const documentId = knowledgeSemanticDocumentId(documentType, id);
5070
+ const idempotencyKey = knowledgeSemanticIdempotencyKey(documentId, operation, version);
5071
+ const now = /* @__PURE__ */ new Date();
5072
+ try {
5073
+ await (await this.#outboxCollection()).insertOne({
5074
+ id: createKnowledgeUlid(),
5075
+ idempotencyKey,
5076
+ documentId,
5077
+ documentType,
5078
+ operation,
5079
+ scope,
5080
+ scopeKey: knowledgeScopeKey(scope),
5081
+ status: "pending",
5082
+ attempts: 0,
5083
+ availableAt: now,
5084
+ claimedAt: null,
5085
+ claimedBy: null,
5086
+ createdAt: now,
5087
+ completedAt: null
5088
+ }, sessionOptions(session));
5089
+ } catch (error) {
5090
+ if (error.code !== 11e3) throw error;
5091
+ }
5092
+ }
5093
+ #escapeRegex(value) {
5094
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5095
+ }
5096
+ };
5097
+ //#endregion
4295
5098
  //#region src/storage/domains/mcp-clients/index.ts
4296
5099
  /**
4297
5100
  * Snapshot config fields that live on MCP client version documents.
@@ -10976,6 +11779,7 @@ var MongoDBStore = class extends MastraCompositeStore {
10976
11779
  indexes: config.indexes
10977
11780
  };
10978
11781
  const memory = new MemoryStorageMongoDB(domainConfig);
11782
+ const knowledge = new KnowledgeMongoDB(domainConfig);
10979
11783
  const notifications = new NotificationsMongoDB(domainConfig);
10980
11784
  const scores = new ScoresStorageMongoDB(domainConfig);
10981
11785
  const workflows = new WorkflowsStorageMongoDB(domainConfig);
@@ -10995,6 +11799,7 @@ var MongoDBStore = class extends MastraCompositeStore {
10995
11799
  const workflowDefinitions = new MongoDBWorkflowDefinitionsStore(domainConfig);
10996
11800
  this.stores = {
10997
11801
  memory,
11802
+ knowledge,
10998
11803
  notifications,
10999
11804
  scores,
11000
11805
  workflows,
@@ -11131,6 +11936,6 @@ Example Complex Query:
11131
11936
  ]
11132
11937
  }`;
11133
11938
  //#endregion
11134
- export { BackgroundTasksStorageMongoDB, MONGODB_PROMPT, MemoryStorageMongoDB, MongoDBAgentsStorage, MongoDBBlobStore, MongoDBDatasetsStorage, MongoDBExperimentsStorage, MongoDBMCPClientsStorage, MongoDBMCPServersStorage, MongoDBPromptBlocksStorage, MongoDBScorerDefinitionsStorage, MongoDBSkillsStorage, MongoDBStore, MongoDBVector, MongoDBWorkflowDefinitionsStore, MongoDBWorkspacesStorage, NotificationsMongoDB, ObservabilityMongoDB, SchedulesMongoDB, ScoresStorageMongoDB, WorkflowsStorageMongoDB };
11939
+ export { BackgroundTasksStorageMongoDB, KnowledgeMongoDB, MONGODB_PROMPT, MemoryStorageMongoDB, MongoDBAgentsStorage, MongoDBBlobStore, MongoDBDatasetsStorage, MongoDBExperimentsStorage, MongoDBMCPClientsStorage, MongoDBMCPServersStorage, MongoDBPromptBlocksStorage, MongoDBScorerDefinitionsStorage, MongoDBSkillsStorage, MongoDBStore, MongoDBVector, MongoDBWorkflowDefinitionsStore, MongoDBWorkspacesStorage, NotificationsMongoDB, ObservabilityMongoDB, SchedulesMongoDB, ScoresStorageMongoDB, WorkflowsStorageMongoDB };
11135
11940
 
11136
11941
  //# sourceMappingURL=index.js.map