310
};
311
}
313
>
removeActiveClient(_session: URI, clientId: string): void {
314
this.removeActiveClientCalls.push({ clientId });
315
}
317
>
onClientToolCallComplete(session: URI, chat: URI, toolCallId: string, result: ToolCallResult): void {
318
this.clientToolCallCompleteCalls.push({ session, chat, toolCallId, result });
319
}
321
>
async shutdown(): Promise<void> { }
322
>
323
>
/**
324
>
* Fires an {@link AgentSignal} on this agent.
325
>
*/
326
>
fireProgress(signal: AgentSignal): void {
327
this._onDidSessionProgress.fire(signal);
328
}
330
>
/**
331
>
* Looks up the active turn id captured from the most recent
332
>
* {@link sendMessage} call for a given session. Returns `undefined` if
333
>
* the session has no active turn yet (e.g. tests that fire progress
334
>
* without first calling sendMessage).
335
>
*/
336
>
getActiveTurnId(session: URI): string | undefined {
337
return this._activeTurnIds.get(uriKey(session));
338
}
340
>
fireCustomizationsChange(): void {
341
this._onDidCustomizationsChange.fire();
342
}
344
>
dispose(): void {
345
this._onDidSessionProgress.dispose();
346
this._onDidSendMessage.dispose();
347
this._onDidCustomizationsChange.dispose();
348
}
350
>
351
>
/**
352
>
* Well-known URI of a pre-existing session seeded in {@link ScriptedMockAgent}.
353
>
* This session appears in `listSessions()` and has message history via
354
>
* `getSessionMessages()`, but was never created through the server's
355
>
* `handleCreateSession`. It simulates a session from a previous server
356
>
* lifetime for testing the restore-on-subscribe path.
357
>
*/
358
>
export const PRE_EXISTING_SESSION_URI = AgentSession.uri('mock', 'pre-existing-session');
359
>
360
>
export class ScriptedMockAgent implements IAgent {
361
>
readonly id: AgentProvider = 'mock';
362
>
363
>
private readonly _onDidSessionProgress = new Emitter<AgentSignal>();
364
>
readonly onDidSessionProgress = this._onDidSessionProgress.event;
365
>
private readonly _models = observableValue<readonly IAgentModelInfo[]>(this, [{ provider: 'mock', id: 'mock-model', name: 'Mock Model', maxContextWindow: 128000, supportsVision: false }]);
366
>
readonly models = this._models;
367
>
368
>
private readonly _sessions = new Map<string, URI>();
369
>
private _nextId = 1;
370
>
371
>
/**
372
>
* Message history for the pre-existing session: a single user→assistant
373
>
* turn with a tool call.
374
>
*/
375
>
private readonly _preExistingMessages: IHistoryRecord[] = [
376
>
{ type: 'message', role: 'user', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-1', content: 'What files are here?' },
377
>
{ type: 'tool_start', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', toolName: 'list_files', displayName: 'List Files', invocationMessage: 'Listing files...' },
378
>
{ type: 'tool_complete', session: PRE_EXISTING_SESSION_URI, toolCallId: 'h-tc-1', result: { pastTenseMessage: 'Listed files', content: [{ type: ToolResultContentType.Text, text: 'file1.ts\nfile2.ts' }], success: true } satisfies ToolCallResult },
379
>
{ type: 'message', role: 'assistant', session: PRE_EXISTING_SESSION_URI, messageId: 'h-msg-2', content: 'Here are the files: file1.ts and file2.ts' },
380
>
];
381
>
382
>
// Track pending permission requests
383
>
private readonly _pendingPermissions = new Map<string, (approved: boolean) => void>();
384
>
// Track the active turn ID per session, captured from sendMessage().
385
>
private readonly _activeTurnIds = new Map<string, string>();
386
>
// Track pending abort callbacks for slow responses
387
>
private readonly _pendingAborts = new Map<string, () => void>();
388
>
389
>
constructor() {
390
// Seed the pre-existing session so it appears in listSessions()
391
this._sessions.set(AgentSession.id(PRE_EXISTING_SESSION_URI), PRE_EXISTING_SESSION_URI);