@@ -11,6 +11,7 @@ import { ConfigKey, IConfigurationService } from '../../../platform/configuratio
1111import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext' ;
1212import { IGitService } from '../../../platform/git/common/gitService' ;
1313import { toGitUri } from '../../../platform/git/common/utils' ;
14+ import { ILogService } from '../../../platform/log/common/logService' ;
1415import { ITelemetryService } from '../../../platform/telemetry/common/telemetry' ;
1516import { disposableTimeout } from '../../../util/vs/base/common/async' ;
1617import { isCancellationError } from '../../../util/vs/base/common/errors' ;
@@ -406,10 +407,10 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
406407 @ICopilotCLISessionService private readonly sessionService : ICopilotCLISessionService ,
407408 @ITelemetryService private readonly telemetryService : ITelemetryService ,
408409 @IToolsService private readonly toolsService : IToolsService ,
409- @IRunCommandExecutionService private readonly commandExecutionService : IRunCommandExecutionService ,
410410 @IInstantiationService private readonly instantiationService : IInstantiationService ,
411411 @IConfigurationService private readonly configurationService : IConfigurationService ,
412412 @ICopilotCLISDK private readonly copilotCLISDK : ICopilotCLISDK ,
413+ @ILogService private readonly logService : ILogService ,
413414 ) {
414415 super ( ) ;
415416 }
@@ -441,7 +442,8 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
441442
442443 const confirmationResults = this . getAcceptedRejectedConfirmationData ( request ) ;
443444 if ( ! chatSessionContext ) {
444- /* Invoked from a 'normal' chat or 'cloud button' without CLI session context */
445+ // Invoked from a 'normal' chat or 'cloud button' without CLI session context
446+ // Or cases such as delegating from Regular chat to CLI chat
445447 // Handle confirmation data
446448 return await this . handlePushConfirmationData ( request , context , stream , token ) ;
447449 }
@@ -452,7 +454,7 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
452454 const additionalReferences = this . previousReferences . get ( id ) || [ ] ;
453455 this . previousReferences . delete ( id ) ;
454456 const [ { prompt, attachments } , modelId , sessionAgent , defaultAgent ] = await Promise . all ( [
455- this . promptResolver . resolvePrompt ( request , additionalReferences , token ) ,
457+ this . promptResolver . resolvePrompt ( request , undefined , additionalReferences , token ) ,
456458 this . getModelId ( id ) ,
457459 this . copilotCLIAgents . getSessionAgent ( id ) ,
458460 this . copilotCLIAgents . getDefaultAgent ( )
@@ -527,8 +529,8 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
527529 return session ;
528530 }
529531
530- private async getModelId ( sessionId : string ) : Promise < string | undefined > {
531- const preferredModelId = _sessionModel . get ( sessionId ) ?. id ?? ( await this . copilotCLIModels . getDefaultModel ( ) ) ?. id ;
532+ private async getModelId ( sessionId : string | undefined ) : Promise < string | undefined > {
533+ const preferredModelId = ( sessionId ? _sessionModel . get ( sessionId ) ?. id : undefined ) ?? ( await this . copilotCLIModels . getDefaultModel ( ) ) ?. id ;
532534 return preferredModelId ? this . copilotCLIModels . toModelProvider ( preferredModelId ) : undefined ;
533535 }
534536
@@ -598,15 +600,15 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
598600 const isolationEnabled = this . configurationService . getConfig ( ConfigKey . Advanced . CLIIsolationEnabled ) ;
599601 if ( ! isolationEnabled ) {
600602 // No isolation, proceed without worktree
601- return await this . createCLISessionAndOpen ( request . prompt , request . references , context , undefined , false , stream , token ) ;
603+ return await this . createCLISessionAndSubmitRequest ( request , undefined , request . references , context , undefined , false , stream , token ) ;
602604 }
603605
604606 // Check for uncommitted changes
605607 const currentRepository = this . gitService . activeRepository ?. get ( ) ;
606608 const hasUncommittedChanges = currentRepository ?. changes && ( currentRepository . changes . indexChanges . length > 0 || currentRepository . changes . workingTree . length > 0 ) ;
607609 if ( ! hasUncommittedChanges ) {
608610 // No uncommitted changes, create worktree and proceed
609- return await this . createCLISessionAndOpen ( request . prompt , request . references , context , undefined , true , stream , token ) ;
611+ return await this . createCLISessionAndSubmitRequest ( request , undefined , request . references , context , undefined , true , stream , token ) ;
610612 }
611613
612614 const message =
@@ -667,7 +669,7 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
667669 const worktreePath = worktreePathValue ? URI . file ( worktreePathValue ) : undefined ;
668670 if ( ! worktreePath ) {
669671 stream . warning ( vscode . l10n . t ( 'Failed to create worktree. Proceeding without isolation.' ) ) ;
670- return await this . createCLISessionAndOpen ( prompt , references , context , undefined , false , stream , token ) ;
672+ return await this . createCLISessionAndSubmitRequest ( request , prompt , references , context , undefined , false , stream , token ) ;
671673 }
672674
673675 // Migrate changes from active repository to worktree
@@ -713,62 +715,74 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
713715 }
714716 }
715717
716- return await this . createCLISessionAndOpen ( prompt , references , context , worktreePath , true , stream , token ) ;
718+ return await this . createCLISessionAndSubmitRequest ( request , prompt , references , context , worktreePath , true , stream , token ) ;
717719 } else {
718720 // Skip changes, just create worktree without migration
719- return await this . createCLISessionAndOpen ( prompt , references , context , undefined , true , stream , token ) ;
721+ return await this . createCLISessionAndSubmitRequest ( request , prompt , references , context , undefined , true , stream , token ) ;
720722 }
721723 }
722724
723- private async createCLISessionAndOpen (
724- prompt : string ,
725+ private async createCLISessionAndSubmitRequest (
726+ request : vscode . ChatRequest ,
727+ userPrompt : string | undefined ,
725728 references : readonly vscode . ChatPromptReference [ ] | undefined ,
726729 context : vscode . ChatContext ,
727730 workingDirectory : Uri | undefined ,
728731 isolationEnabled : boolean ,
729732 stream : vscode . ChatResponseStream ,
730733 token : vscode . CancellationToken
731- ) : Promise < vscode . ChatResult | void > {
734+ ) : Promise < vscode . ChatResult > {
732735 let history : string | undefined ;
733736
734737 if ( this . hasHistoryToSummarize ( context . history ) ) {
735738 stream . progress ( vscode . l10n . t ( 'Analyzing chat history' ) ) ;
736739 history = await this . summarizer . provideChatSummary ( context , token ) ;
737740 }
738741
739- const requestPrompt = history ? `${ prompt } \n**Summary**\n${ history } ` : prompt ;
742+ // Give priority to userPrompt if provided (e.g., from confirmation metadata)
743+ userPrompt = userPrompt || request . prompt ;
744+ const requestPrompt = history ? `${ userPrompt } \n**Summary**\n${ history } ` : userPrompt ;
740745
741746 // Create worktree if isolation is enabled and we don't have one yet
742- let finalWorkingDirectory = workingDirectory ;
743- if ( isolationEnabled && ! finalWorkingDirectory ) {
747+ if ( isolationEnabled && ! workingDirectory ) {
744748 const workTreePath = await this . worktreeManager . createWorktree ( stream ) ;
745- finalWorkingDirectory = workTreePath ? URI . file ( workTreePath ) : undefined ;
749+ workingDirectory = workTreePath ? URI . file ( workTreePath ) : undefined ;
746750 }
747751
748752 // Fallback to default directory if worktree creation failed
749- if ( ! finalWorkingDirectory && ! isolationEnabled ) {
750- finalWorkingDirectory = await this . copilotCLISDK . getDefaultWorkingDirectory ( ) ;
753+ if ( ! workingDirectory && ! isolationEnabled ) {
754+ workingDirectory = await this . copilotCLISDK . getDefaultWorkingDirectory ( ) ;
751755 }
756+ const [ { prompt, attachments } , model , agent ] = await Promise . all ( [
757+ this . promptResolver . resolvePrompt ( request , requestPrompt , ( references || [ ] ) . concat ( [ ] ) , token ) ,
758+ this . getModelId ( undefined ) ,
759+ this . copilotCLIAgents . getDefaultAgent ( ) . then ( agent => this . copilotCLIAgents . resolveAgent ( agent ) )
760+ ] ) ;
752761
753- const session = await this . sessionService . createSession ( requestPrompt , { workingDirectory : finalWorkingDirectory , isolationEnabled } , token ) ;
762+ const session = await this . sessionService . createSession ( requestPrompt , { workingDirectory, isolationEnabled, agent, model } , token ) ;
763+ this . copilotCLIAgents . trackSessionAgent ( session . object . sessionId , agent ?. name ) ;
764+
765+ // Do not await, we want this code path to be as fast as possible.
766+ if ( isolationEnabled && workingDirectory ) {
767+ void this . worktreeManager . storeWorktreePath ( session . object . sessionId , workingDirectory . fsPath ) ;
768+ }
769+
770+ // We don't want to block the caller anymore.
771+ // The caller is most likely a chat editor or the like.
772+ // Now that we've delegated it to a session, we can get out of here.
773+ // Else if the request takes say 10 minutes, the caller would be blocked for that long.
774+ session . object . handleRequest ( request . id , prompt , attachments , model , token )
775+ . catch ( error => {
776+ this . logService . error ( `Failed to handle CLI session request: ${ error } ` ) ;
777+ // Optionally: stream.error(error) to notify the user
778+ } )
779+ . finally ( ( ) => {
780+ session . dispose ( ) ;
781+ } ) ;
754782
755- if ( finalWorkingDirectory ) {
756- await this . worktreeManager . storeWorktreePath ( session . object . sessionId , finalWorkingDirectory . fsPath ) ;
757- }
783+ stream . markdown ( vscode . l10n . t ( '{0} agent has begun working on your request. Follow its progress in the Agents View.' , 'Background Agent' ) ) ;
758784
759- try {
760- // Since we're going to open a new session, store the references for later use.
761- this . previousReferences . set ( session . object . sessionId , ( references || [ ] ) . concat ( [ ] ) ) ;
762- await this . commandExecutionService . executeCommand ( 'vscode.open' , SessionIdForCLI . getResource ( session . object . sessionId ) ) ;
763- await this . commandExecutionService . executeCommand ( 'workbench.action.chat.submit' , { inputValue : requestPrompt } ) ;
764- return { } ;
765- }
766- finally {
767- // The SDK doesn't save the session as no messages were added,
768- // If we dispose this here, then we will not be able to find this session later.
769- // So leave this session alive till it gets used using the `getSession` API later
770- this . _register ( disposableTimeout ( ( ) => session . dispose ( ) , WAIT_FOR_NEW_SESSION_TO_GET_USED ) ) ;
771- }
785+ return { } ;
772786 }
773787
774788 private hasHistoryToSummarize ( history : readonly ( vscode . ChatRequestTurn | vscode . ChatResponseTurn ) [ ] ) : boolean {
0 commit comments