@oh-my-pi/snapcompact 16.4.6 → 16.5.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 +6 -0
- package/package.json +5 -5
- package/src/prompts/snapcompact-summary.md +4 -3
- package/src/snapcompact.ts +31 -28
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.5.0] - 2026-07-13
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Updated archived transcript rendering to use a more compact format with `¶user:`, `¶think:`, `¶ai:`, and `¶call:` scopes, omitting repeated adjacent scope headers and appending tool-call intents as comments.
|
|
10
|
+
|
|
5
11
|
## [16.3.7] - 2026-07-05
|
|
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": "16.
|
|
4
|
+
"version": "16.5.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,10 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@oh-my-pi/pi-ai": "16.
|
|
35
|
-
"@oh-my-pi/pi-natives": "16.
|
|
36
|
-
"@oh-my-pi/pi-utils": "16.
|
|
37
|
-
"@oh-my-pi/pi-wire": "16.
|
|
34
|
+
"@oh-my-pi/pi-ai": "16.5.0",
|
|
35
|
+
"@oh-my-pi/pi-natives": "16.5.0",
|
|
36
|
+
"@oh-my-pi/pi-utils": "16.5.0",
|
|
37
|
+
"@oh-my-pi/pi-wire": "16.5.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/bun": "^1.3.14"
|
|
@@ -1,6 +1,9 @@
|
|
|
1
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.
|
|
2
2
|
|
|
3
|
-
The archived transcript
|
|
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.
|
|
4
7
|
|
|
5
8
|
Reading HISTORY:
|
|
6
9
|
- Plain-text sections are the verbatim transcript — rely on them exactly.
|
|
@@ -9,9 +12,7 @@ Reading HISTORY:
|
|
|
9
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.
|
|
10
13
|
{{/if}}{{#if sentenceInk}} - Ink cycles through six colors, one per sentence.
|
|
11
14
|
{{/if}}{{#if stopwordDimmed}} - Function words are dim gray; content words keep full ink.
|
|
12
|
-
{{/if}}{{#if dimmedToolResults}} - Text inside `<out>` is dim gray — that gray is archived tool output, not conversation.
|
|
13
15
|
{{/if}}{{#if lineRepeated}} - Each line is printed twice (white, then a pale-yellow band); the two copies are identical.
|
|
14
|
-
{{/if}}{{#if mixedShapes}} - The compressed middle frames use a smaller, denser font than the edge frames; the reading order is unchanged.
|
|
15
16
|
{{/if}}{{/if}}{{#if includedPreviousSummary}}- HISTORY opens with a condensed digest of still-older context that predates the archived turns.
|
|
16
17
|
{{/if}}{{#if truncatedChars}}- About {{truncatedChars}} characters of older middle history were dropped to fit the archive budget.
|
|
17
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.
|
package/src/snapcompact.ts
CHANGED
|
@@ -638,8 +638,6 @@ export function createFileOps(): FileOperations {
|
|
|
638
638
|
}
|
|
639
639
|
const URL_SCHEME_RE = /[a-z][a-z0-9+.-]*:\/\//i;
|
|
640
640
|
|
|
641
|
-
const HEADING_MARKER = " ¶";
|
|
642
|
-
|
|
643
641
|
export function isUrlSchemePath(path: string): boolean {
|
|
644
642
|
return URL_SCHEME_RE.test(path);
|
|
645
643
|
}
|
|
@@ -776,10 +774,22 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
776
774
|
const headRatio = options?.truncateHeadRatio ?? TRUNCATE_HEAD_RATIO;
|
|
777
775
|
const dimToolResults = options?.dimToolResults !== false;
|
|
778
776
|
const parts: string[] = [];
|
|
777
|
+
let lastPrefix: string | null = null;
|
|
778
|
+
|
|
779
|
+
const pushPart = (prefix: string, content: string) => {
|
|
780
|
+
const lastIndex = parts.length - 1;
|
|
781
|
+
if (lastIndex >= 0 && lastPrefix === prefix) {
|
|
782
|
+
const sep = parts[lastIndex].endsWith("\n") || content.startsWith("\n") ? "" : "\n";
|
|
783
|
+
parts[lastIndex] += sep + content;
|
|
784
|
+
} else {
|
|
785
|
+
parts.push(prefix + content);
|
|
786
|
+
lastPrefix = prefix;
|
|
787
|
+
}
|
|
788
|
+
};
|
|
779
789
|
|
|
780
790
|
// Tool results flagged contextually useless (and their paired calls) carry no
|
|
781
791
|
// information worth archiving — skip the whole pair. Surviving results are
|
|
782
|
-
// indexed by tool-call id so each merges into its originating
|
|
792
|
+
// indexed by tool-call id so each merges into its originating `¶call:` scope.
|
|
783
793
|
const uselessCallIds = new Set<string>();
|
|
784
794
|
const resultTextByCallId = new Map<string, string>();
|
|
785
795
|
for (const msg of messages) {
|
|
@@ -796,7 +806,7 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
796
806
|
}
|
|
797
807
|
|
|
798
808
|
// Wrap a raw tool-result body in an `<out>` block, dimming only the body so
|
|
799
|
-
// the frame coloring keeps
|
|
809
|
+
// the frame coloring keeps scope markers and calls loud.
|
|
800
810
|
const renderResultBlock = (rawText: string): string => {
|
|
801
811
|
const body = truncateForSummary(stripDimMarkers(rawText), toolResultMaxChars, headRatio);
|
|
802
812
|
return `<out>\n${dimToolResults ? `${DIM_ON}${body}${DIM_OFF}` : body}\n</out>`;
|
|
@@ -813,18 +823,19 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
813
823
|
.filter((content): content is { type: "text"; text: string } => content.type === "text")
|
|
814
824
|
.map(content => content.text)
|
|
815
825
|
.join("");
|
|
816
|
-
if (content)
|
|
826
|
+
if (content) pushPart("¶user:", stripDimMarkers(content));
|
|
817
827
|
} else if (msg.role === "assistant") {
|
|
818
828
|
// Stream blocks in content order: buffer thinking/text, then flush a
|
|
819
|
-
//
|
|
820
|
-
// each tool call, so text or thinking after a call stays after it.
|
|
829
|
+
// separate section for each block type right before each tool call.
|
|
821
830
|
let pendingThinking: string[] = [];
|
|
822
831
|
let pendingText: string[] = [];
|
|
823
832
|
const flushAssistant = () => {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if (
|
|
833
|
+
if (pendingThinking.length > 0) {
|
|
834
|
+
pushPart("¶think:", pendingThinking.join("\n"));
|
|
835
|
+
}
|
|
836
|
+
if (pendingText.length > 0) {
|
|
837
|
+
pushPart("¶ai:", pendingText.join("\n"));
|
|
838
|
+
}
|
|
828
839
|
pendingThinking = [];
|
|
829
840
|
pendingText = [];
|
|
830
841
|
};
|
|
@@ -860,24 +871,27 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
860
871
|
toolCallMaxChars,
|
|
861
872
|
headRatio,
|
|
862
873
|
);
|
|
863
|
-
const lines = [
|
|
864
|
-
|
|
865
|
-
|
|
874
|
+
const lines: string[] = [];
|
|
875
|
+
let firstLine = `${block.name}(${argsStr})`;
|
|
876
|
+
if (intent) {
|
|
877
|
+
firstLine += `//${intent}`;
|
|
878
|
+
}
|
|
879
|
+
lines.push(firstLine);
|
|
866
880
|
const resultText = resultTextByCallId.get(block.id);
|
|
867
881
|
if (resultText !== undefined) {
|
|
868
882
|
mergedCallIds.add(block.id);
|
|
869
883
|
lines.push(renderResultBlock(resultText));
|
|
870
884
|
}
|
|
871
|
-
|
|
885
|
+
pushPart("¶call:", lines.join("\n"));
|
|
872
886
|
}
|
|
873
887
|
}
|
|
874
888
|
flushAssistant();
|
|
875
889
|
} else if (msg.role === "toolResult") {
|
|
876
|
-
// Paired results already merged into their
|
|
890
|
+
// Paired results already merged into their tool call block above;
|
|
877
891
|
// only orphans (call archived outside this window) render standalone.
|
|
878
892
|
if (uselessCallIds.has(msg.toolCallId) || mergedCallIds.has(msg.toolCallId)) continue;
|
|
879
893
|
const resultText = resultTextByCallId.get(msg.toolCallId);
|
|
880
|
-
if (resultText !== undefined)
|
|
894
|
+
if (resultText !== undefined) pushPart("¶call:", `\n${renderResultBlock(resultText)}`);
|
|
881
895
|
}
|
|
882
896
|
}
|
|
883
897
|
|
|
@@ -1911,15 +1925,6 @@ export async function compact<TMessage = Message>(
|
|
|
1911
1925
|
|
|
1912
1926
|
const frames = await Promise.all(newFrames);
|
|
1913
1927
|
const totalChars = frames.reduce((sum, frame) => sum + frame.chars, 0) + textChars;
|
|
1914
|
-
const mixedShapes = frames.some(
|
|
1915
|
-
frame =>
|
|
1916
|
-
frame.cols !== geo.cols ||
|
|
1917
|
-
frame.rows !== geo.rows ||
|
|
1918
|
-
(frame.variant ?? "sent") !== high.variant ||
|
|
1919
|
-
(frame.lineRepeat ?? 1) !== high.lineRepeat ||
|
|
1920
|
-
(frame.columns ?? 1) !== (high.columns ?? 1) ||
|
|
1921
|
-
(frame.stopwordDim ?? false) !== (high.stopwordDim ?? false),
|
|
1922
|
-
);
|
|
1923
1928
|
|
|
1924
1929
|
const { readFiles, modifiedFiles } = computeFileLists(fileOps);
|
|
1925
1930
|
const files = formatFileList(readFiles, modifiedFiles, fileOps.read);
|
|
@@ -1936,9 +1941,7 @@ export async function compact<TMessage = Message>(
|
|
|
1936
1941
|
rows: geo.rows,
|
|
1937
1942
|
sentenceInk: high.variant === "sent",
|
|
1938
1943
|
stopwordDimmed: high.stopwordDim === true,
|
|
1939
|
-
dimmedToolResults: options?.dimToolResults !== false,
|
|
1940
1944
|
lineRepeated: high.lineRepeat > 1,
|
|
1941
|
-
mixedShapes,
|
|
1942
1945
|
truncatedChars,
|
|
1943
1946
|
includedPreviousSummary,
|
|
1944
1947
|
files: files.length > 0 ? files : undefined,
|