@@ -3,14 +3,17 @@ import { executeAbility } from './index.js'
33
44/**
55 * Minimal ExecutionManager
6- *
6+ *
77 * Simplified to track SINGLE execution at a time.
88 * No session management, no cleanup timers, no multi-execution.
9- *
9+ *
1010 * This is the bare minimum to test the core concept.
1111 */
1212export class ExecutionManager {
1313 private activeExecution : AbilityExecution | null = null
14+ private executionHistory : AbilityExecution [ ] = [ ]
15+ private maxHistory = 50
16+ private abortController : AbortController | null = null
1417
1518 async execute (
1619 ability : Ability ,
@@ -23,10 +26,33 @@ export class ExecutionManager {
2326 }
2427
2528 console . log ( `[abilities] Starting execution: ${ ability . name } ` )
26-
27- const execution = await executeAbility ( ability , inputs , ctx )
29+
30+ this . abortController = new AbortController ( )
31+
32+ // Set activeExecution BEFORE awaiting so getActive() returns a live
33+ // reference during execution (needed for chat-context injection and
34+ // concurrent-execution guards).
35+ this . activeExecution = {
36+ id : `exec_${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) } ` ,
37+ ability,
38+ inputs,
39+ status : 'running' ,
40+ currentStep : null ,
41+ currentStepIndex : - 1 ,
42+ completedSteps : [ ] ,
43+ pendingSteps : [ ...ability . steps ] ,
44+ startedAt : Date . now ( ) ,
45+ }
46+
47+ const execution = await executeAbility ( ability , inputs , ctx , this . abortController . signal )
2848 this . activeExecution = execution
2949
50+ // Track in history
51+ this . executionHistory . push ( execution )
52+ if ( this . executionHistory . length > this . maxHistory ) {
53+ this . executionHistory = this . executionHistory . slice ( - this . maxHistory )
54+ }
55+
3056 // Clear active if completed/failed
3157 if ( execution . status !== 'running' ) {
3258 this . activeExecution = null
@@ -35,14 +61,24 @@ export class ExecutionManager {
3561 return execution
3662 }
3763
64+ get ( id : string ) : AbilityExecution | undefined {
65+ return this . executionHistory . find ( ( e ) => e . id === id )
66+ }
67+
68+ list ( ) : AbilityExecution [ ] {
69+ return [ ...this . executionHistory ]
70+ }
71+
3872 getActive ( ) : AbilityExecution | null {
3973 return this . activeExecution
4074 }
4175
4276 cancel ( ) : boolean {
4377 if ( ! this . activeExecution ) return false
44-
78+
4579 if ( this . activeExecution . status === 'running' ) {
80+ this . abortController ?. abort ( )
81+ this . abortController = null
4682 this . activeExecution . status = 'failed'
4783 this . activeExecution . error = 'Cancelled by user'
4884 this . activeExecution . completedAt = Date . now ( )
@@ -53,7 +89,24 @@ export class ExecutionManager {
5389 return false
5490 }
5591
92+ cancelActive ( ) : boolean {
93+ return this . cancel ( )
94+ }
95+
96+ onSessionDeleted ( sessionId : string ) : void {
97+ if ( this . activeExecution && this . activeExecution . status === 'running' ) {
98+ this . abortController ?. abort ( )
99+ this . abortController = null
100+ this . activeExecution . status = 'failed'
101+ this . activeExecution . error = `Session ${ sessionId } deleted`
102+ this . activeExecution . completedAt = Date . now ( )
103+ this . activeExecution = null
104+ }
105+ }
106+
56107 cleanup ( ) : void {
108+ this . abortController ?. abort ( )
109+ this . abortController = null
57110 this . activeExecution = null
58111 }
59112}
0 commit comments