Skip to content

Commit 10987a3

Browse files
committed
feat(compressor): redesign compressor with depth-aware ratios and grace period
The old compressor selected candidates greedily and merged them at a fixed 2:1 ratio until the history block fit the budget. On large sessions this could swallow 80%+ of all compartments in one pass (selecting ~292 out of 352 compartments into one LLM call), causing ordinal drift in the output and pathological over-compression of recent work that did not need it yet. Selection rewrite: - Bound each pass to at most `max_compartments_per_pass` (default 15) so the LLM never handles more context than it can track reliably. - Always exclude the newest `grace_compartments` compartments (default 10) so freshly published historian output has time to be used before compression touches it. Count-based instead of time-based so long autonomous runs that publish dozens of compartments overnight stay protected. - Pick the oldest contiguous band of same-rounded-depth compartments, respecting the count floor and merge-depth cap. Mixed-depth bands are skipped so compression progresses from depth 0 upward uniformly. - Replace the fixed 2:1 ratio with per-depth ratios: 4:3 at depth 1, 3:2 at depth 2, 2:1 at depths 3 and 4, title-only collapse at depth 5. Depth 1 keeps ~75% of tokens instead of halving them, protecting narrative content. Ordinal-snap fix: - `compartment-runner-compressor.ts` previously required exact messageId matches when mapping LLM output back to DB rows. Any off-by-one drift failed the whole pass. Now off-by-one LLM output snaps to the enclosing input compartment's canonical boundary. The snap count is logged for observability. New config surfaces under top-level `compressor`: `enabled`, `min_compartment_ratio`, `max_merge_depth`, `cooldown_ms`, `max_compartments_per_pass`, `grace_compartments`. Schema regenerated, documented in CONFIGURATION.md, and threaded through hook, transform, and runner dependencies.
1 parent beb191e commit 10987a3

16 files changed

Lines changed: 1462 additions & 272 deletions

CONFIGURATION.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ Higher-tier models with longer cache windows benefit from a longer TTL. Setting
7474
| `history_budget_percentage` | `number` (0.05–0.5) | `0.15` | Fraction of usable context (`context_limit × execute_threshold`) reserved for the history block. Triggers compression when exceeded. |
7575
| `compaction_markers` | `boolean` | `true` | Inject compaction boundaries into OpenCode's DB after historian publishes. Reduces transform input size for long sessions. |
7676
| `commit_cluster_trigger` | `object` | See below | Controls the commit-cluster historian trigger. |
77+
| `compressor` | `object` | See below | Controls the background compressor that merges older compartments when the history block exceeds its budget. |
7778

7879
### `commit_cluster_trigger`
7980

@@ -88,6 +89,56 @@ A **commit cluster** is a distinct work phase where the agent made one or more g
8889
}
8990
```
9091

92+
### `compressor`
93+
94+
Compressor is a background pass that runs when the rendered `<session-history>` block exceeds its budget. It merges older compartments using progressively aggressive **caveman-style** compression at each depth level, enforcing style consistency via a deterministic post-process after the historian LLM call. Each compartment range can be compressed at most `max_merge_depth` times.
95+
96+
**Depth tiers** (applied progressively as compartments are re-compressed):
97+
98+
| Depth | Style | What happens |
99+
|---|---|---|
100+
| 1 | **Merge only** | Preserve narrative and all U: lines. Drop only duplicates spanning compartments. |
101+
| 2 | **Lite caveman** | Drop filler words (just, really, basically) and hedging. Keep grammar. |
102+
| 3 | **Full caveman** | Drop articles (the, a, an), weak auxiliaries. Fragments OK. Single paragraph per compartment. |
103+
| 4 | **Ultra caveman** | Telegraphic. Symbol connectives (``, `+`, `//`, `\|`). Pattern: `[thing] [action] [reason]`. |
104+
| 5 | **Title-only collapse** | Content cleared (no LLM call). Raw messages recoverable via `ctx_expand`. |
105+
106+
Inspired by the [caveman Claude Code skill](https://github.com/JuliusBrussee/caveman) which validated telegraph-style compression as LLM-friendly (and saves tokens without tokenizer fallback issues that character-dropping causes).
107+
108+
```jsonc
109+
{
110+
"compressor": {
111+
"enabled": true, // default: true
112+
"min_compartment_ratio": 1000, // default: 1000 (floor = ceil(total_raw_messages / ratio))
113+
"max_merge_depth": 5, // default: 5 (1-5, deeper = more aggressive)
114+
"cooldown_ms": 600000, // default: 600000 (10 min between background runs)
115+
"max_compartments_per_pass": 15, // default: 15 (LLM batch cap)
116+
"grace_compartments": 10 // default: 10 (newest N compartments never compressed)
117+
}
118+
}
119+
```
120+
121+
**Merge ratios per depth** (applied per LLM pass — small ratios preserve more narrative):
122+
123+
| Depth transition | Ratio | Shape |
124+
|---|---|---|
125+
| 0 → 1 | 1.33× (4:3) | Narrative merge; preserve all `U:` lines |
126+
| 1 → 2 | 1.5× (3:2) | Drop filler, keep grammar (caveman-lite) |
127+
| 2 → 3 | 2× (2:1) | Paragraph, fragments OK (caveman-full) |
128+
| 3 → 4 | 2× (2:1) | Telegraph + symbol connectives (caveman-ultra) |
129+
| 4 → 5 || Title-only collapse (no LLM, recoverable via `ctx_expand`) |
130+
131+
**Selection strategy:** The compressor picks the oldest contiguous run of compartments that share the SAME rounded compression depth (up to `max_compartments_per_pass`). This progresses naturally: depth-0 bands get compressed first → depth-1 bands compressed next → and so on. Each run goes through one LLM call.
132+
133+
**Floor protection:** The compressor never reduces your session's compartment count below `ceil(total_raw_messages / min_compartment_ratio)`. For a 20K-message session with the default ratio, that's a floor of 20 compartments.
134+
135+
**Grace period:** The newest `grace_compartments` compartments are always excluded from compression. This protects freshly-published historian output from being re-compressed before it has been used. Default is 10, which works well even for long autonomous runs that publish many compartments per hour.
136+
137+
**Ordinal snap:** When the LLM drifts by ±1-2 ordinals on merged boundaries (e.g. outputs `start=8161` when the actual input boundary is `8160`), the runtime snaps those values to the enclosing input compartment's canonical boundary rather than rejecting the whole pass. Snaps are logged for observability.
138+
139+
**Disable entirely:** Set `compressor.enabled: false` to skip all background compression. Older sessions will simply carry a larger history footprint.
140+
141+
91142
| Field | Type | Default | Description |
92143
|-------|------|---------|-------------|
93144
| `enabled` | `boolean` | `true` | Enable commit-cluster based historian triggering. |
@@ -401,6 +452,12 @@ When enabled, dreamer analyzes which files each session's agent reads most frequ
401452
"drop_tool_structure": true,
402453
"history_budget_percentage": 0.15,
403454
"compaction_markers": true,
455+
"compressor": {
456+
"enabled": true,
457+
"min_compartment_ratio": 1000,
458+
"max_merge_depth": 5,
459+
"cooldown_ms": 600000
460+
},
404461

405462
"historian": {
406463
"model": "github-copilot/gpt-5.4",

assets/magic-context.schema.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,60 @@
166166
"default": true,
167167
"description": "Inject compaction boundaries into OpenCode's DB after historian publishes. Reduces transform input size for long sessions by letting OpenCode's filterCompacted skip older messages."
168168
},
169+
"compressor": {
170+
"type": "object",
171+
"properties": {
172+
"enabled": {
173+
"type": "boolean",
174+
"default": true,
175+
"description": "Enable background compressor. When false, history block compression never runs and older sessions may carry a larger history footprint."
176+
},
177+
"min_compartment_ratio": {
178+
"type": "number",
179+
"minimum": 100,
180+
"maximum": 10000,
181+
"default": 1000,
182+
"description": "Floor = ceil(total_raw_messages / min_compartment_ratio). Compressor never reduces compartment count below this floor."
183+
},
184+
"max_merge_depth": {
185+
"type": "number",
186+
"minimum": 1,
187+
"maximum": 5,
188+
"default": 5,
189+
"description": "Maximum compression depth a compartment range can reach. Depth 5 collapses to title-only (recoverable via ctx_expand). Depths 1-4 apply caveman lite/full/ultra compression."
190+
},
191+
"cooldown_ms": {
192+
"type": "number",
193+
"minimum": 60000,
194+
"default": 600000,
195+
"description": "Minimum milliseconds between background compressor runs for a session."
196+
},
197+
"max_compartments_per_pass": {
198+
"type": "number",
199+
"minimum": 3,
200+
"maximum": 50,
201+
"default": 15,
202+
"description": "Cap on compartments sent to the LLM in one pass. Smaller batches avoid ordinal drift and dedup mistakes on large inputs."
203+
},
204+
"grace_compartments": {
205+
"type": "number",
206+
"minimum": 0,
207+
"maximum": 100,
208+
"default": 10,
209+
"description": "Number of newest compartments always excluded from compression. Protects freshly published historian output from being re-compressed before it has been used."
210+
}
211+
},
212+
"additionalProperties": false,
213+
"default": {
214+
"enabled": true,
215+
"min_compartment_ratio": 1000,
216+
"max_merge_depth": 5,
217+
"cooldown_ms": 600000,
218+
"max_compartments_per_pass": 15,
219+
"grace_compartments": 10
220+
},
221+
"description": "Background compressor configuration — merges older compartments with caveman-style compression when the history block exceeds its budget."
222+
},
169223
"experimental": {
170224
"type": "object",
171225
"properties": {

packages/plugin/scripts/build-schema.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,68 @@ function buildSchema(): Record<string, unknown> {
221221
"Inject compaction boundaries into OpenCode's DB after historian publishes. Reduces transform input size for long sessions by letting OpenCode's filterCompacted skip older messages.",
222222
},
223223

224+
compressor: {
225+
type: "object",
226+
properties: {
227+
enabled: {
228+
type: "boolean",
229+
default: true,
230+
description:
231+
"Enable background compressor. When false, history block compression never runs and older sessions may carry a larger history footprint.",
232+
},
233+
min_compartment_ratio: {
234+
type: "number",
235+
minimum: 100,
236+
maximum: 10000,
237+
default: 1000,
238+
description:
239+
"Floor = ceil(total_raw_messages / min_compartment_ratio). Compressor never reduces compartment count below this floor.",
240+
},
241+
max_merge_depth: {
242+
type: "number",
243+
minimum: 1,
244+
maximum: 5,
245+
default: 5,
246+
description:
247+
"Maximum compression depth a compartment range can reach. Depth 5 collapses to title-only (recoverable via ctx_expand). Depths 1-4 apply caveman lite/full/ultra compression.",
248+
},
249+
cooldown_ms: {
250+
type: "number",
251+
minimum: 60000,
252+
default: 600000,
253+
description:
254+
"Minimum milliseconds between background compressor runs for a session.",
255+
},
256+
max_compartments_per_pass: {
257+
type: "number",
258+
minimum: 3,
259+
maximum: 50,
260+
default: 15,
261+
description:
262+
"Cap on compartments sent to the LLM in one pass. Smaller batches avoid ordinal drift and dedup mistakes on large inputs.",
263+
},
264+
grace_compartments: {
265+
type: "number",
266+
minimum: 0,
267+
maximum: 100,
268+
default: 10,
269+
description:
270+
"Number of newest compartments always excluded from compression. Protects freshly published historian output from being re-compressed before it has been used.",
271+
},
272+
},
273+
additionalProperties: false,
274+
default: {
275+
enabled: true,
276+
min_compartment_ratio: 1000,
277+
max_merge_depth: 5,
278+
cooldown_ms: 600000,
279+
max_compartments_per_pass: 15,
280+
grace_compartments: 10,
281+
},
282+
description:
283+
"Background compressor configuration — merges older compartments with caveman-style compression when the history block exceeds its budget.",
284+
},
285+
224286
experimental: {
225287
type: "object",
226288
properties: {

packages/plugin/src/config/schema/magic-context.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ describe("MagicContextConfigSchema", () => {
6262
min_clusters: 3,
6363
},
6464
compaction_markers: true,
65+
compressor: {
66+
enabled: true,
67+
min_compartment_ratio: 1000,
68+
max_merge_depth: 5,
69+
cooldown_ms: 600_000,
70+
max_compartments_per_pass: 15,
71+
grace_compartments: 10,
72+
},
6573
experimental: {
6674
user_memories: {
6775
enabled: false,

packages/plugin/src/config/schema/magic-context.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,26 @@ export const DEFAULT_EXECUTE_THRESHOLD_PERCENTAGE = 65;
88
export const DEFAULT_HISTORIAN_TIMEOUT_MS = 300_000;
99
export const DEFAULT_HISTORY_BUDGET_PERCENTAGE = 0.15;
1010
export const DEFAULT_LOCAL_EMBEDDING_MODEL = "Xenova/all-MiniLM-L6-v2";
11+
/** Compressor defaults — see CompressorConfigSchema below for details. */
12+
export const DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO = 1000;
13+
export const DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH = 5;
14+
export const DEFAULT_COMPRESSOR_COOLDOWN_MS = 600_000;
15+
/** Max compartments merged in one LLM pass. LLM quality degrades with larger inputs,
16+
* and smaller batches reduce ordinal-drift risk when the model outputs merged boundaries. */
17+
export const DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS = 15;
18+
/** Number of newest compartments always excluded from compression.
19+
* Protects freshly-published historian compartments from immediate re-compression,
20+
* which would lose narrative quality before the agent has even used the compartment. */
21+
export const DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS = 10;
22+
/** Output count at each depth = ceil(input / ratio).
23+
* Lower ratios = gentler compression. Depth 5 is title-only (no LLM call). */
24+
export const COMPRESSOR_MERGE_RATIO_BY_DEPTH: Record<number, number> = {
25+
1: 1.33, // 4:3 — merge only, preserve narrative + U: lines
26+
2: 1.5, // 3:2 — lite caveman
27+
3: 2.0, // 2:1 — full caveman
28+
4: 2.0, // 2:1 — ultra caveman (caveman post-process does heavy lifting)
29+
5: 0, // title-only collapse (no merge, no LLM)
30+
};
1131

1232
export const DREAMER_TASKS = [
1333
"consolidate",
@@ -141,6 +161,14 @@ export interface MagicContextConfig {
141161
min_clusters: number;
142162
};
143163
compaction_markers: boolean;
164+
compressor: {
165+
enabled: boolean;
166+
min_compartment_ratio: number;
167+
max_merge_depth: number;
168+
cooldown_ms: number;
169+
max_compartments_per_pass: number;
170+
grace_compartments: number;
171+
};
144172
experimental: {
145173
user_memories: {
146174
enabled: boolean;
@@ -231,6 +259,62 @@ export const MagicContextConfigSchema = z
231259
* After historian publishes compartments, a compaction boundary is written into
232260
* OpenCode's message/part tables so older messages are skipped at load time. Default: true. */
233261
compaction_markers: z.boolean().default(true),
262+
/** Compressor configuration — controls second-pass compression of older
263+
* compartments when the rendered history block exceeds its budget.
264+
* The compressor merges adjacent compartments and applies progressively
265+
* aggressive caveman-style compression at deeper depths. */
266+
compressor: z
267+
.object({
268+
/** Enable background compressor. When false, history block compression
269+
* never runs, so very old sessions may carry a larger history footprint.
270+
* (default: true) */
271+
enabled: z.boolean().default(true),
272+
/** Floor = ceil(total_raw_messages / min_compartment_ratio).
273+
* Compressor will never reduce session compartment count below this
274+
* floor. Lower ratio = more compartments preserved. Prevents runaway
275+
* merging into a single mega-compartment. (min: 100, max: 10000, default: 1000) */
276+
min_compartment_ratio: z
277+
.number()
278+
.min(100)
279+
.max(10000)
280+
.default(DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO),
281+
/** Maximum compression depth for any compartment range. At depth 5,
282+
* compartments are collapsed to title-only (content recoverable via
283+
* ctx_expand). Depths 1-4 apply caveman-lite/full/ultra compression.
284+
* (min: 1, max: 5, default: 5) */
285+
max_merge_depth: z
286+
.number()
287+
.min(1)
288+
.max(5)
289+
.default(DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH),
290+
/** Minimum milliseconds between background compressor runs for a session.
291+
* (min: 60000, default: 600000 = 10 min) */
292+
cooldown_ms: z.number().min(60_000).default(DEFAULT_COMPRESSOR_COOLDOWN_MS),
293+
/** Max compartments the compressor will send to one LLM call in a single pass.
294+
* Keeping this low avoids LLM ordinal drift and dedup mistakes on large inputs.
295+
* (min: 3, max: 50, default: 15) */
296+
max_compartments_per_pass: z
297+
.number()
298+
.min(3)
299+
.max(50)
300+
.default(DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS),
301+
/** Number of newest compartments always excluded from compression.
302+
* Protects freshly published historian output from being re-compressed before it
303+
* has been used. (min: 0, max: 100, default: 10) */
304+
grace_compartments: z
305+
.number()
306+
.min(0)
307+
.max(100)
308+
.default(DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS),
309+
})
310+
.default({
311+
enabled: true,
312+
min_compartment_ratio: DEFAULT_COMPRESSOR_MIN_COMPARTMENT_RATIO,
313+
max_merge_depth: DEFAULT_COMPRESSOR_MAX_MERGE_DEPTH,
314+
cooldown_ms: DEFAULT_COMPRESSOR_COOLDOWN_MS,
315+
max_compartments_per_pass: DEFAULT_COMPRESSOR_MAX_COMPARTMENTS_PER_PASS,
316+
grace_compartments: DEFAULT_COMPRESSOR_GRACE_COMPARTMENTS,
317+
}),
234318
/** Embedding provider configuration */
235319
embedding: EmbeddingConfigSchema.default({
236320
provider: "local",

0 commit comments

Comments
 (0)