From 08fd4957afb486aa2dec75499feac42e9d1d0fc1 Mon Sep 17 00:00:00 2001 From: Zhichao Li Date: Fri, 31 Jul 2026 17:25:09 -0700 Subject: [PATCH] fix: preserve inherited native exporter headers --- .../agentHost/common/otel/agentHostOTelService.ts | 6 +++++- .../platform/agentHost/node/claude/claudeSdkOptions.ts | 3 +++ .../platform/agentHost/node/codex/codexLaunchConfig.ts | 9 ++++++--- .../platform/agentHost/node/otel/agentHostOTelService.ts | 6 +++++- .../agentHost/test/node/codex/codexLaunchConfig.test.ts | 6 +++--- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts b/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts index 9c2182376ea..95cfad33a40 100644 --- a/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts +++ b/src/vs/platform/agentHost/common/otel/agentHostOTelService.ts @@ -38,7 +38,11 @@ export interface IAgentHostNativeOTelConfig { /** Trace destination. In DB mode this is the Agent Host HTTP/JSON loopback. */ readonly traces?: { readonly endpoint: string; readonly protocol: 'http/json' | 'http/protobuf' | 'grpc' }; /** User-owned OTLP destination used directly by native SDK logs and metrics. */ - readonly external?: { readonly endpoint: string; readonly protocol: 'http/json' | 'http/protobuf' | 'grpc' }; + readonly external?: { + readonly endpoint: string; + readonly protocol: 'http/json' | 'http/protobuf' | 'grpc'; + readonly headers?: Readonly>; + }; readonly captureContent: boolean; } diff --git a/src/vs/platform/agentHost/node/claude/claudeSdkOptions.ts b/src/vs/platform/agentHost/node/claude/claudeSdkOptions.ts index 4c700dba39d..491352e61aa 100644 --- a/src/vs/platform/agentHost/node/claude/claudeSdkOptions.ts +++ b/src/vs/platform/agentHost/node/claude/claudeSdkOptions.ts @@ -269,6 +269,9 @@ export function buildClaudeTelemetryEnv(config: IAgentHostNativeOTelConfig | und env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL = config.external.protocol; env.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = resolveSignalEndpoint(config.external.endpoint, 'metrics'); env.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL = config.external.protocol; + if (config.external.headers && Object.keys(config.external.headers).length > 0) { + env.OTEL_EXPORTER_OTLP_HEADERS = Object.entries(config.external.headers).map(([key, value]) => `${key}=${value}`).join(','); + } } if (traceContext) { env.TRACEPARENT = traceContext.traceparent; diff --git a/src/vs/platform/agentHost/node/codex/codexLaunchConfig.ts b/src/vs/platform/agentHost/node/codex/codexLaunchConfig.ts index 91e981e099b..a5dead08168 100644 --- a/src/vs/platform/agentHost/node/codex/codexLaunchConfig.ts +++ b/src/vs/platform/agentHost/node/codex/codexLaunchConfig.ts @@ -86,12 +86,15 @@ export function codexTelemetryOverrides(config: IAgentHostNativeOTelConfig | und ]; } -function codexExporter(config: { endpoint: string; protocol: 'http/json' | 'http/protobuf' | 'grpc' }): string { +function codexExporter(config: { endpoint: string; protocol: 'http/json' | 'http/protobuf' | 'grpc'; headers?: Readonly> }): string { + const headers = config.headers && Object.keys(config.headers).length > 0 + ? `, headers = { ${Object.entries(config.headers).map(([key, value]) => `${JSON.stringify(key)} = ${JSON.stringify(value)}`).join(', ')} }` + : ''; if (config.protocol === 'grpc') { - return `{ otlp-grpc = { endpoint = ${JSON.stringify(config.endpoint)} } }`; + return `{ otlp-grpc = { endpoint = ${JSON.stringify(config.endpoint)}${headers} } }`; } const protocol = config.protocol === 'http/json' ? 'json' : 'binary'; - return `{ otlp-http = { endpoint = ${JSON.stringify(config.endpoint)}, protocol = ${JSON.stringify(protocol)} } }`; + return `{ otlp-http = { endpoint = ${JSON.stringify(config.endpoint)}, protocol = ${JSON.stringify(protocol)}${headers} } }`; } function resolveSignalEndpoint(endpoint: string, signal: 'logs' | 'metrics'): string { diff --git a/src/vs/platform/agentHost/node/otel/agentHostOTelService.ts b/src/vs/platform/agentHost/node/otel/agentHostOTelService.ts index bde70c5f13b..63adc7e6cef 100644 --- a/src/vs/platform/agentHost/node/otel/agentHostOTelService.ts +++ b/src/vs/platform/agentHost/node/otel/agentHostOTelService.ts @@ -195,7 +195,11 @@ export class AgentHostOTelService extends Disposable implements IAgentHostOTelSe const protocol = this._config.otlpProtocol === 'grpc' ? 'grpc' : this._config.otlpProtocol === 'http/protobuf' ? 'http/protobuf' : 'http/json'; - const external = this._config.otlpEndpoint ? { endpoint: this._config.otlpEndpoint, protocol } as const : undefined; + const external = this._config.otlpEndpoint ? { + endpoint: this._config.otlpEndpoint, + protocol, + ...(this._config.headers ? { headers: this._config.headers } : {}), + } as const : undefined; if (!this._config.dbSpanExporter) { return { traces: external, external, captureContent: this._config.captureContent === true }; } diff --git a/src/vs/platform/agentHost/test/node/codex/codexLaunchConfig.test.ts b/src/vs/platform/agentHost/test/node/codex/codexLaunchConfig.test.ts index ced99ffb11c..f8179c9c3f5 100644 --- a/src/vs/platform/agentHost/test/node/codex/codexLaunchConfig.test.ts +++ b/src/vs/platform/agentHost/test/node/codex/codexLaunchConfig.test.ts @@ -31,13 +31,13 @@ suite('CodexLaunchConfig', () => { test('routes traces to loopback and logs/metrics directly to the external sink', () => { const config = buildCodexLaunchConfig('openai', {}, undefined, [], { traces: { endpoint: 'http://127.0.0.1:4567/v1/traces', protocol: 'http/json' }, - external: { endpoint: 'http://collector:4318', protocol: 'http/protobuf' }, + external: { endpoint: 'http://collector:4318', protocol: 'http/protobuf', headers: { authorization: 'Bearer test' } }, captureContent: false, }); assert.ok(config.args.includes('otel.log_user_prompt=false')); assert.ok(config.args.includes('otel.trace_exporter={ otlp-http = { endpoint = "http://127.0.0.1:4567/v1/traces", protocol = "json" } }')); - assert.ok(config.args.includes('otel.exporter={ otlp-http = { endpoint = "http://collector:4318/v1/logs", protocol = "binary" } }')); - assert.ok(config.args.includes('otel.metrics_exporter={ otlp-http = { endpoint = "http://collector:4318/v1/metrics", protocol = "binary" } }')); + assert.ok(config.args.includes('otel.exporter={ otlp-http = { endpoint = "http://collector:4318/v1/logs", protocol = "binary", headers = { "authorization" = "Bearer test" } } }')); + assert.ok(config.args.includes('otel.metrics_exporter={ otlp-http = { endpoint = "http://collector:4318/v1/metrics", protocol = "binary", headers = { "authorization" = "Bearer test" } } }')); }); test('identifies provider-compatible threads', () => {