opencode
Trace opencode agent turns, model calls, tools, subagents, usage, cost, and session events.
@telemetry-dev/opencode connects the opencode plugin API to telemetry.dev. It requires opencode 1.15.11 or later.
What it captures
A prompt produces one trace with this shape:
invoke_agent
├── chat {model}
│ ├── execute_tool {tool}
│ └── execute_tool task
│ └── invoke_agent
└── chat {model}
invoke_agentrecords the prompt, agent name, and opencode session ID.chat {model}records provider, model, finish reason, usage, host-reported cost, and errors.execute_tool {tool}records the tool-call ID, arguments, result, duration, and error state.- Task subagents nest under the open span from the parent session.
- Session creation, compaction, and error events become logs.
Assistant output text is not captured. The completed assistant event supplies metadata and usage, but it does not supply final text.
Reasoning tokens count as output tokens. The span also keeps separate reasoning, cache-read, and cache-write token values.
Set up the integration
Make a project at telemetry.dev. Then copy an API key from the project’s setup page.
Set the API key in the environment that starts opencode:
export TELEMETRY_DEV_API_KEY=td_live_...
export TELEMETRY_DEV_ENVIRONMENT=development
export OTEL_SERVICE_NAME=opencode
Select one plugin setup after the package becomes public.
Project plugin file
Install the package:
npm install @telemetry-dev/opencodepnpm add @telemetry-dev/opencodeyarn add @telemetry-dev/opencodebun add @telemetry-dev/opencodeAdd .opencode/plugin/telemetry-dev.ts:
import { telemetryDevPlugin } from "@telemetry-dev/opencode";
export const TelemetryDev = telemetryDevPlugin();
opencode finds project plugin files in this directory.
opencode configuration
The /server export supplies opencode’s PluginModule shape. Add it to opencode.json:
{
"plugin": [
["@telemetry-dev/opencode/server", { "environment": "production" }]
]
}
Keep the API key out of opencode.json. Configuration tuple options take precedence over factory options.
Session lifecycle
A chat.message event starts invoke_agent. The span closes on one of these signals:
- A modern
session.statusevent withstatus.typeset toidle - A legacy
session.idleevent - Plugin disposal
If session.compacted follows the error, the integration removes the stale error. Otherwise, the integration applies the error when the session settles.
Plugin disposal closes open tool, chat, and agent spans. It marks spans without an error as incomplete and waits for a flush.
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
TELEMETRY_DEV_API_KEY |
Yes | None | Ingest API key. Without a key, the integration is a no-op. |
TELEMETRY_DEV_BASE_URL |
No | https://ingest.telemetry.dev |
OTLP ingest base URL. |
TELEMETRY_DEV_ENVIRONMENT |
No | production |
Environment on exported telemetry. |
OTEL_SERVICE_NAME |
No | opencode |
OpenTelemetry service name. |
Options
telemetryDevPlugin() accepts TelemetryDevPluginOptions. The type contains SDK options except registerGlobal, plus agentName.
| Option | Type | Default | Description |
|---|---|---|---|
agentName |
string |
opencode |
Value for gen_ai.agent.name. |
apiKey |
string |
TELEMETRY_DEV_API_KEY |
Ingest API key. |
baseUrl |
string |
TELEMETRY_DEV_BASE_URL or production ingest |
Ingest base URL. |
environment |
string |
TELEMETRY_DEV_ENVIRONMENT or production |
Deployment environment. |
serviceName |
string |
OTEL_SERVICE_NAME or opencode |
Service name on each trace. |
enabled |
boolean |
true |
Set to false for a complete no-op. |
exportMode |
"batched" | "immediate" |
batched |
Span export mode. |
captureInput |
boolean |
true |
Capture prompts and tool arguments. |
captureOutput |
boolean |
true |
Capture tool results. |
mask |
(value, context) => unknown |
None | Change captured values before serialization. |
maxAttributeLength |
number |
65536 |
Maximum content attribute length. |
batch |
BatchOptions |
SDK defaults | Batch size, delay, queue, and timeout settings. |
spanFilter |
(span) => boolean |
None | Select spans for export. |
resourceAttributes |
Record<string, AttributeValue> |
None | Add OpenTelemetry resource attributes. |
logLevel |
"debug" | "info" | "warn" | "error" | "silent" |
warn |
SDK diagnostic level. |
fetch |
typeof fetch |
globalThis.fetch |
Custom fetch implementation. |
waitUntil |
(promise) => void |
None | Extend serverless work for export. |
onError |
(error) => void |
None | Receive integration errors. The plugin does not throw them into opencode. |
The integration forces registerGlobal to false. The first initialization in a process supplies the SDK options.
Use captureInput, captureOutput, and mask to control content:
import { telemetryDevPlugin } from "@telemetry-dev/opencode";
export const TelemetryDev = telemetryDevPlugin({
captureInput: false,
captureOutput: false,
mask: (value, { key }) => key === "password" ? "[REDACTED]" : value,
});
Open the trace explorer after a prompt completes. For API key setup, refer to the quickstart.