@fortemi/core 2026.7.0 → 2026.7.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
@@ -596,6 +596,19 @@ var migration0009 = {
596
596
  `
597
597
  };
598
598
 
599
+ // src/migrations/0010_attachment_text_metadata.ts
600
+ var migration0010 = {
601
+ version: 10,
602
+ name: "0010_attachment_text_metadata",
603
+ sql: `
604
+ ALTER TABLE attachment
605
+ ADD COLUMN IF NOT EXISTS mime_type TEXT;
606
+
607
+ ALTER TABLE attachment
608
+ ADD COLUMN IF NOT EXISTS extracted_text TEXT;
609
+ `
610
+ };
611
+
599
612
  // src/migrations/index.ts
600
613
  var allMigrations = [
601
614
  migration0001,
@@ -606,7 +619,8 @@ var allMigrations = [
606
619
  migration0006,
607
620
  migration0007,
608
621
  migration0008,
609
- migration0009
622
+ migration0009,
623
+ migration0010
610
624
  ];
611
625
 
612
626
  // src/data-archive.ts
@@ -1119,6 +1133,16 @@ function buildNoteConditions(options, startIdx, includeDeleted = false) {
1119
1133
  }
1120
1134
 
1121
1135
  // src/repositories/embedding-sets-repository.ts
1136
+ var ATTACHMENT_TEXT_JOIN = `
1137
+ LEFT JOIN (
1138
+ SELECT note_id,
1139
+ string_agg(extracted_text, ' ' ORDER BY position, created_at)
1140
+ FILTER (WHERE extracted_text IS NOT NULL AND extracted_text <> '') as extracted_text
1141
+ FROM attachment
1142
+ WHERE deleted_at IS NULL
1143
+ GROUP BY note_id
1144
+ ) ax ON ax.note_id = n.id`;
1145
+ var COMBINED_TEXT_VECTOR_SQL = `to_tsvector('english', (coalesce(c.content, '') || ' ' || coalesce(ax.extracted_text, '')))`;
1122
1146
  var DEFAULT_COMPATIBILITY = {
1123
1147
  model: "require-same",
1124
1148
  dimension: "require-same",
@@ -1498,7 +1522,7 @@ var EmbeddingSetsRepository = class {
1498
1522
  params.push(criteria.updatedBefore);
1499
1523
  }
1500
1524
  if (criteria.query?.trim()) {
1501
- conditions.push(`(n.tsv @@ plainto_tsquery('english', $${idx}) OR to_tsvector('english', coalesce(c.content, '')) @@ plainto_tsquery('english', $${idx}))`);
1525
+ conditions.push(`(n.tsv @@ plainto_tsquery('english', $${idx}) OR ${COMBINED_TEXT_VECTOR_SQL} @@ plainto_tsquery('english', $${idx}))`);
1502
1526
  params.push(criteria.query);
1503
1527
  }
1504
1528
  const result = await this.db.query(
@@ -1506,6 +1530,7 @@ var EmbeddingSetsRepository = class {
1506
1530
  FROM embedding e
1507
1531
  JOIN note n ON n.id = e.note_id
1508
1532
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1533
+ ${ATTACHMENT_TEXT_JOIN}
1509
1534
  WHERE ${conditions.join(" AND ")}
1510
1535
  ORDER BY e.note_id, e.created_at DESC`,
1511
1536
  params
@@ -1623,6 +1648,17 @@ var EmbeddingSetsRepository = class {
1623
1648
  };
1624
1649
 
1625
1650
  // src/repositories/search-repository.ts
1651
+ var ATTACHMENT_TEXT_JOIN2 = `
1652
+ LEFT JOIN (
1653
+ SELECT note_id,
1654
+ string_agg(extracted_text, ' ' ORDER BY position, created_at)
1655
+ FILTER (WHERE extracted_text IS NOT NULL AND extracted_text <> '') as extracted_text
1656
+ FROM attachment
1657
+ WHERE deleted_at IS NULL
1658
+ GROUP BY note_id
1659
+ ) ax ON ax.note_id = n.id`;
1660
+ var COMBINED_TEXT_SQL = `(coalesce(c.content, '') || ' ' || coalesce(ax.extracted_text, ''))`;
1661
+ var COMBINED_TEXT_VECTOR_SQL2 = `to_tsvector('english', ${COMBINED_TEXT_SQL})`;
1626
1662
  var SearchRepository = class {
1627
1663
  constructor(db, semanticAvailable = false) {
1628
1664
  this.db = db;
@@ -1707,7 +1743,7 @@ var SearchRepository = class {
1707
1743
  const { conditions, params, nextIdx } = buildNoteConditions(options, 2);
1708
1744
  conditions.unshift(
1709
1745
  `(n.tsv @@ ${tsqFn}('english', $1) OR
1710
- to_tsvector('english', coalesce(c.content, '')) @@ ${tsqFn}('english', $1))`
1746
+ ${COMBINED_TEXT_VECTOR_SQL2} @@ ${tsqFn}('english', $1))`
1711
1747
  );
1712
1748
  let paramIdx = this.scopeToResolvedEmbeddingSet(conditions, params, nextIdx, resolvedEmbeddingSet);
1713
1749
  const allParams = [query, ...params];
@@ -1716,6 +1752,7 @@ var SearchRepository = class {
1716
1752
  `SELECT COUNT(*) as count
1717
1753
  FROM note n
1718
1754
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1755
+ ${ATTACHMENT_TEXT_JOIN2}
1719
1756
  WHERE ${where}`,
1720
1757
  allParams
1721
1758
  );
@@ -1724,17 +1761,18 @@ var SearchRepository = class {
1724
1761
  const result = await this.db.query(
1725
1762
  `SELECT n.id, n.title, n.created_at, n.updated_at,
1726
1763
  ts_rank(
1727
- setweight(n.tsv, 'A') || setweight(to_tsvector('english', coalesce(c.content, '')), 'B'),
1764
+ setweight(n.tsv, 'A') || setweight(${COMBINED_TEXT_VECTOR_SQL2}, 'B'),
1728
1765
  ${tsqFn}('english', $1)
1729
1766
  ) as rank,
1730
1767
  ts_headline(
1731
1768
  'english',
1732
- coalesce(c.content, ''),
1769
+ ${COMBINED_TEXT_SQL},
1733
1770
  ${tsqFn}('english', $1),
1734
1771
  'StartSel=<mark>, StopSel=</mark>, MaxWords=35, MinWords=15'
1735
1772
  ) as snippet
1736
1773
  FROM note n
1737
1774
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1775
+ ${ATTACHMENT_TEXT_JOIN2}
1738
1776
  WHERE ${where}
1739
1777
  ORDER BY rank DESC, n.created_at DESC
1740
1778
  LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
@@ -1750,6 +1788,7 @@ var SearchRepository = class {
1750
1788
  const idsResult = await this.db.query(
1751
1789
  `SELECT n.id FROM note n
1752
1790
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1791
+ ${ATTACHMENT_TEXT_JOIN2}
1753
1792
  WHERE ${where}`,
1754
1793
  allParams
1755
1794
  );
@@ -1796,10 +1835,11 @@ var SearchRepository = class {
1796
1835
  const result = await this.db.query(
1797
1836
  `SELECT n.id, n.title, n.created_at, n.updated_at,
1798
1837
  (e.vector <=> $${vecIdx}::vector) as distance,
1799
- LEFT(coalesce(c.content, ''), 200) as snippet
1838
+ LEFT(${COMBINED_TEXT_SQL}, 200) as snippet
1800
1839
  FROM embedding e
1801
1840
  JOIN note n ON n.id = e.note_id
1802
1841
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1842
+ ${ATTACHMENT_TEXT_JOIN2}
1803
1843
  WHERE ${where}
1804
1844
  ORDER BY e.vector <=> $${vecIdx}::vector ASC
1805
1845
  LIMIT $${limIdx} OFFSET $${offIdx}`,
@@ -1840,7 +1880,7 @@ var SearchRepository = class {
1840
1880
  const textConditions = [
1841
1881
  ...textCond.conditions,
1842
1882
  `(n.tsv @@ ${tsqFn}('english', $1) OR
1843
- to_tsvector('english', coalesce(c.content, '')) @@ ${tsqFn}('english', $1))`
1883
+ ${COMBINED_TEXT_VECTOR_SQL2} @@ ${tsqFn}('english', $1))`
1844
1884
  ];
1845
1885
  this.scopeToResolvedEmbeddingSet(textConditions, textCond.params, textCond.nextIdx, resolvedEmbeddingSet);
1846
1886
  const textWhere = textConditions.join(" AND ");
@@ -1848,11 +1888,12 @@ var SearchRepository = class {
1848
1888
  const textResult = await this.db.query(
1849
1889
  `SELECT n.id,
1850
1890
  ts_rank(
1851
- setweight(n.tsv, 'A') || setweight(to_tsvector('english', coalesce(c.content, '')), 'B'),
1891
+ setweight(n.tsv, 'A') || setweight(${COMBINED_TEXT_VECTOR_SQL2}, 'B'),
1852
1892
  ${tsqFn}('english', $1)
1853
1893
  ) as rank
1854
1894
  FROM note n
1855
1895
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1896
+ ${ATTACHMENT_TEXT_JOIN2}
1856
1897
  WHERE ${textWhere}
1857
1898
  ORDER BY rank DESC
1858
1899
  LIMIT 100`,
@@ -1886,9 +1927,10 @@ var SearchRepository = class {
1886
1927
  }
1887
1928
  const noteResult = await this.db.query(
1888
1929
  `SELECT n.id, n.title, n.created_at, n.updated_at,
1889
- LEFT(coalesce(c.content, ''), 200) as snippet
1930
+ LEFT(${COMBINED_TEXT_SQL}, 200) as snippet
1890
1931
  FROM note n
1891
1932
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1933
+ ${ATTACHMENT_TEXT_JOIN2}
1892
1934
  WHERE n.id = ANY($1)`,
1893
1935
  [pageIds]
1894
1936
  );
@@ -1936,9 +1978,10 @@ var SearchRepository = class {
1936
1978
  const listParams = [...params, limit, offset];
1937
1979
  const result = await this.db.query(
1938
1980
  `SELECT n.id, n.title, n.created_at, n.updated_at,
1939
- LEFT(coalesce(c.content, ''), 200) as snippet
1981
+ LEFT(${COMBINED_TEXT_SQL}, 200) as snippet
1940
1982
  FROM note n
1941
1983
  LEFT JOIN note_revised_current c ON c.note_id = n.id
1984
+ ${ATTACHMENT_TEXT_JOIN2}
1942
1985
  WHERE ${where}
1943
1986
  ORDER BY n.created_at DESC
1944
1987
  LIMIT $${paramIdx++} OFFSET $${paramIdx++}`,
@@ -3811,6 +3854,29 @@ function getLlmFunction() {
3811
3854
  return llmFn;
3812
3855
  }
3813
3856
 
3857
+ // src/repositories/note-text.ts
3858
+ async function getNoteTextWithExtractedAttachments(db, noteId) {
3859
+ const result = await db.query(
3860
+ `SELECT c.content,
3861
+ COALESCE(string_agg(a.extracted_text, E'
3862
+ ' ORDER BY a.position, a.created_at)
3863
+ FILTER (WHERE a.extracted_text IS NOT NULL AND a.extracted_text <> ''), '') as extracted_text
3864
+ FROM note_revised_current c
3865
+ LEFT JOIN attachment a ON a.note_id = c.note_id AND a.deleted_at IS NULL
3866
+ WHERE c.note_id = $1
3867
+ GROUP BY c.content`,
3868
+ [noteId]
3869
+ );
3870
+ if (result.rows.length === 0) return null;
3871
+ const content = result.rows[0].content;
3872
+ const extractedText = result.rows[0].extracted_text ?? "";
3873
+ return {
3874
+ content,
3875
+ extracted_text: extractedText,
3876
+ combined: [content, extractedText].filter(Boolean).join("\n")
3877
+ };
3878
+ }
3879
+
3814
3880
  // src/job-queue-worker.ts
3815
3881
  var JOB_PRIORITIES = {
3816
3882
  ai_revision: 1,
@@ -4092,12 +4158,9 @@ function conceptTaggingHandler(job, db) {
4092
4158
  return (async () => {
4093
4159
  const llmFn2 = getLlmFunction();
4094
4160
  if (!llmFn2) return { skipped: true, reason: "no LLM function registered" };
4095
- const result = await db.query(
4096
- `SELECT content FROM note_revised_current WHERE note_id = $1`,
4097
- [job.note_id]
4098
- );
4099
- if (result.rows.length === 0) throw new Error(`No content found for note ${job.note_id}`);
4100
- const content = result.rows[0].content;
4161
+ const noteText = await getNoteTextWithExtractedAttachments(db, job.note_id);
4162
+ if (!noteText) throw new Error(`No content found for note ${job.note_id}`);
4163
+ const content = noteText.combined;
4101
4164
  const prompt = `Task: Extract 3-5 topic tags from the text below.
4102
4165
  Rules:
4103
4166
  - Each tag is 1-3 words, lowercase
@@ -4990,9 +5053,17 @@ var AttachmentsRepository = class {
4990
5053
  }
4991
5054
  const attachmentId = generateId();
4992
5055
  await this.db.query(
4993
- `INSERT INTO attachment (id, note_id, blob_id, filename, display_name)
4994
- VALUES ($1, $2, $3, $4, $5)`,
4995
- [attachmentId, input.noteId, blobId, input.filename, input.displayName ?? null]
5056
+ `INSERT INTO attachment (id, note_id, blob_id, filename, display_name, mime_type, extracted_text)
5057
+ VALUES ($1, $2, $3, $4, $5, $6, $7)`,
5058
+ [
5059
+ attachmentId,
5060
+ input.noteId,
5061
+ blobId,
5062
+ input.filename,
5063
+ input.displayName ?? null,
5064
+ input.mimeType ?? null,
5065
+ input.extractedText ?? null
5066
+ ]
4996
5067
  );
4997
5068
  return this.get(attachmentId);
4998
5069
  }
@@ -5055,6 +5126,7 @@ var ManageAttachmentsInputSchema = z.object({
5055
5126
  data_base64: z.string().optional(),
5056
5127
  filename: z.string().optional(),
5057
5128
  mime_type: z.string().optional(),
5129
+ extracted_text: z.string().optional(),
5058
5130
  display_name: z.string().optional()
5059
5131
  });
5060
5132
  async function manageAttachments(db, blobStore, rawInput) {
@@ -5073,6 +5145,7 @@ async function manageAttachments(db, blobStore, rawInput) {
5073
5145
  data,
5074
5146
  filename: input.filename,
5075
5147
  mimeType: input.mime_type,
5148
+ extractedText: input.extracted_text,
5076
5149
  displayName: input.display_name
5077
5150
  });
5078
5151
  return { action: "attach", attachment, size_bytes: data.length };
@@ -5324,12 +5397,9 @@ function averageEmbeddings(embeddings) {
5324
5397
  async function embeddingGenerationHandler(job, db) {
5325
5398
  const fn = embedFn;
5326
5399
  if (!fn) return { skipped: true, reason: "no embed function registered" };
5327
- const noteResult = await db.query(
5328
- `SELECT content FROM note_revised_current WHERE note_id = $1`,
5329
- [job.note_id]
5330
- );
5331
- if (noteResult.rows.length === 0) throw new Error(`No content for note ${job.note_id}`);
5332
- const content = noteResult.rows[0].content;
5400
+ const noteText = await getNoteTextWithExtractedAttachments(db, job.note_id);
5401
+ if (!noteText) throw new Error(`No content for note ${job.note_id}`);
5402
+ const content = noteText.combined;
5333
5403
  const chunks = chunkText(content);
5334
5404
  const embeddings = await fn(chunks);
5335
5405
  const vector2 = averageEmbeddings(embeddings);
@@ -6512,6 +6582,7 @@ function noteToShard(note) {
6512
6582
  title: note.title,
6513
6583
  original_content: note.original_content,
6514
6584
  revised_content: note.revised_content,
6585
+ ...note.binary_sources?.length ? { binary_sources: note.binary_sources } : {},
6515
6586
  format: note.format,
6516
6587
  source: note.source,
6517
6588
  starred: note.is_starred,
@@ -6532,6 +6603,7 @@ function noteFromShard(shard) {
6532
6603
  is_archived: shard.archived,
6533
6604
  original_content: shard.original_content,
6534
6605
  revised_content: shard.revised_content,
6606
+ binary_sources: shard.binary_sources,
6535
6607
  tags: shard.tags,
6536
6608
  created_at: shard.created_at,
6537
6609
  updated_at: shard.updated_at,
@@ -6789,9 +6861,40 @@ async function exportShard(db, options) {
6789
6861
  tags.push(row.tag);
6790
6862
  tagsByNote.set(row.note_id, tags);
6791
6863
  }
6864
+ const attachmentRows = await db.query(
6865
+ `SELECT a.note_id,
6866
+ a.id,
6867
+ a.filename,
6868
+ a.mime_type,
6869
+ a.extracted_text,
6870
+ b.content_hash,
6871
+ b.size_bytes,
6872
+ b.storage_path
6873
+ FROM attachment a
6874
+ JOIN attachment_blob b ON b.id = a.blob_id
6875
+ WHERE a.deleted_at IS NULL
6876
+ ORDER BY a.note_id, a.position, a.created_at`
6877
+ );
6878
+ const binarySourcesByNote = /* @__PURE__ */ new Map();
6879
+ for (const row of attachmentRows.rows) {
6880
+ const source = {
6881
+ extracted_text: row.extracted_text ?? "",
6882
+ attachment: {
6883
+ id: row.id,
6884
+ path: row.storage_path ?? row.filename,
6885
+ mime: row.mime_type,
6886
+ checksum: row.content_hash,
6887
+ bytes: Number(row.size_bytes)
6888
+ }
6889
+ };
6890
+ const sources = binarySourcesByNote.get(row.note_id) ?? [];
6891
+ sources.push(source);
6892
+ binarySourcesByNote.set(row.note_id, sources);
6893
+ }
6792
6894
  const notes = noteRows.rows.map((row) => ({
6793
6895
  ...row,
6794
- tags: tagsByNote.get(row.id) ?? []
6896
+ tags: tagsByNote.get(row.id) ?? [],
6897
+ binary_sources: binarySourcesByNote.get(row.id)
6795
6898
  }));
6796
6899
  const exportedNoteIds = new Set(notes.map((n) => n.id));
6797
6900
  const shardNotes = notes.map((n) => noteToShard(n));
@@ -7653,7 +7756,8 @@ function tokenize(query) {
7653
7756
  return query.toLowerCase().split(/[^a-z0-9]+/i).filter((token) => token.length > 0);
7654
7757
  }
7655
7758
  function noteSearchText(note) {
7656
- return `${note.title ?? ""} ${note.original_content} ${note.revised_content ?? ""}`.toLowerCase();
7759
+ const extractedText = note.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join(" ") ?? "";
7760
+ return `${note.title ?? ""} ${note.original_content} ${note.revised_content ?? ""} ${extractedText}`.toLowerCase();
7657
7761
  }
7658
7762
  function countOccurrences(haystack, needle) {
7659
7763
  if (!needle) return 0;
@@ -7674,7 +7778,8 @@ function noteMatchesTokens(note, tokens) {
7674
7778
  function rankNote(note, tokens, weights) {
7675
7779
  if (tokens.length === 0) return 0;
7676
7780
  const title = (note.title ?? "").toLowerCase();
7677
- const content = `${note.original_content} ${note.revised_content ?? ""}`.toLowerCase();
7781
+ const extractedText = note.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join(" ") ?? "";
7782
+ const content = `${note.original_content} ${note.revised_content ?? ""} ${extractedText}`.toLowerCase();
7678
7783
  const tagText = note.tags.join(" ").toLowerCase();
7679
7784
  let score = 0;
7680
7785
  for (const token of tokens) {
@@ -7685,7 +7790,8 @@ function rankNote(note, tokens, weights) {
7685
7790
  return score;
7686
7791
  }
7687
7792
  function makeSnippet(note, tokens, length) {
7688
- const content = `${note.original_content} ${note.revised_content ?? ""}`.trim();
7793
+ const extractedText = note.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join(" ") ?? "";
7794
+ const content = `${note.original_content} ${note.revised_content ?? ""} ${extractedText}`.trim();
7689
7795
  if (tokens.length === 0) return content.slice(0, length);
7690
7796
  const lower = content.toLowerCase();
7691
7797
  const firstAt = tokens.map((token) => lower.indexOf(token)).filter((index) => index !== -1).sort((a, b) => a - b)[0] ?? 0;
@@ -8093,8 +8199,6 @@ var REQUIRED_RECORD_FIELDS = [
8093
8199
  "id",
8094
8200
  "type",
8095
8201
  "source",
8096
- "title",
8097
- "text",
8098
8202
  "facets",
8099
8203
  "tags",
8100
8204
  "concepts",
@@ -8135,6 +8239,12 @@ function isPlainRecord(value) {
8135
8239
  function isOptionalStringArray(value) {
8136
8240
  return value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
8137
8241
  }
8242
+ function isSupportedIndexSchemaVersion(value) {
8243
+ return value === "aiwg.fortemi.index.export.v1" || value === "aiwg.fortemi.index.export.v2";
8244
+ }
8245
+ function isSupportedRecordSchemaVersion(value) {
8246
+ return value === "aiwg.fortemi.index.record.v1" || value === "aiwg.fortemi.index.record.v2";
8247
+ }
8138
8248
  function validateOptionalRichMetadata(item, index, errors) {
8139
8249
  if (item.skos_concepts !== void 0) {
8140
8250
  if (!Array.isArray(item.skos_concepts)) {
@@ -8181,28 +8291,80 @@ function validateOptionalRichMetadata(item, index, errors) {
8181
8291
  if (relationship.metadata !== void 0 && !isPlainRecord(relationship.metadata)) {
8182
8292
  errors.push("items[" + index + "].relationships[" + relationshipIndex + "].metadata must be an object");
8183
8293
  }
8294
+ if (relationship.direction !== void 0 && relationship.direction !== "upstream" && relationship.direction !== "downstream" && relationship.direction !== "related") {
8295
+ errors.push("items[" + index + "].relationships[" + relationshipIndex + "].direction must be upstream, downstream, or related");
8296
+ }
8297
+ if (relationship.target_path !== void 0 && typeof relationship.target_path !== "string") {
8298
+ errors.push("items[" + index + "].relationships[" + relationshipIndex + "].target_path must be a string");
8299
+ }
8184
8300
  }
8185
8301
  }
8302
+ if (item.search !== void 0) {
8303
+ if (!isPlainRecord(item.search)) {
8304
+ errors.push("items[" + index + "].search must be an object");
8305
+ } else {
8306
+ if (!isOptionalStringArray(item.search.triggers)) errors.push("items[" + index + "].search.triggers must be a string array");
8307
+ if (!isOptionalStringArray(item.search.aliases)) errors.push("items[" + index + "].search.aliases must be a string array");
8308
+ if (!isOptionalStringArray(item.search.tags)) errors.push("items[" + index + "].search.tags must be a string array");
8309
+ if (item.search.frontmatter !== void 0 && !isPlainRecord(item.search.frontmatter)) {
8310
+ errors.push("items[" + index + "].search.frontmatter must be an object");
8311
+ }
8312
+ }
8313
+ }
8314
+ if (item.chunks !== void 0) {
8315
+ if (!Array.isArray(item.chunks)) {
8316
+ errors.push("items[" + index + "].chunks must be an array when present");
8317
+ } else {
8318
+ for (const [chunkIndex, chunk] of item.chunks.entries()) {
8319
+ if (!isPlainRecord(chunk)) errors.push("items[" + index + "].chunks[" + chunkIndex + "] must be an object");
8320
+ if (chunk.metadata !== void 0 && !isPlainRecord(chunk.metadata)) {
8321
+ errors.push("items[" + index + "].chunks[" + chunkIndex + "].metadata must be an object");
8322
+ }
8323
+ }
8324
+ }
8325
+ }
8326
+ if (item.embeddings !== void 0) {
8327
+ if (!Array.isArray(item.embeddings)) {
8328
+ errors.push("items[" + index + "].embeddings must be an array when present");
8329
+ } else {
8330
+ for (const [embeddingIndex, embedding] of item.embeddings.entries()) {
8331
+ if (!isPlainRecord(embedding)) errors.push("items[" + index + "].embeddings[" + embeddingIndex + "] must be an object");
8332
+ const vector2 = embedding.embedding ?? embedding.vector;
8333
+ if (vector2 !== void 0 && (!Array.isArray(vector2) || !vector2.every((entry) => typeof entry === "number"))) {
8334
+ errors.push("items[" + index + "].embeddings[" + embeddingIndex + "].embedding/vector must be a number array");
8335
+ }
8336
+ if (embedding.metadata !== void 0 && !isPlainRecord(embedding.metadata)) {
8337
+ errors.push("items[" + index + "].embeddings[" + embeddingIndex + "].metadata must be an object");
8338
+ }
8339
+ }
8340
+ }
8341
+ }
8342
+ if (item.compatibility !== void 0 && !isPlainRecord(item.compatibility)) {
8343
+ errors.push("items[" + index + "].compatibility must be an object");
8344
+ }
8186
8345
  }
8187
8346
  function validateAiwgFortemiIndexExport(value) {
8188
8347
  const errors = [];
8189
8348
  const counts = {};
8190
8349
  const data = value;
8191
- if (data?.schema_version !== "aiwg.fortemi.index.export.v1") {
8192
- errors.push("schema_version must be aiwg.fortemi.index.export.v1");
8350
+ if (!isSupportedIndexSchemaVersion(data?.schema_version)) {
8351
+ errors.push("schema_version must be aiwg.fortemi.index.export.v1 or aiwg.fortemi.index.export.v2");
8193
8352
  }
8194
8353
  if (!hasString(data?.generated_at)) errors.push("generated_at is required");
8195
8354
  if (!hasString(data?.source?.repo)) errors.push("source.repo is required");
8196
8355
  if (!hasString(data?.source?.privacy)) errors.push("source.privacy is required");
8197
8356
  if (!Array.isArray(data?.items)) errors.push("items must be an array");
8357
+ if (data.compatibility !== void 0 && !isPlainRecord(data.compatibility)) {
8358
+ errors.push("compatibility must be an object");
8359
+ }
8198
8360
  const ids = /* @__PURE__ */ new Set();
8199
8361
  let previousId = "";
8200
8362
  for (const [index, item] of (data.items ?? []).entries()) {
8201
8363
  for (const field of REQUIRED_RECORD_FIELDS) {
8202
8364
  if (!(field in item)) errors.push("items[" + index + "]." + field + " is required");
8203
8365
  }
8204
- if (item.schema_version !== "aiwg.fortemi.index.record.v1") {
8205
- errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1");
8366
+ if (!isSupportedRecordSchemaVersion(item.schema_version)) {
8367
+ errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2");
8206
8368
  }
8207
8369
  if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
8208
8370
  if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
@@ -8216,6 +8378,12 @@ function validateAiwgFortemiIndexExport(value) {
8216
8378
  if (!hasString(item.source?.path)) errors.push("items[" + index + "].source.path is required");
8217
8379
  if (!hasString(item.source?.repo_relative_path)) errors.push("items[" + index + "].source.repo_relative_path is required");
8218
8380
  if (!hasString(item.source?.locator)) errors.push("items[" + index + "].source.locator is required");
8381
+ if (typeof item.title !== "string" && typeof item.search?.title !== "string" && typeof item.search?.name !== "string") {
8382
+ errors.push("items[" + index + "].title or search.title/search.name is required");
8383
+ }
8384
+ if (typeof item.text !== "string" && typeof item.search?.body !== "string" && typeof item.search?.summary !== "string") {
8385
+ errors.push("items[" + index + "].text or search.body/search.summary is required");
8386
+ }
8219
8387
  if (!Array.isArray(item.tags)) errors.push("items[" + index + "].tags must be an array");
8220
8388
  if (!Array.isArray(item.concepts)) errors.push("items[" + index + "].concepts must be an array");
8221
8389
  if (!Array.isArray(item.relationships)) errors.push("items[" + index + "].relationships must be an array");
@@ -8296,8 +8464,8 @@ function validateProjectedRecords(items) {
8296
8464
  const ids = /* @__PURE__ */ new Set();
8297
8465
  let previousId = "";
8298
8466
  for (const [index, item] of items.entries()) {
8299
- if (item.schema_version !== "aiwg.fortemi.index.record.v1") {
8300
- errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1");
8467
+ if (!isSupportedRecordSchemaVersion(item.schema_version)) {
8468
+ errors.push("items[" + index + "].schema_version must be aiwg.fortemi.index.record.v1 or aiwg.fortemi.index.record.v2");
8301
8469
  }
8302
8470
  if (!hasString(item.id)) errors.push("items[" + index + "].id is required");
8303
8471
  if (hasString(item.id) && ids.has(item.id)) errors.push("duplicate id: " + item.id);
@@ -8307,8 +8475,12 @@ function validateProjectedRecords(items) {
8307
8475
  }
8308
8476
  if (hasString(item.id)) previousId = item.id;
8309
8477
  if (!hasString(item.type)) errors.push("items[" + index + "].type must be a non-empty string");
8310
- if (!hasString(item.title)) errors.push("items[" + index + "].title is required");
8311
- if (typeof item.text !== "string") errors.push("items[" + index + "].text is required");
8478
+ if (typeof item.title !== "string" && typeof item.search?.title !== "string" && typeof item.search?.name !== "string") {
8479
+ errors.push("items[" + index + "].title or search.title/search.name is required");
8480
+ }
8481
+ if (typeof item.text !== "string" && typeof item.search?.body !== "string" && typeof item.search?.summary !== "string") {
8482
+ errors.push("items[" + index + "].text or search.body/search.summary is required");
8483
+ }
8312
8484
  if (!item.facets || typeof item.facets !== "object" || Array.isArray(item.facets)) {
8313
8485
  errors.push("items[" + index + "].facets must be an object");
8314
8486
  }
@@ -8400,6 +8572,86 @@ function getAiwgFortemiFacets(items) {
8400
8572
  }
8401
8573
  return result;
8402
8574
  }
8575
+ function recordTitle(item) {
8576
+ return item.title ?? item.search?.title ?? item.search?.name ?? item.id;
8577
+ }
8578
+ function recordText(item) {
8579
+ const base = item.text ?? item.search?.body ?? item.search?.summary ?? item.chunks?.map((chunk) => chunk.text ?? chunk.body ?? chunk.summary ?? "").filter(Boolean).join("\n") ?? "";
8580
+ const extractedText = "binary_sources" in item ? item.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "" : "";
8581
+ return [base, extractedText].filter(Boolean).join("\n");
8582
+ }
8583
+ function defaultEmbeddingInput(record, granularity) {
8584
+ const title = recordTitle(record);
8585
+ const text = recordText(record);
8586
+ if (granularity === "title-summary") {
8587
+ const extractedText = record.binary_sources?.map((source) => source.extracted_text).filter(Boolean).join("\n") ?? "";
8588
+ return [title, record.search?.summary ?? "", extractedText].filter(Boolean).join("\n");
8589
+ }
8590
+ return [title, text].filter(Boolean).join("\n");
8591
+ }
8592
+ function generatedAtString(value) {
8593
+ if (value instanceof Date) return value.toISOString();
8594
+ return value ?? (/* @__PURE__ */ new Date()).toISOString();
8595
+ }
8596
+ async function buildAiwgStaticEmbeddingSet(index, options) {
8597
+ assertAiwgFortemiIndexExport(index);
8598
+ const granularity = options.granularity ?? "body";
8599
+ const records = options.records ?? index.items;
8600
+ const embeddings = [];
8601
+ for (const record of records) {
8602
+ const input = options.textForRecord?.(record) ?? defaultEmbeddingInput(record, granularity);
8603
+ const embedding = await options.backend.embed(input, record);
8604
+ if (embedding.length !== options.backend.dimensions) {
8605
+ throw new Error(`Embedding for ${record.id} has ${embedding.length} dimensions; expected ${options.backend.dimensions}`);
8606
+ }
8607
+ embeddings.push({
8608
+ record_id: record.id,
8609
+ embedding,
8610
+ granularity,
8611
+ input_hash: computeHash(new TextEncoder().encode(input)),
8612
+ source_path: record.source.path
8613
+ });
8614
+ }
8615
+ const embeddingSet = {
8616
+ schema_version: "aiwg.fortemi.embedding.set.v1",
8617
+ id: options.id,
8618
+ model: options.backend.model,
8619
+ dimensions: options.backend.dimensions,
8620
+ generated_at: generatedAtString(options.generatedAt),
8621
+ granularity,
8622
+ ...options.metric ? { metric: options.metric } : {},
8623
+ input_hash_algorithm: "sha256",
8624
+ embeddings
8625
+ };
8626
+ assertAiwgStaticEmbeddingSet(embeddingSet);
8627
+ return embeddingSet;
8628
+ }
8629
+ function recordSearchValues(item) {
8630
+ const search = item.search;
8631
+ const values = [
8632
+ item.id,
8633
+ recordTitle(item),
8634
+ recordText(item),
8635
+ search?.title,
8636
+ search?.name,
8637
+ search?.summary,
8638
+ search?.body,
8639
+ search?.capability,
8640
+ search?.phase,
8641
+ search?.type,
8642
+ ...search?.triggers ?? [],
8643
+ ...search?.aliases ?? [],
8644
+ ...search?.tags ?? [],
8645
+ ...(item.chunks ?? []).flatMap((chunk) => [chunk.text, chunk.body, chunk.summary, chunk.source_path])
8646
+ ];
8647
+ if (search?.frontmatter) {
8648
+ for (const value of Object.values(search.frontmatter)) {
8649
+ if (typeof value === "string") values.push(value);
8650
+ else if (Array.isArray(value)) values.push(...value.filter((entry) => typeof entry === "string"));
8651
+ }
8652
+ }
8653
+ return values.filter((value) => typeof value === "string" && value.length > 0);
8654
+ }
8403
8655
  function buildAiwgChunkedIndex(index, options = {}) {
8404
8656
  const partSize = hasPositiveInteger(options.partSize) ? options.partSize : 500;
8405
8657
  const projection = options.projection;
@@ -8461,14 +8713,21 @@ function matchesFacetFilters(item, filters) {
8461
8713
  function queryMatches(item, q) {
8462
8714
  if (!q) return [];
8463
8715
  const matches = [];
8464
- if (item.title.toLowerCase().includes(q)) matches.push({ field: "title", value: item.title });
8465
- if (item.text.toLowerCase().includes(q)) matches.push({ field: "text", value: item.text });
8716
+ const title = recordTitle(item);
8717
+ const text = recordText(item);
8718
+ if (title.toLowerCase().includes(q)) matches.push({ field: "title", value: title });
8719
+ if (text.toLowerCase().includes(q)) matches.push({ field: "text", value: text });
8466
8720
  for (const tag of item.tags) {
8467
8721
  if (tag.toLowerCase().includes(q)) matches.push({ field: "tag", value: tag });
8468
8722
  }
8469
8723
  for (const concept of item.concepts) {
8470
8724
  if (concept.toLowerCase().includes(q)) matches.push({ field: "concept", value: concept });
8471
8725
  }
8726
+ for (const value of recordSearchValues(item)) {
8727
+ if (value !== title && value !== text && value.toLowerCase().includes(q)) {
8728
+ matches.push({ field: "text", value, score: DEFAULT_QUERY_WEIGHTS.text });
8729
+ }
8730
+ }
8472
8731
  return matches;
8473
8732
  }
8474
8733
  var DISCOVERY_STOPWORDS = /* @__PURE__ */ new Set([
@@ -8524,17 +8783,28 @@ function discoveryMatches(item, query) {
8524
8783
  const idParts = item.id.split(/[:/]/);
8525
8784
  const names = [
8526
8785
  item.id,
8527
- item.title,
8786
+ recordTitle(item),
8787
+ item.search?.name,
8788
+ item.search?.title,
8789
+ ...item.search?.aliases ?? [],
8528
8790
  ...idParts,
8529
8791
  ...facetValues(item, ["name", "canonical_name", "command", "skill", "agent", "rule"])
8530
- ].filter(Boolean);
8531
- const triggers = facetValues(item, ["trigger", "triggers", "trigger_phrase", "trigger_phrases"]);
8792
+ ].filter((value) => hasString(value));
8793
+ const triggers = [
8794
+ ...facetValues(item, ["trigger", "triggers", "trigger_phrase", "trigger_phrases"]),
8795
+ ...item.search?.triggers ?? []
8796
+ ];
8532
8797
  const capabilities = [
8533
8798
  ...facetValues(item, ["capability", "capabilities", "summary", "description"]),
8799
+ item.search?.capability,
8800
+ item.search?.summary,
8801
+ item.search?.phase,
8802
+ item.search?.type,
8803
+ ...item.search?.tags ?? [],
8534
8804
  ...item.concepts,
8535
8805
  ...item.tags
8536
- ];
8537
- const sourceValues = [item.source?.path, item.source?.repo_relative_path, item.source?.locator].filter(Boolean);
8806
+ ].filter((value) => hasString(value));
8807
+ const sourceValues = [item.source?.path, item.source?.repo_relative_path, item.source?.locator].filter((value) => hasString(value));
8538
8808
  for (const name of names) {
8539
8809
  const canonicalName = canonicalDiscoveryName(name);
8540
8810
  if (!canonicalName) continue;
@@ -8544,8 +8814,9 @@ function discoveryMatches(item, query) {
8544
8814
  addDiscoveryMatch(matches, { field: "id", value: name, score: 48, reason: "near canonical name" });
8545
8815
  }
8546
8816
  }
8547
- const titleOverlap = tokenOverlapScore(tokens, item.title);
8548
- if (titleOverlap > 0) addDiscoveryMatch(matches, { field: "title", value: item.title, score: 18 * titleOverlap, reason: "title token overlap" });
8817
+ const title = recordTitle(item);
8818
+ const titleOverlap = tokenOverlapScore(tokens, title);
8819
+ if (titleOverlap > 0) addDiscoveryMatch(matches, { field: "title", value: title, score: 18 * titleOverlap, reason: "title token overlap" });
8549
8820
  for (const trigger of triggers) {
8550
8821
  const overlap = tokenOverlapScore(tokens, trigger);
8551
8822
  if (overlap > 0) addDiscoveryMatch(matches, { field: "facet", value: trigger, score: 34 * overlap, reason: "trigger phrase" });
@@ -8554,8 +8825,9 @@ function discoveryMatches(item, query) {
8554
8825
  const overlap = tokenOverlapScore(tokens, capability);
8555
8826
  if (overlap > 0) addDiscoveryMatch(matches, { field: "concept", value: capability, score: 22 * overlap, reason: "capability overlap" });
8556
8827
  }
8557
- const textOverlap = tokenOverlapScore(tokens, item.text);
8558
- if (textOverlap > 0) addDiscoveryMatch(matches, { field: "text", value: item.text, score: 8 * textOverlap, reason: "body token overlap" });
8828
+ const text = recordText(item);
8829
+ const textOverlap = tokenOverlapScore(tokens, text);
8830
+ if (textOverlap > 0) addDiscoveryMatch(matches, { field: "text", value: text, score: 8 * textOverlap, reason: "body token overlap" });
8559
8831
  for (const source of sourceValues) {
8560
8832
  const overlap = tokenOverlapScore(tokens, source);
8561
8833
  if (overlap > 0) addDiscoveryMatch(matches, { field: "source", value: source, score: 2 * overlap, reason: "path overlap" });
@@ -8583,7 +8855,7 @@ function createSnippet(item, matches, q, maxLength) {
8583
8855
  const textMatch = matches.find((match) => match.field === "text");
8584
8856
  const titleMatch = matches.find((match) => match.field === "title");
8585
8857
  const firstMatch = textMatch ?? titleMatch ?? matches[0];
8586
- return clipSnippet(firstMatch?.value ?? item.text, q, maxLength);
8858
+ return clipSnippet(firstMatch?.value ?? recordText(item), q, maxLength);
8587
8859
  }
8588
8860
  function createRankedEntries(items, q, options, ordinalBase = 0) {
8589
8861
  const weights = { ...DEFAULT_QUERY_WEIGHTS, ...options.weights };
@@ -8845,13 +9117,16 @@ function edgeFromRelationship(sourceId, relationship) {
8845
9117
  source_id: sourceId,
8846
9118
  target_id: relationship.target_id,
8847
9119
  type: relationship.type,
8848
- ...relationship.source_path ? { source_path: relationship.source_path } : {}
9120
+ ...relationship.source_path ? { source_path: relationship.source_path } : {},
9121
+ ...relationship.target_path ? { target_path: relationship.target_path } : {},
9122
+ ...relationship.direction ? { direction: relationship.direction } : {}
8849
9123
  };
8850
9124
  }
8851
9125
  function relationshipMatches(edge, options = {}) {
8852
9126
  const type = relationshipTypeFilter(options);
8853
9127
  const direction = options.direction ?? "both";
8854
9128
  if (type && edge.type !== type) return false;
9129
+ if (options.relationshipDirection && edge.direction !== options.relationshipDirection) return false;
8855
9130
  if (options.sourceId && edge.source_id !== options.sourceId) return false;
8856
9131
  if (options.targetId && edge.target_id !== options.targetId) return false;
8857
9132
  if (direction === "out" && options.targetId && edge.target_id !== options.targetId) return false;
@@ -8859,7 +9134,7 @@ function relationshipMatches(edge, options = {}) {
8859
9134
  return true;
8860
9135
  }
8861
9136
  function nodeSummary(item) {
8862
- return { id: item.id, type: item.type, title: item.title };
9137
+ return { id: item.id, type: item.type, title: recordTitle(item) };
8863
9138
  }
8864
9139
  function addNode(nodes, item) {
8865
9140
  if (item) nodes.set(item.id, nodeSummary(item));
@@ -9243,8 +9518,8 @@ function communityIdsFor(item, options) {
9243
9518
  }
9244
9519
 
9245
9520
  // src/index.ts
9246
- var VERSION = "2026.7.0";
9521
+ var VERSION = "2026.7.2";
9247
9522
 
9248
- export { AIWG_SCAN_REQUIRED_FIELDS, ArchiveManager, AttachmentsRepository, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DB_SNAPSHOT_SCHEMA_VERSION, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LOCAL_ENDPOINTS, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, SHARD_FORMAT, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexToCommunityGraph, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, computeHash, computeSri, conceptTaggingHandler, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLegacyProvider, createPGliteBackend, createPGliteInstance, createRemoteBackend, createRoutes, createShardBackend, createWorkerEmbedFunction, defaultStorageBackendFactory, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, discoverLocalProviders, dumpDbSnapshot, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, exportShard, fetchPluginScript, findAiwgStaticDuplicatePairs, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getEmbedFunction, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getLlmFunction, getNote, getPrefetchedSha256, handleEmbedRequests, hasFortemiSecureSecrets, importShard, isPluginScriptAllowed, isShardPrefetched, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, prefetchShard, provenanceEdgeToShard, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, restoreDbSnapshot, searchTool, selectBackend, selectLlmModel, setEmbedFunction, setLlmFunction, sha256Hex, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsFromShard, tagsToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet, validateChecksums, verifyDbSnapshotMeta, verifySri };
9523
+ export { AIWG_SCAN_REQUIRED_FIELDS, ArchiveManager, AttachmentsRepository, CURRENT_MIGRATION_HEAD, CURRENT_SHARD_VERSION, CapabilityManager, CaptureKnowledgeInputSchema, CollectionsRepository, CommunitiesRepository, DB_SNAPSHOT_SCHEMA_VERSION, DbSnapshotVersionError, EMBED_REQUEST_KIND, EMBED_RESPONSE_KIND, EmbeddingSetsRepository, FallbackRouter, FortemiToolManifest, GetNoteInputSchema, GraphRepository, JOB_CAPABILITIES, JOB_PRIORITIES, JobQueueWorker, LOCAL_ENDPOINTS, LinksRepository, ListNotesInputSchema, ManageArchiveInputSchema, ManageAttachmentsInputSchema, ManageCapabilitiesInputSchema, ManageCollectionsInputSchema, ManageLinksInputSchema, ManageNoteInputSchema, ManageTagsInputSchema, MemoryBlobStore, MigrationRunner, NotesRepository, OpenAICompatibleProvider, PGliteStorageBackend, PGliteStorageBackendFactory, PGliteWorkerClient, PGliteWorkerStorageBackend, PGliteWorkerStorageBackendFactory, ProvenanceRepository, ProviderRegistry, SHARD_FORMAT, SUPPORTED_PGLITE_VERSION, SearchInputSchema, SearchRepository, SkosRepository, TagsRepository, TransactionProxy, TypedEventBus, VERSION, aiRevisionHandler, aiwgFortemiIndexToCommunityGraph, allMigrations, appendPluginScript, assertAiwgFortemiChunkManifest, assertAiwgFortemiChunkPart, assertAiwgFortemiIndexExport, assertAiwgStaticEmbeddingSet, buildAiwgChunkedIndex, buildAiwgStaticEmbeddingSet, buildNoteConditions, buildPluginCsp, captureKnowledge, chunkText, classifyError, classifyModel, clearPrefetchedShard, collectionFromShard, collectionToShard, computeHash, computeSri, conceptTaggingHandler, cosineSimilarity, createAiwgFetchChunkLoader, createAiwgFetchDetailLoader, createAiwgIndexController, createAiwgReviewDecisionExport, createBlobStore, createCosineSemanticProvider, createCspReportHandler, createFortemi, createLegacyProvider, createPGliteBackend, createPGliteInstance, createRemoteBackend, createRoutes, createShardBackend, createWorkerEmbedFunction, defaultStorageBackendFactory, detectCommunities, detectGpuCapabilities, detectInferenceCapabilities, discoverLocalProviders, dumpDbSnapshot, embeddingFromShard, embeddingGenerationHandler, embeddingSetFromShard, embeddingSetMemberToShard, embeddingSetToShard, embeddingToShard, enqueueFullWorkflow, enqueueJob, enqueueNoteCreationJobs, estimateModelFit, estimateVramMB, estimateVramTier, exportShard, fetchPluginScript, findAiwgStaticDuplicatePairs, fortemiManifest, fromPrefetched, generateId, getAiwgFortemiFacets, getEmbedFunction, getFortemiBridge, getFortemiSecretStore, getJobQueueStatus, getLlmFunction, getNote, getPrefetchedSha256, handleEmbedRequests, hasFortemiSecureSecrets, importShard, isPluginScriptAllowed, isShardPrefetched, linkFromShard, linkToShard, linkingHandler, listNotes, manageArchive, manageAttachments, manageCapabilities, manageCollections, manageLinks, manageNote, manageTags, matchRoute, noteFromShard, noteSkosTagToShard, noteToShard, openShard, packTarGz, parseCspReport, prefetchShard, provenanceEdgeToShard, queryAiwgFortemiIndex, queryAiwgHybridIndex, queryAiwgSemanticIndex, registerLlmCapability, registerSemanticCapability, registerSemanticCapabilityWorker, registerServiceWorker, restoreDbSnapshot, searchTool, selectBackend, selectLlmModel, setEmbedFunction, setLlmFunction, sha256Hex, skosConceptToShard, skosRelationToShard, skosSchemeToShard, suggestTags, tagsFromShard, tagsToShard, titleGenerationHandler, unpackTarGz, unregisterLlmCapability, unregisterSemanticCapability, validateAiwgFortemiChunkManifest, validateAiwgFortemiChunkPart, validateAiwgFortemiIndexExport, validateAiwgStaticEmbeddingSet, validateChecksums, verifyDbSnapshotMeta, verifySri };
9249
9524
  //# sourceMappingURL=index.js.map
9250
9525
  //# sourceMappingURL=index.js.map