From d0e47b163a649d362535c72a524b74fab2f6b525 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 2 Nov 2025 13:36:36 +1100 Subject: [PATCH 1/5] Rmove Copilot CLI Agent manager and introduce ICopilotCLISession interface --- .../copilotcli/node/copilotcliAgentManager.ts | 52 --------------- .../copilotcli/node/copilotcliSession.ts | 32 +++++++--- .../node/copilotcliSessionService.ts | 12 +--- .../chatSessions/vscode-node/chatSessions.ts | 4 +- .../copilotCLIChatSessionsContribution.ts | 64 +++++++------------ 5 files changed, 48 insertions(+), 116 deletions(-) delete mode 100644 src/extension/agents/copilotcli/node/copilotcliAgentManager.ts diff --git a/src/extension/agents/copilotcli/node/copilotcliAgentManager.ts b/src/extension/agents/copilotcli/node/copilotcliAgentManager.ts deleted file mode 100644 index 5c10158957..0000000000 --- a/src/extension/agents/copilotcli/node/copilotcliAgentManager.ts +++ /dev/null @@ -1,52 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import type { ModelProvider } from '@github/copilot/sdk'; -import * as l10n from '@vscode/l10n'; -import type * as vscode from 'vscode'; -import { ILogService } from '../../../../platform/log/common/logService'; -import { Disposable } from '../../../../util/vs/base/common/lifecycle'; -import { CopilotCLIPromptResolver } from './copilotcliPromptResolver'; -import { ICopilotCLISessionService } from './copilotcliSessionService'; - -export class CopilotCLIAgentManager extends Disposable { - constructor( - private readonly promptResolver: CopilotCLIPromptResolver, - @ILogService private readonly logService: ILogService, - @ICopilotCLISessionService private readonly sessionService: ICopilotCLISessionService, - ) { - super(); - } - - async handleRequest( - copilotcliSessionId: string | undefined, - request: vscode.ChatRequest, - context: vscode.ChatContext, - stream: vscode.ChatResponseStream, - modelId: ModelProvider | undefined, - workingDirectory: string | undefined, - token: vscode.CancellationToken - ): Promise<{ copilotcliSessionId: string | undefined }> { - const sessionIdForLog = copilotcliSessionId ?? 'new'; - this.logService.trace(`[CopilotCLIAgentManager] Handling request for sessionId=${sessionIdForLog}.`); - - const { prompt, attachments } = await this.promptResolver.resolvePrompt(request, token); - // Check if we already have a session wrapper - let session = copilotcliSessionId ? await this.sessionService.getSession(copilotcliSessionId, modelId, false, token) : undefined; - - if (session) { - this.logService.trace(`[CopilotCLIAgentManager] Reusing CopilotCLI session ${copilotcliSessionId}.`); - } else if (copilotcliSessionId) { - stream.warning(l10n.t('Chat session not found.')); - return { copilotcliSessionId: undefined }; - } else { - session = await this.sessionService.createSession(prompt, modelId, token); - } - - await session.invoke(prompt, attachments, request.toolInvocationToken, stream, modelId, workingDirectory, token); - - return { copilotcliSessionId: session.sessionId }; - } -} diff --git a/src/extension/agents/copilotcli/node/copilotcliSession.ts b/src/extension/agents/copilotcli/node/copilotcliSession.ts index c0ea97d4d1..f25e6085c8 100644 --- a/src/extension/agents/copilotcli/node/copilotcliSession.ts +++ b/src/extension/agents/copilotcli/node/copilotcliSession.ts @@ -19,7 +19,27 @@ import { buildChatHistoryFromEvents, processToolExecutionComplete, processToolEx import { getCopilotLogger } from './logger'; import { getConfirmationToolParams, PermissionRequest } from './permissionHelpers'; -export class CopilotCLISession extends DisposableStore { +export interface ICopilotCLISession { + readonly sessionId: string; + readonly status: vscode.ChatSessionStatus | undefined; + readonly onDidChangeStatus: vscode.Event; + + handleRequest( + prompt: string, + attachments: Attachment[], + toolInvocationToken: vscode.ChatParticipantToolToken, + stream: vscode.ChatResponseStream, + modelId: ModelProvider | undefined, + workingDirectory: string | undefined, + token: vscode.CancellationToken + ): Promise; + + addUserMessage(content: string): void; + addUserAssistantMessage(content: string): void; + getSelectedModelId(): Promise; + getChatHistory(): Promise<(ChatRequestTurn2 | ChatResponseTurn2)[]>; +} +export class CopilotCLISession extends DisposableStore implements ICopilotCLISession { private _abortController = new AbortController(); private _pendingToolInvocations = new Map(); private _editTracker = new ExternalEditTracker(); @@ -32,14 +52,6 @@ export class CopilotCLISession extends DisposableStore { public readonly onDidChangeStatus = this._statusChange.event; - private _aborted?: boolean; - public get aborted(): boolean { - return this._aborted ?? false; - } - private readonly _onDidAbort = this.add(new EventEmitter()); - - public readonly onDidAbort = this._onDidAbort.event; - constructor( private readonly _sdkSession: Session, @ILogService private readonly logService: ILogService, @@ -64,7 +76,7 @@ export class CopilotCLISession extends DisposableStore { yield* agent.query(prompt, attachments); } - public async invoke( + public async handleRequest( prompt: string, attachments: Attachment[], toolInvocationToken: vscode.ChatParticipantToolToken, diff --git a/src/extension/agents/copilotcli/node/copilotcliSessionService.ts b/src/extension/agents/copilotcli/node/copilotcliSessionService.ts index 372a0cfecd..99b800c161 100644 --- a/src/extension/agents/copilotcli/node/copilotcliSessionService.ts +++ b/src/extension/agents/copilotcli/node/copilotcliSessionService.ts @@ -15,7 +15,7 @@ import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation'; import { ChatSessionStatus } from '../../../../vscodeTypes'; import { ICopilotCLISDK } from './copilotCli'; -import { CopilotCLISession } from './copilotcliSession'; +import { CopilotCLISession, ICopilotCLISession } from './copilotcliSession'; import { stripReminders } from './copilotcliToolInvocationFormatter'; import { getCopilotLogger } from './logger'; @@ -41,8 +41,8 @@ export interface ICopilotCLISessionService { deleteSession(sessionId: string): Promise; // Session wrapper tracking - getSession(sessionId: string, model: ModelProvider | undefined, readonly: boolean, token: CancellationToken): Promise; - createSession(prompt: string, model: ModelProvider | undefined, token: CancellationToken): Promise; + getSession(sessionId: string, model: ModelProvider | undefined, readonly: boolean, token: CancellationToken): Promise; + createSession(prompt: string, model: ModelProvider | undefined, token: CancellationToken): Promise; } export const ICopilotCLISessionService = createServiceIdentifier('ICopilotCLISessionService'); @@ -240,12 +240,6 @@ export class CopilotCLISessionService extends Disposable implements ICopilotCLIS session.add(sessionDisposables); session.add(session.onDidChangeStatus(() => this._onDidChangeSessions.fire())); - sessionDisposables.add(session.onDidAbort(() => { - // We need to start with a new session. - // https://github.com/microsoft/vscode/issues/274169 - session.dispose(); - })); - this._sessionWrappers.set(sdkSession.sessionId, session); return session; } catch (error) { diff --git a/src/extension/chatSessions/vscode-node/chatSessions.ts b/src/extension/chatSessions/vscode-node/chatSessions.ts index 9007ce19e8..d1996615b5 100644 --- a/src/extension/chatSessions/vscode-node/chatSessions.ts +++ b/src/extension/chatSessions/vscode-node/chatSessions.ts @@ -16,7 +16,6 @@ import { ClaudeAgentManager } from '../../agents/claude/node/claudeCodeAgent'; import { ClaudeCodeSdkService, IClaudeCodeSdkService } from '../../agents/claude/node/claudeCodeSdkService'; import { ClaudeCodeSessionService, IClaudeCodeSessionService } from '../../agents/claude/node/claudeCodeSessionService'; import { CopilotCLIModels, CopilotCLISDK, ICopilotCLIModels, ICopilotCLISDK } from '../../agents/copilotcli/node/copilotCli'; -import { CopilotCLIAgentManager } from '../../agents/copilotcli/node/copilotcliAgentManager'; import { CopilotCLIPromptResolver } from '../../agents/copilotcli/node/copilotcliPromptResolver'; import { CopilotCLISessionService, ICopilotCLISessionService } from '../../agents/copilotcli/node/copilotcliSessionService'; import { ILanguageModelServer, LanguageModelServer } from '../../agents/node/langModelServer'; @@ -112,13 +111,12 @@ export class ChatSessionsContrib extends Disposable implements IExtensionContrib const copilotcliSessionItemProvider = this._register(copilotcliAgentInstaService.createInstance(CopilotCLIChatSessionItemProvider, copilotCLIWorktreeManager)); this._register(vscode.chat.registerChatSessionItemProvider(this.copilotcliSessionType, copilotcliSessionItemProvider)); const promptResolver = copilotcliAgentInstaService.createInstance(CopilotCLIPromptResolver); - const copilotcliAgentManager = this._register(copilotcliAgentInstaService.createInstance(CopilotCLIAgentManager, promptResolver)); const copilotcliChatSessionContentProvider = copilotcliAgentInstaService.createInstance(CopilotCLIChatSessionContentProvider, copilotCLIWorktreeManager); const summarizer = copilotcliAgentInstaService.createInstance(ChatSummarizerProvider); const copilotcliChatSessionParticipant = copilotcliAgentInstaService.createInstance( CopilotCLIChatSessionParticipant, - copilotcliAgentManager, + promptResolver, copilotcliSessionItemProvider, copilotSessionsProvider, summarizer, diff --git a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts index 3c53c5ebea..fe897c4011 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts @@ -12,8 +12,8 @@ import { Emitter, Event } from '../../../util/vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from '../../../util/vs/base/common/lifecycle'; import { localize } from '../../../util/vs/nls'; import { ICopilotCLIModels } from '../../agents/copilotcli/node/copilotCli'; -import { CopilotCLIAgentManager } from '../../agents/copilotcli/node/copilotcliAgentManager'; -import { CopilotCLISession } from '../../agents/copilotcli/node/copilotcliSession'; +import { CopilotCLIPromptResolver } from '../../agents/copilotcli/node/copilotcliPromptResolver'; +import { ICopilotCLISession } from '../../agents/copilotcli/node/copilotcliSession'; import { ICopilotCLISessionService } from '../../agents/copilotcli/node/copilotcliSessionService'; import { ChatSummarizerProvider } from '../../prompt/node/summarizer'; import { ICopilotCLITerminalIntegration } from './copilotCLITerminalIntegration'; @@ -100,29 +100,6 @@ export class CopilotCLIWorktreeManager { } } -/** - * Convert a model ID to a ModelProvider object for the Copilot CLI SDK - */ -function getModelProvider(modelId: string | undefined): { type: 'anthropic' | 'openai'; model: string } | undefined { - if (!modelId) { - return undefined; - } - - // Map model IDs to their provider and model name - if (modelId.startsWith('claude-')) { - return { - type: 'anthropic', - model: modelId - }; - } else if (modelId.startsWith('gpt-')) { - return { - type: 'openai', - model: modelId - }; - } - - return undefined; -} namespace SessionIdForCLI { export function getResource(sessionId: string): vscode.Uri { @@ -312,12 +289,13 @@ export class CopilotCLIChatSessionContentProvider implements vscode.ChatSessionC export class CopilotCLIChatSessionParticipant { constructor( - private readonly copilotcliAgentManager: CopilotCLIAgentManager, + private readonly promptResolver: CopilotCLIPromptResolver, private readonly sessionItemProvider: CopilotCLIChatSessionItemProvider, private readonly cloudSessionProvider: CopilotChatSessionsProvider | undefined, private readonly summarizer: ChatSummarizerProvider, private readonly worktreeManager: CopilotCLIWorktreeManager, @IGitService private readonly gitService: IGitService, + @ICopilotCLIModels private readonly copilotCLIModels: ICopilotCLIModels, @ICopilotCLISessionService private readonly sessionService: ICopilotCLISessionService, ) { } @@ -338,26 +316,29 @@ export class CopilotCLIChatSessionParticipant { return await this.handlePushConfirmationData(request, context, stream, token); } + const defaultModel = await this.copilotCLIModels.getDefaultModel(); + const { resource } = chatSessionContext.chatSessionItem; + const id = SessionIdForCLI.parse(resource); + const preferredModel = _sessionModel.get(id); + // For existing sessions we cannot fall back, as the model would info would be updated in _sessionModel + const modelId = this.copilotCLIModels.toModelProvider(preferredModel?.id || defaultModel.id); + const { prompt, attachments } = await this.promptResolver.resolvePrompt(request, token); + if (chatSessionContext.isUntitled) { - const untitledCopilotcliSessionId = SessionIdForCLI.parse(chatSessionContext.chatSessionItem.resource); - const workingDirectory = await this.worktreeManager.createWorktreeIfNeeded(untitledCopilotcliSessionId, stream); + const session = await this.sessionService.createSession(prompt, modelId, token); + const workingDirectory = await this.worktreeManager.createWorktreeIfNeeded(session.sessionId, stream); - const { copilotcliSessionId } = await this.copilotcliAgentManager.handleRequest(undefined, request, context, stream, undefined, workingDirectory, token); - if (!copilotcliSessionId) { - stream.warning(localize('copilotcli.failedToCreateSession', "Failed to create a new CopilotCLI session.")); - return {}; - } - this.sessionItemProvider.swap(chatSessionContext.chatSessionItem, { resource: SessionIdForCLI.getResource(copilotcliSessionId), label: request.prompt ?? 'CopilotCLI' }); + await session.handleRequest(prompt, attachments, request.toolInvocationToken, stream, modelId, workingDirectory, token); + + this.sessionItemProvider.swap(chatSessionContext.chatSessionItem, { resource: SessionIdForCLI.getResource(session.sessionId), label: request.prompt ?? 'CopilotCLI' }); if (workingDirectory) { - await this.worktreeManager.storeWorktreePath(copilotcliSessionId, workingDirectory); + await this.worktreeManager.storeWorktreePath(session.sessionId, workingDirectory); } return {}; } - const { resource } = chatSessionContext.chatSessionItem; - const id = SessionIdForCLI.parse(resource); const session = await this.sessionService.getSession(id, undefined, false, token); if (!session) { stream.warning(vscode.l10n.t('Chat session not found.')); @@ -374,12 +355,11 @@ export class CopilotCLIChatSessionParticipant { } const workingDirectory = this.worktreeManager.getWorktreePath(id); - - await this.copilotcliAgentManager.handleRequest(id, request, context, stream, getModelProvider(_sessionModel.get(id)?.id), workingDirectory, token); + await session.handleRequest(prompt, attachments, request.toolInvocationToken, stream, modelId, workingDirectory, token); return {}; } - private async handleDelegateCommand(session: CopilotCLISession, request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) { + private async handleDelegateCommand(session: ICopilotCLISession, request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) { if (!this.cloudSessionProvider) { stream.warning(localize('copilotcli.missingCloudAgent', "No cloud agent available")); return {}; @@ -411,7 +391,7 @@ export class CopilotCLIChatSessionParticipant { } } - private async handleConfirmationData(session: CopilotCLISession, request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) { + private async handleConfirmationData(session: ICopilotCLISession, request: vscode.ChatRequest, context: vscode.ChatContext, stream: vscode.ChatResponseStream, token: vscode.CancellationToken) { const results: ConfirmationResult[] = []; results.push(...(request.acceptedConfirmationData?.map(data => ({ step: data.step, accepted: true, metadata: data?.metadata })) ?? [])); results.push(...((request.rejectedConfirmationData ?? []).filter(data => !results.some(r => r.step === data.step)).map(data => ({ step: data.step, accepted: false, metadata: data?.metadata })))); @@ -460,7 +440,7 @@ export class CopilotCLIChatSessionParticipant { } private async recordPushToSession( - session: CopilotCLISession, + session: ICopilotCLISession, userPrompt: string, prInfo: { uri: string; title: string; description: string; author: string; linkTag: string }, token: vscode.CancellationToken From eaf414556899e3903ef8f6a7075074a6593a441e Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 2 Nov 2025 04:46:25 +0100 Subject: [PATCH 2/5] Update src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../vscode-node/copilotCLIChatSessionsContribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts index fe897c4011..322fc1f148 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts @@ -320,7 +320,7 @@ export class CopilotCLIChatSessionParticipant { const { resource } = chatSessionContext.chatSessionItem; const id = SessionIdForCLI.parse(resource); const preferredModel = _sessionModel.get(id); - // For existing sessions we cannot fall back, as the model would info would be updated in _sessionModel + // For existing sessions we cannot fall back, as the model info would be updated in _sessionModel const modelId = this.copilotCLIModels.toModelProvider(preferredModel?.id || defaultModel.id); const { prompt, attachments } = await this.promptResolver.resolvePrompt(request, token); From 545219ede40216783fbccbc2abcb508ae3087710 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 2 Nov 2025 14:49:10 +1100 Subject: [PATCH 3/5] Updates --- .../agents/copilotcli/node/copilotcliSession.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/extension/agents/copilotcli/node/copilotcliSession.ts b/src/extension/agents/copilotcli/node/copilotcliSession.ts index f25e6085c8..3f67d497e8 100644 --- a/src/extension/agents/copilotcli/node/copilotcliSession.ts +++ b/src/extension/agents/copilotcli/node/copilotcliSession.ts @@ -10,7 +10,7 @@ import { ILogService } from '../../../../platform/log/common/logService'; import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService'; import { CancellationToken } from '../../../../util/vs/base/common/cancellation'; import { DisposableStore } from '../../../../util/vs/base/common/lifecycle'; -import { ChatRequestTurn2, ChatResponseThinkingProgressPart, ChatResponseTurn2, ChatSessionStatus, EventEmitter, LanguageModelTextPart } from '../../../../vscodeTypes'; +import { ChatRequestTurn2, ChatResponseThinkingProgressPart, ChatResponseTurn2, ChatSessionStatus, EventEmitter, LanguageModelTextPart, Uri } from '../../../../vscodeTypes'; import { IToolsService } from '../../../tools/common/toolsService'; import { ExternalEditTracker } from '../../common/externalEditTracker'; import { getAffectedUrisForEditTool } from '../common/copilotcliTools'; @@ -229,6 +229,16 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes permissionRequest: PermissionRequest, toolInvocationToken: vscode.ChatParticipantToolToken ): Promise<{ kind: 'approved' } | { kind: 'denied-interactively-by-user' }> { + if (permissionRequest.kind === 'read') { + // If user is reading a file in the workspace, auto-approve read requests. + // Outisde workspace reads (e.g., /etc/passwd) will still require approval. + const data = Uri.file(permissionRequest.path); + if (this.workspaceService.getWorkspaceFolder(data)) { + this.logService.trace(`[CopilotCLISession] Auto Approving request to read workspace file ${permissionRequest.path}`); + return { kind: 'approved' }; + } + } + try { const { tool, input } = getConfirmationToolParams(permissionRequest); const result = await this.toolsService.invokeTool(tool, From 54faba58861cf8823fe9b909f7419652faa117f3 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 2 Nov 2025 15:01:54 +1100 Subject: [PATCH 4/5] Updates --- .../vscode-node/copilotCLIChatSessionsContribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts index 322fc1f148..1fdd49b032 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts @@ -325,8 +325,9 @@ export class CopilotCLIChatSessionParticipant { const { prompt, attachments } = await this.promptResolver.resolvePrompt(request, token); if (chatSessionContext.isUntitled) { + const untitledCopilotcliSessionId = SessionIdForCLI.parse(chatSessionContext.chatSessionItem.resource); const session = await this.sessionService.createSession(prompt, modelId, token); - const workingDirectory = await this.worktreeManager.createWorktreeIfNeeded(session.sessionId, stream); + const workingDirectory = await this.worktreeManager.createWorktreeIfNeeded(untitledCopilotcliSessionId, stream); await session.handleRequest(prompt, attachments, request.toolInvocationToken, stream, modelId, workingDirectory, token); From a30af24c76839ffe4dc6c7014a9f16ccecafa36b Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Sun, 2 Nov 2025 15:21:36 +1100 Subject: [PATCH 5/5] Refactor creation of work trees --- .../copilotCLIChatSessionsContribution.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts index 1fdd49b032..79e63f08a7 100644 --- a/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts +++ b/src/extension/chatSessions/vscode-node/copilotCLIChatSessionsContribution.ts @@ -35,12 +35,7 @@ export class CopilotCLIWorktreeManager { constructor( @IVSCodeExtensionContext private readonly extensionContext: IVSCodeExtensionContext) { } - async createWorktreeIfNeeded(sessionId: string, stream: vscode.ChatResponseStream): Promise { - const isolationEnabled = this._sessionIsolation.get(sessionId) ?? false; - if (!isolationEnabled) { - return undefined; - } - + async createWorktree(stream: vscode.ChatResponseStream): Promise { try { const worktreePath = await vscode.commands.executeCommand('git.createWorktreeWithDefaults') as string | undefined; if (worktreePath) { @@ -323,20 +318,19 @@ export class CopilotCLIChatSessionParticipant { // For existing sessions we cannot fall back, as the model info would be updated in _sessionModel const modelId = this.copilotCLIModels.toModelProvider(preferredModel?.id || defaultModel.id); const { prompt, attachments } = await this.promptResolver.resolvePrompt(request, token); + const isolationEnabled = this.worktreeManager.getIsolationPreference(id); if (chatSessionContext.isUntitled) { - const untitledCopilotcliSessionId = SessionIdForCLI.parse(chatSessionContext.chatSessionItem.resource); + const workingDirectory = isolationEnabled ? await this.worktreeManager.createWorktree(stream) : undefined; const session = await this.sessionService.createSession(prompt, modelId, token); - const workingDirectory = await this.worktreeManager.createWorktreeIfNeeded(untitledCopilotcliSessionId, stream); + if (workingDirectory) { + await this.worktreeManager.storeWorktreePath(session.sessionId, workingDirectory); + } await session.handleRequest(prompt, attachments, request.toolInvocationToken, stream, modelId, workingDirectory, token); this.sessionItemProvider.swap(chatSessionContext.chatSessionItem, { resource: SessionIdForCLI.getResource(session.sessionId), label: request.prompt ?? 'CopilotCLI' }); - if (workingDirectory) { - await this.worktreeManager.storeWorktreePath(session.sessionId, workingDirectory); - } - return {}; }