@@ -59,7 +59,7 @@ export class CopilotRemoteAgentManager extends Disposable {
5959 readonly onDidChangeChatSessions = this . _onDidChangeChatSessions . event ;
6060
6161 private readonly gitOperationsManager : GitOperationsManager ;
62- private readonly ephemeralChatSessions : Map < string , ChatSessionFromSummarizedChat > = new Map ( ) ; // TODO: Clean these up
62+ private readonly ephemeralChatSessions : Map < string , ChatSessionFromSummarizedChat > = new Map ( ) ;
6363
6464 constructor ( private credentialStore : CredentialStore , public repositoriesManager : RepositoriesManager , private telemetry : ITelemetry , private context : vscode . ExtensionContext ) {
6565 super ( ) ;
@@ -445,7 +445,7 @@ export class CopilotRemoteAgentManager extends Disposable {
445445
446446 const result = await this . invokeRemoteAgent (
447447 userPrompt ,
448- summary || userPrompt ,
448+ summary ,
449449 autoPushAndCommit ,
450450 ) ;
451451
@@ -510,7 +510,7 @@ export class CopilotRemoteAgentManager extends Disposable {
510510 return vscode . l10n . t ( '🚀 Coding agent will continue work in [#{0}]({1}). Track progress [here]({2}).' , number , link , webviewUri . toString ( ) ) ;
511511 }
512512
513- async invokeRemoteAgent ( prompt : string , problemContext : string , autoPushAndCommit = true ) : Promise < RemoteAgentResult > {
513+ async invokeRemoteAgent ( prompt : string , problemContext ? : string , autoPushAndCommit = true ) : Promise < RemoteAgentResult > {
514514 const capiClient = await this . copilotApi ;
515515 if ( ! capiClient ) {
516516 return { error : vscode . l10n . t ( 'Failed to initialize Copilot API' ) , state : 'error' } ;
@@ -557,7 +557,7 @@ export class CopilotRemoteAgentManager extends Disposable {
557557 }
558558
559559 let title = prompt ;
560- const titleMatch = problemContext . match ( / T I T L E : \s * ( .* ) / i) ;
560+ const titleMatch = problemContext ? .match ( / T I T L E : \s * ( .* ) / i) ;
561561 if ( titleMatch && titleMatch [ 1 ] ) {
562562 title = titleMatch [ 1 ] . trim ( ) ;
563563 }
@@ -574,15 +574,15 @@ export class CopilotRemoteAgentManager extends Disposable {
574574 event_type : 'visual_studio_code_remote_agent_tool_invoked' ,
575575 pull_request : {
576576 title,
577- body_placeholder : formatBodyPlaceholder ( problemContext ) ,
577+ body_placeholder : formatBodyPlaceholder ( problemContext || prompt ) ,
578578 base_ref,
579579 body_suffix,
580580 ...( hasChanges && { head_ref : ref } )
581581 }
582582 } ;
583583
584584 try {
585- const { pull_request } = await capiClient . postRemoteAgentJob ( owner , repo , payload ) ;
585+ const { pull_request, session_id } = await capiClient . postRemoteAgentJob ( owner , repo , payload ) ;
586586 this . _onDidCreatePullRequest . fire ( pull_request . number ) ;
587587 const webviewUri = await toOpenPullRequestWebviewUri ( { owner, repo, pullRequestNumber : pull_request . number } ) ;
588588 const prLlmString = `The remote agent has begun work and has created a pull request. Details about the pull request are being shown to the user. If the user wants to track progress or iterate on the agent's work, they should use the pull request.` ;
@@ -591,7 +591,8 @@ export class CopilotRemoteAgentManager extends Disposable {
591591 number : pull_request . number ,
592592 link : pull_request . html_url ,
593593 webviewUri,
594- llmDetails : hasChanges ? `The pending changes have been pushed to branch '${ ref } '. ${ prLlmString } ` : prLlmString
594+ llmDetails : hasChanges ? `The pending changes have been pushed to branch '${ ref } '. ${ prLlmString } ` : prLlmString ,
595+ sessionId : session_id
595596 } ;
596597 } catch ( error ) {
597598 return { error : error . message , state : 'error' } ;
@@ -724,8 +725,29 @@ export class CopilotRemoteAgentManager extends Disposable {
724725 return this . _stateModel . getCounts ( ) ;
725726 }
726727
727- public async provideNewChatSessionItem ( options : { prompt ?: string ; history : ReadonlyArray < vscode . ChatRequestTurn | vscode . ChatResponseTurn > ; metadata ?: any ; } , _token : vscode . CancellationToken ) : Promise < ChatSessionWithPR | ChatSessionFromSummarizedChat > {
728- const { prompt } = options ;
728+ async extractHistory ( history : ReadonlyArray < vscode . ChatRequestTurn | vscode . ChatResponseTurn > ) : Promise < string | undefined > {
729+ if ( ! history ) {
730+ return ;
731+ }
732+ const parts : string [ ] = [ ] ;
733+ for ( const turn of history ) {
734+ if ( turn instanceof vscode . ChatRequestTurn ) {
735+ parts . push ( `User: ${ turn . prompt } ` ) ;
736+ } else if ( turn instanceof vscode . ChatResponseTurn ) {
737+ const textParts = turn . response
738+ . filter ( part => part instanceof vscode . ChatResponseMarkdownPart )
739+ . map ( part => part . value ) ;
740+ if ( textParts . length > 0 ) {
741+ parts . push ( `Copilot: ${ textParts . join ( '\n' ) } ` ) ;
742+ }
743+ }
744+ }
745+ const fullText = parts . join ( '\n' ) ; // TODO: Summarization if too long
746+ return fullText ;
747+ }
748+
749+ public async provideNewChatSessionItem ( options : { prompt ?: string ; history : ReadonlyArray < vscode . ChatRequestTurn | vscode . ChatResponseTurn > ; metadata ?: any ; } , token : vscode . CancellationToken ) : Promise < ChatSessionWithPR | ChatSessionFromSummarizedChat > {
750+ const { prompt, history } = options ;
729751 if ( ! prompt ) {
730752 throw new Error ( `Prompt is expected to provide a new chat session item` ) ;
731753 }
@@ -748,29 +770,32 @@ export class CopilotRemoteAgentManager extends Disposable {
748770
749771 const result = await this . invokeRemoteAgent (
750772 prompt ,
751- prompt ,
773+ ( await this . extractHistory ( history ) ) ,
752774 false ,
753775 ) ;
754776 if ( result . state !== 'success' ) {
755777 Logger . error ( `Failed to provide new chat session item: ${ result . error } ` , CopilotRemoteAgentManager . ID ) ;
756778 throw new Error ( `Failed to provide new chat session item: ${ result . error } ` ) ;
757779 }
758780
759- const { number } = result ;
781+ const { number, sessionId } = result ;
760782
761- const session = await this . findPullRequestById ( number , true ) ;
762- if ( ! session ) {
783+ const pullRequest = await this . findPullRequestById ( number , true ) ;
784+ if ( ! pullRequest ) {
763785 throw new Error ( `Failed to find session for pull request: ${ number } ` ) ;
764786 }
765- const timeline = await session . getCopilotTimelineEvents ( session ) ;
787+
788+ await this . waitForQueuedToInProgress ( sessionId , token ) ;
789+
790+ const timeline = await pullRequest . getCopilotTimelineEvents ( pullRequest ) ;
766791 const status = copilotEventToSessionStatus ( mostRecentCopilotEvent ( timeline ) ) ;
767- const tooltip = await issueMarkdown ( session , this . context , this . repositoriesManager ) ;
768- const timestampNumber = new Date ( session . createdAt ) . getTime ( ) ;
792+ const tooltip = await issueMarkdown ( pullRequest , this . context , this . repositoriesManager ) ;
793+ const timestampNumber = new Date ( pullRequest . createdAt ) . getTime ( ) ;
769794 return {
770- id : `${ session . number } ` ,
771- label : session . title || `Session ${ session . number } ` ,
795+ id : `${ pullRequest . number } ` ,
796+ label : pullRequest . title || `Session ${ pullRequest . number } ` ,
772797 iconPath : this . getIconForSession ( status ) ,
773- pullRequest : session ,
798+ pullRequest : pullRequest ,
774799 tooltip,
775800 status,
776801 timing : {
@@ -817,10 +842,9 @@ export class CopilotRemoteAgentManager extends Disposable {
817842 return [ ] ;
818843 }
819844
820-
821845 private async newSessionFlowFromPrompt ( id : string ) : Promise < vscode . ChatSession > {
822- const session = this . ephemeralChatSessions . get ( id ) ;
823- if ( ! session ) {
846+ const chatSession = this . ephemeralChatSessions . get ( id ) ;
847+ if ( ! chatSession ) {
824848 return this . createEmptySession ( ) ;
825849 }
826850
@@ -829,13 +853,7 @@ export class CopilotRemoteAgentManager extends Disposable {
829853 return this . createEmptySession ( ) ; // TODO: Explain how to enroll repo in coding agent, etc..?
830854 }
831855 const { repo, owner } = repoInfo ;
832-
833- // Remove from ephemeral sessions
834- this . ephemeralChatSessions . delete ( id ) ;
835-
836- // Create a placeholder session that will invoke the remote agent when confirmed
837-
838- const { prompt } = session ;
856+ const { prompt, summary } = chatSession ;
839857 const sessionRequest = new vscode . ChatRequestTurn2 (
840858 prompt ,
841859 undefined ,
@@ -880,15 +898,16 @@ export class CopilotRemoteAgentManager extends Disposable {
880898 stream . progress ( 'Delegating to coding agent' ) ;
881899 const result = await this . invokeRemoteAgent (
882900 prompt ,
883- prompt ,
901+ summary || prompt ,
884902 false ,
885903 ) ;
904+ this . ephemeralChatSessions . delete ( id ) ; // TODO: Better state management
886905 if ( result . state !== 'success' ) {
887906 stream . warning ( `Could not create coding agent session: ${ result . error } ` ) ;
888907 return { } ;
889908 }
890-
891909 const pullRequest = await this . findPullRequestById ( result . number , true ) ;
910+ chatSession . pullRequest = pullRequest ; // Cache for later
892911 if ( ! pullRequest ) {
893912 stream . warning ( `Could not find coding agent session.` ) ;
894913 return { } ;
@@ -898,16 +917,9 @@ export class CopilotRemoteAgentManager extends Disposable {
898917 stream . warning ( vscode . l10n . t ( 'Could not initialize Copilot API.' ) ) ;
899918 return { } ;
900919 }
901- // Poll for the new session
902- const sessions = await capi . getAllSessions ( pullRequest . id ) ;
903- const newSession = sessions . find ( s => s . state === 'in_progress' || s . state === 'queued' ) ;
904- if ( ! newSession ) {
905- stream . warning ( vscode . l10n . t ( 'Could not find coding agent session in progress.' ) ) ;
906- return { } ;
907- }
908920 stream . markdown ( vscode . l10n . t ( 'Coding agent is now working on your request...' ) ) ;
909921 stream . markdown ( '\n\n' ) ;
910- await this . streamSessionLogs ( stream , pullRequest , newSession . id , token ) ;
922+ await this . streamSessionLogs ( stream , pullRequest , result . sessionId , token ) ;
911923 return { } ;
912924 default :
913925 Logger . error ( `Unknown confirmation state: ${ state } ` , CopilotRemoteAgentManager . ID ) ;
@@ -1380,6 +1392,35 @@ export class CopilotRemoteAgentManager extends Disposable {
13801392 } ;
13811393 }
13821394
1395+ private async waitForQueuedToInProgress (
1396+ sessionId : string ,
1397+ token : vscode . CancellationToken
1398+ ) : Promise < SessionInfo | undefined > {
1399+ const capi = await this . copilotApi ;
1400+ if ( ! capi ) {
1401+ return undefined ;
1402+ }
1403+
1404+ const maxWaitTime = 2 * 60 * 1_000 ; // 2 minutes
1405+ const pollInterval = 3_000 ; // 3 seconds
1406+ const startTime = Date . now ( ) ;
1407+
1408+ const sessionInfo = await capi . getSessionInfo ( sessionId ) ;
1409+ if ( ! sessionInfo || sessionInfo . state !== 'queued' ) {
1410+ return ;
1411+ }
1412+
1413+ Logger . appendLine ( `Session ${ sessionInfo . id } is queued, waiting to start...` , CopilotRemoteAgentManager . ID ) ;
1414+ while ( Date . now ( ) - startTime < maxWaitTime && ! token . isCancellationRequested ) {
1415+ const sessionInfo = await capi . getSessionInfo ( sessionId ) ;
1416+ if ( sessionInfo ?. state === 'in_progress' ) {
1417+ Logger . appendLine ( `Session ${ sessionInfo . id } now in progress.` , CopilotRemoteAgentManager . ID ) ;
1418+ return sessionInfo ;
1419+ }
1420+ await new Promise ( resolve => setTimeout ( resolve , pollInterval ) ) ;
1421+ }
1422+ }
1423+
13831424 private async waitForNewSession (
13841425 pullRequest : PullRequestModel ,
13851426 stream : vscode . ChatResponseStream ,
0 commit comments