5-minute install. Zero LLM, deterministic 30–470 ms compaction. Works from source, npm, or git. No build step —
type:module+allowImportingTsExtensions.
| Need | Check | Why |
|---|---|---|
oh-my-pi (omp) |
omp --version |
Host that loads the plugin |
bun ≥ 1.1 |
bun --version |
Runs typecheck/test/smoke; Node alone won't resolve .ts imports |
git |
git --version |
github: installs and source link |
POSIX shell, ~/.omp writable |
ls -ld ~/.omp |
Config lives at ~/.omp/omp-vcc/config.json |
If you installed oh-my-pi via npm, omp is already on PATH (/opt/homebrew/bin/omp on macOS).
flowchart LR
A[Prereqs ok?] -->|omp + bun| B[Choose install path]
A -->|missing omp| P1[Install oh-my-pi first]
A -->|missing bun| P2[bun install via curl]
B --> C1[Local link — dev]
B --> C2[npm — stable]
B --> C3[git — latest]
C1 & C2 & C3 --> D[Verify]
D --> E[First /omp-vcc]
Use this when hacking on omp-vcc or pinning to a local checkout.
git clone https://github.com/zhulinchng/omp-vcc.git
cd omp-vcc
# link into oh-my-pi (creates symlink in ~/.omp/plugins)
omp plugin link .
# or explicit path
omp plugin link /Users/zhu/code/projects/omp-vcc
# verify
omp plugin list --json | jq '.[] | select(.name | contains("omp-vcc"))'
# → { "name": "@zhulinchng/omp-vcc", "enabled": true, "version": "0.1.0", ... }
omp plugin doctor
# → 5 ok 0 warnings 0 errors (marketplace warning is fine)
# optional local gates (same as CI)
bunx tsc --noEmit
bun test
bun run smokeUpdate: git pull && omp plugin link . (re-links). Uninstall: see Uninstall & reset.
Unscoped package omp-vcc lives on npmjs; scoped @zhulinchng/omp-vcc on GitHub Packages (needs auth).
# from npmjs — no auth needed
omp plugin install omp-vcc
# from GitHub Packages — requires PAT (once)
# add to ~/.npmrc:
# @zhulinchng:registry=https://npm.pkg.github.com
# //npm.pkg.github.com/:_authToken=YOUR_PAT # PAT needs read:packages
omp plugin install @zhulinchng/omp-vcc
omp plugin list | grep omp-vccUpgrade: omp plugin update @zhulinchng/omp-vcc or reinstall.
No registry needed, tracks main:
omp plugin install github:zhulinchng/omp-vcc
# specific ref
omp plugin install github:zhulinchng/omp-vcc#main# 1. plugin appears and is enabled
omp plugin list --json | jq '.[] | select(.name | contains("omp-vcc")) | {name, enabled, version, settings}'
# 2. commands are discovered
omp --help | grep -E "omp-vcc|vcc-recall" # or check inside TUI: /help
# 3. smoke inside a live session
omp -e @zhulinchng/omp-vcc
# inside TUI:
/omp-vcc keep:1 hello from setup
# expect summary with [Session Goal]/[Brief transcript] + toast:
# omp-vcc: kept 1/2 turns, ~0.8k tokIf you see omp-vcc: kept ... the hook is live. If not, check overrideDefaultCompaction (see Configuration).
sequenceDiagram
participant U as You
participant CLI as omp CLI
participant Host as oh-my-pi Host
participant Plug as omp-vcc Extension
U->>CLI: omp plugin install ...
CLI->>Host: register extension ./extensions/main.ts
Host->>Plug: ExtensionAPI factory(pi)
Plug->>Host: scaffoldSettings() → ~/.omp/omp-vcc/config.json
Plug->>Host: pi.on(session_before_compact)
Plug->>Host: pi.registerTool(vcc_recall) + pi.registerCommand(omp-vcc)
U->>Host: omp -e @zhulinchng/omp-vcc
Host->>Plug: session_before_compact event
Plug-->>Host: {compaction: {summary, firstKeptEntryId}}
Host-->>U: summary + toast
Inside any omp TUI session with the plugin enabled:
# keep last 1 user turn (default), summarize the rest
/omp-vcc
# explicit keep + focus hint (focus is free text, not a scope filter)
/omp-vcc keep:2 fix auth token refresh
# compact everything (keep 0) — useful before /clear
/omp-vcc keep:0
# alias (migration from pi-vcc)
/pi-vcc keep:1
Manual compaction never needs overrideDefaultCompaction:true. Auto threshold/overflow compaction does — it's true by default.
# slash command (temporal, document-oriented)
/vcc-recall cache page:1
/vcc-recall "hook|inject" scope:all
/vcc-recall touched mode:touched
# tool (agent-callable)
/vcc-recall is a thin wrapper over the tool:
# vcc_recall({ query: "redis cache", scope: "all", page: 1 })
# vcc_recall({ query: "#12:src/auth.ts" }) # drill-down
# vcc_recall({ query: "auth", mode: "touched" })Plain keywords rank best; regex|pipes work too. Pagination is 5/page. mode:file searches only file tool arguments, CJK terms use script-aware tokenization, and model-facing responses are bounded while slash-command output remains human-readable.
You have two surfaces — file (durable source of truth) and manifest /settings (host plugin-settings overlay). The current host bridge reads the public getPluginSettings("omp-vcc", ctx.cwd) API; older ctx.settings/ctx.config bridges remain supported. File changes apply on the next compaction; /settings changes apply immediately.
# file (XDG)
cat ~/.omp/omp-vcc/config.json
# "vccEnabled": true,
# "overrideDefaultCompaction": true,
# "smartKeepTail": true,
# "continueAfterThresholdCompact": true,
# "debug": false,
# "compactionSummaryMode": "append",
# "retainedToolOutputMaxTokens": 20000,
# "showPreCompactionMessage": true,
# "recallResponseMaxChars": 48000,
# "nativeMemory": true,
# "debugLog": false
# }
# toggle without editing file (takes effect next compaction)
omp config set plugins."@zhulinchng/omp-vcc".debug true
omp config set plugins."@zhulinchng/omp-vcc".overrideDefaultCompaction false
omp config list | grep vcc
# or inside TUI: /settings → plugin section @zhulinchng/omp-vcc| Flag | Default | When to flip |
|---|---|---|
vccEnabled |
true |
false to disable all interception (plugin stays loaded) |
overrideDefaultCompaction |
true |
false to let native LLM compaction handle threshold/overflow |
smartKeepTail |
true |
false to always keep:1 even when tail < 5 k tokens |
continueAfterThresholdCompact |
true |
false to stop after auto-compaction instead of invisible-continue |
debug |
false |
true to dump /tmp/omp-vcc-debug.json per compaction |
compactionSummaryMode |
append |
rewrite for legacy complete-summary replacement; invalid append chains already fail closed |
retainedToolOutputMaxTokens |
20000 |
0 disables provider-visible tool-output omission |
showPreCompactionMessage |
true |
false to suppress display-only dropped assistant output |
recallResponseMaxChars |
48000 |
0 makes model recall responses unbounded |
nativeMemory |
true |
false to skip the public host-memory query |
debugLog |
false |
true for bounded rotating redacted JSONL metrics |
See configuration.md for full flag semantics and the optional one-file core patch for a native /settings → Context → Compaction dropdown.
oh-my-pi ships 5 ordered strategies — remote (provider-native), snapcompact (bitmap archive, vision only), handoff (markdown doc), shake (local artifact:// elision), soft (local LLM summary) — walked from compaction.methodOrder → packages/coding-agent/src/session/compaction-methods.ts:10-84 (default ["remote","snapcompact","handoff","shake","soft"], docs/compaction.md:142-151). omp-vcc does not replace them in methodOrder — it adds a context-full extension hook (session_before_compact → hook.ts:708-850) that preempts the walk when it returns {compaction}. Full harness map: harness.md §8.
flowchart TB
TRIG["auto trigger\nthreshold / overflow"] --> GATE{"omp-vcc gate\nvccEnabled && (sentinel || overrideDefaultCompaction)"}
GATE -->|"override:true (default)"| VUI["omp-vcc handles → V_ui (no LLM)\nmethodOrder ignored for auto"]
GATE -->|"override:false + no sentinel"| WALK["host walks methodOrder\nremote → snapcompact* → handoff → shake → soft\n*vision gate"]
VUI --> NEXT["next turn from summary + kept tail"]
WALK --> NEXT
subgraph Legend["You choose by toggling override"]
direction LR
O1["override:true = deterministic"] --> O2["override:false = native order"]
end
classDef vcc fill:#e3f2fd,stroke:#1565c0
class VUI vcc
classDef host fill:#fff3e0,stroke:#ef6c00
class WALK host
omp-vcc summarizes history (V_ui 5 sections + ranked brief) while shake elides kept tail heavy blocks and snapcompact is an alternative history archiver (vision bitmaps). They touch disjoint regions or are mutually exclusive history paths, so combinations are additive or sequential — never simultaneous double-summarization.
One entry per trigger: host commits exactly one
CompactionEntrypersession_before_compact(shared-events.ts:375-381). A “combo” is either (a) one Entry that merges VCC history + shake tail, or (b) two Entries chained across triggers/fallbacks (VCC then snapcompact/shake).
| Trigger | override |
methodOrder |
Result |
|---|---|---|---|
threshold, override:true, ["remote","snapcompact","handoff","shake","soft"] (default) |
VCC handles, host rescue may shake if still over band | VCC + (shake if dead-end) | |
threshold, override:false, ["vcc","remote","snapcompact","handoff","shake","soft"] (with patch) |
walker picks VCC via methodOrder, fallback to snapcompact/shake |
VCC → snapcompact/shake fallback | |
manual /omp-vcc keep:2 |
always VCC (sentinel __omp_vcc__) |
VCC | |
manual /omp-vcc keep:2 then /compact snapcompact |
second call needs override:false (explicit modes are served by VCC while override:true) |
VCC entry + snapcompact entry (sequential) |
flowchart LR
VCC["VCC V_ui\n(history)"] -->|cancel / void| SNAP["snapcompact\n(bitmap, vision only)\nmodel.input includes image"]
SNAP -->|fail / not vision| SHAKE["shake\nartifact:// elision\ntail only"]
SHAKE -->|fail| SOFT["soft / remote\nLLM summary"]
VCC -. "additive\n(disjoint regions)" .-> SHAKE
classDef vcc fill:#e3f2fd,stroke:#1565c0
class VCC vcc
classDef host fill:#fff3e0,stroke:#ef6c00
class SNAP,SHAKE,SOFT host
Additive: VCC summarizes history, shake elides kept tail — disjoint, so both can apply across one or two Entries. With default methodOrder containing shake, host's #rescueCompactionDeadEnd (session-maintenance.ts:2604) runs shake elide automatically after VCC if !compactionCreatedHeadroom(). No second summary needed.
Sequential: VCC and snapcompact both archive the same messagesToSummarize slice; running both on the same cut would double-summarize. Valid sequential is: /omp-vcc keep:2 (entry 1: VCC), do 10 more turns, then /compact snapcompact (entry 2: bitmap). For auto, put vcc first in methodOrder via the optional patch so the walk prefers VCC and falls to snapcompact/shake when VCC cancels or vision gate passes — see harness §8.4.
No action needed for the common case — default is correct:
# file is source of truth, but /settings overlay works immediately
cat ~/.omp/omp-vcc/config.json
# { "overrideDefaultCompaction": true, "vccEnabled": true, ... }
# host order can stay at default — it is dormant for auto while omp-vcc handles
omp config list | grep compaction.methodOrder
# ["remote","snapcompact","handoff","shake","soft"] (dormant for threshold)
# manual /omp-vcc always works, and /vcc-recall works regardless of override
/omp-vcc keep:2 fix auth # V_ui
/vcc-recall hook scope:all # V_adapt
/vcc-config # show effective config with per-key source (file/host overlay/default)
Flip the intercept off - the walk resumes, omp-vcc only handles explicit /omp-vcc (sentinel path at hook.ts:733 bypasses the flag):
# let host own threshold/overflow via methodOrder
omp config set plugins."@zhulinchng/omp-vcc".overrideDefaultCompaction false
# or edit file: "overrideDefaultCompaction": false then restart TUI
# pick your preferred order in /settings → Context → General → Compaction method order
# or via CLI (example: prefer handoff + shake, no remote)
omp config set compaction.methodOrder '["handoff","shake","soft"]'
# example: prefer provider-native when available, else handoff
omp config set compaction.methodOrder '["remote","handoff","shake"]'
# verify host will walk it
omp config list | grep -E "compaction.enabled|compaction.methodOrder"
# expect at least one method + enabled:true
# explicit omp-vcc still works even with override:false
/omp-vcc keep:1 # still V_ui — sentinel is exempt
/compact # now uses host order (remote/handoff/…), not omp-vccWhen to keep snapcompact alongside omp-vcc: snapcompact needs a vision model (model.input includes "image" → compaction-methods.ts:124). Keep snapcompact in methodOrder if you sometimes use vision models and want a verbatim image archive (costs vision tokens but preserves every char) vs omp-vcc's distilled V_ui (5 sections + 1100→2000 tok brief, cheaper). With override:true manual /snapcompact still requires an explicit snapcompact mode — leave the method in order and it stays reachable for deliberate archival; with override:false the walk can pick it automatically for vision models.
When to keep handoff: handoff writes a long-form markdown doc via handoff-document.md and preserves it on disk (docs/compaction.md:261-268). Choose it if your workflow hands the session doc to another agent. Note auto overflow skips handoff (its request would reuse overflowing input → docs/compaction.md:112), so even first in order it will not run for overflow — omp-vcc's cancel heuristic (hook.ts:817 tokensBefore>50k → defer to host) then falls to shake/soft.
flowchart LR
subgraph OverrideTrue["override:true (default) — omp-vcc owns auto"]
T1["threshold"] --> V1["omp-vcc V_ui"]
O1["overflow"] --> V2["omp-vcc V_ui\n(can defer to host if too few msgs)"]
M1["/omp-vcc"] --> V3["omp-vcc V_ui"]
C1["/compact"] --> V4["omp-vcc V_ui (non-sentinel)"]
SC1["/snapcompact (explicit)"] -. "needs override:false\nto stay snapcompact" .-> S1["snapcompact bitmap"]
end
subgraph OverrideFalse["override:false — host owns auto"]
T2["threshold"] --> W1["walk methodOrder\nremote/handoff/shake..."]
O2["overflow"] --> W2["walk (handoff skipped)"]
M2["/omp-vcc"] --> V5["omp-vcc V_ui (sentinel)"]
C2["/compact"] --> W3["walk methodOrder"]
SC2["/snapcompact"] --> S2["snapcompact"]
end
classDef vcc fill:#e3f2fd,stroke:#1565c0
class V1,V2,V3,V4,V5 vcc
classDef snap fill:#ede7f6,stroke:#5e35b1
class S1,S2 snap
Without a patch /settings shows omp-vcc as a separate plugin section @zhulinchng/omp-vcc (11 settings) and override drives interception — no core edit needed. If you want VCC as a first-class entry in /settings → Context → General → Compaction method order, apply the one-file patch from configuration.md:243 (packages/coding-agent/src/session/compaction-methods.ts:11 add {value:"vcc",...} + STRATEGY_BY["vcc"]="context-full" + DEFAULT put vcc first, isCompactionMethod = Object.hasOwn at 60). Then set methodOrder = ["vcc","remote","snapcompact","handoff","shake","soft"] and override:false so the walk treats vcc as the preferred context-full candidate whose impl is still the extension hook. See configuration.md#optional-native-dropdown.
# verify where auto will go without switching TUI
grep -q '"overrideDefaultCompaction": true' ~/.omp/omp-vcc/config.json && echo "auto: omp-vcc" || echo "auto: host methodOrder"
omp config list | grep compaction.methodOrder
# with patch: expect ["vcc",...] when you set it; without patch: unknown "vcc" is filtered by resolveCompactionMethodOrder# link installs
cd /path/to/omp-vcc && git pull && omp plugin link .
# registry installs
omp plugin update @zhulinchng/omp-vcc
# or
omp plugin install omp-vcc # re-install latest
# verify
bunx tsc --noEmit && bun test && bun run smoke && omp plugin doctor# linked dev (Option A)
omp plugin uninstall omp-vcc
# registry / git (Option B/C) — use the name you installed with
omp plugin uninstall omp-vcc # npmjs (unscoped)
omp plugin uninstall @zhulinchng/omp-vcc # GPR or github: spec (scoped)
# if unsure:
omp plugin list --json | jq -r '.npm[].name | select(contains("omp-vcc"))'
# config + debug snapshot (either case)
rm -rf ~/.omp/omp-vcc
rm -f /tmp/omp-vcc-debug.json /tmp/pi-vcc-debug.json
# historic rename leftovers (@zhu/omp-vcc → omp-vcc) and orphaned
# symlinks left by old hosts are auto-cleaned here and on next
# extension startup (migrate-stale.ts); also runs on npm postuninstall
node scripts/uninstall-reset.js
omp plugin doctor # should show no omp-vccRe-install: omp plugin link . (dev) or omp plugin install omp-vcc / omp plugin install github:zhulinchng/omp-vcc.
| Symptom | Cause | Fix |
|---|---|---|
omp plugin list doesn't show omp-vcc |
Not linked / install failed | omp plugin link . then omp plugin doctor |
/omp-vcc says unknown command |
Extension not loaded | omp -e @zhulinchng/omp-vcc or check extensions: ["./extensions/main.ts"] in package.json |
| Auto compaction still calls LLM | overrideDefaultCompaction:false |
omp config set plugins."@zhulinchng/omp-vcc".overrideDefaultCompaction true or edit config.json |
Toast no_live_messages canceled |
Session too small / sent keep too large |
Use keep:0 or add more turns |
| Config change ignored | Wrong path or host settings bridge unavailable | Check OMP_VCC_CONFIG_PATH, cat ~/.omp/omp-vcc/config.json, and omp plugin list; current host overlay is read through the public plugin-settings bridge |
/tmp/omp-vcc-debug.json missing |
debug:false |
omp config set plugins."@zhulinchng/omp-vcc".debug true then compact again |
bun test fails with import errors |
Ran with node |
Use bun test and bun run smoke (allowImportingTsExtensions) |
types errors |
Vendored core is // @ts-nocheck |
bunx tsc --noEmit should be 0; check types.d.ts shim |
Still stuck? Capture:
cat ~/.omp/omp-vcc/config.json
omp plugin list --json | jq '.[] | select(.name|contains("omp-vcc"))'
cat /tmp/omp-vcc-debug.json 2>/dev/null | jq '{usedOwnCut, tokensBefore, firstKeptEntryId, sections}'
omp plugin doctorOpen an issue at https://github.com/zhulinchng/omp-vcc/issues with that output.
- Concepts →
architecture.md(VCC pipeline, extension lifecycle, invariants) - Flags & XDG →
configuration.md - Paper mapping →
paper-notes.md - Tests & proof →
verification.md - Harness impact →
harness.md(what is added vs intercepted, with mermaid)