@arvoretech/pi-smart-context
Intelligent model routing and prompt compression for Pi/Kiro
Package details
Install @arvoretech/pi-smart-context from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@arvoretech/pi-smart-context- Package
@arvoretech/pi-smart-context- Version
0.4.0- Published
- Jun 24, 2026
- Downloads
- 412/mo · 32/wk
- Author
- joao.barros.arvore
- License
- MIT
- Types
- extension
- Size
- 110 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./dist/index.js"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@arvoretech/pi-smart-context
Intelligent model routing and retrieval-augmented prompt compression for Pi/Kiro.
Features
Model Routing
Uses a cheap classifier model (Haiku by default) to classify task complexity based on the full conversation context — not just the current message. So "bora" after a complex architecture discussion correctly routes to Opus.
| Classification | Default model | When |
|---|---|---|
| trivial | claude-haiku-4-5 |
Greetings, meta-conversation, no pending task |
| simple | claude-sonnet-4-6 |
Single-file fixes, quick questions |
| medium | claude-opus-4-8 |
Standard multi-file work |
| complex | claude-opus-4-8 |
Architecture, large refactors, security audits |
| Large context (>500K) | claude-sonnet-4-6 |
1M window needed |
Every slot above — provider and model — is configurable (see Configuration).
Retrieval-Augmented Compression
The core principle (from the prompt-compression literature): the model never loses access to information — it just pays less to carry it by default.
Compressed/dropped content is replaced by a summary + a recover_context("id") hint. The original is kept in an in-memory store. If the model actually needs the detail, it calls the recover_context tool to pull back the full text. This lets us compress aggressively with no quality loss.
Pipeline
| Stage | Technique | Safety |
|---|---|---|
| Tool output (structural) | Log folding, n-gram dedup, JSON tabularize, cross-turn delta | Lossless / near-lossless |
| BM25 relevance | Score old messages vs current query | — |
| Haiku summarization | Summarize old messages preserving load-bearing facts, cached by hash | Lossy but recoverable |
| Retrieval drop | Replace low-relevance content with stub + recover hint | Recoverable |
Cache-aware (critical)
Anthropic/Kiro use prompt caching keyed by prefix. Compression that rewrites the context differently each turn would break the cache and increase cost.
Two protections:
- Runtime cache detection — the extension inspects the last assistant message's
cacheRead/cacheWrite. If the provider is actively caching, lossy compression of the prefix is disabled (only safe structural compression of new tool output runs). No cache break, ever. - Stable/monotonic compression — when cache is off, once a message is compressed the identical compressed form is reused on every subsequent turn, so even the one-time prefix change rebuilds and stays stable.
Note: at the time of writing, the Kiro provider reports
cacheRead: 0 / cacheWrite: 0across sessions — caching is effectively off, so compression is pure savings (the full context is re-billed every turn with no cache to break). The cache-detection path future-proofs the extension for when Kiro enables caching.
Aggressive quality gate
The pipeline runs with one of two aggression profiles, chosen per turn:
| Profile | Trigger | Protected turns | Min savings | Summarize ≥ | BM25 drop | Tool trim ≥ | Compress under cache |
|---|---|---|---|---|---|---|---|
| Balanced | Large-context model, low usage | 4 | 15% | 400 chars | 0.25 | 4000 chars | no |
| Aggressive | Small context window (<200K) or usage ≥60% of the window | 2 | 10% | 250 chars | 0.35 | 2000 chars | yes |
Detection uses ctx.getModel().contextWindow and ctx.getContextUsage().tokens. On a smaller-context model (or when the window is filling up) the extension protects fewer turns, summarizes shorter messages, drops more low-relevance content, trims tool output sooner, and — only in this mode — keeps compressing the prefix even when the provider is caching (the cost of a cache rebuild beats overflowing the window).
Commands
/smart-context— Stats: chars saved, avg ratio, Haiku calls/cache hits, recoverable items
Configuration
Model routing is fully configurable via a project-local .pi/smart-context.json file. The extension walks up from the current working directory looking for the nearest .pi/.git root, then reads .pi/smart-context.json from it. If the file is missing or malformed, the built-in defaults are used (behavior identical to no config).
Every slot is a { "provider", "model" } pair, so you are not locked to the kiro provider — you can mix providers and models freely across slots. The provider/model strings must match what modelRegistry.find(provider, model) resolves for your Pi setup.
{
"classifier": { "provider": "kiro", "model": "claude-haiku-4-5" },
"routing": {
"trivial": { "provider": "kiro", "model": "claude-haiku-4-5" },
"simple": { "provider": "kiro", "model": "claude-sonnet-4-6" },
"medium": { "provider": "kiro", "model": "claude-opus-4-8" },
"complex": { "provider": "kiro", "model": "claude-opus-4-8" }
},
"largeContext": {
"thresholdTokens": 500000,
"model": { "provider": "kiro", "model": "claude-sonnet-4-6" }
}
}
| Key | Purpose |
|---|---|
classifier |
The cheap model that classifies task complexity. Also reused by the compression summarizer. |
routing.trivial / .simple / .medium / .complex |
Model picked for each classified complexity. |
largeContext.thresholdTokens |
When context usage exceeds this, skip classification and use largeContext.model directly. |
largeContext.model |
Model forced for very large contexts. |
Partial configs are deep-merged over the defaults — you only need to specify the slots you want to override. Each slot independently falls back to its default if the value is missing or not a valid { provider, model } pair.
Example: use a different provider
{
"classifier": { "provider": "anthropic", "model": "claude-haiku-4-5" },
"routing": {
"complex": { "provider": "openai", "model": "gpt-5" }
}
}
This classifies with Anthropic Haiku, routes complex tasks to OpenAI, and keeps the defaults for every other slot.
Architecture
src/
├── index.ts # Hooks + recover_context tool
├── config.ts # .pi/smart-context.json loader (provider+model per slot)
├── router.ts # Classifier-based complexity routing
└── compression/
├── pipeline.ts # Orchestrates stages, cache-stable, retrieval-augmented
├── store.ts # Content store for recover_context
├── haiku-summarize.ts # Haiku summarizer with hash cache
├── types.ts
└── stages/
├── bm25.ts # BM25 relevance scoring
├── dedup.ts # N-gram line deduplication
├── log-fold.ts # Log error extraction + folding
├── json-compact.ts # JSON array tabularization
└── delta.ts # Cross-turn delta compression
Usage
cd arvore-pi-extensions && pnpm install
cd packages/smart-context && pnpm build
Add to your Pi packages. The extension hooks into before_agent_start (routing), context (compression), and tool_result (structural tool-output compression).