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.
/*---------------------------------------------------------------------------------------------
agentHostTelemetryService.ts ×29
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { hostname, release } from 'os';
import { Disposable, isDisposable, toDisposable, type DisposableStore } from '../../../base/common/lifecycle.js';
import { joinPath } from '../../../base/common/resources.js';
import { URI } from '../../../base/common/uri.js';
import { getDevDeviceId, getMachineId, getSqmMachineId } from '../../../base/node/id.js';
import { ConfigurationService } from '../../configuration/common/configurationService.js';
import { INativeEnvironmentService } from '../../environment/common/environment.js';
import { IFileService } from '../../files/common/files.js';
import { ILogService, ILoggerService } from '../../log/common/log.js';
import { NullPolicyService } from '../../policy/common/policy.js';
import { IProductService } from '../../product/common/productService.js';
import { IRequestService } from '../../request/common/request.js';
import { OneDataSystemAppender } from '../../telemetry/node/1dsAppender.js';
import { resolveCommonProperties } from '../../telemetry/common/commonProperties.js';
import { ClassifiedEvent, IGDPRProperty, OmitMetadata, StrictPropertyCheck } from '../../telemetry/common/gdprTypings.js';
import { ITelemetryData, ITelemetryService, TelemetryLevel } from '../../telemetry/common/telemetry.js';
import { TelemetryLogAppender } from '../../telemetry/common/telemetryLogAppender.js';
import { TelemetryService } from '../../telemetry/common/telemetryService.js';
import { getPiiPathsFromEnvironment, isInternalTelemetry, isLoggingOnly, NullTelemetryService, supportsTelemetry, type ITelemetryAppender } from '../../telemetry/common/telemetryUtils.js';
import { AgentHostTelemetryLevelConfigKey, agentHostConfigValueToTelemetryLevel } from '../common/agentHostSchema.js';
import { AgentHostDevDeviceIdEnvKey, AgentHostMachineIdEnvKey, AgentHostSqmIdEnvKey } from '../common/agentHostTelemetryEnv.js';
import { AgentHostRestrictedTelemetrySender, IAgentHostRestrictedTelemetry, IAgentHostInternalTelemetryContext, IAgentHostRestrictedTelemetryContext, TelemetryMeasurements, TelemetryProps } from './agentHostRestrictedTelemetry.js';
import { AgentHostInternalTelemetrySender } from './agentHostMicrosoftTelemetry.js';
export interface IAgentHostTelemetryServiceOptions {
readonly environmentService: INativeEnvironmentService;
readonly productService: IProductService;
readonly fileService: IFileService;
readonly loggerService: ILoggerService | undefined;
readonly logService: ILogService;
readonly disposables: DisposableStore;
readonly disableTelemetry?: boolean;
readonly fetchFn?: typeof globalThis.fetch;
readonly requestService?: IRequestService;
}
export interface IAgentHostTelemetryService extends ITelemetryService, IAgentHostRestrictedTelemetry {
updateTelemetryLevel(telemetryLevel: TelemetryLevel): void;
}
export class AgentHostTelemetryService extends Disposable implements IAgentHostTelemetryService {
declare readonly _serviceBrand: undefined;
private _telemetryLevel = TelemetryLevel.USAGE;
/**
* Whether the current Copilot token opts into enhanced/restricted telemetry (`rt=1`). Defaults
* to `false` so nothing restricted is sent until an authenticated token confirms the opt-in,
* keeping public users off the enhanced pipeline the way the Copilot extension does.
*/
private _restrictedTelemetryEnabled = false;
private _internalTelemetryEnabled = false;
constructor(
private readonly _restricted?: IAgentHostRestrictedTelemetry,
) {
super();
if (isDisposable(_delegate)) {
}
get telemetryLevel(): TelemetryLevel {
return Math.min(this._delegate.telemetryLevel, this._telemetryLevel);
agentHostTelemetryService.ts ×1
}
get sendErrorTelemetry(): boolean {
return this.telemetryLevel >= TelemetryLevel.ERROR && this._delegate.sendErrorTelemetry;
agentHostTelemetryService.ts ×5
}
get sessionId(): string {
return this._delegate.sessionId;
}
get machineId(): string {
return this._delegate.machineId;
}
get sqmId(): string {
return this._delegate.sqmId;
}
get devDeviceId(): string {
return this._delegate.devDeviceId;
}
get firstSessionDate(): string {
return this._delegate.firstSessionDate;
}
get msftInternal(): boolean | undefined {
return this._delegate.msftInternal;
}
publicLog(eventName: string, data?: ITelemetryData): void {
return;
}
this._delegate.publicLog(eventName, data);
}
publicLogError(eventName: string, data?: ITelemetryData): void {
return;
}
this._delegate.publicLogError(eventName, data);
publicLog2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void {
}
publicLogError2<E extends ClassifiedEvent<OmitMetadata<T>> = never, T extends IGDPRProperty = never>(eventName: string, data?: StrictPropertyCheck<T, E>): void {
}
sendGHTelemetryEvent(eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
}
this._restricted?.sendGHTelemetryEvent(eventName, properties, measurements);
agentHostTelemetryService.ts ×3
}
sendEnhancedGHTelemetryEvent(eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
if (this.telemetryLevel < TelemetryLevel.USAGE || !this._restrictedTelemetryEnabled) {
agentHostTelemetryService.ts ×3
return;
}
this._restricted?.sendEnhancedGHTelemetryEvent(eventName, properties, measurements);
}
sendEnhancedGHTelemetryEventForContext(context: IAgentHostRestrictedTelemetryContext, eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
if (this.telemetryLevel < TelemetryLevel.USAGE || !context.restrictedTelemetryEnabled) {
return;
}
this._restricted?.sendEnhancedGHTelemetryEventForContext(context, eventName, properties, measurements);
}
sendInternalMSFTTelemetryEvent(eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
if (this.telemetryLevel < TelemetryLevel.USAGE || !this._internalTelemetryEnabled) {
agentHostTelemetryService.ts ×2
return;
}
this._restricted?.sendInternalMSFTTelemetryEvent(eventName, properties, measurements);
}
sendInternalMSFTTelemetryEventForContext(context: IAgentHostInternalTelemetryContext, eventName: string, properties?: TelemetryProps, measurements?: TelemetryMeasurements): void {
if (this.telemetryLevel < TelemetryLevel.USAGE || !context.isInternal) {
return;
}
this._restricted?.sendInternalMSFTTelemetryEventForContext(context, eventName, properties, measurements);
}
setCopilotTrackingId(trackingId: string | undefined): void {
}
setRestrictedTelemetryEndpoint(endpointUrl: string | undefined): void {
this._restricted?.setRestrictedTelemetryEndpoint(endpointUrl);
agentHostTelemetryService.ts ×2
}
setRestrictedTelemetryEnabled(enabled: boolean): void {
// Mirror onto the sender so the restricted-table writer enforces the same `rt` gate
// independently (defense in depth), matching the extension's opted-in-only reporter.
this._restricted?.setRestrictedTelemetryEnabled(enabled);
}
setInternalTelemetryContext(context: IAgentHostInternalTelemetryContext | undefined): void {
this._internalTelemetryEnabled = context?.isInternal === true;
agentHostTelemetryService.ts ×2
this._restricted?.setInternalTelemetryContext(context);
}
setExperimentProperty(name: string, value: string): void {
this._delegate.setExperimentProperty(name, value);
}
setCommonProperty(name: string, value: string | boolean): void {
this._delegate.setCommonProperty(name, value);
}
updateTelemetryLevel(telemetryLevel: TelemetryLevel): void {
this._telemetryLevel = Math.min(this._telemetryLevel, telemetryLevel);
agentHostTelemetryService.ts ×1
}
export function updateAgentHostTelemetryLevelFromConfig(telemetryService: ITelemetryService, config: Record<string, unknown> | undefined): void {
const telemetryLevelValue = agentHostConfigValueToTelemetryLevel(telemetryLevel);
if (!isAgentHostTelemetryService(telemetryService) || telemetryLevelValue === undefined) {
}
}
export function isAgentHostTelemetryService(telemetryService: ITelemetryService): telemetryService is IAgentHostTelemetryService {
return typeof (telemetryService as IAgentHostTelemetryService).updateTelemetryLevel === 'function';
agentHostTelemetryService.ts ×1
}
async function resolveCopilotExtensionVersion(environmentService: INativeEnvironmentService, fileService: IFileService, logService: ILogService): Promise<string | undefined> {
1dsAppender.ts ×11
if (!environmentService.builtinExtensionsPath) {
return undefined;
}
const manifest = JSON.parse((await fileService.readFile(joinPath(URI.file(environmentService.builtinExtensionsPath), 'copilot', 'package.json'))).value.toString()) as { version?: unknown };
return typeof manifest.version === 'string' ? manifest.version : undefined;
} catch (error) {
logService.debug(`[agentHostTelemetry] Failed to resolve Copilot extension version: ${error instanceof Error ? error.message : String(error)}`);
return undefined;
}
export async function createAgentHostTelemetryService(options: IAgentHostTelemetryServiceOptions): Promise<IAgentHostTelemetryService> {
telemetryService.ts ×9
const { environmentService, productService, fileService, loggerService, logService, disposables } = options;
if (options.disableTelemetry || !loggerService || !supportsTelemetry(productService, environmentService)) {
return disposables.add(new AgentHostTelemetryService(NullTelemetryService));
}
const configurationService = disposables.add(new ConfigurationService(joinPath(environmentService.appSettingsHome, 'settings.json'), fileService, new NullPolicyService(), logService));
await configurationService.initialize();
const appenders: ITelemetryAppender[] = [
disposables.add(new TelemetryLogAppender('', false, loggerService, environmentService, productService)),
];
const internalTelemetry = isInternalTelemetry(productService, configurationService);
const loggingOnly = isLoggingOnly(productService, environmentService);
if (!loggingOnly && productService.aiConfig?.ariaKey) {
const collectorAppender = new OneDataSystemAppender(options.requestService, internalTelemetry, 'monacoworkbench', null, productService.aiConfig.ariaKey);
1dsAppender.ts ×11
disposables.add(toDisposable(() => { void collectorAppender.flush(); }));
appenders.push(collectorAppender);
}
// Prefer the host-forwarded identifiers (see `agentHostTelemetryEnv`) so the
// agent host reports the same persisted machineId/sqmId/devDeviceId as the
// workbench. Fall back to computing them live when not provided (e.g. the
// remote/server agent host, which does not forward them).
const [machineId, sqmId, devDeviceId] = await Promise.all([
process.env[AgentHostMachineIdEnvKey] || getMachineId(error => logService.error(error)),
process.env[AgentHostSqmIdEnvKey] || getSqmMachineId(error => logService.error(error)),
process.env[AgentHostDevDeviceIdEnvKey] || getDevDeviceId(error => logService.error(error)),
]);
const commonProperties = resolveCommonProperties(release(), hostname(), process.arch, productService.commit, productService.version, machineId, sqmId, devDeviceId, internalTelemetry, productService.date);
const telemetryService = new TelemetryService({
appenders,
sendErrorTelemetry: true,
commonProperties,
piiPaths: getPiiPathsFromEnvironment(environmentService),
}, configurationService, productService);
const extensionVersion = loggingOnly ? undefined : await resolveCopilotExtensionVersion(environmentService, fileService, logService);
const internalSender = loggingOnly ? undefined : disposables.add(new AgentHostInternalTelemetrySender({ requestService: options.requestService, commonProperties, extensionVersion }));
const restricted = loggingOnly ? undefined : new AgentHostRestrictedTelemetrySender(commonProperties, logService, undefined, internalSender, options.fetchFn);
return disposables.add(new AgentHostTelemetryService(telemetryService, restricted));
}