@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,362 @@
1
+ /**
2
+ * Request-body transformer. Extracts the static system prompt + tool definitions,
3
+ * renders them as PNG image blocks, and rewrites the body to reference those images —
4
+ * saving 65-73% input tokens while preserving reasoning quality.
5
+ */
6
+ import type { ImageBlock, MessagesRequest } from './types.js';
7
+ import type { GptHistoryOptions } from './openai-history.js';
8
+ /** Per-block descriptor passed to `TransformOptions.keepSharp`. */
9
+ export interface KeepSharpBlock {
10
+ /** Which live-region path is asking: `reminder`, `tool_result`, or `tool_result_part`. */
11
+ readonly kind: 'reminder' | 'tool_result' | 'tool_result_part';
12
+ /** The block's text exactly as the caller produced it (pre-render, pre-compaction). */
13
+ readonly text: string;
14
+ /** `tool_use_id` of the owning tool_result, when applicable. */
15
+ readonly toolUseId?: string;
16
+ }
17
+ /** A block pxpipe rendered to image(s), returned in `TransformInfo.recoverable`
18
+ * when the caller sets `emitRecoverable`. Lets a stateful harness restore
19
+ * byte-exact content if the model needs the imaged region verbatim. */
20
+ export interface RecoverableBlock {
21
+ /** `rec_` + 8 hex SHA-256 over kind + toolUseId + original text. */
22
+ readonly id: string;
23
+ readonly kind: 'reminder' | 'tool_result' | 'tool_result_part';
24
+ readonly toolUseId?: string;
25
+ /** Original text before compaction/reflow/paging — the bytes to restore. */
26
+ readonly text: string;
27
+ readonly imageCount: number;
28
+ }
29
+ export interface TransformOptions {
30
+ /** Master switch — false makes this a no-op pass-through. */
31
+ compress?: boolean;
32
+ /** Move tool descriptions into the same image (and stub the originals). */
33
+ compressTools?: boolean;
34
+ /** Compress large `<system-reminder>` text blocks in the first user message. */
35
+ compressReminders?: boolean;
36
+ /** Compress large tool_result text content across all user messages. */
37
+ compressToolResults?: boolean;
38
+ /** Don't compress if total compressible chars below this. */
39
+ minCompressChars?: number;
40
+ /** Per-block threshold for compressReminders (chars). */
41
+ minReminderChars?: number;
42
+ /** Per-block threshold for compressToolResults (chars). */
43
+ minToolResultChars?: number;
44
+ /** Soft-wrap column count. */
45
+ cols?: number;
46
+ /** Hard upper bound on images per tool_result; source text truncated with a paging
47
+ * marker above this to stay under Anthropic's 100-image/request cap. Default 10. */
48
+ maxImagesPerToolResult?: number;
49
+ /** Pack N text columns side-by-side per image. Default 1. Auto-clamped to stay
50
+ * under 2000 px wide. OCR ordering risk at N≥2: model must read col 1 top-to-bottom
51
+ * before col 2. */
52
+ multiCol?: number;
53
+ /** Chars-per-token assumption for `isCompressionProfitable()`. Default 4. */
54
+ charsPerToken?: number;
55
+ /** Multi-turn amortization horizon for the history-collapse gate. N≥2 evaluates as
56
+ * if N future turns share the prefix (worst-case-warm-image vs best-case-warm-text).
57
+ * Default 1 (per-turn cold gate). See docs/HISTORY_CACHE_MODEL.md. */
58
+ historyAmortizationHorizon?: number;
59
+ /** Tokens the un-rewritten path would have cache-hit on. Adds a one-time burn
60
+ * penalty `priorWarmTokens × (CC − CR)` to the image side so the gate accounts
61
+ * for invalidating a warm text cache. Default 0 (cold-start). ≤0 clamped to 0. */
62
+ priorWarmTokens?: number;
63
+ /** Symmetric counterpart: tokens the image path would have cache-hit on. Adds the
64
+ * same burn formula to the TEXT side, preventing the gate from flipping out of
65
+ * image mode when the image prefix is already warm. Default 0. ≤0 clamped to 0. */
66
+ priorWarmImageTokens?: number;
67
+ /** GPT only: collapse the OLD closed-tool-call conversation prefix into history
68
+ * image(s), keeping the recent tail as text. Independent of the static slab.
69
+ * Default on. See src/core/openai-history.ts. */
70
+ collapseHistory?: boolean;
71
+ /** GPT only: history-collapse tuning overrides (keepTail / collapseChunk / …). */
72
+ gptHistory?: Partial<GptHistoryOptions>;
73
+ /** Re-pack image-bound text into a ↵-delimited stream to fill `cols` (~29%→75-80%
74
+ * glyph-fill). ON by default (98.95% char accuracy at L1 OCR eval, +1pp vs baseline).
75
+ * Hard newlines become visible ↵ glyphs — tell the model via system prompt. */
76
+ reflow?: boolean;
77
+ /** Caller fidelity hint: return `true` for a block that must stay as text (IDs,
78
+ * hashes, file paths — content where mis-OCR would be silent and wrong). Only
79
+ * consulted on per-block live-region paths (reminders, tool_results). A throwing
80
+ * or non-boolean return is treated as `false`. */
81
+ keepSharp?: (block: KeepSharpBlock) => boolean;
82
+ /** When true, populate `TransformInfo.recoverable` with original text + provenance
83
+ * for every block rendered to images. Off by default (entries inflate `info`;
84
+ * only a stateful harness can use them). */
85
+ emitRecoverable?: boolean;
86
+ /** EXPERIMENT (`PXPIPE_CAVEMAN=1`): deterministic rule-based prose compression
87
+ * (drop EN/PT articles + filler adverbs, rewrite politeness phrases) on
88
+ * image-bound text BEFORE compaction/reflow — fewer chars → fewer pixels →
89
+ * fewer image tokens. Applies to the static slab and reminder blocks when
90
+ * they classify as prose ('other'); tool_results are out of scope for v1
91
+ * (mostly logs/code — the classify gate would skip them anyway). Never
92
+ * touches code, table rows, quoted spans, or non-alphabetic tokens
93
+ * (src/core/caveman.ts). Off by default until the A/B shows gist/verbatim
94
+ * recall holds — see FINDINGS.md on redundancy as the channel's ECC. */
95
+ caveman?: boolean;
96
+ }
97
+ /** Empirical cpt for the system-slab path (Opus 4.7 tokenizer, N=391, observed 1.91).
98
+ * Slab-specific because reminders/tool_results have unknown shape; those stay at 4. */
99
+ export declare const SLAB_CHARS_PER_TOKEN = 2;
100
+ /** Empirical cpt for the history-collapse path (same Opus 4.7 telemetry as SLAB_CHARS_PER_TOKEN).
101
+ * History is even denser (tool_use JSON dominates), so 2.0 is doubly conservative. */
102
+ export declare const HISTORY_CHARS_PER_TOKEN = 2;
103
+ /** Chars-per-token for the `pxpipe export` *reporting* estimate (factsheet & savings %).
104
+ * Less conservative than the gate's CHARS_PER_TOKEN=4: reporting wants an accurate
105
+ * figure (~3.7 for source/prose text), not a safe-side under-estimate. Single source
106
+ * of truth — src/core/export.ts imports this rather than redefining it. */
107
+ export declare const REPORT_CHARS_PER_TOKEN = 3.7;
108
+ /** Anthropic image-billing formula: `tokens ≈ width × height / 750`.
109
+ * https://docs.anthropic.com/en/docs/build-with-claude/vision#image-tokens
110
+ * Accurate to ~5% on dense glyph PNGs (N=14 empirical calibration). The renderer
111
+ * sizes height to content, so per-block images cost far less than full-canvas.
112
+ * Exported so the export pipeline can reuse the same constant rather than hardcoding. */
113
+ export declare const ANTHROPIC_PIXELS_PER_TOKEN = 750;
114
+ /** Conservative 10% upward bias on Anthropic image token estimates — keeps the gate
115
+ * on the safe (pass-through) side when the true cost is near the break-even point.
116
+ * Exported so the export pipeline reuses the same value. */
117
+ export declare const IMAGE_COST_SAFETY_MARGIN = 1.1;
118
+ /** Visual rows per image: `floor((MAX_HEIGHT_PX − 2·PAD_Y) / CELL_H)`. Derived
119
+ * from render.ts constants so break-even math auto-tracks cell geometry changes. */
120
+ export declare const LINES_PER_IMAGE: number;
121
+ export declare function maxCharsPerImage(cols: number): number;
122
+ /** Lossless pre-render whitespace compactor (each `\n` costs ≥1 visual row):
123
+ * 1. Strip trailing whitespace per line (preserves leading indent).
124
+ * 2. Collapse 3+ consecutive newlines to 2. Typically saves 10-25% rows on
125
+ * markdown/tool-doc slabs, enough to flip borderline gates to profitable. */
126
+ export declare function compactSlabWhitespace(text: string): string;
127
+ /** Decompose the break-even gate into components for telemetry. Returns the
128
+ * imageTokens, textTokens, and symmetric burn terms the gate uses internally,
129
+ * or `null` for empty/non-finite input. */
130
+ export declare function evalCompressionProfitability(text: string, cols: number, imageCountCap?: number | undefined, numCols?: number, charsPerToken?: number, priorWarmTokens?: number, priorWarmImageTokens?: number, shrinkWidth?: boolean): {
131
+ imageTokens: number;
132
+ textTokens: number;
133
+ burnImageSide: number;
134
+ burnTextSide: number;
135
+ profitable: boolean;
136
+ } | null;
137
+ export declare function isCompressionProfitable(text: string, cols?: number, imageCountCap?: number, numCols?: number, charsPerToken?: number, priorWarmTokens?: number, priorWarmImageTokens?: number, shrinkWidth?: boolean, maxCharsPerImage?: number): boolean;
138
+ /**
139
+ * Horizon-aware variant of `isCompressionProfitable` for history-collapse.
140
+ *
141
+ * Evaluates expected lifetime cost over N turns: worst-case-warm for image
142
+ * (cache_create turn 1, cache_read turns 2..N) vs best-case-warm for text
143
+ * (cache_read all N). Gate condition: I×(CC + CR×(N-1)) < T×CR×N.
144
+ * Examples: N=5 → I < 0.30×T; N=10 → I < 0.47×T.
145
+ * Falls back to cold per-turn gate when `horizon <= 1`. See docs/HISTORY_CACHE_MODEL.md.
146
+ */
147
+ export declare function isCompressionProfitableAmortized(text: string, cols: number, imageCountCap: number | undefined, numCols: number, charsPerToken: number, horizon: number, priorWarmTokens?: number, priorWarmImageTokens?: number, shrinkWidth?: boolean, maxCharsPerImage?: number): boolean;
148
+ /** Logical bucket for per-gate-call char attribution. Used by the rolling-cpt
149
+ * regression to derive per-bucket marginal cpt from production telemetry. */
150
+ export type BucketName = 'static_slab' | 'reminder' | 'tool_result_json' | 'tool_result_log' | 'tool_result_prose' | 'history';
151
+ /** Pre-compaction TEXT char totals per bucket. Absent when no bucket fired. */
152
+ export type BucketChars = Partial<Record<BucketName, number>>;
153
+ /** Parsed contents of Claude Code's <env> + git status blocks. All optional —
154
+ * fields are only populated if the corresponding line is present. */
155
+ export interface EnvFields {
156
+ /** Working directory at the time `claude` was launched. */
157
+ cwd?: string;
158
+ isGitRepo?: boolean;
159
+ /** Current git branch, parsed from <git_status> or a "Branch:" line. */
160
+ gitBranch?: string;
161
+ platform?: string;
162
+ osVersion?: string;
163
+ /** "Today's date" as Claude Code reported it (YYYY-MM-DD). */
164
+ today?: string;
165
+ }
166
+ export interface TransformInfo {
167
+ compressed: boolean;
168
+ reason?: string;
169
+ origChars: number;
170
+ /** Total source chars image-encoded this request (static slab + reminders + tool_results).
171
+ * Unlike `origChars` (static slab + tool docs only), reflects what `imageCount` replaced. */
172
+ compressedChars: number;
173
+ /** Chars removed by the opt-in caveman prose pass (`TransformOptions.caveman`).
174
+ * Reported as a separate delta; `origChars`/`compressedChars` stay anchored
175
+ * to the RAW source lengths so savings baselines remain comparable across
176
+ * caveman on/off runs. */
177
+ cavemanChars?: number;
178
+ imageCount: number;
179
+ imageBytes: number;
180
+ /** Σ width×height across all rendered images. Pairs with upstream token count for
181
+ * empirical px/token regression: `tokens ≈ α·outgoingTextChars + β·imagePixels`. */
182
+ imagePixels?: number;
183
+ /** GPT only. Vision tokens the rendered images actually cost as input
184
+ * (Σ openAIVisionTokens over real image dims). The "Sent as image" basis. */
185
+ imageTokens?: number;
186
+ /** GPT only. o200k_base text tokens of the content pxpipe imaged/stripped —
187
+ * the would-have-paid "as plain text" baseline. Compared against imageTokens
188
+ * for the per-request saving. See src/core/openai-savings.ts. */
189
+ baselineImagedTokens?: number;
190
+ /** Total TEXT chars in the outgoing body (system + messages, excluding image base64).
191
+ * Denominator for empirical chars-per-token regression on cold-miss events. */
192
+ outgoingTextChars?: number;
193
+ /** Length of the static (cacheable) slab rendered into the image. */
194
+ staticChars: number;
195
+ /** Length of the dynamic (per-turn) slab kept as plain text. */
196
+ dynamicChars: number;
197
+ /** Chars of volatile env/context text relocated from system to the tail of
198
+ * the last user message (absent when kept in system fallback). */
199
+ envRelocatedChars?: number;
200
+ dynamicBlockCount: number;
201
+ /** Tag-shaped blocks in the static slab not in DYNAMIC_BLOCK_TAGS.
202
+ * Canary: a new per-turn Claude Code tag would appear here before cache rate collapses. */
203
+ unknownStaticTags?: string[];
204
+ /** Static-slab tags whose content changed within a session — proven dynamic,
205
+ * busting the image cache each turn. The real alert signal. */
206
+ churningStaticTags?: string[];
207
+ env?: EnvFields;
208
+ /** sha8 of static slab + tool docs (what goes in the image). Repeats across turns → cache hits. */
209
+ systemSha8?: string;
210
+ /** sha8 of the CLAUDE.md section, for bucketing by project when cwd is absent. */
211
+ claudeMdSha8?: string;
212
+ /** sha8 of first user message text (first 4 KiB). Rough thread/session id. */
213
+ firstUserSha8?: string;
214
+ /** Raw bytes of the first rendered image. Dashboard preview only; NOT persisted to JSONL. */
215
+ firstImagePng?: Uint8Array;
216
+ firstImageWidth?: number;
217
+ firstImageHeight?: number;
218
+ /** All rendered PNGs this request. Dashboard only; NOT persisted to JSONL. */
219
+ imagePngs?: Uint8Array[];
220
+ imageDims?: Array<{
221
+ width: number;
222
+ height: number;
223
+ }>;
224
+ /** Source text rendered to images (slab + header), capped at 64 KiB. NOT persisted. */
225
+ imageSourceText?: string;
226
+ reminderImgs?: number;
227
+ toolResultImgs?: number;
228
+ /** Chars of tool docs moved to the system-text Tool Reference (not imaged). */
229
+ toolDocsChars?: number;
230
+ /** Codepoints missing from the atlas (rendered as blank cells). Telemetry for atlas tuning. */
231
+ droppedChars?: number;
232
+ /** Top dropped codepoints by frequency (`U+HHHH` → count), at most 20 entries. */
233
+ droppedCodepointsTop?: Record<string, number>;
234
+ /** Why blocks passed through without compression. Only present when count > 0. */
235
+ passthroughReasons?: {
236
+ below_threshold?: number;
237
+ not_profitable?: number;
238
+ kept_sharp?: number;
239
+ };
240
+ /** Slab gate diagnostics — imageTokens, textTokens, burn terms, and verdict.
241
+ * Lets hosts measure flap-prevention efficacy and tune amortization horizon. */
242
+ gateEval?: {
243
+ readonly site: 'slab';
244
+ readonly imageTokens: number;
245
+ readonly textTokens: number;
246
+ /** `priorWarmTokens × (CC − CR)` added to image side. */
247
+ readonly burnImageSide: number;
248
+ /** `priorWarmImageTokens × (CC − CR)` added to text side (anti-flapping anchor). */
249
+ readonly burnTextSide: number;
250
+ readonly profitable: boolean;
251
+ };
252
+ /** Pre-compaction TEXT char totals per gate-call bucket. Rolling-cpt regression denominator. */
253
+ bucketChars?: BucketChars;
254
+ /** Chars fed into the history-image renderer. Folded into `bucketChars.history` too. */
255
+ historyTextChars?: number;
256
+ /** Blocks pinned as text by the caller's `keepSharp` predicate this request. */
257
+ keptSharpBlocks?: number;
258
+ /** Imaged live-region blocks with original text + provenance, when `emitRecoverable`. */
259
+ recoverable?: RecoverableBlock[];
260
+ truncatedToolResults?: number;
261
+ omittedChars?: number;
262
+ /** History-collapse: messages collapsed into the synthetic prepended user message. */
263
+ collapsedTurns?: number;
264
+ collapsedChars?: number;
265
+ /** History-collapse images. Also folded into `info.imageCount`. */
266
+ collapsedImages?: number;
267
+ /** sha8 of concatenated history-image base64. Stable across the collapse window →
268
+ * proves Anthropic's prompt cache can `cache_read` (0.1×) instead of `cache_create`.
269
+ * A changing hash means cache-key drift is back. Only set when collapse produced images. */
270
+ historyImageSha?: string;
271
+ /** sha8 of the ACTUAL cacheable prefix sent this turn (tools + system +
272
+ * message blocks through the imaged history/slab boundary; the live tail is
273
+ * excluded). Read-only measurement. A change turn-over-turn within a session
274
+ * ⇒ pxpipe serialized different prefix bytes (we busted our own cache,
275
+ * pxpipe-side); STABLE while cache_create spikes / cache_read collapses ⇒ the
276
+ * prefix was evicted upstream. Decisive attribution signal (see #11). */
277
+ cachePrefixSha8?: string;
278
+ /** Approx size (chars) of that cached prefix — pairs with cachePrefixSha8 so a
279
+ * bust reads as growth (size up) vs pure invalidation (size unchanged). */
280
+ cachePrefixBytes?: number;
281
+ /** Why the history collapse didn't run (or did). Diagnostic only. */
282
+ historyReason?: 'no_history' | 'prefix_too_short' | 'no_closed_prefix' | 'below_min_chars' | 'below_min_tokens' | 'not_profitable' | 'too_many_images' | 'render_empty' | 'collapsed';
283
+ /** Token count of the pre-compression body from /v1/messages/count_tokens (free).
284
+ * Absent when probe failed — event excluded from savings rollup. */
285
+ baselineTokens?: number;
286
+ /** Token count of the pre-compression body truncated at the last cache_control marker.
287
+ * Absent when the original body has no cache_control markers (cacheable=0 exactly). */
288
+ baselineCacheableTokens?: number;
289
+ /** 'ok': both probes resolved. 'partial': full-body resolved but cacheable-prefix
290
+ * didn't (exclude from rollup — cacheable=0 fallback is dishonest). 'failed': no
291
+ * baseline. undefined: no probe attempted. */
292
+ baselineProbeStatus?: 'ok' | 'partial' | 'failed';
293
+ }
294
+ /** sha256[0..8] hex via Web Crypto (works in Node 18+ and Workers). 32-bit collision-safe. */
295
+ export declare function sha8(text: string): Promise<string>;
296
+ /** Best-effort extraction of the CLAUDE.md slab from a system text (heuristic).
297
+ * Returns empty string if nothing CLAUDE.md-shaped is detected. */
298
+ export declare function extractClaudeMdSlab(staticText: string): string;
299
+ /** First user message text, capped at 4 KiB (stable thread id; hashing large pastes is wasteful). */
300
+ export declare function firstUserText(req: MessagesRequest): string;
301
+ /** Parse structured fields from the dynamic slab for telemetry. Read-only. */
302
+ export declare function extractEnvFields(dynamicText: string): EnvFields;
303
+ /** Estimate how many images `text` will render to at the given column width.
304
+ * Counts soft-wrapped visual rows, which is what render.ts actually budgets
305
+ * against. Exported for tests + the paging gate.
306
+ *
307
+ * `numCols` (default 1) packs that many text columns side-by-side per
308
+ * image — must match the `multiCol` setting wired through to the renderer
309
+ * for the math to predict the actual image count. */
310
+ export declare function estimateImageCount(textOrLen: string | number, cols: number, numCols?: number, maxCharsPerImage?: number): number;
311
+ /** Classify content so we can pick a truncation strategy. Cheap heuristics on
312
+ * the first ~4 KiB. Returns:
313
+ * - `'structured'`: JSON/YAML/diff markers at the top. Truncate tail.
314
+ * - `'log'`: ≥30% of lines start with a log level or timestamp. Truncate middle.
315
+ * - `'other'`: prose, file dumps, etc. Truncate middle.
316
+ * Exported for tests. */
317
+ export declare function classifyContent(text: string): 'structured' | 'log' | 'other';
318
+ /** Truncate `text` so it renders to roughly `maxImages` images at the given
319
+ * `cols`. Picks head/tail split based on `classifyContent`. Budget measured
320
+ * in visual rows (what render.ts actually slices on). Returns the truncated
321
+ * text (with paging marker embedded) and the count of chars omitted. If
322
+ * `text` already fits, returns unchanged with `omittedChars: 0`. Exported
323
+ * for tests. */
324
+ export declare function truncateForBudget(text: string, maxImages: number, cols: number, numCols?: number, maxCharsPerImage?: number): {
325
+ text: string;
326
+ omittedChars: number;
327
+ truncated: boolean;
328
+ };
329
+ /**
330
+ * Render text → Anthropic image blocks for the proxy. The column-selection rule below
331
+ * (shrink, then single-col unless the content fills the width) is mirrored exactly by
332
+ * the public SDK primitive `renderTextToImages` (library.ts), so the proxy and the
333
+ * `pxpipe export` CLI emit byte-identical PNGs for the same text. Exported so
334
+ * export-proxy-align.test.ts can pin that invariant against the real proxy code.
335
+ */
336
+ export declare function textToImageBlocks(text: string, cols: number, numCols?: number,
337
+ /** Shrink canvas to the longest wrapped line. `false` for the slab path
338
+ * (fills full `cols` for multi-col packing). Default `true`. */
339
+ shrinkWidth?: boolean): Promise<{
340
+ blocks: ImageBlock[];
341
+ /** Raw PNG bytes parallel to `blocks` (avoids re-decoding base64 for dashboard). */
342
+ pngs: Uint8Array[];
343
+ /** Pixel dimensions parallel to `pngs`. */
344
+ dims: Array<{
345
+ width: number;
346
+ height: number;
347
+ }>;
348
+ droppedChars: number;
349
+ droppedCodepoints: Map<number, number>;
350
+ /** Σ width×height — caller accumulates into `info.imagePixels` for px/token regression. */
351
+ pixels: number;
352
+ }>;
353
+ /**
354
+ * Rewrite a Messages API request body. Returns the new body (still JSON
355
+ * bytes) plus diagnostic info. On any error, returns the original bytes
356
+ * unchanged.
357
+ */
358
+ export declare function transformRequest(body: Uint8Array, opts?: TransformOptions): Promise<{
359
+ body: Uint8Array;
360
+ info: TransformInfo;
361
+ }>;
362
+ //# sourceMappingURL=transform.d.ts.map