@@ -35,6 +35,7 @@ import type {
3535 ListMcpServerStatusParams ,
3636 ListMcpServerStatusResponse , ConfigReadParams , ConfigReadResponse ,
3737} from "./app-server/v2" ;
38+ import { logger } from "./Logger" ;
3839
3940export interface ApprovalHandler {
4041 handleCommandExecution ( params : CommandExecutionRequestApprovalParams ) : Promise < CommandExecutionRequestApprovalResponse > ;
@@ -60,6 +61,8 @@ const FileChangeApprovalRequest = new RequestType<
6061export class CodexAppServerClient {
6162 readonly connection : MessageConnection ;
6263 private approvalHandlers = new Map < string , ApprovalHandler > ( ) ;
64+ private readonly notificationHandlers = new Map < string , ( event : ServerNotification ) => void | Promise < void > > ( ) ;
65+ private readonly notificationQueues = new Map < string , Promise < void > | null > ( ) ;
6366 private mcpStartupCompleteVersion = 0 ;
6467 private lastMcpStartupComplete : McpStartupCompleteEvent | null = null ;
6568 private readonly mcpStartupCompleteResolvers : Array < SignalResolver < McpStartupCompleteEvent > > = [ ] ;
@@ -190,22 +193,56 @@ export class CodexAppServerClient {
190193 * Registers a notification handler for a specific session.
191194 * Replaces any existing handler for the same session, preventing handler accumulation.
192195 */
193- onServerNotification ( sessionId : string , callback : ( event : ServerNotification ) => void ) {
196+ onServerNotification ( sessionId : string , callback : ( event : ServerNotification ) => void | Promise < void > ) {
194197 this . notificationHandlers . set ( sessionId , callback ) ;
198+ this . notificationQueues . set ( sessionId , null ) ;
195199 }
196200
197201 private codexEventHandlers : Array < ( event : CodexConnectionEvent ) => void > = [ ] ;
198202 onClientTransportEvent ( callback : ( event : CodexConnectionEvent ) => void ) {
199203 this . codexEventHandlers . push ( callback ) ;
200204 }
201205
202- private notificationHandlers = new Map < string , ( event : ServerNotification ) => void > ( ) ;
203206 private notify ( notification : ServerNotification ) {
204- for ( const notificationHandler of this . notificationHandlers . values ( ) ) {
205- notificationHandler ( notification ) ;
207+ for ( const [ sessionId , notificationHandler ] of this . notificationHandlers . entries ( ) ) {
208+ const queue = this . notificationQueues . get ( sessionId ) ;
209+ if ( queue ) {
210+ const next = queue
211+ . then ( ( ) => notificationHandler ( notification ) )
212+ . catch ( ( error ) => {
213+ logger . error ( "Error handling server notification" , error ) ;
214+ } ) ;
215+ this . notificationQueues . set ( sessionId , this . trackNotificationQueue ( sessionId , next ) ) ;
216+ continue ;
217+ }
218+
219+ try {
220+ const result = notificationHandler ( notification ) ;
221+ if ( result instanceof Promise ) {
222+ const next = result . catch ( ( error ) => {
223+ logger . error ( "Error handling server notification" , error ) ;
224+ } ) ;
225+ this . notificationQueues . set ( sessionId , this . trackNotificationQueue ( sessionId , next ) ) ;
226+ }
227+ } catch ( error ) {
228+ logger . error ( "Error handling server notification" , error ) ;
229+ }
206230 }
207231 }
208232
233+ async flushServerNotifications ( sessionId : string ) : Promise < void > {
234+ await ( this . notificationQueues . get ( sessionId ) ?? Promise . resolve ( ) ) ;
235+ }
236+
237+ private trackNotificationQueue ( sessionId : string , queue : Promise < void > ) : Promise < void > {
238+ const trackedQueue = queue . finally ( ( ) => {
239+ if ( this . notificationQueues . get ( sessionId ) === trackedQueue ) {
240+ this . notificationQueues . set ( sessionId , null ) ;
241+ }
242+ } ) ;
243+ return trackedQueue ;
244+ }
245+
209246 private resolveSignal < T > (
210247 event : T ,
211248 version : number ,
0 commit comments