@@ -18,13 +18,15 @@ import type {
1818 ItemCompletedNotification ,
1919 ItemStartedNotification , ThreadItem ,
2020 ModelReroutedNotification ,
21+ TerminalInteractionNotification ,
2122 ThreadTokenUsageUpdatedNotification ,
2223 TurnPlanUpdatedNotification ,
2324 WarningNotification
2425} from "./app-server/v2" ;
2526import type { McpStartupCompleteEvent } from "./app-server" ;
2627import { toTokenCount } from "./TokenCount" ;
2728import {
29+ commandExecutionUsesTerminalOutput ,
2830 createCommandExecutionUpdate ,
2931 createDynamicToolCallUpdate ,
3032 createFileChangeUpdate ,
@@ -36,6 +38,7 @@ import {
3638 fuzzyFileSearchToolCallId ,
3739} from "./CodexToolCallMapper" ;
3840import { stripShellPrefix } from "./CommandUtils" ;
41+ import { createTerminalOutputMeta , type TerminalOutputMode } from "./TerminalOutputMode" ;
3942
4043export { stripShellPrefix } ;
4144
@@ -45,6 +48,8 @@ export class CodexEventHandler {
4548 private readonly sessionState : SessionState ;
4649 private failure : RequestError | null = null ;
4750 private readonly activeFuzzyFileSearchSessions = new Set < string > ( ) ;
51+ private readonly terminalCommandIds = new Set < string > ( ) ;
52+ private readonly terminalCommandOutputIds = new Set < string > ( ) ;
4853
4954 constructor ( connection : acp . AgentSideConnection , sessionState : SessionState ) {
5055 this . connection = connection ;
@@ -138,6 +143,8 @@ export class CodexEventHandler {
138143 return this . handleFuzzyFileSearchSessionUpdated ( notification . params ) ;
139144 case "fuzzyFileSearch/sessionCompleted" :
140145 return this . handleFuzzyFileSearchSessionCompleted ( notification . params ) ;
146+ case "item/commandExecution/terminalInteraction" :
147+ return this . createTerminalInteractionEvent ( notification . params ) ;
141148 // ignored events
142149 case "command/exec/outputDelta" :
143150 case "item/autoApprovalReview/started" :
@@ -148,7 +155,6 @@ export class CodexEventHandler {
148155 case "item/reasoning/summaryPartAdded" :
149156 case "item/reasoning/textDelta" :
150157 case "turn/diff/updated" :
151- case "item/commandExecution/terminalInteraction" :
152158 case "item/fileChange/outputDelta" :
153159 case "item/fileChange/patchUpdated" :
154160 case "account/updated" :
@@ -249,8 +255,15 @@ export class CodexEventHandler {
249255 switch ( event . item . type ) {
250256 case "fileChange" :
251257 return await createFileChangeUpdate ( event . item ) ;
252- case "commandExecution" :
258+ case "commandExecution" : {
259+ if ( commandExecutionUsesTerminalOutput ( event . item ) ) {
260+ this . terminalCommandIds . add ( event . item . id ) ;
261+ } else {
262+ this . terminalCommandIds . delete ( event . item . id ) ;
263+ this . terminalCommandOutputIds . delete ( event . item . id ) ;
264+ }
253265 return await createCommandExecutionUpdate ( event . item ) ;
266+ }
254267 case "mcpToolCall" :
255268 return await createMcpToolCallUpdate ( event . item ) ;
256269 case "dynamicToolCall" :
@@ -316,18 +329,40 @@ export class CodexEventHandler {
316329 }
317330
318331 private createCommandOutputDeltaEvent ( event : CommandExecutionOutputDeltaNotification ) : UpdateSessionEvent {
332+ if ( this . terminalCommandIds . has ( event . itemId ) && event . delta . length > 0 ) {
333+ this . terminalCommandOutputIds . add ( event . itemId ) ;
334+ }
335+ return this . createCommandOutputEvent ( event . itemId , event . delta , this . commandOutputMode ( event . itemId ) ) ;
336+ }
337+
338+ private createCommandOutputEvent (
339+ itemId : string ,
340+ data : string ,
341+ terminalOutputMode : TerminalOutputMode
342+ ) : UpdateSessionEvent {
319343 return {
320344 sessionUpdate : "tool_call_update" ,
321- toolCallId : event . itemId ,
322- _meta : {
323- terminal_output_delta : {
324- data : event . delta ,
325- terminal_id : event . itemId
326- }
327- }
345+ toolCallId : itemId ,
346+ _meta : createTerminalOutputMeta ( terminalOutputMode , itemId , data ) ,
328347 }
329348 }
330349
350+ private createTerminalInteractionEvent ( event : TerminalInteractionNotification ) : UpdateSessionEvent {
351+ return this . createCommandOutputDeltaEvent ( {
352+ threadId : event . threadId ,
353+ turnId : event . turnId ,
354+ itemId : event . itemId ,
355+ delta : `\n${ event . stdin } \n` ,
356+ } ) ;
357+ }
358+
359+ private commandOutputMode ( itemId : string ) : TerminalOutputMode {
360+ if ( this . sessionState . terminalOutputMode === "terminal_output" && ! this . terminalCommandIds . has ( itemId ) ) {
361+ return "terminal_output_delta" ;
362+ }
363+ return this . sessionState . terminalOutputMode ;
364+ }
365+
331366 private createMcpToolProgressEvent ( event : { itemId : string , message : string } ) : UpdateSessionEvent {
332367 const logDelta = event . message . trim ( ) ;
333368 return {
@@ -376,22 +411,37 @@ export class CodexEventHandler {
376411 }
377412
378413 private completeCommandExecutionEvent ( item : ThreadItem & { "type" : "commandExecution" } ) : UpdateSessionEvent {
379- return {
414+ const update : UpdateSessionEvent = {
380415 sessionUpdate : "tool_call_update" ,
381416 toolCallId : item . id ,
382417 status : item . status === "completed" ? "completed" : "failed" ,
383418 rawOutput : {
384419 formatted_output : item . aggregatedOutput ?? "" ,
385420 exit_code : item . exitCode
386421 } ,
387- _meta : {
388- terminal_exit : {
389- exit_code : item . exitCode ,
390- signal : null ,
391- terminal_id : item . id
392- }
393- }
422+ } ;
423+
424+ const commandHadTerminal = this . terminalCommandIds . delete ( item . id ) ;
425+ const commandHadOutput = this . terminalCommandOutputIds . delete ( item . id ) ;
426+ if ( ! commandHadTerminal ) {
427+ return update ;
428+ }
429+ const terminalMeta : Record < string , unknown > = { } ;
430+ if ( ! commandHadOutput && item . aggregatedOutput ) {
431+ Object . assign (
432+ terminalMeta ,
433+ createTerminalOutputMeta ( this . sessionState . terminalOutputMode , item . id , item . aggregatedOutput )
434+ ) ;
394435 }
436+ terminalMeta [ "terminal_exit" ] = {
437+ exit_code : item . exitCode ,
438+ signal : null ,
439+ terminal_id : item . id
440+ } ;
441+ return {
442+ ...update ,
443+ _meta : terminalMeta ,
444+ } ;
395445 }
396446
397447 private async updatePlan ( event : TurnPlanUpdatedNotification ) : Promise < UpdateSessionEvent > {
0 commit comments