@cloven/sdk

The official TypeScript client. One factory, pack-scoped methods, typed errors, async-iterable subscriptions. Ships ESM-only, sub-50KB gzipped, zero runtime deps beyond viem (peer, optional — only needed for x402 wallet mode).

pnpm add @cloven/sdk
# or: npm i @cloven/sdk

The SDK targets Node ≥ 20 and the Edge runtime. It does not run in browsers — your API key would be exposed in client-side JavaScript. Use it from your backend, your serverless functions, or your agent process.

Construct the client

import { cloven } from "@cloven/sdk";
 
const client = cloven({
  apiKey: process.env.CLOVEN_KEY,
});

Options

interface ClovenClientOptions {
  /** Bearer API key. Mutually exclusive with `wallet`. */
  apiKey?: string;
  /** viem WalletClient — enables x402 mode. Mutually exclusive with `apiKey`. */
  wallet?: import("viem").WalletClient;
  /** Override the base URL. Defaults to https://api.cloven.cloud. */
  baseUrl?: string;
  /** Custom fetch — useful for retries / observability. Defaults to globalThis.fetch. */
  fetch?: typeof fetch;
}

If both apiKey and wallet are passed, apiKey wins (Bearer auth, no x402 dance). Pass wallet alone to opt into x402 mode.

Pack-scoped methods

Every call is scoped to a pack via .pack(id):

const ctx = await client.pack("crypto").fresh();
 
console.log(ctx.brief);
console.log(ctx.freshness.ageSeconds);
console.log(ctx.state.top_movers);

.fresh(opts?)

Latest Mind State + brief + citations + freshness.

interface FreshOpts {
  /** Optional natural-language refinement. */
  query?: string;
}
 
interface MindResponse<TState> {
  state: TState;
  brief: string;
  citations: Citation[];
  freshness: { generatedAt: string; ageSeconds: number };
}

.brief()

Brief-only — cheaper when the caller maintains its own state cache.

const { brief, citations, freshness } = await client.pack("crypto").brief();

.search(q, opts?)

Top-k walk over compacted state.

interface SearchOpts { k?: number; }                  // 1–50, default 10
 
interface SearchResponse {
  matches: Array<{ path: string; value: unknown; score: number }>;
  total: number;
  freshness: { generatedAt: string; ageSeconds: number };
}
 
const { matches } = await client.pack("crypto").search("ETH funding", { k: 5 });

.snapshot(at)

Time-travel. at is an ISO8601 string or a Date.

const past = await client.pack("crypto").snapshot("2026-05-23T10:00:00Z");

.cite(ref)

Resolve a [N] citation. ref is a 1-indexed integer.

const { citation, source } = await client.pack("crypto").cite(3);

.subscribe()

Long-lived pulse stream as an AsyncIterable. The SDK handles SSE reconnection, keepalive parsing, and clean shutdown.

const stream = client.pack("crypto").subscribe();
 
for await (const pulse of stream) {
  console.log(pulse.ts, pulse.state.top_movers[0]);
  if (shouldStop) break;     // breaking closes the underlying connection
}

pulse is shaped { pack: string; state: TState; ts: string; eventType: "pulse" | "delta" }.

To stop early, break out of the loop or call stream.return(). Both close the SSE connection cleanly.

Typed errors

Every error inherits from ClovenError:

import {
  ClovenError,
  ClovenApiError,
  X402Required,
  QuotaExhausted,
  PackAccessDenied,
} from "@cloven/sdk";
 
try {
  await client.pack("ai").fresh();
} catch (err) {
  if (err instanceof PackAccessDenied) {
    // upgrade flow
  } else if (err instanceof QuotaExhausted) {
    console.log("retry after", err.retryAfterSeconds);
  } else if (err instanceof X402Required) {
    // only thrown in wallet mode if the auto-pay flow fails
    console.log("quote was", err.payment);
  } else if (err instanceof ClovenApiError) {
    console.error(err.code, err.message, err.requestId);
  } else {
    throw err;             // network errors, JSON parse errors, etc.
  }
}
ErrorWhen
X402Requiredx402 mode failed mid-auto-retry (wallet rejected, RPC down).
ClovenApiErrorAny structured 4xx/5xx with code + message + requestId.
QuotaExhausted429 — exposes retryAfterSeconds.
PackAccessDenied403 — tier doesn't include the pack.
ClovenErrorBase class; catch-all.

Pack-typed state

If you import type { CryptoState } from "@cloven/sdk/packs/crypto", you get the full Zod-derived TypeScript type for the crypto pack's state. The SDK exports one per shipped pack — IDE autocomplete on ctx.state.narratives[0].momentum works out of the box.

import { cloven } from "@cloven/sdk";
import type { CryptoState } from "@cloven/sdk/packs/crypto";
 
const ctx = await client.pack<CryptoState>("crypto").fresh();
//          ^ typed:  MindResponse<CryptoState>

Custom fetch

Pass any fetch-compatible function:

const client = cloven({
  apiKey: process.env.CLOVEN_KEY,
  fetch: async (input, init) => {
    const start = Date.now();
    const res = await fetch(input, init);
    metrics.histogram("cloven.latency", Date.now() - start);
    return res;
  },
});

Use this for retries, OpenTelemetry instrumentation, or request mocking in tests.

Bundling

The SDK ships as ESM with TypeScript declarations. The Node entry is tree-shaken — unused transports (the SSE polyfill, the x402 wallet code) drop out at bundle time if you never import them.

viem is a peer dep. If you only use Bearer auth, you can omit it from your install:

pnpm add @cloven/sdk
# x402 mode requires viem:
pnpm add @cloven/sdk viem