---
title: API reference
summary: Every public method and type, TypeScript and Python side by side.
canonical: https://docs.caveman.so/docs/sdk/reference
license: MIT
capability: sdk-ts
updated: 2026-09-16T21:32:40-07:00
basis: inferred
---

# API reference

> Every public method and type, TypeScript and Python side by side.
Every public export of `@caveman-ai/sdk` and `caveman-sdk`, with the TypeScript signature beside the Python one. Both packages are version 1.1.0, MIT licensed, and have no runtime dependencies.

## Packages

| | TypeScript | Python |
| --- | --- | --- |
| Install | `npm install @caveman-ai/sdk` | `pip install caveman-sdk` |
| Client entry point | `@caveman-ai/sdk` | `caveman_cloud` |
| Middleware entry point | `@caveman-ai/sdk/middleware` | `caveman_cloud.middleware` |
| Runtime | Node 22.13 or newer | Python 3.13 or newer |
| Module format | ESM, with type declarations | Typed package, `py.typed` |
| Runtime dependencies | None | None |
| HTTP | `fetch` | `urllib` |

The TypeScript package publishes an `import` entry point. The Python distribution is `caveman-sdk` on PyPI and imports as `caveman_cloud`. [Middleware](/docs/sdk/middleware) documents the second entry point.

## Constructor options

`CaveOptions`, reformatted from one line in the source:

```ts
export type CaveOptions = {
  apiKey: string;
  baseURL: string;
  agent: string;
  defaultWorkflow?: string;
  retention?: "metadata" | "zdr" | "configured";
  verifyOnInit?: boolean;
  controlURL?: string;
  user?: string;
  /** Finite deadline for every SDK HTTP request; defaults to 30 seconds. */
  timeoutMs?: number;
  /** Caller cancellation applied to every SDK HTTP request. */
  signal?: AbortSignal;
};
```

The Python `Cave` dataclass, public fields only, with its comments and two private ledger fields dropped:

```python
@dataclass
class Cave:
    api_key: str
    base_url: str
    agent: str
    default_workflow: str = field(default_factory=lambda: _env_workflow())
    retention: str = "metadata"
    verify_on_init: bool = False
    control_url: str | None = None
    user: str | None = None
```

`apiKey`, `baseURL` and `agent` are required. TypeScript throws `apiKey, baseURL, and agent are required` when one is empty. Python raises `ValueError` at construction for an empty or non-absolute `base_url`; an empty `api_key` or `agent` is accepted and fails at the first request.

`baseURL` and `controlURL` must be absolute `http` or `https` URLs with no credentials, query or fragment, and a trailing slash is stripped. `defaultWorkflow` falls back to the `CAVE_WORKFLOW` environment variable, lowercased and accepted only as 1 to 96 characters of `[a-z0-9_-]`, then to `unlabeled-workflow`. `retention` rides on every request as `x-cave-retention`. `user` is your own opaque end-user identifier, sent as `x-cave-user-hash`; hash it yourself if the raw value identifies a person.

`timeoutMs` and `signal` exist in TypeScript only, and `timeoutMs` must be a positive integer. Python's deadlines are fixed per call: 30 seconds for tool search, context packing, policy refresh, the plan read, events, checkpoints and artifacts, and 300 seconds for compression, provider calls and shared context.

## Cave

| Method | TypeScript | Python | What it does |
| --- | --- | --- | --- |
| Provider client | `openai(config?)` | `openai(upstream_key=None)` | OpenAI client on `/openai/v1` |
| | `anthropic(config?)` | `anthropic(upstream_key=None)` | Anthropic client on `/anthropic` |
| | `gemini(config?)` | `gemini(upstream_key=None)` | Gemini client on `/gemini` |
| | `vertex(config?)` | `vertex(upstream_key=None)` | Vertex client on `/vertex` |
| Descriptor | `bedrock({ region, endpoint? })` | `bedrock(region, *, endpoint="runtime")` | Describes the Bedrock route, sends nothing |
| Trace | `trace(options, fn)` | `trace(workflow=None, tags=None, *, trace_id=None, span_id=None)` | Async callback in TypeScript, context manager in Python |
| Compression | `compress(payload, options?)` | `compress(payload, *, content_type=None)` | Compress one payload, passing through on failure |
| Tools | `tools({ catalog, strategy?, initialToolCount?, maxLoadedTools? })` | `tools(catalog, *, strategy="all", initial_tool_count=8, max_loaded_tools=None)` | Build a catalog handle with `initial` and `search` |
| | `toolSearch(catalog, query, options?)` | `tool_search(tools, query, *, context=None, max_tools=None, workflow=None, ranker=None, session_id=None)` | Search a catalog you manage yourself |
| Context | `context.pack(query, items, options)` | `context.pack(query, items, options)` | Fit fragments to a token budget |
| | `assemble(options)` | `assemble(options)` | Build a request with stable content above volatile |
| Shared context | `sharedContext.put(key, content)` | `shared_context.put(key, content)` | Store handoff context under a session key |
| | `sharedContext.get(key)` | `shared_context.get(key)` | Read it back byte-exact |
| Telemetry | `exporter({ serviceName? })` | `exporter(*, service_name=None)` | An OTLP exporter bound to this client |
| Policy | `runtimePolicy(options?)` | `runtime_policy(*, public_key=None, auto_refresh_seconds=None, kill_env="CAVEMAN_POLICY_KILL", workflow=None)` | A local runtime policy client |
| Breaker | `retryLoopBreaker(threshold = 3)` | `retry_loop_breaker(threshold=3)` | A fresh retry loop breaker |
| Prompts | `prompts.internalBrevity({ style, preserveErrorsVerbatim?, preserveCodeVerbatim? })` | `prompts.internal_brevity(*, style, preserve_errors_verbatim=False, preserve_code_verbatim=False)` | An output-style instruction string |
| Plan | `cavePlan()` | `cave_plan()` | Read the project's published plan verbatim; throws on a non-200 |
| Jobs | `jobs` | `jobs` | Reserved, see below |

TypeScript's `search()` on a `tools()` handle is `async` from 1.1.0 and returns a `ToolSearchResult`. In 1.0 it was synchronous and returned an array.

`prompts.internalBrevity({ style: "technical-concise", preserveErrorsVerbatim: true })` returns, in both languages:

```console
Internal output style: technical-concise. Preserve errors verbatim: true. Preserve code verbatim: false.
```

`style` is `"technical-concise"`, `"caveman"` or `"none"`, and `"none"` returns the empty string.

## Provider client

| TypeScript | Python | What it does |
| --- | --- | --- |
| `provider.responses.create(body, init?)` | `provider.responses.create(body, *, latency_class=None, tool_session_id=None)` | POST to `{prefix}/responses` |
| `provider.chat.completions.create(body, init?)` | `provider.chat["completions"].create(body, ...)` | POST to `{prefix}/chat/completions` |
| `provider.raw(input, init?)` | `provider.raw(path, body)` | TypeScript returns the `Response`; Python returns decoded JSON |
| `provider.provider` | `provider.prefix` | The provider name, and the route prefix |

TypeScript's `init.cave` carries `{ latencyClass?, toolSessionId? }`, which become the `x-cave-async` and `x-cave-tool-session` headers. [Provider calls](/docs/sdk/providers) has the examples.

## Trace

TypeScript calls the class `CaveTrace`; Python calls it `Trace` and reaches it through the context manager rather than an export.

| TypeScript | Python | What it does |
| --- | --- | --- |
| `trace.traceId`, `trace.spanId` | `trace.trace_id`, `trace.span_id` | 32 and 16 lowercase hex characters |
| `trace.tool(name, options, fn)` | `trace.tool(name, options, fn)` | Run `fn` and report a `tool.call` event |
| `trace.model.openai.responses.create(body, init?)` | `trace.model["openai"].responses.create(body)` | A provider call carrying this trace's ids |
| `trace.exporter({ serviceName? })` | `trace.exporter(*, service_name=None)` | An exporter sharing this trace's id, memoized per service name |
| `trace.artifacts.page(value, options)` | `trace.page_artifact(value, options)` | Store a large value, return a stub |
| `trace.artifacts.get(id)` | `trace.get_artifact(artifact_id)` | Fetch a stored artifact |
| `trace.context.checkpoint(messages, options)` | `trace.checkpoint(messages, options)` | Store a conversation snapshot |
| `trace.context.expand(sourceRef)` | `trace.expand(source_ref)` | Read a checkpoint back |

Python also exposes `trace.artifacts.page` and `trace.artifacts.get`, which delegate to the two methods above. [Context packing](/docs/sdk/context) covers all four.

## OTelExporter

| TypeScript | Python | What it does |
| --- | --- | --- |
| `recordSpan(name, options?)` | `record_span(name, **options)` | Buffer a span and return it |
| `export()` | `export()` | POST the buffer to `{baseURL}/v1/traces` and clear it |
| `flush()` | `flush()` | Alias for `export()` |
| `buildPayload()` | `build_payload()` | The OTLP/JSON body, without sending |
| `pending` | `pending` | Spans buffered and in flight |
| `newTraceId()`, `newSpanId()` | `new_trace_id()`, `new_span_id()` | Fresh ids |
| `serviceName`, `defaultTraceId` | `service_name`, `default_trace_id` | What this exporter was bound to |

`SpanOptions` in TypeScript is `{ traceId?, spanId?, parentSpanId?, kind?, provider?, model?, operation?, toolName?, inputTokens?, outputTokens?, cachedTokens?, costUsd?, workflow?, status?, startTimeNs?, endTimeNs?, attributes? }`. Python takes the same set as keyword arguments in snake_case, with `status="ok"` and `kind=3` as defaults. [Tracing](/docs/sdk/tracing) has the attribute mapping.

## RuntimePolicyClient

| TypeScript | Python | What it does |
| --- | --- | --- |
| `refresh()` | `refresh()` | Fetch and verify the bundle; returns a result rather than raising |
| `decide(taskFamily, options?)` | `decide(task_family, unit_key=None, context=None, trace=None)` | Route one task locally |
| `kill()` | `kill()` | Latch this client onto baseline |
| `state()` | `state()` | What the client holds now |
| `close()` | `close()` | Stop the background refresh |

TypeScript's `DecideOptions` is `{ unitKey?, context?, trace? }`; Python takes the three as positional or keyword arguments. `policyUnitFraction(...keys)` and `policy_unit_fraction(*keys)` are module-level functions that return the assignment fraction. [Runtime policy](/docs/sdk/policy) covers every decision reason.

## RetryLoopBreaker

| TypeScript | Python | What it does |
| --- | --- | --- |
| `record(name, args)` | `record(name, arguments)` | Count a call, throwing past the threshold |
| `guard(name, args, fn)` | `guard(name, arguments, fn)` | Record, then run `fn` |
| `signature(name, args)` | `signature(name, arguments)` | The canonical signature string |
| `reset()` | `reset()` | Clear the streak |
| `threshold` | `threshold` | Default 3, fires on the fourth identical call |

## JobsClient

`cave.jobs` is a reserved surface. `submit`, `status`, `cancel`, `wait` and `submitAndWait` all raise `AsyncJobsUnavailableError` locally, before any request.

## Errors

| Class | Package | When |
| --- | --- | --- |
| `CaveRequestError` | TypeScript | A non-200 or an unparseable body on an events, artifact, checkpoint or shared-context call. Carries `status` and `path` |
| `AssemblyStabilityError` | Both | A `stable` or `session` slot changed inside its session |
| `RetryLoopError` | Both | An identical tool call repeated past the threshold. Carries `signature`, `repeats`, `threshold` |
| `AsyncJobsUnavailableError` | Both | Any `jobs` method. Carries `code` `cave_async_jobs_unavailable` |
| `urllib.error.HTTPError` | Python | Any non-200 the Python client does not pass through |

Python raises `ValueError` for bad arguments where TypeScript throws a plain `Error`. [Troubleshooting](/docs/sdk/troubleshooting) lists the messages.

## Exported types

TypeScript exports these type declarations alongside the classes: `CaveOptions`, `TraceOptions`, `ToolOptions`, `CaveTool`, `ToolSearchResult`, `CompressOptions`, `CompressResult`, `ContextPackItem`, `ContextPackOptions`, `ContextPackResult`, `AssemblyStability`, `EmitCacheHints`, `AssemblySlot`, `AssembleOptions`, `AssemblyResult`, `TaskProfile`, `SpanOptions`, `OTelSpan`, `LatencyClass`, `Job`, `RuntimePolicyGuard`, `RuntimePolicyArm`, `RuntimePolicyExperiment`, `RuntimePolicyDoc`, `PolicyDecisionReason`, `PolicyDecision`, `RuntimePolicyOptions`, `RuntimePolicyRefresh`, `RuntimePolicyState`, `PolicySpanRecorder`, `PolicySpanSink`, `DecideOptions`, and the `CavePlan` interfaces.

Python's `__all__` is `AsyncJobsUnavailableError`, `AssembleOptions`, `AssemblyResult`, `AssemblySlot`, `AssemblyStabilityError`, `Cave`, `CaveTool`, `CompressResult`, `ContextPackItem`, `ContextPackOptions`, `ContextPackResult`, `Job`, `JobsClient`, `OTelExporter`, `OTelSpan`, `PolicyDecision`, `RetryLoopBreaker`, `RetryLoopError`, `RuntimePolicyClient`, `RuntimePolicyRefresh`, `RuntimePolicyState`, `TaskProfile`, `ToolSearchResult`, `policy_unit_fraction`.

Python's results are dataclasses, so field access is `report.tokens_before` rather than a dictionary lookup. `ToolSearchResult.saved_tokens` and `reduction_pct` are properties computed from the two schema-token counts.
