Skip to content

Commit 94c7bf8

Browse files
roblourensCopilot
andauthored
Unify agentHost server-side dispatch: remove IProtocolSideEffectHandler (#306158)
* Unify agentHost server-side dispatch: remove IProtocolSideEffectHandler Eliminate the IProtocolSideEffectHandler interface and make ProtocolServerHandler talk to IAgentService directly. This removes the duplicate adapter layer between the WebSocket protocol server and the real service implementation. Changes: - ProtocolServerHandler now takes IAgentService + SessionStateManager + IProtocolServerConfig instead of IProtocolSideEffectHandler - Deleted ~40-line inline adapter in agentHostMain.ts - agentHostServerMain.ts now uses AgentService instead of manually wiring SessionStateManager + AgentSideEffects - Removed implements IProtocolSideEffectHandler from AgentSideEffects - Removed dead methods from AgentSideEffects that were only needed by the deleted interface (handleCreateSession, handleDisposeSession, handleListSessions, handleGetResourceMetadata, handleAuthenticate, getDefaultDirectory) - Type conversions (URI<->string, IAgentSessionMetadata<->ISessionSummary) now happen at the protocol boundary in ProtocolServerHandler - Fixed dispatchAction double-dispatch: WS path previously dispatched to stateManager AND called handleAction (which dispatched again) - Extension methods (getResourceMetadata, authenticate, etc.) now call IAgentService directly instead of untyped fallbacks (Written by Copilot) * comments Co-authored-by: Copilot <copilot@github.com> * Simplify further Co-authored-by: Copilot <copilot@github.com> --------- Co-authored-by: Copilot <copilot@github.com>
1 parent bf71412 commit 94c7bf8

9 files changed

Lines changed: 511 additions & 843 deletions

src/vs/platform/agentHost/node/agentHostMain.ts

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,10 @@ import { Emitter } from '../../../base/common/event.js';
1111
import { DisposableStore } from '../../../base/common/lifecycle.js';
1212
import { URI } from '../../../base/common/uri.js';
1313
import * as os from 'os';
14-
import { AgentHostIpcChannels, AgentSession } from '../common/agentService.js';
15-
import { SessionStatus } from '../common/state/sessionState.js';
14+
import { AgentHostIpcChannels } from '../common/agentService.js';
1615
import { AgentService } from './agentService.js';
1716
import { CopilotAgent } from './copilot/copilotAgent.js';
18-
import { ProtocolServerHandler, type IProtocolSideEffectHandler } from './protocolServerHandler.js';
17+
import { ProtocolServerHandler } from './protocolServerHandler.js';
1918
import { WebSocketProtocolServer } from './webSocketTransport.js';
2019
import { NativeEnvironmentService } from '../../environment/node/environmentService.js';
2120
import { parseArgs, OPTIONS } from '../../environment/node/argv.js';
@@ -144,58 +143,13 @@ async function startWebSocketServer(agentService: AgentService, logService: ILog
144143
logService,
145144
));
146145

147-
// Create a side-effect handler that delegates to AgentService
148-
const sideEffects: IProtocolSideEffectHandler = {
149-
handleAction(action) {
150-
agentService.dispatchAction(action, 'ws-server', 0);
151-
},
152-
async handleCreateSession(command) {
153-
await agentService.createSession({
154-
provider: command.provider,
155-
model: command.model,
156-
workingDirectory: command.workingDirectory ? URI.parse(command.workingDirectory) : undefined,
157-
session: URI.parse(command.session),
158-
});
159-
},
160-
handleDisposeSession(session) {
161-
agentService.disposeSession(URI.parse(session));
162-
},
163-
async handleListSessions() {
164-
const sessions = await agentService.listSessions();
165-
return sessions.map(s => ({
166-
resource: s.session.toString(),
167-
provider: AgentSession.provider(s.session) ?? 'copilot',
168-
title: s.summary ?? 'Session',
169-
status: SessionStatus.Idle,
170-
createdAt: s.startTime,
171-
modifiedAt: s.modifiedTime,
172-
workingDirectory: s.workingDirectory?.toString(),
173-
}));
174-
},
175-
handleGetResourceMetadata() {
176-
return agentService.getResourceMetadataSync();
177-
},
178-
async handleAuthenticate(params) {
179-
return agentService.authenticate(params);
180-
},
181-
handleBrowseDirectory(uri) {
182-
return agentService.browseDirectory(URI.parse(uri));
183-
},
184-
handleWriteFile(params) {
185-
return agentService.writeFile(params);
186-
},
187-
async handleRestoreSession(session) {
188-
return agentService.restoreSession(URI.parse(session));
189-
},
190-
handleFetchContent(uri) {
191-
return agentService.fetchContent(URI.parse(uri));
192-
},
193-
getDefaultDirectory() {
194-
return URI.file(os.homedir()).toString();
195-
},
196-
};
197-
198-
const protocolHandler = disposables.add(new ProtocolServerHandler(agentService.stateManager, wsServer, sideEffects, logService));
146+
const protocolHandler = disposables.add(new ProtocolServerHandler(
147+
agentService,
148+
agentService.stateManager,
149+
wsServer,
150+
{ defaultDirectory: URI.file(os.homedir()).toString() },
151+
logService,
152+
));
199153
disposables.add(protocolHandler.onDidChangeConnectionCount(onConnectionCountChanged));
200154

201155
const listenTarget = socketPath ?? `${host}:${port}`;

src/vs/platform/agentHost/node/agentHostServerMain.ts

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { fileURLToPath } from 'url';
1414
globalThis._VSCODE_FILE_ROOT = fileURLToPath(new URL('../../../..', import.meta.url));
1515

1616
import * as fs from 'fs';
17+
import * as os from 'os';
1718
import { DisposableStore } from '../../../base/common/lifecycle.js';
18-
import { observableValue } from '../../../base/common/observable.js';
1919
import { URI } from '../../../base/common/uri.js';
2020
import { generateUuid } from '../../../base/common/uuid.js';
2121
import { localize } from '../../../nls.js';
@@ -30,9 +30,7 @@ import { IProductService } from '../../product/common/productService.js';
3030
import { InstantiationService } from '../../instantiation/common/instantiationService.js';
3131
import { ServiceCollection } from '../../instantiation/common/serviceCollection.js';
3232
import { CopilotAgent } from './copilot/copilotAgent.js';
33-
import { AgentSession, type AgentProvider, type IAgent } from '../common/agentService.js';
34-
import { AgentSideEffects } from './agentSideEffects.js';
35-
import { SessionStateManager } from './sessionStateManager.js';
33+
import { AgentService } from './agentService.js';
3634
import { WebSocketProtocolServer } from './webSocketTransport.js';
3735
import { ProtocolServerHandler } from './protocolServerHandler.js';
3836
import { FileService } from '../../files/common/fileService.js';
@@ -140,38 +138,16 @@ async function main(): Promise<void> {
140138

141139
logService.info('[AgentHostServer] Starting standalone agent host server');
142140

143-
// Create state manager
144-
const stateManager = disposables.add(new SessionStateManager(logService));
145-
146-
// Agent registry — maps provider id to agent instance
147-
const agents = new Map<AgentProvider, IAgent>();
148-
149-
// Observable agents list for root state
150-
const registeredAgents = observableValue<readonly IAgent[]>('agents', []);
151-
152141
// File service
153142
const fileService = disposables.add(new FileService(logService));
154143
disposables.add(fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))));
155144

156145
// Session data service
157146
const sessionDataService = new SessionDataService(URI.file(environmentService.userDataPath), fileService, logService);
158147

159-
// Shared side-effect handler
160-
const sideEffects = disposables.add(new AgentSideEffects(stateManager, {
161-
getAgent(session) {
162-
const provider = AgentSession.provider(session);
163-
return provider ? agents.get(provider) : agents.values().next().value;
164-
},
165-
agents: registeredAgents,
166-
sessionDataService,
167-
}, logService, fileService));
168-
169-
function registerAgent(agent: IAgent): void {
170-
agents.set(agent.id, agent);
171-
disposables.add(sideEffects.registerProgressListener(agent));
172-
registeredAgents.set([...agents.values()], undefined);
173-
logService.info(`[AgentHostServer] Registered agent: ${agent.id}`);
174-
}
148+
// Create the agent service (owns SessionStateManager + AgentSideEffects internally)
149+
const agentService = new AgentService(logService, fileService, sessionDataService);
150+
disposables.add(agentService);
175151

176152
// Register agents
177153
if (!options.quiet) {
@@ -184,15 +160,15 @@ async function main(): Promise<void> {
184160
diServices.set(ISessionDataService, sessionDataService);
185161
const instantiationService = new InstantiationService(diServices);
186162
const copilotAgent = disposables.add(instantiationService.createInstance(CopilotAgent));
187-
registerAgent(copilotAgent);
163+
agentService.registerProvider(copilotAgent);
188164
log('CopilotAgent registered');
189165
}
190166

191167
if (options.enableMockAgent) {
192168
// Dynamic import to avoid bundling test code in production
193169
import('../test/node/mockAgent.js').then(({ ScriptedMockAgent }) => {
194170
const mockAgent = disposables.add(new ScriptedMockAgent());
195-
registerAgent(mockAgent);
171+
agentService.registerProvider(mockAgent);
196172
}).catch(err => {
197173
logService.error('[AgentHostServer] Failed to load mock agent', err);
198174
});
@@ -207,7 +183,13 @@ async function main(): Promise<void> {
207183
}, logService));
208184

209185
// Wire up protocol handler
210-
disposables.add(new ProtocolServerHandler(stateManager, wsServer, sideEffects, logService));
186+
disposables.add(new ProtocolServerHandler(
187+
agentService,
188+
agentService.stateManager,
189+
wsServer,
190+
{ defaultDirectory: URI.file(os.homedir()).toString() },
191+
logService,
192+
));
211193

212194
// Report ready
213195
function reportReady(addr: string): void {

0 commit comments

Comments
 (0)