TypeScript SDK
The Kelet TypeScript SDK instruments your Node.js agent to send traces and signals. Built on OpenTelemetry, it works in any Node.js environment — Next.js, Express, serverless. Use agenticSession() to declare session boundaries and signal() to capture feedback.
npm install kelet | Requires Node.js
Install
Section titled “Install”Install kelet and required OTEL peer dependencies:
npm install kelet @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-httppnpm add kelet @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-httpyarn add kelet @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-httpAPI keys
Section titled “API keys”Two keys — never mix them:
- Secret key (
sk_...): server-only. Use this inconfigure(). Never expose it in frontend code. - Publishable key (
pk_...): frontend-safe, for the React SDK only. Cannot send traces.
Get both from Settings → API Keys in the console.
configure()
Section titled “configure()”Call once at startup, before any agent code runs.
import { configure } from 'kelet';
configure({ apiKey: process.env.KELET_API_KEY, project: 'my-agent',});| Option | Type | Description |
|---|---|---|
apiKey | string | Required. Your Kelet secret API key |
project | string | Optional. Kelet project name |
apiUrl | string | Optional. Override the API base URL |
agenticSession()
Section titled “agenticSession()”Groups all work that belongs to one unit. Callback-based — uses AsyncLocalStorage to propagate context. Node.js only.
import { agenticSession } from 'kelet';
const result = await agenticSession( { sessionId: 'abc123', userId: 'user-1' }, async () => { return await agent.run(...); });The callback IS the session boundary. Context propagates through the entire call tree inside the callback.
| Option | Type | Description |
|---|---|---|
sessionId | string | Required. Unique ID for this unit of work |
userId | string | Optional. Associates the session with a user |
signal()
Section titled “signal()”Sends a signal. Returns Promise<void>.
import { signal, SignalKind, SignalSource } from 'kelet';
await signal({ kind: SignalKind.FEEDBACK, source: SignalSource.HUMAN, triggerName: 'user-vote', score: 1.0,});Call inside agenticSession() — sessionId is resolved from context automatically, or pass it explicitly.
| Option | Type | Description |
|---|---|---|
kind | SignalKind | FEEDBACK or EDIT |
source | SignalSource | HUMAN or SYNTHETIC |
triggerName | string | Use source-action format: user-vote, user-edit |
score | number | 0.0–1.0. For votes: 1.0 = positive, 0.0 = negative |
value | string | Optional text: feedback content, diff |
sessionId | string | Optional. Resolved from context if omitted |
Context helpers
Section titled “Context helpers”import { getSessionId, getUserId, getTraceId } from 'kelet';
getSessionId() // current session IDgetUserId() // current user IDgetTraceId() // current trace IDReturns undefined if called outside agenticSession().
Next.js
Section titled “Next.js”Use KeletExporter via @vercel/otel in instrumentation.ts:
import { registerOTel } from '@vercel/otel';import { KeletExporter } from 'kelet';
export function register() { registerOTel({ serviceName: 'my-app', traceExporter: new KeletExporter({ apiKey: process.env.KELET_API_KEY, }), });}Two required steps that are easy to miss:
- Enable instrumentation hook in
next.config.js:
experimental: { instrumentationHook: true }Without this, instrumentation.ts never runs — no traces.
- Enable telemetry per Vercel AI SDK call:
const result = await generateText({ model, prompt, experimental_telemetry: { isEnabled: true },});Vercel AI SDK telemetry is off by default per call.
Use agenticSession() at the route level for Vercel AI SDK (it doesn’t infer sessions automatically):
export async function POST(req: Request) { const { sessionId } = await req.json(); return agenticSession({ sessionId }, async () => { const result = await generateText({ ... }); return Response.json(result); });}Reasoning capture
Section titled “Reasoning capture”To capture reasoning traces:
node --import kelet/reasoning/register app.jsenum SignalKind { FEEDBACK = 'feedback', EDIT = 'edit',}
enum SignalSource { HUMAN = 'human', SYNTHETIC = 'synthetic',}