Mirror of headroomlabs-ai/headroom (AI context compression proxy)
Find a file
Nick Vigilante 8d6c175d60
fix(subscription): only reset 5h contribution on real rollover, not API jitter (#1255)
## Description

The 5-hour-window rollover detector in
`SubscriptionTracker._maybe_reset_contribution` zeroes the
`HeadroomContribution` counters on **every poll** instead of once per
window, so the dashboard's per-window savings figure stays pinned near
0%.

Root cause: the rollover check compared `five_hour.resets_at` between
consecutive polls with a bare `!=`. Anthropic's usage API reports that
timestamp with **second-level jitter** — on my account it flaps between
`01:59:59Z` and `02:00:00Z` on consecutive polls *within the same
window* — so the `!=` is true on essentially every poll and fires a
spurious `5h window rolled over; resetting headroom contribution
counters`.

The fix treats only a **forward jump larger than `_ROLLOVER_MIN_ADVANCE`
(1 minute)** as a genuine rollover. Jitter is sub-second; a real
rollover advances `resets_at` by ~5 hours, so the threshold cleanly
separates the two.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- `headroom/subscription/tracker.py`: replaced the `curr_resets_at !=
prev_resets_at` rollover test with `curr_resets_at - prev_resets_at >
_ROLLOVER_MIN_ADVANCE`, and added the `_ROLLOVER_MIN_ADVANCE =
timedelta(minutes=1)` constant with a comment explaining the API jitter.
- `tests/test_subscription_tracker.py`: extended `_make_snapshot` to
accept an explicit `resets_at`; added
`test_second_level_reset_jitter_does_not_reset_contribution` (1-second
flap must NOT reset) and
`test_genuine_five_hour_rollover_resets_contribution` (5-hour jump still
resets).
- `CHANGELOG.md`: added a Bug Fixes entry under Unreleased.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ uv run pytest tests/test_subscription_tracker.py -v
test_tracker_notify_active_update_and_basic_state PASSED                 [ 14%]
test_tracker_start_stop_and_rollover_reset PASSED                        [ 28%]
test_second_level_reset_jitter_does_not_reset_contribution PASSED        [ 42%]
test_genuine_five_hour_rollover_resets_contribution PASSED               [ 57%]
test_maybe_poll_handles_inactive_and_none_snapshot PASSED               [ 71%]
test_maybe_poll_success_updates_state_and_metrics PASSED                [ 85%]
test_persist_and_load_state_round_trip PASSED                          [100%]
======================= 7 passed in 0.10s =======================

# Fails-before proof: stash the source fix, keep the new tests, re-run the jitter test
$ git stash push -- headroom/subscription/tracker.py
$ uv run pytest tests/test_subscription_tracker.py::test_second_level_reset_jitter_does_not_reset_contribution -q
E   AssertionError: assert 0 == 99
E    +  where 0 = HeadroomContribution(tokens_submitted=0, ...).tokens_submitted
FAILED tests/test_subscription_tracker.py::test_second_level_reset_jitter_does_not_reset_contribution
1 failed in 0.09s

$ uv run ruff check headroom/subscription/tracker.py tests/test_subscription_tracker.py
All checks passed!
$ uv run mypy headroom/subscription/tracker.py
Success: no issues found in 1 source file
```

## Real Behavior Proof

- Environment: Linux (kernel 7.0), Python 3.14, `uv` 0.11.23, headroom
proxy on `127.0.0.1:8787`, Anthropic OAuth subscription account (Claude
Max), model `claude-opus-4-8`, Claude Code `claude-cli/2.1.185`.
- Exact command / steps: Inspected the live proxy on `main` before
patching. `~/.headroom/logs/proxy.log` contained 65 `5h window rolled
over; resetting headroom contribution counters` lines over a ~6h
session; I computed the gaps between consecutive events, and dumped
`five_hour.resets_at` from `~/.headroom/subscription_state.json`
history.
- Observed result: Median gap between resets was **exactly 300.0s** (=
the default `poll_interval_s`), not ~5h — i.e. it reset every poll. The
persisted history showed `five_hour.resets_at` flapping across only 4
distinct values, all within ~2s of `02:00:00Z` (e.g.
`2026-06-22T01:59:59Z` ↔ `2026-06-22T02:00:00Z`), and `contribution` was
all-zeros. After the patch, the unit tests reproduce this exact flap
(`base` vs `base + 1s`) and the counters are preserved; a genuine +5h
jump still resets.
- Not tested: I did not run the patched proxy live for a full 5-hour
window to observe a real rollover end-to-end (would need a multi-hour
session); the genuine-rollover path is covered by unit test only. No
change to the dashboard rendering code. Verified on Linux/Python 3.14
only.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I have updated the CHANGELOG.md if applicable

## Additional Notes

Docs checklist item is N/A — this is an internal accounting fix with no
user-facing API/config change. The threshold constant
(`_ROLLOVER_MIN_ADVANCE = 1 min`) is deliberately generous over the
observed sub-second jitter while remaining far below a real ~5h advance;
happy to tune or switch to an "old deadline has elapsed" guard
(`prev_resets_at <= now`) if maintainers prefer that framing.

Co-authored-by: JD Davis <mxjerrett@gmail.com>
2026-06-26 14:13:44 -05:00
.claude-plugin ci(release): align manifest + pyproject + package.json to 0.22.3 2026-05-25 18:41:38 -07:00
.codegraph perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
.devcontainer fix(transforms): use thread-local tree-sitter parsers to prevent pyo3 Unsendable panic (#604) 2026-06-10 18:30:00 -05:00
.github fix(ci): guarantee model present in test shards to end cache-miss flakiness (#1399) 2026-06-24 21:41:50 -05:00
.serena feat: headroom wrap opencode / unwrap opencode CLI (#1105) 2026-06-22 11:07:12 -05:00
agent-evals fix(agent-evals): Phase 0 — coding-agent accuracy A/B framework (#1037) 2026-06-22 15:01:44 -05:00
benchmarks perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
crates feat(pricing): add DeepSeek V4 model pricing (deepseek-v4-flash, deepseek-v4-pro) (#1168) 2026-06-24 09:44:27 -05:00
docker feat: add differential network capture harness (#761) 2026-06-08 22:18:31 -07:00
docs fix(opencode): write local MCP config (#1381) 2026-06-26 12:23:54 -05:00
e2e ci: restore green lint (reformat for ruff 0.15.17, fix mypy no-any-return, pin linters) (#1295) 2026-06-22 15:14:40 -05:00
examples feat(transforms): tabular + spreadsheet (.xlsx/.xls) compression (#1128) 2026-06-19 11:30:20 -05:00
headroom fix(subscription): only reset 5h contribution on real rollover, not API jitter (#1255) 2026-06-26 14:13:44 -05:00
plugins docs: clarify OpenCode integration (#1317) 2026-06-24 21:54:05 -05:00
REALIGNMENT docs: add Realignment plan (40 PRs, 9 phases) 2026-05-01 23:34:46 -07:00
scripts fix(ci): normalize Windows CRLF line endings in PR governance script (#1012) 2026-06-22 18:45:12 -05:00
sdk/typescript chore: release main (#1274) 2026-06-21 22:28:55 -07:00
sql feat(telemetry): add headroom_stack and install_mode identity fields 2026-04-17 17:12:38 +02:00
tests fix(subscription): only reset 5h contribution on real rollover, not API jitter (#1255) 2026-06-26 14:13:44 -05:00
wiki fix: surface output reduction without a restart, and explain $0.00 savings on Python 3.14 (#1296) 2026-06-26 12:15:42 -05:00
.actrc feat: add act testing config, fix gitignore, make workflow production-ready 2026-04-15 20:28:29 -05:00
.actrc.local.example feat: add act testing config, fix gitignore, make workflow production-ready 2026-04-15 20:28:29 -05:00
.changelog.md fix: use /tmp for changelog artifact to avoid . file matching issues 2026-04-15 22:54:34 -05:00
.commitlintrc.json ci: fix smart_crusher branch CI failures + add make ci-precheck pre-push gate 2026-04-27 11:13:47 -07:00
.dockerignore chore: normalize line endings in init diffs 2026-04-21 20:17:14 -05:00
.env.act.example feat: add act testing config, fix gitignore, make workflow production-ready 2026-04-15 20:28:29 -05:00
.env.example fix(security): patch loopback guard, retry None raise, blocking subprocess, and cache stats race 2026-06-04 16:09:53 -04:00
.git-blame-ignore-revs chore: add .git-blame-ignore-revs 2026-04-24 15:35:29 +02:00
.gitattributes chore: enforce LF checkout for Python files 2026-04-23 08:43:03 -05:00
.gitguardian.yaml fix: harden Copilot API auth token handling (#557) 2026-06-11 12:57:48 -05:00
.gitignore feat(wrap): make tokensave the primary coding-task compressor, Serena the backup (#1230) 2026-06-25 16:55:37 -05:00
.pre-commit-config.yaml Fix CI lint failure by formatting PR governance scripts (#933) 2026-06-12 17:11:39 -05:00
.release-please-config.json ci(release-please): set versioned PR title pattern to fix tagging jam 2026-06-04 09:56:35 -07:00
.release-please-manifest.json chore: release main (#1274) 2026-06-21 22:28:55 -07:00
AGENTS.md feat: headroom wrap opencode / unwrap opencode CLI (#1105) 2026-06-22 11:07:12 -05:00
Cargo.lock feat(wrap): make tokensave the primary coding-task compressor, Serena the backup (#1230) 2026-06-25 16:55:37 -05:00
Cargo.toml Pin ORT dylib on Windows; init Python logging (#1010) 2026-06-23 07:46:24 -05:00
CHANGELOG.md fix(subscription): only reset 5h contribution on real rollover, not API jitter (#1255) 2026-06-26 14:13:44 -05:00
claude_analysis_ttl.py chore: add cache TTL cost analysis script 2026-05-13 10:49:17 -07:00
CODE_OF_CONDUCT.md Prepare for OSS release v0.2.0 2026-01-07 11:36:44 -08:00
codecov.yml fix(codex): poll /wham/usage for subscription limits (handshake no longer sends x-codex-* headers) (#924) 2026-06-12 17:03:14 -05:00
CONTRIBUTING.md fix: support Copilot Business subscription auth (#641) 2026-06-12 20:46:38 -05:00
deny.toml feat(rust): scaffold workspace + parity harness (phase-0) 2026-04-24 13:39:48 -07:00
docker-bake.hcl refactor(docker): migrate to bake with multi-variant distroless images 2026-04-04 21:51:37 +05:30
docker-compose.yml fix(docker): persist session history across container revisions (#1118) 2026-06-22 11:08:17 -05:00
Dockerfile fix(docker): persist session history across container revisions (#1118) 2026-06-22 11:08:17 -05:00
ENTERPRISE.md docs: add enterprise.md 2026-06-04 15:36:47 -07:00
Headroom-2.gif Add demo GIF to README 2026-01-20 18:57:17 -08:00
headroom-savings.png docs: rewrite README for clarity and highlight Kompress-base, leaderboard, RTK 2026-04-18 09:36:49 -07:00
headroom_learn.gif docs: add headroom learn demo GIF to README 2026-03-07 17:56:17 -08:00
HeadroomDemo-Fast.gif Replace demo GIF with HeadroomDemo-Fast.gif 2026-04-10 15:05:25 -07:00
LICENSE Prepare for OSS release v0.2.0 2026-01-07 11:36:44 -08:00
llms.txt docs: list OpenCode in the agent compatibility matrix (#1286) (#1340) 2026-06-23 14:39:40 -05:00
Makefile feat: headroom wrap opencode / unwrap opencode CLI (#1105) 2026-06-22 11:07:12 -05:00
mkdocs.yml feat: add Vertex AI proxy routing (#793) 2026-06-09 23:05:30 -07:00
NOTICE Prepare for OSS release v0.2.0 2026-01-07 11:36:44 -08:00
PR.md fix: restore release and compress regressions 2026-04-18 16:01:57 -05:00
pyproject.toml chore: release main (#1274) 2026-06-21 22:28:55 -07:00
README.md fix: surface output reduction without a restart, and explain $0.00 savings on Python 3.14 (#1296) 2026-06-26 12:15:42 -05:00
rust-toolchain.toml fix(rust): clippy 1.95 unnecessary_sort_by + pin toolchain 2026-04-27 12:11:49 -07:00
RUST_DEV.md fix(proxy): make CCR multi-worker warning conditional on backend (#770) 2026-06-11 18:59:11 -05:00
SECURITY.md docs: use headroom-ai package name in install commands (#1014) (#1257) 2026-06-22 19:25:00 -05:00
TESTING-copilot-subscription.md fix(copilot): restore generic endpoint for non-subscription OAuth (#610) (#612) 2026-06-04 16:27:54 -07:00
uv.lock fix(dashboard): include RTK stats in the historical tab (#1324) 2026-06-25 07:55:36 -07:00

  ██╗  ██╗███████╗ █████╗ ██████╗ ██████╗  ██████╗  ██████╗ ███╗   ███╗
  ██║  ██║██╔════╝██╔══██╗██╔══██╗██╔══██╗██╔═══██╗██╔═══██╗████╗ ████║
  ███████║█████╗  ███████║██║  ██║██████╔╝██║   ██║██║   ██║██╔████╔██║
  ██╔══██║██╔══╝  ██╔══██║██║  ██║██╔══██╗██║   ██║██║   ██║██║╚██╔╝██║
  ██║  ██║███████╗██║  ██║██████╔╝██║  ██║╚██████╔╝╚██████╔╝██║ ╚═╝ ██║
  ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚═╝  ╚═╝ ╚═════╝  ╚═════╝ ╚═╝     ╚═╝
                  The context compression layer for AI agents

6095% fewer tokens · library · proxy · MCP · 6 algorithms · local-first · reversible

CI codecov PyPI npm Model: Kompress-v2-base License: Apache 2.0 Docs

Docs · Install · Proof · Agents · Discord · llms.txt · Enterprise

AI agents / LLMs: read /llms.txt here, or fetch the live index / full docs blob.


chopratejas%2Fheadroom | Trendshift

Headroom compresses everything your AI agent reads — tool outputs, logs, RAG chunks, files, and conversation history — before it reaches the LLM. Same answers, fraction of the tokens.

Headroom in action
Live: 10,144 → 1,260 tokens — same FATAL found.

What it does

  • Librarycompress(messages) in Python or TypeScript, inline in any app
  • Proxyheadroom proxy --port 8787, zero code changes, any language
  • Agent wrapheadroom wrap claude|codex|aider|copilot|opencode in one command; Cursor prints manual proxy settings to paste into the app
  • MCP serverheadroom_compress, headroom_retrieve, headroom_stats for any MCP client
  • Cross-agent memory — shared store across Claude, Codex, Gemini, auto-dedup
  • headroom learn — mines failed sessions, writes corrections to CLAUDE.md / AGENTS.md
  • Output token reduction — trims what the model writes back (not just what you send): drops ceremony/restated code and skips deep "thinking" on routine steps. See Output token reduction.
  • Reversible (CCR) — originals are cached for retrieval on demand

How it works (30 seconds)

 Your agent / app
   (Claude Code, Cursor, Codex, LangChain, Agno, Strands, your own code…)
        │   prompts · tool outputs · logs · RAG results · files
        ▼
    ┌────────────────────────────────────────────────────┐
    │  Headroom   (runs locally — your data stays here)  │
    │  ────────────────────────────────────────────────  │
    │  CacheAligner  →  ContentRouter  →  CCR            │
    │                    ├─ SmartCrusher   (JSON)        │
    │                    ├─ CodeCompressor (AST)         │
    │                    └─ Kompress-base  (text, HF)    │
    │                                                    │
    │  Cross-agent memory  ·  headroom learn  ·  MCP     │
    └────────────────────────────────────────────────────┘
        │   compressed prompt  +  retrieval tool
        ▼
 LLM provider  (Anthropic · OpenAI · Bedrock · …)
  • ContentRouter — detects content type, selects the right compressor
  • SmartCrusher / CodeCompressor / Kompress-base — compress JSON, AST, or prose
  • CacheAligner — stabilizes prefixes so provider KV caches actually hit
  • CCR — stores originals locally; LLM calls headroom_retrieve if it needs them

Architecture · CCR reversible compression · Kompress-v2-base model card

Get started (60 seconds)

# 1 — Install
pip install "headroom-ai[all]"          # Python
npm install headroom-ai                 # Node / TypeScript

# 2 — Pick your mode
headroom wrap claude                    # wrap a coding agent
headroom proxy --port 8787              # drop-in proxy, zero code changes
# or: from headroom import compress      # inline library

# 3 — See the savings
headroom perf
headroom dashboard                      # live savings dashboard (proxy must be running)

Granular extras: [proxy], [mcp], [ml], [code], [memory], [relevance], [image], [agno], [langchain], [evals], [pytorch-mps] (Apple-GPU memory-embedder offload — set HEADROOM_EMBEDDER_RUNTIME=pytorch_mps). Requires Python 3.10+.

Proof

Savings on real agent workloads:

Workload Before After Savings
Code search (100 results) 17,765 1,408 92%
SRE incident debugging 65,694 5,118 92%
GitHub issue triage 54,174 14,761 73%
Codebase exploration 78,502 41,254 47%

Accuracy preserved on standard benchmarks:

Benchmark Category N Baseline Headroom Delta
GSM8K Math 100 0.870 0.870 ±0.000
TruthfulQA Factual 100 0.530 0.560 +0.030
SQuAD v2 QA 100 97% 19% compression
BFCL Tools 100 97% 32% compression

Reproduce: python -m headroom.evals suite --tier 1 · Full benchmarks & methodology

Output token reduction (cut what the model writes back)

Everything above shrinks the prompt you send. But you also pay for every token the model writes back — and on Opus-class models output costs 5× input. A lot of that output is waste: "Great, let me…" preambles, re-printing code you just showed it, and deep "thinking" on routine steps like reading a file.

Headroom can trim that too, from the proxy, without you changing any code:

  • Verbosity steering — appends a short "be terse, don't restate context" note to the end of the system prompt (so your prompt cache still hits).
  • Effort routing — when a turn is just the model resuming after a tool result (a file read, a passing test), it dials the model's thinking effort down. New questions and errors keep full effort.

Turn it on:

export HEADROOM_OUTPUT_SHAPER=1     # off by default
headroom proxy --port 8787

Already running a proxy? These switches are read live on every request, so a proxy that headroom wrap reused (rather than started) would not see a value you export afterwards — its environment was snapshotted at launch. headroom wrap now hot-syncs your current settings to the running proxy via a loopback POST /admin/runtime-env, so they take effect immediately with no restart (no cold start, no dropped requests, no lost caches). Set them before you wrap. On a shared proxy these overrides are global — the last explicit setting wins.

Learn the right terseness for you. People don't say how terse they want answers — they show it (they interrupt long replies, or move on before they could have read them). headroom learn --verbosity reads your past sessions and picks the level automatically:

headroom learn --verbosity            # preview what it found (dry run)
headroom learn --verbosity --apply    # save it; the proxy uses it from now on

See how many output tokens you saved. Output savings are counterfactual — we never see what the model would have written — so Headroom reports an honest estimate with a confidence range, never a made-up number:

headroom output-savings
# Reduction: 31.7%  (95% CI 27.7% … 35.7%)   [estimated]

Want a measured number instead of an estimate? Leave 10% of conversations unshaped as a control group: export HEADROOM_OUTPUT_HOLDOUT=0.1. The dashboard shows an Output Tokens Saved card next to input compression, labelled measured or estimated with the confidence band.

→ Full write-up incl. the measurement methodology: docs/proposals/output-token-reduction.md

Star History Chart

Agent compatibility matrix

Agent headroom wrap Notes
Claude Code --memory · --code-graph · --1m
Codex shares memory with Claude
Cursor Manual setup starts proxy and prints base URLs for Cursor settings
Aider starts proxy + launches
Copilot CLI starts proxy + launches
OpenClaw installs as ContextEngine plugin
OpenCode injects config · starts proxy + launches
Cortex Code 6065% savings · library mode

Any OpenAI-compatible client works via headroom proxy. MCP-native: headroom mcp install.

GitHub Copilot CLI subscription mode

Headroom can route GitHub Copilot CLI subscription traffic through the local proxy:

headroom copilot-auth login
headroom wrap copilot --subscription -- --model gpt-4o

This lets Headroom intercept OpenAI-compatible Copilot CLI requests and apply the same proxy compression pipeline before forwarding to GitHub Copilot's hosted API. The wrapper exchanges Headroom's reusable GitHub OAuth token for Copilot's short-lived API token and prints the upstream endpoint as COPILOT_PROVIDER_API_URL=... during launch.

headroom copilot-auth login stores a Headroom-specific Copilot OAuth token. This avoids relying on generic GitHub or Copilot CLI tokens that can read Copilot account metadata but may still be rejected by Copilot's token-exchange endpoint.

For GitHub Enterprise Server or custom-domain Copilot deployments, set the deployment domain before launching:

export GITHUB_COPILOT_ENTERPRISE_DOMAIN=ghe.example.com

For GitHub.com Enterprise Cloud URLs such as github.com/enterprises/your-enterprise, do not set an enterprise-domain override. Headroom uses GitHub's normal token-exchange endpoint and the Copilot API endpoint advertised for the signed-in account.

Platform support note: macOS auth reuse via Copilot CLI Keychain storage has been smoke-tested. Windows Credential Manager, Linux Secret Service / secret-tool, and Docker/CI token-injection paths are implemented or planned as auth-discovery paths, but still need real OS validation before they should be considered fully vetted. For Docker and CI, prefer passing an explicit GITHUB_COPILOT_TOKEN or GITHUB_COPILOT_GITHUB_TOKEN rather than relying on host keychain access.

When to use · When to skip

Great fit if you…

  • run AI coding agents daily and want savings without changing your code
  • work across multiple agents and want shared memory
  • need reversible compression — originals are retrievable via CCR within the configured TTL

Skip it if you…

  • only use a single provider's native compaction and don't need cross-agent memory
  • work in a sandboxed environment where local processes can't run
Integrations — drop Headroom into any stack
Your setup Hook in with
Any Python app compress(messages, model=…)
Any TypeScript app await compress(messages, { model })
Anthropic / OpenAI SDK withHeadroom(new Anthropic()) · withHeadroom(new OpenAI())
Vercel AI SDK wrapLanguageModel({ model, middleware: headroomMiddleware() })
LiteLLM litellm.callbacks = [HeadroomCallback()]
LangChain HeadroomChatModel(your_llm)
Agno HeadroomAgnoModel(your_model)
Strands Strands guide
ASGI apps app.add_middleware(CompressionMiddleware)
Multi-agent SharedContext().put / .get
MCP clients headroom mcp install
What's inside
  • SmartCrusher — universal JSON: arrays of dicts, nested objects, mixed types.
  • CodeCompressor — AST-aware for Python, JS, Go, Rust, Java, C++.
  • Kompress-base — our HuggingFace model, trained on agentic traces.
  • Image compression — 4090% reduction via trained ML router.
  • CacheAligner — stabilizes prefixes so Anthropic/OpenAI KV caches actually hit.
  • IntelligentContext — score-based context fitting with learned importance.
  • CCR — reversible compression; LLM retrieves originals on demand.
  • Cross-agent memory — shared store, agent provenance, auto-dedup.
  • SharedContext — compressed context passing across multi-agent workflows.
  • headroom learn — plugin-based failure mining for Claude, Codex, Gemini.
Pipeline internals

Headroom exposes one stable request lifecycle across compress(), the SDK, and the proxy:

SetupPre-StartPost-StartInput ReceivedInput CachedInput RoutedInput CompressedInput RememberedPre-SendPost-SendResponse Received

  • Transforms do the work: CacheAligner, ContentRouter, SmartCrusher, CodeCompressor, Kompress-base, IntelligentContext / RollingWindow.
  • Pipeline extensions observe or customize lifecycle stages via on_pipeline_event(...).
  • Compression hooks sit alongside the canonical lifecycle as an additional extension seam.
  • Proxy extensions remain the server/app integration seam for ASGI middleware, routes, and startup policy.

Provider and tool-specific behavior lives under headroom/providers/ so core orchestration stays focused on lifecycle, sequencing, and policy.

  • CLI/tool slices: headroom/providers/claude, copilot, codex, openclaw
  • Provider runtime slices: headroom/providers/claude, gemini, plus shared backend/runtime dispatch in headroom/providers/registry.py
  • Core files stay orchestration-first: wrap.py, client.py, cli/proxy.py, and proxy/server.py delegate provider-specific env shaping, API target normalization, backend selection, and transport dispatch.

Install

pip install "headroom-ai[all]"          # Python, everything
npm install headroom-ai                 # TypeScript / Node
docker pull ghcr.io/chopratejas/headroom:latest

Granular extras: [proxy], [mcp], [ml] (Kompress-base), [code], [memory], [relevance], [image], [agno], [langchain], [evals], [pytorch-mps] (Apple-GPU memory-embedder offload — set HEADROOM_EMBEDDER_RUNTIME=pytorch_mps). Requires Python 3.10+.

Using pipx? Choose a supported interpreter explicitly:

pipx install --python python3.13 "headroom-ai[all]"

Pick 3.13 if you want dollar savings. The dashboard's Proxy $ Saved tile prices compression with LiteLLM, and LiteLLM can't be installed on Python 3.14+. On 3.14 token savings still track, but the dollar figure stays $0.00. If you already installed on 3.14, switch with pipx reinstall headroom-ai --python python3.13 and restart the proxy.

Installation guide — Docker tags, persistent service, PowerShell, devcontainers.

Updating

headroom update          # detects pip / pipx / uv tool and upgrades in place
headroom update --check  # report the latest release without upgrading
headroom update --pre    # include pre-releases

headroom update figures out how Headroom was installed (pip/venv, pip --user, pipx, uv tool) and runs the matching upgrade across macOS, Linux, and Windows. For git checkouts, editable installs, Docker images, and externally-managed system Pythons (PEP 668) it prints the correct manual step instead of guessing.

The proxy also shows a one-line "update available" notice on startup. It checks PyPI at most once a day, in the background, and never blocks. Opt out with HEADROOM_UPDATE_CHECK=off (also skipped in --stateless mode and CI).

Corporate / SSL-inspection environments

If pip install "headroom-ai[all]" fails with CERTIFICATE_VERIFY_FAILED (unable to get local issuer certificate), your network uses SSL inspection — a MITM proxy presenting a company-issued CA. The build backend (maturin) downloads rustup over a connection your TLS stack doesn't trust. Install Rust first so the build doesn't fetch it:

# macOS / Linux
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh && rustup default stable
# Windows
winget install Rustlang.Rustup && rustup default stable

Restart your shell, then pip install "headroom-ai[all]". A prebuilt wheel avoids the Rust build entirely where available: pip install --only-binary headroom-ai headroom-ai. Prebuilt wheels are published for Windows (win_amd64), Linux (x86_64 / aarch64), and macOS (Apple Silicon), so installs on those platforms never need a local Rust toolchain — the Rust-first dance above is only for the platform-independent sdist fallback (e.g. Intel macOS).

Two runtime assets are fetched over TLS; if they are blocked, trust your corporate CA via REQUESTS_CA_BUNDLE / SSL_CERT_FILE / CURL_CA_BUNDLE:

  • cdn.pyke.io — the ONNX Runtime for the Rust core. Alternatively pre-provide it with ORT_STRATEGY=system and ORT_LIB_LOCATION=/path/to/onnxruntime.
  • huggingface.co — the kompress-base compression model. Pre-download it and run with HF_HUB_OFFLINE=1, or set HF_ENDPOINT to a trusted mirror.

Running with compression disabled (pure gateway) requires neither asset.

"Basic Constraints of CA cert not marked critical" (Python 3.13+ strict mode)

A different failure from the one above. If TLS fails with:

[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
Basic Constraints of CA cert not marked critical

then the corporate CA is found and trusted — adding it to a CA bundle changes nothing. Python 3.13 + OpenSSL 3.x enable VERIFY_X509_STRICT by default, which enforces RFC 5280 §4.2.1.9: a CA cert's basicConstraints must be marked critical. Inspection roots like Zscaler set CA:TRUE without the critical bit, so the chain is rejected.

Set HEADROOM_TLS_STRICT=0 to clear only the strict flag from every TLS context Headroom controls — the proxy's httpx upstream client and the urllib3/huggingface_hub path used for model downloads. Chain validation, signature, expiry, and hostname checks all stay on; this is strictly narrower than disabling verification.

HEADROOM_TLS_STRICT=0 headroom proxy --port 8787

The Rust core's ONNX download (cdn.pyke.io) uses a separate TLS stack (rustls / OS trust store), unaffected by HEADROOM_TLS_STRICT. On Windows the corporate root must be in the machine certificate store (browsers already trust it there); or pre-provision ONNX Runtime with ORT_STRATEGY=system + ORT_LIB_LOCATION=/path/to/onnxruntime to skip the download entirely.

headroom learn

headroom learn in action

headroom learn — mines failed sessions, writes corrections to CLAUDE.md / AGENTS.md / GEMINI.md.

Documentation

Start here Go deeper
Quickstart Architecture
Proxy How compression works
MCP tools CCR — reversible compression
Memory Cache optimization
Failure learning Benchmarks
Configuration Limitations

Compared to

Headroom runs locally, covers every content type, works with every major framework, and is reversible.

Scope Deploy Local Reversible
Headroom All context — tools, RAG, logs, files, history Proxy · library · middleware · MCP Yes Yes
RTK CLI command outputs CLI wrapper Yes No
lean-ctx CLI commands, MCP tools, editor rules CLI wrapper · MCP Yes No
Compresr, Token Co. Text sent to their API Hosted API call No No
OpenAI Compaction Conversation history Provider-native No No

Attribution. Headroom ships with the excellent RTK binary for shell-output rewriting — git show --short, scoped ls, summarized installers. Huge thanks to the RTK team; their tool is a first-class part of our stack, and Headroom compresses everything downstream of it. Headroom can also use lean-ctx as the selected CLI context tool; set HEADROOM_CONTEXT_TOOL=lean-ctx before running headroom wrap ....

Contributing

git clone https://github.com/chopratejas/headroom.git && cd headroom
uv sync --extra dev && uv run pytest

Devcontainers in .devcontainer/ (default + memory-stack with Qdrant & Neo4j). See CONTRIBUTING.md.

Community

License

Apache 2.0 — see LICENSE.