@oh-my-pi/snapcompact 16.0.7 → 16.0.9
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 +20 -0
- package/dist/types/snapcompact.d.ts +1 -0
- package/package.json +4 -4
- package/src/snapcompact.ts +68 -32
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.0.8] - 2026-06-18
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `<out>` block wrapping for tool results to improve document structure
|
|
10
|
+
- Rendered thinking process as italicized blocks above assistant text
|
|
11
|
+
- Displayed tool call intents as `//` comments in tool call headers
|
|
12
|
+
- Changed conversation role markers to standard Markdown headings
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Merged tool results into their corresponding tool call blocks
|
|
17
|
+
- Preserved prose formatting around tool calls to maintain conversation flow
|
|
18
|
+
- Hidden `_i` argument from tool call output when an intent is provided
|
|
19
|
+
- Optimized assistant turn output to group thinking and text blocks efficiently
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Fixed improper splitting of assistant messages around useless tool calls
|
|
24
|
+
|
|
5
25
|
## [16.0.1] - 2026-06-15
|
|
6
26
|
|
|
7
27
|
### Added
|
|
@@ -409,6 +409,7 @@ export interface CompactionResult<T = CompactionDetails> {
|
|
|
409
409
|
}
|
|
410
410
|
export type ConvertToLlm<TMessage = Message> = (messages: TMessage[]) => Message[];
|
|
411
411
|
export declare function createFileOps(): FileOperations;
|
|
412
|
+
export declare function isUrlSchemePath(path: string): boolean;
|
|
412
413
|
export declare function computeFileLists(fileOps: FileOperations): CompactionDetails;
|
|
413
414
|
export declare function upsertFileOperations(summary: string, readFiles: string[], modifiedFiles: string[], readSet?: ReadonlySet<string>): string;
|
|
414
415
|
/** Default per-tool-result character cap in serialized history. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/snapcompact",
|
|
4
|
-
"version": "16.0.
|
|
4
|
+
"version": "16.0.9",
|
|
5
5
|
"description": "Bitmap-frame context compression for vision-capable LLMs",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"fmt": "biome format --write ."
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@oh-my-pi/pi-ai": "16.0.
|
|
35
|
-
"@oh-my-pi/pi-natives": "16.0.
|
|
36
|
-
"@oh-my-pi/pi-utils": "16.0.
|
|
34
|
+
"@oh-my-pi/pi-ai": "16.0.9",
|
|
35
|
+
"@oh-my-pi/pi-natives": "16.0.9",
|
|
36
|
+
"@oh-my-pi/pi-utils": "16.0.9"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/bun": "^1.3.14"
|
package/src/snapcompact.ts
CHANGED
|
@@ -551,10 +551,17 @@ export function createFileOps(): FileOperations {
|
|
|
551
551
|
edited: new Set(),
|
|
552
552
|
};
|
|
553
553
|
}
|
|
554
|
+
const URL_SCHEME_RE = /[a-z][a-z0-9+.-]*:\/\//i;
|
|
555
|
+
|
|
556
|
+
const HEADING_MARKER = " ¶";
|
|
557
|
+
|
|
558
|
+
export function isUrlSchemePath(path: string): boolean {
|
|
559
|
+
return URL_SCHEME_RE.test(path);
|
|
560
|
+
}
|
|
554
561
|
|
|
555
562
|
export function computeFileLists(fileOps: FileOperations): CompactionDetails {
|
|
556
|
-
const modified = new Set([...fileOps.edited, ...fileOps.written]);
|
|
557
|
-
const readFiles = [...fileOps.read].filter(file => !modified.has(file)).sort();
|
|
563
|
+
const modified = new Set([...fileOps.edited, ...fileOps.written].filter(file => !isUrlSchemePath(file)));
|
|
564
|
+
const readFiles = [...fileOps.read].filter(file => !isUrlSchemePath(file) && !modified.has(file)).sort();
|
|
558
565
|
const modifiedFiles = [...modified].sort();
|
|
559
566
|
return { readFiles, modifiedFiles };
|
|
560
567
|
}
|
|
@@ -679,15 +686,33 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
679
686
|
const dimToolResults = options?.dimToolResults !== false;
|
|
680
687
|
const parts: string[] = [];
|
|
681
688
|
|
|
682
|
-
// Tool results flagged contextually useless (and their paired calls) carry
|
|
683
|
-
//
|
|
689
|
+
// Tool results flagged contextually useless (and their paired calls) carry no
|
|
690
|
+
// information worth archiving — skip the whole pair. Surviving results are
|
|
691
|
+
// indexed by tool-call id so each merges into its originating `# Tool call`.
|
|
684
692
|
const uselessCallIds = new Set<string>();
|
|
693
|
+
const resultTextByCallId = new Map<string, string>();
|
|
685
694
|
for (const msg of messages) {
|
|
686
|
-
if (msg.role
|
|
695
|
+
if (msg.role !== "toolResult") continue;
|
|
696
|
+
if (msg.useless === true && msg.isError !== true) {
|
|
687
697
|
uselessCallIds.add(msg.toolCallId);
|
|
698
|
+
continue;
|
|
688
699
|
}
|
|
700
|
+
const text = msg.content
|
|
701
|
+
.filter((block): block is { type: "text"; text: string } => block.type === "text")
|
|
702
|
+
.map(block => block.text)
|
|
703
|
+
.join("");
|
|
704
|
+
if (text) resultTextByCallId.set(msg.toolCallId, text);
|
|
689
705
|
}
|
|
690
706
|
|
|
707
|
+
// Wrap a raw tool-result body in an `<out>` block, dimming only the body so
|
|
708
|
+
// the frame coloring keeps structure (headings, calls) loud.
|
|
709
|
+
const renderResultBlock = (rawText: string): string => {
|
|
710
|
+
const body = truncateForSummary(stripDimMarkers(rawText), toolResultMaxChars, headRatio);
|
|
711
|
+
return `<out>\n${dimToolResults ? `${DIM_ON}${body}${DIM_OFF}` : body}\n</out>`;
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
const mergedCallIds = new Set<string>();
|
|
715
|
+
|
|
691
716
|
for (const msg of messages) {
|
|
692
717
|
if (msg.role === "user") {
|
|
693
718
|
const content =
|
|
@@ -697,22 +722,39 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
697
722
|
.filter((content): content is { type: "text"; text: string } => content.type === "text")
|
|
698
723
|
.map(content => content.text)
|
|
699
724
|
.join("");
|
|
700
|
-
if (content) parts.push(
|
|
725
|
+
if (content) parts.push(`# User${HEADING_MARKER}\n${stripDimMarkers(content)}`);
|
|
701
726
|
} else if (msg.role === "assistant") {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
727
|
+
// Stream blocks in content order: buffer thinking/text, then flush a
|
|
728
|
+
// `# Assistant` block (thinking as italics above the text) right before
|
|
729
|
+
// each tool call, so text or thinking after a call stays after it.
|
|
730
|
+
let pendingThinking: string[] = [];
|
|
731
|
+
let pendingText: string[] = [];
|
|
732
|
+
const flushAssistant = () => {
|
|
733
|
+
const sections: string[] = [];
|
|
734
|
+
if (pendingThinking.length > 0) sections.push(`_${pendingThinking.join("\n")}_`);
|
|
735
|
+
if (pendingText.length > 0) sections.push(pendingText.join("\n"));
|
|
736
|
+
if (sections.length > 0) parts.push(`# Assistant${HEADING_MARKER}\n${sections.join("\n\n")}`);
|
|
737
|
+
pendingThinking = [];
|
|
738
|
+
pendingText = [];
|
|
739
|
+
};
|
|
705
740
|
|
|
706
741
|
for (const block of msg.content) {
|
|
707
742
|
if (block.type === "text") {
|
|
708
|
-
|
|
743
|
+
pendingText.push(stripDimMarkers(block.text));
|
|
709
744
|
} else if (block.type === "thinking") {
|
|
710
|
-
|
|
745
|
+
pendingThinking.push(stripDimMarkers(block.thinking));
|
|
711
746
|
} else if (block.type === "toolCall") {
|
|
712
747
|
if (uselessCallIds.has(block.id)) continue;
|
|
748
|
+
flushAssistant();
|
|
713
749
|
const args = block.arguments as Record<string, unknown>;
|
|
750
|
+
// Prefer the harness-derived intent, else the raw `_i` arg; render it as
|
|
751
|
+
// a one-line `//comment` and drop `_i` from the args below.
|
|
752
|
+
const rawIntent =
|
|
753
|
+
typeof block.intent === "string" ? block.intent : typeof args._i === "string" ? args._i : "";
|
|
754
|
+
const intent = stripDimMarkers(rawIntent).replace(/\s+/g, " ").trim();
|
|
714
755
|
const argsStr = truncateForSummary(
|
|
715
756
|
Object.entries(args)
|
|
757
|
+
.filter(([key]) => key !== "_i")
|
|
716
758
|
.map(
|
|
717
759
|
([key, value]) =>
|
|
718
760
|
`${key}=${truncateForSummary(JSON.stringify(value) ?? "undefined", toolArgMaxChars, headRatio)}`,
|
|
@@ -721,30 +763,24 @@ export function serializeConversation(messages: Message[], options?: SerializeOp
|
|
|
721
763
|
toolCallMaxChars,
|
|
722
764
|
headRatio,
|
|
723
765
|
);
|
|
724
|
-
|
|
766
|
+
const lines = [`# Tool call${HEADING_MARKER}`];
|
|
767
|
+
if (intent) lines.push(`//${intent}`);
|
|
768
|
+
lines.push(`${block.name}(${argsStr})`);
|
|
769
|
+
const resultText = resultTextByCallId.get(block.id);
|
|
770
|
+
if (resultText !== undefined) {
|
|
771
|
+
mergedCallIds.add(block.id);
|
|
772
|
+
lines.push(renderResultBlock(resultText));
|
|
773
|
+
}
|
|
774
|
+
parts.push(lines.join("\n"));
|
|
725
775
|
}
|
|
726
776
|
}
|
|
727
|
-
|
|
728
|
-
if (thinkingParts.length > 0) {
|
|
729
|
-
parts.push(`[Think]: ${thinkingParts.join("\n")}`);
|
|
730
|
-
}
|
|
731
|
-
if (textParts.length > 0) {
|
|
732
|
-
parts.push(`[Assistant]: ${textParts.join("\n")}`);
|
|
733
|
-
}
|
|
734
|
-
if (toolCalls.length > 0) {
|
|
735
|
-
parts.push(`[Tool Call]: ${toolCalls.join("; ")}`);
|
|
736
|
-
}
|
|
777
|
+
flushAssistant();
|
|
737
778
|
} else if (msg.role === "toolResult") {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
if (content) {
|
|
744
|
-
// Args above are JSON-escaped, so only raw result text can carry toggles.
|
|
745
|
-
const body = truncateForSummary(stripDimMarkers(content), toolResultMaxChars, headRatio);
|
|
746
|
-
parts.push(dimToolResults ? `[Tool Result]: ${DIM_ON}${body}${DIM_OFF}` : `[Tool Result]: ${body}`);
|
|
747
|
-
}
|
|
779
|
+
// Paired results already merged into their `# Tool call` block above;
|
|
780
|
+
// only orphans (call archived outside this window) render standalone.
|
|
781
|
+
if (uselessCallIds.has(msg.toolCallId) || mergedCallIds.has(msg.toolCallId)) continue;
|
|
782
|
+
const resultText = resultTextByCallId.get(msg.toolCallId);
|
|
783
|
+
if (resultText !== undefined) parts.push(`# Tool call${HEADING_MARKER}\n${renderResultBlock(resultText)}`);
|
|
748
784
|
}
|
|
749
785
|
}
|
|
750
786
|
|