@xamukavila/pxpipe 0.8.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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +312 -0
  3. package/bin/cli.js +7 -0
  4. package/dist/core/applicability.d.ts +31 -0
  5. package/dist/core/applicability.js +96 -0
  6. package/dist/core/atlas-gray.d.ts +26 -0
  7. package/dist/core/atlas-gray.js +64 -0
  8. package/dist/core/atlas.d.ts +34 -0
  9. package/dist/core/atlas.js +71 -0
  10. package/dist/core/baseline.d.ts +80 -0
  11. package/dist/core/baseline.js +101 -0
  12. package/dist/core/caveman.d.ts +38 -0
  13. package/dist/core/caveman.js +183 -0
  14. package/dist/core/export.d.ts +128 -0
  15. package/dist/core/export.js +390 -0
  16. package/dist/core/factsheet.d.ts +78 -0
  17. package/dist/core/factsheet.js +216 -0
  18. package/dist/core/gpt-model-profiles.d.ts +60 -0
  19. package/dist/core/gpt-model-profiles.js +161 -0
  20. package/dist/core/history.d.ts +141 -0
  21. package/dist/core/history.js +553 -0
  22. package/dist/core/index.d.ts +8 -0
  23. package/dist/core/index.js +8 -0
  24. package/dist/core/library.d.ts +74 -0
  25. package/dist/core/library.js +133 -0
  26. package/dist/core/measurement.d.ts +22 -0
  27. package/dist/core/measurement.js +213 -0
  28. package/dist/core/openai-history.d.ts +124 -0
  29. package/dist/core/openai-history.js +494 -0
  30. package/dist/core/openai-savings.d.ts +44 -0
  31. package/dist/core/openai-savings.js +75 -0
  32. package/dist/core/openai.d.ts +24 -0
  33. package/dist/core/openai.js +839 -0
  34. package/dist/core/png.d.ts +11 -0
  35. package/dist/core/png.js +132 -0
  36. package/dist/core/proxy.d.ts +81 -0
  37. package/dist/core/proxy.js +730 -0
  38. package/dist/core/render.d.ts +188 -0
  39. package/dist/core/render.js +785 -0
  40. package/dist/core/schema-strip.d.ts +29 -0
  41. package/dist/core/schema-strip.js +160 -0
  42. package/dist/core/tracker.d.ts +154 -0
  43. package/dist/core/tracker.js +216 -0
  44. package/dist/core/transform.d.ts +362 -0
  45. package/dist/core/transform.js +1828 -0
  46. package/dist/core/types.d.ts +77 -0
  47. package/dist/core/types.js +8 -0
  48. package/dist/dashboard/fragments.d.ts +36 -0
  49. package/dist/dashboard/fragments.js +938 -0
  50. package/dist/dashboard/types.d.ts +154 -0
  51. package/dist/dashboard/types.js +3 -0
  52. package/dist/dashboard/vendor.d.ts +3 -0
  53. package/dist/dashboard/vendor.js +6 -0
  54. package/dist/dashboard.d.ts +245 -0
  55. package/dist/dashboard.js +1140 -0
  56. package/dist/export-collect.d.ts +36 -0
  57. package/dist/export-collect.js +59 -0
  58. package/dist/node.d.ts +9 -0
  59. package/dist/node.js +9038 -0
  60. package/dist/sessions.d.ts +172 -0
  61. package/dist/sessions.js +510 -0
  62. package/dist/stats.d.ts +74 -0
  63. package/dist/stats.js +248 -0
  64. package/dist/worker.d.ts +53 -0
  65. package/dist/worker.js +102 -0
  66. package/package.json +96 -0
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Core logic for `pxpipe export` — renders a source text to PNG pages, extracts
3
+ * a verbatim factsheet, builds a manifest and paste-ready prompt, and returns a
4
+ * token-cost report + list of artifacts to write.
5
+ *
6
+ * Pure-ish: no argv, no stdout, no process.exit, no fs calls — all I/O is
7
+ * delegated to the thin CLI runner in src/node.ts.
8
+ */
9
+ import { DENSE_CONTENT_CHARS_PER_IMAGE, DENSE_CONTENT_COLS, MAX_HEIGHT_PX, PAD_X, CELL_W, } from './render.js';
10
+ // Dogfood the public SDK: render via the same `./transform` entry external
11
+ // consumers import (pxpipe-proxy/transform → renderTextToImages), not the
12
+ // internal leaf renderer.
13
+ import { renderTextToImages } from './library.js';
14
+ import { estimateImageCount, ANTHROPIC_PIXELS_PER_TOKEN, IMAGE_COST_SAFETY_MARGIN, REPORT_CHARS_PER_TOKEN } from './transform.js';
15
+ import { openAIVisionTokens } from './openai.js';
16
+ import { factSheetTextFromEntries, extractFactSheetTokensAllPages, extractFactSheetEntriesAllPages, } from './factsheet.js';
17
+ // ---------------------------------------------------------------------------
18
+ // Public constants
19
+ // ---------------------------------------------------------------------------
20
+ export const DEFAULT_EXPORT_MODEL = 'claude-sonnet-4-5';
21
+ // Chars-per-token for the reporting estimate now lives in transform.ts as
22
+ // REPORT_CHARS_PER_TOKEN (single source of truth for all token-estimate constants).
23
+ /** Default column width — dense content mode (312 cols = 1568 px). */
24
+ export const DEFAULT_EXPORT_COLS = DENSE_CONTENT_COLS;
25
+ // ---------------------------------------------------------------------------
26
+ // Glob matching (no external glob library — node:fs only per convention)
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Match a relative file path against a glob pattern.
30
+ * Supported wildcards: `*` (non-separator), `**` (any including `/`), `?` (single non-sep).
31
+ * When the pattern contains no `/`, matching is against the basename only
32
+ * (e.g. `*.ts` matches `src/foo.ts`).
33
+ */
34
+ export function matchGlob(pattern, filePath) {
35
+ const pat = pattern.replace(/\\/g, '/');
36
+ const fp = filePath.replace(/\\/g, '/').replace(/^\.\//, '');
37
+ if (!pat.includes('/')) {
38
+ // No separator → match basename only
39
+ const sep = fp.lastIndexOf('/');
40
+ const basename = sep >= 0 ? fp.slice(sep + 1) : fp;
41
+ return _globTest(pat, basename);
42
+ }
43
+ return _globTest(pat, fp);
44
+ }
45
+ function _globTest(pattern, str) {
46
+ const SPECIAL = new Set([
47
+ '.', '+', '^', '$', '{', '}', '(', ')', '|', '[', ']', '\\',
48
+ ]);
49
+ let re = '';
50
+ let i = 0;
51
+ while (i < pattern.length) {
52
+ const ch = pattern[i];
53
+ if (ch === undefined)
54
+ break;
55
+ const next = pattern[i + 1];
56
+ const nextNext = pattern[i + 2];
57
+ if (ch === '*' && next === '*') {
58
+ if (nextNext === '/') {
59
+ re += '(?:.*/)?';
60
+ i += 3;
61
+ }
62
+ else {
63
+ re += '.*';
64
+ i += 2;
65
+ }
66
+ }
67
+ else if (ch === '*') {
68
+ re += '[^/]*';
69
+ i++;
70
+ }
71
+ else if (ch === '?') {
72
+ re += '[^/]';
73
+ i++;
74
+ }
75
+ else if (SPECIAL.has(ch)) {
76
+ re += '\\' + ch;
77
+ i++;
78
+ }
79
+ else {
80
+ re += ch;
81
+ i++;
82
+ }
83
+ }
84
+ try {
85
+ return new RegExp(`^${re}$`).test(str);
86
+ }
87
+ catch {
88
+ return false;
89
+ }
90
+ }
91
+ /**
92
+ * Decide whether a relative file path should be included given include/exclude glob lists.
93
+ * - Exclude patterns are checked first; any match → exclude.
94
+ * - If include patterns are given, at least one must match.
95
+ * - If no include patterns are given, everything passes (unless excluded).
96
+ */
97
+ export function shouldIncludeFile(filePath, include, exclude) {
98
+ if (exclude.some((pat) => matchGlob(pat, filePath)))
99
+ return false;
100
+ if (include.length > 0)
101
+ return include.some((pat) => matchGlob(pat, filePath));
102
+ return true;
103
+ }
104
+ /**
105
+ * Parse the argv array for the `export` subcommand.
106
+ * Returns a discriminated union so the caller (node.ts) decides
107
+ * whether to exit, print help, or proceed.
108
+ *
109
+ * @param defaultOut Base output directory (default: $TMPDIR or /tmp).
110
+ */
111
+ export function parseExportArgv(argv, defaultOut) {
112
+ const targets = [];
113
+ const include = [];
114
+ const exclude = [];
115
+ let git = false;
116
+ let diff;
117
+ let stdin = false;
118
+ // Locked to the proxy's density — NO CLI knob. Export must render exactly what the
119
+ // proxy ships to the model; the proxy has no width flag, so neither does export.
120
+ const cols = DENSE_CONTENT_COLS;
121
+ let out = defaultOut ??
122
+ (typeof process !== 'undefined'
123
+ ? (process.env['TMPDIR'] ?? process.env['TEMP'] ?? '/tmp')
124
+ : '/tmp');
125
+ let model = DEFAULT_EXPORT_MODEL;
126
+ let json = false;
127
+ let open = false;
128
+ for (let i = 0; i < argv.length; i++) {
129
+ const a = argv[i];
130
+ if (a === undefined)
131
+ break;
132
+ if (a === '-h' || a === '--help') {
133
+ return { kind: 'help' };
134
+ }
135
+ else if (a === '--include' || a === '--exclude') {
136
+ i++;
137
+ const val = argv[i];
138
+ if (val === undefined || val.startsWith('-')) {
139
+ return { kind: 'error', message: `${a} requires a value` };
140
+ }
141
+ if (a === '--include')
142
+ include.push(val);
143
+ else
144
+ exclude.push(val);
145
+ }
146
+ else if (a.startsWith('--include=')) {
147
+ const v = a.slice('--include='.length);
148
+ if (!v)
149
+ return { kind: 'error', message: '--include= requires a non-empty value' };
150
+ include.push(v);
151
+ }
152
+ else if (a.startsWith('--exclude=')) {
153
+ const v = a.slice('--exclude='.length);
154
+ if (!v)
155
+ return { kind: 'error', message: '--exclude= requires a non-empty value' };
156
+ exclude.push(v);
157
+ }
158
+ else if (a === '--git') {
159
+ git = true;
160
+ }
161
+ else if (a === '--diff') {
162
+ i++;
163
+ const val = argv[i];
164
+ if (val === undefined || val.startsWith('-')) {
165
+ return { kind: 'error', message: '--diff requires a value' };
166
+ }
167
+ diff = val;
168
+ }
169
+ else if (a.startsWith('--diff=')) {
170
+ const v = a.slice('--diff='.length);
171
+ if (!v)
172
+ return { kind: 'error', message: '--diff= requires a non-empty value' };
173
+ diff = v;
174
+ }
175
+ else if (a === '--stdin') {
176
+ stdin = true;
177
+ }
178
+ else if (a === '--out') {
179
+ i++;
180
+ const val = argv[i];
181
+ if (val === undefined)
182
+ return { kind: 'error', message: '--out requires a value' };
183
+ out = val;
184
+ }
185
+ else if (a.startsWith('--out=')) {
186
+ const v = a.slice('--out='.length);
187
+ if (!v)
188
+ return { kind: 'error', message: '--out= requires a non-empty value' };
189
+ out = v;
190
+ }
191
+ else if (a === '--model') {
192
+ i++;
193
+ const val = argv[i];
194
+ if (val === undefined)
195
+ return { kind: 'error', message: '--model requires a value' };
196
+ model = val;
197
+ }
198
+ else if (a.startsWith('--model=')) {
199
+ const v = a.slice('--model='.length);
200
+ if (!v)
201
+ return { kind: 'error', message: '--model= requires a non-empty value' };
202
+ model = v;
203
+ }
204
+ else if (a === '--json') {
205
+ json = true;
206
+ }
207
+ else if (a === '--open') {
208
+ open = true;
209
+ }
210
+ else if (a.startsWith('-')) {
211
+ return { kind: 'error', message: `unknown option: ${a}` };
212
+ }
213
+ else {
214
+ targets.push(a);
215
+ }
216
+ }
217
+ return {
218
+ kind: 'opts',
219
+ parsed: { targets, include, exclude, git, diff, stdin, cols, out, model, json, open },
220
+ };
221
+ }
222
+ // ---------------------------------------------------------------------------
223
+ // Model-routing image-token helper
224
+ // ---------------------------------------------------------------------------
225
+ /**
226
+ * Per-image vision-token cost for a rendered PNG at the given pixel dimensions.
227
+ *
228
+ * - **Claude / Anthropic models** (`model.startsWith('claude')` or
229
+ * `model.includes('anthropic')`): uses Anthropic's billing formula
230
+ * `ceil(width × height / 750 × 1.10)` (the same formula and constants as
231
+ * `imageTokensForRows` in transform.ts, reusing the exported
232
+ * `ANTHROPIC_PIXELS_PER_TOKEN` / `IMAGE_COST_SAFETY_MARGIN` consts).
233
+ * - **GPT / o-series models**: delegates to `openAIVisionTokens` which uses the
234
+ * GPT-4o tile-pricing formula (85 + 170 × tiles after scaling).
235
+ */
236
+ export function exportImageTokens(model, width, height) {
237
+ if (model.startsWith('claude') || model.includes('anthropic')) {
238
+ return Math.ceil((width * height / ANTHROPIC_PIXELS_PER_TOKEN) * IMAGE_COST_SAFETY_MARGIN);
239
+ }
240
+ return openAIVisionTokens(model, width, height);
241
+ }
242
+ /**
243
+ * Estimate text vs image token cost for `sourceText` without rendering.
244
+ * Uses the same formula as the internal gate:
245
+ * stripW = 2·PAD_X + cols·CELL_W
246
+ * imageTokens = estimateImageCount(text, cols) × exportImageTokens(model, stripW, MAX_HEIGHT_PX)
247
+ * textTokens = sourceText.length / REPORT_CHARS_PER_TOKEN
248
+ *
249
+ * `exportImageTokens` routes to the Anthropic billing formula (width×height/750×1.10)
250
+ * for Claude models, and to the GPT tile-pricing formula for GPT/o-series models.
251
+ *
252
+ * `factsheetItemCount` is the number of unique precision-critical identifier strings
253
+ * extracted across all pages (paths, SHAs, ids, …); it is NOT an LLM token count.
254
+ * `factsheetDropped` is the count of extracted identifiers that did not fit within
255
+ * the 64-item per-export budget.
256
+ */
257
+ export function computeTokenReport(sourceText, cols, model) {
258
+ const stripW = 2 * PAD_X + cols * CELL_W;
259
+ const estImages = estimateImageCount(sourceText, cols, 1, DENSE_CONTENT_CHARS_PER_IMAGE);
260
+ const perStrip = exportImageTokens(model, stripW, MAX_HEIGHT_PX);
261
+ const imageTokens = Math.round(estImages * perStrip);
262
+ const textTokens = Math.round(sourceText.length / REPORT_CHARS_PER_TOKEN);
263
+ const percentSaved = textTokens > 0
264
+ ? Math.round(((textTokens - imageTokens) / textTokens) * 1000) / 10
265
+ : 0;
266
+ const { kept, dropped } = extractFactSheetTokensAllPages(sourceText, DENSE_CONTENT_CHARS_PER_IMAGE);
267
+ return {
268
+ textTokens,
269
+ imageTokens,
270
+ percentSaved,
271
+ factsheetItemCount: kept.length,
272
+ factsheetDropped: dropped,
273
+ };
274
+ }
275
+ // ---------------------------------------------------------------------------
276
+ // Prompt template
277
+ // ---------------------------------------------------------------------------
278
+ export function buildPromptText(pageCount, factsheet, files, droppedItems = 0) {
279
+ const lastPageStr = String(pageCount).padStart(3, '0');
280
+ const fileSection = files.length > 0
281
+ ? `Source files (${files.length}):\n${files.map((f) => ` ${f}`).join('\n')}\n\n`
282
+ : '';
283
+ const factsheetNote = droppedItems > 0
284
+ ? `2. For any path, file name, identifier, hash, version number, or other precision-critical\n` +
285
+ ` value: check factsheet.txt first — identifiers listed there are verbatim exact.\n` +
286
+ ` Note: ${droppedItems} identifier(s) were extracted but not captured due to the 64-item\n` +
287
+ ` budget — OCR carefully for any exact values not listed in factsheet.txt.\n`
288
+ : `2. For any path, file name, identifier, hash, version number, or other precision-critical\n` +
289
+ ` value: use the verbatim text from factsheet.txt, NOT what you read off the image.\n` +
290
+ ` factsheet.txt is the authoritative source of truth for all exact strings.\n`;
291
+ return (`These ${pageCount} image${pageCount !== 1 ? 's' : ''} contain source code/text rendered as PNG pages by pxpipe.\n\n` +
292
+ fileSection +
293
+ `Instructions for the reading agent:\n` +
294
+ `1. Read the images in order: page-001.png through page-${lastPageStr}.png.\n` +
295
+ factsheetNote +
296
+ `3. Treat the content as if you had read the source files directly.\n\n` +
297
+ `Factsheet (verbatim identifiers — quote from here, not from image pixels):\n` +
298
+ (factsheet || '(none)') +
299
+ '\n');
300
+ }
301
+ // ---------------------------------------------------------------------------
302
+ // Simple hash for output directory naming
303
+ // ---------------------------------------------------------------------------
304
+ /** FNV-1a 32-bit hash — deterministic, no crypto dep. */
305
+ function fnv32a(str) {
306
+ let h = 0x811c9dc5;
307
+ for (let i = 0; i < str.length; i++) {
308
+ h ^= str.charCodeAt(i) & 0xff;
309
+ h = (h * 0x01000193) >>> 0;
310
+ }
311
+ return h;
312
+ }
313
+ /** 8-char hex hash of the first 512 chars + length of `text`. */
314
+ export function sourceShortHash(text) {
315
+ const sample = text.slice(0, 512) + '\x00' + String(text.length);
316
+ return fnv32a(sample).toString(16).padStart(8, '0');
317
+ }
318
+ // ---------------------------------------------------------------------------
319
+ // Core export runner (pure-ish: no fs, no argv, no stdout)
320
+ // ---------------------------------------------------------------------------
321
+ export async function runExportCore(sourceText, opts) {
322
+ const generatedAt = new Date().toISOString();
323
+ const enc = new TextEncoder();
324
+ // Render to PNG pages via the public SDK primitive. shrink=true sizes the canvas
325
+ // to the widest line so short-line code isn't padded to full width; multiCol='auto'
326
+ // packs as many columns side-by-side as fit. Same render surface shipped to external
327
+ // SDK consumers (pxpipe-proxy/transform).
328
+ const { pages: images } = await renderTextToImages(sourceText, {
329
+ cols: opts.cols,
330
+ shrink: true,
331
+ multiCol: 'auto',
332
+ reflow: true,
333
+ });
334
+ // Compute token costs using actual rendered image dimensions (more accurate than estimate).
335
+ // exportImageTokens routes to the Anthropic billing formula for claude-* models and to
336
+ // the GPT tile-pricing formula for GPT/o-series models.
337
+ const textTokens = Math.round(sourceText.length / REPORT_CHARS_PER_TOKEN);
338
+ let imageTokens = 0;
339
+ for (const img of images) {
340
+ imageTokens += exportImageTokens(opts.model, img.width, img.height);
341
+ }
342
+ imageTokens = Math.round(imageTokens);
343
+ const percentSaved = textTokens > 0
344
+ ? Math.round(((textTokens - imageTokens) / textTokens) * 1000) / 10
345
+ : 0;
346
+ // Extract factsheet tokens across ALL pages so identifiers from page 3+ are covered.
347
+ const { kept: fsKept, dropped: fsDropped } = extractFactSheetEntriesAllPages(sourceText, DENSE_CONTENT_CHARS_PER_IMAGE);
348
+ const fsText = factSheetTextFromEntries(fsKept);
349
+ const tokenReport = {
350
+ textTokens,
351
+ imageTokens,
352
+ percentSaved,
353
+ factsheetItemCount: fsKept.length,
354
+ factsheetDropped: fsDropped,
355
+ };
356
+ // Build artifacts
357
+ const artifacts = [];
358
+ const pages = [];
359
+ for (const [i, img] of images.entries()) {
360
+ const filename = `page-${String(i + 1).padStart(3, '0')}.png`;
361
+ artifacts.push({ filename, data: img.png });
362
+ pages.push({
363
+ filename,
364
+ bytes: img.png.byteLength,
365
+ width: img.width,
366
+ height: img.height,
367
+ });
368
+ }
369
+ // factsheet.txt
370
+ artifacts.push({ filename: 'factsheet.txt', data: enc.encode(fsText) });
371
+ // manifest.json
372
+ const manifest = {
373
+ sourceChars: sourceText.length,
374
+ files: opts.sourceFiles,
375
+ pages,
376
+ cols: opts.cols,
377
+ model: opts.model,
378
+ generatedAt,
379
+ tokenReport,
380
+ };
381
+ artifacts.push({
382
+ filename: 'manifest.json',
383
+ data: enc.encode(JSON.stringify(manifest, null, 2)),
384
+ });
385
+ // prompt.txt
386
+ const promptText = buildPromptText(images.length, fsText, opts.sourceFiles, fsDropped);
387
+ artifacts.push({ filename: 'prompt.txt', data: enc.encode(promptText) });
388
+ return { manifest, artifacts };
389
+ }
390
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Verbatim fact-sheet for imaged content.
3
+ *
4
+ * When pxpipe renders a block (system slab, history, tool_result, reminder) to a PNG,
5
+ * the precision-critical, hard-to-OCR strings inside it — file paths, URLs, SHAs/UUIDs,
6
+ * version numbers, CLI flags, large numbers, CONST_IDS — are exactly what a model is
7
+ * most likely to misread off the image yet most likely to need quoted verbatim. This
8
+ * module extracts those tokens so they ride next to the image as plain text: the model
9
+ * quotes them without re-reading the PNG, and they stay in the cached prefix.
10
+ *
11
+ * Deterministic by construction (fixed pattern order, length-desc/lexical sort, no
12
+ * Date/random) → the emitted text is byte-stable across turns and never busts the
13
+ * Anthropic prompt cache. Empirically ~5% of source chars on production history
14
+ * (median 4.9%, max 12.1%, N=10), which preserves the imaging token win.
15
+ */
16
+ /** Budget cap: highest-priority tokens kept first. Exported so consumers can report drops. */
17
+ export declare const MAX_TOKENS = 64;
18
+ /**
19
+ * Extract deduped, precision-critical tokens from `text`. Substrings of a longer kept
20
+ * token are dropped (so `/github.com` inside the full URL, `lib/x.ts` inside
21
+ * `src/lib/x.ts`, etc. collapse to the most specific form); the 64-token budget is then
22
+ * filled by priority tier (see `priorityTier`) so short, high-consequence tokens are never
23
+ * evicted by long low-risk URLs.
24
+ *
25
+ * Every token class is whitespace-free, so we split on whitespace first and skip
26
+ * blob-length chunks. That bounds each regex to a short chunk and keeps extraction
27
+ * strictly O(n) — no quadratic backtracking on delimiter-heavy input like base64 or
28
+ * minified bundles (which embed `/` and would otherwise make the path patterns blow up).
29
+ */
30
+ export declare function extractFactSheetTokens(text: string): string[];
31
+ /** A kept fact-sheet token plus how many times it occurs in the scanned text.
32
+ * Counts are advisory (occurrences, not lines) but deterministic → cache-stable. */
33
+ export interface FactSheetEntry {
34
+ readonly token: string;
35
+ readonly count: number;
36
+ }
37
+ /**
38
+ * Like `extractFactSheetTokens`, but each kept token carries its occurrence count.
39
+ * Counts make the fact sheet a *quantitative* index: tally questions over imaged
40
+ * content ("how many lines mention CODE-X?") become answerable from text instead
41
+ * of from counting rows of 5×8 px glyphs — the one operation page images are worst
42
+ * at. The kept-token SET and its order are byte-identical to the pre-count
43
+ * behaviour; only counts are new. Same-token spans matched by two patterns are
44
+ * deduped by offset so a token is never double-counted.
45
+ */
46
+ export declare function extractFactSheetEntries(text: string): FactSheetEntry[];
47
+ /**
48
+ * Page-aware variant of `extractFactSheetTokens` for large source texts.
49
+ *
50
+ * Splits `text` into chunks of `charsPerPage` (use `DENSE_CONTENT_CHARS_PER_IMAGE`
51
+ * from render.ts for the export pipeline), calls `extractFactSheetTokens` on each
52
+ * chunk (each chunk is smaller than MAX_SCAN so no truncation occurs), merges the
53
+ * results across all chunks with first-seen deduplication, then applies a single
54
+ * global priority-budget pass to select the best MAX_TOKENS identifiers.
55
+ *
56
+ * Returns `{ kept, dropped }` where `dropped` is the count of identifiers that
57
+ * survived extraction across all pages but did not fit in the MAX_TOKENS budget.
58
+ *
59
+ * Does NOT mutate the behaviour of `extractFactSheetTokens` or `factSheetText`.
60
+ */
61
+ export declare function extractFactSheetTokensAllPages(text: string, charsPerPage: number): {
62
+ kept: string[];
63
+ dropped: number;
64
+ };
65
+ /** Entry-carrying variant of `extractFactSheetTokensAllPages`: same kept set and
66
+ * order, with per-token occurrence counts summed across all pages. */
67
+ export declare function extractFactSheetEntriesAllPages(text: string, charsPerPage: number): {
68
+ kept: FactSheetEntry[];
69
+ dropped: number;
70
+ };
71
+ /** Build the one-line fact-sheet string from a pre-extracted token list. */
72
+ export declare function factSheetTextFromTokens(tokens: string[]): string;
73
+ /** Build the one-line fact-sheet string from token+count entries. Byte-identical to
74
+ * `factSheetTextFromTokens` when no token repeats, so existing sheets stay cache-stable. */
75
+ export declare function factSheetTextFromEntries(entries: readonly FactSheetEntry[]): string;
76
+ /** One-line fact-sheet string for `text`, or `''` when nothing notable was found. */
77
+ export declare function factSheetText(text: string): string;
78
+ //# sourceMappingURL=factsheet.d.ts.map