@hraness/wordcell 0.22.5 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +67 -18
  2. package/dist/authoring.js +2 -2
  3. package/dist/{authority-3ga47syb.js → authority-35cbvp1x.js} +2 -2
  4. package/dist/benchmark.js +3 -3
  5. package/dist/capture.js +6 -6
  6. package/dist/cli.js +26 -24
  7. package/dist/clip/acquire.js +3 -3
  8. package/dist/clip/args.js +1 -1
  9. package/dist/clip/cli.js +6 -6
  10. package/dist/clip/doctor.js +4 -4
  11. package/dist/evaluation-builder.js +10 -10
  12. package/dist/evaluation-kb.js +10 -10
  13. package/dist/graph-authority.js +4 -4
  14. package/dist/graph-percolation.js +6 -6
  15. package/dist/graph.js +3 -1
  16. package/dist/{index-bcknqxrq.js → index-0eacgvpv.js} +9 -1
  17. package/dist/{index-f75zmmdr.js → index-1er88ckw.js} +4 -4
  18. package/dist/{index-8v6k9h4r.js → index-1s8mc4hz.js} +1 -1
  19. package/dist/{index-n9kwenpt.js → index-1w0sp1q7.js} +1 -1
  20. package/dist/{index-de2w8crk.js → index-2fmnttca.js} +82 -15
  21. package/dist/{index-8bgnhjgq.js → index-44kw9tr8.js} +3867 -162
  22. package/dist/{index-42tb8qqp.js → index-5d1rd5v9.js} +444 -9
  23. package/dist/{index-aer0jdrq.js → index-ahyhryb8.js} +1 -1
  24. package/dist/{index-f7fnww7a.js → index-b0khj0h5.js} +164 -9
  25. package/dist/{index-qdb8f8va.js → index-bdwcjvr4.js} +1 -1
  26. package/dist/{index-dfag79p7.js → index-byz4kzww.js} +3 -0
  27. package/dist/{index-hgve9rh2.js → index-cd75vky9.js} +96 -1
  28. package/dist/{index-h2qn3y3f.js → index-f184qyh1.js} +4 -4
  29. package/dist/{index-n57tewfr.js → index-fp732bgg.js} +2 -2
  30. package/dist/{index-zy7an84p.js → index-jvb7w0gg.js} +5 -2
  31. package/dist/{index-f984hw45.js → index-k86wepd8.js} +1 -1
  32. package/dist/{index-1r5rnkfr.js → index-mvs6f7ne.js} +2 -2
  33. package/dist/{index-p96kb6t2.js → index-nn67kzwa.js} +1 -1
  34. package/dist/{index-fc4dr114.js → index-nwrehke7.js} +1 -1
  35. package/dist/{index-pz2b2x0y.js → index-pgtm2nhf.js} +1 -1
  36. package/dist/{index-054mb7d3.js → index-qfqjfxaa.js} +10 -9
  37. package/dist/{index-xzvcw9ga.js → index-r0m5taz2.js} +2 -2
  38. package/dist/{index-gtwqye5a.js → index-tcaq1c7f.js} +4 -4
  39. package/dist/{index-g5vsqmdy.js → index-xwxy71ew.js} +190 -4
  40. package/dist/index.js +15 -13
  41. package/dist/pdf.js +3 -3
  42. package/dist/percolate.js +2 -2
  43. package/dist/portfolio.js +10 -10
  44. package/dist/publish.js +3 -3
  45. package/dist/sdk.js +9 -9
  46. package/dist/search.js +2 -2
  47. package/dist/semantic.js +4 -4
  48. package/dist/{support-jf9v10j7.js → support-1yhv6vm6.js} +25 -23
  49. package/dist/url-intelligence.js +1 -1
  50. package/dist/workflows/decision-context.js +10 -10
  51. package/dist/workflows/index.js +10 -10
  52. package/package.json +12 -4
  53. package/skills/wordcell/AGENTS.md +1 -0
  54. package/skills/wordcell/SKILL.md +5 -3
  55. package/skills/wordcell/references/query.md +52 -4
  56. package/skills/wordcell/references/session-memory.md +207 -0
  57. package/src/authoring-import.ts +32 -0
  58. package/src/authoring-model.ts +185 -2
  59. package/src/authoring-program.ts +19 -7
  60. package/src/cli-help.ts +894 -0
  61. package/src/cli-intro.ts +1 -1
  62. package/src/cli-program.ts +612 -83
  63. package/src/cli-style.ts +132 -0
  64. package/src/clip/acquire.ts +20 -0
  65. package/src/clip/args.ts +3 -0
  66. package/src/clip/cli.ts +82 -5
  67. package/src/clip/cookie-permission.ts +241 -0
  68. package/src/clip/url-metadata-cli.ts +6 -5
  69. package/src/clip/url-metadata.ts +14 -0
  70. package/src/graph.ts +9 -4
  71. package/src/import-supermemory.ts +1629 -0
  72. package/src/mcp-server.ts +415 -0
  73. package/src/mcp-tools.ts +1138 -0
  74. package/src/pdf/cli.ts +5 -4
  75. package/src/sdk.ts +149 -1
  76. package/src/selected-passage.ts +409 -0
  77. package/src/support-profile.ts +1 -1
  78. package/src/support.ts +3 -0
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  openSemanticSearchSession,
9
9
  recommendedEmbeddingModel
10
- } from "./index-n57tewfr.js";
10
+ } from "./index-fp732bgg.js";
11
11
  import {
12
12
  UntrustedContentBudgetError,
13
13
  createUntrustedToolResult
@@ -19,14 +19,14 @@ import {
19
19
  } from "./index-j70m75wd.js";
20
20
  import {
21
21
  percolateWithGraph
22
- } from "./index-gtwqye5a.js";
22
+ } from "./index-tcaq1c7f.js";
23
23
  import {
24
24
  openGraphAuthority
25
- } from "./index-f75zmmdr.js";
25
+ } from "./index-1er88ckw.js";
26
26
  import {
27
27
  validateGraphPercolationOptions,
28
28
  validateGraphQueryRequest
29
- } from "./index-pz2b2x0y.js";
29
+ } from "./index-pgtm2nhf.js";
30
30
  import {
31
31
  GitHistoryError,
32
32
  gitHistoryForNotes,
@@ -41,10 +41,10 @@ import {
41
41
  fuseRankedCandidates,
42
42
  searchExactVault,
43
43
  validateSearchQuery
44
- } from "./index-aer0jdrq.js";
44
+ } from "./index-ahyhryb8.js";
45
45
  import {
46
46
  scanVault
47
- } from "./index-8v6k9h4r.js";
47
+ } from "./index-1s8mc4hz.js";
48
48
  import {
49
49
  NavigationBudgetError,
50
50
  navigateLinks
@@ -54,10 +54,321 @@ import {
54
54
  } from "./index-48pz4jpc.js";
55
55
  import {
56
56
  lookupNote
57
- } from "./index-zy7an84p.js";
57
+ } from "./index-jvb7w0gg.js";
58
58
 
59
59
  // src/sdk.ts
60
60
  import { resolve } from "path";
61
+
62
+ // src/selected-passage.ts
63
+ import { createHash } from "crypto";
64
+ var SELECTED_PASSAGE_MAX_BYTES = 512;
65
+ var SELECTED_PASSAGE_MAX_HEADING_BYTES = 256;
66
+ var SELECTED_PASSAGE_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
67
+ var SELECTED_PASSAGE_MAX_SEARCH_BYTES = 8 * 1024 * 1024;
68
+ var MAX_QUERY_BYTES = 16 * 1024;
69
+ var MAX_QUERY_TERMS = 64;
70
+ var MAX_CANDIDATES = 1e4;
71
+ var WORD = /[\p{L}\p{N}][\p{L}\p{N}\p{M}]*/gu;
72
+ var STOP_WORDS = new Set(("a an and are as at be been being but by can could did do does doing each either " + "for from had has have having he her here hers herself him himself his how i " + "if in into is it its itself may me might mine more most must my myself neither " + "no nor not of on once only or our ours ourselves out own same she should so " + "some such than that the their theirs them themselves then there these they " + "this those through to too under until up us very was we were what when where " + "which while who whom why will with would you your yours yourself yourselves").split(" "));
73
+ var SELECTED_PASSAGE_NONE_REASONS = Object.freeze([
74
+ "no-query-terms",
75
+ "no-lexical-match"
76
+ ]);
77
+ var SELECTED_PASSAGE_UNAVAILABLE_REASONS = Object.freeze([
78
+ "query-limit",
79
+ "source-limit",
80
+ "search-budget",
81
+ "candidate-limit",
82
+ "grapheme-limit",
83
+ "invalid-source",
84
+ "display-sanitized"
85
+ ]);
86
+ function createSelectedPassageBudget(maximumBytes = SELECTED_PASSAGE_MAX_SEARCH_BYTES) {
87
+ if (!Number.isSafeInteger(maximumBytes) || maximumBytes < 0) {
88
+ throw new RangeError("Selected passage budget must be a non-negative safe integer.");
89
+ }
90
+ let remaining = maximumBytes;
91
+ return Object.freeze({
92
+ take: (bytes) => {
93
+ if (bytes > remaining)
94
+ return false;
95
+ remaining -= bytes;
96
+ return true;
97
+ }
98
+ });
99
+ }
100
+ function unavailable(reason) {
101
+ return Object.freeze({ status: "unavailable", reason });
102
+ }
103
+ function none(reason) {
104
+ return Object.freeze({ status: "none", reason });
105
+ }
106
+ function normalize(value) {
107
+ return value.normalize("NFC").toLocaleLowerCase("en-US");
108
+ }
109
+ function wellFormed(value) {
110
+ return !/\p{Surrogate}/u.test(value);
111
+ }
112
+ function lowerBound(values, target) {
113
+ let low = 0;
114
+ let high = values.length;
115
+ while (low < high) {
116
+ const middle = low + high >>> 1;
117
+ if (values[middle] < target)
118
+ low = middle + 1;
119
+ else
120
+ high = middle;
121
+ }
122
+ return low;
123
+ }
124
+ function upperBound(values, target) {
125
+ let low = 0;
126
+ let high = values.length;
127
+ while (low < high) {
128
+ const middle = low + high >>> 1;
129
+ if (values[middle] <= target)
130
+ low = middle + 1;
131
+ else
132
+ high = middle;
133
+ }
134
+ return low;
135
+ }
136
+ function sourceLayout(text) {
137
+ const lineStarts = [0];
138
+ for (let index = text.indexOf(`
139
+ `);index >= 0; index = text.indexOf(`
140
+ `, index + 1)) {
141
+ lineStarts.push(index + 1);
142
+ }
143
+ const lineEnd = (index) => {
144
+ const next = lineStarts[index + 1];
145
+ let end = next === undefined ? text.length : next - 1;
146
+ if (end > lineStarts[index] && text[end - 1] === "\r")
147
+ end--;
148
+ return end;
149
+ };
150
+ let firstBodyLine = 0;
151
+ if (text.slice(0, lineEnd(0)).trim() === "---") {
152
+ for (let index = 1;index < lineStarts.length; index++) {
153
+ if (text.slice(lineStarts[index], lineEnd(index)).trim() === "---") {
154
+ firstBodyLine = index + 1;
155
+ break;
156
+ }
157
+ }
158
+ }
159
+ const bodyStart = lineStarts[firstBodyLine] ?? text.length;
160
+ const paragraphs = [];
161
+ const headings = [];
162
+ let fence;
163
+ let current;
164
+ for (let index = firstBodyLine;index < lineStarts.length; index++) {
165
+ const start = lineStarts[index];
166
+ const end = lineEnd(index);
167
+ const line = text.slice(start, end);
168
+ if (line.trim() === "") {
169
+ if (current !== undefined)
170
+ paragraphs.push(current);
171
+ current = undefined;
172
+ continue;
173
+ }
174
+ const delimiter = /^\s{0,3}(`{3,}|~{3,})/u.exec(line)?.[1];
175
+ const marker = fence === undefined ? /^\s{0,3}(#{1,6})(?:\s|$)/u.exec(line)?.[1] : undefined;
176
+ const heading = marker !== undefined;
177
+ if (heading)
178
+ headings.push({ start, end, level: marker.length, line: index + 1 });
179
+ if (delimiter !== undefined) {
180
+ if (fence === undefined)
181
+ fence = { character: delimiter[0], length: delimiter.length };
182
+ else if (delimiter[0] === fence.character && delimiter.length >= fence.length && line.trim() === delimiter)
183
+ fence = undefined;
184
+ }
185
+ if (current === undefined)
186
+ current = { start, end, headingOnly: heading };
187
+ else {
188
+ current.end = end;
189
+ current.headingOnly &&= heading;
190
+ }
191
+ }
192
+ if (current !== undefined)
193
+ paragraphs.push(current);
194
+ return { bodyStart, lineStarts, paragraphs, headings };
195
+ }
196
+ function graphemePrefix(value, maximumBytes) {
197
+ if (Buffer.byteLength(value, "utf8") <= maximumBytes)
198
+ return { text: value, truncated: false };
199
+ let end = 0;
200
+ let bytes = 0;
201
+ for (const part of new Intl.Segmenter("en-US", { granularity: "grapheme" }).segment(value)) {
202
+ const width = Buffer.byteLength(part.segment, "utf8");
203
+ if (bytes + width > maximumBytes)
204
+ break;
205
+ bytes += width;
206
+ end = part.index + part.segment.length;
207
+ }
208
+ return { text: value.slice(0, end), truncated: true };
209
+ }
210
+ function enclosingHeadings(text, headings, passageStart, passageEnd) {
211
+ const stack = [];
212
+ const pop = (level) => {
213
+ while (stack.length > 0 && stack[stack.length - 1].level >= level)
214
+ stack.pop();
215
+ };
216
+ for (const heading of headings) {
217
+ if (heading.start >= passageEnd)
218
+ break;
219
+ if (heading.start >= passageStart) {
220
+ const before = text.slice(passageStart, heading.start).split(`
221
+ `);
222
+ if (!before.every((line) => line.trim() === "" || /^\s{0,3}#{1,6}(?:\s|$)/u.test(line)))
223
+ break;
224
+ pop(heading.level);
225
+ continue;
226
+ }
227
+ pop(heading.level);
228
+ stack.push(heading);
229
+ }
230
+ return Object.freeze(stack.map((heading) => {
231
+ const end = Math.min(heading.end, passageStart);
232
+ const clipped = graphemePrefix(text.slice(heading.start, end), SELECTED_PASSAGE_MAX_HEADING_BYTES);
233
+ const startByte = Buffer.byteLength(text.slice(0, heading.start), "utf8");
234
+ return Object.freeze({
235
+ level: heading.level,
236
+ text: clipped.text,
237
+ startByte,
238
+ endByte: startByte + Buffer.byteLength(clipped.text, "utf8"),
239
+ startLine: heading.line,
240
+ endLine: heading.line,
241
+ truncated: clipped.truncated || end < heading.end
242
+ });
243
+ }));
244
+ }
245
+ function selectSearchPassage(input) {
246
+ const { query, text, budget } = input;
247
+ if (typeof query !== "string" || Buffer.byteLength(query, "utf8") > MAX_QUERY_BYTES) {
248
+ return unavailable("query-limit");
249
+ }
250
+ if (typeof text !== "string" || !wellFormed(text))
251
+ return unavailable("invalid-source");
252
+ const allTerms = new Set([...normalize(query).matchAll(WORD)].map((match) => match[0]));
253
+ if (allTerms.size > MAX_QUERY_TERMS)
254
+ return unavailable("query-limit");
255
+ const terms = new Set([...allTerms].filter((term) => !STOP_WORDS.has(term)));
256
+ if (terms.size === 0)
257
+ return none("no-query-terms");
258
+ const sourceBytes = Buffer.byteLength(text, "utf8");
259
+ if (sourceBytes > SELECTED_PASSAGE_MAX_SOURCE_BYTES)
260
+ return unavailable("source-limit");
261
+ if (!budget.take(sourceBytes))
262
+ return unavailable("search-budget");
263
+ if (text.length === 0)
264
+ return none("no-lexical-match");
265
+ const maxBytes = SELECTED_PASSAGE_MAX_BYTES;
266
+ const { bodyStart, lineStarts, paragraphs, headings } = sourceLayout(text);
267
+ const headingStarts = new Set(headings.map(({ start }) => start));
268
+ const headingOnlyFile = paragraphs.every((paragraph) => paragraph.headingOnly);
269
+ const positions = [bodyStart];
270
+ const bytePositions = [Buffer.byteLength(text.slice(0, bodyStart), "utf8")];
271
+ let bytes = bytePositions[0];
272
+ for (const part of new Intl.Segmenter("en-US", { granularity: "grapheme" }).segment(text.slice(bodyStart))) {
273
+ const width = Buffer.byteLength(part.segment, "utf8");
274
+ if (width > maxBytes)
275
+ return unavailable("grapheme-limit");
276
+ bytes += width;
277
+ positions.push(bodyStart + part.index + part.segment.length);
278
+ bytePositions.push(bytes);
279
+ }
280
+ const tokens = [];
281
+ const tokenStarts = [];
282
+ for (const match of text.slice(bodyStart).matchAll(WORD)) {
283
+ const start = bodyStart + match.index;
284
+ const term = normalize(match[0]);
285
+ const inHeading = headingStarts.has(lineStarts[upperBound(lineStarts, start) - 1]);
286
+ tokenStarts.push(start);
287
+ tokens.push({
288
+ start,
289
+ end: start + match[0].length,
290
+ term: terms.has(term) && (headingOnlyFile || !inHeading) ? term : undefined
291
+ });
292
+ }
293
+ const regions = [];
294
+ let pendingHeading;
295
+ for (const paragraph of paragraphs) {
296
+ if (paragraph.headingOnly) {
297
+ pendingHeading = pendingHeading === undefined ? paragraph : { ...pendingHeading, end: paragraph.end };
298
+ } else {
299
+ regions.push({ start: pendingHeading?.start ?? paragraph.start, end: paragraph.end });
300
+ pendingHeading = undefined;
301
+ }
302
+ }
303
+ if (regions.length === 0 && pendingHeading !== undefined)
304
+ regions.push(pendingHeading);
305
+ let best;
306
+ let considered = 0;
307
+ for (const region of regions) {
308
+ const first = lowerBound(positions, region.start);
309
+ const last = upperBound(positions, region.end) - 1;
310
+ if (first >= last)
311
+ continue;
312
+ let startIndex = first;
313
+ for (;; ) {
314
+ const endIndex = Math.min(last, upperBound(bytePositions, bytePositions[startIndex] + maxBytes) - 1);
315
+ if (endIndex <= startIndex)
316
+ return unavailable("grapheme-limit");
317
+ if (++considered > MAX_CANDIDATES)
318
+ return unavailable("candidate-limit");
319
+ const start = positions[startIndex];
320
+ const end = positions[endIndex];
321
+ const matched = new Set;
322
+ let tokenCount = 0;
323
+ for (let index = lowerBound(tokenStarts, start);index < tokens.length; index++) {
324
+ const token = tokens[index];
325
+ if (token.start >= end)
326
+ break;
327
+ if (token.end > end)
328
+ continue;
329
+ tokenCount++;
330
+ if (token.term !== undefined)
331
+ matched.add(token.term);
332
+ }
333
+ if (matched.size > 0) {
334
+ const score = matched.size + matched.size / (tokenCount + 1) / 10;
335
+ if (best === undefined || score > best.score) {
336
+ best = {
337
+ start,
338
+ end,
339
+ startByte: bytePositions[startIndex],
340
+ endByte: bytePositions[endIndex],
341
+ regionStart: positions[first],
342
+ regionEnd: positions[last],
343
+ score
344
+ };
345
+ }
346
+ }
347
+ if (endIndex === last)
348
+ break;
349
+ const stepped = lowerBound(bytePositions, bytePositions[startIndex] + Math.max(1, Math.floor(maxBytes / 2)));
350
+ const finalStart = lowerBound(bytePositions, bytePositions[last] - maxBytes);
351
+ startIndex = Math.max(startIndex + 1, Math.min(stepped, finalStart));
352
+ }
353
+ }
354
+ if (best === undefined)
355
+ return none("no-lexical-match");
356
+ return Object.freeze({
357
+ status: "selected",
358
+ text: text.slice(best.start, best.end),
359
+ startByte: best.startByte,
360
+ endByte: best.endByte,
361
+ startLine: upperBound(lineStarts, best.start),
362
+ endLine: upperBound(lineStarts, best.end - 1),
363
+ sourceSha256: createHash("sha256").update(text, "utf8").digest("hex"),
364
+ sourceEncoding: "utf8-snapshot",
365
+ clippedStart: best.start > best.regionStart,
366
+ clippedEnd: best.end < best.regionEnd,
367
+ headings: enclosingHeadings(text, headings, best.start, best.end)
368
+ });
369
+ }
370
+
371
+ // src/sdk.ts
61
372
  var MAX_SEARCH_RESULTS = 100;
62
373
  var MAX_SEARCH_CANDIDATES = 500;
63
374
  var DEFAULT_SEARCH_RESULTS = 10;
@@ -332,6 +643,9 @@ async function openKnowledgeBase(options, dependencies = {}) {
332
643
  const { query: requestedQuery } = validateSearchQuery(searchOptions.query);
333
644
  const expansion = searchRules === null ? { request: searchOptions, alias: null } : expandSearchRequest(searchOptions, searchRules);
334
645
  const effectiveOptions = expansion.request;
646
+ if (effectiveOptions.selectedPassage !== undefined && typeof effectiveOptions.selectedPassage !== "boolean") {
647
+ throw new TypeError("Search selectedPassage must be a boolean.");
648
+ }
335
649
  const { query: effectiveQuery } = validateSearchQuery(effectiveOptions.query);
336
650
  const mode = checkedSearchMode(effectiveOptions.mode);
337
651
  const ordering = checkedSearchOrdering(effectiveOptions.ordering);
@@ -561,6 +875,18 @@ async function openKnowledgeBase(options, dependencies = {}) {
561
875
  results = Object.freeze(selectedHits.map((hit, index) => hit.rank === index + 1 ? hit : { ...hit, rank: index + 1 }));
562
876
  }
563
877
  }
878
+ if (effectiveOptions.selectedPassage === true) {
879
+ const budget = createSelectedPassageBudget();
880
+ results = Object.freeze(results.map((hit) => {
881
+ const note = notesById.get(hit.id);
882
+ if (note === undefined)
883
+ throw new Error("Selected search hit has no snapshot note.");
884
+ return Object.freeze({
885
+ ...hit,
886
+ selectedPassage: selectSearchPassage({ query: effectiveQuery, text: note.content, budget })
887
+ });
888
+ }));
889
+ }
564
890
  let graph = null;
565
891
  if (graphOptions !== null) {
566
892
  try {
@@ -793,7 +1119,25 @@ Evidence: `))
793
1119
 
794
1120
  `))
795
1121
  return writer.result();
796
- if (!append(hit.snippet))
1122
+ const selected = selectedPassageForContext(hit, `Knowledge-base search result ${hit.rank}`);
1123
+ if (selected?.status === "selected") {
1124
+ for (const heading of selected.headings) {
1125
+ if (!append(`Section (level ${heading.level}, lines ${heading.startLine}-${heading.endLine}${heading.truncated ? ", shortened" : ""}):
1126
+ `))
1127
+ return writer.result();
1128
+ if (!append(heading.text))
1129
+ return writer.result();
1130
+ if (!append(`
1131
+
1132
+ `))
1133
+ return writer.result();
1134
+ }
1135
+ if (!append(`Selected source (UTF-8 snapshot ${selected.sourceSha256}; bytes ${selected.startByte}-${selected.endByte}; lines ${selected.startLine}-${selected.endLine}${selected.clippedStart || selected.clippedEnd ? "; excerpt clipped" : ""}):
1136
+ `))
1137
+ return writer.result();
1138
+ if (!append(selected.text))
1139
+ return writer.result();
1140
+ } else if (!append(hit.snippet))
797
1141
  return writer.result();
798
1142
  }
799
1143
  const graph = result.graph;
@@ -954,6 +1298,95 @@ function optionalDataNumber(record, key, label) {
954
1298
  return;
955
1299
  return dataNumber(record[key], `${label}.${key}`);
956
1300
  }
1301
+ function dataCount(value, label, minimum = 0) {
1302
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum) {
1303
+ throw new TypeError(`${label} must be an integer of at least ${minimum}.`);
1304
+ }
1305
+ return value;
1306
+ }
1307
+ function dataSourceText(value, label, maximumBytes) {
1308
+ const text = dataString(value, label);
1309
+ if (/\p{Surrogate}/u.test(text))
1310
+ throw new TypeError(`${label} must be well-formed Unicode.`);
1311
+ if (Buffer.byteLength(text, "utf8") > maximumBytes) {
1312
+ throw new RangeError(`${label} exceeds ${maximumBytes} UTF-8 bytes.`);
1313
+ }
1314
+ return text;
1315
+ }
1316
+ function sourceSpan(record, label, text) {
1317
+ const startByte = dataCount(requiredData(record, "startByte", label), `${label}.startByte`);
1318
+ const endByte = dataCount(requiredData(record, "endByte", label), `${label}.endByte`);
1319
+ const startLine = dataCount(requiredData(record, "startLine", label), `${label}.startLine`, 1);
1320
+ const endLine = dataCount(requiredData(record, "endLine", label), `${label}.endLine`, startLine);
1321
+ if (endByte - startByte !== Buffer.byteLength(text, "utf8")) {
1322
+ throw new TypeError(`${label} byte span must match its text.`);
1323
+ }
1324
+ return { startByte, endByte, startLine, endLine };
1325
+ }
1326
+ function selectedPassageData(value, label) {
1327
+ const record = inspectedDataRecord(value, label);
1328
+ const status = requiredData(record, "status", label);
1329
+ if (status === "none" || status === "unavailable") {
1330
+ const reason = requiredData(record, "reason", label);
1331
+ const reasons = status === "none" ? SELECTED_PASSAGE_NONE_REASONS : SELECTED_PASSAGE_UNAVAILABLE_REASONS;
1332
+ if (typeof reason !== "string" || !reasons.includes(reason)) {
1333
+ throw new TypeError(`${label}.reason is not a known ${status} reason.`);
1334
+ }
1335
+ return Object.freeze({ status, reason });
1336
+ }
1337
+ if (status !== "selected")
1338
+ throw new TypeError(`${label}.status is not a known selected-passage status.`);
1339
+ const text = dataSourceText(requiredData(record, "text", label), `${label}.text`, SELECTED_PASSAGE_MAX_BYTES);
1340
+ if (text === "")
1341
+ throw new TypeError(`${label}.text must not be empty.`);
1342
+ const span = sourceSpan(record, label, text);
1343
+ const sourceSha256 = dataString(requiredData(record, "sourceSha256", label), `${label}.sourceSha256`);
1344
+ if (!/^[0-9a-f]{64}$/u.test(sourceSha256))
1345
+ throw new TypeError(`${label}.sourceSha256 must be a SHA-256 digest.`);
1346
+ if (requiredData(record, "sourceEncoding", label) !== "utf8-snapshot") {
1347
+ throw new TypeError(`${label}.sourceEncoding must be utf8-snapshot.`);
1348
+ }
1349
+ let previousLevel = 0;
1350
+ let previousEnd = 0;
1351
+ const headings = inspectedDataArray(requiredData(record, "headings", label), `${label}.headings`, 6).map((entry, index) => {
1352
+ const headingLabel = `${label}.headings[${index}]`;
1353
+ const heading = inspectedDataRecord(entry, headingLabel);
1354
+ const level = dataCount(requiredData(heading, "level", headingLabel), `${headingLabel}.level`, previousLevel + 1);
1355
+ if (level > 6)
1356
+ throw new TypeError(`${headingLabel}.level must be at most 6.`);
1357
+ const headingText = dataSourceText(requiredData(heading, "text", headingLabel), `${headingLabel}.text`, SELECTED_PASSAGE_MAX_HEADING_BYTES);
1358
+ const headingSpan = sourceSpan(heading, headingLabel, headingText);
1359
+ if (headingSpan.startByte < previousEnd || headingSpan.endByte > span.startByte) {
1360
+ throw new TypeError(`${headingLabel} must precede the selected passage in source order.`);
1361
+ }
1362
+ previousLevel = level;
1363
+ previousEnd = headingSpan.endByte;
1364
+ return Object.freeze({
1365
+ level,
1366
+ text: headingText,
1367
+ ...headingSpan,
1368
+ truncated: dataBoolean(requiredData(heading, "truncated", headingLabel), `${headingLabel}.truncated`)
1369
+ });
1370
+ });
1371
+ return Object.freeze({
1372
+ status,
1373
+ text,
1374
+ ...span,
1375
+ sourceSha256,
1376
+ sourceEncoding: "utf8-snapshot",
1377
+ clippedStart: dataBoolean(requiredData(record, "clippedStart", label), `${label}.clippedStart`),
1378
+ clippedEnd: dataBoolean(requiredData(record, "clippedEnd", label), `${label}.clippedEnd`),
1379
+ headings: Object.freeze(headings)
1380
+ });
1381
+ }
1382
+ function selectedPassageForContext(hit, label) {
1383
+ const descriptor = Object.getOwnPropertyDescriptor(hit, "selectedPassage");
1384
+ if (descriptor === undefined)
1385
+ return;
1386
+ if (!("value" in descriptor))
1387
+ throw new TypeError(`${label} contains an accessor property.`);
1388
+ return descriptor.value === undefined ? undefined : selectedPassageData(descriptor.value, `${label}.selectedPassage`);
1389
+ }
957
1390
  function searchEvidenceSummary(value, label) {
958
1391
  const evidence = inspectedDataRecord(value, label);
959
1392
  return Object.freeze({
@@ -966,6 +1399,7 @@ function searchHitContextRecord(value, index) {
966
1399
  const hit = inspectedDataRecord(value, label);
967
1400
  const evidence = inspectedDataArray(requiredData(hit, "evidence", label), `${label}.evidence`, 32).map((entry, evidenceIndex) => searchEvidenceSummary(entry, `${label}.evidence[${evidenceIndex}]`));
968
1401
  const line = optionalDataNumber(hit, "line", label);
1402
+ const selectedPassage = selectedPassageForContext(hit, label);
969
1403
  return Object.freeze({
970
1404
  kind: "search-result",
971
1405
  id: dataString(requiredData(hit, "id", label), `${label}.id`),
@@ -976,7 +1410,8 @@ function searchHitContextRecord(value, index) {
976
1410
  identity: dataBoolean(requiredData(hit, "identity", label), `${label}.identity`),
977
1411
  ...line === undefined ? {} : { line },
978
1412
  evidence: Object.freeze(evidence),
979
- snippet: dataString(requiredData(hit, "snippet", label), `${label}.snippet`)
1413
+ snippet: dataString(requiredData(hit, "snippet", label), `${label}.snippet`),
1414
+ ...selectedPassage === undefined ? {} : { selectedPassage }
980
1415
  });
981
1416
  }
982
1417
  function graphContextRecords(value) {
@@ -7,7 +7,7 @@ import {
7
7
  } from "./index-48pz4jpc.js";
8
8
  import {
9
9
  lookupNote
10
- } from "./index-zy7an84p.js";
10
+ } from "./index-jvb7w0gg.js";
11
11
 
12
12
  // src/search.ts
13
13
  var MAX_EXACT_RESULTS = 500;