@mastra/libsql 1.29.0-alpha.0 → 1.29.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.
Files changed (43) hide show
  1. package/dist/_types/{@internal_core → @internal_core@0.1.4}/dist/knowledge-compat/index.d.ts +17 -2
  2. package/dist/index.cjs +258 -42
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.js +258 -42
  5. package/dist/index.js.map +1 -1
  6. package/dist/storage/domains/agents/index.d.ts.map +1 -1
  7. package/dist/storage/domains/knowledge/index.d.ts +8 -1
  8. package/dist/storage/domains/knowledge/index.d.ts.map +1 -1
  9. package/dist/storage/domains/knowledge/pages.d.ts +28 -12
  10. package/dist/storage/domains/knowledge/pages.d.ts.map +1 -1
  11. package/dist/storage/domains/memory/index.d.ts +1 -1
  12. package/package.json +8 -8
  13. package/dist/docs/SKILL.md +0 -54
  14. package/dist/docs/assets/SOURCE_MAP.json +0 -6
  15. package/dist/docs/references/docs-agents-human-in-the-loop.md +0 -773
  16. package/dist/docs/references/docs-agents-networks.md +0 -186
  17. package/dist/docs/references/docs-deployment-workers.md +0 -404
  18. package/dist/docs/references/docs-memory-memory-processors.md +0 -442
  19. package/dist/docs/references/docs-memory-message-history.md +0 -424
  20. package/dist/docs/references/docs-memory-multi-user-threads.md +0 -210
  21. package/dist/docs/references/docs-memory-overview.md +0 -305
  22. package/dist/docs/references/docs-memory-semantic-recall.md +0 -485
  23. package/dist/docs/references/docs-memory-working-memory.md +0 -450
  24. package/dist/docs/references/docs-signals-overview.md +0 -94
  25. package/dist/docs/references/docs-storage.md +0 -224
  26. package/dist/docs/references/docs-studio-editor.md +0 -369
  27. package/dist/docs/references/docs-workflows-snapshots.md +0 -244
  28. package/dist/docs/references/integrations-channels-github.md +0 -156
  29. package/dist/docs/references/integrations-databases-dynamodb.md +0 -286
  30. package/dist/docs/references/integrations-databases-libsql.md +0 -161
  31. package/dist/docs/references/integrations-vectors-libsql.md +0 -325
  32. package/dist/docs/references/reference-ai-sdk-overview.md +0 -144
  33. package/dist/docs/references/reference-core-getMemory.md +0 -53
  34. package/dist/docs/references/reference-core-listMemory.md +0 -59
  35. package/dist/docs/references/reference-core-mastra-class.md +0 -177
  36. package/dist/docs/references/reference-file-based-agents-memory.md +0 -60
  37. package/dist/docs/references/reference-file-based-agents-storage.md +0 -32
  38. package/dist/docs/references/reference-memory-memory-class.md +0 -237
  39. package/dist/docs/references/reference-rag-retrieval.md +0 -672
  40. package/dist/docs/references/reference-storage-composite.md +0 -433
  41. package/dist/docs/references/reference-storage-retention.md +0 -310
  42. /package/dist/_types/{@internal_core → @internal_core@0.1.4}/dist/memory/index.d.ts +0 -0
  43. /package/dist/_types/{@internal_core → @internal_core@0.1.4}/package.json +0 -0
package/dist/index.js CHANGED
@@ -2643,7 +2643,8 @@ var AgentsLibSQL = class extends AgentsStorage {
2643
2643
  "skillsFormat",
2644
2644
  "durable",
2645
2645
  "browser",
2646
- "toolProviders"
2646
+ "toolProviders",
2647
+ "modelList"
2647
2648
  ]
2648
2649
  });
2649
2650
  await this.#migrateToolsToJsonbFormat();
@@ -3060,8 +3061,9 @@ var AgentsLibSQL = class extends AgentsStorage {
3060
3061
  versionNumber: input.versionNumber,
3061
3062
  name: input.name ?? null,
3062
3063
  description: input.description ?? null,
3063
- instructions: this.serializeInstructions(input.instructions),
3064
- model: input.model,
3064
+ instructions: this.serializeInstructions(input.instructions ?? ""),
3065
+ model: input.model ?? {},
3066
+ modelList: input.modelList ?? null,
3065
3067
  tools: input.tools ?? null,
3066
3068
  defaultOptions: input.defaultOptions ?? null,
3067
3069
  workflows: input.workflows ?? null,
@@ -3318,6 +3320,7 @@ var AgentsLibSQL = class extends AgentsStorage {
3318
3320
  description: row.description,
3319
3321
  instructions: this.deserializeInstructions(row.instructions),
3320
3322
  model: this.parseJson(row.model, "model"),
3323
+ modelList: this.parseJson(row.modelList, "modelList"),
3321
3324
  tools: this.parseJson(row.tools, "tools"),
3322
3325
  defaultOptions: this.parseJson(row.defaultOptions, "defaultOptions"),
3323
3326
  workflows: this.parseJson(row.workflows, "workflows"),
@@ -6646,7 +6649,8 @@ const KNOWLEDGE_PAGES_CORE_FUNCTIONS = [
6646
6649
  const KNOWLEDGE_PAGES_CORE_NUMBERS = [
6647
6650
  "MAX_KNOWLEDGE_MOVE_PAGES",
6648
6651
  "MAX_KNOWLEDGE_PAGE_DEPTH",
6649
- "MAX_KNOWLEDGE_PAGE_LIST_LIMIT"
6652
+ "MAX_KNOWLEDGE_PAGE_LIST_LIMIT",
6653
+ "MAX_KNOWLEDGE_PAGE_LINKS"
6650
6654
  ];
6651
6655
  function resolveKnowledgePagesCore(storageModule) {
6652
6656
  const module = typeof storageModule === "object" && storageModule !== null ? storageModule : {};
@@ -6667,13 +6671,25 @@ function createKnowledgePagesCoreLoader(features, loadStorageModule) {
6667
6671
  };
6668
6672
  }
6669
6673
  /**
6670
- * Keyword-search words: the query split on whitespace, lowercased, deduplicated. Every word must
6671
- * appear (as a case-insensitive substring) in a hit; an empty list means the search returns nothing.
6674
+ * Keyword-search words: the query split on whitespace, lowercased, deduplicated, keeping the first
6675
+ * {@link KNOWLEDGE_SEARCH_MAX_WORDS}. A hit needs at least one of them (as a case-insensitive
6676
+ * substring); an empty list means the search returns nothing.
6672
6677
  */
6673
6678
  function knowledgeSearchWords(query) {
6674
- return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))];
6679
+ return [...new Set(query.toLowerCase().split(/\s+/).filter(Boolean))].slice(0, 16);
6675
6680
  }
6676
6681
  /**
6682
+ * Points one matched search word earns on a page, by the best field it appears in: name, then
6683
+ * description, then body. A page whose name contains the whole query ranks first; below that, pages
6684
+ * rank by how many query words they match, and these points only break ties between pages that
6685
+ * match the same number of words.
6686
+ */
6687
+ const KNOWLEDGE_SEARCH_WEIGHTS = {
6688
+ name: 3,
6689
+ description: 2,
6690
+ body: 1
6691
+ };
6692
+ /**
6677
6693
  * Why `key` can't be a Knowledge page key, or undefined when it can. Keys are one path segment:
6678
6694
  * non-empty, with no `/` (path separator), `#` (scope/path separator) or `%` (escape character).
6679
6695
  */
@@ -6692,9 +6708,37 @@ const KNOWLEDGE_PAGE_TABLES = {
6692
6708
  mentions: "mastra_knowledge_mentions",
6693
6709
  pins: "mastra_knowledge_pins",
6694
6710
  pinAudiences: "mastra_knowledge_pin_audiences",
6695
- activity: "mastra_knowledge_activity"
6711
+ activity: "mastra_knowledge_activity",
6712
+ links: "mastra_knowledge_page_links"
6696
6713
  };
6697
6714
  const T = KNOWLEDGE_PAGE_TABLES;
6715
+ function pinsTableDDL(name) {
6716
+ return `CREATE TABLE IF NOT EXISTS "${name}" (
6717
+ id TEXT PRIMARY KEY,
6718
+ pageId TEXT NOT NULL,
6719
+ kind TEXT NOT NULL DEFAULT 'page',
6720
+ depth INTEGER,
6721
+ audience TEXT NOT NULL,
6722
+ createdBy TEXT NOT NULL,
6723
+ createdAt TEXT NOT NULL,
6724
+ updatedAt TEXT NOT NULL,
6725
+ UNIQUE (pageId, kind)
6726
+ )`;
6727
+ }
6728
+ /**
6729
+ * Pins tables from before pin kinds kept one pin per page (`pageId UNIQUE`). SQLite can't drop that
6730
+ * constraint in place, so the table is rebuilt with every existing pin as a `page` pin. Run after
6731
+ * {@link KNOWLEDGE_PAGE_DDL}, in the same transaction; a no-op once `kind` exists.
6732
+ */
6733
+ async function upgradeKnowledgePinKinds(tx) {
6734
+ if ((await tx.execute(`SELECT name FROM pragma_table_info('${T.pins}')`)).rows.some((row) => String(row.name) === "kind")) return;
6735
+ const next = `${T.pins}_kinds`;
6736
+ await tx.execute(`DROP TABLE IF EXISTS "${next}"`);
6737
+ await tx.execute(pinsTableDDL(next));
6738
+ await tx.execute(`INSERT INTO "${next}" (id, pageId, kind, audience, createdBy, createdAt, updatedAt) SELECT id, pageId, 'page', audience, createdBy, createdAt, updatedAt FROM "${T.pins}"`);
6739
+ await tx.execute(`DROP TABLE "${T.pins}"`);
6740
+ await tx.execute(`ALTER TABLE "${next}" RENAME TO "${T.pins}"`);
6741
+ }
6698
6742
  /** DDL for the schema-4 page tables, in creation order. Every statement is idempotent. */
6699
6743
  const KNOWLEDGE_PAGE_DDL = [
6700
6744
  `CREATE TABLE IF NOT EXISTS "mastra_knowledge_access_state" (
@@ -6758,14 +6802,7 @@ const KNOWLEDGE_PAGE_DDL = [
6758
6802
  PRIMARY KEY (noteId, pageId)
6759
6803
  )`,
6760
6804
  `CREATE INDEX IF NOT EXISTS idx_kmentions_page ON "${T.mentions}" (pageId)`,
6761
- `CREATE TABLE IF NOT EXISTS "${T.pins}" (
6762
- id TEXT PRIMARY KEY,
6763
- pageId TEXT NOT NULL UNIQUE,
6764
- audience TEXT NOT NULL,
6765
- createdBy TEXT NOT NULL,
6766
- createdAt TEXT NOT NULL,
6767
- updatedAt TEXT NOT NULL
6768
- )`,
6805
+ pinsTableDDL(T.pins),
6769
6806
  `CREATE TABLE IF NOT EXISTS "${T.pinAudiences}" (
6770
6807
  pinId TEXT NOT NULL,
6771
6808
  audience TEXT NOT NULL,
@@ -6782,7 +6819,16 @@ const KNOWLEDGE_PAGE_DDL = [
6782
6819
  provenance TEXT NOT NULL,
6783
6820
  createdAt TEXT NOT NULL
6784
6821
  )`,
6785
- `CREATE INDEX IF NOT EXISTS idx_kactivity_page ON "${T.activity}" (pageId, id DESC)`
6822
+ `CREATE INDEX IF NOT EXISTS idx_kactivity_page ON "${T.activity}" (pageId, id DESC)`,
6823
+ `CREATE TABLE IF NOT EXISTS "${T.links}" (
6824
+ fromPageId TEXT NOT NULL,
6825
+ ref TEXT NOT NULL,
6826
+ toPageId TEXT,
6827
+ count INTEGER NOT NULL,
6828
+ PRIMARY KEY (fromPageId, ref)
6829
+ ) WITHOUT ROWID`,
6830
+ `CREATE INDEX IF NOT EXISTS idx_klinks_to ON "${T.links}" (toPageId) WHERE toPageId IS NOT NULL`,
6831
+ `CREATE INDEX IF NOT EXISTS idx_klinks_ref ON "${T.links}" (ref) WHERE toPageId IS NULL`
6786
6832
  ];
6787
6833
  function str(value) {
6788
6834
  return value === null || value === void 0 ? void 0 : String(value);
@@ -6837,6 +6883,8 @@ function toPin(row) {
6837
6883
  return {
6838
6884
  id: String(row.id),
6839
6885
  pageId: String(row.pageId),
6886
+ kind: String(row.kind),
6887
+ ...row.depth !== null && row.depth !== void 0 ? { depth: Number(row.depth) } : {},
6840
6888
  audience: JSON.parse(String(row.audience)),
6841
6889
  createdBy: String(row.createdBy),
6842
6890
  createdAt: new Date(String(row.createdAt)),
@@ -6965,10 +7013,11 @@ var LibSQLKnowledgePages = class {
6965
7013
  this.#read = config.read;
6966
7014
  this.#transaction = config.transaction;
6967
7015
  }
6968
- /** Deletes every row from the page tables (scopes, pages, notes, pins, activity). */
7016
+ /** Deletes every row from the page tables (scopes, pages, notes, pins, activity, links). */
6969
7017
  async clearAll() {
6970
7018
  await this.#transaction(async (tx) => {
6971
7019
  for (const table of [
7020
+ T.links,
6972
7021
  T.activity,
6973
7022
  T.pinAudiences,
6974
7023
  T.pins,
@@ -7150,13 +7199,19 @@ var LibSQLKnowledgePages = class {
7150
7199
  const limit = limitOf(input.limit, core.MAX_KNOWLEDGE_PAGE_LIST_LIMIT);
7151
7200
  const cursor = decodeCursor(input.cursor, 2);
7152
7201
  const args = [input.parentId];
7202
+ let filter = "";
7203
+ if (input.read) {
7204
+ const readable = readableScopes(input.read);
7205
+ filter = ` AND id IN (${readable.sql})`;
7206
+ args.push(...readable.args);
7207
+ }
7153
7208
  let after = "";
7154
7209
  if (cursor) {
7155
7210
  after = ` AND (position > ? OR (position = ? AND id > ?))`;
7156
7211
  args.push(cursor[0], cursor[0], cursor[1]);
7157
7212
  }
7158
7213
  args.push(limit + 1);
7159
- const rows = await this.#query(this.#read, `SELECT * FROM "${T.pages}" WHERE isScope = 1 AND parentId IS ?${after} ORDER BY position, id LIMIT ?`, args);
7214
+ const rows = await this.#query(this.#read, `SELECT * FROM "${T.pages}" WHERE isScope = 1 AND parentId IS ?${filter}${after} ORDER BY position, id LIMIT ?`, args);
7160
7215
  return this.#pageOutput(rows, limit, (row) => [String(row.position), String(row.id)]);
7161
7216
  }
7162
7217
  #pageOutput(rows, limit, cursorOf) {
@@ -7441,9 +7496,8 @@ var LibSQLKnowledgePages = class {
7441
7496
  args: [input.sourceId]
7442
7497
  });
7443
7498
  const pins = await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId IN (?, ?)`, [input.sourceId, input.targetId]);
7444
- const sourcePin = pins.find((pin) => String(pin.pageId) === input.sourceId);
7445
- if (sourcePin) {
7446
- const targetPin = pins.find((pin) => String(pin.pageId) === input.targetId);
7499
+ for (const sourcePin of pins.filter((pin) => String(pin.pageId) === input.sourceId)) {
7500
+ const targetPin = pins.find((pin) => String(pin.pageId) === input.targetId && pin.kind === sourcePin.kind);
7447
7501
  const audience = JSON.parse(String(sourcePin.audience));
7448
7502
  if (targetPin) {
7449
7503
  const merged = [.../* @__PURE__ */ new Set([...JSON.parse(String(targetPin.audience)), ...audience])];
@@ -7543,6 +7597,129 @@ var LibSQLKnowledgePages = class {
7543
7597
  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);
7544
7598
  return this.#noteOutput(rows, limit);
7545
7599
  }
7600
+ async countChildScopes(input) {
7601
+ const ids = [...new Set(input.parentIds)];
7602
+ const counts = Object.fromEntries(ids.map((id) => [id, 0]));
7603
+ if (ids.length === 0) return counts;
7604
+ const readable = readableScopes(input.read);
7605
+ const rows = await this.#query(this.#read, `SELECT parentId, COUNT(*) AS total FROM "${T.pages}" WHERE parentId IN (SELECT value FROM json_each(?)) AND isScope = 1 AND id IN (${readable.sql}) GROUP BY parentId`, [jsonList(ids), ...readable.args]);
7606
+ for (const row of rows) counts[String(row.parentId)] = Number(row.total);
7607
+ return counts;
7608
+ }
7609
+ async countChildPages(input) {
7610
+ const ids = [...new Set(input.parentIds)];
7611
+ const counts = Object.fromEntries(ids.map((id) => [id, 0]));
7612
+ if (ids.length === 0) return counts;
7613
+ const readable = pageReadable("p", input.read);
7614
+ 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]);
7615
+ for (const row of rows) counts[String(row.parentId)] = Number(row.total);
7616
+ return counts;
7617
+ }
7618
+ async replacePageBodyLinks(input) {
7619
+ await this.#transaction(async (tx) => {
7620
+ await tx.execute({
7621
+ sql: `DELETE FROM "${T.links}" WHERE fromPageId = ?`,
7622
+ args: [input.pageId]
7623
+ });
7624
+ if (input.links.length === 0) return;
7625
+ await tx.execute({
7626
+ sql: `INSERT INTO "${T.links}" (fromPageId, ref, toPageId, count) SELECT ?, json_extract(value, '$.ref'), json_extract(value, '$.toPageId'), json_extract(value, '$.count') FROM json_each(?)`,
7627
+ args: [input.pageId, JSON.stringify(input.links.map(({ ref, toPageId, count }) => ({
7628
+ ref,
7629
+ toPageId,
7630
+ count
7631
+ })))]
7632
+ });
7633
+ });
7634
+ }
7635
+ async listPageBodyLinkSources(input) {
7636
+ const refs = input.refs ?? [];
7637
+ const targets = input.toPageIds ?? [];
7638
+ if (refs.length === 0 && targets.length === 0) return [];
7639
+ return (await this.#query(this.#read, `SELECT DISTINCT fromPageId FROM "${T.links}" WHERE (toPageId IS NULL AND ref IN (SELECT value FROM json_each(?))) OR toPageId IN (SELECT value FROM json_each(?)) ORDER BY fromPageId`, [jsonList(refs), jsonList(targets)])).map((row) => String(row.fromPageId));
7640
+ }
7641
+ /**
7642
+ * Visible links touching `ids` (from them for `out`, to them for `in`): resolved, both ends live
7643
+ * and readable, not self-links; a note link also needs a visible live note. One row per
7644
+ * (from, to, kind), counts summed.
7645
+ */
7646
+ async #links(read, kinds, ids, direction) {
7647
+ const wanted = new Set(kinds ?? ["body", "note"]);
7648
+ const touches = (from, to) => {
7649
+ const parts = [];
7650
+ const args = [];
7651
+ if (direction !== "in") {
7652
+ parts.push(`${from} IN (SELECT value FROM json_each(?))`);
7653
+ args.push(jsonList(ids));
7654
+ }
7655
+ if (direction !== "out") {
7656
+ parts.push(`${to} IN (SELECT value FROM json_each(?))`);
7657
+ args.push(jsonList(ids));
7658
+ }
7659
+ return {
7660
+ sql: `(${parts.join(" OR ")})`,
7661
+ args
7662
+ };
7663
+ };
7664
+ const sources = [];
7665
+ const args = [];
7666
+ if (wanted.has("body")) {
7667
+ const near = touches("l.fromPageId", "l.toPageId");
7668
+ sources.push(`SELECT l.fromPageId AS fromPageId, l.toPageId AS toPageId, 'body' AS kind, l.count AS count FROM "${T.links}" l WHERE l.toPageId IS NOT NULL AND ${near.sql}`);
7669
+ args.push(...near.args);
7670
+ }
7671
+ if (wanted.has("note")) {
7672
+ const near = touches("n.pageId", "m.pageId");
7673
+ const visible = noteReadable("n", read);
7674
+ sources.push(`SELECT n.pageId AS fromPageId, m.pageId AS toPageId, 'note' AS kind, 1 AS count FROM "${T.mentions}" m JOIN "${T.notes}" n ON n.id = m.noteId WHERE n.deletedAt IS NULL AND ${near.sql} AND ${visible.sql}`);
7675
+ args.push(...near.args, ...visible.args);
7676
+ }
7677
+ if (sources.length === 0) return [];
7678
+ const fromReadable = pageReadable("fp", read);
7679
+ const toReadable = pageReadable("tp", read);
7680
+ return this.#query(this.#read, `SELECT e.fromPageId AS fromPageId, e.toPageId AS toPageId, e.kind AS kind, SUM(e.count) AS count
7681
+ FROM (${sources.join(" UNION ALL ")}) e
7682
+ JOIN "${T.pages}" fp ON fp.id = e.fromPageId AND fp.deletedAt IS NULL AND ${fromReadable.sql}
7683
+ JOIN "${T.pages}" tp ON tp.id = e.toPageId AND tp.deletedAt IS NULL AND ${toReadable.sql}
7684
+ WHERE e.fromPageId <> e.toPageId
7685
+ GROUP BY e.fromPageId, e.toPageId, e.kind
7686
+ ORDER BY e.fromPageId, e.toPageId, CASE e.kind WHEN 'body' THEN 0 ELSE 1 END`, [
7687
+ ...args,
7688
+ ...fromReadable.args,
7689
+ ...toReadable.args
7690
+ ]);
7691
+ }
7692
+ async countPageLinks(input) {
7693
+ const ids = [...new Set(input.pageIds)];
7694
+ const linked = new Map(ids.map((id) => [id, {
7695
+ in: /* @__PURE__ */ new Set(),
7696
+ out: /* @__PURE__ */ new Set()
7697
+ }]));
7698
+ if (ids.length === 0) return {};
7699
+ for (const row of await this.#links(input.read, input.kinds, ids, "both")) {
7700
+ const from = String(row.fromPageId);
7701
+ const to = String(row.toPageId);
7702
+ linked.get(from)?.out.add(to);
7703
+ linked.get(to)?.in.add(from);
7704
+ }
7705
+ return Object.fromEntries([...linked].map(([id, sets]) => [id, {
7706
+ in: sets.in.size,
7707
+ out: sets.out.size
7708
+ }]));
7709
+ }
7710
+ async listPageLinks(input) {
7711
+ const limit = input.limit ?? 1e3;
7712
+ if (!Number.isInteger(limit) || limit < 1) throw new RangeError("Knowledge link limit must be a positive integer");
7713
+ const ids = [...new Set(input.pageIds)];
7714
+ if (ids.length === 0) return [];
7715
+ const core = await loadPagesCore();
7716
+ return (await this.#links(input.read, input.kinds, ids, input.direction ?? "both")).slice(0, Math.min(limit, core.MAX_KNOWLEDGE_PAGE_LINKS)).map((row) => ({
7717
+ fromPageId: String(row.fromPageId),
7718
+ toPageId: String(row.toPageId),
7719
+ kind: row.kind === "note" ? "note" : "body",
7720
+ count: Number(row.count)
7721
+ }));
7722
+ }
7546
7723
  async countNotes(input) {
7547
7724
  const ids = [...new Set(input.pageIds)];
7548
7725
  const counts = Object.fromEntries(ids.map((id) => [id, 0]));
@@ -7714,18 +7891,22 @@ var LibSQLKnowledgePages = class {
7714
7891
  const readable = pageReadable("p", input.read);
7715
7892
  const scopeFilter = input.scopeIds ? ` AND p.scopeId IN (SELECT value FROM json_each(?))` : "";
7716
7893
  const scopeArgs = input.scopeIds ? [jsonList(input.scopeIds)] : [];
7717
- const pageMatch = words.map(() => `(lower(p.name) LIKE ? ESCAPE '=' OR lower(COALESCE(p.description, '')) LIKE ? ESCAPE '=' OR lower(COALESCE(p.body, '')) LIKE ? ESCAPE '=')`).join(" AND ");
7718
- const nameMatch = words.map(() => `lower(p.name) LIKE ? ESCAPE '='`).join(" AND ");
7719
- 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 WHEN ${nameMatch} THEN 1 ELSE 2 END, p.updatedAt DESC, p.id ASC LIMIT ?`, [
7894
+ const field = (column) => `lower(COALESCE(${column}, '')) LIKE ? ESCAPE '='`;
7895
+ const pageMatch = words.map(() => `(${field("p.name")} OR ${field("p.description")} OR ${field("p.body")})`).join(" OR ");
7896
+ 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(" + ");
7897
+ const pageCount = words.map(() => `(CASE WHEN ${field("p.name")} OR ${field("p.description")} OR ${field("p.body")} THEN 1 ELSE 0 END)`).join(" + ");
7898
+ const triple = likes.flatMap((like) => [
7899
+ like,
7900
+ like,
7901
+ like
7902
+ ]);
7903
+ 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 ?`, [
7720
7904
  ...readable.args,
7721
7905
  ...scopeArgs,
7722
- ...likes.flatMap((like) => [
7723
- like,
7724
- like,
7725
- like
7726
- ]),
7906
+ ...triple,
7727
7907
  phrase,
7728
- ...likes,
7908
+ ...triple,
7909
+ ...triple,
7729
7910
  limit
7730
7911
  ])).map((row) => ({
7731
7912
  type: "page",
@@ -7733,11 +7914,12 @@ var LibSQLKnowledgePages = class {
7733
7914
  }));
7734
7915
  if (hits.length >= limit) return hits;
7735
7916
  const noteVisible = noteReadable("n", input.read);
7736
- const noteMatch = words.map(() => `lower(n.text) LIKE ? ESCAPE '='`).join(" AND ");
7737
- 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 ${noteMatch} AND ${noteVisible.sql} ORDER BY n.updatedAt DESC, n.id DESC LIMIT ?`, [
7917
+ const noteHas = words.map(() => `lower(n.text) LIKE ? ESCAPE '='`);
7918
+ 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 ?`, [
7738
7919
  ...scopeArgs,
7739
7920
  ...likes,
7740
7921
  ...noteVisible.args,
7922
+ ...likes,
7741
7923
  limit - hits.length
7742
7924
  ]);
7743
7925
  const notes = await this.#hydrateNotes(this.#read, noteRows);
@@ -7799,17 +7981,21 @@ var LibSQLKnowledgePages = class {
7799
7981
  });
7800
7982
  }
7801
7983
  addPinAudience(input) {
7984
+ const kind = input.kind ?? "page";
7985
+ const depth = kind === "list" ? input.depth ?? 1 : null;
7802
7986
  return this.#transaction(async (tx) => {
7803
7987
  const page = await this.#livePage(tx, input.pageId);
7804
7988
  const now = (/* @__PURE__ */ new Date()).toISOString();
7805
- const existing = (await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId = ?`, [input.pageId]))[0];
7989
+ const existing = (await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId = ? AND kind = ?`, [input.pageId, kind]))[0];
7806
7990
  if (!existing) {
7807
7991
  const id = createKnowledgeUlid();
7808
7992
  await tx.execute({
7809
- sql: `INSERT INTO "${T.pins}" (id, pageId, audience, createdBy, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)`,
7993
+ sql: `INSERT INTO "${T.pins}" (id, pageId, kind, depth, audience, createdBy, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
7810
7994
  args: [
7811
7995
  id,
7812
7996
  input.pageId,
7997
+ kind,
7998
+ depth,
7813
7999
  JSON.stringify([...new Set(input.audience)]),
7814
8000
  input.createdBy,
7815
8001
  now,
@@ -7826,11 +8012,13 @@ var LibSQLKnowledgePages = class {
7826
8012
  }
7827
8013
  const current = JSON.parse(String(existing.audience));
7828
8014
  const merged = [.../* @__PURE__ */ new Set([...current, ...input.audience])];
7829
- if (merged.length !== current.length) {
8015
+ const depthChanged = (existing.depth === null ? null : Number(existing.depth)) !== depth;
8016
+ if (merged.length !== current.length || depthChanged) {
7830
8017
  await tx.execute({
7831
- sql: `UPDATE "${T.pins}" SET audience = ?, updatedAt = ? WHERE id = ?`,
8018
+ sql: `UPDATE "${T.pins}" SET audience = ?, depth = ?, updatedAt = ? WHERE id = ?`,
7832
8019
  args: [
7833
8020
  JSON.stringify(merged),
8021
+ depth,
7834
8022
  now,
7835
8023
  String(existing.id)
7836
8024
  ]
@@ -7847,7 +8035,7 @@ var LibSQLKnowledgePages = class {
7847
8035
  }
7848
8036
  removePinAudience(input) {
7849
8037
  return this.#transaction(async (tx) => {
7850
- const existing = (await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId = ?`, [input.pageId]))[0];
8038
+ const existing = (await this.#query(tx, `SELECT * FROM "${T.pins}" WHERE pageId = ? AND kind = ?`, [input.pageId, input.kind ?? "page"]))[0];
7851
8039
  if (!existing) return null;
7852
8040
  const current = JSON.parse(String(existing.audience));
7853
8041
  const remove = new Set(input.audience);
@@ -7891,15 +8079,19 @@ var LibSQLKnowledgePages = class {
7891
8079
  filters += ` AND pin.id IN (SELECT pa.pinId FROM json_each(?) a JOIN "${T.pinAudiences}" pa ON pa.audience = a.value)`;
7892
8080
  args.push(jsonList(input.audience));
7893
8081
  }
8082
+ if (input.kind) {
8083
+ filters += ` AND pin.kind = ?`;
8084
+ args.push(input.kind);
8085
+ }
7894
8086
  if (input.pageIds) {
7895
8087
  filters += ` AND pin.pageId IN (SELECT value FROM json_each(?))`;
7896
8088
  args.push(jsonList(input.pageIds));
7897
8089
  }
7898
8090
  if (input.excludePageIds?.length) {
7899
- filters += ` AND pin.pageId NOT IN (SELECT value FROM json_each(?))`;
8091
+ filters += ` AND (pin.kind <> 'page' OR pin.pageId NOT IN (SELECT value FROM json_each(?)))`;
7900
8092
  args.push(jsonList(input.excludePageIds));
7901
8093
  }
7902
- if (input.withBody) filters += ` AND p.body IS NOT NULL AND trim(p.body, char(32, 9, 10, 11, 12, 13)) <> ''`;
8094
+ if (input.withBody) filters += ` AND (pin.kind <> 'page' OR (p.body IS NOT NULL AND trim(p.body, char(32, 9, 10, 11, 12, 13)) <> ''))`;
7903
8095
  const from = `FROM "${T.pins}" pin JOIN "${T.pages}" p ON p.id = pin.pageId WHERE p.deletedAt IS NULL AND ${readable.sql}${filters}`;
7904
8096
  let total;
7905
8097
  if (input.count) total = Number((await this.#query(this.#read, `SELECT COUNT(*) AS n ${from}`, args))[0].n);
@@ -7971,7 +8163,8 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
7971
8163
  contractVersion: 4,
7972
8164
  schemaVersion: 4,
7973
8165
  supportsSchemaInspection: true,
7974
- supportsExplicitReset: true
8166
+ supportsExplicitReset: true,
8167
+ supportsPinKinds: true
7975
8168
  };
7976
8169
  }
7977
8170
  async dangerouslyClearAll() {
@@ -8001,6 +8194,7 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
8001
8194
  const inspection = await this.#inspectPages(tx);
8002
8195
  if (inspection.status === "incompatible-reset-required") throw new KnowledgeSchemaResetRequiredError(inspection);
8003
8196
  for (const statement of KNOWLEDGE_PAGE_DDL) await tx.execute(statement);
8197
+ await upgradeKnowledgePinKinds(tx);
8004
8198
  await tx.execute({
8005
8199
  sql: `INSERT OR IGNORE INTO "${TABLE_KNOWLEDGE_PAGES_STATE}" (id, schemaVersion) VALUES ('knowledge', ?)`,
8006
8200
  args: [4]
@@ -8009,7 +8203,11 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
8009
8203
  }
8010
8204
  async dangerouslyReset() {
8011
8205
  await withClientWriteLock(this.#client, async () => {
8012
- await this.#client.batch([...KNOWLEDGE_RESETTABLE_TABLE_NAMES, KNOWLEDGE_PAGE_TABLES.pinAudiences].map((table) => ({
8206
+ await this.#client.batch([
8207
+ ...KNOWLEDGE_RESETTABLE_TABLE_NAMES,
8208
+ KNOWLEDGE_PAGE_TABLES.pinAudiences,
8209
+ KNOWLEDGE_PAGE_TABLES.links
8210
+ ].map((table) => ({
8013
8211
  sql: `DROP TABLE IF EXISTS "${table}"`,
8014
8212
  args: []
8015
8213
  })), "write");
@@ -8031,6 +8229,9 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
8031
8229
  listChildScopes(input) {
8032
8230
  return this.#pages.listChildScopes(input);
8033
8231
  }
8232
+ countChildScopes(input) {
8233
+ return this.#pages.countChildScopes(input);
8234
+ }
8034
8235
  getPage(input) {
8035
8236
  return this.#pages.getPage(input);
8036
8237
  }
@@ -8067,6 +8268,21 @@ var KnowledgeLibSQL = class extends KnowledgeStorage {
8067
8268
  listNotes(input) {
8068
8269
  return this.#pages.listNotes(input);
8069
8270
  }
8271
+ countChildPages(input) {
8272
+ return this.#pages.countChildPages(input);
8273
+ }
8274
+ replacePageBodyLinks(input) {
8275
+ return this.#pages.replacePageBodyLinks(input);
8276
+ }
8277
+ listPageBodyLinkSources(input) {
8278
+ return this.#pages.listPageBodyLinkSources(input);
8279
+ }
8280
+ countPageLinks(input) {
8281
+ return this.#pages.countPageLinks(input);
8282
+ }
8283
+ listPageLinks(input) {
8284
+ return this.#pages.listPageLinks(input);
8285
+ }
8070
8286
  countNotes(input) {
8071
8287
  return this.#pages.countNotes(input);
8072
8288
  }