← CavemanCONTENT HISTORY

Update to Caveman

Snapshot Sep 30, 2026 · 23:17 UTC · version 2.7.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "name": "caveman-setup",
  "description": "Use when the user explicitly asks to configure Caveman for a repository, workspace, or cloud-backed workflow.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 216
    }
  ],
  "skill_md_contents": "---\nname: caveman-setup\ndescription: Use when the user explicitly asks to configure Caveman for a repository, workspace, or cloud-backed workflow.\n---\n\nYou are wiring this repository through the Caveman gateway. Caveman is a\nbyte-preserving LLM proxy: in record mode it measures what your app sends and\nwhat it costs, and changes nothing else. Your job is a minimal, verified\nintegration — not a refactor.\n\nThe prompt that sent you here provides four values. Refer to them as:\n\n- `GATEWAY` — the gateway base URL (e.g. `https://gateway.caveman.so` or `http://127.0.0.1:8787`)\n- `CAVE_API_KEY` — the gateway auth secret (treat like any API key: env var only, never committed, never printed in full)\n- `PROVIDER_KEYS` — `stored` (provider keys live encrypted in Caveman Cloud) or `byok` (this app sends its own provider key per request)\n- `DASHBOARD` — the dashboard base URL (e.g. `https://app.caveman.so`)\n\nIf any value is missing, stop and ask for it. Do not guess a URL or mint a key.\n\n## Rules (non-negotiable)\n\n1. **Coherent integration.** Wire every live LLM callsite through existing\n   configuration and responsible seams. Touch each layer correctness requires.\n   No drive-by refactors or formatting sweeps; add an abstraction only when it\n   clarifies ownership or lowers lifecycle cost.\n2. **Secrets stay in env vars.** `CAVE_API_KEY` goes into the env file the repo\n   already uses (`.env`, `.env.local`, …). If that file isn't gitignored, add it\n   to `.gitignore` and say so. Never hardcode the key in source.\n3. **Report only what you observed.** The final report states the HTTP status\n   and usage numbers from the real verification response — never assumed\n   success. If verification fails, report the failure template instead.\n4. **Record mode only.** You are adding measurement. You do not enable any\n   optimization, and you do not claim any savings — verified savings are $0\n   until an optimizer is explicitly turned on and passes its eval gate.\n5. **Provider keys are not your business.** With `PROVIDER_KEYS: stored` you\n   never see one. With `byok`, the app's existing provider key stays exactly\n   where it already is.\n\n## Step 1 — Find every live LLM callsite\n\nRead dependency files (`package.json`, `requirements.txt`, `pyproject.toml`,\n`go.mod`, lockfiles) and search the source for LLM clients:\n\n- SDK imports: `openai`, `@anthropic-ai/sdk`, `anthropic`, `ai` +\n  `@ai-sdk/*` (Vercel), `langchain*`, `litellm`, `google-genai` /\n  `@google/genai`, `crewai`, `pydantic_ai`, `openai-agents` / `agents`\n- Raw HTTP to `api.openai.com`, `api.anthropic.com`, `generativelanguage.googleapis.com`\n- Existing base-URL env vars: `OPENAI_BASE_URL`, `OPENAI_API_BASE`,\n  `ANTHROPIC_BASE_URL`, `GEMINI_BASE_URL`, `GOOGLE_GEMINI_BASE_URL`\n\nList what you found (file:line per callsite) before changing anything. If you\nfind **no** LLM callsites, stop and report the \"nothing to wire\" template at\nthe end of this file — do not invent an integration.\n\n## Step 2 — Pick the app slug\n\nOne slug names this app in the gateway path: `GATEWAY/w/<app>`. Derive it from\nthe package/module name (e.g. `support-bot`, `acme-api`). Grammar:\nlowercase `[a-z0-9]` first, then `[a-z0-9._-]`, max 64 chars. Spend for this\nwhole app groups under that slug on the dashboard.\n\n## Step 3 — Wire each callsite\n\nThe pattern is always the same: **base URL → the gateway with `/w/<app>`,\nplus one auth header.** Gateway auth is `x-cave-api-key: CAVE_API_KEY`\n(`Authorization: Bearer CAVE_API_KEY` also works where a header is awkward).\nWith `PROVIDER_KEYS: byok`, also send `x-cave-upstream-key: <the provider key\nthe app already uses>`.\n\nTwo facts that make the wiring safe (both are gateway-enforced, not hopes):\nthe gateway rebuilds upstream auth headers from scratch, so a client's\n`Authorization`/`x-api-key` value is never forwarded to the provider; and with\n`stored`, upstream auth comes from the encrypted connection server-side. So in\n`stored` mode, where an SDK insists on an api-key parameter, set it to the\nCave key — it authenticates the gateway and goes no further.\n\nExact shapes (use the one matching each callsite — these are the product's\npublished recipes, not suggestions):\n\n**OpenAI SDK (TS)** — Chat Completions and Responses both route through:\n```ts\nconst client = new OpenAI({\n  baseURL: `${process.env.CAVE_GATEWAY_URL}/w/<app>/openai/v1`,\n  apiKey: process.env.OPENAI_API_KEY,           // byok: unchanged · stored: use CAVE_API_KEY\n  defaultHeaders: {\n    \"x-cave-api-key\": process.env.CAVE_API_KEY!,\n    // byok only:\n    \"x-cave-upstream-key\": process.env.OPENAI_API_KEY!,\n  },\n});\n```\n\n**OpenAI SDK (Python)** — same shape: `base_url=f\"{gw}/w/<app>/openai/v1\"`,\n`default_headers={\"x-cave-api-key\": ..., \"x-cave-upstream-key\": ...}`.\n\n**Anthropic SDK (TS/Python)** — the SDK appends `/v1/messages` itself. The\n`x-cave-api-key` header is required here in both modes (this SDK's own key\nparam rides `x-api-key`, which is not a gateway-auth header):\n```python\nclient = anthropic.Anthropic(\n    base_url=f\"{os.environ['CAVE_GATEWAY_URL']}/w/<app>\",\n    api_key=os.environ[\"ANTHROPIC_API_KEY\"],      # byok: unchanged · stored: use CAVE_API_KEY\n    default_headers={\n        \"x-cave-api-key\": os.environ[\"CAVE_API_KEY\"],\n        # byok only:\n        \"x-cave-upstream-key\": os.environ[\"ANTHROPIC_API_KEY\"],\n    },\n)\n```\n\n**Vercel AI SDK** — `createOpenAICompatible({ baseURL: `${gw}/w/<app>/openai/v1`,\nheaders: { \"x-cave-api-key\": ... } })`; Anthropic models via\n`createAnthropic({ baseURL: `${gw}/w/<app>/v1`, headers: { ... } })`.\n\n**LangChain / LangGraph** — `ChatOpenAI(base_url=f\"{gw}/w/<app>/openai/v1\",\ndefault_headers={...})`; `ChatAnthropic(base_url=f\"{gw}/w/<app>\",\ndefault_headers={...})`. LangGraph inherits whatever model you pass it.\n\n**LiteLLM** — per call `api_base=f\"{gw}/w/<app>/openai/v1\"` +\n`extra_headers={...}`, or fleet-wide in the LiteLLM proxy `config.yaml`.\n\n**Raw HTTP / anything else** — swap the host, keep the provider's native path:\n`GATEWAY/w/<app>/v1/chat/completions` (OpenAI protocol) or\n`GATEWAY/w/<app>/v1/messages` (Anthropic protocol), add the header(s).\n\nConcretely, with slug `support-bot` and the hosted gateway, an OpenAI-SDK base\nURL reads `https://gateway.caveman.so/w/support-bot/openai/v1`. And in `stored`\nmode, drop every `x-cave-upstream-key` line entirely — it is byok-only.\n\nFor frameworks not listed (google-genai, crewai, pydantic-ai, openai-agents),\nfetch the matching page under `<docs origin>/docs/integrations/` — same origin\nthis skill came from — and follow it.\n\nAdd to the repo's env file (and reference from code — no literals):\n\n```\nCAVE_GATEWAY_URL=<GATEWAY>\nCAVE_API_KEY=<CAVE_API_KEY>\n```\n\n## Step 4 — Verify with one real request\n\nThe user pasted the setup prompt to authorize exactly this: one small\nverification request. Send it now — do not pause to ask permission for it.\nAn integration that ends unverified because you hesitated is a worse outcome\nthan one tiny request; finishing the verification and the report autonomously\nis the point of this skill.\n\nSend one minimal request through the wiring you just built — the app's own\ncheapest path if it has a script for it, otherwise curl **on the path matching\nthe protocol you just wired** with the app's own model and a small cap\n(`max_tokens` ≤ 32):\n\n```bash\n# OpenAI-protocol wiring:\ncurl -sS \"$CAVE_GATEWAY_URL/w/<app>/v1/chat/completions\" \\\n  -H \"x-cave-api-key: $CAVE_API_KEY\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"model\":\"<model the repo already uses>\",\"max_tokens\":16,\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}'\n\n# Anthropic-protocol wiring:\ncurl -sS \"$CAVE_GATEWAY_URL/w/<app>/v1/messages\" \\\n  -H \"x-cave-api-key: $CAVE_API_KEY\" \\\n  -H \"anthropic-version: 2023-06-01\" \\\n  -H \"content-type: application/json\" \\\n  -d '{\"model\":\"<model the repo already uses>\",\"max_tokens\":16,\"messages\":[{\"role\":\"user\",\"content\":\"ping\"}]}'\n```\n\n(byok: add `-H \"x-cave-upstream-key: $PROVIDER_KEY\"`.) This is one real,\nbillable provider request — that is the point: real traffic, real measurement.\n\nRead the response. Success = HTTP 200 with a `usage` block. Anything else =\nthe matching failure template below.\n\n## Step 5 — Report\n\nEnd with exactly this shape, values filled from what you actually did and saw:\n\n```\n## Caveman is live in this repo\n\nWired: <n> callsite(s) in <n> file(s)\n  - <file> — <one-line what changed>\nApp slug: <app> — spend for this app groups under it\nVerified: HTTP 200 · model <model> · <in> in / <out> out tokens (one real request)\nMode: record — measured only. No model-visible bytes changed, no optimization\nenabled. Verified savings are $0 until you turn an optimizer on and it passes\nits eval gate. That honesty is the product.\n\nSee the dollars: <DASHBOARD>/traces — your request is the top row, priced from\nthe public catalog. <DASHBOARD>/getting-started flips to \"First request received.\"\n\nWant spend split by workflow (e.g. support-reply vs nightly-digest), not just\nby app? Say \"discover workflows\" — I'll fetch <docs origin>/docs/discover-workflows.md\nand label every callsite by the job it does.\n```\n\n## Failure templates (use verbatim, filled in — never soften)\n\n- **Nothing to wire**: \"I found no LLM callsites in this repo (searched SDKs,\n  raw provider HTTP, base-URL env vars). If this repo runs a coding agent\n  rather than shipping LLM code, use `caveman wrap <agent>` instead — see\n  <DASHBOARD>/getting-started.\"\n- **Gateway unreachable**: \"The verification request could not reach GATEWAY\n  (<error>). Wiring is in place but unverified — nothing will be measured\n  until the gateway is reachable. Check the URL and network, then re-run the\n  verification curl above.\"\n- **401 cave_invalid_api_key**: \"The gateway rejected CAVE_API_KEY. Mint a new\n  key at <DASHBOARD>/getting-started and update the env file; the wiring\n  itself is unchanged.\"\n- **404 cave_route_not_found**: \"The gateway matched no route — usually a\n  malformed /w/<app> slug (lowercase [a-z0-9] first, then [a-z0-9._-], max 64)\n  or a path that doesn't match the SDK's protocol. Fix the URL and re-verify.\"\n- **Provider error (4xx/5xx via gateway)**: report status + body verbatim; the\n  gateway is reachable and auth passed, the upstream call failed — usually a\n  provider key or model-name issue in the app itself.\n\nNever report success on any of these. An unverified integration is reported as\nunverified.\n"
}

SHA-256: 5ee65a424e62b14d43f40748630ca6180433054b89d12cb9e02ddf2dd2090eb3