Fix Enterprise Managed OTel Race in Copilot Extension - #336701
Conversation
Preserve the event-driven, single-attempt recovery implementation and OTel settings-block replacement before evaluating an in-process alternative. Refs #336102. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
I'm investigating whether we can restart/reinitialize the OTel service in-process instead of restarting the extension host, without introducing significant destabilization risk for this release. Keeping this PR in draft while I assess that alternative. |
This comment was marked as low quality.
This comment was marked as low quality.
Prototyped this. The extension exporter can initialize in-process, but the embedded SDK captures configuration during startup and ignores updates after native telemetry initializes. Coordinating both safely needs broader lifecycle work, so we are keeping the bounded host restart for this release. |
Track recognized managed settings independently of export enablement so later updates to a disabled startup policy only offer reload. Add regression coverage and align OTel Settings descriptions with the retained environment-variable precedence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| "name": "CopilotOtelEnabled" | ||
| }, | ||
| "markdownDescription": "Enable OpenTelemetry trace/metric/log emission for Copilot Chat operations. Precedence: enterprise policy > env var `COPILOT_OTEL_ENABLED` > user setting. Requires window reload.", | ||
| "markdownDescription": "Enable OpenTelemetry trace/metric/log emission for Copilot Chat operations. Recognized enterprise OTel settings replace personal OTel settings, but env var `COPILOT_OTEL_ENABLED` can still override enablement. Requires window reload.", |
There was a problem hiding this comment.
These are documentation-only edits. Environment variables can already override or supplement managed OTel settings; this PR does not introduce that behavior. Full enterprise-over-environment enforcement across the resolver, exporters, and embedded SDK is separate work.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use a lifecycle-bound progress notification instead of a persistent warning so successful restart cannot leave contradictory notices. Preserve the existing restart guard and fallback grace, and cover command failure, fallback, and unmanaged settings in contribution tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the redundant success notification while retaining the persisted restart acknowledgement, output log, monitoring indicator, and manual reload warnings. Add regression coverage for silent success and update the monitoring guide. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Draft implementation: #336814, stacked on this PR. It adds governed identity capture for Local and now includes the shared telemetry-block precedence fix. The follow-up still needs core validation and a live editor smoke test; the draft is not rollout-ready. Review and signed approval remain outstanding. |
|
|
||
| ### Activation | ||
|
|
||
| When late enterprise OTel settings turn on external export after Copilot's telemetry service |
There was a problem hiding this comment.
Is all this text necessary or could it be more concise?
Enterprises use Managed Settings to configure OTel for Copilot chat sessions. Previously, an initialization race could leave the Copilot extension running with its original OTel configuration after the enterprise configuration arrived. In particular, export could remain off for the rest of the window even though Settings showed it enabled.
Fix
Compare current OTel settings against the configuration used to construct the running service, both after activation and on OTel configuration changes. If a late enterprise configuration enables OTLP export that was initially off, show a progress notice and request an extension-host restart to rebuild the telemetry service with the managed configuration.
Why restart the host? Replacing just the OTel object would leave existing consumers, event subscriptions, and the embedded runtime using old state. In-process recovery is feasible, but needs a coordinated reinitialization path. For this Local-harness hotfix, we reuse the existing host-restart lifecycle and limit it to one automatic attempt per session.
Recognized enterprise OTel settings replace the whole personal OTel settings block, rather than merging with it. Fields omitted by the enterprise use defaults. Other settings are unaffected.
Restart behavior
An eligible restart affects other extensions in the window and can interrupt work. It is preceded by an auto-clearing progress notice and respects existing active-session shutdown vetoes. Successful recovery is logged without a confirmation toast. A failed or declined attempt falls back to a manual reload, not repeated automatic attempts.
Accepted gaps and rationale
Telemetry produced before policy arrives and recovery completes is not guaranteed to be exported. This is not limited to a first-ever run, and we do not assume policy always arrives before the first chat request. Policies indistinguishable from schema defaults remain a limitation of using the existing configuration API.
Environment-variable precedence is unchanged: overrides remain an accepted risk. The
package.jsondescriptions correct the documentation, not the behavior. Full enterprise-over-environment enforcement across the resolver, exporters, and embedded SDK is separate work.This is a tactical fix for the Local harness while moving toward Agent Host. A proposed policy-inspection API would improve provenance but would not make policy arrive earlier or reconfigure an initialized service. Holding activation would require a readiness signal before OTel initialization and defined failure/timeout behavior without delaying unmanaged users. Those broader startup/API changes are deferred rather than added to this hotfix.
The comparison/fingerprint covers the resolved configuration and
github.copilot.chat.otel.{enabled,exporterType,protocol,otlpEndpoint,captureContent,serviceName,resourceAttributes,headers,outfile,maxAttributeSizeChars,dbSpanExporter.enabled}defaults. The last two are tracked but are not policy signals.Manual Testing
Manual Test Steps
Use the single-script macOS repro, which combines a console collector and temporary managed-settings file. Requires macOS, Node.js 18+, and a free port 4318.
Start without managed OTel. Use a disposable test setup and an approved account with no existing OTel policy from the account or MDM.
/Library/Application Support/GitHubCopilot/managed-settings.jsonmust not exist; the script refuses to overwrite it. Close other VS Code windows because this policy is machine-wide. Leave personal OTel and DB export disabled and clear OTel environment overrides from the test launcher. A fresh profile alone does not isolate machine policy.Activate Copilot before applying policy. Open the patched extension on Insiders (
--extensionDevelopmentPath /path/to/vscode/extensions/copilot) or the local build (./scripts/code.sh). Sign in and select the Local harness, not Agent Host. Send a harmless request such as "Reply with OK" while OTel is still off, ensuring Copilot has activated.Run the gist in one terminal. Download
otel-restart-test.cjsand run:The collector starts immediately. Press Enter only after step 2, then switch back to VS Code. The script deliberately waits 8 seconds to make late policy arrival easy to observe; this is a test delay, not an estimate of production latency. It then prints
Test policy applied for 120 seconds.and enables managed OTel pointing at the collector. The automatic extension-host restart is usually near-instant: you may only glimpse a brief progress notification, which dismisses automatically as the host restarts. No Developer: Sync Account Policy command is needed.Verify export. Once Copilot chat is ready, send another harmless Local request and allow about 10 seconds for batching. The script's terminal should print
POST /v1/tracesand JSON data. There is no success toast; recovery is recorded in the Copilot output log. Use harmless prompts and review printed telemetry before sharing.Cleanup: After 120 seconds, or on Ctrl+C, the script removes only its unchanged test file and stops the collector. Do not edit that file during the run. Policy withdrawal should offer a manual reload, not another automatic restart. Reload or close the test window afterward; use a fresh editor session to repeat the test.
Before-fix comparison: Repeat the same flow on an unpatched build. Policy becomes visible, but there is no automatic restart or new trace export until a manual reload.
Additional controls: Without enterprise OTel policy, personal settings edits should only offer a reload. To check settings replacement, repeat with personal service-name/resource-attribute values configured before the test; these must not appear in the printed managed telemetry.
This deliberately delivers policy after activation. The unit regression separately covers policy arriving between service construction and watcher registration.
Resolves #336102.