From 09aeec1e92a7331524f81aa7871e0590b378ccb6 Mon Sep 17 00:00:00 2001 From: Antoine GAUVAIN Date: Fri, 3 Jul 2026 21:17:04 +0200 Subject: [PATCH] feat(main): opt-in prompt logging --- README.md | 3 ++- src/config.ts | 2 ++ src/index.ts | 8 ++++++++ tests/config.test.ts | 7 +++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 272a9e6..2946880 100644 --- a/README.md +++ b/README.md @@ -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) | @@ -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`. | diff --git a/src/config.ts b/src/config.ts index 6b110df..1e1ae53 100644 --- a/src/config.ts +++ b/src/config.ts @@ -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 @@ -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" diff --git a/src/index.ts b/src/index.ts index e192e2b..0b28eba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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, @@ -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", diff --git a/tests/config.test.ts b/tests/config.test.ts index 4be5d99..c046efc 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -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", @@ -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) @@ -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")