New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@tracelanedev/sdk

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tracelanedev/sdk

Tracelane TypeScript SDK — auto-instrument AI agent frameworks

latest
Source
npmnpm
Version
0.4.0
Version published
Maintainers
1
Created
Source

@tracelanedev/sdk

npm License: Apache 2.0

Instrumentation for TypeScript AI agents, built on the OpenTelemetry Node SDK. Spans are emitted via OTLP/HTTP (JSON) to the endpoint you configure — https://gateway.tracelane.dev on Tracelane Cloud, or a receiver you run.

Install

npm install @tracelanedev/sdk
# or
pnpm add @tracelanedev/sdk

Fastest path — route through the gateway (no SDK)

For Tracelane Cloud, the shortest path to your first trace needs no SDK at all: point your existing client's base URL at the gateway and use your tlane_… key. The gateway routes the call and captures the trace.

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://gateway.tracelane.dev/v1",
  apiKey: process.env.TRACELANE_API_KEY!, // tlane_… from app.tracelane.dev
});

await client.chat.completions.create({
  model: "claude-sonnet-4-6",
  messages: [{ role: "user", content: "Hello" }],
});
// → Trace visible at https://app.tracelane.dev/traces

That captures the model call. Use this SDK when you want the shape of your agent — the planner step, each tool call, the summariser — as a nested trace rather than one span per model call. It exports OTLP to Tracelane Cloud's gateway, a self-hosted Tracelane ingest, or your own collector (Jaeger, Tempo, …).

Sessions — group turns into one conversation

/sessions groups traces by conversation id. The id travels as the x-conversation-id request header (the gateway also accepts x-session-id), and it lands on the span as gen_ai.conversation.id. This SDK is what sets it — an un-sessioned call sends no session header and carries no conversation id.

Wrap the turn and every call inside the scope joins the session:

import { instrumentOpenAI, withSession } from "@tracelanedev/sdk";

instrumentOpenAI(client);

await withSession(conversationId, async () => {
  await client.chat.completions.create({ model: "claude-sonnet-4-6", messages });
  await client.chat.completions.create({ model: "claude-sonnet-4-6", messages: followUp });
});
// Both calls land in the same session.

withSession is backed by AsyncLocalStorage, so overlapping conversations in one server process never bleed into each other. For a single-conversation script or CLI, setSession(id) sets it process-wide instead, and getSession() reads back whichever is active.

Auto-attach covers the four adapters that wrap a request-options argument: instrumentOpenAI, instrumentAnthropic, instrumentLiteLLM and instrumentOpenRouter. For every other client — including the no-SDK gateway path above — pass the header yourself:

import { sessionHeaders } from "@tracelanedev/sdk";

await client.chat.completions.create(
  { model: "claude-sonnet-4-6", messages },
  { headers: sessionHeaders(conversationId) },
);

sessionHeaders() with no argument returns the active session, or {} when there is none, so it is always safe to spread. A header you set explicitly always wins over the ambient session.

A session id must be non-empty visible ASCII, at most 256 characters. Anything else throws at the call you wrote, rather than being dropped in transit and leaving the session silently empty — and it is rejected, never truncated, because a truncated id would split one conversation into two.

SDK quick start (OTLP export)

Two steps: init() once at startup, then wrap each client with its instrument* function. There is no zero-config magic in v1 — wrapping is explicit, so what's traced is exactly what you opted in.

import { init, instrumentAnthropic } from "@tracelanedev/sdk";
import Anthropic from "@anthropic-ai/sdk";

// 1. Initialise once. endpoint + apiKey are REQUIRED (no env-var auto-read).
init({
  endpoint: "https://gateway.tracelane.dev", // or a receiver you run
  apiKey: process.env.TRACELANE_API_KEY!,    // needs the `ingest` scope
  serviceName: "my-agent",
});

// 2. Wrap the client — instrumentAnthropic patches it in place.
const client = new Anthropic();
instrumentAnthropic(client);

// 3. Use it normally — every call now emits a span.
await client.messages.create({
  model: "claude-sonnet-4-6",
  messages: [{ role: "user", content: "Hello" }],
  max_tokens: 128,
});

Your key needs the ingest scope. Keys minted before scopes existed carry the full API surface and work as-is. A key scoped to chat and read does not — the gateway answers 403 insufficient_scope and names ingest in the body. Tick Ingest in Settings → API Keys; it is on by default for new keys.

init() options

FieldRequiredDescription
endpointyesOTLP HTTP endpoint. https://gateway.tracelane.dev for Cloud, or a receiver you run (e.g. http://localhost:4318). Spans POST to ${endpoint}/v1/traces.
apiKeyyesYour tlane_… key. Sent as the x-tracelane-api-key header.
serviceNamenoResource service.name (default unknown-service).
sampleRateno0.0–1.0 (default 1.0 — full trace; the tail sampler decides).

Call shutdown() on exit to flush pending spans (an automatic flush is also registered on beforeExit).

Streaming (v1 limitation)

Streamed calls (stream: true) pass through untouched and still produce a span with model + latency, marked tracelane.streaming = true. Token usage and finish reason are not captured for streamed responses yet — that is not implemented. A once-per-process runtime warning says exactly this.

Instrumented libraries

Each library has its own instrument*(client) function — import it from the package root or the matching subpath. Call it once, after constructing the client (or, for module-level libraries, after import).

ImportWrap withWhat is traced
@anthropic-ai/sdkinstrumentAnthropic(client)messages.create, streaming, tool use
openaiinstrumentOpenAI(client)chat.completions, embeddings, Responses
@openai/agentsinstrumentOpenAIAgents(...)agent steps, tool calls, handoffs
langchaininstrumentLangGraph(graph)chains, agents, tool calls
@modelcontextprotocol/sdkinstrumentMCP(...)tool_call, tool_result
Vercel AI SDKinstrumentVercelAI(...)generateText, streamText, generateObject

Full list (one export per library): instrumentAnthropic, instrumentOpenAI, instrumentOpenAIAsync, instrumentLiteLLM, instrumentOpenRouter, instrumentLangGraph, instrumentOpenAIAgents, instrumentVercelAI, instrumentMCP, instrumentClaudeCode, instrumentCursor, instrumentPinecone, instrumentQdrant, instrumentComposio, instrumentBrowserbase, instrumentE2B, instrumentMem0, instrumentLetta, instrumentFirecrawl.

Zero-config autoInstrument() is not in v1 — calling it throws with a pointer to this explicit API. Auto-detection is not implemented.

Next.js App Router

Initialise in instrumentation.ts (runs once per server process):

// instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    const { init } = await import("@tracelanedev/sdk");
    init({
      endpoint: "http://localhost:4318", // an OTLP receiver you run
      apiKey: process.env.TRACELANE_API_KEY!,
      serviceName: "my-nextjs-app",
    });
  }
}

Manual spans

The SDK sets up a standard OpenTelemetry tracer provider, so custom spans use @opentelemetry/api directly — no Tracelane-specific wrapper:

import { trace } from "@opentelemetry/api";

const tracer = trace.getTracer("my-agent");
const hits = await tracer.startActiveSpan("retrieval", async (span) => {
  span.setAttribute("retrieval.top_k", 10);
  const results = await vectorStore.search(query, { topK: 10 });
  span.end();
  return results;
});

Design invariants

  • Telemetry goes to your configured endpoint only — the SDK never calls home.
  • Instrumentation is additive — instrument* patches a client in place and does not modify the OpenAI/Anthropic module exports.
  • Prompt and completion text is not captured, and there is no switch you need to set. The SDK never sends it, and the gateway stores it only for tenants an operator has explicitly allowlisted in its own tracelane.yaml — off for everyone otherwise. Earlier revisions of this file told you to set TRACELANE_TRACE_CONTENT=false; no component has ever read that variable, so the instruction was inert and the honest statement is the stronger one.
  • Zero runtime dependencies beyond the OpenTelemetry SDK.

Documentation

Full docs at docs.tracelane.dev/sdk-typescript.

Stack

TypeScript 5.5+ strict, Biome (lint + format), Vitest.

License

Apache 2.0 — see LICENSE.

Keywords

tracelane

FAQs

Package last updated on 09 Oct 2026

Related posts