@@ -294,6 +294,76 @@ export class CopilotRemoteAgentManager extends Disposable {
294
294
return vscode . l10n . t ( '🚀 Coding agent will continue work in [#{0}]({1}). Track progress [here]({2}).' , number , link , webviewUri . toString ( ) ) ;
295
295
}
296
296
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
+
297
367
async invokeRemoteAgent ( prompt : string , problemContext : string , autoPushAndCommit = true ) : Promise < RemoteAgentResult > {
298
368
const capiClient = await this . copilotApi ;
299
369
if ( ! capiClient ) {
@@ -319,13 +389,29 @@ export class CopilotRemoteAgentManager extends Disposable {
319
389
const asyncBranch = `copilot/vscode${ Date . now ( ) } ` ;
320
390
try {
321
391
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' } ;
329
415
}
330
416
}
331
417
await repository . push ( remote . remoteName , asyncBranch , true ) ;
0 commit comments