Edictum
TypeScript SDK

Observability

Add OpenTelemetry spans and metrics to Edictum TypeScript agents with the @edictum/otel package.

AI Assistance

Right page if: you want to add OpenTelemetry tracing or metrics to an Edictum TypeScript agent. Wrong page if: you want Python observability -- see https://docs.edictum.ai/docs/reference/observability. For audit sinks (file, server, stdout), see https://docs.edictum.ai/docs/reference/audit-sinks. Gotcha: `@edictum/otel` requires `@opentelemetry/api` as a peer dependency. If OTel is not installed, `createTelemetry()` returns a no-op implementation that silently drops all spans and metrics.

Audit events tell you what happened. OpenTelemetry tells you how long it took and where it fits in your distributed trace. The @edictum/otel package emits governance-specific spans for every contract evaluation and counters for denied/allowed tool calls.

Getting Started

Install

pnpm add @edictum/otel @opentelemetry/api

For the configureOtel() setup helper, you also need the SDK packages:

pnpm add @opentelemetry/sdk-trace-base @opentelemetry/sdk-metrics @opentelemetry/resources

For gRPC export:

pnpm add @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc

Quick setup with configureOtel

import { configureOtel } from '@edictum/otel'

await configureOtel({
  serviceName: 'my-agent',
  endpoint: 'http://localhost:4317',
  protocol: 'grpc',
  resourceAttributes: {
    'deployment.environment': 'production',
  },
})

This registers a TracerProvider and MeterProvider globally. If a tracer provider is already registered, it is not overridden (set force: true to override). The meter provider is always registered so that Edictum metrics work regardless of your tracing setup.

Use GovernanceTelemetry

import { GovernanceTelemetry } from '@edictum/otel'

const telemetry = new GovernanceTelemetry()

// Start a span for a tool call
const span = telemetry.startToolSpan(envelope)

// After pipeline evaluation
telemetry.setSpanOk(span)
// or on error:
telemetry.setSpanError(span, 'Denied by contract: block-dotenv')

span.end()

configureOtel Options

OptionTypeDefaultDescription
serviceNamestring"edictum-agent"Service name in traces
endpointstring"http://localhost:4317"Collector endpoint
protocol"grpc" | "http" | "http/protobuf""grpc"Export protocol
resourceAttributesRecord<string, string>{}Extra resource attributes
edictumVersionstringSets edictum.version attribute
forcebooleanfalseOverride existing TracerProvider

Environment variables take precedence:

Env VarOverrides
OTEL_SERVICE_NAMEserviceName
OTEL_EXPORTER_OTLP_ENDPOINTendpoint
OTEL_EXPORTER_OTLP_PROTOCOLprotocol

Spans and Metrics

GovernanceTelemetry emits:

Spans (tracer name: edictum):

  • One span per tool call evaluation, with attributes for tool name, verdict, contract IDs, and principal

Counters (meter name: edictum):

  • edictum.calls.denied -- number of denied tool calls
  • edictum.calls.allowed -- number of allowed tool calls

Both counters include a tool_name attribute for per-tool breakdowns.

No-Op Fallback

If @opentelemetry/api is not installed, you can still import @edictum/otel safely:

import { createTelemetry } from '@edictum/otel'

// Returns GovernanceTelemetry if OTel is available, NoOpTelemetry otherwise
const telemetry = await createTelemetry()

NoOpTelemetry implements the same interface but silently drops all spans and metrics. This lets library code use telemetry without forcing a dependency on consumers.

You can also check availability explicitly:

import { hasOtel, hasOtelAsync } from '@edictum/otel'

// Synchronous check (may miss async-loaded modules)
if (hasOtel()) {
  const telemetry = new GovernanceTelemetry()
}

// Async check (reliable)
if (await hasOtelAsync()) {
  const telemetry = new GovernanceTelemetry()
}

Input Sanitization

All string inputs to spans and metrics are sanitized:

  • Control characters are stripped
  • Attribute keys are capped at 1,000 characters
  • Attribute values are capped at 10,000 characters
  • Tool names are sanitized before use in span names and metric labels

This prevents telemetry injection from malicious tool names or arguments.

Next Steps

Last updated on

On this page