@@ -23,6 +23,7 @@ import type {
2323 ReasoningSummaryPartAddedNotification ,
2424 ReasoningSummaryTextDeltaNotification ,
2525 ReasoningTextDeltaNotification ,
26+ TerminalInteractionNotification ,
2627 ThreadGoalClearedNotification ,
2728 ThreadGoalUpdatedNotification ,
2829 ThreadTokenUsageUpdatedNotification ,
@@ -32,6 +33,7 @@ import type {
3233import type { McpStartupCompleteEvent } from "./app-server" ;
3334import { toTokenCount } from "./TokenCount" ;
3435import {
36+ commandExecutionUsesTerminalOutput ,
3537 createCommandExecutionUpdate ,
3638 createDynamicToolCallUpdate ,
3739 createFileChangeUpdate ,
@@ -51,6 +53,7 @@ import {
5153 fuzzyFileSearchToolCallId ,
5254} from "./CodexToolCallMapper" ;
5355import { stripShellPrefix } from "./CommandUtils" ;
56+ import { createTerminalOutputMeta , type TerminalOutputMode } from "./TerminalOutputMode" ;
5457
5558export { stripShellPrefix } ;
5659
@@ -64,6 +67,8 @@ export class CodexEventHandler {
6467 private readonly activeImageGenerationItems = new Set < string > ( ) ;
6568 private readonly emittedImageViewItems = new Set < string > ( ) ;
6669 private readonly seenReasoningDeltaItemIds = new Set < string > ( ) ;
70+ private readonly terminalCommandIds = new Set < string > ( ) ;
71+ private readonly terminalCommandOutputIds = new Set < string > ( ) ;
6772
6873 constructor ( connection : acp . AgentSideConnection , sessionState : SessionState ) {
6974 this . connection = connection ;
@@ -165,13 +170,14 @@ export class CodexEventHandler {
165170 return this . createThreadGoalUpdatedEvent ( notification . params ) ;
166171 case "thread/goal/cleared" :
167172 return this . createThreadGoalClearedEvent ( notification . params ) ;
173+ case "item/commandExecution/terminalInteraction" :
174+ return this . createTerminalInteractionEvent ( notification . params ) ;
168175 // ignored events
169176 case "command/exec/outputDelta" :
170177 case "hook/started" :
171178 case "hook/completed" :
172179 case "turn/diff/updated" :
173180 case "turn/moderationMetadata" :
174- case "item/commandExecution/terminalInteraction" :
175181 case "item/fileChange/outputDelta" :
176182 case "item/fileChange/patchUpdated" :
177183 case "account/updated" :
@@ -324,8 +330,15 @@ export class CodexEventHandler {
324330 switch ( event . item . type ) {
325331 case "fileChange" :
326332 return await createFileChangeUpdate ( event . item ) ;
327- case "commandExecution" :
333+ case "commandExecution" : {
334+ if ( commandExecutionUsesTerminalOutput ( event . item ) ) {
335+ this . terminalCommandIds . add ( event . item . id ) ;
336+ } else {
337+ this . terminalCommandIds . delete ( event . item . id ) ;
338+ this . terminalCommandOutputIds . delete ( event . item . id ) ;
339+ }
328340 return await createCommandExecutionUpdate ( event . item ) ;
341+ }
329342 case "mcpToolCall" :
330343 return await createMcpToolCallUpdate ( event . item ) ;
331344 case "dynamicToolCall" :
@@ -435,18 +448,40 @@ export class CodexEventHandler {
435448 }
436449
437450 private createCommandOutputDeltaEvent ( event : CommandExecutionOutputDeltaNotification ) : UpdateSessionEvent {
451+ if ( this . terminalCommandIds . has ( event . itemId ) && event . delta . length > 0 ) {
452+ this . terminalCommandOutputIds . add ( event . itemId ) ;
453+ }
454+ return this . createCommandOutputEvent ( event . itemId , event . delta , this . commandOutputMode ( event . itemId ) ) ;
455+ }
456+
457+ private createCommandOutputEvent (
458+ itemId : string ,
459+ data : string ,
460+ terminalOutputMode : TerminalOutputMode
461+ ) : UpdateSessionEvent {
438462 return {
439463 sessionUpdate : "tool_call_update" ,
440- toolCallId : event . itemId ,
441- _meta : {
442- terminal_output_delta : {
443- data : event . delta ,
444- terminal_id : event . itemId
445- }
446- }
464+ toolCallId : itemId ,
465+ _meta : createTerminalOutputMeta ( terminalOutputMode , itemId , data ) ,
447466 }
448467 }
449468
469+ private createTerminalInteractionEvent ( event : TerminalInteractionNotification ) : UpdateSessionEvent {
470+ return this . createCommandOutputDeltaEvent ( {
471+ threadId : event . threadId ,
472+ turnId : event . turnId ,
473+ itemId : event . itemId ,
474+ delta : `\n${ event . stdin } \n` ,
475+ } ) ;
476+ }
477+
478+ private commandOutputMode ( itemId : string ) : TerminalOutputMode {
479+ if ( this . sessionState . terminalOutputMode === "terminal_output" && ! this . terminalCommandIds . has ( itemId ) ) {
480+ return "terminal_output_delta" ;
481+ }
482+ return this . sessionState . terminalOutputMode ;
483+ }
484+
450485 private createMcpToolProgressEvent ( event : { itemId : string , message : string } ) : UpdateSessionEvent {
451486 const logDelta = event . message . trim ( ) ;
452487 return {
@@ -495,22 +530,37 @@ export class CodexEventHandler {
495530 }
496531
497532 private completeCommandExecutionEvent ( item : ThreadItem & { "type" : "commandExecution" } ) : UpdateSessionEvent {
498- return {
533+ const update : UpdateSessionEvent = {
499534 sessionUpdate : "tool_call_update" ,
500535 toolCallId : item . id ,
501536 status : item . status === "completed" ? "completed" : "failed" ,
502537 rawOutput : {
503538 formatted_output : item . aggregatedOutput ?? "" ,
504539 exit_code : item . exitCode
505540 } ,
506- _meta : {
507- terminal_exit : {
508- exit_code : item . exitCode ,
509- signal : null ,
510- terminal_id : item . id
511- }
512- }
541+ } ;
542+
543+ const commandHadTerminal = this . terminalCommandIds . delete ( item . id ) ;
544+ const commandHadOutput = this . terminalCommandOutputIds . delete ( item . id ) ;
545+ if ( ! commandHadTerminal ) {
546+ return update ;
547+ }
548+ const terminalMeta : Record < string , unknown > = { } ;
549+ if ( ! commandHadOutput && item . aggregatedOutput ) {
550+ Object . assign (
551+ terminalMeta ,
552+ createTerminalOutputMeta ( this . sessionState . terminalOutputMode , item . id , item . aggregatedOutput )
553+ ) ;
513554 }
555+ terminalMeta [ "terminal_exit" ] = {
556+ exit_code : item . exitCode ,
557+ signal : null ,
558+ terminal_id : item . id
559+ } ;
560+ return {
561+ ...update ,
562+ _meta : terminalMeta ,
563+ } ;
514564 }
515565
516566 private async updatePlan ( event : TurnPlanUpdatedNotification ) : Promise < UpdateSessionEvent > {
0 commit comments