@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/sdkThe 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.
}
}| Error | When |
|---|---|
X402Required | x402 mode failed mid-auto-retry (wallet rejected, RPC down). |
ClovenApiError | Any structured 4xx/5xx with code + message + requestId. |
QuotaExhausted | 429 — exposes retryAfterSeconds. |
PackAccessDenied | 403 — tier doesn't include the pack. |
ClovenError | Base 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