@monotykamary/pi-vcc 0.3.16
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 +688 -0
- package/__tests__/helpers.ts +135 -0
- package/__tests__/root-causes.test.ts +858 -0
- package/demo.gif +0 -0
- package/index.ts +21 -0
- package/knip.json +4 -0
- package/npm-shrinkwrap.json +3001 -0
- package/package.json +36 -0
- package/scripts/audit-sessions.ts +88 -0
- package/scripts/benchmark-real-sessions.ts +25 -0
- package/scripts/compare-before-after.ts +36 -0
- package/scripts/dump-branch-output.ts +20 -0
- package/src/commands/pi-vcc.ts +36 -0
- package/src/commands/vcc-recall.ts +65 -0
- package/src/core/brief.ts +497 -0
- package/src/core/build-sections.ts +431 -0
- package/src/core/content.ts +71 -0
- package/src/core/filter-noise.ts +42 -0
- package/src/core/format-recall.ts +177 -0
- package/src/core/format.ts +98 -0
- package/src/core/invisible-continue.ts +172 -0
- package/src/core/lineage.ts +26 -0
- package/src/core/load-messages.ts +40 -0
- package/src/core/normalize.ts +73 -0
- package/src/core/recall-scope.ts +49 -0
- package/src/core/render-entries.ts +58 -0
- package/src/core/report.ts +237 -0
- package/src/core/sanitize.ts +5 -0
- package/src/core/search-entries.ts +375 -0
- package/src/core/settings.ts +190 -0
- package/src/core/skill-collapse.ts +35 -0
- package/src/core/summarize.ts +179 -0
- package/src/core/tool-args.ts +14 -0
- package/src/details.ts +19 -0
- package/src/extract/commits.ts +69 -0
- package/src/extract/goals.ts +79 -0
- package/src/extract/preferences.ts +55 -0
- package/src/extract/shared-symbols.ts +357 -0
- package/src/hooks/before-compact.ts +564 -0
- package/src/hooks/proactive-threshold.ts +131 -0
- package/src/sections.ts +23 -0
- package/src/tools/recall.ts +204 -0
- package/src/types.ts +20 -0
- package/tests/australian-government.test.ts +479 -0
- package/tests/before-compact-hook.test.ts +185 -0
- package/tests/before-compact.test.ts +227 -0
- package/tests/brief.test.ts +215 -0
- package/tests/build-sections.test.ts +59 -0
- package/tests/compile.test.ts +89 -0
- package/tests/content.test.ts +66 -0
- package/tests/deep-extraction.test.ts +313 -0
- package/tests/extract-goals.test.ts +86 -0
- package/tests/extract-preferences.test.ts +30 -0
- package/tests/filter-noise.test.ts +61 -0
- package/tests/fixtures.ts +61 -0
- package/tests/format-recall.test.ts +87 -0
- package/tests/format.test.ts +73 -0
- package/tests/government-domain.test.ts +542 -0
- package/tests/lineage.test.ts +33 -0
- package/tests/load-messages.test.ts +49 -0
- package/tests/model-threshold-hook.test.ts +377 -0
- package/tests/model-threshold.test.ts +223 -0
- package/tests/normalize.test.ts +104 -0
- package/tests/proactive-threshold.test.ts +490 -0
- package/tests/real-sessions.test.ts +38 -0
- package/tests/recall-expand.test.ts +15 -0
- package/tests/recall-scope.test.ts +32 -0
- package/tests/recall-tool-scope.test.ts +67 -0
- package/tests/render-entries.test.ts +92 -0
- package/tests/report.test.ts +58 -0
- package/tests/sanitize.test.ts +24 -0
- package/tests/search-entries.test.ts +233 -0
- package/tests/support/load-session.ts +23 -0
- package/tests/support/real-sessions.ts +51 -0
- package/tests/threshold-integration.test.ts +359 -0
- package/tsconfig.json +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🗜️ pi-vcc
|
|
4
|
+
|
|
5
|
+
**Algorithmic conversation compactor for [pi](https://github.com/earendil-works/pi-coding-agent)**
|
|
6
|
+
|
|
7
|
+
_No LLM calls — 35-99% token reduction via extraction and formatting. Same input = same output, always._
|
|
8
|
+
|
|
9
|
+
[](https://github.com/earendil-works/pi-coding-agent)
|
|
10
|
+
[](./LICENSE)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Inspired by [VCC](https://github.com/lllyasviel/VCC) **(View-oriented Conversation Compiler)**.
|
|
17
|
+
|
|
18
|
+
## Demo
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
## Why pi-vcc
|
|
23
|
+
|
|
24
|
+
| | Pi default | pi-vcc |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **Method** | LLM-generated summary | Algorithmic extraction, no LLM |
|
|
27
|
+
| **Determinism** | Non-deterministic, can hallucinate | Same input = same output, always |
|
|
28
|
+
| **Token reduction** | Varies | 35-99% on real sessions (higher on longer sessions) |
|
|
29
|
+
| **Compaction latency** | Waits for LLM call | 30-470ms, no API calls |
|
|
30
|
+
| **History after compaction** | Gone — agent only sees summary | Active lineage searchable via `vcc_recall` (`scope:"all"` available) |
|
|
31
|
+
| **Repeated compactions** | Each rewrite risks losing more | Sections merge and accumulate |
|
|
32
|
+
| **Cost** | Burns tokens on summarization call | Zero — no API calls |
|
|
33
|
+
| **Structure** | Free-form prose | Brief transcript + 7 semantic sections + priority tags + metadata footer |
|
|
34
|
+
| **Code awareness** | None (summarizes text only) | Symbol-annotated files, type catalog, deep error extraction |
|
|
35
|
+
|
|
36
|
+
### Real session metrics
|
|
37
|
+
|
|
38
|
+
Measured on real session JSONLs under `~/.pi/agent/sessions` (chars = rendered message text).
|
|
39
|
+
|
|
40
|
+
| Session | Messages | Before | After | Reduction | Time |
|
|
41
|
+
|---|---|---|---|---|---|
|
|
42
|
+
| Session A | 2,943 | 997,162 | 7,959 | 99.2% | 64ms |
|
|
43
|
+
| Session B | 1,703 | 428,334 | 7,762 | 98.2% | 29ms |
|
|
44
|
+
| Session C | 1,657 | 424,183 | 9,577 | 97.7% | 54ms |
|
|
45
|
+
| Session D | 1,004 | 2,258,477 | 4,439 | 99.8% | 30ms |
|
|
46
|
+
| Session E | 486 | 295,006 | 11,163 | 96.2% | 30ms |
|
|
47
|
+
| Session F | 46 | 5,234 | 3,364 | 35.7% | 5ms |
|
|
48
|
+
| Session G | 27 | 8,595 | 2,489 | 71.0% | 2ms |
|
|
49
|
+
|
|
50
|
+
## Compaction Deep Dive
|
|
51
|
+
|
|
52
|
+
pi-vcc is one of four compaction approaches in the AI coding-agent ecosystem. Here is how they compare.
|
|
53
|
+
|
|
54
|
+
### Pi Default Harness
|
|
55
|
+
|
|
56
|
+
*Based in `@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js`*
|
|
57
|
+
|
|
58
|
+
**Architecture**: LLM-based structured summarization via a summarization model.
|
|
59
|
+
|
|
60
|
+
**Flow**:
|
|
61
|
+
1. `shouldCompact()` — checks if `contextTokens > contextWindow - reserveTokens (16k)`
|
|
62
|
+
2. `prepareCompaction()` — walks branch entries, finds previous compaction boundary, calculates cut point by walking newest→oldest accumulating estimated message sizes until hitting `keepRecentTokens` (20k default)
|
|
63
|
+
3. `compact()` → `generateSummary()` — serializes conversation to plain text (not LLM messages, to prevent the model from continuing it), calls LLM with structured summarization prompt
|
|
64
|
+
4. Two prompt variants: initial `SUMMARIZATION_PROMPT` (first time) or `UPDATE_SUMMARIZATION_PROMPT` (merges into existing summary)
|
|
65
|
+
5. Output format: `## Goal / ## Constraints & Preferences / ## Progress / ## Key Decisions / ## Next Steps / ## Critical Context`
|
|
66
|
+
6. Detects mid-turn splits — when the cut falls mid-turn, generates a separate turn prefix summary in parallel and merges both
|
|
67
|
+
7. Tracks file operations (read/write/edit from tool calls) and appends `<read-files>` / `<modified-files>` XML tags to each summary
|
|
68
|
+
|
|
69
|
+
**Key characteristics**:
|
|
70
|
+
- Pure LLM — every compaction costs a model call
|
|
71
|
+
- Token-budget backwalk keeps a configurable tail (20k recent tokens)
|
|
72
|
+
- Turn-aware: `isSplitTurn` preserves incomplete assistant turns
|
|
73
|
+
- Previous-summary merging via update prompt (incremental)
|
|
74
|
+
- Non-deterministic — different runs produce different summaries
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Claude Code
|
|
79
|
+
|
|
80
|
+
*Based in `claude-code/src/services/compact/`*
|
|
81
|
+
|
|
82
|
+
**Architecture**: Three-tier compaction — proactive/manual (LLM), session memory (LLM-free), and micro-compaction (cache-editing).
|
|
83
|
+
|
|
84
|
+
**Flow (Main Compaction — `compactConversation()`)** :
|
|
85
|
+
1. `shouldAutoCompact()` → `getAutoCompactThreshold()` = context window minus reserved output minus buffer (13k)
|
|
86
|
+
2. PreCompact hooks execute (SDK extensions can inject custom instructions)
|
|
87
|
+
3. `getCompactPrompt()` builds a prompt with a `NO_TOOLS_PREAMBLE`, a detailed 9-section template, and a trailer rejecting tool calls
|
|
88
|
+
4. `streamCompactSummary()` first tries a **cache-sharing fork path** (piggybacks on the main thread's prompt-cache prefix with a forked agent), then falls back to a direct streaming path with only `FileReadTool` + `ToolSearchTool`
|
|
89
|
+
5. Strips images/documents from messages before sending to the compact API (replaces with `[image]` / `[document]` markers)
|
|
90
|
+
6. PTL (Prompt Too Long) retry: `truncateHeadForPTLRetry()` drops oldest API-round groups and retries (up to 3)
|
|
91
|
+
7. After summary generation: creates post-compact file attachments (re-reads recently accessed files), plan attachments, skill attachments, delta tool announcements
|
|
92
|
+
8. Executes SessionStart hooks and PostCompact hooks
|
|
93
|
+
9. Returns `CompactionResult { boundaryMarker, summaryMessages, attachments, hookResults, messagesToKeep }`
|
|
94
|
+
|
|
95
|
+
**Flow (Session Memory Compact — `trySessionMemoryCompaction()`)** :
|
|
96
|
+
1. Feature-gated: `tengu_session_memory` + `tengu_sm_compact` flags
|
|
97
|
+
2. Waits for in-progress session memory extraction to finish
|
|
98
|
+
3. `calculateMessagesToKeepIndex()` starts from `lastSummarizedMessageId`, expands backwards to meet `minTokens` (10k) and `minTextBlockMessages` (5), capped at `maxTokens` (40k)
|
|
99
|
+
4. `adjustIndexToPreserveAPIInvariants()` ensures tool_use/tool_result pairs are not split (handles streaming message fragmentation)
|
|
100
|
+
5. No LLM call — uses already-extracted session memory content as the summary
|
|
101
|
+
6. Truncates oversized sections via `truncateSessionMemoryForCompact()`
|
|
102
|
+
7. Falls back to legacy compact if session memory is empty or boundary can't be found
|
|
103
|
+
|
|
104
|
+
**Flow (Micro Compact — `microcompactMessages()`)** :
|
|
105
|
+
1. **Time-based trigger**: if the gap since the last main-loop assistant message exceeds the threshold (cold server cache), content-clear old tool results to shrink what gets rewritten
|
|
106
|
+
2. **Cached microcompact** (experimental, `CACHED_MICROCOMPACT` feature): tracks tool results per message, queues `cache_edits` blocks for the API layer — removes tool results from the server-side cached prompt without mutating local messages and without invalidating the cached prefix
|
|
107
|
+
3. Legacy microcompact (content-clear) fully replaced by the cache-editing approach
|
|
108
|
+
|
|
109
|
+
**Key characteristics**:
|
|
110
|
+
- Three compaction tiers: full LLM / session memory (LLM-free) / micro (cache-edit only)
|
|
111
|
+
- Cache-aware: cache-sharing fork path, cache-editing microcompact, PTL retry
|
|
112
|
+
- Heavy hook system: 3 hook sets (PreCompact → SessionStart → PostCompact)
|
|
113
|
+
- File restoration: re-attaches recently read files post-compact
|
|
114
|
+
- Circuit breaker: 3 consecutive failures stops retrying
|
|
115
|
+
- Partial compact: supports `up_to` (summarize before, keep prefix) / `from` (summarize after, keep suffix) directions
|
|
116
|
+
- Analytics: `tengu_compact` events with full token breakdowns, `analyzeContext()` walks every content block
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Codex (OpenAI)
|
|
121
|
+
|
|
122
|
+
*Based in `codex/codex-rs/core/src/compact.rs`, `compact_remote.rs`, `compact_remote_v2.rs`*
|
|
123
|
+
|
|
124
|
+
**Architecture**: Rust-based, three concurrent compaction paths — inline (local LLM), remote (server-side), and remote v2 (streaming).
|
|
125
|
+
|
|
126
|
+
**Flow**:
|
|
127
|
+
1. Decision: `should_use_remote_compact_task()` checks whether the provider supports remote compaction
|
|
128
|
+
2. Three parallel implementations:
|
|
129
|
+
|
|
130
|
+
**Inline (local) Path** (`compact.rs`):
|
|
131
|
+
1. Pre-hooks → LLM call with a compact prompt → Post-hooks
|
|
132
|
+
2. Uses `ContextCompactionItem` — a first-class protocol item embedded in conversation history (not a hack)
|
|
133
|
+
3. `COMPACT_USER_MESSAGE_MAX_TOKENS` = 20k token cap
|
|
134
|
+
4. `InitialContextInjection` controls when system context is re-injected:
|
|
135
|
+
- `DoNotInject` — for pre-turn/manual compaction (next regular turn handles reinjection)
|
|
136
|
+
- `BeforeLastUserMessage` — for mid-turn compaction (injects above the last real user message)
|
|
137
|
+
5. Summarization prompt (from `templates/compact/prompt.md`):
|
|
138
|
+
- "Context checkpoint compaction" handoff summary
|
|
139
|
+
- Key sections: progress, decisions, constraints, remaining work
|
|
140
|
+
6. Summary prefix (`templates/compact/summary_prefix.md`): `"Another language model started to solve this problem..."`
|
|
141
|
+
7. `trim_function_call_history_to_fit_context_window()` — truncates oversized call histories before compact
|
|
142
|
+
8. Event-driven: emits TurnStarted, stream events, TurnCompleted
|
|
143
|
+
9. Backoff retry via `codex_util::backoff`
|
|
144
|
+
|
|
145
|
+
**Remote Path** (`compact_remote.rs`):
|
|
146
|
+
1. Delegates compaction to the codex-backend server via the Responses API Compact endpoint
|
|
147
|
+
2. Server-side compaction uses OpenAI's own compact infrastructure
|
|
148
|
+
3. Client sends history, server returns a `CompactedItem`
|
|
149
|
+
4. `process_compacted_history()` replaces conversation items with the compacted version
|
|
150
|
+
5. Same hook system (PreCompact → PostCompact) and analytics tracking
|
|
151
|
+
6. Logs request/response data via `build_compact_request_log_data()`
|
|
152
|
+
|
|
153
|
+
**Remote v2 Path** (`compact_remote_v2.rs`):
|
|
154
|
+
1. Uses Responses API streaming compact — same endpoint as v1 but leverages the existing `ModelClientSession` for streaming
|
|
155
|
+
2. Feature-gated: `Feature::RemoteCompactionV2` (under development, disabled by default)
|
|
156
|
+
3. Reuses `process_compacted_history()` and `trim_function_call_history_to_fit_context_window()`
|
|
157
|
+
4. Rollout-trace aware: `CompactionCheckpointTracePayload` for end-to-end observability
|
|
158
|
+
|
|
159
|
+
**Key characteristics**:
|
|
160
|
+
- Three parallel compaction implementations: inline / remote / remote-v2
|
|
161
|
+
- Server-side compaction can delegate to OpenAI's backend (token savings on the client)
|
|
162
|
+
- Rust async with cancellation tokens throughout
|
|
163
|
+
- `ContextCompactionItem` is a first-class protocol type, not a synthetic message
|
|
164
|
+
- Fine-grained `InitialContextInjection` control over system context reinjection
|
|
165
|
+
- Event-driven architecture: full turn lifecycle for compaction (start → stream → complete → error)
|
|
166
|
+
- `CompactionAnalyticsAttempt` tracks every phase, status, and implementation
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
### Comparison Summary
|
|
171
|
+
|
|
172
|
+
| Aspect | Pi Default | pi-vcc | Claude Code | Codex |
|
|
173
|
+
|--------|-----------|--------|-------------|-------|
|
|
174
|
+
| **Language** | TypeScript (compiled) | TypeScript (extension) | TypeScript (source) | Rust |
|
|
175
|
+
| **LLM dependency** | Always required | None | Optional (session memory bypass) | Always (inline) / server-offloaded |
|
|
176
|
+
| **Cut strategy** | Token-budget backwalk (20k recent) | Keep last user message | Min tokens (10k) + min text messages (5) | Context window trim |
|
|
177
|
+
| **Summary format** | Markdown structured sections `## Goal` etc. | Bracket-tagged sections `[Session Goal]` + `[Anchors]` + `[Earlier Turns]` | `<analysis>` scratchpad + 9-section `<summary>` | Markdown handoff |
|
|
178
|
+
| **Merge with prev** | Update prompt (LLM merges) | Header-by-header deterministic dedup | Via session memory (LLM-free) or prompt | Replaces (no merge) |
|
|
179
|
+
| **File tracking** | `<read-files>` / `<modified-files>` XML tags | `[Files And Changes]` with symbol annotations | Post-compact file re-attachment (re-reads recent files) | Via server (server-managed) |
|
|
180
|
+
| **Turn splitting** | Yes (`isSplitTurn` with parallel prefix summary) | Task-boundary-aware (pushes back on mid-flight turns) | Via `preservedSegment` metadata | Via `InitialContextInjection` |
|
|
181
|
+
| **Cache awareness** | None | Section ordering (stable first for prompt cache) | Cache-sharing fork path, cache-editing microcompact, PTL retry | Server-side cache (remote path) |
|
|
182
|
+
| **Hook system** | 2 hooks (`session_before_compact`, `session_compact`) | 5 hooks (`session_before_compact`, `session_compact`, `agent_end`, `model_select`, `session_start`) | 3 hooks (PreCompact, SessionStart, PostCompact) | 2 hooks (PreCompact, PostCompact) |
|
|
183
|
+
| **Micro compaction** | None | None | Yes (cache-editing + time-based content clear) | None |
|
|
184
|
+
| **Partial compact** | None | None | Yes (`up_to` / `from` directions) | None |
|
|
185
|
+
| **Error handling** | Basic | Orphan recovery, resolution detection (`[RESOLVED]` tag) | PTL retry (3x), circuit breaker (3 failures) | Backoff retry |
|
|
186
|
+
| **Token estimation** | chars/4 heuristic | chars/4 heuristic | `roughTokenCountEstimation` + 4/3 padding | `approx_token_count` |
|
|
187
|
+
| **Determinism** | Non-deterministic (LLM) | Deterministic (no LLM) | Non-deterministic (LLM) / deterministic (SM) | Non-deterministic (LLM) / deterministic (server) |
|
|
188
|
+
| **Latency** | LLM call time | 2–64ms | LLM call time (or instant with SM/micro) | LLM call time (or server-offloaded) |
|
|
189
|
+
| **Cost** | Per-compact LLM tokens | Zero | Per-compact LLM tokens or zero (SM/micro) | Per-compact LLM tokens or server-side |
|
|
190
|
+
| **Debugging** | Basic | `/tmp/pi-vcc-debug.json` snapshots | `logForDebugging`, analytics events | Rollout trace, compaction analytics |
|
|
191
|
+
|
|
192
|
+
## Features
|
|
193
|
+
|
|
194
|
+
- **No LLM** — purely algorithmic, zero extra API cost
|
|
195
|
+
- **Brief transcript** — chronological conversation flow, each tool call collapsed to a one-liner with `(#N)` refs, text truncated to keep it compact
|
|
196
|
+
- **8 semantic sections** — session goal, files & changes, type catalog, commits, outstanding context, earlier turns, anchors, user preferences
|
|
197
|
+
- **Bounded merge** — rolling sections re-capped after merge instead of growing unbounded
|
|
198
|
+
- **Lossless recall** — `vcc_recall` reads raw session JSONL, so active-lineage history stays searchable across compactions
|
|
199
|
+
- **Scoped recall** — default search is active lineage; use `scope:"all"` for all lineages, or `scope:"compaction:N"` / `scope:"compaction:latest"` to search within a specific compaction segment's original messages
|
|
200
|
+
- **Priority error tags** — outstanding context items tagged `[ERROR]`, `[WARN]`, `[INFO]`, `[RESOLVED]` for urgency at a glance
|
|
201
|
+
- **Metadata footer** — each compaction summary ends with timestamp, compression ratio, and message range
|
|
202
|
+
- **Cache-friendly ordering** — stable sections (goal, preferences, files, commits, anchors) come first; volatile sections (outstanding context, earlier turns, current status) come last, maximizing prompt-cacheable prefix across compactions
|
|
203
|
+
- **Adaptive recall view** — search results grouped by conversation segments (turns) with match indicators (`>`) and context preservation, so the agent sees the conversational structure around each match
|
|
204
|
+
- **Regex search** — `vcc_recall` supports regex patterns (`hook|inject`, `fail.*build`) and OR-ranked multi-word queries
|
|
205
|
+
- **Result ranking** — search results ranked by BM25 term relevance, rare terms weighted higher than common ones
|
|
206
|
+
- **`/pi-vcc-recall`** — slash command to search history directly, results shown as collapsible message and auto-fed to agent as context
|
|
207
|
+
- **Fallback cut** — still works when Pi core returns nothing to summarize
|
|
208
|
+
- **`/pi-vcc`** — manual compaction on demand
|
|
209
|
+
- **Multi-resolution transcript** — three-zone brief: `[Earlier Turns]` (one-liner per conversational turn, heaviest compression), brief transcript (tool calls collapsed, medium compression), and the kept tail (uncompressed). Eliminates the information cliff where older turns vanish entirely.
|
|
210
|
+
- **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.
|
|
211
|
+
- **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.
|
|
212
|
+
- **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`.
|
|
213
|
+
- **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. Works in both directions: compact earlier for small-context models, compact later for large-context ones. Applies to both pi-vcc and pi-core compaction. Proactive triggering on `agent_end` and `model_select` events.
|
|
214
|
+
|
|
215
|
+
## Install
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
pi install https://github.com/monotykamary/pi-vcc@tom
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Or try without installing:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
pi -e https://github.com/monotykamary/pi-vcc@tom
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Usage
|
|
228
|
+
|
|
229
|
+
Once installed, pi-vcc registers a `session_before_compact` hook.
|
|
230
|
+
|
|
231
|
+
- Run `/pi-vcc` to trigger pi-vcc compaction manually.
|
|
232
|
+
- By default, pi-vcc handles all compaction paths (`/compact`, auto-threshold, `/pi-vcc`). Set `overrideDefaultCompaction: false` in the config to fall back to pi core's LLM-based compaction for `/compact` and auto-threshold.
|
|
233
|
+
- To search older active-lineage history after compaction, use `vcc_recall`.
|
|
234
|
+
- To intentionally search across all lineages, pass `scope:"all"` to `vcc_recall` or run `/pi-vcc-recall <query> scope:all`.
|
|
235
|
+
- To search and feed results to agent yourself, run `/pi-vcc-recall <query> [page:N]`.
|
|
236
|
+
- Tip: type `/recall` and Pi will autocomplete to `/pi-vcc-recall`.
|
|
237
|
+
|
|
238
|
+
### How compaction works
|
|
239
|
+
|
|
240
|
+
Pi splits the conversation at the **last user message**. Everything after — the **kept tail** — stays intact and untouched. pi-vcc only summarizes the older portion before that cut point.
|
|
241
|
+
|
|
242
|
+
### Compacted message structure
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
[Session Goal]
|
|
246
|
+
- Fix the authentication bug in login flow
|
|
247
|
+
- [Scope change]
|
|
248
|
+
- Also update the session token refresh logic
|
|
249
|
+
|
|
250
|
+
[Files And Changes]
|
|
251
|
+
- Modified: src/auth/session.ts (refreshToken, verifyToken, Session)
|
|
252
|
+
- Read: src/types.ts (User, AuthPayload)
|
|
253
|
+
- Created: tests/auth-refresh.test.ts
|
|
254
|
+
|
|
255
|
+
[Type Catalog]
|
|
256
|
+
- src/auth/session.ts [modified]:
|
|
257
|
+
export function refreshToken(token: string): Promise<Session>
|
|
258
|
+
export function verifyToken(token: string): Promise<User>
|
|
259
|
+
export interface Session {
|
|
260
|
+
- src/types.ts [read]:
|
|
261
|
+
export interface User {
|
|
262
|
+
export type AuthPayload = {
|
|
263
|
+
|
|
264
|
+
[Commits]
|
|
265
|
+
- a1b2c3d: fix(auth): refresh token after password reset
|
|
266
|
+
|
|
267
|
+
[Anchors]
|
|
268
|
+
- commits: a1b2c3d
|
|
269
|
+
- errors: TS2304
|
|
270
|
+
- files: src/auth/session.ts, src/types.ts, tests/auth-refresh.test.ts
|
|
271
|
+
|
|
272
|
+
[Outstanding Context]
|
|
273
|
+
- [RESOLVED] [tsc] src/session.ts(5,18): error TS2304: Cannot find name 'authenticateUser'
|
|
274
|
+
- [ERROR] [bash:exit 1] bun test tests/auth.test.ts → 3 tests failed
|
|
275
|
+
- [WARN] [tests] FAIL auth.test.ts > refresh token should work
|
|
276
|
+
- [INFO] [no matches] grep "verifyCredentials"
|
|
277
|
+
|
|
278
|
+
[Earlier Turns]
|
|
279
|
+
- Set up the project structure → read package.json, tsconfig.json
|
|
280
|
+
- Install auth dependencies → ran bun add, edited package.json
|
|
281
|
+
- Configure the test runner → edited bunfig.toml, ran bun test
|
|
282
|
+
|
|
283
|
+
[Current Status]
|
|
284
|
+
- Working on: fix the auth bug, users can't log in after password reset
|
|
285
|
+
- Last action: Edit "src/auth/session.ts"
|
|
286
|
+
- Next: need to add the refreshToken function signature
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
[user]
|
|
291
|
+
Fix the auth bug, users can't log in after password reset
|
|
292
|
+
|
|
293
|
+
[assistant]
|
|
294
|
+
Root cause is a missing token refresh after password reset...
|
|
295
|
+
* Read "src/auth/session.ts" (#3)
|
|
296
|
+
* Read "src/types.ts" (#5)
|
|
297
|
+
* Edit "src/auth/session.ts" (#7)
|
|
298
|
+
* bash "bun test tests/auth.test.ts" (#9)
|
|
299
|
+
...(28 earlier lines omitted)
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
Compaction at 2026-05-18T14:32:00Z — 47 msgs → 23k tok (12x) | tail: 3 msgs ~5.2k tok (range: [#0, #43])
|
|
305
|
+
|
|
306
|
+
Use `vcc_recall` to search for prior work, decisions, and context from before this summary.
|
|
307
|
+
Do not redo work already completed.
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Sections appear only when relevant — a session with no git commits won't have `[Commits]`.
|
|
311
|
+
|
|
312
|
+
**Sections:**
|
|
313
|
+
|
|
314
|
+
| Section | Description |
|
|
315
|
+
|---|---|
|
|
316
|
+
| `[Session Goal]` | Initial goal + scope changes (regex-based extraction) |
|
|
317
|
+
| `[Files And Changes]` | Modified/created/read files from tool calls, annotated with exported symbol names (capped, paths trimmed to common root) |
|
|
318
|
+
| `[Type Catalog]` | Exported signature lines from modified and read files — the public API surface the model needs for continuation |
|
|
319
|
+
| `[Commits]` | Git commits made during the session (last 8, hash + first line) |
|
|
320
|
+
| `[Anchors]` | Structured reference points — commit hashes, error IDs, key file paths — for zero-tool-call recall |
|
|
321
|
+
| `[Outstanding Context]` | Unresolved items — error exit codes, test failures, tsc errors, empty search results, pending questions — tagged `[ERROR]`/`[WARN]`/`[INFO]`/`[RESOLVED]` by severity |
|
|
322
|
+
| `[Earlier Turns]` | Per-turn one-liner summaries for every conversational turn — heaviest compression layer covering turns that would otherwise fall off the brief transcript |
|
|
323
|
+
| `[Current Status]` | Current focus, last file-modifying action, and next steps — extracted from the conversation tail |
|
|
324
|
+
| `[User Preferences]` | Regex-extracted from user messages (`always`, `never`, `prefer`...) |
|
|
325
|
+
| Brief transcript | Chronological conversation flow — rolling window of ~120 recent lines, tool calls collapsed to one-liners with `(#N)` refs |
|
|
326
|
+
|
|
327
|
+
**Merge policy:**
|
|
328
|
+
- `Session Goal`, `User Preferences`: concise sticky sections
|
|
329
|
+
- `Session Goal`, `User Preferences`, `Earlier Turns`: sticky sections that accumulate across compactions (capped)
|
|
330
|
+
- `Outstanding Context`, `Type Catalog`, `Current Status`, `Anchors`: volatile (replaced each compaction)
|
|
331
|
+
- `Files And Changes`, `Commits`: unique union across compactions
|
|
332
|
+
- Brief transcript: rolling window, older lines drop off
|
|
333
|
+
|
|
334
|
+
### Deep error extraction
|
|
335
|
+
|
|
336
|
+
`[Outstanding Context]` goes beyond keyword matching. It captures:
|
|
337
|
+
|
|
338
|
+
| Signal | Format | Example |
|
|
339
|
+
|---|---|---|
|
|
340
|
+
| Bash non-zero exit code | `[bash:exit N]` | `[bash:exit 1] npm test → 3 tests failed` |
|
|
341
|
+
| TypeScript compiler error | `[tsc]` | `[tsc] src/auth.ts(12,5): error TS2322: Type 'string' is not...` |
|
|
342
|
+
| Test failure | `[tests]` | `[tests] FAIL auth.test.ts > login should work` |
|
|
343
|
+
| Empty grep/glob | `[no matches]` | `[no matches] Grep "verifyCredentials"` |
|
|
344
|
+
| Tool error result | `[tool]` | `[bash] Command not found` |
|
|
345
|
+
| Blocker text | `[user]` or plain | `[user] The build is still failing with...` |
|
|
346
|
+
|
|
347
|
+
Items tagged `[RESOLVED]` when the file they reference was subsequently edited — the model can skip them:
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
- [RESOLVED] [tsc] src/auth.ts(5,18): error TS2304: Cannot find name 'authenticateUser'
|
|
351
|
+
- [ERROR] [bash:exit 1] bun test tests/api.test.ts → 2 tests failed
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
All items are deduplicated — the same error won't appear twice.
|
|
355
|
+
|
|
356
|
+
### Symbol-level file annotations
|
|
357
|
+
|
|
358
|
+
`[Files And Changes]` annotates file paths with exported symbol names extracted from tool call arguments and results:
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
- Modified: src/auth.ts (login, verifyToken, Session)
|
|
362
|
+
- Read: src/types.ts (User, AuthPayload)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Supported languages: TypeScript/JavaScript (`export function/class/type/interface`), Python (`def`/`class`), Go (`func`, exported only), Rust (`pub fn/struct/enum/trait`).
|
|
366
|
+
|
|
367
|
+
### Type catalog
|
|
368
|
+
|
|
369
|
+
`[Type Catalog]` captures the exact exported signature lines from modified and read files. This gives the compacted model the type signatures it needs to continue coding — without re-reading files.
|
|
370
|
+
|
|
371
|
+
Modified files appear first, read files second. Entries are capped at 8 signatures per file and 12 files total.
|
|
372
|
+
|
|
373
|
+
## Recall (Lossless History)
|
|
374
|
+
|
|
375
|
+
Pi's default compaction discards old messages permanently. After compaction, the agent only sees the summary.
|
|
376
|
+
|
|
377
|
+
`vcc_recall` bypasses this by reading the raw session JSONL file directly. By default it searches only the active conversation lineage, regardless of how many compactions have happened. Use `scope:"all"` only when you intentionally want to include off-lineage branches.
|
|
378
|
+
|
|
379
|
+
### Adaptive View (Structure-Preserving Search Results)
|
|
380
|
+
|
|
381
|
+
Search results are grouped by **conversation segments** (turns) instead of showing flat ranked entries. Each segment starts at a user or bash message and includes all subsequent assistant responses, tool calls, and tool results.
|
|
382
|
+
|
|
383
|
+
Matched entries are marked with `>`, non-matched entries within the same segment are shown for context:
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
vcc_recall({ query: "auth bug" })
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
```
|
|
391
|
+
Found 4 matches for "auth bug" — 2 matches across 1 segment
|
|
392
|
+
|
|
393
|
+
--- #12-#17 (2/6 entries match) ---
|
|
394
|
+
> #12 [user] I found an auth bug in the login flow
|
|
395
|
+
#13 [assistant] Let me check the auth module...
|
|
396
|
+
#14 [tool_call] Read src/auth.ts
|
|
397
|
+
#15 [tool_result] export function login...
|
|
398
|
+
> #16 [assistant] The bug is in refreshToken
|
|
399
|
+
#17 [tool_result] Edit src/auth.ts (success)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
When matches span multiple segments, adjacent non-matching turns are shown with a `(context)` tag:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
Found 3 matches for "cache" — 2 matches across 2 segments
|
|
406
|
+
|
|
407
|
+
--- #5-#8 (1/4 entries match) ---
|
|
408
|
+
#5 [user] add caching to the API layer
|
|
409
|
+
#6 [assistant] I'll set up Redis...
|
|
410
|
+
#7 [tool_call] Edit src/cache.ts
|
|
411
|
+
> #8 [tool_result] Redis connected successfully
|
|
412
|
+
|
|
413
|
+
--- #20-#23 (1/4 entries match) ---
|
|
414
|
+
> #20 [user] the cache eviction policy is wrong
|
|
415
|
+
#21 [assistant] Let me check the TTL config...
|
|
416
|
+
#22 [tool_call] Read src/cache.ts
|
|
417
|
+
#23 [tool_result] export const TTL = 3600
|
|
418
|
+
|
|
419
|
+
--- #9-#19 (context) ---
|
|
420
|
+
#9 [user] also fix the error handling
|
|
421
|
+
#10 [assistant] Added try/catch around cache calls
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
This format preserves the conversational structure around matches, so the agent can understand *where* in the conversation flow each match occurred and what context surrounds it.
|
|
425
|
+
|
|
426
|
+
### Search
|
|
427
|
+
|
|
428
|
+
Queries support **regex** and **multi-word OR logic** ranked by relevance:
|
|
429
|
+
|
|
430
|
+
```
|
|
431
|
+
vcc_recall({ query: "auth token" }) // active-lineage OR search, ranked
|
|
432
|
+
vcc_recall({ query: "auth token", page: 2 }) // paginated (5 results/page)
|
|
433
|
+
vcc_recall({ query: "hook|inject" }) // regex pattern
|
|
434
|
+
vcc_recall({ query: "fail.*build" }) // regex pattern
|
|
435
|
+
vcc_recall({ query: "auth token", scope: "all" }) // search all lineages
|
|
436
|
+
vcc_recall({ query: "race condition", scope: "compaction:2" }) // search within compaction #2's segment
|
|
437
|
+
vcc_recall({ query: "design rationale", scope: "compaction:latest" }) // search most recent compaction segment
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Compaction-scoped search targets only the original messages that were summarized by that compaction cycle. This lets you drill into specific conversation segments without sifting through unrelated chat.
|
|
441
|
+
|
|
442
|
+
Manual slash command:
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
/pi-vcc-recall auth token scope:all
|
|
446
|
+
/pi-vcc-recall race condition scope:compaction:latest
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Browse
|
|
450
|
+
|
|
451
|
+
Without a query, returns the last 25 entries as brief summaries:
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
vcc_recall()
|
|
455
|
+
vcc_recall({ scope: "all" }) // browse recent entries across all lineages
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Expand
|
|
459
|
+
|
|
460
|
+
Returns full untruncated content for specific indices found via search:
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
vcc_recall({ expand: [41, 42] }) // active-lineage expand
|
|
464
|
+
vcc_recall({ expand: [41, 42], scope: "all" }) // expand across all lineages
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Typical workflow: **search → find relevant entry indices → expand those indices for full content**.
|
|
468
|
+
|
|
469
|
+
> Some tool results are truncated by Pi core at save time. `expand` returns everything in the JSONL but can't recover what Pi already cut.
|
|
470
|
+
|
|
471
|
+
## Performance
|
|
472
|
+
|
|
473
|
+
pi-vcc processes 3.7 MB sessions (2,600 messages, 3,000 blocks) in **~31 ms** — no LLM calls, no I/O waits beyond reading the session JSONL. Below are the optimizations that got us there.
|
|
474
|
+
|
|
475
|
+
### Pipeline profile (3.7 MB session)
|
|
476
|
+
|
|
477
|
+
| Stage | Time | % of total |
|
|
478
|
+
|---|---|---|
|
|
479
|
+
| `normalize` | 4 ms | 13% |
|
|
480
|
+
| `filterNoise` | <1 ms | <1% |
|
|
481
|
+
| `buildToolResultIndex` | <1 ms | <1% |
|
|
482
|
+
| **`extractFileAndSymbolData`** | **23 ms** | **74%** |
|
|
483
|
+
| Other extractors | <1 ms | <1% |
|
|
484
|
+
| `buildBriefSections` | 1 ms | 4% |
|
|
485
|
+
| `formatSummary` + merge | ~2 ms | 7% |
|
|
486
|
+
| **Total** | **~31 ms** | |
|
|
487
|
+
|
|
488
|
+
### Optimizations
|
|
489
|
+
|
|
490
|
+
#### Catastrophic backtracking fix (`C_FUNC_RE`)
|
|
491
|
+
|
|
492
|
+
The C/C++ function-declaration regex used a repeated group `(?:\w+(?:\s*[*&]+\s*)?)+` that triggered exponential backtracking on long non-matching identifiers (e.g. `createAssistantMessageEventStream`). A single pathological line took **1.2 s**; a session with many such lines could stall compaction for seconds.
|
|
493
|
+
|
|
494
|
+
Replaced with a lazy-quantifier pattern `\w[\w:*&\s]*?` and a negative lookahead to skip Go `func` lines. The same line now takes **<0.1 ms** — a **>1000×** speedup. This was the root cause of the original "slow compaction" report on 170k-token sessions.
|
|
495
|
+
|
|
496
|
+
#### Unified symbol extraction (`extractFileAndSymbolData`)
|
|
497
|
+
|
|
498
|
+
Previously, three independent extractors (`extractFiles`, `extractSymbolChanges`, `extractTypeCatalog`) each scanned the same tool results with overlapping regex patterns — a **triple-redundant parse**. The unified `extractFileAndSymbolData()` in `shared-symbols.ts` does it once and feeds all three consumers from a single pass.
|
|
499
|
+
|
|
500
|
+
Also added `ToolResultIndex` and `buildToolResultIndex()` to pre-compute the tool_call → tool_result look-ahead map once, shared across all extractors instead of each scanning forward independently.
|
|
501
|
+
|
|
502
|
+
#### `DECL_SCREEN_RE` pre-filter
|
|
503
|
+
|
|
504
|
+
Each line was tested against a 15-regex cascade to find declaration names. ~60% of lines in a real session are body code, comments, or blank — none can match, yet every line ran all 15 tests.
|
|
505
|
+
|
|
506
|
+
`DECL_SCREEN_RE` is a single anchored regex that rejects non-declaration lines in one test. Matching lines then fall through to the full cascade. Measured at **2.6× faster** for the `parseDeclName` stage.
|
|
507
|
+
|
|
508
|
+
#### `eachLine()` generator replaces `split().slice()`
|
|
509
|
+
|
|
510
|
+
`extractSymbolsFromText` used `text.split("\n").slice(0, N)` to read the first N lines — allocating a full temporary string array every call. Over 600+ tool results in a large session, this added up to ~18 ms of allocation overhead.
|
|
511
|
+
|
|
512
|
+
Replaced with an `eachLine()` generator using `indexOf("\n")` + `slice()` — zero intermediate array allocation. Produces identical iteration behavior.
|
|
513
|
+
|
|
514
|
+
#### `Set`-based dedup replaces `Array.includes()`
|
|
515
|
+
|
|
516
|
+
Symbol dedup used `Array.includes()` on value arrays that grew to 200+ entries per file — O(n) per check. A parallel `Map<string, Set<string>>` makes dedup O(1). Measured at **5.7× faster** for dedup operations.
|
|
517
|
+
|
|
518
|
+
#### `Intl.Segmenter` → regex word split
|
|
519
|
+
|
|
520
|
+
`brief.ts` used `Intl.Segmenter` for token-aware truncation, which allocated granular objects per word. Replaced with `\p{L}[\p{L}\p{N}]*|\p{N}+` regex — identical output, ~2× faster, zero object allocation.
|
|
521
|
+
|
|
522
|
+
#### `convertToLlm()` elimination
|
|
523
|
+
|
|
524
|
+
The `before-compact` hook called `convertToLlm()` to transform messages into an LLM message format before processing. Since pi-vcc processes messages algorithmically via `normalize()` (which already handles `user`, `assistant`, `toolResult`, and `bashExecution` directly), this conversion was both lossy (flattened bash `command`/`output`/`exitCode` into plain text) and wasteful. Removed entirely.
|
|
525
|
+
|
|
526
|
+
#### Missing `read` in `FILE_READ_TOOLS`
|
|
527
|
+
|
|
528
|
+
pi's built-in file-read tool uses the lowercase `read` tool name, but `FILE_READ_TOOLS` only contained `Read`. All read operations were invisible to file-activity and symbol extraction — a correctness fix, not strictly a performance fix, but it meant the symbol extractor was silently skipping data it should have processed.
|
|
529
|
+
|
|
530
|
+
### Summary
|
|
531
|
+
|
|
532
|
+
| Optimization | Impact |
|
|
533
|
+
|---|---|
|
|
534
|
+
| `C_FUNC_RE` backtracking fix | 1.2 s → <0.1 ms per line (>1000×) |
|
|
535
|
+
| Unified symbol extraction | 3× fewer redundant scans |
|
|
536
|
+
| `DECL_SCREEN_RE` pre-filter | 2.6× faster `parseDeclName` |
|
|
537
|
+
| `eachLine()` generator | ~18 ms saved on large sessions |
|
|
538
|
+
| `Set`-based dedup | 5.7× faster symbol dedup |
|
|
539
|
+
| Regex word split | 2× faster token truncation |
|
|
540
|
+
| `convertToLlm()` removal | Eliminated redundant message conversion |
|
|
541
|
+
|
|
542
|
+
## Pipeline
|
|
543
|
+
|
|
544
|
+
1. **Normalize** — raw Pi messages → uniform blocks (user, assistant, tool_call, tool_result, thinking, bash)
|
|
545
|
+
2. **Filter noise** — strip system messages, empty blocks, noise tools (TodoWrite, etc.)
|
|
546
|
+
3. **Build sections** — extract goal, file paths + symbols, type catalog, blockers (exit codes, tsc, tests, empty grep), preferences
|
|
547
|
+
4. **Brief transcript** — chronological conversation flow, tool calls collapsed to one-liners, text truncated
|
|
548
|
+
5. **Format** — render into bracketed sections + transcript, with cache-friendly ordering (stable sections first, volatile last)
|
|
549
|
+
6. **Merge** — if previous summary exists: sticky sections merge, volatile sections replace, transcript rolls
|
|
550
|
+
7. **Footer** — append timestamp, compression ratio, message range, and recall note
|
|
551
|
+
|
|
552
|
+
## Config
|
|
553
|
+
|
|
554
|
+
Config lives at `~/.pi/agent/pi-vcc-config.json` (auto-scaffolded on first load with safe defaults):
|
|
555
|
+
|
|
556
|
+
```json
|
|
557
|
+
{
|
|
558
|
+
"overrideDefaultCompaction": true,
|
|
559
|
+
"debug": false,
|
|
560
|
+
"modelThresholds": {
|
|
561
|
+
"neuralwatt/zai-org/GLM-5.1-FP8": { "reserveTokens": 32768 },
|
|
562
|
+
"neuralwatt/moonshotai/Kimi-K2.6": { "compactPercent": 65 },
|
|
563
|
+
"neuralwatt/neuralwatt/glm-5.1-long": { "compactPercent": 80 }
|
|
564
|
+
},
|
|
565
|
+
"globalThreshold": { "compactPercent": 70 }
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
- **`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.
|
|
570
|
+
- **`debug`** *(default `false`)*: when `true`, each compaction writes detailed info to `/tmp/pi-vcc-debug.json` — message counts, cut boundary, summary preview, sections.
|
|
571
|
+
- **`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:
|
|
572
|
+
- **`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.
|
|
573
|
+
- **`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.
|
|
574
|
+
- **`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`.
|
|
575
|
+
- **`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).
|
|
576
|
+
- **`defaultThreshold`** *(default: none, deprecated)*: use `globalThreshold` instead. Backward compatible — still works.
|
|
577
|
+
|
|
578
|
+
### How compaction thresholds work
|
|
579
|
+
|
|
580
|
+
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.
|
|
581
|
+
|
|
582
|
+
Pi-vcc's thresholds control compaction timing in **both directions**, and work at both the per-model and global level:
|
|
583
|
+
|
|
584
|
+
| Direction | How it works |
|
|
585
|
+
|---|---|
|
|
586
|
+
| **Compact later** (model can handle more context) | `session_before_compact` cancels compaction when context hasn't crossed the model's threshold. The global threshold might trigger compaction prematurely for a model with a large context window. |
|
|
587
|
+
| **Compact earlier** (model needs compaction sooner) | `agent_end` and `model_select` proactively trigger compaction when context exceeds the model's threshold but hasn't hit the global threshold yet. The `globalThreshold` also proactively triggers for unmatched models. |
|
|
588
|
+
|
|
589
|
+
This works regardless of whether pi-vcc or pi-core handles the actual summary (`overrideDefaultCompaction` true or false) — compaction thresholds control the *when*, not the *how*.
|
|
590
|
+
|
|
591
|
+
Key matching order: exact `"provider/modelId"` → `"modelId"` → `globalThreshold` → pi-core's global setting.
|
|
592
|
+
|
|
593
|
+
Explicit `/pi-vcc` commands bypass threshold checks — if you ask for compaction, you get it.
|
|
594
|
+
|
|
595
|
+
## Related Work
|
|
596
|
+
|
|
597
|
+
- [VCC](https://github.com/lllyasviel/VCC) — the original transcript-preserving conversation compiler
|
|
598
|
+
- [Pi](https://github.com/badlogic/pi-mono) — the AI coding agent this extension is built for
|
|
599
|
+
- [DeepSeek-V4](https://huggingface.co/deepseek-ai/DeepSeek-V4-Pro/blob/main/DeepSeek_V4.pdf) — hybrid attention architecture that directly inspired pi-vcc's multi-resolution transcript, resolution detection, task-boundary cut, and anchors
|
|
600
|
+
- [Mastra](https://mastra.ai) — Observational Memory patterns that inspired Current Status, priority error tags, cache-friendly ordering, and compaction-scoped recall
|
|
601
|
+
- [Claude Code](https://github.com/anthropics/claude-code) — three-tier compaction architecture (LLM / session memory / micro-compact) that influenced cache-friendly ordering and compaction-scoped recall design
|
|
602
|
+
- [Codex (OpenAI)](https://github.com/openai/codex) — Rust-based three-path compaction that inspired the handoff preamble and first-class structured compaction output
|
|
603
|
+
- [VCC Paper](https://arxiv.org/abs/2603.29678) — adaptive view concept that inspired structure-preserving search results and thinking content surfacing in recall
|
|
604
|
+
|
|
605
|
+
### Inspirations & Attribution
|
|
606
|
+
|
|
607
|
+
This fork builds on the upstream `sting8k/pi-vcc` with novel features inspired by five external projects. Below is a comprehensive mapping of each inspiration source to the features it produced.
|
|
608
|
+
|
|
609
|
+
#### DeepSeek-V4 — Hybrid Attention Architecture
|
|
610
|
+
|
|
611
|
+
Inspired by DeepSeek-V4's CSA/HCA/SWA attention architecture, Lightning Indexer, Attention Sink, Quick Instruction, and contextual parallelism.
|
|
612
|
+
|
|
613
|
+
| DeepSeek-V4 Technique | pi-vcc Equivalent | Shared Principle |
|
|
614
|
+
|---|---|---|
|
|
615
|
+
| **CSA** (light compression, m=4) | `[Files And Changes]`, `[Type Catalog]` | Medium-fidelity: keeps structure but drops full content |
|
|
616
|
+
| **HCA** (heavy compression, m'=128) | `[Earlier Turns]` | Heaviest compression: one-liner per conversational turn |
|
|
617
|
+
| **Sliding Window Attention** (n_win=128) | Brief transcript rolling window + `[Current Status]` | Uncompressed recent context for local fidelity |
|
|
618
|
+
| **Lightning Indexer** (top-k sparse selection) | `vcc_recall` (BM25 + regex) | Selective, not exhaustive, access to compressed memory |
|
|
619
|
+
| **Attention Sink** (near-zero on stale entries) | `[RESOLVED]` tag on fixed errors | Let the consumer gracefully ignore stale compressed context |
|
|
620
|
+
| **On-disk KV cache** (prefix reuse) | Raw JSONL recall via `vcc_recall` | Lossless cold store alongside compressed hot context |
|
|
621
|
+
| **Quick Instruction** (cache reuse for aux tasks) | `[Anchors]` (zero-tool-call recall) | Self-serve lookups from already-present context |
|
|
622
|
+
| **Contextual Parallelism** (boundary alignment) | Task-boundary-aware cut | Compression segments align to meaningful units, not arbitrary positions |
|
|
623
|
+
| **Hybrid precision** (BF16+FP8, cache-aligned) | Cache-friendly section ordering | Stable prefix survives across compactions for prompt caching |
|
|
624
|
+
| **Interleaved thinking preservation** | Brief transcript preservation | Discarding intermediate reasoning forces reconstruction from scratch |
|
|
625
|
+
|
|
626
|
+
**Features delivered:**
|
|
627
|
+
1. **Multi-resolution transcript** — three-zone brief: `[Earlier Turns]` (one-liner/turn), brief transcript (medium compression), kept tail (uncompressed)
|
|
628
|
+
2. **Error resolution detection** — tsc errors tagged `[RESOLVED]` when the file they reference was subsequently edited
|
|
629
|
+
3. **Task-boundary-aware cut** — cut point detects mid-flight turns and pushes back to keep the whole turn in the tail
|
|
630
|
+
4. **Structured anchors** — `[Anchors]` section with commit hashes, error IDs, key file paths for zero-tool-call recall
|
|
631
|
+
|
|
632
|
+
#### Mastra — Observational Memory
|
|
633
|
+
|
|
634
|
+
Inspired by Mastra's OM patterns — treating compaction output as a structured observation layer rather than free-form prose.
|
|
635
|
+
|
|
636
|
+
| Mastra OM Pattern | pi-vcc Equivalent |
|
|
637
|
+
|---|---|
|
|
638
|
+
| Observational memory summary | `[Current Status]` section — auto-extracted focus, last action, next steps |
|
|
639
|
+
| Priority-tagged observations | `[ERROR]`/`[WARN]`/`[INFO]`/`[RESOLVED]` tags on Outstanding Context |
|
|
640
|
+
| Stable-first observation ordering | Cache-friendly section ordering (stable sections first, volatile last) |
|
|
641
|
+
| Per-observation metadata | Timestamp + compression-ratio metadata footer |
|
|
642
|
+
| Scoped observation retrieval | Compaction-scoped `vcc_recall` (`scope:'compaction:N'`) |
|
|
643
|
+
|
|
644
|
+
#### Claude Code — Three-Tier Compaction
|
|
645
|
+
|
|
646
|
+
Claude Code's three-tier architecture (full LLM, session memory = LLM-free, micro-compact = cache-editing) influenced pragmatic design choices:
|
|
647
|
+
|
|
648
|
+
| Claude Code Technique | pi-vcc Influence |
|
|
649
|
+
|---|---|
|
|
650
|
+
| Cache-sharing fork path | Cache-friendly section ordering — stable prefix survives across compactions for prompt cache hits |
|
|
651
|
+
| `lastSummarizedMessageId` boundary tracking | Compaction-scoped recall — `scope:'compaction:N'` drills into specific segments |
|
|
652
|
+
| Session memory (deterministic, LLM-free) | Validates the zero-LLM approach; pi-vcc achieves similar determinism via extraction instead of a separate memory pipeline |
|
|
653
|
+
|
|
654
|
+
#### Codex (OpenAI) — Three-Path Compaction
|
|
655
|
+
|
|
656
|
+
Codex's Rust-based compaction (inline/remote/remote-v2) inspired higher-level design decisions:
|
|
657
|
+
|
|
658
|
+
| Codex Technique | pi-vcc Influence |
|
|
659
|
+
|---|---|
|
|
660
|
+
| `summary_prefix.md` continuation directive | Handoff preamble — continuation directive prepended to every compaction summary |
|
|
661
|
+
| `ContextCompactionItem` as first-class protocol type | Structured bracket-tagged sections act as a first-class compaction artifact, not free-form prose |
|
|
662
|
+
| `InitialContextInjection` control over system context | Task-boundary-aware cut ensures meaningful boundaries, not arbitrary splits |
|
|
663
|
+
|
|
664
|
+
#### VCC Paper — Adaptive View
|
|
665
|
+
|
|
666
|
+
The original VCC paper (arxiv.org/abs/2603.29678) introduced the adaptive view concept — preserving conversation structure and role tags in search projections.
|
|
667
|
+
|
|
668
|
+
| VCC Paper Concept | pi-vcc Equivalent |
|
|
669
|
+
|---|---|
|
|
670
|
+
| Adaptive view (structure-preserving projection) | Structure-preserving search results — grouped by conversation segments (turns) with `>` match indicators |
|
|
671
|
+
| Role tag preservation | Thinking content surfacing — `thinkingOf()` extracts model reasoning for recall display and search indexing |
|
|
672
|
+
|
|
673
|
+
#### Original Novel Work
|
|
674
|
+
|
|
675
|
+
Features with no external inspiration — original engineering contributions unique to this fork:
|
|
676
|
+
|
|
677
|
+
1. **Deep error extraction** — captures bash exit codes `[bash:exit N]`, tsc errors `[tsc]`, test failures `[tests]`, empty grep/glob `[no matches]` with structured tags and dedup
|
|
678
|
+
2. **Symbol-annotated files** — file paths annotated with exported symbol names extracted from tool call arguments and results
|
|
679
|
+
3. **Type catalog** — `[Type Catalog]` section with exact exported signature lines from modified/read files
|
|
680
|
+
4. **Multi-language symbol extraction** — Rust, Java, C/C++, Zig, Ruby, Elixir symbol detection with language-specific regex patterns
|
|
681
|
+
5. **Performance optimization suite** — catastrophic backtracking fix (>1000×), unified extraction (3×), DECL_SCREEN_RE pre-filter (2.6×), eachLine() generator, Set-based dedup (5.7×), Intl.Segmenter replacement (2×), convertToLlm() elimination
|
|
682
|
+
6. **Entry-ID-based message range** — stores entry IDs instead of branch-relative indices for correct cross-branch resolution
|
|
683
|
+
7. **Neuralwatt-MCR interop** — signals compaction override so MCR models don't discard pi-vcc's summary
|
|
684
|
+
8. **Supply-chain hardening** — pinned deps, npm-shrinkwrap, audit fixes
|
|
685
|
+
|
|
686
|
+
## License
|
|
687
|
+
|
|
688
|
+
MIT
|