# Table of Contents - [Quickstart · Caveman docs](#quickstart-caveman-docs) - [caveman explore · Caveman docs](#caveman-explore-caveman-docs) - [caveman learn · Caveman docs](#caveman-learn-caveman-docs) - [Skill · Caveman docs](#skill-caveman-docs) - [How numbers are counted · Caveman docs](#how-numbers-are-counted-caveman-docs) - [Architecture · Caveman docs](#architecture-caveman-docs) - [Agent profiles · Caveman docs](#agent-profiles-caveman-docs) - [Security · Caveman docs](#security-caveman-docs) - [The engine · Caveman docs](#the-engine-caveman-docs) - [Wrap an agent · Caveman docs](#wrap-an-agent-caveman-docs) - [Overview · Caveman docs](#overview-caveman-docs) - [Modes · Caveman docs](#modes-caveman-docs) - [Proxy · Caveman docs](#proxy-caveman-docs) - [Compressors · Caveman docs](#compressors-caveman-docs) - [Recoverable compression · Caveman docs](#recoverable-compression-caveman-docs) - [Token counting · Caveman docs](#token-counting-caveman-docs) - [Providers · Caveman docs](#providers-caveman-docs) - [Configure · Caveman docs](#configure-caveman-docs) - [Caveman documentation](#caveman-documentation) - [CLI · Caveman docs](#cli-caveman-docs) - [TypeScript SDK · Caveman docs](#typescript-sdk-caveman-docs) - [Python SDK · Caveman docs](#python-sdk-caveman-docs) - [caveman-shrink · Caveman docs](#caveman-shrink-caveman-docs) - [caveman-browse · Caveman docs](#caveman-browse-caveman-docs) - [MCP server · Caveman docs](#mcp-server-caveman-docs) - [Overview · Caveman docs](#overview-caveman-docs) - [Create an agent · Caveman docs](#create-an-agent-caveman-docs) - [Install · Caveman docs](#install-caveman-docs) - [Define an agent · Caveman docs](#define-an-agent-caveman-docs) - [Tools · Caveman docs](#tools-caveman-docs) - [Memory · Caveman docs](#memory-caveman-docs) - [Run a turn · Caveman docs](#run-a-turn-caveman-docs) - [Sandbox · Caveman docs](#sandbox-caveman-docs) - [Memory · Caveman docs](#memory-caveman-docs) - [Compaction · Caveman docs](#compaction-caveman-docs) - [Connect · Caveman docs](#connect-caveman-docs) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Programmatic tools · Caveman docs](#programmatic-tools-caveman-docs) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Recall and offload · Caveman docs](#recall-and-offload-caveman-docs) - [Evals and builds · Caveman docs](#evals-and-builds-caveman-docs) - [Adapters · Caveman docs](#adapters-caveman-docs) - [Unknown](#unknown) - [Unknown](#unknown) - [Unknown](#unknown) - [Licensing · Caveman docs](#licensing-caveman-docs) - [Cloud · Caveman docs](#cloud-caveman-docs) - [Eval graders · Caveman docs](#eval-graders-caveman-docs) - [Model router · Caveman docs](#model-router-caveman-docs) - [Telemetry · Caveman docs](#telemetry-caveman-docs) - [Provider catalog · Caveman docs](#provider-catalog-caveman-docs) --- # Quickstart · Caveman docs [Skip to content](https://docs.caveman.so/docs/quickstart#main) There are two products, and they are independent. The skill changes how your agent writes and takes about thirty seconds. The proxy, launched through the CLI, shrinks what the agent reads and gives you a local report on where those tokens actually go. Install the skill[#](https://docs.caveman.so/docs/quickstart#install-the-skill) -------------------------------------------------------------------------------- 1. 1 This works for most agents: terminal npx skills add JuliusBrussee/caveman Without `-g` this writes into `./.agents/skills` under the directory you are standing in, not into a global config. That is usually what you want for a project, and surprising if you expected otherwise. 2. 2 Some agents have a native path that wires up more than the skill file: terminal # Claude Codeclaude plugin marketplace add JuliusBrussee/cavemanclaude plugin install caveman@caveman# Gemini CLIgemini extensions install https://github.com/JuliusBrussee/caveman# Codex CLInpx skills add JuliusBrussee/caveman -a codex# Cursor, which needs the global flagnpx skills add JuliusBrussee/caveman -a cursor -g 3. 3 Check it took. In your agent, run: text /caveman You should get a confirmation that the mode is active. `/caveman off` turns it back off, and so does saying "normal mode". Install the local tools[#](https://docs.caveman.so/docs/quickstart#install-the-local-tools) -------------------------------------------------------------------------------------------- The CLI is published on npm under a scoped name. terminal npm i -g @caveman-ai/clicaveman setup --install `caveman setup --install` downloads the companion binaries into `~/.caveman/bin`. It checks a key-signed manifest first, then verifies each artefact against its own SHA-256 before installing it. Then start your agent through the CLI so traffic goes through the [local proxy](https://docs.caveman.so/docs/proxy) : terminal caveman claude The same works for `codex`, `gemini`, `aider`, `hermes`, `kilo`, `openclaw`, `opencode`, `pi`, and `qwen`. Ten wrap profiles ship today, and each is one JSON file in the registry. That set is smaller than the thirty-plus agents the skill supports, because wrap has to know the host's provider endpoint. Find out where your tokens go[#](https://docs.caveman.so/docs/quickstart#find-out-where-your-tokens-go) -------------------------------------------------------------------------------------------------------- terminal caveman learn This reads your local agent sessions and ranks what is actually consuming the budget. It measures and does nothing else. Applying a fix is a separate, consent-gated step: terminal caveman learn report --jsoncaveman learn apply --dry-runcaveman learn apply Every applied fix is checked for being net token negative. If an edit does not make the thing smaller, it is reverted. What you should expect to see[#](https://docs.caveman.so/docs/quickstart#what-you-should-expect-to-see) -------------------------------------------------------------------------------------------------------- On the committed benchmark suite, the skill cuts output tokens by about 65 percent on average, with a range from 22 to 87 percent across ten prompts. That is a per-run inferred estimate measured on that suite, not a promise about your bill. Nothing you run locally will ever report a dollar figure or a monthly total. That is deliberate and [the reasoning is here](https://docs.caveman.so/docs/counting) . --- # caveman explore · Caveman docs [Skip to content](https://docs.caveman.so/docs/skill/explore#main) `caveman-explore` is a read-only repository scout for Claude Code. It answers one localization question with small `path:line` citations, so the main agent can inspect the right code without carrying every exploratory read in its conversation. Host Claude Code only. Tools `Read, Glob, Grep` Model `haiku` Output `path/to/file.ext:START-END reason` Licence MIT. Install[#](https://docs.caveman.so/docs/skill/explore#install) --------------------------------------------------------------- terminal caveman explore install --agent claude The command is a compatibility alias for installing the `caveman-explore` skill. It writes the canonical `SKILL.md` unchanged and disables pixel conversion for this file. You can choose a destination explicitly: terminal caveman explore install --agent claude --dir ./path/to/caveman-explore When to use it[#](https://docs.caveman.so/docs/skill/explore#when-to-use-it) ----------------------------------------------------------------------------- Use the explorer when you need to locate code before you can solve the task: * A cold start in an unfamiliar repository * A question that spans several files or packages * A direct search that did not reveal the owning code * A request that names behaviour but no file or symbol Skip it when the task already names the exact file or symbol, or when a previous turn already returned usable line citations. The explorer is a localization step, not a second opinion on a known location. What it returns[#](https://docs.caveman.so/docs/skill/explore#what-it-returns) ------------------------------------------------------------------------------- The response contains citations only: text src/router/pick.go:42-71 route selection lives heresrc/router/pick_test.go:18-40 table test covering selection Every range must have been read. The range may not extend beyond the file. If no relevant code exists, the only valid answer is: text no relevant locations found This narrow output contract matters. The solving agent gets evidence it can open directly, without inheriting a summary, a proposed fix, or a large search transcript. How it searches[#](https://docs.caveman.so/docs/skill/explore#how-it-searches) ------------------------------------------------------------------------------- The explorer starts with several complementary reads in parallel: likely path patterns, symbol or string matches, and the most promising file contents. It follows the evidence for one or two short rounds, then stops as soon as it can name the locations. The fixed tool list enforces the boundary. It cannot run a command, edit a file, write a patch, or test a proposed solution. What it cannot prove[#](https://docs.caveman.so/docs/skill/explore#what-it-cannot-prove) ----------------------------------------------------------------------------------------- A citation proves that code exists at a location the explorer read. It does not prove that the code is correct, that a test passes, or that the cited path is the only implementation. The solving agent still owns diagnosis, changes, and verification. --- # caveman learn · Caveman docs [Skip to content](https://docs.caveman.so/docs/skill/learn#main) `caveman learn` reads local agent sessions and shows where tokens are being spent. The command measures and prepares candidates. The `caveman-learn` skill is the separate, consent-gated part that can edit a file. Input Local Claude Code, Codex, and Caveman session records. Default window The last 30 days. Report basis `inferred` Edits One approved change at a time. Protected class `load_bearing` Run the profiler[#](https://docs.caveman.so/docs/skill/learn#run-the-profiler) ------------------------------------------------------------------------------- terminal caveman learn In an interactive terminal this shows a local Setup Score, grouped top moves, and a menu for the full report or the editing flow. Stable output modes are available for scripts and pipes: terminal caveman learn --plaincaveman learn --allcaveman learn --jsoncaveman learn --mdcaveman learn --since 7d --sources codex,claude,caveman `--plain` has no animation or keyboard menu. `--all` includes every finding, its internal id, its basis, and its suggestion. JSON uses the `caveman.learn.v1` schema. If no matching sessions exist, the command returns no score. Repeated-context findings need the same block in three or more sessions, so a new installation may need several sessions before that class appears. Read the sink classes[#](https://docs.caveman.so/docs/skill/learn#read-the-sink-classes) ----------------------------------------------------------------------------------------- | Class | Meaning | Automatic edit | | --- | --- | --- | | `reducible` | A heavy instruction file or an installed skill that local evidence says can be smaller. | Candidate only. The skill still asks first. | | `recurring_context` | A block re-established across sessions that may fit local memory better. | Candidate only. Recall must work before source text is removed. | | `behavioral` | An observation about how the agent or user works. | None. Repetition does not prove the behaviour is wrong. | | `load_bearing` | Context that must remain available on every turn. | Never. It stays in the score so the score remains honest. | Rates such as `tokens_per_turn` and `tokens_per_day_rate` describe the observed local pattern. The daily field is a rate, not a historical total and not a monthly projection. Review fixes with an agent[#](https://docs.caveman.so/docs/skill/learn#review-fixes-with-an-agent) --------------------------------------------------------------------------------------------------- terminal caveman learn implementcaveman learn implement claudecaveman learn implement codex --prompt "focus on project instructions" This installs the safety guide when it is missing, opens Claude Code or Codex, and asks the agent to read the current JSON report. It does not grant permission to apply every finding. You can also install the guide directly: terminal caveman tools skills install caveman-learn --agent claudecaveman tools skills install caveman-learn --agent codex Preview one candidate[#](https://docs.caveman.so/docs/skill/learn#preview-one-candidate) ----------------------------------------------------------------------------------------- terminal caveman learn apply --dry-runcaveman learn apply Both commands prepare information for review. The second writes a candidate under `~/.caveman/candidates`; it does not edit your repository. Only the installed skill performs an edit, after showing the proposed change and asking for consent. For a reducible item, the skill compares inferred tokens per turn before and after. If the result is not smaller, it restores the original. For recurring context, the skill re-reads the source block, verifies its SHA-256 locator, stores the raw block in cavemem, and confirms that recall returns it. The source is trimmed only after the pointer and recall path both work. A failed recall removes the new memory and leaves the source in place. What it will not do[#](https://docs.caveman.so/docs/skill/learn#what-it-will-not-do) ------------------------------------------------------------------------------------- * Apply all fixes behind one confirmation * Edit a `load_bearing` finding * Treat a behavioral finding as an instruction * Remove recurring context without a working pointer and recall path * Keep an edit whose measured result is not net token negative * Attach currency, monthly savings, or `verified` to a local result Reports are also written under `~/.caveman/reports`, including HTML and JSON forms. They contain local analysis, so review them before sharing them outside your machine. --- # Skill · Caveman docs [Skip to content](https://docs.caveman.so/docs/skill#main) The skill is the cheapest layer in the stack because it is not software. It is a file your agent reads, and it changes how the agent writes for the rest of the session. What it is One Markdown instruction file, plus optional commands and subagent presets. Runtime None. No process, no port, no config file. Account Not required. Licence MIT. Install `npx skills add JuliusBrussee/caveman` Install[#](https://docs.caveman.so/docs/skill#install) ------------------------------------------------------- terminal npx skills add JuliusBrussee/caveman Without `-g` this writes into `./.agents/skills` under your current directory rather than a global location. Several agents have their own native path: terminal # Claude Codeclaude plugin marketplace add JuliusBrussee/cavemanclaude plugin install caveman@caveman# Gemini CLIgemini extensions install https://github.com/JuliusBrussee/caveman# Codex CLInpx skills add JuliusBrussee/caveman -a codex# Cursor, which needs -gnpx skills add JuliusBrussee/caveman -a cursor -g# opencodenpx -y github:JuliusBrussee/caveman -- --only opencode There is also a full installer that wires up hooks and a status line for Claude Code. It is pinned to a tag, it is safe to re-run, and it supports `--dry-run`. terminal curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/v2.3.1/install.sh | bash powershell irm https://raw.githubusercontent.com/JuliusBrussee/caveman/v2.3.1/install.ps1 | iex What it removes[#](https://docs.caveman.so/docs/skill#what-it-removes) ----------------------------------------------------------------------- Articles, filler, pleasantries, hedging and tool-call narration. Sentence fragments are allowed. Decorative tables and emoji are not. What it will not touch[#](https://docs.caveman.so/docs/skill#what-it-will-not-touch) ------------------------------------------------------------------------------------- This list is the reason the skill is safe to leave on. * Code blocks, verbatim * Function, method and API names * CLI commands and flags * Commit type keywords * Exact error strings * The language you are writing in It also refuses to invent abbreviations. `cfg`, `impl`, `req`, `res` and `fn` are banned, and so are arrow characters standing in for words. The reason is measurable rather than stylistic: the tokenizer splits those the same way it splits the full word, so the abbreviation saves nothing and costs clarity. When it steps aside[#](https://docs.caveman.so/docs/skill#when-it-steps-aside) ------------------------------------------------------------------------------- The skill drops out of terse mode on its own for: * Security warnings * Confirmations of anything irreversible * Ambiguous multi-step sequences * Any case where compressing the answer would create the ambiguity Levels[#](https://docs.caveman.so/docs/skill#levels) ----------------------------------------------------- text /caveman lite/caveman full # default/caveman ultra/caveman wenyan-lite/caveman wenyan-full/caveman wenyan-ultra/caveman off `off`, "stop caveman" and "normal mode" all do the same thing. What else comes with it[#](https://docs.caveman.so/docs/skill#what-else-comes-with-it) --------------------------------------------------------------------------------------- One install also brings a set of commands and work-pattern skills. Commands `/caveman-commit · /caveman-review · /caveman-compress · /caveman-stats · /caveman-help` Subagent presets cavecrew-investigator, cavecrew-builder, cavecrew-reviewer Work patterns investigate-first, lean-build, surgical-patch, safe-refactor, migration, verify-and-stop What it saves[#](https://docs.caveman.so/docs/skill#what-it-saves) ------------------------------------------------------------------- On the committed benchmark suite of ten prompts, output tokens drop by about 65 percent on average, with a range of 22 to 87 percent. That is a per-run inferred estimate measured on that suite. It is not a promise about your bill, and it covers output tokens only. The range matters more than the average here. A tightly scoped refactor prompt saved 22 percent. A long explanatory answer saved 87 percent. Which end you land on depends on how much of your traffic is prose. --- # How numbers are counted · Caveman docs [Skip to content](https://docs.caveman.so/docs/counting#main) Every number Caveman shows you carries a word that says how it was obtained. The words are not interchangeable and the code does not let them blur. Three words, kept apart[#](https://docs.caveman.so/docs/counting#three-words-kept-apart) ----------------------------------------------------------------------------------------- inferred A local estimate from bytes, token counters and local records. Every public offline tool emits this. measured Observed traffic. Real, but not proof that a dollar was saved. verified Reserved for hosted rollout systems that can compare against a bill. No tool on this site emits it. If you install anything documented here and it shows you a percentage, that percentage is `inferred`. There is no configuration that upgrades it. The four rules[#](https://docs.caveman.so/docs/counting#the-four-rules) ------------------------------------------------------------------------ ### 1\. No fake savings[#](https://docs.caveman.so/docs/counting#1-no-fake-savings) Headline figures in these docs are local examples or target ranges. They stay labelled `inferred`, and they are never multiplied out into a monthly saving. Where the code only supports a range, the docs show the range rather than picking a number from inside it. ### 2\. Byte safe[#](https://docs.caveman.so/docs/counting#2-byte-safe) `record` mode is pass-through. On a parse problem, an unsupported input, a missing recovery store, or an output that is not actually smaller, the engine keeps the original bytes. text record mode -> never transformstransform error -> forward the original bytesresult not smaller -> keep the original ### 3\. No placeholders, fail closed[#](https://docs.caveman.so/docs/counting#3-no-placeholders-fail-closed) No stub responses on shippable paths, and unknown cases resolve toward the conservative answer. text unknown engine mode -> record, which is pass-throughlow-confidence content -> textunknown grader -> passed: falseunknown route -> 404unknown model price -> zero, plus an "unpriced:" tag An unpriced model contributes zero to a cost figure and says so. It does not get a guessed price. ### 4\. Recoverable, so lossy stays honest[#](https://docs.caveman.so/docs/counting#4-recoverable-so-lossy-stays-honest) Compressors drop bytes from the model-visible payload. A lossy result is emitted only after the original has been stored under a content-addressed handle, and `retrieve(handle)` returns the original byte for byte. Per run, never per month[#](https://docs.caveman.so/docs/counting#per-run-never-per-month) ------------------------------------------------------------------------------------------- An inferred ratio is a property of one payload on one run. Projecting it forward assumes your next month looks like that payload, which nobody can know. So the engine reports a ratio and does not offer to annualise it. There is no setting for this. Tokens are not money[#](https://docs.caveman.so/docs/counting#tokens-are-not-money) ------------------------------------------------------------------------------------ On the local path, savings are counted in tokens and have no dollar field anywhere in the data structure. There is no token-to-dollar multiplication at any layer. This matters because token prices differ per model, per provider, per cache state and per contract. A local tool that has not seen your invoice cannot convert one into the other, so it does not try. What the counter actually is[#](https://docs.caveman.so/docs/counting#what-the-counter-actually-is) ---------------------------------------------------------------------------------------------------- The default token counter is a real BPE tokenizer using the `o200k_base` encoding, with the vocabulary compiled into the binary. It is deterministic, so the same bytes always produce the same count, and it is offline, so it works air-gapped and never sends your payload anywhere. If the embedded codec fails to load, it degrades to a characters-divided-by-four approximation. That approximation errs low. It never guesses high. Provider-reported usage from your actual API response stays authoritative for spend. The local counter is an estimate of that, and the two are kept in separate fields. Numbers you will see quoted[#](https://docs.caveman.so/docs/counting#numbers-you-will-see-quoted) -------------------------------------------------------------------------------------------------- Both of these are real measurements with real limits, and the limits travel with the number. **The skill, output tokens.** About 65 percent fewer on average across ten prompts, ranging from 22 to 87 percent. Measured on the committed benchmark suite. Output tokens only. A per-run inferred estimate, not a bill-savings promise. **The wrap path, input tokens.** 33.2 percent fewer provider-reported input tokens across 18 paired runs of six cases, with a case-clustered 95 percent interval of 14.6 to 48.5 percent. All 18 exact-answer checks passed. One of the six cases regressed by 9.9 percent and stays in the aggregate, because dropping a negative case would make the average a fiction. That second result is controlled benchmark evidence on a pinned agent build. It is not production traffic, not a provider invoice, and not `verified`. --- # Architecture · Caveman docs [Skip to content](https://docs.caveman.so/docs/architecture#main) Caveman is not one program. Most people install the skill, the proxy, or both, and never touch the rest. This page shows how a single request moves through the pieces and why they are separable. It describes the `caveman` repository. The [Agent SDK](https://docs.caveman.so/docs/agent-sdk) is a different repository with a different job: it is how you build an agent, not how you make an existing one cheaper, and nothing on this page is required to use it. One request[#](https://docs.caveman.so/docs/architecture#one-request) ---------------------------------------------------------------------- Your agentClaude Code, Cursor, yoursSkilldecides what to sendEnginecompresses the payloadProviderthe model APIthis is what you pay forthe agent asks for what was left outMemorywhat you never resend One request, left to right. The dashed path is recovery: the agent can ask for anything the engine left out. The agent decides what to say. The skill has already shaped how it says it. When the request is wrapped, it goes to the [local proxy](https://docs.caveman.so/docs/proxy) on loopback; the [engine](https://docs.caveman.so/docs/proxy/engine) reduces whatever payload is about to leave; the provider bills for what arrives. Without the proxy, the engine is a binary you call yourself. The dashed line is the part that makes the rest defensible. Compression here is not a one-way door. Before the engine emits a smaller payload it writes the original bytes to a local store keyed by a hash of those bytes, and the compressed output carries that handle. An agent that needs the part that was removed asks for the handle and gets the original back, byte for byte, in the same turn. Why the pieces are separable[#](https://docs.caveman.so/docs/architecture#why-the-pieces-are-separable) -------------------------------------------------------------------------------------------------------- Each layer answers a different question, and they fail independently. Skill Changes what the agent writes. No process, no config, no network. Proxy Changes the base URL and what a payload weighs. A loopback process that routes and records, plus the engine binary it calls to compress. Memory Changes what you have to say twice. A local SQLite store. Cloud Changes what you can prove to someone else. An account, across a network. Nothing above a layer is required by the layer below it. The skill does not know the proxy exists. The proxy does not need an account. This is why the licence split follows the same lines rather than cutting across them. Where the open-core line falls[#](https://docs.caveman.so/docs/architecture#where-the-open-core-line-falls) ------------------------------------------------------------------------------------------------------------ MIT Adoption and interop surfaces. Use them anywhere, including inside a commercial product. * The skill * CLI * TypeScript SDK * Python SDK * Agent SDK * create-agent * cavemem JS and Python clients * Eval graders * Provider catalog * Wire contracts * Extension shell BSL 1.1 Source available. Run it for your own traffic, including in production. Offering it to third parties as a hosted service needs a commercial licence. Each release converts to Apache 2.0 after four years. * Compression engine * Local proxy * cavemem core * caveman-shrink core * MCP server core * caveman-browse * Shared platform libraries Commercial Runs on our machines, under an account. * Caveman Cloud Three licences, one per surface. Nothing here changes licence based on how much you use it. The rule that governs new code is one sentence: a module that imports, links, embeds or ships as part of the engine-linked runtime is BSL 1.1 unless a decision record explicitly classifies it as an adoption surface. The practical effect for you is in the additional use grant. BSL 1.1 here permits internal evaluation, local development, CI, integration, and self-hosted use for your own first-party traffic, production included. What it does not permit is offering Caveman's functionality to third parties as a hosted or embedded service. That boundary is the commercial line, and it is the only one. Each BSL release converts to Apache 2.0 on the earlier of 21 June 2030 or four years after that version was first distributed. Content detection[#](https://docs.caveman.so/docs/architecture#content-detection) ---------------------------------------------------------------------------------- The engine does not ask you what a payload is. It reads the bytes and decides, then routes to a compressor built for that shape. The types it recognises: `json` · `log` · `code` · `diff` · `search-result` · `text` · `toon` · `html` · `a11y` · `terminal` · `tabular` · `config` Two compressors are never chosen automatically and have to be asked for by name: the tool-schema compressor and the TOON encoder. Automatic selection of either would surprise a caller who did not opt in. When detection is not confident, it answers `text`, which is the compressor that removes the least. Every unknown case in this system resolves toward doing less, not more. Failure behaviour[#](https://docs.caveman.so/docs/architecture#failure-behaviour) ---------------------------------------------------------------------------------- This is worth stating on its own, because it is the part that decides whether you can leave the thing switched on. text unknown mode -> record, which never transformstransform error -> forward the original bytesresult not smaller -> keep the originallow-confidence type -> textunknown grader -> passed: falseunknown model price -> zero, tagged unpriced Every one of those resolves to the conservative answer. A compression system that is willing to guess is a compression system you have to babysit. --- # Agent profiles · Caveman docs [Skip to content](https://docs.caveman.so/docs/skill/profiles#main) Agent profiles are the registry entries that tell the CLI how a coding agent is installed, launched, and given local Caveman tools. Ten profiles ship in the registry. The JSON files are compiled into the CLI, so source and distributed behaviour stay tied together. Schema `schema_version: "1"` Profiles Aider, Claude Code, Codex CLI, Gemini CLI, Hermes, Kilo, OpenClaw, opencode, Pi, and Qwen Code. Fallback `generic-env` Validation Unknown hook or injection methods fail compilation. Licence MIT. Registered agents[#](https://docs.caveman.so/docs/skill/profiles#registered-agents) ------------------------------------------------------------------------------------ | Profile id | Wire protocol | Configuration shape | Command-output hook | | --- | --- | --- | --- | | `aider` | OpenAI Chat | Environment | Manual | | `claude` | Anthropic Messages | Environment | Claude Code hook | | `codex` | OpenAI Responses | Host-specific code path | Codex hook | | `gemini` | Gemini generateContent | Environment | Gemini hook | | `hermes` | OpenAI Chat | Environment plus builder | Hermes plugin | | `kilo` | OpenAI Chat | Inline config via environment | Manual | | `openclaw` | OpenAI Chat | Config file overlay | OpenClaw plugin | | `opencode` | OpenAI Chat | Config content overlay | opencode plugin | | `pi` | OpenAI Chat | Native extension | Pi extension | | `qwen` | OpenAI Chat | Temporary system-settings overlay | Manual | This table describes declared integration shape. It does not say that every host version or provider credential has been tested. Each profile carries a tested version, and profiles that need code beyond their JSON declaration say so with `builder-assisted` or `code-only`. What one profile contains[#](https://docs.caveman.so/docs/skill/profiles#what-one-profile-contains) ---------------------------------------------------------------------------------------------------- Every profile has an id, display name, binary names, install hint, wire protocol, injection description, attribution header, tested agent version, completeness label, and fallback. Optional fields describe command hooks, memory hooks, on-disk skill directories, and config overlays. The completeness labels have exact meanings: | Label | Meaning | | --- | --- | | `declarative` | Profile data alone describes routing setup. | | `builder-assisted` | Profile data is the base, and CLI code adds host-specific setup. | | `code-only` | Declared injection is inert; host setup lives in reviewed CLI code. | The registry compiler cross-checks these labels against the real builders. A profile cannot call itself declarative when it depends on code. Inspect what is installed[#](https://docs.caveman.so/docs/skill/profiles#inspect-what-is-installed) ---------------------------------------------------------------------------------------------------- terminal caveman statuscaveman doctor claudecaveman doctor codexcaveman setup `status` shows local state. `doctor ` checks the selected host integration. `setup` reports companion binary availability and repair commands. Launch shortcuts use profile ids: terminal caveman claudecaveman codexcaveman geminicaveman aidercaveman hermescaveman kilocaveman openclawcaveman opencodecaveman picaveman qwen Each shortcut is equivalent to `caveman wrap `. [Wrap an agent](https://docs.caveman.so/docs/proxy/wrap) is the page for the hop itself. Hooks are capabilities, not assumptions[#](https://docs.caveman.so/docs/skill/profiles#hooks-are-capabilities-not-assumptions) ------------------------------------------------------------------------------------------------------------------------------- Command hooks rewrite noisy shell output through `caveman shrink` before the model reads it. A profile without a verified hard hook surface stays manual and receives command guidance instead. Memory hooks are stricter. They are off by default, and a profile may declare one only when the host exposes a verified live user-prompt hook. Today only Claude Code declares that capability. Skill directories are also explicit. Claude Code declares user and project skill roots. Codex declares its user skill root. A profile with no verified convention omits the field, and conversion skips it. Failure behaviour[#](https://docs.caveman.so/docs/skill/profiles#failure-behaviour) ------------------------------------------------------------------------------------ Registry compilation rejects duplicate ids, unknown methods, false completeness labels, invalid tested-version metadata, and schema drift. Runtime detection falls back to `generic-env` when a declared setup path cannot be used. Adding a profile[#](https://docs.caveman.so/docs/skill/profiles#adding-a-profile) ---------------------------------------------------------------------------------- New profiles begin as JSON that passes the shared schema and compiler. A profile also needs runtime tests for its launch path, configuration injection, fallback, and any hook it claims. Adding a name to the registry without those paths does not make an agent supported. --- # Security · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/security#main) The local proxy is a single-operator developer tool. It binds to loopback and has no multi-user authentication layer. Treat it with the same care you already give a coding agent that holds a provider key. Listen Loopback only. Non-loopback addresses are rejected. Auth on the listener None. That is why the bind check exists. Keys Inbound header, then a named environment variable. Never YAML. Savings label `inferred` Trust model[#](https://docs.caveman.so/docs/proxy/security#trust-model) ------------------------------------------------------------------------ One trusted operating-system user. Do not expose the listener on a LAN, a container bridge, a public interface, or a shared host. Signing in to Caveman Cloud does not turn this process into a shared service. Those are different products and different data flows. Local wrap sends request content to the model provider the agent selected, and keeps recovery copies on disk. "Local" describes the Caveman hop, not the whole model request. The provider still sees whatever the proxy forwards. Credentials[#](https://docs.caveman.so/docs/proxy/security#credentials) ------------------------------------------------------------------------ Keep API keys in the environment or in the provider's own credential store. Do not write them into `caveman.yaml`, a project overlay, a prompt, a benchmark fixture, or shell history. The proxy preserves an inbound authorization header and does not log it. Environment fallbacks apply only when the integration sent no credential. Distinct development keys are better than sharing a production key with a local listener, where the provider lets you do that. If a terminal, a trace, or an issue report ever printed a key, rotate it. SSRF[#](https://docs.caveman.so/docs/proxy/security#ssrf) ---------------------------------------------------------- Outbound checks cover configured endpoints and redirects. Private, loopback, link-local, and other unsafe address classes are blocked by default. A self-hosted model on this machine needs an exact `CAVE_SSRF_ALLOWLIST` entry. Allow only the hosts you need. A broad private-network range can let a prompt-driven request reach an unrelated local service. A listen-address change will not fix a blocked upstream. The bind check and the outbound check are different doors. What sits on disk[#](https://docs.caveman.so/docs/proxy/security#what-sits-on-disk) ------------------------------------------------------------------------------------ | Store | Typical contents | | --- | --- | | `~/.caveman/caveman.db` | Request metadata, usage rows, learn data | | `~/.caveman/ccr.db` | Exact originals behind recovery handles | | Feature configuration | Mode and switch values, not keys | | Agent-native hooks | Host-specific files the profile installed | A recovery handle is an identifier, not encryption and not an authorization token. Anyone who can read `ccr.db` can retrieve the payload. Do not share handles across a trust boundary. See [Recoverable compression](https://docs.caveman.so/docs/proxy/recoverable) . Restrict permissions on these files to the owner. Hook writes use restrictive modes and symlink-safe replacement; do not loosen that to make a backup script easier. Lossy transforms[#](https://docs.caveman.so/docs/proxy/security#lossy-transforms) ---------------------------------------------------------------------------------- Record mode is the byte pass-through. Compress and pixel change what the model sees. The safety rails around that change are parse validation, a size comparison, explicit capability gates, exact-source recovery, and original-byte fallback when the store fails. Recovery means the source is still available. It does not mean the model will ask for the missing detail, and it does not mean the compressed view had equal quality. For work where every input byte must remain visible, stay in record. Local against connected[#](https://docs.caveman.so/docs/proxy/security#local-against-connected) ------------------------------------------------------------------------------------------------ `caveman help tools` is the local surface and needs no account. `caveman help cloud` is the connected surface and requires login. Command discovery keeps them apart on purpose. A connected installation can persist a hosted endpoint. That path necessarily receives request and response content while it proxies. This page documents the loopback listener. The Cloud product page covers the managed plane at the level a public site can. Checklist[#](https://docs.caveman.so/docs/proxy/security#checklist) -------------------------------------------------------------------- 1. Confirm the proxy listens on `127.0.0.1`. 2. Keep secrets out of both configuration files. 3. Review enabled transforms and the pixel model allowlist. 4. Set a precise SSRF allowlist only when a self-hosted upstream needs it. 5. Restrict permissions on the local databases and hook state. 6. Test recovery before a long lossy session. 7. Use record mode when the input bytes are the point. 8. Review agent, browser, hook, and plugin permissions separately. Reporting a vulnerability[#](https://docs.caveman.so/docs/proxy/security#reporting-a-vulnerability) ---------------------------------------------------------------------------------------------------- Do not publish an exploitable report in a public issue before maintainers can assess it. Use the repository security policy. Include the affected version, a minimal reproduction, the impact, and a suggested mitigation, without real credentials or customer data. --- # The engine · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/engine#main) The engine is the compressor inside the [proxy](https://docs.caveman.so/docs/proxy) . It reduces a payload before it reaches a model: it is written in Go, it links into other tools as a library, and it also runs as a standalone binary that reads stdin and writes stdout. Most people never invoke it directly. If you are wrapping an agent, the proxy already calls this on every eligible request and there is nothing on this page you have to do. Read it when you want to know what compression is actually doing to your bytes, or when you want the binary in a pipeline of your own. Part of The proxy. Not a separate product and not a separate install. Licence BSL 1.1, converting to Apache 2.0 after four years. Distribution Source only. There is no npm, pip, Docker or Homebrew package. Binary `caveman-engine` Input limit 64 MiB on stdin, refused with cave\_input\_too\_large. Reports Always `inferred`. It cannot emit `verified`. Build it[#](https://docs.caveman.so/docs/proxy/engine#build-it) ---------------------------------------------------------------- terminal git clone https://github.com/JuliusBrussee/cavemango build -o ./bin/caveman-engine ./public/engine/cmd/caveman-engine Use it[#](https://docs.caveman.so/docs/proxy/engine#use-it) ------------------------------------------------------------ The engine reads stdin and writes the result to stdout. The accounting report goes to stderr, so you can pipe the output without stripping the numbers out of it. terminal cat large-payload.json | caveman-engine compress > compressed.txt terminal caveman-engine detect < payload.txtcaveman-engine retrieve > original.txtcaveman-engine retrieve "connection pool" > narrowed.txtcaveman-engine statscaveman-engine registry `retrieve` with no query returns the original bytes exactly. With a query it returns the sections of the original that match, ranked by BM25, which is usually what an agent actually wanted. How it picks a compressor[#](https://docs.caveman.so/docs/proxy/engine#how-it-picks-a-compressor) -------------------------------------------------------------------------------------------------- Detection reads the bytes and answers with a content type. The type selects the compressor. There is no model call and no configuration step in that path. `json` · `log` · `code` · `diff` · `search-result` · `text` · `html` · `terminal` · `tabular` · `config` Five routes require an explicit type: `toolschema`, `toolschema-annotations`, `toon`, `a11y`, and `repetition`. None is selected automatically because each needs caller intent or an input contract that byte detection cannot prove. See [Compressors](https://docs.caveman.so/docs/proxy/compressors) for route-specific behaviour. Recovery[#](https://docs.caveman.so/docs/proxy/engine#recovery) ---------------------------------------------------------------- Before any lossy result is emitted, the original bytes are written to a local store. Its public handle contains the first 16 bytes of the payload's SHA-256 digest as 32 hexadecimal characters after `ccr_`. Compressing the same payload twice produces the same handle and stores it once. Store `~/.caveman/ccr.db` Backend SQLite on host platforms, an in-memory map under WASM. Same contract. Budget 512 MiB of payloads by default, tunable with CAVEMAN\_CCR\_MAX\_BYTES. At the cap New lossy transforms pass through instead. Existing handles are never evicted. The last row is the one to read twice. When the store fills, the engine stops compressing rather than dropping old originals to make room. Losing a recovery handle would turn an earlier honest compression into an unrecoverable one after the fact, so it does not happen. Token counting[#](https://docs.caveman.so/docs/proxy/engine#token-counting) ---------------------------------------------------------------------------- Ratios come from a real BPE tokenizer using the `o200k_base` encoding, with the vocabulary compiled into the binary. It is deterministic and offline. Every ratio the engine produces is `inferred`. Provider-reported usage from your actual response stays authoritative for spend, and the two live in different fields. Targets by content type[#](https://docs.caveman.so/docs/proxy/engine#targets-by-content-type) ---------------------------------------------------------------------------------------------- These are honest reduction targets, not measured guarantees. What you get depends on your payload. | Content type | Target reduction | | --- | --- | | `search-result` | 80 to 95 percent | | `log` | 85 to 95 percent | | `json` | 70 to 90 percent | | `diff` | 60 to 80 percent | | `text` and HTML | 50 to 80 percent | | `code` | 40 to 70 percent | For one concrete measurement: on a committed tool-output fixture, 1,366 tokens became 246, a reduction of 82 percent. That figure is `inferred` and the original is recoverable. It is one fixture, and it is not a `verified` saving. Other subcommands[#](https://docs.caveman.so/docs/proxy/engine#other-subcommands) ---------------------------------------------------------------------------------- terminal caveman-engine toon encode | decodecaveman-engine pixel render --density balanced caveman-engine pixel simulate --model anthropic caveman-engine evals run --fixtures ./fixtures Pixel mode renders text as an image, which is cheaper than text for some models and much more expensive for others. `pixel simulate` exists so you can check which case you are in before switching anything on. --- # Wrap an agent · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/wrap#main) Wrapping starts an existing coding agent with the local proxy as its provider endpoint. Caveman does not replace the agent. The agent still owns its model calls, user interface, permissions, and project workflow. Ten profiles ship today. The skill works with thirty-plus agents; wrap is a different, smaller set. A profile is a JSON file the CLI compiles, not a promise that every host version has been tested on your machine. Run `caveman doctor ` where you will actually launch it. Command `caveman wrap claude` Shortcuts One verb per profile id, same as wrap. Default mode `compress` Pass-through `caveman wrap --off claude` Launch[#](https://docs.caveman.so/docs/proxy/wrap#launch) ---------------------------------------------------------- terminal caveman claudecaveman codexcaveman geminicaveman aidercaveman hermescaveman kilocaveman openclawcaveman opencodecaveman picaveman qwen `caveman kilocode` is an alias for the same `kilo` profile. Arguments after the shortcut are passed through: terminal caveman wrap claudecaveman wrap --off codexcaveman wrap --pixel geminicaveman wrap --workflow review opencodecaveman codex --full-auto `--off` and `--pixel` cannot be used together. For an agent that has no profile: terminal caveman run -- my-agent --flag value Generic wrapping supplies proxy environment. It cannot infer every host's native hook or plugin format. Registered agents[#](https://docs.caveman.so/docs/proxy/wrap#registered-agents) -------------------------------------------------------------------------------- | Profile | Wire protocol | How it is pointed at the proxy | | --- | --- | --- | | `aider` | OpenAI Chat Completions | Environment | | `claude` | Anthropic Messages | Environment | | `codex` | OpenAI Responses | Environment | | `gemini` | Gemini GenerateContent | Environment | | `hermes` | OpenAI Chat Completions | Environment | | `kilo` | OpenAI Chat Completions | Inline config via environment | | `openclaw` | OpenAI Chat Completions | Configuration file | | `opencode` | OpenAI Chat Completions | Configuration plus environment | | `pi` | OpenAI Chat Completions | Native extension | | `qwen` | OpenAI Chat Completions | Temporary system-settings overlay | This table is the integration shape. It does not say every credential path or host version works. [Agent profiles](https://docs.caveman.so/docs/skill/profiles) is the registry page, including completeness labels and hook rules. Kilo and Qwen need their own CLI installed first. The profiles pin the versions the wrap path was checked against: terminal npm install -g @kilocode/cli@7.5.6caveman kilonpm i -g @qwen-code/qwen-code@0.22.3caveman qwen Kilo organization or enterprise-managed config can override the injected provider. When that happens, or when account state cannot be verified safely, the wrapper launches Kilo directly instead of claiming a confinement it cannot keep. Qwen's overlay is a temporary file. Enterprise policy and unrelated provider settings survive the merge. `~/.qwen/settings.json` and the source system settings are not rewritten. First run[#](https://docs.caveman.so/docs/proxy/wrap#first-run) ---------------------------------------------------------------- On the first interactive local wrap, the CLI can install the signed runtime bundle and continue the same command. Non-interactive runs do not change installation state without an explicit: terminal caveman setup --install Wrap starts `caveman-proxy` for that session when it can. If something the CLI does not own is already listening on the port, wrap will not restart or trust it. An interactive run can offer to launch the agent directly when the proxy binary is missing. A script must make sure the listener is up. Native setup[#](https://docs.caveman.so/docs/proxy/wrap#native-setup) ---------------------------------------------------------------------- Claude Code and Codex can install the files those hosts actually read: terminal caveman setup --agent-native claudecaveman setup --agent-native codexcaveman setup --agent-native claude --remove Review the resulting hooks and plugins before you commit dotfiles. Native setup is per agent, not a global rewrite of every tool on the machine. Recovery during a run[#](https://docs.caveman.so/docs/proxy/wrap#recovery-during-a-run) ---------------------------------------------------------------------------------------- Compressed context carries a `ccr_…` handle, or a typed `ccr://…` pointer. Agents with the MCP integration can retrieve the original through a tool call. You can do the same from a terminal: terminal caveman tools retrieve caveman tools mcp install qwen --server caveman The Qwen installer preserves sibling MCP entries and refuses to overwrite one it does not own. Streaming and subscription-auth sessions need that agent-side recovery path. Non-streaming API-key requests can use proxy-side handling where the adapter supports it. The CLI checks that `caveman-mcp` is present for the selected agent before it advertises the MCP path. If recovery is missing on a path that needs it, that path stays uncompressed. See [Recoverable compression](https://docs.caveman.so/docs/proxy/recoverable) for handle format and store limits. Skill and wrap are separate switches[#](https://docs.caveman.so/docs/proxy/wrap#skill-and-wrap-are-separate-switches) ---------------------------------------------------------------------------------------------------------------------- An installed skill does not prove that request compression is on. Proxy traffic does not prove that the response skill is on. Hooks can add reminders or shrink command output without either of those being true. Turn each one on because you want that effect, not because you installed the other. When wrap goes wrong[#](https://docs.caveman.so/docs/proxy/wrap#when-wrap-goes-wrong) -------------------------------------------------------------------------------------- 1. `caveman status` for the selected mode and whether the listener is ours. 2. `caveman setup` for missing binaries. 3. `caveman wrap --off ` to see whether the failure survives without transforms. 4. Check provider credential variables without printing the secret values. 5. Confirm the agent is using the local endpoint the profile emitted. If a transform cannot parse the body, cannot store a recovery record, or cannot produce a smaller safe result, the original bytes go upstream. That is success for the request, and a zero-ratio row in the local store. --- # Overview · Caveman docs [Skip to content](https://docs.caveman.so/docs#main) Caveman makes agents cheaper to run. It is not one program and not a framework you commit to. It is a short list of separate things, each of which you can adopt on its own and remove on its own. There are two repositories and one hosted plane. Knowing which is which is most of what you need to navigate this site. caveman Makes an agent you already use cheaper. The skill, the proxy and the compression engine inside it, memory, and the adoption surfaces: CLI, SDKs, MCP server. agent-sdk Builds a new agent that is efficient by construction. A separate repository and a separate install. It does not need the proxy. Caveman Cloud The managed plane, across a network boundary. The only paid part, and the only place a number can be verified against a bill. caveman: make an existing agent cheaper[#](https://docs.caveman.so/docs#caveman-make-an-existing-agent-cheaper) ---------------------------------------------------------------------------------------------------------------- Everything in this repository runs on your machine and needs no account. The four rows below are the operating stack, bottom to top. Each one answers a different question, and each fails on its own. [03CloudWhat a fleet can put in front of financeCommercial](https://docs.caveman.so/docs/cloud) network boundary [02MemoryWhat you never have to send twiceBSL 1.1](https://docs.caveman.so/docs/memory) [01ProxyWhat the payload weighs when it leavesBSL 1.1](https://docs.caveman.so/docs/proxy) [00SkillWhat the agent does before it calls outMIT](https://docs.caveman.so/docs/skill) your machine The stack, bottom to top. Everything below the network boundary runs locally and needs no account. ### 00 Skill[#](https://docs.caveman.so/docs#00-skill) A [skill](https://docs.caveman.so/docs/skill) is a persistent instruction set your coding agent reads before it answers. It cuts filler, narration and hedging out of the agent's own output, and it is explicit about what it must never touch: code blocks, function names, CLI commands and exact error strings stay byte for byte. It costs nothing to run because it is not software. It is text your agent already reads. ### 01 Proxy[#](https://docs.caveman.so/docs#01-proxy) The [proxy](https://docs.caveman.so/docs/proxy) is a reverse proxy on loopback. You point an agent at `127.0.0.1:8787` instead of the provider, it keeps your key, and it shrinks the context on the way out. The compressor doing that shrinking is the engine. It detects what a payload is, routes it to a compressor built for that shape, and stores the original bytes under a content-addressed handle before it removes anything. If the agent needs what was dropped, it asks for the handle and gets the original back exactly. The engine is not a separate product and not a separate install; it is [documented inside the proxy](https://docs.caveman.so/docs/proxy/engine) , because the proxy is how you actually run it. ### 02 Memory[#](https://docs.caveman.so/docs#02-memory) [cavemem](https://docs.caveman.so/docs/memory) holds the context you would otherwise paste into every session. It stores text once, recalls the parts a question actually needs, and compresses each hit on the way out. ### 03 Cloud[#](https://docs.caveman.so/docs#03-cloud) [Caveman Cloud](https://docs.caveman.so/docs/cloud) is the managed plane. It is where a fleet's numbers stop being local estimates and become something a finance team can read. It is in private development and the waitlist is open. ### Build with it[#](https://docs.caveman.so/docs#build-with-it) The same capabilities as a [CLI](https://docs.caveman.so/docs/cli) , a [TypeScript](https://docs.caveman.so/docs/sdk/typescript) or [Python](https://docs.caveman.so/docs/sdk/python) SDK, an [MCP server](https://docs.caveman.so/docs/mcp) , and two standalone commands: [caveman-shrink](https://docs.caveman.so/docs/shrink) for command output and [caveman-browse](https://docs.caveman.so/docs/browse) for web pages. These are the MIT adoption surfaces. Reach for them when you want the behaviour without a proxy hop. agent-sdk: build a new agent[#](https://docs.caveman.so/docs#agent-sdk-build-a-new-agent) ------------------------------------------------------------------------------------------ The [Agent SDK](https://docs.caveman.so/docs/agent-sdk) is the other direction. Rather than making an existing agent's traffic smaller, it is how you write an agent whose context, tools and budget are declared up front, so a turn cannot quietly exceed them. An agent is one frozen [definition](https://docs.caveman.so/docs/agent-sdk/define) : context, output schema, filesystem, [tools](https://docs.caveman.so/docs/agent-sdk/tools) , and subagents. You [run a turn](https://docs.caveman.so/docs/agent-sdk/run) against it and get a receipt rather than a vibe. [Evals](https://docs.caveman.so/docs/agent-sdk/build) gate the build, so a failed eval never produces a lock. [Compaction](https://docs.caveman.so/docs/agent-sdk/compaction) is a fail-closed ladder inside a declared budget: recover, summarise, clamp, then stop. It lives in its own repository, ships as its own package, and shares no runtime with the proxy. You can use either without the other. What the numbers mean[#](https://docs.caveman.so/docs#what-the-numbers-mean) ----------------------------------------------------------------------------- Every local tool on this site reports `inferred`. That word is load bearing. It means a per-run estimate computed on your machine from an offline token counter, and it is never turned into a monthly figure or a dollar amount. No tool in either repository can emit `verified`. That label is reserved for hosted rollout systems that can compare against a real bill. Where to go next[#](https://docs.caveman.so/docs#where-to-go-next) ------------------------------------------------------------------- [Quickstart5 min\ \ Install the skill, then compress something real and read the report.](https://docs.caveman.so/docs/quickstart) [Architecture\ \ The path one request takes, and where the open-core line falls.](https://docs.caveman.so/docs/architecture) [The skillMIT\ \ One command, thirty agents, no runtime.](https://docs.caveman.so/docs/skill) [The proxyBSL 1.1\ \ Point an agent at loopback. The engine shrinks what leaves.](https://docs.caveman.so/docs/proxy) [Agent SDKMIT\ \ Build an agent that returns a receipt, not a vibe.](https://docs.caveman.so/docs/agent-sdk) [LicensingMIT and BSL 1.1\ \ Which surfaces are open, and what the additional use grant permits.](https://docs.caveman.so/docs/licensing) --- # Modes · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/modes#main) Mode decides whether the proxy is allowed to change model-visible bytes. Unknown values become `record`. That fail-closed default is why you can leave the listener running when you are unsure. The CLI exposes three operator modes. The binary accepts four more for controlled evaluation. Wrap default `compress` Start default `record` Unknown mode `record` Pass-through flag `caveman wrap --off` Record[#](https://docs.caveman.so/docs/proxy/modes#record) ----------------------------------------------------------- terminal caveman wrap --off claudecaveman start Forward the model-visible request unchanged. Usage is still written locally. Use record to take a baseline, to prove an integration works, or for any workflow where every input byte must stay visible. `CAVEMAN_OBSERVE_ESTIMATE=1` can run the compressor on copies of live-zone segments while mode is `record`. The forwarded request is not mutated, no recovery record is stored, and no saving is booked. It answers "what would compress have done" without doing it. Compress[#](https://docs.caveman.so/docs/proxy/modes#compress) --------------------------------------------------------------- terminal caveman claudecaveman tools config set think.mode compress Eligible context is sent through the engine. The original is stored first. The smaller view is emitted only after that write commits, and it carries a recovery handle. If the result is not smaller, if parse fails, if the store is full, or if the content type is unsupported, the original bytes go upstream. TOON, the compact encoding for uniform tabular JSON, is allowed in this mode when the feature gate is on and the encoded form is smaller. It is never selected by automatic detection, and it is never applied to tool-call arguments. Changing those can change program behaviour even when the data looks similar. terminal caveman tools toon encode < data.jsoncaveman tools toon decode < data.toon The decoder rejects malformed input rather than inventing structure. See [Compressors](https://docs.caveman.so/docs/proxy/compressors) for the content types compress actually routes. Pixel[#](https://docs.caveman.so/docs/proxy/modes#pixel) --------------------------------------------------------- terminal caveman wrap --pixel gemini Pixel renders text as a PNG for a vision-capable model. It is lossy, model-dependent, and CCR-recoverable. The model must appear in `think.pixel.models`. Caveman does not infer vision support from a name. Density is `conservative`, `balanced`, or `max`. Higher density packs more text into the image and can make small characters harder to read. A smaller local representation does not prove a lower provider bill. Providers count image tokens differently from text. Check `pixel simulate` against the model you will actually call before leaving this on: terminal caveman-engine pixel simulate --model anthropic `--pixel` is not supported for Codex subscription sessions. ### Skills as images[#](https://docs.caveman.so/docs/proxy/modes#skills-as-images) `caveman convert` renders installed `SKILL.md` bodies to PNG pages in place. Front matter stays text, so discovery and triggering still work. The model reads the body as an image. terminal caveman convert --dry-runcaveman convert --agent claudecaveman convert --revert Conversion runs only when the pages beat the text on the local token counter. Any failure leaves the skill byte-identical and names the gate that said no. `--revert` restores `SKILL.orig.md`. Skills installed through `caveman skills install` auto-pixel by default; pass `--no-pixel` to skip. Measured on the caveman skill itself: 1,069 to 415 estimated tokens, a 61 percent reduction. That figure is `inferred`, one skill, and not a bill. Evaluation modes[#](https://docs.caveman.so/docs/proxy/modes#evaluation-modes) ------------------------------------------------------------------------------- The binary also accepts `recommend`, `shadow`, `canary`, and `active`. Standard local CLI workflows do not put you in these. They exist so a controlled evaluation can score or apply experimental behaviour without changing the three operator modes. If you have not been told to set one, you want `record`, `compress`, or `pixel`. How a mode is chosen[#](https://docs.caveman.so/docs/proxy/modes#how-a-mode-is-chosen) --------------------------------------------------------------------------------------- Wrap reads feature configuration (`think.mode`), then environment (`CAVEMAN_WRAP_MODE` or `CAVEMAN_MODE`), then the `--off` / `--pixel` flags. Start reads the same feature configuration, but a bare `caveman start` with no override resolves to `record` so an unattended listener does not transform traffic. `caveman.yaml` `mode` is what the binary itself loads. `CAVEMAN_MODE` overrides that file. Command flags win for one session. Full precedence is on [Configure](https://docs.caveman.so/docs/proxy/configure) . Failure behaviour[#](https://docs.caveman.so/docs/proxy/modes#failure-behaviour) --------------------------------------------------------------------------------- text unknown mode -> recordmalformed transform input -> original bytesresult not smaller -> original bytesCCR unavailable or full -> original bytes, no handleunsupported type or model -> original bytesunknown safety class -> do not transformmissing recovery MCP -> leave that path uncompressedforeign process on the port -> do not restart or trust it Every row resolves toward doing less. A mode you did not ask for cannot turn itself on. --- # Proxy · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy#main) The local proxy is a reverse proxy on loopback. You point an existing coding agent or provider SDK at `http://127.0.0.1:8787`, it forwards each request to your chosen model API with the credential that came in, and it compresses eligible context before those bytes leave. It is one of two things you install. The [skill](https://docs.caveman.so/docs/skill) changes how an agent writes. The proxy changes how much the agent has to read. They run independently. Repository `JuliusBrussee/caveman` Binary `caveman-proxy` Listen `127.0.0.1:8787` Account Not required. Licence BSL 1.1, converting to Apache 2.0 after four years. Reports Always `inferred`. It cannot emit `verified`. Your agentor any provider SDKLocal proxy127.0.0.1:8787Enginecompresses, then storesProvideryour key, their APIthis is what you pay forthe agent retrieves what was left outUsage storeinferred rows onlyCCR storeexact original bytes One request through the local proxy. Compression happens on the way out. Recovery comes back on the dashed line. The agent and the provider request format stay as they are. The proxy is a base-URL swap, not a new agent loop. Recovery copies live in `~/.caveman/ccr.db` on this machine. Usage rows live in `~/.caveman/caveman.db`. Neither file is a bill. Two ways in[#](https://docs.caveman.so/docs/proxy#two-ways-in) --------------------------------------------------------------- **Wrap an agent.** The CLI starts the proxy and points a known coding agent at it: terminal npm install -g @caveman-ai/clicaveman setup --installcaveman claude The same shortcut exists for Codex, Gemini CLI, Aider, Hermes, Kilo, OpenClaw, opencode, Pi, and Qwen Code. See [Wrap an agent](https://docs.caveman.so/docs/proxy/wrap) . **Start the listener yourself.** Use this when an application already has a provider SDK and you only want to change its base URL: terminal caveman start Default mode for a bare `caveman start` is `record`: every model-visible byte is forwarded unchanged, and the proxy writes local usage rows. Wrap defaults the other way, to `compress`. [Modes](https://docs.caveman.so/docs/proxy/modes) explains the difference. What happens to a request[#](https://docs.caveman.so/docs/proxy#what-happens-to-a-request) ------------------------------------------------------------------------------------------- 1. The agent sends a provider-shaped HTTP request to loopback. 2. The proxy matches a [route](https://docs.caveman.so/docs/proxy/providers) , keeps or resolves the credential, and inspects the body. 3. If the mode allows it and every gate passes, the engine stores the original bytes and emits a smaller view plus a recovery handle. 4. The proxy forwards the (possibly smaller) request upstream and streams the provider response back. 5. A local usage row is written. Token reductions on that row are `inferred`. If any transform gate fails, step 3 is skipped and the original body is forwarded. A compression problem never becomes a fake success or a client-side parse error. Provider errors still arrive as provider errors. Build from source[#](https://docs.caveman.so/docs/proxy#build-from-source) --------------------------------------------------------------------------- Most people never do this. The CLI installs a signed `caveman-proxy` into `~/.caveman/bin`. If you want the binary yourself: terminal git clone https://github.com/JuliusBrussee/cavemancd cavemango build -o ./bin/caveman-proxy ./proxy/cmd/caveman-proxyANTHROPIC_API_KEY=… ./bin/caveman-proxy There is no npm, pip, Docker, or Homebrew package for the proxy itself. `@caveman-ai/cli` is the MIT launcher. What the numbers mean[#](https://docs.caveman.so/docs/proxy#what-the-numbers-mean) ----------------------------------------------------------------------------------- In a pinned Claude Code benchmark, the wrap path used 33.2 percent fewer provider-reported input tokens across 18 paired runs of six cases. The case-clustered 95 percent interval is 14.6 to 48.5 percent. All 18 exact-answer checks passed. One case regressed by 9.9 percent and stays in the aggregate. That is controlled benchmark evidence on a pinned agent build, not production traffic and not `verified`. Local rows the proxy writes after your own sessions stay `inferred`. They are never multiplied into a monthly figure and never converted to money. What it will not do[#](https://docs.caveman.so/docs/proxy#what-it-will-not-do) ------------------------------------------------------------------------------- It will not bind to a LAN, container bridge, or public interface. A non-loopback listen address is rejected, because the listener has no inbound authentication. It will not log authorization headers, put API keys in YAML, or emit `verified`. It will not replace your agent. Claude Code still owns its UI, permissions, and model calls. The proxy owns the hop between that agent and the provider. It will not compress when the recovery store cannot accept the original. No store, no lossy transform. Running it[#](https://docs.caveman.so/docs/proxy#running-it) ------------------------------------------------------------- [Wrap an agent\ \ Launch a coding agent through the local listener.](https://docs.caveman.so/docs/proxy/wrap) [Providers\ \ Routes, credentials, and compatibility mounts.](https://docs.caveman.so/docs/proxy/providers) [Modes\ \ Record, compress, pixel, and the evaluation modes.](https://docs.caveman.so/docs/proxy/modes) [Configure\ \ Feature flags, caveman.yaml, and local storage.](https://docs.caveman.so/docs/proxy/configure) [Security\ \ Loopback, BYOK, and what stays on disk.](https://docs.caveman.so/docs/proxy/security) What it does to your bytes[#](https://docs.caveman.so/docs/proxy#what-it-does-to-your-bytes) --------------------------------------------------------------------------------------------- [The engine\ \ The compressor the proxy calls, and the standalone binary behind it.](https://docs.caveman.so/docs/proxy/engine) [Compressors\ \ One router per content shape: JSON, logs, code, diffs, search results, prose.](https://docs.caveman.so/docs/proxy/compressors) [Recoverable compression\ \ Why lossy stays honest: every removed byte can be fetched back.](https://docs.caveman.so/docs/proxy/recoverable) [Token counting\ \ The offline counter behind every ratio the proxy reports.](https://docs.caveman.so/docs/proxy/tokens) --- # Compressors · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/compressors#main) The engine routes each payload to a compressor built for its shape. Detection and compression are deterministic local code. Neither step calls a model. Automatic routes[#](https://docs.caveman.so/docs/proxy/compressors#automatic-routes) ------------------------------------------------------------------------------------- Detection checks stronger structural signals first and falls back to `text` when confidence is low. | Content type | What the compressor keeps | | --- | --- | | `json` | Object keys, error subtrees, array edges, error items, anomalies, change points, and query-relevant items. | | `terminal` | Final progress state, head and tail context, warnings, errors, and failure lines. ANSI control bytes are removed. | | `diff` | File and hunk headers, every changed line, and nearby unchanged context. | | `html` | Main readable article content rather than scripts, styles, navigation, and repeated chrome. | | `tabular` | Table structure and signal-bearing rows from CSV, TSV, or Markdown tables. | | `code` | Imports, declarations, signatures, and type structure. Function bodies may be elided. | | `log` | Head and tail context, warnings, errors, stack frames, and query-relevant lines. | | `search-result` | Top and bottom hits, diagnostic lines, and query-relevant results. | | `config` | Structure and selected values from YAML, TOML, and INI. | | `text` | Headings, opening and closing sections, marked important sections, and query-relevant prose. | The order matters. Raw terminal escape sequences are conclusive, for example, so terminal output is recognized before code or logs. Plain prose containing one code keyword does not become code. Unknown input takes the conservative `text` path. Forced routes[#](https://docs.caveman.so/docs/proxy/compressors#forced-routes) ------------------------------------------------------------------------------- Some transforms are available only when a caller names them: | Type | Why it is explicit | | --- | --- | | `toolschema` | Changes model-visible tool descriptions and annotations. The caller must identify a tool catalog. | | `toolschema-annotations` | Strips a reviewed allowlist of schema annotations without treating arbitrary JSON as schema. | | `toon` | Re-encodes JSON into another wire format. Automatic selection would surprise a JSON caller. | | `a11y` | Expects a Chrome accessibility tree and emits a compact UID view. Ordinary JSON must not enter this path. | | `repetition` | Collapses consecutive identical lines. Repetition alone does not identify the payload's meaning. | Do not depend on a published compressor count. The registry changes as content shapes and safety contracts change. Depend on the named type you need, or ask the engine for its current registry. terminal caveman-engine registrycaveman-engine detect < payload.txt Elision markers[#](https://docs.caveman.so/docs/proxy/compressors#elision-markers) ----------------------------------------------------------------------------------- Lossy compressors replace dropped runs with explicit markers. A marker states how much was removed, and some compressors attach invariants calculated from those exact units. text … 42 lines elided (caveman) …… 18 context lines elided (caveman) … Markers are part of the idempotence contract. Compressing an already compressed view does not keep collapsing its own markers or append a second contract line. Query-aware selection[#](https://docs.caveman.so/docs/proxy/compressors#query-aware-selection) ----------------------------------------------------------------------------------------------- JSON, logs, search results, and text can receive a query. Query-aware compressors use deterministic BM25 to keep matching units alongside their fixed safety anchors. They do not use embeddings or a model call. A query may change which records remain visible. It may not make the output larger than the queryless view. Exact original bytes remain behind the same recovery handle. Code builds[#](https://docs.caveman.so/docs/proxy/compressors#code-builds) --------------------------------------------------------------------------- With cgo enabled, the code route uses tree-sitter for Go, Python, JavaScript, and TypeScript. A build without cgo, including WASM, uses Go's standard parser and compresses Go only. Unsupported languages pass through. Comment removal is opt-in. Default code compression keeps comments and Python docstrings, elides bodies, then parses the result again. A result that no longer parses is rejected. Shared safety rules[#](https://docs.caveman.so/docs/proxy/compressors#shared-safety-rules) ------------------------------------------------------------------------------------------- Every compressor returns either a candidate or `ok: false`. The engine applies the remaining gates around it: text parse or shape mismatch -> original bytesrecovery write failure -> original bytescandidate not smaller -> original bytesunknown forced type -> original bytes Reduction figures[#](https://docs.caveman.so/docs/proxy/compressors#reduction-figures) --------------------------------------------------------------------------------------- Content-type ranges shown on the engine overview are targets, not guarantees. Payload size, repetition, query, and important-line density decide whether a result is smaller. The engine reports the result of each run as `inferred`; it passes through when no reduction survives the gates. --- # Recoverable compression · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/recoverable#main) Recoverable compression lets the model see a smaller view without destroying source bytes. Caveman calls the local store CCR, short for Context Recovery. Your agentClaude Code, Cursor, yoursSkilldecides what to sendEnginecompresses the payloadProviderthe model APIthis is what you pay forthe agent asks for what was left outMemorywhat you never resend One request, left to right. The dashed path is recovery: the agent can ask for anything the engine left out. Store before showing less[#](https://docs.caveman.so/docs/proxy/recoverable#store-before-showing-less) ------------------------------------------------------------------------------------------------------- A lossy transform follows this order: 1. 1 The compressor creates a smaller candidate and the token counter confirms that it is smaller. 2. 2 The engine writes the original bytes and accounting metadata to CCR. 3. 3 Only after that write commits does the engine return the smaller view and its handle. If step two fails, step three never publishes a lossy view. The caller receives the original bytes, a zero ratio, and no recovery handle. Handle format[#](https://docs.caveman.so/docs/proxy/recoverable#handle-format) ------------------------------------------------------------------------------- CCR hashes the original with SHA-256 and uses the first 16 digest bytes in the public handle: text ccr_<32 lowercase hexadecimal characters> The same input produces the same handle and is stored once. A handle identifies content, not a session or a filename. Persistent store[#](https://docs.caveman.so/docs/proxy/recoverable#persistent-store) ------------------------------------------------------------------------------------- Default path `~/.caveman/ccr.db` Override `CAVEMAN_CCR_DB` Default budget 512 MiB of retained payload and metadata bytes. Budget override `CAVEMAN_CCR_MAX_BYTES` Host backend SQLite with WAL and a busy timeout. `CAVEMAN_HOME` changes the parent directory when `CAVEMAN_CCR_DB` is absent. WASM uses an in-memory store because the host SQLite implementation is unavailable there. A WASM handle lasts only as long as that in-memory store. The budget accepts a positive byte count. At the limit, new lossy transforms pass through. Existing handles are not evicted to make room, because eviction would invalidate a recovery promise already shown to an agent. Retrieve[#](https://docs.caveman.so/docs/proxy/recoverable#retrieve) --------------------------------------------------------------------- terminal caveman-engine retrieve ccr_xxxxxxxx > original.bincaveman-engine retrieve ccr_xxxxxxxx "connection pool" > relevant.txt An empty query returns the stored original byte for byte. A query asks for a deterministic BM25-selected view of the original and is useful when full recovery would refill the context window. The MCP surface exposes the same distinction: text caveman_retrieve({ recovery_handle: "ccr_..." })caveman_retrieve({ recovery_handle: "ccr_...", query: "connection pool" }) Unknown handles fail with an explicit error. Retrieval never guesses, returns a nearby payload, or treats a missing handle as empty content. Shared and separate stores[#](https://docs.caveman.so/docs/proxy/recoverable#shared-and-separate-stores) --------------------------------------------------------------------------------------------------------- The engine CLI, MCP server, and caveman-shrink use the shared CCR path by default, so a handle minted by one can resolve in a later process through another. cavemem keeps its recovery data under `~/.caveman/mem/ccr.db`. Recover a memory hit with `cavemem recover` or `caveman mem recover`; do not send that handle to the engine store. What recovery does not prove[#](https://docs.caveman.so/docs/proxy/recoverable#what-recovery-does-not-prove) ------------------------------------------------------------------------------------------------------------- Recovery proves that omitted bytes remain available. It does not prove that the model will ask for them, that a compressed answer has equal quality, or that the local token count matches a provider bill. Those are separate grader, behaviour, and accounting questions. --- # Token counting · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/tokens#main) The engine needs a stable local token count to decide whether a transform is smaller. Its default counter uses an offline `o200k_base` tokenizer and records enough identity to say exactly which counter produced a result. Backend `tiktoken-go/o200k_base` Pinned revision `github.com/tiktoken-go/tokenizer@v0.8.0` Input surface Raw text, with no normalization. Network None. Product basis `inferred` What exact means here[#](https://docs.caveman.so/docs/proxy/tokens#what-exact-means-here) ------------------------------------------------------------------------------------------ For valid UTF-8, the counter returns the exact result of the pinned `o200k_base` codec on the raw string. That is exact for this local backend and this input surface. It is not an exact provider bill. Providers may count a request envelope, messages, tools, cached input, or model- specific tokens differently. Provider-reported usage from the real response remains authoritative for spend. The code keeps these two claims separate: text local tokenizer result -> deterministic local evidenceprovider response usage -> observed provider evidence Public compression reports still use `basis: "inferred"` because a local counter did not observe the bill. Fallback counter[#](https://docs.caveman.so/docs/proxy/tokens#fallback-counter) -------------------------------------------------------------------------------- If the codec cannot be constructed, or if a count fails, the engine uses a deterministic approximation: text max(1, floor(UTF-8 rune count / 4)) Empty input counts as zero. Invalid UTF-8 also takes the approximation path. Detailed count evidence labels this backend `approx/chars-per-four`, marks it modeled rather than exact, and records the fallback reason. Provider-specific registry[#](https://docs.caveman.so/docs/proxy/tokens#provider-specific-registry) ---------------------------------------------------------------------------------------------------- The token package also supports exact provider, model, and surface bindings. A binding must name a reviewed local backend. Unknown or unsupported tuples return `unmeasured`. They do not silently fall back to a nearby model or the default counter. A caller that wants a modeled fallback must choose it explicitly. Supported surfaces are: * Raw text * Messages * Tools * Full request envelope A backend may support one surface without supporting the others. Exactness never expands beyond the surface in its descriptor. How compression uses it[#](https://docs.caveman.so/docs/proxy/tokens#how-compression-uses-it) ---------------------------------------------------------------------------------------------- The engine counts input and candidate output with the same counter. A candidate is used only when output count is strictly lower. Equal counts pass through. terminal cat payload.json | caveman-engine compress > compact.txtcaveman-engine stats Reports include tokens before, tokens after, ratio, and basis. The ratio is a property of that payload and run. It is not annualized, converted to currency, or relabeled `verified`. Determinism and cache identity[#](https://docs.caveman.so/docs/proxy/tokens#determinism-and-cache-identity) ------------------------------------------------------------------------------------------------------------ The `o200k_base` descriptor pins backend name, module revision, semantic contract digest, artifact digest, and surface. That identity lets caches and build locks distinguish a real counter change from the same name backed by different code. No Unicode normalization runs before counting. The same byte sequence and counter identity produce the same result. What the counter cannot tell you[#](https://docs.caveman.so/docs/proxy/tokens#what-the-counter-cannot-tell-you) ---------------------------------------------------------------------------------------------------------------- * What a provider charged for a request * How cached input was billed * Which model answered * Whether compression preserved answer quality * A monthly or dollar saving Use provider usage for observed token accounting and graders for quality. Keep local token estimates labeled `inferred`. --- # Providers · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/providers#main) The proxy speaks the HTTP shapes your agent already uses. Change the base URL to `http://127.0.0.1:8787` (plus the provider prefix when the profile needs one) and keep the same request bodies. Compatibility here means the route and the credential mapping, not every vendor extension. Credentials stay on the request or in the environment. They never belong in YAML. Default listen `http://127.0.0.1:8787` Unknown route 404, then nothing is forwarded. Inbound key Preserved. Environment is a fallback only. Price gaps Zero, tagged unpriced. No guessed list price. Anthropic[#](https://docs.caveman.so/docs/proxy/providers#anthropic) --------------------------------------------------------------------- text /anthropic/v1/messages/anthropic/v1/messages/count_tokens/v1/messages/v1/messages/count_tokens Claude Pro and Max OAuth tokens pass through as `Authorization: Bearer`. Remapping those to `x-api-key` would 401, so the proxy does not. Environment fallback: `ANTHROPIC_API_KEY`. OpenAI[#](https://docs.caveman.so/docs/proxy/providers#openai) --------------------------------------------------------------- text /openai/v1/chat/completions/openai/v1/responses/openai/v1/embeddings/v1/chat/completions/v1/responses/v1/embeddings Environment fallback: `OPENAI_API_KEY`. Google Gemini[#](https://docs.caveman.so/docs/proxy/providers#google-gemini) ----------------------------------------------------------------------------- text /gemini/v1beta/models/{model}:generateContent/gemini/v1beta/models/{model}:streamGenerateContent/gemini/v1beta/models/{model}:countTokens Bare `/v1beta/models/{model}:…` paths are also accepted when a profile uses them. Environment fallback: `GEMINI_API_KEY`. Amazon Bedrock[#](https://docs.caveman.so/docs/proxy/providers#amazon-bedrock) ------------------------------------------------------------------------------- text /bedrock/model/{model}/invoke/bedrock/model/{model}/invoke-with-response-stream/bedrock/model/{model}/converse/bedrock/model/{model}/converse-stream No raw endpoint URL is required in standalone mode. Use a bearer key or a complete IAM pair: terminal AWS_REGION=us-east-1 AWS_BEARER_TOKEN_BEDROCK=… caveman-proxyAWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=… AWS_SECRET_ACCESS_KEY=… caveman-proxy `AWS_SESSION_TOKEN` is honoured for temporary credentials. A partial IAM pair fails closed. Credential precedence is the inbound credential, then the Bedrock bearer token, then a complete IAM pair. Region precedence is `providers.bedrock.region` in `caveman.yaml`, then `CAVE_BEDROCK_REGION`, `AWS_REGION`, `AWS_DEFAULT_REGION`, then `us-east-1`. The Mantle-style Anthropic Messages route at `/bedrock/anthropic` stays off unless `CAVE_BEDROCK_MANTLE_ENABLED` is set. A Claude Code user agent cannot relabel paid Bedrock traffic as subscription traffic. Inbound `x-api-key` and bearer credentials are stamped as Bedrock API keys before that classification. Azure OpenAI and Vertex AI[#](https://docs.caveman.so/docs/proxy/providers#azure-openai-and-vertex-ai) ------------------------------------------------------------------------------------------------------- Azure mounts under `/azure/…` after you set its base URL. Vertex mounts under `/vertex/v1/projects/…` and accepts the public Google and Anthropic publisher route forms the adapter implements. Both are opt-in: the endpoint and identity are installation-specific. yaml providers: azure: base_url: https://example-resource.openai.azure.com Environment fallback for Azure: `AZURE_OPENAI_API_KEY`. OpenAI-compatible mounts[#](https://docs.caveman.so/docs/proxy/providers#openai-compatible-mounts) --------------------------------------------------------------------------------------------------- Named compatibility mounts live at `/compat/{name}/…`. Each mount declares a `base_url` and the environment variable that holds its credential: yaml compat: local-model: base_url: http://127.0.0.1:11434/v1 api_key_env: LOCAL_MODEL_API_KEY Compatibility means HTTP shape. It does not mean every provider extension works. Private, loopback, and link-local upstreams are blocked until you add an exact host to `CAVE_SSRF_ALLOWLIST`. See [Security](https://docs.caveman.so/docs/proxy/security) . Applications and SDKs[#](https://docs.caveman.so/docs/proxy/providers#applications-and-sdks) --------------------------------------------------------------------------------------------- A wrap profile is one way to set the base URL. An application can do the same thing itself. The CLI prints copy-ready recipes: terminal caveman tools sdk Recipes cover Anthropic, OpenAI, Google Gen AI, Vercel AI SDK, LangChain, LiteLLM, CrewAI, Pydantic AI, the OpenAI Agents SDK, and raw HTTP. The TypeScript and Python SDKs also take a `baseURL` of `http://127.0.0.1:8787`; see [TypeScript SDK](https://docs.caveman.so/docs/sdk/typescript) and [Python SDK](https://docs.caveman.so/docs/sdk/python) . Streaming[#](https://docs.caveman.so/docs/proxy/providers#streaming) --------------------------------------------------------------------- Transforms finish before the request is dispatched. The response stays a stream. Provider streaming protocols and status codes are preserved. A transform never converts a streaming call into a buffered fake. Pricing on the local row[#](https://docs.caveman.so/docs/proxy/providers#pricing-on-the-local-row) --------------------------------------------------------------------------------------------------- The [provider catalog](https://docs.caveman.so/docs/provider-catalog) supplies dated public list prices. An unknown provider or model resolves to zero with an `unpriced` marker. Provider-reported token counts stay in a different field from engine estimates. A displayed list-price subtotal is not an invoice. When a route misses[#](https://docs.caveman.so/docs/proxy/providers#when-a-route-misses) ----------------------------------------------------------------------------------------- A 404 usually means the agent used the wrong provider prefix, or a bare path the selected adapter does not own. Authentication failures should be checked at the inbound header and at the environment fallback, without printing the secret. A blocked custom base URL almost always needs a precise `CAVE_SSRF_ALLOWLIST` entry, not a wider listen address. --- # Configure · Caveman docs [Skip to content](https://docs.caveman.so/docs/proxy/configure#main) Two files, two jobs. Feature configuration tells `caveman wrap` which switches are on. Proxy configuration tells `caveman start` how the listener binds and which provider endpoints it may call. API keys belong in neither file. Feature file Printed by `caveman tools config path` Proxy file `~/.caveman/caveman.yaml` Usage store `~/.caveman/caveman.db` Recovery store `~/.caveman/ccr.db` Feature configuration[#](https://docs.caveman.so/docs/proxy/configure#feature-configuration) --------------------------------------------------------------------------------------------- Inspect the resolved path and a single key: terminal caveman tools config pathcaveman tools config get think.modecaveman tools config set think.mode compress A project may add a restricted overlay at `./.caveman/config.json`. The overlay can set `think.toon`, `think.shrink`, `remember.*`, and `execute.*`. It cannot change `think.mode`, `think.core`, or pixel settings. A checked-in project file is not allowed to silently turn on a more invasive transform. | Key | Default | Meaning | | --- | --- | --- | | `think.mode` | `compress` | `compress`, `record`, or `pixel` | | `think.core` | `true` | Core context compression | | `think.toon` | `true` | Allow TOON when it is smaller | | `think.shrink` | `true` | Shrink command output where supported | | `think.pixel.models` | `[]` | Models allowed to receive pixel context | | `think.pixel.density` | `balanced` | `conservative`, `balanced`, or `max` | | `remember.mem` | `true` | Local memory integration | | `remember.offload` | `auto` | `auto`, `on`, or `off` | | `remember.recall` | `false` | Automatic memory recall | | `execute.mcp` | `auto` | MCP recovery wiring | | `execute.browse_tool` | `true` | Browser tool integration | | `execute.browse_cli` | `false` | Browser command integration | | `execute.delegate` | `false` | Delegation integration | | `execute.proxy` | `true` | Route supported agents through the local proxy | Environment overrides beat the stored file. Use them for one session; use `config set` for a choice you want to keep. | Variable | Key | | --- | --- | | `CAVEMAN_WRAP_MODE` | `think.mode` | | `CAVEMAN_CORE` | `think.core` | | `CAVEMAN_TOON` | `think.toon` | | `CAVEMAN_SHRINK` | `think.shrink` | | `CAVEMAN_MCP` | `execute.mcp` | | `CAVE_PIXEL_MODELS` | `think.pixel.models` | | `CAVE_PIXEL_DENSITY` | `think.pixel.density` | `CAVEMAN_MODE` also overrides wrap and start mode when it is set. Proxy configuration[#](https://docs.caveman.so/docs/proxy/configure#proxy-configuration) ----------------------------------------------------------------------------------------- Default path: `~/.caveman/caveman.yaml`. `CAVEMAN_CONFIG` loads a different file. A missing file is valid: the listener starts in `record` on `127.0.0.1:8787`. yaml label: localmode: recordlisten: 127.0.0.1:8787optimizers: {}subscription_compress: live_zonetoolschema_strip: offbreakpoint_plan: frontierproviders: {}compat: {} | Field | Meaning | | --- | --- | | `label` | Tag on local usage rows. Trials set this through `CAVEMAN_LABEL`. | | `mode` | Runtime mode. Unknown values become `record`. | | `listen` | Loopback `host:port` only. | | `optimizers` | Per-id overrides. Three provider-cache planners default on. | | `subscription_compress` | `live_zone` (or empty) allows eligible subscription compression. `off` and any unknown value disable it. | | `toolschema_strip` | Only `annotations` turns this on. Everything else, including empty, is off. | | `breakpoint_plan` | Cache-breakpoint planner. Default `frontier`. Explicit `off` disables it. Unknown values become `off`. | | `providers` | Endpoint, billing tier, and region overrides. No secrets. | | `compat` | Named OpenAI-compatible mounts. | The three planner ids that default on are `anthropic-cache-breakpoints`, `openai-prompt-cache-key`, and `bedrock-cache-points`. Set any one to `false` to disable it. Planner metadata does not change model-visible bytes. Record mode stays an unconditional pass-through even when they are on. yaml providers: bedrock: region: eu-west-1 azure: base_url: https://example-resource.openai.azure.comcompat: local-model: base_url: http://127.0.0.1:11434/v1 api_key_env: LOCAL_MODEL_API_KEY `caveman start` flags for one run: terminal caveman startcaveman start --port 8787 --host 127.0.0.1caveman start --config /path/to/caveman.yaml `--host` still has to be loopback. The binary refuses anything else. Environment the binary reads[#](https://docs.caveman.so/docs/proxy/configure#environment-the-binary-reads) ----------------------------------------------------------------------------------------------------------- | Variable | Role | | --- | --- | | `CAVEMAN_CONFIG` | Path to `caveman.yaml` | | `CAVEMAN_MODE` | Override YAML mode | | `CAVEMAN_LISTEN` | Override listen address | | `CAVEMAN_LABEL` | Override row label | | `CAVEMAN_HOME` | Override the `~/.caveman` directory | | `CAVEMAN_PROXY_BIN` | Explicit path to the proxy binary | | `CAVEMAN_SUBSCRIPTION_COMPRESS` | Override `subscription_compress` | | `CAVEMAN_TOOLSCHEMA_STRIP` | Override `toolschema_strip` | | `CAVEMAN_BREAKPOINT_PLAN` | Override `breakpoint_plan` | | `CAVEMAN_OBSERVE_ESTIMATE` | Record-mode observe-only estimation | | `CAVE_SSRF_ALLOWLIST` | Exact hosts allowed as private or loopback upstreams | | `CAVE_BEDROCK_REGION` | Bedrock region, before `AWS_REGION` | | `CAVE_BEDROCK_MANTLE_ENABLED` | Enable `/bedrock/anthropic` | Companion binaries resolve in this order: the matching `CAVEMAN_*_BIN` override, then `PATH`, then `~/.caveman/bin`. Local storage[#](https://docs.caveman.so/docs/proxy/configure#local-storage) ----------------------------------------------------------------------------- Default state lives under `~/.caveman`: | Path | Contents | | --- | --- | | `bin/` | Verified companion binaries | | `caveman.db` | Request usage, prefix-replacement cache, trials, learn data | | `ccr.db` | Exact recovery payloads and typed working-memory objects | | `caveman.yaml` | Proxy configuration | | `receipts/` | Local native-agent run receipts when produced | CCR defaults to 512 MiB of payloads, tunable with `CAVEMAN_CCR_MAX_BYTES`. At the cap, new lossy transforms pass through. Existing handles are never evicted. See [Recoverable compression](https://docs.caveman.so/docs/proxy/recoverable) . Protect these files the way you protect agent transcripts. They can contain recovered prompts. Stats[#](https://docs.caveman.so/docs/proxy/configure#stats) ------------------------------------------------------------- terminal caveman statscaveman stats --jsoncaveman-proxy statscaveman-proxy stats --recent 20 Rows are local and `inferred`. They do not become a monthly total or a dollar field. Precedence[#](https://docs.caveman.so/docs/proxy/configure#precedence) ----------------------------------------------------------------------- Feature configuration resolves from defaults, then the global file, then the allowed project overlay, then environment. Proxy mode resolves from default, then YAML, then `CAVEMAN_MODE`. Command flags (`--off`, `--pixel`) select one session. When resolution fails or a mode is unknown, the runtime uses record or original-byte behaviour. It does not guess. --- # Caveman documentation [Skip to content](https://docs.caveman.so/#main) docs.caveman.so Documentation for the parts of Caveman you run yourself. ======================================================== Caveman is an efficiency stack for AI agents. Four layers sit between an agent you already use and the bill it generates, and three of them run on your machine and are open. A second repository, the Agent SDK, is for the other case: writing an agent whose budget is declared rather than discovered. This site documents both, and says plainly what each can and cannot prove. [03CloudWhat a fleet can put in front of financeCommercial](https://docs.caveman.so/docs/cloud) network boundary [02MemoryWhat you never have to send twiceBSL 1.1](https://docs.caveman.so/docs/memory) [01ProxyWhat the payload weighs when it leavesBSL 1.1](https://docs.caveman.so/docs/proxy) [00SkillWhat the agent does before it calls outMIT](https://docs.caveman.so/docs/skill) your machine The stack, bottom to top. Everything below the network boundary runs locally and needs no account. [Start here](https://docs.caveman.so/docs/quickstart) [Read the overview](https://docs.caveman.so/docs) in one picture What happens to a request ------------------------- The skill shapes what the agent writes. The engine reduces what leaves. The dashed path is the part that makes the rest safe: anything the engine removed can be fetched back exactly. Your agentClaude Code, Cursor, yoursSkilldecides what to sendEnginecompresses the payloadProviderthe model APIthis is what you pay forthe agent asks for what was left outMemorywhat you never resend One request, left to right. The dashed path is recovery: the agent can ask for anything the engine left out. get going First three pages ----------------- [Quickstart\ \ Install the skill and compress a real payload in about five minutes.](https://docs.caveman.so/docs/quickstart) [Architecture\ \ The path a request takes through the stack, and where the open-core line falls.](https://docs.caveman.so/docs/architecture) [How numbers are counted\ \ Inferred against verified, per run against per month, and the four rules the code follows.](https://docs.caveman.so/docs/counting) everything there is Two repositories and one hosted plane ------------------------------------- Layers carry a rank and stack bottom to top. Nothing above a layer is required by the one below it, and the Agent SDK needs none of them. cavemanrepository 00[Skill](https://docs.caveman.so/docs/skill) What the agent does before it calls out * [OverviewA set of instructions that changes how your agent spends tokens. No runtime, no proxy.](https://docs.caveman.so/docs/skill) * [caveman learnProfile a local agent session and rank what is actually eating the budget.](https://docs.caveman.so/docs/skill/learn) * [caveman exploreRead a repository without dragging every file into context.](https://docs.caveman.so/docs/skill/explore) * [Agent profilesThe registry of coding agents the skill knows how to configure.](https://docs.caveman.so/docs/skill/profiles) 01[Proxy](https://docs.caveman.so/docs/proxy) What the payload weighs when it leaves * [OverviewA loopback reverse proxy: point an agent at it, keep your provider key, and it shrinks what leaves.](https://docs.caveman.so/docs/proxy) * [Wrap an agentLaunch Claude Code, Codex, Gemini CLI, and seven other agents through the local listener.](https://docs.caveman.so/docs/proxy/wrap) * [ProvidersThe HTTP routes, credentials, and compatibility mounts the proxy forwards.](https://docs.caveman.so/docs/proxy/providers) * [ModesRecord, compress, and pixel, plus the evaluation modes the runtime accepts.](https://docs.caveman.so/docs/proxy/modes) * [ConfigureFeature flags, caveman.yaml, local storage, and how mode is chosen.](https://docs.caveman.so/docs/proxy/configure) * [SecurityLoopback only, BYOK, SSRF checks, and what stays on disk.](https://docs.caveman.so/docs/proxy/security) * [The engineThe compressor the proxy calls, and the standalone binary behind it.](https://docs.caveman.so/docs/proxy/engine) * [CompressorsOne router per content shape: JSON, logs, code, diffs, search results, prose.](https://docs.caveman.so/docs/proxy/compressors) * [Recoverable compressionWhy lossy stays honest: every removed byte can be fetched back.](https://docs.caveman.so/docs/proxy/recoverable) * [Token countingThe offline counter behind every ratio the engine reports.](https://docs.caveman.so/docs/proxy/tokens) 02[Memory](https://docs.caveman.so/docs/memory) What you never have to send twice * [Overviewcavemem holds the context you would otherwise paste into every session.](https://docs.caveman.so/docs/memory) * [Recall and offloadMoving a heavy instruction file out of the prompt and into recall.](https://docs.caveman.so/docs/memory/offload) ··[Build with it](https://docs.caveman.so/docs/cli) The command surface: compress, detect, learn, and agent setup. * [CLIThe command surface: compress, detect, learn, and agent setup.](https://docs.caveman.so/docs/cli) * [TypeScript SDKCompress payloads and read spend from Node.](https://docs.caveman.so/docs/sdk/typescript) * [Python SDKThe same surface for Python agents.](https://docs.caveman.so/docs/sdk/python) * [MCP serverExpose compression and recall as tools any MCP client can call.](https://docs.caveman.so/docs/mcp) * [caveman-shrinkShrink command output before it reaches the model.](https://docs.caveman.so/docs/shrink) * [caveman-browseRead web pages as compressed accessibility trees instead of raw HTML.](https://docs.caveman.so/docs/browse) agent-sdkrepository ··[Agent SDK](https://docs.caveman.so/docs/agent-sdk) Build an agent that is efficient by construction. * [OverviewBuild an agent that is efficient by construction.](https://docs.caveman.so/docs/agent-sdk) * [InstallBuild 0.2 from source, run doctor, and the gap with the npm 0.1 package.](https://docs.caveman.so/docs/agent-sdk/install) * [Create an agentThe initializer that writes a typechecking project with a starter eval graph.](https://docs.caveman.so/docs/agent-sdk/create) * [Define an agentagent(), context, output, filesystem, and subagents as one frozen definition.](https://docs.caveman.so/docs/agent-sdk/define) * [ToolsInput schemas, effect classes, timeouts, and what happens to a tool result.](https://docs.caveman.so/docs/agent-sdk/tools) * [Run a turnrun, stream, receipts, budgets, and durable resume.](https://docs.caveman.so/docs/agent-sdk/run) * [Sandboxrequired, fixture, and host: where tool closures run, and what lock eligibility requires.](https://docs.caveman.so/docs/agent-sdk/sandbox) * [MemoryLocal, opt-in, one-turn-behind recall. Never verified, never in the frozen prefix.](https://docs.caveman.so/docs/agent-sdk/memory) * [CompactionA fail-closed ladder: recover, summarise, clamp, then stop. Only inside a declared budget.](https://docs.caveman.so/docs/agent-sdk/compaction) * [Programmatic toolsOne caveman\_code cell instead of a large JSON tool catalog. Nested calls still go through the runtime.](https://docs.caveman.so/docs/agent-sdk/code) * [ConnectPaginated provider data through one stable tool. Fail closed. The daemon is not in this package.](https://docs.caveman.so/docs/agent-sdk/connect) * [Evals and buildsProfile, search, freeze, holdout. Failed evals never produce a lock.](https://docs.caveman.so/docs/agent-sdk/build) * [AdaptersExact-pinned framework lanes. Presence is not a grant of execution.](https://docs.caveman.so/docs/agent-sdk/adapters) caveman cloudhosted, across a network boundary 03[Cloud](https://docs.caveman.so/docs/cloud) What a fleet can put in front of finance * [What Cloud addsThe managed plane: fleet visibility, spend attribution, and verified numbers.](https://docs.caveman.so/docs/cloud) * [Model routerOne model id: classify the task, apply a cost tier, route to measured evidence, fall back to your baseline.](https://docs.caveman.so/docs/router) the part worth reading twice What the numbers are allowed to say ----------------------------------- Every local tool reports `inferred`, meaning a per-run estimate computed on your machine. Nothing here can emit `verified`, and no setting changes that. [1No fake savings\ \ Local figures stay labelled inferred and are never multiplied into a monthly number.](https://docs.caveman.so/docs/counting#1-no-fake-savings) [2Byte safe\ \ On any error, unsupported input, or result that is not smaller, the original bytes are kept.](https://docs.caveman.so/docs/counting#2-byte-safe) [3Fail closed\ \ Every unknown case resolves toward doing less. Unknown mode means pass-through.](https://docs.caveman.so/docs/counting#3-no-placeholders-fail-closed) [4Recoverable\ \ Nothing lossy is emitted until the original is stored and can be fetched back exactly.](https://docs.caveman.so/docs/counting#4-recoverable-so-lossy-stays-honest) for agents This site is readable without a browser --------------------------------------- Every page is also plain text. You do not need to parse the HTML, and there is no separate copy to go stale: the Markdown is generated from the same source the page renders from. [/llms.txt](https://docs.caveman.so/llms.txt) Every page with a one-line summary, grouped by layer. [/llms-full.txt](https://docs.caveman.so/llms-full.txt) The full text of the whole site in one file. [/docs/proxy.md](https://docs.caveman.so/docs/proxy.md) Any page as plain Markdown. Append .md to the path. [/sitemap.xml](https://docs.caveman.so/sitemap.xml) Machine-readable index of everything that exists. the fine print, up front Licensing and telemetry ----------------------- [Licensing\ \ Which surfaces are MIT, which are BSL 1.1, and what that means for you.](https://docs.caveman.so/docs/licensing) [Telemetry\ \ What the CLI sends, what it never sends, and the three ways to turn it off.](https://docs.caveman.so/docs/telemetry) [Eval graders\ \ The grader set used to check that compression did not change an answer.](https://docs.caveman.so/docs/evals) [Provider catalog\ \ The model price table every cost figure is read from.](https://docs.caveman.so/docs/provider-catalog) [Source\ \ The public repository. Everything documented here is in it.](https://github.com/JuliusBrussee/caveman) --- # CLI · Caveman docs [Skip to content](https://docs.caveman.so/docs/cli#main) `caveman` is the command surface for local tools, supported coding agents, and connected account operations. The same binary is also available as `cave`. npm package `@caveman-ai/cli` Runtime Node.js 22.13 or newer. Licence MIT. JavaScript dependencies None at runtime. Heavy work Companion Go binaries. Install[#](https://docs.caveman.so/docs/cli#install) ----------------------------------------------------- terminal npm install -g @caveman-ai/clicaveman setup The bare `caveman` npm package is unrelated. Use the scoped package name. `setup` shows which companion binaries are available. Install or repair the signed local bundle with: terminal caveman setup --install Artifacts are checked against a key-signed manifest and their own SHA-256 before atomic installation under `~/.caveman/bin`. Resolution checks an explicit `CAVEMAN_*_BIN` override, `PATH`, then that directory. Command groups[#](https://docs.caveman.so/docs/cli#command-groups) ------------------------------------------------------------------- terminal caveman helpcaveman toolscaveman cloud `tools` contains local commands and needs no account. `cloud` contains connected commands and requires login. Compatibility aliases keep older top-level commands working, but grouped discovery is the clearest way to see the current surface. Common local commands: terminal caveman tools compress < payload.jsoncaveman tools shrink -- npm testcaveman tools toon encode < payload.jsoncaveman tools mem recall "migration context"caveman tools retrieve ccr_xxxxxxxxcaveman tools browse snapshot https://example.comcaveman tools evals runcaveman tools stats --jsoncaveman tools config get Some commands also keep their top-level form, such as `caveman learn`, `caveman stats`, `caveman mem`, and `caveman retrieve`. Launch an agent[#](https://docs.caveman.so/docs/cli#launch-an-agent) --------------------------------------------------------------------- terminal caveman claudecaveman codexcaveman geminicaveman aidercaveman hermescaveman kilocaveman openclawcaveman opencodecaveman picaveman qwen Each shortcut calls `caveman wrap `. The profile registry decides binary name, protocol, setup method, hooks, and fallback. See [Wrap an agent](https://docs.caveman.so/docs/proxy/wrap) for what that hop actually does. On the first interactive local wrap, the CLI can install the signed runtime bundle and continue the same command. Non-interactive runs do not change installation state without an explicit setup command. Learn from local sessions[#](https://docs.caveman.so/docs/cli#learn-from-local-sessions) ----------------------------------------------------------------------------------------- terminal caveman learncaveman learn --plaincaveman learn --jsoncaveman learn implement claude The profiler is read-only. Applying a proposed file change belongs to the consent-gated `caveman-learn` skill. See [caveman learn](https://docs.caveman.so/docs/skill/learn) for sink classes and edit gates. Stable pipelines[#](https://docs.caveman.so/docs/cli#stable-pipelines) ----------------------------------------------------------------------- Compression writes payload bytes to stdout and accounting to stderr: terminal cat large.json | caveman tools compress > compact.json 2> report.json When the engine binary is missing, this path emits original bytes and a structured zero-ratio warning. It does not break the pipe or claim compression. Use machine modes when output feeds another process: terminal caveman learn --jsoncaveman learn --plaincaveman stats --jsoncaveman tools skills list --json They do not open interactive menus or prompt for input. Missing runtime behaviour[#](https://docs.caveman.so/docs/cli#missing-runtime-behaviour) ----------------------------------------------------------------------------------------- Most affected local commands degrade to an explicit pass-through when a companion binary is missing. Output stays unchanged, reduction is zero, and the warning names the repair command. `wrap` starts the [local proxy](https://docs.caveman.so/docs/proxy) for that session when it can. If a foreign process already owns the port, wrap will not restart or trust it. An interactive invocation can offer to launch the agent directly when the proxy binary is missing. A script must make sure the listener is up. Numbers and telemetry[#](https://docs.caveman.so/docs/cli#numbers-and-telemetry) --------------------------------------------------------------------------------- Local reduction figures stay `inferred`. Commands do not convert them into monthly savings or `verified` claims. Anonymous CLI telemetry covers command name, version, platform, duration, exit class, and aggregate local token counts. It excludes prompts, code, file paths, arguments, model names, credentials, and dollar fields. Disable it with any of these: terminal caveman telemetry offCAVEMAN_TELEMETRY=0 caveman learnDO_NOT_TRACK=1 caveman learn See [Telemetry](https://docs.caveman.so/docs/telemetry) for the complete payload and precedence. --- # TypeScript SDK · Caveman docs [Skip to content](https://docs.caveman.so/docs/sdk/typescript#main) `@caveman-ai/sdk` is a zero-runtime-dependency TypeScript client for provider calls, recoverable compression, tracing, deferred tools, and request policy. It talks to a Caveman service you configure; it does not embed the local engine. Package `@caveman-ai/sdk` Runtime Node.js 22.13 or newer. Dependencies None at runtime. Module ES module with bundled TypeScript declarations. Licence MIT. Install[#](https://docs.caveman.so/docs/sdk/typescript#install) ---------------------------------------------------------------- terminal npm install @caveman-ai/sdk Create a client[#](https://docs.caveman.so/docs/sdk/typescript#create-a-client) -------------------------------------------------------------------------------- typescript import { Cave } from "@caveman-ai/sdk";const cave = new Cave({ apiKey: process.env.CAVE_API_KEY!, baseURL: "http://127.0.0.1:8787", agent: "support-agent",}); `apiKey`, `baseURL`, and `agent` are required. Service URLs must be absolute HTTP or HTTPS URLs without embedded credentials, query strings, or fragments. Compress[#](https://docs.caveman.so/docs/sdk/typescript#compress) ------------------------------------------------------------------ typescript const result = await cave.compress("large payload");console.log(result.output);console.log(result.tokensBefore, result.tokensAfter);console.log(result.ratio, result.basis);console.log(result.recoveryHandle); The SDK sends the payload to the configured compression endpoint and maps the engine report. On a transport or parse problem it returns original payload, ratio zero, and no recovery handle. It never reimplements a compressor inside JavaScript. `basis` is `inferred`. Token fields come from the compressor's local counter, not provider usage. Provider clients[#](https://docs.caveman.so/docs/sdk/typescript#provider-clients) ---------------------------------------------------------------------------------- typescript const openai = cave.openai({ upstreamKey: process.env.OPENAI_API_KEY });const anthropic = cave.anthropic({ upstreamKey: process.env.ANTHROPIC_API_KEY });const gemini = cave.gemini({ upstreamKey: process.env.GEMINI_API_KEY });const vertex = cave.vertex({ upstreamKey: process.env.GOOGLE_ACCESS_TOKEN });const response = await openai.responses.create({ model: "gpt-5.6", input: "Summarize this incident",}); Provider clients constrain raw requests to their provider prefix. The separate `bedrock()` method returns a validated descriptor for AWS SDK configuration and performs no request. Narrow one request[#](https://docs.caveman.so/docs/sdk/typescript#narrow-one-request) -------------------------------------------------------------------------------------- Request options can switch project-enabled work off for one call: typescript await cave.openai().responses.create(body, { cave: { optimize: "off" },});await cave.openai().responses.create(body, { cave: { optimize: { compress: false, cacheHints: false } },}); Boolean `true` asks for the corresponding capability. It does not grant permission or bypass project policy. Unknown option fields and styles throw before the request. Read disclosure headers[#](https://docs.caveman.so/docs/sdk/typescript#read-disclosure-headers) ------------------------------------------------------------------------------------------------ typescript import { parseReceipt } from "@caveman-ai/sdk";const response = await cave.openai().raw("/v1/responses", { method: "POST", body,});const receipt = parseReceipt(response.headers); The receipt may include mode, applied optimizations, cache status, request id, inferred compression counts, and a recovery handle. A missing header remains absent. The parser does not invent a default value. Local helpers[#](https://docs.caveman.so/docs/sdk/typescript#local-helpers) ---------------------------------------------------------------------------- Several helpers run without a request: * `assemble()` orders stable, session, and volatile context slots * `retryLoopBreaker()` stops a consecutive identical tool-call loop * `parseReceipt()` decodes response headers * `gatewayHeaders()` and `gatewayConfig()` build provider client configuration * `policyUnitFraction()` produces deterministic policy assignment input `trace()` and `exporter()` create correlated spans. `tools()` can keep an initial subset of a catalog and load more through connected search. `context.pack()` is also connected and returns deferred item ids rather than silently discarding omitted context. The `jobs` surface is reserved. Its methods throw `cave_async_jobs_unavailable` locally and send no request. What it will not claim[#](https://docs.caveman.so/docs/sdk/typescript#what-it-will-not-claim) ---------------------------------------------------------------------------------------------- SDK compression counts are not provider counts. Response receipts are not invoices. No local SDK result becomes a monthly or `verified` saving. --- # Python SDK · Caveman docs [Skip to content](https://docs.caveman.so/docs/sdk/python#main) `caveman-sdk` is the Python client for the same connected surface as the TypeScript SDK. It uses only the Python standard library and includes type information. Distribution `caveman-sdk` Import `caveman_cloud` Runtime Python 3.13 or newer. Dependencies None at runtime. Licence MIT. Install[#](https://docs.caveman.so/docs/sdk/python#install) ------------------------------------------------------------ terminal python -m pip install caveman-sdk The `caveman` package on PyPI is unrelated. Distribution and import names are deliberately different. Create a client[#](https://docs.caveman.so/docs/sdk/python#create-a-client) ---------------------------------------------------------------------------- python import osfrom caveman_cloud import Cavecave = Cave( api_key=os.environ["CAVE_API_KEY"], base_url="http://127.0.0.1:8787", agent="support-agent",) `api_key`, `base_url`, and `agent` are required. URLs must be absolute HTTP or HTTPS service URLs without embedded credentials, a query, or a fragment. Compress[#](https://docs.caveman.so/docs/sdk/python#compress) -------------------------------------------------------------- python result = cave.compress("large payload")print(result.output)print(result.tokens_before, result.tokens_after)print(result.ratio, result.basis)print(result.recovery_handle) `compress()` delegates to the configured service. A transport or response problem returns original payload, ratio zero, and no recovery handle. Python does not carry a second compressor implementation. Counts use `basis="inferred"`. They are local compressor estimates, not provider-reported usage. Provider clients[#](https://docs.caveman.so/docs/sdk/python#provider-clients) ------------------------------------------------------------------------------ python openai = cave.openai(upstream_key=os.environ.get("OPENAI_API_KEY"))anthropic = cave.anthropic(upstream_key=os.environ.get("ANTHROPIC_API_KEY"))gemini = cave.gemini(upstream_key=os.environ.get("GEMINI_API_KEY"))vertex = cave.vertex(upstream_key=os.environ.get("GOOGLE_ACCESS_TOKEN"))response = openai.responses.create({ "model": "gpt-5.6", "input": "Summarize this incident",}) The provider wrappers expose native request paths through the configured provider prefix. `bedrock()` returns a validated configuration descriptor without making a network call. Narrow one request[#](https://docs.caveman.so/docs/sdk/python#narrow-one-request) ---------------------------------------------------------------------------------- python cave.openai().responses.create(body, optimize="off")cave.openai().responses.create( body, optimize={"compress": False, "cache_hints": False},) Boolean `True` asks for a capability and remains subject to project policy. It does not enable anything by itself. Unknown fields and invalid styles raise `ValueError` before a request. Read disclosure headers[#](https://docs.caveman.so/docs/sdk/python#read-disclosure-headers) -------------------------------------------------------------------------------------------- python from caveman_cloud import parse_receiptreceipt = parse_receipt(response.headers)print(receipt.mode, receipt.optimizations)print(receipt.tokens_before, receipt.tokens_after)print(receipt.recovery_handle) `parse_receipt` accepts urllib headers, a dictionary, or key-value pairs. Missing headers stay `None`. Local helpers[#](https://docs.caveman.so/docs/sdk/python#local-helpers) ------------------------------------------------------------------------ Python mirrors the TypeScript names in Python style: * `assemble()` orders context by stability * `retry_loop_breaker()` interrupts identical consecutive tool calls * `parse_receipt()` decodes response disclosure * `gateway_headers()` and `gateway_config()` build provider configuration * `policy_unit_fraction()` returns deterministic policy assignment input Tracing, OTLP export, deferred tool search, context packing, shared context, checkpoints, and artifacts are also available. Connected features use the configured service. The reserved jobs client raises `cave_async_jobs_unavailable` locally and performs no request. What it cannot do alone[#](https://docs.caveman.so/docs/sdk/python#what-it-cannot-do-alone) -------------------------------------------------------------------------------------------- Installing the Python package does not install or start the local engine. It does not turn inferred compression counts into provider usage, currency, monthly savings, or `verified` results. --- # caveman-shrink · Caveman docs [Skip to content](https://docs.caveman.so/docs/shrink#main) `caveman-shrink` reduces MCP and OpenAI tool catalogs before a model reads them. It keeps the structural surface used to form a tool call and shortens model-visible annotation text. Input MCP or OpenAI tool-catalog JSON. Stdin limit 32 MiB. Core licence BSL 1.1. npm launcher MIT. Reports `inferred` Build the documented source[#](https://docs.caveman.so/docs/shrink#build-the-documented-source) ------------------------------------------------------------------------------------------------ terminal git clone https://github.com/JuliusBrussee/cavemango build -o ./bin/caveman-shrink ./public/shrink/cmd/caveman-shrink A published npm launcher also downloads a matching binary and verifies a key-signed manifest plus artifact SHA-256. The registry release may trail the source documented here. terminal npx -y caveman-shrink lint tools.json Compress a catalog[#](https://docs.caveman.so/docs/shrink#compress-a-catalog) ------------------------------------------------------------------------------ terminal cat tools.json | caveman-shrink > tools.min.json Compressed JSON goes to stdout. The JSON accounting report goes to stderr, so a pipeline receives catalog bytes only. The report includes tokens before, tokens after, ratio, basis, content type, and a recovery handle when a lossy result was emitted. Accepted shapes include an MCP `{ "tools": [...] }` object, an OpenAI tool array, a `{ "functions": [...] }` object, and a single named tool object. What survives[#](https://docs.caveman.so/docs/shrink#what-survives) -------------------------------------------------------------------- The structural selection profile preserves: * Tool and parameter names * Parameter types * Enum values * Required fields * Default and constant values * Internal reference targets Short descriptions remain whole. Long descriptions keep their lead and recognized constraint sentences. Examples, titles, comments, and other annotation bloat may be removed. terminal caveman-shrink lint tools.json `lint` prints inferred before and after counts per tool and for the complete catalog. It does not commit a recovery record or replace the file. Recover[#](https://docs.caveman.so/docs/shrink#recover) -------------------------------------------------------- terminal caveman-shrink recover ccr_xxxxxxxx > tools.original.json Before returning a lossy view, shrink stores exact original bytes in the shared CCR database. The default is `~/.caveman/ccr.db`; `CAVEMAN_CCR_DB` selects another path. Recovery works from a later process. Malformed input, an unavailable recovery store, or a candidate that is not smaller returns the original catalog unchanged with ratio zero and no handle. Input beyond 32 MiB fails with `cave_input_too_large`. CLI wrapper[#](https://docs.caveman.so/docs/shrink#cli-wrapper) ---------------------------------------------------------------- The main CLI exposes two different shrink paths: terminal caveman tools shrink -- npm testcaveman tools compress catalog < tools.jsoncaveman tools compress catalog lint tools.jsoncaveman tools compress catalog recover ccr_xxxxxxxx The first command compresses command output. The catalog commands delegate to `caveman-shrink`. They are separate because terminal output and tool definitions have different structures and safety contracts. Licence boundary[#](https://docs.caveman.so/docs/shrink#licence-boundary) -------------------------------------------------------------------------- The npm launcher is MIT. Go source and downloaded binary use BSL 1.1 with the first-party self-hosting grant. Offering the core functionality to third parties as a hosted, managed, or embedded service needs a commercial licence. --- # caveman-browse · Caveman docs [Skip to content](https://docs.caveman.so/docs/browse#main) `caveman-browse` is a local Chrome driver for agents. It reads Chrome's accessibility tree, turns it into compact UID-addressed text, and stores raw tree bytes behind a recovery handle. Transport MCP over stdio, with a direct CLI helper. Browser path Chrome DevTools Protocol. Snapshots Compressed accessibility text, not raw HTML. Basis `inferred` Licence BSL 1.1. Build from source[#](https://docs.caveman.so/docs/browse#build-from-source) ---------------------------------------------------------------------------- `caveman-browse` is not published on npm today. terminal git clone https://github.com/JuliusBrussee/cavemango build -o ./bin/caveman-browse ./public/browse/cmd/caveman-browse Run with no arguments to serve MCP over stdio: terminal ./bin/caveman-browse MCP tools[#](https://docs.caveman.so/docs/browse#mcp-tools) ------------------------------------------------------------ | Tool | Purpose | | --- | --- | | `browser_snapshot` | Navigate when a URL is supplied, wait, then return a compact accessibility view. | | `browser_act` | Click, type, select, scroll, or wait using a UID from the latest snapshot. | | `browser_eval` | Evaluate a JavaScript expression in the page. | | `browser_recover` | Return raw accessibility bytes, or a query-selected view of them. | Configure an MCP host with the absolute binary path: json { "mcpServers": { "caveman-browse": { "command": "/absolute/path/to/bin/caveman-browse", "args": [] } }} Take a useful snapshot[#](https://docs.caveman.so/docs/browse#take-a-useful-snapshot) -------------------------------------------------------------------------------------- Large pages should include the task as a query: text browser_snapshot({ url: "https://example.com/settings", query: "save notification settings"}) The query keeps matching accessible nodes and their ancestors. CCR retains full raw tree. This usually costs less than taking a full snapshot and recovering later. `interactive: true` keeps UID-bearing nodes and their ancestors only. It is useful for dense control surfaces but hides most page text. Query focus remains the better default when the agent must read. Snapshot output is text: text [a1] button "Save"[a2] checkbox "Email alerts"caveman before=... view=... after=... ratio=... basis=inferred handle=ccr_... Only actionable or unknown custom roles receive UID tokens. The trailing line counts the exact result delivered to the agent. `view` isolates compact tree cost; `after` includes the accounting line. Act, then resnapshot[#](https://docs.caveman.so/docs/browse#act-then-resnapshot) --------------------------------------------------------------------------------- text browser_act({ action: "click", uid: "a1" })browser_act({ action: "type", uid: "a3", text: "alerts@example.com" })browser_act({ action: "select", uid: "a4", option: "Daily" })browser_act({ action: "scroll", uid: "a5" })browser_act({ action: "wait" }) UIDs belong to the latest successful snapshot. Unknown or stale UIDs fail explicitly. Click, type, select, and scroll report that dispatch succeeded, but they do not prove application state settled. Take another focused snapshot to verify the result. `wait` is a fixed short delay, not a condition-based page assertion. Direct CLI[#](https://docs.caveman.so/docs/browse#direct-cli) -------------------------------------------------------------- terminal caveman-browse snapshot https://example.com "save settings"caveman-browse snapshot -i https://example.comcaveman-browse act clickcaveman-browse eval "document.title"caveman-browse recover caveman-browse close Direct commands share one detached, isolated Chrome until `close`. Navigation and limits[#](https://docs.caveman.so/docs/browse#navigation-and-limits) ------------------------------------------------------------------------------------ Navigation accepts HTTP, HTTPS, `about:blank`, and bounded `data:text/html` URLs. Local files and privileged browser schemes fail with `cave_browser_url_denied`. Snapshot wait is limited to 30 seconds. Query text is limited to 4 KiB, and a data URL to 1 MiB. A snapshot that cannot produce a smaller recovery-backed UID view fails and keeps the previous UID map rather than dumping raw AX JSON into model context. Browser selection[#](https://docs.caveman.so/docs/browse#browser-selection) ---------------------------------------------------------------------------- Environment variables select runtime behaviour: text CAVEMAN_BROWSE_CHROME explicit Chrome executableCAVEMAN_BROWSE_CDP attach to an existing CDP endpointCAVEMAN_BROWSE_USER_DATA_DIR profile directoryCAVEMAN_BROWSE_HEADFUL=1 show the browser windowCAVEMAN_BROWSE_EPHEMERAL=1 use in-memory recovery Default mode is headless. The recovery store otherwise uses the shared CCR path. `browser_eval` runs caller-supplied JavaScript in the active page. Treat it as code execution in that browser session and use it only on pages you intend to control. --- # MCP server · Caveman docs [Skip to content](https://docs.caveman.so/docs/mcp#main) `caveman-mcp` exposes engine compression, recovery, statistics, and TOON conversion to any MCP host. It speaks line-delimited JSON-RPC over stdin and stdout and opens no network connection. Transport MCP over stdio. Protocol version `2024-11-05` Tools Compression, recovery, session stats, TOON encode, and TOON decode. Reports `inferred` Core licence BSL 1.1. Build from source[#](https://docs.caveman.so/docs/mcp#build-from-source) ------------------------------------------------------------------------- `caveman-mcp` is not published on npm today. Build the server from the public source: terminal git clone https://github.com/JuliusBrussee/cavemango build -o ./bin/caveman-mcp ./public/mcp/cmd/caveman-mcp Register the resulting binary with an MCP host: json { "mcpServers": { "caveman": { "command": "/absolute/path/to/bin/caveman-mcp", "args": [] } }} The main CLI can install host configuration after the binary is available: terminal caveman tools mcp install claude --server cavemancaveman tools mcp uninstall claude --server caveman Tools[#](https://docs.caveman.so/docs/mcp#tools) ------------------------------------------------- | Tool | Input | Result | | --- | --- | --- | | `caveman_compress` | `input`, optional `content_type` | Smaller text or original input, counts, ratio, basis, content type, method, and optional handle. | | `caveman_retrieve` | `recovery_handle`, optional `query` | Exact original for an empty query, or a query-selected view. | | `caveman_stats` | None | Session requests, tokens before and after, ratio, and `basis: "inferred"`. | | `caveman_toon_encode` | JSON string in `input` | TOON text plus input and output byte counts, or original input with a note. | | `caveman_toon_decode` | TOON string in `input` | Decoded JSON, or an explicit invalid-TOON error. | Compression detects content type unless the caller forces one. Malformed, incompressible, or non-smaller input passes through with ratio zero. A recovery persistence error also passes through only when the returned bytes and accounting prove that no lossy view escaped. Recovery[#](https://docs.caveman.so/docs/mcp#recovery) ------------------------------------------------------- The server opens the shared store at `~/.caveman/ccr.db`. `CAVEMAN_CCR_DB` selects another path, and `CAVEMAN_HOME` changes the default parent directory. text caveman_compress({ input: largePayload })caveman_retrieve({ recovery_handle: "ccr_..." }) Set `CAVEMAN_MCP_EPHEMERAL=1` for an in-memory store. Handles from that process stop resolving when the process exits. Use recovery as a last resort. Elision markers and visible invariants often answer count or field questions without adding another agent turn. When recovery is needed, one broad query is usually cheaper than many narrow calls. An unknown handle returns `cave_unknown_handle`. It never becomes an empty successful result. TOON conversion[#](https://docs.caveman.so/docs/mcp#toon-conversion) --------------------------------------------------------------------- TOON encoding is explicit and checks JSON round-trip. Encoding may return a valid TOON result even when it is not smaller, because the caller asked for conversion and receives both sizes. Invalid or unsupported JSON returns unchanged input with a note. Decoding fails with `cave_invalid_toon` on invalid input. It never emits raw TOON while claiming the result is JSON. Protocol limits[#](https://docs.caveman.so/docs/mcp#protocol-limits) --------------------------------------------------------------------- Inbound JSON-RPC lines and ordinary tool results are capped at 16 MiB. Oversized values return `cave_payload_too_large` and the server continues serving later requests. Exact recovery is exempt from the result cap so a valid original is never made unrecoverable by the MCP framing limit. Handler panics become `cave_tool_panicked` tool errors instead of terminating the server. Unknown tools also return an explicit tool error. What it cannot do[#](https://docs.caveman.so/docs/mcp#what-it-cannot-do) ------------------------------------------------------------------------- The server does not browse, store durable semantic memories, call a model, or connect to a hosted account. Its session stats are local inferred compression accounting, not provider usage or `verified` savings. --- # Overview · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk#main) `@caveman-ai/agent` is a TypeScript runtime for tool-using agents. You define the agent, its tools, context, memory, output contract, and evals in one source graph. Every successful run returns a receipt. A run that fails after spending throws with the same partial receipt attached. It does not need a Caveman account. On a machine with Node and a provider key it calls the provider directly, in `observe-only` mode: no local transform, and no efficiency claim. When the local Caveman engine is running, eligible calls can run in `optimized` mode. Local figures stay `inferred`. Nothing in this package emits `verified`. Package `@caveman-ai/agent` Source version `0.2.0` On npm today `0.1.0` Runtime Node.js 22.19 or newer. Licence MIT. The pages that follow document the 0.2 source in [JuliusBrussee/agent-sdk](https://github.com/JuliusBrussee/agent-sdk) . npm still ships 0.1.0, which is an older surface. Use the checkout if you are testing anything described here. How a run is assembled[#](https://docs.caveman.so/docs/agent-sdk#how-a-run-is-assembled) ----------------------------------------------------------------------------------------- **Definition.** agent(), tools, context, memory, output, evals. One source graph. ↓ **Runtime.** Sandbox, budgets, breakers, compaction, receipts. Tools never skip schema, timeout, or abort checks. ↓ observe-only Direct to your provider. Default when the local engine is not running. optimized Local engine reachable. Eligible transforms and recovery can run. Still inferred. ↓ **Receipt.** Model calls, tool calls, catalog cost basis, stop reason, compactions, subagents, resume state. claimBasis: "inferred". A mixed graph that partly bypassed the local runtime reports observe-only. The label under-claims rather than averaging. `auto()` picks a configured model. It does not classify tasks or route between models. That is a different product; see [Model router](https://docs.caveman.so/docs/router) . Smallest agent[#](https://docs.caveman.so/docs/agent-sdk#smallest-agent) ------------------------------------------------------------------------- typescript import { agent, auto, run } from "@caveman-ai/agent";const support = agent({ id: "support", instructions: "Answer from policy. Never invent policy.", model: auto(),});const result = await run(support, "Can I get a refund?");console.log(result.text);console.log(result.mode);console.log(result.receipt); That run works with nothing but Node and a provider credential. `result.mode` is `observe-only` there. Prefer a directory if the agent will grow: text support-bot/├── instructions.md├── agent.ts├── skills/├── tools/├── subagents/└── evals/ typescript import { loadAgentDir, run } from "@caveman-ai/agent";const support = await loadAgentDir("./support-bot");const result = await run(support, "Where is order A-123?"); Skill descriptions enter the stable prefix. Skill bodies stay on disk until the model asks for one, so adding a large skill does not enlarge every request. What lives in this product[#](https://docs.caveman.so/docs/agent-sdk#what-lives-in-this-product) ------------------------------------------------------------------------------------------------- The Agent SDK is one thing you install, with several surfaces behind it. [Install\ \ Source build, doctor, and the npm version gap.](https://docs.caveman.so/docs/agent-sdk/install) [Create an agent\ \ The initializer that writes a typechecking project.](https://docs.caveman.so/docs/agent-sdk/create) [Define an agent\ \ agent(), context, output, filesystem, subagents.](https://docs.caveman.so/docs/agent-sdk/define) [Tools\ \ Effects, result policies, schemas.](https://docs.caveman.so/docs/agent-sdk/tools) [Run a turn\ \ run, stream, receipts, budgets, durable resume.](https://docs.caveman.so/docs/agent-sdk/run) [Sandbox\ \ required, fixture, and host.](https://docs.caveman.so/docs/agent-sdk/sandbox) [Memory\ \ Next-turn recall, local only.](https://docs.caveman.so/docs/agent-sdk/memory) [Compaction\ \ Recover, summarise, clamp, stop.](https://docs.caveman.so/docs/agent-sdk/compaction) [Programmatic tools\ \ One caveman\_code cell.](https://docs.caveman.so/docs/agent-sdk/code) [Connect\ \ Paginated provider data, fail closed.](https://docs.caveman.so/docs/agent-sdk/connect) [Evals and builds\ \ Profile, search, freeze, holdout.](https://docs.caveman.so/docs/agent-sdk/build) [Adapters\ \ Exact-pinned framework lanes.](https://docs.caveman.so/docs/agent-sdk/adapters) The [TypeScript SDK](https://docs.caveman.so/docs/sdk/typescript) is a different package. `@caveman-ai/sdk` talks to a configured Caveman service. `@caveman-ai/agent` is the agent runtime. Installing one does not start the other. The Agent SDK repository also ships a frozen wire-contract package used by other Caveman products. It is not this runtime, and this site does not document it. What it will not do[#](https://docs.caveman.so/docs/agent-sdk#what-it-will-not-do) ----------------------------------------------------------------------------------- It will not mint `verified` savings. `claimBasis` is `inferred` on every public run. USD figures are public-catalog list-price subtotals, never invoices. Token counts are not money. It will not silently call itself optimized. If any call in the graph went straight to the provider, `mode` is `observe-only`. It will not invent a routing policy from `auto()`. A pinned string model is a pinned string model. A lock hash binds reviewed bytes and policy. It is not a signature, a runtime attestation, or proof that those bytes served production traffic. Claude locked compilation is refused. The public Claude lane stays unlocked. Do not publish a savings percentage from a local build report. --- # Create an agent · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/create#main) `@caveman-ai/create-agent` writes a TypeScript agent project that typechecks, loads under doctor, and has a starter eval graph. It has no runtime of its own. The generated project depends on `@caveman-ai/agent`. Package `@caveman-ai/create-agent` Source version `0.1.0` On npm today `0.1.0` Node 22.19 or newer. Licence MIT. npm has published this package. The template it currently ships targets `@caveman-ai/agent` 0.2. A generated project will not resolve that dependency from the registry until 0.2 is published; build the Agent SDK from source and point the project at that checkout, or wait for the registry. Scaffold[#](https://docs.caveman.so/docs/agent-sdk/create#scaffold) -------------------------------------------------------------------- From a published initializer: terminal npm create @caveman-ai/agent@latest my-agentcd my-agent From the Agent SDK checkout, after `npm ci --prefix packages/create-caveman-agent` and its build: terminal node packages/create-caveman-agent/dist/index.js my-agent Non-interactive: terminal npm create @caveman-ai/agent@latest my-agent -- --provider anthropic Supported providers: `anthropic`, `openai`, `google`. Exactly one detected provider credential is selected without a prompt. Zero or several credentials prompt once. Secrets are never printed or written. Skip dependency installation when another tool owns it: terminal npm create @caveman-ai/agent@latest my-agent -- --provider openai --no-install The initializer copies the `support-bot` template, rewrites the pinned model line for the chosen provider, and writes `.caveman/provider.json` with mode `0o600`. | Provider | Model written into `agent.ts` | | --- | --- | | `anthropic` | `anthropic/claude-sonnet-5` | | `openai` | `openai/gpt-5.4-mini` | | `google` | `google/gemini-2.5-flash` | Those pins exist so the frozen prefix clears the provider's minimum cacheable length. `auto()` is not the template default, because it can pick a model whose cache floor the prefix does not meet. What you get[#](https://docs.caveman.so/docs/agent-sdk/create#what-you-get) ---------------------------------------------------------------------------- text my-agent/├── instructions.md├── agent.ts├── run.ts├── caveman.config.ts├── AGENTS.md├── skills/├── tools/│ └── lookup_order.ts├── tickets/├── evals/│ └── support.eval.ts└── package.json Scripts in the generated `package.json`: terminal npm run doctornpm run ticket -- tickets/refund-request.mdnpm run devnpm run buildnpm run check Review `evals/support.eval.ts` before `npm run build`. That command runs every declared fixture inside the configured search budget. There is no separate approval gate. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/create#what-it-will-not-do) ------------------------------------------------------------------------------------------ The initializer does not log you in, start the local engine, or spend a provider call. Doctor on the generated project still makes no model request. Local build evidence stays `inferred`; verified savings stay `$0`. --- # Install · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/install#main) The documented surface is `@caveman-ai/agent` 0.2 in source. npm currently publishes 0.1.0, which is an older API. Clone the repository if you are following these pages. Node 22.19 or newer. CLI binary `caveman-agent` Source `JuliusBrussee/agent-sdk` npm today `@caveman-ai/agent@0.1.0` Build 0.2 from source[#](https://docs.caveman.so/docs/agent-sdk/install#build-02-from-source) ---------------------------------------------------------------------------------------------- terminal git clone https://github.com/JuliusBrussee/agent-sdk.gitcd agent-sdknpm ci --prefix packages/pebble-protocolnpm ci --prefix packages/agentnpm ci --prefix packages/create-caveman-agentnpm --prefix packages/agent run build The package binary is `caveman-agent`, from `packages/agent`. After the build you can run it with `node packages/agent/dist/cli.js`, or link the package into a project that depends on `@caveman-ai/agent@0.2.0`. A full repository test pass is `npm test` at the repo root. Restricted macOS sandbox and loopback tests need host permissions before a failure counts as a product defect. Check the machine before a provider call[#](https://docs.caveman.so/docs/agent-sdk/install#check-the-machine-before-a-provider-call) ------------------------------------------------------------------------------------------------------------------------------------- terminal caveman-agent doctorcaveman-agent doctor --json Doctor makes no model request. Human output prints `verified savings: $0`. | Check | Pass | Warn | Fail | | --- | --- | --- | --- | | `node` | 22.19 or newer | | older Node | | `sandbox` | containment probe passed | | probe failed | | `engine` | transform registry loaded | engine missing; observe-only still works | | | `runtime_cli` | `caveman` CLI answers `version` | CLI missing; observe-only still works | | | `gateway` | local runtime reachable | not reachable; telemetry off, observe-only | | | `project` | `caveman.config.ts` loads | no config in this directory | config, entry, eval, or context error | | `lock` | lock is current | no lock; unlocked runs are fine | lock drift | | `provider` | selected model has a credential | no model, or several credentials and no `CAVE_MODEL` | bad `CAVE_MODEL` or `provider.json` | Missing engine, runtime CLI, or local runtime is a warning and doctor still exits 0, because observe-only runs still reach a real model. Broken sandbox containment, invalid config, or lock drift fails. Optional local engine: terminal npm install -g @caveman-ai/clicaveman start The Agent SDK accepts a loopback runtime only after identity, process state, and executable ownership check out. An unrelated listener on the same port does not receive provider credentials; the run goes direct in observe-only mode. CLI[#](https://docs.caveman.so/docs/agent-sdk/install#cli) ----------------------------------------------------------- terminal caveman-agent --helpcaveman-agent --versioncaveman-agent doctor [--json]caveman-agent dev [entry] [prompt]caveman-agent build [config] [--verbose] [--accept-prefix-shrink]caveman-agent check [config]caveman-agent registercaveman-agent connect … `dev` watches the project and reuses one staged source snapshot until source, config, eval, or file context changes. `build` and `check` own Cave Build execution. `register` is a separate command; it is not a substitute for `build`. A generated project wires the same commands through npm scripts: `npm run doctor`, `npm run dev`, `npm run build`, `npm run check`. npm 0.1.0[#](https://docs.caveman.so/docs/agent-sdk/install#npm-010) --------------------------------------------------------------------- terminal npm install @caveman-ai/agent That installs 0.1.0 today. These pages describe 0.2 source. Do not treat a registry install as the surface documented here until 0.2 is published. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/install#what-it-will-not-do) ------------------------------------------------------------------------------------------- Doctor does not spend a provider call, and a green doctor is not a lock. Observe-only is a valid run mode, not a broken install. Linking `@caveman-ai/cli` is optional; without it the SDK still calls your provider. --- # Define an agent · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/define#main) An agent is one frozen `AgentDefinition`. `agent()` is the constructor. Everything downstream, sandbox, Context IR, build, receipts, sees that object. There is no second hidden definition. typescript import { agent, auto } from "@caveman-ai/agent";export default agent({ id: "support", instructions: "Answer from policy. Never invent policy.", model: auto(),}); `id` must match `^[a-z0-9][a-z0-9_-]{0,95}$`. Duplicate tool names fail at construction. Tool names starting with `cave_` are reserved and also fail at construction. Default sandbox is `required`. Default reasoning is `low`. Options: `off`, `minimal`, `low`, `medium`, `high`. Model selection[#](https://docs.caveman.so/docs/agent-sdk/define#model-selection) ---------------------------------------------------------------------------------- `model` is one of: `auto()`, a `provider/model` string, or a Pi model object. `auto()` resolves in this order: `CAVE_MODEL`, then `.caveman/provider.json`, then the baseline model for the sole supported credential in the environment. | Credential | Baseline if it is the only one | | --- | --- | | `ANTHROPIC_API_KEY` | `anthropic/claude-haiku-4-5` | | `OPENAI_API_KEY` | `openai/gpt-5.4-mini` | | `GEMINI_API_KEY` or `GOOGLE_API_KEY` | `google/gemini-2.5-flash` | Zero credentials fail. Several credentials and no `CAVE_MODEL` fail. A pinned string must use `provider/model` form. `auto()` never classifies the task or picks between models on quality. Context and output[#](https://docs.caveman.so/docs/agent-sdk/define#context-and-output) ---------------------------------------------------------------------------------------- typescript import { context, file, output, schema } from "@caveman-ai/agent";const playbook = context({ id: "support.playbook", kind: "skill", source: file("./support.md"), stability: "build", safety: "S0", priority: "required",});const answer = output({ maxTokens: 500, schema: schema.object({ answer: schema.string() }),}); `context()` labels a segment. Kind, stability, safety class, priority, recovery, cache region, and privacy class are all explicit. Defaults: safety `S0`, priority `required`, recovery `none`, privacy `local_sensitive`, cache region `frozen_prefix` when stability is `build` and `live_zone` otherwise. Build-stable context enters the frozen prefix. Session and turn context stay live. Volatile data in the stable cache zone is rejected. On drift or transform failure the runtime fails open to the original provider-visible bytes. `output({ maxTokens })` is a token budget on the answer. An optional schema constrains the shape. Filesystem layout[#](https://docs.caveman.so/docs/agent-sdk/define#filesystem-layout) -------------------------------------------------------------------------------------- `loadAgentDir(rootDir)` lowers a convention directory into an ordinary `agent()` call. text support-bot/├── instructions.md├── agent.ts├── skills/│ ├── refund-policy.md│ └── shipping-claims.md├── tools/│ └── lookup_order.ts├── subagents/└── evals/ `agent.ts` default-exports an `AgentDirConfig`: model, optional budget, breakers, and extra context. Instructions come from `instructions.md`. Each `tools/*.ts` default-exports a `tool()`. Each `skills/.md` contributes a short description to one build-stable skill segment; the body is served later by `cave_skill` as a live-zone tool result. The model picks from descriptions. There is no embedding ranker. The loader writes a generated module entry at `.caveman/agent-dir-entry.mjs`. Directory-loaded runs print a receipt by default, because stdout is yours. `run()` from a hand-built definition does not, because stdout may be a protocol channel. Subagents[#](https://docs.caveman.so/docs/agent-sdk/define#subagents) ---------------------------------------------------------------------- typescript import { agent, auto, subagent } from "@caveman-ai/agent";const researcher = agent({ id: "researcher", instructions: "Search, then return sources.", model: auto(),});const root = agent({ id: "support", instructions: "Delegate research. Answer from policy.", model: auto(), tools: [ subagent({ name: "research", description: "Look up one question.", agent: researcher, }), ],}); Defaults: `maxInputChars` 32,768, `maxCalls` 1, `maxCostUsd` 1, `maxContextTokens` 128,000. Under a USD-metered parent the child's wallet is carved from the remaining budget and unused remainder returns. A token-metered parent cannot fund a child that declares no `maxTokens`. A host-mode child is refused under a `sandbox: "required"` parent. Optional workspace files[#](https://docs.caveman.so/docs/agent-sdk/define#optional-workspace-files) ---------------------------------------------------------------------------------------------------- The core runtime does not search the repository. Products that want `AGENTS.md`, Agent Skills, Agent Plugins v1, or Vercel OpenPlugin can opt in: typescript import { agent } from "@caveman-ai/agent";import { applyAgentEnvironment, loadAgentEnvironment,} from "@caveman-ai/agent/plugins";const environment = await loadAgentEnvironment({ cwd: process.cwd() });const reviewer = applyAgentEnvironment(agent({ id: "reviewer", instructions: "Review requested changes.", model: "openai/gpt-5.4", sandbox: "host",}), environment); Metadata enters the stable prefix. Full skill bodies and command markdown enter only after activation. Plugin MCP, hooks, custom agents, and plugin subprocesses are reported and not launched. Ambient secrets are not inherited. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/define#what-it-will-not-do) ------------------------------------------------------------------------------------------ `agent()` does not start a process, open a network connection, or look for files. Shared-memory provenance is refused here, at construction, not after a model call. A definition with `sandbox: "host"` cannot produce a Cave Build lock. --- # Tools · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/tools#main) A tool declares its input, side effect, timeout, and what happens to the result, then implements `execute`. The runtime validates input before your code runs, and validates output before any result can enter model context or a durable journal. typescript import { schema, tool } from "@caveman-ai/agent";const lookupPolicy = tool({ name: "lookup_policy", description: "Read current refund policy.", input: schema.object({ region: schema.string() }), output: schema.object({ region: schema.string(), refundWindowDays: schema.number(), }), effect: "read", result: "auto", async execute({ region }) { return { region, refundWindowDays: 14 }; },}); Names match `^[a-zA-Z][a-zA-Z0-9_-]{0,127}$`. Names beginning with `cave_` are reserved by the framework (`cave_skill`, memory tools, recovery). Duplicate names on one agent fail at `agent()`. Default timeout is 30,000 ms. Default result policy is `auto`. `effect` has no default; you have to say it. Effects[#](https://docs.caveman.so/docs/agent-sdk/tools#effects) ----------------------------------------------------------------- | Effect | Meaning | | --- | --- | | `read` | No durable side effect. Fixture sandbox allows it. Programmatic mode may speculate it. | | `write` | Mutates something. Fixture sandbox blocks it. Host mode executes it. | | `idempotent` | Safe to retry with the same arguments. Never speculated. | | `external` | Leaves the process. Never speculated. | Repeated identical calls are stopped by default. Set `allowRepeat: true` on a polling tool whose job is to be called again with the same arguments. Result policies[#](https://docs.caveman.so/docs/agent-sdk/tools#result-policies) --------------------------------------------------------------------------------- | Policy | What the model sees | | --- | --- | | `auto` | A locked plan may choose inline, paging, compression, or exact recovery. | | `inline` | The result stays in current context. | | `page` | Bounded pages. | | `compress` | An eligible locked transform. | | `exact_ccr` | Replacement only after byte-exact recovery is stored. | `artifact()` can stand in for a result policy when you need paging or exact recovery with an inline token cap. Default artifact strategy is `page`, default `maxInlineTokens` is 1,200, default recovery is `exact_ccr`. Schemas[#](https://docs.caveman.so/docs/agent-sdk/tools#schemas) ----------------------------------------------------------------- `schema` is a small TypeBox wrapper: `string`, `number`, `integer`, `boolean`, `object`, `array`, `optional`, `union`, `literal`, `null`, `any`. `input` also accepts Standard Schema v1. Libraries that emit Standard JSON Schema v1 convert to draft-07 for the provider. Validation-only libraries pass `inputJSONSchema`; the framework still runs the vendor validator first, including async transforms. `output` follows the same rules. Output validation runs after `execute` and before anything reaches the model. A mismatch is a tool error. The invalid raw value stays hidden. Schema-less tools keep native JSON serialization. Standard Schema validators can close over mutable state. Ordinary runs allow that. Cave Build locks and durable runs refuse opaque validator identity. Pass `schemaSemanticsSHA256` as the lowercase SHA-256 of validator code, dependencies, and captured state, and change it when any of those change. Nested tools[#](https://docs.caveman.so/docs/agent-sdk/tools#nested-tools) --------------------------------------------------------------------------- A composite tool may declare `nestedTools`. Nested tools are flat: no nested-nested. A `read` composite cannot contain an effectful child. Nested dispatch still goes through the same budget, breaker, schema, timeout, and receipt path as a top-level call. Programmatic mode is the usual reason to do this. See [Programmatic tools](https://docs.caveman.so/docs/agent-sdk/code) . Sandbox reminder[#](https://docs.caveman.so/docs/agent-sdk/tools#sandbox-reminder) ----------------------------------------------------------------------------------- Production tools with the default `sandbox: "required"` run in a restricted worker imported from a staged source graph. Programmatic `run()` must pass `RunOptions.entryPath` pointing at the module that exports the definition. The CLI supplies this. The [Sandbox](https://docs.caveman.so/docs/agent-sdk/sandbox) page is the contract. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/tools#what-it-will-not-do) ----------------------------------------------------------------------------------------- A tool closure does not inherit the parent environment. Signing, deployment, bootstrap, database, and ambient secret names are denied in the worker. `sandboxProfile.network: true` is not scoped egress; it requests unbounded egress and fails closed, because scoped egress does not exist yet. --- # Memory · Caveman docs [Skip to content](https://docs.caveman.so/docs/memory#main) cavemem is a local store for the context you keep re-explaining. You write something once, and later ask a question. It returns only the parts that answer the question, compressed through the engine on the way out. Licence Go core BSL 1.1. The JavaScript and Python clients are MIT. Storage `~/.caveman/mem` Ranking BM25 behind a conservative threshold. Basis Always `inferred`. Binary `cavemem` Build it[#](https://docs.caveman.so/docs/memory#build-it) ---------------------------------------------------------- terminal go build -o cavemem ./public/mem/cmd/cavemem Use it[#](https://docs.caveman.so/docs/memory#use-it) ------------------------------------------------------ terminal cavemem remember "the deploy key lives in vault under ops/deploy"cavemem recall "where is the deploy key"cavemem recall "full migration context" 5 0cavemem supersede mem_xxxxxxxx "deploy key moved to vault ops/deploy-v2"cavemem history mem_yyyyyyyycavemem forget mem_xxxxxxxxcavemem recover ccr_xxxxxxxx > original.txt `recall` returns JSON with the hits and a `basis` field. The two trailing numbers on the second recall are the result limit and the token budget. A budget of `0` means unlimited, and you have to write it out, because unbounded recall should be a decision rather than a default. Run `cavemem` with no subcommand and it speaks MCP over stdio: json { "mcpServers": { "cavemem": { "command": "cavemem" } } } Four guarantees[#](https://docs.caveman.so/docs/memory#four-guarantees) ------------------------------------------------------------------------ **Byte safe on write.** Raw text is stored first. Compression happens on the way out, never on the way in, so a compressor bug can never cost you the original. **Fails toward nothing.** An off-topic query recalls nothing rather than returning the closest thing it found. A memory system that always answers is worse than one that admits it has nothing. **Bounded by default.** Recall packs at most 2,000 inferred tokens unless you explicitly opt out. **Reversible.** Every compressed hit carries a recovery handle, and `cavemem recover` returns the original bytes exactly. Superseding rather than editing[#](https://docs.caveman.so/docs/memory#superseding-rather-than-editing) -------------------------------------------------------------------------------------------------------- `supersede` writes a new version and links it to the old one. `recall` returns only current entries. `history` shows the chain from oldest to current. Facts about a codebase go stale, and a store that silently overwrites gives you no way to notice when the thing you remembered stopped being true. Clients[#](https://docs.caveman.so/docs/memory#clients) -------------------------------------------------------- Both clients shell text over stdin rather than passing it as an argument, so a large block does not hit an operating system argv limit. javascript import { remember, recall } from "cavemem";await remember("the deploy key lives in vault under ops/deploy");await recall("where is the deploy key", 5, 2000); python import cavememcavemem.remember("the deploy key lives in vault under ops/deploy")cavemem.recall("where is the deploy key", limit=5, token_budget=2000) Set `CAVEMEM_BIN` if the binary is not on `PATH`. An oversized `remember` exits with code 65. One thing to know about handles[#](https://docs.caveman.so/docs/memory#one-thing-to-know-about-handles) -------------------------------------------------------------------------------------------------------- cavemem keeps its own recovery store at `~/.caveman/mem/ccr.db`, separate from the engine's store at `~/.caveman/ccr.db`. Handles are not interchangeable between the two. A handle from a cavemem recall is recovered with `cavemem recover`, not with the engine. --- # Run a turn · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/run#main) `run()` executes one turn and returns a `RunResult`. `stream()` yields typed events for the same work. Both take an `AgentDefinition`, an input, and optional `RunOptions`. typescript import support from "./agent.js";import { run } from "@caveman-ai/agent";const result = await run(support, "Can I get a refund?");console.log(result.text);console.log(result.mode);console.log(result.receipt); `result.claimBasis` is always `"inferred"`. `result.receipt` is schema `caveman.agent.run-receipt.v1`. USD fields are public-catalog list-price subtotals. Observe-only and optimized[#](https://docs.caveman.so/docs/agent-sdk/run#observe-only-and-optimized) ----------------------------------------------------------------------------------------------------- On a machine with only Node and a provider key, `mode` is `observe-only`: the SDK calls the provider's own base URL. No local transform runs. Provider usage and local context estimates remain. With `@caveman-ai/cli` installed and `caveman start` running, eligible calls can return `mode: "optimized"`. Anthropic, OpenAI, and Google can take that path. Other providers stay direct and still report `observe-only`. Set `cave: "off"` in `RunOptions` to force observe-only. A run that carries a Cave Build lock or candidate plan refuses silent downgrade with `cave_gateway_required_for_locked_plan`. A graph that mixed the two modes reports `observe-only`. The label under-claims rather than averaging. Conversations and streaming[#](https://docs.caveman.so/docs/agent-sdk/run#conversations-and-streaming) ------------------------------------------------------------------------------------------------------- typescript import { createConversation, run, stream } from "@caveman-ai/agent";const conversation = createConversation();await run(support, "My order is late.", { conversation });const followUp = await run(support, "What should I do next?", { conversation }); Concurrent use of one conversation fails closed. A failed turn rolls back conversation and cache state. Definition, model, or plan changes rotate the cache epoch so stale prefix bytes are not replayed. `stream()` emits run, context, model, completion, and error events. Calling the iterator's `return()` aborts in-flight provider, tool, and subagent work before conversation ownership is released. Terminal `run_end` and `run_error` release ownership before delivery. Receipts and errors[#](https://docs.caveman.so/docs/agent-sdk/run#receipts-and-errors) --------------------------------------------------------------------------------------- Every successful result includes the receipt: model calls, tool calls, provider usage basis, catalog cost basis, stop reason, compactions, retries, subagents, and resume state. A failure after spend throws `CavemanRunError` with the same partial receipt on `error.receipt`. `run_error` events carry it too. A run that fails after spending never loses its per-call breakdown. Directory-loaded agents print the receipt to stdout by default and write JSON under `.caveman/runs/`. Hand-built `run()` does not print, because stdout may be a protocol channel. Set `printReceipt: true` to opt in. Budgets and ceilings[#](https://docs.caveman.so/docs/agent-sdk/run#budgets-and-ceilings) ----------------------------------------------------------------------------------------- typescript import { run, type RunBudget } from "@caveman-ai/agent";const budget: RunBudget = { maxTokens: 12_000, onExhausted: "stop",};const result = await run(support, "Summarise this ticket.", { budget }); `onExhausted` is `compact` or `stop`. Default is `compact`. See [Compaction](https://docs.caveman.so/docs/agent-sdk/compaction) . `RunOptions.maxCostUsd` is a best-effort local spend cap in USD at public catalog list prices. It is not a provider invoice, a platform quota, or a cross-process reservation. Each priced root or descendant call reserves worst-case catalog price before the request and settles measured catalog cost after it. Exhaustion ends the run with `cave_run_cost_budget_exceeded` before the next model call. An unpriced model cannot consume `$0` of a USD cap: with the cap set, that call fails closed. Leave the cap unset for unpriced models and bound them with call ceilings instead. `maxCostUsd` and `budget` are mutually exclusive. Default `maxModelCalls` is 64. Reaching it ends the run between calls with `stopReason: "call_budget_exhausted"` and returns the partial result; it does not throw. Default `maxToolCalls` is 64. A tool call past that ceiling is blocked (the model sees a blocked result) and the run continues. Breakers (repeated tool calls, no-progress, fan-out) are opt-in. A run that declares none does not guess at a loop. Durable resume[#](https://docs.caveman.so/docs/agent-sdk/run#durable-resume) ----------------------------------------------------------------------------- typescript const result = await run(support, "Continue the investigation.", { durable: { runId: "case-42-analysis-1" },}); Durable mode journals call intent before network work. Resume restores known spend and the last committed execution boundary. A request that was in flight during a crash stays unknown, because the SDK cannot know whether the provider billed it. The receipt says so rather than guessing. `runLocked()` embeds a Pi Cave Build after `caveman-agent check` has passed at deployment. It accepts only Pi locks. Before provider traffic it validates lock integrity, agent definition, runtime, adapter, catalog, Context IR, selected plan, and the live engine registry when transforms exist. Parsing the lock file alone does not prove source freshness; `check` does. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/run#what-it-will-not-do) --------------------------------------------------------------------------------------- `run()` cannot inject a plan or build identity. Locked and candidate execution belong to `dev`, `build`, `check`, and `runLocked()`. Costs are catalog estimates. `verifiedSavingsUsd` stays 0 on this path. Unknown model, pricing, or usage fails closed or returns an honest zero; it does not become a favourable number. --- # Sandbox · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/sandbox#main) Every agent has a sandbox mode. The default is `required`. The mode is set on the definition, not guessed at run time. | Mode | Where tool closures run | Writes | Lock eligible | | --- | --- | --- | --- | | `required` | Isolated, network-denied Node workers, from a staged source graph | Allowed inside the worker | Yes, when the rest of the build passes | | `fixture` | Host process. Trusted tests only | Blocked before execute | For fixture corpora | | `host` | This process, no worker | Execute | No | Host mode is an explicit opt-in. It is never the default, and it is never isolation. A host-mode child under a `required` parent is refused, so a subagent cannot walk out of its root's containment. `compile` refuses a host-mode agent with `cave_host_sandbox_lock_ineligible` before any search run. Live coding sessions therefore cannot produce a Cave Build. Coding agents that need a lock compile against fixture corpora with a contained sandbox. Required mode[#](https://docs.caveman.so/docs/agent-sdk/sandbox#required-mode) ------------------------------------------------------------------------------- Before provider traffic, the framework copies the project-relative source graph into a per-run immutable staging directory, imports tools only from that snapshot, and tears the staging down when the stream settles. `RunOptions.entryPath` must point at the module that exports the definition. The CLI fills this in. Programmatic `run()` of a tool-using required-sandbox agent without `entryPath` is refused. The worker starts from a fixed environment baseline (`LANG`, `LC_ALL`, `PATH`, `TZ`, plus a fixture marker), not a spread of the parent env. One runtime-owned provider capability may be requested: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or the Google pair (`GEMINI_API_KEY` / `GOOGLE_API_KEY` as aliases). Signing, deployment, bootstrap, database, loader, and ambient secret names are denied. Network egress is blocked at the OS: a network namespace on Linux (`unshare --net`), `sandbox-exec (deny network*)` on macOS. In-process monkeypatching is defence in depth, not the boundary. Requesting unbounded egress fails closed with `cave_sandbox_network_egress_unbounded`. There is no scoped-egress mechanism yet. Linux unix-domain sockets are not covered by a network namespace. That gap is documented in the package threat model; do not treat required mode as a complete host firewall. Windows[#](https://docs.caveman.so/docs/agent-sdk/sandbox#windows) ------------------------------------------------------------------- Ordinary agent runs, runtime startup, and explicit `sandbox: "host"` coding tools work on native Windows through `cmd.exe`. `sandbox: "required"` is fail-closed on native Windows because the package has no verified OS network-isolation boundary there. Use WSL2 for production sandboxed tools. `doctor` reports `cave_sandbox_os_network_isolation_unavailable` and the WSL2 remedy. Do not replace required sandbox with fixture mode in production. Fixture mode[#](https://docs.caveman.so/docs/agent-sdk/sandbox#fixture-mode) ----------------------------------------------------------------------------- Trusted tests may set `sandbox: "fixture"`. Closures run in the host process, which is why writes are blocked: a test that needs to observe a write belongs on a contained worker or an explicit host-mode agent, not in fixture. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/sandbox#what-it-will-not-do) ------------------------------------------------------------------------------------------- `sandbox: "host"` is not a synonym for isolation. Required mode is not available on native Windows. Fixture mode is not a production fallback. A passing doctor sandbox check is a containment probe, not a proof that every OS gap is closed. --- # Memory · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/memory#main) Agent memory is local, opt-in, and one turn behind on purpose. Turn N starts retrieval while the model runs. Turn N+1 consumes whatever finished, without waiting. Recalled text is injected immediately before the current user message. It never enters the frozen system prefix and never mutates append-only conversation history. Every injected block is labelled `inferred` and potentially stale. Current user intent, code, tools, and runtime evidence win. Default TTL `30d` Default recall budget 800 tokens. Provenance `local` Consent `local_only` Scope tenant, agentId, namespace Declare it[#](https://docs.caveman.so/docs/agent-sdk/memory#declare-it) ------------------------------------------------------------------------ typescript import { agent, auto, createMemoryEngine, memory, run,} from "@caveman-ai/agent";const definition = agent({ id: "support", instructions: "Resolve support requests.", model: auto(), memory: memory({ namespace: "support" }),});const engine = createMemoryEngine({ scope: { tenant: "tenant-1", agentId: "support", namespace: "support" }, ttlMs: 30 * 86_400_000,});await run(definition, "Remember that I prefer email updates.", { memory: { tenant: "tenant-1", engine },}); TTL must be a positive `m`, `h`, or `d` duration. `provenance: "project"` or `"external"`, and `consent: "project_shared"`, fail at `memory()` construction. The package does not implement a shared backend, so it refuses the config before a model call could discover that. Two agents that declare the same namespace in one process still do not see each other's rows: scope includes `agentId`. `RunOptions.memory.root` defaults to `CAVE_AGENT_MEMORY_ROOT`, else `~/.caveman/agent-memory`. `tenant` defaults to a single-tenant value. A one-shot `run()` without an engine keeps the explicit memory tools and starts no background work, so nothing is still running after return. Reuse one engine across turns. Coding sessions can pass `memory: true`, which creates one scoped engine and reuses it; `session.close()` flushes. What the engine does[#](https://docs.caveman.so/docs/agent-sdk/memory#what-the-engine-does) -------------------------------------------------------------------------------------------- `@caveman-ai/agent/memory` exposes the engine, workflow helper, and adapters. 1. `beginTurn()` returns finished recall from the prior turn and queues current retrieval. Embedding latency does not block the model. 2. `endTurn()` queues the assistant turn for session search and optional extraction. 3. Explicit `remember`, `search`, `searchSessions`, `forget`, and `link` cover active workflows. Native agents expose remember, memory search, and session search as framework tools (`cave_memory_*`). 4. `endSession()` flushes, extracts remaining turns, and runs optional consolidation. Default retrieval is a dependency-free sparse lexical vector plus lexical overlap. It is useful with no extra service. It is not semantic. Optional embedding adapters and bounded graph expansion (`relates_to`, `supersedes`, `contradicts`, `derived_from`, depth default 1, cap 3) exist behind `@caveman-ai/agent/memory`. An OpenAI-compatible embedding adapter uses `fetch`, takes an explicit API key, and never reads ambient key environment variables. No extra model call happens unless you supply a sidecar adapter for review, extraction, or consolidation. Consolidation is reversible: superseded records stay as inactive evidence. Sidecar output is never verified policy and never verified savings. Storage[#](https://docs.caveman.so/docs/agent-sdk/memory#storage) ------------------------------------------------------------------ Default persistence is per-namespace JSON with atomic temp-write plus rename. Vectors store as normalized int8 plus base64, not floating-point arrays. Expired records become inactive reversible evidence. In-process writes are serialized per file. Across processes, last writer wins on concurrent update. Deployments that need multi-writer transactions supply a storage adapter (`read` plus serialized atomic `update`). Obvious private keys, provider tokens, credential assignments, and access keys are rejected before storage, indexing, or sidecar processing. For a host agent that is not this runtime: typescript import { createMemoryWorkflow } from "@caveman-ai/agent/memory";const memory = createMemoryWorkflow(engine, sessionId);const memoryContext = memory.beforeTurn(userText);const answer = await callAgent({ userText, memoryContext });memory.afterTurn(answer);await memory.close(); `beforeTurn` returns a string or `undefined` and does not wait. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/memory#what-it-will-not-do) ------------------------------------------------------------------------------------------ Memory is not a saving. Recalled text is not a source of truth against current tools. Shared or project-scoped memory is not implemented. Semantic recall is not claimed for the default sparse vector. Closing the engine is your job if you created one. --- # Compaction · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/compaction#main) Compaction runs only inside a declared token or USD budget. It is not a background summariser that fires because the context "looks large". The runtime applies a fail-closed ladder, then stops. **1 · Exact-recovery eviction.** Drop what can be fetched back byte for byte. ↓ still over budget **2 · Typed summary.** Replace eligible history with a cave.context-summary.v2 capsule. ↓ still over budget **3 · Output clamp.** Bound what the model may still emit. ↓ still over budget **4 · Stop.** The run ends. The receipt keeps every call that already happened. Ember is the last exit. Nothing after stop invents a cheaper context. typescript import { run, type RunBudget } from "@caveman-ai/agent";const budget: RunBudget = { maxTokens: 120_000, onExhausted: "compact", compaction: { keepRecentTokens: 8_000, summaryMaxTokens: 2_048, preserveFirstUserMessage: true, },};const result = await run(support, "Continue investigation.", { budget }); `onExhausted: "stop"` skips the compact rungs and ends the run at the cap. Capsule rules[#](https://docs.caveman.so/docs/agent-sdk/compaction#capsule-rules) ---------------------------------------------------------------------------------- The public API lives at `@caveman-ai/agent/compaction`. typescript import { parseContextSummary, summarizationInstruction, validateContextSummaryTransition,} from "@caveman-ai/agent/compaction"; A replacement is accepted only when all of these hold: * the schema parses * generation increments by exactly one * required user sources are covered, with matching source IDs and SHA-256 digests * every prior critical anchor survives byte-identically, unless a later current user source grounds a new critical anchor with an explicit `supersedes` edge * root user intent and a self-contained recent tail remain verbatim * the rewritten context is smaller * tool content cannot mint critical policy One paid summary is the default. Structural support for repeated compaction is tested. A higher default is not shipped without repeated live-model semantic evidence, and that evidence is not claimed here. Harness[#](https://docs.caveman.so/docs/agent-sdk/compaction#harness) ---------------------------------------------------------------------- typescript import { runContextCompactionHarness, type ContextCompactionSummarizer,} from "@caveman-ai/agent/compaction";const summarize: ContextCompactionSummarizer = async (request) => { return explicitClient.complete({ messages: [ ...request.messages, { role: "user", content: request.instruction }, ], maxTokens: 2_048, });};const report = await runContextCompactionHarness(fixture, summarize, { repetitions: 20,});if (!report.stable) throw new Error(report.failures.join("\n")); The harness owns fixtures and validation. The adapter owns the model and transport, with explicit credentials. Test code does not inherit ambient secrets. The public harness measures transition validity, anchor recall, exact-recovery coverage, and compression ratio. Structural stability is tested. Semantic superiority is not claimed. Compaction events land on the run receipt with the rest of the spend. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/compaction#what-it-will-not-do) ---------------------------------------------------------------------------------------------- Compaction is not a quality improvement. A smaller context that dropped a commitment is a failed transition, not a savings. Ratios from the harness are local, inferred, per run. They are not monthly figures and not `verified`. --- # Connect · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/connect#main) Caveman Connect lets an agent read allowed provider data through one stable tool. The SDK owns configuration, bounded invocation, quality policy, and evidence. OAuth, credentials, encrypted storage, and sync execution stay in a local Connect daemon. The SDK does not bundle that daemon. typescript import { agent, auto, createConnect } from "@caveman-ai/agent";const data = createConnect({ sources: [{ id: "work-github", provider: "github", collect: ["issues"], models: ["Issue"], }],});export default agent({ id: "issue-triage", instructions: "Use connected source data. Never invent missing records.", model: auto(), tools: [data.tool],}); Authorize once: terminal caveman-agent connect github Start configured collection from the app or a job: typescript await data.collect("work-github"); `collect()` triggers configured syncs and returns daemon acknowledgements. It does not pretend a queued sync is finished. Poll with `connected_data` `operation: "sync_status"`, then read records. Recurring schedules are not part of the current protocol; call `collect()` from whatever scheduler you already run. When a source omits `connectionId`, the runtime accepts exactly one active saved connection for that provider. Zero or several matches fail closed. Set the exact id to remove the ambiguity. Why the prefix stays small[#](https://docs.caveman.so/docs/agent-sdk/connect#why-the-prefix-stays-small) --------------------------------------------------------------------------------------------------------- One stable `connected_data` schema enters the provider-visible prefix. Provider catalog, action schemas, sync schemas, and records stay out of the prompt until the agent asks. 1. `sources` returns a tiny allowlist. 2. `search_syncs` or `search_actions` loads bounded matching metadata. 3. `collect` triggers an allowlisted sync. 4. `records` reads exact paginated records. No retrieval model, embedding, semantic reranker, or automatic summariser runs by default. That avoids hidden model spend and keeps the provider cache prefix byte-stable inside one cache epoch. Token reduction is still not a savings proof. Extra model turns, expensive collection, stale data, or retries after incomplete context can cost more than the prefix you saved. Incomplete reads fail closed[#](https://docs.caveman.so/docs/agent-sdk/connect#incomplete-reads-fail-closed) ------------------------------------------------------------------------------------------------------------- Paginated reads never silently summarise or skip records. Capped output returns `complete: false`, exact continuation when available, and `must_refuse: true`. The agent is supposed to refuse to answer from a partial page rather than invent the missing rows. Hosts that need Connect's native tool catalog without building a second MCP client can use `ConnectRuntime` from `@caveman-ai/agent/connect`. Default Connect timeout is 30,000 ms. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/connect#what-it-will-not-do) ------------------------------------------------------------------------------------------- Connect will not copy credentials into the agent process. It will not hide a truncated page as a complete answer. It will not schedule recurring syncs by itself. It will not claim that a smaller prompt saved money. --- # Unknown \--- title: How numbers are counted summary: Inferred against verified, per run against per month, and the four rules the code follows. canonical: https://docs.caveman.so/docs/counting updated: 2026-08-26T03:57:26+02:00 basis: inferred --- # How numbers are counted > Inferred against verified, per run against per month, and the four rules the code follows. Every number Caveman shows you carries a word that says how it was obtained. The words are not interchangeable and the code does not let them blur. ## Three words, kept apart - inferred: A local estimate from bytes, token counters and local records. Every public offline tool emits this. - measured: Observed traffic. Real, but not proof that a dollar was saved. - verified: Reserved for hosted rollout systems that can compare against a bill. No tool on this site emits it. If you install anything documented here and it shows you a percentage, that percentage is \`inferred\`. There is no configuration that upgrades it. ## The four rules ### 1. No fake savings Headline figures in these docs are local examples or target ranges. They stay labelled \`inferred\`, and they are never multiplied out into a monthly saving. Where the code only supports a range, the docs show the range rather than picking a number from inside it. ### 2. Byte safe \`record\` mode is pass-through. On a parse problem, an unsupported input, a missing recovery store, or an output that is not actually smaller, the engine keeps the original bytes. \`\`\`text record mode -> never transforms transform error -> forward the original bytes result not smaller -> keep the original \`\`\` ### 3. No placeholders, fail closed No stub responses on shippable paths, and unknown cases resolve toward the conservative answer. \`\`\`text unknown engine mode -> record, which is pass-through low-confidence content -> text unknown grader -> passed: false unknown route -> 404 unknown model price -> zero, plus an "unpriced:" tag \`\`\` An unpriced model contributes zero to a cost figure and says so. It does not get a guessed price. ### 4. Recoverable, so lossy stays honest Compressors drop bytes from the model-visible payload. A lossy result is emitted only after the original has been stored under a content-addressed handle, and \`retrieve(handle)\` returns the original byte for byte. If a tool lies about savings, every report and every rollout decision downstream inherits that lie. Public Caveman tools report what they can support locally, and stop there. ## Per run, never per month An inferred ratio is a property of one payload on one run. Projecting it forward assumes your next month looks like that payload, which nobody can know. So the engine reports a ratio and does not offer to annualise it. There is no setting for this. ## Tokens are not money On the local path, savings are counted in tokens and have no dollar field anywhere in the data structure. There is no token-to-dollar multiplication at any layer. This matters because token prices differ per model, per provider, per cache state and per contract. A local tool that has not seen your invoice cannot convert one into the other, so it does not try. ## What the counter actually is The default token counter is a real BPE tokenizer using the \`o200k\_base\` encoding, with the vocabulary compiled into the binary. It is deterministic, so the same bytes always produce the same count, and it is offline, so it works air-gapped and never sends your payload anywhere. If the embedded codec fails to load, it degrades to a characters-divided-by-four approximation. That approximation errs low. It never guesses high. Provider-reported usage from your actual API response stays authoritative for spend. The local counter is an estimate of that, and the two are kept in separate fields. ## Numbers you will see quoted Both of these are real measurements with real limits, and the limits travel with the number. \*\*The skill, output tokens.\*\* About 65 percent fewer on average across ten prompts, ranging from 22 to 87 percent. Measured on the committed benchmark suite. Output tokens only. A per-run inferred estimate, not a bill-savings promise. \*\*The wrap path, input tokens.\*\* 33.2 percent fewer provider-reported input tokens across 18 paired runs of six cases, with a case-clustered 95 percent interval of 14.6 to 48.5 percent. All 18 exact-answer checks passed. One of the six cases regressed by 9.9 percent and stays in the aggregate, because dropping a negative case would make the average a fiction. That second result is controlled benchmark evidence on a pinned agent build. It is not production traffic, not a provider invoice, and not \`verified\`. --- # Unknown \--- title: Overview summary: What Caveman is, which parts are open, which repository each one ships from. canonical: https://docs.caveman.so/docs updated: 2026-08-30T15:24:20+02:00 basis: inferred --- # Overview > What Caveman is, which parts are open, which repository each one ships from. Caveman makes agents cheaper to run. It is not one program and not a framework you commit to. It is a short list of separate things, each of which you can adopt on its own and remove on its own. There are two repositories and one hosted plane. Knowing which is which is most of what you need to navigate this site. - caveman: Makes an agent you already use cheaper. The skill, the proxy and the compression engine inside it, memory, and the adoption surfaces: CLI, SDKs, MCP server. - agent-sdk: Builds a new agent that is efficient by construction. A separate repository and a separate install. It does not need the proxy. - Caveman Cloud: The managed plane, across a network boundary. The only paid part, and the only place a number can be verified against a bill. ## caveman: make an existing agent cheaper Everything in this repository runs on your machine and needs no account. The four rows below are the operating stack, bottom to top. Each one answers a different question, and each fails on its own. \[Diagram: the four layers of the stack, bottom to top: Skill, Engine, Memory, Cloud.\] ### 00 Skill A \[skill\](/docs/skill) is a persistent instruction set your coding agent reads before it answers. It cuts filler, narration and hedging out of the agent's own output, and it is explicit about what it must never touch: code blocks, function names, CLI commands and exact error strings stay byte for byte. It costs nothing to run because it is not software. It is text your agent already reads. ### 01 Proxy The \[proxy\](/docs/proxy) is a reverse proxy on loopback. You point an agent at \`127.0.0.1:8787\` instead of the provider, it keeps your key, and it shrinks the context on the way out. The compressor doing that shrinking is the engine. It detects what a payload is, routes it to a compressor built for that shape, and stores the original bytes under a content-addressed handle before it removes anything. If the agent needs what was dropped, it asks for the handle and gets the original back exactly. The engine is not a separate product and not a separate install; it is \[documented inside the\ proxy\](/docs/proxy/engine), because the proxy is how you actually run it. ### 02 Memory \[cavemem\](/docs/memory) holds the context you would otherwise paste into every session. It stores text once, recalls the parts a question actually needs, and compresses each hit on the way out. ### 03 Cloud \[Caveman Cloud\](/docs/cloud) is the managed plane. It is where a fleet's numbers stop being local estimates and become something a finance team can read. It is in private development and the waitlist is open. ### Build with it The same capabilities as a \[CLI\](/docs/cli), a \[TypeScript\](/docs/sdk/typescript) or \[Python\](/docs/sdk/python) SDK, an \[MCP server\](/docs/mcp), and two standalone commands: \[caveman-shrink\](/docs/shrink) for command output and \[caveman-browse\](/docs/browse) for web pages. These are the MIT adoption surfaces. Reach for them when you want the behaviour without a proxy hop. ## agent-sdk: build a new agent The \[Agent SDK\](/docs/agent-sdk) is the other direction. Rather than making an existing agent's traffic smaller, it is how you write an agent whose context, tools and budget are declared up front, so a turn cannot quietly exceed them. An agent is one frozen \[definition\](/docs/agent-sdk/define): context, output schema, filesystem, \[tools\](/docs/agent-sdk/tools), and subagents. You \[run a turn\](/docs/agent-sdk/run) against it and get a receipt rather than a vibe. \[Evals\](/docs/agent-sdk/build) gate the build, so a failed eval never produces a lock. \[Compaction\](/docs/agent-sdk/compaction) is a fail-closed ladder inside a declared budget: recover, summarise, clamp, then stop. It lives in its own repository, ships as its own package, and shares no runtime with the proxy. You can use either without the other. If you already have Claude Code, Codex or a provider SDK and want it to cost less, you want the skill and the \[proxy\](/docs/proxy). If you are writing an agent from scratch and want its budget to be a declared property rather than something you discover on the bill, you want the \[Agent SDK\](/docs/agent-sdk). ## What the numbers mean Every local tool on this site reports \`inferred\`. That word is load bearing. It means a per-run estimate computed on your machine from an offline token counter, and it is never turned into a monthly figure or a dollar amount. No tool in either repository can emit \`verified\`. That label is reserved for hosted rollout systems that can compare against a real bill. If a number here has no basis label next to it, treat it as an example rather than a promise. \[How numbers are counted\](/docs/counting) explains the four rules the code follows. ## Where to go next Install the skill, then compress something real and read the report. The path one request takes, and where the open-core line falls. One command, thirty agents, no runtime. Point an agent at loopback. The engine shrinks what leaves. Build an agent that returns a receipt, not a vibe. Which surfaces are open, and what the additional use grant permits. --- # Unknown \--- title: Quickstart summary: Install the skill and compress a real payload in about five minutes. canonical: https://docs.caveman.so/docs/quickstart updated: 2026-08-30T15:03:57+02:00 basis: inferred --- # Quickstart > Install the skill and compress a real payload in about five minutes. There are two products, and they are independent. The skill changes how your agent writes and takes about thirty seconds. The proxy, launched through the CLI, shrinks what the agent reads and gives you a local report on where those tokens actually go. ## Install the skill
This works for most agents: \`\`\`bash npx skills add JuliusBrussee/caveman \`\`\` Without \`-g\` this writes into \`./.agents/skills\` under the directory you are standing in, not into a global config. That is usually what you want for a project, and surprising if you expected otherwise.
Some agents have a native path that wires up more than the skill file: \`\`\`bash # Claude Code claude plugin marketplace add JuliusBrussee/caveman claude plugin install caveman@caveman # Gemini CLI gemini extensions install https://github.com/JuliusBrussee/caveman # Codex CLI npx skills add JuliusBrussee/caveman -a codex # Cursor, which needs the global flag npx skills add JuliusBrussee/caveman -a cursor -g \`\`\`
Check it took. In your agent, run: \`\`\`text /caveman \`\`\` You should get a confirmation that the mode is active. \`/caveman off\` turns it back off, and so does saying "normal mode".
The skill drops articles, filler and narration from your agent's replies. It is explicit about what stays byte for byte: code blocks, function and API names, CLI commands, and exact error strings. It also steps aside on its own for security warnings, irreversible actions, and any moment where being terse would make an instruction ambiguous. ## Install the local tools The CLI is published on npm under a scoped name. \`\`\`bash npm i -g @caveman-ai/cli caveman setup --install \`\`\` The bare \`caveman\` package on npm is an unrelated JavaScript templating library. Install \`@caveman-ai/cli\`. On PyPI the same applies: our SDK is \`caveman-sdk\`, imported as \`caveman\_cloud\`. \`caveman setup --install\` downloads the companion binaries into \`~/.caveman/bin\`. It checks a key-signed manifest first, then verifies each artefact against its own SHA-256 before installing it. Command names, version, platform, duration, exit class, and aggregate token counts from local sessions. Never prompts, code, file paths, arguments, model names or dollar figures. It is how we see which commands people use and which ones break, and it is a large part of why the local tools can stay free. Turn it off with \`caveman telemetry off\`, \`CAVEMAN\_TELEMETRY=0\`, or \`DO\_NOT\_TRACK=1\`. The full payload is listed on \[Telemetry\](/docs/telemetry). Then start your agent through the CLI so traffic goes through the \[local proxy\](/docs/proxy): \`\`\`bash caveman claude \`\`\` The same works for \`codex\`, \`gemini\`, \`aider\`, \`hermes\`, \`kilo\`, \`openclaw\`, \`opencode\`, \`pi\`, and \`qwen\`. Ten wrap profiles ship today, and each is one JSON file in the registry. That set is smaller than the thirty-plus agents the skill supports, because wrap has to know the host's provider endpoint. ## Find out where your tokens go \`\`\`bash caveman learn \`\`\` This reads your local agent sessions and ranks what is actually consuming the budget. It measures and does nothing else. Applying a fix is a separate, consent-gated step: \`\`\`bash caveman learn report --json caveman learn apply --dry-run caveman learn apply \`\`\` Every applied fix is checked for being net token negative. If an edit does not make the thing smaller, it is reverted. ## What you should expect to see On the committed benchmark suite, the skill cuts output tokens by about 65 percent on average, with a range from 22 to 87 percent across ten prompts. That is a per-run inferred estimate measured on that suite, not a promise about your bill. Nothing you run locally will ever report a dollar figure or a monthly total. That is deliberate and \[the reasoning is here\](/docs/counting). --- # Programmatic tools · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/code#main) Programmatic mode replaces a large JSON tool catalog with one provider-visible tool named `caveman_code`. The model writes a bounded JavaScript cell. Nested tools are called through typed proxies. Every nested call still passes through the runtime's schema, effect policy, budget, breaker, timeout, abort, and receipt path. The cell does not bypass those checks. caveman\_code The only tool the provider sees. ↓ cell source, possibly still streaming **Nested dispatch.** Same kernel as a normal tool call. Receipt records the cell and each nested call. ↓ early reads only **Speculative reads.** Literal effect: "read" calls may start while the cell is still streaming. Writes, idempotent calls, external calls, and variable-dependent arguments never speculate. Ember marks the only early path. Anything that can change the world waits for a complete cell. Coding session[#](https://docs.caveman.so/docs/agent-sdk/code#coding-session) ------------------------------------------------------------------------------ New applications import `@caveman-ai/coding-agent`. `@caveman-ai/agent/code` remains a compatibility export while that package is extracted. typescript import { createCodingAgent, runCodingTurn, startCodingSession,} from "@caveman-ai/agent/code";const codingAgent = createCodingAgent({ workspace: process.cwd(), toolMode: "programmatic",});const session = await startCodingSession(codingAgent);await runCodingTurn(session, "Find failing tests and fix root cause."); `toolMode: "direct"` exposes ordinary JSON tools instead. `speculativeToolCalls: false` keeps programmatic mode without early reads. Programmatic mode supports host agents only. The code-cell worker is not an isolation boundary. Use a normal `sandbox: "required"` agent when containment matters. Declaring `effect: "read"` in this mode means the work is safe to start and abandon: it may run even if the generated cell is later discarded. Unknown stream provenance executes fresh work rather than reusing stale speculation. Generic embedders[#](https://docs.caveman.so/docs/agent-sdk/code#generic-embedders) ------------------------------------------------------------------------------------ typescript import { createProgrammaticToolRuntime, PROGRAMMATIC_TOOL_NAME,} from "@caveman-ai/agent/programmatic-tools"; That kernel does not require the coding-agent helpers. Nested tools still cannot skip the dispatcher. Interactive coding agent[#](https://docs.caveman.so/docs/agent-sdk/code#interactive-coding-agent) -------------------------------------------------------------------------------------------------- `@caveman-ai/coding-agent` is the interactive coding agent on this runtime, with host-sandbox `read_file`, `grep`, `bash`, `write_file`, and `edit_file` over one workspace. `read_tool_output` pages or literal-searches large captured results without repeating the original command. The CLI binary is `caveman-code`. With the local engine present, a session starts the local runtime and applies a default efficiency plan: one recoverable route per live-zone segment kind (`tool_result` through `caveman.engine.terminal.v1`, `history` through `caveman.engine.text.v1`), with `cave_retrieve` registered so the model can pull original bytes back. Only CCR-recoverable transforms are eligible. Two routes matching one runtime segment collapse into `dynamic_route_ambiguous` and that segment passes through untouched. When the runtime cannot be reached, the session degrades to observe-only and says so: a banner, `session.notices`, the prompt, and every turn's bill. A turn that already carries a plan refuses to degrade on its own (`cave_gateway_required_for_locked_plan`). The runtime is probed once per session, not once per turn. After every turn the session prints context tokens before and after transforms, tokens saved labelled `inferred (local estimate)`, provider usage with `usageBasis`, and spend in USD with `priceBasis`. Savings are never printed as currency. `/prove-recovery` round-trips a recorded tool output through the same compress/retrieve pair and compares SHA-256. Tool output is capped before compression (24 KB for `read_file` and `bash`, 16 KB for `grep`). Captured results are bounded to 8 MiB each and 16 MiB total in memory. Handles expire with the process. Live coding sessions are never lock-eligible. Long-running commands[#](https://docs.caveman.so/docs/agent-sdk/code#long-running-commands) -------------------------------------------------------------------------------------------- `@caveman-ai/agent/command-session` is the kernel behind hosted command tools. You pass an explicit `env`. Ambient `process.env` is never inherited. Session state is `running`, `exited`, `timed_out`, `killed`, or `unknown_after_restart`. Reads are cursor-based and can wait for bytes or a literal match. This is a credential boundary, not a sandbox: a `bash` session on a host agent is uncontained. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/code#what-it-will-not-do) ---------------------------------------------------------------------------------------- A code cell is not a sandbox. Host execution can run `bash`. Programmatic mode will not speculate a write. The coding session will not silently drop into observe-only on a planned turn, and it will not call a local token reduction a dollar saving. --- # Unknown \--- title: caveman explore summary: Read a repository without dragging every file into context. canonical: https://docs.caveman.so/docs/skill/explore layer: skill license: MIT capability: skill-caveman-explore updated: 2026-08-26T04:05:35+02:00 basis: inferred --- # caveman explore > Read a repository without dragging every file into context. \`caveman-explore\` is a read-only repository scout for Claude Code. It answers one localization question with small \`path:line\` citations, so the main agent can inspect the right code without carrying every exploratory read in its conversation. - Host: Claude Code only. - Tools: \`Read, Glob, Grep\` - Model: \`haiku\` - Output: \`path/to/file.ext:START-END reason\` - Licence: MIT. ## Install \`\`\`bash caveman explore install --agent claude \`\`\` The command is a compatibility alias for installing the \`caveman-explore\` skill. It writes the canonical \`SKILL.md\` unchanged and disables pixel conversion for this file. You can choose a destination explicitly: \`\`\`bash caveman explore install --agent claude --dir ./path/to/caveman-explore \`\`\` \`caveman explore install --agent codex\` exits non-zero and writes no skill. Transcript isolation for this pattern has not been verified on Codex, so the installer does not claim support. ## When to use it Use the explorer when you need to locate code before you can solve the task: - A cold start in an unfamiliar repository - A question that spans several files or packages - A direct search that did not reveal the owning code - A request that names behaviour but no file or symbol Skip it when the task already names the exact file or symbol, or when a previous turn already returned usable line citations. The explorer is a localization step, not a second opinion on a known location. ## What it returns The response contains citations only: \`\`\`text src/router/pick.go:42-71 route selection lives here src/router/pick\_test.go:18-40 table test covering selection \`\`\` Every range must have been read. The range may not extend beyond the file. If no relevant code exists, the only valid answer is: \`\`\`text no relevant locations found \`\`\` This narrow output contract matters. The solving agent gets evidence it can open directly, without inheriting a summary, a proposed fix, or a large search transcript. ## How it searches The explorer starts with several complementary reads in parallel: likely path patterns, symbol or string matches, and the most promising file contents. It follows the evidence for one or two short rounds, then stops as soon as it can name the locations. The fixed tool list enforces the boundary. It cannot run a command, edit a file, write a patch, or test a proposed solution. ## What it cannot prove A citation proves that code exists at a location the explorer read. It does not prove that the code is correct, that a test passes, or that the cited path is the only implementation. The solving agent still owns diagnosis, changes, and verification. --- # Unknown \--- title: "Skill: Overview" summary: A set of instructions that changes how your agent spends tokens. No runtime, no proxy. canonical: https://docs.caveman.so/docs/skill layer: skill license: MIT capability: skill updated: 2026-08-26T03:57:26+02:00 basis: inferred --- # Skill: Overview > A set of instructions that changes how your agent spends tokens. No runtime, no proxy. The skill is the cheapest layer in the stack because it is not software. It is a file your agent reads, and it changes how the agent writes for the rest of the session. - What it is: One Markdown instruction file, plus optional commands and subagent presets. - Runtime: None. No process, no port, no config file. - Account: Not required. - Licence: MIT. - Install: \`npx skills add JuliusBrussee/caveman\` ## Install \`\`\`bash npx skills add JuliusBrussee/caveman \`\`\` Without \`-g\` this writes into \`./.agents/skills\` under your current directory rather than a global location. Several agents have their own native path: \`\`\`bash # Claude Code claude plugin marketplace add JuliusBrussee/caveman claude plugin install caveman@caveman # Gemini CLI gemini extensions install https://github.com/JuliusBrussee/caveman # Codex CLI npx skills add JuliusBrussee/caveman -a codex # Cursor, which needs -g npx skills add JuliusBrussee/caveman -a cursor -g # opencode npx -y github:JuliusBrussee/caveman -- --only opencode \`\`\` There is also a full installer that wires up hooks and a status line for Claude Code. It is pinned to a tag, it is safe to re-run, and it supports \`--dry-run\`. \`\`\`bash curl -fsSL https://raw.githubusercontent.com/JuliusBrussee/caveman/v2.3.1/install.sh | bash \`\`\` \`\`\`powershell irm https://raw.githubusercontent.com/JuliusBrussee/caveman/v2.3.1/install.ps1 | iex \`\`\` ## What it removes Articles, filler, pleasantries, hedging and tool-call narration. Sentence fragments are allowed. Decorative tables and emoji are not. ## What it will not touch This list is the reason the skill is safe to leave on. - Code blocks, verbatim - Function, method and API names - CLI commands and flags - Commit type keywords - Exact error strings - The language you are writing in It also refuses to invent abbreviations. \`cfg\`, \`impl\`, \`req\`, \`res\` and \`fn\` are banned, and so are arrow characters standing in for words. The reason is measurable rather than stylistic: the tokenizer splits those the same way it splits the full word, so the abbreviation saves nothing and costs clarity. ## When it steps aside The skill drops out of terse mode on its own for: - Security warnings - Confirmations of anything irreversible - Ambiguous multi-step sequences - Any case where compressing the answer would create the ambiguity ## Levels \`\`\`text /caveman lite /caveman full # default /caveman ultra /caveman wenyan-lite /caveman wenyan-full /caveman wenyan-ultra /caveman off \`\`\` \`off\`, "stop caveman" and "normal mode" all do the same thing. ## What else comes with it One install also brings a set of commands and work-pattern skills. - Commands: \`/caveman-commit · /caveman-review · /caveman-compress · /caveman-stats · /caveman-help\` - Subagent presets: cavecrew-investigator, cavecrew-builder, cavecrew-reviewer - Work patterns: investigate-first, lean-build, surgical-patch, safe-refactor, migration, verify-and-stop ## What it saves On the committed benchmark suite of ten prompts, output tokens drop by about 65 percent on average, with a range of 22 to 87 percent. That is a per-run inferred estimate measured on that suite. It is not a promise about your bill, and it covers output tokens only. The range matters more than the average here. A tightly scoped refactor prompt saved 22 percent. A long explanatory answer saved 87 percent. Which end you land on depends on how much of your traffic is prose. --- # Unknown \--- title: Architecture summary: "The path a request takes through the stack, and where the open-core line falls." canonical: https://docs.caveman.so/docs/architecture updated: 2026-08-30T15:24:20+02:00 basis: inferred --- # Architecture > The path a request takes through the stack, and where the open-core line falls. Caveman is not one program. Most people install the skill, the proxy, or both, and never touch the rest. This page shows how a single request moves through the pieces and why they are separable. It describes the \`caveman\` repository. The \[Agent SDK\](/docs/agent-sdk) is a different repository with a different job: it is how you build an agent, not how you make an existing one cheaper, and nothing on this page is required to use it. ## One request \[Diagram: one request, from your agent through the skill and the engine to the provider, with a recovery path back.\] The agent decides what to say. The skill has already shaped how it says it. When the request is wrapped, it goes to the \[local proxy\](/docs/proxy) on loopback; the \[engine\](/docs/proxy/engine) reduces whatever payload is about to leave; the provider bills for what arrives. Without the proxy, the engine is a binary you call yourself. The dashed line is the part that makes the rest defensible. Compression here is not a one-way door. Before the engine emits a smaller payload it writes the original bytes to a local store keyed by a hash of those bytes, and the compressed output carries that handle. An agent that needs the part that was removed asks for the handle and gets the original back, byte for byte, in the same turn. The rule the engine follows has three possible outcomes and no fourth: the payload is compressed and recoverable, or it passes through untouched, or it is recovered. If the recovery store is unavailable, the engine does not compress. It passes the original through. ## Why the pieces are separable Each layer answers a different question, and they fail independently. - Skill: Changes what the agent writes. No process, no config, no network. - Proxy: Changes the base URL and what a payload weighs. A loopback process that routes and records, plus the engine binary it calls to compress. - Memory: Changes what you have to say twice. A local SQLite store. - Cloud: Changes what you can prove to someone else. An account, across a network. Nothing above a layer is required by the layer below it. The skill does not know the proxy exists. The proxy does not need an account. This is why the licence split follows the same lines rather than cutting across them. ## Where the open-core line falls \[Diagram: the licence split across MIT, BSL 1.1 and Commercial surfaces.\] The rule that governs new code is one sentence: a module that imports, links, embeds or ships as part of the engine-linked runtime is BSL 1.1 unless a decision record explicitly classifies it as an adoption surface. The practical effect for you is in the additional use grant. BSL 1.1 here permits internal evaluation, local development, CI, integration, and self-hosted use for your own first-party traffic, production included. What it does not permit is offering Caveman's functionality to third parties as a hosted or embedded service. That boundary is the commercial line, and it is the only one. Each BSL release converts to Apache 2.0 on the earlier of 21 June 2030 or four years after that version was first distributed. ## Content detection The engine does not ask you what a payload is. It reads the bytes and decides, then routes to a compressor built for that shape. The types it recognises: \`json\` · \`log\` · \`code\` · \`diff\` · \`search-result\` · \`text\` · \`toon\` · \`html\` · \`a11y\` · \`terminal\` · \`tabular\` · \`config\` Two compressors are never chosen automatically and have to be asked for by name: the tool-schema compressor and the TOON encoder. Automatic selection of either would surprise a caller who did not opt in. When detection is not confident, it answers \`text\`, which is the compressor that removes the least. Every unknown case in this system resolves toward doing less, not more. ## Failure behaviour This is worth stating on its own, because it is the part that decides whether you can leave the thing switched on. \`\`\`text unknown mode -> record, which never transforms transform error -> forward the original bytes result not smaller -> keep the original low-confidence type -> text unknown grader -> passed: false unknown model price -> zero, tagged unpriced \`\`\` Every one of those resolves to the conservative answer. A compression system that is willing to guess is a compression system you have to babysit. --- # Unknown \--- title: caveman learn summary: Profile a local agent session and rank what is actually eating the budget. canonical: https://docs.caveman.so/docs/skill/learn layer: skill license: MIT capability: skill-caveman-learn updated: 2026-08-26T04:05:35+02:00 basis: inferred --- # caveman learn > Profile a local agent session and rank what is actually eating the budget. \`caveman learn\` reads local agent sessions and shows where tokens are being spent. The command measures and prepares candidates. The \`caveman-learn\` skill is the separate, consent-gated part that can edit a file. - Input: Local Claude Code, Codex, and Caveman session records. - Default window: The last 30 days. - Report basis: \`inferred\` - Edits: One approved change at a time. - Protected class: \`load\_bearing\` ## Run the profiler \`\`\`bash caveman learn \`\`\` In an interactive terminal this shows a local Setup Score, grouped top moves, and a menu for the full report or the editing flow. Stable output modes are available for scripts and pipes: \`\`\`bash caveman learn --plain caveman learn --all caveman learn --json caveman learn --md caveman learn --since 7d --sources codex,claude,caveman \`\`\` \`--plain\` has no animation or keyboard menu. \`--all\` includes every finding, its internal id, its basis, and its suggestion. JSON uses the \`caveman.learn.v1\` schema. If no matching sessions exist, the command returns no score. Repeated-context findings need the same block in three or more sessions, so a new installation may need several sessions before that class appears. ## Read the sink classes | Class | Meaning | Automatic edit | | --- | --- | --- | | \`reducible\` | A heavy instruction file or an installed skill that local evidence says can be smaller. | Candidate only. The skill still asks first. | | \`recurring\_context\` | A block re-established across sessions that may fit local memory better. | Candidate only. Recall must work before source text is removed. | | \`behavioral\` | An observation about how the agent or user works. | None. Repetition does not prove the behaviour is wrong. | | \`load\_bearing\` | Context that must remain available on every turn. | Never. It stays in the score so the score remains honest. | Rates such as \`tokens\_per\_turn\` and \`tokens\_per\_day\_rate\` describe the observed local pattern. The daily field is a rate, not a historical total and not a monthly projection. ## Review fixes with an agent \`\`\`bash caveman learn implement caveman learn implement claude caveman learn implement codex --prompt "focus on project instructions" \`\`\` This installs the safety guide when it is missing, opens Claude Code or Codex, and asks the agent to read the current JSON report. It does not grant permission to apply every finding. You can also install the guide directly: \`\`\`bash caveman tools skills install caveman-learn --agent claude caveman tools skills install caveman-learn --agent codex \`\`\` ## Preview one candidate \`\`\`bash caveman learn apply --dry-run caveman learn apply \`\`\` Both commands prepare information for review. The second writes a candidate under \`~/.caveman/candidates\`; it does not edit your repository. Only the installed skill performs an edit, after showing the proposed change and asking for consent. For a reducible item, the skill compares inferred tokens per turn before and after. If the result is not smaller, it restores the original. For recurring context, the skill re-reads the source block, verifies its SHA-256 locator, stores the raw block in cavemem, and confirms that recall returns it. The source is trimmed only after the pointer and recall path both work. A failed recall removes the new memory and leaves the source in place. \`caveman learn apply\` materializes a candidate. It does not edit a file, move a block, or prove a reduction. The proof happens after an approved edit is measured again. ## What it will not do - Apply all fixes behind one confirmation - Edit a \`load\_bearing\` finding - Treat a behavioral finding as an instruction - Remove recurring context without a working pointer and recall path - Keep an edit whose measured result is not net token negative - Attach currency, monthly savings, or \`verified\` to a local result Reports are also written under \`~/.caveman/reports\`, including HTML and JSON forms. They contain local analysis, so review them before sharing them outside your machine. --- # Unknown \--- title: Agent profiles summary: The registry of coding agents the skill knows how to configure. canonical: https://docs.caveman.so/docs/skill/profiles layer: skill license: MIT capability: agent-profiles updated: 2026-08-30T15:03:57+02:00 basis: inferred --- # Agent profiles > The registry of coding agents the skill knows how to configure. Agent profiles are the registry entries that tell the CLI how a coding agent is installed, launched, and given local Caveman tools. Ten profiles ship in the registry. The JSON files are compiled into the CLI, so source and distributed behaviour stay tied together. - Schema: \`schema\_version: "1"\` - Profiles: Aider, Claude Code, Codex CLI, Gemini CLI, Hermes, Kilo, OpenClaw, opencode, Pi, and Qwen Code. - Fallback: \`generic-env\` - Validation: Unknown hook or injection methods fail compilation. - Licence: MIT. ## Registered agents | Profile id | Wire protocol | Configuration shape | Command-output hook | | --- | --- | --- | --- | | \`aider\` | OpenAI Chat | Environment | Manual | | \`claude\` | Anthropic Messages | Environment | Claude Code hook | | \`codex\` | OpenAI Responses | Host-specific code path | Codex hook | | \`gemini\` | Gemini generateContent | Environment | Gemini hook | | \`hermes\` | OpenAI Chat | Environment plus builder | Hermes plugin | | \`kilo\` | OpenAI Chat | Inline config via environment | Manual | | \`openclaw\` | OpenAI Chat | Config file overlay | OpenClaw plugin | | \`opencode\` | OpenAI Chat | Config content overlay | opencode plugin | | \`pi\` | OpenAI Chat | Native extension | Pi extension | | \`qwen\` | OpenAI Chat | Temporary system-settings overlay | Manual | This table describes declared integration shape. It does not say that every host version or provider credential has been tested. Each profile carries a tested version, and profiles that need code beyond their JSON declaration say so with \`builder-assisted\` or \`code-only\`. ## What one profile contains Every profile has an id, display name, binary names, install hint, wire protocol, injection description, attribution header, tested agent version, completeness label, and fallback. Optional fields describe command hooks, memory hooks, on-disk skill directories, and config overlays. The completeness labels have exact meanings: | Label | Meaning | | --- | --- | | \`declarative\` | Profile data alone describes routing setup. | | \`builder-assisted\` | Profile data is the base, and CLI code adds host-specific setup. | | \`code-only\` | Declared injection is inert; host setup lives in reviewed CLI code. | The registry compiler cross-checks these labels against the real builders. A profile cannot call itself declarative when it depends on code. ## Inspect what is installed \`\`\`bash caveman status caveman doctor claude caveman doctor codex caveman setup \`\`\` \`status\` shows local state. \`doctor \` checks the selected host integration. \`setup\` reports companion binary availability and repair commands. Launch shortcuts use profile ids: \`\`\`bash caveman claude caveman codex caveman gemini caveman aider caveman hermes caveman kilo caveman openclaw caveman opencode caveman pi caveman qwen \`\`\` Each shortcut is equivalent to \`caveman wrap \`. \[Wrap an agent\](/docs/proxy/wrap) is the page for the hop itself. ## Hooks are capabilities, not assumptions Command hooks rewrite noisy shell output through \`caveman shrink\` before the model reads it. A profile without a verified hard hook surface stays manual and receives command guidance instead. Memory hooks are stricter. They are off by default, and a profile may declare one only when the host exposes a verified live user-prompt hook. Today only Claude Code declares that capability. Skill directories are also explicit. Claude Code declares user and project skill roots. Codex declares its user skill root. A profile with no verified convention omits the field, and conversion skips it. ## Failure behaviour Registry compilation rejects duplicate ids, unknown methods, false completeness labels, invalid tested-version metadata, and schema drift. Runtime detection falls back to \`generic-env\` when a declared setup path cannot be used. A profile means the CLI knows the host's integration shape. Live support still depends on installed host version, credential path, protocol, and available recovery surface. Use \`doctor\` on the machine that will run it. ## Adding a profile New profiles begin as JSON that passes the shared schema and compiler. A profile also needs runtime tests for its launch path, configuration injection, fallback, and any hook it claims. Adding a name to the registry without those paths does not make an agent supported. --- # Recall and offload · Caveman docs [Skip to content](https://docs.caveman.so/docs/memory/offload#main) Offloading moves recurring context out of an always-read instruction file and into local cavemem. A short pointer stays where the block used to be, so an agent knows how to recall the compact form and recover exact source bytes. Good candidates[#](https://docs.caveman.so/docs/memory/offload#good-candidates) -------------------------------------------------------------------------------- Offload context when all of these are true: * The same block appears across several sessions * It is useful for some tasks, not every turn * A specific recall query can find it * Pointer plus recall costs fewer inferred tokens than keeping the block resident * Removing it does not weaken a load-bearing instruction Repetition is evidence of recurring cost, not proof that content is unnecessary. Security rules, required build commands, repository boundaries, and other load-bearing instructions stay in place. Measure first[#](https://docs.caveman.so/docs/memory/offload#measure-first) ---------------------------------------------------------------------------- terminal caveman learn --jsoncaveman learn apply recurring_context: --dry-run The learn report identifies repeated blocks by local session evidence. A candidate contains a locator, expected token figures, and proposed pointer text. It does not contain a trusted copy of the block body. Use the consent-gated editing flow for the full procedure: terminal caveman learn implement Safe offload sequence[#](https://docs.caveman.so/docs/memory/offload#safe-offload-sequence) -------------------------------------------------------------------------------------------- 1. 1 Re-read the block from its real source file. Verify its file, session line, block index, and SHA-256 against the candidate locator. Stop if source changed after the scan. 2. 2 Store exact block text. `--` ends option parsing, so a block beginning with a rule such as `---` stays literal. terminal caveman mem remember -- "" 3. 3 Recall it with a topic query and record `tokens_added`. terminal caveman mem recall "" 4. 4 Compare resident cost against pointer plus recall cost. If the new path is not smaller, forget the new memory and leave source untouched. terminal caveman mem forget mem_xxxxxxxx 5. 5 After approval, replace source block with pointer. Confirm recall still returns a hit before finishing. Pointer shape[#](https://docs.caveman.so/docs/memory/offload#pointer-shape) ---------------------------------------------------------------------------- A useful pointer names the topic and both recovery paths: text Recurring migration context lives in cavemem.Recall: caveman mem recall "migration context"Exact source: caveman mem recover Do not put the removed block into the pointer. That would preserve the recurring cost under a new heading. Recall remains bounded[#](https://docs.caveman.so/docs/memory/offload#recall-remains-bounded) ---------------------------------------------------------------------------------------------- Normal cavemem recall returns up to five hits and packs at most 2,000 inferred tokens. The direct `cavemem` binary allows an explicit token budget of zero for unlimited recall: terminal cavemem recall "migration context" 5 0 Unbounded recall is opt-in. The `caveman mem recall` wrapper keeps the safe default and exposes only the result limit. Exact recovery[#](https://docs.caveman.so/docs/memory/offload#exact-recovery) ------------------------------------------------------------------------------ Each compressed recall hit carries a `recovery_handle` from cavemem's own store: terminal caveman mem recover ccr_xxxxxxxx > original.txt That store lives under `~/.caveman/mem`, separate from the engine's shared CCR database. Use the memory recovery command for a memory handle. Auto-recall is optional[#](https://docs.caveman.so/docs/memory/offload#auto-recall-is-optional) ------------------------------------------------------------------------------------------------ Pointer-driven recall is the default. A verified host hook can inject relevant memory on each prompt, but it is off until you enable it: terminal caveman mem hook install claudecaveman mem hook uninstall claude Only hosts with a declared live prompt hook are eligible. Injection fails open, so a recall problem does not block the user's prompt. Every injected hit discloses its inferred token cost. Undo[#](https://docs.caveman.so/docs/memory/offload#undo) ---------------------------------------------------------- Undo requires both parts: restore original block to its source location, then remove stored memory with `caveman mem forget `. Report both changes so the user can verify that recurring context did not disappear. --- # Evals and builds · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/build#main) A Cave Build is an eval-gated freeze of one plan. The command searches on development cases, writes nothing if quality gates fail, then opens holdout cases that the search never saw. Failed evals never produce a lock. **Profile.** Declared split: "profile" evals, or content-blind traces already under .caveman/traces/. ↓ **Search on development.** Candidate plans run only on split: "development" cases. ↓ quality gates pass **Freeze.** The winner is written. Holdout has not been opened yet. ↓ **Holdout.** Untouched split: "holdout" cases. If they fail, there is no lock. Ember is the first time holdout is visible. Search never trains on it. terminal caveman-agent buildcaveman-agent check In a generated project those are `npm run build` and `npm run check`. No Caveman account is required. `npm run build` runs every declared fixture inside the configured search budget. The SDK adds no approval prompt. Eval splits[#](https://docs.caveman.so/docs/agent-sdk/build#eval-splits) ------------------------------------------------------------------------- typescript import { eval as defineEval } from "@caveman-ai/agent";export const profile = defineEval({ id: "profile-a", lineageId: "profile-family", split: "profile", input: "representative task", quality: [{ type: "exact_match", expected: "expected result" }],});export const development = defineEval({ id: "development-a", lineageId: "development-family-a", split: "development", input: "different representative task", quality: [{ type: "exact_match", expected: "expected result" }],});export const holdout = defineEval({ id: "holdout-a", lineageId: "holdout-family-a", split: "holdout", input: "unseen representative task", quality: [{ type: "exact_match", expected: "expected result" }],}); `lineageId` is the stable task-family identifier. Graders the native compiler can lower without a network: `contains`, `not_contains`, `tool_called`, `exact_match`, `json_schema`. `exact_match` is trimmed and case-insensitive by default; `case_sensitive` and `remove_punctuation` opt into stricter variants. A v3 build needs explicit splits. Unsplit legacy suites still produce Pi Cave Build v2. If content-blind Caveman `RunResult`, OpenTelemetry, or OpenInference rows already exist under `.caveman/traces/`, the same `build` command imports them and skips profile-eval spend. Raw prompt and result span attributes are refused. Generic OTel and OpenInference spans stay unpriced; only strict Caveman evidence can be repriced from the pinned public catalog. What a successful v3 build writes[#](https://docs.caveman.so/docs/agent-sdk/build#what-a-successful-v3-build-writes) --------------------------------------------------------------------------------------------------------------------- * `.caveman/agent.lock.json`: Cave Build proof envelope * `.caveman/workload-profile.json`: content-blind profile and provenance * `.caveman/build-report.json`: search cost, holdout evidence, claims, and a local inferred point-estimate `break_even_tasks` when search cost is complete and holdout catalog delta is positive (`null` otherwise) No envelope is written when usage is missing, the model is unpriced, cache regresses, recovery fails, sandbox or privacy fails, quality drops, search is incomplete, or the cost ceiling is exceeded. `check` rejects drift before a model call. Who can change behaviour[#](https://docs.caveman.so/docs/agent-sdk/build#who-can-change-behaviour) --------------------------------------------------------------------------------------------------- Exact native Pi (`tool-free-v1`) owns candidate generation and the locked runner. For agents with no declared tools it can select a priced model, lower reasoning effort, add reversible Context IR routes with derived recovery, and lower output budget. Any root tool, including a subagent, refuses that path before runner spend. Generic Pi, Vercel AI SDK, Eve, and Mastra v3 builds remain baseline-equivalent: they bind identity and evidence around an unchanged baseline plan. They do not currently construct changed model, reasoning, context, transform, recovery, retry, or output-budget behaviour from compiler output. Claude Cave Build compilation and registration refuse. Public `runClaudeAgent` stays unlocked. Callers cannot inject native candidates, runners, or target identity into `compileProfiledNativePi`. `compileProfiled` remains the generic caller-owned-runner API and emits baseline-equivalent v3 only. Hashes bind canonical bytes for integrity. They are not signatures, SBOM provenance, runtime attestation, or proof that registered bytes served traffic. Running a lock[#](https://docs.caveman.so/docs/agent-sdk/build#running-a-lock) ------------------------------------------------------------------------------- typescript import { readFile } from "node:fs/promises";import { runLocked } from "@caveman-ai/agent";import { parseAnyCaveBuildLock } from "@caveman-ai/agent/build";import support from "./agent.js";const build = parseAnyCaveBuildLock(JSON.parse( await readFile(".caveman/agent.lock.json", "utf8"),));const result = await runLocked(support, "Can I get a refund?", build); `runLocked()` accepts only Pi locks. Source freshness remains `caveman-agent check` at deployment. A Pi lock cannot authorise Claude or third-party execution. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/build#what-it-will-not-do) ----------------------------------------------------------------------------------------- A local build report is not a bill. Publish no savings percentage from it. `break_even_tasks` is a point estimate that stays `null` when evidence is incomplete; missing evidence is not a favourable zero. Verified savings remain `$0` until real traffic passes separate rollout and ledger gates that this package does not run. --- # Adapters · Caveman docs [Skip to content](https://docs.caveman.so/docs/agent-sdk/adapters#main) Framework adapters are separate packages with exact upstream version pins. Install only the lane you run. A shared capability manifest records what the adapter actually implements. The registry is discovery, not a grant of execution and not a release certificate. Adapter presence never implies behavioural compiler support or live provider certification. Candidate conformance never mints either. First-party lanes[#](https://docs.caveman.so/docs/agent-sdk/adapters#first-party-lanes) ---------------------------------------------------------------------------------------- These are the lanes the 0.2 source treats as execution harnesses: terminal npm install @caveman-ai/agent @caveman-ai/adapter-vercel-ai-sdk ai@7.0.84npm install @caveman-ai/agent @caveman-ai/adapter-mastra @mastra/core@1.63.2npm install @caveman-ai/agent @caveman-ai/adapter-eve eve@0.29.2 | Package | Upstream pin | Node | Locked native compile | | --- | --- | --- | --- | | `@caveman-ai/adapter-pi` | `@earendil-works/pi-agent-core@0.83.0` | 22.19+ | Yes, tool-free native Pi only | | `@caveman-ai/adapter-claude-agent-sdk` | `@anthropic-ai/claude-agent-sdk@0.3.220` | 22.19+ | No. Public lane is unlocked | | `@caveman-ai/adapter-vercel-ai-sdk` | `ai@7.0.84` | 22.19+ | Baseline-equivalent v3 | | `@caveman-ai/adapter-mastra` | `@mastra/core@1.63.2` | 22.19+ | Baseline-equivalent v3 | | `@caveman-ai/adapter-eve` | `eve@0.29.2` | 24+ | Baseline-equivalent v3, `reasoning: "none"` only | Eve 0.29.2 requires Node.js 24. The other lanes keep the package minimum of 22.19. Each adapter owns and tests its pin. `@caveman-ai/agent/adapters` and `@caveman-ai/agent/claude` remain compatibility exports. Vercel and Mastra host integration is not duplicated there. Claude lane[#](https://docs.caveman.so/docs/agent-sdk/adapters#claude-lane) ---------------------------------------------------------------------------- typescript import { runClaudeAgent } from "@caveman-ai/adapter-claude-agent-sdk";import { fileURLToPath } from "node:url";import support from "./agent.js";const result = await runClaudeAgent(support, "Can I get a refund?", { entryPath: fileURLToPath(new URL("./agent.js", import.meta.url)), maxTurns: 8, maxBudgetUsd: 0.50,}); The public lane is always unlocked. `claimBasis` is `"inferred"`. Verified savings are `$0`. It disables Claude built-in tools and settings, maps declared read-and-inline Caveman tools into one in-process MCP server, reuses the same immutable source snapshot and sandbox executor as Pi, and enforces declared model, reasoning, and output schema. Write, idempotent, and external tools, plus `auto` / page / compress / CCR results, reject before SDK or tool execution, so a side effect cannot land without recovery. Memory and framework subagents fail closed in this lane until equivalent semantics exist. The caller cannot inject Cave Plan or Cave Build identity. Default is 16 turns. Pinned Agent SDK reports aggregate output tokens but no authoritative thinking-token split, so `reasoningUsageBasis` is `"unavailable"`. `reasoningTokens: 0` is a non-evidence placeholder; do not read it as measured zero. Anthropic's SDK is not open source. The Agent SDK remains MIT; using the Claude lane still means reviewing Anthropic's terms. Other adapter packages in source[#](https://docs.caveman.so/docs/agent-sdk/adapters#other-adapter-packages-in-source) ---------------------------------------------------------------------------------------------------------------------- The repository also contains observation and lifecycle adapters. They are not the five execution lanes above. | Package | Upstream pin | | --- | --- | | `@caveman-ai/adapter-langgraph` | `@langchain/langgraph@1.4.13` | | `@caveman-ai/adapter-openai-agents` | `@openai/agents@0.17.0` | | `@caveman-ai/adapter-cloudflare-agents` | `agents@0.22.0` | | `@caveman-ai/adapter-strands-agents` | `@strands-agents/sdk@1.15.0` | A passing candidate report is not compiler support and not live provider certification. What it will not do[#](https://docs.caveman.so/docs/agent-sdk/adapters#what-it-will-not-do) -------------------------------------------------------------------------------------------- An adapter will not lower a baseline plan into a cheaper one unless its capability manifest proves that behaviour, and today only exact native Pi does. Mixing a Pi lock into Claude or a third-party runner is refused. Local adapter results stay `inferred`. --- # Unknown \# Caveman > Documentation for the open Caveman stack: the agent skill, the local proxy and its compression engine, cavemem, and the Agent SDK. What each part does, what it costs, and how every number is counted. Caveman is an efficiency stack for AI agents, in four layers. Skill (00) shapes what the agent writes. Proxy (01) is a loopback listener plus the engine it calls, which compresses payloads and keeps the originals recoverable. Memory (02) stores context so it is not resent. Cloud (03) is the managed plane and is the only paid layer. Layers 00 to 02 run on the reader's own machine and their source is public. The engine is not a separate product and has no top-level route. It is documented under the proxy. Two repositories, plus a hosted plane. github.com/JuliusBrussee/caveman holds the skill, the proxy and its engine, memory, and the CLI, SDK, MCP and standalone-command adoption surfaces: these make an agent you already run cheaper. github.com/JuliusBrussee/agent-sdk holds the Agent SDK, which is for building a new agent whose context, tools and budget are declared up front. Neither requires the other. Caveman Cloud is hosted and has no repository. Each \`##\` section below names which of the three it belongs to. Measurement vocabulary. These three words are fixed and are not interchangeable: - \`inferred\`: A local, per-run estimate computed on your own machine from an offline token counter. Every public Caveman tool emits this and nothing else. Not a saving, not a dollar figure, and never valid to multiply into a monthly or annual total. - \`measured\`: Traffic that was actually observed, rather than estimated from bytes. Not proof that a dollar was saved. Observing a request is not comparing it to its alternative. - \`verified\`: A saving confirmed against a real bill by a hosted rollout system that ran both arms of the comparison. No tool documented on this site can emit it. There is no configuration, flag or account tier that upgrades a local number to this. Every tool documented here emits \`inferred\`. None of them can emit \`verified\`, and no setting changes that. Local figures are never multiplied into a monthly total and never converted to money. How to read this site without HTML: - Append \`.md\` to any page URL, for example https://docs.caveman.so/docs/proxy.md - Or send \`Accept: text/markdown\` to the page URL itself and get the same thing back - https://docs.caveman.so/llms-full.txt is every page in one file - https://docs.caveman.so/llms-index.json is a machine index: headings, anchors, licences and last-changed dates Last changed: 2026-08-30T15:24:20+02:00 ## Start - \[Overview\](https://docs.caveman.so/docs): What Caveman is, which parts are open, which repository each one ships from. - \[Quickstart\](https://docs.caveman.so/docs/quickstart): Install the skill and compress a real payload in about five minutes. - \[Architecture\](https://docs.caveman.so/docs/architecture): The path a request takes through the stack, and where the open-core line falls. - \[How numbers are counted\](https://docs.caveman.so/docs/counting): Inferred against verified, per run against per month, and the four rules the code follows. ## 00 Skill Ships from https://github.com/JuliusBrussee/caveman. What the agent does before it calls out. - \[Skill\](https://docs.caveman.so/docs/skill) \[MIT\]: A set of instructions that changes how your agent spends tokens. No runtime, no proxy. - \[caveman learn\](https://docs.caveman.so/docs/skill/learn) \[MIT\]: Profile a local agent session and rank what is actually eating the budget. - \[caveman explore\](https://docs.caveman.so/docs/skill/explore) \[MIT\]: Read a repository without dragging every file into context. - \[Agent profiles\](https://docs.caveman.so/docs/skill/profiles) \[MIT\]: The registry of coding agents the skill knows how to configure. ## 01 Proxy Ships from https://github.com/JuliusBrussee/caveman. What the payload weighs when it leaves. - \[Proxy\](https://docs.caveman.so/docs/proxy) \[BSL-1.1\]: A loopback reverse proxy: point an agent at it, keep your provider key, and it shrinks what leaves. - \[Wrap an agent\](https://docs.caveman.so/docs/proxy/wrap) \[BSL-1.1\]: Launch Claude Code, Codex, Gemini CLI, and seven other agents through the local listener. - \[Providers\](https://docs.caveman.so/docs/proxy/providers) \[BSL-1.1\]: The HTTP routes, credentials, and compatibility mounts the proxy forwards. - \[Modes\](https://docs.caveman.so/docs/proxy/modes) \[BSL-1.1\]: Record, compress, and pixel, plus the evaluation modes the runtime accepts. - \[Configure\](https://docs.caveman.so/docs/proxy/configure) \[BSL-1.1\]: Feature flags, caveman.yaml, local storage, and how mode is chosen. - \[Security\](https://docs.caveman.so/docs/proxy/security) \[BSL-1.1\]: Loopback only, BYOK, SSRF checks, and what stays on disk. - \[The engine\](https://docs.caveman.so/docs/proxy/engine) \[BSL-1.1\]: The compressor the proxy calls, and the standalone binary behind it. - \[Compressors\](https://docs.caveman.so/docs/proxy/compressors) \[BSL-1.1\]: One router per content shape: JSON, logs, code, diffs, search results, prose. - \[Recoverable compression\](https://docs.caveman.so/docs/proxy/recoverable) \[BSL-1.1\]: Why lossy stays honest: every removed byte can be fetched back. - \[Token counting\](https://docs.caveman.so/docs/proxy/tokens) \[BSL-1.1\]: The offline counter behind every ratio the engine reports. ## 02 Memory Ships from https://github.com/JuliusBrussee/caveman. What you never have to send twice. - \[Memory\](https://docs.caveman.so/docs/memory) \[MIT\]: cavemem holds the context you would otherwise paste into every session. - \[Recall and offload\](https://docs.caveman.so/docs/memory/offload) \[MIT\]: Moving a heavy instruction file out of the prompt and into recall. ## Build with it Ships from https://github.com/JuliusBrussee/caveman. - \[CLI\](https://docs.caveman.so/docs/cli) \[MIT\]: The command surface: compress, detect, learn, and agent setup. - \[TypeScript SDK\](https://docs.caveman.so/docs/sdk/typescript) \[MIT\]: Compress payloads and read spend from Node. - \[Python SDK\](https://docs.caveman.so/docs/sdk/python) \[MIT\]: The same surface for Python agents. - \[MCP server\](https://docs.caveman.so/docs/mcp) \[MIT\]: Expose compression and recall as tools any MCP client can call. - \[caveman-shrink\](https://docs.caveman.so/docs/shrink) \[MIT\]: Shrink command output before it reaches the model. - \[caveman-browse\](https://docs.caveman.so/docs/browse) \[BSL-1.1\]: Read web pages as compressed accessibility trees instead of raw HTML. ## Agent SDK Ships from https://github.com/JuliusBrussee/agent-sdk. - \[Overview\](https://docs.caveman.so/docs/agent-sdk) \[MIT\]: Build an agent that is efficient by construction. - \[Install\](https://docs.caveman.so/docs/agent-sdk/install) \[MIT\]: Build 0.2 from source, run doctor, and the gap with the npm 0.1 package. - \[Create an agent\](https://docs.caveman.so/docs/agent-sdk/create) \[MIT\]: The initializer that writes a typechecking project with a starter eval graph. - \[Define an agent\](https://docs.caveman.so/docs/agent-sdk/define) \[MIT\]: agent(), context, output, filesystem, and subagents as one frozen definition. - \[Tools\](https://docs.caveman.so/docs/agent-sdk/tools) \[MIT\]: Input schemas, effect classes, timeouts, and what happens to a tool result. - \[Run a turn\](https://docs.caveman.so/docs/agent-sdk/run) \[MIT\]: run, stream, receipts, budgets, and durable resume. - \[Sandbox\](https://docs.caveman.so/docs/agent-sdk/sandbox) \[MIT\]: required, fixture, and host: where tool closures run, and what lock eligibility requires. - \[Memory\](https://docs.caveman.so/docs/agent-sdk/memory) \[MIT\]: Local, opt-in, one-turn-behind recall. Never verified, never in the frozen prefix. - \[Compaction\](https://docs.caveman.so/docs/agent-sdk/compaction) \[MIT\]: A fail-closed ladder: recover, summarise, clamp, then stop. Only inside a declared budget. - \[Programmatic tools\](https://docs.caveman.so/docs/agent-sdk/code) \[MIT\]: One caveman\_code cell instead of a large JSON tool catalog. Nested calls still go through the runtime. - \[Connect\](https://docs.caveman.so/docs/agent-sdk/connect) \[MIT\]: Paginated provider data through one stable tool. Fail closed. The daemon is not in this package. - \[Evals and builds\](https://docs.caveman.so/docs/agent-sdk/build) \[MIT\]: Profile, search, freeze, holdout. Failed evals never produce a lock. - \[Adapters\](https://docs.caveman.so/docs/agent-sdk/adapters) \[MIT\]: Exact-pinned framework lanes. Presence is not a grant of execution. ## 03 Cloud Hosted. There is no repository to clone. What a fleet can put in front of finance. - \[Cloud\](https://docs.caveman.so/docs/cloud) \[Commercial\]: The managed plane: fleet visibility, spend attribution, and verified numbers. - \[Model router\](https://docs.caveman.so/docs/router) \[Commercial\]: One model id: classify the task, apply a cost tier, route to measured evidence, fall back to your baseline. ## Reference - \[Licensing\](https://docs.caveman.so/docs/licensing) \[MIT\]: Which surfaces are MIT, which are BSL 1.1, and what that means for you. - \[Telemetry\](https://docs.caveman.so/docs/telemetry) \[MIT\]: What the CLI sends, what it never sends, and the three ways to turn it off. - \[Eval graders\](https://docs.caveman.so/docs/evals) \[MIT\]: The grader set used to check that compression did not change an answer. - \[Provider catalog\](https://docs.caveman.so/docs/provider-catalog) \[MIT\]: The model price table every cost figure is read from. ## Optional - \[Caveman\](https://caveman.so): the product site, pricing, and the Cloud waitlist. - \[Product index for machines\](https://caveman.so/llms.txt): that site's own llms.txt. - \[Source\](https://github.com/JuliusBrussee/caveman): the public repository behind everything documented here. --- # Unknown \--- title: Wrap an agent summary: Launch Claude Code, Codex, Gemini CLI, and seven other agents through the local listener. canonical: https://docs.caveman.so/docs/proxy/wrap layer: proxy license: BSL-1.1 updated: 2026-08-30T15:24:20+02:00 basis: inferred --- # Wrap an agent > Launch Claude Code, Codex, Gemini CLI, and seven other agents through the local listener. Wrapping starts an existing coding agent with the local proxy as its provider endpoint. Caveman does not replace the agent. The agent still owns its model calls, user interface, permissions, and project workflow. Ten profiles ship today. The skill works with thirty-plus agents; wrap is a different, smaller set. A profile is a JSON file the CLI compiles, not a promise that every host version has been tested on your machine. Run \`caveman doctor \` where you will actually launch it. - Command: \`caveman wrap claude\` - Shortcuts: One verb per profile id, same as wrap. - Default mode: \`compress\` - Pass-through: \`caveman wrap --off claude\` ## Launch \`\`\`bash caveman claude caveman codex caveman gemini caveman aider caveman hermes caveman kilo caveman openclaw caveman opencode caveman pi caveman qwen \`\`\` \`caveman kilocode\` is an alias for the same \`kilo\` profile. Arguments after the shortcut are passed through: \`\`\`bash caveman wrap claude caveman wrap --off codex caveman wrap --pixel gemini caveman wrap --workflow review opencode caveman codex --full-auto \`\`\` \`--off\` and \`--pixel\` cannot be used together. For an agent that has no profile: \`\`\`bash caveman run -- my-agent --flag value \`\`\` Generic wrapping supplies proxy environment. It cannot infer every host's native hook or plugin format. ## Registered agents | Profile | Wire protocol | How it is pointed at the proxy | | --- | --- | --- | | \`aider\` | OpenAI Chat Completions | Environment | | \`claude\` | Anthropic Messages | Environment | | \`codex\` | OpenAI Responses | Environment | | \`gemini\` | Gemini GenerateContent | Environment | | \`hermes\` | OpenAI Chat Completions | Environment | | \`kilo\` | OpenAI Chat Completions | Inline config via environment | | \`openclaw\` | OpenAI Chat Completions | Configuration file | | \`opencode\` | OpenAI Chat Completions | Configuration plus environment | | \`pi\` | OpenAI Chat Completions | Native extension | | \`qwen\` | OpenAI Chat Completions | Temporary system-settings overlay | This table is the integration shape. It does not say every credential path or host version works. \[Agent profiles\](/docs/skill/profiles) is the registry page, including completeness labels and hook rules. Kilo and Qwen need their own CLI installed first. The profiles pin the versions the wrap path was checked against: \`\`\`bash npm install -g @kilocode/cli@7.5.6 caveman kilo npm i -g @qwen-code/qwen-code@0.22.3 caveman qwen \`\`\` Kilo organization or enterprise-managed config can override the injected provider. When that happens, or when account state cannot be verified safely, the wrapper launches Kilo directly instead of claiming a confinement it cannot keep. Qwen's overlay is a temporary file. Enterprise policy and unrelated provider settings survive the merge. \`~/.qwen/settings.json\` and the source system settings are not rewritten. ## First run On the first interactive local wrap, the CLI can install the signed runtime bundle and continue the same command. Non-interactive runs do not change installation state without an explicit: \`\`\`bash caveman setup --install \`\`\` Wrap starts \`caveman-proxy\` for that session when it can. If something the CLI does not own is already listening on the port, wrap will not restart or trust it. An interactive run can offer to launch the agent directly when the proxy binary is missing. A script must make sure the listener is up. ## Native setup Claude Code and Codex can install the files those hosts actually read: \`\`\`bash caveman setup --agent-native claude caveman setup --agent-native codex caveman setup --agent-native claude --remove \`\`\` Review the resulting hooks and plugins before you commit dotfiles. Native setup is per agent, not a global rewrite of every tool on the machine. ## Recovery during a run Compressed context carries a \`ccr\_…\` handle, or a typed \`ccr://…\` pointer. Agents with the MCP integration can retrieve the original through a tool call. You can do the same from a terminal: \`\`\`bash caveman tools retrieve caveman tools mcp install qwen --server caveman \`\`\` The Qwen installer preserves sibling MCP entries and refuses to overwrite one it does not own. Streaming and subscription-auth sessions need that agent-side recovery path. Non-streaming API-key requests can use proxy-side handling where the adapter supports it. The CLI checks that \`caveman-mcp\` is present for the selected agent before it advertises the MCP path. If recovery is missing on a path that needs it, that path stays uncompressed. See \[Recoverable compression\](/docs/proxy/recoverable) for handle format and store limits. ## Skill and wrap are separate switches An installed skill does not prove that request compression is on. Proxy traffic does not prove that the response skill is on. Hooks can add reminders or shrink command output without either of those being true. Turn each one on because you want that effect, not because you installed the other. ## When wrap goes wrong 1. \`caveman status\` for the selected mode and whether the listener is ours. 2. \`caveman setup\` for missing binaries. 3. \`caveman wrap --off \` to see whether the failure survives without transforms. 4. Check provider credential variables without printing the secret values. 5. Confirm the agent is using the local endpoint the profile emitted. If a transform cannot parse the body, cannot store a recovery record, or cannot produce a smaller safe result, the original bytes go upstream. That is success for the request, and a zero-ratio row in the local store. --- # Unknown \--- title: Security summary: Loopback only, BYOK, SSRF checks, and what stays on disk. canonical: https://docs.caveman.so/docs/proxy/security layer: proxy license: BSL-1.1 updated: 2026-08-30T15:24:20+02:00 basis: inferred --- # Security > Loopback only, BYOK, SSRF checks, and what stays on disk. The local proxy is a single-operator developer tool. It binds to loopback and has no multi-user authentication layer. Treat it with the same care you already give a coding agent that holds a provider key. - Listen: Loopback only. Non-loopback addresses are rejected. - Auth on the listener: None. That is why the bind check exists. - Keys: Inbound header, then a named environment variable. Never YAML. - Savings label: \`inferred\` ## Trust model One trusted operating-system user. Do not expose the listener on a LAN, a container bridge, a public interface, or a shared host. Signing in to Caveman Cloud does not turn this process into a shared service. Those are different products and different data flows. Local wrap sends request content to the model provider the agent selected, and keeps recovery copies on disk. "Local" describes the Caveman hop, not the whole model request. The provider still sees whatever the proxy forwards. ## Credentials Keep API keys in the environment or in the provider's own credential store. Do not write them into \`caveman.yaml\`, a project overlay, a prompt, a benchmark fixture, or shell history. The proxy preserves an inbound authorization header and does not log it. Environment fallbacks apply only when the integration sent no credential. Distinct development keys are better than sharing a production key with a local listener, where the provider lets you do that. If a terminal, a trace, or an issue report ever printed a key, rotate it. ## SSRF Outbound checks cover configured endpoints and redirects. Private, loopback, link-local, and other unsafe address classes are blocked by default. A self-hosted model on this machine needs an exact \`CAVE\_SSRF\_ALLOWLIST\` entry. Allow only the hosts you need. A broad private-network range can let a prompt-driven request reach an unrelated local service. A listen-address change will not fix a blocked upstream. The bind check and the outbound check are different doors. ## What sits on disk | Store | Typical contents | | --- | --- | | \`~/.caveman/caveman.db\` | Request metadata, usage rows, learn data | | \`~/.caveman/ccr.db\` | Exact originals behind recovery handles | | Feature configuration | Mode and switch values, not keys | | Agent-native hooks | Host-specific files the profile installed | A recovery handle is an identifier, not encryption and not an authorization token. Anyone who can read \`ccr.db\` can retrieve the payload. Do not share handles across a trust boundary. See \[Recoverable compression\](/docs/proxy/recoverable). Restrict permissions on these files to the owner. Hook writes use restrictive modes and symlink-safe replacement; do not loosen that to make a backup script easier. ## Lossy transforms Record mode is the byte pass-through. Compress and pixel change what the model sees. The safety rails around that change are parse validation, a size comparison, explicit capability gates, exact-source recovery, and original-byte fallback when the store fails. Recovery means the source is still available. It does not mean the model will ask for the missing detail, and it does not mean the compressed view had equal quality. For work where every input byte must remain visible, stay in record. ## Local against connected \`caveman help tools\` is the local surface and needs no account. \`caveman help cloud\` is the connected surface and requires login. Command discovery keeps them apart on purpose. A connected installation can persist a hosted endpoint. That path necessarily receives request and response content while it proxies. This page documents the loopback listener. The Cloud product page covers the managed plane at the level a public site can. ## Checklist 1. Confirm the proxy listens on \`127.0.0.1\`. 2. Keep secrets out of both configuration files. 3. Review enabled transforms and the pixel model allowlist. 4. Set a precise SSRF allowlist only when a self-hosted upstream needs it. 5. Restrict permissions on the local databases and hook state. 6. Test recovery before a long lossy session. 7. Use record mode when the input bytes are the point. 8. Review agent, browser, hook, and plugin permissions separately. ## Reporting a vulnerability Do not publish an exploitable report in a public issue before maintainers can assess it. Use the repository security policy. Include the affected version, a minimal reproduction, the impact, and a suggested mitigation, without real credentials or customer data. --- # Licensing · Caveman docs [Skip to content](https://docs.caveman.so/docs/licensing#main) Caveman uses three licences, one per kind of surface. Which one applies depends on what a piece of code is, not on how much you use it. Nothing here has a usage threshold that changes your terms. MIT Adoption and interop surfaces. Use them anywhere, including inside a commercial product. * The skill * CLI * TypeScript SDK * Python SDK * Agent SDK * create-agent * cavemem JS and Python clients * Eval graders * Provider catalog * Wire contracts * Extension shell BSL 1.1 Source available. Run it for your own traffic, including in production. Offering it to third parties as a hosted service needs a commercial licence. Each release converts to Apache 2.0 after four years. * Compression engine * Local proxy * cavemem core * caveman-shrink core * MCP server core * caveman-browse * Shared platform libraries Commercial Runs on our machines, under an account. * Caveman Cloud Three licences, one per surface. Nothing here changes licence based on how much you use it. MIT: adoption and interop[#](https://docs.caveman.so/docs/licensing#mit-adoption-and-interop) ---------------------------------------------------------------------------------------------- Anything a developer installs to work with Caveman is MIT, and stays MIT inside your commercial product. The skill, the CLI, the TypeScript and Python SDKs, the Agent SDK and its initialiser, cavekit, the eval graders, the extension shell, the wire contracts, the provider catalog, and the JavaScript and Python clients for cavemem. BSL 1.1: the engine-linked runtime[#](https://docs.caveman.so/docs/licensing#bsl-11-the-engine-linked-runtime) --------------------------------------------------------------------------------------------------------------- The compression engine and everything that links it: the local proxy, the cavemem core, the caveman-shrink core, the MCP server core, caveman-browse, and the shared platform libraries. BSL 1.1 is source available, not open source. The distinction is one paragraph long. ### What you may do[#](https://docs.caveman.so/docs/licensing#what-you-may-do) Read the source, modify it, run it. Internal evaluation, local development, CI, integration work, and self-hosted use for your own first-party traffic, production included. ### What needs a commercial licence[#](https://docs.caveman.so/docs/licensing#what-needs-a-commercial-licence) Offering Caveman or its functionality to third parties as a hosted, managed or embedded service. That is the OEM and platform boundary, and it is the only restriction. ### When it becomes Apache 2.0[#](https://docs.caveman.so/docs/licensing#when-it-becomes-apache-20) Each BSL version converts on the earlier of 21 June 2030, or the fourth anniversary of that version's first public distribution under BSL. The conversion is per release, so an old version converts before a new one. Commercial: Caveman Cloud[#](https://docs.caveman.so/docs/licensing#commercial-caveman-cloud) ---------------------------------------------------------------------------------------------- Multi-tenancy, verified accounting, governance, billing and the hosted dashboards. These run on our machines under an account and are not distributed. Mixed directories[#](https://docs.caveman.so/docs/licensing#mixed-directories) ------------------------------------------------------------------------------- Two packages ship an MIT launcher around a BSL binary: the MCP server and caveman-shrink. In those directories the launcher terms are in `LICENSE.launcher`, and the Go source plus downloaded-binary terms are in `LICENSE` and `BINARY_LICENSE.md`. The default for new code[#](https://docs.caveman.so/docs/licensing#the-default-for-new-code) --------------------------------------------------------------------------------------------- One sentence, quoted because the wording is the rule: > A new module that imports, links, embeds, or ships as part of Engine-linked runtime is BSL 1.1 unless a later ADR explicitly classifies it as MIT adoption surface. Contributing[#](https://docs.caveman.so/docs/licensing#contributing) --------------------------------------------------------------------- In MIT areas, inbound equals outbound. Your contribution is MIT. In BSL areas, contributions need a DCO sign-off and a relicense grant to Julius Brussee. That grant is what makes the eventual Apache 2.0 conversion possible, since a licence cannot be changed on code the project does not have the rights to change. Third-party code[#](https://docs.caveman.so/docs/licensing#third-party-code) ----------------------------------------------------------------------------- The pixel renderer is a Go port of pxpipe, which is MIT, and uses glyph atlases from Spleen 5x8 (BSD 2-Clause) and GNU Unifont (OFL 1.1, or GPLv2 with the font embedding exception). caveman-browse vendors chromedp, which is MIT. Notices ship next to the code in each case. Trademarks[#](https://docs.caveman.so/docs/licensing#trademarks) ----------------------------------------------------------------- "Caveman" and the Caveman logos are trademarks of Julius Brussee. A code licence grants no trademark rights. Nominative use is fine when it is true: "Powered by Caveman" and "Optimized by Caveman" are both allowed if that is what your product does. --- # Cloud · Caveman docs [Skip to content](https://docs.caveman.so/docs/cloud#main) Everything else on this site runs on your machine and reports `inferred`. Caveman Cloud is the layer across the network boundary, and it exists to answer one question the local tools structurally cannot. The question local tools cannot answer[#](https://docs.caveman.so/docs/cloud#the-question-local-tools-cannot-answer) --------------------------------------------------------------------------------------------------------------------- A local tool sees your bytes. It does not see your invoice. It can tell you a payload got 82 percent smaller. It cannot tell you what that was worth, because the price depends on the model, the provider, the cache state at the time, and your contract. It also cannot tell you whether the smaller payload produced the same answer, because it only saw one arm of the comparison. That gap is the whole reason the managed plane exists. It runs both arms, holds the price table, and compares against traffic that actually happened. What it adds[#](https://docs.caveman.so/docs/cloud#what-it-adds) ----------------------------------------------------------------- Fleet visibility One view across every agent, developer and workflow, instead of one local database per laptop. Spend attribution Which person, project, agent or branch produced which part of the bill. Eval gates An optimisation is only applied to traffic that passed a grader on that traffic. Verified numbers The only place the word verified is allowed, because it is the only place with both arms of the comparison. The vocabulary does not change[#](https://docs.caveman.so/docs/cloud#the-vocabulary-does-not-change) ----------------------------------------------------------------------------------------------------- The same rules that bind the local tools bind the managed plane. `inferred` stays `inferred` when it arrives from a laptop. A number does not get promoted by crossing a network. The four money figures are kept separate and are never added together: measured spend, inferred headroom, verified savings, and observed outcome. There is no fifth bucket, and in particular there is no "realised savings" figure that quietly blends the others. Getting access[#](https://docs.caveman.so/docs/cloud#getting-access) --------------------------------------------------------------------- The waitlist and contact details are on [caveman.so](https://caveman.so/) . Self-hosting the open layers needs no account and no waitlist. If all you want is compression on your own machine, you already have everything you need in the three layers below this one. --- # Eval graders · Caveman docs [Skip to content](https://docs.caveman.so/docs/evals#main) `@caveman/evals` is the public fail-closed grader package. Each grader takes one candidate and returns `{ passed, reason }`. Unknown types, invalid options, missing measurements, and unsafe execution paths fail. Package `@caveman/evals` Runtime Node.js with no production package dependencies. Result `{ passed: boolean, reason: string }` Unknown grader `passed: false` Licence MIT. Build from source[#](https://docs.caveman.so/docs/evals#build-from-source) --------------------------------------------------------------------------- No public registry install is documented for this package. Build the workspace package from source: terminal git clone https://github.com/JuliusBrussee/cavemancd cavemanpnpm install --frozen-lockfilepnpm --filter @caveman/evals build Grade a value[#](https://docs.caveman.so/docs/evals#grade-a-value) ------------------------------------------------------------------- typescript import { grade } from "@caveman/evals";const result = await grade( { type: "exact_match", expected: "ready" }, "READY",);if (!result.passed) { throw new Error(result.reason);} Exact match is case-insensitive by default and compares structured values with sorted object keys. Optional `case_sensitive` and string-only `remove_punctuation` knobs change that behaviour. Invalid knob types fail. Deterministic graders[#](https://docs.caveman.so/docs/evals#deterministic-graders) ----------------------------------------------------------------------------------- Text and structure: * `exact_match`, `contains`, `not_contains` * `regex`, `not_regex`, `blocklist` * `json_schema`, `json_path_assertion` * `bleu_score`, `rouge_score`, `context_f1` * `localization_f1`, `no_pii` Tool and request evidence: * `tool_called`, `tool_not_called`, `tool_sequence` * `tool_argument_assertion` * `http_status`, `latency_threshold`, `cost_threshold`, `token_threshold` Threshold graders require a present, finite, non-negative measurement. An absent cost, token count, or latency cannot pass a ceiling by being treated as zero. The JSON Schema grader implements a documented subset: primitive type, enum, required keys, properties, and items. An unknown schema type fails rather than being ignored. Network graders[#](https://docs.caveman.so/docs/evals#network-graders) ----------------------------------------------------------------------- `custom_webhook` posts `{ candidate }` and requires a JSON response with `passed`. Judge graders are `llm_judge`, `llm_score`, `llm_category`, `llm_pairwise`, and `llm_answer_match`. Network calls share a default ten-second deadline and refuse redirects. Private, loopback, link-local, and documentation IP ranges are blocked. A hostname requires an injected transport that pins the checked DNS result through connection setup; a normal fetch wrapper cannot claim that property. Model judges also fail when configured judge and subject model share a known family. A model grading its own family is a bias risk, not independent evidence. Regex limits[#](https://docs.caveman.so/docs/evals#regex-limits) ----------------------------------------------------------------- Patterns are capped at 1,024 characters and candidates at 64 KiB. Backreferences and risky quantified groups are rejected. Accepted patterns run in a worker with a 250 ms execution budget. These limits choose a failed grade over a process hang. They do not truncate the candidate and pretend the partial result is complete. Localization evidence[#](https://docs.caveman.so/docs/evals#localization-evidence) ----------------------------------------------------------------------------------- `localization_f1` accepts compact `path:start-end` lines or a structured file-to-ranges map. It scores both cited files and line overlap. Empty, unparseable, or zero-quality candidates fail even when a threshold is explicitly zero. This makes it suitable for checking repository explorer output without treating a correct filename and wrong lines as a full match. Run engine fixtures[#](https://docs.caveman.so/docs/evals#run-engine-fixtures) ------------------------------------------------------------------------------- The engine bundles a local fixture harness around the grader contract: terminal caveman tools evals runcaveman tools evals run --fixtures ./fixtures Caller-supplied fixture paths are confined to the supplied directory. A failing grader makes the command exit non-zero. --- # Model router · Caveman docs [Skip to content](https://docs.caveman.so/docs/router#main) Set `model` to `auto` and the gateway picks the model for each request: it classifies the task, applies a cost tier, and routes to the cheapest model with evidence it can hold quality there. Every decision comes back in response headers, and a request that routing cannot answer falls back to your baseline model instead of failing. Model id `auto` Cost tiers `low · medium · high · xhigh · max` Tier override `auto:` in the model field, or a `cost_tier` body field When routing cannot run The request goes to your project's baseline model. Receipts Response headers on every routed request. One model id[#](https://docs.caveman.so/docs/router#one-model-id) ------------------------------------------------------------------ Send your provider's native request through the gateway with `auto` as the model. Nothing else about the request changes. terminal curl https://gateway.caveman.so/openai/v1/chat/completions \ -H "authorization: Bearer cave_live_…" \ -H "content-type: application/json" \ -d '{"model":"auto","messages":[{"role":"user","content":"…"}]}' typescript const completion = await client.chat.completions.create({ model: "auto", messages: [{ role: "user", content: "…" }],}); `cave-auto` is an alias for `auto` and behaves identically. A request that names a real model is never rewritten. Routing only ever acts on requests that ask for it by sending `auto`. Cost tiers[#](https://docs.caveman.so/docs/router#cost-tiers) -------------------------------------------------------------- A tier is a cost band, not a quality promise. There are five, in ascending spend: `low`, `medium`, `high`, `xhigh`, `max`. By default you do not set one. The router reads each prompt's complexity and weights cost against quality accordingly, so a one-line question and a long refactor in the same session are spent on differently. To pin a band yourself, either suffix the model id or send a body field: json { "model": "auto:low", "messages": [] } json { "model": "auto", "cost_tier": "high", "messages": [] } If both are present, the model suffix wins. An unknown tier value is rejected with a client error rather than forwarded upstream as a garbage model name. A pinned tier narrows the public-matrix rankings. When the decision is driven by your project's own graded outcomes instead, those rankings are not price-binned: the pin does not narrow them, and the tier header stays absent so the receipt never claims it did. Pinning can also change what the evidence rests on. Bare `auto` reads every tier's cells and prefers direct evidence wherever it finds it; a pinned tier reads one cell, so a task measured directly in a pricier band may answer a cheap pin from the fallback table instead. That is what asking for the cheap band means. How a request is routed[#](https://docs.caveman.so/docs/router#how-a-request-is-routed) ---------------------------------------------------------------------------------------- model: auto · your request, unchanged otherwise ↓ **1 · Classify.** Task type (one of about thirty, like agent:multi\_step\_planning) and a complexity score that sets the tier, unless you pinned one. ↓ **2 · Look up evidence.** Your project's own graded outcomes for this task type when there are enough of them, otherwise the public benchmark matrix. ↓ **3 · Apply your policy.** Allowed models, denylist, provider health, and capacity ceilings only ever narrow the pool. ↓ **4 · Route with fallbacks.** The top surviving model gets the request; the rest are ordered fallbacks. ↓ anything missing at any step **Baseline.** The request goes to your configured baseline model. It never fails because routing was unavailable. The pipeline narrows, then routes. Every exit lands on a model you approved. The evidence in step 2 has an order. Once your project has graded outcomes for a task type, roughly twenty or more covering at least two candidate models, the router ranks by what worked on your traffic. Before that, it ranks by the public matrix: measured quality per dollar from published benchmark results, refreshed weekly. Your own evidence always outranks the public table, because it is the only table that measured your workload. Receipts[#](https://docs.caveman.so/docs/router#receipts) ---------------------------------------------------------- Every routed response carries the decision: | Header | Meaning | | --- | --- | | `x-cave-router-version` | Which router answered. | | `x-cave-route-from` | The model string you sent, for example `auto:high`. | | `x-cave-route-to` | The model the request was sent to. | | `x-cave-route-tier` | Your pinned tier, present exactly when it narrowed the decision. Absent means no pin, or a pin the decision did not consult. | | `x-cave-route-decision-latency-us` | What deciding cost, in microseconds. | When routing does not run[#](https://docs.caveman.so/docs/router#when-routing-does-not-run) -------------------------------------------------------------------------------------------- Routing is a per-project capability, and a project can mark it opt-in. On an opt-in project, a request activates it by sending `x-cave-optimize: route`. Lossless and pass-through requests are never routed, because those modes promise byte-identical forwarding. And a named model is a named model: the router does not touch it. What it will not do, and what its numbers mean[#](https://docs.caveman.so/docs/router#what-it-will-not-do-and-what-its-numbers-mean) ------------------------------------------------------------------------------------------------------------------------------------- The router does not claim savings. It records decisions; what a decision was worth is computed by the spend plane against traffic that actually happened, under the same vocabulary as everything else here: benchmark-derived rankings are measured on those benchmarks, your task-type rankings are measured on your graded traffic, and neither is `verified` by itself. Where the public matrix has no evidence for a task and tier, the router abstains and uses your baseline rather than guessing. Abstaining is a routing decision and shows up in the receipts like any other. --- # Telemetry · Caveman docs [Skip to content](https://docs.caveman.so/docs/telemetry#main) The CLI sends anonymous usage counts. It is on by default, it is disclosed on the first run that would send anything, and there are three separate ways to turn it off. This page lists the exact contents. Nothing here is a summary of a longer policy: this is the payload. What is sent[#](https://docs.caveman.so/docs/telemetry#what-is-sent) --------------------------------------------------------------------- Two kinds of event, both counts. **Command events.** One per command you run. Event name Which of the known command names ran. CLI version The version of @caveman-ai/cli. Platform Operating system, architecture, and Node major version. Duration How long the command took. Exit class Whether it succeeded, and if not, which class of failure. **Engine session events.** One aggregate after a local wrapped session that was eligible for compression. Counts Request count and token counts. Eligibility How much of the session was eligible for compression. Before and after Measured totals and the amount cut. Cache counters Hit and miss counts. Coverage flags Which parts of the session could be measured at all. Mode observe or compress. There is also a one-time `first_run` event carrying scan aggregates. What is never sent[#](https://docs.caveman.so/docs/telemetry#what-is-never-sent) --------------------------------------------------------------------------------- This list is enforced server side by an allowlist, not by convention on the client. * Prompts, completions, or any model output * Your code, file paths, or URLs * Command arguments * Provider or model names * Request, session or account identifiers * Payload hashes * Any dollar figure The token figures in a session event are local `inferred` measurements. They cannot become a verified saving, because there is no account attached to them and no bill to compare against. The anonymous id[#](https://docs.caveman.so/docs/telemetry#the-anonymous-id) ----------------------------------------------------------------------------- The first interactive run of a real command writes a stable anonymous id to your config, so a returning user is not counted as a new one. It is not linked to an account, and there is nothing on our side that can turn it back into a person. Automation never mints one. CI and non-interactive runs default to off and do not persist a decision, because generating an anonymous identity for a build machine would corrupt the counts it is meant to inform. Turning it off[#](https://docs.caveman.so/docs/telemetry#turning-it-off) ------------------------------------------------------------------------- Any one of these is enough. terminal caveman telemetry off terminal export CAVEMAN_TELEMETRY=0 terminal export DO_NOT_TRACK=1 `DO_NOT_TRACK` is the cross-tool convention and it is honoured here without needing to know that Caveman exists. Setting `CAVEMAN_TELEMETRY=1` turns it on for a single automated run without persisting a decision. If you already answered the old prompt[#](https://docs.caveman.so/docs/telemetry#if-you-already-answered-the-old-prompt) ------------------------------------------------------------------------------------------------------------------------- Earlier versions asked a yes or no question on first run. Whichever way you answered, that answer is kept and is never rewritten by the current default. Opting out once means opting out permanently. The disclosure line[#](https://docs.caveman.so/docs/telemetry#the-disclosure-line) ----------------------------------------------------------------------------------- On the first interactive run that would send anything, the CLI prints this and does not hide it behind a flag: text anonymous usage stats on — command + aggregate engine counts only,never prompts, code, or file paths · caveman telemetry off What is deliberately outside this[#](https://docs.caveman.so/docs/telemetry#what-is-deliberately-outside-this) --------------------------------------------------------------------------------------------------------------- Agent SDK economic receipts contain a per-call spend tree. They are never sent to this endpoint. Moving them anywhere would need an authenticated, account-scoped transport with explicit consent, and that is a different decision from this one. Download counts come from the npm registry. They are not collected by the client. --- # Provider catalog · Caveman docs [Skip to content](https://docs.caveman.so/docs/provider-catalog#main) The provider catalog is the public, source-backed table of model prices and operational facts. Cost calculations read a specific catalog version instead of scattering model prices through application code. Package `@caveman/provider-catalog` Source `catalog/current.yaml` Snapshots Immutable files named by price verification date. Currency Declared per row, currently USD rows. Licence MIT. What a row says[#](https://docs.caveman.so/docs/provider-catalog#what-a-row-says) ---------------------------------------------------------------------------------- One row identifies provider, model, region, currency, prices, capabilities, source links, and verification times. Typed fields may also describe lifecycle, aliases, historical price intervals, tokenizer support, count surface, and cache behaviour. Support facts use three states: | State | Meaning | | --- | --- | | `supported` | Reviewed source says the capability exists. | | `unsupported` | Reviewed source says it does not exist. | | `unknown` | No reviewed fact is available. | Unknown is not false and not permission to guess. Read generated data[#](https://docs.caveman.so/docs/provider-catalog#read-generated-data) ------------------------------------------------------------------------------------------ Build consumers use generated JSON rather than parsing YAML on a request path: text generated/catalog.jsongenerated/manifest.json The catalog carries a semantic digest, price-provenance digest, source digest, and content digest per entry. The manifest records snapshot digests and entry counts. These hashes detect drift; they do not prove that a provider served a request or charged a particular invoice. Use the generated manifest to read current counts. Do not copy a fixed model or provider count into dependent code, because rows change as providers publish and retire models. Unknown prices fail closed[#](https://docs.caveman.so/docs/provider-catalog#unknown-prices-fail-closed) -------------------------------------------------------------------------------------------------------- An unknown provider, model, or region combination is zero-priced with `unpriced:` provenance. Callers must not borrow a nearby model's price or assume two regions match. Zero in this case means no supported price entered the calculation. It does not mean the model is free. Price provenance[#](https://docs.caveman.so/docs/provider-catalog#price-provenance) ------------------------------------------------------------------------------------ `verified_at` is an RFC3339 timestamp tied to price sources. Current rows may not be future-dated or older than 120 days. Every current row must match an immutable `catalog/YYYY-MM-DD.yaml` snapshot selected by its verification date. A price change adds a new dated file. It never rewrites an old snapshot. Sources use provider-owned HTTPS pricing or model documentation. Automated proposals carry a review marker and cannot pass default validation until a person confirms each changed row against its sources and removes the marker. Capability provenance has its own timestamp. Updating a non-price capability does not pretend that price was re-verified on that day. Validate a change[#](https://docs.caveman.so/docs/provider-catalog#validate-a-change) -------------------------------------------------------------------------------------- From the catalog package directory: terminal python3 -m pip install -r requirements-dev.txtpython3 validate_catalog.pypython3 -m unittest discover -s tests -p 'test_*.py' From repository root: terminal pnpm --filter @caveman/provider-catalog buildpnpm --filter @caveman/provider-catalog lintpnpm --filter @caveman/provider-catalog test Validation rejects duplicate YAML keys, duplicate provider/model/region identities, unsafe identifiers, invalid rates, stale provenance, broken aliases, unavailable replacements, unknown fields, and price changes without a matching snapshot. Lifecycle and routing[#](https://docs.caveman.so/docs/provider-catalog#lifecycle-and-routing) ---------------------------------------------------------------------------------------------- Lifecycle distinguishes discovery, review, routing eligibility, and retirement. A reviewed price row is not automatically eligible for routing. Routing needs its own capability and evidence gates outside this data file. Aliases stay provider-local, may not collide with canonical model ids, may not form cycles, and may not resolve to a retired or missing model. What this catalog does not prove[#](https://docs.caveman.so/docs/provider-catalog#what-this-catalog-does-not-prove) -------------------------------------------------------------------------------------------------------------------- The table can support list-price accounting from observed usage. It does not turn inferred local token reductions into saved money, prove a contract rate, or compare against an invoice. Those claims need observed traffic and the right accounting authority. ---