@noetaris/harness-otel
v0.3.2
Published
OpenTelemetry observer bridge for @noetaris/harness
Readme
@noetaris/harness-otel
OpenTelemetry observer bridge for @noetaris/harness.
Overview
@noetaris/harness-otel bridges the harness Observer telemetry API to OpenTelemetry, following the OpenTelemetry GenAI semantic conventions. It translates harness lifecycle events into OTel spans and metrics, giving you distributed tracing and token usage metrics with zero changes to your agent code.
Takes @opentelemetry/api as a peer dependency — works with any OTel SDK implementation (Node SDK, collector exporters, etc.).
Installation
pnpm add @noetaris/harness-otelPeer dependencies:
pnpm add @noetaris/harness @opentelemetry/apiRequires Node.js ≥ 22.
Usage
import { createOtelObserver } from '@noetaris/harness-otel'
import { trace, metrics } from '@opentelemetry/api'
const observer = createOtelObserver(
trace.getTracer('my-agent'),
{ meterProvider: metrics.getMeterProvider() },
)
h.observe(observer)API
createOtelObserver(tracer, options?)
Returns an Observer that maps harness events to OTel spans and metrics, named
after the GenAI semantic conventions.
Spans produced:
| Span | Kind | Opened on / closed on |
|------|------|-----------------------|
| invoke_agent {agentId} | root | one per agent.run(); carries gen_ai.agent.id and gen_ai.conversation.id |
| harness.step {stepName} | child | per step; carries gen_ai.step.name |
| chat {modelId} | INTERNAL | opened on "llm.request", closed on "llm.response"; carries model/provider/token attributes |
| execute_tool {toolName} | INTERNAL | opened on "tool.call", closed on "tool.result"; carries gen_ai.tool.name |
A run paused on an interrupt closes its in-flight step and root spans (via
onInterrupt + onRunEnd); the next agent.run() on the same observer starts a
fresh root span.
Metrics (requires options.meterProvider):
| Metric | Instrument | Unit | Recorded from |
|--------|-----------|------|---------------|
| gen_ai.client.token.usage | histogram | {token} | input/output tokens on each "llm.response" |
| gen_ai.client.operation.duration | histogram | s | agent invocation duration on onRunEnd |
Options:
| Option | Type | Description |
|--------|------|-------------|
| parentContext | Context | OTel context for the root span. Defaults to context.active(). |
| meterProvider | MeterProvider | Enables metrics. Omit to skip metric recording. |
| attributes | Attributes | Extra attributes merged onto the root span (built-in attributes win on conflict). |
| captureInputs | boolean | Serialise request messages into a gen_ai.content.prompt span event. Default false. |
| captureOutputs | boolean | Serialise response output into a gen_ai.content.completion span event. Default false. |
| captureToolIO | boolean | Serialise tool input/result into gen_ai.tool.input/gen_ai.tool.output span events. Default false. |
| maxContentLength | number | Truncation limit for any captured content payload. Default 8192. |
Related Packages
@noetaris/harness— core execution engine@noetaris/harness-anthropic— Anthropic Claude adapter (emits"llm.response"events)@noetaris/harness-openai— OpenAI adapter (emits"llm.response"events)@noetaris/harness-google— Google Gemini adapter (emits"llm.response"events)
License
MIT
