Telemetry
OpenTelemetry spans, privacy-by-default, lazy load.
Canonical contract:
packages/sdk/docs.md.
The SDK emits OpenTelemetry spans for agent runs, tool calls, workflow steps, cache lookups, eval rows, and handoff transfers. Telemetry is lazy-loaded — if @opentelemetry/api isn't installed, you pay zero cost (ADR D34).
Enable
pnpm add @opentelemetry/apiPlus your exporter of choice (Langfuse, Sentry, PostHog, console):
pnpm add @langfuse/otel-sdkThe SDK auto-detects exporters via createRequire feature-detect (ADR D42). No code change needed.
Spans emitted
| Span | Where |
|---|---|
agent.send | Per agent.send() call |
agent.tool_call.<toolName> | Per tool invocation |
workflow.run | Per Workflow.run() |
workflow.step.<id> | Per workflow step (retries share kind) |
cache.lookup / cache.store | Semantic cache hits/misses |
eval.run / eval.row.<n> | Eval suite |
handoff.transfer | Per peer handoff |
memory.recall / memory.write | Per memory op |
Privacy by default
Attribute values are routed through redactSecrets via the wrapSpan decorator (ADR D34 + D68). No raw API key, OAuth token, or env-var value lands in spans.
Toggle
export THEOKIT_TELEMETRY=0 # disable entirely
export THEOKIT_REDACT_SECRETS=0 # disable redaction (NOT recommended)Disabling redaction emits a one-time stderr warning (ADR D70).
Custom tracer
For a custom tracer (e.g. your team's wrapper):
import { trace } from "@opentelemetry/api";
trace.setGlobalTracerProvider(myProvider);
// SDK picks it up via the standard OTel global tracerTelemetry namespace (SDK-internal)
The internal/telemetry/tracer.ts module is the canonical wrapper. All other modules (cache, eval, handoff, workflow telemetry) go through it — never call setAttribute directly on raw spans.
API reference
The Telemetry namespace itself isn't exposed publicly — telemetry happens transparently inside agent operations. Configure via env vars + OTel ecosystem.