From 9640d889baa58fa01ed612a6372ba77462f79d9f Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Wed, 15 Apr 2026 12:35:14 -0400 Subject: [PATCH] fix: register OTel context manager so AI SDK spans thread into Effect traces (#22645) --- bun.lock | 2 ++ packages/opencode/package.json | 6 ++++-- packages/opencode/src/effect/observability.ts | 12 ++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index a6f9891dd1..aeab042cf3 100644 --- a/bun.lock +++ b/bun.lock @@ -359,6 +359,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/server": "workspace:*", "@openrouter/ai-sdk-provider": "2.5.1", + "@opentelemetry/api": "1.9.0", + "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 9ddf1fa9f6..59be93d620 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -113,13 +113,15 @@ "@octokit/rest": "catalog:", "@openauthjs/openauth": "catalog:", "@opencode-ai/plugin": "workspace:*", - "@opencode-ai/server": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", + "@opencode-ai/server": "workspace:*", + "@openrouter/ai-sdk-provider": "2.5.1", + "@opentelemetry/api": "1.9.0", + "@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1", - "@openrouter/ai-sdk-provider": "2.5.1", "@opentui/core": "0.1.99", "@opentui/solid": "0.1.99", "@parcel/watcher": "2.5.1", diff --git a/packages/opencode/src/effect/observability.ts b/packages/opencode/src/effect/observability.ts index 1e4863f924..f79306bf1e 100644 --- a/packages/opencode/src/effect/observability.ts +++ b/packages/opencode/src/effect/observability.ts @@ -46,6 +46,18 @@ export namespace Observability { const OTLP = await import("@opentelemetry/exporter-trace-otlp-http") const SdkBase = await import("@opentelemetry/sdk-trace-base") + // @effect/opentelemetry creates a NodeTracerProvider but never calls + // register(), so the global @opentelemetry/api context manager stays + // as the no-op default. Non-Effect code (like the AI SDK) that calls + // tracer.startActiveSpan() relies on context.active() to find the + // parent span — without a real context manager every span starts a + // new trace. Registering AsyncLocalStorageContextManager fixes this. + const { AsyncLocalStorageContextManager } = await import("@opentelemetry/context-async-hooks") + const { context } = await import("@opentelemetry/api") + const mgr = new AsyncLocalStorageContextManager() + mgr.enable() + context.setGlobalContextManager(mgr) + return NodeSdk.layer(() => ({ resource, spanProcessor: new SdkBase.BatchSpanProcessor(