@mastra/libsql 1.25.2-alpha.1 → 1.26.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { createClient } from "@libsql/client";
2
2
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
3
- import { AGENTS_SCHEMA, AGENT_VERSIONS_SCHEMA, AgentsStorage, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, ExperimentsStorage, FAVORITES_SCHEMA, FactoryStorage, FavoritesStorage, HarnessStorage, KNOWLEDGE_ACTIVITY_SCHEMA, KNOWLEDGE_CURSORS_SCHEMA, KNOWLEDGE_MENTIONS_SCHEMA, KNOWLEDGE_NODES_SCHEMA, KNOWLEDGE_RECORDS_SCHEMA, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, MCP_CLIENTS_SCHEMA, MCP_CLIENT_VERSIONS_SCHEMA, MCP_SERVERS_SCHEMA, MCP_SERVER_VERSIONS_SCHEMA, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PROMPT_BLOCKS_SCHEMA, PROMPT_BLOCK_VERSIONS_SCHEMA, PromptBlocksStorage, SCORERS_SCHEMA, SCORER_DEFINITIONS_SCHEMA, SCORER_DEFINITION_VERSIONS_SCHEMA, SKILLS_SCHEMA, SKILL_BLOBS_SCHEMA, SKILL_VERSIONS_SCHEMA, SPAN_SCHEMA, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_HARNESS_SESSIONS, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_CURSORS, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, THREAD_STATE_SCHEMA, TOOL_PROVIDER_CONNECTIONS_SCHEMA, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WORKSPACES_SCHEMA, WORKSPACE_VERSIONS_SCHEMA, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, assertKnowledgeCeilingRaised, assertKnowledgeScopeWithinCeiling, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getSqlType, hasErrorCode, isKnowledgeScopeVisible, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listTracesArgsSchema, matchesExpectedWorkflowStatus, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, retentionCutoffMs, runRetentionBatches, safelyParseJSON, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
3
+ import { AGENTS_SCHEMA, AGENT_VERSIONS_SCHEMA, AgentsStorage, BackgroundTasksStorage, BlobStore, ChannelsStorage, DATASETS_SCHEMA, DATASET_ITEMS_SCHEMA, DATASET_VERSIONS_SCHEMA, DatasetsStorage, EXPERIMENTS_SCHEMA, EXPERIMENT_RESULTS_SCHEMA, ExperimentsStorage, FAVORITES_SCHEMA, FactoryStorage, FavoritesStorage, HarnessStorage, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA, KnowledgeConflictError, KnowledgeNotFoundError, KnowledgeStorage, MCPClientsStorage, MCPServersStorage, MCP_CLIENTS_SCHEMA, MCP_CLIENT_VERSIONS_SCHEMA, MCP_SERVERS_SCHEMA, MCP_SERVER_VERSIONS_SCHEMA, MastraCompositeStore, MemoryStorage, NotificationsStorage, OBSERVATIONAL_MEMORY_TABLE_SCHEMA, ObservabilityStorage, PROMPT_BLOCKS_SCHEMA, PROMPT_BLOCK_VERSIONS_SCHEMA, PromptBlocksStorage, SCORERS_SCHEMA, SCORER_DEFINITIONS_SCHEMA, SCORER_DEFINITION_VERSIONS_SCHEMA, SKILLS_SCHEMA, SKILL_BLOBS_SCHEMA, SKILL_VERSIONS_SCHEMA, SPAN_SCHEMA, SchedulesStorage, ScorerDefinitionsStorage, ScoresStorage, SkillsStorage, TABLE_AGENTS, TABLE_AGENT_VERSIONS, TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_DATASETS, TABLE_DATASET_ITEMS, TABLE_DATASET_VERSIONS, TABLE_EXPERIMENTS, TABLE_EXPERIMENT_RESULTS, TABLE_FAVORITES, TABLE_HARNESS_SESSIONS, TABLE_KNOWLEDGE_ACTIVITY, TABLE_KNOWLEDGE_MENTIONS, TABLE_KNOWLEDGE_NODES, TABLE_KNOWLEDGE_RECORDS, TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, TABLE_MCP_CLIENTS, TABLE_MCP_CLIENT_VERSIONS, TABLE_MCP_SERVERS, TABLE_MCP_SERVER_VERSIONS, TABLE_MESSAGES, TABLE_NOTIFICATIONS, TABLE_PROMPT_BLOCKS, TABLE_PROMPT_BLOCK_VERSIONS, TABLE_RESOURCES, TABLE_SCHEDULES, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEMAS, TABLE_SCORERS, TABLE_SCORER_DEFINITIONS, TABLE_SCORER_DEFINITION_VERSIONS, TABLE_SKILLS, TABLE_SKILL_BLOBS, TABLE_SKILL_VERSIONS, TABLE_SPANS, TABLE_THREADS, TABLE_THREAD_STATE, TABLE_TOOL_PROVIDER_CONNECTIONS, TABLE_WORKFLOW_DEFINITIONS, TABLE_WORKFLOW_SNAPSHOT, TABLE_WORKSPACES, TABLE_WORKSPACE_VERSIONS, THREAD_STATE_SCHEMA, TOOL_PROVIDER_CONNECTIONS_SCHEMA, ThreadStateStorage, ToolProviderConnectionsStorage, TraceStatus, UniqueViolationError, WORKSPACES_SCHEMA, WORKSPACE_VERSIONS_SCHEMA, WorkflowDefinitionsStorage, WorkflowsStorage, WorkspacesStorage, calculatePagination, canonicalizeKnowledgeScope, createKnowledgeUlid, createStorageErrorId, createVectorErrorId, ensureDate, executeRetentionPrune, getSqlType, hasErrorCode, isKnowledgeScopeVisible, knowledgeScopeKey, knowledgeSemanticDocumentId, knowledgeSemanticIdempotencyKey, listTracesArgsSchema, matchesExpectedWorkflowStatus, mergeWorkflowStepResult, normalizePerPage, normalizeScheduleTarget, parseKnowledgeNodeCursor, parseKnowledgeWikilinks, resolveListOrderBy, resolveRetentionTargets, retentionCutoffMs, runRetentionBatches, safelyParseJSON, toTraceSpans, transformScoreRow, validateStorageMetadataFilter } from "@mastra/core/storage";
4
4
  import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
5
5
  import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
6
6
  import { BaseFilterTranslator } from "@mastra/core/vector/filter";
7
7
  import { realpath } from "fs/promises";
8
8
  import { basename, dirname, isAbsolute, join, resolve } from "path";
9
9
  import { MastraBase } from "@mastra/core/base";
10
+ import { randomUUID } from "crypto";
11
+ import { coreFeatures } from "@mastra/core/features";
10
12
  import { MessageList } from "@mastra/core/agent";
11
13
  import { saveScorePayloadSchema } from "@mastra/core/evals";
12
14
  import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
@@ -1786,7 +1788,7 @@ var LibSQLDB = class extends MastraBase {
1786
1788
  * @param args.tableName - The name of the table to create
1787
1789
  * @param args.schema - The schema definition for the table columns
1788
1790
  */
1789
- async createTable({ tableName, schema, compositePrimaryKey }) {
1791
+ async createTable({ tableName, schema, compositePrimaryKey, executor = this.client }) {
1790
1792
  try {
1791
1793
  const parsedTableName = parseSqlIdentifier(tableName, "table name");
1792
1794
  if (compositePrimaryKey) {
@@ -1804,7 +1806,7 @@ var LibSQLDB = class extends MastraBase {
1804
1806
  if (tableName === TABLE_WORKFLOW_SNAPSHOT) tableConstraints.push("UNIQUE (workflow_name, run_id)");
1805
1807
  if (tableName === TABLE_SPANS) tableConstraints.push("UNIQUE (spanId, traceId)");
1806
1808
  const sql = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (\n ${[...columnDefinitions, ...tableConstraints].join(",\n ")}\n)`;
1807
- await this.client.execute(sql);
1809
+ await executor.execute(sql);
1808
1810
  this.logger.debug(`LibSQLDB: Created table ${tableName}`);
1809
1811
  if (tableName === TABLE_SPANS) await this.migrateSpansTable();
1810
1812
  } catch (error) {
@@ -6047,20 +6049,678 @@ var HarnessLibSQL = class HarnessLibSQL extends HarnessStorage {
6047
6049
  }
6048
6050
  };
6049
6051
  //#endregion
6052
+ //#region ../../packages/_internal-core/dist/knowledge-compat/index.js
6053
+ const KNOWLEDGE_V2_CORE_FEATURE = "knowledge-v2";
6054
+ /** Hard cap on scope nodes returned by one `listScopeNodes` read. */
6055
+ const MAX_KNOWLEDGE_SCOPE_NODES = 1e3;
6056
+ function knowledgeScopeNodeFilterKey(input) {
6057
+ return JSON.stringify([
6058
+ input.withinAddress ?? null,
6059
+ input.addresses ? [...input.addresses].sort() : null,
6060
+ input.ids ? [...input.ids].sort() : null
6061
+ ]);
6062
+ }
6063
+ function createKnowledgeScopeNodeCursor(scope, input) {
6064
+ return encodeURIComponent(JSON.stringify({
6065
+ version: 1,
6066
+ type: "scope",
6067
+ name: scope.name,
6068
+ id: scope.id,
6069
+ filter: knowledgeScopeNodeFilterKey(input)
6070
+ }));
6071
+ }
6072
+ /** Validates the page size and cursor of a `listScopeNodes` query; throws on a cursor from a different query. */
6073
+ function parseListKnowledgeScopeNodesInput(input = {}) {
6074
+ const requested = Number.isFinite(input.limit) ? Math.trunc(input.limit) : MAX_KNOWLEDGE_SCOPE_NODES;
6075
+ const limit = Math.min(Math.max(requested, 1), MAX_KNOWLEDGE_SCOPE_NODES);
6076
+ if (!input.cursor) return {
6077
+ limit,
6078
+ after: null
6079
+ };
6080
+ let value;
6081
+ try {
6082
+ value = JSON.parse(decodeURIComponent(input.cursor));
6083
+ } catch {
6084
+ throw new Error("Invalid Knowledge scope node cursor.");
6085
+ }
6086
+ const parsed = value && typeof value === "object" ? value : {};
6087
+ 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.");
6088
+ return {
6089
+ limit,
6090
+ after: {
6091
+ name: parsed.name,
6092
+ id: parsed.id
6093
+ }
6094
+ };
6095
+ }
6096
+ /** Builds a page from up to `limit + 1` name/id-ordered scope nodes. */
6097
+ function pageKnowledgeScopeNodes(rows, limit, input) {
6098
+ const scopes = rows.slice(0, limit);
6099
+ const last = scopes.at(-1);
6100
+ return {
6101
+ scopes,
6102
+ nextCursor: rows.length > limit && last ? createKnowledgeScopeNodeCursor(last, input) : null
6103
+ };
6104
+ }
6105
+ /** Clamps a requested member limit to 1–{@link MAX_KNOWLEDGE_SCOPE_MEMBERS_LIMIT}. */
6106
+ function knowledgeScopeMembersLimit(limit) {
6107
+ if (limit === void 0 || !Number.isFinite(limit)) return 500;
6108
+ return Math.min(Math.max(Math.trunc(limit), 1), 500);
6109
+ }
6110
+ /** Validates the page size and cursor of a `listScopeMembers` query; throws on a cursor from another scope. */
6111
+ function parseListKnowledgeScopeMembersInput(input) {
6112
+ const limit = knowledgeScopeMembersLimit(input.limit);
6113
+ if (!input.cursor) return {
6114
+ limit,
6115
+ after: null
6116
+ };
6117
+ let value;
6118
+ try {
6119
+ value = JSON.parse(decodeURIComponent(input.cursor));
6120
+ } catch {
6121
+ throw new Error("Invalid Knowledge scope member cursor.");
6122
+ }
6123
+ const parsed = value && typeof value === "object" ? value : {};
6124
+ const updatedAt = typeof parsed.updatedAt === "string" ? new Date(parsed.updatedAt) : /* @__PURE__ */ new Date(NaN);
6125
+ 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.");
6126
+ return {
6127
+ limit,
6128
+ after: {
6129
+ updatedAt,
6130
+ name: parsed.name,
6131
+ id: parsed.id
6132
+ }
6133
+ };
6134
+ }
6135
+ /** Builds a page from up to `limit + 1` members in `listScopeMembers` order. */
6136
+ function pageKnowledgeScopeMembers(rows, limit, input) {
6137
+ const members = rows.slice(0, limit);
6138
+ const last = members.at(-1);
6139
+ const hasMore = rows.length > limit;
6140
+ return {
6141
+ members,
6142
+ hasMore,
6143
+ nextCursor: hasMore && last ? encodeURIComponent(JSON.stringify({
6144
+ version: 1,
6145
+ type: "scope-member",
6146
+ scopeNodeId: input.scopeNodeId,
6147
+ updatedAt: last.updatedAt.toISOString(),
6148
+ name: last.name,
6149
+ id: last.id
6150
+ })) : null
6151
+ };
6152
+ }
6153
+ const TABLE_KNOWLEDGE_NODES$1 = "mastra_knowledge_nodes";
6154
+ const TABLE_KNOWLEDGE_RECORDS$1 = "mastra_knowledge_records";
6155
+ const TABLE_KNOWLEDGE_MENTIONS$1 = "mastra_knowledge_mentions";
6156
+ const TABLE_KNOWLEDGE_ACTIVITY$1 = "mastra_knowledge_activity";
6157
+ const TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1 = "mastra_knowledge_semantic_outbox";
6158
+ const TABLE_KNOWLEDGE_NODE_SCOPES = "mastra_knowledge_node_scopes";
6159
+ const TABLE_KNOWLEDGE_RECORD_SCOPES = "mastra_knowledge_record_scopes";
6160
+ const TABLE_KNOWLEDGE_SCOPE_GRANTS = "mastra_knowledge_scope_grants";
6161
+ const TABLE_KNOWLEDGE_ACCESS_STATE = "mastra_knowledge_access_state";
6162
+ const TABLE_KNOWLEDGE_SCOPE_ADDRESSES = "mastra_knowledge_scope_addresses";
6163
+ const TABLE_KNOWLEDGE_NODE_ADDRESSES = "mastra_knowledge_node_addresses";
6164
+ const TABLE_KNOWLEDGE_IMPORT_STATE = "mastra_knowledge_import_state";
6165
+ const TABLE_KNOWLEDGE_IMPORT_RUNS = "mastra_knowledge_import_runs";
6166
+ const TABLE_KNOWLEDGE_PROPOSALS = "mastra_knowledge_proposals";
6167
+ const KNOWLEDGE_TABLE_NAMES = [
6168
+ TABLE_KNOWLEDGE_NODES$1,
6169
+ TABLE_KNOWLEDGE_RECORDS$1,
6170
+ TABLE_KNOWLEDGE_MENTIONS$1,
6171
+ TABLE_KNOWLEDGE_ACTIVITY$1,
6172
+ TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1,
6173
+ TABLE_KNOWLEDGE_NODE_SCOPES,
6174
+ TABLE_KNOWLEDGE_RECORD_SCOPES,
6175
+ TABLE_KNOWLEDGE_SCOPE_GRANTS,
6176
+ TABLE_KNOWLEDGE_ACCESS_STATE,
6177
+ TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
6178
+ TABLE_KNOWLEDGE_NODE_ADDRESSES,
6179
+ TABLE_KNOWLEDGE_IMPORT_STATE,
6180
+ TABLE_KNOWLEDGE_IMPORT_RUNS,
6181
+ TABLE_KNOWLEDGE_PROPOSALS
6182
+ ];
6183
+ /** Knowledge v1 tables with no v2 equivalent; an explicit schema reset drops them. */
6184
+ const RETIRED_KNOWLEDGE_TABLE_NAMES = ["mastra_knowledge_cursors"];
6185
+ /** Tables and indexes published v1 adapters created for every app, whether or not it used Knowledge. */
6186
+ const PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES = /* @__PURE__ */ new Set([
6187
+ TABLE_KNOWLEDGE_NODES$1,
6188
+ TABLE_KNOWLEDGE_RECORDS$1,
6189
+ TABLE_KNOWLEDGE_MENTIONS$1,
6190
+ TABLE_KNOWLEDGE_ACTIVITY$1,
6191
+ TABLE_KNOWLEDGE_SEMANTIC_OUTBOX$1,
6192
+ ...RETIRED_KNOWLEDGE_TABLE_NAMES
6193
+ ]);
6194
+ const PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES = /* @__PURE__ */ new Set([
6195
+ "idx_knowledge_nodes_identity",
6196
+ "idx_knowledge_nodes_scope",
6197
+ "idx_knowledge_records_node_latest",
6198
+ "idx_knowledge_records_thread_latest",
6199
+ "idx_knowledge_mentions_record",
6200
+ "idx_knowledge_activity_latest",
6201
+ "idx_knowledge_outbox_idempotency",
6202
+ "idx_knowledge_outbox_claim"
6203
+ ]);
6204
+ const KNOWLEDGE_V2_NODES_SCHEMA = {
6205
+ id: {
6206
+ type: "text",
6207
+ nullable: false,
6208
+ primaryKey: true
6209
+ },
6210
+ name: {
6211
+ type: "text",
6212
+ nullable: false
6213
+ },
6214
+ kind: {
6215
+ type: "text",
6216
+ nullable: true
6217
+ },
6218
+ isScope: {
6219
+ type: "boolean",
6220
+ nullable: false
6221
+ },
6222
+ metadata: {
6223
+ type: "jsonb",
6224
+ nullable: true
6225
+ },
6226
+ version: {
6227
+ type: "integer",
6228
+ nullable: false
6229
+ },
6230
+ createdAt: {
6231
+ type: "timestamp",
6232
+ nullable: false
6233
+ },
6234
+ updatedAt: {
6235
+ type: "timestamp",
6236
+ nullable: false
6237
+ },
6238
+ deletedAt: {
6239
+ type: "timestamp",
6240
+ nullable: true
6241
+ },
6242
+ deletedBy: {
6243
+ type: "text",
6244
+ nullable: true
6245
+ },
6246
+ type: {
6247
+ type: "text",
6248
+ nullable: true
6249
+ },
6250
+ canonicalName: {
6251
+ type: "text",
6252
+ nullable: true
6253
+ },
6254
+ content: {
6255
+ type: "text",
6256
+ nullable: true
6257
+ },
6258
+ description: {
6259
+ type: "text",
6260
+ nullable: true
6261
+ },
6262
+ scope: {
6263
+ type: "jsonb",
6264
+ nullable: true
6265
+ },
6266
+ scopeKey: {
6267
+ type: "text",
6268
+ nullable: true
6269
+ },
6270
+ mergedInto: {
6271
+ type: "text",
6272
+ nullable: true
6273
+ }
6274
+ };
6275
+ const KNOWLEDGE_V2_RECORDS_SCHEMA = {
6276
+ id: {
6277
+ type: "text",
6278
+ nullable: false,
6279
+ primaryKey: true
6280
+ },
6281
+ node: {
6282
+ type: "text",
6283
+ nullable: false,
6284
+ references: {
6285
+ table: TABLE_KNOWLEDGE_NODES$1,
6286
+ column: "id"
6287
+ }
6288
+ },
6289
+ text: {
6290
+ type: "text",
6291
+ nullable: false
6292
+ },
6293
+ metadata: {
6294
+ type: "jsonb",
6295
+ nullable: true
6296
+ },
6297
+ version: {
6298
+ type: "integer",
6299
+ nullable: false
6300
+ },
6301
+ capturedAt: {
6302
+ type: "timestamp",
6303
+ nullable: false
6304
+ },
6305
+ updatedAt: {
6306
+ type: "timestamp",
6307
+ nullable: false
6308
+ },
6309
+ deletedAt: {
6310
+ type: "timestamp",
6311
+ nullable: true
6312
+ },
6313
+ deletedBy: {
6314
+ type: "text",
6315
+ nullable: true
6316
+ },
6317
+ scope: {
6318
+ type: "jsonb",
6319
+ nullable: true
6320
+ },
6321
+ scopeKey: {
6322
+ type: "text",
6323
+ nullable: true
6324
+ },
6325
+ sourceThreadId: {
6326
+ type: "text",
6327
+ nullable: true
6328
+ },
6329
+ when: {
6330
+ type: "timestamp",
6331
+ nullable: true
6332
+ }
6333
+ };
6334
+ const KNOWLEDGE_V2_MENTIONS_SCHEMA = {
6335
+ recordId: {
6336
+ type: "text",
6337
+ nullable: false,
6338
+ references: {
6339
+ table: TABLE_KNOWLEDGE_RECORDS$1,
6340
+ column: "id"
6341
+ }
6342
+ },
6343
+ targetNodeId: {
6344
+ type: "text",
6345
+ nullable: true,
6346
+ references: {
6347
+ table: TABLE_KNOWLEDGE_NODES$1,
6348
+ column: "id"
6349
+ }
6350
+ },
6351
+ sourceType: {
6352
+ type: "text",
6353
+ nullable: true
6354
+ },
6355
+ sourceId: {
6356
+ type: "text",
6357
+ nullable: true
6358
+ }
6359
+ };
6360
+ const KNOWLEDGE_NODE_SCOPES_SCHEMA = {
6361
+ nodeId: {
6362
+ type: "text",
6363
+ nullable: false,
6364
+ references: {
6365
+ table: TABLE_KNOWLEDGE_NODES$1,
6366
+ column: "id"
6367
+ }
6368
+ },
6369
+ scopeNodeId: {
6370
+ type: "text",
6371
+ nullable: false,
6372
+ references: {
6373
+ table: TABLE_KNOWLEDGE_NODES$1,
6374
+ column: "id"
6375
+ }
6376
+ },
6377
+ addedAt: {
6378
+ type: "timestamp",
6379
+ nullable: false
6380
+ }
6381
+ };
6382
+ const KNOWLEDGE_RECORD_SCOPES_SCHEMA = {
6383
+ recordId: {
6384
+ type: "text",
6385
+ nullable: false,
6386
+ references: {
6387
+ table: TABLE_KNOWLEDGE_RECORDS$1,
6388
+ column: "id"
6389
+ }
6390
+ },
6391
+ scopeNodeId: {
6392
+ type: "text",
6393
+ nullable: false,
6394
+ references: {
6395
+ table: TABLE_KNOWLEDGE_NODES$1,
6396
+ column: "id"
6397
+ }
6398
+ },
6399
+ addedAt: {
6400
+ type: "timestamp",
6401
+ nullable: false
6402
+ }
6403
+ };
6404
+ const KNOWLEDGE_SCOPE_GRANTS_SCHEMA = {
6405
+ scopeNodeId: {
6406
+ type: "text",
6407
+ nullable: false,
6408
+ references: {
6409
+ table: TABLE_KNOWLEDGE_NODES$1,
6410
+ column: "id"
6411
+ }
6412
+ },
6413
+ scopeRefId: {
6414
+ type: "text",
6415
+ nullable: false,
6416
+ references: {
6417
+ table: TABLE_KNOWLEDGE_NODES$1,
6418
+ column: "id"
6419
+ }
6420
+ },
6421
+ role: {
6422
+ type: "text",
6423
+ nullable: false
6424
+ },
6425
+ canSuggest: {
6426
+ type: "boolean",
6427
+ nullable: true
6428
+ }
6429
+ };
6430
+ const KNOWLEDGE_ACCESS_STATE_SCHEMA = {
6431
+ id: {
6432
+ type: "text",
6433
+ nullable: false,
6434
+ primaryKey: true
6435
+ },
6436
+ epoch: {
6437
+ type: "integer",
6438
+ nullable: false
6439
+ },
6440
+ schemaVersion: {
6441
+ type: "integer",
6442
+ nullable: false
6443
+ }
6444
+ };
6445
+ const KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA = {
6446
+ address: {
6447
+ type: "text",
6448
+ nullable: false,
6449
+ primaryKey: true
6450
+ },
6451
+ scopeNodeId: {
6452
+ type: "text",
6453
+ nullable: false,
6454
+ references: {
6455
+ table: TABLE_KNOWLEDGE_NODES$1,
6456
+ column: "id"
6457
+ }
6458
+ }
6459
+ };
6460
+ const KNOWLEDGE_NODE_ADDRESSES_SCHEMA = {
6461
+ source: {
6462
+ type: "text",
6463
+ nullable: false
6464
+ },
6465
+ address: {
6466
+ type: "text",
6467
+ nullable: false
6468
+ },
6469
+ nodeId: {
6470
+ type: "text",
6471
+ nullable: false,
6472
+ references: {
6473
+ table: TABLE_KNOWLEDGE_NODES$1,
6474
+ column: "id"
6475
+ }
6476
+ }
6477
+ };
6478
+ const KNOWLEDGE_IMPORT_STATE_SCHEMA = {
6479
+ importerId: {
6480
+ type: "text",
6481
+ nullable: false
6482
+ },
6483
+ binding: {
6484
+ type: "text",
6485
+ nullable: false
6486
+ },
6487
+ key: {
6488
+ type: "text",
6489
+ nullable: false
6490
+ },
6491
+ value: {
6492
+ type: "text",
6493
+ nullable: false
6494
+ }
6495
+ };
6496
+ const KNOWLEDGE_IMPORT_RUNS_SCHEMA = {
6497
+ id: {
6498
+ type: "text",
6499
+ nullable: false,
6500
+ primaryKey: true
6501
+ },
6502
+ importerId: {
6503
+ type: "text",
6504
+ nullable: false
6505
+ },
6506
+ binding: {
6507
+ type: "text",
6508
+ nullable: false
6509
+ },
6510
+ importKind: {
6511
+ type: "text",
6512
+ nullable: false
6513
+ },
6514
+ triggerKind: {
6515
+ type: "text",
6516
+ nullable: false
6517
+ },
6518
+ status: {
6519
+ type: "text",
6520
+ nullable: false
6521
+ },
6522
+ error: {
6523
+ type: "text",
6524
+ nullable: true
6525
+ },
6526
+ transcriptThreadId: {
6527
+ type: "text",
6528
+ nullable: true
6529
+ },
6530
+ traceId: {
6531
+ type: "text",
6532
+ nullable: true
6533
+ },
6534
+ queuedAt: {
6535
+ type: "timestamp",
6536
+ nullable: false
6537
+ },
6538
+ startedAt: {
6539
+ type: "timestamp",
6540
+ nullable: true
6541
+ },
6542
+ completedAt: {
6543
+ type: "timestamp",
6544
+ nullable: true
6545
+ }
6546
+ };
6547
+ const KNOWLEDGE_V2_ACTIVITY_SCHEMA = {
6548
+ id: {
6549
+ type: "text",
6550
+ nullable: false,
6551
+ primaryKey: true
6552
+ },
6553
+ action: {
6554
+ type: "text",
6555
+ nullable: false
6556
+ },
6557
+ targetType: {
6558
+ type: "text",
6559
+ nullable: true
6560
+ },
6561
+ targetId: {
6562
+ type: "text",
6563
+ nullable: true
6564
+ },
6565
+ contextScopeId: {
6566
+ type: "text",
6567
+ nullable: true,
6568
+ references: {
6569
+ table: TABLE_KNOWLEDGE_NODES$1,
6570
+ column: "id"
6571
+ }
6572
+ },
6573
+ importRunId: {
6574
+ type: "text",
6575
+ nullable: true,
6576
+ references: {
6577
+ table: TABLE_KNOWLEDGE_IMPORT_RUNS,
6578
+ column: "id"
6579
+ }
6580
+ },
6581
+ details: {
6582
+ type: "jsonb",
6583
+ nullable: true
6584
+ },
6585
+ createdAt: {
6586
+ type: "timestamp",
6587
+ nullable: false
6588
+ },
6589
+ recordType: {
6590
+ type: "text",
6591
+ nullable: true
6592
+ },
6593
+ recordId: {
6594
+ type: "text",
6595
+ nullable: true
6596
+ },
6597
+ scope: {
6598
+ type: "jsonb",
6599
+ nullable: true
6600
+ },
6601
+ scopeKey: {
6602
+ type: "text",
6603
+ nullable: true
6604
+ },
6605
+ sourceThreadId: {
6606
+ type: "text",
6607
+ nullable: true
6608
+ }
6609
+ };
6610
+ const KNOWLEDGE_PROPOSALS_SCHEMA = {
6611
+ id: {
6612
+ type: "text",
6613
+ nullable: false,
6614
+ primaryKey: true
6615
+ },
6616
+ targetType: {
6617
+ type: "text",
6618
+ nullable: false
6619
+ },
6620
+ targetId: {
6621
+ type: "text",
6622
+ nullable: false
6623
+ },
6624
+ action: {
6625
+ type: "text",
6626
+ nullable: false
6627
+ },
6628
+ changes: {
6629
+ type: "jsonb",
6630
+ nullable: false
6631
+ },
6632
+ reason: {
6633
+ type: "text",
6634
+ nullable: true
6635
+ },
6636
+ proposerContextScopeId: {
6637
+ type: "text",
6638
+ nullable: false
6639
+ },
6640
+ expectedVersion: {
6641
+ type: "integer",
6642
+ nullable: false
6643
+ },
6644
+ status: {
6645
+ type: "text",
6646
+ nullable: false
6647
+ },
6648
+ reviewerContextScopeId: {
6649
+ type: "text",
6650
+ nullable: true
6651
+ },
6652
+ reviewedAt: {
6653
+ type: "timestamp",
6654
+ nullable: true
6655
+ },
6656
+ createdAt: {
6657
+ type: "timestamp",
6658
+ nullable: false
6659
+ }
6660
+ };
6661
+ function assertKnowledgeV2CoreSupport(features) {
6662
+ 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.`);
6663
+ }
6664
+ function resolveKnowledgeV2Core(storageModule) {
6665
+ if (typeof storageModule !== "object" || storageModule === null) throw new Error("@mastra/core advertises Knowledge v2 without the required storage API");
6666
+ const { assertKnowledgeDescriptionWithinBound: assertDescription, assertKnowledgeRecordTextWithinBound: assertRecordText, assertKnowledgeSchemaCompatible: assertCompatible, inspectKnowledgeSchema: inspectSchema } = storageModule;
6667
+ 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");
6668
+ return {
6669
+ assertKnowledgeDescriptionWithinBound: (description) => assertDescription(description),
6670
+ assertKnowledgeRecordTextWithinBound: (text) => assertRecordText(text),
6671
+ assertKnowledgeSchemaCompatible: (inspection) => assertCompatible(inspection),
6672
+ inspectKnowledgeSchema: (snapshot) => inspectSchema(snapshot)
6673
+ };
6674
+ }
6675
+ function createKnowledgeV2CoreLoader(features, loadStorageModule) {
6676
+ let knowledgeV2Core;
6677
+ return async () => {
6678
+ assertKnowledgeV2CoreSupport(features);
6679
+ knowledgeV2Core ??= loadStorageModule().then(resolveKnowledgeV2Core).catch((error) => {
6680
+ knowledgeV2Core = void 0;
6681
+ throw error;
6682
+ });
6683
+ return knowledgeV2Core;
6684
+ };
6685
+ }
6686
+ //#endregion
6050
6687
  //#region src/storage/domains/knowledge/index.ts
6051
- let assertDescriptionWithinBound;
6688
+ const loadKnowledgeV2Core = createKnowledgeV2CoreLoader(coreFeatures, () => import("@mastra/core/storage"));
6052
6689
  async function assertKnowledgeDescriptionWithinBoundCompat(description) {
6053
- let assertWithinBound = assertDescriptionWithinBound;
6054
- if (!assertWithinBound) {
6055
- const resolvedAssert = (await import("@mastra/core/storage")).assertKnowledgeDescriptionWithinBound ?? ((value) => {
6056
- if (value !== void 0 && value.length > 400) throw new Error("Knowledge node description exceeds the 400 UTF-16 code unit limit");
6057
- });
6058
- assertDescriptionWithinBound = resolvedAssert;
6059
- assertWithinBound = resolvedAssert;
6060
- }
6061
- assertWithinBound(description);
6690
+ const { assertKnowledgeDescriptionWithinBound } = await loadKnowledgeV2Core();
6691
+ assertKnowledgeDescriptionWithinBound(description);
6692
+ }
6693
+ async function assertKnowledgeRecordTextWithinBoundCompat(text) {
6694
+ const { assertKnowledgeRecordTextWithinBound } = await loadKnowledgeV2Core();
6695
+ assertKnowledgeRecordTextWithinBound(text);
6696
+ }
6697
+ const reconcileChains = /* @__PURE__ */ new Map();
6698
+ const unidentifiedClientReconcileKey = {};
6699
+ function withReconcileLock(key, operation) {
6700
+ const lockKey = typeof key === "string" ? key : unidentifiedClientReconcileKey;
6701
+ const result = (reconcileChains.get(lockKey) ?? Promise.resolve()).then(operation, operation);
6702
+ const tail = result.then(() => void 0, () => void 0);
6703
+ reconcileChains.set(lockKey, tail);
6704
+ tail.finally(() => {
6705
+ if (reconcileChains.get(lockKey) === tail) reconcileChains.delete(lockKey);
6706
+ });
6707
+ return result;
6062
6708
  }
6063
- const visibleSql = `(scopeKey = ? OR substr(?, 1, length(scopeKey) + 1) = scopeKey || char(31))`;
6709
+ /**
6710
+ * Scope keys visible to a caller holding `scope`: a row is visible when its scope is a subset of the
6711
+ * caller's (companion entries such as `resource:x:uncurated` make this more than a prefix match).
6712
+ * Matching rows by `scopeKey` keeps visibility filters on an index; a caller scope has only a few
6713
+ * entries, so the list stays small.
6714
+ */
6715
+ function visibleScopeKeys(scope) {
6716
+ const canonical = canonicalizeKnowledgeScope(scope);
6717
+ const keys = /* @__PURE__ */ new Set();
6718
+ for (let mask = 1; mask < 1 << canonical.length; mask++) try {
6719
+ keys.add(knowledgeScopeKey(canonical.filter((_, index) => mask & 1 << index)));
6720
+ } catch {}
6721
+ return [...keys];
6722
+ }
6723
+ const visibleSql = (keys, scopeKeyColumn = "scopeKey") => `${scopeKeyColumn} IN (${keys.map(() => "?").join(",")})`;
6064
6724
  function parseJson$3(value) {
6065
6725
  if (typeof value === "string") return JSON.parse(value);
6066
6726
  if (value instanceof Uint8Array) return JSON.parse(new TextDecoder().decode(value));
@@ -6090,6 +6750,7 @@ function parseNode(row) {
6090
6750
  kind: row.kind == null ? "" : String(row.kind),
6091
6751
  content: row.content == null ? void 0 : String(row.content),
6092
6752
  description: row.description == null ? void 0 : String(row.description),
6753
+ isScope: row.isScope === true || row.isScope === 1 ? true : void 0,
6093
6754
  scope: parseJson$3(row.scopeJson ?? row.scope),
6094
6755
  version: Number(row.version),
6095
6756
  mergedInto: row.mergedInto == null ? void 0 : String(row.mergedInto),
@@ -6106,7 +6767,6 @@ function parseKnowledge(row) {
6106
6767
  sourceThreadId: String(row.sourceThreadId),
6107
6768
  capturedAt: toDate(row.capturedAt),
6108
6769
  when: optionalDate(row.when),
6109
- maxScope: row.maxScope == null ? void 0 : String(row.maxScope),
6110
6770
  metadata: (row.metadataJson ?? row.metadata) == null ? void 0 : parseJson$3(row.metadataJson ?? row.metadata),
6111
6771
  deletedAt: optionalDate(row.deletedAt),
6112
6772
  deletedBy: row.deletedBy == null ? void 0 : String(row.deletedBy)
@@ -6129,100 +6789,458 @@ function parseOutbox(row) {
6129
6789
  completedAt: optionalDate(row.completedAt)
6130
6790
  };
6131
6791
  }
6792
+ const KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE = "Knowledge curation cursors were removed: observation-time curate is the only Knowledge writer and needs no cursor.";
6793
+ const knowledgeV2TableSchemas = /* @__PURE__ */ new Map([
6794
+ [TABLE_KNOWLEDGE_NODES, KNOWLEDGE_V2_NODES_SCHEMA],
6795
+ [TABLE_KNOWLEDGE_RECORDS, KNOWLEDGE_V2_RECORDS_SCHEMA],
6796
+ [TABLE_KNOWLEDGE_MENTIONS, KNOWLEDGE_V2_MENTIONS_SCHEMA],
6797
+ [TABLE_KNOWLEDGE_ACTIVITY, KNOWLEDGE_V2_ACTIVITY_SCHEMA],
6798
+ [TABLE_KNOWLEDGE_SEMANTIC_OUTBOX, KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA],
6799
+ [TABLE_KNOWLEDGE_NODE_SCOPES, KNOWLEDGE_NODE_SCOPES_SCHEMA],
6800
+ [TABLE_KNOWLEDGE_RECORD_SCOPES, KNOWLEDGE_RECORD_SCOPES_SCHEMA],
6801
+ [TABLE_KNOWLEDGE_SCOPE_GRANTS, KNOWLEDGE_SCOPE_GRANTS_SCHEMA],
6802
+ [TABLE_KNOWLEDGE_ACCESS_STATE, KNOWLEDGE_ACCESS_STATE_SCHEMA],
6803
+ [TABLE_KNOWLEDGE_SCOPE_ADDRESSES, KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA],
6804
+ [TABLE_KNOWLEDGE_NODE_ADDRESSES, KNOWLEDGE_NODE_ADDRESSES_SCHEMA],
6805
+ [TABLE_KNOWLEDGE_IMPORT_STATE, KNOWLEDGE_IMPORT_STATE_SCHEMA],
6806
+ [TABLE_KNOWLEDGE_IMPORT_RUNS, KNOWLEDGE_IMPORT_RUNS_SCHEMA],
6807
+ [TABLE_KNOWLEDGE_PROPOSALS, KNOWLEDGE_PROPOSALS_SCHEMA]
6808
+ ]);
6809
+ function canonicalizeLibSQLUrl(url) {
6810
+ if (url.includes(":memory:")) return /[?&]cache=shared(?:&|$)/.test(url) ? "file::memory:?cache=shared" : void 0;
6811
+ if (url.startsWith("file:")) {
6812
+ const [path] = url.slice(5).split("?");
6813
+ if (url.startsWith("file://")) {
6814
+ const parsed = new URL(url);
6815
+ const host = parsed.hostname.toLocaleLowerCase();
6816
+ const filePath = host && host !== "localhost" ? `//${host}${parsed.pathname}` : parsed.pathname;
6817
+ return `file:${resolve(decodeURIComponent(filePath))}`;
6818
+ }
6819
+ return `file:${resolve(decodeURIComponent(path))}`;
6820
+ }
6821
+ try {
6822
+ const parsed = new URL(url);
6823
+ const port = parsed.port || (parsed.protocol === "libsql:" || parsed.protocol === "https:" ? "443" : "80");
6824
+ return `${parsed.protocol}//${parsed.hostname.toLocaleLowerCase()}:${port}${parsed.pathname}`;
6825
+ } catch {
6826
+ return url;
6827
+ }
6828
+ }
6829
+ const unidentifiedClientIsolationKey = {};
6830
+ function getLibSQLKnowledgeIsolationKey(config, client) {
6831
+ if (config.storageIsolationKey !== void 0) return config.storageIsolationKey;
6832
+ if (!config.url) return unidentifiedClientIsolationKey;
6833
+ const urlKey = canonicalizeLibSQLUrl(config.url);
6834
+ return urlKey ? `libsql:${urlKey}` : client ?? config.client ?? config;
6835
+ }
6132
6836
  var KnowledgeLibSQL = class extends KnowledgeStorage {
6133
6837
  #client;
6134
6838
  #db;
6135
6839
  constructor(config) {
6136
- super();
6137
- this.#client = resolveClient(config);
6840
+ const client = resolveClient(config);
6841
+ const storageIsolationKey = config.storageIsolationKey ?? getLibSQLKnowledgeIsolationKey(config, client);
6842
+ super({ storageIsolationKey });
6843
+ this.#client = client;
6138
6844
  this.#db = new LibSQLDB({
6139
6845
  client: this.#client,
6140
6846
  maxRetries: config.maxRetries,
6141
6847
  initialBackoffMs: config.initialBackoffMs
6142
6848
  });
6143
6849
  }
6850
+ getCapabilities() {
6851
+ return {
6852
+ contractVersion: 2,
6853
+ schemaVersion: 2,
6854
+ supportsV2: true,
6855
+ supportsSchemaInspection: true,
6856
+ supportsExplicitReset: true
6857
+ };
6858
+ }
6859
+ async inspectSchema() {
6860
+ return this.#inspectSchema(this.#client);
6861
+ }
6862
+ async #inspectSchema(executor) {
6863
+ const { inspectKnowledgeSchema } = await loadKnowledgeV2Core();
6864
+ try {
6865
+ const placeholders = KNOWLEDGE_TABLE_NAMES.map(() => "?").join(",");
6866
+ const tableNames = (await executor.execute({
6867
+ sql: `SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (${placeholders})`,
6868
+ args: [...KNOWLEDGE_TABLE_NAMES]
6869
+ })).rows.map((row) => String(row.name));
6870
+ if (tableNames.length === 0) return inspectKnowledgeSchema({
6871
+ available: true,
6872
+ tableNames
6873
+ });
6874
+ const missingTables = KNOWLEDGE_TABLE_NAMES.filter((table) => !tableNames.includes(table));
6875
+ if (missingTables.length > 0) return inspectKnowledgeSchema({
6876
+ available: true,
6877
+ tableNames,
6878
+ reason: `Missing Knowledge v2 tables: ${missingTables.join(", ")}`
6879
+ });
6880
+ for (const [table, schema] of knowledgeV2TableSchemas) {
6881
+ const columns = await executor.execute(`PRAGMA table_info("${table}")`);
6882
+ const actual = new Set(columns.rows.map((row) => String(row.name)));
6883
+ const missing = Object.keys(schema).filter((column) => !actual.has(column));
6884
+ if (missing.length > 0) return inspectKnowledgeSchema({
6885
+ available: true,
6886
+ tableNames,
6887
+ reason: `Knowledge table ${table} is missing v2 columns: ${missing.join(", ")}`
6888
+ });
6889
+ }
6890
+ const accessState = await executor.execute(`SELECT schemaVersion FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
6891
+ if (Number(accessState.rows[0]?.schemaVersion) !== 2) return inspectKnowledgeSchema({
6892
+ available: true,
6893
+ tableNames,
6894
+ reason: "Knowledge schema version marker is missing or incompatible"
6895
+ });
6896
+ return inspectKnowledgeSchema({
6897
+ available: true,
6898
+ tableNames,
6899
+ schemaVersion: 2
6900
+ });
6901
+ } catch (error) {
6902
+ return inspectKnowledgeSchema({
6903
+ available: false,
6904
+ tableNames: [],
6905
+ reason: error instanceof Error ? error.message : String(error)
6906
+ });
6907
+ }
6908
+ }
6144
6909
  async init() {
6145
- await this.#db.createTable({
6146
- tableName: TABLE_KNOWLEDGE_NODES,
6147
- schema: KNOWLEDGE_NODES_SCHEMA
6910
+ await this.#transaction((tx) => this.#initializeSchema(tx));
6911
+ }
6912
+ async #initializeSchema(tx) {
6913
+ const { assertKnowledgeSchemaCompatible } = await loadKnowledgeV2Core();
6914
+ let inspection = await this.#inspectSchema(tx);
6915
+ if (inspection.status === "incompatible-reset-required" && await this.#isPublishedV1Layout(tx)) {
6916
+ for (const table of [...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()]) await tx.execute(`DROP TABLE IF EXISTS "${table}"`);
6917
+ inspection = await this.#inspectSchema(tx);
6918
+ }
6919
+ assertKnowledgeSchemaCompatible(inspection);
6920
+ const createTable = (input) => this.#db.createTable({
6921
+ ...input,
6922
+ executor: tx
6148
6923
  });
6149
- await this.#db.alterTable({
6924
+ await createTable({
6150
6925
  tableName: TABLE_KNOWLEDGE_NODES,
6151
- schema: KNOWLEDGE_NODES_SCHEMA,
6152
- ifNotExists: ["description"]
6926
+ schema: KNOWLEDGE_V2_NODES_SCHEMA
6153
6927
  });
6154
- await this.#db.createTable({
6928
+ await createTable({
6155
6929
  tableName: TABLE_KNOWLEDGE_RECORDS,
6156
- schema: KNOWLEDGE_RECORDS_SCHEMA
6930
+ schema: KNOWLEDGE_V2_RECORDS_SCHEMA
6157
6931
  });
6158
- await this.#db.createTable({
6932
+ await createTable({
6159
6933
  tableName: TABLE_KNOWLEDGE_MENTIONS,
6160
- schema: KNOWLEDGE_MENTIONS_SCHEMA,
6934
+ schema: KNOWLEDGE_V2_MENTIONS_SCHEMA,
6935
+ compositePrimaryKey: ["recordId", "sourceId"]
6936
+ });
6937
+ await createTable({
6938
+ tableName: TABLE_KNOWLEDGE_NODE_SCOPES,
6939
+ schema: KNOWLEDGE_NODE_SCOPES_SCHEMA,
6940
+ compositePrimaryKey: ["nodeId", "scopeNodeId"]
6941
+ });
6942
+ await createTable({
6943
+ tableName: TABLE_KNOWLEDGE_RECORD_SCOPES,
6944
+ schema: KNOWLEDGE_RECORD_SCOPES_SCHEMA,
6945
+ compositePrimaryKey: ["recordId", "scopeNodeId"]
6946
+ });
6947
+ await createTable({
6948
+ tableName: TABLE_KNOWLEDGE_SCOPE_GRANTS,
6949
+ schema: KNOWLEDGE_SCOPE_GRANTS_SCHEMA,
6950
+ compositePrimaryKey: ["scopeNodeId", "scopeRefId"]
6951
+ });
6952
+ await createTable({
6953
+ tableName: TABLE_KNOWLEDGE_ACCESS_STATE,
6954
+ schema: KNOWLEDGE_ACCESS_STATE_SCHEMA
6955
+ });
6956
+ await createTable({
6957
+ tableName: TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
6958
+ schema: KNOWLEDGE_SCOPE_ADDRESSES_SCHEMA
6959
+ });
6960
+ await createTable({
6961
+ tableName: TABLE_KNOWLEDGE_NODE_ADDRESSES,
6962
+ schema: KNOWLEDGE_NODE_ADDRESSES_SCHEMA,
6963
+ compositePrimaryKey: ["source", "address"]
6964
+ });
6965
+ await createTable({
6966
+ tableName: TABLE_KNOWLEDGE_IMPORT_STATE,
6967
+ schema: KNOWLEDGE_IMPORT_STATE_SCHEMA,
6161
6968
  compositePrimaryKey: [
6162
- "sourceType",
6163
- "sourceId",
6164
- "recordId"
6969
+ "importerId",
6970
+ "binding",
6971
+ "key"
6165
6972
  ]
6166
6973
  });
6167
- await this.#db.createTable({
6168
- tableName: TABLE_KNOWLEDGE_CURSORS,
6169
- schema: KNOWLEDGE_CURSORS_SCHEMA,
6170
- compositePrimaryKey: ["sourceThreadId", "agent"]
6974
+ await createTable({
6975
+ tableName: TABLE_KNOWLEDGE_IMPORT_RUNS,
6976
+ schema: KNOWLEDGE_IMPORT_RUNS_SCHEMA
6171
6977
  });
6172
- await this.#db.createTable({
6978
+ await createTable({
6979
+ tableName: TABLE_KNOWLEDGE_PROPOSALS,
6980
+ schema: KNOWLEDGE_PROPOSALS_SCHEMA
6981
+ });
6982
+ await createTable({
6173
6983
  tableName: TABLE_KNOWLEDGE_ACTIVITY,
6174
- schema: KNOWLEDGE_ACTIVITY_SCHEMA
6984
+ schema: KNOWLEDGE_V2_ACTIVITY_SCHEMA
6175
6985
  });
6176
- await this.#db.createTable({
6986
+ await createTable({
6177
6987
  tableName: TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
6178
6988
  schema: KNOWLEDGE_SEMANTIC_OUTBOX_SCHEMA
6179
6989
  });
6180
- await this.#client.batch([
6181
- {
6182
- sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_nodes_identity ON "${TABLE_KNOWLEDGE_NODES}" (type, scopeKey, canonicalName)`,
6183
- args: []
6184
- },
6185
- {
6186
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON "${TABLE_KNOWLEDGE_NODES}" (scopeKey, type)`,
6187
- args: []
6188
- },
6189
- {
6190
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON "${TABLE_KNOWLEDGE_RECORDS}" (node, id DESC)`,
6191
- args: []
6192
- },
6193
- {
6194
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_records_thread_latest ON "${TABLE_KNOWLEDGE_RECORDS}" (sourceThreadId, id DESC)`,
6195
- args: []
6196
- },
6197
- {
6198
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_mentions_record ON "${TABLE_KNOWLEDGE_MENTIONS}" (recordId, sourceType, sourceId)`,
6199
- args: []
6200
- },
6201
- {
6202
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON "${TABLE_KNOWLEDGE_ACTIVITY}" (id DESC)`,
6203
- args: []
6204
- },
6205
- {
6206
- sql: `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (idempotencyKey)`,
6207
- args: []
6208
- },
6209
- {
6210
- sql: `CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (status, availableAt, createdAt)`,
6211
- args: []
6212
- }
6213
- ], "write");
6990
+ for (const statement of [
6991
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_nodes_identity ON "${TABLE_KNOWLEDGE_NODES}" (type, scopeKey, canonicalName)`,
6992
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_scope ON "${TABLE_KNOWLEDGE_NODES}" (scopeKey, type)`,
6993
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_nodes_name ON "${TABLE_KNOWLEDGE_NODES}" (type, canonicalName)`,
6994
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_records_node_latest ON "${TABLE_KNOWLEDGE_RECORDS}" (node, id DESC)`,
6995
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_records_thread_latest ON "${TABLE_KNOWLEDGE_RECORDS}" (sourceThreadId, id DESC)`,
6996
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_mentions_record ON "${TABLE_KNOWLEDGE_MENTIONS}" (recordId, sourceType, sourceId)`,
6997
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_latest ON "${TABLE_KNOWLEDGE_ACTIVITY}" (id DESC)`,
6998
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_records_scope ON "${TABLE_KNOWLEDGE_RECORDS}" (scopeKey, id)`,
6999
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_scope ON "${TABLE_KNOWLEDGE_ACTIVITY}" (scopeKey, id DESC)`,
7000
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_knowledge_outbox_idempotency ON "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (idempotencyKey)`,
7001
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_outbox_claim ON "${TABLE_KNOWLEDGE_SEMANTIC_OUTBOX}" (status, availableAt, createdAt)`,
7002
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_node_scopes_scope ON "${TABLE_KNOWLEDGE_NODE_SCOPES}" (scopeNodeId, nodeId)`,
7003
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_record_scopes_scope ON "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (scopeNodeId, recordId)`,
7004
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_scope_grants_ref ON "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" (scopeRefId, scopeNodeId)`,
7005
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_node_addresses_node ON "${TABLE_KNOWLEDGE_NODE_ADDRESSES}" (nodeId)`,
7006
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_import_runs_lookup ON "${TABLE_KNOWLEDGE_IMPORT_RUNS}" (importerId, binding, queuedAt DESC)`,
7007
+ `CREATE INDEX IF NOT EXISTS idx_knowledge_activity_import_run ON "${TABLE_KNOWLEDGE_ACTIVITY}" (importRunId, id DESC)`
7008
+ ]) await tx.execute(statement);
7009
+ await tx.execute({
7010
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_ACCESS_STATE}" (id, epoch, schemaVersion) VALUES ('global', 0, ?)`,
7011
+ args: [2]
7012
+ });
7013
+ }
7014
+ /**
7015
+ * True when the only Knowledge objects are the tables and indexes published v1 adapters created,
7016
+ * with or without rows. Anything else (v2 tables, unknown tables, views, triggers) needs an
7017
+ * explicit reset.
7018
+ */
7019
+ async #isPublishedV1Layout(executor) {
7020
+ 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 '\\'");
7021
+ for (const row of objects.rows) {
7022
+ const type = String(row.type);
7023
+ const name = String(row.name);
7024
+ if (type === "table" && PUBLISHED_KNOWLEDGE_V1_TABLE_NAMES.has(name)) continue;
7025
+ const knownIndex = PUBLISHED_KNOWLEDGE_V1_INDEX_NAMES.has(name) || name.startsWith("sqlite_autoindex_");
7026
+ if (type !== "index" || !knownIndex) return false;
7027
+ }
7028
+ return true;
6214
7029
  }
6215
7030
  async dangerouslyClearAll() {
6216
7031
  await this.#transaction(async (tx) => {
6217
7032
  for (const table of [
7033
+ TABLE_KNOWLEDGE_RECORD_SCOPES,
7034
+ TABLE_KNOWLEDGE_NODE_SCOPES,
7035
+ TABLE_KNOWLEDGE_SCOPE_GRANTS,
7036
+ TABLE_KNOWLEDGE_SCOPE_ADDRESSES,
7037
+ TABLE_KNOWLEDGE_NODE_ADDRESSES,
6218
7038
  TABLE_KNOWLEDGE_MENTIONS,
6219
- TABLE_KNOWLEDGE_RECORDS,
6220
- TABLE_KNOWLEDGE_NODES,
6221
- TABLE_KNOWLEDGE_CURSORS,
7039
+ TABLE_KNOWLEDGE_PROPOSALS,
6222
7040
  TABLE_KNOWLEDGE_ACTIVITY,
6223
- TABLE_KNOWLEDGE_SEMANTIC_OUTBOX
7041
+ TABLE_KNOWLEDGE_IMPORT_STATE,
7042
+ TABLE_KNOWLEDGE_IMPORT_RUNS,
7043
+ TABLE_KNOWLEDGE_SEMANTIC_OUTBOX,
7044
+ TABLE_KNOWLEDGE_RECORDS,
7045
+ TABLE_KNOWLEDGE_NODES
6224
7046
  ]) await tx.execute(`DELETE FROM "${table}"`);
7047
+ await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch = 0 WHERE id = 'global'`);
7048
+ });
7049
+ }
7050
+ async dangerouslyReset() {
7051
+ await withClientWriteLock(this.#client, async () => {
7052
+ await this.#client.batch([...RETIRED_KNOWLEDGE_TABLE_NAMES, ...[...KNOWLEDGE_TABLE_NAMES].reverse()].map((table) => ({
7053
+ sql: `DROP TABLE IF EXISTS "${table}"`,
7054
+ args: []
7055
+ })), "write");
6225
7056
  });
7057
+ await this.init();
7058
+ }
7059
+ async reconcileStructure(plan) {
7060
+ return withReconcileLock(this.getStorageIsolationKey(), () => this.#transaction(async (tx) => {
7061
+ await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch=epoch WHERE id='global'`);
7062
+ const scopes = {};
7063
+ const createdScopeIds = [];
7064
+ const createdAddresses = /* @__PURE__ */ new Set();
7065
+ const retrofit = plan.retrofit ?? true;
7066
+ let structureChanged = false;
7067
+ const deletedScopeAddresses = /* @__PURE__ */ new Set();
7068
+ const resolveAddress = async (address) => {
7069
+ if (scopes[address]) return scopes[address];
7070
+ const row = (await tx.execute({
7071
+ sql: `SELECT a.scopeNodeId,n.isScope,n.deletedAt FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" a JOIN "${TABLE_KNOWLEDGE_NODES}" n ON n.id=a.scopeNodeId WHERE a.address=?`,
7072
+ args: [address]
7073
+ })).rows[0];
7074
+ if (!row) return void 0;
7075
+ if (!row.isScope) throw new Error(`Knowledge address ${address} does not reference a scope`);
7076
+ if (row.deletedAt) deletedScopeAddresses.add(address);
7077
+ scopes[address] = String(row.scopeNodeId);
7078
+ return scopes[address];
7079
+ };
7080
+ for (const scope of plan.scopes) {
7081
+ if (await resolveAddress(scope.address)) continue;
7082
+ const id = randomUUID();
7083
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7084
+ await tx.execute({
7085
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,description,isScope,metadata,version,createdAt,updatedAt) VALUES (?,'node',?,?,?,?,TRUE,jsonb(?),1,?,?)`,
7086
+ args: [
7087
+ id,
7088
+ scope.name,
7089
+ canonicalName(scope.name),
7090
+ scope.kind ?? null,
7091
+ scope.description ?? null,
7092
+ scope.metadata ? JSON.stringify(scope.metadata) : null,
7093
+ now,
7094
+ now
7095
+ ]
7096
+ });
7097
+ await tx.execute({
7098
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" (address,scopeNodeId) VALUES (?,?)`,
7099
+ args: [scope.address, id]
7100
+ });
7101
+ scopes[scope.address] = id;
7102
+ createdAddresses.add(scope.address);
7103
+ createdScopeIds.push(id);
7104
+ structureChanged = true;
7105
+ }
7106
+ for (const scope of plan.scopes) {
7107
+ if (!createdAddresses.has(scope.address) && (!retrofit || deletedScopeAddresses.has(scope.address))) continue;
7108
+ const scopeNodeId = scopes[scope.address];
7109
+ for (const parentAddress of scope.parentAddresses ?? []) {
7110
+ const parentId = await resolveAddress(parentAddress);
7111
+ if (!parentId || deletedScopeAddresses.has(parentAddress)) {
7112
+ const deletedParentId = scopes[parentAddress];
7113
+ if ((deletedParentId ? await tx.execute({
7114
+ sql: `SELECT 1 FROM "mastra_knowledge_node_scopes" WHERE nodeId=? AND scopeNodeId=?`,
7115
+ args: [scopeNodeId, deletedParentId]
7116
+ }) : void 0)?.rows.length) continue;
7117
+ throw new Error(`Knowledge parent scope does not exist: ${parentAddress}`);
7118
+ }
7119
+ if ((await tx.execute({
7120
+ sql: `SELECT 1 FROM "mastra_knowledge_node_scopes" WHERE nodeId=? AND scopeNodeId=?`,
7121
+ args: [scopeNodeId, parentId]
7122
+ })).rows.length) continue;
7123
+ if ((await tx.execute({
7124
+ sql: `SELECT n.id FROM "mastra_knowledge_node_scopes" ns JOIN "${TABLE_KNOWLEDGE_NODES}" n ON n.id=ns.nodeId WHERE ns.scopeNodeId=? AND n.isScope AND n.canonicalName=? AND n.deletedAt IS NULL AND n.id<>? LIMIT 1`,
7125
+ args: [
7126
+ parentId,
7127
+ canonicalName(scope.name),
7128
+ scopeNodeId
7129
+ ]
7130
+ })).rows.length) throw new Error(`Knowledge scope name ${scope.name} already exists under ${parentAddress}`);
7131
+ const inserted = await tx.execute({
7132
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
7133
+ args: [
7134
+ scopeNodeId,
7135
+ parentId,
7136
+ (/* @__PURE__ */ new Date()).toISOString()
7137
+ ]
7138
+ });
7139
+ structureChanged ||= inserted.rowsAffected > 0;
7140
+ }
7141
+ for (const grant of scope.grants ?? []) {
7142
+ const scopeRefId = await resolveAddress(grant.scopeRefAddress);
7143
+ if (!scopeRefId || deletedScopeAddresses.has(grant.scopeRefAddress)) {
7144
+ const deletedScopeRefId = scopes[grant.scopeRefAddress];
7145
+ if ((deletedScopeRefId ? await tx.execute({
7146
+ sql: `SELECT 1 FROM "mastra_knowledge_scope_grants" WHERE scopeNodeId=? AND scopeRefId=?`,
7147
+ args: [scopeNodeId, deletedScopeRefId]
7148
+ }) : void 0)?.rows.length) continue;
7149
+ throw new Error(`Knowledge grant scope does not exist: ${grant.scopeRefAddress}`);
7150
+ }
7151
+ const inserted = await tx.execute({
7152
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_SCOPE_GRANTS}" (scopeNodeId,scopeRefId,role,canSuggest) VALUES (?,?,?,?)`,
7153
+ args: [
7154
+ scopeNodeId,
7155
+ scopeRefId,
7156
+ grant.role,
7157
+ grant.canSuggest ?? null
7158
+ ]
7159
+ });
7160
+ structureChanged ||= inserted.rowsAffected > 0;
7161
+ }
7162
+ }
7163
+ if (structureChanged) await tx.execute(`UPDATE "${TABLE_KNOWLEDGE_ACCESS_STATE}" SET epoch=epoch+1 WHERE id='global'`);
7164
+ const state = await tx.execute(`SELECT epoch FROM "${TABLE_KNOWLEDGE_ACCESS_STATE}" WHERE id='global'`);
7165
+ return {
7166
+ scopes,
7167
+ createdScopeIds,
7168
+ deletedScopeAddresses: [...deletedScopeAddresses],
7169
+ changed: structureChanged,
7170
+ accessEpoch: Number(state.rows[0]?.epoch ?? 0)
7171
+ };
7172
+ }));
7173
+ }
7174
+ async listScopeNodes(input = {}) {
7175
+ const { limit, after } = parseListKnowledgeScopeNodesInput(input);
7176
+ if (input.addresses?.length === 0 || input.ids?.length === 0) return {
7177
+ scopes: [],
7178
+ nextCursor: null
7179
+ };
7180
+ const args = [];
7181
+ const where = [`n.isScope`, `n.deletedAt IS NULL`];
7182
+ let within = "";
7183
+ if (input.withinAddress !== void 0) {
7184
+ within = `WITH RECURSIVE within_scope(id) AS (SELECT r.id FROM "${TABLE_KNOWLEDGE_NODES}" r JOIN "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" ra ON ra.scopeNodeId=r.id WHERE ra.address=? AND r.isScope AND r.deletedAt IS NULL UNION SELECT ns.nodeId FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns JOIN within_scope w ON ns.scopeNodeId=w.id JOIN "${TABLE_KNOWLEDGE_NODES}" c ON c.id=ns.nodeId WHERE c.isScope AND c.deletedAt IS NULL) `;
7185
+ args.push(input.withinAddress);
7186
+ where.push("n.id IN (SELECT id FROM within_scope)");
7187
+ }
7188
+ if (input.addresses) {
7189
+ where.push(`a.address IN (${input.addresses.map(() => "?").join(",")})`);
7190
+ args.push(...input.addresses);
7191
+ }
7192
+ if (input.ids) {
7193
+ where.push(`n.id IN (${input.ids.map(() => "?").join(",")})`);
7194
+ args.push(...input.ids);
7195
+ }
7196
+ if (after) {
7197
+ where.push("(n.name > ? OR (n.name = ? AND n.id > ?))");
7198
+ args.push(after.name, after.name, after.id);
7199
+ }
7200
+ args.push(limit + 1);
7201
+ const rows = (await this.#client.execute({
7202
+ sql: `${within}SELECT n.id,n.name,n.kind,n.description,a.address FROM "${TABLE_KNOWLEDGE_NODES}" n LEFT JOIN "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" a ON a.scopeNodeId=n.id WHERE ${where.join(" AND ")} ORDER BY n.name, n.id LIMIT ?`,
7203
+ args
7204
+ })).rows;
7205
+ if (rows.length === 0) return {
7206
+ scopes: [],
7207
+ nextCursor: null
7208
+ };
7209
+ const parents = await this.#client.execute({
7210
+ sql: `SELECT ns.nodeId,ns.scopeNodeId FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" ns WHERE ns.nodeId IN (${rows.map(() => "?").join(",")}) ORDER BY ns.scopeNodeId`,
7211
+ args: rows.map((row) => String(row.id))
7212
+ });
7213
+ const parentsByScopeId = /* @__PURE__ */ new Map();
7214
+ for (const row of parents.rows) {
7215
+ const scopeId = String(row.nodeId);
7216
+ const parentId = String(row.scopeNodeId);
7217
+ const existing = parentsByScopeId.get(scopeId);
7218
+ if (existing) existing.push(parentId);
7219
+ else parentsByScopeId.set(scopeId, [parentId]);
7220
+ }
7221
+ return pageKnowledgeScopeNodes(rows.map((row) => ({
7222
+ id: String(row.id),
7223
+ address: String(row.address),
7224
+ name: String(row.name),
7225
+ ...row.kind == null ? {} : { kind: String(row.kind) },
7226
+ ...row.description == null ? {} : { description: String(row.description) },
7227
+ parentIds: parentsByScopeId.get(String(row.id)) ?? []
7228
+ })), limit, input);
7229
+ }
7230
+ async listScopeMembers(input) {
7231
+ const { limit, after } = parseListKnowledgeScopeMembersInput(input);
7232
+ const args = [input.scopeNodeId];
7233
+ let keyset = "";
7234
+ if (after) {
7235
+ const updatedAt = after.updatedAt.toISOString();
7236
+ keyset = ` AND (n.updatedAt < ? OR (n.updatedAt = ? AND (n.name > ? OR (n.name = ? AND n.id > ?))))`;
7237
+ args.push(updatedAt, updatedAt, after.name, after.name, after.id);
7238
+ }
7239
+ args.push(limit + 1);
7240
+ return pageKnowledgeScopeMembers((await this.#client.execute({
7241
+ sql: `SELECT *, json(scope) AS scopeJson FROM "${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 ?`,
7242
+ args
7243
+ })).rows.map(parseNode), limit, input);
6226
7244
  }
6227
7245
  async createNode(input) {
6228
7246
  await assertKnowledgeDescriptionWithinBoundCompat(input.description);
@@ -6232,6 +7250,7 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6232
7250
  if (existing) {
6233
7251
  const terminal = await this.#resolveTerminalNode(tx, existing.id);
6234
7252
  if (!isKnowledgeScopeVisible(terminal.scope, scope)) throw new Error(`Merged knowledge node is not visible from scope: ${input.name}`);
7253
+ await this.#placeNodeInScopes(tx, terminal.id, input.scopeAddresses, /* @__PURE__ */ new Date());
6235
7254
  return terminal;
6236
7255
  }
6237
7256
  const now = /* @__PURE__ */ new Date();
@@ -6248,7 +7267,7 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6248
7267
  updatedAt: now
6249
7268
  };
6250
7269
  await tx.execute({
6251
- sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,description,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,NULL,?,?)`,
7270
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,description,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,?,?,FALSE,jsonb(?),?,?,NULL,?,?)`,
6252
7271
  args: [
6253
7272
  node.id,
6254
7273
  "node",
@@ -6264,6 +7283,8 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6264
7283
  now.toISOString()
6265
7284
  ]
6266
7285
  });
7286
+ await this.#replaceNodeScopes(tx, node.id, scope, now);
7287
+ await this.#placeNodeInScopes(tx, node.id, input.scopeAddresses, now);
6267
7288
  await this.#replaceMentions(tx, "node", node.id, node.content ?? "", input.resolutionScope ?? scope, scope);
6268
7289
  await this.#activity(tx, "node-created", "node", node.id, scope);
6269
7290
  await this.#outbox(tx, "node", node.id, "upsert", node.version, scope);
@@ -6280,13 +7301,13 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6280
7301
  return this.#resolveNode(this.#client, input.name, canonicalizeKnowledgeScope(input.scope));
6281
7302
  }
6282
7303
  async listNodes(input) {
6283
- const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
7304
+ const keys = visibleScopeKeys(canonicalizeKnowledgeScope(input.scope));
6284
7305
  const clauses = [
6285
7306
  `type = 'node'`,
6286
7307
  "mergedInto IS NULL",
6287
- visibleSql
7308
+ visibleSql(keys)
6288
7309
  ];
6289
- const args = [key, key];
7310
+ const args = [...keys];
6290
7311
  if (input.namePrefix) {
6291
7312
  clauses.push("canonicalName LIKE ? ESCAPE '='");
6292
7313
  args.push(`${escapeLikePattern(canonicalName(input.namePrefix))}%`);
@@ -6338,6 +7359,7 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6338
7359
  input.version
6339
7360
  ]
6340
7361
  })).rowsAffected === 0) throw new KnowledgeConflictError(input.id);
7362
+ if (knowledgeScopeKey(existing.scope) !== knowledgeScopeKey(scope)) await this.#swapIdentityScopes(tx, input.id, existing.scope, scope, now);
6341
7363
  if (input.content !== void 0 || input.name !== void 0 || input.scope !== void 0) await this.#replaceMentions(tx, "node", input.id, content ?? "", input.resolutionScope ?? scope, scope);
6342
7364
  await this.#activity(tx, "node-updated", "node", input.id, scope);
6343
7365
  if (knowledgeScopeKey(existing.scope) !== knowledgeScopeKey(scope)) {
@@ -6392,8 +7414,12 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6392
7414
  ]
6393
7415
  })).rowsAffected === 0) throw new KnowledgeConflictError(source.id);
6394
7416
  await tx.execute({
6395
- sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET node=? WHERE node=?`,
6396
- args: [target.id, source.id]
7417
+ sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET node=?,version=version+1,updatedAt=? WHERE node=?`,
7418
+ args: [
7419
+ target.id,
7420
+ (/* @__PURE__ */ new Date()).toISOString(),
7421
+ source.id
7422
+ ]
6397
7423
  });
6398
7424
  await tx.execute({
6399
7425
  sql: `DELETE FROM "${TABLE_KNOWLEDGE_MENTIONS}" WHERE recordId=? AND EXISTS (SELECT 1 FROM "${TABLE_KNOWLEDGE_MENTIONS}" target WHERE target.sourceType="${TABLE_KNOWLEDGE_MENTIONS}".sourceType AND target.sourceId="${TABLE_KNOWLEDGE_MENTIONS}".sourceId AND target.recordId=?)`,
@@ -6433,10 +7459,10 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6433
7459
  });
6434
7460
  }
6435
7461
  async appendKnowledge(input) {
7462
+ await assertKnowledgeRecordTextWithinBoundCompat(input.text);
6436
7463
  const scope = canonicalizeKnowledgeScope(input.scope);
6437
7464
  const resolutionScope = canonicalizeKnowledgeScope(input.resolutionScope);
6438
7465
  const defaultScope = canonicalizeKnowledgeScope(input.defaultScope);
6439
- assertKnowledgeScopeWithinCeiling(scope, input.maxScope);
6440
7466
  return this.#transaction(async (tx) => {
6441
7467
  const node = nodeReferenceId(input.node);
6442
7468
  const parent = await this.#resolveTerminalNode(tx, node);
@@ -6449,11 +7475,10 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6449
7475
  sourceThreadId: input.sourceThreadId,
6450
7476
  capturedAt: /* @__PURE__ */ new Date(),
6451
7477
  when: input.when ? new Date(input.when) : void 0,
6452
- maxScope: input.maxScope,
6453
7478
  metadata: input.metadata
6454
7479
  };
6455
7480
  await tx.execute({
6456
- sql: `INSERT INTO "${TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",maxScope,metadata,deletedAt,deletedBy) VALUES (?,?,?,jsonb(?),?,?,?,?,?,jsonb(?),NULL,NULL)`,
7481
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_RECORDS}" (id,node,text,scope,scopeKey,sourceThreadId,capturedAt,"when",metadata,version,updatedAt,deletedAt,deletedBy) VALUES (?,?,?,jsonb(?),?,?,?,?,jsonb(?),?,?,NULL,NULL)`,
6457
7482
  args: [
6458
7483
  record.id,
6459
7484
  record.node,
@@ -6463,10 +7488,12 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6463
7488
  record.sourceThreadId,
6464
7489
  record.capturedAt.toISOString(),
6465
7490
  record.when?.toISOString() ?? null,
6466
- record.maxScope ?? null,
6467
- record.metadata ? JSON.stringify(record.metadata) : null
7491
+ record.metadata ? JSON.stringify(record.metadata) : null,
7492
+ 1,
7493
+ record.capturedAt.toISOString()
6468
7494
  ]
6469
7495
  });
7496
+ await this.#replaceRecordScopes(tx, record.id, scope, record.capturedAt);
6470
7497
  await this.#replaceMentions(tx, "record", record.id, record.text, resolutionScope, defaultScope);
6471
7498
  await this.#activity(tx, "record-created", "record", record.id, scope, record.sourceThreadId);
6472
7499
  await this.#outbox(tx, "record", record.id, "upsert", record.id, scope);
@@ -6490,17 +7517,13 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6490
7517
  return this.#queryKnowledge(input, "related");
6491
7518
  }
6492
7519
  async knowledgeBySource(input) {
6493
- const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
6494
- const args = [
6495
- input.sourceThreadId,
6496
- key,
6497
- key
6498
- ];
7520
+ const keys = visibleScopeKeys(canonicalizeKnowledgeScope(input.scope));
7521
+ const args = [input.sourceThreadId, ...keys];
6499
7522
  if (input.after) args.push(input.after);
6500
7523
  const limit = input.limit ?? 100;
6501
7524
  args.push(limit + 1);
6502
7525
  const records = (await this.#client.execute({
6503
- sql: `SELECT *,json(scope) AS scopeJson,json(metadata) AS metadataJson FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE sourceThreadId=? AND ${visibleSql}${input.includeDeleted ? "" : " AND deletedAt IS NULL"}${input.after ? " AND id > ?" : ""} ORDER BY id ASC LIMIT ?`,
7526
+ sql: `SELECT *,json(scope) AS scopeJson,json(metadata) AS metadataJson FROM "${TABLE_KNOWLEDGE_RECORDS}" WHERE sourceThreadId=? AND ${visibleSql(keys)}${input.includeDeleted ? "" : " AND deletedAt IS NULL"}${input.after ? " AND id > ?" : ""} ORDER BY id ASC LIMIT ?`,
6504
7527
  args
6505
7528
  })).rows.map(parseKnowledge);
6506
7529
  return {
@@ -6515,10 +7538,11 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6515
7538
  if (record.deletedAt) return record;
6516
7539
  const deletedAt = /* @__PURE__ */ new Date();
6517
7540
  await tx.execute({
6518
- sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=? WHERE id=? AND deletedAt IS NULL`,
7541
+ sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=?,deletedBy=?,version=version+1,updatedAt=? WHERE id=? AND deletedAt IS NULL`,
6519
7542
  args: [
6520
7543
  deletedAt.toISOString(),
6521
7544
  input.deletedBy,
7545
+ deletedAt.toISOString(),
6522
7546
  input.id
6523
7547
  ]
6524
7548
  });
@@ -6537,8 +7561,8 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6537
7561
  if (!record) throw new KnowledgeNotFoundError("record", input.id);
6538
7562
  if (!record.deletedAt) return record;
6539
7563
  await tx.execute({
6540
- sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL WHERE id=?`,
6541
- args: [input.id]
7564
+ sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET deletedAt=NULL,deletedBy=NULL,version=version+1,updatedAt=? WHERE id=?`,
7565
+ args: [(/* @__PURE__ */ new Date()).toISOString(), input.id]
6542
7566
  });
6543
7567
  await this.#activity(tx, "record-restored", "record", input.id, record.scope, record.sourceThreadId);
6544
7568
  await this.#outbox(tx, "record", input.id, "upsert", createKnowledgeUlid(), record.scope);
@@ -6554,15 +7578,17 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6554
7578
  return this.#transaction(async (tx) => {
6555
7579
  const record = await this.#getKnowledge(tx, input.id, true);
6556
7580
  if (!record) throw new KnowledgeNotFoundError("record", input.id);
6557
- assertKnowledgeScopeWithinCeiling(scope, record.maxScope);
7581
+ const updatedAt = /* @__PURE__ */ new Date();
6558
7582
  await tx.execute({
6559
- sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=? WHERE id=?`,
7583
+ sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET scope=jsonb(?),scopeKey=?,version=version+1,updatedAt=? WHERE id=?`,
6560
7584
  args: [
6561
7585
  JSON.stringify(scope),
6562
7586
  knowledgeScopeKey(scope),
7587
+ updatedAt.toISOString(),
6563
7588
  input.id
6564
7589
  ]
6565
7590
  });
7591
+ await this.#replaceRecordScopes(tx, input.id, scope, updatedAt);
6566
7592
  await this.#activity(tx, "record-rescoped", "record", input.id, scope, record.sourceThreadId);
6567
7593
  if (knowledgeScopeKey(record.scope) !== knowledgeScopeKey(scope)) await this.#outbox(tx, "record", input.id, "delete", createKnowledgeUlid(), record.scope);
6568
7594
  if (!record.deletedAt) await this.#outbox(tx, "record", input.id, "upsert", createKnowledgeUlid(), scope);
@@ -6572,32 +7598,16 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6572
7598
  };
6573
7599
  });
6574
7600
  }
6575
- async raiseKnowledgeCeiling(input) {
6576
- return this.#transaction(async (tx) => {
6577
- const record = await this.#getKnowledge(tx, input.id, true);
6578
- if (!record) throw new KnowledgeNotFoundError("record", input.id);
6579
- assertKnowledgeScopeWithinCeiling(record.scope, input.maxScope);
6580
- assertKnowledgeCeilingRaised(record.maxScope, input.maxScope);
6581
- await tx.execute({
6582
- sql: `UPDATE "${TABLE_KNOWLEDGE_RECORDS}" SET maxScope=? WHERE id=?`,
6583
- args: [input.maxScope ?? null, input.id]
6584
- });
6585
- return {
6586
- ...record,
6587
- maxScope: input.maxScope
6588
- };
6589
- });
6590
- }
6591
7601
  async search(input) {
6592
- const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
7602
+ const scope = canonicalizeKnowledgeScope(input.scope);
7603
+ const keys = visibleScopeKeys(scope);
6593
7604
  const normalizedQuery = input.query.trim().toLocaleLowerCase();
6594
7605
  if (!normalizedQuery) return [];
6595
7606
  const query = `%${escapeLikePattern(normalizedQuery)}%`;
6596
7607
  const results = (await this.#client.execute({
6597
- sql: `SELECT *,json(scope) AS scopeJson FROM "${TABLE_KNOWLEDGE_NODES}" WHERE mergedInto IS NULL AND ${visibleSql} AND (canonicalName LIKE ? ESCAPE '=' OR lower(COALESCE(kind,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(content,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(description,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
7608
+ sql: `SELECT *,json(scope) AS scopeJson FROM "${TABLE_KNOWLEDGE_NODES}" WHERE mergedInto IS NULL AND ${visibleSql(keys)} AND (canonicalName LIKE ? ESCAPE '=' OR lower(COALESCE(kind,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(content,'')) LIKE ? ESCAPE '=' OR lower(COALESCE(description,'')) LIKE ? ESCAPE '=') ORDER BY updatedAt DESC LIMIT ?`,
6598
7609
  args: [
6599
- key,
6600
- key,
7610
+ ...keys,
6601
7611
  query,
6602
7612
  query,
6603
7613
  query,
@@ -6618,60 +7628,47 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6618
7628
  }));
6619
7629
  if (results.length < (input.limit ?? 20)) {
6620
7630
  const knowledge = await this.#client.execute({
6621
- sql: `SELECT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson,n.name FROM "${TABLE_KNOWLEDGE_RECORDS}" k JOIN "${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.replaceAll("scopeKey", "k.scopeKey")} AND lower(k.text) LIKE ? ESCAPE '=' ORDER BY k.id DESC LIMIT ?`,
7631
+ sql: `SELECT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson,n.name,json(n.scope) AS parentScopeJson FROM "${TABLE_KNOWLEDGE_RECORDS}" k CROSS JOIN "${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 ?`,
6622
7632
  args: [
6623
- key,
6624
- key,
7633
+ ...keys,
6625
7634
  query,
6626
7635
  (input.limit ?? 20) - results.length
6627
7636
  ]
6628
7637
  });
6629
- results.push(...knowledge.rows.map((row) => ({
6630
- type: "record",
6631
- id: String(row.id),
6632
- recordId: String(row.node),
6633
- name: String(row.name),
6634
- text: String(row.text),
6635
- scope: parseJson$3(row.scopeJson)
6636
- })));
7638
+ results.push(...knowledge.rows.map((row) => {
7639
+ const parentVisible = isKnowledgeScopeVisible(parseJson$3(row.parentScopeJson), scope);
7640
+ return {
7641
+ type: "record",
7642
+ id: String(row.id),
7643
+ recordId: parentVisible ? String(row.node) : String(row.id),
7644
+ name: parentVisible ? String(row.name) : "(private node)",
7645
+ text: String(row.text),
7646
+ scope: parseJson$3(row.scopeJson)
7647
+ };
7648
+ }));
6637
7649
  }
6638
7650
  return results;
6639
7651
  }
6640
- async getCurationCursor(input) {
6641
- const row = (await this.#client.execute({
6642
- sql: `SELECT * FROM "${TABLE_KNOWLEDGE_CURSORS}" WHERE sourceThreadId=? AND agent=?`,
6643
- args: [input.sourceThreadId, input.agent]
6644
- })).rows[0];
6645
- return row ? {
6646
- sourceThreadId: String(row.sourceThreadId),
6647
- agent: String(row.agent),
6648
- lastKnowledgeId: String(row.lastKnowledgeId),
6649
- updatedAt: toDate(row.updatedAt)
6650
- } : null;
6651
- }
6652
- async advanceCurationCursor(input) {
6653
- const updatedAt = /* @__PURE__ */ new Date();
6654
- if ((await this.#db.executeWriteOperationWithRetry(() => withClientWriteLock(this.#client, () => this.#client.execute({
6655
- sql: `INSERT INTO "${TABLE_KNOWLEDGE_CURSORS}" (sourceThreadId,agent,lastKnowledgeId,updatedAt) VALUES (?,?,?,?) ON CONFLICT(sourceThreadId,agent) DO UPDATE SET lastKnowledgeId=excluded.lastKnowledgeId,updatedAt=excluded.updatedAt WHERE excluded.lastKnowledgeId >= "${TABLE_KNOWLEDGE_CURSORS}".lastKnowledgeId`,
6656
- args: [
6657
- input.sourceThreadId,
6658
- input.agent,
6659
- input.lastKnowledgeId,
6660
- updatedAt.toISOString()
6661
- ]
6662
- })), "advance knowledge curation cursor")).rowsAffected === 0) throw new Error("Knowledge curation cursor cannot move backwards");
6663
- return {
6664
- ...input,
6665
- updatedAt
6666
- };
7652
+ /**
7653
+ * @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
7654
+ * cursor. Always throws.
7655
+ */
7656
+ async getCurationCursor(_input) {
7657
+ throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
7658
+ }
7659
+ /**
7660
+ * @deprecated Curation cursors were removed. Observation-time curate is the only Knowledge writer and needs no
7661
+ * cursor. Always throws.
7662
+ */
7663
+ async advanceCurationCursor(_input) {
7664
+ throw new Error(KNOWLEDGE_CURATION_CURSOR_REMOVED_MESSAGE);
6667
7665
  }
6668
7666
  async listActivity(input) {
6669
- const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
7667
+ const keys = visibleScopeKeys(canonicalizeKnowledgeScope(input.scope));
6670
7668
  return (await this.#client.execute({
6671
- sql: `SELECT *,json(scope) AS scopeJson FROM "${TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
7669
+ sql: `SELECT *,json(scope) AS scopeJson FROM "${TABLE_KNOWLEDGE_ACTIVITY}" WHERE ${visibleSql(keys)}${input.after ? " AND id < ?" : ""} ORDER BY id DESC LIMIT ?`,
6672
7670
  args: [
6673
- key,
6674
- key,
7671
+ ...keys,
6675
7672
  ...input.after ? [input.after] : [],
6676
7673
  input.limit ?? 100
6677
7674
  ]
@@ -6693,9 +7690,9 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6693
7690
  args.push(input.status);
6694
7691
  }
6695
7692
  if (input.scope) {
6696
- const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
6697
- clauses.push(visibleSql);
6698
- args.push(key, key);
7693
+ const keys = visibleScopeKeys(input.scope);
7694
+ clauses.push(visibleSql(keys));
7695
+ args.push(...keys);
6699
7696
  }
6700
7697
  args.push(input.limit ?? 100);
6701
7698
  return (await this.#client.execute({
@@ -6714,9 +7711,9 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6714
7711
  ];
6715
7712
  const args = [now.toISOString(), stale.toISOString()];
6716
7713
  if (input.scope) {
6717
- const key = knowledgeScopeKey(canonicalizeKnowledgeScope(input.scope));
6718
- clauses.push(visibleSql);
6719
- args.push(key, key);
7714
+ const keys = visibleScopeKeys(input.scope);
7715
+ clauses.push(visibleSql(keys));
7716
+ args.push(...keys);
6720
7717
  }
6721
7718
  args.push(input.limit ?? 100);
6722
7719
  const ids = (await tx.execute({
@@ -6793,12 +7790,14 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6793
7790
  return result.rows[0] ? parseNode(result.rows[0]) : null;
6794
7791
  }
6795
7792
  async #resolveNode(executor, name, scope) {
6796
- for (let length = scope.length; length > 0; length--) {
6797
- const node = await this.#getNodeByName(executor, name, scope.slice(0, length));
6798
- if (node) {
6799
- const terminal = await this.#resolveTerminalNode(executor, node.id);
6800
- if (terminal && isKnowledgeScopeVisible(terminal.scope, scope)) return terminal;
6801
- }
7793
+ const keys = visibleScopeKeys(scope);
7794
+ const candidates = (await executor.execute({
7795
+ sql: `SELECT *,json(scope) AS scopeJson FROM "${TABLE_KNOWLEDGE_NODES}" WHERE type='node' AND canonicalName=? AND (scopeKey IN (${keys.map(() => "?").join(",")}) OR mergedInto IS NOT NULL)`,
7796
+ args: [canonicalName(name), ...keys]
7797
+ })).rows.map(parseNode).sort((left, right) => right.scope.length - left.scope.length);
7798
+ for (const candidate of candidates) {
7799
+ const terminal = await this.#resolveTerminalNode(executor, candidate.id);
7800
+ if (terminal && isKnowledgeScopeVisible(terminal.scope, scope)) return terminal;
6802
7801
  }
6803
7802
  return null;
6804
7803
  }
@@ -6823,19 +7822,18 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6823
7822
  const scope = canonicalizeKnowledgeScope(input.scope);
6824
7823
  const node = await this.#resolveTerminalNode(this.#client, nodeReferenceId(input.node));
6825
7824
  if (!node) return { records: [] };
6826
- const key = knowledgeScopeKey(scope);
7825
+ const keys = visibleScopeKeys(scope);
6827
7826
  const includesMentions = relationship !== "about";
6828
7827
  const relationSql = relationship === "about" ? "k.node=?" : relationship === "mentioning" ? "m.recordId=?" : "(k.node=? OR m.recordId=?)";
6829
7828
  const args = [
6830
7829
  node.id,
6831
7830
  ...relationship === "related" ? [node.id] : [],
6832
- key,
6833
- key
7831
+ ...keys
6834
7832
  ];
6835
7833
  if (input.after) args.push(input.after);
6836
7834
  args.push((input.limit ?? 100) + 1);
6837
7835
  const result = await this.#client.execute({
6838
- sql: `SELECT DISTINCT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson FROM "${TABLE_KNOWLEDGE_RECORDS}" k${includesMentions ? ` LEFT JOIN "${TABLE_KNOWLEDGE_MENTIONS}" m ON m.sourceType='record' AND m.sourceId=k.id` : ""} WHERE ${relationSql} AND ${visibleSql.replaceAll("scopeKey", "k.scopeKey")}${input.includeDeleted ? "" : " AND k.deletedAt IS NULL"}${input.after ? " AND k.id < ?" : ""} ORDER BY k.id DESC LIMIT ?`,
7836
+ sql: `SELECT DISTINCT k.*,json(k.scope) AS scopeJson,json(k.metadata) AS metadataJson FROM "${TABLE_KNOWLEDGE_RECORDS}" k${includesMentions ? ` LEFT JOIN "${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 ?`,
6839
7837
  args
6840
7838
  });
6841
7839
  const limit = input.limit ?? 100;
@@ -6845,6 +7843,90 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6845
7843
  nextCursor: result.rows.length > limit ? String(rows.at(-1)?.id) : void 0
6846
7844
  };
6847
7845
  }
7846
+ async #resolveScopeNodeIds(executor, addresses) {
7847
+ const scopeNodeIds = /* @__PURE__ */ new Set();
7848
+ for (const address of addresses) {
7849
+ const result = await executor.execute({
7850
+ sql: `SELECT scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" WHERE address=?`,
7851
+ args: [address]
7852
+ });
7853
+ if (result.rows[0]?.scopeNodeId != null) scopeNodeIds.add(String(result.rows[0].scopeNodeId));
7854
+ }
7855
+ return [...scopeNodeIds];
7856
+ }
7857
+ async #replaceNodeScopes(executor, nodeId, addresses, addedAt) {
7858
+ await executor.execute({
7859
+ sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=?`,
7860
+ args: [nodeId]
7861
+ });
7862
+ for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
7863
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
7864
+ args: [
7865
+ nodeId,
7866
+ scopeNodeId,
7867
+ addedAt.toISOString()
7868
+ ]
7869
+ });
7870
+ }
7871
+ /**
7872
+ * Move a node's identity memberships from `from` to `to` without touching structural
7873
+ * placements added by `#placeNodeInScopes`, which a scope change must not erase.
7874
+ */
7875
+ async #swapIdentityScopes(executor, nodeId, from, to, addedAt) {
7876
+ const next = await this.#resolveScopeNodeIds(executor, to);
7877
+ for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, from)) {
7878
+ if (next.includes(scopeNodeId)) continue;
7879
+ await executor.execute({
7880
+ sql: `DELETE FROM "${TABLE_KNOWLEDGE_NODE_SCOPES}" WHERE nodeId=? AND scopeNodeId=?`,
7881
+ args: [nodeId, scopeNodeId]
7882
+ });
7883
+ }
7884
+ for (const scopeNodeId of next) await executor.execute({
7885
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
7886
+ args: [
7887
+ nodeId,
7888
+ scopeNodeId,
7889
+ addedAt.toISOString()
7890
+ ]
7891
+ });
7892
+ }
7893
+ /**
7894
+ * Additive structural placement: every address must resolve to a live scope node
7895
+ * (unlike identity membership, an unknown address is a caller error, not a lazy
7896
+ * materialization miss). Runs inside the caller's transaction, so a throw aborts
7897
+ * the whole mutation.
7898
+ */
7899
+ async #placeNodeInScopes(executor, nodeId, addresses, addedAt) {
7900
+ for (const address of addresses ?? []) {
7901
+ const scopeNodeId = (await executor.execute({
7902
+ sql: `SELECT sa.scopeNodeId AS scopeNodeId FROM "${TABLE_KNOWLEDGE_SCOPE_ADDRESSES}" sa JOIN "${TABLE_KNOWLEDGE_NODES}" n ON n.id=sa.scopeNodeId WHERE sa.address=? AND n.isScope AND n.deletedAt IS NULL`,
7903
+ args: [address]
7904
+ })).rows[0]?.scopeNodeId;
7905
+ if (scopeNodeId == null) throw new KnowledgeNotFoundError("scope", address);
7906
+ await executor.execute({
7907
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_NODE_SCOPES}" (nodeId,scopeNodeId,addedAt) VALUES (?,?,?)`,
7908
+ args: [
7909
+ nodeId,
7910
+ String(scopeNodeId),
7911
+ addedAt.toISOString()
7912
+ ]
7913
+ });
7914
+ }
7915
+ }
7916
+ async #replaceRecordScopes(executor, recordId, addresses, addedAt) {
7917
+ await executor.execute({
7918
+ sql: `DELETE FROM "${TABLE_KNOWLEDGE_RECORD_SCOPES}" WHERE recordId=?`,
7919
+ args: [recordId]
7920
+ });
7921
+ for (const scopeNodeId of await this.#resolveScopeNodeIds(executor, addresses)) await executor.execute({
7922
+ sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_RECORD_SCOPES}" (recordId,scopeNodeId,addedAt) VALUES (?,?,?)`,
7923
+ args: [
7924
+ recordId,
7925
+ scopeNodeId,
7926
+ addedAt.toISOString()
7927
+ ]
7928
+ });
7929
+ }
6848
7930
  async #replaceMentions(tx, sourceType, sourceId, text, resolutionScope, defaultScope) {
6849
7931
  await tx.execute({
6850
7932
  sql: `DELETE FROM "${TABLE_KNOWLEDGE_MENTIONS}" WHERE sourceType=? AND sourceId=?`,
@@ -6868,7 +7950,7 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6868
7950
  updatedAt: now
6869
7951
  };
6870
7952
  await tx.execute({
6871
- sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,jsonb(?),?,?,NULL,?,?)`,
7953
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_NODES}" (id,type,name,canonicalName,kind,content,isScope,scope,scopeKey,version,mergedInto,createdAt,updatedAt) VALUES (?,?,?,?,?,NULL,FALSE,jsonb(?),?,?,NULL,?,?)`,
6872
7954
  args: [
6873
7955
  node.id,
6874
7956
  "node",
@@ -6882,6 +7964,7 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6882
7964
  now.toISOString()
6883
7965
  ]
6884
7966
  });
7967
+ await this.#replaceNodeScopes(tx, node.id, defaultScope, now);
6885
7968
  await this.#activity(tx, "node-created", "node", node.id, defaultScope);
6886
7969
  await this.#outbox(tx, "node", node.id, "upsert", 1, defaultScope);
6887
7970
  }
@@ -6898,13 +7981,17 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
6898
7981
  }
6899
7982
  async #activity(executor, action, recordType, recordId, scope, sourceThreadId) {
6900
7983
  const now = /* @__PURE__ */ new Date();
7984
+ const [contextScopeId] = await this.#resolveScopeNodeIds(executor, scope);
6901
7985
  await executor.execute({
6902
- sql: `INSERT INTO "${TABLE_KNOWLEDGE_ACTIVITY}" (id,action,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,jsonb(?),?,?,?)`,
7986
+ sql: `INSERT INTO "${TABLE_KNOWLEDGE_ACTIVITY}" (id,action,targetType,targetId,contextScopeId,recordType,recordId,scope,scopeKey,sourceThreadId,createdAt) VALUES (?,?,?,?,?,?,?,jsonb(?),?,?,?)`,
6903
7987
  args: [
6904
7988
  createKnowledgeUlid(),
6905
7989
  action,
6906
7990
  recordType,
6907
7991
  recordId,
7992
+ contextScopeId ?? null,
7993
+ recordType,
7994
+ recordId,
6908
7995
  JSON.stringify(scope),
6909
7996
  knowledgeScopeKey(scope),
6910
7997
  sourceThreadId ?? null,
@@ -14682,7 +15769,10 @@ var LibSQLStore = class extends MastraCompositeStore {
14682
15769
  const workflows = new WorkflowsLibSQL(domainConfig);
14683
15770
  const workflowDefinitions = new WorkflowDefinitionsLibSQL(domainConfig);
14684
15771
  const memory = new MemoryLibSQL(domainConfig);
14685
- const knowledge = new KnowledgeLibSQL(domainConfig);
15772
+ const knowledge = new KnowledgeLibSQL({
15773
+ ...domainConfig,
15774
+ storageIsolationKey: config.storageIsolationKey ?? getLibSQLKnowledgeIsolationKey(config, this.client)
15775
+ });
14686
15776
  const observability = new ObservabilityLibSQL(domainConfig);
14687
15777
  const agents = new AgentsLibSQL(domainConfig);
14688
15778
  const channels = new ChannelsLibSQL(domainConfig);
@@ -14746,7 +15836,7 @@ var LibSQLStore = class extends MastraCompositeStore {
14746
15836
  }
14747
15837
  }
14748
15838
  getStoresToInit() {
14749
- return Object.values(this.stores).filter(Boolean);
15839
+ return Object.entries(this.stores).filter(([name]) => name !== "knowledge").map(([, store]) => store).filter(Boolean);
14750
15840
  }
14751
15841
  async initDomainsSequentially() {
14752
15842
  for (const store of this.getStoresToInit()) await store.init();