pi-reasoning-zip 0.2.4 → 0.2.6
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 +16 -1
- package/README.md +13 -4
- package/dist/compactPrompt.d.ts +2 -1
- package/dist/compactPrompt.js +7 -2
- package/dist/compactorClient.js +1 -1
- package/dist/messageTransform.js +3 -1
- package/dist/settings.js +6 -2
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
- package/src/compactPrompt.ts +10 -2
- package/src/compactorClient.ts +1 -1
- package/src/messageTransform.ts +3 -1
- package/src/settings.ts +7 -3
- package/src/types.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.6] - 2026-07-03
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Replaced `thresholds.targetRatio` with `compressionRole` presets for compactor prompt style.
|
|
15
|
+
|
|
16
|
+
## [0.2.5] - 2026-07-03
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Skip Pi `thinkingSignature` and redacted thinking blocks instead of rewriting opaque provider metadata.
|
|
21
|
+
- Enforce `thresholds.targetRatio` when accepting compacted thinking output.
|
|
22
|
+
|
|
10
23
|
## [0.2.4] - 2026-07-03
|
|
11
24
|
|
|
12
25
|
### Fixed
|
|
@@ -71,7 +84,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
71
84
|
- Added package motto to README.
|
|
72
85
|
- Added npm release metadata: description, keywords, repository links, exports, and Node engine.
|
|
73
86
|
|
|
74
|
-
[Unreleased]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.
|
|
87
|
+
[Unreleased]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.6...HEAD
|
|
88
|
+
[0.2.6]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.5...v0.2.6
|
|
89
|
+
[0.2.5]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.4...v0.2.5
|
|
75
90
|
[0.2.4]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.3...v0.2.4
|
|
76
91
|
[0.2.3]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.2...v0.2.3
|
|
77
92
|
[0.2.2]: https://github.com/Ryu-CZ/pi-reasoning-zip/compare/v0.2.1...v0.2.2
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Compress reasoning blocks to keep the context short.
|
|
|
27
27
|
verbose thinking block -> facts / decisions / constraints / failed / next
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Use this when local models expose long reasoning and you want future Pi turns to replay compact traces instead of raw reasoning
|
|
30
|
+
Use this when local models expose long reasoning and you want future Pi turns to replay compact traces instead of raw reasoning as verbose prose.
|
|
31
31
|
|
|
32
32
|
## Install
|
|
33
33
|
|
|
@@ -60,7 +60,7 @@ The npm library entrypoint still builds to `dist/index.js`, but Pi package metad
|
|
|
60
60
|
- **llama.cpp-first targeting** — defaults to llama.cpp-like providers such as `llama-server=http://127.0.0.1:7484`.
|
|
61
61
|
- **Prompt minimization** — optional grug-style request injection for target local providers.
|
|
62
62
|
- **Fail-open safety** — preserves original messages on errors, timeouts, invalid output, or unknown payloads.
|
|
63
|
-
- **Opaque reasoning guard** — skips signed
|
|
63
|
+
- **Opaque reasoning guard** — skips signed, encrypted, signature-bearing, redacted, or provider-opaque reasoning metadata.
|
|
64
64
|
|
|
65
65
|
## Commands
|
|
66
66
|
|
|
@@ -83,6 +83,7 @@ Settings live in project `.pi/settings.json` or global `~/.pi/agent/settings.jso
|
|
|
83
83
|
"enabled": true,
|
|
84
84
|
"mode": "llama-only",
|
|
85
85
|
"storageMode": "compact-new",
|
|
86
|
+
"compressionRole": "grug",
|
|
86
87
|
"injectPrompt": true,
|
|
87
88
|
"compactor": {
|
|
88
89
|
"baseUrl": "http://127.0.0.1:7484/v1",
|
|
@@ -94,7 +95,6 @@ Settings live in project `.pi/settings.json` or global `~/.pi/agent/settings.jso
|
|
|
94
95
|
},
|
|
95
96
|
"thresholds": {
|
|
96
97
|
"minChars": 1000,
|
|
97
|
-
"targetRatio": 0.15,
|
|
98
98
|
"maxTraceChars": 2000
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -117,6 +117,14 @@ Settings live in project `.pi/settings.json` or global `~/.pi/agent/settings.jso
|
|
|
117
117
|
| `compact-new` | Compact new assistant thinking before storage |
|
|
118
118
|
| `off` | Do not alter assistant messages |
|
|
119
119
|
|
|
120
|
+
### Compression roles
|
|
121
|
+
|
|
122
|
+
| Role | Behavior |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `balanced` | concise bullets while preserving extra context |
|
|
125
|
+
| `grug` | terse, keyword-heavy default |
|
|
126
|
+
| `ultra-grug` | most aggressive fragment-style trace |
|
|
127
|
+
|
|
120
128
|
## Compactor endpoint
|
|
121
129
|
|
|
122
130
|
The compactor must expose an OpenAI-compatible chat completions endpoint:
|
|
@@ -142,7 +150,7 @@ next:
|
|
|
142
150
|
- ...
|
|
143
151
|
```
|
|
144
152
|
|
|
145
|
-
If the compactor returns `none`, empty output, output longer than the original, or output over `thresholds.maxTraceChars`, the original block is preserved.
|
|
153
|
+
The configured `compressionRole` guides the compactor's terse style. If the compactor returns `none`, empty output, output longer than the original, or output over `thresholds.maxTraceChars`, the original block is preserved.
|
|
146
154
|
|
|
147
155
|
## Safety model
|
|
148
156
|
|
|
@@ -159,6 +167,7 @@ It skips:
|
|
|
159
167
|
- non-assistant messages
|
|
160
168
|
- messages without array content
|
|
161
169
|
- short thinking below `thresholds.minChars`
|
|
170
|
+
- signature-bearing or redacted thinking blocks
|
|
162
171
|
- unknown providers by default in `llama-only`
|
|
163
172
|
- hosted/non-local providers in `local-only`
|
|
164
173
|
|
package/dist/compactPrompt.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ReasoningZipCompressionRole } from "./types.js";
|
|
2
|
+
export declare function buildCompactionPrompt(thinking: string, compressionRole?: ReasoningZipCompressionRole): string;
|
package/dist/compactPrompt.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
const ROLE_INSTRUCTIONS = {
|
|
2
|
+
balanced: "Compression role: balanced. Use concise bullets, but keep enough context that another coding agent can continue without guessing.",
|
|
3
|
+
grug: "Compression role: grug. Few words. Keyword-heavy. No prose. Keep only useful state for the next coding turn.",
|
|
4
|
+
"ultra-grug": "Compression role: ultra-grug. Compress hard. Fragment bullets only. Symbols and exact names over sentences. Keep only critical state.",
|
|
5
|
+
};
|
|
6
|
+
export function buildCompactionPrompt(thinking, compressionRole = "grug") {
|
|
2
7
|
return `Compress this model reasoning into a compact decision trace for future coding-agent context.
|
|
3
8
|
|
|
4
9
|
Keep exact paths, commands, symbols, errors, decisions, constraints, failed attempts, and next actions.
|
|
5
10
|
Drop self-talk, repeated planning, obvious reasoning, filler, and prose.
|
|
6
11
|
Use terse bullets under: facts, decisions, constraints, failed, next.
|
|
7
|
-
|
|
12
|
+
${ROLE_INSTRUCTIONS[compressionRole]}
|
|
8
13
|
If no useful content remains, output exactly: none
|
|
9
14
|
|
|
10
15
|
Reasoning:
|
package/dist/compactorClient.js
CHANGED
|
@@ -4,7 +4,7 @@ function buildPayload(thinking, settings, disableThinking) {
|
|
|
4
4
|
model: settings.compactor.model,
|
|
5
5
|
messages: [
|
|
6
6
|
{ role: "system", content: "You compress reasoning traces. Output only compact trace." },
|
|
7
|
-
{ role: "user", content: buildCompactionPrompt(thinking) },
|
|
7
|
+
{ role: "user", content: buildCompactionPrompt(thinking, settings.compressionRole) },
|
|
8
8
|
],
|
|
9
9
|
max_tokens: settings.compactor.maxTokens,
|
|
10
10
|
temperature: settings.compactor.temperature,
|
package/dist/messageTransform.js
CHANGED
|
@@ -5,8 +5,10 @@ function isThinkingBlock(block) {
|
|
|
5
5
|
function hasOpaqueReasoningMetadata(block) {
|
|
6
6
|
return (typeof block.signature === "string" ||
|
|
7
7
|
typeof block.reasoning_signature === "string" ||
|
|
8
|
+
typeof block.thinkingSignature === "string" ||
|
|
8
9
|
typeof block.encrypted_content === "string" ||
|
|
9
|
-
Array.isArray(block.reasoning_details)
|
|
10
|
+
Array.isArray(block.reasoning_details) ||
|
|
11
|
+
block.redacted === true);
|
|
10
12
|
}
|
|
11
13
|
function acceptableCompaction(original, compacted, settings) {
|
|
12
14
|
const text = compacted.trim();
|
package/dist/settings.js
CHANGED
|
@@ -2,6 +2,7 @@ export const DEFAULT_SETTINGS = {
|
|
|
2
2
|
enabled: true,
|
|
3
3
|
mode: "llama-only",
|
|
4
4
|
storageMode: "compact-new",
|
|
5
|
+
compressionRole: "grug",
|
|
5
6
|
injectPrompt: true,
|
|
6
7
|
compactor: {
|
|
7
8
|
baseUrl: "http://127.0.0.1:7484/v1",
|
|
@@ -13,12 +14,12 @@ export const DEFAULT_SETTINGS = {
|
|
|
13
14
|
},
|
|
14
15
|
thresholds: {
|
|
15
16
|
minChars: 1000,
|
|
16
|
-
targetRatio: 0.15,
|
|
17
17
|
maxTraceChars: 2000,
|
|
18
18
|
},
|
|
19
19
|
};
|
|
20
20
|
const modes = new Set(["llama-only", "local-only", "all", "disabled"]);
|
|
21
21
|
const storageModes = new Set(["compact-new", "off"]);
|
|
22
|
+
const compressionRoles = new Set(["balanced", "grug", "ultra-grug"]);
|
|
22
23
|
function asObject(value) {
|
|
23
24
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
24
25
|
}
|
|
@@ -39,10 +40,14 @@ export function resolveReasoningZipSettings(input) {
|
|
|
39
40
|
const storageMode = storageModes.has(root.storageMode)
|
|
40
41
|
? root.storageMode
|
|
41
42
|
: DEFAULT_SETTINGS.storageMode;
|
|
43
|
+
const compressionRole = compressionRoles.has(root.compressionRole)
|
|
44
|
+
? root.compressionRole
|
|
45
|
+
: DEFAULT_SETTINGS.compressionRole;
|
|
42
46
|
return {
|
|
43
47
|
enabled: booleanValue(root.enabled, DEFAULT_SETTINGS.enabled),
|
|
44
48
|
mode,
|
|
45
49
|
storageMode,
|
|
50
|
+
compressionRole,
|
|
46
51
|
injectPrompt: booleanValue(root.injectPrompt, DEFAULT_SETTINGS.injectPrompt),
|
|
47
52
|
compactor: {
|
|
48
53
|
baseUrl: stringValue(compactor.baseUrl, DEFAULT_SETTINGS.compactor.baseUrl).replace(/\/$/, ""),
|
|
@@ -54,7 +59,6 @@ export function resolveReasoningZipSettings(input) {
|
|
|
54
59
|
},
|
|
55
60
|
thresholds: {
|
|
56
61
|
minChars: numberValue(thresholds.minChars, DEFAULT_SETTINGS.thresholds.minChars, 0),
|
|
57
|
-
targetRatio: numberValue(thresholds.targetRatio, DEFAULT_SETTINGS.thresholds.targetRatio, 0),
|
|
58
62
|
maxTraceChars: numberValue(thresholds.maxTraceChars, DEFAULT_SETTINGS.thresholds.maxTraceChars, 1),
|
|
59
63
|
},
|
|
60
64
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export type ReasoningZipMode = "llama-only" | "local-only" | "all" | "disabled";
|
|
2
2
|
export type ReasoningZipStorageMode = "compact-new" | "off";
|
|
3
|
+
export type ReasoningZipCompressionRole = "balanced" | "grug" | "ultra-grug";
|
|
3
4
|
export interface ReasoningZipSettings {
|
|
4
5
|
enabled: boolean;
|
|
5
6
|
mode: ReasoningZipMode;
|
|
6
7
|
storageMode: ReasoningZipStorageMode;
|
|
8
|
+
compressionRole: ReasoningZipCompressionRole;
|
|
7
9
|
injectPrompt: boolean;
|
|
8
10
|
compactor: {
|
|
9
11
|
baseUrl: string;
|
|
@@ -15,7 +17,6 @@ export interface ReasoningZipSettings {
|
|
|
15
17
|
};
|
|
16
18
|
thresholds: {
|
|
17
19
|
minChars: number;
|
|
18
|
-
targetRatio: number;
|
|
19
20
|
maxTraceChars: number;
|
|
20
21
|
};
|
|
21
22
|
}
|
package/package.json
CHANGED
package/src/compactPrompt.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ReasoningZipCompressionRole } from "./types.js";
|
|
2
|
+
|
|
3
|
+
const ROLE_INSTRUCTIONS: Record<ReasoningZipCompressionRole, string> = {
|
|
4
|
+
balanced: "Compression role: balanced. Use concise bullets, but keep enough context that another coding agent can continue without guessing.",
|
|
5
|
+
grug: "Compression role: grug. Few words. Keyword-heavy. No prose. Keep only useful state for the next coding turn.",
|
|
6
|
+
"ultra-grug": "Compression role: ultra-grug. Compress hard. Fragment bullets only. Symbols and exact names over sentences. Keep only critical state.",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function buildCompactionPrompt(thinking: string, compressionRole: ReasoningZipCompressionRole = "grug"): string {
|
|
2
10
|
return `Compress this model reasoning into a compact decision trace for future coding-agent context.
|
|
3
11
|
|
|
4
12
|
Keep exact paths, commands, symbols, errors, decisions, constraints, failed attempts, and next actions.
|
|
5
13
|
Drop self-talk, repeated planning, obvious reasoning, filler, and prose.
|
|
6
14
|
Use terse bullets under: facts, decisions, constraints, failed, next.
|
|
7
|
-
|
|
15
|
+
${ROLE_INSTRUCTIONS[compressionRole]}
|
|
8
16
|
If no useful content remains, output exactly: none
|
|
9
17
|
|
|
10
18
|
Reasoning:
|
package/src/compactorClient.ts
CHANGED
|
@@ -6,7 +6,7 @@ function buildPayload(thinking: string, settings: ReasoningZipSettings, disableT
|
|
|
6
6
|
model: settings.compactor.model,
|
|
7
7
|
messages: [
|
|
8
8
|
{ role: "system", content: "You compress reasoning traces. Output only compact trace." },
|
|
9
|
-
{ role: "user", content: buildCompactionPrompt(thinking) },
|
|
9
|
+
{ role: "user", content: buildCompactionPrompt(thinking, settings.compressionRole) },
|
|
10
10
|
],
|
|
11
11
|
max_tokens: settings.compactor.maxTokens,
|
|
12
12
|
temperature: settings.compactor.temperature,
|
package/src/messageTransform.ts
CHANGED
|
@@ -11,8 +11,10 @@ function hasOpaqueReasoningMetadata(block: PiMessageBlock): boolean {
|
|
|
11
11
|
return (
|
|
12
12
|
typeof block.signature === "string" ||
|
|
13
13
|
typeof block.reasoning_signature === "string" ||
|
|
14
|
+
typeof block.thinkingSignature === "string" ||
|
|
14
15
|
typeof block.encrypted_content === "string" ||
|
|
15
|
-
Array.isArray(block.reasoning_details)
|
|
16
|
+
Array.isArray(block.reasoning_details) ||
|
|
17
|
+
block.redacted === true
|
|
16
18
|
);
|
|
17
19
|
}
|
|
18
20
|
|
package/src/settings.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { ReasoningZipMode, ReasoningZipSettings, ReasoningZipStorageMode } from "./types.js";
|
|
1
|
+
import type { ReasoningZipCompressionRole, ReasoningZipMode, ReasoningZipSettings, ReasoningZipStorageMode } from "./types.js";
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_SETTINGS: ReasoningZipSettings = {
|
|
4
4
|
enabled: true,
|
|
5
5
|
mode: "llama-only",
|
|
6
6
|
storageMode: "compact-new",
|
|
7
|
+
compressionRole: "grug",
|
|
7
8
|
injectPrompt: true,
|
|
8
9
|
compactor: {
|
|
9
10
|
baseUrl: "http://127.0.0.1:7484/v1",
|
|
@@ -15,13 +16,13 @@ export const DEFAULT_SETTINGS: ReasoningZipSettings = {
|
|
|
15
16
|
},
|
|
16
17
|
thresholds: {
|
|
17
18
|
minChars: 1000,
|
|
18
|
-
targetRatio: 0.15,
|
|
19
19
|
maxTraceChars: 2000,
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const modes = new Set<ReasoningZipMode>(["llama-only", "local-only", "all", "disabled"]);
|
|
24
24
|
const storageModes = new Set<ReasoningZipStorageMode>(["compact-new", "off"]);
|
|
25
|
+
const compressionRoles = new Set<ReasoningZipCompressionRole>(["balanced", "grug", "ultra-grug"]);
|
|
25
26
|
|
|
26
27
|
function asObject(value: unknown): Record<string, unknown> {
|
|
27
28
|
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
|
|
@@ -48,11 +49,15 @@ export function resolveReasoningZipSettings(input: unknown): ReasoningZipSetting
|
|
|
48
49
|
const storageMode = storageModes.has(root.storageMode as ReasoningZipStorageMode)
|
|
49
50
|
? (root.storageMode as ReasoningZipStorageMode)
|
|
50
51
|
: DEFAULT_SETTINGS.storageMode;
|
|
52
|
+
const compressionRole = compressionRoles.has(root.compressionRole as ReasoningZipCompressionRole)
|
|
53
|
+
? (root.compressionRole as ReasoningZipCompressionRole)
|
|
54
|
+
: DEFAULT_SETTINGS.compressionRole;
|
|
51
55
|
|
|
52
56
|
return {
|
|
53
57
|
enabled: booleanValue(root.enabled, DEFAULT_SETTINGS.enabled),
|
|
54
58
|
mode,
|
|
55
59
|
storageMode,
|
|
60
|
+
compressionRole,
|
|
56
61
|
injectPrompt: booleanValue(root.injectPrompt, DEFAULT_SETTINGS.injectPrompt),
|
|
57
62
|
compactor: {
|
|
58
63
|
baseUrl: stringValue(compactor.baseUrl, DEFAULT_SETTINGS.compactor.baseUrl).replace(/\/$/, ""),
|
|
@@ -64,7 +69,6 @@ export function resolveReasoningZipSettings(input: unknown): ReasoningZipSetting
|
|
|
64
69
|
},
|
|
65
70
|
thresholds: {
|
|
66
71
|
minChars: numberValue(thresholds.minChars, DEFAULT_SETTINGS.thresholds.minChars, 0),
|
|
67
|
-
targetRatio: numberValue(thresholds.targetRatio, DEFAULT_SETTINGS.thresholds.targetRatio, 0),
|
|
68
72
|
maxTraceChars: numberValue(thresholds.maxTraceChars, DEFAULT_SETTINGS.thresholds.maxTraceChars, 1),
|
|
69
73
|
},
|
|
70
74
|
};
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
export type ReasoningZipMode = "llama-only" | "local-only" | "all" | "disabled";
|
|
2
2
|
export type ReasoningZipStorageMode = "compact-new" | "off";
|
|
3
|
+
export type ReasoningZipCompressionRole = "balanced" | "grug" | "ultra-grug";
|
|
3
4
|
|
|
4
5
|
export interface ReasoningZipSettings {
|
|
5
6
|
enabled: boolean;
|
|
6
7
|
mode: ReasoningZipMode;
|
|
7
8
|
storageMode: ReasoningZipStorageMode;
|
|
9
|
+
compressionRole: ReasoningZipCompressionRole;
|
|
8
10
|
injectPrompt: boolean;
|
|
9
11
|
compactor: {
|
|
10
12
|
baseUrl: string;
|
|
@@ -16,7 +18,6 @@ export interface ReasoningZipSettings {
|
|
|
16
18
|
};
|
|
17
19
|
thresholds: {
|
|
18
20
|
minChars: number;
|
|
19
|
-
targetRatio: number;
|
|
20
21
|
maxTraceChars: number;
|
|
21
22
|
};
|
|
22
23
|
}
|