Context compression
Switch from Headroom to Caveman: step by step guide
Move a coding agent or a Vercel AI SDK app from Headroom to Caveman: free port 8787, swap the wrapper or adapter, check recovery, compare, and roll back.
- 01Note your setup
- 02Free the port
- 03Swap wrapper or adapter
- 04Check or roll back
Switching from Headroom to Caveman is mostly a launcher swap for coding agents and a one-line adapter swap for apps. The part that needs care is state: Headroom's retrieval handles point into Headroom's store, and Caveman cannot resolve them. Always start fresh sessions on Caveman and let running Headroom sessions finish first.
Read the Headroom comparison first if you are still deciding. Headroom is a strong tool, and for some setups it is the better one.
Step 1: Write down what Headroom does today
Note which Headroom mode you run:
headroom wrap <agent>around a coding agentheadroom proxywith a client pointed at it- a framework hook in your code, like the Vercel AI SDK middleware or the LiteLLM callback
- the MCP server
Record headroom --version and any settings you changed, such as HEADROOM_CCR_TTL_SECONDS. If you used Headroom's memory, headroom learn or output shaping, note those too. Caveman does not replace them, so decide if you still need them.
Step 2: Free port 8787
Both tools default to 127.0.0.1:8787. Stop Headroom's proxy for the agent you are moving, or move it to another port if other sessions still use it:
headroom proxy --port 8788If you wrapped Claude Code durably, undo that first so plain claude stops routing through Headroom:
headroom unwrap claudeLeave Headroom's ccr_store.db alone until every old session is done.
Step 3a: Move a coding agent
Install the Caveman CLI and its binaries. It needs Node.js 22.13 or newer and downloads about 205 MB of signed Go binaries into ~/.caveman/bin/:
npm install -g @caveman-ai/cli
caveman setup --installFor a trial, use the ephemeral form. It runs one session through the local proxy and writes nothing to your config:
caveman wrap claudeWhen you are ready to keep it, caveman claude installs the integration so later claude sessions route through Caveman. Both forms run in compress mode and register caveman_retrieve through Caveman's MCP server. Codex, Gemini CLI, opencode, Aider and five more agents have profiles; the wrap reference shows how recovery installs on each.
Do not nest one wrapper inside the other. Two tools rewriting the same tool results and injecting two retrieval tools gives you a result you cannot read.
Step 3b: Move an app on the Vercel AI SDK
Start the runtime, which is the same local proxy:
npm install @caveman-ai/middleware @caveman-ai/sdk ai
CAVEMAN_MODE=compress caveman startThen replace Headroom's language model middleware with Caveman's adapter:
-const model = wrapLanguageModel({ model: baseModel, middleware: headroomMiddleware() });
-const result = streamText({ model, messages, tools });
+import { stepCountIs } from "ai";
+import { createMiddlewareRuntime } from "@caveman-ai/sdk/middleware";
+import { withCaveman } from "@caveman-ai/middleware/ai-sdk";
+
+const mode = process.env.CAVEMAN_CLIENT_MODE ?? "compress"; // "record" measures only, "off" disables
+const runtime = createMiddlewareRuntime({ endpoint: "http://127.0.0.1:8787", mode });
+const scope = { namespace: "support", session_id: conversationId, branch_id: "main", cache_epoch: "0" };
+if (mode !== "off") await runtime.ready(); // throws if the runtime is unreachable
+const result = streamText(withCaveman(
+ { model: baseModel, messages, tools, stopWhen: stepCountIs(8) }, { runtime, scope }));stopWhen matters: without a multi-step limit the call ends after the first tool step, so the model never gets to run caveman_retrieve and read the result. scope names one conversation, and a handle only resolves under the scope that created it. Keep session_id stable across turns and never share it between users. Python apps follow the same shape with adapters for LangChain, LiteLLM, Agno, Strands, ASGI and more; see Python middleware.
Step 4: Check that it worked
Confirm the runtime is in the right mode:
curl -s http://127.0.0.1:8787/caveman/v1/middleware/capabilities | jq '{mode, persistent, recovery}'Plain caveman start runs record, which measures and changes nothing. You want compress.
In an app, pass onReport to the runtime and watch the statuses. applied means a tool result was shortened, reused means a previous turn's choice was kept to protect the prompt cache, and skipped carries a reason such as not_smaller or runtime_unavailable.
Then test recovery for real. Give the agent a large tool result, like a long log or a big JSON export, and ask a follow-up that needs one detail the short view left out. You should see a caveman_retrieve call followed by the right answer. For coding agents, caveman stats summarises what the local layer did.
Step 5: Compare on your own tasks
Run three arms on copies of the same tasks: direct, Headroom, Caveman. Use fresh sessions, the same model and the same instructions. Grade the final answers first and count provider input tokens second, including recovery calls and the follow-up requests they trigger. Note whether the Caveman output skill was on, since caveman wrap can include it. The cost measurement guide has a reporting format.
Local token counts on both sides are estimates. The provider's usage report is the number that counts.
Roll back
For a trial with caveman wrap claude, there is nothing to undo. Run headroom wrap claude again.
If you ran caveman claude, run caveman disable claude to restore each agent's config, then re-wrap with Headroom. In an app, go back to the old streamText call. withCaveman returns a new options object and mutates nothing, so deleting it restores the previous behaviour exactly. Setting CAVEMAN_CLIENT_MODE=off also stops compression without a code change.
Keep Caveman's runtime up until conversations that hold Caveman handles are finished. Handles from one tool never work in the other.
Related
Try it on work you know
Keep your agent. Add Caveman.
Install the local tools, run a task you can check, and compare the result.
Open the quickstart Explore comparisons ↗