@henryqw/pi-auto-compact 1.1.9 → 2.0.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/README.md +23 -21
- package/extensions/auto-compact.ts +16 -21
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# `@henryqw/pi-auto-compact`
|
|
2
2
|
|
|
3
|
-
Compact context before it
|
|
3
|
+
Compact context before it reaches the configured threshold. Then resume the current task.
|
|
4
4
|
|
|
5
5
|
## Why
|
|
6
6
|
|
|
7
|
-
- **Created for**:
|
|
8
|
-
- **Advantage**:
|
|
7
|
+
- **Created for**: Compact session context automatically before it reaches the configured threshold.
|
|
8
|
+
- **Advantage**: Trigger early, use a dedicated model, and resume the interrupted task without manual work.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -14,14 +14,6 @@ pi install npm:@henryqw/pi-task-models
|
|
|
14
14
|
pi install npm:@henryqw/pi-auto-compact
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Requires Pi Coding Agent 0.84.4+.
|
|
18
|
-
|
|
19
|
-
## With
|
|
20
|
-
|
|
21
|
-
| Package | Why |
|
|
22
|
-
| --- | --- |
|
|
23
|
-
| `@henryqw/pi-task-models` | Required. Shared model profiles for compaction routes. |
|
|
24
|
-
|
|
25
17
|
Disable Pi's built-in auto-compaction in `~/.pi/agent/settings.json`:
|
|
26
18
|
|
|
27
19
|
```json
|
|
@@ -34,17 +26,27 @@ Disable Pi's built-in auto-compaction in `~/.pi/agent/settings.json`:
|
|
|
34
26
|
|
|
35
27
|
Restart Pi after install or settings changes. Trusted project settings in `.pi/settings.json` must not set `compaction.enabled` back to `true`.
|
|
36
28
|
|
|
29
|
+
## With
|
|
30
|
+
|
|
31
|
+
This package requires `@henryqw/pi-task-models` for shared compaction routes.
|
|
32
|
+
|
|
33
|
+
`~/.pi/agent/config/pi-task-models/config.json` is shared and owned by `@henryqw/pi-task-models`. The local `pi-auto-compact/autoCompact` declaration defaults to `fast`. A task entry is an explicit user override.
|
|
34
|
+
|
|
35
|
+
Malformed shared config is reported and left unchanged. Compaction then uses the current session model.
|
|
36
|
+
|
|
37
37
|
## Use
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
| --- | --- | --- |
|
|
41
|
-
| `/auto-compact` | command | Set the compaction threshold. |
|
|
39
|
+
Use `/auto-compact` to set the compaction threshold.
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
- It refuses to activate unless effective `compaction.enabled` is `false`.
|
|
42
|
+
- It checks `turn_start`, tool-call `turn_end`, `agent_end`, `context`, and resumed or forked `session_start`.
|
|
43
|
+
- Its consumer-owned `pi-auto-compact/autoCompact` task defaults to `fast`.
|
|
44
|
+
- It tries the effective profile primary, then fallback. If neither route works, the current session model still compacts.
|
|
45
|
+
- After mid-task compaction, a follow-up message continues the current task.
|
|
44
46
|
|
|
45
47
|
## Config
|
|
46
48
|
|
|
47
|
-
Package-owned: `~/.pi/agent/config/pi-auto-compact.json`
|
|
49
|
+
Package-owned: `~/.pi/agent/config/pi-auto-compact/config.json`
|
|
48
50
|
|
|
49
51
|
```json
|
|
50
52
|
{
|
|
@@ -52,10 +54,10 @@ Package-owned: `~/.pi/agent/config/pi-auto-compact.json`
|
|
|
52
54
|
}
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
`autoCompactThreshold` is optional. It must be a number that is at least 25 and below 100. The default is `50`.
|
|
58
|
+
|
|
59
|
+
Unknown fields are ignored. Legacy model fields are obsolete. `/auto-compact` writes this file.
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
A missing file uses 50%. Reads do not create it. A malformed or invalid file fails visibly at session start, falls back to 50%, and stays unchanged.
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
Only `/auto-compact` writes this file. Its write is atomic.
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join } from "node:path";
|
|
3
1
|
import {
|
|
4
2
|
compact,
|
|
5
3
|
estimateTokens,
|
|
6
4
|
getAgentDir,
|
|
7
5
|
SettingsManager,
|
|
8
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { createConfigStore } from "@henryqw/pi-config-store";
|
|
9
8
|
import type {
|
|
10
9
|
ExtensionAPI,
|
|
11
10
|
ExtensionContext,
|
|
@@ -38,22 +37,13 @@ export const AUTO_COMPACT_TASK = {
|
|
|
38
37
|
purpose: "Compact session context before it is exhausted.",
|
|
39
38
|
defaultProfile: "fast",
|
|
40
39
|
} as const satisfies ModelTask;
|
|
41
|
-
|
|
40
|
+
type AutoCompactConfig = { autoCompactThreshold: number };
|
|
42
41
|
|
|
43
42
|
function isValidThreshold(value: unknown): value is number {
|
|
44
43
|
return typeof value === "number" && Number.isFinite(value) && value >= MIN_COMPACT_THRESHOLD_PERCENT && value < 100;
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
function
|
|
48
|
-
let value: unknown;
|
|
49
|
-
try {
|
|
50
|
-
value = JSON.parse(readFileSync(configPath(), "utf8"));
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
53
|
-
return DEFAULT_COMPACT_THRESHOLD_PERCENT;
|
|
54
|
-
}
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
46
|
+
function parseAutoCompactConfig(value: unknown): AutoCompactConfig {
|
|
57
47
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
58
48
|
throw new Error("Config must be an object.");
|
|
59
49
|
}
|
|
@@ -61,13 +51,15 @@ function readConfig(): number {
|
|
|
61
51
|
if (!isValidThreshold(threshold)) {
|
|
62
52
|
throw new Error(`autoCompactThreshold must be at least ${MIN_COMPACT_THRESHOLD_PERCENT} and below 100.`);
|
|
63
53
|
}
|
|
64
|
-
return threshold;
|
|
54
|
+
return { autoCompactThreshold: threshold };
|
|
65
55
|
}
|
|
66
56
|
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
function createAutoCompactConfigStore() {
|
|
58
|
+
return createConfigStore<AutoCompactConfig>({
|
|
59
|
+
extensionId: "pi-auto-compact",
|
|
60
|
+
defaults: () => ({ autoCompactThreshold: DEFAULT_COMPACT_THRESHOLD_PERCENT }),
|
|
61
|
+
parse: parseAutoCompactConfig,
|
|
62
|
+
});
|
|
71
63
|
}
|
|
72
64
|
|
|
73
65
|
function configuredTaskRoutes(ctx: ExtensionContext): ResolvedTaskRoute[] {
|
|
@@ -75,6 +67,7 @@ function configuredTaskRoutes(ctx: ExtensionContext): ResolvedTaskRoute[] {
|
|
|
75
67
|
return resolveConfiguredTaskRoutes(ctx, AUTO_COMPACT_TASK);
|
|
76
68
|
} catch (error) {
|
|
77
69
|
const { taskRouteCode, profileName } = error as TaskRouteError;
|
|
70
|
+
if (taskRouteCode === "config-missing") return [];
|
|
78
71
|
const cause = taskRouteCode === "profile-missing"
|
|
79
72
|
? `Task model profile ${profileName} is not configured`
|
|
80
73
|
: taskRouteCode === "no-route"
|
|
@@ -155,6 +148,7 @@ function hasToolCall(message: AgentMessage): boolean {
|
|
|
155
148
|
|
|
156
149
|
export default function (pi: ExtensionAPI) {
|
|
157
150
|
registerModelTask(pi, AUTO_COMPACT_TASK);
|
|
151
|
+
const configStore = createAutoCompactConfigStore();
|
|
158
152
|
let active = false;
|
|
159
153
|
let autoCompactThreshold = DEFAULT_COMPACT_THRESHOLD_PERCENT;
|
|
160
154
|
// Prevent lifecycle hooks from starting duplicate summaries.
|
|
@@ -256,7 +250,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
256
250
|
|
|
257
251
|
let currentThreshold: number;
|
|
258
252
|
try {
|
|
259
|
-
currentThreshold =
|
|
253
|
+
currentThreshold = configStore.loadSync().value.autoCompactThreshold;
|
|
260
254
|
} catch {
|
|
261
255
|
ctx.ui.notify("Couldn't read pi-auto-compact config.", "error");
|
|
262
256
|
return;
|
|
@@ -275,7 +269,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
275
269
|
}
|
|
276
270
|
|
|
277
271
|
try {
|
|
278
|
-
|
|
272
|
+
await configStore.save({ autoCompactThreshold: threshold });
|
|
279
273
|
} catch {
|
|
280
274
|
ctx.ui.notify("Couldn't save pi-auto-compact config.", "error");
|
|
281
275
|
return;
|
|
@@ -289,7 +283,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
289
283
|
// activation unless effective global/project settings disable it.
|
|
290
284
|
pi.on("session_start", (event, ctx) => {
|
|
291
285
|
try {
|
|
292
|
-
|
|
286
|
+
const config = configStore.loadSync();
|
|
287
|
+
autoCompactThreshold = config.value.autoCompactThreshold;
|
|
293
288
|
} catch {
|
|
294
289
|
autoCompactThreshold = DEFAULT_COMPACT_THRESHOLD_PERCENT;
|
|
295
290
|
ctx.ui.notify("Couldn't read pi-auto-compact config; using 50%.", "error");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@henryqw/pi-auto-compact",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Proactively compact Pi context at a configurable threshold and resume the current task.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
},
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"files": [
|
|
17
|
+
"LICENSE",
|
|
17
18
|
"extensions",
|
|
18
|
-
"README.md"
|
|
19
|
-
"LICENSE"
|
|
19
|
+
"README.md"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"test": "node --test test/*.test.ts",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
]
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@henryqw/pi-
|
|
47
|
+
"@henryqw/pi-config-store": "^0.1.0",
|
|
48
|
+
"@henryqw/pi-task-models": "^4.0.0"
|
|
48
49
|
}
|
|
49
50
|
}
|