_ensure_proxy() now queries the running proxy's /health config to
detect feature mismatches. When --memory, --learn, or --code-graph
is requested but not enabled on the running proxy, it merges flags
and restarts the proxy with the upgraded configuration.
- Expose memory, learn, code_graph, pid in /health config response
- Add _query_proxy_config() and _kill_proxy_by_pid() helpers
- Refactor _detect_running_proxy_backend() to use _query_proxy_config()
Thread the proxy's --memory flag through _start_proxy, _ensure_proxy,
and _launch_tool so that `headroom wrap <tool> --memory` enables
persistent cross-session memory for claude, copilot, codex, aider,
and cursor.
Implements compress_batch() for issue #151. Compresses N texts with
batched forward passes on GPU and falls back to sequential compress()
on CPU where batching doesn't help.
Measured performance (RTX 3080 Ti, 1000-word / ~6K-char inputs):
GPU (PyTorch + CUDA):
N=1: 2.68x speedup (multi-chunk text batches within single call)
N=5: 2.75x speedup
N=12: 2.49x speedup
CPU (ONNX): fallback to sequential — parity with compress() in loop
ONNX Runtime's CPU execution provider does not parallelize across the
batch dimension for this model architecture; verified across default,
physical-cores-only, and single-thread configurations. The fallback
keeps the API useful while that limitation exists.
Features:
- Per-item target_ratio: scalar applies to all, list allows per-text
- Input order preserved in output
- Passthrough parity with compress() on short texts / errors
- Configurable batch_size (default 32)
Tests: 8 new (TestKompressCompressorBatch), 21 total pass.
Closes#151
The global QuotaTrackerRegistry singleton persists across the process lifetime.
When tests spin up multiple proxy app instances in the same process, each call
to startup() tries to register the same tracker keys (subscription_window,
codex_rate_limits, copilot_quota) into the same registry, triggering:
ValueError: A tracker with key 'subscription_window' is already registered.
Fix: call reset_quota_registry() at the start of startup() so each server
instance begins with a clean registry, then re-registers and re-starts all
trackers fresh. The tracker singletons themselves (configure_subscription_tracker,
get_codex_rate_limit_state, get_copilot_quota_tracker) remain alive and their
state is preserved across restarts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three fixes for Docker-native install with --memory:
1. Add sqlite-vec to proxy extras — memory's vector index now installs
with pip install headroom-ai[proxy]. No separate pip install needed.
2. Fix AUTO vector backend fallback — was: SQLITE_VEC → HNSW → crash.
Now: SQLITE_VEC → HNSW → clear error message listing install options.
3. Fix ONNX pthread_setaffinity_np error in Docker containers — set
intra/inter thread count to 1 in SessionOptions. Prevents the
"Invalid argument" error on containers with limited CPU affinity.
Passively capture x-codex-* response headers from proxied Codex API
calls and surface them in /stats and the dashboard.
Unlike Anthropic subscription window tracking (which polls a dedicated
OAuth endpoint), Codex embeds rate-limit data directly in every API
response header — no polling, no new credentials needed.
Changes:
- headroom/subscription/codex_rate_limits.py: new module with
CodexRateLimitWindow, CodexCreditsSnapshot, CodexRateLimitSnapshot
data models and a thread-safe CodexRateLimitState singleton;
parse_codex_rate_limits() parses x-codex-primary/secondary-used-percent,
window-minutes, reset-at, credits, limit-name, and promo-message headers
- headroom/subscription/__init__.py: re-export new public symbols
- headroom/proxy/handlers/openai.py: call
get_codex_rate_limit_state().update_from_headers() after each
proxied /v1/chat/completions and /v1/responses response
- headroom/proxy/server.py: add codex_rate_limits key to /stats
via _get_codex_rate_limit_stats() helper
- headroom/dashboard/templates/dashboard.html: new OpenAI Codex
Rate-Limit Window panel (primary + secondary progress bars, credits
balance, limit name, reset countdown); hidden when no data
- tests/test_codex_rate_limits.py: 25 unit tests covering parsing,
window labels, reset time, credits, state updates
- docs/screenshots/subscription_window_active.png: updated screenshot
showing both Anthropic and Codex panels in the real dashboard
Header schema (from codex-rs/codex-api/src/rate_limits.rs):
x-codex-primary-used-percent / x-codex-primary-window-minutes /
x-codex-primary-reset-at (and secondary- variants)
x-codex-credits-has-credits / -unlimited / -balance
x-codex-limit-name / x-codex-promo-message
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Active state shows the full headroom dashboard with the Anthropic
Subscription Window panel integrated in-line after Savings Breakdown,
including 5h/7d utilisation bars, overage credit bar, Headroom
contribution grid, and anomaly detection alert.
Inactive state shows the standard dashboard without the panel —
it is conditionally rendered only when subscription_window data
is present in /stats.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
No manual install needed. headroom wrap claude --code-graph:
1. Checks if binary exists (PATH or ~/.local/bin/)
2. Downloads from GitHub releases if not found (~15MB, one-time)
3. Indexes project + starts file watcher
Same pattern as rtk auto-download. Platform-aware:
darwin-arm64, darwin-amd64, linux-arm64, linux-amd64, windows-amd64.
Also: add watchdog>=4.0.0 to proxy extras.
Single flag: --code-graph does everything:
1. Indexes project via codebase-memory-mcp at startup
2. Starts background file watcher (watchdog) in the proxy
3. On source file changes: debounced incremental reindex (~0.4s)
4. Cross-platform: FSEvents (macOS), inotify (Linux), Windows
The watcher is agent-agnostic — watches the filesystem, not agent
hooks. Works for Claude Code, Codex, Gemini, Cursor, Aider, anything.
Removed --code-aware flag (broken AST compressor). One flag for code
intelligence, not two confusing options.
New module: headroom/graph/watcher.py (CodeGraphWatcher)
- Debounce: waits 2s after last change before reindexing
- Filters: only source file extensions, ignores .git/node_modules/etc.
- Stats: tracks reindex count, integrates with proxy lifecycle
Make Unix-only file locking imports type-safe on Windows, tighten beacon lock cleanup, and fix the remaining exposed typing issues so the repository's mypy check passes cleanly on Windows again.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add focused regression coverage for install, runtime, provider, state, health, supervisor, and persistent wrap flows so the new persistent deployment surfaces are exercised more thoroughly in CI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align Docker-native wrapper help and runtime behavior with the Python install contract, including persistent deployment metadata, baked install-image defaults, and explicit unsupported wrap targets.
Harden the Python persistent-install path with profile validation, safer provider-scope handling, Windows environment restoration, runtime parity improvements, and rollback-safe apply/update behavior.
Update README, Docker install docs, CI, and focused regressions to cover the Windows BOM failure, wrapper parity, compose coverage, and Docker-native wrap behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tighten Docker-native bash and PowerShell wrapper validation for wrap and proxy flows, pin the bash wrapper to the install-time interpreter, clean up failed persistent container starts, and extend docs, CI, e2e, and native installer coverage for persistent Docker installs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change codebase-memory-mcp from default to opt-in:
- --no-code-graph → --code-graph (explicit enable)
- No regression risk: code graph only activates when user wants it
- With Claude Code's MCP Tool Search, tool tax is ~200 tokens
when enabled (lazy-loaded, not all 14 tools in every prompt)
Update README wrap examples to show --code-graph flag.
Code graph (codebase-memory-mcp):
- Auto-index project at wrap time via _setup_code_graph()
- --no-code-graph flag on wrap claude/codex/aider to skip
- Silently skips if binary not installed (not a hard dependency)
- With Claude Code's MCP Tool Search, 14 graph tools add ~200
tokens/request (lazy-loaded), not the full ~1,915
CodeCompressor disabled:
- code_aware_enabled defaults to False in ProxyConfig
- enable_code_aware + prefer_code_aware_for_code default False
in ContentRouterConfig
- Code passes through uncompressed (LLMs handle code well)
- Headroom focuses on compressing non-code content (JSON, logs,
tool outputs) where the real waste is