Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ An [opencode](https://opencode.ai) plugin that exports telemetry via OpenTelemet
| `session.created` | Session started |
| `session.idle` | Session went idle (includes total tokens, cost, messages) |
| `session.error` | Session error |
| `user_prompt` | User sent a message (includes `prompt_length`, `model`, `agent`) |
| `user_prompt` | User sent a message (includes `prompt_length`, `model`, `agent`; also `prompt` when `OPENCODE_CAPTURE_PROMPT` is set) |
| `api_request` | Completed assistant message (tokens, cost, duration) |
| `api_error` | Failed assistant message (error summary, duration) |
| `tool_result` | Tool completed or errored (duration, success, output size) |
Expand Down Expand Up @@ -96,6 +96,7 @@ All configuration is via environment variables. Set them in your shell profile (
| `OPENCODE_METRIC_PREFIX` | `opencode.` | Prefix for all metric names (e.g. set to `claude_code.` for Claude Code dashboard compatibility) |
| `OPENCODE_DISABLE_METRICS` | *(unset)* | Comma-separated list of metric name suffixes to disable (e.g. `cache.count,session.duration`) |
| `OPENCODE_DISABLE_LOGS` | *(unset)* | Set to any non-empty value to suppress all OTLP log events while leaving metrics and traces unchanged |
| `OPENCODE_CAPTURE_PROMPT` | *(unset)* | Set to any non-empty value to include the full prompt text in the `prompt` field of `user_prompt` log events. **Off by default — prompts may contain secrets or PII; enable only for trusted collectors.** |
| `OPENCODE_DISABLE_TRACES` | *(unset)* | Comma-separated list of trace types to disable (`session`, `llm`, `tool`). Use `all`, `*`, `true`, or `1` to disable every trace type |
| `OPENCODE_OTLP_HEADERS` | *(unset)* | Comma-separated `key=value` headers added to all OTLP exports. **Keep out of version control — may contain sensitive auth tokens.** |
| `OPENCODE_OTLP_HEADERS_HELPER` | *(unset)* | Executable script/binary that returns dynamic OTLP headers as JSON after an auth failure. Helper headers override `OPENCODE_OTLP_HEADERS`. |
Expand Down
2 changes: 2 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const TRACE_DISABLE_ALL_VALUES = new Set(["all", "*", "true", "1"])
export type PluginConfig = {
enabled: boolean
logsEnabled: boolean
capturePrompt: boolean
endpoint: string
protocol: "grpc" | "http/protobuf" | "http/json"
metricsInterval: number
Expand Down Expand Up @@ -119,6 +120,7 @@ export function loadConfig(): PluginConfig {
return {
enabled: hasNonEmptyEnv("OPENCODE_ENABLE_TELEMETRY"),
logsEnabled: !hasNonEmptyEnv("OPENCODE_DISABLE_LOGS"),
capturePrompt: hasNonEmptyEnv("OPENCODE_CAPTURE_PROMPT"),
endpoint: process.env["OPENCODE_OTLP_ENDPOINT"] ?? "http://localhost:4317",
protocol: protocol === "http/protobuf"
? "http/protobuf"
Expand Down
8 changes: 8 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ export const OtelPlugin: Plugin = async ({ project, client, directory, worktree
await log("info", "OTLP log events disabled")
}

if (config.capturePrompt) {
await log(
"info",
"prompt capture enabled - full prompt text emitted in user_prompt log events",
)
}

const ctx: HandlerContext = {
log,
emitLog,
Expand Down Expand Up @@ -269,6 +276,7 @@ export const OtelPlugin: Plugin = async ({ project, client, directory, worktree
"session.id": input.sessionID,
...agentAttrs(agent, agentType),
prompt_length: promptLength,
...(config.capturePrompt ? { prompt: promptText } : {}),
model: input.model
? `${input.model.providerID}/${input.model.modelID}`
: "unknown",
Expand Down
7 changes: 7 additions & 0 deletions tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ describe("loadConfig", () => {
"OPENCODE_OTLP_METRICS_TEMPORALITY",
"OPENCODE_DISABLE_METRICS",
"OPENCODE_DISABLE_LOGS",
"OPENCODE_CAPTURE_PROMPT",
"OPENCODE_DISABLE_TRACES",
"OTEL_EXPORTER_OTLP_HEADERS",
"OTEL_RESOURCE_ATTRIBUTES",
Expand All @@ -86,6 +87,7 @@ describe("loadConfig", () => {
const cfg = loadConfig()
expect(cfg.enabled).toBe(false)
expect(cfg.logsEnabled).toBe(true)
expect(cfg.capturePrompt).toBe(false)
expect(cfg.endpoint).toBe("http://localhost:4317")
expect(cfg.protocol).toBe("grpc")
expect(cfg.metricsInterval).toBe(60000)
Expand All @@ -102,6 +104,11 @@ describe("loadConfig", () => {
expect(loadConfig().logsEnabled).toBe(false)
})

test("capturePrompt is true when OPENCODE_CAPTURE_PROMPT is set", () => {
process.env["OPENCODE_CAPTURE_PROMPT"] = "1"
expect(loadConfig().capturePrompt).toBe(true)
})

test("reads custom endpoint", () => {
process.env["OPENCODE_OTLP_ENDPOINT"] = "http://collector:4317"
expect(loadConfig().endpoint).toBe("http://collector:4317")
Expand Down
Loading