@oh-my-pi/snapcompact 15.12.4 → 15.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,16 +2,14 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [15.12.1] - 2026-06-12
6
-
7
- ### Changed
8
-
9
- - `serializeConversation` now skips tool call/result pairs whose result is flagged contextually useless (`useless: true`, non-error), so archived frames stop carrying zero-match searches and timed-out waits
5
+ ### Breaking Changes
10
6
 
11
- ## [15.11.7] - 2026-06-12
7
+ - Renamed every export to drop the `snapcompact`/`Snapcompact`/`SNAPCOMPACT_` qualifier — the package is meant to be consumed via `import * as snapcompact from "@oh-my-pi/snapcompact"`. Functions: `snapcompactCompact` → `compact`, `renderSnapcompactFrame` → `render`, `snapcompactGeometry` → `geometry`, `normalizeForSnapcompact` → `normalize`, `serializeSnapcompactConversation` → `serializeConversation`, `snapcompactImages` → `images`, `getPreservedSnapcompactArchive` → `getPreservedArchive`, `isSnapcompactShape` → `isShape`, `resolveSnapcompactShape` → `resolveShape`, `createSnapcompactFileOps` → `createFileOps`, `computeSnapcompactFileLists` → `computeFileLists`, `upsertSnapcompactFileOperations` → `upsertFileOperations`. Types: `SnapcompactShape` → `Shape`, `SnapcompactFrame` → `Frame`, `SnapcompactArchive` → `Archive`, `SnapcompactGeometry` → `Geometry`, `SnapcompactOptions` → `Options`, `SnapcompactSerializeOptions` → `SerializeOptions`, `SnapcompactFileOperations` → `FileOperations`, `SnapcompactCompactionDetails`/`Preparation`/`Result` → `CompactionDetails`/`CompactionPreparation`/`CompactionResult`, `SnapcompactConvertToLlm` → `ConvertToLlm`. Constants: `SNAPCOMPACT_X` → `X` (`SHAPES`, `FRAME_SIZE`, `MAX_FRAMES`, `FRAME_TOKEN_ESTIMATE`, `PRESERVE_KEY`, `TOOL_RESULT_MAX_CHARS`, `TOOL_ARG_MAX_CHARS`, `TOOL_CALL_MAX_CHARS`, `TRUNCATE_HEAD_RATIO`, `DIM_ON`, `DIM_OFF`).
8
+ - Changed `renderSnapcompactFrame` output from `png: Uint8Array` to `data: string` base64, requiring consumers to read frame payloads from `frame.data`
12
9
 
13
10
  ### Added
14
11
 
12
+ - Added two spacing-tuned frame variants to `SHAPE_VARIANTS`: `8on22-bw` (8x13 glyphs on a 22px pitch — extra line spacing) and `11on16-bw` (8x13 glyphs on an 11px advance — extra letter spacing). Both pin the indexed `stretch: false` path, so the native renderer draws natural-size glyphs on the padded cell box (the Rust path already advances by `cellWidth` and the new variants validate horizontal padding)
15
13
  - Added `SHAPE_VARIANTS`, the catalog of research-eval frame variants the native renderer reproduces faithfully (`8x8r`/`8x8u`/`6x6u`/`5x8` × `sent`/`bw`), with `ShapeVariantName`, `SHAPE_VARIANT_NAMES`, and the `isShapeVariantName` guard
16
14
  - `resolveShape(api, variant?)` now accepts an explicit variant name (or `"auto"`); forced variants keep their geometry but are re-priced for the target provider's image billing (token estimate and OpenAI `original` detail hint)
17
15
  - Added the six research-eval winning frame variants to `SHAPE_VARIANTS`: `6x12-dim` (Claude fable), `8x13-bw` (Opus), `8on16-bw` (GPT grid runner-up), `doc-8on16-bw` (GPT), `doc-8on16-sent` (GLM), and `doc-8on16-sent-dim` (Gemini/Kimi), backed by new `Shape` fields `stretch` (disable Lanczos stretch: natural glyphs on a larger cell pitch), `columns` (two word-wrapped newspaper columns), `stopwordDim`, and the X.org `6x12`/`8x13` fonts
@@ -20,34 +18,7 @@
20
18
  - `resolveShape` now also resolves an ideal **frame size** per model line, and billing estimates come from verified per-family formulas instead of flat 1568px constants: Anthropic bills 28px patches capped at 4,784 visual tokens (+5% margin), Gemini 3.x bills a fixed 1,120-token `media_resolution` budget per image at any pixel size, and OpenAI bills 32px patches × 1.2 under the 10,000-patch `detail: "original"` budget. High-res Claude lines (Opus 4.7+, Fable, Mythos — native 2576px-edge ingestion) get 1932px frames (same recall and cost, a third fewer frames); Gemini gets 2048px frames (+70% chars per frame at the same bill); GPT and Kimi stay at 1568px (area-proportional billing and a model-side 1792px processor cap, respectively). `idealShapeVariant` now returns an `IdealShape` (`{ variant, frameSize? }`)
21
19
  - Added per-provider image-count budgets: `PROVIDER_IMAGE_BUDGETS`, `DEFAULT_PROVIDER_IMAGE_BUDGET`, `providerImageBudget()`, and `providerFrameBudget()` (the image budget clamped to `MAX_FRAMES`). OpenRouter is capped at its measured hard limit of 8 images per request (excess images are silently dropped with no error); unknown providers get a safe floor of 5
22
20
  - Added `Archive.textTail`: archive content past the frame budget is no longer dropped — `compact()` stops rendering at the budget and keeps the newest unframed slice as verbatim text on the summary (capped at two frame capacities with middle elision, counted into `truncatedChars` when elided). The tail persists in `preserveData` and is folded back into frames by the next compaction
23
-
24
- ### Changed
25
-
26
- - Frames are no longer padded to a square: the native renderer clips each PNG's height to the text rows actually printed, so a partially filled frame (typically the newest) bills only the pixel rows it uses
27
- - **Changed the OpenAI default shape from `6x6u-sent` to `8on16-bw`.** A production-regime mono eval (gpt-5.5, the full 800k-char SQuAD flow in one request, n=50) scored the old dense default f1 .602 vs .851 for `8on16-bw` rendered by the production pipeline, at near-equal total cost (the dense cells burned the frame savings on reasoning tokens); chunked exp14 had already scored `8on16-bw` .906. `SHAPES.openaiDense` is renamed to `SHAPES.openai`
28
- - **Changed the Google default shape from `8x8r-sent` to `doc-8on16-sent-dim`.** Production-rendered mono eval on gemini-3.5-flash (400k chars, one request, n=25): f1 .900 vs .853 for the repeated grid at lower cost, agreeing with the chunked round-2 winner
29
- - **Changed the Anthropic default shape from `8x8r-bw` to `6x12-dim`.** Production mono eval on claude-fable (400k chars, one request, n=25): f1 .840 vs .877 for the repeated grid — within noise — at 37% lower cost (12 frames instead of 21 per 400k chars), with clean completions in every probe; opus reads the same trade (.800 vs .833 at 42% lower cost)
30
- - `normalize()` now keeps line structure: whitespace runs containing a line break collapse to `NEWLINE_GLYPH` (U+2588 FULL BLOCK, drawn by the native renderer as a pitch-black cell one character wide) instead of a plain space; leading/trailing breaks are trimmed, and the frame-reading prompt explains the marker
31
- - `normalize()` now skips characters the fonts cannot render instead of printing `?` blanks: whole ANSI escape sequences are stripped, and bare control characters, zero-width format characters (ZWSP, BOM, directional marks), combining marks, and lone surrogates are dropped without occupying a cell; `?` remains the fallback for unsupported graphic characters only
32
-
33
- ## [15.11.4] - 2026-06-12
34
-
35
- ### Breaking Changes
36
-
37
- - Renamed every export to drop the `snapcompact`/`Snapcompact`/`SNAPCOMPACT_` qualifier — the package is meant to be consumed via `import * as snapcompact from "@oh-my-pi/snapcompact"`. Functions: `snapcompactCompact` → `compact`, `renderSnapcompactFrame` → `render`, `snapcompactGeometry` → `geometry`, `normalizeForSnapcompact` → `normalize`, `serializeSnapcompactConversation` → `serializeConversation`, `snapcompactImages` → `images`, `getPreservedSnapcompactArchive` → `getPreservedArchive`, `isSnapcompactShape` → `isShape`, `resolveSnapcompactShape` → `resolveShape`, `createSnapcompactFileOps` → `createFileOps`, `computeSnapcompactFileLists` → `computeFileLists`, `upsertSnapcompactFileOperations` → `upsertFileOperations`. Types: `SnapcompactShape` → `Shape`, `SnapcompactFrame` → `Frame`, `SnapcompactArchive` → `Archive`, `SnapcompactGeometry` → `Geometry`, `SnapcompactOptions` → `Options`, `SnapcompactSerializeOptions` → `SerializeOptions`, `SnapcompactFileOperations` → `FileOperations`, `SnapcompactCompactionDetails`/`Preparation`/`Result` → `CompactionDetails`/`CompactionPreparation`/`CompactionResult`, `SnapcompactConvertToLlm` → `ConvertToLlm`. Constants: `SNAPCOMPACT_X` → `X` (`SHAPES`, `FRAME_SIZE`, `MAX_FRAMES`, `FRAME_TOKEN_ESTIMATE`, `PRESERVE_KEY`, `TOOL_RESULT_MAX_CHARS`, `TOOL_ARG_MAX_CHARS`, `TOOL_CALL_MAX_CHARS`, `TRUNCATE_HEAD_RATIO`, `DIM_ON`, `DIM_OFF`).
38
-
39
- ### Added
40
-
41
21
  - Added `renderMany()` for paging arbitrary text into snapcompact PNG frames as LLM image blocks, and `frames()` for predicting the frame count without rendering
42
-
43
- ## [15.11.0] - 2026-06-10
44
-
45
- ### Breaking Changes
46
-
47
- - Changed `renderSnapcompactFrame` output from `png: Uint8Array` to `data: string` base64, requiring consumers to read frame payloads from `frame.data`
48
-
49
- ### Added
50
-
51
22
  - Added new serialization options `toolResultMaxChars`, `toolArgMaxChars`, `toolCallMaxChars`, `truncateHeadRatio`, and `dimToolResults` to `snapcompactCompact`/`serializeSnapcompactConversation` so callers can tune how tool results and arguments are archived
52
23
  - Added exported default constants `SNAPCOMPACT_TOOL_RESULT_MAX_CHARS`, `SNAPCOMPACT_TOOL_ARG_MAX_CHARS`, `SNAPCOMPACT_TOOL_CALL_MAX_CHARS`, and `SNAPCOMPACT_TRUNCATE_HEAD_RATIO` for reuse when configuring truncation limits
53
24
  - Added provider-specific snapcompact frame-shape presets and shape helpers (`SNAPCOMPACT_SHAPES`, `resolveSnapcompactShape`, `isSnapcompactShape`) so callers can consistently select validated image-frame geometry for archive renders
@@ -58,6 +29,14 @@
58
29
 
59
30
  ### Changed
60
31
 
32
+ - **Changed the per-provider default shapes to the spacing-tuned cells.** The previous shapes were tuned on the SQuAD *prose* eval, where dense cells won; a new tool-result legibility benchmark (`research/toolbench.py` — real `search`/`read`/`find` output with structure-sensitive QA) showed the prose-era density erases the line numbers and indentation that code/search output depends on. Anthropic moves from `6x12-dim` to `11on16-bw` (opus-4.8 f1 .806 vs .755 for plain `8on16-bw` and .351 for `6x12-dim`, which fell below the OCR ~16px/char floor and abstained); OpenAI and Google move to `8on22-bw` (gemini-3.5-flash f1 .934 vs .807 for `8on16-bw` and .287 for `doc-8on16-sent-dim`; same leading win on gpt-5.5/gpt-5.4-mini). Kimi and GLM keep their measured `8on16-bw`. The bigger cells pack fewer chars per frame, so inline frame-swapping now breaks even at a larger tool-result size
33
+ - `serializeConversation` now skips tool call/result pairs whose result is flagged contextually useless (`useless: true`, non-error), so archived frames stop carrying zero-match searches and timed-out waits
34
+ - Frames are no longer padded to a square: the native renderer clips each PNG's height to the text rows actually printed, so a partially filled frame (typically the newest) bills only the pixel rows it uses
35
+ - **Changed the OpenAI default shape from `6x6u-sent` to `8on16-bw`.** A production-regime mono eval (gpt-5.5, the full 800k-char SQuAD flow in one request, n=50) scored the old dense default f1 .602 vs .851 for `8on16-bw` rendered by the production pipeline, at near-equal total cost (the dense cells burned the frame savings on reasoning tokens); chunked exp14 had already scored `8on16-bw` .906. `SHAPES.openaiDense` is renamed to `SHAPES.openai`
36
+ - **Changed the Google default shape from `8x8r-sent` to `doc-8on16-sent-dim`.** Production-rendered mono eval on gemini-3.5-flash (400k chars, one request, n=25): f1 .900 vs .853 for the repeated grid at lower cost, agreeing with the chunked round-2 winner
37
+ - **Changed the Anthropic default shape from `8x8r-bw` to `6x12-dim`.** Production mono eval on claude-fable (400k chars, one request, n=25): f1 .840 vs .877 for the repeated grid — within noise — at 37% lower cost (12 frames instead of 21 per 400k chars), with clean completions in every probe; opus reads the same trade (.800 vs .833 at 42% lower cost)
38
+ - `normalize()` now keeps line structure: whitespace runs containing a line break collapse to `NEWLINE_GLYPH` (U+2588 FULL BLOCK, drawn by the native renderer as a pitch-black cell one character wide) instead of a plain space; leading/trailing breaks are trimmed, and the frame-reading prompt explains the marker
39
+ - `normalize()` now skips characters the fonts cannot render instead of printing `?` blanks: whole ANSI escape sequences are stripped, and bare control characters, zero-width format characters (ZWSP, BOM, directional marks), combining marks, and lone surrogates are dropped without occupying a cell; `?` remains the fallback for unsupported graphic characters only
61
40
  - Changed truncation in archived tool output to keep both the beginning and end of long text using a configurable head/tail ratio instead of a single hard cut
62
41
  - Changed tool-result text rendering so archived tool results are shown in dim gray ink by default and the summary prompt notes that dim text is archived tool output
63
42
  - Changed `RenderedFrame` visible-character accounting so `chars` no longer includes invisible dim-control markers
@@ -67,3 +46,13 @@
67
46
 
68
47
  - Fixed frame rendering at archive chunk boundaries to reopen dim spans when a chunk ends inside a dimmed tool-result segment
69
48
  - Fixed message serialization to strip user- and assistant-provided dim markers so only renderer-generated dim spans can be applied
49
+
50
+ ## [15.13.0] - 2026-06-14
51
+
52
+ ## [15.12.1] - 2026-06-12
53
+
54
+ ## [15.11.7] - 2026-06-12
55
+
56
+ ## [15.11.4] - 2026-06-12
57
+
58
+ ## [15.11.0] - 2026-06-10
@@ -7,25 +7,28 @@
7
7
  * reader. Frames are `frameSize` wide; their height hugs the text rows
8
8
  * actually printed, so a partially filled frame never bills blank rows.
9
9
  *
10
- * The frame shape is provider-aware, following the snapcompact SQuAD evals
11
- * (`packages/snapcompact`, 200k-token monolithic runs):
10
+ * The frame shape is provider-aware. Original choices came from the SQuAD
11
+ * prose evals (`packages/snapcompact`, 200k-token monolithic runs); the
12
+ * spacing choices below come from the tool-result legibility bench
13
+ * (`research/toolbench.py`, real search/read/find output with structure QA),
14
+ * which exposed that the prose-tuned dense cells erase the line numbers and
15
+ * indentation that code/search output depends on:
12
16
  *
13
- * - **Anthropic** (`6x12-dim`): X.org 6x12 glyphs, black ink, stopwords
14
- * dimmed gray recall within noise of the repeated `8x8r-bw` grid at
15
- * ~40% lower cost; `8x8r-bw` remains the max-recall choice via the shape
16
- * setting. Opus 4.7+/Fable/Mythos ingest high-res natively (2576px edge,
17
- * 4,784 visual-token cap, no flag needed), so those lines get 1932px
18
- * frames: same recall and cost, a third fewer frames. Older Claude lines
19
- * downscale past 1568px and keep the standard frame.
20
- * - **Google** (`doc-8on16-sent-dim` @2048): two word-wrapped newspaper
21
- * columns of 8x13 glyphs, sentence-hue ink, dimmed stopwords. Gemini 3.x
22
- * bills a fixed `media_resolution` budget per image (default 1,120
23
- * tokens) regardless of pixels, so the 2048px frame carries +70% chars at
24
- * the same bill (f1 .88 vs .90 at 1568). `ULTRA_HIGH` doubles the budget
25
- * and reads 3072px frames, but loses on chars/$ — deliberately unused.
26
- * - **OpenAI** (`8on16-bw`): 8x13 glyphs on a patch-aligned 16px pitch,
27
- * black ink (gpt-5.5 mono F1 .867 vs .602 for the previous `6x6u-sent`).
28
- * Patch billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
17
+ * - **Anthropic** (`11on16-bw`): 8x13 glyphs on an 11px advance (extra
18
+ * letter-spacing), black ink. On the tool-result bench, tracking the
19
+ * readable cell beat plain `8on16-bw` (opus-4.8 f1 .806 vs .755) and far
20
+ * beat the prior dense `6x12-dim` (.351, which fell below the OCR ~16px/char
21
+ * floor and abstained). Opus 4.7+/Fable/Mythos ingest high-res natively
22
+ * (2576px edge, 4,784 visual-token cap), so those lines get 1932px frames:
23
+ * same bill, fewer frames. Older Claude lines downscale past 1568px.
24
+ * - **Google** (`8on22-bw` @2048): 8x13 glyphs on a 22px pitch (extra line
25
+ * spacing), black ink. Leading lifted gemini-3.5-flash to f1 .934 vs .807
26
+ * for `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`. Gemini 3.x
27
+ * bills a fixed `media_resolution` budget per image (default 1,120 tokens)
28
+ * regardless of pixels, so the 2048px frame carries more chars at the same
29
+ * bill.
30
+ * - **OpenAI** (`8on22-bw`): same leading win (gpt-5.5/gpt-5.4-mini). Patch
31
+ * billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
29
32
  * area-proportional, so resolution cannot improve chars/$ — 1568 stays.
30
33
  * `detail: "high"` would downgrade (2,500-patch cap); `original` is sent.
31
34
  * - **Unknown providers** default to the Anthropic shape. Gateways can
@@ -82,9 +85,11 @@ export type ShapeGeometry = Omit<Shape, "frameTokenEstimate" | "imageDetail">;
82
85
  * (redundancy coding), `6x6u` unscii Lanczos-squeezed to 6x6 (densest
83
86
  * readable cell), `5x8` the X.org legacy font on its 2576px frame, `6x12`
84
87
  * and `8x13` the X.org misc fonts, `8on16` 8x13 glyphs on an 8x16 cell pitch
85
- * (no stretch, extra leading), `doc-` prefixed shapes a two-column
86
- * word-wrapped newspaper layout. Ink: `sent` cycles six hues at sentence
87
- * boundaries, `bw` is plain black, `-dim` suffix prints stopwords in gray.
88
+ * (no stretch, extra leading), `8on22` the same glyphs on a 22px pitch (more
89
+ * leading), `11on16` the same glyphs on an 11px advance (more tracking),
90
+ * `doc-` prefixed shapes a two-column word-wrapped newspaper layout. Ink:
91
+ * `sent` cycles six hues at sentence boundaries, `bw` is plain black, `-dim`
92
+ * suffix prints stopwords in gray.
88
93
  */
89
94
  export declare const SHAPE_VARIANTS: {
90
95
  readonly "8x8r-bw": {
@@ -177,6 +182,24 @@ export declare const SHAPE_VARIANTS: {
177
182
  readonly lineRepeat: 1;
178
183
  readonly frameSize: 1568;
179
184
  };
185
+ readonly "8on22-bw": {
186
+ readonly font: "8x13";
187
+ readonly cellWidth: 8;
188
+ readonly cellHeight: 22;
189
+ readonly stretch: false;
190
+ readonly variant: "bw";
191
+ readonly lineRepeat: 1;
192
+ readonly frameSize: 1568;
193
+ };
194
+ readonly "11on16-bw": {
195
+ readonly font: "8x13";
196
+ readonly cellWidth: 11;
197
+ readonly cellHeight: 16;
198
+ readonly stretch: false;
199
+ readonly variant: "bw";
200
+ readonly lineRepeat: 1;
201
+ readonly frameSize: 1568;
202
+ };
180
203
  readonly "doc-8on16-bw": {
181
204
  readonly font: "8x13";
182
205
  readonly cellWidth: 8;
@@ -217,19 +240,20 @@ export declare const SHAPE_VARIANT_NAMES: readonly ShapeVariantName[];
217
240
  export declare function isShapeVariantName(value: unknown): value is ShapeVariantName;
218
241
  /** Eval-validated shapes, keyed by the provider family they won on. */
219
242
  export declare const SHAPES: {
220
- /** `6x12-dim`: X.org 6x12 glyphs, black ink with stopwords dimmed gray.
221
- * Production mono eval on claude-fable: f1 .840 vs .877 for the repeated
222
- * `8x8r-bw` grid (within noise at n=25) at 37% lower cost 12 frames
223
- * instead of 21 per 400k chars. Never refused in any run. */
243
+ /** `11on16-bw`: 8x13 glyphs on an 11px advance (extra tracking), black ink.
244
+ * Tool-result legibility bench (real search/read/find output, structure QA)
245
+ * on opus-4.8: f1 .806 vs .755 for plain `8on16-bw` and .351 for the prior
246
+ * `6x12-dim` default letter-spacing the readable cell wins; the dense
247
+ * 6x12 was below the OCR ~16px/char floor and abstained. */
224
248
  anthropic: Shape;
225
- /** `doc-8on16-sent-dim`: two word-wrapped columns, sentence hues, dimmed
226
- * stopwords. Production mono eval on gemini-3.5-flash: f1 .900 vs .853
227
- * for the repeated grid, at lower cost; also the chunked round-2 winner. */
249
+ /** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
250
+ * Tool-result legibility bench on gemini-3.5-flash: f1 .934 vs .807 for
251
+ * plain `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`; the
252
+ * line-spacing reduces row crowding so line numbers stay legible. */
228
253
  google: Shape;
229
- /** `8on16-bw`: 8x13 X.org glyphs on a 16px pitch, black ink. Mono eval on
230
- * gpt-5.5 (200k-token single request, n=50): f1 .851 vs .602 for the
231
- * previous `6x6u-sent` default at near-equal total cost; chunked exp14
232
- * scored it .906. */
254
+ /** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
255
+ * Same line-spacing win for OpenAI; bench on gpt-5.5/gpt-5.4-mini showed
256
+ * leading lifts recall on the readable cell over plain `8on16-bw`. */
233
257
  openai: Shape;
234
258
  /** Original 5x8 X.org shape (pre-shape-table sessions rendered this). */
235
259
  legacy: Shape;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/snapcompact",
4
- "version": "15.12.4",
4
+ "version": "15.13.0",
5
5
  "description": "Bitmap-frame context compression for vision-capable LLMs",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -31,9 +31,9 @@
31
31
  "fmt": "biome format --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@oh-my-pi/pi-ai": "15.12.4",
35
- "@oh-my-pi/pi-natives": "15.12.4",
36
- "@oh-my-pi/pi-utils": "15.12.4"
34
+ "@oh-my-pi/pi-ai": "15.13.0",
35
+ "@oh-my-pi/pi-natives": "15.13.0",
36
+ "@oh-my-pi/pi-utils": "15.13.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/bun": "^1.3.14"
@@ -7,25 +7,28 @@
7
7
  * reader. Frames are `frameSize` wide; their height hugs the text rows
8
8
  * actually printed, so a partially filled frame never bills blank rows.
9
9
  *
10
- * The frame shape is provider-aware, following the snapcompact SQuAD evals
11
- * (`packages/snapcompact`, 200k-token monolithic runs):
10
+ * The frame shape is provider-aware. Original choices came from the SQuAD
11
+ * prose evals (`packages/snapcompact`, 200k-token monolithic runs); the
12
+ * spacing choices below come from the tool-result legibility bench
13
+ * (`research/toolbench.py`, real search/read/find output with structure QA),
14
+ * which exposed that the prose-tuned dense cells erase the line numbers and
15
+ * indentation that code/search output depends on:
12
16
  *
13
- * - **Anthropic** (`6x12-dim`): X.org 6x12 glyphs, black ink, stopwords
14
- * dimmed gray recall within noise of the repeated `8x8r-bw` grid at
15
- * ~40% lower cost; `8x8r-bw` remains the max-recall choice via the shape
16
- * setting. Opus 4.7+/Fable/Mythos ingest high-res natively (2576px edge,
17
- * 4,784 visual-token cap, no flag needed), so those lines get 1932px
18
- * frames: same recall and cost, a third fewer frames. Older Claude lines
19
- * downscale past 1568px and keep the standard frame.
20
- * - **Google** (`doc-8on16-sent-dim` @2048): two word-wrapped newspaper
21
- * columns of 8x13 glyphs, sentence-hue ink, dimmed stopwords. Gemini 3.x
22
- * bills a fixed `media_resolution` budget per image (default 1,120
23
- * tokens) regardless of pixels, so the 2048px frame carries +70% chars at
24
- * the same bill (f1 .88 vs .90 at 1568). `ULTRA_HIGH` doubles the budget
25
- * and reads 3072px frames, but loses on chars/$ — deliberately unused.
26
- * - **OpenAI** (`8on16-bw`): 8x13 glyphs on a patch-aligned 16px pitch,
27
- * black ink (gpt-5.5 mono F1 .867 vs .602 for the previous `6x6u-sent`).
28
- * Patch billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
17
+ * - **Anthropic** (`11on16-bw`): 8x13 glyphs on an 11px advance (extra
18
+ * letter-spacing), black ink. On the tool-result bench, tracking the
19
+ * readable cell beat plain `8on16-bw` (opus-4.8 f1 .806 vs .755) and far
20
+ * beat the prior dense `6x12-dim` (.351, which fell below the OCR ~16px/char
21
+ * floor and abstained). Opus 4.7+/Fable/Mythos ingest high-res natively
22
+ * (2576px edge, 4,784 visual-token cap), so those lines get 1932px frames:
23
+ * same bill, fewer frames. Older Claude lines downscale past 1568px.
24
+ * - **Google** (`8on22-bw` @2048): 8x13 glyphs on a 22px pitch (extra line
25
+ * spacing), black ink. Leading lifted gemini-3.5-flash to f1 .934 vs .807
26
+ * for `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`. Gemini 3.x
27
+ * bills a fixed `media_resolution` budget per image (default 1,120 tokens)
28
+ * regardless of pixels, so the 2048px frame carries more chars at the same
29
+ * bill.
30
+ * - **OpenAI** (`8on22-bw`): same leading win (gpt-5.5/gpt-5.4-mini). Patch
31
+ * billing (32px × 1.2, 10k-patch budget at `detail: "original"`) is
29
32
  * area-proportional, so resolution cannot improve chars/$ — 1568 stays.
30
33
  * `detail: "high"` would downgrade (2,500-patch cap); `original` is sent.
31
34
  * - **Unknown providers** default to the Anthropic shape. Gateways can
@@ -94,9 +97,11 @@ export type ShapeGeometry = Omit<Shape, "frameTokenEstimate" | "imageDetail">;
94
97
  * (redundancy coding), `6x6u` unscii Lanczos-squeezed to 6x6 (densest
95
98
  * readable cell), `5x8` the X.org legacy font on its 2576px frame, `6x12`
96
99
  * and `8x13` the X.org misc fonts, `8on16` 8x13 glyphs on an 8x16 cell pitch
97
- * (no stretch, extra leading), `doc-` prefixed shapes a two-column
98
- * word-wrapped newspaper layout. Ink: `sent` cycles six hues at sentence
99
- * boundaries, `bw` is plain black, `-dim` suffix prints stopwords in gray.
100
+ * (no stretch, extra leading), `8on22` the same glyphs on a 22px pitch (more
101
+ * leading), `11on16` the same glyphs on an 11px advance (more tracking),
102
+ * `doc-` prefixed shapes a two-column word-wrapped newspaper layout. Ink:
103
+ * `sent` cycles six hues at sentence boundaries, `bw` is plain black, `-dim`
104
+ * suffix prints stopwords in gray.
100
105
  */
101
106
  export const SHAPE_VARIANTS = {
102
107
  "8x8r-bw": { font: "8x8", cellWidth: 8, cellHeight: 8, variant: "bw", lineRepeat: 2, frameSize: 1568 },
@@ -126,6 +131,24 @@ export const SHAPE_VARIANTS = {
126
131
  lineRepeat: 1,
127
132
  frameSize: 1568,
128
133
  },
134
+ "8on22-bw": {
135
+ font: "8x13",
136
+ cellWidth: 8,
137
+ cellHeight: 22,
138
+ stretch: false,
139
+ variant: "bw",
140
+ lineRepeat: 1,
141
+ frameSize: 1568,
142
+ },
143
+ "11on16-bw": {
144
+ font: "8x13",
145
+ cellWidth: 11,
146
+ cellHeight: 16,
147
+ stretch: false,
148
+ variant: "bw",
149
+ lineRepeat: 1,
150
+ frameSize: 1568,
151
+ },
129
152
  "doc-8on16-bw": {
130
153
  font: "8x13",
131
154
  cellWidth: 8,
@@ -223,20 +246,21 @@ function priceShape(base: ShapeGeometry, family: BillingFamily): Shape {
223
246
 
224
247
  /** Eval-validated shapes, keyed by the provider family they won on. */
225
248
  export const SHAPES = {
226
- /** `6x12-dim`: X.org 6x12 glyphs, black ink with stopwords dimmed gray.
227
- * Production mono eval on claude-fable: f1 .840 vs .877 for the repeated
228
- * `8x8r-bw` grid (within noise at n=25) at 37% lower cost 12 frames
229
- * instead of 21 per 400k chars. Never refused in any run. */
230
- anthropic: priceShape(SHAPE_VARIANTS["6x12-dim"], "anthropic"),
231
- /** `doc-8on16-sent-dim`: two word-wrapped columns, sentence hues, dimmed
232
- * stopwords. Production mono eval on gemini-3.5-flash: f1 .900 vs .853
233
- * for the repeated grid, at lower cost; also the chunked round-2 winner. */
234
- google: priceShape(SHAPE_VARIANTS["doc-8on16-sent-dim"], "google"),
235
- /** `8on16-bw`: 8x13 X.org glyphs on a 16px pitch, black ink. Mono eval on
236
- * gpt-5.5 (200k-token single request, n=50): f1 .851 vs .602 for the
237
- * previous `6x6u-sent` default at near-equal total cost; chunked exp14
238
- * scored it .906. */
239
- openai: priceShape(SHAPE_VARIANTS["8on16-bw"], "openai"),
249
+ /** `11on16-bw`: 8x13 glyphs on an 11px advance (extra tracking), black ink.
250
+ * Tool-result legibility bench (real search/read/find output, structure QA)
251
+ * on opus-4.8: f1 .806 vs .755 for plain `8on16-bw` and .351 for the prior
252
+ * `6x12-dim` default letter-spacing the readable cell wins; the dense
253
+ * 6x12 was below the OCR ~16px/char floor and abstained. */
254
+ anthropic: priceShape(SHAPE_VARIANTS["11on16-bw"], "anthropic"),
255
+ /** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
256
+ * Tool-result legibility bench on gemini-3.5-flash: f1 .934 vs .807 for
257
+ * plain `8on16-bw` and .287 for the prior `doc-8on16-sent-dim`; the
258
+ * line-spacing reduces row crowding so line numbers stay legible. */
259
+ google: priceShape(SHAPE_VARIANTS["8on22-bw"], "google"),
260
+ /** `8on22-bw`: 8x13 glyphs on a 22px pitch (extra leading), black ink.
261
+ * Same line-spacing win for OpenAI; bench on gpt-5.5/gpt-5.4-mini showed
262
+ * leading lifts recall on the readable cell over plain `8on16-bw`. */
263
+ openai: priceShape(SHAPE_VARIANTS["8on22-bw"], "openai"),
240
264
  /** Original 5x8 X.org shape (pre-shape-table sessions rendered this). */
241
265
  legacy: priceShape(SHAPE_VARIANTS["5x8-sent"], "anthropic"),
242
266
  } satisfies Record<string, Shape>;
@@ -271,9 +295,9 @@ export function isShape(value: unknown): value is Shape {
271
295
  /** Eval-winning variant per provider family (billing fallback when the
272
296
  * model id matches no known reader line). */
273
297
  const FAMILY_VARIANT: Record<BillingFamily, ShapeVariantName> = {
274
- anthropic: "6x12-dim",
275
- google: "doc-8on16-sent-dim",
276
- openai: "8on16-bw",
298
+ anthropic: "11on16-bw",
299
+ google: "8on22-bw",
300
+ openai: "8on22-bw",
277
301
  };
278
302
 
279
303
  const FAMILY_SHAPE: Record<BillingFamily, Shape> = {
@@ -297,16 +321,16 @@ export interface IdealShape {
297
321
  const MODEL_VARIANTS: readonly (readonly [RegExp, IdealShape])[] = [
298
322
  // Opus 4.7+ and Fable/Mythos read high-res natively (2576px edge under a
299
323
  // 4,784 visual-token cap → 1932px square sweet spot): same recall and
300
- // cost as 1568, a third fewer frames (12 → 8 per 400k chars).
301
- [/claude.*(fable|mythos)/i, { variant: "6x12-dim", frameSize: 1932 }],
302
- [/claude-?opus-?4[.-][7-9]/i, { variant: "6x12-dim", frameSize: 1932 }],
324
+ // cost as 1568, a third fewer frames.
325
+ [/claude.*(fable|mythos)/i, { variant: "11on16-bw", frameSize: 1932 }],
326
+ [/claude-?opus-?4[.-][7-9]/i, { variant: "11on16-bw", frameSize: 1932 }],
303
327
  // Older Claude lines downscale past 1568px — keep the safe size.
304
- [/claude/i, { variant: "6x12-dim" }],
328
+ [/claude/i, { variant: "11on16-bw" }],
305
329
  // Gemini 3.x bills a fixed 1,120-token budget per image regardless of
306
- // pixels: 2048px packs +70% chars per frame at the same bill.
307
- [/gemini/i, { variant: "doc-8on16-sent-dim", frameSize: 2048 }],
330
+ // pixels: 2048px packs more chars per frame at the same bill.
331
+ [/gemini/i, { variant: "8on22-bw", frameSize: 2048 }],
308
332
  // gpt-5.5 patch billing is area-proportional; 1568 is already optimal.
309
- [/gpt|codex/i, { variant: "8on16-bw" }],
333
+ [/gpt|codex/i, { variant: "8on22-bw" }],
310
334
  // kimi's image processor downscales past 1792px (64×64 28px patches);
311
335
  // 1568 wins on chars/$ and reads at f1 .973 (≤8 frames per request).
312
336
  [/kimi/i, { variant: "8on16-bw" }],