@@ -10,7 +10,7 @@ import {ModelId} from "./ModelId";
1010
1111export interface SessionState {
1212 sessionMetadata : SessionMetadata ;
13- pendingPrompt : AbortController | null ;
13+ currentTurnId : string | null ;
1414}
1515
1616export class CodexAcpServer implements acp . Agent {
@@ -62,7 +62,7 @@ export class CodexAcpServer implements acp.Agent {
6262 const { sessionId, currentModelId, models} = sessionMetadata ;
6363 this . sessions . set ( sessionId , {
6464 sessionMetadata : sessionMetadata ,
65- pendingPrompt : null
65+ currentTurnId : null
6666 } ) ;
6767
6868 const availableModels = this . buildAvailableModels ( models ) ;
@@ -153,25 +153,28 @@ export class CodexAcpServer implements acp.Agent {
153153 async prompt ( params : acp . PromptRequest ) : Promise < acp . PromptResponse > {
154154 const sessionState = this . getSessionState ( params . sessionId ) ;
155155
156- sessionState . pendingPrompt ?. abort ( ) ;
157- sessionState . pendingPrompt = new AbortController ( ) ;
156+ sessionState . currentTurnId = null ;
158157
159158 try {
160159 const messageHandler = new CodexEventHandler ( this . connection , sessionState ) ;
161- await this . runWithProcessCheck ( ( ) => this . codexAcpClient . sendPrompt ( params , ( event ) => messageHandler . handleNotification ( event ) ) ) ;
162- } catch ( err ) {
163- if ( sessionState . pendingPrompt . signal . aborted ) {
164- return { stopReason : "cancelled" } ;
160+ const turnCompleted = await this . runWithProcessCheck ( ( ) => this . codexAcpClient . sendPrompt ( params , ( event ) => messageHandler . handleNotification ( event ) ) ) ;
161+
162+ // Check if turn was interrupted (cancelled)
163+ if ( turnCompleted . turn . status === "interrupted" ) {
164+ return {
165+ stopReason : "cancelled" ,
166+ } ;
165167 }
166168
169+ return {
170+ stopReason : "end_turn" ,
171+ } ;
172+ } catch ( err ) {
173+ console . error ( `Prompt for session ${ params . sessionId } failed:` , err ) ;
167174 throw err ;
175+ } finally {
176+ sessionState . currentTurnId = null ;
168177 }
169-
170- sessionState . pendingPrompt = null ;
171-
172- return {
173- stopReason : "end_turn" ,
174- } ;
175178 }
176179
177180 private async runWithProcessCheck < T > ( operation : ( ) => Promise < T > ) : Promise < T > {
@@ -191,7 +194,27 @@ export class CodexAcpServer implements acp.Agent {
191194 }
192195
193196 async cancel ( params : acp . CancelNotification ) : Promise < void > {
194- //TODO not supported yet
195- this . sessions . get ( params . sessionId ) ?. pendingPrompt ?. abort ( ) ;
197+ const sessionState = this . sessions . get ( params . sessionId ) ;
198+ if ( ! sessionState ) {
199+ console . info ( `Can not cancel: session ${ params . sessionId } not found` ) ;
200+ return ;
201+ }
202+
203+ if ( ! sessionState . currentTurnId ) {
204+ console . info ( `Can not cancel: session ${ params . sessionId } has no current turn` ) ;
205+ return ;
206+ }
207+
208+ console . info ( `Cancel session ${ params . sessionId } , currentTurnId: ${ sessionState . currentTurnId } ...` ) ;
209+ try {
210+ // After turnInterrupt(), Codex will send turn/completed event, which will naturally complete awaitTurnCompleted()
211+ await this . codexAcpClient . turnInterrupt ( {
212+ threadId : params . sessionId ,
213+ turnId : sessionState . currentTurnId
214+ } ) ;
215+ console . log ( `Cancel - turnInterrupt succeeded` ) ;
216+ } catch ( err ) {
217+ console . error ( `Cancel - turnInterrupt failed:` , err ) ;
218+ }
196219 }
197220}
0 commit comments