

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# packages/sdk/typescript — TypeScript SDK (`@caveman-ai/sdk`)23Single-file SDK (`src/index.ts`) exported as an ES module. Provides `Cave` (main client),4`CaveTrace` (per-request tracing), and BM25-backed tool-search against the gateway. No runtime5dependencies — only `devDependencies` for TypeScript.67## Layout89- `src/index.ts` — entire SDK; exports `Cave`, `CaveTrace`, `CaveOptions`, `CaveTool`, `ToolSearchResult`, `CompressOptions`, `CompressResult`, and the `ContextPack*` types10- `tests/tool-search.test.ts` — type-level assertions compiled by `tsc --noEmit`11- `tests/tool-search.runtime.mjs` — runtime tests using `node:test` + global fetch mock (imports from `dist/`)12- `tests/runtime-policy.runtime.mjs` + `tests/runtime-policy.test.ts` — the runtime-policy client; drives every section of `../../parity/runtime-policy.fixtures.json` (fetch wire, signature cases, all `assignment_vectors` with exact float equality, all `guard_cases` — the shared operator truth table lives in the fixture, not in this file — and all `decision_cases`). Iterate the arrays; never hard-code their counts13- `tests/parity.runtime.mjs` — cross-language conformance suite; drives `../../parity/fixtures.json` (shared with sdk-python). Same fixtures, two languages → a field in one SDK and not the other fails CI.14- `tests/trace-continuity.runtime.mjs` — trace/span id minting + which requests carry `x-cave-trace-id` / `x-cave-parent-span-id`; mirrors the Python `tests/test_trace_continuity.py`15- `tsconfig.json` / `tsconfig.test.json` — separate configs; test config covers `tests/`. Both extend the **repo-root** `../../../tsconfig.base.json`.1617## Key APIs1819- `new Cave(options)` — requires `apiKey`, `baseURL`, `agent`20- `cave.trace(opts, fn)` — wraps a callback with a `CaveTrace`; sends tool-call spans to `/sdk/v1/events`. The trace mints `traceId` (32 lowercase hex) + a root `spanId` (16 lowercase hex) with the exporter's RNG; `opts.traceId`/`opts.spanId` continue an inbound trace and a value that isn't the exact hex shape is replaced rather than sent. Every provider call made **through the trace** carries `x-cave-trace-id` + `x-cave-parent-span-id`. Generic `/sdk/v1/*` calls and provider clients built off the `Cave` carry neither; the sole SDK-endpoint exception is `CaveTrace.tool`, whose `/sdk/v1/events` call carries the trace id and root parent span id21- `CaveTrace.exporter({serviceName?})` → an `OTelExporter` whose `defaultTraceId` is the trace's, so SDK spans and the gateway's request rows join one trace. MIRRORS the Python `Trace.exporter`22- `cave.tools({ catalog, strategy })` — returns `{ initial, strategy, search(query, opts?) }`. **`search()` is async** (returns `Promise<ToolSearchResult>`); breaking change from 1.0 which was sync. `opts.ranker` (`"bm25"`|`"embeddings"`) is passed through to the gateway verbatim; `opts.toolSessionId` sends `session_id` so provider callbacks can re-inject called deferred tools. The SDK never computes similarity23- `cave.toolSearch(catalog, query, opts?)` — direct variant, same contract (incl. `ranker` / `toolSessionId`). Schema-token counters are estimates; `tokenBasis` discloses the counter and `basis` is always `"inferred"`24- `cave.compress(payload, opts?)` → `Promise<CompressResult>`; POSTs `/sdk/v1/compress`, maps the Engine report. **Byte-safe pass-through** on any transport/parse problem (original input, `ratio:0`, no handle); `tokenCountBasis` discloses the counter and `basis` is always `"inferred"`. The SDK delegates — it never reimplements a compressor25- `cave.context.pack(query, items, options)` → `Promise<ContextPackResult>`; connected-only POST to `/sdk/v1/context/pack`. Lossy selector over caller-owned items, never CCR/ledger; returns exact `deferredIds`. Transport or malformed-report failure returns all original items with zero inferred savings26- `CaveTrace.context.expand(sourceRef)` — the GET half of `checkpoint()`; `GET /sdk/v1/checkpoints/{ref}/expand` returns the stored `{source_ref, version, messages, checkpoint}`27- `cave.openai/anthropic/gemini/vertex()` — thin provider clients; proxied through gateway; each exposes a `.raw` fetch escape hatch (mirrors the Python `Provider.raw`). `cave.bedrock({region, endpoint?})` is a no-network first-party route descriptor: Runtime defaults to `/bedrock`; explicit Mantle returns `/bedrock/anthropic`; `sdkOnly:false` mirrors Python's `sdk_only`28- `cave.prompts.internalBrevity({style, preserveErrorsVerbatim?, preserveCodeVerbatim?})` — output-style snippet (`style:"none"` → `""`); MIRRORS the Python `cave.prompts.internal_brevity`29- `CaveTrace.model.openai.responses.create(body, {cave:{latencyClass, toolSessionId}})` — passing a `latencyClass` hint sets the `x-cave-async` header (`"true"` unless `"interactive"`); passing `toolSessionId` sets `x-cave-tool-session`. Mirrored by the Python `responses.create(body, latency_class=..., tool_session_id=...)`30- `CaveTrace.artifacts.page()` — sends versioned `{value, options, workflow}`; gateway stores only JSON `value`. `artifacts.get(id)` performs authenticated retrieval. `strategy:"verbatim"` bypasses storage. Mirrored by Python.31- `CaveTrace.context.checkpoint()` — POSTs to `/sdk/v1/checkpoints`; gateway persists it (Valkey) + returns a reversible `source_ref` (expand via `GET /sdk/v1/checkpoints/{ref}/expand`)32- `cave.exporter({serviceName?})` → `OTelExporter`; `recordSpan(...)` maps current GenAI fields to `gen_ai.*`, `export()` POSTs OTLP/JSON to standard `/v1/traces` (headers via `otlpHeaders()`; legacy `/otlp/v1/traces` remains server-only compatibility)33- `cave.runtimePolicy({publicKey?, autoRefreshSeconds?, killEnv?})` → `RuntimePolicyClient`. `refresh()` is the only network call (`GET /sdk/v1/runtime-policy`, std headers minus content-type); it Ed25519-verifies the bundle **string's** exact bytes before parsing, TOFU-pins the key **the moment the signature verifies** (before the schema/sequence checks, so a rejected-but-signed bundle cannot open a downgrade window), rejects a regressed `sequence`, and keeps last-known-good on any failure. The fetch carries a 30s `AbortSignal.timeout` (mirrors Python's `timeout=30`); an `autoRefreshSeconds` tick that lands mid-refresh is skipped, not stacked. `decide(taskFamily, {unitKey, context, trace})` is **synchronous, local-only, and never throws**; holdout suppresses onto the fallback path and a missing unit key or invalid experiment never guesses an arm. `kill()` latches locally, `killEnv` is re-read per decide, `state()` snapshots. Routing only — no savings vocabulary anywhere. MIRRORS the Python `cave.runtime_policy()`34- `cave.retryLoopBreaker(threshold=3)` → `RetryLoopBreaker`; `.record(name, args)` throws `RetryLoopError` after `threshold` consecutive identical tool calls; `.guard(name, args, fn)` records then runs `fn`35- `cave.jobs` → reserved `JobsClient` surface. Every method fails locally with `cave_async_jobs_unavailable`; it performs no network request until durable encrypted request storage, credential custody, and a draining worker exist. MIRRORS the Python `Cave.jobs`3637## Conventions3839- Tests: type assertions in `.test.ts` (compiled only), runtime in `.runtime.mjs` (run against `dist/`)40- Build before runtime tests: `pnpm build && pnpm test:node`41- Request body keys are `snake_case` to the gateway; response mapped to `camelCase` in `ToolSearchResult`42- `x-cave-workflow` header defaults to `defaultWorkflow ?? "unlabeled-workflow"`; never omit it43- Deferred tool-search session handoff uses request `session_id`, result `sessionId`, and provider header `x-cave-tool-session`; update sdk-python + parity fixtures with any change4445## Gotchas4647- **byte-safe**: SDK sends request bodies to the gateway verbatim; no rewriting allowed. `compress()` is the one path that yields smaller bytes and it **delegates** to the Engine — on any problem it passes the original through48- **context packing is connected-only and intentionally lossy**: it sends item bytes to gateway, never runs in local wrap, and relies on caller retaining every item named by `deferredIds`. It chooses what enters window; cache-optimal assembly chooses placement49- **mirror sdk-python**: every field/method exists in both, enforced by the shared parity suite — a divergence is a CI failure, not a convention slip. Change one SDK, change both **and** the fixtures50- published as `@caveman-ai/sdk`; the workspace name stays `@caveman-ai/sdk` until the npm redirect plan lands51- `strategy:"deferred"` initial set = `alwaysLoad` tools + up to `initialToolCount` (default 8); never returns the full catalog without a `search()` call52- `reductionPct` rounds to one decimal; `savedTokens` is derived (`full - sent`), not from the gateway response5354See ../../../CLAUDE.md (root)55
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| JuliusBrussee/cavemanCLAUDE.md · 98k | CLAUDE.md | archgitdo-notagent-behaviour+1 | 73/100 | today | |
| JuliusBrussee/cavemanagents/AGENTS.md · 98k | AGENTS.md | agent-behaviour | 16/100 | today | |
| JuliusBrussee/cavemanagents/CLAUDE.md · 98k | CLAUDE.md | stylearchsecurityagent-behaviour | 64/100 | today | |
| JuliusBrussee/cavemanbrowse/CLAUDE.md · 98k | CLAUDE.md | testarch | 59/100 | today | |
| JuliusBrussee/cavemancacheengine/CLAUDE.md · 98k | CLAUDE.md | testarch | 73/100 | today | |
| JuliusBrussee/cavemanengine/AGENTS.md · 98k | AGENTS.md | stylearch | 66/100 | today | |
| JuliusBrussee/cavemanengine/CLAUDE.md · 98k | CLAUDE.md | stylearch | 58/100 | today | |
| JuliusBrussee/cavemanextension/AGENTS.md · 98k | AGENTS.md | buildteststylearch+1 | 73/100 | today | |
| JuliusBrussee/cavemanextension/CLAUDE.md · 98k | CLAUDE.md | buildteststylearch+1 | 73/100 | today | |
| JuliusBrussee/cavemanintegrations/CLAUDE.md · 98k | CLAUDE.md | stylearch | 55/100 | today | |
| JuliusBrussee/cavemanmcp/AGENTS.md · 98k | AGENTS.md | stylearch | 79/100 | today | |
| JuliusBrussee/cavemanmcp/CLAUDE.md · 98k | CLAUDE.md | stylearch | 79/100 | today | |
| JuliusBrussee/cavemanmem/AGENTS.md · 98k | AGENTS.md | stylearchperformanceagent-behaviour | 67/100 | today | |
| JuliusBrussee/cavemanmem/CLAUDE.md · 98k | CLAUDE.md | stylearchperformanceagent-behaviour | 71/100 | today | |
| JuliusBrussee/cavemanpackages/agent/CLAUDE.md · 98k | CLAUDE.md | archtesting-strategysecuritydependencies+2 | 34/100 | today | |
| JuliusBrussee/cavemanpackages/cli/AGENTS.md · 98k | AGENTS.md | stylearchdependenciesmonorepo | 71/100 | today | |
| JuliusBrussee/cavemanpackages/cli/CLAUDE.md · 98k | CLAUDE.md | stylearchdependenciesmonorepo | 71/100 | today | |
| JuliusBrussee/cavemanpackages/create-caveman-agent/CLAUDE.md · 98k | CLAUDE.md | archdependenciesmonorepoagent-behaviour | 36/100 | today | |
| JuliusBrussee/cavemanpackages/graders/AGENTS.md · 98k | AGENTS.md | teststylearchtypes+3 | 75/100 | today | |
| JuliusBrussee/cavemanpackages/graders/CLAUDE.md · 98k | CLAUDE.md | teststylearchtypes+3 | 75/100 | today |
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 68k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 13 days ago | |
| mui/material-uiAGENTS.md · 99k | AGENTS.md | setupbuildtestlint-format+9 | 100/100 | 14 days ago | |
| duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70 | AGENTS.md | buildteststylearch+3 | 100/100 | 14 days ago | |
| deepseek-ai/deepseek-harnessnative/landlock-run/AGENTS.md · 104k | AGENTS.md | setupteststylearch+3 | 100/100 | today | |
| n8n-io/n8npackages/@n8n/agents/AGENTS.md · 201k | AGENTS.md | buildteststylearch+3 | 100/100 | 14 days ago | |
| TryGhost/Ghoste2e/AGENTS.md · 55k | AGENTS.md | setupteststylearch+2 | 100/100 | today | |
| aaif-goose/gooseAGENTS.md · 53k | AGENTS.md | setupbuildtestlint-format+7 | 100/100 | 8 days ago | |
| ethereum/go-ethereumAGENTS.md · 51k | AGENTS.md | buildtestlint-formatgit+1 | 100/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/juliusbrussee-caveman-packages-sdk-typescript-agents)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.