@linxiraos/pi-snapcompact 1.1.14 → 1.1.16

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,74 +2,7 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.1.14] - 2026-09-12
5
+ ## [1.1.16] - 2026-09-19
6
6
 
7
- - 随 1.1.14 版本线发布:bazel 构建面(crates/*/BUILD.bazel)版本号纳入一致性检查,CI 原生构建与桌面冒烟守卫修复。
7
+ - 上游 v18.2.5 维护同步。
8
8
 
9
- ## [1.1.13] - 2026-09-10
10
-
11
- - 上游 v18.1.16 同步,内部修复。
12
-
13
- ## [1.1.12] - 2026-09-10
14
-
15
- - 品牌与合并工具链维护版本;无本包用户可见变更。
16
-
17
- ## [1.1.11] - 2026-09-08
18
-
19
- - OMP v18.1.13 + v18.1.14 dual-tag sync baseline; no package-specific user-visible changes.
20
-
21
- ## [1.1.10] - 2026-09-07
22
-
23
- - OMP v18.1.11 sync baseline (`e3106be68f`); no package-specific user-visible changes.
24
-
25
- ## [1.1.9] - 2026-09-05
26
-
27
- - v18.1.10 sync baseline (published as @linxiraos/pi-snapcompact).
28
-
29
- ## [1.1.8] - 2026-09-04
30
-
31
- - OMP sync v18.1.2–v18.1.5: compaction maintenance aligned with the upstream snapcompact updates.
32
-
33
- ## [1.1.6] - 2026-08-30
34
-
35
- - 同步上游 OMP v18.0.9(`cc14e04f075d`)。
36
-
37
- ## [1.1.5] - 2026-08-26
38
-
39
- - 随 1.1.5 版本线对齐发布:OMP v18.0.6 同步未触及本包,无独立功能变更。
40
-
41
- ## [1.1.4] - 2026-08-26
42
-
43
- ### Changed
44
-
45
- - 同步 1.1.4 发布线(与 1.1.3 无功能差异)。
46
-
47
- ## [1.1.3] - 2026-08-25
48
-
49
- ### Fixed
50
-
51
- - Republished as 1.1.3 to reset the latest tag after the broken 1.1.2 (no functional change over 1.1.1).
52
-
53
- ## [1.1.2] - 2026-08-25
54
-
55
- ### Fixed
56
-
57
- - Republished as 1.1.2 to reset the `latest` tag after the broken 1.1.0 (no functional change over 1.1.1).
58
-
59
- ## [1.1.1] - 2026-08-25
60
-
61
- ### Fixed
62
-
63
- - Published tarballs now carry real dependency versions instead of Bun's `catalog:` protocol (1.1.0 installs failed with "Unsupported URL Type catalog:").
64
-
65
- ## [1.1.0] - 2026-08-25
66
-
67
- ### Changed
68
-
69
- - 同步上游 OMP v18.0.3 / v18.0.4(内部运行时与构建改进,无独立用户可见变更)。
70
-
71
- ## [1.0.0] - 2026-08-13
72
-
73
- ### Changed
74
-
75
- - Reset the version to 1.0.0 and republished under the `@linxiraos/*` scope, breaking from the `@linxiraos` version lineage.
@@ -10843,7 +10843,7 @@ RUST RUNTIME DEPENDENCY LICENSES
10843
10843
 
10844
10844
  Generated from Cargo.lock by cargo-about 0.8.2 in locked, offline, workspace, all-feature, all-target mode, then restricted using cargo metadata to normal and build edges reachable from the workspace (development-only edges are excluded). cargo-deny independently evaluates the complete all-target graph.
10845
10845
 
10846
- Native Opus (audiopus/audiopus_sys) and PCRE2 (pcre2/pcre2-sys) are covered by the exact crate license payloads below. inferno 0.12.8 is package-scoped as CDDL-1.0: its CDDL-covered source remains available in the locked crates.io source archive at https://crates.io/crates/inferno/0.12.8 and https://github.com/jonhoo/inferno. The full locked license text follows below.
10846
+ Native Opus (opus/opusic-sys) and PCRE2 (pcre2/pcre2-sys) are covered by the exact crate license payloads below. inferno 0.12.8 is package-scoped as CDDL-1.0: its CDDL-covered source remains available in the locked crates.io source archive at https://crates.io/crates/inferno/0.12.8 and https://github.com/jonhoo/inferno. The full locked license text follows below.
10847
10847
 
10848
10848
  -------------------------------------------------------------------------------
10849
10849
  License: Apache License 2.0 (Apache-2.0)
@@ -12509,6 +12509,58 @@ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
12509
12509
  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
12510
12510
  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12511
12511
 
12512
+ -------------------------------------------------------------------------------
12513
+ License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)
12514
+ Used by:
12515
+ - opusic-sys 0.7.5 (https://github.com/DoumanAsh/opusic-sys)
12516
+
12517
+ Full license text:
12518
+
12519
+ Copyright 2001-2023 Xiph.Org, Skype Limited, Octasic,
12520
+ Jean-Marc Valin, Timothy B. Terriberry,
12521
+ CSIRO, Gregory Maxwell, Mark Borgerding,
12522
+ Erik de Castro Lopo, Mozilla, Amazon
12523
+
12524
+ Redistribution and use in source and binary forms, with or without
12525
+ modification, are permitted provided that the following conditions
12526
+ are met:
12527
+
12528
+ - Redistributions of source code must retain the above copyright
12529
+ notice, this list of conditions and the following disclaimer.
12530
+
12531
+ - Redistributions in binary form must reproduce the above copyright
12532
+ notice, this list of conditions and the following disclaimer in the
12533
+ documentation and/or other materials provided with the distribution.
12534
+
12535
+ - Neither the name of Internet Society, IETF or IETF Trust, nor the
12536
+ names of specific contributors, may be used to endorse or promote
12537
+ products derived from this software without specific prior written
12538
+ permission.
12539
+
12540
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12541
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
12542
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
12543
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
12544
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
12545
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
12546
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
12547
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
12548
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
12549
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
12550
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12551
+
12552
+ Opus is subject to the royalty-free patent licenses which are
12553
+ specified at:
12554
+
12555
+ Xiph.Org Foundation:
12556
+ https://datatracker.ietf.org/ipr/1524/
12557
+
12558
+ Microsoft Corporation:
12559
+ https://datatracker.ietf.org/ipr/1914/
12560
+
12561
+ Broadcom Corporation:
12562
+ https://datatracker.ietf.org/ipr/1526/
12563
+
12512
12564
  -------------------------------------------------------------------------------
12513
12565
  License: BSD 3-Clause "New" or "Revised" License (BSD-3-Clause)
12514
12566
  Used by:
@@ -13290,29 +13342,6 @@ jurisdiction of the Federal Courts of the Northern District of
13290
13342
  California and the state courts of the State of California, with
13291
13343
  venue lying in Santa Clara County, California.
13292
13344
 
13293
- -------------------------------------------------------------------------------
13294
- License: ISC License (ISC)
13295
- Used by:
13296
- - audiopus_sys 0.2.2 (https://github.com/lakelezz/audiopus_sys.git)
13297
-
13298
- Full license text:
13299
-
13300
- ISC License
13301
-
13302
- Copyright (c) 2019, Lakelezz
13303
-
13304
- Permission to use, copy, modify, and/or distribute this software for any
13305
- purpose with or without fee is hereby granted, provided that the above
13306
- copyright notice and this permission notice appear in all copies.
13307
-
13308
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13309
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13310
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13312
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13313
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13314
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
13315
-
13316
13345
  -------------------------------------------------------------------------------
13317
13346
  License: ISC License (ISC)
13318
13347
  Used by:
@@ -18734,7 +18763,7 @@ DEALINGS IN THE SOFTWARE.
18734
18763
  -------------------------------------------------------------------------------
18735
18764
  License: MIT License (MIT)
18736
18765
  Used by:
18737
- - opus 0.3.1 (https://github.com/SpaceManiac/opus-rs)
18766
+ - opus 0.4.0 (https://github.com/SpaceManiac/opus-rs)
18738
18767
 
18739
18768
  Full license text:
18740
18769
 
@@ -566,10 +566,20 @@ export declare function stripPreservedArchive(preserveData: Record<string, unkno
566
566
  export declare function archiveSourceText(archive: Archive): string | undefined;
567
567
  /** Build the text used to choose and preflight a font-aware snapcompact shape. */
568
568
  export declare function renderabilityProbeText(serialized: string, previousPreserveData?: Record<string, unknown>, previousSummary?: string): string;
569
+ /** A frame payload that can be priced before it is materialized. */
570
+ export interface LazyFrameData {
571
+ readonly bytes: number;
572
+ read(): string;
573
+ }
569
574
  /** Options for reconstructing a persisted snapcompact archive into prompt blocks. */
570
575
  export interface HistoryBlockOptions {
571
576
  /** Hard cap on image base64 bytes attached to one rebuilt provider request. */
572
577
  maxFrameDataBytes?: number;
578
+ /**
579
+ * Price and resolve a frame payload. Called in newest-first budget order.
580
+ * Returning `undefined` drops a missing payload.
581
+ */
582
+ resolveFrameData?: (data: string) => LazyFrameData | undefined;
573
583
  }
574
584
  /** Convert archive frames into LLM image blocks (oldest first). */
575
585
  export declare function images(archive: Archive): ImageContent[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@linxiraos/pi-snapcompact",
4
- "version": "1.1.14",
4
+ "version": "1.1.16",
5
5
  "description": "Bitmap-frame context compression for vision-capable LLMs",
6
6
  "homepage": "https://linxira-os.github.io/zeta/",
7
7
  "author": "Can Boluk",
@@ -31,11 +31,11 @@
31
31
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
32
32
  },
33
33
  "dependencies": {
34
- "@linxiraos/pi-ai": "1.1.14",
35
- "@linxiraos/pi-catalog": "1.1.14",
36
- "@linxiraos/pi-natives": "1.1.14",
37
- "@linxiraos/pi-utils": "1.1.14",
38
- "@linxiraos/pi-wire": "1.1.14"
34
+ "@linxiraos/pi-ai": "1.1.16",
35
+ "@linxiraos/pi-catalog": "1.1.16",
36
+ "@linxiraos/pi-natives": "1.1.16",
37
+ "@linxiraos/pi-utils": "1.1.16",
38
+ "@linxiraos/pi-wire": "1.1.16"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/bun": "^1.3.14"
@@ -1067,15 +1067,16 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
1067
1067
  // Preserve-data helpers
1068
1068
  // ============================================================================
1069
1069
 
1070
- const OPENAI_REMOTE_COMPACTION_PRESERVE_KEY = "openaiRemoteCompaction";
1070
+ /** Provider-native compaction payloads a snapcompact pass supersedes. */
1071
+ const PROVIDER_COMPACTION_PRESERVE_KEYS = ["openaiRemoteCompaction", "anthropicCompaction"] as const;
1071
1072
 
1072
- function stripOpenAiRemoteCompactionPreserveData(
1073
+ function stripProviderCompactionPreserveData(
1073
1074
  preserveData: Record<string, unknown> | undefined,
1074
1075
  ): Record<string, unknown> | undefined {
1075
- if (!preserveData || !(OPENAI_REMOTE_COMPACTION_PRESERVE_KEY in preserveData)) {
1076
+ if (!preserveData || !PROVIDER_COMPACTION_PRESERVE_KEYS.some(key => key in preserveData)) {
1076
1077
  return preserveData;
1077
1078
  }
1078
- const { [OPENAI_REMOTE_COMPACTION_PRESERVE_KEY]: _removed, ...rest } = preserveData;
1079
+ const { openaiRemoteCompaction: _openai, anthropicCompaction: _anthropic, ...rest } = preserveData;
1079
1080
  return Object.keys(rest).length > 0 ? rest : undefined;
1080
1081
  }
1081
1082
 
@@ -1768,10 +1769,21 @@ export function renderabilityProbeText(
1768
1769
  return serialized;
1769
1770
  }
1770
1771
 
1772
+ /** A frame payload that can be priced before it is materialized. */
1773
+ export interface LazyFrameData {
1774
+ readonly bytes: number;
1775
+ read(): string;
1776
+ }
1777
+
1771
1778
  /** Options for reconstructing a persisted snapcompact archive into prompt blocks. */
1772
1779
  export interface HistoryBlockOptions {
1773
1780
  /** Hard cap on image base64 bytes attached to one rebuilt provider request. */
1774
1781
  maxFrameDataBytes?: number;
1782
+ /**
1783
+ * Price and resolve a frame payload. Called in newest-first budget order.
1784
+ * Returning `undefined` drops a missing payload.
1785
+ */
1786
+ resolveFrameData?: (data: string) => LazyFrameData | undefined;
1775
1787
  }
1776
1788
 
1777
1789
  function formatFrameDataBytes(bytes: number): string {
@@ -1780,38 +1792,105 @@ function formatFrameDataBytes(bytes: number): string {
1780
1792
  return `${bytes} B`;
1781
1793
  }
1782
1794
 
1783
- function imagesWithinBudget(
1784
- archive: Archive,
1785
- maxFrameDataBytes: number | undefined,
1786
- ): { images: ImageContent[]; omittedFrames: number; omittedBytes: number } {
1787
- if (maxFrameDataBytes === undefined) {
1788
- return { images: images(archive), omittedFrames: 0, omittedBytes: 0 };
1795
+ /**
1796
+ * Prefix of an externalized frame payload (see the session blob store).
1797
+ *
1798
+ * A frame persisted by a recent session holds this reference rather than
1799
+ * base64, so a caller that never supplies `resolveFrameData` would otherwise
1800
+ * hand the reference string to the provider as image data. Dropping the frame
1801
+ * is the safe failure: a missing picture beats a rejected request.
1802
+ */
1803
+ const BLOB_REFERENCE_PREFIX = "blob:sha256:";
1804
+
1805
+ function isUnresolvedBlobReference(data: string): boolean {
1806
+ return data.startsWith(BLOB_REFERENCE_PREFIX);
1807
+ }
1808
+
1809
+ /** One reconstructed slot: a usable frame, an unavailable gap, or a byte-budget gap. */
1810
+ type FrameSlot = { frame: Frame } | { unavailable: true } | { omittedBytes: number };
1811
+
1812
+ /**
1813
+ * Price every frame newest-first and retain only payloads that fit the byte
1814
+ * budget. Gap slots preserve the original chronology without materializing
1815
+ * rejected payloads.
1816
+ */
1817
+ function imagesWithinBudget(archive: Archive, options: HistoryBlockOptions): FrameSlot[] {
1818
+ const { maxFrameDataBytes, resolveFrameData } = options;
1819
+ const hasUnresolvedReference = archive.frames.some(frame => isUnresolvedBlobReference(frame.data));
1820
+ if (maxFrameDataBytes === undefined && !resolveFrameData && !hasUnresolvedReference) {
1821
+ return archive.frames.map(frame => ({ frame }));
1789
1822
  }
1790
1823
 
1791
1824
  let usedBytes = 0;
1792
- let omittedFrames = 0;
1793
- let omittedBytes = 0;
1794
- const keptNewestFirst: Frame[] = [];
1825
+ const newestFirst: FrameSlot[] = [];
1795
1826
  for (let index = archive.frames.length - 1; index >= 0; index--) {
1796
1827
  const frame = archive.frames[index];
1797
1828
  if (!frame) continue;
1798
- const bytes = frame.data.length;
1799
- if (usedBytes + bytes > maxFrameDataBytes) {
1800
- omittedFrames++;
1801
- omittedBytes += bytes;
1829
+ const lazy = resolveFrameData?.(frame.data);
1830
+ if (!lazy && (resolveFrameData || isUnresolvedBlobReference(frame.data))) {
1831
+ newestFirst.push({ unavailable: true });
1832
+ continue;
1833
+ }
1834
+ const bytes = lazy ? lazy.bytes : frame.data.length;
1835
+ if (maxFrameDataBytes !== undefined && usedBytes + bytes > maxFrameDataBytes) {
1836
+ newestFirst.push({ omittedBytes: bytes });
1802
1837
  continue;
1803
1838
  }
1804
1839
  usedBytes += bytes;
1805
- keptNewestFirst.push(frame);
1840
+ newestFirst.push({ frame: lazy ? { ...frame, data: lazy.read() } : frame });
1806
1841
  }
1807
- keptNewestFirst.reverse();
1808
- return { images: images({ ...archive, frames: keptNewestFirst }), omittedFrames, omittedBytes };
1842
+ newestFirst.reverse();
1843
+ return newestFirst;
1844
+ }
1845
+
1846
+ /** Collapse a run of unavailable frames into one in-place gap marker. */
1847
+ function unavailableFrameNotice(count: number): string {
1848
+ return `-------------- ${count.toLocaleString()} archived image frame${count === 1 ? "" : "s"} unavailable here --------------`;
1849
+ }
1850
+
1851
+ /** Blocks for the imaged middle, with both gap causes kept in chronological position. */
1852
+ function frameBlocks(slots: FrameSlot[]): (TextContent | ImageContent)[] {
1853
+ const blocks: (TextContent | ImageContent)[] = [];
1854
+ let pendingGap: "unavailable" | "budget" | undefined;
1855
+ let pendingFrames = 0;
1856
+ let pendingBytes = 0;
1857
+ const flushGap = (): void => {
1858
+ if (!pendingGap) return;
1859
+ blocks.push({
1860
+ type: "text",
1861
+ text:
1862
+ pendingGap === "unavailable"
1863
+ ? unavailableFrameNotice(pendingFrames)
1864
+ : omittedFrameNotice(pendingFrames, pendingBytes),
1865
+ });
1866
+ pendingGap = undefined;
1867
+ pendingFrames = 0;
1868
+ pendingBytes = 0;
1869
+ };
1870
+ for (const slot of slots) {
1871
+ if ("frame" in slot) {
1872
+ flushGap();
1873
+ blocks.push(...images({ frames: [slot.frame] } as Archive));
1874
+ continue;
1875
+ }
1876
+ const gap = "unavailable" in slot ? "unavailable" : "budget";
1877
+ if (pendingGap && pendingGap !== gap) flushGap();
1878
+ pendingGap = gap;
1879
+ pendingFrames++;
1880
+ if ("omittedBytes" in slot) pendingBytes += slot.omittedBytes;
1881
+ }
1882
+ flushGap();
1883
+ return blocks;
1809
1884
  }
1810
1885
 
1811
1886
  function omittedFrameNotice(omittedFrames: number, omittedBytes: number): string {
1887
+ const budgetNote =
1888
+ omittedBytes > 0
1889
+ ? ` ${formatFrameDataBytes(omittedBytes)} of base64 exceeded the per-request snapcompact payload budget.`
1890
+ : "";
1812
1891
  return [
1813
1892
  "-------------- snapcompact image middle omitted",
1814
- `${omittedFrames.toLocaleString()} archived image frame${omittedFrames === 1 ? "" : "s"} (${formatFrameDataBytes(omittedBytes)} base64) exceeded the per-request snapcompact payload budget. The compacted summary and visible text edges remain available.`,
1893
+ `${omittedFrames.toLocaleString()} archived image frame${omittedFrames === 1 ? "" : "s"} could not be included.${budgetNote} The compacted summary and visible text edges remain available.`,
1815
1894
  "--------------",
1816
1895
  ].join("\n");
1817
1896
  }
@@ -1831,26 +1910,14 @@ export function images(archive: Archive): ImageContent[] {
1831
1910
  * instead of persisted on the session entry. */
1832
1911
  export function historyBlocks(archive: Archive, options: HistoryBlockOptions = {}): (TextContent | ImageContent)[] {
1833
1912
  const blocks: (TextContent | ImageContent)[] = [];
1834
- const budgeted = imagesWithinBudget(archive, options.maxFrameDataBytes);
1835
- const hasImages = budgeted.images.length > 0;
1836
- const hasOmittedImages = budgeted.omittedFrames > 0;
1913
+ const middle = frameBlocks(imagesWithinBudget(archive, options));
1914
+ const hasImages = middle.some(block => block.type === "image");
1915
+ const hasOmittedImages = middle.some(block => block.type === "text");
1837
1916
  if (archive.textHead) {
1838
- const suffix = hasImages
1839
- ? "\n-------------- imaged middle below\n"
1840
- : hasOmittedImages
1841
- ? `\n${omittedFrameNotice(budgeted.omittedFrames, budgeted.omittedBytes)}\n`
1842
- : "";
1917
+ const suffix = hasImages ? "\n-------------- imaged middle below\n" : "";
1843
1918
  blocks.push({ type: "text", text: elideDataUrls(toPlainText(archive.textHead), "archive") + suffix });
1844
- } else if (hasOmittedImages && !hasImages) {
1845
- blocks.push({ type: "text", text: omittedFrameNotice(budgeted.omittedFrames, budgeted.omittedBytes) });
1846
- }
1847
- // Omitted frames are the OLDEST archived images: the byte budget keeps the
1848
- // newest tail frames, so the gap notice precedes the kept images to keep the
1849
- // reconstructed blocks oldest-to-newest.
1850
- if (hasImages && hasOmittedImages) {
1851
- blocks.push({ type: "text", text: omittedFrameNotice(budgeted.omittedFrames, budgeted.omittedBytes) });
1852
1919
  }
1853
- blocks.push(...budgeted.images);
1920
+ blocks.push(...middle);
1854
1921
  if (archive.textTail) {
1855
1922
  const prefix = hasImages
1856
1923
  ? "-------------- imaged middle above\n"
@@ -2157,9 +2224,10 @@ export async function compact<TMessage = Message>(
2157
2224
  });
2158
2225
  }
2159
2226
 
2160
- // A snapcompact pass replaces any provider-side replacement history; strip the
2161
- // OpenAI remote-compaction payload like the default summarizer path does.
2162
- const basePreserve = stripOpenAiRemoteCompactionPreserveData(previousPreserveData) ?? {};
2227
+ // A snapcompact pass replaces any provider-side compaction payload; strip the
2228
+ // OpenAI replacement history and the Anthropic compaction block like the
2229
+ // default summarizer path does.
2230
+ const basePreserve = stripProviderCompactionPreserveData(previousPreserveData) ?? {};
2163
2231
  const persistedText =
2164
2232
  layout.keptText.length > 0 && layout.textTail.length > 0
2165
2233
  ? `${layout.keptText.slice(0, layout.keptText.length - layout.textTail.length)}${textTail}`