@@ -33,9 +33,8 @@ import type { McpStartupCompleteEvent } from "./app-server";
3333import { toTokenCount } from "./TokenCount" ;
3434import {
3535 commandExecutionUsesTerminalOutput ,
36- createCollabAgentToolCallCompleteUpdate ,
37- createCollabAgentToolCallUpdate ,
3836 createCommandExecutionUpdate ,
37+ createCommandExecutionCompleteUpdate ,
3938 createContextCompactionCompleteUpdate ,
4039 createContextCompactionStartUpdate ,
4140 createDynamicToolCallUpdate ,
@@ -51,7 +50,6 @@ import {
5150 createFuzzyFileSearchComplete ,
5251 createFuzzyFileSearchStartOrUpdate ,
5352 createMcpToolCallUpdate ,
54- createSubAgentActivityUpdate ,
5553 createWebSearchCompleteUpdate ,
5654 createWebSearchStartUpdate ,
5755 fuzzyFileSearchToolCallId ,
@@ -64,9 +62,12 @@ import {
6462 createAgentTextThoughtChunk ,
6563} from "./ContentChunks" ;
6664import { sameThreadGoalSnapshot , toThreadGoalSnapshot } from "./ThreadGoalSnapshot" ;
65+ import { getSubAgentActivityTracker } from "./SubAgentActivityTracker" ;
6766
6867export { stripShellPrefix } ;
6968
69+ type UpdateResult = UpdateSessionEvent | UpdateSessionEvent [ ] | null ;
70+
7071export class CodexEventHandler {
7172
7273 private readonly connection : AcpClientConnection ;
@@ -80,7 +81,6 @@ export class CodexEventHandler {
8081 private readonly terminalCommandIds = new Set < string > ( ) ;
8182 private readonly terminalCommandOutputIds = new Set < string > ( ) ;
8283 private readonly agentMessagePhases = new Map < string , string | null > ( ) ;
83- private readonly activeSubAgentActivities = new Set < string > ( ) ;
8484
8585 constructor ( connection : AcpClientConnection , sessionState : SessionState ) {
8686 this . connection = connection ;
@@ -93,13 +93,14 @@ export class CodexEventHandler {
9393
9494 async handleNotification ( notification : ServerNotification ) {
9595 const session = new ACPSessionConnection ( this . connection , this . sessionState . sessionId ) ;
96- const updateEvent = await this . createUpdateEvent ( notification ) ;
97- if ( updateEvent ) {
96+ const result = await this . createUpdateEvent ( notification ) ;
97+ const updateEvents = Array . isArray ( result ) ? result : result ? [ result ] : [ ] ;
98+ for ( const updateEvent of updateEvents ) {
9899 await session . update ( updateEvent ) ;
99100 }
100101 }
101102
102- private async createUpdateEvent ( notification : ServerNotification ) : Promise < UpdateSessionEvent | null > {
103+ private async createUpdateEvent ( notification : ServerNotification ) : Promise < UpdateResult > {
103104 /*
104105 TODO split UpdateSessionEvent to improve completion
105106 createUpdateEvent({
@@ -122,6 +123,12 @@ export class CodexEventHandler {
122123 this . sessionState . currentTurnId = notification . params . turn . id ;
123124 return null ;
124125 case "turn/completed" :
126+ if ( notification . params . threadId !== this . sessionState . sessionId ) {
127+ return getSubAgentActivityTracker ( this . sessionState ) . completeChildTurn (
128+ notification . params . threadId ,
129+ notification . params . turn ,
130+ ) ;
131+ }
125132 this . sessionState . currentTurnId = null ;
126133 return null ;
127134 case "thread/tokenUsage/updated" :
@@ -299,18 +306,21 @@ export class CodexEventHandler {
299306 return createAgentTextThoughtChunk ( text , messageId ) ;
300307 }
301308
302- private async createItemEvent ( event : ItemStartedNotification ) : Promise < UpdateSessionEvent | null > {
309+ private async createItemEvent ( event : ItemStartedNotification ) : Promise < UpdateResult > {
303310 switch ( event . item . type ) {
304311 case "fileChange" :
305312 return await createFileChangeUpdate ( event . item ) ;
306313 case "commandExecution" : {
307- if ( commandExecutionUsesTerminalOutput ( event . item ) ) {
314+ if (
315+ this . sessionState . terminalOutputMode !== "content"
316+ && commandExecutionUsesTerminalOutput ( event . item )
317+ ) {
308318 this . terminalCommandIds . add ( event . item . id ) ;
309319 } else {
310320 this . terminalCommandIds . delete ( event . item . id ) ;
311321 this . terminalCommandOutputIds . delete ( event . item . id ) ;
312322 }
313- return await createCommandExecutionUpdate ( event . item ) ;
323+ return await createCommandExecutionUpdate ( event . item , this . sessionState . terminalOutputMode ) ;
314324 }
315325 case "mcpToolCall" :
316326 return await createMcpToolCallUpdate ( event . item ) ;
@@ -325,15 +335,14 @@ export class CodexEventHandler {
325335 this . activeImageGenerationItems . add ( event . item . id ) ;
326336 return createImageGenerationStartUpdate ( event . item ) ;
327337 case "collabAgentToolCall" :
328- return createCollabAgentToolCallUpdate ( event . item ) ;
338+ return getSubAgentActivityTracker ( this . sessionState ) . mapCollabAgentToolCall ( event . item , "started" ) ;
329339 case "agentMessage" :
330340 this . rememberAgentMessagePhase ( event . item ) ;
331341 return null ;
332342 case "contextCompaction" :
333343 return createContextCompactionStartUpdate ( event . item ) ;
334344 case "subAgentActivity" :
335- this . activeSubAgentActivities . add ( event . item . id ) ;
336- return createSubAgentActivityUpdate ( event . item , "in_progress" , "tool_call" ) ;
345+ return getSubAgentActivityTracker ( this . sessionState ) . mapSubAgentActivity ( event . item , "started" ) ;
337346 case "sleep" :
338347 case "userMessage" :
339348 case "hookPrompt" :
@@ -345,7 +354,7 @@ export class CodexEventHandler {
345354 }
346355 }
347356
348- private async completeItemEvent ( event : ItemCompletedNotification ) : Promise < UpdateSessionEvent | null > {
357+ private async completeItemEvent ( event : ItemCompletedNotification ) : Promise < UpdateResult > {
349358 switch ( event . item . type ) {
350359 case "fileChange" :
351360 case "dynamicToolCall" :
@@ -382,21 +391,24 @@ export class CodexEventHandler {
382391 case "webSearch" :
383392 return createWebSearchCompleteUpdate ( event . item ) ;
384393 case "collabAgentToolCall" :
385- return createCollabAgentToolCallCompleteUpdate ( event . item ) ;
394+ return getSubAgentActivityTracker ( this . sessionState ) . mapCollabAgentToolCall ( event . item , "completed" ) ;
386395 case "agentMessage" :
396+ if ( event . threadId !== this . sessionState . sessionId ) {
397+ getSubAgentActivityTracker ( this . sessionState ) . recordChildMessage (
398+ event . threadId ,
399+ event . item . text ,
400+ ) ;
401+ return null ;
402+ }
387403 this . rememberAgentMessagePhase ( event . item ) ;
388404 return null ;
389405 case "exitedReviewMode" :
390406 return this . createExitedReviewModeEvent ( event . item ) ;
391407 case "contextCompaction" :
392408 return createContextCompactionCompleteUpdate ( event . item ) ;
393409 //ignored types
394- case "subAgentActivity" : {
395- const sessionUpdate = this . activeSubAgentActivities . delete ( event . item . id )
396- ? "tool_call_update"
397- : "tool_call" ;
398- return createSubAgentActivityUpdate ( event . item , "completed" , sessionUpdate ) ;
399- }
410+ case "subAgentActivity" :
411+ return getSubAgentActivityTracker ( this . sessionState ) . mapSubAgentActivity ( event . item , "completed" ) ;
400412 case "sleep" :
401413 case "userMessage" :
402414 case "hookPrompt" :
@@ -433,7 +445,7 @@ export class CodexEventHandler {
433445 }
434446
435447 private createCommandOutputDeltaEvent ( event : CommandExecutionOutputDeltaNotification ) : UpdateSessionEvent {
436- if ( this . terminalCommandIds . has ( event . itemId ) && event . delta . length > 0 ) {
448+ if ( event . delta . length > 0 ) {
437449 this . terminalCommandOutputIds . add ( event . itemId ) ;
438450 }
439451 return this . createCommandOutputEvent ( event . itemId , event . delta , this . commandOutputMode ( event . itemId ) ) ;
@@ -444,6 +456,16 @@ export class CodexEventHandler {
444456 data : string ,
445457 terminalOutputMode : TerminalOutputMode
446458 ) : UpdateSessionEvent {
459+ if ( terminalOutputMode === "content" ) {
460+ return {
461+ sessionUpdate : "tool_call_update" ,
462+ toolCallId : itemId ,
463+ content : [ {
464+ type : "content" ,
465+ content : { type : "text" , text : data } ,
466+ } ] ,
467+ } ;
468+ }
447469 return {
448470 sessionUpdate : "tool_call_update" ,
449471 toolCallId : itemId ,
@@ -515,37 +537,16 @@ export class CodexEventHandler {
515537 }
516538
517539 private completeCommandExecutionEvent ( item : ThreadItem & { "type" : "commandExecution" } ) : UpdateSessionEvent {
518- const update : UpdateSessionEvent = {
519- sessionUpdate : "tool_call_update" ,
520- toolCallId : item . id ,
521- status : item . status === "completed" ? "completed" : "failed" ,
522- rawOutput : {
523- formatted_output : item . aggregatedOutput ?? "" ,
524- exit_code : item . exitCode
525- } ,
526- } ;
527-
528540 const commandHadTerminal = this . terminalCommandIds . delete ( item . id ) ;
529541 const commandHadOutput = this . terminalCommandOutputIds . delete ( item . id ) ;
530- if ( ! commandHadTerminal ) {
531- return update ;
532- }
533- const terminalMeta : Record < string , unknown > = { } ;
534- if ( ! commandHadOutput && item . aggregatedOutput ) {
535- Object . assign (
536- terminalMeta ,
537- createTerminalOutputMeta ( this . sessionState . terminalOutputMode , item . id , item . aggregatedOutput )
538- ) ;
539- }
540- terminalMeta [ "terminal_exit" ] = {
541- exit_code : item . exitCode ,
542- signal : null ,
543- terminal_id : item . id
544- } ;
545- return {
546- ...update ,
547- _meta : terminalMeta ,
548- } ;
542+ return createCommandExecutionCompleteUpdate (
543+ item ,
544+ this . sessionState . terminalOutputMode ,
545+ {
546+ includeOutputContent : ! commandHadOutput ,
547+ includeTerminalMeta : commandHadTerminal ,
548+ } ,
549+ ) ! ;
549550 }
550551
551552 private async updatePlan ( event : TurnPlanUpdatedNotification ) : Promise < UpdateSessionEvent > {
0 commit comments