mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-18 07:47:23 +01:00
Remove outdated docs/instructions for remote agent host (#304341)
This commit is contained in:
@@ -1,35 +0,0 @@
|
||||
---
|
||||
description: Architecture documentation for remote agent host connections. Use when working in `src/vs/sessions/contrib/remoteAgentHost`
|
||||
applyTo: src/vs/sessions/contrib/remoteAgentHost/**
|
||||
---
|
||||
|
||||
# Remote Agent Host
|
||||
|
||||
The remote agent host feature connects the sessions app to agent host processes running on other machines over WebSocket.
|
||||
|
||||
## Key Files
|
||||
|
||||
- `ARCHITECTURE.md` - full architecture documentation (URI conventions, registration flow, data flow diagram)
|
||||
- `REMOTE_AGENT_HOST_RECONNECTION.md` - reconnection lifecycle spec (15 numbered requirements)
|
||||
- `browser/remoteAgentHost.contribution.ts` - central orchestrator
|
||||
- `browser/agentHostFileSystemProvider.ts` - read-only FS provider for remote browsing
|
||||
|
||||
## Architecture Documentation
|
||||
|
||||
When making changes to this feature area, **review and update `ARCHITECTURE.md`** if your changes affect:
|
||||
|
||||
- Connection lifecycle (connect, disconnect, reconnect)
|
||||
- Agent registration flow
|
||||
- URI conventions or naming
|
||||
- Session creation flow
|
||||
- The data flow diagram
|
||||
|
||||
The doc lives at `src/vs/sessions/contrib/remoteAgentHost/ARCHITECTURE.md`.
|
||||
|
||||
## Related Code Outside This Folder
|
||||
|
||||
- `src/vs/platform/agentHost/common/remoteAgentHostService.ts` - service interface (`IRemoteAgentHostService`)
|
||||
- `src/vs/platform/agentHost/electron-browser/remoteAgentHostServiceImpl.ts` - Electron implementation
|
||||
- `src/vs/platform/agentHost/electron-browser/remoteAgentHostProtocolClient.ts` - WebSocket protocol client
|
||||
- `src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionListController.ts` - session list sidebar
|
||||
- `src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts` - session content provider
|
||||
@@ -1,762 +0,0 @@
|
||||
# Remote Agent Host - Architecture Reference
|
||||
|
||||
This file describes the key types in the remote agent host system, from the
|
||||
agent host process itself up through the sessions app integration layer.
|
||||
|
||||
The system has four layers:
|
||||
|
||||
1. **Agent host process** (`platform/agentHost/node/`)
|
||||
The utility process that hosts agent backends (e.g. Copilot SDK).
|
||||
Owns the authoritative state tree and dispatches to IAgent providers.
|
||||
|
||||
2. **Platform services** (`platform/agentHost/common/`, `electron-browser/`)
|
||||
Service interfaces and IPC plumbing that expose the agent host to the
|
||||
renderer. Local connections use MessagePort; remote ones use WebSocket.
|
||||
|
||||
3. **Workbench contributions** (`workbench/contrib/chat/browser/agentSessions/agentHost/`)
|
||||
Shared UI adapters that bridge the agent host protocol with the chat UI:
|
||||
session handlers, session list controllers, language model providers, and
|
||||
state-to-progress adapters. Used by both local and remote agent hosts.
|
||||
|
||||
4. **Sessions app orchestrator** (`sessions/contrib/remoteAgentHost/`)
|
||||
The contribution that discovers remote agent hosts, dynamically registers
|
||||
them as chat session types, and provides the remote filesystem provider.
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ Sessions App (Layer 4) │
|
||||
│ RemoteAgentHostContribution │
|
||||
│ per-connection → SessionClientState + agent registrations │
|
||||
│ AgentHostFileSystemProvider (agenthost:// scheme) │
|
||||
├──────────────────────────────────────┬────────────────────────────────────┤
|
||||
│ Workbench Contributions (3) │ Workbench Contributions (3) │
|
||||
│ AgentHostContribution (local) │ (shared adapters) │
|
||||
│ SessionClientState │ AgentHostSessionHandler │
|
||||
│ per-agent registrations │ AgentHostSessionListController │
|
||||
│ │ AgentHostLanguageModelProvider │
|
||||
├──────────────────────────────────────┴────────────────────────────────────┤
|
||||
│ Platform Services (Layer 2) │
|
||||
│ IAgentHostService (local, MessagePort) │
|
||||
│ IRemoteAgentHostService (remote, WebSocket) │
|
||||
│ └─ both implement IAgentConnection │
|
||||
├───────────────────────────────────────────────────────────────────────────┤
|
||||
│ Agent Host Process (Layer 1) │
|
||||
│ AgentService → SessionStateManager → IAgent (Copilot SDK) │
|
||||
│ ProtocolServerHandler (WebSocket protocol bridge) │
|
||||
│ AgentSideEffects (action dispatch + progress event routing) │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
```typescript
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 1: Agent Host Process (platform/agentHost/node/)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Implemented by each agent backend (e.g. the Copilot SDK wrapper).
|
||||
* The agent host process can host multiple providers, though currently
|
||||
* only `copilot` is supported.
|
||||
*
|
||||
* Registered with {@link AgentService.registerProvider}. Provider progress
|
||||
* events are wired to the state manager through {@link AgentSideEffects}.
|
||||
*
|
||||
* File: `platform/agentHost/common/agentService.ts`
|
||||
*/
|
||||
interface IAgent {
|
||||
/** Unique provider identifier (e.g. `'copilot'`). */
|
||||
readonly id: AgentProvider;
|
||||
/** Fires when the provider streams progress for a session. */
|
||||
readonly onDidSessionProgress: Event<IAgentProgressEvent>;
|
||||
createSession(config?: IAgentCreateSessionConfig): Promise<URI>;
|
||||
sendMessage(session: URI, prompt: string, attachments?: IAgentAttachment[]): Promise<void>;
|
||||
getSessionMessages(session: URI): Promise<IAgentProgressEvent[]>;
|
||||
disposeSession(session: URI): Promise<void>;
|
||||
abortSession(session: URI): Promise<void>;
|
||||
changeModel(session: URI, model: string): Promise<void>;
|
||||
respondToPermissionRequest(requestId: string, approved: boolean): void;
|
||||
getDescriptor(): IAgentDescriptor;
|
||||
listModels(): Promise<IAgentModelInfo[]>;
|
||||
listSessions(): Promise<IAgentSessionMetadata[]>;
|
||||
getProtectedResources(): IAuthorizationProtectedResourceMetadata[];
|
||||
authenticate(resource: string, token: string): Promise<boolean>;
|
||||
shutdown(): Promise<void>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The agent service implementation that runs inside the agent host utility
|
||||
* process. Dispatches to registered {@link IAgent} providers based on the
|
||||
* provider identifier in the session URI scheme.
|
||||
*
|
||||
* Owns the {@link SessionStateManager} (authoritative state tree) and
|
||||
* {@link AgentSideEffects} (action routing + progress event mapping).
|
||||
*
|
||||
* When `VSCODE_AGENT_HOST_PORT` is set, the process also starts a
|
||||
* {@link ProtocolServerHandler} over WebSocket for external clients.
|
||||
*
|
||||
* File: `platform/agentHost/node/agentService.ts`
|
||||
*/
|
||||
interface AgentService extends IAgentService {
|
||||
/** Exposes the state manager for co-hosting a WebSocket protocol server. */
|
||||
readonly stateManager: SessionStateManager;
|
||||
/** Register a new agent backend provider. */
|
||||
registerProvider(provider: IAgent): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side authoritative state manager for the sessions process protocol.
|
||||
*
|
||||
* Maintains the root state (agent list + active session count) and per-session
|
||||
* state trees. Applies actions through pure reducers, assigns monotonic
|
||||
* sequence numbers, and emits {@link IActionEnvelope}s for subscribed clients.
|
||||
*
|
||||
* Consumed by both the IPC proxy (for local clients) and
|
||||
* {@link ProtocolServerHandler} (for WebSocket clients). Both paths share
|
||||
* the same state, so local and remote clients see identical state.
|
||||
*
|
||||
* File: `platform/agentHost/node/sessionStateManager.ts`
|
||||
*/
|
||||
interface SessionStateManager {
|
||||
readonly rootState: IRootState;
|
||||
readonly serverSeq: number;
|
||||
readonly onDidEmitEnvelope: Event<IActionEnvelope>;
|
||||
readonly onDidEmitNotification: Event<INotification>;
|
||||
getSessionState(session: string): ISessionState | undefined;
|
||||
getSnapshot(resource: string): IStateSnapshot | undefined;
|
||||
createSession(summary: ISessionSummary): ISessionState;
|
||||
removeSession(session: string): void;
|
||||
applyAction(action: IStateAction, origin: IActionOrigin): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared side-effect handler that routes client-dispatched actions to the
|
||||
* correct {@link IAgent} backend, handles session create/dispose/list
|
||||
* operations, and wires agent progress events to the state manager.
|
||||
*
|
||||
* Also implements {@link IProtocolSideEffectHandler} so the WebSocket
|
||||
* {@link ProtocolServerHandler} can delegate side effects to the same logic.
|
||||
*
|
||||
* File: `platform/agentHost/node/agentSideEffects.ts`
|
||||
*/
|
||||
interface AgentSideEffects extends IProtocolSideEffectHandler {
|
||||
/** Connects an IAgent's progress events to the state manager. */
|
||||
registerProgressListener(provider: IAgent): IDisposable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side protocol handler for WebSocket clients. Routes JSON-RPC
|
||||
* messages to the {@link SessionStateManager}, manages client subscriptions,
|
||||
* and broadcasts action envelopes to subscribed clients.
|
||||
*
|
||||
* Handles the initialize/reconnect handshake, subscribe/unsubscribe,
|
||||
* dispatchAction, createSession, disposeSession, and browseDirectory commands.
|
||||
*
|
||||
* Exposes {@link onDidChangeConnectionCount} so the server process can
|
||||
* track how many external clients are connected (used by
|
||||
* {@link ServerAgentHostManager} for lifetime management).
|
||||
*
|
||||
* File: `platform/agentHost/node/protocolServerHandler.ts`
|
||||
*/
|
||||
interface ProtocolServerHandler {
|
||||
/** Fires with the current client count when a client connects or disconnects. */
|
||||
readonly onDidChangeConnectionCount: Event<number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Side-effect handler interface for protocol commands that require
|
||||
* business logic beyond pure state management. Implemented by
|
||||
* {@link AgentSideEffects} and consumed by {@link ProtocolServerHandler}.
|
||||
*
|
||||
* File: `platform/agentHost/node/protocolServerHandler.ts`
|
||||
*/
|
||||
interface IProtocolSideEffectHandler {
|
||||
handleAction(action: ISessionAction): void;
|
||||
handleCreateSession(command: ICreateSessionParams): Promise<void>;
|
||||
handleDisposeSession(session: string): void;
|
||||
handleListSessions(): Promise<ISessionSummary[]>;
|
||||
handleGetResourceMetadata(): IResourceMetadata;
|
||||
handleAuthenticate(params: IAuthenticateParams): Promise<IAuthenticateResult>;
|
||||
handleBrowseDirectory(uri: string): Promise<IBrowseDirectoryResult>;
|
||||
getDefaultDirectory(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main-process service that manages the agent host utility process lifecycle:
|
||||
* lazy start on first connection request, crash recovery (up to 5 restarts),
|
||||
* and logger channel forwarding.
|
||||
*
|
||||
* The renderer communicates with the utility process directly via MessagePort;
|
||||
* this class does not relay any agent service calls.
|
||||
*
|
||||
* File: `platform/agentHost/node/agentHostService.ts`
|
||||
*/
|
||||
interface AgentHostProcessManager {
|
||||
// Internal lifecycle management - start, restart, logger forwarding.
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-specific agent host manager. Eagerly starts the agent host process,
|
||||
* handles crash recovery, and tracks both active agent sessions and connected
|
||||
* WebSocket clients via {@link IServerLifetimeService} to keep the server
|
||||
* alive while either signal is active.
|
||||
*
|
||||
* The lifetime token is held when:
|
||||
* - there are active agent sessions (turns in progress), OR
|
||||
* - there are WebSocket clients connected to the agent host
|
||||
*
|
||||
* The token is released (allowing server auto-shutdown) only when both
|
||||
* active sessions = 0 AND connected clients = 0.
|
||||
*
|
||||
* Session count comes from `root/activeSessionsChanged` actions via
|
||||
* {@link IAgentService.onDidAction}. Client connection count comes from
|
||||
* a separate IPC channel ({@link AgentHostIpcChannels.ConnectionTracker})
|
||||
* that is not part of the agent host protocol -- it is a server-only
|
||||
* process-management concern.
|
||||
*
|
||||
* File: `server/node/serverAgentHostManager.ts`
|
||||
*/
|
||||
interface ServerAgentHostManager {
|
||||
// Tracks _hasActiveSessions + _connectionCount, updates lifetime token
|
||||
// when either changes.
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstracts the utility process creation so the same lifecycle management
|
||||
* works for both Electron utility processes and Node child processes.
|
||||
*
|
||||
* File: `platform/agentHost/common/agent.ts`
|
||||
*/
|
||||
interface IAgentHostStarter extends IDisposable {
|
||||
readonly onRequestConnection?: Event<void>;
|
||||
readonly onWillShutdown?: Event<void>;
|
||||
/** Creates the agent host process and connects to it. */
|
||||
start(): IAgentHostConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* The connection returned by {@link IAgentHostStarter.start}. Provides
|
||||
* an IPC channel client and process exit events.
|
||||
*
|
||||
* File: `platform/agentHost/common/agent.ts`
|
||||
*/
|
||||
interface IAgentHostConnection {
|
||||
readonly client: IChannelClient;
|
||||
readonly store: DisposableStore;
|
||||
readonly onDidProcessExit: Event<{ code: number; signal: string }>;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 2: Platform Services (platform/agentHost/common/ & electron-browser/)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Core protocol surface for communicating with an agent host. Methods are
|
||||
* proxied across MessagePort (local) or implemented over WebSocket (remote).
|
||||
*
|
||||
* State synchronization uses the subscribe/unsubscribe/dispatchAction pattern.
|
||||
* Clients observe root state (discovered agents, models) and session state
|
||||
* via subscriptions, and mutate state by dispatching actions (e.g.
|
||||
* `session/turnStarted`, `session/turnCancelled`).
|
||||
*
|
||||
* File: `platform/agentHost/common/agentService.ts`
|
||||
*/
|
||||
interface IAgentService {
|
||||
listAgents(): Promise<IAgentDescriptor[]>;
|
||||
getResourceMetadata(): Promise<IResourceMetadata>;
|
||||
authenticate(params: IAuthenticateParams): Promise<IAuthenticateResult>;
|
||||
refreshModels(): Promise<void>;
|
||||
listSessions(): Promise<IAgentSessionMetadata[]>;
|
||||
createSession(config?: IAgentCreateSessionConfig): Promise<URI>;
|
||||
disposeSession(session: URI): Promise<void>;
|
||||
shutdown(): Promise<void>;
|
||||
|
||||
// ---- Protocol methods ----
|
||||
subscribe(resource: URI): Promise<IStateSnapshot>;
|
||||
unsubscribe(resource: URI): void;
|
||||
readonly onDidAction: Event<IActionEnvelope>;
|
||||
readonly onDidNotification: Event<INotification>;
|
||||
dispatchAction(action: ISessionAction, clientId: string, clientSeq: number): void;
|
||||
browseDirectory(uri: URI): Promise<IBrowseDirectoryResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A concrete connection to an agent host - local utility process or remote
|
||||
* WebSocket. Extends {@link IAgentService} with a `clientId` used for
|
||||
* write-ahead reconciliation of optimistic actions.
|
||||
*
|
||||
* Both {@link IAgentHostService} (local) and per-connection objects from
|
||||
* {@link IRemoteAgentHostService} (remote) satisfy this contract. The
|
||||
* workbench contributions ({@link AgentHostSessionHandler}, etc.) program
|
||||
* against this single interface.
|
||||
*
|
||||
* File: `platform/agentHost/common/agentService.ts`
|
||||
*/
|
||||
interface IAgentConnection extends IAgentService {
|
||||
/** Unique client identifier, used as origin in action envelopes. */
|
||||
readonly clientId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The local agent host service - wraps the utility process connection and
|
||||
* provides lifecycle events. The renderer talks to the utility process
|
||||
* directly via MessagePort using ProxyChannel.
|
||||
*
|
||||
* Registered as a singleton service. Also implements {@link IAgentConnection}
|
||||
* so it can be used interchangeably with remote connections.
|
||||
*
|
||||
* File: `platform/agentHost/common/agentService.ts` (interface)
|
||||
* File: `platform/agentHost/electron-browser/agentHostService.ts` (implementation)
|
||||
*/
|
||||
interface IAgentHostService extends IAgentConnection {
|
||||
readonly onAgentHostExit: Event<number>;
|
||||
readonly onAgentHostStart: Event<void>;
|
||||
restartAgentHost(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages connections to one or more remote agent host processes over
|
||||
* WebSocket. Each connection is identified by its address string (from the
|
||||
* `chat.remoteAgentHosts` setting) and exposed as an {@link IAgentConnection}.
|
||||
*
|
||||
* The implementation reads the setting, creates a
|
||||
* {@link RemoteAgentHostProtocolClient} per address, reconnects when the
|
||||
* setting changes, and fires `onDidChangeConnections` when connections are
|
||||
* established or lost.
|
||||
*
|
||||
* File: `platform/agentHost/common/remoteAgentHostService.ts` (interface)
|
||||
* File: `platform/agentHost/electron-browser/remoteAgentHostServiceImpl.ts` (implementation)
|
||||
*/
|
||||
interface IRemoteAgentHostService {
|
||||
readonly onDidChangeConnections: Event<void>;
|
||||
readonly connections: readonly IRemoteAgentHostConnectionInfo[];
|
||||
getConnection(address: string): IAgentConnection | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata about a single remote connection - address, friendly name,
|
||||
* client ID from the handshake, and the remote machine's home directory.
|
||||
*
|
||||
* File: `platform/agentHost/common/remoteAgentHostService.ts`
|
||||
*/
|
||||
interface IRemoteAgentHostConnectionInfo {
|
||||
readonly address: string;
|
||||
readonly name: string;
|
||||
readonly clientId: string;
|
||||
readonly defaultDirectory?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in the `chat.remoteAgentHosts` setting.
|
||||
*
|
||||
* File: `platform/agentHost/common/remoteAgentHostService.ts`
|
||||
*/
|
||||
interface IRemoteAgentHostEntry {
|
||||
readonly address: string;
|
||||
readonly name: string;
|
||||
readonly connectionToken?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A protocol-level client for a single remote agent host connection.
|
||||
* Manages the WebSocket transport, handshake (initialize command with
|
||||
* protocol version exchange), subscriptions, action dispatch, and
|
||||
* JSON-RPC request/response correlation.
|
||||
*
|
||||
* Implements {@link IAgentConnection} so consumers can program against
|
||||
* a single interface regardless of whether the agent host is local or remote.
|
||||
*
|
||||
* File: `platform/agentHost/electron-browser/remoteAgentHostProtocolClient.ts`
|
||||
*/
|
||||
interface RemoteAgentHostProtocolClient extends IAgentConnection {
|
||||
readonly defaultDirectory: string | undefined;
|
||||
readonly onDidClose: Event<void>;
|
||||
connect(): Promise<void>;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 2: State Protocol Types (platform/agentHost/common/state/)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Root state: the top-level state tree subscribed to at `agenthost:/root`.
|
||||
* Contains the list of discovered agent backends and active session count.
|
||||
* Mutated by `root/agentsChanged` and `root/activeSessionsChanged` actions.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionState.ts` (re-exported from protocol)
|
||||
*/
|
||||
interface IRootState {
|
||||
readonly agents: readonly IAgentInfo[];
|
||||
readonly activeSessions: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes an agent backend discovered via root state subscription.
|
||||
* Each agent exposes a provider name, display metadata, and available models.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionState.ts` (re-exported from protocol)
|
||||
*/
|
||||
interface IAgentInfo {
|
||||
readonly provider: string;
|
||||
readonly displayName: string;
|
||||
readonly description: string;
|
||||
readonly models: readonly ISessionModelInfo[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-session state tree. Contains the session summary, lifecycle, completed
|
||||
* turns, active turn (if any), and server tools. Mutated by session actions
|
||||
* like `session/turnStarted`, `session/delta`, `session/toolCallStart`, etc.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionState.ts` (re-exported from protocol)
|
||||
*/
|
||||
interface ISessionState {
|
||||
readonly summary: ISessionSummary;
|
||||
readonly lifecycle: SessionLifecycle;
|
||||
readonly turns: readonly ITurn[];
|
||||
readonly activeTurn: IActiveTurn | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* An envelope wrapping a state action with origin metadata and a monotonic
|
||||
* server sequence number. Clients use the origin to distinguish their own
|
||||
* echoed actions from concurrent actions from other clients/the server.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionActions.ts` (re-exported from protocol)
|
||||
*/
|
||||
interface IActionEnvelope {
|
||||
readonly action: IStateAction;
|
||||
readonly origin: IActionOrigin;
|
||||
readonly serverSeq: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A state snapshot returned by the subscribe command. Contains the current
|
||||
* state at the given resource URI and the server sequence number at
|
||||
* snapshot time. The client should process subsequent envelopes with
|
||||
* `serverSeq > fromSeq`.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionProtocol.ts` (re-exported from protocol)
|
||||
*/
|
||||
interface IStateSnapshot {
|
||||
readonly resource: string;
|
||||
readonly state: IRootState | ISessionState;
|
||||
readonly fromSeq: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client-side state manager with write-ahead reconciliation.
|
||||
*
|
||||
* Maintains confirmed state (last server-acknowledged), a pending action
|
||||
* queue (optimistically applied), and reconciles when the server echoes
|
||||
* actions back (possibly interleaved with actions from other sources).
|
||||
* Operates on two kinds of subscribable state:
|
||||
* - Root state (agents + models) - server-only mutations, no write-ahead.
|
||||
* - Session state - mixed: client-sendable actions get write-ahead,
|
||||
* server-only actions are applied directly.
|
||||
*
|
||||
* Usage:
|
||||
* 1. `handleSnapshot()` - apply initial state from subscribe response.
|
||||
* 2. `applyOptimistic()` - optimistically apply a client action.
|
||||
* 3. `receiveEnvelope()` - process a server action envelope.
|
||||
* 4. `receiveNotification()` - process an ephemeral notification.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionClientState.ts`
|
||||
*/
|
||||
interface SessionClientState {
|
||||
readonly clientId: string;
|
||||
readonly rootState: IRootState | undefined;
|
||||
readonly onDidChangeRootState: Event<IRootState>;
|
||||
readonly onDidChangeSessionState: Event<{ session: string; state: ISessionState }>;
|
||||
readonly onDidReceiveNotification: Event<INotification>;
|
||||
getSessionState(session: string): ISessionState | undefined;
|
||||
handleSnapshot(resource: string, state: IRootState | ISessionState, fromSeq: number): void;
|
||||
applyOptimistic(action: ISessionAction): number;
|
||||
receiveEnvelope(envelope: IActionEnvelope): void;
|
||||
receiveNotification(notification: INotification): void;
|
||||
unsubscribe(resource: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A bidirectional transport for protocol messages (JSON-RPC 2.0 framing).
|
||||
* Implementations handle serialization, framing, and connection management.
|
||||
* Concrete implementations: MessagePort (ProxyChannel), WebSocket, stdio.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionTransport.ts`
|
||||
*/
|
||||
interface IProtocolTransport extends IDisposable {
|
||||
readonly onMessage: Event<IProtocolMessage>;
|
||||
readonly onClose: Event<void>;
|
||||
send(message: IProtocolMessage): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server-side transport that accepts multiple client connections.
|
||||
* Each connected client gets its own {@link IProtocolTransport}.
|
||||
*
|
||||
* File: `platform/agentHost/common/state/sessionTransport.ts`
|
||||
*/
|
||||
interface IProtocolServer extends IDisposable {
|
||||
readonly onConnection: Event<IProtocolTransport>;
|
||||
readonly address: string | undefined;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 3: Workbench Contributions (workbench/contrib/chat/browser/agentSessions/agentHost/)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Renderer-side handler for a single agent host chat session type.
|
||||
* Bridges the protocol state layer with the chat UI:
|
||||
*
|
||||
* - Subscribes to session state via {@link IAgentConnection}
|
||||
* - Derives `IChatProgress[]` from immutable state changes in
|
||||
* {@link SessionClientState}
|
||||
* - Dispatches client actions (`turnStarted`, `permissionResolved`,
|
||||
* `turnCancelled`) back to the server
|
||||
* - Registers a dynamic chat agent via {@link IChatAgentService}
|
||||
*
|
||||
* Works with both local and remote connections via the {@link IAgentConnection}
|
||||
* interface passed in the config.
|
||||
*
|
||||
* File: `workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts`
|
||||
*/
|
||||
interface AgentHostSessionHandler extends IChatSessionContentProvider {
|
||||
provideChatSessionContent(sessionResource: URI, token: CancellationToken): Promise<IChatSession>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for an {@link AgentHostSessionHandler} instance.
|
||||
* Contains the agent identity, displayName, the connection to use,
|
||||
* and optional callbacks for resolving working directories and
|
||||
* interactive authentication.
|
||||
*
|
||||
* File: `workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts`
|
||||
*/
|
||||
interface IAgentHostSessionHandlerConfig {
|
||||
readonly provider: AgentProvider;
|
||||
readonly agentId: string;
|
||||
readonly sessionType: string;
|
||||
readonly fullName: string;
|
||||
readonly description: string;
|
||||
readonly connection: IAgentConnection;
|
||||
readonly extensionId?: string;
|
||||
readonly extensionDisplayName?: string;
|
||||
/** Resolve a working directory for a new session (e.g. from active session's repository URI). */
|
||||
readonly resolveWorkingDirectory?: (resourceKey: string) => string | undefined;
|
||||
/** Trigger interactive authentication when the server rejects with auth-required. */
|
||||
readonly resolveAuthentication?: () => Promise<boolean>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides session list items for the chat sessions sidebar by querying
|
||||
* active sessions from an agent host connection. Listens to protocol
|
||||
* notifications (`notify/sessionAdded`, `notify/sessionRemoved`) for
|
||||
* incremental updates, and refreshes on `session/turnComplete` actions.
|
||||
*
|
||||
* Works with both local and remote agent host connections via
|
||||
* {@link IAgentConnection}.
|
||||
*
|
||||
* File: `workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionListController.ts`
|
||||
*/
|
||||
interface AgentHostSessionListController extends IChatSessionItemController {
|
||||
readonly items: readonly IChatSessionItem[];
|
||||
readonly onDidChangeChatSessionItems: Event<IChatSessionItemsDelta>;
|
||||
refresh(token: CancellationToken): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes models available from the agent host process as selectable
|
||||
* language models in the chat model picker. Models come from root state
|
||||
* (via {@link IAgentInfo.models}) and are published with IDs prefixed
|
||||
* by the session type (e.g. `remote-localhost__8081-copilot:claude-sonnet-4-20250514`).
|
||||
*
|
||||
* File: `workbench/contrib/chat/browser/agentSessions/agentHost/agentHostLanguageModelProvider.ts`
|
||||
*/
|
||||
interface AgentHostLanguageModelProvider extends ILanguageModelChatProvider {
|
||||
/** Called when models change in root state to push updates to the model picker. */
|
||||
updateModels(models: readonly ISessionModelInfo[]): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The local agent host contribution (for the workbench, not the sessions app).
|
||||
* Discovers agents from the local agent host process and registers each one
|
||||
* as a chat session type. Gated on the `chat.agentHost.enabled` setting.
|
||||
*
|
||||
* Uses the same shared adapters ({@link AgentHostSessionHandler}, etc.)
|
||||
* but connects via {@link IAgentHostService} (MessagePort) instead of
|
||||
* {@link IRemoteAgentHostService} (WebSocket).
|
||||
*
|
||||
* File: `workbench/contrib/chat/browser/agentSessions/agentHost/agentHostChatContribution.ts`
|
||||
*/
|
||||
interface AgentHostContribution extends IWorkbenchContribution {
|
||||
// Registers per-agent: chat session contribution, session list controller,
|
||||
// session handler, and language model provider - same 4 registrations
|
||||
// as RemoteAgentHostContribution but for the local agent host.
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LAYER 4: Sessions App Orchestrator (sessions/contrib/remoteAgentHost/)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Central orchestrator for remote agent hosts in the sessions app.
|
||||
*
|
||||
* For each active remote connection:
|
||||
* 1. Creates a {@link SessionClientState} for write-ahead reconciliation
|
||||
* 2. Subscribes to `agenthost:/root` to discover available agents
|
||||
* 3. For each discovered copilot agent, performs four registrations:
|
||||
* - Chat session contribution (via {@link IChatSessionsService})
|
||||
* - Session list controller ({@link AgentHostSessionListController})
|
||||
* - Session content provider ({@link AgentHostSessionHandler})
|
||||
* - Language model provider ({@link AgentHostLanguageModelProvider})
|
||||
* 4. Registers authority→address mappings for the filesystem provider
|
||||
* 5. Authenticates connections using RFC 9728 resource metadata
|
||||
*
|
||||
* Reconciles when connections change (added/removed/name changed)
|
||||
* and when the default auth account or auth sessions change.
|
||||
*
|
||||
* File: `sessions/contrib/remoteAgentHost/browser/remoteAgentHost.contribution.ts`
|
||||
*/
|
||||
interface RemoteAgentHostContribution extends IWorkbenchContribution {
|
||||
// Per-connection state tracked in a DisposableMap<address, ConnectionState>
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-only {@link IFileSystemProvider} registered under the `agenthost`
|
||||
* scheme. Proxies `stat` and `readdir` calls through the agent host
|
||||
* protocol's `browseDirectory` RPC.
|
||||
*
|
||||
* The URI authority identifies the remote connection (sanitized address),
|
||||
* the URI path is the remote filesystem path. Authority-to-address mappings
|
||||
* are registered by {@link RemoteAgentHostContribution} via
|
||||
* `registerAuthority(authority, address)`.
|
||||
*
|
||||
* File: `sessions/contrib/remoteAgentHost/browser/agentHostFileSystemProvider.ts`
|
||||
*/
|
||||
interface AgentHostFileSystemProvider extends IFileSystemProvider {
|
||||
/** Register a mapping from sanitized URI authority to remote address. */
|
||||
registerAuthority(authority: string, address: string): IDisposable;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Naming Conventions & URI Schemes
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Remote addresses are encoded into URI-safe authority strings:
|
||||
* - `localhost:8081` → `localhost__8081`
|
||||
* - `http://127.0.0.1:3000` → `b64-aHR0cDovLzEyNy4wLjAuMTozMDAw`
|
||||
*
|
||||
* | Context | Scheme | Example |
|
||||
* |--------------------------|------------------|-------------------------------------------------|
|
||||
* | Session resource (UI) | `<sessionType>` | `remote-localhost__8081-copilot:/untitled-abc` |
|
||||
* | Backend session (server) | `<provider>` | `copilot:/abc-123` |
|
||||
* | Root state subscription | (string literal) | `agenthost:/root` |
|
||||
* | Remote filesystem | `agenthost` | `agenthost://localhost__8081/home/user/project` |
|
||||
* | Language model ID | - | `remote-localhost__8081-copilot:claude-sonnet-4-20250514` |
|
||||
*
|
||||
* Session type naming: `remote-${authority}-${provider}` for remote,
|
||||
* `agent-host-${provider}` for local.
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// IPC & Auth Data Types (platform/agentHost/common/agentService.ts)
|
||||
// =============================================================================
|
||||
|
||||
/** Metadata describing an agent backend, discovered over IPC or root state. */
|
||||
interface IAgentDescriptor {
|
||||
readonly provider: AgentProvider;
|
||||
readonly displayName: string;
|
||||
readonly description: string;
|
||||
/** @deprecated Use IResourceMetadata from getResourceMetadata() instead. */
|
||||
readonly requiresAuth: boolean;
|
||||
}
|
||||
|
||||
/** Serializable model information from the agent host. */
|
||||
interface IAgentModelInfo {
|
||||
readonly provider: AgentProvider;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly maxContextWindow: number;
|
||||
readonly supportsVision: boolean;
|
||||
readonly supportsReasoningEffort: boolean;
|
||||
}
|
||||
|
||||
/** Configuration for creating a new session. */
|
||||
interface IAgentCreateSessionConfig {
|
||||
readonly provider?: AgentProvider;
|
||||
readonly model?: string;
|
||||
readonly session?: URI;
|
||||
readonly workingDirectory?: string;
|
||||
}
|
||||
|
||||
/** Metadata for an existing session (returned by listSessions). */
|
||||
interface IAgentSessionMetadata {
|
||||
readonly session: URI;
|
||||
readonly startTime: number;
|
||||
readonly modifiedTime: number;
|
||||
readonly summary?: string;
|
||||
readonly workingDirectory?: string;
|
||||
}
|
||||
|
||||
/** Serializable attachment passed alongside a message to the agent host. */
|
||||
interface IAgentAttachment {
|
||||
readonly type: AttachmentType;
|
||||
readonly path: string;
|
||||
readonly displayName?: string;
|
||||
readonly text?: string;
|
||||
readonly selection?: {
|
||||
readonly start: { readonly line: number; readonly character: number };
|
||||
readonly end: { readonly line: number; readonly character: number };
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the agent host as an OAuth 2.0 protected resource (RFC 9728).
|
||||
* Clients resolve tokens via the VS Code authentication service.
|
||||
*/
|
||||
interface IResourceMetadata {
|
||||
readonly resources: readonly IAuthorizationProtectedResourceMetadata[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for the `authenticate` command (RFC 6750 bearer token delivery).
|
||||
*/
|
||||
interface IAuthenticateParams {
|
||||
readonly resource: string;
|
||||
readonly token: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of the `authenticate` command.
|
||||
*/
|
||||
interface IAuthenticateResult {
|
||||
readonly authenticated: boolean;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Progress Events (platform/agentHost/common/agentService.ts)
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* Discriminated union of progress events streamed from the agent host.
|
||||
* The state-to-progress adapter ({@link stateToProgressAdapter.ts})
|
||||
* translates protocol state changes into `IChatProgress[]` for the chat UI,
|
||||
* but these events are also used in the IPC path for the old event-based API.
|
||||
*
|
||||
* Types: `delta`, `message`, `idle`, `tool_start`, `tool_complete`,
|
||||
* `title_changed`, `error`, `usage`, `permission_request`, `reasoning`.
|
||||
*/
|
||||
type IAgentProgressEvent =
|
||||
| IAgentDeltaEvent
|
||||
| IAgentMessageEvent
|
||||
| IAgentIdleEvent
|
||||
| IAgentToolStartEvent
|
||||
| IAgentToolCompleteEvent
|
||||
| IAgentTitleChangedEvent
|
||||
| IAgentErrorEvent
|
||||
| IAgentUsageEvent
|
||||
| IAgentPermissionRequestEvent
|
||||
| IAgentReasoningEvent;
|
||||
```
|
||||
@@ -1,328 +0,0 @@
|
||||
# Remote Agent Host Chat Agents - Architecture
|
||||
|
||||
This document describes how remote agent host chat agents are registered, how
|
||||
sessions are created, and the URI/target conventions used throughout the system.
|
||||
|
||||
## Overview
|
||||
|
||||
A **remote agent host** is a VS Code agent host process running on another
|
||||
machine, connected over WebSocket. The user configures remote addresses in the
|
||||
`chat.remoteAgentHosts` setting. Each remote host may expose one or more agent
|
||||
backends (currently only the `copilot` provider is supported). The system
|
||||
discovers these agents, dynamically registers them as chat session types, and
|
||||
creates sessions that stream turns via the agent host protocol.
|
||||
|
||||
```
|
||||
┌─────────────┐ WebSocket ┌───────────────────┐
|
||||
│ VS Code │ ◄──────────────► │ Remote Agent Host │
|
||||
│ (client) │ AHP protocol │ (server) │
|
||||
└─────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
## Connection Lifecycle
|
||||
|
||||
### 1. Configuration
|
||||
|
||||
Connections are configured via the `chat.remoteAgentHosts` setting:
|
||||
|
||||
```jsonc
|
||||
"chat.remoteAgentHosts": [
|
||||
{ "address": "http://192.168.1.10:3000", "name": "dev-box", "connectionToken": "..." }
|
||||
]
|
||||
```
|
||||
|
||||
Each entry is an `IRemoteAgentHostEntry` with `address`, `name`, and optional
|
||||
`connectionToken`.
|
||||
|
||||
### 2. Service Layer
|
||||
|
||||
`IRemoteAgentHostService` (`src/vs/platform/agentHost/common/remoteAgentHostService.ts`)
|
||||
manages WebSocket connections. The Electron implementation reads the setting,
|
||||
creates `RemoteAgentHostProtocolClient` instances for each address, and fires
|
||||
`onDidChangeConnections` when connections are established or lost.
|
||||
|
||||
Each connection satisfies the `IAgentConnection` interface (which extends
|
||||
`IAgentService`), providing:
|
||||
|
||||
- `subscribe(resource)` / `unsubscribe(resource)` - state subscriptions
|
||||
- `dispatchAction(action, clientId, seq)` - send client actions
|
||||
- `onDidAction` / `onDidNotification` - receive server events
|
||||
- `createSession(config)` - create a new backend session
|
||||
- `browseDirectory(uri)` - list remote filesystem contents
|
||||
- `clientId` - unique connection identifier for optimistic reconciliation
|
||||
|
||||
### 3. Connection Metadata
|
||||
|
||||
Each active connection exposes `IRemoteAgentHostConnectionInfo`:
|
||||
|
||||
```typescript
|
||||
{
|
||||
address: string; // e.g. "http://192.168.1.10:3000"
|
||||
name: string; // e.g. "dev-box" (from setting)
|
||||
clientId: string; // assigned during handshake
|
||||
defaultDirectory?: string; // home directory on the remote machine
|
||||
}
|
||||
```
|
||||
|
||||
## Agent Discovery
|
||||
|
||||
### Root State Subscription
|
||||
|
||||
`RemoteAgentHostContribution` (`src/vs/sessions/contrib/remoteAgentHost/browser/remoteAgentHost.contribution.ts`)
|
||||
is the central orchestrator. For each connection, it subscribes to `ROOT_STATE_URI`
|
||||
(`agenthost:/root`) to discover available agents.
|
||||
|
||||
The root state (`IRootState`) contains:
|
||||
|
||||
```typescript
|
||||
{
|
||||
agents: IAgentInfo[]; // discovered agent backends
|
||||
activeSessions?: number; // count of active sessions
|
||||
}
|
||||
```
|
||||
|
||||
Each `IAgentInfo` describes an agent:
|
||||
|
||||
```typescript
|
||||
{
|
||||
provider: string; // e.g. "copilot"
|
||||
displayName: string; // e.g. "Copilot"
|
||||
description: string;
|
||||
models: ISessionModelInfo[]; // available language models
|
||||
}
|
||||
```
|
||||
|
||||
### Authority Encoding
|
||||
|
||||
Remote addresses are encoded into URI-safe authority strings via
|
||||
`agentHostAuthority(address)`:
|
||||
|
||||
- Alphanumeric addresses pass through unchanged
|
||||
- "Normal" addresses (`[a-zA-Z0-9.:-]`) get colons replaced with `__`
|
||||
- Everything else is url-safe base64 encoded with a `b64-` prefix
|
||||
|
||||
Examples:
|
||||
- `localhost:8081` → `localhost__8081`
|
||||
- `192.168.1.1:8080` → `192.168.1.1__8080`
|
||||
- `http://127.0.0.1:3000` → `b64-aHR0cDovLzEyNy4wLjAuMTozMDAw`
|
||||
|
||||
## Agent Registration
|
||||
|
||||
When `_registerAgent()` is called for a discovered copilot agent from address `X`:
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
| Concept | Value | Example |
|
||||
|---------|-------|---------|
|
||||
| **Authority** | `agentHostAuthority(address)` | `localhost__8081` |
|
||||
| **Session type** | `remote-${authority}-${provider}` | `remote-localhost__8081-copilot` |
|
||||
| **Agent ID** | same as session type | `remote-localhost__8081-copilot` |
|
||||
| **Vendor** | same as session type | `remote-localhost__8081-copilot` |
|
||||
| **Display name** | `configuredName \|\| "${displayName} (${address})"` | `dev-box` |
|
||||
|
||||
### Four Registrations Per Agent
|
||||
|
||||
1. **Chat session contribution** - via `IChatSessionsService.registerChatSessionContribution()`:
|
||||
```typescript
|
||||
{ type: sessionType, name: agentId, displayName, canDelegate: true, requiresCustomModels: true }
|
||||
```
|
||||
|
||||
2. **Session list controller** - `AgentHostSessionListController` handles the
|
||||
sidebar session list. Lists sessions via `connection.listSessions()`, listens
|
||||
for `notify/sessionAdded` and `notify/sessionRemoved` notifications.
|
||||
|
||||
3. **Session handler** - `AgentHostSessionHandler` implements
|
||||
`IChatSessionContentProvider`, bridging the agent host protocol to chat UI
|
||||
progress events. Also registers a _dynamic chat agent_ via
|
||||
`IChatAgentService.registerDynamicAgent()`.
|
||||
|
||||
4. **Language model provider** - `AgentHostLanguageModelProvider` registers
|
||||
models under the vendor descriptor. Model IDs are prefixed with the session
|
||||
type (e.g., `remote-localhost__8081-copilot:claude-sonnet-4-20250514`).
|
||||
|
||||
## URI Conventions
|
||||
|
||||
| Context | Scheme | Format | Example |
|
||||
|---------|--------|--------|---------|
|
||||
| New session resource | `<sessionType>` | `<sessionType>:/untitled-<uuid>` | `remote-localhost__8081-copilot:/untitled-abc` |
|
||||
| Existing session | `<sessionType>` | `<sessionType>:/<rawId>` | `remote-localhost__8081-copilot:/abc-123` |
|
||||
| Backend session state | `<provider>` | `<provider>:/<rawId>` | `copilot:/abc-123` |
|
||||
| Root state subscription | (string) | `agenthost:/root` | - |
|
||||
| Remote filesystem | `agenthost` | `agenthost://<authority>/<path>` | `agenthost://localhost__8081/home/user/project` |
|
||||
| Language model ID | - | `<sessionType>:<rawModelId>` | `remote-localhost__8081-copilot:claude-sonnet-4-20250514` |
|
||||
|
||||
### Key distinction: session resource vs backend session URI
|
||||
|
||||
- The **session resource** URI uses the session type as its scheme
|
||||
(e.g., `remote-localhost__8081-copilot:/untitled-abc`). This is the URI visible to
|
||||
the chat UI and session management.
|
||||
- The **backend session** URI uses the provider as its scheme
|
||||
(e.g., `copilot:/abc-123`). This is sent over the agent host protocol to the
|
||||
server. The `AgentSession.uri(provider, rawId)` helper creates these.
|
||||
|
||||
The `AgentHostSessionHandler` translates between the two:
|
||||
```typescript
|
||||
private _resolveSessionUri(sessionResource: URI): URI {
|
||||
const rawId = sessionResource.path.substring(1);
|
||||
return AgentSession.uri(this._config.provider, rawId);
|
||||
}
|
||||
```
|
||||
|
||||
## Session Creation Flow
|
||||
|
||||
### 1. User Selects or Adds a Remote Workspace
|
||||
|
||||
In the `WorkspacePicker`, the user clicks **"Browse Remotes..."**. The picker
|
||||
shows existing connected remotes and an **"Add Remote..."** action. When the
|
||||
user chooses **"Add Remote..."**, they:
|
||||
|
||||
1. Paste a host, `host:port`, or WebSocket URL.
|
||||
2. Enter a display name for that remote.
|
||||
3. The parsed address is written into `chat.remoteAgentHosts`.
|
||||
4. The client waits for the new remote connection to come up.
|
||||
5. The user is immediately taken into the remote folder picker.
|
||||
|
||||
Supported address inputs include raw `host:port`, `ws://` / `wss://` URLs, and
|
||||
log-line text such as `Listening on ws://127.0.0.1:8089`. If the input includes
|
||||
`?tkn=...`, the token is extracted into `connectionToken` and the stored address
|
||||
is normalized without that query parameter.
|
||||
|
||||
After choosing an existing remote or successfully adding a new one, the user
|
||||
picks a folder on the remote filesystem. This produces a `SessionWorkspace`
|
||||
with an `agenthost://` URI:
|
||||
|
||||
```
|
||||
agenthost://localhost__8081/home/user/myproject
|
||||
↑ authority ↑ remote filesystem path
|
||||
```
|
||||
|
||||
### 2. Session Target Resolution
|
||||
|
||||
`NewChatWidget._createNewSession()` detects `project.isRemoteAgentHost` and
|
||||
resolves the matching session type via `getRemoteAgentHostSessionTarget()`
|
||||
(defined in `remoteAgentHost.contribution.ts`):
|
||||
|
||||
```typescript
|
||||
// authority "localhost__8081" → find connection → "remote-localhost__8081-copilot"
|
||||
const target = getRemoteAgentHostSessionTarget(connections, authority);
|
||||
```
|
||||
|
||||
### 3. Resource URI Generation
|
||||
|
||||
`getResourceForNewChatSession()` creates the session resource:
|
||||
|
||||
```typescript
|
||||
URI.from({ scheme: target, path: `/untitled-${generateUuid()}` })
|
||||
// → remote-localhost__8081-copilot:/untitled-abc-123
|
||||
```
|
||||
|
||||
### 4. Session Object Creation
|
||||
|
||||
`SessionsManagementService.createNewSessionForTarget()` creates an
|
||||
`AgentHostNewSession` (when the `agentHost` option is set). This is a
|
||||
lightweight `INewSession` that supports local model and mode pickers but
|
||||
skips isolation mode, branch, and cloud option groups.
|
||||
The project URI is set on the session, making it available as
|
||||
`activeSessionItem.repository`.
|
||||
|
||||
### 5. Backend Session Creation (Deferred)
|
||||
|
||||
`AgentHostSessionHandler` defers backend session creation until the first turn
|
||||
(for "untitled" sessions), so the user-selected model is available:
|
||||
|
||||
```typescript
|
||||
const session = await connection.createSession({
|
||||
model: rawModelId,
|
||||
provider: 'copilot',
|
||||
workingDirectory: '/home/user/myproject', // from activeSession.repository.path
|
||||
});
|
||||
```
|
||||
|
||||
### 6. Working Directory Resolution
|
||||
|
||||
The `resolveWorkingDirectory` callback in `RemoteAgentHostContribution` reads
|
||||
the active session's repository URI path:
|
||||
|
||||
```typescript
|
||||
const resolveWorkingDirectory = (resourceKey: string): string | undefined => {
|
||||
const activeSessionItem = this._sessionsManagementService.getActiveSession();
|
||||
if (activeSessionItem?.repository) {
|
||||
return activeSessionItem.repository.path;
|
||||
// For agenthost://authority/home/user/project → "/home/user/project"
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
```
|
||||
|
||||
## Turn Handling
|
||||
|
||||
When the user sends a message, `AgentHostSessionHandler._handleTurn()`:
|
||||
|
||||
1. Converts variable entries to `IAgentAttachment[]` (file, directory, selection)
|
||||
2. Dispatches `session/modelChanged` if the model differs from current
|
||||
3. Dispatches `session/turnStarted` with the user message + attachments
|
||||
4. Listens to `SessionClientState.onDidChangeSessionState` and translates
|
||||
the `activeTurn` state changes into `IChatProgress[]` events:
|
||||
|
||||
| Server State | Chat Progress |
|
||||
|-------------|---------------|
|
||||
| `streamingText` | `markdownContent` |
|
||||
| `reasoning` | `thinking` |
|
||||
| `toolCalls` (new) | `ChatToolInvocation` created |
|
||||
| `toolCalls` (completed) | `ChatToolInvocation` finalized |
|
||||
| `pendingPermissions` | `awaitConfirmation()` prompt |
|
||||
|
||||
5. On cancellation, dispatches `session/turnCancelled`
|
||||
|
||||
## Filesystem Provider
|
||||
|
||||
`AgentHostFileSystemProvider` is a read-only `IFileSystemProvider` registered
|
||||
under the `agenthost` scheme. It proxies `stat` and `readdir` calls through
|
||||
`connection.browseDirectory(uri)` RPC.
|
||||
|
||||
- The URI authority identifies the remote connection (sanitized address)
|
||||
- The URI path is the remote filesystem path
|
||||
- Authority-to-address mappings are registered by `RemoteAgentHostContribution`
|
||||
via `registerAuthority(authority, address)`
|
||||
|
||||
## Data Flow Diagram
|
||||
|
||||
```
|
||||
Settings (chat.remoteAgentHosts)
|
||||
│
|
||||
▼
|
||||
RemoteAgentHostService (WebSocket connections)
|
||||
│
|
||||
▼
|
||||
RemoteAgentHostContribution
|
||||
│
|
||||
├─► subscribe(ROOT_STATE_URI) → IRootState.agents
|
||||
│ │
|
||||
│ ▼
|
||||
│ _registerAgent() for each copilot agent:
|
||||
│ ├─► registerChatSessionContribution()
|
||||
│ ├─► registerChatSessionItemController()
|
||||
│ ├─► registerChatSessionContentProvider()
|
||||
│ └─► registerLanguageModelProvider()
|
||||
│
|
||||
└─► registerProvider(AGENT_HOST_FS_SCHEME, fsProvider)
|
||||
|
||||
User picks remote workspace in WorkspacePicker
|
||||
│
|
||||
▼
|
||||
NewChatWidget._createNewSession(project)
|
||||
│ target = getRemoteAgentHostSessionTarget(connections, authority)
|
||||
▼
|
||||
SessionsManagementService.createNewSessionForTarget()
|
||||
│ creates AgentHostNewSession
|
||||
▼
|
||||
User sends message
|
||||
│
|
||||
▼
|
||||
AgentHostSessionHandler._handleTurn()
|
||||
│ resolves working directory
|
||||
│ creates backend session (if untitled)
|
||||
│ dispatches session/turnStarted
|
||||
▼
|
||||
connection ← streams state changes → IChatProgress[]
|
||||
```
|
||||
Reference in New Issue
Block a user