From 79e948d7860a7ca48d209451bc61c40fa39002a0 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega Date: Mon, 24 Aug 2026 17:00:51 -0700 Subject: [PATCH 1/2] Log AHP frames for the Web PubSub relay transport The Web PubSub relay was the only agent host transport with no AHP JSONL logging, and cloud sandbox hosts do not implement `vscode/collectAgentHostDebugLogs`, so the debug-log export cannot gather frames for them either. That left sandbox sessions with no way to see what was actually put on the wire. Frames are recorded gated on `chat.agentHost.ahpJsonlLoggingEnabled`, which is off by default since the transcript carries full frame contents. Outbound frames are logged before chunking so the transcript holds whole AHP messages rather than the relay frames they were split into. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../browser/webPubSubRelayTransport.ts | 17 ++++++++++++++++- .../browser/cloudSandboxAgentHostService.ts | 15 ++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/agentHost/browser/webPubSubRelayTransport.ts b/src/vs/platform/agentHost/browser/webPubSubRelayTransport.ts index 96484945d1e46e..e714782b7bcbfd 100644 --- a/src/vs/platform/agentHost/browser/webPubSubRelayTransport.ts +++ b/src/vs/platform/agentHost/browser/webPubSubRelayTransport.ts @@ -16,6 +16,7 @@ import { Emitter } from '../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js'; import { IntervalTimer, disposableTimeout } from '../../../base/common/async.js'; import { AgentHostClientConnectionKind } from '../common/agentHostTelemetry.js'; +import { AhpJsonlLogger, getAhpLogByteLength } from '../common/ahpJsonlLogger.js'; import type { AhpServerNotification, JsonRpcNotification, JsonRpcRequest, JsonRpcResponse, ProtocolMessage } from '../common/state/sessionProtocol.js'; import type { IClientTransport } from '../common/state/sessionTransport.js'; import { Reassembler } from '../common/webPubSub/chunking.js'; @@ -82,6 +83,12 @@ export interface IWebPubSubRelayTransportOptions { readonly webSocketFactory?: WebSocketFactory; /** Invoked when an inbound frame can't be parsed or framed. */ readonly onProtocolError?: (err: unknown) => void; + /** + * Records every AHP frame to a JSONL transcript when + * `chat.agentHost.ahpJsonlLoggingEnabled` is on. Cloud sandbox hosts do not implement + * `vscode/collectAgentHostDebugLogs`, so this is the only way to see their frames. + */ + readonly ahpLogger?: AhpJsonlLogger; } /** @@ -118,6 +125,9 @@ export class WebPubSubRelayTransport extends Disposable implements IClientTransp constructor(private readonly _options: IWebPubSubRelayTransportOptions) { super(); + if (this._options.ahpLogger) { + this._register(this._options.ahpLogger); + } } get isOpen(): boolean { @@ -257,7 +267,9 @@ export class WebPubSubRelayTransport extends Disposable implements IClientTransp return; } if (result.kind === 'payload') { - this._onMessage.fire(result.payload as ProtocolMessage); + const payload = result.payload as ProtocolMessage; + this._options.ahpLogger?.log(payload, 's2c', getAhpLogByteLength(JSON.stringify(payload))); + this._onMessage.fire(payload); } } @@ -273,6 +285,9 @@ export class WebPubSubRelayTransport extends Disposable implements IClientTransp if (this._closed || !this._ws) { throw new Error('WebPubSubRelayTransport is closed'); } + // Logged before chunking, so the transcript carries whole AHP messages rather than the + // relay frames they were split into. + this._options.ahpLogger?.log(message, 'c2s', getAhpLogByteLength(JSON.stringify(message))); const frames = buildPublish({ group: this._options.toHostGroup, nextAckId: () => ++this._ackId, diff --git a/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts b/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts index 1d83de5e3d6a5c..86f04a6dbcee1b 100644 --- a/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts +++ b/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts @@ -11,7 +11,8 @@ import { IProtocolTransport } from '../../../../../platform/agentHost/common/sta import { RemoteAgentHostProtocolClient } from '../../../../../platform/agentHost/browser/remoteAgentHostProtocolClient.js'; import { editorWindowAgentHostClientInfo } from '../../../../../platform/agentHost/common/agentHostClientInfo.js'; import { WebPubSubRelayTransport } from '../../../../../platform/agentHost/browser/webPubSubRelayTransport.js'; -import { GITHUB_COPILOT_PROTECTED_RESOURCE } from '../../../../../platform/agentHost/common/agentService.js'; +import { AhpJsonlLogger } from '../../../../../platform/agentHost/common/ahpJsonlLogger.js'; +import { GITHUB_COPILOT_PROTECTED_RESOURCE, AgentHostAhpJsonlLoggingSettingId } from '../../../../../platform/agentHost/common/agentService.js'; import { buildWpsUrl, cloudSandboxAddress, @@ -25,6 +26,7 @@ import { import { IRemoteAgentHostService, RemoteAgentHostConnectionStatus, RemoteAgentHostEntryType, RemoteAgentHostsEnabledSettingId } from '../../../../../platform/agentHost/common/remoteAgentHostService.js'; import { PROTOCOL_VERSION } from '../../../../../platform/agentHost/common/state/protocol/version/registry.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { CloudSandboxCredentialRefresher, MAX_WAKING_DELAY_MS, type ICloudSandboxCreds } from './cloudSandboxCredentialRefresh.js'; @@ -57,6 +59,7 @@ export class CloudSandboxAgentHostService extends Disposable implements ICloudSa @ICloudSandboxApiService private readonly _apiService: ICloudSandboxApiService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, @ILogService private readonly _logService: ILogService, ) { super(); @@ -116,11 +119,21 @@ export class CloudSandboxAgentHostService extends Disposable implements ICloudSa // Three per-client relay lanes: publish to `to_host`; receive replies on `to_client` and // unsolicited session state on `broadcast`. `groupValidation` drops inbound frames whose // group name doesn't carry our own client id. + // Every soft-reconnect builds a fresh transport, so each gets its own logger keyed on the + // connection id; disabled by default, since this records full frame contents. + const ahpLoggingEnabled = !!this._configurationService.getValue(AgentHostAhpJsonlLoggingSettingId); const transportFactory = (): IProtocolTransport => new WebPubSubRelayTransport({ url: buildWpsUrl(creds.token), toHostGroup: creds.token.groups.to_host, joinGroups: [creds.token.groups.broadcast, creds.token.groups.to_client], groupValidation: { expected: { cid: creds.token.client_id } }, + ahpLogger: ahpLoggingEnabled + ? this._instantiationService.createInstance(AhpJsonlLogger, { + logsHome: this._environmentService.logsHome, + connectionId: clientToken.client_id, + transport: 'webpubsub', + }) + : undefined, }); // Mission Control mints the client id and binds the relay lane to it, so the AHP identity From 75929adb25870891f2b1bea4ae493ec8a2778d60 Mon Sep 17 00:00:00 2001 From: Osvaldo Ortega <48293249+osortega@users.noreply.github.com> Date: Mon, 24 Aug 2026 17:45:56 -0700 Subject: [PATCH 2/2] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../remoteAgentHost/browser/cloudSandboxAgentHostService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts b/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts index 86f04a6dbcee1b..93da4db8813514 100644 --- a/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts +++ b/src/vs/sessions/contrib/providers/remoteAgentHost/browser/cloudSandboxAgentHostService.ts @@ -119,8 +119,7 @@ export class CloudSandboxAgentHostService extends Disposable implements ICloudSa // Three per-client relay lanes: publish to `to_host`; receive replies on `to_client` and // unsolicited session state on `broadcast`. `groupValidation` drops inbound frames whose // group name doesn't carry our own client id. - // Every soft-reconnect builds a fresh transport, so each gets its own logger keyed on the - // connection id; disabled by default, since this records full frame contents. + // Each soft reconnect gets a transport-owned logger keyed by connection id. const ahpLoggingEnabled = !!this._configurationService.getValue(AgentHostAhpJsonlLoggingSettingId); const transportFactory = (): IProtocolTransport => new WebPubSubRelayTransport({ url: buildWpsUrl(creds.token),