@@ -90,6 +90,7 @@ export class CoderAgentExecutor implements AgentExecutor {
9090 private tasks : Map < string , TaskWrapper > = new Map ( ) ;
9191 // Track tasks with an active execution loop.
9292 private executingTasks = new Set < string > ( ) ;
93+ private activeAbortControllers = new Map < string , AbortController > ( ) ;
9394
9495 constructor ( private taskStore ?: TaskStore ) { }
9596
@@ -187,6 +188,35 @@ export class CoderAgentExecutor implements AgentExecutor {
187188 logger . info (
188189 `[CoderAgentExecutor] Received cancel request for task ${ taskId } ` ,
189190 ) ;
191+
192+ // If there is an active execution loop, just abort it.
193+ // The `finally` block of the `execute` method will handle cleanup.
194+ const abortController = this . activeAbortControllers . get ( taskId ) ;
195+ if ( abortController ) {
196+ logger . info (
197+ `[CoderAgentExecutor] Aborting active execution loop for task ${ taskId } .` ,
198+ ) ;
199+ const wrapper = this . tasks . get ( taskId ) ;
200+ if ( wrapper ) {
201+ const { task } = wrapper ;
202+ task . cancelPendingTools ( 'Task canceled by user request.' ) ;
203+ task . setTaskStateAndPublishUpdate (
204+ 'canceled' ,
205+ { kind : CoderAgentEvent . StateChangeEvent } ,
206+ 'Task canceled by user request.' ,
207+ undefined ,
208+ true ,
209+ ) ;
210+ }
211+ abortController . abort ( ) ;
212+ return ;
213+ }
214+
215+ // If there is no active execution loop, the task is idle.
216+ // We can clean it up directly.
217+ logger . info (
218+ `[CoderAgentExecutor] No active execution for task ${ taskId } . Cleaning up directly.` ,
219+ ) ;
190220 const wrapper = this . tasks . get ( taskId ) ;
191221
192222 if ( ! wrapper ) {
@@ -244,7 +274,7 @@ export class CoderAgentExecutor implements AgentExecutor {
244274
245275 try {
246276 logger . info (
247- `[CoderAgentExecutor] Initiating cancellation for task ${ taskId } .` ,
277+ `[CoderAgentExecutor] Initiating cancellation for idle task ${ taskId } .` ,
248278 ) ;
249279 task . cancelPendingTools ( 'Task canceled by user request.' ) ;
250280
@@ -332,6 +362,14 @@ export class CoderAgentExecutor implements AgentExecutor {
332362 const abortController = new AbortController ( ) ;
333363 const abortSignal = abortController . signal ;
334364
365+ // Register the abort controller only for the primary execution loop.
366+ if (
367+ ! this . executingTasks . has ( taskId ) &&
368+ ! this . activeAbortControllers . has ( taskId )
369+ ) {
370+ this . activeAbortControllers . set ( taskId , abortController ) ;
371+ }
372+
335373 if ( store ) {
336374 // Grab the raw socket from the request object
337375 const socket = store . req . socket ;
@@ -644,6 +682,7 @@ export class CoderAgentExecutor implements AgentExecutor {
644682 }
645683 } finally {
646684 if ( isPrimaryExecution ) {
685+ this . activeAbortControllers . delete ( taskId ) ; // Clean up the reference
647686 this . executingTasks . delete ( taskId ) ;
648687 logger . info (
649688 `[CoderAgentExecutor] Saving final state for task ${ taskId } .` ,
0 commit comments