@mastra/libsql 1.28.0 → 1.29.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_types/@internal_core@0.1.4/dist/knowledge-compat/index.d.ts +348 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-studio-editor.md +12 -9
- package/dist/index.cjs +1463 -1822
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1464 -1823
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/agents/index.d.ts.map +1 -1
- package/dist/storage/domains/knowledge/index.d.ts +35 -85
- package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
- package/dist/storage/domains/knowledge/pages.d.ts +162 -0
- package/dist/storage/domains/knowledge/pages.d.ts.map +1 -0
- package/dist/storage/domains/memory/index.d.ts +1 -1
- package/package.json +4 -4
- package/dist/_types/@internal_core/dist/knowledge-compat/index.d.ts +0 -592
- /package/dist/_types/{@internal_core → @internal_core@0.1.4}/dist/memory/index.d.ts +0 -0
- /package/dist/_types/{@internal_core → @internal_core@0.1.4}/package.json +0 -0
package/dist/index.cjs
CHANGED
|
@@ -9,7 +9,6 @@ let async_hooks = require("async_hooks");
|
|
|
9
9
|
let _mastra_core_vector_filter = require("@mastra/core/vector/filter");
|
|
10
10
|
let fs_promises = require("fs/promises");
|
|
11
11
|
let _mastra_core_base = require("@mastra/core/base");
|
|
12
|
-
let crypto$1 = require("crypto");
|
|
13
12
|
let _mastra_core_features = require("@mastra/core/features");
|
|
14
13
|
let _mastra_core_agent = require("@mastra/core/agent");
|
|
15
14
|
let _mastra_core_evals = require("@mastra/core/evals");
|
|
@@ -693,7 +692,7 @@ const FILTER_OPERATORS = {
|
|
|
693
692
|
transformValue: () => {
|
|
694
693
|
if (Array.isArray(value)) return [JSON.stringify(value)];
|
|
695
694
|
if (typeof value === "object" && value !== null) return [JSON.stringify(value)];
|
|
696
|
-
if (typeof value === "string") return [escapeLikePattern
|
|
695
|
+
if (typeof value === "string") return [escapeLikePattern(value)];
|
|
697
696
|
return [value];
|
|
698
697
|
}
|
|
699
698
|
};
|
|
@@ -710,7 +709,7 @@ const parseJsonPathKey = (key) => {
|
|
|
710
709
|
const getJsonPath = (key) => {
|
|
711
710
|
return `'$.${parseJsonPathKey(key)}'`;
|
|
712
711
|
};
|
|
713
|
-
function escapeLikePattern
|
|
712
|
+
function escapeLikePattern(str) {
|
|
714
713
|
return str.replace(/([%_\\])/g, "\\$1");
|
|
715
714
|
}
|
|
716
715
|
function buildFilterQuery(filter) {
|
|
@@ -2645,7 +2644,8 @@ var AgentsLibSQL = class extends _mastra_core_storage.AgentsStorage {
|
|
|
2645
2644
|
"skillsFormat",
|
|
2646
2645
|
"durable",
|
|
2647
2646
|
"browser",
|
|
2648
|
-
"toolProviders"
|
|
2647
|
+
"toolProviders",
|
|
2648
|
+
"modelList"
|
|
2649
2649
|
]
|
|
2650
2650
|
});
|
|
2651
2651
|
await this.#migrateToolsToJsonbFormat();
|
|
@@ -3062,8 +3062,9 @@ var AgentsLibSQL = class extends _mastra_core_storage.AgentsStorage {
|
|
|
3062
3062
|
versionNumber: input.versionNumber,
|
|
3063
3063
|
name: input.name ?? null,
|
|
3064
3064
|
description: input.description ?? null,
|
|
3065
|
-
instructions: this.serializeInstructions(input.instructions),
|
|
3066
|
-
model: input.model,
|
|
3065
|
+
instructions: this.serializeInstructions(input.instructions ?? ""),
|
|
3066
|
+
model: input.model ?? {},
|
|
3067
|
+
modelList: input.modelList ?? null,
|
|
3067
3068
|
tools: input.tools ?? null,
|
|
3068
3069
|
defaultOptions: input.defaultOptions ?? null,
|
|
3069
3070
|
workflows: input.workflows ?? null,
|
|
@@ -3320,6 +3321,7 @@ var AgentsLibSQL = class extends _mastra_core_storage.AgentsStorage {
|
|
|
3320
3321
|
description: row.description,
|
|
3321
3322
|
instructions: this.deserializeInstructions(row.instructions),
|
|
3322
3323
|
model: this.parseJson(row.model, "model"),
|
|
3324
|
+
modelList: this.parseJson(row.modelList, "modelList"),
|
|
3323
3325
|
tools: this.parseJson(row.tools, "tools"),
|
|
3324
3326
|
defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
|
|
3325
3327
|
workflows: this.parseJson(row.workflows, "workflows"),
|
|
@@ -3395,7 +3397,7 @@ function serializeJson(v) {
|
|
|
3395
3397
|
if (serialized === void 0) throw new Error("Failed to serialize background task value as JSON");
|
|
3396
3398
|
return serialized;
|
|
3397
3399
|
}
|
|
3398
|
-
function parseJson$
|
|
3400
|
+
function parseJson$3(val) {
|
|
3399
3401
|
if (val == null) return void 0;
|
|
3400
3402
|
if (typeof val === "string") try {
|
|
3401
3403
|
return JSON.parse(val);
|
|
@@ -3410,14 +3412,14 @@ function rowToTask(row) {
|
|
|
3410
3412
|
status: String(row.status),
|
|
3411
3413
|
toolName: String(row.tool_name),
|
|
3412
3414
|
toolCallId: String(row.tool_call_id),
|
|
3413
|
-
args: parseJson$
|
|
3415
|
+
args: parseJson$3(row.args) ?? {},
|
|
3414
3416
|
agentId: String(row.agent_id),
|
|
3415
3417
|
threadId: row.thread_id != null ? String(row.thread_id) : void 0,
|
|
3416
3418
|
resourceId: row.resource_id != null ? String(row.resource_id) : void 0,
|
|
3417
3419
|
runId: String(row.run_id),
|
|
3418
|
-
result: parseJson$
|
|
3419
|
-
error: parseJson$
|
|
3420
|
-
suspendPayload: parseJson$
|
|
3420
|
+
result: parseJson$3(row.result),
|
|
3421
|
+
error: parseJson$3(row.error),
|
|
3422
|
+
suspendPayload: parseJson$3(row.suspend_payload),
|
|
3421
3423
|
retryCount: Number(row.retry_count),
|
|
3422
3424
|
maxRetries: Number(row.max_retries),
|
|
3423
3425
|
timeoutMs: Number(row.timeout_ms),
|
|
@@ -3428,7 +3430,7 @@ function rowToTask(row) {
|
|
|
3428
3430
|
ownerId: row.ownerId != null ? String(row.ownerId) : void 0,
|
|
3429
3431
|
leaseExpiresAt: row.leaseExpiresAt ? new Date(String(row.leaseExpiresAt)) : void 0,
|
|
3430
3432
|
expiresAt: row.expiresAt ? new Date(String(row.expiresAt)) : void 0,
|
|
3431
|
-
metadata: parseJson$
|
|
3433
|
+
metadata: parseJson$3(row.metadata)
|
|
3432
3434
|
};
|
|
3433
3435
|
}
|
|
3434
3436
|
var BackgroundTasksLibSQL = class BackgroundTasksLibSQL extends _mastra_core_storage.BackgroundTasksStorage {
|
|
@@ -6395,10 +6397,10 @@ var FavoritesLibSQL = class extends _mastra_core_storage.FavoritesStorage {
|
|
|
6395
6397
|
}
|
|
6396
6398
|
};
|
|
6397
6399
|
// src/storage/domains/harness/index.ts
|
|
6398
|
-
const toDate
|
|
6400
|
+
const toDate = (value) => new Date(value);
|
|
6399
6401
|
const toOptionalDate = (value) => {
|
|
6400
6402
|
if (value == null) return void 0;
|
|
6401
|
-
return toDate
|
|
6403
|
+
return toDate(value);
|
|
6402
6404
|
};
|
|
6403
6405
|
function cloneJson(value) {
|
|
6404
6406
|
return value === void 0 ? void 0 : structuredClone(value);
|
|
@@ -6409,8 +6411,8 @@ function hydratePendingItem(item) {
|
|
|
6409
6411
|
kind: item.kind,
|
|
6410
6412
|
status: item.status,
|
|
6411
6413
|
sessionId: item.sessionId,
|
|
6412
|
-
createdAt: toDate
|
|
6413
|
-
updatedAt: toDate
|
|
6414
|
+
createdAt: toDate(item.createdAt),
|
|
6415
|
+
updatedAt: toDate(item.updatedAt)
|
|
6414
6416
|
};
|
|
6415
6417
|
if (item.runId !== void 0) record.runId = item.runId;
|
|
6416
6418
|
if (item.traceId !== void 0) record.traceId = item.traceId;
|
|
@@ -6428,8 +6430,8 @@ function rowToSession(row) {
|
|
|
6428
6430
|
origin: row.origin,
|
|
6429
6431
|
modeId: row.modeId,
|
|
6430
6432
|
modelId: row.modelId,
|
|
6431
|
-
createdAt: toDate
|
|
6432
|
-
lastActivityAt: toDate
|
|
6433
|
+
createdAt: toDate(row.createdAt),
|
|
6434
|
+
lastActivityAt: toDate(row.lastActivityAt)
|
|
6433
6435
|
};
|
|
6434
6436
|
if (row.parentSessionId != null) record.parentSessionId = row.parentSessionId;
|
|
6435
6437
|
if (row.subagentDepth != null) record.subagentDepth = row.subagentDepth;
|
|
@@ -6538,1973 +6540,1612 @@ var HarnessLibSQL = class HarnessLibSQL extends _mastra_core_storage.HarnessStor
|
|
|
6538
6540
|
})).map(rowToSession);
|
|
6539
6541
|
}
|
|
6540
6542
|
};
|
|
6541
|
-
// ../../packages/_internal-core/dist/knowledge-compat/index.js
|
|
6542
|
-
const KNOWLEDGE_V2_CORE_FEATURE = "knowledge-v2";
|
|
6543
|
-
/** Hard cap on scope nodes returned by one `listScopeNodes` read. */
|
|
6544
|
-
const MAX_KNOWLEDGE_SCOPE_NODES = 1e3;
|
|
6545
|
-
function knowledgeScopeNodeFilterKey(input) {
|
|
6546
|
-
return JSON.stringify([
|
|
6547
|
-
input.withinAddress ?? null,
|
|
6548
|
-
input.addresses ? [...input.addresses].sort() : null,
|
|
6549
|
-
input.ids ? [...input.ids].sort() : null
|
|
6550
|
-
]);
|
|
6551
|
-
}
|
|
6552
|
-
function createKnowledgeScopeNodeCursor(scope, input) {
|
|
6553
|
-
return encodeURIComponent(JSON.stringify({
|
|
6554
|
-
version: 1,
|
|
6555
|
-
type: "scope",
|
|
6556
|
-
name: scope.name,
|
|
6557
|
-
id: scope.id,
|
|
6558
|
-
filter: knowledgeScopeNodeFilterKey(input)
|
|
6559
|
-
}));
|
|
6560
|
-
}
|
|
6561
|
-
/** Validates the page size and cursor of a `listScopeNodes` query; throws on a cursor from a different query. */
|
|
6562
|
-
function parseListKnowledgeScopeNodesInput(input = {}) {
|
|
6563
|
-
const requested = Number.isFinite(input.limit) ? Math.trunc(input.limit) : MAX_KNOWLEDGE_SCOPE_NODES;
|
|
6564
|
-
const limit = Math.min(Math.max(requested, 1), MAX_KNOWLEDGE_SCOPE_NODES);
|
|
6565
|
-
if (!input.cursor) return {
|
|
6566
|
-
limit,
|
|
6567
|
-
after: null
|
|
6568
|
-
};
|
|
6569
|
-
let value;
|
|
6570
|
-
try {
|
|
6571
|
-
value = JSON.parse(decodeURIComponent(input.cursor));
|
|
6572
|
-
} catch {
|
|
6573
|
-
throw new Error("Invalid Knowledge scope node cursor.");
|
|
6574
|
-
}
|
|
6575
|
-
const parsed = value && typeof value === "object" ? value : {};
|
|
6576
|
-
if (parsed.version !== 1 || parsed.type !== "scope" || typeof parsed.name !== "string" || typeof parsed.id !== "string" || parsed.filter !== knowledgeScopeNodeFilterKey(input)) throw new Error("Knowledge scope node cursor does not match this query.");
|
|
6577
|
-
return {
|
|
6578
|
-
limit,
|
|
6579
|
-
after: {
|
|
6580
|
-
name: parsed.name,
|
|
6581
|
-
id: parsed.id
|
|
6582
|
-
}
|
|
6583
|
-
};
|
|
6584
|
-
}
|
|
6585
|
-
/** Builds a page from up to `limit + 1` name/id-ordered scope nodes. */
|
|
6586
|
-
function pageKnowledgeScopeNodes(rows, limit, input) {
|
|
6587
|
-
const scopes = rows.slice(0, limit);
|
|
6588
|
-
const last = scopes.at(-1);
|
|
6589
|
-
return {
|
|
6590
|
-
scopes,
|
|
6591
|
-
nextCursor: rows.length > limit && last ? createKnowledgeScopeNodeCursor(last, input) : null
|
|
6592
|
-
};
|
|
6593
|
-
}
|
|
6594
|
-
/** Clamps a requested member limit to 1–{@link MAX_KNOWLEDGE_SCOPE_MEMBERS_LIMIT}. */
|
|
6595
|
-
function knowledgeScopeMembersLimit(limit) {
|
|
6596
|
-
if (limit === void 0 || !Number.isFinite(limit)) return 500;
|
|
6597
|
-
return Math.min(Math.max(Math.trunc(limit), 1), 500);
|
|
6598
|
-
}
|
|
6599
|
-
/** Validates the page size and cursor of a `listScopeMembers` query; throws on a cursor from another scope. */
|
|
6600
|
-
function parseListKnowledgeScopeMembersInput(input) {
|
|
6601
|
-
const limit = knowledgeScopeMembersLimit(input.limit);
|
|
6602
|
-
if (!input.cursor) return {
|
|
6603
|
-
limit,
|
|
6604
|
-
after: null
|
|
6605
|
-
};
|
|
6606
|
-
let value;
|
|
6607
|
-
try {
|
|
6608
|
-
value = JSON.parse(decodeURIComponent(input.cursor));
|
|
6609
|
-
} catch {
|
|
6610
|
-
throw new Error("Invalid Knowledge scope member cursor.");
|
|
6611
|
-
}
|
|
6612
|
-
const parsed = value && typeof value === "object" ? value : {};
|
|
6613
|
-
const updatedAt = typeof parsed.updatedAt === "string" ? new Date(parsed.updatedAt) : /* @__PURE__ */ new Date(NaN);
|
|
6614
|
-
if (parsed.version !== 1 || parsed.type !== "scope-member" || parsed.scopeNodeId !== input.scopeNodeId || typeof parsed.name !== "string" || typeof parsed.id !== "string" || Number.isNaN(updatedAt.getTime())) throw new Error("Knowledge scope member cursor does not match this query.");
|
|
6615
|
-
return {
|
|
6616
|
-
limit,
|
|
6617
|
-
after: {
|
|
6618
|
-
updatedAt,
|
|
6619
|
-
name: parsed.name,
|
|
6620
|
-
id: parsed.id
|
|
6621
|
-
}
|
|
6622
|
-
};
|
|
6623
|
-
}
|
|
6624
|
-
/** Builds a page from up to `limit + 1` members in `listScopeMembers` order. */
|
|
6625
|
-
function pageKnowledgeScopeMembers(rows, limit, input) {
|
|
6626
|
-
const members = rows.slice(0, limit);
|
|
6627
|
-
const last = members.at(-1);
|
|
6628
|
-
const hasMore = rows.length > limit;
|
|
6629
|
-
return {
|
|
6630
|
-
members,
|
|
6631
|
-
hasMore,
|
|
6632
|
-
nextCursor: hasMore && last ? encodeURIComponent(JSON.stringify({
|
|
6633
|
-
version: 1,
|
|
6634
|
-
type: "scope-member",
|
|
6635
|
-
scopeNodeId: input.scopeNodeId,
|
|
6636
|
-
updatedAt: last.updatedAt.toISOString(),
|
|
6637
|
-
name: last.name,
|
|
6638
|
-
id: last.id
|
|
6639
|
-
})) : null
|
|
6640
|
-
};
|
|
6641
|
-
}
|
|
6642
|
-
const TABLE_KNOWLEDGE_NODES$1 = "mastra_knowledge_nodes";
|
|
6643
|
-
const TABLE_KNOWLEDGE_RECORDS$1 = "mastra_knowledge_records";
|
|
6644
|
-
const TABLE_KNOWLEDGE_MENTIONS$1 = "mastra_knowledge_mentions";
|
|
6645
|
-
const TABLE_KNOWLEDGE_ACTIVITY$1 = "mastra_knowledge_activity";
|
|
6646
|
-
const TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1 = "mastra_knowledge_semantic_outbox";
|
|
6647
|
-
const TABLE_KNOWLEDGE_NODE_SCOPES = "mastra_knowledge_node_scopes";
|
|
6648
|
-
const TABLE_KNOWLEDGE_RECORD_SCOPES = "mastra_knowledge_record_scopes";
|
|
6649
|
-
const TABLE_KNOWLEDGE_SCOPE_GRANTS = "mastra_knowledge_scope_grants";
|
|
6650
|
-
const TABLE_KNOWLEDGE_ACCESS_STATE = "mastra_knowledge_access_state";
|
|
6651
|
-
const TABLE_KNOWLEDGE_SCOPE_ADDRESSES = "mastra_knowledge_scope_addresses";
|
|
6652
|
-
const TABLE_KNOWLEDGE_NODE_ADDRESSES = "mastra_knowledge_node_addresses";
|
|
6653
|
-
const TABLE_KNOWLEDGE_IMPORT_STATE = "mastra_knowledge_import_state";
|
|
6654
|
-
const TABLE_KNOWLEDGE_IMPORT_RUNS = "mastra_knowledge_import_runs";
|
|
6655
|
-
const TABLE_KNOWLEDGE_PROPOSALS = "mastra_knowledge_proposals";
|
|
6656
6543
|
const KNOWLEDGE_TABLE_NAMES = [
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6544
|
+
"mastra_knowledge_nodes",
|
|
6545
|
+
"mastra_knowledge_records",
|
|
6546
|
+
"mastra_knowledge_mentions",
|
|
6547
|
+
"mastra_knowledge_activity",
|
|
6548
|
+
"mastra_knowledge_semantic_outbox",
|
|
6549
|
+
"mastra_knowledge_node_scopes",
|
|
6550
|
+
"mastra_knowledge_record_scopes",
|
|
6551
|
+
"mastra_knowledge_scope_grants",
|
|
6552
|
+
"mastra_knowledge_access_state",
|
|
6553
|
+
"mastra_knowledge_scope_addresses",
|
|
6554
|
+
"mastra_knowledge_node_addresses",
|
|
6555
|
+
"mastra_knowledge_import_state",
|
|
6556
|
+
"mastra_knowledge_import_runs",
|
|
6557
|
+
"mastra_knowledge_proposals"
|
|
6671
6558
|
];
|
|
6672
6559
|
/** Knowledge v1 tables with no v2 equivalent; an explicit schema reset drops them. */
|
|
6673
6560
|
const RETIRED_KNOWLEDGE_TABLE_NAMES = ["mastra_knowledge_cursors"];
|
|
6674
|
-
|
|
6675
|
-
const
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6561
|
+
const KNOWLEDGE_PAGES_CORE_FEATURE = "knowledge-pages";
|
|
6562
|
+
const TABLE_KNOWLEDGE_PAGES = "mastra_knowledge_pages";
|
|
6563
|
+
const TABLE_KNOWLEDGE_NOTES = "mastra_knowledge_notes";
|
|
6564
|
+
const TABLE_KNOWLEDGE_NOTE_STAMPS = "mastra_knowledge_note_stamps";
|
|
6565
|
+
/** Same name as the schema-2 table; schema 4 re-keys it to note → page. */
|
|
6566
|
+
const TABLE_KNOWLEDGE_PAGE_MENTIONS = "mastra_knowledge_mentions";
|
|
6567
|
+
const TABLE_KNOWLEDGE_PINS = "mastra_knowledge_pins";
|
|
6568
|
+
/** Same name as schema 2; schema 4 re-keys it to scope/page/note. */
|
|
6569
|
+
const TABLE_KNOWLEDGE_PAGE_ACTIVITY = "mastra_knowledge_activity";
|
|
6570
|
+
/** Holds `{ id: 'knowledge', schemaVersion: 4 }`, written when the schema-4 tables are created. */
|
|
6571
|
+
const TABLE_KNOWLEDGE_PAGES_STATE = "mastra_knowledge_access_state";
|
|
6572
|
+
const KNOWLEDGE_PAGES_TABLE_NAMES = [
|
|
6573
|
+
TABLE_KNOWLEDGE_PAGES,
|
|
6574
|
+
TABLE_KNOWLEDGE_NOTES,
|
|
6575
|
+
TABLE_KNOWLEDGE_NOTE_STAMPS,
|
|
6576
|
+
TABLE_KNOWLEDGE_PAGE_MENTIONS,
|
|
6577
|
+
TABLE_KNOWLEDGE_PINS,
|
|
6578
|
+
TABLE_KNOWLEDGE_PAGE_ACTIVITY,
|
|
6579
|
+
TABLE_KNOWLEDGE_PAGES_STATE
|
|
6580
|
+
];
|
|
6581
|
+
/** Every Knowledge table any schema version created. Reset drops these and nothing else. */
|
|
6582
|
+
const KNOWLEDGE_RESETTABLE_TABLE_NAMES = [.../* @__PURE__ */ new Set([
|
|
6583
|
+
...KNOWLEDGE_PAGES_TABLE_NAMES,
|
|
6584
|
+
...KNOWLEDGE_TABLE_NAMES,
|
|
6681
6585
|
...RETIRED_KNOWLEDGE_TABLE_NAMES
|
|
6682
|
-
]);
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
metadata: {
|
|
6712
|
-
type: "jsonb",
|
|
6713
|
-
nullable: true
|
|
6714
|
-
},
|
|
6715
|
-
version: {
|
|
6716
|
-
type: "integer",
|
|
6717
|
-
nullable: false
|
|
6718
|
-
},
|
|
6719
|
-
createdAt: {
|
|
6720
|
-
type: "timestamp",
|
|
6721
|
-
nullable: false
|
|
6722
|
-
},
|
|
6723
|
-
updatedAt: {
|
|
6724
|
-
type: "timestamp",
|
|
6725
|
-
nullable: false
|
|
6726
|
-
},
|
|
6727
|
-
deletedAt: {
|
|
6728
|
-
type: "timestamp",
|
|
6729
|
-
nullable: true
|
|
6730
|
-
},
|
|
6731
|
-
deletedBy: {
|
|
6732
|
-
type: "text",
|
|
6733
|
-
nullable: true
|
|
6734
|
-
},
|
|
6735
|
-
type: {
|
|
6736
|
-
type: "text",
|
|
6737
|
-
nullable: true
|
|
6738
|
-
},
|
|
6739
|
-
canonicalName: {
|
|
6740
|
-
type: "text",
|
|
6741
|
-
nullable: true
|
|
6742
|
-
},
|
|
6743
|
-
content: {
|
|
6744
|
-
type: "text",
|
|
6745
|
-
nullable: true
|
|
6746
|
-
},
|
|
6747
|
-
description: {
|
|
6748
|
-
type: "text",
|
|
6749
|
-
nullable: true
|
|
6750
|
-
},
|
|
6751
|
-
scope: {
|
|
6752
|
-
type: "jsonb",
|
|
6753
|
-
nullable: true
|
|
6754
|
-
},
|
|
6755
|
-
scopeKey: {
|
|
6756
|
-
type: "text",
|
|
6757
|
-
nullable: true
|
|
6758
|
-
},
|
|
6759
|
-
mergedInto: {
|
|
6760
|
-
type: "text",
|
|
6761
|
-
nullable: true
|
|
6762
|
-
}
|
|
6763
|
-
};
|
|
6764
|
-
const KNOWLEDGE_V2_RECORDS_SCHEMA = {
|
|
6765
|
-
id: {
|
|
6766
|
-
type: "text",
|
|
6767
|
-
nullable: false,
|
|
6768
|
-
primaryKey: true
|
|
6769
|
-
},
|
|
6770
|
-
node: {
|
|
6771
|
-
type: "text",
|
|
6772
|
-
nullable: false,
|
|
6773
|
-
references: {
|
|
6774
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6775
|
-
column: "id"
|
|
6776
|
-
}
|
|
6777
|
-
},
|
|
6778
|
-
text: {
|
|
6779
|
-
type: "text",
|
|
6780
|
-
nullable: false
|
|
6781
|
-
},
|
|
6782
|
-
metadata: {
|
|
6783
|
-
type: "jsonb",
|
|
6784
|
-
nullable: true
|
|
6785
|
-
},
|
|
6786
|
-
version: {
|
|
6787
|
-
type: "integer",
|
|
6788
|
-
nullable: false
|
|
6789
|
-
},
|
|
6790
|
-
capturedAt: {
|
|
6791
|
-
type: "timestamp",
|
|
6792
|
-
nullable: false
|
|
6793
|
-
},
|
|
6794
|
-
updatedAt: {
|
|
6795
|
-
type: "timestamp",
|
|
6796
|
-
nullable: false
|
|
6797
|
-
},
|
|
6798
|
-
deletedAt: {
|
|
6799
|
-
type: "timestamp",
|
|
6800
|
-
nullable: true
|
|
6801
|
-
},
|
|
6802
|
-
deletedBy: {
|
|
6803
|
-
type: "text",
|
|
6804
|
-
nullable: true
|
|
6805
|
-
},
|
|
6806
|
-
scope: {
|
|
6807
|
-
type: "jsonb",
|
|
6808
|
-
nullable: true
|
|
6809
|
-
},
|
|
6810
|
-
scopeKey: {
|
|
6811
|
-
type: "text",
|
|
6812
|
-
nullable: true
|
|
6813
|
-
},
|
|
6814
|
-
sourceThreadId: {
|
|
6815
|
-
type: "text",
|
|
6816
|
-
nullable: true
|
|
6817
|
-
},
|
|
6818
|
-
when: {
|
|
6819
|
-
type: "timestamp",
|
|
6820
|
-
nullable: true
|
|
6821
|
-
}
|
|
6822
|
-
};
|
|
6823
|
-
const KNOWLEDGE_V2_MENTIONS_SCHEMA = {
|
|
6824
|
-
recordId: {
|
|
6825
|
-
type: "text",
|
|
6826
|
-
nullable: false,
|
|
6827
|
-
references: {
|
|
6828
|
-
table: TABLE_KNOWLEDGE_RECORDS$1,
|
|
6829
|
-
column: "id"
|
|
6830
|
-
}
|
|
6831
|
-
},
|
|
6832
|
-
targetNodeId: {
|
|
6833
|
-
type: "text",
|
|
6834
|
-
nullable: true,
|
|
6835
|
-
references: {
|
|
6836
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6837
|
-
column: "id"
|
|
6838
|
-
}
|
|
6839
|
-
},
|
|
6840
|
-
sourceType: {
|
|
6841
|
-
type: "text",
|
|
6842
|
-
nullable: true
|
|
6843
|
-
},
|
|
6844
|
-
sourceId: {
|
|
6845
|
-
type: "text",
|
|
6846
|
-
nullable: true
|
|
6847
|
-
}
|
|
6848
|
-
};
|
|
6849
|
-
const KNOWLEDGE_NODE_SCOPES_SCHEMA = {
|
|
6850
|
-
nodeId: {
|
|
6851
|
-
type: "text",
|
|
6852
|
-
nullable: false,
|
|
6853
|
-
references: {
|
|
6854
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6855
|
-
column: "id"
|
|
6856
|
-
}
|
|
6857
|
-
},
|
|
6858
|
-
scopeNodeId: {
|
|
6859
|
-
type: "text",
|
|
6860
|
-
nullable: false,
|
|
6861
|
-
references: {
|
|
6862
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6863
|
-
column: "id"
|
|
6864
|
-
}
|
|
6865
|
-
},
|
|
6866
|
-
addedAt: {
|
|
6867
|
-
type: "timestamp",
|
|
6868
|
-
nullable: false
|
|
6869
|
-
}
|
|
6870
|
-
};
|
|
6871
|
-
const KNOWLEDGE_RECORD_SCOPES_SCHEMA = {
|
|
6872
|
-
recordId: {
|
|
6873
|
-
type: "text",
|
|
6874
|
-
nullable: false,
|
|
6875
|
-
references: {
|
|
6876
|
-
table: TABLE_KNOWLEDGE_RECORDS$1,
|
|
6877
|
-
column: "id"
|
|
6878
|
-
}
|
|
6879
|
-
},
|
|
6880
|
-
scopeNodeId: {
|
|
6881
|
-
type: "text",
|
|
6882
|
-
nullable: false,
|
|
6883
|
-
references: {
|
|
6884
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6885
|
-
column: "id"
|
|
6886
|
-
}
|
|
6887
|
-
},
|
|
6888
|
-
addedAt: {
|
|
6889
|
-
type: "timestamp",
|
|
6890
|
-
nullable: false
|
|
6891
|
-
}
|
|
6892
|
-
};
|
|
6893
|
-
const KNOWLEDGE_SCOPE_GRANTS_SCHEMA = {
|
|
6894
|
-
scopeNodeId: {
|
|
6895
|
-
type: "text",
|
|
6896
|
-
nullable: false,
|
|
6897
|
-
references: {
|
|
6898
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6899
|
-
column: "id"
|
|
6900
|
-
}
|
|
6901
|
-
},
|
|
6902
|
-
scopeRefId: {
|
|
6903
|
-
type: "text",
|
|
6904
|
-
nullable: false,
|
|
6905
|
-
references: {
|
|
6906
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6907
|
-
column: "id"
|
|
6908
|
-
}
|
|
6909
|
-
},
|
|
6910
|
-
role: {
|
|
6911
|
-
type: "text",
|
|
6912
|
-
nullable: false
|
|
6913
|
-
},
|
|
6914
|
-
canSuggest: {
|
|
6915
|
-
type: "boolean",
|
|
6916
|
-
nullable: true
|
|
6917
|
-
}
|
|
6918
|
-
};
|
|
6919
|
-
const KNOWLEDGE_ACCESS_STATE_SCHEMA = {
|
|
6920
|
-
id: {
|
|
6921
|
-
type: "text",
|
|
6922
|
-
nullable: false,
|
|
6923
|
-
primaryKey: true
|
|
6924
|
-
},
|
|
6925
|
-
epoch: {
|
|
6926
|
-
type: "integer",
|
|
6927
|
-
nullable: false
|
|
6928
|
-
},
|
|
6929
|
-
schemaVersion: {
|
|
6930
|
-
type: "integer",
|
|
6931
|
-
nullable: false
|
|
6932
|
-
}
|
|
6933
|
-
};
|
|
6934
|
-
const KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA = {
|
|
6935
|
-
address: {
|
|
6936
|
-
type: "text",
|
|
6937
|
-
nullable: false,
|
|
6938
|
-
primaryKey: true
|
|
6939
|
-
},
|
|
6940
|
-
scopeNodeId: {
|
|
6941
|
-
type: "text",
|
|
6942
|
-
nullable: false,
|
|
6943
|
-
references: {
|
|
6944
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6945
|
-
column: "id"
|
|
6946
|
-
}
|
|
6947
|
-
}
|
|
6948
|
-
};
|
|
6949
|
-
const KNOWLEDGE_NODE_ADDRESSES_SCHEMA = {
|
|
6950
|
-
source: {
|
|
6951
|
-
type: "text",
|
|
6952
|
-
nullable: false
|
|
6953
|
-
},
|
|
6954
|
-
address: {
|
|
6955
|
-
type: "text",
|
|
6956
|
-
nullable: false
|
|
6957
|
-
},
|
|
6958
|
-
nodeId: {
|
|
6959
|
-
type: "text",
|
|
6960
|
-
nullable: false,
|
|
6961
|
-
references: {
|
|
6962
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
6963
|
-
column: "id"
|
|
6964
|
-
}
|
|
6965
|
-
}
|
|
6966
|
-
};
|
|
6967
|
-
const KNOWLEDGE_IMPORT_STATE_SCHEMA = {
|
|
6968
|
-
importerId: {
|
|
6969
|
-
type: "text",
|
|
6970
|
-
nullable: false
|
|
6971
|
-
},
|
|
6972
|
-
binding: {
|
|
6973
|
-
type: "text",
|
|
6974
|
-
nullable: false
|
|
6975
|
-
},
|
|
6976
|
-
key: {
|
|
6977
|
-
type: "text",
|
|
6978
|
-
nullable: false
|
|
6979
|
-
},
|
|
6980
|
-
value: {
|
|
6981
|
-
type: "text",
|
|
6982
|
-
nullable: false
|
|
6983
|
-
}
|
|
6984
|
-
};
|
|
6985
|
-
const KNOWLEDGE_IMPORT_RUNS_SCHEMA = {
|
|
6986
|
-
id: {
|
|
6987
|
-
type: "text",
|
|
6988
|
-
nullable: false,
|
|
6989
|
-
primaryKey: true
|
|
6990
|
-
},
|
|
6991
|
-
importerId: {
|
|
6992
|
-
type: "text",
|
|
6993
|
-
nullable: false
|
|
6994
|
-
},
|
|
6995
|
-
binding: {
|
|
6996
|
-
type: "text",
|
|
6997
|
-
nullable: false
|
|
6998
|
-
},
|
|
6999
|
-
importKind: {
|
|
7000
|
-
type: "text",
|
|
7001
|
-
nullable: false
|
|
7002
|
-
},
|
|
7003
|
-
triggerKind: {
|
|
7004
|
-
type: "text",
|
|
7005
|
-
nullable: false
|
|
7006
|
-
},
|
|
7007
|
-
status: {
|
|
7008
|
-
type: "text",
|
|
7009
|
-
nullable: false
|
|
7010
|
-
},
|
|
7011
|
-
error: {
|
|
7012
|
-
type: "text",
|
|
7013
|
-
nullable: true
|
|
7014
|
-
},
|
|
7015
|
-
transcriptThreadId: {
|
|
7016
|
-
type: "text",
|
|
7017
|
-
nullable: true
|
|
7018
|
-
},
|
|
7019
|
-
traceId: {
|
|
7020
|
-
type: "text",
|
|
7021
|
-
nullable: true
|
|
7022
|
-
},
|
|
7023
|
-
queuedAt: {
|
|
7024
|
-
type: "timestamp",
|
|
7025
|
-
nullable: false
|
|
7026
|
-
},
|
|
7027
|
-
startedAt: {
|
|
7028
|
-
type: "timestamp",
|
|
7029
|
-
nullable: true
|
|
7030
|
-
},
|
|
7031
|
-
completedAt: {
|
|
7032
|
-
type: "timestamp",
|
|
7033
|
-
nullable: true
|
|
7034
|
-
}
|
|
7035
|
-
};
|
|
7036
|
-
const KNOWLEDGE_V2_ACTIVITY_SCHEMA = {
|
|
7037
|
-
id: {
|
|
7038
|
-
type: "text",
|
|
7039
|
-
nullable: false,
|
|
7040
|
-
primaryKey: true
|
|
7041
|
-
},
|
|
7042
|
-
action: {
|
|
7043
|
-
type: "text",
|
|
7044
|
-
nullable: false
|
|
7045
|
-
},
|
|
7046
|
-
targetType: {
|
|
7047
|
-
type: "text",
|
|
7048
|
-
nullable: true
|
|
7049
|
-
},
|
|
7050
|
-
targetId: {
|
|
7051
|
-
type: "text",
|
|
7052
|
-
nullable: true
|
|
7053
|
-
},
|
|
7054
|
-
contextScopeId: {
|
|
7055
|
-
type: "text",
|
|
7056
|
-
nullable: true,
|
|
7057
|
-
references: {
|
|
7058
|
-
table: TABLE_KNOWLEDGE_NODES$1,
|
|
7059
|
-
column: "id"
|
|
7060
|
-
}
|
|
7061
|
-
},
|
|
7062
|
-
importRunId: {
|
|
7063
|
-
type: "text",
|
|
7064
|
-
nullable: true,
|
|
7065
|
-
references: {
|
|
7066
|
-
table: TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
7067
|
-
column: "id"
|
|
7068
|
-
}
|
|
7069
|
-
},
|
|
7070
|
-
details: {
|
|
7071
|
-
type: "jsonb",
|
|
7072
|
-
nullable: true
|
|
7073
|
-
},
|
|
7074
|
-
createdAt: {
|
|
7075
|
-
type: "timestamp",
|
|
7076
|
-
nullable: false
|
|
7077
|
-
},
|
|
7078
|
-
recordType: {
|
|
7079
|
-
type: "text",
|
|
7080
|
-
nullable: true
|
|
7081
|
-
},
|
|
7082
|
-
recordId: {
|
|
7083
|
-
type: "text",
|
|
7084
|
-
nullable: true
|
|
7085
|
-
},
|
|
7086
|
-
scope: {
|
|
7087
|
-
type: "jsonb",
|
|
7088
|
-
nullable: true
|
|
7089
|
-
},
|
|
7090
|
-
scopeKey: {
|
|
7091
|
-
type: "text",
|
|
7092
|
-
nullable: true
|
|
7093
|
-
},
|
|
7094
|
-
sourceThreadId: {
|
|
7095
|
-
type: "text",
|
|
7096
|
-
nullable: true
|
|
7097
|
-
}
|
|
7098
|
-
};
|
|
7099
|
-
const KNOWLEDGE_PROPOSALS_SCHEMA = {
|
|
7100
|
-
id: {
|
|
7101
|
-
type: "text",
|
|
7102
|
-
nullable: false,
|
|
7103
|
-
primaryKey: true
|
|
7104
|
-
},
|
|
7105
|
-
targetType: {
|
|
7106
|
-
type: "text",
|
|
7107
|
-
nullable: false
|
|
7108
|
-
},
|
|
7109
|
-
targetId: {
|
|
7110
|
-
type: "text",
|
|
7111
|
-
nullable: false
|
|
7112
|
-
},
|
|
7113
|
-
action: {
|
|
7114
|
-
type: "text",
|
|
7115
|
-
nullable: false
|
|
7116
|
-
},
|
|
7117
|
-
changes: {
|
|
7118
|
-
type: "jsonb",
|
|
7119
|
-
nullable: false
|
|
7120
|
-
},
|
|
7121
|
-
reason: {
|
|
7122
|
-
type: "text",
|
|
7123
|
-
nullable: true
|
|
7124
|
-
},
|
|
7125
|
-
proposerContextScopeId: {
|
|
7126
|
-
type: "text",
|
|
7127
|
-
nullable: false
|
|
7128
|
-
},
|
|
7129
|
-
expectedVersion: {
|
|
7130
|
-
type: "integer",
|
|
7131
|
-
nullable: false
|
|
7132
|
-
},
|
|
7133
|
-
status: {
|
|
7134
|
-
type: "text",
|
|
7135
|
-
nullable: false
|
|
7136
|
-
},
|
|
7137
|
-
reviewerContextScopeId: {
|
|
7138
|
-
type: "text",
|
|
7139
|
-
nullable: true
|
|
7140
|
-
},
|
|
7141
|
-
reviewedAt: {
|
|
7142
|
-
type: "timestamp",
|
|
7143
|
-
nullable: true
|
|
7144
|
-
},
|
|
7145
|
-
createdAt: {
|
|
7146
|
-
type: "timestamp",
|
|
7147
|
-
nullable: false
|
|
6586
|
+
])];
|
|
6587
|
+
/** Schema-1/2 tables whose presence means the store needs a reset before schema 4 can run. */
|
|
6588
|
+
const LEGACY_KNOWLEDGE_TABLE_NAMES = new Set([...KNOWLEDGE_TABLE_NAMES, ...RETIRED_KNOWLEDGE_TABLE_NAMES].filter((name) => !KNOWLEDGE_PAGES_TABLE_NAMES.includes(name)));
|
|
6589
|
+
/**
|
|
6590
|
+
* Classifies what an adapter found before creating schema-4 tables. `tableNames` is every
|
|
6591
|
+
* `mastra_knowledge_*` table present; `schemaVersion` is the state row's version, if any.
|
|
6592
|
+
*/
|
|
6593
|
+
function inspectKnowledgePagesSchema(snapshot) {
|
|
6594
|
+
const legacy = snapshot.tableNames.filter((name) => LEGACY_KNOWLEDGE_TABLE_NAMES.has(name));
|
|
6595
|
+
if (legacy.length > 0) return {
|
|
6596
|
+
status: "incompatible-reset-required",
|
|
6597
|
+
schemaVersion: snapshot.schemaVersion ?? null,
|
|
6598
|
+
reason: `found Knowledge tables from an earlier schema (${legacy.join(", ")})`
|
|
6599
|
+
};
|
|
6600
|
+
if (snapshot.schemaVersion === 4) return {
|
|
6601
|
+
status: "compatible",
|
|
6602
|
+
schemaVersion: 4
|
|
6603
|
+
};
|
|
6604
|
+
if (snapshot.schemaVersion === void 0) {
|
|
6605
|
+
const present = snapshot.tableNames.filter((name) => KNOWLEDGE_PAGES_TABLE_NAMES.includes(name));
|
|
6606
|
+
if (present.length === 0) return {
|
|
6607
|
+
status: "uninitialized",
|
|
6608
|
+
schemaVersion: null
|
|
6609
|
+
};
|
|
6610
|
+
return {
|
|
6611
|
+
status: "incompatible-reset-required",
|
|
6612
|
+
schemaVersion: null,
|
|
6613
|
+
reason: `found Knowledge tables (${present.join(", ")}) without a schema version`
|
|
6614
|
+
};
|
|
7148
6615
|
}
|
|
7149
|
-
};
|
|
7150
|
-
function assertKnowledgeV2CoreSupport(features) {
|
|
7151
|
-
if (!features.has("knowledge-v2")) throw new Error(`Knowledge v2 requires a @mastra/core release with the "${KNOWLEDGE_V2_CORE_FEATURE}" feature. Upgrade @mastra/core to use Knowledge; other storage domains keep working on this version.`);
|
|
7152
|
-
}
|
|
7153
|
-
function resolveKnowledgeV2Core(storageModule) {
|
|
7154
|
-
if (typeof storageModule !== "object" || storageModule === null) throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
|
|
7155
|
-
const { assertKnowledgeDescriptionWithinBound: assertDescription, assertKnowledgeRecordTextWithinBound: assertRecordText, assertKnowledgeSchemaCompatible: assertCompatible, inspectKnowledgeSchema: inspectSchema } = storageModule;
|
|
7156
|
-
if (typeof assertDescription !== "function" || typeof assertRecordText !== "function" || typeof assertCompatible !== "function" || typeof inspectSchema !== "function") throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
|
|
7157
6616
|
return {
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
inspectKnowledgeSchema: (snapshot) => inspectSchema(snapshot)
|
|
6617
|
+
status: "incompatible-reset-required",
|
|
6618
|
+
schemaVersion: snapshot.schemaVersion,
|
|
6619
|
+
reason: `Knowledge schema version ${snapshot.schemaVersion} is not 4`
|
|
7162
6620
|
};
|
|
7163
6621
|
}
|
|
7164
|
-
|
|
7165
|
-
|
|
6622
|
+
/** Schema-2 table names schema 4 reuses with a different shape. */
|
|
6623
|
+
const REUSED_KNOWLEDGE_TABLE_NAMES = KNOWLEDGE_TABLE_NAMES.filter((name) => KNOWLEDGE_PAGES_TABLE_NAMES.includes(name));
|
|
6624
|
+
/**
|
|
6625
|
+
* The old Knowledge tables an adapter drops on init, rows included: Knowledge before schema 4 was an
|
|
6626
|
+
* experimental store with no migration. Every schema-1/2-only table present is dropped. The table
|
|
6627
|
+
* names schema 4 reuses (mentions, activity, the state table) are dropped too, but only when an old
|
|
6628
|
+
* table is present and no schema-4 state row exists, so they are old-shaped. Schema-4-only tables
|
|
6629
|
+
* are never listed. On a schema-4 store this lists only tables an older process recreated during a
|
|
6630
|
+
* rolling deploy. Drop order is safe for adapters that enforce references.
|
|
6631
|
+
*/
|
|
6632
|
+
function legacyKnowledgeTablesToDrop(snapshot) {
|
|
6633
|
+
const present = new Set(snapshot.tableNames);
|
|
6634
|
+
const legacy = [...LEGACY_KNOWLEDGE_TABLE_NAMES].filter((name) => present.has(name));
|
|
6635
|
+
if (legacy.length === 0) return [];
|
|
6636
|
+
const reused = snapshot.schemaVersion === 4 ? [] : REUSED_KNOWLEDGE_TABLE_NAMES.filter((name) => present.has(name));
|
|
6637
|
+
const drop = /* @__PURE__ */ new Set([...legacy, ...reused]);
|
|
6638
|
+
return [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()].filter((name) => drop.has(name));
|
|
6639
|
+
}
|
|
6640
|
+
const KNOWLEDGE_PAGES_CORE_FUNCTIONS = [
|
|
6641
|
+
"assertKnowledgePageDescriptionWithinBound",
|
|
6642
|
+
"assertKnowledgeNoteTextWithinBound",
|
|
6643
|
+
"canonicalKnowledgeName",
|
|
6644
|
+
"knowledgePositionBetween",
|
|
6645
|
+
"KnowledgeMoveTooLargeError",
|
|
6646
|
+
"KnowledgePageDepthError",
|
|
6647
|
+
"KnowledgePageHasChildrenError",
|
|
6648
|
+
"KnowledgePageKeyConflictError"
|
|
6649
|
+
];
|
|
6650
|
+
const KNOWLEDGE_PAGES_CORE_NUMBERS = [
|
|
6651
|
+
"MAX_KNOWLEDGE_MOVE_PAGES",
|
|
6652
|
+
"MAX_KNOWLEDGE_PAGE_DEPTH",
|
|
6653
|
+
"MAX_KNOWLEDGE_PAGE_LIST_LIMIT"
|
|
6654
|
+
];
|
|
6655
|
+
function resolveKnowledgePagesCore(storageModule) {
|
|
6656
|
+
const module = typeof storageModule === "object" && storageModule !== null ? storageModule : {};
|
|
6657
|
+
const missing = [...KNOWLEDGE_PAGES_CORE_FUNCTIONS.filter((name) => typeof module[name] !== "function"), ...KNOWLEDGE_PAGES_CORE_NUMBERS.filter((name) => typeof module[name] !== "number")];
|
|
6658
|
+
if (missing.length > 0) throw new Error(`@mastra/core advertises Knowledge pages without the required storage API (missing ${missing.join(", ")})`);
|
|
6659
|
+
return module;
|
|
6660
|
+
}
|
|
6661
|
+
/** Loads core's page-store runtime once, after checking core advertises `knowledge-pages`. */
|
|
6662
|
+
function createKnowledgePagesCoreLoader(features, loadStorageModule) {
|
|
6663
|
+
let core;
|
|
7166
6664
|
return async () => {
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
6665
|
+
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.`);
|
|
6666
|
+
core ??= loadStorageModule().then(resolveKnowledgePagesCore).catch((error) => {
|
|
6667
|
+
core = void 0;
|
|
7170
6668
|
throw error;
|
|
7171
6669
|
});
|
|
7172
|
-
return
|
|
6670
|
+
return core;
|
|
7173
6671
|
};
|
|
7174
6672
|
}
|
|
7175
|
-
// src/storage/domains/knowledge/index.ts
|
|
7176
|
-
const loadKnowledgeV2Core = createKnowledgeV2CoreLoader(_mastra_core_features.coreFeatures, () => import("@mastra/core/storage"));
|
|
7177
|
-
async function assertKnowledgeDescriptionWithinBoundCompat(description) {
|
|
7178
|
-
const { assertKnowledgeDescriptionWithinBound } = await loadKnowledgeV2Core();
|
|
7179
|
-
assertKnowledgeDescriptionWithinBound(description);
|
|
7180
|
-
}
|
|
7181
|
-
async function assertKnowledgeRecordTextWithinBoundCompat(text) {
|
|
7182
|
-
const { assertKnowledgeRecordTextWithinBound } = await loadKnowledgeV2Core();
|
|
7183
|
-
assertKnowledgeRecordTextWithinBound(text);
|
|
7184
|
-
}
|
|
7185
|
-
const reconcileChains = /* @__PURE__ */ new Map();
|
|
7186
|
-
const unidentifiedClientReconcileKey = {};
|
|
7187
|
-
function withReconcileLock(key, operation) {
|
|
7188
|
-
const lockKey = typeof key === "string" ? key : unidentifiedClientReconcileKey;
|
|
7189
|
-
const result = (reconcileChains.get(lockKey) ?? Promise.resolve()).then(operation, operation);
|
|
7190
|
-
const tail = result.then(() => void 0, () => void 0);
|
|
7191
|
-
reconcileChains.set(lockKey, tail);
|
|
7192
|
-
tail.finally(() => {
|
|
7193
|
-
if (reconcileChains.get(lockKey) === tail) reconcileChains.delete(lockKey);
|
|
7194
|
-
});
|
|
7195
|
-
return result;
|
|
7196
|
-
}
|
|
7197
6673
|
/**
|
|
7198
|
-
*
|
|
7199
|
-
*
|
|
7200
|
-
* Matching rows by `scopeKey` keeps visibility filters on an index; a caller scope has only a few
|
|
7201
|
-
* entries, so the list stays small.
|
|
6674
|
+
* Keyword-search words: the query split on whitespace, lowercased, deduplicated. A hit needs at
|
|
6675
|
+
* least one of them (as a case-insensitive substring); an empty list means the search returns nothing.
|
|
7202
6676
|
*/
|
|
7203
|
-
function
|
|
7204
|
-
|
|
7205
|
-
const keys = /* @__PURE__ */ new Set();
|
|
7206
|
-
for (let mask = 1; mask < 1 << canonical.length; mask++) try {
|
|
7207
|
-
keys.add((0, _mastra_core_storage.knowledgeScopeKey)(canonical.filter((_, index) => mask & 1 << index)));
|
|
7208
|
-
} catch {}
|
|
7209
|
-
return [...keys];
|
|
6677
|
+
function knowledgeSearchWords(query) {
|
|
6678
|
+
return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
|
|
7210
6679
|
}
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
}
|
|
7221
|
-
|
|
7222
|
-
|
|
6680
|
+
/**
|
|
6681
|
+
* Points one matched search word earns on a page, by the best field it appears in: name, then
|
|
6682
|
+
* description, then body. Pages rank by how many query words they match first; these points only
|
|
6683
|
+
* break ties between pages that match the same number of words.
|
|
6684
|
+
*/
|
|
6685
|
+
const KNOWLEDGE_SEARCH_WEIGHTS = {
|
|
6686
|
+
name: 3,
|
|
6687
|
+
description: 2,
|
|
6688
|
+
body: 1
|
|
6689
|
+
};
|
|
6690
|
+
/**
|
|
6691
|
+
* Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
|
|
6692
|
+
* non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
|
|
6693
|
+
*/
|
|
6694
|
+
function knowledgePageKeyProblem(key) {
|
|
6695
|
+
if (!key) return "Knowledge page key must not be empty";
|
|
6696
|
+
if (/[/#%]/.test(key)) return `Knowledge page key "${key}" must not contain "/", "#" or "%"`;
|
|
7223
6697
|
}
|
|
7224
|
-
|
|
7225
|
-
|
|
6698
|
+
// src/storage/domains/knowledge/pages.ts
|
|
6699
|
+
const loadPagesCore = createKnowledgePagesCoreLoader(_mastra_core_features.coreFeatures, () => import("@mastra/core/storage"));
|
|
6700
|
+
const assertKnowledgePageDescriptionWithinBound = async (description) => (await loadPagesCore()).assertKnowledgePageDescriptionWithinBound(description);
|
|
6701
|
+
const assertKnowledgeNoteTextWithinBound = async (text) => (await loadPagesCore()).assertKnowledgeNoteTextWithinBound(text);
|
|
6702
|
+
const KNOWLEDGE_PAGE_TABLES = {
|
|
6703
|
+
pages: "mastra_knowledge_pages",
|
|
6704
|
+
notes: "mastra_knowledge_notes",
|
|
6705
|
+
stamps: "mastra_knowledge_note_stamps",
|
|
6706
|
+
mentions: "mastra_knowledge_mentions",
|
|
6707
|
+
pins: "mastra_knowledge_pins",
|
|
6708
|
+
pinAudiences: "mastra_knowledge_pin_audiences",
|
|
6709
|
+
activity: "mastra_knowledge_activity"
|
|
6710
|
+
};
|
|
6711
|
+
const T = KNOWLEDGE_PAGE_TABLES;
|
|
6712
|
+
/** DDL for the schema-4 page tables, in creation order. Every statement is idempotent. */
|
|
6713
|
+
const KNOWLEDGE_PAGE_DDL = [
|
|
6714
|
+
`CREATE TABLE IF NOT EXISTS "mastra_knowledge_access_state" (
|
|
6715
|
+
id TEXT PRIMARY KEY,
|
|
6716
|
+
schemaVersion INTEGER NOT NULL
|
|
6717
|
+
)`,
|
|
6718
|
+
`CREATE TABLE IF NOT EXISTS "${T.pages}" (
|
|
6719
|
+
id TEXT PRIMARY KEY,
|
|
6720
|
+
scopeId TEXT NOT NULL,
|
|
6721
|
+
isScope INTEGER NOT NULL,
|
|
6722
|
+
address TEXT,
|
|
6723
|
+
declaration TEXT,
|
|
6724
|
+
key TEXT,
|
|
6725
|
+
parentId TEXT,
|
|
6726
|
+
position TEXT NOT NULL,
|
|
6727
|
+
path TEXT NOT NULL,
|
|
6728
|
+
depth INTEGER NOT NULL,
|
|
6729
|
+
name TEXT NOT NULL,
|
|
6730
|
+
canonicalName TEXT NOT NULL,
|
|
6731
|
+
description TEXT,
|
|
6732
|
+
body TEXT,
|
|
6733
|
+
version INTEGER NOT NULL,
|
|
6734
|
+
mergedInto TEXT,
|
|
6735
|
+
createdBy TEXT NOT NULL,
|
|
6736
|
+
updatedBy TEXT NOT NULL,
|
|
6737
|
+
createdAt TEXT NOT NULL,
|
|
6738
|
+
updatedAt TEXT NOT NULL,
|
|
6739
|
+
deletedAt TEXT,
|
|
6740
|
+
deletedBy TEXT
|
|
6741
|
+
)`,
|
|
6742
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_kpages_address ON "${T.pages}" (address) WHERE isScope = 1`,
|
|
6743
|
+
`DROP INDEX IF EXISTS idx_kpages_key`,
|
|
6744
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_kpages_parent_key ON "${T.pages}" (parentId, key) WHERE key IS NOT NULL AND deletedAt IS NULL`,
|
|
6745
|
+
`CREATE INDEX IF NOT EXISTS idx_kpages_scope ON "${T.pages}" (scopeId)`,
|
|
6746
|
+
`CREATE INDEX IF NOT EXISTS idx_kpages_parent ON "${T.pages}" (parentId, position, id)`,
|
|
6747
|
+
`CREATE INDEX IF NOT EXISTS idx_kpages_path ON "${T.pages}" (path)`,
|
|
6748
|
+
`CREATE INDEX IF NOT EXISTS idx_kpages_declaration_parent ON "${T.pages}" (declaration, parentId) WHERE isScope = 1`,
|
|
6749
|
+
`CREATE INDEX IF NOT EXISTS idx_kpages_canonical ON "${T.pages}" (canonicalName)`,
|
|
6750
|
+
`CREATE TABLE IF NOT EXISTS "${T.notes}" (
|
|
6751
|
+
id TEXT PRIMARY KEY,
|
|
6752
|
+
pageId TEXT NOT NULL,
|
|
6753
|
+
text TEXT NOT NULL,
|
|
6754
|
+
"when" TEXT,
|
|
6755
|
+
provenance TEXT NOT NULL,
|
|
6756
|
+
version INTEGER NOT NULL,
|
|
6757
|
+
createdAt TEXT NOT NULL,
|
|
6758
|
+
updatedAt TEXT NOT NULL,
|
|
6759
|
+
deletedAt TEXT,
|
|
6760
|
+
deletedBy TEXT
|
|
6761
|
+
)`,
|
|
6762
|
+
`CREATE INDEX IF NOT EXISTS idx_knotes_page ON "${T.notes}" (pageId, createdAt, id)`,
|
|
6763
|
+
`CREATE TABLE IF NOT EXISTS "${T.stamps}" (
|
|
6764
|
+
noteId TEXT NOT NULL,
|
|
6765
|
+
scopeId TEXT NOT NULL,
|
|
6766
|
+
PRIMARY KEY (noteId, scopeId)
|
|
6767
|
+
)`,
|
|
6768
|
+
`CREATE INDEX IF NOT EXISTS idx_kstamps_scope ON "${T.stamps}" (scopeId)`,
|
|
6769
|
+
`CREATE TABLE IF NOT EXISTS "${T.mentions}" (
|
|
6770
|
+
noteId TEXT NOT NULL,
|
|
6771
|
+
pageId TEXT NOT NULL,
|
|
6772
|
+
PRIMARY KEY (noteId, pageId)
|
|
6773
|
+
)`,
|
|
6774
|
+
`CREATE INDEX IF NOT EXISTS idx_kmentions_page ON "${T.mentions}" (pageId)`,
|
|
6775
|
+
`CREATE TABLE IF NOT EXISTS "${T.pins}" (
|
|
6776
|
+
id TEXT PRIMARY KEY,
|
|
6777
|
+
pageId TEXT NOT NULL UNIQUE,
|
|
6778
|
+
audience TEXT NOT NULL,
|
|
6779
|
+
createdBy TEXT NOT NULL,
|
|
6780
|
+
createdAt TEXT NOT NULL,
|
|
6781
|
+
updatedAt TEXT NOT NULL
|
|
6782
|
+
)`,
|
|
6783
|
+
`CREATE TABLE IF NOT EXISTS "${T.pinAudiences}" (
|
|
6784
|
+
pinId TEXT NOT NULL,
|
|
6785
|
+
audience TEXT NOT NULL,
|
|
6786
|
+
PRIMARY KEY (audience, pinId)
|
|
6787
|
+
) WITHOUT ROWID`,
|
|
6788
|
+
`CREATE INDEX IF NOT EXISTS idx_kpin_audiences_pin ON "${T.pinAudiences}" (pinId)`,
|
|
6789
|
+
`INSERT OR IGNORE INTO "${T.pinAudiences}" (audience, pinId) SELECT j.value, pin.id FROM "${T.pins}" pin, json_each(pin.audience) j`,
|
|
6790
|
+
`CREATE TABLE IF NOT EXISTS "${T.activity}" (
|
|
6791
|
+
id TEXT PRIMARY KEY,
|
|
6792
|
+
action TEXT NOT NULL,
|
|
6793
|
+
scopeId TEXT NOT NULL,
|
|
6794
|
+
pageId TEXT NOT NULL,
|
|
6795
|
+
noteId TEXT,
|
|
6796
|
+
provenance TEXT NOT NULL,
|
|
6797
|
+
createdAt TEXT NOT NULL
|
|
6798
|
+
)`,
|
|
6799
|
+
`CREATE INDEX IF NOT EXISTS idx_kactivity_page ON "${T.activity}" (pageId, id DESC)`
|
|
6800
|
+
];
|
|
6801
|
+
function str(value) {
|
|
6802
|
+
return value === null || value === void 0 ? void 0 : String(value);
|
|
7226
6803
|
}
|
|
7227
|
-
function
|
|
7228
|
-
return value
|
|
6804
|
+
function date(value) {
|
|
6805
|
+
return value === null || value === void 0 ? void 0 : new Date(String(value));
|
|
7229
6806
|
}
|
|
7230
|
-
function
|
|
7231
|
-
return
|
|
6807
|
+
function provenance(raw) {
|
|
6808
|
+
return JSON.parse(String(raw));
|
|
7232
6809
|
}
|
|
7233
|
-
function
|
|
6810
|
+
function toPage(row) {
|
|
7234
6811
|
return {
|
|
7235
6812
|
id: String(row.id),
|
|
7236
|
-
|
|
6813
|
+
scopeId: String(row.scopeId),
|
|
6814
|
+
parentId: str(row.parentId) ?? null,
|
|
6815
|
+
isScope: Number(row.isScope) === 1,
|
|
6816
|
+
...row.address !== null && row.address !== void 0 ? { address: String(row.address) } : {},
|
|
6817
|
+
...row.declaration !== null && row.declaration !== void 0 ? { declaration: String(row.declaration) } : {},
|
|
6818
|
+
...row.key !== null && row.key !== void 0 ? { key: String(row.key) } : {},
|
|
6819
|
+
position: String(row.position),
|
|
7237
6820
|
name: String(row.name),
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
description: row.description == null ? void 0 : String(row.description),
|
|
7241
|
-
isScope: row.isScope === true || row.isScope === 1 ? true : void 0,
|
|
7242
|
-
scope: parseJson$3(row.scopeJson ?? row.scope),
|
|
6821
|
+
...row.description !== null && row.description !== void 0 ? { description: String(row.description) } : {},
|
|
6822
|
+
...row.body !== null && row.body !== void 0 ? { body: String(row.body) } : {},
|
|
7243
6823
|
version: Number(row.version),
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
6824
|
+
...row.mergedInto !== null && row.mergedInto !== void 0 ? { mergedInto: String(row.mergedInto) } : {},
|
|
6825
|
+
createdBy: provenance(row.createdBy),
|
|
6826
|
+
updatedBy: provenance(row.updatedBy),
|
|
6827
|
+
createdAt: new Date(String(row.createdAt)),
|
|
6828
|
+
updatedAt: new Date(String(row.updatedAt)),
|
|
6829
|
+
...row.deletedAt ? { deletedAt: new Date(String(row.deletedAt)) } : {},
|
|
6830
|
+
...row.deletedBy ? { deletedBy: String(row.deletedBy) } : {}
|
|
7247
6831
|
};
|
|
7248
6832
|
}
|
|
7249
|
-
function
|
|
6833
|
+
function toNote(row, stamps, mentions) {
|
|
6834
|
+
const when = date(row.when);
|
|
7250
6835
|
return {
|
|
7251
6836
|
id: String(row.id),
|
|
7252
|
-
|
|
6837
|
+
pageId: String(row.pageId),
|
|
7253
6838
|
text: String(row.text),
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
6839
|
+
...when ? { when } : {},
|
|
6840
|
+
stampScopeIds: stamps,
|
|
6841
|
+
mentions,
|
|
6842
|
+
provenance: provenance(row.provenance),
|
|
6843
|
+
version: Number(row.version),
|
|
6844
|
+
createdAt: new Date(String(row.createdAt)),
|
|
6845
|
+
updatedAt: new Date(String(row.updatedAt)),
|
|
6846
|
+
...row.deletedAt ? { deletedAt: new Date(String(row.deletedAt)) } : {},
|
|
6847
|
+
...row.deletedBy ? { deletedBy: String(row.deletedBy) } : {}
|
|
7261
6848
|
};
|
|
7262
6849
|
}
|
|
7263
|
-
function
|
|
6850
|
+
function toPin(row) {
|
|
7264
6851
|
return {
|
|
7265
6852
|
id: String(row.id),
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
status: String(row.status),
|
|
7272
|
-
attempts: Number(row.attempts),
|
|
7273
|
-
availableAt: toDate(row.availableAt),
|
|
7274
|
-
claimedAt: optionalDate(row.claimedAt),
|
|
7275
|
-
claimedBy: row.claimedBy == null ? void 0 : String(row.claimedBy),
|
|
7276
|
-
createdAt: toDate(row.createdAt),
|
|
7277
|
-
completedAt: optionalDate(row.completedAt)
|
|
6853
|
+
pageId: String(row.pageId),
|
|
6854
|
+
audience: JSON.parse(String(row.audience)),
|
|
6855
|
+
createdBy: String(row.createdBy),
|
|
6856
|
+
createdAt: new Date(String(row.createdAt)),
|
|
6857
|
+
updatedAt: new Date(String(row.updatedAt))
|
|
7278
6858
|
};
|
|
7279
6859
|
}
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
[TABLE_KNOWLEDGE_IMPORT_RUNS, KNOWLEDGE_IMPORT_RUNS_SCHEMA],
|
|
7295
|
-
[TABLE_KNOWLEDGE_PROPOSALS, KNOWLEDGE_PROPOSALS_SCHEMA]
|
|
7296
|
-
]);
|
|
7297
|
-
function canonicalizeLibSQLUrl(url) {
|
|
7298
|
-
if (url.includes(":memory:")) return /[?&]cache=shared(?:&|$)/.test(url) ? "file::memory:?cache=shared" : void 0;
|
|
7299
|
-
if (url.startsWith("file:")) {
|
|
7300
|
-
const [path$1] = url.slice(5).split("?");
|
|
7301
|
-
if (url.startsWith("file://")) {
|
|
7302
|
-
const parsed = new URL(url);
|
|
7303
|
-
const host = parsed.hostname.toLocaleLowerCase();
|
|
7304
|
-
const filePath = host && host !== "localhost" ? `//${host}${parsed.pathname}` : parsed.pathname;
|
|
7305
|
-
return `file:${(0, path.resolve)(decodeURIComponent(filePath))}`;
|
|
7306
|
-
}
|
|
7307
|
-
return `file:${(0, path.resolve)(decodeURIComponent(path$1))}`;
|
|
7308
|
-
}
|
|
6860
|
+
function toListedPin(row) {
|
|
6861
|
+
return {
|
|
6862
|
+
...toPin(row),
|
|
6863
|
+
pageName: String(row.pageName)
|
|
6864
|
+
};
|
|
6865
|
+
}
|
|
6866
|
+
function limitOf(limit, max) {
|
|
6867
|
+
return Math.max(1, Math.min(limit ?? max, max));
|
|
6868
|
+
}
|
|
6869
|
+
function encodeCursor(value) {
|
|
6870
|
+
return Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
6871
|
+
}
|
|
6872
|
+
function decodeCursor(cursor, length) {
|
|
6873
|
+
if (cursor === void 0) return void 0;
|
|
7309
6874
|
try {
|
|
7310
|
-
const parsed =
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
6875
|
+
const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
6876
|
+
if (Array.isArray(parsed) && parsed.length === length && parsed.every((item) => typeof item === "string")) return parsed;
|
|
6877
|
+
} catch {}
|
|
6878
|
+
throw new Error("Invalid Knowledge cursor");
|
|
6879
|
+
}
|
|
6880
|
+
function escapeLike(value) {
|
|
6881
|
+
return value.replace(/[=%_]/g, (match) => `=${match}`);
|
|
6882
|
+
}
|
|
6883
|
+
/**
|
|
6884
|
+
* A string above every string that starts with `prefix`, comparing by code point (which is also
|
|
6885
|
+
* UTF-8 byte order, so it matches SQLite's BINARY collation). `column >= prefix AND column < bound`
|
|
6886
|
+
* is an exact, case-sensitive prefix match that an index range scan can serve, where
|
|
6887
|
+
* `substr(column, 1, length(prefix)) = prefix` cannot. When the last code point cannot be raised
|
|
6888
|
+
* (an empty prefix, or one made only of U+10FFFF), the bound is the prefix plus more U+10FFFF than
|
|
6889
|
+
* any stored column can hold.
|
|
6890
|
+
*/
|
|
6891
|
+
function prefixUpperBound(prefix) {
|
|
6892
|
+
const points = Array.from(prefix);
|
|
6893
|
+
while (points.length > 0) {
|
|
6894
|
+
const last = points.pop().codePointAt(0);
|
|
6895
|
+
if (last < 1114111) {
|
|
6896
|
+
const next = last === 55295 ? 57344 : last + 1;
|
|
6897
|
+
return points.join("") + String.fromCodePoint(next);
|
|
6898
|
+
}
|
|
7315
6899
|
}
|
|
6900
|
+
return prefix + "".repeat(4096);
|
|
7316
6901
|
}
|
|
7317
|
-
|
|
7318
|
-
function
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
6902
|
+
/** `column` starts with `prefix`, as an index-friendly range. */
|
|
6903
|
+
function prefixRange(column, prefix) {
|
|
6904
|
+
return {
|
|
6905
|
+
sql: `(${column} >= ? AND ${column} < ?)`,
|
|
6906
|
+
args: [prefix, prefixUpperBound(prefix)]
|
|
6907
|
+
};
|
|
7323
6908
|
}
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
6909
|
+
/**
|
|
6910
|
+
* The readable scope page ids as a subquery. The caller's addresses, prefixes and children go in as
|
|
6911
|
+
* JSON arrays so a large read scope never hits a bind-parameter limit. Each kind is its own index
|
|
6912
|
+
* lookup (address equality, address range for a prefix, `(declaration, parentId)` for children),
|
|
6913
|
+
* unioned, so the cost follows the caller's reach rather than the number of scopes in the store.
|
|
6914
|
+
*/
|
|
6915
|
+
function readableScopes(read) {
|
|
6916
|
+
if (read === "all") return {
|
|
6917
|
+
sql: `SELECT id FROM "${T.pages}" WHERE isScope = 1`,
|
|
6918
|
+
args: []
|
|
6919
|
+
};
|
|
6920
|
+
const parts = [];
|
|
6921
|
+
const args = [];
|
|
6922
|
+
if (read.addresses.length > 0) {
|
|
6923
|
+
parts.push(`SELECT id FROM "${T.pages}" WHERE isScope = 1 AND address IN (SELECT value FROM json_each(?))`);
|
|
6924
|
+
args.push(JSON.stringify(read.addresses));
|
|
6925
|
+
}
|
|
6926
|
+
if (read.prefixes.length > 0) {
|
|
6927
|
+
parts.push(`SELECT sp.id FROM json_each(?) j CROSS JOIN "${T.pages}" sp ON sp.isScope = 1 AND sp.address >= json_extract(j.value, '$.lo') AND sp.address < json_extract(j.value, '$.hi') AND +sp.declaration = json_extract(j.value, '$.declaration')`);
|
|
6928
|
+
args.push(JSON.stringify(read.prefixes.map((entry) => ({
|
|
6929
|
+
declaration: entry.declaration,
|
|
6930
|
+
lo: entry.prefix,
|
|
6931
|
+
hi: prefixUpperBound(entry.prefix)
|
|
6932
|
+
}))));
|
|
6933
|
+
}
|
|
6934
|
+
if (read.children.length > 0) {
|
|
6935
|
+
parts.push(`SELECT sp.id FROM json_each(?) j CROSS JOIN "${T.pages}" sp ON sp.isScope = 1 AND sp.declaration = json_extract(j.value, '$.declaration') AND sp.parentId IS json_extract(j.value, '$.parentId')`);
|
|
6936
|
+
args.push(JSON.stringify(read.children));
|
|
6937
|
+
}
|
|
6938
|
+
if (parts.length === 0) return {
|
|
6939
|
+
sql: `SELECT id FROM "${T.pages}" WHERE 0`,
|
|
6940
|
+
args: []
|
|
6941
|
+
};
|
|
6942
|
+
return {
|
|
6943
|
+
sql: parts.join(" UNION "),
|
|
6944
|
+
args
|
|
6945
|
+
};
|
|
6946
|
+
}
|
|
6947
|
+
/** `alias.scopeId` is readable. */
|
|
6948
|
+
function pageReadable(alias, read) {
|
|
6949
|
+
const scopes = readableScopes(read);
|
|
6950
|
+
return {
|
|
6951
|
+
sql: `${alias}.scopeId IN (${scopes.sql})`,
|
|
6952
|
+
args: scopes.args
|
|
6953
|
+
};
|
|
6954
|
+
}
|
|
6955
|
+
/**
|
|
6956
|
+
* Note `alias` is visible: its page's scope is readable, a stamp is readable, and every resolved
|
|
6957
|
+
* mention's page sits in a readable scope.
|
|
6958
|
+
*/
|
|
6959
|
+
function noteReadable(alias, read) {
|
|
6960
|
+
const scopes = readableScopes(read);
|
|
6961
|
+
return {
|
|
6962
|
+
sql: `EXISTS (SELECT 1 FROM "${T.pages}" vp WHERE vp.id = ${alias}.pageId AND vp.scopeId IN (${scopes.sql}))
|
|
6963
|
+
AND EXISTS (SELECT 1 FROM "${T.stamps}" vs WHERE vs.noteId = ${alias}.id AND vs.scopeId IN (${scopes.sql}))
|
|
6964
|
+
AND NOT EXISTS (SELECT 1 FROM "${T.mentions}" vm JOIN "${T.pages}" vmp ON vmp.id = vm.pageId WHERE vm.noteId = ${alias}.id AND vmp.scopeId NOT IN (${scopes.sql}))`,
|
|
6965
|
+
args: [
|
|
6966
|
+
...scopes.args,
|
|
6967
|
+
...scopes.args,
|
|
6968
|
+
...scopes.args
|
|
6969
|
+
]
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
function jsonList(values) {
|
|
6973
|
+
return JSON.stringify(values);
|
|
6974
|
+
}
|
|
6975
|
+
var LibSQLKnowledgePages = class {
|
|
6976
|
+
#read;
|
|
6977
|
+
#transaction;
|
|
7327
6978
|
constructor(config) {
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
6979
|
+
this.#read = config.read;
|
|
6980
|
+
this.#transaction = config.transaction;
|
|
6981
|
+
}
|
|
6982
|
+
/** Deletes every row from the page tables (scopes, pages, notes, pins, activity). */
|
|
6983
|
+
async clearAll() {
|
|
6984
|
+
await this.#transaction(async (tx) => {
|
|
6985
|
+
for (const table of [
|
|
6986
|
+
T.activity,
|
|
6987
|
+
T.pinAudiences,
|
|
6988
|
+
T.pins,
|
|
6989
|
+
T.mentions,
|
|
6990
|
+
T.stamps,
|
|
6991
|
+
T.notes,
|
|
6992
|
+
T.pages
|
|
6993
|
+
]) await tx.execute({
|
|
6994
|
+
sql: `DELETE FROM ${table}`,
|
|
6995
|
+
args: []
|
|
6996
|
+
});
|
|
7336
6997
|
});
|
|
7337
6998
|
}
|
|
7338
|
-
|
|
7339
|
-
return {
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
supportsSchemaInspection: true,
|
|
7344
|
-
supportsExplicitReset: true
|
|
7345
|
-
};
|
|
6999
|
+
async #query(executor, sql, args = []) {
|
|
7000
|
+
return (await executor.execute({
|
|
7001
|
+
sql,
|
|
7002
|
+
args
|
|
7003
|
+
})).rows;
|
|
7346
7004
|
}
|
|
7347
|
-
async
|
|
7348
|
-
return this.#
|
|
7005
|
+
async #pageRow(executor, id) {
|
|
7006
|
+
return (await this.#query(executor, `SELECT * FROM "${T.pages}" WHERE id = ?`, [id]))[0];
|
|
7349
7007
|
}
|
|
7350
|
-
async #
|
|
7351
|
-
const
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
const tableNames = (await executor.execute({
|
|
7355
|
-
sql: `SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders})`,
|
|
7356
|
-
args: [...KNOWLEDGE_TABLE_NAMES]
|
|
7357
|
-
})).rows.map((row) => String(row.name));
|
|
7358
|
-
if (tableNames.length === 0) return inspectKnowledgeSchema({
|
|
7359
|
-
available: true,
|
|
7360
|
-
tableNames
|
|
7361
|
-
});
|
|
7362
|
-
const missingTables = KNOWLEDGE_TABLE_NAMES.filter((table) => !tableNames.includes(table));
|
|
7363
|
-
if (missingTables.length > 0) return inspectKnowledgeSchema({
|
|
7364
|
-
available: true,
|
|
7365
|
-
tableNames,
|
|
7366
|
-
reason: `Missing Knowledge v2 tables: ${missingTables.join(", ")}`
|
|
7367
|
-
});
|
|
7368
|
-
for (const [table, schema] of knowledgeV2TableSchemas) {
|
|
7369
|
-
const columns = await executor.execute(`PRAGMA table_info("${table}")`);
|
|
7370
|
-
const actual = new Set(columns.rows.map((row) => String(row.name)));
|
|
7371
|
-
const missing = Object.keys(schema).filter((column) => !actual.has(column));
|
|
7372
|
-
if (missing.length > 0) return inspectKnowledgeSchema({
|
|
7373
|
-
available: true,
|
|
7374
|
-
tableNames,
|
|
7375
|
-
reason: `Knowledge table ${table} is missing v2 columns: ${missing.join(", ")}`
|
|
7376
|
-
});
|
|
7377
|
-
}
|
|
7378
|
-
const accessState = await executor.execute(`SELECT schemaVersion FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
|
|
7379
|
-
if (Number(accessState.rows[0]?.schemaVersion) !== 2) return inspectKnowledgeSchema({
|
|
7380
|
-
available: true,
|
|
7381
|
-
tableNames,
|
|
7382
|
-
reason: "Knowledge schema version marker is missing or incompatible"
|
|
7383
|
-
});
|
|
7384
|
-
return inspectKnowledgeSchema({
|
|
7385
|
-
available: true,
|
|
7386
|
-
tableNames,
|
|
7387
|
-
schemaVersion: 2
|
|
7388
|
-
});
|
|
7389
|
-
} catch (error) {
|
|
7390
|
-
return inspectKnowledgeSchema({
|
|
7391
|
-
available: false,
|
|
7392
|
-
tableNames: [],
|
|
7393
|
-
reason: error instanceof Error ? error.message : String(error)
|
|
7394
|
-
});
|
|
7395
|
-
}
|
|
7008
|
+
async #livePage(executor, id) {
|
|
7009
|
+
const row = await this.#pageRow(executor, id);
|
|
7010
|
+
if (!row || row.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("page", id);
|
|
7011
|
+
return row;
|
|
7396
7012
|
}
|
|
7397
|
-
async
|
|
7398
|
-
await this.#transaction((tx) => this.#initializeSchema(tx));
|
|
7399
|
-
}
|
|
7400
|
-
async #initializeSchema(tx) {
|
|
7401
|
-
const { assertKnowledgeSchemaCompatible } = await loadKnowledgeV2Core();
|
|
7402
|
-
let inspection = await this.#inspectSchema(tx);
|
|
7403
|
-
if (inspection.status === "incompatible-reset-required" && await this.#isPublishedV1Layout(tx)) {
|
|
7404
|
-
for (const table of [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()]) await tx.execute(`DROP TABLE IF EXISTS "${table}"`);
|
|
7405
|
-
inspection = await this.#inspectSchema(tx);
|
|
7406
|
-
}
|
|
7407
|
-
assertKnowledgeSchemaCompatible(inspection);
|
|
7408
|
-
const createTable = (input) => this.#db.createTable({
|
|
7409
|
-
...input,
|
|
7410
|
-
executor: tx
|
|
7411
|
-
});
|
|
7412
|
-
await createTable({
|
|
7413
|
-
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_NODES,
|
|
7414
|
-
schema: KNOWLEDGE_V2_NODES_SCHEMA
|
|
7415
|
-
});
|
|
7416
|
-
await createTable({
|
|
7417
|
-
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
|
|
7418
|
-
schema: KNOWLEDGE_V2_RECORDS_SCHEMA
|
|
7419
|
-
});
|
|
7420
|
-
await createTable({
|
|
7421
|
-
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS,
|
|
7422
|
-
schema: KNOWLEDGE_V2_MENTIONS_SCHEMA,
|
|
7423
|
-
compositePrimaryKey: ["recordId", "sourceId"]
|
|
7424
|
-
});
|
|
7425
|
-
await createTable({
|
|
7426
|
-
tableName: TABLE_KNOWLEDGE_NODE_SCOPES,
|
|
7427
|
-
schema: KNOWLEDGE_NODE_SCOPES_SCHEMA,
|
|
7428
|
-
compositePrimaryKey: ["nodeId", "scopeNodeId"]
|
|
7429
|
-
});
|
|
7430
|
-
await createTable({
|
|
7431
|
-
tableName: TABLE_KNOWLEDGE_RECORD_SCOPES,
|
|
7432
|
-
schema: KNOWLEDGE_RECORD_SCOPES_SCHEMA,
|
|
7433
|
-
compositePrimaryKey: ["recordId", "scopeNodeId"]
|
|
7434
|
-
});
|
|
7435
|
-
await createTable({
|
|
7436
|
-
tableName: TABLE_KNOWLEDGE_SCOPE_GRANTS,
|
|
7437
|
-
schema: KNOWLEDGE_SCOPE_GRANTS_SCHEMA,
|
|
7438
|
-
compositePrimaryKey: ["scopeNodeId", "scopeRefId"]
|
|
7439
|
-
});
|
|
7440
|
-
await createTable({
|
|
7441
|
-
tableName: TABLE_KNOWLEDGE_ACCESS_STATE,
|
|
7442
|
-
schema: KNOWLEDGE_ACCESS_STATE_SCHEMA
|
|
7443
|
-
});
|
|
7444
|
-
await createTable({
|
|
7445
|
-
tableName: TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
|
|
7446
|
-
schema: KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA
|
|
7447
|
-
});
|
|
7448
|
-
await createTable({
|
|
7449
|
-
tableName: TABLE_KNOWLEDGE_NODE_ADDRESSES,
|
|
7450
|
-
schema: KNOWLEDGE_NODE_ADDRESSES_SCHEMA,
|
|
7451
|
-
compositePrimaryKey: ["source", "address"]
|
|
7452
|
-
});
|
|
7453
|
-
await createTable({
|
|
7454
|
-
tableName: TABLE_KNOWLEDGE_IMPORT_STATE,
|
|
7455
|
-
schema: KNOWLEDGE_IMPORT_STATE_SCHEMA,
|
|
7456
|
-
compositePrimaryKey: [
|
|
7457
|
-
"importerId",
|
|
7458
|
-
"binding",
|
|
7459
|
-
"key"
|
|
7460
|
-
]
|
|
7461
|
-
});
|
|
7462
|
-
await createTable({
|
|
7463
|
-
tableName: TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
7464
|
-
schema: KNOWLEDGE_IMPORT_RUNS_SCHEMA
|
|
7465
|
-
});
|
|
7466
|
-
await createTable({
|
|
7467
|
-
tableName: TABLE_KNOWLEDGE_PROPOSALS,
|
|
7468
|
-
schema: KNOWLEDGE_PROPOSALS_SCHEMA
|
|
7469
|
-
});
|
|
7470
|
-
await createTable({
|
|
7471
|
-
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
|
|
7472
|
-
schema: KNOWLEDGE_V2_ACTIVITY_SCHEMA
|
|
7473
|
-
});
|
|
7474
|
-
await createTable({
|
|
7475
|
-
tableName: _mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
7476
|
-
schema: _mastra_core_storage.KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA
|
|
7477
|
-
});
|
|
7478
|
-
for (const statement of [
|
|
7479
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_nodes_identity ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (type, scopeKey, canonicalName)`,
|
|
7480
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (scopeKey, type)`,
|
|
7481
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_name ON "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (type, canonicalName)`,
|
|
7482
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (node, id DESC)`,
|
|
7483
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_records_thread_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (sourceThreadId, id DESC)`,
|
|
7484
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_mentions_record ON "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" (recordId, sourceType, sourceId)`,
|
|
7485
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (id DESC)`,
|
|
7486
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_records_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" (scopeKey, id)`,
|
|
7487
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_activity_scope ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (scopeKey, id DESC)`,
|
|
7488
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (idempotencyKey)`,
|
|
7489
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (status, availableAt, createdAt)`,
|
|
7490
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_node_scopes_scope ON "${TABLE_KNOWLEDGE_NODE_SCOPES}" (scopeNodeId, nodeId)`,
|
|
7491
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_record_scopes_scope ON "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (scopeNodeId, recordId)`,
|
|
7492
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_scope_grants_ref ON "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" (scopeRefId, scopeNodeId)`,
|
|
7493
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_node_addresses_node ON "${TABLE_KNOWLEDGE_NODE_ADDRESSES}" (nodeId)`,
|
|
7494
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_import_runs_lookup ON "${TABLE_KNOWLEDGE_IMPORT_RUNS}" (importerId, binding, queuedAt DESC)`,
|
|
7495
|
-
`CREATE INDEX IF NOT EXISTS idx_knowledge_activity_import_run ON "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (importRunId, id DESC)`
|
|
7496
|
-
]) await tx.execute(statement);
|
|
7013
|
+
async #logActivity(tx, action, event, now) {
|
|
7497
7014
|
await tx.execute({
|
|
7498
|
-
sql: `INSERT
|
|
7499
|
-
args: [
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
const objects = await executor.execute("SELECT type, name FROM sqlite_master WHERE name LIKE 'mastra\\_knowledge\\_%' ESCAPE '\\' OR tbl_name LIKE 'mastra\\_knowledge\\_%' ESCAPE '\\' OR sql LIKE '%mastra\\_knowledge\\_%' ESCAPE '\\'");
|
|
7509
|
-
for (const row of objects.rows) {
|
|
7510
|
-
const type = String(row.type);
|
|
7511
|
-
const name = String(row.name);
|
|
7512
|
-
if (type === "table" && PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES.has(name)) continue;
|
|
7513
|
-
const knownIndex = PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES.has(name) || name.startsWith("sqlite_autoindex_");
|
|
7514
|
-
if (type !== "index" || !knownIndex) return false;
|
|
7515
|
-
}
|
|
7516
|
-
return true;
|
|
7517
|
-
}
|
|
7518
|
-
async dangerouslyClearAll() {
|
|
7519
|
-
await this.#transaction(async (tx) => {
|
|
7520
|
-
for (const table of [
|
|
7521
|
-
TABLE_KNOWLEDGE_RECORD_SCOPES,
|
|
7522
|
-
TABLE_KNOWLEDGE_NODE_SCOPES,
|
|
7523
|
-
TABLE_KNOWLEDGE_SCOPE_GRANTS,
|
|
7524
|
-
TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
|
|
7525
|
-
TABLE_KNOWLEDGE_NODE_ADDRESSES,
|
|
7526
|
-
_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS,
|
|
7527
|
-
TABLE_KNOWLEDGE_PROPOSALS,
|
|
7528
|
-
_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY,
|
|
7529
|
-
TABLE_KNOWLEDGE_IMPORT_STATE,
|
|
7530
|
-
TABLE_KNOWLEDGE_IMPORT_RUNS,
|
|
7531
|
-
_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
|
|
7532
|
-
_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS,
|
|
7533
|
-
_mastra_core_storage.TABLE_KNOWLEDGE_NODES
|
|
7534
|
-
]) await tx.execute(`DELETE FROM "${table}"`);
|
|
7535
|
-
await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch = 0 WHERE id = 'global'`);
|
|
7015
|
+
sql: `INSERT INTO "${T.activity}" (id, action, scopeId, pageId, noteId, provenance, createdAt) VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
7016
|
+
args: [
|
|
7017
|
+
(0, _mastra_core_storage.createKnowledgeUlid)(),
|
|
7018
|
+
action,
|
|
7019
|
+
event.scopeId,
|
|
7020
|
+
event.pageId,
|
|
7021
|
+
event.noteId ?? null,
|
|
7022
|
+
JSON.stringify(event.provenance),
|
|
7023
|
+
now
|
|
7024
|
+
]
|
|
7536
7025
|
});
|
|
7537
7026
|
}
|
|
7538
|
-
async
|
|
7539
|
-
await
|
|
7540
|
-
|
|
7541
|
-
sql: `DROP TABLE IF EXISTS "${table}"`,
|
|
7542
|
-
args: []
|
|
7543
|
-
})), "write");
|
|
7544
|
-
});
|
|
7545
|
-
await this.init();
|
|
7027
|
+
async #lastPosition(tx, parentId, scopesOnly) {
|
|
7028
|
+
const rows = await this.#query(tx, `SELECT position FROM "${T.pages}" WHERE parentId IS ? AND isScope = ? AND deletedAt IS NULL ORDER BY position DESC, id DESC LIMIT 1`, [parentId, scopesOnly ? 1 : 0]);
|
|
7029
|
+
return rows[0] ? String(rows[0].position) : void 0;
|
|
7546
7030
|
}
|
|
7547
|
-
async
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
const
|
|
7552
|
-
const
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
if (!row.isScope) throw new Error(`Knowledge address ${address} does not reference a scope`);
|
|
7564
|
-
if (row.deletedAt) deletedScopeAddresses.add(address);
|
|
7565
|
-
scopes[address] = String(row.scopeNodeId);
|
|
7566
|
-
return scopes[address];
|
|
7567
|
-
};
|
|
7568
|
-
for (const scope of plan.scopes) {
|
|
7569
|
-
if (await resolveAddress(scope.address)) continue;
|
|
7570
|
-
const id = (0, crypto$1.randomUUID)();
|
|
7571
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7031
|
+
async materializeScope(input) {
|
|
7032
|
+
const core = await loadPagesCore();
|
|
7033
|
+
await assertKnowledgePageDescriptionWithinBound(input.description);
|
|
7034
|
+
return this.#transaction(async (tx) => {
|
|
7035
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7036
|
+
const ensureScope = async (spec, parentId) => {
|
|
7037
|
+
await assertKnowledgePageDescriptionWithinBound(spec.description);
|
|
7038
|
+
const existing = (await this.#query(tx, `SELECT * FROM "${T.pages}" WHERE isScope = 1 AND address = ?`, [spec.address]))[0];
|
|
7039
|
+
if (existing) return {
|
|
7040
|
+
row: existing,
|
|
7041
|
+
created: false
|
|
7042
|
+
};
|
|
7043
|
+
if (parentId !== null) await this.#livePage(tx, parentId);
|
|
7044
|
+
const id = (0, _mastra_core_storage.createKnowledgeUlid)();
|
|
7045
|
+
const position = core.knowledgePositionBetween(await this.#lastPosition(tx, parentId, true));
|
|
7046
|
+
const who = JSON.stringify(input.provenance);
|
|
7572
7047
|
await tx.execute({
|
|
7573
|
-
sql: `INSERT INTO "${
|
|
7048
|
+
sql: `INSERT INTO "${T.pages}" (id, scopeId, isScope, address, declaration, parentId, position, path, depth, name, canonicalName, description, version, createdBy, updatedBy, createdAt, updatedAt) VALUES (?, ?, 1, ?, ?, ?, ?, ?, 0, ?, ?, ?, 1, ?, ?, ?, ?)`,
|
|
7574
7049
|
args: [
|
|
7575
7050
|
id,
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7051
|
+
id,
|
|
7052
|
+
spec.address,
|
|
7053
|
+
spec.declaration,
|
|
7054
|
+
parentId,
|
|
7055
|
+
position,
|
|
7056
|
+
`/${id}/`,
|
|
7057
|
+
spec.name,
|
|
7058
|
+
core.canonicalKnowledgeName(spec.name),
|
|
7059
|
+
spec.description ?? null,
|
|
7060
|
+
who,
|
|
7061
|
+
who,
|
|
7581
7062
|
now,
|
|
7582
7063
|
now
|
|
7583
7064
|
]
|
|
7584
7065
|
});
|
|
7066
|
+
await this.#logActivity(tx, "scope-created", {
|
|
7067
|
+
scopeId: id,
|
|
7068
|
+
pageId: id,
|
|
7069
|
+
provenance: input.provenance
|
|
7070
|
+
}, now);
|
|
7071
|
+
return {
|
|
7072
|
+
row: await this.#pageRow(tx, id),
|
|
7073
|
+
created: true
|
|
7074
|
+
};
|
|
7075
|
+
};
|
|
7076
|
+
const ensured = await ensureScope(input, input.parentId ?? null);
|
|
7077
|
+
let row = ensured.row;
|
|
7078
|
+
const created = ensured.created;
|
|
7079
|
+
const scopeId = String(row.id);
|
|
7080
|
+
if (!created && input.parentId != null && str(row.parentId) === void 0) {
|
|
7081
|
+
await this.#livePage(tx, input.parentId);
|
|
7082
|
+
if (input.parentId === scopeId) throw new Error(`Scope ${scopeId} cannot be its own parent`);
|
|
7083
|
+
let ancestor = input.parentId;
|
|
7084
|
+
while (ancestor) {
|
|
7085
|
+
const up = await this.#pageRow(tx, ancestor);
|
|
7086
|
+
if (String(up.id) === scopeId) throw new Error(`Scope ${scopeId} cannot be linked under its own descendant`);
|
|
7087
|
+
ancestor = str(up.parentId);
|
|
7088
|
+
}
|
|
7585
7089
|
await tx.execute({
|
|
7586
|
-
sql: `
|
|
7587
|
-
args: [
|
|
7090
|
+
sql: `UPDATE "${T.pages}" SET parentId = ?, position = ?, version = version + 1, updatedAt = ? WHERE id = ? AND parentId IS NULL`,
|
|
7091
|
+
args: [
|
|
7092
|
+
input.parentId,
|
|
7093
|
+
core.knowledgePositionBetween(await this.#lastPosition(tx, input.parentId, true)),
|
|
7094
|
+
now,
|
|
7095
|
+
scopeId
|
|
7096
|
+
]
|
|
7588
7097
|
});
|
|
7589
|
-
|
|
7590
|
-
createdAddresses.add(scope.address);
|
|
7591
|
-
createdScopeIds.push(id);
|
|
7592
|
-
structureChanged = true;
|
|
7098
|
+
row = await this.#pageRow(tx, scopeId);
|
|
7593
7099
|
}
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
args: [
|
|
7614
|
-
parentId,
|
|
7615
|
-
canonicalName(scope.name),
|
|
7616
|
-
scopeNodeId
|
|
7617
|
-
]
|
|
7618
|
-
})).rows.length) throw new Error(`Knowledge scope name ${scope.name} already exists under ${parentAddress}`);
|
|
7619
|
-
const inserted = await tx.execute({
|
|
7620
|
-
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
|
|
7621
|
-
args: [
|
|
7622
|
-
scopeNodeId,
|
|
7623
|
-
parentId,
|
|
7624
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
7625
|
-
]
|
|
7626
|
-
});
|
|
7627
|
-
structureChanged ||= inserted.rowsAffected > 0;
|
|
7628
|
-
}
|
|
7629
|
-
for (const grant of scope.grants ?? []) {
|
|
7630
|
-
const scopeRefId = await resolveAddress(grant.scopeRefAddress);
|
|
7631
|
-
if (!scopeRefId || deletedScopeAddresses.has(grant.scopeRefAddress)) {
|
|
7632
|
-
const deletedScopeRefId = scopes[grant.scopeRefAddress];
|
|
7633
|
-
if ((deletedScopeRefId ? await tx.execute({
|
|
7634
|
-
sql: `SELECT 1 FROM "mastra_knowledge_scope_grants" WHERE scopeNodeId=? AND scopeRefId=?`,
|
|
7635
|
-
args: [scopeNodeId, deletedScopeRefId]
|
|
7636
|
-
}) : void 0)?.rows.length) continue;
|
|
7637
|
-
throw new Error(`Knowledge grant scope does not exist: ${grant.scopeRefAddress}`);
|
|
7638
|
-
}
|
|
7639
|
-
const inserted = await tx.execute({
|
|
7640
|
-
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" (scopeNodeId,scopeRefId,role,canSuggest) VALUES (?,?,?,?)`,
|
|
7641
|
-
args: [
|
|
7642
|
-
scopeNodeId,
|
|
7643
|
-
scopeRefId,
|
|
7644
|
-
grant.role,
|
|
7645
|
-
grant.canSuggest ?? null
|
|
7646
|
-
]
|
|
7647
|
-
});
|
|
7648
|
-
structureChanged ||= inserted.rowsAffected > 0;
|
|
7100
|
+
let parentConflict;
|
|
7101
|
+
if (!created && input.parentId !== void 0 && (str(row.parentId) ?? null) !== input.parentId) parentConflict = {
|
|
7102
|
+
existingParentId: str(row.parentId) ?? null,
|
|
7103
|
+
requestedParentId: input.parentId
|
|
7104
|
+
};
|
|
7105
|
+
for (const child of input.children ?? []) await ensureScope(child, scopeId);
|
|
7106
|
+
const ensureTemplates = async (templates, parentId) => {
|
|
7107
|
+
for (const template of templates) {
|
|
7108
|
+
const problem = knowledgePageKeyProblem(template.key);
|
|
7109
|
+
if (problem) throw new Error(problem);
|
|
7110
|
+
const existing = (await this.#query(tx, `SELECT id FROM "${T.pages}" WHERE parentId = ? AND isScope = 0 AND key = ? AND deletedAt IS NULL`, [parentId, template.key]))[0];
|
|
7111
|
+
const id = existing ? String(existing.id) : (await this.#insertPage(tx, {
|
|
7112
|
+
parentId,
|
|
7113
|
+
key: template.key,
|
|
7114
|
+
name: template.name,
|
|
7115
|
+
description: template.description,
|
|
7116
|
+
provenance: input.provenance
|
|
7117
|
+
})).id;
|
|
7118
|
+
if (template.pages?.length) await ensureTemplates(template.pages, id);
|
|
7649
7119
|
}
|
|
7650
|
-
}
|
|
7651
|
-
|
|
7652
|
-
const state = await tx.execute(`SELECT epoch FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
|
|
7120
|
+
};
|
|
7121
|
+
await ensureTemplates(input.pages ?? [], scopeId);
|
|
7653
7122
|
return {
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
|
|
7657
|
-
changed: structureChanged,
|
|
7658
|
-
accessEpoch: Number(state.rows[0]?.epoch ?? 0)
|
|
7123
|
+
scope: toPage(await this.#pageRow(tx, scopeId)),
|
|
7124
|
+
created,
|
|
7125
|
+
...parentConflict ? { parentConflict } : {}
|
|
7659
7126
|
};
|
|
7127
|
+
});
|
|
7128
|
+
}
|
|
7129
|
+
async listScopeDeclarations() {
|
|
7130
|
+
return (await this.#query(this.#read, `SELECT declaration, COUNT(*) AS scopes FROM "${T.pages}" WHERE isScope = 1 AND declaration IS NOT NULL GROUP BY declaration ORDER BY declaration`)).map((row) => ({
|
|
7131
|
+
declaration: String(row.declaration),
|
|
7132
|
+
scopes: Number(row.scopes)
|
|
7660
7133
|
}));
|
|
7661
7134
|
}
|
|
7662
|
-
async
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7135
|
+
async renameScopeDeclaration(input) {
|
|
7136
|
+
return this.#transaction(async (tx) => {
|
|
7137
|
+
const within = prefixRange("declaration", `${input.from}/`);
|
|
7138
|
+
const affected = (await this.#query(tx, `SELECT id, address, declaration FROM "${T.pages}" WHERE isScope = 1 AND (declaration = ? OR ${within.sql})`, [input.from, ...within.args])).map((row) => ({
|
|
7139
|
+
id: String(row.id),
|
|
7140
|
+
address: String(row.address),
|
|
7141
|
+
from: String(row.declaration)
|
|
7142
|
+
})).filter((row) => row.from === input.from || row.from.startsWith(`${input.from}/`)).map((row) => ({
|
|
7143
|
+
...row,
|
|
7144
|
+
to: input.to + row.from.slice(input.from.length)
|
|
7145
|
+
}));
|
|
7146
|
+
input.check(affected.map(({ address, from, to }) => ({
|
|
7147
|
+
address,
|
|
7148
|
+
from,
|
|
7149
|
+
to
|
|
7150
|
+
})));
|
|
7151
|
+
for (const row of affected) await tx.execute({
|
|
7152
|
+
sql: `UPDATE "${T.pages}" SET declaration = ? WHERE id = ?`,
|
|
7153
|
+
args: [row.to, row.id]
|
|
7154
|
+
});
|
|
7155
|
+
return affected.length;
|
|
7156
|
+
});
|
|
7157
|
+
}
|
|
7158
|
+
async getScopesByAddress(addresses) {
|
|
7159
|
+
if (addresses.length === 0) return [];
|
|
7160
|
+
return (await this.#query(this.#read, `SELECT * FROM "${T.pages}" WHERE isScope = 1 AND address IN (SELECT value FROM json_each(?))`, [jsonList(addresses)])).map(toPage);
|
|
7161
|
+
}
|
|
7162
|
+
async listChildScopes(input) {
|
|
7163
|
+
const core = await loadPagesCore();
|
|
7164
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7165
|
+
const cursor = decodeCursor(input.cursor, 2);
|
|
7166
|
+
const args = [input.parentId];
|
|
7167
|
+
let after = "";
|
|
7168
|
+
if (cursor) {
|
|
7169
|
+
after = ` AND (position > ? OR (position = ? AND id > ?))`;
|
|
7170
|
+
args.push(cursor[0], cursor[0], cursor[1]);
|
|
7171
|
+
}
|
|
7172
|
+
args.push(limit + 1);
|
|
7173
|
+
const rows = await this.#query(this.#read, `SELECT * FROM "${T.pages}" WHERE isScope = 1 AND parentId IS ?${after} ORDER BY position, id LIMIT ?`, args);
|
|
7174
|
+
return this.#pageOutput(rows, limit, (row) => [String(row.position), String(row.id)]);
|
|
7175
|
+
}
|
|
7176
|
+
#pageOutput(rows, limit, cursorOf) {
|
|
7177
|
+
const page = rows.slice(0, limit);
|
|
7178
|
+
return {
|
|
7179
|
+
pages: page.map(toPage),
|
|
7180
|
+
...rows.length > limit ? { nextCursor: encodeCursor(cursorOf(page[page.length - 1])) } : {}
|
|
7667
7181
|
};
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7182
|
+
}
|
|
7183
|
+
async getPage(input) {
|
|
7184
|
+
const readable = pageReadable("p", input.read);
|
|
7185
|
+
const rows = await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.id = ? AND ${readable.sql}`, [input.id, ...readable.args]);
|
|
7186
|
+
return rows[0] ? toPage(rows[0]) : null;
|
|
7187
|
+
}
|
|
7188
|
+
async getPages(input) {
|
|
7189
|
+
const ids = [...new Set(input.ids)];
|
|
7190
|
+
if (ids.length === 0) return [];
|
|
7191
|
+
const readable = pageReadable("p", input.read);
|
|
7192
|
+
return (await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.deletedAt IS NULL AND p.id IN (SELECT value FROM json_each(?)) AND ${readable.sql}`, [jsonList(ids), ...readable.args])).map(toPage);
|
|
7193
|
+
}
|
|
7194
|
+
async getPageByPath(input) {
|
|
7195
|
+
const readable = pageReadable("p", input.read);
|
|
7196
|
+
let parentId = input.scopeId;
|
|
7197
|
+
let row;
|
|
7198
|
+
for (const key of input.path) {
|
|
7199
|
+
row = (await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.parentId = ? AND p.isScope = 0 AND p.key = ? AND p.deletedAt IS NULL AND ${readable.sql}`, [
|
|
7200
|
+
parentId,
|
|
7201
|
+
key,
|
|
7202
|
+
...readable.args
|
|
7203
|
+
]))[0];
|
|
7204
|
+
if (!row) return null;
|
|
7205
|
+
parentId = String(row.id);
|
|
7206
|
+
}
|
|
7207
|
+
return row ? toPage(row) : null;
|
|
7208
|
+
}
|
|
7209
|
+
async findPagesByName(input) {
|
|
7210
|
+
const core = await loadPagesCore();
|
|
7211
|
+
const readable = pageReadable("p", input.read);
|
|
7212
|
+
const args = [core.canonicalKnowledgeName(input.name), ...readable.args];
|
|
7213
|
+
let scope = "";
|
|
7214
|
+
if (input.scopeId !== void 0) {
|
|
7215
|
+
scope = ` AND p.scopeId = ?`;
|
|
7216
|
+
args.push(input.scopeId);
|
|
7217
|
+
}
|
|
7218
|
+
args.push(limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT));
|
|
7219
|
+
return (await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.canonicalName = ? AND p.deletedAt IS NULL AND ${readable.sql}${scope} ORDER BY p.createdAt DESC, p.id DESC LIMIT ?`, args)).map(toPage);
|
|
7220
|
+
}
|
|
7221
|
+
async listChildPages(input) {
|
|
7222
|
+
const core = await loadPagesCore();
|
|
7223
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7224
|
+
const cursor = decodeCursor(input.cursor, 2);
|
|
7225
|
+
const readable = pageReadable("p", input.read);
|
|
7226
|
+
const args = [input.parentId, ...readable.args];
|
|
7227
|
+
let after = "";
|
|
7228
|
+
if (cursor) {
|
|
7229
|
+
after = ` AND (p.position > ? OR (p.position = ? AND p.id > ?))`;
|
|
7230
|
+
args.push(cursor[0], cursor[0], cursor[1]);
|
|
7675
7231
|
}
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7232
|
+
args.push(limit + 1);
|
|
7233
|
+
const rows = await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.parentId = ? AND p.isScope = 0 AND p.deletedAt IS NULL AND ${readable.sql}${after} ORDER BY p.position, p.id LIMIT ?`, args);
|
|
7234
|
+
return this.#pageOutput(rows, limit, (row) => [String(row.position), String(row.id)]);
|
|
7235
|
+
}
|
|
7236
|
+
async listPages(input) {
|
|
7237
|
+
const core = await loadPagesCore();
|
|
7238
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7239
|
+
const cursor = decodeCursor(input.cursor, 2);
|
|
7240
|
+
const readable = pageReadable("p", input.read);
|
|
7241
|
+
const args = [...readable.args];
|
|
7242
|
+
let filters = "";
|
|
7243
|
+
if (input.scopeId !== void 0) {
|
|
7244
|
+
filters += ` AND p.scopeId = ?`;
|
|
7245
|
+
args.push(input.scopeId);
|
|
7679
7246
|
}
|
|
7680
|
-
if (input.
|
|
7681
|
-
|
|
7682
|
-
|
|
7247
|
+
if (input.namePrefix) {
|
|
7248
|
+
const prefix = core.canonicalKnowledgeName(input.namePrefix);
|
|
7249
|
+
filters += ` AND substr(p.canonicalName, 1, length(?)) = ?`;
|
|
7250
|
+
args.push(prefix, prefix);
|
|
7683
7251
|
}
|
|
7684
|
-
if (
|
|
7685
|
-
|
|
7686
|
-
args.push(
|
|
7252
|
+
if (cursor) {
|
|
7253
|
+
filters += ` AND (p.name > ? OR (p.name = ? AND p.id > ?))`;
|
|
7254
|
+
args.push(cursor[0], cursor[0], cursor[1]);
|
|
7687
7255
|
}
|
|
7688
7256
|
args.push(limit + 1);
|
|
7689
|
-
const rows =
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
const
|
|
7698
|
-
|
|
7699
|
-
|
|
7257
|
+
const rows = await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.isScope = 0 AND p.deletedAt IS NULL AND ${readable.sql}${filters} ORDER BY p.name, p.id LIMIT ?`, args);
|
|
7258
|
+
return this.#pageOutput(rows, limit, (row) => [String(row.name), String(row.id)]);
|
|
7259
|
+
}
|
|
7260
|
+
async #insertPage(tx, input) {
|
|
7261
|
+
const core = await loadPagesCore();
|
|
7262
|
+
await assertKnowledgePageDescriptionWithinBound(input.description);
|
|
7263
|
+
const parent = await this.#livePage(tx, input.parentId);
|
|
7264
|
+
const isScopeParent = Number(parent.isScope) === 1;
|
|
7265
|
+
const scopeId = isScopeParent ? String(parent.id) : String(parent.scopeId);
|
|
7266
|
+
const depth = isScopeParent ? 1 : Number(parent.depth) + 1;
|
|
7267
|
+
if (depth > core.MAX_KNOWLEDGE_PAGE_DEPTH) throw new core.KnowledgePageDepthError(input.parentId);
|
|
7268
|
+
const id = input.id ?? (0, _mastra_core_storage.createKnowledgeUlid)();
|
|
7269
|
+
if (await this.#pageRow(tx, id)) throw new _mastra_core_storage.KnowledgeConflictError(id);
|
|
7270
|
+
if (input.key !== void 0) {
|
|
7271
|
+
const problem = knowledgePageKeyProblem(input.key);
|
|
7272
|
+
if (problem) throw new Error(problem);
|
|
7273
|
+
if ((await this.#query(tx, `SELECT id FROM "${T.pages}" WHERE parentId = ? AND isScope = 0 AND key = ? AND deletedAt IS NULL`, [String(parent.id), input.key])).length > 0) throw new core.KnowledgePageKeyConflictError(input.key, String(parent.id));
|
|
7274
|
+
}
|
|
7275
|
+
const position = input.position ?? core.knowledgePositionBetween(await this.#lastPosition(tx, String(parent.id), false));
|
|
7276
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7277
|
+
const who = JSON.stringify(input.provenance);
|
|
7278
|
+
await tx.execute({
|
|
7279
|
+
sql: `INSERT INTO "${T.pages}" (id, scopeId, isScope, key, parentId, position, path, depth, name, canonicalName, description, body, version, createdBy, updatedBy, createdAt, updatedAt) VALUES (?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?)`,
|
|
7280
|
+
args: [
|
|
7281
|
+
id,
|
|
7282
|
+
scopeId,
|
|
7283
|
+
input.key ?? null,
|
|
7284
|
+
String(parent.id),
|
|
7285
|
+
position,
|
|
7286
|
+
`${String(parent.path)}${id}/`,
|
|
7287
|
+
depth,
|
|
7288
|
+
input.name,
|
|
7289
|
+
core.canonicalKnowledgeName(input.name),
|
|
7290
|
+
input.description || null,
|
|
7291
|
+
input.body ?? null,
|
|
7292
|
+
who,
|
|
7293
|
+
who,
|
|
7294
|
+
now,
|
|
7295
|
+
now
|
|
7296
|
+
]
|
|
7700
7297
|
});
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
parentIds: parentsByScopeId.get(String(row.id)) ?? []
|
|
7716
|
-
})), limit, input);
|
|
7717
|
-
}
|
|
7718
|
-
async listScopeMembers(input) {
|
|
7719
|
-
const { limit, after } = parseListKnowledgeScopeMembersInput(input);
|
|
7720
|
-
const args = [input.scopeNodeId];
|
|
7721
|
-
let keyset = "";
|
|
7722
|
-
if (after) {
|
|
7723
|
-
const updatedAt = after.updatedAt.toISOString();
|
|
7724
|
-
keyset = ` AND (n.updatedAt < ? OR (n.updatedAt = ? AND (n.name > ? OR (n.name = ? AND n.id > ?))))`;
|
|
7725
|
-
args.push(updatedAt, updatedAt, after.name, after.name, after.id);
|
|
7726
|
-
}
|
|
7727
|
-
args.push(limit + 1);
|
|
7728
|
-
return pageKnowledgeScopeMembers((await this.#client.execute({
|
|
7729
|
-
sql: `SELECT *, json(scope) AS scopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n WHERE EXISTS (SELECT 1 FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns WHERE ns.nodeId=n.id AND ns.scopeNodeId=?) AND n.deletedAt IS NULL AND n.mergedInto IS NULL${keyset} ORDER BY n.updatedAt DESC, n.name ASC, n.id ASC LIMIT ?`,
|
|
7730
|
-
args
|
|
7731
|
-
})).rows.map(parseNode), limit, input);
|
|
7732
|
-
}
|
|
7733
|
-
async createNode(input) {
|
|
7734
|
-
await assertKnowledgeDescriptionWithinBoundCompat(input.description);
|
|
7735
|
-
const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
|
|
7298
|
+
await this.#logActivity(tx, "page-created", {
|
|
7299
|
+
scopeId,
|
|
7300
|
+
pageId: id,
|
|
7301
|
+
provenance: input.provenance
|
|
7302
|
+
}, now);
|
|
7303
|
+
return toPage(await this.#pageRow(tx, id));
|
|
7304
|
+
}
|
|
7305
|
+
async createPage(input) {
|
|
7306
|
+
await assertKnowledgePageDescriptionWithinBound(input.description);
|
|
7307
|
+
return this.#transaction((tx) => this.#insertPage(tx, input));
|
|
7308
|
+
}
|
|
7309
|
+
async updatePage(input) {
|
|
7310
|
+
const core = await loadPagesCore();
|
|
7311
|
+
await assertKnowledgePageDescriptionWithinBound(input.description);
|
|
7736
7312
|
return this.#transaction(async (tx) => {
|
|
7737
|
-
const
|
|
7738
|
-
if (
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
}
|
|
7744
|
-
const now = /* @__PURE__ */ new Date();
|
|
7745
|
-
const node = {
|
|
7746
|
-
id: input.id ?? crypto.randomUUID(),
|
|
7747
|
-
type: "node",
|
|
7748
|
-
name: input.name.trim(),
|
|
7749
|
-
kind: input.kind,
|
|
7750
|
-
content: input.content,
|
|
7751
|
-
description: input.description,
|
|
7752
|
-
scope,
|
|
7753
|
-
version: 1,
|
|
7754
|
-
createdAt: now,
|
|
7755
|
-
updatedAt: now
|
|
7756
|
-
};
|
|
7313
|
+
const row = await this.#livePage(tx, input.id);
|
|
7314
|
+
if (Number(row.version) !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
|
|
7315
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7316
|
+
const name = input.name ?? String(row.name);
|
|
7317
|
+
const description = input.description === void 0 ? row.description ?? null : input.description || null;
|
|
7318
|
+
const body = input.body === void 0 ? row.body ?? null : input.body;
|
|
7757
7319
|
await tx.execute({
|
|
7758
|
-
sql: `
|
|
7320
|
+
sql: `UPDATE "${T.pages}" SET name = ?, canonicalName = ?, description = ?, body = ?, version = version + 1, updatedBy = ?, updatedAt = ? WHERE id = ?`,
|
|
7759
7321
|
args: [
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
JSON.stringify(scope),
|
|
7768
|
-
(0, _mastra_core_storage.knowledgeScopeKey)(scope),
|
|
7769
|
-
node.version,
|
|
7770
|
-
now.toISOString(),
|
|
7771
|
-
now.toISOString()
|
|
7322
|
+
name,
|
|
7323
|
+
core.canonicalKnowledgeName(name),
|
|
7324
|
+
description,
|
|
7325
|
+
body,
|
|
7326
|
+
JSON.stringify(input.provenance),
|
|
7327
|
+
now,
|
|
7328
|
+
input.id
|
|
7772
7329
|
]
|
|
7773
7330
|
});
|
|
7774
|
-
await this.#
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
return
|
|
7331
|
+
await this.#logActivity(tx, "page-updated", {
|
|
7332
|
+
scopeId: String(row.scopeId),
|
|
7333
|
+
pageId: input.id,
|
|
7334
|
+
provenance: input.provenance
|
|
7335
|
+
}, now);
|
|
7336
|
+
return toPage(await this.#pageRow(tx, input.id));
|
|
7780
7337
|
});
|
|
7781
7338
|
}
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
const
|
|
7793
|
-
const
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7339
|
+
/**
|
|
7340
|
+
* Rehomes `root` (and its subtree) under `parent`: new path prefix, scope and depth in one go.
|
|
7341
|
+
* Throws when the subtree is too large, too deep, or the root's key clashes with a live child of `parent`.
|
|
7342
|
+
*/
|
|
7343
|
+
async #rehome(tx, root, parent, options) {
|
|
7344
|
+
const core = await loadPagesCore();
|
|
7345
|
+
const isScopeParent = Number(parent.isScope) === 1;
|
|
7346
|
+
const newScopeId = isScopeParent ? String(parent.id) : String(parent.scopeId);
|
|
7347
|
+
const newDepth = isScopeParent ? 1 : Number(parent.depth) + 1;
|
|
7348
|
+
const oldPath = String(root.path);
|
|
7349
|
+
const within = prefixRange("path", oldPath);
|
|
7350
|
+
const live = (await this.#query(tx, `SELECT id, key, depth, deletedAt FROM "${T.pages}" WHERE ${within.sql}`, within.args)).filter((row) => !row.deletedAt);
|
|
7351
|
+
if (live.length > core.MAX_KNOWLEDGE_MOVE_PAGES) throw new core.KnowledgeMoveTooLargeError(options.countLimitId, live.length);
|
|
7352
|
+
const shift = newDepth - Number(root.depth);
|
|
7353
|
+
if (Math.max(...live.map((row) => Number(row.depth))) + shift > core.MAX_KNOWLEDGE_PAGE_DEPTH) throw new core.KnowledgePageDepthError(String(root.id));
|
|
7354
|
+
if (!root.deletedAt && root.key !== null) {
|
|
7355
|
+
if ((await this.#query(tx, `SELECT id FROM "${T.pages}" WHERE parentId = ? AND isScope = 0 AND key = ? AND deletedAt IS NULL AND id <> ?`, [
|
|
7356
|
+
String(parent.id),
|
|
7357
|
+
String(root.key),
|
|
7358
|
+
String(root.id)
|
|
7359
|
+
])).length > 0) throw new core.KnowledgePageKeyConflictError(String(root.key), String(parent.id));
|
|
7360
|
+
}
|
|
7361
|
+
const newPath = `${String(parent.path)}${String(root.id)}/`;
|
|
7362
|
+
const position = options.position ?? core.knowledgePositionBetween(await this.#lastPosition(tx, String(parent.id), false));
|
|
7363
|
+
await tx.execute({
|
|
7364
|
+
sql: `UPDATE "${T.pages}" SET path = ? || substr(path, length(?) + 1), depth = depth + ?, scopeId = ? WHERE ${within.sql}`,
|
|
7365
|
+
args: [
|
|
7366
|
+
newPath,
|
|
7367
|
+
oldPath,
|
|
7368
|
+
shift,
|
|
7369
|
+
newScopeId,
|
|
7370
|
+
...within.args
|
|
7371
|
+
]
|
|
7372
|
+
});
|
|
7373
|
+
await tx.execute({
|
|
7374
|
+
sql: `UPDATE "${T.pages}" SET parentId = ?, position = ? WHERE id = ?`,
|
|
7375
|
+
args: [
|
|
7376
|
+
String(parent.id),
|
|
7377
|
+
position,
|
|
7378
|
+
String(root.id)
|
|
7379
|
+
]
|
|
7380
|
+
});
|
|
7381
|
+
return {
|
|
7382
|
+
moved: live.length,
|
|
7383
|
+
scopeId: newScopeId
|
|
7384
|
+
};
|
|
7823
7385
|
}
|
|
7824
|
-
|
|
7825
|
-
await assertKnowledgeDescriptionWithinBoundCompat(input.description);
|
|
7386
|
+
movePage(input) {
|
|
7826
7387
|
return this.#transaction(async (tx) => {
|
|
7827
|
-
const
|
|
7828
|
-
if (
|
|
7829
|
-
if (
|
|
7830
|
-
const
|
|
7831
|
-
const
|
|
7832
|
-
|
|
7833
|
-
const
|
|
7834
|
-
const
|
|
7835
|
-
|
|
7836
|
-
|
|
7388
|
+
const row = await this.#livePage(tx, input.id);
|
|
7389
|
+
if (Number(row.isScope) === 1) throw new Error(`Cannot move ${input.id}: it is a scope page`);
|
|
7390
|
+
if (Number(row.version) !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
|
|
7391
|
+
const parent = await this.#livePage(tx, input.parentId);
|
|
7392
|
+
const path = String(row.path);
|
|
7393
|
+
if (String(parent.id) === input.id || String(parent.path).startsWith(path)) throw new Error(`Cannot move ${input.id} into its own subtree`);
|
|
7394
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7395
|
+
const { scopeId } = await this.#rehome(tx, row, parent, {
|
|
7396
|
+
position: input.position,
|
|
7397
|
+
countLimitId: input.id
|
|
7398
|
+
});
|
|
7399
|
+
await tx.execute({
|
|
7400
|
+
sql: `UPDATE "${T.pages}" SET version = version + 1, updatedBy = ?, updatedAt = ? WHERE id = ?`,
|
|
7837
7401
|
args: [
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
input.
|
|
7841
|
-
content ?? null,
|
|
7842
|
-
description ?? null,
|
|
7843
|
-
JSON.stringify(scope),
|
|
7844
|
-
(0, _mastra_core_storage.knowledgeScopeKey)(scope),
|
|
7845
|
-
now.toISOString(),
|
|
7846
|
-
input.id,
|
|
7847
|
-
input.version
|
|
7402
|
+
JSON.stringify(input.provenance),
|
|
7403
|
+
now,
|
|
7404
|
+
input.id
|
|
7848
7405
|
]
|
|
7849
|
-
})
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
sql: `SELECT id,json(scope) AS scopeJson,deletedAt FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" WHERE node=?`,
|
|
7857
|
-
args: [input.id]
|
|
7858
|
-
});
|
|
7859
|
-
for (const row of knowledge.rows) {
|
|
7860
|
-
const recordScope = parseJson$3(row.scopeJson);
|
|
7861
|
-
await this.#outbox(tx, "record", String(row.id), "delete", (0, _mastra_core_storage.createKnowledgeUlid)(), recordScope);
|
|
7862
|
-
if (row.deletedAt == null) await this.#outbox(tx, "record", String(row.id), "upsert", (0, _mastra_core_storage.createKnowledgeUlid)(), recordScope);
|
|
7863
|
-
}
|
|
7864
|
-
}
|
|
7865
|
-
await this.#outbox(tx, "node", input.id, "upsert", input.version + 1, scope);
|
|
7866
|
-
return {
|
|
7867
|
-
...existing,
|
|
7868
|
-
name,
|
|
7869
|
-
kind: input.kind ?? existing.kind,
|
|
7870
|
-
content,
|
|
7871
|
-
description,
|
|
7872
|
-
scope,
|
|
7873
|
-
version: input.version + 1,
|
|
7874
|
-
updatedAt: now
|
|
7875
|
-
};
|
|
7406
|
+
});
|
|
7407
|
+
await this.#logActivity(tx, "page-moved", {
|
|
7408
|
+
scopeId,
|
|
7409
|
+
pageId: input.id,
|
|
7410
|
+
provenance: input.provenance
|
|
7411
|
+
}, now);
|
|
7412
|
+
return toPage(await this.#pageRow(tx, input.id));
|
|
7876
7413
|
});
|
|
7877
7414
|
}
|
|
7878
|
-
async
|
|
7879
|
-
|
|
7415
|
+
async mergePages(input) {
|
|
7416
|
+
const core = await loadPagesCore();
|
|
7880
7417
|
return this.#transaction(async (tx) => {
|
|
7881
|
-
const source = await this.#
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
if (
|
|
7885
|
-
|
|
7886
|
-
if (
|
|
7887
|
-
const
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
const movedKnowledge = await tx.execute({
|
|
7892
|
-
sql: `SELECT id,json(scope) AS scopeJson,deletedAt FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" WHERE node=?`,
|
|
7893
|
-
args: [source.id]
|
|
7894
|
-
});
|
|
7895
|
-
if ((await tx.execute({
|
|
7896
|
-
sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" SET mergedInto=?,version=version+1,updatedAt=? WHERE id=? AND type='node' AND version=? AND mergedInto IS NULL`,
|
|
7897
|
-
args: [
|
|
7898
|
-
target.id,
|
|
7899
|
-
(/* @__PURE__ */ new Date()).toISOString(),
|
|
7900
|
-
source.id,
|
|
7901
|
-
input.sourceVersion
|
|
7902
|
-
]
|
|
7903
|
-
})).rowsAffected === 0) throw new _mastra_core_storage.KnowledgeConflictError(source.id);
|
|
7418
|
+
const source = await this.#livePage(tx, input.sourceId);
|
|
7419
|
+
const target = await this.#livePage(tx, input.targetId);
|
|
7420
|
+
if (Number(source.isScope) === 1 || Number(target.isScope) === 1) throw new Error("Cannot merge a scope page: merging applies to non-scope pages only");
|
|
7421
|
+
if (Number(source.version) !== input.sourceVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.sourceId);
|
|
7422
|
+
const sourcePath = String(source.path);
|
|
7423
|
+
if (input.sourceId === input.targetId || String(target.path).startsWith(sourcePath)) throw new Error(`Page ${input.targetId} is inside page ${input.sourceId}; it cannot be the merge target (subtree)`);
|
|
7424
|
+
const within = prefixRange("path", sourcePath);
|
|
7425
|
+
const carried = await this.#query(tx, `SELECT COUNT(*) AS n FROM "${T.pages}" WHERE ${within.sql} AND id <> ? AND deletedAt IS NULL`, [...within.args, input.sourceId]);
|
|
7426
|
+
if (Number(carried[0].n) > core.MAX_KNOWLEDGE_MOVE_PAGES) throw new core.KnowledgeMoveTooLargeError(input.sourceId, Number(carried[0].n));
|
|
7427
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7904
7428
|
await tx.execute({
|
|
7905
|
-
sql: `UPDATE "${
|
|
7429
|
+
sql: `UPDATE "${T.pages}" SET deletedAt = ?, deletedBy = ?, mergedInto = ?, scopeId = ?, version = version + 1, updatedBy = ?, updatedAt = ? WHERE id = ?`,
|
|
7906
7430
|
args: [
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7431
|
+
now,
|
|
7432
|
+
input.provenance.author,
|
|
7433
|
+
input.targetId,
|
|
7434
|
+
String(target.scopeId),
|
|
7435
|
+
JSON.stringify(input.provenance),
|
|
7436
|
+
now,
|
|
7437
|
+
input.sourceId
|
|
7910
7438
|
]
|
|
7911
7439
|
});
|
|
7440
|
+
const children = await this.#query(tx, `SELECT * FROM "${T.pages}" WHERE parentId = ? AND isScope = 0`, [input.sourceId]);
|
|
7441
|
+
for (const child of children) await this.#rehome(tx, child, target, {
|
|
7442
|
+
position: String(child.position),
|
|
7443
|
+
countLimitId: input.sourceId
|
|
7444
|
+
});
|
|
7912
7445
|
await tx.execute({
|
|
7913
|
-
sql: `
|
|
7914
|
-
args: [
|
|
7446
|
+
sql: `UPDATE "${T.notes}" SET pageId = ? WHERE pageId = ?`,
|
|
7447
|
+
args: [input.targetId, input.sourceId]
|
|
7915
7448
|
});
|
|
7916
7449
|
await tx.execute({
|
|
7917
|
-
sql: `UPDATE "${
|
|
7918
|
-
args: [
|
|
7450
|
+
sql: `UPDATE OR IGNORE "${T.mentions}" SET pageId = ? WHERE pageId = ?`,
|
|
7451
|
+
args: [input.targetId, input.sourceId]
|
|
7919
7452
|
});
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7453
|
+
await tx.execute({
|
|
7454
|
+
sql: `DELETE FROM "${T.mentions}" WHERE pageId = ?`,
|
|
7455
|
+
args: [input.sourceId]
|
|
7456
|
+
});
|
|
7457
|
+
const pins = await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId IN (?, ?)`, [input.sourceId, input.targetId]);
|
|
7458
|
+
const sourcePin = pins.find((pin) => String(pin.pageId) === input.sourceId);
|
|
7459
|
+
if (sourcePin) {
|
|
7460
|
+
const targetPin = pins.find((pin) => String(pin.pageId) === input.targetId);
|
|
7461
|
+
const audience = JSON.parse(String(sourcePin.audience));
|
|
7462
|
+
if (targetPin) {
|
|
7463
|
+
const merged = [.../* @__PURE__ */ new Set([...JSON.parse(String(targetPin.audience)), ...audience])];
|
|
7464
|
+
await tx.execute({
|
|
7465
|
+
sql: `UPDATE "${T.pins}" SET audience = ?, updatedAt = ? WHERE id = ?`,
|
|
7466
|
+
args: [
|
|
7467
|
+
JSON.stringify(merged),
|
|
7468
|
+
now,
|
|
7469
|
+
String(targetPin.id)
|
|
7470
|
+
]
|
|
7471
|
+
});
|
|
7472
|
+
await this.#indexAudience(tx, String(targetPin.id), merged);
|
|
7473
|
+
await this.#indexAudience(tx, String(sourcePin.id), []);
|
|
7474
|
+
await tx.execute({
|
|
7475
|
+
sql: `DELETE FROM "${T.pins}" WHERE id = ?`,
|
|
7476
|
+
args: [String(sourcePin.id)]
|
|
7477
|
+
});
|
|
7478
|
+
} else await tx.execute({
|
|
7479
|
+
sql: `UPDATE "${T.pins}" SET pageId = ?, updatedAt = ? WHERE id = ?`,
|
|
7927
7480
|
args: [
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
target.version
|
|
7481
|
+
input.targetId,
|
|
7482
|
+
now,
|
|
7483
|
+
String(sourcePin.id)
|
|
7932
7484
|
]
|
|
7933
|
-
})
|
|
7934
|
-
mergedTarget = {
|
|
7935
|
-
...target,
|
|
7936
|
-
description: source.description,
|
|
7937
|
-
version: target.version + 1,
|
|
7938
|
-
updatedAt: adoptedAt
|
|
7939
|
-
};
|
|
7940
|
-
await this.#activity(tx, "node-updated", "node", target.id, target.scope);
|
|
7941
|
-
}
|
|
7485
|
+
});
|
|
7942
7486
|
}
|
|
7943
|
-
await this.#
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7487
|
+
await this.#logActivity(tx, "page-merged", {
|
|
7488
|
+
scopeId: String(target.scopeId),
|
|
7489
|
+
pageId: input.sourceId,
|
|
7490
|
+
provenance: input.provenance
|
|
7491
|
+
}, now);
|
|
7492
|
+
return toPage(await this.#pageRow(tx, input.targetId));
|
|
7947
7493
|
});
|
|
7948
7494
|
}
|
|
7949
|
-
async
|
|
7950
|
-
await
|
|
7951
|
-
const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
|
|
7952
|
-
const resolutionScope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.resolutionScope);
|
|
7953
|
-
const defaultScope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.defaultScope);
|
|
7495
|
+
async deletePage(input) {
|
|
7496
|
+
const core = await loadPagesCore();
|
|
7954
7497
|
return this.#transaction(async (tx) => {
|
|
7955
|
-
const
|
|
7956
|
-
|
|
7957
|
-
if (
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
node: parent.id,
|
|
7961
|
-
text: input.text,
|
|
7962
|
-
scope,
|
|
7963
|
-
sourceThreadId: input.sourceThreadId,
|
|
7964
|
-
capturedAt: /* @__PURE__ */ new Date(),
|
|
7965
|
-
when: input.when ? new Date(input.when) : void 0,
|
|
7966
|
-
metadata: input.metadata
|
|
7967
|
-
};
|
|
7498
|
+
const row = await this.#livePage(tx, input.id);
|
|
7499
|
+
if (Number(row.isScope) === 1) throw new Error(`Cannot delete ${input.id}: it is a scope page`);
|
|
7500
|
+
if (Number(row.version) !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
|
|
7501
|
+
if ((await this.#query(tx, `SELECT 1 FROM "${T.pages}" WHERE parentId = ? AND deletedAt IS NULL LIMIT 1`, [input.id])).length > 0) throw new core.KnowledgePageHasChildrenError(input.id);
|
|
7502
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7968
7503
|
await tx.execute({
|
|
7969
|
-
sql: `
|
|
7504
|
+
sql: `UPDATE "${T.pages}" SET deletedAt = ?, deletedBy = ?, version = version + 1, updatedBy = ?, updatedAt = ? WHERE id = ?`,
|
|
7970
7505
|
args: [
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
record.sourceThreadId,
|
|
7977
|
-
record.capturedAt.toISOString(),
|
|
7978
|
-
record.when?.toISOString() ?? null,
|
|
7979
|
-
record.metadata ? JSON.stringify(record.metadata) : null,
|
|
7980
|
-
1,
|
|
7981
|
-
record.capturedAt.toISOString()
|
|
7506
|
+
now,
|
|
7507
|
+
input.deletedBy,
|
|
7508
|
+
JSON.stringify({ author: input.deletedBy }),
|
|
7509
|
+
now,
|
|
7510
|
+
input.id
|
|
7982
7511
|
]
|
|
7983
7512
|
});
|
|
7984
|
-
await
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7513
|
+
await tx.execute({
|
|
7514
|
+
sql: `DELETE FROM "${T.pinAudiences}" WHERE pinId IN (SELECT id FROM "${T.pins}" WHERE pageId = ?)`,
|
|
7515
|
+
args: [input.id]
|
|
7516
|
+
});
|
|
7517
|
+
await tx.execute({
|
|
7518
|
+
sql: `DELETE FROM "${T.pins}" WHERE pageId = ?`,
|
|
7519
|
+
args: [input.id]
|
|
7520
|
+
});
|
|
7521
|
+
await this.#logActivity(tx, "page-deleted", {
|
|
7522
|
+
scopeId: String(row.scopeId),
|
|
7523
|
+
pageId: input.id,
|
|
7524
|
+
provenance: { author: input.deletedBy }
|
|
7525
|
+
}, now);
|
|
7526
|
+
return toPage(await this.#pageRow(tx, input.id));
|
|
7995
7527
|
});
|
|
7996
|
-
return result.rows[0] ? parseKnowledge(result.rows[0]) : null;
|
|
7997
|
-
}
|
|
7998
|
-
async listKnowledgeAbout(input) {
|
|
7999
|
-
return this.#queryKnowledge(input, "about");
|
|
8000
|
-
}
|
|
8001
|
-
async listKnowledgeMentioning(input) {
|
|
8002
|
-
return this.#queryKnowledge(input, "mentioning");
|
|
8003
|
-
}
|
|
8004
|
-
async listKnowledgeRelatedTo(input) {
|
|
8005
|
-
return this.#queryKnowledge(input, "related");
|
|
8006
7528
|
}
|
|
8007
|
-
async
|
|
8008
|
-
|
|
8009
|
-
const
|
|
8010
|
-
|
|
8011
|
-
const
|
|
7529
|
+
async #hydrateNotes(executor, rows) {
|
|
7530
|
+
if (rows.length === 0) return [];
|
|
7531
|
+
const ids = jsonList(rows.map((row) => String(row.id)));
|
|
7532
|
+
const stamps = await this.#query(executor, `SELECT noteId, scopeId FROM "${T.stamps}" WHERE noteId IN (SELECT value FROM json_each(?)) ORDER BY scopeId`, [ids]);
|
|
7533
|
+
const mentions = await this.#query(executor, `SELECT noteId, pageId FROM "${T.mentions}" WHERE noteId IN (SELECT value FROM json_each(?)) ORDER BY pageId`, [ids]);
|
|
7534
|
+
return rows.map((row) => toNote(row, stamps.filter((stamp) => String(stamp.noteId) === String(row.id)).map((stamp) => String(stamp.scopeId)), mentions.filter((mention) => String(mention.noteId) === String(row.id)).map((mention) => String(mention.pageId))));
|
|
7535
|
+
}
|
|
7536
|
+
async #noteById(executor, id) {
|
|
7537
|
+
const rows = await this.#query(executor, `SELECT * FROM "${T.notes}" WHERE id = ?`, [id]);
|
|
7538
|
+
return (await this.#hydrateNotes(executor, rows))[0] ?? null;
|
|
7539
|
+
}
|
|
7540
|
+
async getNote(input) {
|
|
7541
|
+
const readable = noteReadable("n", input.read);
|
|
7542
|
+
const rows = await this.#query(this.#read, `SELECT n.* FROM "${T.notes}" n WHERE n.id = ?${input.includeDeleted ? "" : " AND n.deletedAt IS NULL"} AND ${readable.sql}`, [input.id, ...readable.args]);
|
|
7543
|
+
return (await this.#hydrateNotes(this.#read, rows))[0] ?? null;
|
|
7544
|
+
}
|
|
7545
|
+
async listNotes(input) {
|
|
7546
|
+
const core = await loadPagesCore();
|
|
7547
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7548
|
+
const cursor = decodeCursor(input.cursor, 2);
|
|
7549
|
+
const readable = noteReadable("n", input.read);
|
|
7550
|
+
const args = [input.pageId, ...readable.args];
|
|
7551
|
+
let after = "";
|
|
7552
|
+
if (cursor) {
|
|
7553
|
+
after = ` AND (n.createdAt > ? OR (n.createdAt = ? AND n.id > ?))`;
|
|
7554
|
+
args.push(cursor[0], cursor[0], cursor[1]);
|
|
7555
|
+
}
|
|
8012
7556
|
args.push(limit + 1);
|
|
8013
|
-
const
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
|
|
7557
|
+
const rows = await this.#query(this.#read, `SELECT n.* FROM "${T.notes}" n WHERE n.pageId = ?${input.includeDeleted ? "" : " AND n.deletedAt IS NULL"} AND ${readable.sql}${after} ORDER BY n.createdAt, n.id LIMIT ?`, args);
|
|
7558
|
+
return this.#noteOutput(rows, limit);
|
|
7559
|
+
}
|
|
7560
|
+
async countChildPages(input) {
|
|
7561
|
+
const ids = [...new Set(input.parentIds)];
|
|
7562
|
+
const counts = Object.fromEntries(ids.map((id) => [id, 0]));
|
|
7563
|
+
if (ids.length === 0) return counts;
|
|
7564
|
+
const readable = pageReadable("p", input.read);
|
|
7565
|
+
const rows = await this.#query(this.#read, `SELECT p.parentId AS parentId, COUNT(*) AS total FROM "${T.pages}" p WHERE p.parentId IN (SELECT value FROM json_each(?)) AND p.isScope = 0 AND p.deletedAt IS NULL AND ${readable.sql} GROUP BY p.parentId`, [jsonList(ids), ...readable.args]);
|
|
7566
|
+
for (const row of rows) counts[String(row.parentId)] = Number(row.total);
|
|
7567
|
+
return counts;
|
|
7568
|
+
}
|
|
7569
|
+
async countNotes(input) {
|
|
7570
|
+
const ids = [...new Set(input.pageIds)];
|
|
7571
|
+
const counts = Object.fromEntries(ids.map((id) => [id, 0]));
|
|
7572
|
+
if (ids.length === 0) return counts;
|
|
7573
|
+
const readable = noteReadable("n", input.read);
|
|
7574
|
+
const rows = await this.#query(this.#read, `SELECT n.pageId AS pageId, COUNT(*) AS total FROM "${T.notes}" n WHERE n.pageId IN (SELECT value FROM json_each(?)) AND n.deletedAt IS NULL AND ${readable.sql} GROUP BY n.pageId`, [jsonList(ids), ...readable.args]);
|
|
7575
|
+
for (const row of rows) counts[String(row.pageId)] = Number(row.total);
|
|
7576
|
+
return counts;
|
|
7577
|
+
}
|
|
7578
|
+
async listNotesMentioning(input) {
|
|
7579
|
+
const core = await loadPagesCore();
|
|
7580
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7581
|
+
const cursor = decodeCursor(input.cursor, 2);
|
|
7582
|
+
const readable = noteReadable("n", input.read);
|
|
7583
|
+
const args = [
|
|
7584
|
+
input.pageId,
|
|
7585
|
+
input.pageId,
|
|
7586
|
+
...readable.args
|
|
7587
|
+
];
|
|
7588
|
+
let after = "";
|
|
7589
|
+
if (cursor) {
|
|
7590
|
+
after = ` AND (n.createdAt < ? OR (n.createdAt = ? AND n.id < ?))`;
|
|
7591
|
+
args.push(cursor[0], cursor[0], cursor[1]);
|
|
7592
|
+
}
|
|
7593
|
+
args.push(limit + 1);
|
|
7594
|
+
const rows = await this.#query(this.#read, `SELECT n.* FROM "${T.notes}" n WHERE n.pageId <> ? AND n.deletedAt IS NULL AND EXISTS (SELECT 1 FROM "${T.mentions}" m WHERE m.noteId = n.id AND m.pageId = ?) AND ${readable.sql}${after} ORDER BY n.createdAt DESC, n.id DESC LIMIT ?`, args);
|
|
7595
|
+
return this.#noteOutput(rows, limit);
|
|
7596
|
+
}
|
|
7597
|
+
async #noteOutput(rows, limit) {
|
|
7598
|
+
const page = rows.slice(0, limit);
|
|
8017
7599
|
return {
|
|
8018
|
-
|
|
8019
|
-
|
|
7600
|
+
notes: await this.#hydrateNotes(this.#read, page),
|
|
7601
|
+
...rows.length > limit ? { nextCursor: encodeCursor([String(page[page.length - 1].createdAt), String(page[page.length - 1].id)]) } : {}
|
|
8020
7602
|
};
|
|
8021
7603
|
}
|
|
8022
|
-
async
|
|
7604
|
+
async #assertScopes(tx, ids) {
|
|
7605
|
+
if (ids.length === 0) throw new Error("A note needs at least one stamp scope");
|
|
7606
|
+
const found = await this.#query(tx, `SELECT id FROM "${T.pages}" WHERE isScope = 1 AND id IN (SELECT value FROM json_each(?))`, [jsonList(ids)]);
|
|
7607
|
+
const known = new Set(found.map((row) => String(row.id)));
|
|
7608
|
+
const missing = ids.find((id) => !known.has(id));
|
|
7609
|
+
if (missing) throw new Error(`Note stamp ${missing} is not a scope page`);
|
|
7610
|
+
}
|
|
7611
|
+
async addNote(input) {
|
|
7612
|
+
await assertKnowledgeNoteTextWithinBound(input.text);
|
|
8023
7613
|
return this.#transaction(async (tx) => {
|
|
8024
|
-
const
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
const
|
|
7614
|
+
const page = await this.#livePage(tx, input.pageId);
|
|
7615
|
+
const stamps = [...new Set(input.stampScopeIds)];
|
|
7616
|
+
await this.#assertScopes(tx, stamps);
|
|
7617
|
+
const mentions = [...new Set(input.mentions)];
|
|
7618
|
+
for (const mention of mentions) await this.#livePage(tx, mention);
|
|
7619
|
+
const id = input.id ?? (0, _mastra_core_storage.createKnowledgeUlid)();
|
|
7620
|
+
if ((await this.#query(tx, `SELECT 1 FROM "${T.notes}" WHERE id = ?`, [id])).length > 0) throw new _mastra_core_storage.KnowledgeConflictError(id);
|
|
7621
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8028
7622
|
await tx.execute({
|
|
8029
|
-
sql: `
|
|
7623
|
+
sql: `INSERT INTO "${T.notes}" (id, pageId, text, "when", provenance, version, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
8030
7624
|
args: [
|
|
8031
|
-
|
|
8032
|
-
input.
|
|
8033
|
-
|
|
8034
|
-
input.
|
|
7625
|
+
id,
|
|
7626
|
+
input.pageId,
|
|
7627
|
+
input.text,
|
|
7628
|
+
input.when ? input.when.toISOString() : null,
|
|
7629
|
+
JSON.stringify(input.provenance),
|
|
7630
|
+
now,
|
|
7631
|
+
now
|
|
8035
7632
|
]
|
|
8036
7633
|
});
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
7634
|
+
for (const stamp of stamps) await tx.execute({
|
|
7635
|
+
sql: `INSERT INTO "${T.stamps}" (noteId, scopeId) VALUES (?, ?)`,
|
|
7636
|
+
args: [id, stamp]
|
|
7637
|
+
});
|
|
7638
|
+
for (const mention of mentions) await tx.execute({
|
|
7639
|
+
sql: `INSERT INTO "${T.mentions}" (noteId, pageId) VALUES (?, ?)`,
|
|
7640
|
+
args: [id, mention]
|
|
7641
|
+
});
|
|
7642
|
+
await this.#logActivity(tx, "note-added", {
|
|
7643
|
+
scopeId: String(page.scopeId),
|
|
7644
|
+
pageId: input.pageId,
|
|
7645
|
+
noteId: id,
|
|
7646
|
+
provenance: input.provenance
|
|
7647
|
+
}, now);
|
|
7648
|
+
return await this.#noteById(tx, id);
|
|
8044
7649
|
});
|
|
8045
7650
|
}
|
|
8046
|
-
async
|
|
7651
|
+
async #noteWithPage(tx, id) {
|
|
7652
|
+
const note = (await this.#query(tx, `SELECT * FROM "${T.notes}" WHERE id = ?`, [id]))[0];
|
|
7653
|
+
if (!note) throw new _mastra_core_storage.KnowledgeNotFoundError("note", id);
|
|
7654
|
+
const page = await this.#pageRow(tx, String(note.pageId));
|
|
7655
|
+
if (!page) throw new _mastra_core_storage.KnowledgeNotFoundError("page", String(note.pageId));
|
|
7656
|
+
return {
|
|
7657
|
+
note,
|
|
7658
|
+
page
|
|
7659
|
+
};
|
|
7660
|
+
}
|
|
7661
|
+
restampNote(input) {
|
|
8047
7662
|
return this.#transaction(async (tx) => {
|
|
8048
|
-
const
|
|
8049
|
-
if (
|
|
8050
|
-
if (
|
|
7663
|
+
const { note, page } = await this.#noteWithPage(tx, input.id);
|
|
7664
|
+
if (note.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("note", input.id);
|
|
7665
|
+
if (Number(note.version) !== input.expectedVersion) throw new _mastra_core_storage.KnowledgeConflictError(input.id);
|
|
7666
|
+
const stamps = [...new Set(input.stampScopeIds)];
|
|
7667
|
+
await this.#assertScopes(tx, stamps);
|
|
7668
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8051
7669
|
await tx.execute({
|
|
8052
|
-
sql: `
|
|
8053
|
-
args: [
|
|
7670
|
+
sql: `DELETE FROM "${T.stamps}" WHERE noteId = ?`,
|
|
7671
|
+
args: [input.id]
|
|
8054
7672
|
});
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
7673
|
+
for (const stamp of stamps) await tx.execute({
|
|
7674
|
+
sql: `INSERT INTO "${T.stamps}" (noteId, scopeId) VALUES (?, ?)`,
|
|
7675
|
+
args: [input.id, stamp]
|
|
7676
|
+
});
|
|
7677
|
+
await tx.execute({
|
|
7678
|
+
sql: `UPDATE "${T.notes}" SET version = version + 1, updatedAt = ? WHERE id = ?`,
|
|
7679
|
+
args: [now, input.id]
|
|
7680
|
+
});
|
|
7681
|
+
await this.#logActivity(tx, "note-restamped", {
|
|
7682
|
+
scopeId: String(page.scopeId),
|
|
7683
|
+
pageId: String(page.id),
|
|
7684
|
+
noteId: input.id,
|
|
7685
|
+
provenance: input.provenance
|
|
7686
|
+
}, now);
|
|
7687
|
+
return await this.#noteById(tx, input.id);
|
|
8062
7688
|
});
|
|
8063
7689
|
}
|
|
8064
|
-
|
|
8065
|
-
const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
|
|
7690
|
+
removeNote(input) {
|
|
8066
7691
|
return this.#transaction(async (tx) => {
|
|
8067
|
-
const
|
|
8068
|
-
if (
|
|
8069
|
-
const
|
|
7692
|
+
const { note, page } = await this.#noteWithPage(tx, input.id);
|
|
7693
|
+
if (note.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("note", input.id);
|
|
7694
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8070
7695
|
await tx.execute({
|
|
8071
|
-
sql: `UPDATE "${
|
|
7696
|
+
sql: `UPDATE "${T.notes}" SET deletedAt = ?, deletedBy = ?, version = version + 1, updatedAt = ? WHERE id = ?`,
|
|
8072
7697
|
args: [
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
7698
|
+
now,
|
|
7699
|
+
input.deletedBy,
|
|
7700
|
+
now,
|
|
8076
7701
|
input.id
|
|
8077
7702
|
]
|
|
8078
7703
|
});
|
|
8079
|
-
await this.#
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
};
|
|
7704
|
+
await this.#logActivity(tx, "note-removed", {
|
|
7705
|
+
scopeId: String(page.scopeId),
|
|
7706
|
+
pageId: String(page.id),
|
|
7707
|
+
noteId: input.id,
|
|
7708
|
+
provenance: input.provenance
|
|
7709
|
+
}, now);
|
|
7710
|
+
return await this.#noteById(tx, input.id);
|
|
8087
7711
|
});
|
|
8088
7712
|
}
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
args: [
|
|
8098
|
-
...keys,
|
|
8099
|
-
query,
|
|
8100
|
-
query,
|
|
8101
|
-
query,
|
|
8102
|
-
query,
|
|
8103
|
-
input.limit ?? 20
|
|
8104
|
-
]
|
|
8105
|
-
})).rows.map((row) => ({
|
|
8106
|
-
type: String(row.type),
|
|
8107
|
-
id: String(row.id),
|
|
8108
|
-
recordId: String(row.id),
|
|
8109
|
-
name: String(row.name),
|
|
8110
|
-
text: [
|
|
8111
|
-
String(row.name),
|
|
8112
|
-
...row.description ? [String(row.description)] : [],
|
|
8113
|
-
...row.content ? [String(row.content)] : []
|
|
8114
|
-
].join("\n"),
|
|
8115
|
-
scope: parseJson$3(row.scopeJson)
|
|
8116
|
-
}));
|
|
8117
|
-
if (results.length < (input.limit ?? 20)) {
|
|
8118
|
-
const knowledge = await this.#client.execute({
|
|
8119
|
-
sql: `SELECT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson,n.name,json(n.scope) AS parentScopeJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" k CROSS JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=k.node AND n.type='node' AND n.mergedInto IS NULL WHERE k.deletedAt IS NULL AND ${visibleSql(keys, "k.scopeKey")} AND lower(k.text) LIKE ? ESCAPE '=' ORDER BY k.id DESC LIMIT ?`,
|
|
8120
|
-
args: [
|
|
8121
|
-
...keys,
|
|
8122
|
-
query,
|
|
8123
|
-
(input.limit ?? 20) - results.length
|
|
8124
|
-
]
|
|
7713
|
+
restoreNote(input) {
|
|
7714
|
+
return this.#transaction(async (tx) => {
|
|
7715
|
+
const { note, page } = await this.#noteWithPage(tx, input.id);
|
|
7716
|
+
if (!note.deletedAt) throw new _mastra_core_storage.KnowledgeNotFoundError("note", input.id);
|
|
7717
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7718
|
+
await tx.execute({
|
|
7719
|
+
sql: `UPDATE "${T.notes}" SET deletedAt = NULL, deletedBy = NULL, version = version + 1, updatedAt = ? WHERE id = ?`,
|
|
7720
|
+
args: [now, input.id]
|
|
8125
7721
|
});
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
scope: parseJson$3(row.scopeJson)
|
|
8135
|
-
};
|
|
8136
|
-
}));
|
|
8137
|
-
}
|
|
8138
|
-
return results;
|
|
8139
|
-
}
|
|
8140
|
-
/**
|
|
8141
|
-
* @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
|
|
8142
|
-
* cursor. Always throws.
|
|
8143
|
-
*/
|
|
8144
|
-
async getCurationCursor(_input) {
|
|
8145
|
-
throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
|
|
8146
|
-
}
|
|
8147
|
-
/**
|
|
8148
|
-
* @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
|
|
8149
|
-
* cursor. Always throws.
|
|
8150
|
-
*/
|
|
8151
|
-
async advanceCurationCursor(_input) {
|
|
8152
|
-
throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
|
|
7722
|
+
await this.#logActivity(tx, "note-restored", {
|
|
7723
|
+
scopeId: String(page.scopeId),
|
|
7724
|
+
pageId: String(page.id),
|
|
7725
|
+
noteId: input.id,
|
|
7726
|
+
provenance: input.provenance
|
|
7727
|
+
}, now);
|
|
7728
|
+
return await this.#noteById(tx, input.id);
|
|
7729
|
+
});
|
|
8153
7730
|
}
|
|
8154
|
-
async
|
|
8155
|
-
const
|
|
8156
|
-
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
7731
|
+
async searchPages(input) {
|
|
7732
|
+
const words = knowledgeSearchWords(input.query);
|
|
7733
|
+
if (!words.length) return [];
|
|
7734
|
+
const phrase = input.query.trim().toLowerCase().replace(/\s+/g, " ");
|
|
7735
|
+
const limit = Math.max(1, input.limit ?? 20);
|
|
7736
|
+
const likes = words.map((word) => `%${escapeLike(word)}%`);
|
|
7737
|
+
const readable = pageReadable("p", input.read);
|
|
7738
|
+
const scopeFilter = input.scopeIds ? ` AND p.scopeId IN (SELECT value FROM json_each(?))` : "";
|
|
7739
|
+
const scopeArgs = input.scopeIds ? [jsonList(input.scopeIds)] : [];
|
|
7740
|
+
const field = (column) => `lower(COALESCE(${column}, '')) LIKE ? ESCAPE '='`;
|
|
7741
|
+
const pageMatch = words.map(() => `(${field("p.name")} OR ${field("p.description")} OR ${field("p.body")})`).join(" OR ");
|
|
7742
|
+
const pageScore = words.map(() => `(CASE WHEN ${field("p.name")} THEN ${KNOWLEDGE_SEARCH_WEIGHTS.name} WHEN ${field("p.description")} THEN ${KNOWLEDGE_SEARCH_WEIGHTS.description} WHEN ${field("p.body")} THEN ${KNOWLEDGE_SEARCH_WEIGHTS.body} ELSE 0 END)`).join(" + ");
|
|
7743
|
+
const pageCount = words.map(() => `(CASE WHEN ${field("p.name")} OR ${field("p.description")} OR ${field("p.body")} THEN 1 ELSE 0 END)`).join(" + ");
|
|
7744
|
+
const triple = likes.flatMap((like) => [
|
|
7745
|
+
like,
|
|
7746
|
+
like,
|
|
7747
|
+
like
|
|
7748
|
+
]);
|
|
7749
|
+
const hits = (await this.#query(this.#read, `SELECT p.* FROM "${T.pages}" p WHERE p.deletedAt IS NULL AND p.isScope = 0 AND ${readable.sql}${scopeFilter} AND (${pageMatch}) ORDER BY CASE WHEN instr(p.canonicalName, ?) > 0 THEN 0 ELSE 1 END, (${pageCount}) DESC, (${pageScore}) DESC, p.updatedAt DESC, p.id ASC LIMIT ?`, [
|
|
7750
|
+
...readable.args,
|
|
7751
|
+
...scopeArgs,
|
|
7752
|
+
...triple,
|
|
7753
|
+
phrase,
|
|
7754
|
+
...triple,
|
|
7755
|
+
...triple,
|
|
7756
|
+
limit
|
|
7757
|
+
])).map((row) => ({
|
|
7758
|
+
type: "page",
|
|
7759
|
+
page: toPage(row)
|
|
8171
7760
|
}));
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
const
|
|
8175
|
-
const
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
args
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
7761
|
+
if (hits.length >= limit) return hits;
|
|
7762
|
+
const noteVisible = noteReadable("n", input.read);
|
|
7763
|
+
const noteHas = words.map(() => `lower(n.text) LIKE ? ESCAPE '='`);
|
|
7764
|
+
const noteRows = await this.#query(this.#read, `SELECT n.* FROM "${T.notes}" n JOIN "${T.pages}" p ON p.id = n.pageId WHERE n.deletedAt IS NULL AND p.deletedAt IS NULL${scopeFilter} AND (${noteHas.join(" OR ")}) AND ${noteVisible.sql} ORDER BY (${noteHas.map((has) => `(CASE WHEN ${has} THEN 1 ELSE 0 END)`).join(" + ")}) DESC, n.updatedAt DESC, n.id DESC LIMIT ?`, [
|
|
7765
|
+
...scopeArgs,
|
|
7766
|
+
...likes,
|
|
7767
|
+
...noteVisible.args,
|
|
7768
|
+
...likes,
|
|
7769
|
+
limit - hits.length
|
|
7770
|
+
]);
|
|
7771
|
+
const notes = await this.#hydrateNotes(this.#read, noteRows);
|
|
7772
|
+
for (const note of notes) {
|
|
7773
|
+
const page = await this.#pageRow(this.#read, note.pageId);
|
|
7774
|
+
if (page) hits.push({
|
|
7775
|
+
type: "note",
|
|
7776
|
+
note,
|
|
7777
|
+
page: toPage(page)
|
|
7778
|
+
});
|
|
8184
7779
|
}
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
7780
|
+
return hits;
|
|
7781
|
+
}
|
|
7782
|
+
async listPageActivity(input) {
|
|
7783
|
+
const core = await loadPagesCore();
|
|
7784
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7785
|
+
const cursor = decodeCursor(input.cursor, 1);
|
|
7786
|
+
const readable = pageReadable("p", input.read);
|
|
7787
|
+
const noteVisible = noteReadable("n", input.read);
|
|
7788
|
+
const args = [...readable.args];
|
|
7789
|
+
let filters = "";
|
|
7790
|
+
if (input.scopeId !== void 0) {
|
|
7791
|
+
filters += ` AND p.scopeId = ?`;
|
|
7792
|
+
args.push(input.scopeId);
|
|
7793
|
+
}
|
|
7794
|
+
if (input.pageId !== void 0) {
|
|
7795
|
+
filters += ` AND e.pageId = ?`;
|
|
7796
|
+
args.push(input.pageId);
|
|
7797
|
+
}
|
|
7798
|
+
if (cursor) {
|
|
7799
|
+
filters += ` AND e.id < ?`;
|
|
7800
|
+
args.push(cursor[0]);
|
|
7801
|
+
}
|
|
7802
|
+
args.push(...noteVisible.args, limit + 1);
|
|
7803
|
+
const rows = await this.#query(this.#read, `SELECT e.* FROM "${T.activity}" e JOIN "${T.pages}" p ON p.id = e.pageId WHERE ${readable.sql}${filters} AND (e.noteId IS NULL OR EXISTS (SELECT 1 FROM "${T.notes}" n WHERE n.id = e.noteId AND ${noteVisible.sql})) ORDER BY e.id DESC LIMIT ?`, args);
|
|
7804
|
+
const page = rows.slice(0, limit);
|
|
7805
|
+
return {
|
|
7806
|
+
events: page.map((row) => ({
|
|
7807
|
+
id: String(row.id),
|
|
7808
|
+
action: String(row.action),
|
|
7809
|
+
scopeId: String(row.scopeId),
|
|
7810
|
+
pageId: String(row.pageId),
|
|
7811
|
+
...row.noteId ? { noteId: String(row.noteId) } : {},
|
|
7812
|
+
provenance: provenance(row.provenance),
|
|
7813
|
+
createdAt: new Date(String(row.createdAt))
|
|
7814
|
+
})),
|
|
7815
|
+
...rows.length > limit ? { nextCursor: encodeCursor([String(page[page.length - 1].id)]) } : {}
|
|
7816
|
+
};
|
|
8190
7817
|
}
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
7818
|
+
/** Rewrites the audience index rows of one pin to match `audience`. */
|
|
7819
|
+
async #indexAudience(tx, pinId, audience) {
|
|
7820
|
+
await tx.execute({
|
|
7821
|
+
sql: `DELETE FROM "${T.pinAudiences}" WHERE pinId = ?`,
|
|
7822
|
+
args: [pinId]
|
|
7823
|
+
});
|
|
7824
|
+
for (const entry of audience) await tx.execute({
|
|
7825
|
+
sql: `INSERT OR IGNORE INTO "${T.pinAudiences}" (pinId, audience) VALUES (?, ?)`,
|
|
7826
|
+
args: [pinId, entry]
|
|
7827
|
+
});
|
|
7828
|
+
}
|
|
7829
|
+
addPinAudience(input) {
|
|
8194
7830
|
return this.#transaction(async (tx) => {
|
|
8195
|
-
const
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
7831
|
+
const page = await this.#livePage(tx, input.pageId);
|
|
7832
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7833
|
+
const existing = (await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId = ?`, [input.pageId]))[0];
|
|
7834
|
+
if (!existing) {
|
|
7835
|
+
const id = (0, _mastra_core_storage.createKnowledgeUlid)();
|
|
7836
|
+
await tx.execute({
|
|
7837
|
+
sql: `INSERT INTO "${T.pins}" (id, pageId, audience, createdBy, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
7838
|
+
args: [
|
|
7839
|
+
id,
|
|
7840
|
+
input.pageId,
|
|
7841
|
+
JSON.stringify([...new Set(input.audience)]),
|
|
7842
|
+
input.createdBy,
|
|
7843
|
+
now,
|
|
7844
|
+
now
|
|
7845
|
+
]
|
|
7846
|
+
});
|
|
7847
|
+
await this.#indexAudience(tx, id, [...new Set(input.audience)]);
|
|
7848
|
+
await this.#logActivity(tx, "pin-added", {
|
|
7849
|
+
scopeId: String(page.scopeId),
|
|
7850
|
+
pageId: input.pageId,
|
|
7851
|
+
provenance: { author: input.createdBy }
|
|
7852
|
+
}, now);
|
|
7853
|
+
return toPin((await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE id = ?`, [id]))[0]);
|
|
8205
7854
|
}
|
|
8206
|
-
|
|
8207
|
-
const
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
}
|
|
7855
|
+
const current = JSON.parse(String(existing.audience));
|
|
7856
|
+
const merged = [.../* @__PURE__ */ new Set([...current, ...input.audience])];
|
|
7857
|
+
if (merged.length !== current.length) {
|
|
7858
|
+
await tx.execute({
|
|
7859
|
+
sql: `UPDATE "${T.pins}" SET audience = ?, updatedAt = ? WHERE id = ?`,
|
|
7860
|
+
args: [
|
|
7861
|
+
JSON.stringify(merged),
|
|
7862
|
+
now,
|
|
7863
|
+
String(existing.id)
|
|
7864
|
+
]
|
|
7865
|
+
});
|
|
7866
|
+
await this.#indexAudience(tx, String(existing.id), merged);
|
|
7867
|
+
await this.#logActivity(tx, "pin-added", {
|
|
7868
|
+
scopeId: String(page.scopeId),
|
|
7869
|
+
pageId: input.pageId,
|
|
7870
|
+
provenance: { author: input.createdBy }
|
|
7871
|
+
}, now);
|
|
7872
|
+
}
|
|
7873
|
+
return toPin((await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE id = ?`, [String(existing.id)]))[0]);
|
|
8224
7874
|
});
|
|
8225
7875
|
}
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
8231
|
-
|
|
7876
|
+
removePinAudience(input) {
|
|
7877
|
+
return this.#transaction(async (tx) => {
|
|
7878
|
+
const existing = (await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId = ?`, [input.pageId]))[0];
|
|
7879
|
+
if (!existing) return null;
|
|
7880
|
+
const current = JSON.parse(String(existing.audience));
|
|
7881
|
+
const remove = new Set(input.audience);
|
|
7882
|
+
const remaining = current.filter((entry) => !remove.has(entry));
|
|
7883
|
+
if (remaining.length === current.length) return toPin(existing);
|
|
7884
|
+
const page = await this.#pageRow(tx, input.pageId);
|
|
7885
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7886
|
+
if (page) await this.#logActivity(tx, "pin-removed", {
|
|
7887
|
+
scopeId: String(page.scopeId),
|
|
7888
|
+
pageId: input.pageId,
|
|
7889
|
+
provenance: { author: input.removedBy }
|
|
7890
|
+
}, now);
|
|
7891
|
+
if (remaining.length === 0) {
|
|
7892
|
+
await this.#indexAudience(tx, String(existing.id), []);
|
|
7893
|
+
await tx.execute({
|
|
7894
|
+
sql: `DELETE FROM "${T.pins}" WHERE id = ?`,
|
|
7895
|
+
args: [String(existing.id)]
|
|
7896
|
+
});
|
|
7897
|
+
return null;
|
|
7898
|
+
}
|
|
7899
|
+
await tx.execute({
|
|
7900
|
+
sql: `UPDATE "${T.pins}" SET audience = ?, updatedAt = ? WHERE id = ?`,
|
|
8232
7901
|
args: [
|
|
7902
|
+
JSON.stringify(remaining),
|
|
8233
7903
|
now,
|
|
8234
|
-
id
|
|
8235
|
-
input.workerId
|
|
8236
|
-
]
|
|
8237
|
-
});
|
|
8238
|
-
});
|
|
8239
|
-
}
|
|
8240
|
-
async releaseSemanticOutbox(input) {
|
|
8241
|
-
if (!input.ids.length) return;
|
|
8242
|
-
await this.#transaction(async (tx) => {
|
|
8243
|
-
for (const id of input.ids) await tx.execute({
|
|
8244
|
-
sql: `UPDATE "${_mastra_core_storage.TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" SET status='pending',availableAt=?,claimedAt=NULL,claimedBy=NULL WHERE id=? AND status='processing' AND claimedBy=?`,
|
|
8245
|
-
args: [
|
|
8246
|
-
(input.retryAt ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
8247
|
-
id,
|
|
8248
|
-
input.workerId
|
|
7904
|
+
String(existing.id)
|
|
8249
7905
|
]
|
|
8250
7906
|
});
|
|
7907
|
+
await this.#indexAudience(tx, String(existing.id), remaining);
|
|
7908
|
+
return toPin((await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE id = ?`, [String(existing.id)]))[0]);
|
|
8251
7909
|
});
|
|
8252
7910
|
}
|
|
8253
|
-
async
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
7911
|
+
async listPins(input) {
|
|
7912
|
+
const core = await loadPagesCore();
|
|
7913
|
+
const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
|
|
7914
|
+
const cursor = decodeCursor(input.cursor, 2);
|
|
7915
|
+
const readable = pageReadable("p", input.read);
|
|
7916
|
+
const args = [...readable.args];
|
|
7917
|
+
let filters = "";
|
|
7918
|
+
if (input.audience !== "all") {
|
|
7919
|
+
filters += ` AND pin.id IN (SELECT pa.pinId FROM json_each(?) a JOIN "${T.pinAudiences}" pa ON pa.audience = a.value)`;
|
|
7920
|
+
args.push(jsonList(input.audience));
|
|
7921
|
+
}
|
|
7922
|
+
if (input.pageIds) {
|
|
7923
|
+
filters += ` AND pin.pageId IN (SELECT value FROM json_each(?))`;
|
|
7924
|
+
args.push(jsonList(input.pageIds));
|
|
7925
|
+
}
|
|
7926
|
+
if (input.excludePageIds?.length) {
|
|
7927
|
+
filters += ` AND pin.pageId NOT IN (SELECT value FROM json_each(?))`;
|
|
7928
|
+
args.push(jsonList(input.excludePageIds));
|
|
7929
|
+
}
|
|
7930
|
+
if (input.withBody) filters += ` AND p.body IS NOT NULL AND trim(p.body, char(32, 9, 10, 11, 12, 13)) <> ''`;
|
|
7931
|
+
const from = `FROM "${T.pins}" pin JOIN "${T.pages}" p ON p.id = pin.pageId WHERE p.deletedAt IS NULL AND ${readable.sql}${filters}`;
|
|
7932
|
+
let total;
|
|
7933
|
+
if (input.count) total = Number((await this.#query(this.#read, `SELECT COUNT(*) AS n ${from}`, args))[0].n);
|
|
7934
|
+
const pageArgs = [...args];
|
|
7935
|
+
let cursorFilter = "";
|
|
7936
|
+
if (cursor) {
|
|
7937
|
+
cursorFilter = ` AND (pin.createdAt < ? OR (pin.createdAt = ? AND pin.id < ?))`;
|
|
7938
|
+
pageArgs.push(cursor[0], cursor[0], cursor[1]);
|
|
7939
|
+
}
|
|
7940
|
+
pageArgs.push(limit + 1);
|
|
7941
|
+
const rows = await this.#query(this.#read, `SELECT pin.*, p.name AS pageName ${from}${cursorFilter} ORDER BY pin.createdAt DESC, pin.id DESC LIMIT ?`, pageArgs);
|
|
7942
|
+
const page = rows.slice(0, limit);
|
|
7943
|
+
return {
|
|
7944
|
+
pins: page.map(toListedPin),
|
|
7945
|
+
...rows.length > limit ? { nextCursor: encodeCursor([String(page[page.length - 1].createdAt), String(page[page.length - 1].id)]) } : {},
|
|
7946
|
+
...total === void 0 ? {} : { total }
|
|
7947
|
+
};
|
|
8272
7948
|
}
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
args: [canonicalName(name), ...keys]
|
|
8285
|
-
})).rows.map(parseNode).sort((left, right) => right.scope.length - left.scope.length);
|
|
8286
|
-
for (const candidate of candidates) {
|
|
8287
|
-
const terminal = await this.#resolveTerminalNode(executor, candidate.id);
|
|
8288
|
-
if (terminal && (0, _mastra_core_storage.isKnowledgeScopeVisible)(terminal.scope, scope)) return terminal;
|
|
8289
|
-
}
|
|
8290
|
-
return null;
|
|
8291
|
-
}
|
|
8292
|
-
async #resolveTerminalNode(executor, id) {
|
|
8293
|
-
let node = await this.#getNode(executor, id);
|
|
8294
|
-
const seen = /* @__PURE__ */ new Set();
|
|
8295
|
-
while (node?.mergedInto) {
|
|
8296
|
-
if (seen.has(node.id)) throw new Error(`Knowledge merge cycle detected at ${node.id}`);
|
|
8297
|
-
seen.add(node.id);
|
|
8298
|
-
node = await this.#getNode(executor, node.mergedInto);
|
|
7949
|
+
};
|
|
7950
|
+
// src/storage/domains/knowledge/index.ts
|
|
7951
|
+
function canonicalizeLibSQLUrl(url) {
|
|
7952
|
+
if (url.includes(":memory:")) return /[?&]cache=shared(?:&|$)/.test(url) ? "file::memory:?cache=shared" : void 0;
|
|
7953
|
+
if (url.startsWith("file:")) {
|
|
7954
|
+
const [path$1] = url.slice(5).split("?");
|
|
7955
|
+
if (url.startsWith("file://")) {
|
|
7956
|
+
const parsed = new URL(url);
|
|
7957
|
+
const host = parsed.hostname.toLocaleLowerCase();
|
|
7958
|
+
const filePath = host && host !== "localhost" ? `//${host}${parsed.pathname}` : parsed.pathname;
|
|
7959
|
+
return `file:${(0, path.resolve)(decodeURIComponent(filePath))}`;
|
|
8299
7960
|
}
|
|
8300
|
-
return
|
|
7961
|
+
return `file:${(0, path.resolve)(decodeURIComponent(path$1))}`;
|
|
8301
7962
|
}
|
|
8302
|
-
|
|
8303
|
-
const
|
|
8304
|
-
|
|
8305
|
-
|
|
7963
|
+
try {
|
|
7964
|
+
const parsed = new URL(url);
|
|
7965
|
+
const port = parsed.port || (parsed.protocol === "libsql:" || parsed.protocol === "https:" ? "443" : "80");
|
|
7966
|
+
return `${parsed.protocol}//${parsed.hostname.toLocaleLowerCase()}:${port}${parsed.pathname}`;
|
|
7967
|
+
} catch {
|
|
7968
|
+
return url;
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7971
|
+
const unidentifiedClientIsolationKey = {};
|
|
7972
|
+
function getLibSQLKnowledgeIsolationKey(config, client) {
|
|
7973
|
+
if (config.storageIsolationKey !== void 0) return config.storageIsolationKey;
|
|
7974
|
+
if (!config.url) return unidentifiedClientIsolationKey;
|
|
7975
|
+
const urlKey = canonicalizeLibSQLUrl(config.url);
|
|
7976
|
+
return urlKey ? `libsql:${urlKey}` : client ?? config.client ?? config;
|
|
7977
|
+
}
|
|
7978
|
+
var KnowledgeLibSQL = class extends _mastra_core_storage.KnowledgeStorage {
|
|
7979
|
+
#client;
|
|
7980
|
+
#db;
|
|
7981
|
+
#pages;
|
|
7982
|
+
constructor(config) {
|
|
7983
|
+
const client = resolveClient(config);
|
|
7984
|
+
const storageIsolationKey = config.storageIsolationKey ?? getLibSQLKnowledgeIsolationKey(config, client);
|
|
7985
|
+
super({ storageIsolationKey });
|
|
7986
|
+
this.#client = client;
|
|
7987
|
+
this.#db = new LibSQLDB({
|
|
7988
|
+
client: this.#client,
|
|
7989
|
+
maxRetries: config.maxRetries,
|
|
7990
|
+
initialBackoffMs: config.initialBackoffMs
|
|
8306
7991
|
});
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
const scope = (0, _mastra_core_storage.canonicalizeKnowledgeScope)(input.scope);
|
|
8311
|
-
const node = await this.#resolveTerminalNode(this.#client, nodeReferenceId(input.node));
|
|
8312
|
-
if (!node) return { records: [] };
|
|
8313
|
-
const keys = visibleScopeKeys(scope);
|
|
8314
|
-
const includesMentions = relationship !== "about";
|
|
8315
|
-
const relationSql = relationship === "about" ? "k.node=?" : relationship === "mentioning" ? "m.recordId=?" : "(k.node=? OR m.recordId=?)";
|
|
8316
|
-
const args = [
|
|
8317
|
-
node.id,
|
|
8318
|
-
...relationship === "related" ? [node.id] : [],
|
|
8319
|
-
...keys
|
|
8320
|
-
];
|
|
8321
|
-
if (input.after) args.push(input.after);
|
|
8322
|
-
args.push((input.limit ?? 100) + 1);
|
|
8323
|
-
const result = await this.#client.execute({
|
|
8324
|
-
sql: `SELECT DISTINCT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson FROM "${_mastra_core_storage.TABLE_KNOWLEDGE_RECORDS}" k${includesMentions ? ` LEFT JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_MENTIONS}" m ON m.sourceType='record' AND m.sourceId=k.id` : ""} WHERE ${relationSql} AND ${visibleSql(keys, "k.scopeKey")}${input.includeDeleted ? "" : " AND k.deletedAt IS NULL"}${input.after ? " AND k.id < ?" : ""} ORDER BY k.id DESC LIMIT ?`,
|
|
8325
|
-
args
|
|
7992
|
+
this.#pages = new LibSQLKnowledgePages({
|
|
7993
|
+
read: this.#client,
|
|
7994
|
+
transaction: (operation) => this.#transaction(operation)
|
|
8326
7995
|
});
|
|
8327
|
-
|
|
8328
|
-
|
|
7996
|
+
}
|
|
7997
|
+
getCapabilities() {
|
|
8329
7998
|
return {
|
|
8330
|
-
|
|
8331
|
-
|
|
7999
|
+
contractVersion: 4,
|
|
8000
|
+
schemaVersion: 4,
|
|
8001
|
+
supportsSchemaInspection: true,
|
|
8002
|
+
supportsExplicitReset: true
|
|
8332
8003
|
};
|
|
8333
8004
|
}
|
|
8334
|
-
async
|
|
8335
|
-
|
|
8336
|
-
for (const address of addresses) {
|
|
8337
|
-
const result = await executor.execute({
|
|
8338
|
-
sql: `SELECT scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" WHERE address=?`,
|
|
8339
|
-
args: [address]
|
|
8340
|
-
});
|
|
8341
|
-
if (result.rows[0]?.scopeNodeId != null) scopeNodeIds.add(String(result.rows[0].scopeNodeId));
|
|
8342
|
-
}
|
|
8343
|
-
return [...scopeNodeIds];
|
|
8344
|
-
}
|
|
8345
|
-
async #replaceNodeScopes(executor, nodeId, addresses, addedAt) {
|
|
8346
|
-
await executor.execute({
|
|
8347
|
-
sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=?`,
|
|
8348
|
-
args: [nodeId]
|
|
8349
|
-
});
|
|
8350
|
-
for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
|
|
8351
|
-
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
|
|
8352
|
-
args: [
|
|
8353
|
-
nodeId,
|
|
8354
|
-
scopeNodeId,
|
|
8355
|
-
addedAt.toISOString()
|
|
8356
|
-
]
|
|
8357
|
-
});
|
|
8005
|
+
async dangerouslyClearAll() {
|
|
8006
|
+
await this.#pages.clearAll();
|
|
8358
8007
|
}
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
* placements added by `#placeNodeInScopes`, which a scope change must not erase.
|
|
8362
|
-
*/
|
|
8363
|
-
async #swapIdentityScopes(executor, nodeId, from, to, addedAt) {
|
|
8364
|
-
const next = await this.#resolveScopeNodeIds(executor, to);
|
|
8365
|
-
for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, from)) {
|
|
8366
|
-
if (next.includes(scopeNodeId)) continue;
|
|
8367
|
-
await executor.execute({
|
|
8368
|
-
sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=? AND scopeNodeId=?`,
|
|
8369
|
-
args: [nodeId, scopeNodeId]
|
|
8370
|
-
});
|
|
8371
|
-
}
|
|
8372
|
-
for (const scopeNodeId of next) await executor.execute({
|
|
8373
|
-
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
|
|
8374
|
-
args: [
|
|
8375
|
-
nodeId,
|
|
8376
|
-
scopeNodeId,
|
|
8377
|
-
addedAt.toISOString()
|
|
8378
|
-
]
|
|
8379
|
-
});
|
|
8008
|
+
async #inspectPages(tx) {
|
|
8009
|
+
return inspectKnowledgePagesSchema(await this.#pagesSnapshot(tx));
|
|
8380
8010
|
}
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
|
|
8387
|
-
async #placeNodeInScopes(executor, nodeId, addresses, addedAt) {
|
|
8388
|
-
for (const address of addresses ?? []) {
|
|
8389
|
-
const scopeNodeId = (await executor.execute({
|
|
8390
|
-
sql: `SELECT sa.scopeNodeId AS scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" sa JOIN "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" n ON n.id=sa.scopeNodeId WHERE sa.address=? AND n.isScope AND n.deletedAt IS NULL`,
|
|
8391
|
-
args: [address]
|
|
8392
|
-
})).rows[0]?.scopeNodeId;
|
|
8393
|
-
if (scopeNodeId == null) throw new _mastra_core_storage.KnowledgeNotFoundError("scope", address);
|
|
8394
|
-
await executor.execute({
|
|
8395
|
-
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
|
|
8396
|
-
args: [
|
|
8397
|
-
nodeId,
|
|
8398
|
-
String(scopeNodeId),
|
|
8399
|
-
addedAt.toISOString()
|
|
8400
|
-
]
|
|
8401
|
-
});
|
|
8011
|
+
async #pagesSnapshot(tx) {
|
|
8012
|
+
const tableNames = (await tx.execute({ sql: "SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'mastra\\_knowledge\\_%' ESCAPE '\\'" })).rows.map((row) => String(row.name));
|
|
8013
|
+
let schemaVersion;
|
|
8014
|
+
if (tableNames.includes("mastra_knowledge_access_state")) {
|
|
8015
|
+
const state = await tx.execute({ sql: `SELECT schemaVersion FROM "${TABLE_KNOWLEDGE_PAGES_STATE}" WHERE id = 'knowledge'` });
|
|
8016
|
+
if (state.rows[0]) schemaVersion = Number(state.rows[0].schemaVersion);
|
|
8402
8017
|
}
|
|
8018
|
+
return {
|
|
8019
|
+
tableNames,
|
|
8020
|
+
schemaVersion
|
|
8021
|
+
};
|
|
8403
8022
|
}
|
|
8404
|
-
async
|
|
8405
|
-
|
|
8406
|
-
sql: `DELETE FROM "${TABLE_KNOWLEDGE_RECORD_SCOPES}" WHERE recordId=?`,
|
|
8407
|
-
args: [recordId]
|
|
8408
|
-
});
|
|
8409
|
-
for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
|
|
8410
|
-
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (recordId,scopeNodeId,addedAt) VALUES (?,?,?)`,
|
|
8411
|
-
args: [
|
|
8412
|
-
recordId,
|
|
8413
|
-
scopeNodeId,
|
|
8414
|
-
addedAt.toISOString()
|
|
8415
|
-
]
|
|
8416
|
-
});
|
|
8023
|
+
async inspectSchema() {
|
|
8024
|
+
return this.#inspectPages(this.#client);
|
|
8417
8025
|
}
|
|
8418
|
-
async
|
|
8419
|
-
await tx
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8423
|
-
|
|
8424
|
-
let node = await this.#resolveNode(tx, name, resolutionScope);
|
|
8425
|
-
if (!node) {
|
|
8426
|
-
node = await this.#getNodeByName(tx, name, defaultScope);
|
|
8427
|
-
if (node) node = await this.#resolveTerminalNode(tx, node.id);
|
|
8428
|
-
if (!node) {
|
|
8429
|
-
const now = /* @__PURE__ */ new Date();
|
|
8430
|
-
node = {
|
|
8431
|
-
id: crypto.randomUUID(),
|
|
8432
|
-
type: "node",
|
|
8433
|
-
name,
|
|
8434
|
-
kind: "node",
|
|
8435
|
-
scope: defaultScope,
|
|
8436
|
-
version: 1,
|
|
8437
|
-
createdAt: now,
|
|
8438
|
-
updatedAt: now
|
|
8439
|
-
};
|
|
8440
|
-
await tx.execute({
|
|
8441
|
-
sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,FALSE,jsonb(?),?,?,NULL,?,?)`,
|
|
8442
|
-
args: [
|
|
8443
|
-
node.id,
|
|
8444
|
-
"node",
|
|
8445
|
-
node.name,
|
|
8446
|
-
canonicalName(node.name),
|
|
8447
|
-
node.kind,
|
|
8448
|
-
JSON.stringify(defaultScope),
|
|
8449
|
-
(0, _mastra_core_storage.knowledgeScopeKey)(defaultScope),
|
|
8450
|
-
1,
|
|
8451
|
-
now.toISOString(),
|
|
8452
|
-
now.toISOString()
|
|
8453
|
-
]
|
|
8454
|
-
});
|
|
8455
|
-
await this.#replaceNodeScopes(tx, node.id, defaultScope, now);
|
|
8456
|
-
await this.#activity(tx, "node-created", "node", node.id, defaultScope);
|
|
8457
|
-
await this.#outbox(tx, "node", node.id, "upsert", 1, defaultScope);
|
|
8458
|
-
}
|
|
8459
|
-
}
|
|
8026
|
+
async init() {
|
|
8027
|
+
await this.#transaction(async (tx) => {
|
|
8028
|
+
for (const table of legacyKnowledgeTablesToDrop(await this.#pagesSnapshot(tx))) await tx.execute(`DROP TABLE IF EXISTS "${table}"`);
|
|
8029
|
+
const inspection = await this.#inspectPages(tx);
|
|
8030
|
+
if (inspection.status === "incompatible-reset-required") throw new _mastra_core_storage.KnowledgeSchemaResetRequiredError(inspection);
|
|
8031
|
+
for (const statement of KNOWLEDGE_PAGE_DDL) await tx.execute(statement);
|
|
8460
8032
|
await tx.execute({
|
|
8461
|
-
sql: `INSERT OR IGNORE INTO "${
|
|
8462
|
-
args: [
|
|
8463
|
-
sourceType,
|
|
8464
|
-
sourceId,
|
|
8465
|
-
node.id
|
|
8466
|
-
]
|
|
8033
|
+
sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_PAGES_STATE}" (id, schemaVersion) VALUES ('knowledge', ?)`,
|
|
8034
|
+
args: [4]
|
|
8467
8035
|
});
|
|
8468
|
-
}
|
|
8469
|
-
}
|
|
8470
|
-
async #activity(executor, action, recordType, recordId, scope, sourceThreadId) {
|
|
8471
|
-
const now = /* @__PURE__ */ new Date();
|
|
8472
|
-
const [contextScopeId] = await this.#resolveScopeNodeIds(executor, scope);
|
|
8473
|
-
await executor.execute({
|
|
8474
|
-
sql: `INSERT INTO "${_mastra_core_storage.TABLE_KNOWLEDGE_ACTIVITY}" (id,action,targetType,targetId,contextScopeId,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,?)`,
|
|
8475
|
-
args: [
|
|
8476
|
-
(0, _mastra_core_storage.createKnowledgeUlid)(),
|
|
8477
|
-
action,
|
|
8478
|
-
recordType,
|
|
8479
|
-
recordId,
|
|
8480
|
-
contextScopeId ?? null,
|
|
8481
|
-
recordType,
|
|
8482
|
-
recordId,
|
|
8483
|
-
JSON.stringify(scope),
|
|
8484
|
-
(0, _mastra_core_storage.knowledgeScopeKey)(scope),
|
|
8485
|
-
sourceThreadId ?? null,
|
|
8486
|
-
now.toISOString()
|
|
8487
|
-
]
|
|
8488
8036
|
});
|
|
8489
8037
|
}
|
|
8490
|
-
async
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
args: [
|
|
8497
|
-
(0, _mastra_core_storage.createKnowledgeUlid)(),
|
|
8498
|
-
idempotencyKey,
|
|
8499
|
-
documentId,
|
|
8500
|
-
documentType,
|
|
8501
|
-
operation,
|
|
8502
|
-
JSON.stringify(scope),
|
|
8503
|
-
(0, _mastra_core_storage.knowledgeScopeKey)(scope),
|
|
8504
|
-
now.toISOString(),
|
|
8505
|
-
now.toISOString()
|
|
8506
|
-
]
|
|
8038
|
+
async dangerouslyReset() {
|
|
8039
|
+
await withClientWriteLock(this.#client, async () => {
|
|
8040
|
+
await this.#client.batch([...KNOWLEDGE_RESETTABLE_TABLE_NAMES, KNOWLEDGE_PAGE_TABLES.pinAudiences].map((table) => ({
|
|
8041
|
+
sql: `DROP TABLE IF EXISTS "${table}"`,
|
|
8042
|
+
args: []
|
|
8043
|
+
})), "write");
|
|
8507
8044
|
});
|
|
8045
|
+
await this.init();
|
|
8046
|
+
}
|
|
8047
|
+
materializeScope(input) {
|
|
8048
|
+
return this.#pages.materializeScope(input);
|
|
8049
|
+
}
|
|
8050
|
+
listScopeDeclarations() {
|
|
8051
|
+
return this.#pages.listScopeDeclarations();
|
|
8052
|
+
}
|
|
8053
|
+
renameScopeDeclaration(input) {
|
|
8054
|
+
return this.#pages.renameScopeDeclaration(input);
|
|
8055
|
+
}
|
|
8056
|
+
getScopesByAddress(input) {
|
|
8057
|
+
return this.#pages.getScopesByAddress(input);
|
|
8058
|
+
}
|
|
8059
|
+
listChildScopes(input) {
|
|
8060
|
+
return this.#pages.listChildScopes(input);
|
|
8061
|
+
}
|
|
8062
|
+
getPage(input) {
|
|
8063
|
+
return this.#pages.getPage(input);
|
|
8064
|
+
}
|
|
8065
|
+
getPageByPath(input) {
|
|
8066
|
+
return this.#pages.getPageByPath(input);
|
|
8067
|
+
}
|
|
8068
|
+
findPagesByName(input) {
|
|
8069
|
+
return this.#pages.findPagesByName(input);
|
|
8070
|
+
}
|
|
8071
|
+
listChildPages(input) {
|
|
8072
|
+
return this.#pages.listChildPages(input);
|
|
8073
|
+
}
|
|
8074
|
+
listPages(input) {
|
|
8075
|
+
return this.#pages.listPages(input);
|
|
8076
|
+
}
|
|
8077
|
+
createPage(input) {
|
|
8078
|
+
return this.#pages.createPage(input);
|
|
8079
|
+
}
|
|
8080
|
+
updatePage(input) {
|
|
8081
|
+
return this.#pages.updatePage(input);
|
|
8082
|
+
}
|
|
8083
|
+
movePage(input) {
|
|
8084
|
+
return this.#pages.movePage(input);
|
|
8085
|
+
}
|
|
8086
|
+
mergePages(input) {
|
|
8087
|
+
return this.#pages.mergePages(input);
|
|
8088
|
+
}
|
|
8089
|
+
deletePage(input) {
|
|
8090
|
+
return this.#pages.deletePage(input);
|
|
8091
|
+
}
|
|
8092
|
+
getNote(input) {
|
|
8093
|
+
return this.#pages.getNote(input);
|
|
8094
|
+
}
|
|
8095
|
+
listNotes(input) {
|
|
8096
|
+
return this.#pages.listNotes(input);
|
|
8097
|
+
}
|
|
8098
|
+
countChildPages(input) {
|
|
8099
|
+
return this.#pages.countChildPages(input);
|
|
8100
|
+
}
|
|
8101
|
+
countNotes(input) {
|
|
8102
|
+
return this.#pages.countNotes(input);
|
|
8103
|
+
}
|
|
8104
|
+
listNotesMentioning(input) {
|
|
8105
|
+
return this.#pages.listNotesMentioning(input);
|
|
8106
|
+
}
|
|
8107
|
+
addNote(input) {
|
|
8108
|
+
return this.#pages.addNote(input);
|
|
8109
|
+
}
|
|
8110
|
+
restampNote(input) {
|
|
8111
|
+
return this.#pages.restampNote(input);
|
|
8112
|
+
}
|
|
8113
|
+
removeNote(input) {
|
|
8114
|
+
return this.#pages.removeNote(input);
|
|
8115
|
+
}
|
|
8116
|
+
restoreNote(input) {
|
|
8117
|
+
return this.#pages.restoreNote(input);
|
|
8118
|
+
}
|
|
8119
|
+
searchPages(input) {
|
|
8120
|
+
return this.#pages.searchPages(input);
|
|
8121
|
+
}
|
|
8122
|
+
listPageActivity(input) {
|
|
8123
|
+
return this.#pages.listPageActivity(input);
|
|
8124
|
+
}
|
|
8125
|
+
addPinAudience(input) {
|
|
8126
|
+
return this.#pages.addPinAudience(input);
|
|
8127
|
+
}
|
|
8128
|
+
removePinAudience(input) {
|
|
8129
|
+
return this.#pages.removePinAudience(input);
|
|
8130
|
+
}
|
|
8131
|
+
getPages(input) {
|
|
8132
|
+
return this.#pages.getPages(input);
|
|
8133
|
+
}
|
|
8134
|
+
listPins(input) {
|
|
8135
|
+
return this.#pages.listPins(input);
|
|
8136
|
+
}
|
|
8137
|
+
async #transaction(operation) {
|
|
8138
|
+
return this.#db.executeWriteOperationWithRetry(() => withClientWriteLock(this.#client, async () => {
|
|
8139
|
+
const tx = await this.#client.transaction("write");
|
|
8140
|
+
try {
|
|
8141
|
+
const result = await operation(tx);
|
|
8142
|
+
await tx.commit();
|
|
8143
|
+
return result;
|
|
8144
|
+
} catch (error) {
|
|
8145
|
+
if (!tx.closed) await tx.rollback();
|
|
8146
|
+
throw error;
|
|
8147
|
+
}
|
|
8148
|
+
}), "write knowledge state");
|
|
8508
8149
|
}
|
|
8509
8150
|
};
|
|
8510
8151
|
// src/storage/domains/mcp-clients/index.ts
|