The short answer

The current OTel path is exposed by agentsight debug trace --otel. The trace pipeline first normalizes and materializes captured evidence. When that materialized view emits a completedllm_call, the OTel sink maps the call onto one CLIENT span named chat {model} and POSTs an OTLP/HTTP JSON ExportTraceServiceRequest to the configured traces endpoint.

This is a projection, not a second raw packet parser. Request/response correlation and token extraction happen before the OTel sink receives the row. In v1.0.31, an unfinished call with no end timestamp is skipped rather than emitted as a partial span.

What crosses the OTel boundary—and what stays in AgentSight

EvidenceOTel GenAI export in v1.0.31Interpretation
Completed materialized LLM callYesOne CLIENT span. Parsed response fields appear when available; a completed SSE response whose body cannot be reparsed can still yield request attributes and HTTP status without parsed response/token fields.
Prompt/completion contentOpt-inAdded only with --otel-capture-content.
Process/file/network/resource evidenceNo automatic span mappingRemains AgentSight evidence unless another pipeline exports it separately.
Tool/workflow rowsNot emitted yetCurrent product docs explicitly list tool/workflow spans as a limitation.
AgentSight row provenance/confidenceNot mapped by this sinkKeep the AgentSight artifact when evidence lineage matters.

That boundary is useful operationally: an existing OTel backend can receive standardized model-call spans, while AgentSight remains the richer local artifact for questions such as “which process wrote this file?” or “which system observation produced this reconstructed row?”.

The exporter starts from the materialized view, not raw TLS events

cmd_trace.rs builds one MaterializingAnalyzer and can attach both the SQLite sink and the OTel sink to it. The OTel exporter therefore sees normalized LlmCallRow values after AgentSight has already done the capture-specific work needed to construct a model call. The exporter source itself says that correlation and token extraction happen before the sink.

The pinned docs/otel.md diagram is looser: it labels OtelExporter as the step that pairs request/response traffic by PID/TID. The v1.0.31 implementation is more precise than that diagram. Raw SSL/plaintext events are parsed and materialized upstream; OtelExporter::llm_call() receives the completed call and only maps that stable row to OTLP. For this source-level boundary, the code path is the authoritative description rather than the simplified diagram.

Trace grouping uses explicit conversation identity, then session identity

Every exported call gets a fresh random span ID. Trace identity is reused more selectively. The exporter first looks for an explicit conversation or thread identifier in a bounded set of request/response JSON paths. If it finds one, calls with that identifier share a trace ID. Otherwise it uses the AgentSight session ID. If neither is available, calls share one recording-scoped fallback trace ID.

A generic provider response ID is deliberately not treated as a conversation ID. That matters because a response identifier usually names one response, not the multi-turn conversation. v1.0.31 also does not infer a parent/child span tree for model calls; the product documentation explicitly notes that root and child spans are not inferred yet.

Which GenAI attributes are actually emitted?

The span is a CLIENT span; its operation name is chat, provider comes from the normalized call or API host, and server.address records that host. Model/request parameters are copied when available, while response metadata and token usage are derived from the completed response body. The table below covers the emitted v1.0.31 mapping rather than only a representative subset.

OTel fieldAgentSight sourceImportant limit
gen_ai.operation.namechatCurrent exporter is model-call focused.
gen_ai.provider.nameNormalized provider or provider inferred from host.It is not an AgentSight process identity.
server.addressCaptured API host.A network destination, not a model/provider identity by itself.
gen_ai.conversation.idExplicit recognized conversation/thread field.Not synthesized from a generic response ID.
gen_ai.request.modelNormalized model or request model.Absent when the capture cannot establish it.
gen_ai.request.max_tokensRequest max_tokens or max_output_tokens.Emitted only when the request exposes an integer value.
gen_ai.request.temperature / gen_ai.request.top_pRequest sampling parameters.Omitted when absent or non-numeric.
gen_ai.response.model / gen_ai.response.idParsed response body.Requires a parseable response JSON carrying those fields.
gen_ai.usage.input_tokensinput_tokens or prompt_tokens.Only when response usage exposes a matching integer field.
gen_ai.usage.output_tokensoutput_tokens or completion_tokens.Observed response usage, not a billing calculation.
gen_ai.response.finish_reasonsOpenAI-style choice reasons or Anthropic-style stop_reason.Omitted when the response shape has no recognized reason.
http.response.status_codeCaptured HTTP response status.Status 400 or above also marks the span ERROR.
gen_ai.input.messages / gen_ai.output.messagesRequest messages/input and response JSON.Only with --otel-capture-content.

Span start and end timestamps come from the materialized call's captured request/response times. The OTel resource uses service.name, defaulting to agentsight, and the instrumentation scope is named agentsight with the current package version.

Content export is off by default for a reason

Without --otel-capture-content, the exporter sends model-call metadata but does not add prompt or completion bodies. When content capture is enabled, v1.0.31 can attach request messages/input asgen_ai.input.messages and the response JSON as gen_ai.output.messages.

OpenTelemetry itself warns that GenAI message attributes are likely to contain sensitive user or PII data. AgentSight recordings can also contain sensitive development context. Treat the flag as a data-export decision, not a display preference: once content enters a collector pipeline, its retention, access controls, and downstream exporters are properties of that telemetry system.

Endpoint precedence follows the OTel split between base and trace-specific URLs

AgentSight accepts --otel-endpoint as an OTLP/HTTP base URL. If that flag is absent, it checksOTEL_EXPORTER_OTLP_ENDPOINT, then falls back to http://localhost:4318. Those base forms get /v1/traces appended. If OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set, it takes precedence and is used as-is.

That matches the OpenTelemetry exporter distinction: the general OTLP endpoint is a base from which the signal path is constructed, while the signal-specific trace endpoint is already the final URL. The current AgentSight sink sends OTLP/HTTP JSON, not OTLP/gRPC.

Export failure is not the same as capture failure

For each completed call, the sink spawns an asynchronous HTTP POST. A non-success collector response or transport failure is logged as an OTel exporter warning. The llm_call() sink method itself returns successfully after scheduling that work rather than synchronously turning collector delivery into a failure of the AgentSight materialization path.

Those spawned POST tasks are not retained and awaited during run_traceshutdown. The trace loop drops its stream and agent and returns; when the CLI's Tokio runtime then exits, an in-flight export may be cancelled before the collector accepts it. “The AgentSight run contains the call” and “the collector accepted the span” are therefore separate facts, especially near shutdown. Verify the collector or downstream backend when export completeness matters; do not infer successful remote delivery only from the local call.

The current public CLI surface is debug trace, not record

In v1.0.31, --otel, --otel-endpoint, and --otel-capture-content are flags on agentsight debug trace. The normal agentsight record command exposes command/PID, binary path, SQLite DB, and web-server controls, but does not expose those OTel flags. The two commands share trace infrastructure internally, but that does not make an unexposed flag part of the record CLI.

# Scope one command family and export metadata-only GenAI spans
sudo agentsight debug trace -c claude --otel --otel-endpoint http://localhost:4318

# Keep the same scope when opting in to prompt/completion content
sudo agentsight debug trace -c claude --otel --otel-capture-content

debug trace enables broad SSL and process monitoring by default, so use -c,--pid, or an explicit --binary-path to bound a verification run. Installation and collector setup belong in the canonical AgentSight documentation. For a reproducible check, run a collector that exposes its received spans, issue one model request inside the selected scope, and compare the emitted attributes with the local AgentSight call.

Use OTel for the model-call projection; keep AgentSight when system evidence matters

If your existing telemetry stack already speaks OTLP, AgentSight does not need to replace it. The current exporter is useful for feeding standardized GenAI call spans into that pipeline while preserving AgentSight as the independent local record of system behavior. The two views intentionally have different information.

For a model-latency or provider-error question, the exported span can be enough. For a question about the child process that ran after the model call, the file a tool opened, the network endpoint a subprocess reached, or the provenance of a reconstructed row, inspect the AgentSight artifact as well. Exporting one boundary does not make the other boundary disappear.

How to verify the implementation yourself

Start with cmd_debug.rs for the CLI flags and cmd_trace.rs for where the OTel sink is attached to the shared materialized view and how shutdown returns. Then read otel.rs for completion gating, trace-ID selection, attribute mapping, endpoint precedence, content opt-in, and asynchronous POST behavior. Finally compare main.rs to keep the publicrecord and debug trace surfaces distinct.

Primary sources