mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-11 03:35:04 -05:00
* add OTel instrumentation spec and plan for all agents * feat: OTel instrumentation for Copilot CLI background agent - Add agentOTelEnv.ts config derivation helpers (CLI + Claude) - Enable SDK OtelLifecycle via env vars before LocalSessionManager ctor - Add invoke_agent copilotcli wrapper span with traceparent propagation - Forward OTel env vars to terminal CLI sessions - Update spec and plan docs for all agents - 33 tests passing (14 new + 19 existing) * feat: filter debug-panel-only spans from OTLP export Spans with non-standard gen_ai.operation.name values (content_event, user_message) are excluded from external OTLP export while remaining visible in the Agent Debug Log panel via onDidCompleteSpan. Only GenAI-conventional operations (invoke_agent, chat, execute_tool, embeddings, execute_hook) are exported to the user's collector. * fix: add IOTelService to CopilotCLISessionService ctor in participant test * fix: pass chatSessionId to CapturingToken for debug panel routing The CapturingToken was created without chatSessionId, so the debug panel couldn't route copilotcli OTel spans to the correct session view. Also: Copilot CLI runtime only supports otlp-http (not gRPC). Terminal CLI sessions require an HTTP-compatible OTLP endpoint. * docs: add CLI HTTP-only limitation to spec and dual-port Aspire setup to test plan * fix: forward OTel env vars to CLI terminal sessions - Include OTel env vars in terminal profile provider path (dropdown) which previously only set shell info without auth/OTel env - Pass empty env to deriveCopilotCliOTelEnv for terminal sessions so vars are always included regardless of process.env pollution from the in-process background agent - Update test plan to use Grafana LGTM stack * fix: add CHAT_SESSION_ID to attributes in CopilotCLISession * docs: update OTel instrumentation specification for Copilot CLI and Claude Code * feat: bridge SDK native OTel spans to Agent Debug panel Replace synthetic span approach (PR #4494) with a bridge SpanProcessor that forwards SDK-native spans from the Copilot CLI runtime's BasicTracerProvider into the extension's IOTelService event stream. This gives the debug panel the full SDK span hierarchy (subagents, permissions, hooks, nested tool calls) — identical to what Grafana shows. Architecture: - Add injectCompletedSpan() to IOTelService interface for external span injection without OTLP re-export - Create CopilotCliBridgeSpanProcessor that converts ReadableSpan to ICompletedSpanData, injects copilot_chat.chat_session_id from a traceId→sessionId map, and fires onDidCompleteSpan - Install bridge on SDK's TracerProvider via internal MultiSpanProcessor._spanProcessors array (OTel SDK v2 removed the public addSpanProcessor API, but this internal array is the same pattern the SDK itself uses in forceFlush) - Propagate traceparent from extension root span to SDK via otelLifecycle.updateParentTraceContext() so all spans share a traceId - Filter bridge to only forward spans from registered CLI sessions Code changes: - copilotCliBridgeSpanProcessor.ts: new bridge processor - copilotcliSession.ts: remove all synthetic spans (chat, tool, error), keep root invoke_agent span + traceparent propagation + bridge wiring - copilotcliSessionService.ts: install bridge after first session creation, wire bridge + SDK trace context updater to sessions - IOTelService: add injectCompletedSpan to interface + all impls - Remove outdated synthetic span tests - Add OTel data flow architecture diagram (HTML) * fix: update span processing to use parent span context and enhance subagent event identification * display names for tool call and subagent events * docs: merge arch and spec into single developer guide Combine agent_monitoring_arch.md (foreground-only) and agent-otel-spec.md (all agents) into a single comprehensive developer reference covering all four agent paths, bridge architecture, and SDK internal access warnings. * docs: fix stale addSpanProcessor reference in data flow diagram * chore: move plan and test docs to offline archive These documents are reference material for the OTel sprint, not needed in the shipped PR. Archived to ~/Documents/copilot-otel-archive/. * test: add bridge SpanProcessor unit tests 13 tests covering: traceId filtering, parentSpanContext conversion, CHAT_SESSION_ID injection, attribute flattening, event conversion, HrTime→ms conversion, unregister/shutdown behavior. * test: add span event identification and naming tests 7 tests covering invoke_agent identification logic: top-level skip, SDK wrapper skip (no agent name), subagent detection (name attribute and span name parsing), unknown/missing operation name handling. * fix: always enable SDK OTel for debug panel regardless of user config The CLI SDK's OtelLifecycle must always initialize so the bridge processor can forward native spans to the debug panel. When user OTel is disabled, COPILOT_OTEL_ENABLED is still set but no OTLP endpoint is configured — the SDK creates spans (for debug panel) but doesn't export to any external collector. The bridge installation is also now unconditional — it installs even when user OTel is disabled. * chore: remove transient sprint plan * fix: suppress SDK OTLP export when user OTel is disabled When user OTel is disabled, force the SDK to use file exporter to /dev/null instead of letting it default to OTLP. Also clear any leftover OTEL_EXPORTER_OTLP_ENDPOINT from previous sessions to prevent orphaned traces in Grafana. * docs: add background agents section to user monitoring guide Cover Copilot CLI (background + terminal) and Claude Code agent tracing in the user-facing guide. Includes span hierarchy examples, service.name filtering table, and CLI HTTP-only limitation note. * docs: remove Claude Code from user guide (not yet supported) * fixup! feat: OTel instrumentation for Copilot CLI background agent * fix: address PR review comments - Use GenAiOperationName constants in EXPORTABLE_OPERATION_NAMES (avoids drift) - Remove unnecessary delete of OTEL_EXPORTER_OTLP_ENDPOINT from process.env - Replace 'as any' OTel mocks with typed NoopOTelService in terminal tests - Clarify comment on empty env arg for terminal OTel env derivation - Add ExportResultCode.SUCCESS comment for clarity * fixup! fix: always enable SDK OTel for debug panel regardless of user config * fix: handle SDK native hook spans in debug panel The SDK's OtelSessionTracker creates 'hook {type}' spans with github.copilot.hook.type attributes (not gen_ai.operation.name). These were silently dropped by completedSpanToDebugEvent. Now detected by span name prefix and converted to Hook: {type} events. * add execute_hook spans for Claude hook executions in monitoring documentation * docs: add hook spans to CLI trace hierarchy in user guide
654 lines
37 KiB
HTML
654 lines
37 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>OTel Data Flow — All Agents</title>
|
|
<style>
|
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, monospace; background: #0d1117; color: #c9d1d9; padding: 24px; }
|
|
h1 { color: #58a6ff; margin-bottom: 8px; font-size: 20px; }
|
|
h2 { color: #8b949e; font-size: 14px; margin-bottom: 24px; font-weight: normal; }
|
|
.legend { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 20px; font-size: 12px; }
|
|
.legend-item { display: flex; align-items: center; gap: 6px; }
|
|
.legend-swatch { width: 14px; height: 14px; border-radius: 3px; }
|
|
|
|
svg { width: 100%; height: auto; }
|
|
.node { cursor: default; }
|
|
.node rect { rx: 6; ry: 6; stroke-width: 1.5; }
|
|
.node text { font-family: inherit; font-size: 11px; fill: #c9d1d9; }
|
|
.node .label { font-weight: 600; font-size: 12px; }
|
|
.node .sub { font-size: 10px; fill: #8b949e; }
|
|
|
|
/* Provider colors */
|
|
.provider-a rect { fill: #1a2332; stroke: #58a6ff; }
|
|
.provider-b rect { fill: #1a2a1a; stroke: #3fb950; }
|
|
.provider-none rect { fill: #2a1a2a; stroke: #bc8cff; }
|
|
.debug-panel rect { fill: #2a2218; stroke: #d29922; }
|
|
.exporter rect { fill: #2a1a1a; stroke: #f85149; }
|
|
.process rect { fill: #1a1a2a; stroke: #8b949e; }
|
|
.bridge rect { fill: #1a2a2a; stroke: #39d353; }
|
|
|
|
.arrow { stroke-width: 1.5; fill: none; marker-end: url(#arrowhead); }
|
|
.arrow-data { stroke: #58a6ff; }
|
|
.arrow-bridge { stroke: #39d353; stroke-dasharray: 6,3; }
|
|
.arrow-export { stroke: #f85149; }
|
|
.arrow-env { stroke: #d29922; stroke-dasharray: 3,3; }
|
|
.arrow-context { stroke: #bc8cff; stroke-dasharray: 8,4; }
|
|
|
|
.section { margin-top: 32px; }
|
|
.section h3 { color: #58a6ff; font-size: 16px; margin-bottom: 12px; border-bottom: 1px solid #21262d; padding-bottom: 8px; }
|
|
|
|
.info-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-top: 16px; }
|
|
.info-card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px; }
|
|
.info-card h4 { color: #58a6ff; margin-bottom: 8px; font-size: 13px; }
|
|
.info-card p, .info-card li { font-size: 12px; line-height: 1.6; color: #8b949e; }
|
|
.info-card ul { padding-left: 16px; }
|
|
.info-card code { background: #21262d; padding: 1px 5px; border-radius: 3px; color: #c9d1d9; font-size: 11px; }
|
|
|
|
.tab-bar { display: flex; gap: 2px; margin-bottom: 0; }
|
|
.tab { padding: 8px 16px; background: #161b22; border: 1px solid #30363d; border-bottom: none; border-radius: 6px 6px 0 0; cursor: pointer; font-size: 12px; color: #8b949e; }
|
|
.tab.active { background: #0d1117; color: #58a6ff; border-color: #58a6ff; border-bottom: 1px solid #0d1117; position: relative; z-index: 1; }
|
|
.tab-content { border: 1px solid #30363d; border-radius: 0 6px 6px 6px; padding: 0; background: #0d1117; }
|
|
.tab-pane { display: none; }
|
|
.tab-pane.active { display: block; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<h1>OTel Data Flow — VS Code Copilot Chat Extension</h1>
|
|
<h2>Complete tracing architecture for foreground agent, CLI background agent, CLI terminal, and Claude Code</h2>
|
|
|
|
<div class="legend">
|
|
<div class="legend-item"><div class="legend-swatch" style="background:#1a2332;border:1.5px solid #58a6ff"></div> Provider A (Extension NodeOTelService)</div>
|
|
<div class="legend-item"><div class="legend-swatch" style="background:#1a2a1a;border:1.5px solid #3fb950"></div> Provider B (SDK BasicTracerProvider)</div>
|
|
<div class="legend-item"><div class="legend-swatch" style="background:#1a2a2a;border:1.5px solid #39d353"></div> Bridge SpanProcessor</div>
|
|
<div class="legend-item"><div class="legend-swatch" style="background:#2a2218;border:1.5px solid #d29922"></div> Debug Panel Consumer</div>
|
|
<div class="legend-item"><div class="legend-swatch" style="background:#2a1a1a;border:1.5px solid #f85149"></div> External Exporter</div>
|
|
<div class="legend-item"><div class="legend-swatch" style="background:#2a1a2a;border:1.5px solid #bc8cff"></div> Synthetic Spans (ext-created)</div>
|
|
</div>
|
|
|
|
<div class="tab-bar">
|
|
<div class="tab active" onclick="switchTab('overview')">Overview (All Agents)</div>
|
|
<div class="tab" onclick="switchTab('foreground')">Foreground Agent</div>
|
|
<div class="tab" onclick="switchTab('cli-bg')">CLI Background</div>
|
|
<div class="tab" onclick="switchTab('cli-term')">CLI Terminal</div>
|
|
<div class="tab" onclick="switchTab('claude')">Claude Code</div>
|
|
</div>
|
|
|
|
<div class="tab-content">
|
|
|
|
<!-- OVERVIEW -->
|
|
<div class="tab-pane active" id="tab-overview">
|
|
<svg viewBox="0 0 1100 720" xmlns="http://www.w3.org/2000/svg">
|
|
<defs>
|
|
<marker id="arrowhead" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#8b949e"/></marker>
|
|
<marker id="ah-blue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#58a6ff"/></marker>
|
|
<marker id="ah-green" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#39d353"/></marker>
|
|
<marker id="ah-red" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f85149"/></marker>
|
|
<marker id="ah-yellow" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#d29922"/></marker>
|
|
<marker id="ah-purple" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#bc8cff"/></marker>
|
|
</defs>
|
|
|
|
<!-- Extension Host Process boundary -->
|
|
<rect x="10" y="10" width="850" height="700" rx="10" fill="none" stroke="#30363d" stroke-dasharray="8,4"/>
|
|
<text x="24" y="32" fill="#8b949e" font-size="11" font-family="monospace">VS Code Extension Host Process</text>
|
|
|
|
<!-- Provider A: NodeOTelService -->
|
|
<g class="node provider-a" transform="translate(30,50)">
|
|
<rect width="260" height="90"/>
|
|
<text x="12" y="20" class="label" fill="#58a6ff">Provider A: NodeOTelService</text>
|
|
<text x="12" y="36" class="sub">Registered at extension activation</text>
|
|
<text x="12" y="52" class="sub">tracer A (stored ref, survives override)</text>
|
|
<text x="12" y="66" class="sub">service.name = "copilot-chat"</text>
|
|
<text x="12" y="80" class="sub">Emitters: onDidCompleteSpan, onDidEmitSpanEvent</text>
|
|
</g>
|
|
|
|
<!-- Provider B: SDK BasicTracerProvider -->
|
|
<g class="node provider-b" transform="translate(30,170)">
|
|
<rect width="260" height="90"/>
|
|
<text x="12" y="20" class="label" fill="#3fb950">Provider B: SDK BasicTracerProvider</text>
|
|
<text x="12" y="36" class="sub">Created by OtelLifecycle.initializeOtelSdk()</text>
|
|
<text x="12" y="52" class="sub">Replaces Provider A as global</text>
|
|
<text x="12" y="66" class="sub">service.name = "github-copilot"</text>
|
|
<text x="12" y="80" class="sub">SpanProcessors: BatchSP + BridgeSP</text>
|
|
</g>
|
|
|
|
<!-- Bridge SpanProcessor -->
|
|
<g class="node bridge" transform="translate(30,290)">
|
|
<rect width="260" height="70"/>
|
|
<text x="12" y="20" class="label" fill="#39d353">CopilotCliBridgeSpanProcessor</text>
|
|
<text x="12" y="36" class="sub">Added to Provider B after trackSession()</text>
|
|
<text x="12" y="52" class="sub">Maps traceId → sessionId, injects CHAT_SESSION_ID</text>
|
|
<text x="12" y="66" class="sub">Filters: only forwards registered traceIds</text>
|
|
</g>
|
|
|
|
<!-- Foreground Agent -->
|
|
<g class="node provider-a" transform="translate(340,50)">
|
|
<rect width="220" height="70"/>
|
|
<text x="12" y="20" class="label">Foreground Agent</text>
|
|
<text x="12" y="36" class="sub">toolCallingLoop.ts → chatMLFetcher.ts</text>
|
|
<text x="12" y="52" class="sub">Uses tracer A (Provider A)</text>
|
|
<text x="12" y="66" class="sub">invoke_agent → chat → execute_tool</text>
|
|
</g>
|
|
|
|
<!-- CLI Background Agent -->
|
|
<g class="node provider-b" transform="translate(340,150)">
|
|
<rect width="220" height="90"/>
|
|
<text x="12" y="20" class="label">CLI Background Agent</text>
|
|
<text x="12" y="36" class="sub">OtelSessionTracker (SDK native)</text>
|
|
<text x="12" y="52" class="sub">Uses tracer B (Provider B / global)</text>
|
|
<text x="12" y="66" class="sub">invoke_agent → chat → execute_tool</text>
|
|
<text x="12" y="80" class="sub">+ subagents, permissions, hooks</text>
|
|
</g>
|
|
|
|
<!-- CLI Background Extension Wrapper -->
|
|
<g class="node provider-a" transform="translate(340,270)">
|
|
<rect width="220" height="55"/>
|
|
<text x="12" y="20" class="label">CLI Extension Wrapper</text>
|
|
<text x="12" y="36" class="sub">copilotcliSession.ts</text>
|
|
<text x="12" y="50" class="sub">invoke_agent copilotcli (tracer A)</text>
|
|
</g>
|
|
|
|
<!-- Claude Agent -->
|
|
<g class="node provider-none" transform="translate(340,355)">
|
|
<rect width="220" height="70"/>
|
|
<text x="12" y="20" class="label" fill="#bc8cff">Claude Code Agent</text>
|
|
<text x="12" y="36" class="sub">claudeCodeAgent.ts (synthetic spans)</text>
|
|
<text x="12" y="52" class="sub">Uses tracer A (Provider A)</text>
|
|
<text x="12" y="66" class="sub">invoke_agent → execute_tool (flat)</text>
|
|
</g>
|
|
|
|
<!-- Claude Subprocess -->
|
|
<g class="node process" transform="translate(340,455)">
|
|
<rect width="220" height="55"/>
|
|
<text x="12" y="20" class="label">Claude Code Subprocess</text>
|
|
<text x="12" y="36" class="sub">Separate Node process</text>
|
|
<text x="12" y="50" class="sub">Metrics + events only (no traces)</text>
|
|
</g>
|
|
|
|
<!-- CLI Terminal -->
|
|
<g class="node process" transform="translate(340,540)">
|
|
<rect width="220" height="55"/>
|
|
<text x="12" y="20" class="label">CLI Terminal Process</text>
|
|
<text x="12" y="36" class="sub">Separate terminal process</text>
|
|
<text x="12" y="50" class="sub">Full SDK OTel (independent traces)</text>
|
|
</g>
|
|
|
|
<!-- Debug Panel: Trajectory Provider -->
|
|
<g class="node debug-panel" transform="translate(620,50)">
|
|
<rect width="230" height="70"/>
|
|
<text x="12" y="20" class="label" fill="#d29922">OTelChatDebugLogProvider</text>
|
|
<text x="12" y="36" class="sub">Listens: onDidCompleteSpan</text>
|
|
<text x="12" y="52" class="sub">Buckets by copilot_chat.chat_session_id</text>
|
|
<text x="12" y="66" class="sub">Feeds: Agent Debug Log panel UI</text>
|
|
</g>
|
|
|
|
<!-- Debug Panel: File Logger -->
|
|
<g class="node debug-panel" transform="translate(620,150)">
|
|
<rect width="230" height="55"/>
|
|
<text x="12" y="20" class="label" fill="#d29922">ChatDebugFileLoggerService</text>
|
|
<text x="12" y="36" class="sub">Listens: onDidCompleteSpan</text>
|
|
<text x="12" y="50" class="sub">Writes: debug-logs/<sessionId>/main.jsonl</text>
|
|
</g>
|
|
|
|
<!-- OTLP Exporter (Provider A) -->
|
|
<g class="node exporter" transform="translate(620,240)">
|
|
<rect width="230" height="55"/>
|
|
<text x="12" y="20" class="label" fill="#f85149">OTLP Exporter (Provider A)</text>
|
|
<text x="12" y="36" class="sub">BatchSpanProcessor → HTTP/gRPC endpoint</text>
|
|
<text x="12" y="50" class="sub">Exports foreground + CLI wrapper spans</text>
|
|
</g>
|
|
|
|
<!-- OTLP Exporter (Provider B) -->
|
|
<g class="node exporter" transform="translate(620,325)">
|
|
<rect width="230" height="55"/>
|
|
<text x="12" y="20" class="label" fill="#f85149">OTLP Exporter (Provider B)</text>
|
|
<text x="12" y="36" class="sub">BatchSpanProcessor → same HTTP endpoint</text>
|
|
<text x="12" y="50" class="sub">Exports SDK native spans</text>
|
|
</g>
|
|
|
|
<!-- Grafana / External -->
|
|
<g class="node exporter" transform="translate(620,420)">
|
|
<rect width="230" height="55"/>
|
|
<text x="12" y="20" class="label" fill="#f85149">Grafana / Jaeger / Collector</text>
|
|
<text x="12" y="36" class="sub">Receives all spans from all exporters</text>
|
|
<text x="12" y="50" class="sub">Shows full trace hierarchy</text>
|
|
</g>
|
|
|
|
<!-- === ARROWS === -->
|
|
|
|
<!-- Provider A → Debug Panel (onDidCompleteSpan) -->
|
|
<path d="M 290,95 L 310,85 Q 615,55 620,85" class="arrow arrow-data" marker-end="url(#ah-blue)"/>
|
|
<text x="390" y="48" fill="#58a6ff" font-size="9">onDidCompleteSpan</text>
|
|
|
|
<!-- Provider A → File Logger -->
|
|
<path d="M 290,110 L 615,175" class="arrow arrow-data" marker-end="url(#ah-blue)"/>
|
|
|
|
<!-- Provider A → OTLP A -->
|
|
<path d="M 290,130 Q 450,260 620,267" class="arrow arrow-export" marker-end="url(#ah-red)"/>
|
|
<text x="420" y="228" fill="#f85149" font-size="9">BatchSpanProcessor</text>
|
|
|
|
<!-- Bridge → Provider A (injectCompletedSpan → onDidCompleteSpan) -->
|
|
<path d="M 290,325 Q 600,290 620,85" class="arrow arrow-bridge" marker-end="url(#ah-green)"/>
|
|
<text x="470" y="160" fill="#39d353" font-size="9">injectCompletedSpan()</text>
|
|
|
|
<!-- Provider B → OTLP B -->
|
|
<path d="M 290,215 Q 450,330 620,350" class="arrow arrow-export" marker-end="url(#ah-red)"/>
|
|
|
|
<!-- Provider B → Bridge -->
|
|
<path d="M 160,260 L 160,290" class="arrow arrow-bridge" marker-end="url(#ah-green)"/>
|
|
<text x="165" y="278" fill="#39d353" font-size="9">onEnd()</text>
|
|
|
|
<!-- OTLP A → Grafana -->
|
|
<path d="M 735,295 L 735,420" class="arrow arrow-export" marker-end="url(#ah-red)"/>
|
|
<!-- OTLP B → Grafana -->
|
|
<path d="M 755,380 L 755,420" class="arrow arrow-export" marker-end="url(#ah-red)"/>
|
|
|
|
<!-- CLI wrapper → traceparent → SDK -->
|
|
<path d="M 450,325 L 450,240" class="arrow arrow-context" marker-end="url(#ah-purple)"/>
|
|
<text x="455" y="285" fill="#bc8cff" font-size="9">traceparent</text>
|
|
|
|
<!-- Env vars → CLI Terminal -->
|
|
<path d="M 290,355 Q 320,540 340,565" class="arrow arrow-env" marker-end="url(#ah-yellow)"/>
|
|
<text x="265" y="480" fill="#d29922" font-size="9">env vars</text>
|
|
|
|
<!-- Env vars → Claude subprocess -->
|
|
<path d="M 290,355 Q 320,455 340,480" class="arrow arrow-env" marker-end="url(#ah-yellow)"/>
|
|
|
|
<!-- CLI Terminal → Grafana (independent) -->
|
|
<path d="M 560,565 Q 750,540 750,475" class="arrow arrow-export" marker-end="url(#ah-red)"/>
|
|
<text x="630" y="530" fill="#f85149" font-size="9">independent traces</text>
|
|
|
|
<!-- Claude subprocess → Grafana -->
|
|
<path d="M 560,480 Q 760,490 760,475" class="arrow arrow-export" marker-end="url(#ah-red)"/>
|
|
<text x="610" y="498" fill="#f85149" font-size="9">metrics+events</text>
|
|
|
|
<!-- Claude → Debug Panel (synthetic) -->
|
|
<path d="M 560,390 Q 610,200 620,120" class="arrow arrow-data" marker-end="url(#ah-blue)"/>
|
|
<text x="580" y="260" fill="#bc8cff" font-size="9">synthetic spans</text>
|
|
|
|
<!-- Config source -->
|
|
<g class="node" transform="translate(30,630)">
|
|
<rect width="260" height="70" fill="#161b22" stroke="#30363d" rx="6"/>
|
|
<text x="12" y="20" class="label" fill="#8b949e">resolveOTelConfig()</text>
|
|
<text x="12" y="38" class="sub">Settings: github.copilot.chat.otel.*</text>
|
|
<text x="12" y="52" class="sub">Env: COPILOT_OTEL_*, OTEL_EXPORTER_*</text>
|
|
<text x="12" y="66" class="sub">Kill switch: telemetry.telemetryLevel=off</text>
|
|
</g>
|
|
|
|
<!-- Config → Providers -->
|
|
<path d="M 160,630 L 160,360" class="arrow arrow-env" marker-end="url(#ah-yellow)"/>
|
|
<text x="85" y="500" fill="#d29922" font-size="9">OTelConfig</text>
|
|
<path d="M 200,630 Q 310,600 350,540" class="arrow arrow-env" marker-end="url(#ah-yellow)"/>
|
|
<path d="M 220,630 Q 320,580 350,510" class="arrow arrow-env" marker-end="url(#ah-yellow)"/>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- FOREGROUND AGENT -->
|
|
<div class="tab-pane" id="tab-foreground">
|
|
<svg viewBox="0 0 900 380" xmlns="http://www.w3.org/2000/svg">
|
|
<defs>
|
|
<marker id="ah2-blue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#58a6ff"/></marker>
|
|
<marker id="ah2-red" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f85149"/></marker>
|
|
</defs>
|
|
|
|
<text x="20" y="24" fill="#58a6ff" font-size="14" font-weight="600">Foreground Agent — Provider A Only</text>
|
|
<text x="20" y="42" fill="#8b949e" font-size="11">All spans created by tracer A (extension's NodeOTelService)</text>
|
|
|
|
<!-- Span hierarchy -->
|
|
<g transform="translate(30,60)">
|
|
<rect x="0" y="0" width="400" height="280" rx="8" fill="#161b22" stroke="#30363d"/>
|
|
<text x="12" y="20" fill="#58a6ff" font-size="12" font-weight="600">Span Hierarchy (tracer A)</text>
|
|
|
|
<rect x="16" y="34" width="370" height="24" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="24" y="50" fill="#c9d1d9" font-size="10">invoke_agent copilot (INTERNAL) — toolCallingLoop.ts</text>
|
|
|
|
<rect x="32" y="66" width="345" height="24" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="40" y="82" fill="#c9d1d9" font-size="10">chat gpt-4o (CLIENT) — chatMLFetcher.ts</text>
|
|
|
|
<rect x="48" y="98" width="320" height="22" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="56" y="113" fill="#c9d1d9" font-size="10">execute_tool readFile (INTERNAL) — toolsService.ts</text>
|
|
|
|
<rect x="48" y="128" width="320" height="22" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="56" y="143" fill="#c9d1d9" font-size="10">execute_tool runCommand (INTERNAL) — toolsService.ts</text>
|
|
|
|
<rect x="32" y="160" width="345" height="24" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="40" y="176" fill="#c9d1d9" font-size="10">chat gpt-4o (CLIENT) — chatMLFetcher.ts</text>
|
|
|
|
<rect x="48" y="192" width="320" height="22" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="56" y="207" fill="#c9d1d9" font-size="10">execute_tool runSubagent (INTERNAL) — toolsService.ts</text>
|
|
|
|
<rect x="64" y="222" width="305" height="24" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="72" y="238" fill="#c9d1d9" font-size="10">invoke_agent Explore (INTERNAL) — toolCallingLoop.ts</text>
|
|
|
|
<text x="16" y="270" fill="#8b949e" font-size="10">All spans have copilot_chat.chat_session_id</text>
|
|
</g>
|
|
|
|
<!-- Flow -->
|
|
<g transform="translate(460,60)">
|
|
<rect x="0" y="0" width="200" height="50" rx="6" fill="#1a2332" stroke="#58a6ff"/>
|
|
<text x="12" y="20" fill="#58a6ff" font-size="11" font-weight="600">NodeOTelService</text>
|
|
<text x="12" y="38" fill="#8b949e" font-size="10">Provider A tracer</text>
|
|
|
|
<path d="M 100,50 L 100,90" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#ah2-blue)"/>
|
|
<text x="110" y="75" fill="#58a6ff" font-size="9">onDidCompleteSpan</text>
|
|
|
|
<rect x="0" y="90" width="200" height="40" rx="6" fill="#2a2218" stroke="#d29922"/>
|
|
<text x="12" y="115" fill="#d29922" font-size="11">Debug Panel ✅</text>
|
|
|
|
<path d="M 100,50 L 250,90" stroke="#f85149" stroke-width="1.5" marker-end="url(#ah2-red)"/>
|
|
<rect x="210" y="90" width="200" height="40" rx="6" fill="#2a1a1a" stroke="#f85149"/>
|
|
<text x="222" y="115" fill="#f85149" font-size="11">OTLP → Grafana ✅</text>
|
|
|
|
<text x="0" y="170" fill="#3fb950" font-size="12" font-weight="600">✅ Fully working</text>
|
|
<text x="0" y="188" fill="#8b949e" font-size="10">Same spans in debug panel and Grafana</text>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- CLI BACKGROUND -->
|
|
<div class="tab-pane" id="tab-cli-bg">
|
|
<svg viewBox="0 0 1000 620" xmlns="http://www.w3.org/2000/svg">
|
|
<defs>
|
|
<marker id="ah3-blue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#58a6ff"/></marker>
|
|
<marker id="ah3-green" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#39d353"/></marker>
|
|
<marker id="ah3-red" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f85149"/></marker>
|
|
<marker id="ah3-purple" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#bc8cff"/></marker>
|
|
</defs>
|
|
|
|
<text x="20" y="24" fill="#3fb950" font-size="14" font-weight="600">CLI Background Agent — Two Providers (Bridge Architecture)</text>
|
|
<text x="20" y="42" fill="#8b949e" font-size="11">Extension root span (tracer A) + SDK native spans (tracer B) + Bridge for debug panel</text>
|
|
|
|
<!-- Extension root span -->
|
|
<g transform="translate(30,60)">
|
|
<rect width="380" height="130" rx="8" fill="#161b22" stroke="#30363d"/>
|
|
<text x="12" y="20" fill="#58a6ff" font-size="12" font-weight="600">Extension Root (tracer A)</text>
|
|
|
|
<rect x="16" y="34" width="350" height="24" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="24" y="50" fill="#c9d1d9" font-size="10">invoke_agent copilotcli — copilotcliSession.ts</text>
|
|
<text x="24" y="68" fill="#8b949e" font-size="9">CHAT_SESSION_ID ✅ | CONVERSATION_ID ✅ | SESSION_ID ✅</text>
|
|
|
|
<text x="16" y="92" fill="#bc8cff" font-size="10">↓ traceparent = 00-{traceId}-{spanId}-01</text>
|
|
<text x="16" y="106" fill="#bc8cff" font-size="10">↓ via otelLifecycle.updateParentTraceContext()</text>
|
|
<text x="16" y="120" fill="#8b949e" font-size="9">SDK spans inherit this traceId → same trace in Grafana ✅</text>
|
|
</g>
|
|
|
|
<!-- SDK native spans -->
|
|
<g transform="translate(30,210)">
|
|
<rect width="380" height="280" rx="8" fill="#161b22" stroke="#30363d"/>
|
|
<text x="12" y="20" fill="#3fb950" font-size="12" font-weight="600">SDK Native Spans (tracer B, same traceId)</text>
|
|
|
|
<rect x="16" y="34" width="350" height="24" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="24" y="50" fill="#c9d1d9" font-size="10">invoke_agent (CLIENT) — OtelSessionTracker</text>
|
|
|
|
<rect x="32" y="66" width="330" height="22" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="40" y="81" fill="#c9d1d9" font-size="10">chat claude-opus-4.6-1m (CLIENT)</text>
|
|
|
|
<rect x="48" y="96" width="310" height="22" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="56" y="111" fill="#c9d1d9" font-size="10">execute_tool report_intent</text>
|
|
|
|
<rect x="48" y="126" width="310" height="22" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="56" y="141" fill="#c9d1d9" font-size="10">execute_tool task</text>
|
|
|
|
<rect x="64" y="156" width="290" height="22" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="72" y="171" fill="#c9d1d9" font-size="10">invoke_agent explore (SUBAGENT!)</text>
|
|
|
|
<rect x="80" y="186" width="270" height="22" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="88" y="201" fill="#c9d1d9" font-size="10">execute_tool grep + permission</text>
|
|
|
|
<rect x="32" y="216" width="330" height="22" rx="4" fill="#1a2a1a" stroke="#3fb950" stroke-width="1"/>
|
|
<text x="40" y="231" fill="#c9d1d9" font-size="10">chat claude-opus-4.6-1m (final response)</text>
|
|
|
|
<text x="16" y="260" fill="#8b949e" font-size="9">Bridge injects copilot_chat.chat_session_id on each span</text>
|
|
<text x="16" y="274" fill="#3fb950" font-size="9" font-weight="600">✅ All spans reach debug panel via bridge</text>
|
|
</g>
|
|
|
|
<!-- Data flow -->
|
|
<g transform="translate(450,60)">
|
|
<!-- Provider B box -->
|
|
<rect width="210" height="60" rx="6" fill="#1a2a1a" stroke="#3fb950"/>
|
|
<text x="12" y="20" fill="#3fb950" font-size="11" font-weight="600">Provider B (SDK)</text>
|
|
<text x="12" y="38" fill="#8b949e" font-size="10">BatchSpanProcessor → OTLP</text>
|
|
<text x="12" y="52" fill="#8b949e" font-size="10">BridgeSpanProcessor → ext (injected)</text>
|
|
|
|
<!-- Bridge box -->
|
|
<rect x="0" y="80" width="210" height="65" rx="6" fill="#1a2a2a" stroke="#39d353"/>
|
|
<text x="12" y="98" fill="#39d353" font-size="11" font-weight="600">BridgeSpanProcessor ✅</text>
|
|
<text x="12" y="114" fill="#8b949e" font-size="10">Injected via internal _spanProcessors</text>
|
|
<text x="12" y="128" fill="#8b949e" font-size="9">ProxyTP._delegate._activeSpanProcessor._spanProcessors.push()</text>
|
|
|
|
<path d="M 105,60 L 105,80" stroke="#39d353" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#ah3-green)"/>
|
|
|
|
<!-- Provider A for injection -->
|
|
<rect x="0" y="165" width="210" height="50" rx="6" fill="#1a2332" stroke="#58a6ff"/>
|
|
<text x="12" y="185" fill="#58a6ff" font-size="11" font-weight="600">NodeOTelService (Provider A)</text>
|
|
<text x="12" y="201" fill="#8b949e" font-size="10">injectCompletedSpan → fire event</text>
|
|
|
|
<path d="M 105,145 L 105,165" stroke="#39d353" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#ah3-green)"/>
|
|
|
|
<!-- Debug panel -->
|
|
<rect x="0" y="235" width="210" height="40" rx="6" fill="#2a2218" stroke="#d29922"/>
|
|
<text x="12" y="260" fill="#d29922" font-size="11" font-weight="600">Debug Panel ✅ (full hierarchy)</text>
|
|
|
|
<path d="M 105,215 L 105,235" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#ah3-blue)"/>
|
|
<text x="110" y="228" fill="#58a6ff" font-size="9">onDidCompleteSpan</text>
|
|
|
|
<!-- OTLP — this works fine -->
|
|
<rect x="240" y="0" width="200" height="40" rx="6" fill="#2a1a1a" stroke="#f85149"/>
|
|
<text x="252" y="25" fill="#f85149" font-size="11">OTLP → Grafana ✅</text>
|
|
|
|
<path d="M 210,30 L 240,20" stroke="#f85149" stroke-width="1.5" marker-end="url(#ah3-red)"/>
|
|
|
|
<!-- Key insight -->
|
|
<rect x="0" y="300" width="440" height="100" rx="8" fill="#161b22" stroke="#39d353"/>
|
|
<text x="16" y="320" fill="#39d353" font-size="12" font-weight="600">HOW: Bridge injection via OTel v2 internals</text>
|
|
<text x="16" y="340" fill="#8b949e" font-size="10">1. api.trace.getTracerProvider() → ProxyTracerProvider</text>
|
|
<text x="16" y="356" fill="#8b949e" font-size="10">2. ._delegate → BasicTracerProvider</text>
|
|
<text x="16" y="372" fill="#8b949e" font-size="10">3. ._activeSpanProcessor → MultiSpanProcessor</text>
|
|
<text x="16" y="388" fill="#8b949e" font-size="10">4. ._spanProcessors.push(bridge) — same pattern SDK uses in forceFlush()</text>
|
|
</g>
|
|
|
|
<!-- Working path annotation -->
|
|
<g transform="translate(30,510)">
|
|
<rect width="920" height="80" rx="8" fill="#161b22" stroke="#3fb950"/>
|
|
<text x="16" y="20" fill="#3fb950" font-size="12" font-weight="600">✅ Full data flow working</text>
|
|
<text x="16" y="40" fill="#8b949e" font-size="10">• Grafana: full hierarchy via Provider B → BatchSpanProcessor → OTLP</text>
|
|
<text x="16" y="56" fill="#8b949e" font-size="10">• Debug Panel: full hierarchy via Provider B → Bridge → injectCompletedSpan → onDidCompleteSpan</text>
|
|
<text x="16" y="72" fill="#8b949e" font-size="10">• Trace context: SDK spans are children of extension root span (traceparent propagation)</text>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- CLI TERMINAL -->
|
|
<div class="tab-pane" id="tab-cli-term">
|
|
<svg viewBox="0 0 800 300" xmlns="http://www.w3.org/2000/svg">
|
|
<defs>
|
|
<marker id="ah4-red" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f85149"/></marker>
|
|
<marker id="ah4-yellow" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#d29922"/></marker>
|
|
</defs>
|
|
|
|
<text x="20" y="24" fill="#8b949e" font-size="14" font-weight="600">CLI Terminal — Separate Process, Independent Traces</text>
|
|
<text x="20" y="42" fill="#8b949e" font-size="11">Extension only forwards env vars. No bridge. No debug panel visibility.</text>
|
|
|
|
<g transform="translate(30,60)">
|
|
<!-- Extension -->
|
|
<rect width="250" height="70" rx="6" fill="#161b22" stroke="#30363d"/>
|
|
<text x="12" y="20" fill="#58a6ff" font-size="11" font-weight="600">Extension (at terminal creation)</text>
|
|
<text x="12" y="40" fill="#8b949e" font-size="10">deriveCopilotCliOTelEnv(config, {})</text>
|
|
<text x="12" y="56" fill="#8b949e" font-size="10">→ TerminalOptions.env</text>
|
|
|
|
<path d="M 250,50 L 310,50" stroke="#d29922" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#ah4-yellow)"/>
|
|
<text x="255" y="42" fill="#d29922" font-size="9">env vars</text>
|
|
|
|
<!-- Terminal process -->
|
|
<rect x="310" y="0" width="250" height="130" rx="6" fill="#161b22" stroke="#8b949e"/>
|
|
<text x="322" y="20" fill="#8b949e" font-size="11" font-weight="600">Terminal Process (copilot binary)</text>
|
|
<text x="322" y="40" fill="#8b949e" font-size="10">Own OtelLifecycle + BasicTracerProvider</text>
|
|
<text x="322" y="56" fill="#8b949e" font-size="10">service.name = "github-copilot"</text>
|
|
<text x="322" y="76" fill="#8b949e" font-size="10">invoke_agent → chat → execute_tool</text>
|
|
<text x="322" y="96" fill="#8b949e" font-size="10">Independent root traceId</text>
|
|
<text x="322" y="116" fill="#8b949e" font-size="10">No CHAT_SESSION_ID (not needed)</text>
|
|
|
|
<path d="M 560,65 L 620,65" stroke="#f85149" stroke-width="1.5" marker-end="url(#ah4-red)"/>
|
|
|
|
<!-- Grafana -->
|
|
<rect x="620" y="30" width="150" height="40" rx="6" fill="#2a1a1a" stroke="#f85149"/>
|
|
<text x="632" y="55" fill="#f85149" font-size="11">Grafana ✅</text>
|
|
|
|
<!-- No debug panel -->
|
|
<rect x="620" y="90" width="150" height="40" rx="6" fill="#21262d" stroke="#484f58"/>
|
|
<text x="632" y="115" fill="#484f58" font-size="11">Debug Panel ❌</text>
|
|
<text x="622" y="148" fill="#484f58" font-size="9">Separate process — spans</text>
|
|
<text x="622" y="162" fill="#484f58" font-size="9">never reach extension</text>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- CLAUDE -->
|
|
<div class="tab-pane" id="tab-claude">
|
|
<svg viewBox="0 0 900 420" xmlns="http://www.w3.org/2000/svg">
|
|
<defs>
|
|
<marker id="ah5-blue" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#58a6ff"/></marker>
|
|
<marker id="ah5-red" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#f85149"/></marker>
|
|
<marker id="ah5-yellow" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><polygon points="0 0, 8 3, 0 6" fill="#d29922"/></marker>
|
|
</defs>
|
|
|
|
<text x="20" y="24" fill="#bc8cff" font-size="14" font-weight="600">Claude Code — Synthetic Spans (Only Option)</text>
|
|
<text x="20" y="42" fill="#8b949e" font-size="11">Child process internal spans inaccessible. Extension creates spans from message loop.</text>
|
|
|
|
<!-- Span hierarchy -->
|
|
<g transform="translate(30,60)">
|
|
<rect width="380" height="200" rx="8" fill="#161b22" stroke="#30363d"/>
|
|
<text x="12" y="20" fill="#bc8cff" font-size="12" font-weight="600">Extension Synthetic Spans (tracer A)</text>
|
|
|
|
<rect x="16" y="36" width="350" height="24" rx="4" fill="#2a1a2a" stroke="#bc8cff" stroke-width="1"/>
|
|
<text x="24" y="52" fill="#c9d1d9" font-size="10">invoke_agent claude — claudeCodeAgent.ts</text>
|
|
|
|
<rect x="32" y="68" width="330" height="22" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="40" y="83" fill="#c9d1d9" font-size="10">chat claude-sonnet-4 — chatMLFetcher.ts (FREE)</text>
|
|
|
|
<rect x="32" y="98" width="330" height="22" rx="4" fill="#2a1a2a" stroke="#bc8cff" stroke-width="1"/>
|
|
<text x="40" y="113" fill="#c9d1d9" font-size="10">execute_tool Read — message loop (PR #4505)</text>
|
|
|
|
<rect x="32" y="128" width="330" height="22" rx="4" fill="#1a2332" stroke="#58a6ff" stroke-width="1"/>
|
|
<text x="40" y="143" fill="#c9d1d9" font-size="10">chat claude-sonnet-4 — chatMLFetcher.ts</text>
|
|
|
|
<rect x="32" y="158" width="330" height="22" rx="4" fill="#2a1a2a" stroke="#bc8cff" stroke-width="1"/>
|
|
<text x="40" y="173" fill="#c9d1d9" font-size="10">execute_tool Edit — message loop (PR #4505)</text>
|
|
|
|
<text x="16" y="195" fill="#8b949e" font-size="9">FLAT hierarchy — no subagent nesting, no permissions</text>
|
|
</g>
|
|
|
|
<!-- Claude subprocess -->
|
|
<g transform="translate(30,280)">
|
|
<rect width="380" height="80" rx="8" fill="#161b22" stroke="#8b949e"/>
|
|
<text x="12" y="20" fill="#8b949e" font-size="12" font-weight="600">Claude Code Subprocess (separate process)</text>
|
|
<text x="12" y="40" fill="#8b949e" font-size="10">Own OTel SDK: metrics + events (no traces)</text>
|
|
<text x="12" y="56" fill="#8b949e" font-size="10">claude_code.token.usage, claude_code.cost.usage</text>
|
|
<text x="12" y="72" fill="#8b949e" font-size="10">claude_code.tool_result, claude_code.api_request</text>
|
|
</g>
|
|
|
|
<!-- Flow -->
|
|
<g transform="translate(460,60)">
|
|
<rect width="210" height="50" rx="6" fill="#1a2332" stroke="#58a6ff"/>
|
|
<text x="12" y="20" fill="#58a6ff" font-size="11" font-weight="600">NodeOTelService</text>
|
|
<text x="12" y="38" fill="#8b949e" font-size="10">Provider A tracer</text>
|
|
|
|
<path d="M 105,50 L 105,90" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#ah5-blue)"/>
|
|
|
|
<rect x="0" y="90" width="210" height="40" rx="6" fill="#2a2218" stroke="#d29922"/>
|
|
<text x="12" y="115" fill="#d29922" font-size="11">Debug Panel ✅ (flat)</text>
|
|
|
|
<rect x="240" y="0" width="200" height="40" rx="6" fill="#2a1a1a" stroke="#f85149"/>
|
|
<text x="252" y="25" fill="#f85149" font-size="11">OTLP → Grafana ✅</text>
|
|
|
|
<path d="M 210,25 L 240,20" stroke="#f85149" stroke-width="1.5" marker-end="url(#ah5-red)"/>
|
|
|
|
<!-- Subprocess → Grafana -->
|
|
<path d="M -50,260 Q 300,300 350,40" stroke="#f85149" stroke-width="1.5" stroke-dasharray="4,2" marker-end="url(#ah5-red)"/>
|
|
<text x="220" y="200" fill="#f85149" font-size="9">subprocess metrics/events</text>
|
|
|
|
<!-- Limitation callout -->
|
|
<rect x="0" y="170" width="440" height="80" rx="8" fill="#161b22" stroke="#bc8cff"/>
|
|
<text x="16" y="190" fill="#bc8cff" font-size="12" font-weight="600">WHY SYNTHETIC (not bridge)?</text>
|
|
<text x="16" y="210" fill="#8b949e" font-size="10">• Claude SDK runs as separate child process</text>
|
|
<text x="16" y="226" fill="#8b949e" font-size="10">• Internal spans are inaccessible from extension</text>
|
|
<text x="16" y="242" fill="#8b949e" font-size="10">• Bridge approach is impossible — synthetic is the only option</text>
|
|
</g>
|
|
</svg>
|
|
</div>
|
|
|
|
</div><!-- tab-content -->
|
|
|
|
<!-- Info cards -->
|
|
<div class="section">
|
|
<h3>Key Design Facts</h3>
|
|
<div class="info-grid">
|
|
<div class="info-card">
|
|
<h4>Two TracerProviders in the Same Process</h4>
|
|
<ul>
|
|
<li><b>Provider A</b> (<code>NodeOTelService</code>): Created at extension activation. Stores tracer ref. Serves foreground agent, CLI wrapper span, Claude synthetic spans.</li>
|
|
<li><b>Provider B</b> (<code>BasicTracerProvider</code>): Created by SDK's <code>OtelLifecycle.initializeOtelSdk()</code>. Replaces A as global. Serves all SDK-native spans.</li>
|
|
<li>Provider A's stored <code>this._tracer</code> still works after override — OTel tracers are bound to their provider at creation time.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>Bridge SpanProcessor</h4>
|
|
<ul>
|
|
<li>Injected into Provider B via internal <code>_spanProcessors.push()</code></li>
|
|
<li>Converts <code>ReadableSpan</code> → <code>ICompletedSpanData</code></li>
|
|
<li>Injects <code>copilot_chat.chat_session_id</code> from <code>traceId → sessionId</code> map</li>
|
|
<li><b>Filters</b>: only forwards spans whose traceId is registered (CLI sessions)</li>
|
|
<li>Fires <code>IOTelService.injectCompletedSpan()</code> → <code>onDidCompleteSpan</code></li>
|
|
</ul>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>Trace Context Propagation</h4>
|
|
<ul>
|
|
<li>Extension root span → <code>traceparent = 00-{traceId}-{spanId}-01</code></li>
|
|
<li>Passed to SDK via <code>otelLifecycle.updateParentTraceContext(sessionId, traceparent)</code></li>
|
|
<li>SDK spans inherit the extension's traceId → same trace in Grafana</li>
|
|
<li>Bridge matches traceId → injects CHAT_SESSION_ID → debug panel buckets correctly</li>
|
|
</ul>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>Debug Panel Data Path</h4>
|
|
<ul>
|
|
<li><b>Foreground</b>: tracer A → TrackedSpanHandle.end() → <code>_onDidCompleteSpan.fire()</code></li>
|
|
<li><b>CLI background</b>: tracer B → Bridge.onEnd() → <code>injectCompletedSpan()</code> → <code>_onDidCompleteSpan.fire()</code></li>
|
|
<li><b>Claude</b>: tracer A → TrackedSpanHandle.end() → <code>_onDidCompleteSpan.fire()</code> (same as foreground)</li>
|
|
<li><b>CLI terminal</b>: N/A (separate process, no debug panel visibility)</li>
|
|
<li>Both consumers: <code>OTelChatDebugLogProvider</code> (trajectory) and <code>ChatDebugFileLoggerService</code> (file)</li>
|
|
</ul>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>service.name Values</h4>
|
|
<ul>
|
|
<li><code>copilot-chat</code> — Extension Provider A spans (foreground, CLI wrapper, Claude synthetic)</li>
|
|
<li><code>github-copilot</code> — SDK Provider B spans (CLI native), CLI terminal</li>
|
|
<li><code>claude-code</code> — Claude subprocess metrics/events</li>
|
|
</ul>
|
|
</div>
|
|
<div class="info-card">
|
|
<h4>What Goes Where</h4>
|
|
<ul>
|
|
<li><b>Grafana</b>: Everything from all OTLP exporters (Provider A + B + terminal + Claude subprocess)</li>
|
|
<li><b>Debug Panel</b>: Only spans that fire <code>onDidCompleteSpan</code> with <code>copilot_chat.chat_session_id</code></li>
|
|
<li><b>File Logger</b>: Same as debug panel (listens to same event)</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function switchTab(id) {
|
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
document.querySelectorAll('.tab-pane').forEach(p => p.classList.remove('active'));
|
|
document.getElementById('tab-' + id).classList.add('active');
|
|
event.target.classList.add('active');
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|