Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/src/commands/agent_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use tokio::io::{AsyncBufReadExt, BufReader};
use crate::auth::Auth;
use crate::constants::{self, AGENT_HOST_PORT};
use crate::log;
use crate::options::TelemetryLevel;
use crate::state::LauncherPaths;
use crate::tunnels::agent_host::{
classify_agent_host, serve_agent_host_tunnel_connection, AgentHostConfig, AgentHostManager,
Expand Down Expand Up @@ -288,6 +289,11 @@ async fn run_supervisor(mut ctx: CommandContext, mut args: AgentHostArgs) -> Res
Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())),
AgentHostConfig {
server_data_dir: args.server_data_dir.clone(),
telemetry_level: if ctx.args.global_options.disable_telemetry {
Some(TelemetryLevel::Off)
} else {
ctx.args.global_options.telemetry_level
},
// The AH backend runs on an internal-only unix socket / named
// pipe between this supervisor and its child, so we
// deliberately disable the backend's token check; this
Expand Down
8 changes: 7 additions & 1 deletion cli/src/tunnels/agent_host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::async_pipe::{
use crate::constants::VSCODE_CLI_QUALITY;
use crate::download_cache::DownloadCache;
use crate::log;
use crate::options::Quality;
use crate::options::{Quality, TelemetryLevel};
use crate::state::LauncherPaths;
use crate::update_service::{
unzip_downloaded_release, Platform, Release, TargetKind, UpdateService,
Expand Down Expand Up @@ -98,6 +98,7 @@ const UPGRADE_KILL_DELAY: Duration = Duration::from_secs(3);
#[derive(Clone, Debug)]
pub struct AgentHostConfig {
pub server_data_dir: Option<String>,
pub telemetry_level: Option<TelemetryLevel>,
pub without_connection_token: bool,
pub connection_token: Option<String>,
pub connection_token_file: Option<String>,
Expand Down Expand Up @@ -262,6 +263,10 @@ impl AgentHostManager {
cmd.arg("--server-data-dir");
cmd.arg(a);
}
if let Some(level) = self.config.telemetry_level {
cmd.arg("--telemetry-level");
cmd.arg(level.to_string());
}
if self.config.without_connection_token {
cmd.arg("--without-connection-token");
}
Expand Down Expand Up @@ -2255,6 +2260,7 @@ mod tests {
Arc::new(ReqwestSimpleHttp::new()),
AgentHostConfig {
server_data_dir: None,
telemetry_level: None,
without_connection_token: true,
connection_token: None,
connection_token_file: None,
Expand Down
31 changes: 19 additions & 12 deletions src/vs/platform/agentHost/browser/remoteAgentHostProtocolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ import { ChatSourceKind, ContentEncoding, ResourceRequestParams, type Completion
import type { InvokeChangesetOperationParams, InvokeChangesetOperationResult } from '../common/state/protocol/channels-changeset/commands.js';
import { encodeBase64 } from '../../../base/common/buffer.js';
import { ILoadEstimator, LoadEstimator } from '../../../base/parts/ipc/common/ipc.net.js';
import { ITelemetryService, TELEMETRY_CRASH_REPORTER_SETTING_ID, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SETTING_ID, TelemetryLevel, telemetryLevelEnabled } from '../../telemetry/common/telemetry.js';
import { ITelemetryService, TelemetryLevel, TELEMETRY_CRASH_REPORTER_SETTING_ID, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SETTING_ID } from '../../telemetry/common/telemetry.js';
import { getTelemetryLevel } from '../../telemetry/common/telemetryUtils.js';
import { AgentHostTelemetryLevelConfigKey, AgentHostTerminalAutoApproveEnabledConfigKey, AgentHostTerminalAutoApproveRulesConfigKey, AgentHostDisableRepoInfoTelemetryConfigKey, getAgentHostTerminalAutoApproveRulesConfig, TERMINAL_AUTO_APPROVE_ENABLED_SETTING_ID, TERMINAL_AUTO_APPROVE_SETTING_ID, TERMINAL_IGNORE_DEFAULT_AUTO_APPROVE_RULES_SETTING_ID, DISABLE_REPO_INFO_TELEMETRY_SETTING_ID, telemetryLevelToAgentHostConfigValue } from '../common/agentHostSchema.js';
import { getAgentHostConfigurationSyncEntries, resolveAgentHostConfigurationSyncPatch, resolveAgentHostConfigurationSyncValue } from '../common/agentHostConfigurationSync.js';
import { managedPermissionsConfigurationIds, resolveManagedSettingsPermissions, type IAgentHostManagedSettingsPermissions } from '../common/agentHostManagedSettings.js';
import { AgentHostClientConnectionKind, toClientTelemetryMeta } from '../common/agentHostTelemetry.js';
import { AgentHostClientConnectionKind, toAgentHostClientMeta } from '../common/agentHostTelemetry.js';
import type { OtlpExportLogsParams } from '../common/state/protocol/channels-otlp/notifications.js';
import type { TelemetryCapabilities } from '../common/state/protocol/channels-otlp/state.js';
import type { Implementation, InitializeResult } from '../common/state/protocol/common/commands.js';
Expand Down Expand Up @@ -455,7 +455,7 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
protocolVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
clientId: this._clientId,
clientInfo: this._clientInfo,
...this._clientConnectionTelemetryMeta(),
_meta: this._clientMeta(),
initialSubscriptions: [ROOT_STATE_URI],
}, { bypassInitializeQueue: true });
this._applyInitializeResult(result);
Expand Down Expand Up @@ -705,7 +705,7 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
clientId: this._clientId,
lastSeenServerSeq,
subscriptions,
...this._clientConnectionTelemetryMeta(),
_meta: this._clientMeta(),
}, { bypassReconnectGate: true });
return { result, freshInitialize: false };
} catch (error) {
Expand All @@ -720,7 +720,7 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
protocolVersions: [...SUPPORTED_PROTOCOL_VERSIONS],
clientId: this._clientId,
clientInfo: this._clientInfo,
...this._clientConnectionTelemetryMeta(),
_meta: this._clientMeta(),
initialSubscriptions: subscriptions,
}, { bypassReconnectGate: true });
this._applyInitializeResult(initializeResult, false);
Expand Down Expand Up @@ -774,12 +774,15 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
}, { bypassReconnectGate: true })));
}

private _clientConnectionTelemetryMeta(): { _meta: Record<string, unknown> } | Record<string, never> {
const sendIdentity = telemetryLevelEnabled(this._telemetryService, TelemetryLevel.USAGE);
const machineId = sendIdentity ? this._telemetryService.machineId : undefined;
const devDeviceId = sendIdentity ? this._telemetryService.devDeviceId : undefined;
const meta = toClientTelemetryMeta(this._transport.clientConnectionKind, machineId, devDeviceId);
return meta ? { _meta: meta } : {};
private _clientMeta(): Record<string, unknown> {
const telemetryLevel = this._effectiveTelemetryLevel();
const sendIdentity = telemetryLevel >= TelemetryLevel.USAGE;
return toAgentHostClientMeta(
this._transport.clientConnectionKind,
telemetryLevel,
sendIdentity ? this._telemetryService.machineId : undefined,
sendIdentity ? this._telemetryService.devDeviceId : undefined,
);
}

private _applyInitializeResult(result: CommandMap['initialize']['result'], forwardClientConfig = true): void {
Expand Down Expand Up @@ -1616,7 +1619,11 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
}

private _updateTelemetryLevel(): void {
this._dispatchRootConfig({ [AgentHostTelemetryLevelConfigKey]: telemetryLevelToAgentHostConfigValue(getTelemetryLevel(this._configurationService)) });
this._dispatchRootConfig({ [AgentHostTelemetryLevelConfigKey]: telemetryLevelToAgentHostConfigValue(this._effectiveTelemetryLevel()) });
}

private _effectiveTelemetryLevel(): TelemetryLevel {
return Math.min(getTelemetryLevel(this._configurationService), this._telemetryService.telemetryLevel);
}

/** Merge a patch into the agent host's root configuration. */
Expand Down
12 changes: 2 additions & 10 deletions src/vs/platform/agentHost/common/agentHostSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ConfigurationTarget, type IConfigurationService, type IConfigurationVal
import { DEFAULT_EDIT_AUTO_APPROVE_PATTERNS, type ChatEditAutoApprovePatterns } from '../../chat/common/chatSettings.js';
import type { IMcpServerConfiguration } from '../../mcp/common/mcpPlatformTypes.js';
import { TelemetryConfiguration, TelemetryLevel } from '../../telemetry/common/telemetry.js';
import { telemetryLevelToAgentHostValue } from './agentHostTelemetry.js';
import { SessionConfigKey } from './sessionConfigKeys.js';
import type { SessionConfigPropertySchema, SessionConfigSchema } from './state/protocol/commands.js';
import { JsonRpcErrorCodes, ProtocolError } from './state/sessionProtocol.js';
Expand Down Expand Up @@ -593,16 +594,7 @@ export const AgentHostMcpServersConfigKey = 'mcpServers';
export type AgentHostMcpServers = Record<string, IMcpServerConfiguration>;

export function telemetryLevelToAgentHostConfigValue(telemetryLevel: TelemetryLevel): TelemetryConfiguration {
switch (telemetryLevel) {
case TelemetryLevel.NONE:
return TelemetryConfiguration.OFF;
case TelemetryLevel.CRASH:
return TelemetryConfiguration.CRASH;
case TelemetryLevel.ERROR:
return TelemetryConfiguration.ERROR;
case TelemetryLevel.USAGE:
return TelemetryConfiguration.ON;
}
return telemetryLevelToAgentHostValue(telemetryLevel);
}

export function agentHostConfigValueToTelemetryLevel(value: unknown): TelemetryLevel | undefined {
Expand Down
39 changes: 36 additions & 3 deletions src/vs/platform/agentHost/common/agentHostTelemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { TelemetryConfiguration, TelemetryLevel } from '../../telemetry/common/telemetry.js';
import type { AgentHostClientType } from './agentHostClientInfo.js';

export const enum AgentHostLaunchKind {
Expand Down Expand Up @@ -49,11 +50,14 @@ export function createUnknownAgentHostClientTelemetryContext(clientType: AgentHo
}

const CLIENT_CONNECTION_KIND_META_KEY = 'vscode.clientConnectionKind';
const CLIENT_TELEMETRY_LEVEL_META_KEY = 'vscode.telemetryLevel';
const CLIENT_MACHINE_ID_META_KEY = 'vscode.clientMachineId';
const CLIENT_DEV_DEVICE_ID_META_KEY = 'vscode.clientDevDeviceId';

export function toClientTelemetryMeta(connectionKind: AgentHostClientConnectionKind | undefined, machineId: string | undefined, devDeviceId: string | undefined): Record<string, unknown> | undefined {
const meta: Record<string, unknown> = {};
export function toAgentHostClientMeta(connectionKind: AgentHostClientConnectionKind | undefined, telemetryLevel: TelemetryLevel, machineId: string | undefined, devDeviceId: string | undefined): Record<string, unknown> {
const meta: Record<string, unknown> = {
[CLIENT_TELEMETRY_LEVEL_META_KEY]: telemetryLevelToAgentHostValue(telemetryLevel),
};
if (connectionKind !== undefined && connectionKind !== AgentHostClientConnectionKind.Unknown) {
meta[CLIENT_CONNECTION_KIND_META_KEY] = connectionKind;
}
Expand All @@ -63,7 +67,7 @@ export function toClientTelemetryMeta(connectionKind: AgentHostClientConnectionK
if (devDeviceId) {
meta[CLIENT_DEV_DEVICE_ID_META_KEY] = devDeviceId;
}
return Object.keys(meta).length > 0 ? meta : undefined;
return meta;
}

export function readClientConnectionKind(meta: Record<string, unknown> | undefined): AgentHostClientConnectionKind {
Expand All @@ -82,6 +86,35 @@ export function readClientConnectionKind(meta: Record<string, unknown> | undefin
}
}

export function readClientTelemetryLevel(meta: Record<string, unknown> | undefined): TelemetryLevel | undefined {
const value = meta?.[CLIENT_TELEMETRY_LEVEL_META_KEY];
switch (value) {
case TelemetryConfiguration.OFF:
return TelemetryLevel.NONE;
case TelemetryConfiguration.CRASH:
return TelemetryLevel.CRASH;
case TelemetryConfiguration.ERROR:
return TelemetryLevel.ERROR;
case TelemetryConfiguration.ON:
return TelemetryLevel.USAGE;
default:
return value === undefined ? undefined : TelemetryLevel.NONE;
}
}

export function telemetryLevelToAgentHostValue(telemetryLevel: TelemetryLevel): TelemetryConfiguration {
switch (telemetryLevel) {
case TelemetryLevel.NONE:
return TelemetryConfiguration.OFF;
case TelemetryLevel.CRASH:
return TelemetryConfiguration.CRASH;
case TelemetryLevel.ERROR:
return TelemetryConfiguration.ERROR;
case TelemetryLevel.USAGE:
return TelemetryConfiguration.ON;
}
}

export function readClientMachineId(meta: Record<string, unknown> | undefined): string | undefined {
return readClientTelemetryIdentity(meta, CLIENT_MACHINE_ID_META_KEY);
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/agentHost/common/agentHostTelemetryEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
export const AgentHostMachineIdEnvKey = 'VSCODE_AGENT_HOST_MACHINE_ID';
export const AgentHostSqmIdEnvKey = 'VSCODE_AGENT_HOST_SQM_ID';
export const AgentHostDevDeviceIdEnvKey = 'VSCODE_AGENT_HOST_DEV_DEVICE_ID';
export const AgentHostTelemetryLevelEnvKey = 'VSCODE_AGENT_HOST_TELEMETRY_LEVEL';

export interface IAgentHostForwardedTelemetryIds {
readonly machineId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import { ILifecycleMainService } from '../../lifecycle/electron-main/lifecycleMa
import { ILogService } from '../../log/common/log.js';
import { Schemas } from '../../../base/common/network.js';
import { getResolvedShellEnv } from '../../shell/node/shellEnv.js';
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
import { NullTelemetryService } from '../../telemetry/common/telemetryUtils.js';
import { UtilityProcess } from '../../utilityProcess/electron-main/utilityProcess.js';
import { AgentHostStartError, IAgentHostConnection, IAgentHostShutdownRequest, IAgentHostStarter, IAgentHostStartRequest } from '../common/agent.js';
import { buildAgentHostTelemetryIdEnv, IAgentHostForwardedTelemetryIds } from '../common/agentHostTelemetryEnv.js';
import { AgentHostLaunchKind, AgentHostLaunchKindEnvVar } from '../common/agentHostTelemetry.js';
import { AgentHostLaunchKind, AgentHostLaunchKindEnvVar, telemetryLevelToAgentHostValue } from '../common/agentHostTelemetry.js';
import { AgentHostByokModelsEnabledSettingId, AgentHostClaudeAgentEnabledSettingId, AgentHostCodexAgentBinaryArgsSettingId, AgentHostCodexAgentEnabledSettingId, AgentHostCodexAgentSdkRootSettingId, AgentHostCodexAgentCodexHomeSettingId, AgentHostIpcChannels, AgentHostOTelCaptureContentSettingId, AgentHostOTelDbSpanExporterEnabledSettingId, AgentHostOTelEnabledSettingId, AgentHostOTelExporterTypeSettingId, AgentHostOTelOtlpEndpointSettingId, AgentHostOTelOtlpProtocolSettingId, AgentHostOTelOutfileSettingId, AgentHostOTelResourceAttributesSettingId, AgentHostOTelServiceNameSettingId, AgentHostOTelPolicyIpcChannel, AgentHostRestartIpcChannel, AgentHostWillRestartIpcChannel, buildAgentHostOTelEnv, buildAgentSdkEnv, IAgentHostManagementService, IAgentHostOTelSettings, sanitizeAgentHostOTelPolicySettings } from '../common/agentService.js';
import { deepClone } from '../../../base/common/objects.js';
import '../common/agentHostStarter.config.contribution.js';
Expand Down Expand Up @@ -54,6 +55,7 @@ export class ElectronAgentHostStarter extends Disposable implements IAgentHostSt
@IEnvironmentMainService private readonly _environmentMainService: IEnvironmentMainService,
@ILifecycleMainService private readonly _lifecycleMainService: ILifecycleMainService,
@ILogService private readonly _logService: ILogService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
) {
super();

Expand Down Expand Up @@ -158,10 +160,8 @@ export class ElectronAgentHostStarter extends Disposable implements IAgentHostSt
const args = [
'--logsPath', this._environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath,
'--user-data-dir', this._environmentMainService.userDataPath,
'--telemetry-level', telemetryLevelToAgentHostValue(this._telemetryService.telemetryLevel),
];
if (this._environmentMainService.disableTelemetry) {
args.push('--disable-telemetry');
}

// Forward the host's resolved telemetry identifiers so the agent host
// reuses the same persisted machineId/sqmId/devDeviceId instead of
Expand Down
19 changes: 16 additions & 3 deletions src/vs/platform/agentHost/node/agentHostTelemetryService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { TelemetryLogAppender } from '../../telemetry/common/telemetryLogAppende
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 { AgentHostDevDeviceIdEnvKey, AgentHostMachineIdEnvKey, AgentHostSqmIdEnvKey, AgentHostTelemetryLevelEnvKey } from '../common/agentHostTelemetryEnv.js';
import { AgentHostRestrictedTelemetrySender, IAgentHostRestrictedTelemetry, IAgentHostInternalTelemetryContext, IAgentHostRestrictedTelemetryContext, TelemetryMeasurements, TelemetryProps } from './agentHostRestrictedTelemetry.js';
import { AgentHostInternalTelemetrySender } from './agentHostMicrosoftTelemetry.js';

Expand All @@ -37,6 +37,7 @@ export interface IAgentHostTelemetryServiceOptions {
readonly disableTelemetry?: boolean;
readonly fetchFn?: typeof globalThis.fetch;
readonly requestService?: IRequestService;
readonly readTelemetryLevelEnvironment?: () => string | undefined;
}

export interface IAgentHostTelemetryService extends ITelemetryService, IAgentHostRestrictedTelemetry {
Expand All @@ -46,7 +47,7 @@ export interface IAgentHostTelemetryService extends ITelemetryService, IAgentHos
export class AgentHostTelemetryService extends Disposable implements IAgentHostTelemetryService {
declare readonly _serviceBrand: undefined;

private _telemetryLevel = TelemetryLevel.USAGE;
private _telemetryLevel: TelemetryLevel;

/**
* Whether the current Copilot token opts into enhanced/restricted telemetry (`rt=1`). Defaults
Expand All @@ -61,8 +62,10 @@ export class AgentHostTelemetryService extends Disposable implements IAgentHostT
private readonly _restricted?: IAgentHostRestrictedTelemetry,
copilotSdkVersion?: string,
copilotRuntimeVersion?: string,
initialTelemetryLevel: TelemetryLevel = TelemetryLevel.USAGE,
) {
super();
this._telemetryLevel = initialTelemetryLevel;
if (isDisposable(_delegate)) {
this._register(_delegate);
}
Expand Down Expand Up @@ -274,5 +277,15 @@ export async function createAgentHostTelemetryService(options: IAgentHostTelemet
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, productService.copilotVersions?.sdk, productService.copilotVersions?.runtime));
const initialTelemetryLevel = Math.min(
parseLaunchTelemetryLevel(environmentService.args?.['telemetry-level']),
parseLaunchTelemetryLevel((options.readTelemetryLevelEnvironment ?? (() => process.env[AgentHostTelemetryLevelEnvKey]))()),
);
return disposables.add(new AgentHostTelemetryService(telemetryService, restricted, productService.copilotVersions?.sdk, productService.copilotVersions?.runtime, initialTelemetryLevel));
}

function parseLaunchTelemetryLevel(value: string | undefined): TelemetryLevel {
return value === undefined
? TelemetryLevel.USAGE
: agentHostConfigValueToTelemetryLevel(value) ?? TelemetryLevel.NONE;
}
Loading
Loading