One installer for rtk + bili (billion-context) + headroom + caveman in front of Claude Code, Codex, OpenCode and DeepSeek Harness. macOS, Linux (systemd) and Windows.
Every request from those agents is routed through a local proxy chain that removes tokens before they ever reach a provider — shell output, tool results and tool schemas — while keeping the prompt prefix stable so the provider's own cache keeps hitting.
agent → rtk → bili → headroom → provider
│ │ │
│ │ └─ per-request compression (tool results / schemas)
│ └─ session context folding, prefix-cache aware
└─ shrinks shell output inside the agent (hooks / rules / skills)
# macOS
./install.sh
# or
curl -fsSL https://raw.githubusercontent.com/flywalk4/agent-stack/main/install.sh | bash# Debian / Ubuntu — same script, apt path (node, uv and git are installed for you)
curl -fsSL https://raw.githubusercontent.com/flywalk4/agent-stack/main/install.sh | bash
# unattended (no prompts, every detected agent + layer)
curl -fsSL https://raw.githubusercontent.com/flywalk4/agent-stack/main/install.sh | bash -s -- --yes# Windows (PowerShell 5.1+)
.\install.ps1
# or
irm https://raw.githubusercontent.com/flywalk4/agent-stack/main/install.ps1 | iexThe bootstrap installs node / uv (brew on macOS, apt on Debian/Ubuntu, winget on Windows), then runs the interactive installer:
- Install / update or Uninstall — asked first, so a fresh checkout can also be used to tear down.
- Which agents to wire up — space selects, enter continues.
- Which layers to install — same keys.
- A plan summary, then a confirmation.
Non-interactive: agent-stack install --yes takes every detected agent and every layer.
Interactive prompts need a TTY; without one the installer exits with a hint instead of hanging.
| Command | What it does |
|---|---|
agent-stack install [--yes] |
Interactive setup (also what runs with no command). Asks install or uninstall. |
agent-stack doctor [--json] |
Services, agent configs and end-to-end chain probes. Exit code 1 if anything is down. |
agent-stack dashboard |
Opens the shared dashboard at http://127.0.0.1:18800. |
agent-stack uninstall |
Removes the services and restores every changed config from the backups. |
| Agent | Chain |
|---|---|
| Claude Code | rtk → bili :18788 → headroom :8787 → api.anthropic.com |
| Codex | rtk → bili :18788 → headroom :8787 → chatgpt.com / api.openai.com (HTTP, no WS) |
| OpenCode | rtk → bili-native (in-process) → headroom transport plugin → :8787 → provider |
| DeepSeek Harness | rtk → bili-native (dsh bundle) → headroom :8788 → api.deepseek.com |
Why it is wired this way:
- bili before headroom. bili keeps a stable prefix (prompt cache); headroom runs in
cachemode, compresses only the delta and never busts that prefix. - rtk and caveman live inside the agent (hooks, rules, skills), they are not network hops.
caveman's own launcher/proxy (
caveman claude) is deliberately not installed — it would add a fourth network hop. - OpenCode is wired with a directory, not a file. OpenCode V2 silently drops a configured
.jspath (configured plugin path must be a directory) and resolves a plugin directory through its ownpackage.json. agent-stack therefore generates~/.config/opencode/plugins/agent-stack-headroom/— a wrapper around the transport plugin headroom ships inside its uv tool — and points the config at that directory, which the V1 line loads just as well. Legacy…/plugins/<name>/<file>.jsentries are rewritten to their parent directory, and the entry is written back into whichever key the config already uses (pluginson V2-line configs,pluginotherwise). - Both plugins are dual-API (V1
server()+ V2setup()). Upstream ships headroom's transport and caveman's plugin for the V1 hook API only, so agent-stack adapts both: the generated headroom wrapper default-exports{ id, server, setup }, and caveman's installedplugin.jsis patched to the same shape. On the V1 line the plugin object is loaded through more than one lane (1.18.29+ callsserver(), its native lane callssetup()), so the wrapper installs the fetch transport exactly once per process and releases it once, and every V2 registration is guarded by atypeof ctx.x?.y === 'function'check so a build without that seam stays inert instead of crashing.agent-stack doctorreports both (caveman: plugin runs on both plugin APIs,headroom: transport wrapper runs on both plugin APIs); a caveman self-update overwritesplugin.js, and the nextagent-stack installre-applies the patch. - Codex without WebSocket. On the WS transport bili and headroom both rewrite
previous_response_id, and Codex fails withprevious_response_not_found. - A separate headroom for DeepSeek (
--openai-api-url https://api.deepseek.com--anthropic-api-url https://api.deepseek.com/anthropic) so Codex's OpenAI traffic never lands on DeepSeek. Both upstreams are needed: the harness speaks Anthropic Messages, and without the Anthropic URL headroom would forward DeepSeek keys and payloads to api.anthropic.com. - DeepSeek Harness is patched, not reconfigured. Its profile
(
~/.dsh/profiles/<name>/cordis.patch.yml) gets one entry per mounted DeepSeek route —llm-deepseek(baseURL, the api-key /deepseek-officialroute) andllm-deepseek-account(inferenceOrigin, the platform-account route) — both pointed at headroom:8788. Neither endpoint is in the profile's settings storage, so this patch layer is the only place that can redirect the route. The patch is written idempotently (the olddeepseek-accountentry and any stray markers are scrubbed) and the harness picks it up without a restart.
- macOS — launchd user agents
dev.agent-stack.*in~/Library/LaunchAgents, logs in~/Library/Logs/agent-stack/. Each plist carries an explicitPATH(/opt/homebrew/bin,/usr/local/bin,~/.local/bin,~/.cargo/bin, …) so jobs started by launchd can still find brew/cargo/uv binaries such asrtk. - Linux — systemd user units
dev-agent-stack-<name>.servicein~/.config/systemd/user/, enabled withsystemctl --user enable --now, logs appended to~/.agent-stack/logs/<name>.log(and mirrored into the journal,SyslogIdentifier=agent-stack-<name>). The unit sets the same explicitPATHplus each service's own environment (e.g. the DeepSeek proxy's separateHEADROOM_SAVINGS_PATH), restarts on failure, and stops withSIGTERM/20 s so bili can flush its sessions. On a server, enable lingering once so the units survive logout and start at boot:When the installer runs as root (a container or a root shell without a user manager) it writes system-wide units tosudo loginctl enable-linger "$USER" # then: systemctl --user status dev-agent-stack-bili journalctl --user -u dev-agent-stack-headroom -f
/etc/systemd/system/instead.uninstallremoves whichever scope it finds. - Windows — Task Scheduler tasks
agent-stack-*, started at logon, hidden window, auto-restart. Logs in%USERPROFILE%\.agent-stack\logs.
Hand-made bili / headroom services that would fight for the ports (launchd agents or
systemd units, user and system scope) are detected and renamed to
*.disabled-by-agent-stack during install.
launchctl bootstrap can report Bootstrap failed: 5: Input/output error even though the
job loads: launchd tears the old job down asynchronously and it stays visible to
launchctl print for a moment. The installer therefore waits for the label to really
disappear before bootstrapping, retries the pair if needed, and confirms the job is still
loaded before reporting success — a bare "is it loaded?" check would mistake the dying old
job for the new one and silently leave the service down.
| Symptom | Fix |
|---|---|
systemd user manager is not reachable |
sudo loginctl enable-linger "$USER", log in again, re-run agent-stack install |
| Services disappear after logout | lingering is off: loginctl show-user "$USER" --property=Linger |
| Container without systemd | run the installer as root — it falls back to system units in /etc/systemd/system |
| A service does not come up | systemctl --user status dev-agent-stack-bili, journalctl --user -u dev-agent-stack-bili -n 50 |
npm i -g billion-context needs a compiler |
sudo apt-get install -y build-essential python3 |
rtk: command not found inside a service |
the unit's PATH includes ~/.agent-stack/bin; check agent-stack doctor reports the rtk path it uses |
http://127.0.0.1:18800 — served by the dashboard service (dev.agent-stack.dashboard on
macOS, dev-agent-stack-dashboard under systemd).
- Saved tokens per layer: rtk (shell output), bili (prefix served from the provider's
cache), headroom (compression + deferred tool schemas), plus the combined total and the
USD figure.
- rtk and headroom save tokens by never sending them. headroom's tool-schema share is shown as a nested row, because the ledger stores its two layers so that they sum to the total rather than stacking on top of it.
- bili saves them by keeping the prefix stable so the provider's prompt cache keeps hitting; those tokens are billed at roughly a tenth of the normal input price. The total adds both, and breaks them down underneath.
- headroom's numbers come from its durable savings ledger
(
~/.headroom/savings_events.jsonl, the fileheadroom savingsaggregates), not from the/statscounters: those live in~/.headroom/proxy_savings.json, are rewritten wholesale by whichever proxy saves last, and freeze while requests keep flowing. Each proxy gets its ownHEADROOM_SAVINGS_PATH; the append-only ledger stays shared. - Agent chains with live hop health, service status and a one-click
e2eprobe that sends a bogus-key request through the whole chain (a provider-shaped401means the chain works and no tokens were spent). - Recent bili sessions.
The UI is English by default with an RU toggle in the header (localStorage, per browser).
Every config agent-stack touches is copied once to ~/.agent-stack/backups
(with a manifest.json) before the first change, so the backup always holds the state
from before agent-stack. agent-stack uninstall removes the services and puts those
files back. The tools themselves (rtk / bili / headroom) are left installed.
- macOS, Debian/Ubuntu (anything with a systemd user manager) or Windows
- Node.js ≥ 20, uv, git — installed by the bootstrap when missing
- brew (macOS) / apt (Debian, Ubuntu) / winget (Windows) for the dependencies
bin/agent-stack.js CLI entry point
install.sh bootstrap for macOS / Debian / Ubuntu (node + uv, then the installer)
install.ps1 bootstrap for Windows
src/install.js interactive installer and uninstaller
src/topology.js ports, service definitions and each agent's chain (single source of truth)
src/services.js launchd / systemd / Task Scheduler service management
src/status.js stats collection for the dashboard and doctor
src/doctor.js service, config and end-to-end checks
src/tools.js rtk / bili / headroom install and agent-side add-ons
src/caveman.js caveman rules, hooks and skills
src/targets/*.js per-agent config writers (claude, codex, opencode, dsh)
src/dashboard/ dashboard HTTP server and its single-page UI