@oh-my-pi/snapcompact 18.16.0 → 18.17.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
+ ## [18.17.0] - 2026-11-06
6
+
7
+ ### Fixed
8
+
9
+ - `compact()` now keeps image content from pre-16.1.0 archived histories in the dropped-history summary.
10
+
5
11
  ## [18.16.0] - 2026-11-02
6
12
 
7
13
  ### Added
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/snapcompact",
4
- "version": "18.16.0",
4
+ "version": "18.17.0",
5
5
  "description": "Bitmap-frame context compression for vision-capable LLMs",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -27,19 +27,19 @@
27
27
  "types": "./dist/types/index.d.ts",
28
28
  "scripts": {
29
29
  "check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
30
- "check:types": "tsgo -p tsconfig.json --noEmit",
30
+ "check:types": "bun check --no-script -p tsconfig.json",
31
31
  "lint": "oxlint .",
32
32
  "test": "bun test --parallel",
33
33
  "fix": "oxlint --fix --fix-suggestions . && bun run fmt",
34
34
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
35
35
  },
36
36
  "dependencies": {
37
- "@oh-my-pi/omptype": "18.16.0",
38
- "@oh-my-pi/pi-ai": "18.16.0",
39
- "@oh-my-pi/pi-catalog": "18.16.0",
40
- "@oh-my-pi/pi-natives": "18.16.0",
41
- "@oh-my-pi/pi-utils": "18.16.0",
42
- "@oh-my-pi/pi-wire": "18.16.0"
37
+ "@oh-my-pi/omptype": "18.17.0",
38
+ "@oh-my-pi/pi-ai": "18.17.0",
39
+ "@oh-my-pi/pi-catalog": "18.17.0",
40
+ "@oh-my-pi/pi-natives": "18.17.0",
41
+ "@oh-my-pi/pi-utils": "18.17.0",
42
+ "@oh-my-pi/pi-wire": "18.17.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/bun": "^1.3.14"
@@ -2280,16 +2280,21 @@ export function textWithoutThinking(text: string): string {
2280
2280
  const PREVIOUS_SUMMARY_HEAD = "[Summary of earlier history]";
2281
2281
 
2282
2282
  /**
2283
- * `archive`'s kept source as a reader that excludes reasoning may unfold it.
2284
- * An archive persisted without {@link Archive.text} keeps only its edges, and
2285
- * the tail's lines before its first scope label continue a section cut off in
2286
- * the dropped middle, whose scope is unknown; they are left out.
2283
+ * Length of the opening of a text-less archive's tail that a reader excluding
2284
+ * reasoning leaves out: an archive persisted without {@link Archive.text} keeps
2285
+ * only its edges, and the tail's lines before its first scope label continue a
2286
+ * section cut off in the dropped middle, whose scope is unknown.
2287
2287
  */
2288
- function sourceForReaderWithoutThinking(archive: Archive): string {
2289
- if (archive.text) return archive.text;
2288
+ function unscopedTailLength(archive: Archive): number {
2290
2289
  const tail = archive.textTail ?? "";
2291
2290
  const labelled = tail.search(SECTION_START);
2292
- return [archive.textHead, labelled < 0 ? "" : tail.slice(labelled)]
2291
+ return labelled < 0 ? tail.length : labelled;
2292
+ }
2293
+
2294
+ /** `archive`'s kept source as a reader that excludes reasoning may unfold it. */
2295
+ function sourceForReaderWithoutThinking(archive: Archive): string {
2296
+ if (archive.text) return archive.text;
2297
+ return [archive.textHead, (archive.textTail ?? "").slice(unscopedTailLength(archive))]
2293
2298
  .filter((part): part is string => typeof part === "string" && part.length > 0)
2294
2299
  .join(NEWLINE_GLYPH);
2295
2300
  }
@@ -2535,7 +2540,16 @@ export async function compact<TMessage = Message>(
2535
2540
  archiveText = archiveText.length > 0 ? `${head} [Recent conversation] ${archiveText}` : head;
2536
2541
  }
2537
2542
 
2538
- let truncatedChars = previousArchive?.truncatedChars ?? 0;
2543
+ // An archive persisted without `text` (written before the text-first layout)
2544
+ // holds its middle only as frames, which cannot be re-rendered: the new
2545
+ // archive drops them, and for a reader without reasoning its tail's unscoped
2546
+ // opening too, so count both as dropped history instead of dropping silently.
2547
+ let truncatedChars =
2548
+ (previousArchive?.truncatedChars ?? 0) +
2549
+ (previousArchive && previousArchive.text === undefined
2550
+ ? previousArchive.frames.reduce((sum, frame) => sum + frame.chars, 0) +
2551
+ (excludeThinking ? unscopedTailLength(previousArchive) : 0)
2552
+ : 0);
2539
2553
 
2540
2554
  // Re-compacting a snapcompacted history unfolds the prior archive's source
2541
2555
  // text and treats it as one coherent transcript: the previous kept source