@monotykamary/pi-vcc 0.6.3 → 0.7.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 CHANGED
@@ -211,7 +211,7 @@ pi-vcc is one of four compaction approaches in the AI coding-agent ecosystem. He
211
211
  - **Error resolution detection** — tsc errors in `[Outstanding Context]` are tagged `[RESOLVED]` when the file they reference was subsequently edited, letting the model skip stale errors.
212
212
  - **Task-boundary-aware cut** — compaction splits at complete conversational turns, not mid-tool-call. If the assistant's response is in-flight (unmatched tool calls), the cut pushes back to keep the whole turn in the tail.
213
213
  - **Structured anchors** — `[Anchors]` section lists commit hashes, error IDs, and key file paths for zero-tool-call recall. The model can find references at a glance instead of calling `vcc_recall`.
214
- - **Per-model and global compaction thresholds** — configure different `reserveTokens` or `compactPercent` per model and globally, so models with different context windows compact at the right time. Proactive triggering on `agent_end` and `model_select` events compacts earlier for small-context models. Applies to both pi-vcc and pi-core compaction.
214
+ - **Per-model and global compaction thresholds** — configure different `reserveTokens`, `compactAtTokens`, or `compactPercent` per model and globally, so models with different context windows compact at the right time. Proactive triggering on `agent_end` and `model_select` events compacts earlier for small-context models. Applies to both pi-vcc and pi-core compaction.
215
215
 
216
216
  ## Install
217
217
 
@@ -567,24 +567,25 @@ Config lives at `~/.pi/agent/pi-vcc-config.json` (auto-scaffolded on first load
567
567
  "modelThresholds": {
568
568
  "neuralwatt/zai-org/GLM-5.1-FP8": { "reserveTokens": 32768 },
569
569
  "neuralwatt/moonshotai/Kimi-K2.6": { "compactPercent": 65 },
570
- "neuralwatt/neuralwatt/glm-5.1-long": { "compactPercent": 80 }
570
+ "neuralwatt/glm-5.1-long": { "compactAtTokens": 150000 }
571
571
  },
572
- "globalThreshold": { "compactPercent": 70 }
572
+ "globalThreshold": { "compactAtTokens": 150000 }
573
573
  }
574
574
  ```
575
575
 
576
576
  - **`overrideDefaultCompaction`** *(default `true`)*: when `true` (default), pi-vcc handles all compaction paths (`/compact`, auto-threshold, `/pi-vcc`). Set `false` to let pi core handle `/compact` and auto-threshold compactions via its default LLM-based compaction.
577
577
  - **`debug`** *(default `false`)*: when `true`, each compaction writes detailed info to `/tmp/pi-vcc-debug.json` — message counts, cut boundary, summary preview, sections.
578
578
  - **`modelThresholds`** *(default: none)*: per-model compaction thresholds. Keys match against `"provider/modelId"` (e.g., `"neuralwatt/zai-org/GLM-5.1-FP8"`) or just `"modelId"` (e.g., `"GLM-5.1"` — matched only when `provider/modelId` doesn't). Each value has:
579
- - **`reserveTokens`**: tokens to reserve for the LLM response. Overrides pi-core's global `compaction.reserveTokens` for matching models. Controls *when* compaction triggers: `contextTokens > contextWindow − reserveTokens`. A higher value compacts earlier (more conservative); a lower value lets context grow larger. Takes precedence over `compactPercent` when both are set.
580
- - **`compactPercent`**: compaction trigger as a percentage of context window (1–99). Compaction fires when `contextTokens > contextWindow × compactPercent / 100`. E.g. `65` means "compact when context is 65% full". Ignored when `reserveTokens` is also set.
579
+ - **`reserveTokens`**: tokens to reserve for the LLM response. Overrides pi-core's global `compaction.reserveTokens` for matching models. Controls *when* compaction triggers: `contextTokens > contextWindow − reserveTokens`. A higher value compacts earlier (more conservative); a lower value lets context grow larger. Takes precedence over `compactAtTokens` and `compactPercent` when multiple are set.
580
+ - **`compactAtTokens`**: absolute context token count where compaction triggers: `contextTokens > compactAtTokens`. Useful when you want the same trigger point across models with different context windows, such as `{ "compactAtTokens": 150000 }`. Takes precedence over `compactPercent` when both are set.
581
+ - **`compactPercent`**: compaction trigger as a percentage of context window (1–99). Compaction fires when `contextTokens > contextWindow × compactPercent / 100`. E.g. `65` means "compact when context is 65% full". Ignored when `reserveTokens` or `compactAtTokens` is also set.
581
582
  - **`keepRecentTokens`** *(optional)*: advisory token budget for pi-core's default compaction. Pi-vcc's own `buildOwnCut` uses task-boundary heuristics, so this only affects pi-core's cut when `overrideDefaultCompaction` is `false`.
582
- - **`globalThreshold`** *(default: none)*: global threshold applied to all models not matched by `modelThresholds`. Uses `compactPercent` or `reserveTokens` (`compactPercent` is easier — e.g. `65` means "compact at 65% full"). If omitted, pi-core's global `compaction.reserveTokens` applies (no override).
583
+ - **`globalThreshold`** *(default: none)*: global threshold applied to all models not matched by `modelThresholds`. Uses `reserveTokens`, `compactAtTokens`, or `compactPercent`. If omitted, pi-core's global `compaction.reserveTokens` applies (no override).
583
584
  - **`defaultThreshold`** *(default: none, deprecated)*: use `globalThreshold` instead. Backward compatible — still works.
584
585
 
585
586
  ### How compaction thresholds work
586
587
 
587
- Pi-core's auto-compaction triggers when `contextTokens > contextWindow − reserveTokens`. The global `reserveTokens` (default 16384) is one-size-fits-all — but different models have very different context windows and cost profiles.
588
+ Pi-core's auto-compaction triggers when `contextTokens > contextWindow − reserveTokens`. The global `reserveTokens` (default 16384) is one-size-fits-all — but different models have very different context windows and cost profiles. Pi-vcc also supports `compactAtTokens` when you want an absolute trigger point independent of a model's context window.
588
589
 
589
590
  Pi-vcc's thresholds provide proactive compaction at both the per-model and global level:
590
591
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@monotykamary/pi-vcc",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@monotykamary/pi-vcc",
9
- "version": "0.6.3",
9
+ "version": "0.7.0",
10
10
  "devDependencies": {
11
11
  "knip": "6.14.1",
12
12
  "typescript": "6.0.3"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monotykamary/pi-vcc",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "description": "Algorithmic conversation compactor for pi - transcript-preserving structured summaries, no LLM calls",
5
5
  "main": "index.ts",
6
6
  "keywords": [
@@ -1,9 +1,9 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
- import { homedir } from "os";
3
2
  import { dirname, join } from "path";
3
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
4
4
 
5
- const SETTINGS_PATH_DEFAULT = join(homedir(), ".pi", "agent", "pi-vcc-config.json");
6
- const settingsPath = (): string => process.env.PI_VCC_CONFIG_PATH ?? SETTINGS_PATH_DEFAULT;
5
+ const settingsPath = (): string =>
6
+ process.env.PI_VCC_CONFIG_PATH ?? join(getAgentDir(), "pi-vcc-config.json");
7
7
  /** Backwards-compat export. Resolves at access time, not import time. */
8
8
  const SETTINGS_PATH = settingsPath();
9
9
 
@@ -19,9 +19,20 @@ export interface ModelThreshold {
19
19
  * A higher value compacts earlier (more conservative); a lower value
20
20
  * lets context grow larger before compacting.
21
21
  *
22
- * Takes precedence over compactPercent when both are set.
22
+ * Takes precedence over compactAtTokens and compactPercent when multiple are set.
23
23
  */
24
24
  reserveTokens?: number;
25
+ /**
26
+ * Absolute context token count where compaction triggers.
27
+ *
28
+ * This controls *when* compaction triggers:
29
+ * contextTokens > compactAtTokens
30
+ *
31
+ * Useful when you want the same trigger point across models with
32
+ * different context windows. Ignored when reserveTokens is also set;
33
+ * takes precedence over compactPercent.
34
+ */
35
+ compactAtTokens?: number;
25
36
  /**
26
37
  * Compaction trigger as a percentage of context window (1–99).
27
38
  * Compaction fires when: contextTokens > contextWindow × compactPercent / 100
@@ -29,7 +40,7 @@ export interface ModelThreshold {
29
40
  * E.g. compactPercent: 65 means "compact when context is 65% full",
30
41
  * equivalent to reserveTokens = 35% of contextWindow.
31
42
  *
32
- * Ignored when reserveTokens is also set.
43
+ * Ignored when reserveTokens or compactAtTokens is also set.
33
44
  */
34
45
  compactPercent?: number;
35
46
  /**
@@ -61,17 +72,16 @@ export interface PiVccSettings {
61
72
  * "provider/modelId" (e.g., "neuralwatt/zai-org/GLM-5.1-FP8") or
62
73
  * just "modelId" (e.g., "GLM-5.1-FP8").
63
74
  *
64
- * When a model matches, its reserveTokens/compactPercent overrides
65
- * pi-core's global compaction.reserveTokens for the *when to compact*
66
- * decision. This lets different models compact at different context
67
- * fill levels.
75
+ * When a model matches, its reserveTokens/compactAtTokens/compactPercent
76
+ * overrides pi-core's global compaction.reserveTokens for the *when to
77
+ * compact* decision. This lets different models compact at different
78
+ * context fill levels or absolute token counts.
68
79
  */
69
80
  modelThresholds?: Record<string, ModelThreshold>;
70
81
  /**
71
82
  * Global threshold applied to all models not matched by modelThresholds.
72
- * Uses compactPercent or reserveTokens (compactPercent is easier — e.g.
73
- * 65 means "compact at 65% full"). If omitted, pi-core's global
74
- * compaction settings apply (no override).
83
+ * Uses reserveTokens, compactAtTokens, or compactPercent. If omitted,
84
+ * pi-core's global compaction settings apply (no override).
75
85
  */
76
86
  globalThreshold?: ModelThreshold;
77
87
  /**
@@ -91,8 +101,8 @@ const DEFAULT_SETTINGS: PiVccSettings = {
91
101
  * Lookup order:
92
102
  * 1. Exact match on "provider/modelId" key
93
103
  * 2. Exact match on "modelId" key
94
- * 4. globalThreshold from settings
95
- * 5. undefined (no override — pi-core's global settings apply)
104
+ * 3. globalThreshold from settings
105
+ * 4. undefined (no override — pi-core's global settings apply)
96
106
  */
97
107
  export function getModelThreshold(
98
108
  settings: PiVccSettings,
@@ -136,6 +146,33 @@ export function resolveReserveTokens(
136
146
  return undefined;
137
147
  }
138
148
 
149
+ /**
150
+ * Resolve the context token count where compaction should trigger.
151
+ *
152
+ * Precedence: reserveTokens > compactAtTokens > compactPercent.
153
+ * Returns undefined when the threshold cannot produce a usable trigger.
154
+ */
155
+ export function resolveTriggerTokens(
156
+ threshold: ModelThreshold,
157
+ contextWindow: number,
158
+ ): number | undefined {
159
+ if (contextWindow <= 0) return undefined;
160
+
161
+ if (threshold.reserveTokens != null) {
162
+ return contextWindow - threshold.reserveTokens;
163
+ }
164
+
165
+ if (threshold.compactAtTokens != null) {
166
+ const tokens = threshold.compactAtTokens;
167
+ if (!Number.isFinite(tokens) || tokens < 1) return undefined;
168
+ return Math.round(tokens);
169
+ }
170
+
171
+ const reserve = resolveReserveTokens(threshold, contextWindow);
172
+ if (reserve == null) return undefined;
173
+ return contextWindow - reserve;
174
+ }
175
+
139
176
  const readJson = (path: string): Record<string, unknown> | null => {
140
177
  try {
141
178
  return JSON.parse(readFileSync(path, "utf-8"));
@@ -156,7 +193,7 @@ export function loadSettings(): PiVccSettings {
156
193
  }
157
194
 
158
195
  /**
159
- * Ensure ~/.pi/agent/pi-vcc-config.json exists with default keys.
196
+ * Ensure the pi-vcc config file (default ~/.pi/agent/pi-vcc-config.json) exists with default keys.
160
197
  * - File missing → create with full default block.
161
198
  * - File exists but invalid JSON → no-op (don't clobber user file).
162
199
  * - File exists and valid → fill in missing default keys, preserve existing values.
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import { loadSettings, getModelThreshold, resolveReserveTokens } from "../core/settings";
2
+ import { loadSettings, getModelThreshold, resolveTriggerTokens } from "../core/settings";
3
3
 
4
4
  const formatTokens = (n: number): string => {
5
5
  if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
@@ -32,7 +32,7 @@ export const resetProactiveState = () => {
32
32
  };
33
33
 
34
34
  /**
35
- * Check if per-model threshold has been crossed and trigger compaction
35
+ * Check if a configured threshold has been crossed and trigger compaction
36
36
  * if so. Safe to call from multiple event handlers — cooldown prevents
37
37
  * double-triggering.
38
38
  */
@@ -44,14 +44,13 @@ const checkAndTrigger = (ctx: { model?: any; getContextUsage?: () => any; compac
44
44
  if (!threshold) return;
45
45
 
46
46
  const contextWindow = ctx.model?.contextWindow ?? 0;
47
- const reserve = resolveReserveTokens(threshold, contextWindow);
48
- if (reserve == null || contextWindow <= 0) return;
47
+ const effectiveThreshold = resolveTriggerTokens(threshold, contextWindow);
48
+ if (effectiveThreshold == null) return;
49
49
 
50
50
  const usage = ctx.getContextUsage?.();
51
51
  if (!usage || usage.tokens === null) return;
52
52
 
53
- // This threshold's compaction trigger point
54
- const effectiveThreshold = contextWindow - reserve;
53
+ // This threshold's compaction trigger point.
55
54
 
56
55
  // Only trigger if context EXCEEDS the threshold.
57
56
  if (usage.tokens <= effectiveThreshold) return;
@@ -80,19 +79,18 @@ const checkAndTrigger = (ctx: { model?: any; getContextUsage?: () => any; compac
80
79
  };
81
80
 
82
81
  /**
83
- * Registers proactive per-model compaction thresholds.
82
+ * Registers proactive configured compaction thresholds.
84
83
  *
85
84
  * Three triggers:
86
85
  *
87
86
  * 1. `agent_end` — after each agent run completes, check if context
88
- * exceeds the current model's per-model threshold. If the per-model
89
- * threshold is *lower* than pi-core's global threshold (meaning the
90
- * model wants to compact *earlier*), pi-core won't trigger compaction
91
- * at this point. We step in and trigger it proactively.
87
+ * exceeds the active configured threshold. If that threshold is lower
88
+ * than pi-core's global threshold (meaning this config wants to compact
89
+ * earlier), pi-core won't trigger compaction — so we do.
92
90
  *
93
- * 2. `model_select` — when switching to a model with a lower effective
94
- * threshold, the current context may already exceed the new model's
95
- * capacity. Trigger compaction immediately.
91
+ * 2. `model_select` — when switching models, the new model may have a
92
+ * different threshold. Check immediately in case current context exceeds
93
+ * the new threshold.
96
94
  *
97
95
  * 3. `session_compact` — cooldown tracking + clear proactiveTriggerActive.
98
96
  * After any compaction completes, we set a cooldown to prevent