@oh-my-pi/snapcompact 17.2.14 → 17.3.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,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.2.15] - 2026-08-12
6
+
7
+ ### Fixed
8
+
9
+ - Fixed Anthropic model ID parsing to be case-insensitive and extended the high-resolution 1932px frame tier to Claude Opus 5 and later, preventing sessions from falling back to lower-resolution 1568px frames and preserving full history per compaction.
10
+
5
11
  ## [17.1.5] - 2026-07-27
6
12
 
7
13
  ### Fixed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/snapcompact",
4
- "version": "17.2.14",
4
+ "version": "17.3.0",
5
5
  "description": "Bitmap-frame context compression for vision-capable LLMs",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -31,10 +31,11 @@
31
31
  "fmt": "biome format --write ."
32
32
  },
33
33
  "dependencies": {
34
- "@oh-my-pi/pi-ai": "17.2.14",
35
- "@oh-my-pi/pi-natives": "17.2.14",
36
- "@oh-my-pi/pi-utils": "17.2.14",
37
- "@oh-my-pi/pi-wire": "17.2.14"
34
+ "@oh-my-pi/pi-ai": "17.3.0",
35
+ "@oh-my-pi/pi-catalog": "17.3.0",
36
+ "@oh-my-pi/pi-natives": "17.3.0",
37
+ "@oh-my-pi/pi-utils": "17.3.0",
38
+ "@oh-my-pi/pi-wire": "17.3.0"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/bun": "^1.3.14"
@@ -1,21 +1,21 @@
1
- You are resuming a prior conversation. Its earlier turns were archived to reclaim context and are reproduced under HISTORY below, oldest to newest. Read HISTORY in full, then continue from the live conversation that follows it.
1
+ Resume prior conversation. Earlier turns archived under HISTORY below, oldestnewest. Read HISTORY fully; continue the live conversation following it.
2
2
 
3
- The archived transcript uses compact scopes:
4
- - `¶user:`, `¶think:`, `¶ai:`, and `¶call:` open user, assistant reasoning, assistant reply, and tool-call scopes.
5
- - Following lines without a `¶…:` prefix remain in the current scope. Consecutive blocks of the same kind omit the repeated prefix.
6
- - A tool call reads `¶call:name(args)//intent`; the trailing `//intent` is optional. Its `<out>…</out>` block is tool output.
3
+ Archived transcript scopes:
4
+ - `¶user:`, `¶think:`, `¶ai:`, `¶call:`: user, assistant reasoning, assistant reply, tool call.
5
+ - Unprefixed following lines: current scope. Consecutive same-kind blocks omit repeated prefix.
6
+ - Tool call: `¶call:name(args)//intent`; trailing `//intent` optional. `<out>…</out>`: tool output.
7
7
 
8
8
  Reading HISTORY:
9
- - Plain-text sections are the verbatim transcript rely on them exactly.
10
- {{#if frameCount}}- Some middle sections are attached as images instead of text. Each image is a page of that same transcript and belongs at its place in the reading order, between marked delimiters. Within an image, a solid black cell marks a newline and runs of spaces collapse to one.
11
- {{#if docColumns}} - A frame holds two side-by-side columns, each {{cols}} characters wide and up to {{rows}} rows tall: read the left column top to bottom, then the right.
12
- {{else}} - A frame is one grid {{cols}} characters wide and up to {{rows}} rows tall: read left to right, top to bottom there is no word wrap, so a word may break across rows.
13
- {{/if}}{{#if sentenceInk}} - Ink cycles through six colors, one per sentence.
14
- {{/if}}{{#if stopwordDimmed}} - Function words are dim gray; content words keep full ink.
15
- {{/if}}{{#if lineRepeated}} - Each line is printed twice (white, then a pale-yellow band); the two copies are identical.
16
- {{/if}}{{/if}}{{#if includedPreviousSummary}}- HISTORY opens with a condensed digest of still-older context that predates the archived turns.
17
- {{/if}}{{#if truncatedChars}}- About {{truncatedChars}} characters of older middle history were dropped to fit the archive budget.
18
- {{/if}}- When an exact earlier detail matters and a section reads unclearly, re-derive it from the workspace (re-read files, re-run commands) rather than guessing.
9
+ - Plain text: verbatim transcript; rely on it exactly.
10
+ {{#if frameCount}}- Some middle sections: images, not text. Each image: one page of that transcript, in reading order between marked delimiters. Solid black cell: newline; runs of spaces collapse to one.
11
+ {{#if docColumns}} - Frame: two side-by-side columns, each {{cols}} characters wide, up to {{rows}} rows tall; read left topbottom, then right.
12
+ {{else}} - Frame: one grid {{cols}} characters wide, up to {{rows}} rows tall; read leftright, topbottom. No word wrap; words may break across rows.
13
+ {{/if}}{{#if sentenceInk}} - Ink: six colors, one per sentence.
14
+ {{/if}}{{#if stopwordDimmed}} - Function words: dim gray; content words: full ink.
15
+ {{/if}}{{#if lineRepeated}} - Each line printed twice (white, then pale-yellow band); copies identical.
16
+ {{/if}}{{/if}}{{#if includedPreviousSummary}}- HISTORY opens with a condensed digest of still-older context predating archived turns.
17
+ {{/if}}{{#if truncatedChars}}- About {{truncatedChars}} characters of older middle history dropped to fit archive budget.
18
+ {{/if}}- If an exact earlier detail matters and a section is unclear, re-derive from workspace (re-read files, re-run commands), rather than guess.
19
19
 
20
20
  {{#if files}}FILES
21
21
  ===================
@@ -46,6 +46,7 @@
46
46
  */
47
47
 
48
48
  import type { Api, ImageContent, Message, TextContent } from "@oh-my-pi/pi-ai";
49
+ import { isFableOrMythos, parseAnthropicModel, semverGte } from "@oh-my-pi/pi-catalog/identity";
49
50
  import { renderSnapcompactPng, snapcompactSupportedChars } from "@oh-my-pi/pi-natives";
50
51
  import { formatGroupedPaths, prompt } from "@oh-my-pi/pi-utils";
51
52
  import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
@@ -336,17 +337,17 @@ export interface IdealShape {
336
337
  frameSize?: number;
337
338
  }
338
339
 
339
- /** Eval-winning format per model line, matched against the model id. The
340
- * wire API only identifies the gateway — a Claude served through Vertex or
341
- * OpenRouter still reads best with its own shape. Patterns cover the model
342
- * lines the mono evals measured; everything else falls back to the API
343
- * family's winner at the standard 1568px frame. First match wins. */
340
+ /** Eval-winning format per model line. The wire API only identifies the
341
+ * gateway — a Claude served through Vertex or OpenRouter still reads best
342
+ * with its own shape. Classified Anthropic models use the shared catalog
343
+ * identity parser; remaining model lines use first-match regex rules and fall
344
+ * back to the API family's winner at the standard 1568px frame. */
345
+ const HIGH_RES_ANTHROPIC_VARIANT = { variant: "11on16-bw", frameSize: 1932 } as const satisfies IdealShape;
344
346
  const MODEL_VARIANTS: readonly (readonly [RegExp, IdealShape])[] = [
345
- // Opus 4.7+ and Fable/Mythos read high-res natively (2576px edge under a
346
- // 4,784 visual-token cap 1932px square sweet spot): same recall and
347
- // cost as 1568, a third fewer frames.
348
- [/claude.*(fable|mythos)/i, { variant: "11on16-bw", frameSize: 1932 }],
349
- [/claude-?opus-?4[.-][7-9]/i, { variant: "11on16-bw", frameSize: 1932 }],
347
+ // Versionless Fable/Mythos aliases (e.g. `claude-fable-latest`) never parse
348
+ // a numeric version, so keep them on the high-res tier by name — every
349
+ // Fable/Mythos line reads it natively.
350
+ [/claude.*(fable|mythos)/i, HIGH_RES_ANTHROPIC_VARIANT],
350
351
  // Older Claude lines downscale past 1568px — keep the safe size.
351
352
  [/claude/i, { variant: "11on16-bw" }],
352
353
  // Gemini 3.x bills a fixed 1,120-token budget per image regardless of
@@ -363,6 +364,20 @@ const MODEL_VARIANTS: readonly (readonly [RegExp, IdealShape])[] = [
363
364
 
364
365
  /** Eval-ideal format for a model id, or undefined when unmeasured. */
365
366
  export function idealShapeVariant(modelId: string): IdealShape | undefined {
367
+ // The catalog parser is case-sensitive; the regex rules below are not.
368
+ // Normalize so mixed-case gateway ids keep matching the Anthropic tier.
369
+ const anthropic = parseAnthropicModel(modelId.toLowerCase());
370
+ if (
371
+ anthropic &&
372
+ (isFableOrMythos(anthropic.kind) || (anthropic.kind === "opus" && semverGte(anthropic.version, "4.7")))
373
+ ) {
374
+ // Opus 4.7+ and Fable/Mythos read high-res natively: same recall and
375
+ // cost as 1568, a third fewer frames. 1932 is the largest *square* not
376
+ // downscaled under Anthropic's 4,784 visual-token cap ((1932/28)² =
377
+ // 69² = 4,761 ≤ 4,784 28px patches), and staying below 2000px clears
378
+ // the stricter ≤2000px limit for requests with more than 20 images.
379
+ return HIGH_RES_ANTHROPIC_VARIANT;
380
+ }
366
381
  return MODEL_VARIANTS.find(([pattern]) => pattern.test(modelId))?.[1];
367
382
  }
368
383