@@ -294,6 +294,76 @@ export class CopilotRemoteAgentManager extends Disposable {
294294 return vscode . l10n . t ( '🚀 Coding agent will continue work in [#{0}]({1}). Track progress [here]({2}).' , number , link , webviewUri . toString ( ) ) ;
295295 }
296296
297+ /**
298+ * Opens a terminal and waits for user to successfully commit
299+ * This is a fallback for when the commit cannot be done automatically (eg: GPG signing password needed)
300+ */
301+ private async handleInteractiveCommit ( repository : Repository , commitMessage : string ) : Promise < boolean > {
302+ return new Promise < boolean > ( ( resolve ) => {
303+ const startingCommit = repository . state . HEAD ?. commit ;
304+
305+ // Create terminal with git commit command
306+ const terminal = vscode . window . createTerminal ( {
307+ name : 'GitHub Coding Agent' ,
308+ cwd : repository . rootUri . fsPath ,
309+ message : vscode . l10n . t ( 'Commit your changes to continue Coding Agent session' )
310+ } ) ;
311+
312+ // Show terminal and send commit command
313+ terminal . show ( ) ;
314+ terminal . sendText ( `# Complete this commit to continue with your Coding Agent session. Ctrl+C to cancel.` ) ;
315+ terminal . sendText ( `git commit -m "${ commitMessage } "` ) ;
316+
317+ let disposed = false ;
318+ let timeoutId : NodeJS . Timeout ;
319+ let stateListener : vscode . Disposable | undefined ;
320+ let disposalListener : vscode . Disposable | undefined ;
321+
322+ const cleanup = ( ) => {
323+ if ( disposed ) return ;
324+ disposed = true ;
325+ clearTimeout ( timeoutId ) ;
326+ stateListener ?. dispose ( ) ;
327+ disposalListener ?. dispose ( ) ;
328+ terminal . dispose ( ) ;
329+ } ;
330+
331+ // Listen for repository state changes
332+ stateListener = repository . state . onDidChange ( ( ) => {
333+ // Check if commit was successful (HEAD changed and no more staged changes)
334+ if ( repository . state . HEAD ?. commit !== startingCommit &&
335+ repository . state . indexChanges . length === 0 ) {
336+ cleanup ( ) ;
337+ resolve ( true ) ;
338+ }
339+ } ) ;
340+
341+ // Set a timeout to avoid waiting forever
342+ timeoutId = setTimeout ( ( ) => {
343+ cleanup ( ) ;
344+ vscode . window . showWarningMessage (
345+ vscode . l10n . t ( 'Commit timeout. Please try the operation again after committing your changes.' )
346+ ) ;
347+ resolve ( false ) ;
348+ } , 5 * 60 * 1000 ) ; // 5 minutes timeout
349+
350+ // Listen for terminal disposal (user closed it)
351+ disposalListener = vscode . window . onDidCloseTerminal ( ( closedTerminal ) => {
352+ if ( closedTerminal === terminal ) {
353+ // Give a brief moment for potential state changes to propagate
354+ setTimeout ( ( ) => {
355+ if ( ! disposed ) {
356+ cleanup ( ) ;
357+ // Check one more time if commit happened just before terminal was closed
358+ resolve ( repository . state . HEAD ?. commit !== startingCommit &&
359+ repository . state . indexChanges . length === 0 ) ;
360+ }
361+ } , 1000 ) ;
362+ }
363+ } ) ;
364+ } ) ;
365+ }
366+
297367 async invokeRemoteAgent ( prompt : string , problemContext : string , autoPushAndCommit = true ) : Promise < RemoteAgentResult > {
298368 const capiClient = await this . copilotApi ;
299369 if ( ! capiClient ) {
@@ -319,13 +389,29 @@ export class CopilotRemoteAgentManager extends Disposable {
319389 const asyncBranch = `copilot/vscode${ Date . now ( ) } ` ;
320390 try {
321391 await repository . createBranch ( asyncBranch , true ) ;
322- await repository . add ( [ ] ) ;
323- if ( repository . state . indexChanges . length > 0 ) {
324- try {
325- await repository . commit ( 'Checkpoint for Copilot Agent async session' ) ;
326- } catch ( e ) {
327- // https://github.com/microsoft/vscode/pull/252263
328- return { error : vscode . l10n . t ( 'Could not \'git commit\' pending changes. If GPG signing or git hooks are enabled, please first commit or stash your changes and try again. ({0})' , e . message ) , state : 'error' } ;
392+ const commitMessage = 'Checkpoint for VS Code Coding Agent Session' ;
393+ try {
394+ await repository . commit ( commitMessage , { all : true } ) ;
395+ if ( repository . state . HEAD ?. name !== asyncBranch || repository . state . workingTreeChanges . length > 0 || repository . state . indexChanges . length > 0 ) {
396+ throw new Error ( vscode . l10n . t ( 'Uncommitted changes still detected.' ) ) ;
397+ }
398+ } catch ( e ) {
399+ // Instead of immediately failing, open terminal for interactive commit
400+ const commitSuccessful = await vscode . window . withProgress ( {
401+ title : vscode . l10n . t ( 'Waiting for commit to complete in the integrated terminal...' ) ,
402+ cancellable : true ,
403+ location : vscode . ProgressLocation . Notification
404+ } , async ( progress , token ) => {
405+ const commitPromise = this . handleInteractiveCommit ( repository , commitMessage ) ;
406+ if ( token ) {
407+ token . onCancellationRequested ( ( ) => {
408+ return false ;
409+ } ) ;
410+ }
411+ return await commitPromise ;
412+ } ) ;
413+ if ( ! commitSuccessful ) {
414+ return { error : vscode . l10n . t ( 'Commit was unsuccessful. Manually commit or stash your changes and try again.' ) , state : 'error' } ;
329415 }
330416 }
331417 await repository . push ( remote . remoteName , asyncBranch , true ) ;
0 commit comments