Skip to content
telemetry.dev
Esc
↑↓navigate↵open⌘Jpreview
On this page

opencode

Trace opencode agent turns, model calls, tools, subagents, usage, cost, and session events.

@telemetry-dev/opencode is an opencode plugin. Add it to your opencode config, set an API key, and each prompt appears in telemetry.dev as a trace with its model calls, tool calls, subagents, token usage, and cost.

By default the plugin sends the text of your prompts and every tool call’s arguments and results, which include file contents and command output. It doesn’t send the model’s response text. You can turn content off with two options; see What leaves your machine.

Before you start

  • opencode 1.15.11 or later, on its standard plugin API. opencode’s beta V2 plugin API doesn’t run this plugin.
  • A telemetry.dev project and its API key. Keys start with td_live_.

Set up

Set the API key

Set the key in the environment that starts opencode:

export TELEMETRY_DEV_API_KEY=td_live_...
export TELEMETRY_DEV_ENVIRONMENT=development

Keep the key out of opencode.json, which is often committed to a repository.

Add the plugin

Add the package to the plugin list in opencode.json. Use the project’s opencode.json for one project, or ~/.config/opencode/opencode.json for all of them:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": ["@telemetry-dev/opencode"]
}

opencode installs npm plugins with Bun when it starts, so you don’t need to install the package yourself.

Start opencode

Restart opencode so it loads the plugin.

To pass options, use a [name, options] pair instead of the plain name. Options in opencode.json must be JSON values:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    ["@telemetry-dev/opencode", { "captureInput": false, "captureOutput": false }]
  ]
}

Load the plugin from a file instead

Use a plugin file when you need an option that is a function, such as mask. Don’t also list the package in opencode.json: opencode would load it twice and record every span twice.

Add the package as a dependency of the opencode config directory. opencode runs bun install there when it starts:

{
  "dependencies": {
    "@telemetry-dev/opencode": "^0.1.9"
  }
}

Then create the plugin file. opencode loads every file in .opencode/plugins/, or in ~/.config/opencode/plugins/ for all projects:

import { telemetryDevPlugin } from "@telemetry-dev/opencode";

export const TelemetryDev = telemetryDevPlugin({
  mask: (value, { key }) => (key === "password" ? "[REDACTED]" : value),
});

Check that data arrives

Send a prompt and wait until opencode finishes it. Then open the trace explorer. The plugin sends each prompt’s spans when the session goes idle.

If no trace appears, check that TELEMETRY_DEV_API_KEY is set in the shell that started opencode. Without a key the plugin does nothing and reports no error.

What is recorded

Each prompt produces one trace:

invoke_agent
├── chat {model}
│   ├── execute_tool {tool}
│   └── execute_tool task
│       └── invoke_agent
└── chat {model}
  • invoke_agent records the text of the prompt, the agent name, and the opencode session ID.
  • chat {model} records the provider, model, finish reason, token usage, the cost opencode reports, and errors. Reasoning tokens count as output tokens; the span also keeps separate reasoning, cache-read, and cache-write counts.
  • execute_tool {tool} records the tool call ID, arguments, result, duration, and error.
  • Task subagents appear as an invoke_agent span under the tool call that started them.
  • Session creation, compaction, and errors become logs.

The plugin doesn’t record the model’s response text: the opencode event for a finished message carries usage and metadata but not the final text.

What leaves your machine

By default the plugin sends:

  • The text of your prompts (captureInput).
  • Tool arguments (captureInput), such as shell commands and file paths.
  • Tool results (captureOutput): the tool’s title, output, and metadata, such as file contents and command output.

Set both options to false to stop sending this content, either in opencode.json as shown above or in a plugin file. In a plugin file, mask can also change or drop individual values before they are sent.

These are sent whatever the options:

  • Session IDs, the agent name, provider and model names, token counts, cost, finish reasons, and timings.
  • The error text of failed tools. This is what opencode returns to the model, so it can contain file or command content.
  • The message of session.error events, on spans and as the opencode.error.message log attribute, and provider error messages on chat spans.

mask doesn’t apply to these error messages in version 0.1.9.

telemetry.dev’s privacy settings can also drop recognized prompt and response content before storage, but only after it has left your machine.

How it runs

A chat.message event starts the invoke_agent span. The span ends when the session goes idle (a session.status event with type idle, or the older session.idle event) or when opencode disposes the plugin.

A session.error event doesn’t end the trace, because opencode can compact the context and continue the same prompt. If compaction follows, the plugin drops the error. Otherwise it applies the error when the session goes idle.

When opencode disposes the plugin, it closes open spans, marks those without an error as incomplete, and waits for the last export.

Configuration

Variable Default Description
TELEMETRY_DEV_API_KEY None Ingest API key. Without one, the plugin does nothing.
TELEMETRY_DEV_BASE_URL https://ingest.telemetry.dev OTLP ingest base URL.
TELEMETRY_DEV_ENVIRONMENT production Environment on exported telemetry.
OTEL_SERVICE_NAME opencode OpenTelemetry service name.

Options override the environment variables. Options in opencode.json override options passed to telemetryDevPlugin().

Option Type Default Description
captureInput boolean true Send prompt text and tool arguments.
captureOutput boolean true Send tool results.
mask (value, { key }) => unknown None Change or drop captured content before it is sent. Plugin file only.
maxAttributeLength number 65536 Maximum length of a content attribute.
agentName string opencode Value of gen_ai.agent.name.
apiKey string TELEMETRY_DEV_API_KEY Ingest API key.
baseUrl string TELEMETRY_DEV_BASE_URL Ingest base URL.
environment string TELEMETRY_DEV_ENVIRONMENT Deployment environment.
serviceName string OTEL_SERVICE_NAME Service name.
enabled boolean true false turns the plugin off completely.
onError (error) => void None Receive plugin errors. The plugin never throws them into opencode.

The plugin also accepts the other TypeScript SDK init() options, except registerGlobal, which it always sets to false. The first plugin instance to start in a process sets the options for that process.

Limitations

  • When opencode serve receives SIGTERM, opencode doesn’t dispose plugins, so the last batch of spans can be lost.
  • opencode reports token usage as totals on each finished message, not per step.
  • Tool errors don’t reach the tool.execute.after hook. The plugin rebuilds failed tool spans from message.part.updated events.

Last updated on October 18, 2026

Was this page helpful?