Documentation

Everything you need to run billion-context locally and connect your agents. Exhaustive references live in the repository: CONFIGURATION.md, TECHNICAL-NOTES.md, PLUGIN.md, SESSION-IDENTITY.md.

Quickstart

One npm package, Node 22+. Install it, then start the local compression proxy:

terminal
npm install -g billion-context --prefix=~/.local   # Node 22+ (nvm / Homebrew / Windows: drop --prefix)
bili                                               # start the proxy (or: bili pi / claude / codex / omp / … to launch an agent through it)

Everything binds loopback only — nothing is exposed externally. Every instance serves the same built-in dashboard under its own address:

Next stop: the panel below — configuration, session stats, and live logs all live there.

Web UI

Access: open http://127.0.0.1:8787/__bili/ once the proxy is up — every instance serves this same built-in dashboard on its own loopback address (ACP_PORT overrides). Or just type /acp in your agent — the panel footer carries a direct link to the current session.

Every running instance serves a built-in dashboard at /__bili/:

  • Overview — total sessions and requests, gross and net savings, global hit rate with miss breakdown, protocol distribution.
  • Sessions — usage cards, context trajectory chart, cache economics with fold detail, compression blocks, and the Markdown handoff document (copy or download).
  • Configuration — view and edit the JSON config; changes apply immediately.
  • Connect — per-instance wiring guide with copyable commands.
  • Logs — live tail of the daemon log, filterable by session id or keyword, downloadable as plain text.

Connect clients

Three ways to put the proxy between your agent and the model. Pick whichever fits your client:

Native plugin — recommended

Installs a thin plugin straight into the client (the original file is backed up once); deepest integration, with native in-conversation tools. Covers pi · omp · claude · codex · opencode · dsh · kimi · hermes · zcode:

Click any command chip to copy it.

For opencode, bili plugin install opencode --with-mcp also registers its MCP tools.

Launcher — works for any agent

Zero config edits: wraps any CLI agent and injects the proxy + compression state automatically. Each launch brings up a fresh throwaway proxy instance:

terminal
bili pi        # or: bili codex / claude / omp / opencode / hermes /
dsh / codebuddy / qoder / trae / jcode / kimi / gemini / iflow /
qwen / mcode / aider / copilot / amp / goose      (full list: bili --help)

Your own arguments pass straight through: bili pi -r.

Generic proxy — URL prefix routing

Any client that accepts a custom endpoint works out of the box: point its base URL at the prefix below, keeping the original upstream address as the tail:

base url
http://localhost:8787/bili/https://api.openai.com/v1   # example

Replace the trailing https://api.openai.com/v1 with each client's original upstream address — see the table below for exactly where to put it.

Per-client integration detail:

ClientMethodsKey mechanismCommand (click to copy)
pipluginlauncherfile-free extension + env
omppluginlauncherfile-free env
claudepluginlauncherenv base URL + managed settings
codexpluginlauncherconfig.toml provider / openai_base_url
opencodepluginlauncherbase_url (+ --with-mcp)
dshpluginlauncher--patch plugin · DSH_HOME overlay
kimipluginlauncherKIMI_CODE_HOME / managed OAuth
hermespluginlauncherHTTPS_PROXY + MITM CA
zcodepluginnative plugin · /bili/ prefix · GUI cert-MITM
jcodelauncherHTTPS_PROXY + CA (api.z.ai)
geminilauncherGOOGLE_GEMINI_BASE_URL
iflowlauncherIFLOW_BASE_URL
qwenlauncherMITM · DashScope/Qwen hosts
mcodelauncherMiniMax yaml endpoints
aiderlauncherOPENAI_API_BASE / .aider.conf.yml
copilotlauncherMITM · githubcopilot.com
amplauncherMITM · ampcode.com
gooselauncherOPENAI_HOST / ANTHROPIC_HOST redirect
traelauncherTRAE_CLI_API_HOST
codebuddylauncherCODEBUDDY_BASE_URL
qoderlauncherMITM hosts whitelist

Clients that hard-code HTTPS upstreams are handled by an automatic certificate: domains the client reaches are whitelisted on first sight. Need another host? Add it explicitly:

terminal
bili pi --mitm-domain api.foo.com

Commands

bili
Start the standalone proxy daemon (default port 8787).
bili <client>
Bring up a throwaway proxy + launch the chosen client against it — no config edits.
bili plugin install|list|update|remove
Manage the per-client plugins and their update lanes.
bili test pi
Non-polluting smoke test through the proxy.
bili export [session] [--full]
List sessions, or export one as a Markdown handoff document (--full restores original messages).
bili doctor
Audit every install lane: versions, owners, running proxies (read-only).
bili update
Check for and install a newer version now.
/acp
Live context panel inside your agent — includes this session's web-dashboard link.
/acp-cache
Token economics report for the session: new content vs cache hits vs compression repay.

Configuration

Everything lives in one JSON file, created automatically on first run and hot-reloaded while you edit:

path
~/.config/billion-context/billion-context.json     # { } is valid — defaults cover most setups

Configure it in the panel instead of by hand: the configuration page (here on a launcher agent's instance) validates every edit, offers quick-config controls, and writes this same file — environment variables still win over it when set.

Full parameter reference

Every knob in detail. Generated from the same seed as the repository's CONFIGURATION.md (CI drift-guarded), so the site and the repo docs stay one copy.

compress — every knob

ParameterTypeWhat it does
modelContextLimitnumber | "N%"Effective context window (tokens or "N%"): denominator of the usage ratio and a hard preflight wall. Highest-priority window source — overrides per-route / per-model declarations plus the top-level default.
maxContextLimitnumber | "N%"Forced-compression nudge threshold (default "75%"): once history passes this share of the window the nudge fires immediately, bypassing the growth gates. Not a hard cap.
emergencyThresholdPercentnumber | %Default "95%": emergency truncation of oversized tool outputs when history passes this share of the window (must be ≥ maxContextLimit).
outputHeadroomMaxPctnumber | %Cap (0–1, default 0.25) on the share of the window reserved for output via max_tokens.
nudgeGrowthTokensnumberGrowth gate (default 50000): nudges fire only when a foldable range exceeds baseline growth by this many tokens.
tierNudgeTokensobject {t1?, t2?, t3?}Per-tier token-mass trigger thresholds for the T1/T2/T3 paths; each unset sub-field keeps its derived value (T1 = growth step, T2/T3 = step × 1.5).
nudgeModelDecidedbooleanModel-decided nudge timing (off by default): an armed tier-1 nudge first asks the model, over the session's cached prefix, whether compressing now helps the current task; a strict-JSON yes injects a directive with a program-finalized span, anything else injects nothing this round.
nudgeDecisionMaxTokensnumberOutput budget (tokens) of the model-decision side call (default 200). Must be > 0.
streamSummarybooleanForce streaming (SSE) summarization from the first attempt — stream-mandating upstreams now self-heal via a one-shot SSE probe on any non-streaming 400 (#2494); kept as the escape hatch for gateways that time out long non-streaming calls (e.g. Cloudflare 524), which the self-learn cannot detect, or to skip the one probe round-trip.
preserveRecentMessagesnumberKernel default 3 (tuned 5 → 3, #3058): the most recent messages stay soft-protected from folds.
preserveRecentTokensnumberKernel default ≈5000: the most recent tokens stay soft-protected from folds.
minCompressRangeCharsnumber (deprecated alias: minCompressRange)Smallest foldable range, in characters (kernel default ≈5000) — shorter ranges never fold.
reconcile"off" | "warn" | "repair""off" | "warn" | "repair" (default repair): reconcile folded state when the client rewinds or rewrites history between turns.
promptPackstring (builtin: "default", "lean")Compression prompt pack, resolved project pack → user pack → builtin. Builtins: "default" and "lean" (one-line tool descriptions). Not gated by acknowledgePromptsRisk.
tiersbooleanOn by default: tiered T1→T3 distillation spreads folding cost across generations.
protectedToolsstring[]Hard exclusion across all history: results of listed tools never fold (use with care).
protectedLatestToolsstring[]Protects only the LATEST instance of cumulative-snapshot tools whose newest result supersedes older ones (e.g. todo lists).
neverPreserveRecentToolsstring[] ([] valid)Excluded from the recent protection zone (immediately compressible); default ["decompress", "search_context", "read", "bash"]. An empty array is valid — it excludes nothing (maximum protection).
preserveRecentToolsstring[]Subtraction form: recent-zone tools minus this list get full protection. An empty array here is rejected as a typo.
visibilityMarkersbooleanAppend 📦/❌ visibility markers after compress/decompress/search_context/acp_status results (on by default; off stops imitated narration).
rulesbooleanOff by default: persistent model reminders delivered through an injected acp_rule tool, hard-protected from folds. pi/omp expose an /acp-rule command.
injectToolbooleanRegister the acp_compress tool for clients (on by default; global level only).
injectNudgebooleanSend growth nudges as the window fills (on by default; global level only).
reasoning{ drop?, threshold? }{ drop?, threshold? } — drop finished-round reasoning runs longer than 2048 chars (drop: true by default); strict-reasoning upstreams need drop:false.
  • absorb — opt-in instant distillation of large tool results into short abstracts; originals go to the content store (enabled, minToolTokens (default 1000), contextThresholdPct, excludeTools, toolName "absorb").
  • ccr — content cache & retrieve: large outputs are stored losslessly beside the session and replaced by a head excerpt plus pointer; the model fetches originals back via acp_retrieve. No cap or eviction; on by default in proxy mode since v2 (enabled, minToolTokens (~4000), excludeTools, toolName "acp_retrieve", maxHeadChars).
  • search — search-behavior settings for search_context; currently holds planAware.
  • search.planAware — off by default: when on, search_context candidates are re-ranked against current plan state (latest-wins todos + last user turn); off keeps byte-identical lexical results.
  • imageCompression — opt-in lossy downscale via optional sharp before sending (enabled, minTokens 512, maxDimension 1280, quality 80, format "webp"); image_full restores originals; proxy mode only.
  • prompts — override the kernel doctrine texts (compressPhilosophy, howToCompressRules, tier2DistillRules, tier3CondenseRules); load-bearing for quality — gated behind acknowledgePromptsRisk.
  • reasoningGuard — auto-repair of reasoning-lattice truncation on gpt-5.x/6.x (≤3 continue-nudge rounds, marker "Continue thinking...") (enabled, maxContinue, maxTierN, markerText, base, offset, debugLog).
  • outputSteering — { enabled? false, verbosityLevel? 2 (0–4), effortRouting? }: appends a conciseness directive to the system-prompt tail; clamps mechanical continuation requests.
  • priceProfile — w / r / q ratios normalized to the input price (write / cache-read / output); unset ⇒ models.dev $/Mtok registry rows, kernel fallback {1, 0.1, 4}. Report-only — never affects triggers or the wire.
  • acknowledgePromptsRisk — must be true before custom prompts take effect.

providers — route table fields

Keys are upstream URL prefixes — longest match wins, boundary-safe. https:// keys match /bili/ traffic; mitm:// keys match login-client traffic on the MITM lane. An entry with no fields is still an ordinary compressed lane.

FieldTypeWhat it does
modelsrecord name → { context?, output?, compress?, benefit? }Record of model name → {context?, output?, compress?, benefit?}: declares each model's context window, output ceiling, and per-model compression overrides (benefit marks free-quota billing). Not a traffic filter.
proxystring ("" = explicit direct)Per-route outbound proxy URL; empty string = explicitly direct.
compressProtocol"tools" (default) | "marker"Which ACP injection surface this lane speaks: native tools (default) or prompt markers.
protocol"anthropic" | "openai" | "responses" | "google"Declare this lane's wire protocol. A client-side /bili/<protocol>/ URL marker outranks the declaration; unparseable bodies relay verbatim.
compressobject (level 2)Any compress field can be overridden here — level 2 of the global→provider→model merge (per-field, deepest wins).
compat{ roles?, dropFields? }{ roles?, dropFields? } — remap roles the upstream rejects (e.g. developer→system); provider entries beat the global block per key; dropFields strips fields strict schemas reject (additive union).
directbooleanClient-side exemption: traffic for this upstream is never pointed at bili at all — unlike passthrough, it does not even terminate there.
passthroughbooleanRoute-scoped byte-for-byte relay with no session state — for anti-fraud upstreams.
imageBilling"auto" | "pixels" | "bytes"Image-token counting basis for this route: auto (→ pixels), pixels, or bytes (explicit opt-in for byte relays).
imageTokenCapnumberPer-image token cost cap for this route; unset = uncapped.
resignscheme → { enabled?, passthrough?, credentialRef? }Per-route overrides of the global resign map (level 2, deepest wins).
bindstring (named entries only)Named (non-URL) entry: deep-merged onto the bound URL lane at config load as an alias. Without bind a named entry stays routing-inert.
apiKeyEnvstring (env var name)Lane credential override (#2336): requests forwarded through this lane carry this env variable's value instead of the client's own credential — x-api-key / x-goog-api-key replaced outright, Bearer authorization replaced, injected when absent. Exactly one of apiKeyEnv / credentialRef per entry.
credentialRefstring (store name)Lane credential override via the private summary-credential store (`secret:NAME` semantics); exactly one of apiKeyEnv / credentialRef per entry.
compactionOptInbooleanNamed entries only: opt a non-http(s)-baseUrl provider into compaction ownership (pi/omp lanes); unions with env BILI_NON_HTTP_PROVIDERS.

Other top-level fields

  • Server & core
  • port — listen port (default 8787).
  • host — bind address (default 127.0.0.1; "::" is dual-stack, 0.0.0.0 exposes outward).
  • sessionHeader — HTTP header carrying the session id (default x-acp-session).
  • log — file logging master switch (on by default).
  • logFile — explicit path for bili.log (env ACP_LOG_FILE wins).
  • debug — verbose per-request logging.
  • dumpSse — raw SSE stream dump level for debugging.
  • passthrough — global raw-relay switch (off by default; ACP_PASSTHROUGH / --passthrough win over the file).
  • Upstream & billing
  • upstream — fallback upstream base URL (ACP_UPSTREAM overrides).
  • proxy — global outbound proxy. Resolution: per-route → BILI_UPSTREAM_PROXY → Web UI → this value → HTTP(S)_PROXY env → Windows system proxy → direct. Empty string = explicitly direct; SOCKS5 is rejected.
  • modelContextLimit — legacy global window ceiling (default 200000); prefer compress.modelContextLimit.
  • imageBilling — global image-token estimation: "auto" (default → pixels), "pixels" or "bytes" (explicit opt-in for byte relays).
  • imageTokenCap — global per-image token cap; unset = uncapped (BILI_IMAGE_TOKEN_CAP wins, read live).
  • Behavior toggles
  • rollout — canary gating: features enable for a percent of installs, bucketed on a stable per-machine UUID; nested base rolls B out only inside A's cohort.
  • maskHosts — mask non-public host names in logs as <private-host> (on by default; credentials are always masked).
  • subagentSplit — Claude subagents get their own session namespace (on by default).
  • forkAdoption — adopt anonymous forks into their parent session (off by default).
  • affinitySimhash — simhash chain-alignment re-attaches sessions after pervasive client rewrites (on by default).
  • resumeInheritance — inherited refs/blocks on resume (cc --resume) (on by default).
  • chainContentDetection — recognize bili-owned traffic by body content (off; false-positive risk).
  • chainEgressStamp — model-visible stamp on egress messages (off; burns tokens).
  • stableSystemAnchor — best-effort wire-layer prefix-cache anchor (off; plain-proxy lanes).
  • compat — wire-compatibility block: role remaps, stream error shape, strict-schema field drops, cache-control handling.
  • MITM lane
  • providersPath — external routes file path (env ACP_PROVIDERS beats both it and inline providers).
  • mitm.enabled / mitm.domains — HTTPS interception switch and whitelist domains for login clients.
  • mitm.handshakeTimeoutMs — TLS handshake timeout (default 10000).
  • Process blocks
  • network — transport timeouts & replays: upstreamTimeoutMs 720000 · requestWatchdogMs 2× · replayRetryMax 3 · replayRetryBaseMs 1500 · keepAliveTimeoutMs 5000 · clientErrorBackstopMs 30000 · exposureLogIntervalMs 3600000 · streamKeepAliveMs 15000 · preflightHoldMs 30000 · preflightDeadEndCooldownMs 300000 · proxyKeepAliveMaxMs 55000.
  • persist — session persistence to disk: enabled true · zstd false · debounceMs 500 · tailTokens 16384 · epermAlertThreshold 5 · epermAlertRepeatMs 0.
  • storage — unified storage sidecar: unified legacy — file tier selects the lane-wide storage mode; env BILI_STORAGE_UNIFIED wins when set.
  • sessions — max 256 (LRU) · gc { enabled false, maxAgeDays 7, maxTokens 1000000, intervalMs 3600000 } — GC is opt-in; user data is never pruned silently.
  • plugin.snapshotCapBytes — fork API snapshot cap (default 100 MiB; 0 disables snapshots).
  • update — self-update channel: registry "npmjs" · checkIntervalMs 180000.
  • Diagnostics & updates
  • diagnostics — local troubleshooting knobs: dumpBody false · dumpReq true · rawDumpDir · dump4xx false (+MaxBytes 2 MiB) · renderNone/noInjectTool/noCompressPrompt false · countTokensPassthrough false · compressProtocol "tools".
  • fakeCompletion — retry loop for truncated final chunks: retries 0 (opt-in) · bufCapBytes 16 MiB.
  • autoUpdate — automatic version checks (on by default; disable via ACP_AUTO_UPDATE=0 or --no-auto-update).
  • autoRestartOnUpdate — restart the daemon automatically once a newer version is installed (opt-in).
  • updateTag — npm dist-tag channel: latest (default), dev, or pr.
  • advisoryCheck / advisoryUrl — critical-defect advisory watcher (on by default; force-installs the recommended fix when your version is affected).
  • releaseNotesCheck / releaseNotesUrl — release-notes feed for the panel (on by default; fetch + cache only, never installs).
  • Advanced scalars
  • codexCompact — intercept | pass (default intercept): handle Codex's compaction requests in bili or forward upstream — explicit forms plus the inline auto-compact transiting as an ordinary turn (prompt-form detection, #3008; overridden compact_prompt transits verbatim).
  • ccrRetrievalTtlMs — lifetime of CCR retrieval pointers (default 600000; 0 disables retrieval).
  • decompressTmpCap — size cap for temporary decompressed blocks (default 50).
  • native.attachExternal — let launcher-less native plugins attach to an external daemon (off by default).

Environment variables win over the config file — precedence: env > file > built-in default, and a set variable owns the knob even if its value looks invalid. Common ones:

env
ACP_PORT=8787                    # listen port
ACP_HOST=127.0.0.1               # bind address
ACP_UPSTREAM=https://api.anthropic.com # fallback upstream base URL
ACP_PASSTHROUGH=1                # force passthrough on (wins over file)
ACP_PROVIDERS=/path/to/routes.json # external routes file (beats inline)
BILI_CONFIG_FILE=~/.config/...   # override the config file path
BILI_SESSIONS_DIR=~/.local/share/... # where session records are stored
BILI_UPSTREAM_PROXY=http://host:port   # outbound egress proxy
ACP_MODEL_CONTEXT_LIMIT=200000   # global context window override
BILI_MITM_DOMAINS=api.foo.com    # add MITM whitelist domains
BILI_REPLAY_RETRY_MAX=3          # transport-failure replays
BILI_SESSION_GC=1                # enable session garbage collection
BILI_IMAGE_TOKEN_CAP=8192        # per-image token budget
BILI_SUBAGENT_SPLIT=0            # disable subagent session split
BILI_FORK_ADOPTION=1             # adopt anonymous forks into parent session
ACP_LOG_FILE=/path/to/bili.log   # explicit log path
ACP_COMPRESS_TOOL=0              # disable the injected compress tool
ACP_COMPRESS_NUDGE=0             # disable growth nudges

How compression works

The same four mechanisms as the front page, condensed:

  • Folded, not rewritten When history outgrows its budget, only the newly consumed slice is folded into a compact summary block — once, per block. The window oscillates in a bounded band instead of growing without limit: production sessions run months at ten-thousand-plus calls. Any block decompresses back to full text when you need the detail.
  • The provider's cache stays warm A fold deactivates old ranges without touching the front of the conversation, so the prompt-cache prefix survives turn after turn — 94.2% of active input was served from cache in the study. Rewrite-style compaction invalidates the whole prefix and re-bills it. This does not.
  • The model decides what to forget A versioned compression doctrine lives in the system prompt; a growth-gated nudge says when folding pays off. The agent judges what the task still needs, may refuse, and compresses between steps — never mid-step.
  • Every token is accounted Each request is sampled into a per-session ledger — new content, cache reads, compression repay, TTL expiry — plotted in percentage points on a trajectory chart. Audit real savings with /acp in the terminal or the built-in web UI at /__bili/.

The full treatment, with numbers: read the preprint.

Privacy

billion-context runs only on your machine and binds loopback by default. It forwards model traffic — OAuth tokens pass through untouched, unauthenticated probes aside — and uploads nothing.

Session records stay local (~/.local/share/billion-context/sessions); the log sits beside the state dir. Export any session with bili export, delete files whenever you like.

FAQ

Which Node version?

Node 22 or newer — required by the npm package.

Does it work on Windows?

Recommended: WSL2. Inside WSL everything uses 127.0.0.1 as usual; a Windows-side client should reach the proxy via the WSL host IP, or simply run the client inside WSL too.

Do I pay for the proxy itself?

No — it's free and open source (MIT plus an attribution clause). Token savings come from smaller contexts and your provider's prompt caching; the economics panel shows the real per-session numbers.

How do I upgrade?

Run bili update (or reinstall via npm). Use bili doctor before and after to audit every install lane.

I can't reach the dashboard?

Check the daemon is running (bili doctor) and the port isn't changed (default 8787 for the standalone daemon; launcher lanes start at 18787). The dashboard binds 127.0.0.1 only — from another machine or phone you need an SSH port-forward or an explicit ACP_HOST setting.

How do I check my cache hit rate?

Don't dig through logs — type /acp-cache in your client: a summary report prints in the terminal headed by a clickable Web UI link (the session page adds the hit-rate chart plus per-break attribution). Rule of thumb: a healthy session sits at 95–97%; compression itself costs ≤2%. When lower, the usual suspects in order: ① upstream cache-TTL expiry, ② a mid-session model switch (re-bills the whole stable prefix), ③ a bili bug — report it with the page attached. /acp-cache full lists every fold & line item; HTTP equivalent: GET /__bili/cache-report.

What does /acp show?

In clients with the native plugin (opencode, dsh), /acp renders the ACP status panel of the current conversation straight from the proxy — session, blocks, compressible ranges, usage; before the first model request it shows an idle notice. /acp-cache [full] prints the cache report above.

Can I query sessions and config over the web?

Yes — open http://localhost:8787: overview dashboard, session list with per-session detail, live logs, a config editor, and an upstream connectivity test. Everything is also plain JSON for scripting (/__bili/stats, /__bili/sessions, /__bili/config, …).

When does compression happen?

It is model-driven: the injected context tools are called by the model as context grows, growth nudges (~50K-token steps by design, adjustable via compress.nudgeGrowthTokens) prompt it along the way, and preflight fires as a hard backstop when the input alone exceeds the window. Watch it live with /acp or the web UI.

Why does the first compaction wait until ~200k?

Compaction triggers on growth intervals, not absolute window position: by default the first soft compaction fires 50k tokens past the boot content (compress.nudgeGrowthTokens, flat and window-independent) — with a ~100k boot it may wait until ~200k. To make it fire earlier: trim the system prompt / disable unneeded tools / prune skills, lower compress.nudgeGrowthTokens to ~50k, or enable lean mode.

Is bili transparent? How do I turn it off?

Unrecognized endpoints forward unchanged, and every mode reverses cleanly: bili plugin remove <client> for native installs; stop using the launcher command / env vars / /bili/ prefix for the other two — traffic goes direct again immediately. Per-client mechanics: CLIENTS.md.

Where are logs and session data stored?

Log: ~/.local/state/billion-context/bili.log (also mirrored to stderr); session state: ~/.local/share/billion-context/ (XDG-overridable). Full paths: CONFIGURATION.md.

Resources