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
77 changes: 49 additions & 28 deletions src/vs/platform/agentHost/browser/remoteAgentHostProtocolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { getTelemetryLevel } from '../../telemetry/common/telemetryUtils.js';
import { AgentHostTelemetryLevelConfigKey, AgentHostCodexEnabledConfigKey, AgentHostSessionSyncEnabledConfigKey, AgentHostTerminalAutoApproveEnabledConfigKey, AgentHostGlobalAutoApproveEnabledConfigKey, AgentHostAutoReplyEnabledConfigKey, AgentHostPreferLongContextEnabledConfigKey, AgentHostSystemProxyEnabledConfigKey, AgentHostTerminalAutoApproveRulesConfigKey, AgentHostDisableRepoInfoTelemetryConfigKey, getAgentHostTerminalAutoApproveRulesConfig, SESSION_SYNC_ENABLED_SETTING_ID, TERMINAL_AUTO_APPROVE_ENABLED_SETTING_ID, GLOBAL_AUTO_APPROVE_SETTING_ID, AUTO_REPLY_SETTING_ID, PREFER_LONG_CONTEXT_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 type { OtlpExportLogsParams } from '../common/state/protocol/channels-otlp/notifications.js';
import type { TelemetryCapabilities } from '../common/state/protocol/channels-otlp/state.js';
import type { InitializeResult } from '../common/state/protocol/common/commands.js';
import type { Implementation, InitializeResult } from '../common/state/protocol/common/commands.js';
import { dirname } from '../../../base/common/resources.js';
import { observableValue, type IObservable } from '../../../base/common/observable.js';
import { isFileResourceRead } from '../common/resourceReadLogging.js';
Expand Down Expand Up @@ -304,6 +304,7 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
transportOrFactory: IProtocolTransport | (() => IProtocolTransport),
loadEstimator: ILoadEstimator | undefined,
clientId: string | undefined = undefined,
private readonly _clientInfo: Implementation | undefined,
@ILogService private readonly _logService: ILogService,
@IAgentHostResourceService private readonly _resourceService: IAgentHostResourceService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
Expand Down Expand Up @@ -463,11 +464,10 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
channel: ROOT_STATE_URI,
protocolVersions: [PROTOCOL_VERSION],
clientId: this._clientId,
clientInfo: this._clientInfo,
initialSubscriptions: [ROOT_STATE_URI],
}, { bypassInitializeQueue: true });
this._serverSeq = result.serverSeq;

this._initializeResult.set(result, undefined);
this._applyInitializeResult(result);

// Hydrate root state from the initial snapshot
for (const snapshot of result.snapshots ?? []) {
Expand All @@ -476,25 +476,6 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
}
}

if (result.defaultDirectory) {
const dir = result.defaultDirectory;
if (typeof dir === 'string') {
this._defaultDirectory = URI.parse(dir).path;
} else {
this._defaultDirectory = URI.revive(dir).path;
}
}

this._updateTelemetryLevel();
this._updateSessionSyncEnabled();
this._updateTerminalAutoApproveEnabled();
this._updateGlobalAutoApproveEnabled();
this._updateAutoReplyEnabled();
this._updatePreferLongContextEnabled();
this._updateSystemProxyEnabled();
this._updateTerminalAutoApproveRules();
this._updateCodexEnabled();
this._updateDisableRepoInfoTelemetry();
if (isClientTransport(this._transport) && this._state.kind === AgentHostClientState.Connecting) {
for (const message of this._state.outbox) {
this._transport.send(message);
Expand Down Expand Up @@ -631,11 +612,7 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
subscriptions.unshift(ROOT_STATE_URI);
}
const lastSeenServerSeq = this._serverSeq;
const result = await this._dispatchRequest<CommandMap['reconnect']['result']>('reconnect', {
clientId: this._clientId,
lastSeenServerSeq,
subscriptions,
}, { bypassReconnectGate: true });
const result = await this._reconnectOrInitialize(lastSeenServerSeq, subscriptions);

if (this._state.kind !== AgentHostClientState.Reconnecting) {
return;
Expand Down Expand Up @@ -671,6 +648,50 @@ export class RemoteAgentHostProtocolClient extends Disposable implements IAgentC
}
}

private async _reconnectOrInitialize(lastSeenServerSeq: number, subscriptions: string[]): Promise<CommandMap['reconnect']['result']> {
try {
return await this._dispatchRequest<CommandMap['reconnect']['result']>('reconnect', {
clientId: this._clientId,
lastSeenServerSeq,
subscriptions,
}, { bypassReconnectGate: true });
} catch (error) {
if (!(error instanceof ProtocolError) || error.code !== AhpErrorCodes.NotFound) {
throw error;
}
}

this._logService.info(`[RemoteAgentHostProtocol] Server forgot client ${this._clientId}; initializing a fresh connection.`);
const initializeResult = await this._dispatchRequest<CommandMap['initialize']['result']>('initialize', {
channel: ROOT_STATE_URI,
protocolVersions: [PROTOCOL_VERSION],
clientId: this._clientId,
clientInfo: this._clientInfo,
initialSubscriptions: subscriptions,
}, { bypassReconnectGate: true });
this._applyInitializeResult(initializeResult);
return { type: ReconnectResultType.Snapshot, snapshots: initializeResult.snapshots ?? [] };
}

private _applyInitializeResult(result: CommandMap['initialize']['result']): void {
this._initializeResult.set(result, undefined);
this._serverSeq = result.serverSeq;
if (result.defaultDirectory) {
const directory = result.defaultDirectory;
this._defaultDirectory = typeof directory === 'string' ? URI.parse(directory).path : URI.revive(directory).path;
}
this._updateTelemetryLevel();
this._updateSessionSyncEnabled();
this._updateTerminalAutoApproveEnabled();
this._updateGlobalAutoApproveEnabled();
this._updateAutoReplyEnabled();
this._updatePreferLongContextEnabled();
this._updateSystemProxyEnabled();
this._updateTerminalAutoApproveRules();
this._updateCodexEnabled();
this._updateDisableRepoInfoTelemetry();
}
Comment thread
roblourens marked this conversation as resolved.

/**
* Apply a `reconnect` RPC result to the subscription manager. On `replay`
* we feed each missed envelope through the normal action path; on
Expand Down
25 changes: 24 additions & 1 deletion src/vs/platform/agentHost/browser/remoteAgentHostServiceImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { AGENT_HOST_LABEL_FORMATTER, AGENT_HOST_SCHEME, agentHostAuthority, norm
import { isDefined } from '../../../base/common/types.js';
import { PROTOCOL_VERSION } from '../common/state/protocol/version/registry.js';
import { type IVscodeUpgradeResult } from '../common/state/protocolUpgrade.js';
import { agentsWindowAgentHostClientInfo, editorWindowAgentHostClientInfo } from '../common/agentHostClientInfo.js';

const SSH_REMOTE_AGENT_HOSTS_STORAGE_KEY = 'remoteAgentHost.sshConnections';

Expand Down Expand Up @@ -117,6 +118,10 @@ export class RemoteAgentHostService extends Disposable implements IRemoteAgentHo
*/
private readonly _labelFormatters = new Map<string, IDisposable>();

protected get clientInfo() {
return editorWindowAgentHostClientInfo;
}

constructor(
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
Expand Down Expand Up @@ -495,7 +500,7 @@ export class RemoteAgentHostService extends Disposable implements IRemoteAgentHo
? { logsHome: this._environmentService.logsHome, connectionId: address, transport: 'websocket' }
: undefined,
);
const client = store.add(this._instantiationService.createInstance(RemoteAgentHostProtocolClient, address, transportFactory, undefined, undefined));
const client = store.add(this._instantiationService.createInstance(RemoteAgentHostProtocolClient, address, transportFactory, undefined, undefined, this.clientInfo));
const entry: IConnectionEntry = { store, client, connected: false, status: RemoteAgentHostConnectionStatus.connecting };
this._entries.set(address, entry);

Expand Down Expand Up @@ -843,3 +848,21 @@ export class RemoteAgentHostService extends Disposable implements IRemoteAgentHo
super.dispose();
}
}

export class AgentsWindowRemoteAgentHostService extends RemoteAgentHostService {

protected override get clientInfo() {
return agentsWindowAgentHostClientInfo;
}

constructor(
@IConfigurationService configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService,
@ILogService logService: ILogService,
@ILabelService labelService: ILabelService,
@IEnvironmentService environmentService: IEnvironmentService,
@IStorageService storageService: IStorageService,
) {
super(configurationService, instantiationService, logService, labelService, environmentService, storageService);
}
}
36 changes: 36 additions & 0 deletions src/vs/platform/agentHost/common/agentHostClientInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import type { Implementation } from './state/protocol/common/commands.js';

const EDITOR_WINDOW_CLIENT_NAME = 'vscode-editor-window';
const AGENTS_WINDOW_CLIENT_NAME = 'vscode-agents-window';

export const enum AgentHostClientType {
EditorWindow = 'editor_window',
AgentsWindow = 'agents_window',
Unknown = 'unknown',
}

export const editorWindowAgentHostClientInfo: Readonly<Implementation> = Object.freeze({
name: EDITOR_WINDOW_CLIENT_NAME,
title: 'VS Code',
});

export const agentsWindowAgentHostClientInfo: Readonly<Implementation> = Object.freeze({
name: AGENTS_WINDOW_CLIENT_NAME,
title: 'VS Code Agents Window',
});

export function getAgentHostClientType(clientInfo: Implementation | undefined): AgentHostClientType {
switch (clientInfo?.name) {
case EDITOR_WINDOW_CLIENT_NAME:
return AgentHostClientType.EditorWindow;
case AGENTS_WINDOW_CLIENT_NAME:
return AgentHostClientType.AgentsWindow;
default:
return AgentHostClientType.Unknown;
}
}
5 changes: 3 additions & 2 deletions src/vs/platform/agentHost/common/agentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { createDecorator } from '../../instantiation/common/instantiation.js';
import type { IAgentServerToolHost } from './agentServerTools.js';
import type { IActiveSubscriptionInfo, IAgentSubscription } from './state/agentSubscription.js';
import type { IRemoteWatchHandle } from './agentHostFileSystemProvider.js';
import type { AgentHostClientType } from './agentHostClientInfo.js';
import type { CompletionsParams, CompletionsResult, CreateTerminalParams, ResolveSessionConfigResult, SessionConfigCompletionsResult } from './state/protocol/commands.js';
import type { InitializeResult } from './state/protocol/common/commands.js';
import type { InvokeChangesetOperationParams, InvokeChangesetOperationResult } from './state/protocol/channels-changeset/commands.js';
Expand Down Expand Up @@ -1185,7 +1186,7 @@ export interface IAgentChats {
* Send a user message into `chat`; on first send, the host passes the resolved
* working directory (or `undefined` for workspace-less sessions).
*/
sendMessage(chat: URI, prompt: string, workingDirectory: URI | undefined, attachments?: readonly MessageAttachment[], turnId?: string, senderClientId?: string): Promise<void>;
sendMessage(chat: URI, prompt: string, workingDirectory: URI | undefined, attachments?: readonly MessageAttachment[], turnId?: string, senderClientId?: string, clientType?: AgentHostClientType): Promise<void>;

/** Abort the in-flight turn for `chat`. */
abort(chat: URI): Promise<void>;
Expand Down Expand Up @@ -1994,7 +1995,7 @@ export interface IAgentService {
* rather than {@link URI} objects so that authority-less scheme URIs
* like `ahp-root://` survive the wire format without normalization.
*/
dispatchAction(channel: string, action: SessionAction | ChatAction | TerminalAction | ClientChangesetAction | ClientAnnotationsAction | IRootConfigChangedAction, clientId: string, clientSeq: number): void;
dispatchAction(channel: string, action: SessionAction | ChatAction | TerminalAction | ClientChangesetAction | ClientAnnotationsAction | IRootConfigChangedAction, clientId: string, clientSeq: number, clientType?: AgentHostClientType): void;

/**
* List the contents of a directory on the agent host's filesystem.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ import {
import type { IRemoteWatchHandle } from '../common/agentHostFileSystemProvider.js';
import type { IActiveSubscriptionInfo, IAgentSubscription } from '../common/state/agentSubscription.js';
import type { CompletionsParams, CompletionsResult, CreateTerminalParams, ResolveSessionConfigResult, SessionConfigCompletionsResult } from '../common/state/protocol/commands.js';
import type { InitializeResult } from '../common/state/protocol/common/commands.js';
import type { Implementation, InitializeResult } from '../common/state/protocol/common/commands.js';
import type { InvokeChangesetOperationParams, InvokeChangesetOperationResult } from '../common/state/protocol/channels-changeset/commands.js';
import type { CreateResourceWatchParams, CreateResourceWatchResult, ResourceCopyParams, ResourceCopyResult, ResourceDeleteParams, ResourceDeleteResult, ResourceListResult, ResourceMkdirParams, ResourceMkdirResult, ResourceMoveParams, ResourceMoveResult, ResourceReadResult, ResourceResolveParams, ResourceResolveResult, ResourceWriteParams, ResourceWriteResult } from '../common/state/sessionProtocol.js';
import type { ActionEnvelope, ChatAction, ClientAnnotationsAction, ClientChangesetAction, INotification, IRootConfigChangedAction, SessionAction, TerminalAction } from '../common/state/sessionActions.js';
Expand Down Expand Up @@ -90,6 +90,7 @@ export class LocalAgentHostServiceClient extends Disposable implements IAgentHos
};

constructor(
private readonly _clientInfo: Implementation,
@ILogService private readonly _logService: ILogService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IEnvironmentService environmentService: IEnvironmentService,
Expand Down Expand Up @@ -123,6 +124,7 @@ export class LocalAgentHostServiceClient extends Disposable implements IAgentHos
transport,
undefined,
this.clientId,
this._clientInfo,
));
this._register(this._protocolClient.onDidClose(() => this._onAgentHostExit.fire(0)));
void this._connect().catch(error => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { AhpJsonlLogger } from '../common/ahpJsonlLogger.js';
import { AgentHostAhpJsonlLoggingSettingId } from '../common/agentService.js';
import { SSHRelayTransport } from './sshRelayTransport.js';
import { RemoteAgentHostProtocolClient } from '../browser/remoteAgentHostProtocolClient.js';
import { agentsWindowAgentHostClientInfo } from '../common/agentHostClientInfo.js';
import { PROTOCOL_VERSION } from '../common/state/protocol/version/registry.js';
import {
ISSHRemoteAgentHostService,
Expand Down Expand Up @@ -56,7 +57,7 @@ export class SSHRelayClientFactory implements ISSHRelayClientFactory {
{ logsHome: this._environmentService.logsHome, connectionId, transport: 'ssh' },
) : undefined;
const transport = this._instantiationService.createInstance(SSHRelayTransport, connectionId, mainService, logger);
return this._instantiationService.createInstance(RemoteAgentHostProtocolClient, address, transport, undefined, undefined);
return this._instantiationService.createInstance(RemoteAgentHostProtocolClient, address, transport, undefined, undefined, agentsWindowAgentHostClientInfo);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { AhpJsonlLogger } from '../common/ahpJsonlLogger.js';
import { AgentHostAhpJsonlLoggingSettingId } from '../common/agentService.js';
import { WSLRelayTransport } from './wslRelayTransport.js';
import { RemoteAgentHostProtocolClient } from '../browser/remoteAgentHostProtocolClient.js';
import { agentsWindowAgentHostClientInfo } from '../common/agentHostClientInfo.js';
import {
IWSLRemoteAgentHostService,
WSL_REMOTE_AGENT_HOST_CHANNEL,
Expand Down Expand Up @@ -53,7 +54,7 @@ export class WSLRelayClientFactory implements IWSLRelayClientFactory {
{ logsHome: this._environmentService.logsHome, connectionId, transport: 'wsl' },
) : undefined;
const transport = this._instantiationService.createInstance(WSLRelayTransport, connectionId, mainService, logger);
return this._instantiationService.createInstance(RemoteAgentHostProtocolClient, address, transport, undefined, undefined);
return this._instantiationService.createInstance(RemoteAgentHostProtocolClient, address, transport, undefined, undefined, agentsWindowAgentHostClientInfo);
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/vs/platform/agentHost/node/agentHostGitHubTelemetryRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class AgentHostGitHubTelemetryRouter {
return targetDestinations.has(notification.event.kind);
}

route(notification: GitHubTelemetryNotification, context?: IAgentHostRestrictedTelemetryContext): boolean {
route(notification: GitHubTelemetryNotification, context?: IAgentHostRestrictedTelemetryContext, additionalProperties?: TelemetryProps): boolean {
const { event } = notification;
const eventName = event.kind;
const destinations = targetDestinations.get(eventName);
Expand All @@ -43,9 +43,11 @@ export class AgentHostGitHubTelemetryRouter {
return true;
}

const properties: TelemetryProps = event.model_call_id && event.properties.modelCallId === undefined
? { ...event.properties, modelCallId: event.model_call_id }
: event.properties;
const properties: TelemetryProps = {
...event.properties,
...(event.model_call_id && event.properties.modelCallId === undefined ? { modelCallId: event.model_call_id } : {}),
...additionalProperties,
};
const multiplexedProperties = multiplexProperties(properties);
if ((destinations & TelemetryDestination.EnhancedGH) && context.restrictedTelemetryEnabled) {
this._telemetryService.sendEnhancedGHTelemetryEventForContext(context, eventName, multiplexedProperties, event.metrics);
Expand Down
Loading
Loading