zoklens
v2026.8.2
Published
ZokLens JS/TS SDK — LLM & Agent Observability via OpenTelemetry
Maintainers
Readme
ZokLens JS/TS SDK
LLM & Agent Observability via OpenTelemetry.
Install
npm install "zoklens@^2026.8.2"For production, commit the package lockfile before deploy.
Quickstart
import { ZokLens } from 'zoklens';
const zl = new ZokLens({
apiKey: 'zok_xxx',
endpoint: 'https://api.zoklens.com',
project: 'my-agent',
maxExportBatchSize: 500, // default and hard maximum
});
// Auto-instrument OpenAI-compatible SDKs (OpenAI, DeepSeek, many custom gateways)
await zl.instrument({ providers: ['openai-compatible'] });
// Manual span
const span = zl.startSpan('retrieval', { retrievalSystem: 'support-kb', topK: 5 });
// ... do work
span.end();
// Session tracking — child spans inherit session context
const session = zl.startSession({ userId: 'u123' });
session.run(() => {
const childSpan = zl.startSpan('step-1');
// childSpan is automatically a child of the session span
childSpan.end();
});
session.end();
// Shutdown
await zl.shutdown();The JS/TS SDK exports at most 500 spans in one OTLP request, matching the
server's atomic ingestion-unit limit. Values outside 1..500 fail during SDK
configuration; do not add a collector setting that recombines batches above
this limit.
LLM Telemetry Contract
Typed Agent lifecycle and attempts
const run = zl.startAgentRun({
agentId: 'support-agent',
agentVersion: 'release-7',
sessionId: 'opaque-session-1',
heartbeatMode: 'instrumented',
heartbeatIntervalSeconds: 30,
progressInstrumented: true,
});
run.declareArchitecture({
manifestId: 'support-architecture',
manifestVersion: 'v4',
coverage: 'complete',
relationships: [{
subject: { kind: 'agent', id: 'support-agent', version: 'release-7' },
predicate: 'enters_workflow',
object: { kind: 'workflow', id: 'answer-flow', version: 'v4' },
}],
});
const answer = await run.executeAsync(async () => {
run.delegateToAgent('retrieval-agent', 'release-3');
run.enterWorkflow('answer-flow', 'v4');
const docs = run.toolAttempt('knowledge-search', {
actor: { kind: 'workflow', id: 'answer-flow', version: 'v4' },
toolVersion: 'schema-2',
toolOperation: 'search',
}).execute(() => applicationSearch());
run.progress('tool_result_accepted');
return run.modelAttempt('deepseek-chat', {
actor: { kind: 'workflow', id: 'answer-flow', version: 'v4' },
respondingModel: 'deepseek-chat-2026-08',
providerName: 'deepseek',
}).executeAsync(() => applicationGenerate(docs));
});This API emits only content-free OTel evidence and does not alter routing,
orchestration, retry, model/tool choice, return values, errors, cancellation, or
timeouts. Run IDs use 128 bits of cryptographic randomness; event/attempt IDs
are UUIDv4; retry and fallback lineage require an explicit retryOfAttemptId
or fallbackOfAttemptId. Declared architecture is observational metadata, not
an execution graph. The SDK canonically sorts each manifest's 1–100 unique
relationships and emits a total count, stable zero-based index, and one shared
SHA-256 on every relationship Span. The server honors coverage: 'complete'
only after every indexed relationship arrives and the full digest verifies;
transport loss is shown as partial evidence. Change manifestVersion whenever
relationships or coverage change. Prompt,
completion, tool arguments/results, PII, secrets, and chain-of-thought are not
accepted by the typed API.
Telemetry Data Safety
The legacy generic API still exports scalar startSpan() attributes and
caller-provided userId/sessionId values as OTel attributes. It does not yet
apply the V2 typed governance/content-policy contract to those legacy APIs.
- use opaque or tenant-approved pseudonymous correlation IDs, never email, patient/customer identity, secrets, or credentials;
- do not put raw Prompt/query/context/memory/tool bodies or hidden reasoning in span attributes;
- treat generic spans/attributes as telemetry, not as validated Agent, Prompt, tool, policy, or authorization evidence.
Use the typed Agent API above for strict lifecycle evidence. Existing generic APIs continue at their honest lower coverage.
Version Compatibility
Do not auto-upgrade the SDK at runtime. Do not add startup-time package installs,
runtime self-updaters, or automatic npm update zoklens scripts to customer
applications. Add the SDK through your normal package manager, commit the
lockfile, and upgrade through an explicit dependency change with tests. The
2026.x SDK family is the current compatibility window; patch and minor
releases stay backward compatible for the telemetry protocol unless release
notes say otherwise.
Every trace includes SDK metadata so ZokLens can detect outdated clients:
zoklens.sdk.versionzoklens.sdk.languagezoklens.sdk.protocol_versionzoklens.sdk.compatibility_family
Auto-instrumented LLM calls emit canonical GenAI attributes and legacy compatibility aliases:
gen_ai.request.model,gen_ai.response.model,gen_ai.provider.namegen_ai.usage.input_tokens,gen_ai.usage.output_tokens,gen_ai.usage.total_tokensllm.request.model,llm.response.model,llm.model,llm.providerllm.tokens.input,llm.tokens.output,llm.tokens.total,llm.duration_ms
When both request and response models are present, ZokLens uses the response model for display because it reflects the actual provider result.
For OpenAI-compatible SDKs, ZokLens infers the provider from the exact model id
and, when available, the configured base URL. Preserve the provider response's
exact model string; do not replace missing values with "unknown".
Session Context
startSession() returns a Session object with a run() method.
All spans created inside session.run(() => { ... }) automatically
become children of the session span (same trace ID):
const session = zl.startSession({ userId: 'u123' });
session.run(() => {
const s1 = zl.startSpan('fetch'); // child of session
s1.end();
const s2 = zl.startSpan('process'); // child of session
s2.end();
});
session.end();