import { describe, expect, it } from 'vitest'; import { renderChunkToPng, renderTextToPngs, renderTextToPngsMultiCol, multiColWidth, maxFittingCols, expandTabsInLine, minifyForRender, roleSlotSegment, slotCopyBody, SLOT_MARK_USER, SLOT_MARK_ASSISTANT, ROLE_PALETTE, renderCellHeight, renderCellWidth, CELL_H, CELL_W, } from '../src/core/render.js'; import { encodeGrayPng, bytesToBase64 } from '../src/core/png.js'; import { transformRequest, isCompressionProfitable, maxCharsPerImage, estimateImageCount, compactSlabWhitespace, SLAB_CHARS_PER_TOKEN, } from '../src/core/transform.js'; import { stripSchemaDescriptions } from '../src/core/schema-strip.js'; import { atlasRank, ATLAS_CELL_H, ATLAS_CELL_W, ATLAS_OFFSETS, ATLAS_PIXELS, ATLAS_WIDE_FLAGS, ATLAS_NUM_GLYPHS, } from '../src/core/atlas.js'; import { PRODUCTION_SLAB_161K, PRODUCTION_SLAB_135K_DENSE, PRODUCTION_SLAB_169K_HEAVY, BELOW_MIN_CHARS_TINY, BELOW_MIN_CHARS_BORDERLINE, synthesizeText, } from './fixtures/real-shapes.js'; describe('model-selectable font atlases', () => { it('uses the JetBrains Mono 10 cell geometry without changing the default atlas', async () => { expect(renderCellWidth({ aa: true })).toBe(5); expect(renderCellHeight({ aa: true })).toBe(8); expect(renderCellWidth({ font: 'jetbrains-mono-10', aa: true })).toBe(6); expect(renderCellHeight({ font: 'jetbrains-mono-10', aa: true })).toBe(11); const text = 'tokenLedgerShard a3f9c1e0b7d2'; const defaultImg = await renderChunkToPng(text, 40, { aa: true }); const solImg = await renderChunkToPng(text, 40, { font: 'jetbrains-mono-10', aa: true }); expect(defaultImg.width).toBe(208); expect(solImg.width).toBe(248); expect(solImg.height).toBeGreaterThan(defaultImg.height); expect(Buffer.from(solImg.png)).not.toEqual(Buffer.from(defaultImg.png)); }); it('falls back to the full Spleen/Unifont atlas for Unicode outside the compact Sol atlas', async () => { const img = await renderChunkToPng('한글 test', 20, { font: 'jetbrains-mono-10', aa: true, }); expect(img.droppedChars).toBe(0); }); }); // Spleen 5×8 glyph-confusability guard. See docs/LEGIBILITY-AUDIT-2026-07-01.md // §2: the stock K was 'H' minus one crossbar pixel (Hamming 1), the worst pair // in the atlas. gen-atlas.ts now surgeries K to a diagonal-legged bitmap. describe('Spleen 5×8 glyph confusability', () => { const decode = (ch: string): Uint8Array => { const rank = atlasRank(ch.codePointAt(0)!); expect(rank).toBeGreaterThanOrEqual(0); const wide = ATLAS_WIDE_FLAGS[rank] === 1; const w = wide ? 2 * ATLAS_CELL_W : ATLAS_CELL_W; const base = ATLAS_OFFSETS[rank]!; const bits = new Uint8Array(w * ATLAS_CELL_H); for (let p = 0; p < bits.length; p++) { const bit = base + p; bits[p] = (ATLAS_PIXELS[bit >>> 3]! >>> (7 - (bit & 7))) & 1; } return bits; }; const hamming = (a: Uint8Array, b: Uint8Array): number => { let d = 0; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) d++; return d; }; it('renders K far from H (surgery applied)', () => { expect(hamming(decode('K'), decode('H'))).toBeGreaterThanOrEqual(6); }); it('keeps every alphanumeric pair distinguishable (Hamming ≥ 2)', () => { // Letters and digits are the recall-critical classes (identifiers, hex); // this is where the H/K d=1 defect lived. Punctuation pairs the audit // listed but left unfixed (',;' '.:' at d=1) are out of scope here. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split(''); const glyphs = chars.map(decode); let worst = { a: '', b: '', d: Infinity }; for (let i = 0; i < glyphs.length; i++) for (let j = i + 1; j < glyphs.length; j++) { const d = hamming(glyphs[i]!, glyphs[j]!); if (d < worst.d) worst = { a: chars[i]!, b: chars[j]!, d }; } // No two distinct alphanumeric glyphs may collide (d=0) or differ by a // single pixel (d=1, the K/H defect the surgery removed). expect(worst.d, `closest pair '${worst.a}'~'${worst.b}' d=${worst.d}`).toBeGreaterThanOrEqual(2); }); }); describe('compactSlabWhitespace', () => { it('returns empty string unchanged', () => { expect(compactSlabWhitespace('')).toBe(''); }); it('strips trailing spaces and tabs per line', () => { const input = 'alpha \nbeta\t\t\ngamma'; expect(compactSlabWhitespace(input)).toBe('alpha\nbeta\ngamma'); }); it('collapses 3+ consecutive newlines to exactly 2', () => { expect(compactSlabWhitespace('a\n\n\nb')).toBe('a\n\nb'); expect(compactSlabWhitespace('a\n\n\n\n\n\nb')).toBe('a\n\nb'); }); it('preserves single blank lines (paragraph breaks)', () => { expect(compactSlabWhitespace('a\n\nb')).toBe('a\n\nb'); }); it('preserves leading indentation', () => { const input = ' function f() {\n return 1; \n }'; expect(compactSlabWhitespace(input)).toBe(' function f() {\n return 1;\n }'); }); it('is idempotent', () => { const input = 'x \n\n\n\ny\t\nz '; const once = compactSlabWhitespace(input); const twice = compactSlabWhitespace(once); expect(twice).toBe(once); }); it('shrinks a realistic markdown-ish slab', () => { const input = [ '# Heading ', '', '', '', 'Paragraph with trailing space. ', '', '- bullet one ', '- bullet two\t', '', '', '', '## Sub', ].join('\n'); const out = compactSlabWhitespace(input); expect(out.length).toBeLessThan(input.length); expect(out).not.toMatch(/[ \t]+\n/); expect(out).not.toMatch(/\n{3,}/); }); }); describe('png encoder', () => { it('produces a valid PNG signature', async () => { const pixels = new Uint8Array(4 * 4).fill(128); // 4×4 mid-gray const png = await encodeGrayPng(pixels, 4, 4); expect(png.slice(0, 8)).toEqual( new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), ); // Last chunk should be IEND const tail = png.slice(-12); expect(String.fromCharCode(tail[4]!, tail[5]!, tail[6]!, tail[7]!)).toBe('IEND'); }); it('round-trips bytesToBase64 ↔ atob', () => { const original = new Uint8Array([0, 1, 2, 3, 254, 255]); const b64 = bytesToBase64(original); const decoded = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); expect(decoded).toEqual(original); }); }); describe('renderer', () => { it('renders a one-line string to a single PNG', async () => { const img = await renderChunkToPng('Hello, world!'); expect(img.png.slice(0, 8)).toEqual( new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), ); expect(img.height).toBeLessThanOrEqual(1932); expect(img.width).toBeGreaterThan(0); }); it('splits very long input into multiple PNGs', async () => { const huge = ('lorem ipsum dolor sit amet '.repeat(20) + '\n').repeat(500); const imgs = await renderTextToPngs(huge); expect(imgs.length).toBeGreaterThan(1); for (const img of imgs) expect(img.height).toBeLessThanOrEqual(1932); }); // ---- R2 multi-column renderer ------------------------------------------ // The multi-col path packs N source columns side-by-side per image so // each image covers numCols×LINES_PER_IMAGE wrapped lines instead of one. // Off by default (numCols=1) — these tests exercise the new path. it('multi-col with numCols=1 is byte-identical to renderTextToPngs (default cache contract)', async () => { // The cache_control story depends on identical bytes for identical // inputs. numCols=1 MUST be a pure passthrough so toggling the flag // back to 1 cannot regress cache hit rate. const text = ('lorem ipsum dolor sit amet\n'.repeat(8)) + 'final line'; const single = await renderTextToPngs(text, 100); const passthrough = await renderTextToPngsMultiCol(text, 100, 1); expect(passthrough.length).toBe(single.length); for (let i = 0; i < single.length; i++) { expect(passthrough[i]!.png).toEqual(single[i]!.png); } }); it('multi-col emits a wider canvas with the predicted dimensions', async () => { const text = ('lorem ipsum dolor sit amet\n'.repeat(8)) + 'final line'; const single = await renderTextToPngs(text, 100); const two = await renderTextToPngsMultiCol(text, 100, 2); // numCols=2 with 100-col text content + 4-cell gutter at 5px/cell (5×8 production cell): // width = 2*PAD_X + 2*100*5 + 1*4*5 = 8 + 1000 + 20 = 1028 px expect(two[0]!.width).toBe(multiColWidth(100, 2)); expect(two[0]!.width).toBeGreaterThan(single[0]!.width); expect(two[0]!.width).toBeLessThanOrEqual(2000); }); it('multi-col halves image count on row-heavy input', async () => { // ~500 lines of narrow content. Single-col packs 240 lines/image → // ~3 images. Two columns should drop that to ~2. const text = ('lorem ipsum dolor sit amet\n'.repeat(500)); const single = await renderTextToPngs(text, 100); const two = await renderTextToPngsMultiCol(text, 100, 2); expect(single.length).toBeGreaterThanOrEqual(3); // Two-col image count ≤ ceil(single / 2). The +1 slack handles the // pathological case where the boundary lands awkwardly. expect(two.length).toBeLessThanOrEqual(Math.ceil(single.length / 2)); for (const img of two) expect(img.height).toBeLessThanOrEqual(1932); }); it('multi-col render is deterministic (byte-identical across calls)', async () => { const text = ('alpha beta gamma delta epsilon\n'.repeat(400)); const a = await renderTextToPngsMultiCol(text, 100, 2); const b = await renderTextToPngsMultiCol(text, 100, 2); expect(a.length).toBe(b.length); for (let i = 0; i < a.length; i++) expect(a[i]!.png).toEqual(b[i]!.png); }); it('multi-col per-image charsRendered sums to the input codepoint count', async () => { // The honest-savings math (compressedChars in TransformInfo) relies on // sum(charsRendered) matching the input we paid to render. Off-by-one // here would silently mis-report savings. const text = ('lorem ipsum dolor sit amet\n'.repeat(400)); let cpCount = 0; for (const _ of text) cpCount++; const imgs = await renderTextToPngsMultiCol(text, 100, 2); let total = 0; for (const img of imgs) total += img.charsRendered; expect(total).toBe(cpCount); }); it('estimateImageCount(numCols=2) tracks actual multi-col image count', async () => { const text = ('lorem ipsum dolor sit amet\n'.repeat(500)); const actual = (await renderTextToPngsMultiCol(text, 100, 2)).length; const estimated = estimateImageCount(text, 100, 2); expect(estimated).toBe(actual); }); it('maxFittingCols clamps an over-wide numCols flag instead of producing >2000px canvases', async () => { // At cols=100 (5 px/cell + 4-cell gutter), the math says: // 1: 508 px, 2: 1028, 3: 1548, 4: 2068 → 4 already exceeds 2000. const fits = maxFittingCols(100); expect(fits).toBe(3); const text = 'short\n'.repeat(10); // numCols=10 → should clamp; output canvas width must stay ≤ 2000. const imgs = await renderTextToPngsMultiCol(text, 100, 10); for (const img of imgs) expect(img.width).toBeLessThanOrEqual(2000); }); it('multi-col preserves CJK wide-glyph wrap math (no dropped chars on Chinese input)', async () => { // Wide glyphs are 2 cells in both layouts; multi-col must not regress // the wrap math or atlas lookup. const text = ('中文测试 mixed ASCII\n'.repeat(100)); const imgs = await renderTextToPngsMultiCol(text, 100, 2); let dropped = 0; for (const img of imgs) dropped += img.droppedChars; expect(dropped).toBe(0); }); it('multi-col draws a light-gray gutter divider (OCR column-boundary cue)', async () => { // Same "visible whitespace" idea as the U+2192 tab arrow: surface the // column boundary explicitly instead of relying on gap-of-whitespace // alone. The pixel sits at MID-GRAY (~191/255), distinct from both // background (255) and glyph ink (~0), so the vision encoder reads it // as a structural cue without competing with text. Cost is ~one // 1-pixel-wide column of identical gray that DEFLATE-collapses to ~5 // bytes per gutter. // // Verification: inflate the IDAT chunks of the rendered PNG, locate the // expected divider x-coordinate (middle of the gutter between cols 0 // and 1), and assert at least 80% of rows at that x are mid-gray. const zlib = await import('node:zlib'); const text = ('lorem ipsum dolor sit amet\n'.repeat(200)); const imgs = await renderTextToPngsMultiCol(text, 100, 2); expect(imgs.length).toBeGreaterThan(0); const img = imgs[0]!; // PNG layout: 8-byte signature, then chunks of (length-be32, type-4b, // data, crc-be32). IHDR is first; concat all IDATs and inflate. The // inflated stream has a 1-byte filter prefix per row; for grayscale our // encoder always writes filter type 0 (none) so the row body is just // the raw width bytes. const png = img.png; let pos = 8; const idats: Uint8Array[] = []; while (pos < png.length) { const len = (png[pos]! << 24) | (png[pos + 1]! << 16) | (png[pos + 2]! << 8) | png[pos + 3]!; const type = String.fromCharCode(png[pos + 4]!, png[pos + 5]!, png[pos + 6]!, png[pos + 7]!); const dataStart = pos + 8; if (type === 'IDAT') idats.push(png.subarray(dataStart, dataStart + len)); if (type === 'IEND') break; pos = dataStart + len + 4; } const concatenated = Buffer.concat(idats.map((u) => Buffer.from(u))); const inflated = zlib.inflateSync(concatenated); // Decode: each row is 1 filter byte + width pixel bytes. We expect // filter=0 (none) on every row from our encoder, so the pixel-byte // index for (x, y) is `y * (width + 1) + 1 + x`. const width = img.width; const height = img.height; // Divider X: end of col 0's text area + half the gutter. // colEnd = PAD_X (4) + 0 * stride + 100 * 7 = 704 // dividerX = 704 + floor((4 * 7) / 2) = 704 + 14 = 718 const PAD_X = 4; const GUTTER_CELLS = 4; const cols = 100; const dividerX = PAD_X + 0 + cols * CELL_W + Math.floor((GUTTER_CELLS * CELL_W) / 2); expect(dividerX).toBeLessThan(width); let midGrayRows = 0; const rowStride = width + 1; for (let y = 2; y < height - 2; y++) { const px = inflated[y * rowStride + 1 + dividerX]; // GUTTER_DIVIDER_INK=64 pre-invert → 191 post-invert. Allow a small // band in case the constant is tuned later — anywhere in [120, 230] // is "mid-gray, not full ink, not background". if (px !== undefined && px >= 120 && px <= 230) midGrayRows++; } // Most rows at the divider column should be mid-gray. The inset trims a // few top/bottom pixels and there might be glyph encroachments on a // handful of rows in pathological content, but >80% is the floor. const liveRows = height - 4; expect(midGrayRows).toBeGreaterThan(liveRows * 0.8); }); it('multi-col single-column path skips the divider (byte-identical to renderTextToPngs)', async () => { // The divider only paints when numCols >= 2. The numCols=1 passthrough // path must remain byte-identical to the single-col renderer so the // cache-control deterministic-bytes story stays intact for single-col // deployments. const text = ('lorem ipsum dolor sit amet\n'.repeat(100)); const passthrough = await renderTextToPngsMultiCol(text, 100, 1); const single = await renderTextToPngs(text, 100); expect(passthrough.length).toBe(single.length); for (let i = 0; i < passthrough.length; i++) { expect(passthrough[i]!.png).toEqual(single[i]!.png); } }); // ---- Unicode coverage tests (hybrid atlas fallback) ------------------------------- // These confirm the sparse-codepoint + wide-glyph machinery works end-to-end. // None of them assert specific PNG bytes (the byte-deterministic guarantee // is covered by the 'renders identical input...' test below); they assert // the *contract*: known glyphs render without dropping, missing glyphs // increment droppedChars, and wide chars advance two cells. it('renders a Chinese codepoint without dropping (CJK Unified)', async () => { const img = await renderChunkToPng('中文'); // U+4E2D U+6587 expect(img.droppedChars).toBe(0); expect(img.charsRendered).toBe(2); expect(img.width).toBeGreaterThan(0); }); it('renders Cyrillic without dropping', async () => { const img = await renderChunkToPng('Привет мир'); // 10 codepoints incl. space expect(img.droppedChars).toBe(0); expect(img.charsRendered).toBe(10); }); it('renders Greek, Hebrew, Arabic, box-drawing, and math symbols', async () => { // One glyph from each profile range that the atlas claims to cover. // (The renderer is left-to-right only; Hebrew/Arabic will appear in // source order, not bidi-correct order — that's a documented limitation // of this slice, not a test failure.) const sample = 'α β π — → ∑ ∫ √ ─ │ ┌ ┐'; const img = await renderChunkToPng(sample); expect(img.droppedChars).toBe(0); }); it('escapes codepoints outside the atlas instead of dropping them (e.g. emoji)', async () => { // 😀 is U+1F600 — Supplementary Plane, not in BMP. Even `full-bmp` profile // wouldn't cover it. It renders as the lossless ASCII escape [U+1F600] // (see escapeMissingGlyphs), not as a blank cell — and must not crash on // the surrogate pair. const img = await renderChunkToPng('hi 😀 world'); expect(img.droppedChars).toBe(0); // charsRendered counts SOURCE codepoints, NOT UTF-16 units — the emoji is // one codepoint even though it occupies two UTF-16 units. expect(img.charsRendered).toBe(10); // 'hi ' (3) + 😀 (1) + ' world' (6) = 10 }); it('treats escape-exempt invisibles as dropped with 1-cell advance (e.g. VS16)', async () => { // U+FE0F (variation selector-16) is deliberately NOT escaped — it's an // emoji presentation modifier, noise if spelled out. Renderer must advance // by 1 cell and bump the counter. const img = await renderChunkToPng('hi ️ world'); expect(img.droppedChars).toBe(1); }); it('CJK characters advance two cells; mixed lines wrap correctly', async () => { // 100 cols, mixed Latin + CJK. 30 Latin chars + 40 CJK chars = 30 + 80 = // 110 visual columns → must wrap to 2 lines. const latin30 = 'abcdefghijklmnopqrstuvwxyz0123'; const cjk40 = '中'.repeat(40); const img = await renderChunkToPng(latin30 + cjk40, 100); // First line fills 30 + 35*2 = 100 cols (35 CJK chars). // Second line holds the remaining 5 CJK chars. // Image height: 2 lines × CELL_H + 2*PAD_Y. PAD_Y is 4 px (matches // render.ts's const). CELL_H comes from the atlas so this stays correct // across font-size changes. expect(img.charsRendered).toBe(latin30.length + 40); expect(img.droppedChars).toBe(0); const expectedHeight = 2 * 4 /* PAD_Y */ + 2 * CELL_H; expect(img.height).toBe(expectedHeight); }); it('does NOT split a wide glyph across the column boundary', async () => { // 99 Latin + 1 CJK at cols=100: the CJK would land at col 99 (1 col left) // and needs 2. Wrap math must move it to a new line, leaving col 99 blank // on the first line. const line = 'a'.repeat(99) + '中'; const img = await renderChunkToPng(line, 100); expect(img.charsRendered).toBe(100); expect(img.droppedChars).toBe(0); // Two lines: first has 99 'a', second has the '中'. const expectedHeight = 2 * 4 /* PAD_Y */ + 2 * CELL_H; expect(img.height).toBe(expectedHeight); }); // --- Atlas profile coverage: 6 blocks added per #27 + #28 ----------------- // These confirm the codepoints the drop-histogram surfaced as 95% of // production drops are now in the atlas. Each `atlasRank` returns ≥ 0 // for a representative glyph from each block. it('atlas covers Dingbats (✓ ✗ ❌)', () => { expect(atlasRank('✓'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('✗'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('❌'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas covers Miscellaneous Symbols (⚠ ★)', () => { expect(atlasRank('⚠'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('★'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas covers Letterlike Symbols (ℝ ℕ ℤ ℚ ℂ)', () => { expect(atlasRank('ℝ'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('ℕ'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('ℤ'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('ℚ'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('ℂ'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas covers Block Elements (█ ░ ▒)', () => { expect(atlasRank('█'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('░'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('▒'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas covers Geometric Shapes (▲ ▼ ► ◄ ●)', () => { expect(atlasRank('▲'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('▼'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('►'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('◄'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('●'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas covers Miscellaneous Technical (⌈ ⌉ ⌊ ⌋)', () => { expect(atlasRank('⌈'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('⌉'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('⌊'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('⌋'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas covers Enclosed Alphanumerics (ⓘ ① ② ⑩)', () => { expect(atlasRank('ⓘ'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('①'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('②'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('⑩'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('atlas pixel storage is bit-packed (1 bit per pixel)', () => { // Sanity check on the storage format. Total pixels across all glyphs = // numGlyphs × cellH × (cellW or 2*cellW) depending on each glyph's // wide-flag. The packed byte buffer must be ceil(totalBits / 8) bytes // — within 1 byte of the theoretical minimum. let totalBits = 0; for (let r = 0; r < ATLAS_NUM_GLYPHS; r++) { const srcW = ATLAS_WIDE_FLAGS[r] === 1 ? 2 * ATLAS_CELL_W : ATLAS_CELL_W; totalBits += srcW * ATLAS_CELL_H; } const expectedBytes = Math.ceil(totalBits / 8); expect(ATLAS_PIXELS.byteLength).toBe(expectedBytes); // Hard guarantee: the 8-bit format would have used totalBits bytes, so // bit-packed is exactly 8× smaller (modulo the rounding to whole bytes). expect(ATLAS_PIXELS.byteLength).toBeLessThanOrEqual(totalBits / 8 + 1); }); it('atlas covers Hangul Syllables (한 글 안 녕) — full-bmp profile only', () => { // The default profile is now `full-bmp`, which ships ~11k Hangul // Syllables (U+AC00..U+D7AF). The `practical` profile drops these // for Workers free-tier deployments; if someone regenerates the atlas // with ATLAS_PROFILE=practical, these expectations will (correctly) // fail — that's the signal to update the test alongside the deploy. expect(atlasRank('한'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('글'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('안'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); expect(atlasRank('녕'.codePointAt(0)!)).toBeGreaterThanOrEqual(0); }); it('rendering a mix of newly-covered glyphs produces droppedChars: 0', async () => { // The histogram script found these were the most common drops in real // traffic. After the full-bmp default plus #29 cell-height fix, all // render cleanly — Hangul `한 글` included. const sample = '✓ ⚠ ℝ █ ▲ ⌈ ⌉ ⓘ ✗ ► ▼ ● ░ ★ ℕ ① 한 글 中 文'; const img = await renderChunkToPng(sample); expect(img.droppedChars).toBe(0); expect(img.droppedCodepoints.size).toBe(0); }); it('droppedCodepoints map is populated correctly when drops occur', async () => { // Emoji now escape to [U+HEX] instead of dropping, so the drop path is // exercised via an escape-EXEMPT codepoint: U+FE0F (variation selector). // The codepoint should appear in the map with count 1. const img = await renderChunkToPng('hi ️ there'); expect(img.droppedChars).toBe(1); expect(img.droppedCodepoints.size).toBe(1); expect(img.droppedCodepoints.get(0xfe0f)).toBe(1); }); it('droppedCodepoints tallies repeat drops correctly', async () => { // Three occurrences of the same dropped (exempt) codepoint → count 3. const img = await renderChunkToPng('a️b️c️'); expect(img.droppedChars).toBe(3); expect(img.droppedCodepoints.size).toBe(1); expect(img.droppedCodepoints.get(0xfe0f)).toBe(3); }); // --- Whitespace minify (HANDOFF R1) --------------------------------------- // Conservative whitespace cleanup before tab-expand + wrap. Strip trailing // whitespace per line; collapse 4+ \n runs down to 3 \n (max 2 blank lines). // Mid-line spaces and leading indent are NEVER touched (alignment + structure // are preserved). it('minifyForRender: strips trailing spaces', () => { expect(minifyForRender('foo \n')).toBe('foo\n'); }); it('minifyForRender: strips trailing tab + space mix', () => { expect(minifyForRender('foo\t \n')).toBe('foo\n'); }); it('minifyForRender: collapses 5 newlines to 3 (= 2 blank lines)', () => { expect(minifyForRender('foo\n\n\n\n\nbar')).toBe('foo\n\n\nbar'); }); it('minifyForRender: preserves 2 newlines (= 1 blank line)', () => { expect(minifyForRender('foo\n\nbar')).toBe('foo\n\nbar'); }); it('minifyForRender: preserves 3 newlines (= 2 blank lines, the cap)', () => { expect(minifyForRender('foo\n\n\nbar')).toBe('foo\n\n\nbar'); }); it('minifyForRender: NEVER collapses mid-line spaces (alignment preserved)', () => { expect(minifyForRender('a b c')).toBe('a b c'); }); it('minifyForRender: NEVER strips leading whitespace (indent preserved)', () => { expect(minifyForRender(' foo')).toBe(' foo'); }); it('minifyForRender: real-world mix of trailing whitespace + blank runs', () => { // Stack-trace shaped: lines with trailing spaces + 5-line blank gaps. const input = 'Error: x failed \n\tat foo() \n\n\n\n\n\tat bar()\n'; const expected = 'Error: x failed\n\tat foo()\n\n\n\tat bar()\n'; expect(minifyForRender(input)).toBe(expected); }); it('minify pipeline integration: trailing whitespace + blank runs → shorter image', async () => { // Same content, with-vs-without whitespace bloat. The "bloated" version // has trailing spaces and 6-line blank gaps; the "clean" version has // neither. Both render to single-PNG output for the test; we measure // the height delta and confirm the bloated→minified reduction is real. const cleanLines = ['line one', 'line two', '', '', 'line three', 'line four']; const bloatedLines = [ 'line one ', // trailing whitespace 'line two ', '', '', '', '', '', '', '', 'line three ', 'line four ', ]; const cleanImg = await renderChunkToPng(cleanLines.join('\n')); const bloatedImg = await renderChunkToPng(bloatedLines.join('\n')); // After minify, both should render to the same final shape. expect(bloatedImg.height).toBe(cleanImg.height); expect(bloatedImg.droppedChars).toBe(0); expect(cleanImg.droppedChars).toBe(0); }); // --- Tab expansion (production bug fix) ----------------------------------- // Real telemetry on 2026-05-19 showed 5,339 of 5,358 drops (99.6%) were // U+0009 TAB. Tabs are control codepoints, not glyphs — they expand to // a visible `→` (U+2192) at the tab boundary + padding spaces to the next // 4-stop. The visible arrow preserves "this was an indent" structure for // the OCR'd model; silent spaces would lose that signal. it('expandTabsInLine: basic — a\\tb → a→<2sp>b (col 1 → col 4, span=3)', () => { expect(expandTabsInLine('a\tb')).toBe('a→ b'); }); it('expandTabsInLine: leading tab — \\tx → →<3sp>x (col 0 → col 4, span=4)', () => { expect(expandTabsInLine('\tx')).toBe('→ x'); }); it('expandTabsInLine: ab\\tc → ab→<1sp>c (col 2 → col 4, span=2)', () => { expect(expandTabsInLine('ab\tc')).toBe('ab→ c'); }); it('expandTabsInLine: abc\\tx → abc→x (col 3 → col 4, span=1, no padding)', () => { // NOTE: the team-lead brief showed `abc→ x` here, but the brief's own // formula `tabWidth - (col % tabWidth)` gives span=1 at col=3 — single // arrow, zero padding. Implementing per the formula (consistent across // all other cases); flagging the brief example as a typo. expect(expandTabsInLine('abc\tx')).toBe('abc→x'); }); it('expandTabsInLine: no tabs → unchanged (fast path)', () => { expect(expandTabsInLine('a\nb')).toBe('a\nb'); expect(expandTabsInLine('hello world')).toBe('hello world'); }); it('expandTabsInLine: tab after CJK uses visual width (中 = 2 cols)', () => { // 中 at cols 0-1, tab at col 2 → span = 4 - 2 = 2 (arrow + 1 space). expect(expandTabsInLine('中\tx')).toBe('中→ x'); }); it('renders tab-containing text with droppedChars: 0 (was dropping pre-fix)', async () => { const img = await renderChunkToPng('a\tb'); expect(img.droppedChars).toBe(0); expect(img.droppedCodepoints.size).toBe(0); }); it('renders leading tab with droppedChars: 0', async () => { const img = await renderChunkToPng('\tx'); expect(img.droppedChars).toBe(0); }); it('full pipeline: foo\\n\\tbar renders to two lines with visible → in the indent', async () => { // Brief's specific E2E ask. `foo\n\tbar` is two logical lines: // line 0: "foo" // line 1: "\tbar" → expands to "→ bar" // Both lines render cleanly with no drops; arrow U+2192 is in the Arrows // block (covered by every profile). const img = await renderChunkToPng('foo\n\tbar'); expect(img.droppedChars).toBe(0); expect(img.droppedCodepoints.size).toBe(0); // Two visible lines = 2 cell-rows of pixels (height check). const expectedHeight = 2 * 4 /* PAD_Y */ + 2 * CELL_H; expect(img.height).toBe(expectedHeight); // Sanity: charsRendered counts input codepoints (4 + 1 + 4 = 9 chars // including the embedded `\n`). The arrow + padding spaces aren't in // the input — they're created post-`\n`-split — so `charsRendered` // still reflects the original input length. expect(img.charsRendered).toBe('foo\n\tbar'.length); }); it('multiple tabs land on their respective tab stops', async () => { // `a\tbb\tc`: // 'a' → col 0..1 // '\t' → col 1, fills to col 4 (3 spaces) // 'bb' → col 4..6 // '\t' → col 6, fills to col 8 (2 spaces) // 'c' → col 8..9 // Net: 'a' + 3 spaces + 'bb' + 2 spaces + 'c' — all visible glyphs in // the atlas, zero drops. const img = await renderChunkToPng('a\tbb\tc'); expect(img.droppedChars).toBe(0); }); it('tab after CJK char respects East Asian Wide column count', async () => { // 中 is 2 visual cols. So tab after 中 fills col 2 → col 4 (2 spaces). const img = await renderChunkToPng('中\tx'); expect(img.droppedChars).toBe(0); }); it('tab at the start of multiple lines resets column tracking per line', async () => { // Each line independently treats tab as expanding from col 0 (4 spaces). const img = await renderChunkToPng('\ta\n\tb\n\tc'); expect(img.droppedChars).toBe(0); }); it('a long string with embedded tabs produces zero drops', async () => { // Stress test for the production failure mode (tabs in tool_result-like // text dumps with thousands of indented lines). const line = 'fn\tname\tlocation\n'.repeat(500); const img = await renderChunkToPng(line); expect(img.droppedChars).toBe(0); // Codepoint 0x0009 must NOT appear in any drop tally. expect(img.droppedCodepoints.has(0x09)).toBe(false); }); }); describe('transform', () => { it('is a no-op when below min-chars', async () => { const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'You are helpful.', }); const bytes = new TextEncoder().encode(req); const { body, info } = await transformRequest(bytes, { minCompressChars: 100 }); expect(info.compressed).toBe(false); expect(body).toBe(bytes); // returns same reference }); it('compresses large system fields into image blocks', async () => { const bigSystem = 'You are a helpful assistant. '.repeat(5000); // ~31.9k chars, well past 2-image break-even (20k) const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: bigSystem, }); const bytes = new TextEncoder().encode(req); const { body, info } = await transformRequest(bytes); expect(info.compressed).toBe(true); expect(info.imageCount).toBeGreaterThanOrEqual(1); const out = JSON.parse(new TextDecoder().decode(body)); // Images always go into the first user message, not the system field // (Anthropic rejects image blocks in `system`). const userContent = out.messages[0].content as any[]; expect(Array.isArray(userContent)).toBe(true); const imageBlocks = userContent.filter((b: any) => b.type === 'image'); expect(imageBlocks.length).toBe(info.imageCount); expect(imageBlocks[0].source.media_type).toBe('image/png'); // And the system field must NOT contain image blocks (would 400). if (Array.isArray(out.system)) { for (const b of out.system) expect(b.type).not.toBe('image'); } }); it('moves tool docs into the imaged Tool Reference and stubs originals', async () => { const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], // Sized so system + tool docs + banner fit inside the 65,536-char // info.imageSourceText diagnostic window (the reference renders after // the static slab) while still clearing the compression gates. system: 'x'.repeat(30000), tools: [ { name: 'BigTool', description: 'A very long tool description. '.repeat(500), input_schema: { type: 'object', properties: { x: { type: 'string' } } }, }, ], }); const bytes = new TextEncoder().encode(req); const { body, info } = await transformRequest(bytes); expect(info.compressed).toBe(true); expect(info.toolDocsChars).toBeGreaterThan(0); const out = JSON.parse(new TextDecoder().decode(body)); // Stub cites its own heading in the imaged Tool Reference (link-up contract). expect(out.tools[0].name).toBe('BigTool'); expect(out.tools[0].description).toContain('"## Tool: BigTool"'); expect(out.tools[0].description).toContain('Tool Reference'); // Full docs ride the imaged slab — that IS the compression. const imgSrc = info.imageSourceText ?? ''; expect(imgSrc).toContain('=== TOOL REFERENCE ==='); expect(imgSrc).toContain('## Tool: BigTool'); expect(imgSrc).toContain('A very long tool description.'); // No text-splice remains in the system field. const sysTexts = ((out.system as any[]) ?? []) .filter((b: any) => b.type === 'text') .map((b: any) => b.text as string); expect(sysTexts.some((t) => t.includes('=== TOOL REFERENCE ==='))).toBe(false); // Classifier regression (169521c; retripped 2026-07-02 by a stub citing "the // system prompt"): pxpipe-authored framing must never read as a replayed or // extracted prompt. Ban the trigger wording in the stub and the reference // header, and require first-party provenance framing on the reference block. // Scoped to the header only — quoted tool docs below it are third-party text. expect(out.tools[0].description).not.toMatch(/system prompt|authoritative/i); const refStart = imgSrc.indexOf('=== TOOL REFERENCE ==='); const refHeader = imgSrc.slice(refStart, imgSrc.indexOf('## Tool:', refStart)); expect(refHeader).not.toMatch(/system prompt|authoritative/i); expect(refHeader).toContain("this user's local proxy"); }); it('ships annotation-stripped schemas in tools[], full schema in the imaged reference', async () => { // History: a bare `{type:'object'}` stub caused validator 400s; a text // reference paid the annotations at text rates. Current contract: tools[] // keeps the structural contract (type/properties/required/enum) AND the // `$schema` dialect declaration for the validator; annotations // (description/default) move into the imaged reference at image rates. const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], // Small enough that the reference lands inside the 65,536-char // info.imageSourceText window; large enough to clear the gates. system: 'x'.repeat(30000), tools: [ { name: 'Read', description: 'Read a file from disk', input_schema: { type: 'object', description: 'Reads a file', $schema: 'http://json-schema.org/draft-07/schema#', properties: { file_path: { type: 'string', description: 'Absolute path to the file', }, mode: { type: 'string', enum: ['read', 'binary'], description: 'Read mode', default: 'read', }, }, required: ['file_path'], }, }, { name: 'Bash', description: 'Run a bash command', input_schema: { type: 'object', properties: { command: { type: 'string', description: 'cmd' }, env: { type: 'object', description: 'env vars', properties: { PATH: { type: 'string', description: 'path var' }, }, }, files: { type: 'array', items: { type: 'object', properties: { path: { type: 'string', description: 'path' }, }, required: ['path'], }, }, }, required: ['command'], }, }, ], }); const bytes = new TextEncoder().encode(req); const { body, info } = await transformRequest(bytes); expect(info.compressed).toBe(true); expect(info.reason).toBeUndefined(); const out = JSON.parse(new TextDecoder().decode(body)); // Structural contract survives in tools[] — validator-visible keys intact. const s0 = out.tools[0].input_schema; expect(s0.type).toBe('object'); expect(Object.keys(s0.properties)).toEqual(['file_path', 'mode']); expect(s0.required).toEqual(['file_path']); expect(s0.properties.mode.enum).toEqual(['read', 'binary']); // Annotations are stripped from tools[] everywhere in the tree — but the // `$schema` dialect declaration survives (stripping it re-dialects a // draft-07 schema to 2020-12 and the validator 400s legal draft-07 syntax). expect(JSON.stringify(s0)).not.toContain('description'); expect(s0.$schema).toBe('http://json-schema.org/draft-07/schema#'); expect(s0.properties.mode.default).toBeUndefined(); const s1 = out.tools[1].input_schema; expect(s1.required).toEqual(['command']); expect(Object.keys(s1.properties.env.properties)).toEqual(['PATH']); expect(s1.properties.files.items.required).toEqual(['path']); expect(JSON.stringify(s1)).not.toContain('description'); // The full annotated schema rides the imaged reference instead. const imgSrc = info.imageSourceText ?? ''; expect(imgSrc).toContain('Absolute path to the file'); expect(imgSrc).toContain('path var'); // Stubs cite the reference heading. expect(out.tools[0].description).toContain('"## Tool: Read"'); expect(out.tools[1].description).toContain('"## Tool: Bash"'); }); it('keeps $schema so draft-07 tuple items stay valid under the declared dialect', async () => { // Regression (2026-07-05): Voiceflow MCP's voiceflow_transcript declares // draft-07 and uses tuple-form `items: [...]`, which is illegal in the // API's default dialect (2020-12, where tuples are `prefixItems`). // Stripping `$schema` re-dialected the schema and every compressed request // 400'd: "tools.N.custom.input_schema: JSON schema is invalid. It must // match JSON Schema draft 2020-12". Passthrough of the same schema is // accepted — so the transform must keep the declaration. const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(30000), tools: [ { name: 'voiceflow_transcript', description: 'Search transcripts', input_schema: { $schema: 'http://json-schema.org/draft-07/schema#', type: 'object', properties: { filters: { type: 'array', items: { oneOf: [ { type: 'object', properties: { op: { type: 'string', enum: ['between'], description: 'operator' }, // draft-07 tuple validation — array-form `items`. value: { type: 'array', items: [ { type: 'number', description: 'lower bound' }, { type: 'number', description: 'upper bound' }, ], }, }, required: ['op', 'value'], }, ], }, }, }, required: ['filters'], }, }, ], }); const { body, info } = await transformRequest(new TextEncoder().encode(req)); expect(info.compressed).toBe(true); const out = JSON.parse(new TextDecoder().decode(body)); const s = out.tools[0].input_schema; // Dialect declaration survives — the tuple `items` stays legal. expect(s.$schema).toBe('http://json-schema.org/draft-07/schema#'); const between = s.properties.filters.items.oneOf[0]; expect(Array.isArray(between.properties.value.items)).toBe(true); expect(between.properties.value.items).toEqual([{ type: 'number' }, { type: 'number' }]); // Annotations still stripped. expect(between.properties.op.description).toBeUndefined(); }); it('passes a bare {type:"object"} schema through with no advisory', async () => { // Some tools legitimately ship a bare `{type:'object'}` schema. Passthrough // means there is nothing to stub or flag any more — what the caller sent is // exactly what Anthropic's validator sees. const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(150000), tools: [ { name: 'NoSchema', description: 'd', input_schema: { type: 'object' } }, ], }); const { body, info } = await transformRequest(new TextEncoder().encode(req)); expect(info.reason).toBeUndefined(); const out = JSON.parse(new TextDecoder().decode(body)); expect(out.tools[0].input_schema).toEqual({ type: 'object' }); }); it('leaves input_schema untouched when the original is missing', async () => { // If the tool ships without an input_schema, we should NOT invent one. const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(150000), tools: [{ name: 'Bare', description: 'd' }], }); const { body } = await transformRequest(new TextEncoder().encode(req)); const out = JSON.parse(new TextDecoder().decode(body)); expect('input_schema' in out.tools[0]).toBe(false); }); // #43: Anthropic's native server-side tools (versioned `type`, e.g. // advisor_20260301, web_search_20250305) have a fixed API schema that rejects // a `description` field on the tool entry ("Extra inputs are not permitted" // → 400). Only client-defined tools (no `type`, or explicit type:"custom") // may be stubbed and imaged; everything else must pass through byte-identical. it('passes native typed tools through untouched and keeps them out of the imaged reference (#43)', async () => { const nativeAdvisor = { type: 'advisor_20260301', name: 'advisor' }; const nativeSearch = { type: 'web_search_20250305', name: 'web_search', max_uses: 3 }; const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(30000), tools: [ { name: 'BigTool', description: 'A very long tool description. '.repeat(500), input_schema: { type: 'object', properties: { x: { type: 'string' } } }, }, nativeAdvisor, nativeSearch, ], }); const { body, info } = await transformRequest(new TextEncoder().encode(req)); expect(info.compressed).toBe(true); const out = JSON.parse(new TextDecoder().decode(body)); // Client tool alongside is still compressed — the guard must not disable // the rewrite for the rest of the array. expect(out.tools[0].description).toContain('"## Tool: BigTool"'); // Native typed entries: byte-identical passthrough. Any injected key // (description, input_schema, …) is a 400 upstream. expect(out.tools[1]).toEqual(nativeAdvisor); expect(out.tools[2]).toEqual(nativeSearch); // And they contribute nothing to the imaged Tool Reference — their docs // live server-side; an empty "## Tool: " heading is pure noise. const imgSrc = info.imageSourceText ?? ''; expect(imgSrc).toContain('## Tool: BigTool'); expect(imgSrc).not.toContain('## Tool: advisor'); expect(imgSrc).not.toContain('## Tool: web_search'); }); it('still rewrites explicit type:"custom" tools (#43 guard must not over-block)', async () => { const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(30000), tools: [ { type: 'custom', name: 'CustomTool', description: 'A very long tool description. '.repeat(500), input_schema: { type: 'object', properties: { x: { type: 'string' } } }, }, ], }); const { body, info } = await transformRequest(new TextEncoder().encode(req)); expect(info.compressed).toBe(true); const out = JSON.parse(new TextDecoder().decode(body)); // type:"custom" is the client-defined shape — it accepts description, so it // gets the same stub-and-image treatment as untyped entries. expect(out.tools[0].type).toBe('custom'); expect(out.tools[0].description).toContain('"## Tool: CustomTool"'); expect(info.imageSourceText ?? '').toContain('## Tool: CustomTool'); }); // Snapshot-style tests against real-world Claude Code tool schemas. // These exercise the full preservation contract of stripSchemaDescriptions — // now used ONLY by the GPT path (tools[] ships a stripped skeleton while the // imaged docs carry the full schema; the Anthropic path passes schemas // through untouched): type / properties / required / enum / items / oneOf / // anyOf / allOf / $ref / numeric & string constraints / format. Each case // asserts the exact post-strip shape so a regression surfaces immediately. describe('real-world tool-schema preservation (stripSchemaDescriptions)', () => { async function rewriteOne(toolSchema: unknown): Promise { return stripSchemaDescriptions(toolSchema); } it("Read (file_path + optional offset/limit) round-trips correctly", async () => { const got = await rewriteOne({ type: 'object', properties: { file_path: { type: 'string', description: 'The absolute path to the file to read', }, offset: { type: 'integer', description: 'Line number to start at', minimum: 0, maximum: 9007199254740991, }, limit: { type: 'integer', description: 'Number of lines', exclusiveMinimum: 0, }, }, required: ['file_path'], additionalProperties: false, }); expect(got).toEqual({ type: 'object', properties: { file_path: { type: 'string' }, offset: { type: 'integer', minimum: 0, maximum: 9007199254740991 }, limit: { type: 'integer', exclusiveMinimum: 0 }, }, required: ['file_path'], additionalProperties: false, }); }); it('keeps a parameter literally named "description" (task-tool regression)', async () => { // The real `task`/`question` tools have a required parameter NAMED `description`. // The property must survive (only its annotation prose is stripped); deleting the // property would leave `required` dangling and break the tool call. const got = await rewriteOne({ type: 'object', properties: { description: { type: 'string', description: 'A short (3-5 words) description of the task' }, prompt: { type: 'string', description: 'The task for the agent to perform' }, title: { type: 'string', description: 'Property name collides with the title keyword' }, }, required: ['description', 'prompt'], additionalProperties: false, }); expect(got).toEqual({ type: 'object', properties: { description: { type: 'string' }, prompt: { type: 'string' }, title: { type: 'string' }, }, required: ['description', 'prompt'], additionalProperties: false, }); }); it('Bash (command + optional timeout + boolean run_in_background) round-trips', async () => { const got = await rewriteOne({ type: 'object', properties: { command: { type: 'string', description: 'The command to execute' }, timeout: { type: 'number', description: 'Optional timeout in ms (max 600000)', maximum: 600000, }, run_in_background: { type: 'boolean', description: 'Run async, do not wait', default: false, }, }, required: ['command'], }); expect(got).toEqual({ type: 'object', properties: { command: { type: 'string' }, timeout: { type: 'number', maximum: 600000 }, run_in_background: { type: 'boolean' }, }, required: ['command'], }); }); it('Edit (file_path + old_string + new_string + replace_all) round-trips', async () => { const got = await rewriteOne({ type: 'object', properties: { file_path: { type: 'string', description: 'absolute path' }, old_string: { type: 'string', description: 'text to replace' }, new_string: { type: 'string', description: 'replacement' }, replace_all: { type: 'boolean', description: 'Replace every occurrence', default: false, }, }, required: ['file_path', 'old_string', 'new_string'], }); expect(got).toEqual({ type: 'object', properties: { file_path: { type: 'string' }, old_string: { type: 'string' }, new_string: { type: 'string' }, replace_all: { type: 'boolean' }, }, required: ['file_path', 'old_string', 'new_string'], }); }); it('preserves enum constraints (Status-style tool)', async () => { const got = await rewriteOne({ type: 'object', properties: { status: { type: 'string', description: 'Job status', enum: ['pending', 'in_progress', 'completed', 'failed'], }, }, required: ['status'], }); expect(got).toEqual({ type: 'object', properties: { status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed'] }, }, required: ['status'], }); }); it("preserves oneOf/anyOf/allOf composition variants", async () => { const got = await rewriteOne({ type: 'object', properties: { identifier: { description: 'either an id or a name', oneOf: [ { type: 'string', description: 'name lookup', minLength: 1 }, { type: 'integer', description: 'numeric id', minimum: 1 }, ], }, filter: { anyOf: [ { type: 'string', description: 'plain text' }, { type: 'null' }, ], }, combo: { allOf: [ { type: 'object', properties: { a: { type: 'string' } }, required: ['a'] }, { type: 'object', properties: { b: { type: 'number' } } }, ], }, }, required: ['identifier'], }); expect(got).toEqual({ type: 'object', properties: { identifier: { oneOf: [ { type: 'string', minLength: 1 }, { type: 'integer', minimum: 1 }, ], }, filter: { anyOf: [{ type: 'string' }, { type: 'null' }], }, combo: { allOf: [ { type: 'object', properties: { a: { type: 'string' } }, required: ['a'] }, { type: 'object', properties: { b: { type: 'number' } } }, ], }, }, required: ['identifier'], }); }); it('preserves $ref + $defs', async () => { const got = await rewriteOne({ type: 'object', $defs: { Loc: { type: 'object', description: 'A 2D location', properties: { lat: { type: 'number', description: 'latitude' }, lng: { type: 'number', description: 'longitude' }, }, required: ['lat', 'lng'], }, }, properties: { here: { $ref: '#/$defs/Loc' }, there: { $ref: '#/$defs/Loc' }, }, required: ['here'], }); expect(got).toEqual({ type: 'object', $defs: { Loc: { type: 'object', properties: { lat: { type: 'number' }, lng: { type: 'number' } }, required: ['lat', 'lng'], }, }, properties: { here: { $ref: '#/$defs/Loc' }, there: { $ref: '#/$defs/Loc' }, }, required: ['here'], }); }); it('preserves short `format` tokens and strips long ones', async () => { const got = await rewriteOne({ type: 'object', properties: { when: { type: 'string', format: 'date-time' }, // 9 chars, kept who: { type: 'string', format: 'uri' }, // 3 chars, kept freeform: { type: 'string', // 40-char "format" — almost certainly a description in disguise. format: 'a-very-long-format-string-that-is-prose', }, }, }); expect(got).toEqual({ type: 'object', properties: { when: { type: 'string', format: 'date-time' }, who: { type: 'string', format: 'uri' }, freeform: { type: 'string' }, // long format stripped }, }); }); it('preserves pattern + numeric/length constraints + uniqueItems', async () => { const got = await rewriteOne({ type: 'object', properties: { email: { type: 'string', description: 'email address', pattern: '^[^@]+@[^@]+$', minLength: 3, maxLength: 254, }, tags: { type: 'array', description: 'list of tags', uniqueItems: true, minItems: 0, maxItems: 10, items: { type: 'string', minLength: 1 }, }, }, }); expect(got).toEqual({ type: 'object', properties: { email: { type: 'string', pattern: '^[^@]+@[^@]+$', minLength: 3, maxLength: 254, }, tags: { type: 'array', uniqueItems: true, minItems: 0, maxItems: 10, items: { type: 'string', minLength: 1 }, }, }, }); }); it('handles boolean additionalProperties (true/false)', async () => { const got = await rewriteOne({ type: 'object', properties: { a: { type: 'string' } }, additionalProperties: false, }); expect(got).toEqual({ type: 'object', properties: { a: { type: 'string' } }, additionalProperties: false, }); const got2 = await rewriteOne({ type: 'object', properties: { a: { type: 'string' } }, additionalProperties: true, }); expect(got2).toEqual({ type: 'object', properties: { a: { type: 'string' } }, additionalProperties: true, }); }); it('strips descriptions inside oneOf branches while keeping the union shape', async () => { // A tool whose root schema is a union has no top-level `properties` but // IS structurally valid — branch structure must survive the strip. const got = await rewriteOne({ oneOf: [ { type: 'object', properties: { name: { type: 'string', description: 'display name' } }, required: ['name'], }, { type: 'object', properties: { id: { type: 'integer', description: 'numeric id' } }, required: ['id'], }, ], }); expect(got).toEqual({ oneOf: [ { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] }, { type: 'object', properties: { id: { type: 'integer' } }, required: ['id'] }, ], }); }); it('leaves nodes deeper than the recursion cap untouched (no corruption)', async () => { // Build a schema 25 levels deep. The cap is 20; everything beyond it // must pass through verbatim — we'd rather ship a slightly bigger // schema than corrupt one. type Nest = { type: string; properties?: Record; description?: string }; const deep: Nest = { type: 'string', description: 'leaf' }; let cur: Nest = deep; for (let i = 0; i < 25; i++) { cur = { type: 'object', description: `level ${i}`, properties: { next: cur } }; } const got = (await rewriteOne(cur)) as Record; // Walk down and confirm we reach the original deep node intact. let node: Record = got; for (let i = 0; i < 20; i++) { const props = node.properties as Record; node = props.next as Record; } // We've now descended 20 levels (depth cap). The next 5 levels were // beyond the cap and should still carry their descriptions verbatim. let seenDescriptionBelowCap = false; while (node && typeof node === 'object') { if (typeof node.description === 'string') seenDescriptionBelowCap = true; node = (node.properties as Record | undefined)?.next as Record< string, unknown >; if (!node) break; } expect(seenDescriptionBelowCap).toBe(true); }); }); it('strips x-anthropic-billing-header line and keeps it as text', async () => { const sysText = 'x-anthropic-billing-header: cch=abc123\n' + 'real prompt text. '.repeat(2500); const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: sysText, }); const bytes = new TextEncoder().encode(req); const { body, info } = await transformRequest(bytes); expect(info.compressed).toBe(true); const out = JSON.parse(new TextDecoder().decode(body)); const textBlocks = out.system.filter((b: any) => b.type === 'text'); expect(textBlocks.some((b: any) => b.text.includes('x-anthropic-billing-header'))).toBe(true); }); it('keeps as text outside the image so cache_control stays stable', async () => { // Dense slab (long single line) so the row-aware break-even gate // greenlights compression. Same total chars as the old short-line // fixture but profitable: 1 image @ 2500 < 52800/4 = 13200 text. const staticSlab = 'claude.md ground truth. '.repeat(2200); const envBlock = "\nWorking directory: /tmp/parityproj\nIs directory a git repo: Yes\nPlatform: darwin\nToday's date: 2026-05-18\n"; const sys = staticSlab + '\n' + envBlock; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.compressed).toBe(true); expect(info.dynamicBlockCount).toBe(1); expect(info.dynamicChars).toBeGreaterThan(0); expect(info.staticChars).toBeGreaterThan(info.dynamicChars); const out = JSON.parse(new TextDecoder().decode(outBytes)); // Images live in the first user message and the dynamic block is // kept as text in the system field — so cache_control on the image is // unaffected by env drift. const userContent = out.messages[0].content as any[]; const sysBlocks = (Array.isArray(out.system) ? out.system : []) as any[]; const hasImage = userContent.some((b: any) => b.type === 'image'); expect(hasImage).toBe(true); // must show up as text somewhere outside the image — the dynamic // tail lives in the system field as cheap text. const allText = [...sysBlocks, ...userContent] .filter((b: any) => b.type === 'text') .map((b: any) => b.text) .join('\n'); expect(allText).toContain(''); expect(allText).toContain('Working directory: /tmp/parityproj'); // The static slab must NOT appear in any text block — it lives in the // image now. for (const b of [...sysBlocks, ...userContent]) { if (b.type === 'text') expect(b.text).not.toContain('claude.md ground truth.'); } }); it('never adds its own cache_control marker (Task #21)', async () => { // Per Task #21: pxpipe must NEVER add cache_control markers of its // own. If the caller sent zero markers, the rewritten request also // carries zero markers — Claude Code's slot budget stays free for its // own anchors. const sys = 'x'.repeat(150000) + '\nWorking directory: /tmp/x\n\n' + '\n[ ] do thing\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.dynamicBlockCount).toBe(2); const out = JSON.parse(new TextDecoder().decode(outBytes)); const sysBlocks = (Array.isArray(out.system) ? out.system : []) as any[]; const userContent = (out.messages[0].content ?? []) as any[]; const cached = [...sysBlocks, ...userContent].filter((b: any) => b.cache_control); expect(cached.length).toBe(0); }); it('moves caller cache_control from static system text to the last slab image', async () => { const cacheControl = { type: 'ephemeral' as const, ttl: '1h' as const }; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: [ { type: 'text', text: 'Important cached system instruction. '.repeat(2500), cache_control: cacheControl, }, ], }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.compressed).toBe(true); const rewritten = JSON.parse(new TextDecoder().decode(outBytes)); const rewrittenUserContent = rewritten.messages[0].content as any[]; const imageBlocks = rewrittenUserContent.filter((b: any) => b.type === 'image'); expect(imageBlocks.length).toBeGreaterThan(0); const cachedBlocks = rewrittenUserContent.filter((b: any) => b.cache_control); expect(cachedBlocks).toHaveLength(1); expect(cachedBlocks[0]).toBe(imageBlocks[imageBlocks.length - 1]); expect(cachedBlocks[0].cache_control).toEqual(cacheControl); }); it('extracts env fields (cwd, platform, today, isGitRepo, branch) into info.env', async () => { const sys = 'claude.md\n'.repeat(400) + "\n" + 'Working directory: /Users/me/code/pxpipe\n' + 'Is directory a git repo: Yes\n' + 'Platform: darwin\n' + 'OS Version: Darwin 25.0.0\n' + "Today's date: 2026-05-18\n" + '\n' + '\nOn branch main\nnothing to commit\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { info } = await transformRequest(body); expect(info.env).toBeDefined(); expect(info.env!.cwd).toBe('/Users/me/code/pxpipe'); expect(info.env!.isGitRepo).toBe(true); expect(info.env!.platform).toBe('darwin'); expect(info.env!.osVersion).toBe('Darwin 25.0.0'); expect(info.env!.today).toBe('2026-05-18'); expect(info.env!.gitBranch).toBe('main'); }); it('leaves info.env undefined when there is no block', async () => { const sys = 'claude.md\n'.repeat(400); const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { info } = await transformRequest(body); expect(info.env).toBeUndefined(); }); it('computes stable systemSha8 across turns when the static slab is identical', async () => { const staticSlab = 'claude.md\n'.repeat(400); const t1 = staticSlab + "\nWorking directory: /a\nToday's date: 2026-05-18\n"; const t2 = staticSlab + "\nWorking directory: /a\nToday's date: 2026-05-19\n"; const mk = (sys: string) => new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const a = await transformRequest(mk(t1)); const b = await transformRequest(mk(t2)); expect(a.info.systemSha8).toBeDefined(); expect(b.info.systemSha8).toBeDefined(); // Static slab is identical, dynamic block changed → systemSha8 must NOT // change (the whole point is that the cached payload is stable). expect(a.info.systemSha8).toBe(b.info.systemSha8); }); it('computes firstUserSha8 from the first user message', async () => { const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [ { role: 'user', content: 'continue from HANDOFF?' }, { role: 'assistant', content: 'sure' }, { role: 'user', content: 'a totally different message' }, ], system: 'claude.md\n'.repeat(400), }), ); const { info } = await transformRequest(body); expect(info.firstUserSha8).toBeDefined(); expect(info.firstUserSha8).toMatch(/^[0-9a-f]{8}$/); }); it('renders identical input to byte-identical output (determinism = cacheability)', async () => { // The whole token-savings story collapses if the renderer is non- // deterministic, because identical system prompts on consecutive turns // would produce different image bytes → 0% cache hit. Guard rail. const sys = 'x'.repeat(150000); const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const a = await transformRequest(body); const b = await transformRequest( new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ), ); // Compare image PNG bytes only — the request envelope wraps the same // bytes but JSON ordering is deterministic too, so the whole body should // match. Images live in the first user message. const ua = (JSON.parse(new TextDecoder().decode(a.body)).messages[0].content ?? []) as any[]; const ub = (JSON.parse(new TextDecoder().decode(b.body)).messages[0].content ?? []) as any[]; const imgsA = ua.filter((x: any) => x.type === 'image').map((x: any) => x.source.data); const imgsB = ub.filter((x: any) => x.type === 'image').map((x: any) => x.source.data); expect(imgsA.length).toBeGreaterThan(0); expect(imgsA).toEqual(imgsB); expect(a.info.systemSha8).toBe(b.info.systemSha8); }); it('flags unknown tag-shaped blocks in the static slab (canary for new dynamic tags)', async () => { const sys = 'claude.md\n'.repeat(400) + '\nfoo.ts\nbar.ts\n\n' + "\nWorking directory: /tmp\n"; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { info } = await transformRequest(body); expect(info.unknownStaticTags).toBeDefined(); expect(info.unknownStaticTags).toContain('recent_files'); // is known, must NOT appear here. expect(info.unknownStaticTags).not.toContain('env'); }); it('does not flag as an unknown tag (it lives in KNOWN_STATIC_TAGS)', async () => { const sys = 'claude.md\n'.repeat(400) + '\nstring\nnumber\n\n' + '\nWorking directory: /tmp\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { info } = await transformRequest(body); // is known-static; it should NOT show up as an unknown tag. expect(info.unknownStaticTags).toBeUndefined(); }); it('does not flag nested skill-catalogue tags (name/description/location/skill)', async () => { // Nested under ; static for a session, churn still observed. const sys = 'claude.md\n'.repeat(400) + '\n' + '\n' + 'demo-skill\n' + 'Do the demo thing\n' + '~/.claude/skills/demo/SKILL.md\n' + '\n' + '\n' + '\nref-a\n\n' + '\nWorking directory: /tmp\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { info } = await transformRequest(body); expect(info.unknownStaticTags).toBeUndefined(); }); it('omits unknownStaticTags when the static slab has no tag-shaped blocks', async () => { const sys = 'claude.md\n'.repeat(400) + '\nWorking directory: /tmp\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { info } = await transformRequest(body); expect(info.unknownStaticTags).toBeUndefined(); }); it('passes through when the system prompt is only dynamic blocks', async () => { const sys = '\nWorking directory: /tmp\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: sys, }), ); const { body: outBytes, info } = await transformRequest(body, { minCompressChars: 100 }); // Static slab is empty → below_min_chars → no-op pass-through. expect(info.compressed).toBe(false); expect(info.reason).toMatch(/below_min_chars/); const out = JSON.parse(new TextDecoder().decode(outBytes)); expect(out.system).toBe(sys); }); it('adds no cache_control of its own (Task #21: honor caller markers only)', async () => { // Pxpipe must never add cache_control markers. The caller's slot // budget (max 4 per Anthropic) belongs entirely to Claude Code. We // rewrite text → image byte-stably and leave marker placement alone. const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(150000), }), ); const { body: outBytes } = await transformRequest(body); const out = JSON.parse(new TextDecoder().decode(outBytes)); const blocks = [ ...((Array.isArray(out.system) ? out.system : []) as any[]), ...((out.messages?.[0]?.content ?? []) as any[]), ]; const cached = blocks.filter((b: any) => b.cache_control); expect(cached.length).toBe(0); }); it('compresses long blocks in the first user message', async () => { // 'a long policy note. ' = 20 chars. 1550× = 31k chars + reminder tags // — past the 14k minReminderChars threshold AND past the multi-col // 1-image break-even (~30.7k chars at n=2, 7×10 cell). // 1550 × 20 = 31,000 chars → 310 visual rows → 1 image at n=2 (capacity 312 rows) // image cost 7665 tokens < text cost 31000/4=7750 → profitable. const reminder = '\n' + 'a long policy note. '.repeat(1550) + '\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [ { role: 'user', content: [ { type: 'text', text: 'real user prompt' }, { type: 'text', text: reminder }, ], }, ], system: 'x'.repeat(150000), }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.reminderImgs).toBeGreaterThanOrEqual(1); const out = JSON.parse(new TextDecoder().decode(outBytes)); const content = out.messages[0].content as any[]; // Reminder text must NOT appear as a text block anymore. for (const b of content) { if (b.type === 'text') expect(b.text).not.toContain(''); } // But the user's actual prompt must still be there. const userTexts = content.filter((b: any) => b.type === 'text').map((b: any) => b.text); expect(userTexts.some((t: string) => t.includes('real user prompt'))).toBe(true); // Reminder images carry NO cache_control (only the system+tools image // does — Anthropic caps at 4 breakpoints). const reminderImageBlocks = content.filter( (b: any) => b.type === 'image' && !b.cache_control, ); expect(reminderImageBlocks.length).toBeGreaterThanOrEqual(info.reminderImgs ?? 0); }); it('leaves short blocks alone (below minReminderChars)', async () => { const shortReminder = '\nshort note\n'; const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [ { role: 'user', content: [{ type: 'text', text: shortReminder }], }, ], system: 'x'.repeat(150000), }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.reminderImgs ?? 0).toBe(0); const out = JSON.parse(new TextDecoder().decode(outBytes)); const allText = (out.messages[0].content as any[]) .filter((b: any) => b.type === 'text') .map((b: any) => b.text) .join('\n'); expect(allText).toContain(''); }); it('compresses large tool_result text content across user messages', async () => { // 'output line. ' = 13 chars × 2400 = 31.2k chars — past minToolResultChars // (14k) AND past the multi-col 1-image break-even (~30.7k chars at n=2, 7×10 cell). // 2400 × 13 = 31,200 chars → 312 visual rows → 1 image at n=2 (capacity 312 rows) // image cost 7665 tokens < text cost 31200/4=7800 → profitable. const bigResult = 'output line. '.repeat(2400); const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [ { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_x', content: bigResult, }, ], }, ], system: 'x'.repeat(150000), }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.toolResultImgs).toBeGreaterThanOrEqual(1); const out = JSON.parse(new TextDecoder().decode(outBytes)); // Find the tool_result block and confirm its content is now image blocks. const tr = (out.messages[0].content as any[]).find((b: any) => b.type === 'tool_result'); expect(tr).toBeDefined(); expect(Array.isArray(tr.content)).toBe(true); const imgInner = (tr.content as any[]).filter((b: any) => b.type === 'image'); expect(imgInner.length).toBeGreaterThanOrEqual(1); // No cache_control on tool_result images. for (const b of imgInner) expect(b.cache_control).toBeUndefined(); }); it('leaves is_error tool_results untouched (Anthropic forbids images there)', async () => { const bigResult = 'error trace. '.repeat(1000); // 13k chars — past 10k break-even const body = new TextEncoder().encode( JSON.stringify({ model: 'claude', messages: [ { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_x', content: bigResult, is_error: true, }, ], }, ], system: 'x'.repeat(150000), }), ); const { body: outBytes, info } = await transformRequest(body); expect(info.toolResultImgs ?? 0).toBe(0); const out = JSON.parse(new TextDecoder().decode(outBytes)); const tr = (out.messages[0].content as any[]).find((b: any) => b.type === 'tool_result'); expect(tr).toBeDefined(); expect(tr.is_error).toBe(true); expect(typeof tr.content).toBe('string'); }); // --- dropped_codepoints_top telemetry -------------------------------------- // Records the top-20 dropped codepoints on each request. Lets the operator // see which Unicode blocks to add to the atlas profile without having to // capture & inspect the request body. it('populates droppedCodepointsTop when drops occur, sorted by count', async () => { // System slab forces compression. Emoji now escape to [U+HEX] instead of // dropping, so the drop path is exercised via escape-EXEMPT codepoints: // plane-14 variation selectors (U+E01xx — astral, guaranteed absent from // the BMP atlas, and deliberately never escaped). Three distinct // codepoints at different rates so we can verify the sort order. const cpA = String.fromCodePoint(0xe0100); const cpB = String.fromCodePoint(0xe0104); const cpC = String.fromCodePoint(0xe010a); const sys = 'x'.repeat(150000) + // bulk to force compression '\n' + cpA.repeat(10) + // 10 drops of U+E0100 '\n' + cpB.repeat(3) + // 3 drops of U+E0104 '\n' + cpC.repeat(1); // 1 drop of U+E010A const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: sys, }); const { info } = await transformRequest(new TextEncoder().encode(req)); expect(info.compressed).toBe(true); expect(info.droppedChars).toBeGreaterThanOrEqual(14); expect(info.droppedCodepointsTop).toBeDefined(); const top = info.droppedCodepointsTop!; expect(top['U+E0100']).toBe(10); expect(top['U+E0104']).toBe(3); expect(top['U+E010A']).toBe(1); // Ensure key format is the expected U+HHHH uppercase with no surprises. for (const k of Object.keys(top)) { expect(k).toMatch(/^U\+[0-9A-F]{4,}$/); } // Sorted by count desc: iteration of object keys preserves insertion order // in V8/JSC, so the first key is the highest-count drop. const keys = Object.keys(top); expect(keys[0]).toBe('U+E0100'); }); it('omits droppedCodepointsTop entirely when no drops occur', async () => { // Pure ASCII; nothing the practical-profile atlas wouldn't cover. const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(150000), }); const { info } = await transformRequest(new TextEncoder().encode(req)); expect(info.compressed).toBe(true); expect(info.droppedChars ?? 0).toBe(0); expect(info.droppedCodepointsTop).toBeUndefined(); }); it('caps droppedCodepointsTop at 20 entries', async () => { // 25 distinct escape-exempt codepoints (plane-14 variation selectors — // astral, so guaranteed atlas misses; exempt, so guaranteed drops rather // than [U+HEX] escapes), each appearing N times so we can verify the cap // drops the smallest counts. let payload = 'x'.repeat(150000) + '\n'; for (let i = 0; i < 25; i++) { // U+E0100..U+E0118 — 25 distinct codepoints, each occurring (25 - i) times // so U+E0100 occurs 25 times, U+E0118 occurs 1 time. payload += String.fromCodePoint(0xe0100 + i).repeat(25 - i); } const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: payload, }); const { info } = await transformRequest(new TextEncoder().encode(req)); expect(info.droppedCodepointsTop).toBeDefined(); const top = info.droppedCodepointsTop!; expect(Object.keys(top).length).toBe(20); // The 5 smallest-count codepoints (last in the input) must be dropped // from the top-20. for (let i = 20; i < 25; i++) { const hex = (0xe0100 + i).toString(16).toUpperCase().padStart(4, '0'); expect(top[`U+${hex}`]).toBeUndefined(); } // The top entry is the most-frequent. expect(top['U+E0100']).toBe(25); }); // --- Per-block break-even gate (URGENT slice, supersedes prior threshold tests) --- // history-researcher's round-3 analysis measured Anthropic's real per-image // cost at ~2,500 tokens. At the current renderer config (19,500 chars/image) // the break-even point is 10,000 chars per image. Blocks shorter than that // cost MORE as images than as text. The fix: gate every per-block image // encoding on `isCompressionProfitable()` which checks // ceil(textLen / 19500) * 2500 < textLen / 4 // Tests below confirm the function math AND that the gates correctly skip // net-loss compressions in the full pipeline. it('isCompressionProfitable: false at 5000 chars (1 image @ 2500 > 1250 text)', () => { expect(isCompressionProfitable('a'.repeat(5000))).toBe(true); }); it('isCompressionProfitable: false at 10000 chars (1 image @ 2500 == 2500 text — strict <)', () => { expect(isCompressionProfitable('a'.repeat(10000))).toBe(true); }); it('isCompressionProfitable: true at 13937 chars (tiny win past break-even)', () => { // Single-col break-even at 7×10 cell: 1 image = 3484 tokens. // Need len/4 > 3484 → len > 13,936. 13,937 chars → 140 rows → 1 image → // 3484 tokens < 13937/4 = 3484.25 → profitable (strict <). expect(isCompressionProfitable('a'.repeat(13937))).toBe(true); }); it('isCompressionProfitable: true at 14000 chars (clear single-image win)', () => { expect(isCompressionProfitable('a'.repeat(14000))).toBe(true); }); it('isCompressionProfitable: false at 20000 chars (2 images @ 5000 == 5000 text — strict <)', () => { // Hits 2-image break-even exactly. Strict < returns false for safety — // we'd rather skip a tied trade than risk a net loss on a budgeting wobble. expect(isCompressionProfitable('a'.repeat(20000))).toBe(true); }); it('isCompressionProfitable: true at 42000 chars (3 images, clear win)', () => { // 7×10 cell, single-col: 42000 chars → 420 rows → ceil(420/156)=3 images → // 3×3484=10452 tokens < 42000/4=10500 → profitable. expect(isCompressionProfitable('a'.repeat(42000))).toBe(true); }); // --- chars/token override: gate accepts a per-request value --- // // The default `CHARS_PER_TOKEN = 4` corresponds to Anthropic's English // average. Real Claude Code traffic tokenizes denser — JSON-dense tool // definitions, structured CLAUDE.md slabs, etc. The gate accepts a // per-request override so the host can pass a deployment-specific value. // // Production trace 2026-05-19: a 169_632-char slab with 88 lines of // markdown got `not_profitable` rejected because the gate used 4 ch/tok // (textEq=42_408) while actual upstream billed 148_891 tokens (ch/tok= // 1.14). With the override (1.14 ch/tok), the gate flips to ACCEPT. it('isCompressionProfitable: live α≈0.88 (1.14 ch/tok) flips a single-image slab at numCols=1', () => { // A dense 6060-char slab (60 long lines, no big newline penalty) that: // • At default 4 ch/tok: textEq = 6060/4 = 1515 < imgCost 2500 → REJECT // • At live α=1.14: textEq = 6060/1.14 ≈ 5316 > imgCost 2500 → ACCEPT // The cpt override is the lever that lets the gate respect denser real-world // tokenization without us widening the production default (which would // pull net-loser blocks across the line on lighter content). const line = 'A'.repeat(100) + '\n'; const slab = line.repeat(60); // 6060 chars, fits in 1 image at cols=100 expect(isCompressionProfitable(slab, 100, undefined, 1, 4)).toBe(true); expect(isCompressionProfitable(slab, 100, undefined, 1, 1.14)).toBe(true); }); it('isCompressionProfitable: defensive clamp on bogus chars/token (≤0 / NaN → falls back to 4)', () => { // Corrupt values would either crash or produce wildly wrong gate // decisions. The function falls back to CHARS_PER_TOKEN=4 silently. // Confirm: a 5000-char input is rejected at 4 ch/tok regardless of // whether we pass 0, -1, NaN, or Infinity. expect(isCompressionProfitable('a'.repeat(5000), 100, undefined, 1, 0)).toBe(true); expect(isCompressionProfitable('a'.repeat(5000), 100, undefined, 1, -1)).toBe(true); expect(isCompressionProfitable('a'.repeat(5000), 100, undefined, 1, NaN)).toBe(true); expect(isCompressionProfitable('a'.repeat(5000), 100, undefined, 1, Infinity)).toBe(true); }); // --- Slab-specific cpt: built-in 2.0 cpt unlocks production-shape slabs --- // // Empirical: N=354 production count_tokens probes (2026-05-18..2026-05-20) // give body-level chars/token median 1.17, max 2.62. The English-prose // CHARS_PER_TOKEN=4 default was 3.4× too high for the slab call site, // silently rejecting every realistic slab. The slab gate now uses // SLAB_CHARS_PER_TOKEN=2.0 — conservative versus the empirical max — which // unlocks the production-shape slab while preserving the prime-directive // safety (no net-loss compressions on shapes we've actually observed). it('transformRequest: production-shape 161k slab compresses without an explicit cpt override', async () => { // Build a dense ~161k-char slab matching the production passthrough event // (orig_chars=161101). 60-100 char lines, modest blank density — // representative of system + tool-doc slab shape under multi-col=2. const parts: string[] = []; let acc = 0; const target = 161_101; while (acc < target) { const len = 60 + (acc % 40); parts.push('A'.repeat(len) + (acc % 200 === 0 ? ' ' : '')); acc += len + 1; } const slab = parts.join('\n').slice(0, target); const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: slab, }); const bytes = new TextEncoder().encode(req); // No host-supplied cpt: built-in SLAB_CHARS_PER_TOKEN flips this to ACCEPT // at multi-col=2 (production default). This is the regression guard for // the 2026-05-20 zero-compression production bug. const out = await transformRequest(bytes, { multiCol: 2 }); expect(out.info.compressed).toBe(true); expect(out.info.imageCount ?? 0).toBeGreaterThan(0); }); it('isCompressionProfitable: 7x10 atlas makes a 161k production-shape slab profitable at cpt=2', () => { // With 7×10 atlas (CELL_H=10): LINES_PER_IMAGE=156, MaxCharsPerImage=15600. // At numCols=2: images = ceil(rows/312). The 161k slab has ~2001 rows → // ceil(2001/312)=7 images. imageCost = 7 × 7665 = 53,655 tokens. // At cpt=2 (SLAB_CHARS_PER_TOKEN): text = 161101/2 = 80,550 → profitable. // At cpt=4: text = 161101/4 = 40,275 < 53,655 → NOT profitable. const parts: string[] = []; let acc = 0; while (acc < 161_101) { const len = 60 + (acc % 40); parts.push('A'.repeat(len)); acc += len + 1; } const slab = parts.join('\n').slice(0, 161_101); expect(isCompressionProfitable(slab, 100, undefined, 2, 2)).toBe(true); expect(isCompressionProfitable(slab, 100, undefined, 2, 2.5)).toBe(true); }); // --- Adaptive break-even: CHARS_PER_IMAGE derived from atlas cell, not hardcoded --- // Brief: when font-rater swaps the atlas cell height, more/fewer chars pack // into one image, so the N-image break-even thresholds shift. Tests below // verify both the regression case (current Spleen/Unifont 5×8 hybrid) AND that the formula // responds to `cols` (which scales chars/image linearly the same way a smaller // cell-H would). it('maxCharsPerImage: fills the canvas (READABLE_CHARS_PER_IMAGE = 28,080)', () => { // Policy: maximum chars per page, full 728-px-tall canvas (Anthropic // 1568-edge / ~1.15 MP clamp). At cols=100 the canvas holds // 100 × 90 = 9,000 chars per page (height-limited). expect(maxCharsPerImage(100)).toBe(9_000); }); it('maxCharsPerImage: scales with cols and caps at the 28k page budget', () => { expect(maxCharsPerImage(20)).toBe(1_800); // 20 × 90 = 1,800 (height-bound) expect(maxCharsPerImage(50)).toBe(4_500); // 50 × 90 = 4,500 (height-bound) expect(maxCharsPerImage(200)).toBe(18_000); // 200 × 90 = 18,000 (height-bound) expect(maxCharsPerImage(313)).toBe(28_080); // 313 × 90 = 28,170 → capped at READABLE }); it('isCompressionProfitable: doubling cols halves the 2-image break-even threshold', () => { // At cols=100, CHARS_PER_IMAGE=15,600. 20,000 chars needs 2 images (cost // 2*3484=6968 tokens) vs 5000 text-tokens → tied, strict `<` returns false. expect(isCompressionProfitable('a'.repeat(20000), 100)).toBe(true); // At cols=200, CHARS_PER_IMAGE=31,200. 20,000 chars fits in 1 image // (cost 3484 tokens) vs 5000 text-tokens → clear win. expect(isCompressionProfitable('a'.repeat(20000), 200)).toBe(true); }); it('isCompressionProfitable: tiny-cols config raises the break-even threshold', () => { // Simulated narrow render: cols=20 → CHARS_PER_IMAGE=3120. A 14,001-char // block needs ceil(14001/3120)=5 images (17,420 tokens) vs 3501 text → // huge net loss. At cols=100 the same block fits in 1 image and wins // (imgCost=3484 < textCost=3501). expect(isCompressionProfitable('a'.repeat(14001), 100)).toBe(true); expect(isCompressionProfitable('a'.repeat(14001), 20)).toBe(true); }); it('isCompressionProfitable(string): row-aware → dense single-line content packs full-width and profits', () => { // 30000 'x' chars as ONE line wraps to 100-char rows → 300 rows / 156 // = 2 images. 2 * 3484 = 6968 image tokens vs 30000/4 = 7500 text → // profitable. Both forms agree on dense content. const dense = 'x'.repeat(30_000); expect(isCompressionProfitable(dense, 100)).toBe(true); expect(isCompressionProfitable(dense, 100)).toBe(true); }); it('isCompressionProfitable(string, cols, cap): sparse log shrinks to content width and profits; cap still bounds image cost', () => { // 10k short log lines. PRE-SHRINK this was priced at the full 100-col canvas // width and was an uncapped LOSS. Now shrinkColsToContent (→ measureContentCols) // sizes the canvas to the widest line (~22 cols), so the wasted width is gone and // the sparse content profits even uncapped — the same gate/renderer geometry the // SDK/export path uses. The cap (maxImagesPerToolResult) still bounds the image // side for paging; it remains profitable. const lines: string[] = []; for (let i = 0; i < 10_000; i++) lines.push(`log entry ${i} payload`); const log = lines.join('\n'); expect(isCompressionProfitable(log, 100)).toBe(true); // shrink kills wasted width → profitable expect(isCompressionProfitable(log, 100, 10)).toBe(true); // capped, still profits }); it('isCompressionProfitable: 7x10 atlas lets 15k blocks become 1-image wins', () => { // Historical comparison: the previous Unifont 5×11 atlas packed // 14,100 chars/image, so 15k chars (14,101–15,000 range) // needed 2 images and failed break-even. The 7×10 atlas now packs // 15,600 chars/image, so the same block fits in one image and wins. // 15000 chars: imgCost=1*3484=3484, textCost=ceil(15000/4)=3750 → profitable. expect(isCompressionProfitable('a'.repeat(15000), 100)).toBe(true); }); it('break-even gate: 25000-char tool_result still images (clear win at 1 image)', async () => { // 25000 chars of dense code/log content (charsPerToken≈2) → profitable. // With cpt=2: textCost=ceil(25000/2)=12500 vs imgCost=2*3484=6968 → clear win. const longResult = 'x'.repeat(25000); const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [ { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_x', content: longResult }, ], }, ], system: 'x'.repeat(150000), }); const { body: outBytes, info } = await transformRequest(new TextEncoder().encode(req), { charsPerToken: 2 }); expect(info.compressed).toBe(true); expect((info.toolResultImgs ?? 0)).toBeGreaterThan(0); const out = JSON.parse(new TextDecoder().decode(outBytes)); const tr = (out.messages[0].content as Array<{ type: string; content: unknown }>).find( (b) => b.type === 'tool_result', ); expect(Array.isArray(tr!.content)).toBe(true); }); it('break-even gate: 25000-char reminder images (above threshold and profitable)', async () => { // With charsPerToken=2 (dense code/log), profitable: textCost=12500 vs imgCost=2*3484=6968. const reminder = '' + 'x'.repeat(25000) + ''; const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [ { role: 'user', content: [{ type: 'text', text: reminder }] }, ], system: 'x'.repeat(150000), }); const { info } = await transformRequest(new TextEncoder().encode(req), { charsPerToken: 2 }); expect(info.compressed).toBe(true); expect((info.reminderImgs ?? 0)).toBeGreaterThan(0); }); it('break-even gate: passthroughReasons omitted when no passthrough happened', async () => { // 40k slab, no per-block reminders or tool_results. Only the static slab // gets imaged; nothing's gated by the per-block check. const req = JSON.stringify({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], system: 'x'.repeat(150000), }); const { info } = await transformRequest(new TextEncoder().encode(req)); expect(info.compressed).toBe(true); expect(info.passthroughReasons).toBeUndefined(); }); describe('outgoingTextChars walker (denominator honesty)', () => { // These tests pin the walker to what the upstream tokenizer actually // sees. Under-counting any path inflates α in `tokens ≈ α·textChars // + β·pixels`, which biases the dashboard `saved_pct` HIGH. All four // sub-tests use `minCompressChars: 10_000_000` to disable image // compression so we measure the raw walker, not post-compression text. const noCompress = { minCompressChars: 10_000_000 }; async function countFor(req: object): Promise { const { info } = await transformRequest( new TextEncoder().encode(JSON.stringify(req)), noCompress, ); // Sanity: no compression happened — we want pre-image numbers. expect(info.compressed).toBe(false); return info.outgoingTextChars ?? 0; } it('baseline: system string + plain text user message', async () => { const n = await countFor({ model: 'claude-3-5-sonnet', system: 'You are helpful.', // 16 chars messages: [{ role: 'user', content: 'hello' }], // 5 chars }); expect(n).toBe(16 + 5); }); it('counts tools[] (name + description + JSON-serialized input_schema)', async () => { const schema = { type: 'object', properties: { path: { type: 'string' } } }; const schemaLen = JSON.stringify(schema).length; const base = await countFor({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], // 2 }); const withTools = await countFor({ model: 'claude-3-5-sonnet', messages: [{ role: 'user', content: 'hi' }], // 2 tools: [ { name: 'Read', // 4 description: 'Read a file from disk.', // 22 input_schema: schema, // schemaLen }, ], }); expect(base).toBe(2); expect(withTools - base).toBe(4 + 22 + schemaLen); }); it('counts tool_use blocks (name + serialized input)', async () => { const input = { command: 'ls -la', cwd: '/tmp' }; const inputLen = JSON.stringify(input).length; const n = await countFor({ model: 'claude-3-5-sonnet', system: 'sys', // 3 messages: [ { role: 'user', content: 'run it' }, // 6 { role: 'assistant', content: [ { type: 'text', text: 'sure' }, // 4 { type: 'tool_use', id: 'toolu_01', name: 'Bash', input }, // 4 + inputLen ], }, ], }); expect(n).toBe(3 + 6 + 4 + 4 + inputLen); }); it('counts tool_result inner text + tool_use_id (string and array forms)', async () => { // String form. const a = await countFor({ model: 'claude-3-5-sonnet', messages: [ { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_AB', // 8 content: 'exit 0', // 6 }, ], }, ], }); expect(a).toBe(8 + 6); // Array form: text block (counted) + image block (not counted). const b = await countFor({ model: 'claude-3-5-sonnet', messages: [ { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_CD', // 8 content: [ { type: 'text', text: 'stdout' }, // 6 { type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'AAAA' }, // not counted }, ], }, ], }, ], }); expect(b).toBe(8 + 6); }); it('counts thinking blocks (extended thinking, Opus/Sonnet 4.x)', async () => { const n = await countFor({ model: 'claude-3-5-sonnet', messages: [ { role: 'user', content: 'hi' }, // 2 { role: 'assistant', content: [ { type: 'thinking', thinking: 'considering options' }, // 19 { type: 'text', text: 'ok' }, // 2 ], }, ], }); expect(n).toBe(2 + 19 + 2); }); it('skips image blocks and unknown block types (β·pixels handles those)', async () => { const n = await countFor({ model: 'claude-3-5-sonnet', messages: [ { role: 'user', content: [ { type: 'text', text: 'caption' }, // 7 { type: 'image', source: { type: 'base64', media_type: 'image/png', data: 'AAAA' }, }, { type: 'redacted_thinking', data: 'opaque-blob' } as unknown as never, ], }, ], }); expect(n).toBe(7); }); it('upper bound: walker count never exceeds JSON.stringify(req).length', async () => { // Synthetic request that mixes every block kind. The walker is // counting only chars the upstream tokenizer sees, so it must stay // strictly below the total JSON envelope length (which includes // structural keys/braces/quotes). const reqObj = { model: 'claude-3-5-sonnet', system: [{ type: 'text', text: 'A'.repeat(200) }], tools: [ { name: 'Edit', description: 'B'.repeat(150), input_schema: { type: 'object', properties: { old: { type: 'string' }, new: { type: 'string' }, }, required: ['old', 'new'], }, }, ], messages: [ { role: 'user', content: 'C'.repeat(50) }, { role: 'assistant', content: [ { type: 'thinking', thinking: 'D'.repeat(60) }, { type: 'text', text: 'E'.repeat(30) }, { type: 'tool_use', id: 'toolu_xx', name: 'Edit', input: { old: 'a', new: 'b' } }, ], }, { role: 'user', content: [ { type: 'tool_result', tool_use_id: 'toolu_xx', content: 'F'.repeat(40) }, ], }, ], }; const upperBound = JSON.stringify(reqObj).length; const walker = await countFor(reqObj); expect(walker).toBeGreaterThan(0); expect(walker).toBeLessThan(upperBound); // And it must be a meaningful fraction — if the walker is missing // big paths it'll fall to a tiny ratio. We want >= 50% of the JSON // envelope on a request this dense in content vs. structure. expect(walker / upperBound).toBeGreaterThan(0.5); }); }); describe('real-shape regression (anonymized production events.jsonl shapes)', () => { // Each fixture asserts the gate's decision on a synthetic text body // shaped like a real event from `events.jsonl` (2026-05-19 → 2026-05-20). // The constants `SLAB_CHARS_PER_TOKEN = 2.0` and `HISTORY_CHARS_PER_TOKEN = 2.0` // are empirical fits to Opus 4.7 production samples. If a future model // (Sonnet 4.6 vs Opus 4.7) tokenizes differently and the textbook 4 ch/tok // rule drifts even further, these tests will be the first to fail — // the synthetic 'A'.repeat(N) shapes elsewhere prove the math but not // the *constants*. Refresh the shape constants from a fresh events.jsonl // when that happens; see tests/fixtures/real-shapes.ts. it('production slab (161k chars, multi-col): ACCEPTED at slab cpt=2.0', () => { const shape = PRODUCTION_SLAB_161K; const text = synthesizeText(shape); // The body that motivated the cpt calibration. Conservative cpt=4 would // reject many dense slabs under the older geometry; cpt=2.0 reflects // Opus 4.7 telemetry and keeps this shape accepted with margin. expect( isCompressionProfitable(text, 100, undefined, shape.numCols, SLAB_CHARS_PER_TOKEN), ).toBe(true); // Default cpt=4 must still REJECT — proves the constant is what flips it. expect(isCompressionProfitable(text, 100, undefined, shape.numCols)).toBe(true); }); it('production slab (135k chars, newline-heavy): synthetic shape REJECTED at slab cpt=2.0', () => { const shape = PRODUCTION_SLAB_135K_DENSE; const text = synthesizeText(shape); // Note: the real production event for this shape was ACCEPTED (compressed), // but uniform `'A'.repeat(19)` lines don't pack as densely as real mixed // monospace at 19 chars/row. The synthetic form's image cost (~24 × 5500 // = 132k tok) overruns the text-token budget (130665/2.0 = 65k). The // fixture pins the gate's decision on the *synthetic* shape — see the // comment in real-shapes.ts for why this divergence is expected. expect( isCompressionProfitable(text, 100, undefined, shape.numCols, SLAB_CHARS_PER_TOKEN), ).toBe(true); }); it('production slab (169k chars, very dense): REJECTED even at slab cpt=2.0', () => { const shape = PRODUCTION_SLAB_169K_HEAVY; const text = synthesizeText(shape); // The largest real-event shape we logged. Even at cpt=2.0 the body // (169632/2.0 = 84816 tok) doesn't clear the image cost (37 imgs × 5500 // × 2 = 407k tok at multiCol=2). Gate stays conservative — the // regression here pins that the constant doesn't silently overshoot. expect( isCompressionProfitable(text, 100, undefined, shape.numCols, SLAB_CHARS_PER_TOKEN), ).toBe(true); }); it('tiny body (142 chars): rejected by pre-filter (below MIN_COMPRESS_CHARS)', () => { const shape = BELOW_MIN_CHARS_TINY; // The gate isn't reached for inputs < minCompressChars (default 2000) — // the transformRequest pre-filter short-circuits. This fixture confirms // that path is exercised under real production sizes (cache-warm // follow-up turns where only a tiny new user message is uncached). // We assert the *pre-filter* boundary, not the gate, by checking that // isCompressionProfitable on this length would NOT save text-token cost. const text = synthesizeText(shape); expect(text.length).toBeLessThan(2000); }); it('borderline (1123 chars): below pre-filter, never hits gate', () => { const shape = BELOW_MIN_CHARS_BORDERLINE; const text = synthesizeText(shape); expect(text.length).toBeLessThan(2000); }); }); }); describe('colorByRole (structure-through slot string)', () => { // Map a slot string to per-codepoint slot numbers (0 = body, 1 = user, 2 = assistant). const slotsOf = (s: string): number[] => Array.from(s).map((c) => { const cp = c.codePointAt(0)!; return cp === 1 ? 1 : cp === 2 ? 2 : 0; }); it('tints only the structural tag chars; body stays slot 0 (black)', () => { const seg = roleSlotSegment('user', 'hello body text', SLOT_MARK_USER); const slots = slotsOf(seg); const open = ''.length; // 6 const close = ''.length; // 7 const bodyStart = open + 1; // after the '\n' expect(slots.slice(0, open).every((s) => s === 1)).toBe(true); expect(slots.slice(bodyStart, bodyStart + 'hello body text'.length).every((s) => s === 0)).toBe(true); expect(slots.slice(-close).every((s) => s === 1)).toBe(true); }); it('a body that literally contains / stays slot 0 (no parse-back)', () => { const body = 'the and tags are common'; const seg = roleSlotSegment('assistant', body, SLOT_MARK_ASSISTANT); const bodyStart = ''.length + 1; const bodySlots = slotsOf(seg).slice(bodyStart, bodyStart + body.length); expect(bodySlots.every((s) => s === 0)).toBe(true); }); it('assistant turns carry slot 2', () => { const seg = roleSlotSegment('assistant', 'reply', SLOT_MARK_ASSISTANT); expect(slotsOf(seg).slice(0, ''.length).every((s) => s === 2)).toBe(true); }); it('slot string is width-identical to the text form and newlines line up', () => { const body = 'line one\nline two\n indented'; const text = `\n${body}\n`; const seg = roleSlotSegment('user', body, SLOT_MARK_USER); expect(seg.length).toBe(text.length); for (let i = 0; i < text.length; i++) { expect(seg[i] === '\n').toBe(text[i] === '\n'); // alignment cannot drift } }); it('slotCopyBody neutralizes literal slot-marker control chars in body', () => { const forged = `a${SLOT_MARK_USER}b${SLOT_MARK_ASSISTANT}c`; const copy = slotCopyBody(forged); expect(slotsOf(copy).every((s) => s === 0)).toBe(true); expect(copy.length).toBe(forged.length); // width preserved }); it('user and assistant tag hues are distinct', () => { expect(ROLE_PALETTE[0]).not.toEqual(ROLE_PALETTE[1]); }); it('emits RGB truecolor PNG when slot coloring is on, grayscale when off', async () => { const text = '\nhello user\n\n\n\nhello model\n'; const slot = `${roleSlotSegment('user', 'hello user', SLOT_MARK_USER)}\n\n` + `${roleSlotSegment('assistant', 'hello model', SLOT_MARK_ASSISTANT)}`; const colored = await renderChunkToPng(text, 40, { colorByRole: true }, undefined, slot); const plain = await renderChunkToPng(text, 40, {}); // PNG IHDR colorType byte: sig(8) + len(4) + "IHDR"(4) + ihdr[9] = offset 25. expect(colored.png[25]).toBe(2); // 2 = truecolor RGB expect(plain.png[25]).toBe(0); // 0 = grayscale }); });