src/vs/platform/agentHost/node/agentHostTelemetryService.ts

267 LOC · 233 covered · 34 uncovered · 66 ranges · 1484 concepts · 21 introducers · 685 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- agentHostTelemetryService.ts ×29
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { hostname, release } from 'os';
7 > import { Disposable, isDisposable, toDisposable, type DisposableStore } from '../../../base/common/lifecycle.js';
8 > import { joinPath } from '../../../base/common/resources.js';
9 > import { URI } from '../../../base/common/uri.js';
10 > import { getDevDeviceId, getMachineId, getSqmMachineId } from '../../../base/node/id.js';
11 > import { ConfigurationService } from '../../configuration/common/configurationService.js';
12 > import { INativeEnvironmentService } from '../../environment/common/environment.js';
13 > import { IFileService } from '../../files/common/files.js';
14 > import { ILogService, ILoggerService } from '../../log/common/log.js';
15 > import { NullPolicyService } from '../../policy/common/policy.js';
16 > import { IProductService } from '../../product/common/productService.js';
17 > import { IRequestService } from '../../request/common/request.js';
18 > import { OneDataSystemAppender } from '../../telemetry/node/1dsAppender.js';
19 > import { resolveCommonProperties } from '../../telemetry/common/commonProperties.js';
20 > import { ClassifiedEvent, IGDPRProperty, OmitMetadata, StrictPropertyCheck } from '../../telemetry/common/gdprTypings.js';
21 > import { ITelemetryData, ITelemetryService, TelemetryLevel } from '../../telemetry/common/telemetry.js';
22 > import { TelemetryLogAppender } from '../../telemetry/common/telemetryLogAppender.js';
23 > import { TelemetryService } from '../../telemetry/common/telemetryService.js';
24 > import { getPiiPathsFromEnvironment, isInternalTelemetry, isLoggingOnly, NullTelemetryService, supportsTelemetry, type ITelemetryAppender } from '../../telemetry/common/telemetryUtils.js';
25 > import { AgentHostTelemetryLevelConfigKey, agentHostConfigValueToTelemetryLevel } from '../common/agentHostSchema.js';
26 > import { AgentHostDevDeviceIdEnvKey, AgentHostMachineIdEnvKey, AgentHostSqmIdEnvKey } from '../common/agentHostTelemetryEnv.js';
27 > import { AgentHostRestrictedTelemetrySender, IAgentHostRestrictedTelemetry, IAgentHostInternalTelemetryContext, IAgentHostRestrictedTelemetryContext, TelemetryMeasurements, TelemetryProps } from './agentHostRestrictedTelemetry.js';
28 > import { AgentHostInternalTelemetrySender } from './agentHostMicrosoftTelemetry.js';
29 >
30 > export interface IAgentHostTelemetryServiceOptions {
31 > readonly environmentService: INativeEnvironmentService;
32 > readonly productService: IProductService;
33 > readonly fileService: IFileService;
34 > readonly loggerService: ILoggerService | undefined;
35 > readonly logService: ILogService;
36 > readonly disposables: DisposableStore;
37 > readonly disableTelemetry?: boolean;
38 > readonly fetchFn?: typeof globalThis.fetch;
39 > readonly requestService?: IRequestService;
40 > }
41 >
42 > export interface IAgentHostTelemetryService extends ITelemetryService, IAgentHostRestrictedTelemetry {
43 > updateTelemetryLevel(telemetryLevel: TelemetryLevel): void;
44 > }
45 >
46 > export class AgentHostTelemetryService extends Disposable implements IAgentHostTelemetryService {
47 > declare readonly _serviceBrand: undefined;
48 >
49 > private _telemetryLevel = TelemetryLevel.USAGE;
50 >
51 > /**
52 > * Whether the current Copilot token opts into enhanced/restricted telemetry (`rt=1`). Defaults
53 > * to `false` so nothing restricted is sent until an authenticated token confirms the opt-in,
54 > * keeping public users off the enhanced pipeline the way the Copilot extension does.
55 > */
56 > private _restrictedTelemetryEnabled = false;
57 > private _internalTelemetryEnabled = false;
58 >
59 > constructor(
60 > private readonly _delegate: ITelemetryService, agentHostTelemetryService.ts ×2
61 > private readonly _restricted?: IAgentHostRestrictedTelemetry,
62 > ) {
63 > super();
64 > if (isDisposable(_delegate)) {
65 > this._register(_delegate); telemetryService.ts ×9
66 > }
69 > get telemetryLevel(): TelemetryLevel {
70 > return Math.min(this._delegate.telemetryLevel, this._telemetryLevel); agentHostTelemetryService.ts ×1
71 > }
73 > get sendErrorTelemetry(): boolean {
74 > return this.telemetryLevel >= TelemetryLevel.ERROR && this._delegate.sendErrorTelemetry; agentHostTelemetryService.ts ×5
75 > }
77 > get sessionId(): string {
78 return this._delegate.sessionId;
79 }
81 > get machineId(): string {
82 return this._delegate.machineId;
83 }
85 > get sqmId(): string {
86 return this._delegate.sqmId;
87 }
89 > get devDeviceId(): string {
90 return this._delegate.devDeviceId;
91 }
93 > get firstSessionDate(): string {
94 return this._delegate.firstSessionDate;
95 }
97 > get msftInternal(): boolean | undefined {
98 return this._delegate.msftInternal;
99 }
101 > publicLog(eventName: string, data?: ITelemetryData): void {
102 > if (this.telemetryLevel < TelemetryLevel.USAGE) { agentHostTelemetryService.ts ×5
103 > return;
104 > }
105 > this._delegate.publicLog(eventName, data);
106 > }
108 > publicLogError(eventName: string, data?: ITelemetryData): void {
109 > if (this.telemetryLevel < TelemetryLevel.ERROR) { agentHostTelemetryService.ts ×5
110 > return;
111 > }
112 this._delegate.publicLogError(eventName, data);
115 > publicLog2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void {
116 > if (this.telemetryLevel < TelemetryLevel.USAGE) { agentHostTelemetryService.ts ×2
118 > }
119 > this._delegate.publicLog2(eventName, data); agentHostTelemetryService.ts ×1
122 > publicLogError2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void {
123 > if (this.telemetryLevel < TelemetryLevel.ERROR) { agentHostTelemetryService.ts ×2
125 > }
126 > this._delegate.publicLogError2(eventName, data); agentHostTelemetryService.ts ×1
129 > sendGHTelemetryEvent(eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
130 > if (this.telemetryLevel < TelemetryLevel.USAGE) { agentHostTelemetryService.ts ×3
132 > }
133 > this._restricted?.sendGHTelemetryEvent(eventName, properties, measurements); agentHostTelemetryService.ts ×3
134 > }
136 > sendEnhancedGHTelemetryEvent(eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
137 > if (this.telemetryLevel < TelemetryLevel.USAGE || !this._restrictedTelemetryEnabled) { agentHostTelemetryService.ts ×3
138 > return;
139 > }
140 > this._restricted?.sendEnhancedGHTelemetryEvent(eventName, properties, measurements);
141 > }
143 > sendEnhancedGHTelemetryEventForContext(context: IAgentHostRestrictedTelemetryContext, eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
144 if (this.telemetryLevel < TelemetryLevel.USAGE || !context.restrictedTelemetryEnabled) {
145 return;
146 }
147 this._restricted?.sendEnhancedGHTelemetryEventForContext(context, eventName, properties, measurements);
148 }
150 > sendInternalMSFTTelemetryEvent(eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
151 > if (this.telemetryLevel < TelemetryLevel.USAGE || !this._internalTelemetryEnabled) { agentHostTelemetryService.ts ×2
152 > return;
153 > }
154 > this._restricted?.sendInternalMSFTTelemetryEvent(eventName, properties, measurements);
155 > }
157 > sendInternalMSFTTelemetryEventForContext(context: IAgentHostInternalTelemetryContext, eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
158 if (this.telemetryLevel < TelemetryLevel.USAGE || !context.isInternal) {
159 return;
160 }
161 this._restricted?.sendInternalMSFTTelemetryEventForContext(context, eventName, properties, measurements);
162 }
164 > setCopilotTrackingId(trackingId: string | undefined): void {
165 > this._restricted?.setCopilotTrackingId(trackingId); agentHostTelemetryService.ts ×2
166 > }
168 > setRestrictedTelemetryEndpoint(endpointUrl: string | undefined): void {
169 > this._restricted?.setRestrictedTelemetryEndpoint(endpointUrl); agentHostTelemetryService.ts ×2
170 > }
172 > setRestrictedTelemetryEnabled(enabled: boolean): void {
173 > this._restrictedTelemetryEnabled = enabled; agentHostTelemetryService.ts ×1
174 > // Mirror onto the sender so the restricted-table writer enforces the same `rt` gate
175 > // independently (defense in depth), matching the extension's opted-in-only reporter.
176 > this._restricted?.setRestrictedTelemetryEnabled(enabled);
177 > }
179 > setInternalTelemetryContext(context: IAgentHostInternalTelemetryContext | undefined): void {
180 > this._internalTelemetryEnabled = context?.isInternal === true; agentHostTelemetryService.ts ×2
181 > this._restricted?.setInternalTelemetryContext(context);
182 > }
184 > setExperimentProperty(name: string, value: string): void {
185 this._delegate.setExperimentProperty(name, value);
186 }
188 > setCommonProperty(name: string, value: string | boolean): void {
189 this._delegate.setCommonProperty(name, value);
190 }
192 > updateTelemetryLevel(telemetryLevel: TelemetryLevel): void {
193 > this._telemetryLevel = Math.min(this._telemetryLevel, telemetryLevel); agentHostTelemetryService.ts ×1
194 > }
196 >
197 > export function updateAgentHostTelemetryLevelFromConfig(telemetryService: ITelemetryService, config: Record<string, unknown> | undefined): void {
198 > const telemetryLevel = config?.[AgentHostTelemetryLevelConfigKey]; agentHostSchema.ts ×6
199 > const telemetryLevelValue = agentHostConfigValueToTelemetryLevel(telemetryLevel);
200 > if (!isAgentHostTelemetryService(telemetryService) || telemetryLevelValue === undefined) {
202 > }
203 > telemetryService.updateTelemetryLevel(telemetryLevelValue); agentHostTelemetryService.ts ×1
204 > }
206 > export function isAgentHostTelemetryService(telemetryService: ITelemetryService): telemetryService is IAgentHostTelemetryService {
207 > return typeof (telemetryService as IAgentHostTelemetryService).updateTelemetryLevel === 'function'; agentHostTelemetryService.ts ×1
208 > }
210 > async function resolveCopilotExtensionVersion(environmentService: INativeEnvironmentService, fileService: IFileService, logService: ILogService): Promise<string | undefined> { 1dsAppender.ts ×11
211 > if (!environmentService.builtinExtensionsPath) {
212 return undefined;
213 }
214 > try { 1dsAppender.ts ×11
215 > const manifest = JSON.parse((await fileService.readFile(joinPath(URI.file(environmentService.builtinExtensionsPath), 'copilot', 'package.json'))).value.toString()) as { version?: unknown };
216 > return typeof manifest.version === 'string' ? manifest.version : undefined;
217 > } catch (error) {
218 logService.debug(`[agentHostTelemetry] Failed to resolve Copilot extension version: ${error instanceof Error ? error.message : String(error)}`);
219 return undefined;
220 }
223 > export async function createAgentHostTelemetryService(options: IAgentHostTelemetryServiceOptions): Promise<IAgentHostTelemetryService> { telemetryService.ts ×9
224 > const { environmentService, productService, fileService, loggerService, logService, disposables } = options;
225 > if (options.disableTelemetry || !loggerService || !supportsTelemetry(productService, environmentService)) {
226 return disposables.add(new AgentHostTelemetryService(NullTelemetryService));
227 }
229 > const configurationService = disposables.add(new ConfigurationService(joinPath(environmentService.appSettingsHome, 'settings.json'), fileService, new NullPolicyService(), logService));
230 > await configurationService.initialize();
231 >
232 > const appenders: ITelemetryAppender[] = [
233 > disposables.add(new TelemetryLogAppender('', false, loggerService, environmentService, productService)),
234 > ];
235 > const internalTelemetry = isInternalTelemetry(productService, configurationService);
236 > const loggingOnly = isLoggingOnly(productService, environmentService);
237 > if (!loggingOnly && productService.aiConfig?.ariaKey) {
238 > const collectorAppender = new OneDataSystemAppender(options.requestService, internalTelemetry, 'monacoworkbench', null, productService.aiConfig.ariaKey); 1dsAppender.ts ×11
239 > disposables.add(toDisposable(() => { void collectorAppender.flush(); }));
240 > appenders.push(collectorAppender);
241 > }
243 > // Prefer the host-forwarded identifiers (see `agentHostTelemetryEnv`) so the
244 > // agent host reports the same persisted machineId/sqmId/devDeviceId as the
245 > // workbench. Fall back to computing them live when not provided (e.g. the
246 > // remote/server agent host, which does not forward them).
247 > const [machineId, sqmId, devDeviceId] = await Promise.all([
248 > process.env[AgentHostMachineIdEnvKey] || getMachineId(error => logService.error(error)),
249 > process.env[AgentHostSqmIdEnvKey] || getSqmMachineId(error => logService.error(error)),
250 > process.env[AgentHostDevDeviceIdEnvKey] || getDevDeviceId(error => logService.error(error)),
251 > ]);
252 >
253 > const commonProperties = resolveCommonProperties(release(), hostname(), process.arch, productService.commit, productService.version, machineId, sqmId, devDeviceId, internalTelemetry, productService.date);
254 >
255 > const telemetryService = new TelemetryService({
256 > appenders,
257 > sendErrorTelemetry: true,
258 > commonProperties,
259 > piiPaths: getPiiPathsFromEnvironment(environmentService),
260 > }, configurationService, productService);
261 >
262 > const extensionVersion = loggingOnly ? undefined : await resolveCopilotExtensionVersion(environmentService, fileService, logService);
263 > const internalSender = loggingOnly ? undefined : disposables.add(new AgentHostInternalTelemetrySender({ requestService: options.requestService, commonProperties, extensionVersion }));
264 > const restricted = loggingOnly ? undefined : new AgentHostRestrictedTelemetrySender(commonProperties, logService, undefined, internalSender, options.fetchFn);
265 >
266 > return disposables.add(new AgentHostTelemetryService(telemetryService, restricted));
267 > }