@oh-my-pi-zen/snapcompact 17.2.13-zen.2 → 17.2.15-zen.2

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-zen/snapcompact",
4
- "version": "17.2.13-zen.2",
4
+ "version": "17.2.15-zen.2",
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-zen/pi-ai": "17.2.13-zen.2",
35
- "@oh-my-pi-zen/pi-natives": "17.2.13-zen.2",
36
- "@oh-my-pi-zen/pi-utils": "17.2.13-zen.2",
37
- "@oh-my-pi-zen/pi-wire": "17.2.13-zen.2"
34
+ "@oh-my-pi-zen/pi-ai": "17.2.15-zen.2",
35
+ "@oh-my-pi-zen/pi-catalog": "17.2.15-zen.2",
36
+ "@oh-my-pi-zen/pi-natives": "17.2.15-zen.2",
37
+ "@oh-my-pi-zen/pi-utils": "17.2.15-zen.2",
38
+ "@oh-my-pi-zen/pi-wire": "17.2.15-zen.2"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/bun": "^1.3.14"
@@ -1,22 +1,22 @@
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, oldest→newest. 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.
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 top→bottom, then right.
12
+ {{else}} - Frame: one grid {{cols}} characters wide, up to {{rows}} rows tall; read left→right, top→bottom. 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
18
  {{/if}}{{#if exactStrings}}- Image text may contain exact-string anchors like `[E001]`. Resolve them from EXACT STRING ANCHORS below; use the side-channel value for paths, commands, hashes, architecture tokens, URI refs, and other byte-sensitive facts.
19
- {{/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.
19
+ {{/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.
20
20
 
21
21
  {{#if files}}FILES
22
22
  ===================
@@ -46,6 +46,7 @@
46
46
  */
47
47
 
48
48
  import type { Api, ImageContent, Message, TextContent } from "@oh-my-pi-zen/pi-ai";
49
+ import { isFableOrMythos, parseAnthropicModel, semverGte } from "@oh-my-pi-zen/pi-catalog/identity";
49
50
  import { renderSnapcompactPng, snapcompactAdvanceFrameCounts, snapcompactSupportedChars } from "@oh-my-pi-zen/pi-natives";
50
51
  import { formatGroupedPaths, prompt } from "@oh-my-pi-zen/pi-utils";
51
52
  import { INTENT_FIELD } from "@oh-my-pi-zen/pi-wire";
@@ -396,17 +397,17 @@ export interface IdealShape {
396
397
  frameSize?: number;
397
398
  }
398
399
 
399
- /** Eval-winning format per model line, matched against the model id. The
400
- * wire API only identifies the gateway — a Claude served through Vertex or
401
- * OpenRouter still reads best with its own shape. Patterns cover the model
402
- * lines the mono evals measured; everything else falls back to the API
403
- * family's winner at the standard 1568px frame. First match wins. */
400
+ /** Eval-winning format per model line. The wire API only identifies the
401
+ * gateway — a Claude served through Vertex or OpenRouter still reads best
402
+ * with its own shape. Classified Anthropic models use the shared catalog
403
+ * identity parser; remaining model lines use first-match regex rules and fall
404
+ * back to the API family's winner at the standard 1568px frame. */
405
+ const HIGH_RES_ANTHROPIC_VARIANT = { variant: "11on16-bw", frameSize: 1932 } as const satisfies IdealShape;
404
406
  const MODEL_VARIANTS: readonly (readonly [RegExp, IdealShape])[] = [
405
- // Opus 4.7+ and Fable/Mythos read high-res natively (2576px edge under a
406
- // 4,784 visual-token cap → 1932px square sweet spot): same recall and
407
- // cost as 1568, a third fewer frames.
408
- [/claude.*(fable|mythos)/i, { variant: "11on16-bw", frameSize: 1932 }],
409
- [/claude-?opus-?4[.-][7-9]/i, { variant: "11on16-bw", frameSize: 1932 }],
407
+ // Versionless Fable/Mythos aliases (e.g. `claude-fable-latest`) never parse
408
+ // a numeric version, so keep them on the high-res tier by name — every
409
+ // Fable/Mythos line reads it natively.
410
+ [/claude.*(fable|mythos)/i, HIGH_RES_ANTHROPIC_VARIANT],
410
411
  // Older Claude lines downscale past 1568px — keep the safe size.
411
412
  [/claude/i, { variant: "11on16-bw" }],
412
413
  // Gemini 3.x bills a fixed 1,120-token budget per image regardless of
@@ -423,6 +424,20 @@ const MODEL_VARIANTS: readonly (readonly [RegExp, IdealShape])[] = [
423
424
 
424
425
  /** Eval-ideal format for a model id, or undefined when unmeasured. */
425
426
  export function idealShapeVariant(modelId: string): IdealShape | undefined {
427
+ // The catalog parser is case-sensitive; the regex rules below are not.
428
+ // Normalize so mixed-case gateway ids keep matching the Anthropic tier.
429
+ const anthropic = parseAnthropicModel(modelId.toLowerCase());
430
+ if (
431
+ anthropic &&
432
+ (isFableOrMythos(anthropic.kind) || (anthropic.kind === "opus" && semverGte(anthropic.version, "4.7")))
433
+ ) {
434
+ // Opus 4.7+ and Fable/Mythos read high-res natively: same recall and
435
+ // cost as 1568, a third fewer frames. 1932 is the largest *square* not
436
+ // downscaled under Anthropic's 4,784 visual-token cap ((1932/28)² =
437
+ // 69² = 4,761 ≤ 4,784 28px patches), and staying below 2000px clears
438
+ // the stricter ≤2000px limit for requests with more than 20 images.
439
+ return HIGH_RES_ANTHROPIC_VARIANT;
440
+ }
426
441
  return MODEL_VARIANTS.find(([pattern]) => pattern.test(modelId))?.[1];
427
442
  }
428
443
 
@@ -993,7 +1008,7 @@ function textBlocks(content: Message["content"]): string[] {
993
1008
  if (typeof content === "string") return [content];
994
1009
  const texts: string[] = [];
995
1010
  for (const block of content) {
996
- if (block.type === "text") texts.push(block.text);
1011
+ if (block.type === "text" && "text" in block) texts.push(block.text);
997
1012
  }
998
1013
  return texts;
999
1014
  }