@@ -12,6 +12,7 @@ use std::time::Instant;
1212use async_channel:: Sender ;
1313use tokio:: io:: AsyncRead ;
1414use tokio:: io:: AsyncReadExt ;
15+ use tokio:: io:: AsyncWriteExt ;
1516use tokio:: io:: BufReader ;
1617use tokio:: process:: Child ;
1718use tokio_util:: sync:: CancellationToken ;
@@ -80,13 +81,21 @@ pub struct ExecParams {
8081 pub expiration : ExecExpiration ,
8182 pub env : HashMap < String , String > ,
8283 pub network : Option < NetworkProxy > ,
84+ pub stdin : ExecStdin ,
8385 pub sandbox_permissions : SandboxPermissions ,
8486 pub windows_sandbox_level : codex_protocol:: config_types:: WindowsSandboxLevel ,
8587 pub windows_sandbox_private_desktop : bool ,
8688 pub justification : Option < String > ,
8789 pub arg0 : Option < String > ,
8890}
8991
92+ #[ derive( Clone , Debug , Default ) ]
93+ pub enum ExecStdin {
94+ #[ default]
95+ Closed ,
96+ Bytes ( Vec < u8 > ) ,
97+ }
98+
9099fn select_process_exec_tool_sandbox_type (
91100 file_system_sandbox_policy : & FileSystemSandboxPolicy ,
92101 network_sandbox_policy : NetworkSandboxPolicy ,
@@ -231,6 +240,7 @@ pub fn build_exec_request(
231240 mut env,
232241 expiration,
233242 network,
243+ stdin : _stdin,
234244 sandbox_permissions,
235245 windows_sandbox_level,
236246 windows_sandbox_private_desktop,
@@ -291,6 +301,7 @@ pub(crate) async fn execute_exec_request(
291301 cwd,
292302 env,
293303 network,
304+ stdin,
294305 expiration,
295306 sandbox,
296307 windows_sandbox_level,
@@ -310,6 +321,7 @@ pub(crate) async fn execute_exec_request(
310321 expiration,
311322 env,
312323 network : network. clone ( ) ,
324+ stdin,
313325 sandbox_permissions,
314326 windows_sandbox_level,
315327 windows_sandbox_private_desktop,
@@ -343,6 +355,7 @@ pub(crate) async fn execute_exec_request_raw_output(
343355 cwd,
344356 env,
345357 network,
358+ stdin,
346359 expiration,
347360 sandbox,
348361 windows_sandbox_level,
@@ -362,6 +375,7 @@ pub(crate) async fn execute_exec_request_raw_output(
362375 expiration,
363376 env,
364377 network : network. clone ( ) ,
378+ stdin,
365379 sandbox_permissions,
366380 windows_sandbox_level,
367381 windows_sandbox_private_desktop,
@@ -465,6 +479,7 @@ async fn exec_windows_sandbox(
465479 cwd,
466480 mut env,
467481 network,
482+ stdin,
468483 expiration,
469484 windows_sandbox_level,
470485 windows_sandbox_private_desktop,
@@ -501,6 +516,10 @@ async fn exec_windows_sandbox(
501516 command,
502517 & cwd,
503518 env,
519+ match stdin {
520+ ExecStdin :: Closed => None ,
521+ ExecStdin :: Bytes ( bytes) => Some ( bytes) ,
522+ } ,
504523 timeout_ms,
505524 windows_sandbox_private_desktop,
506525 )
@@ -512,6 +531,10 @@ async fn exec_windows_sandbox(
512531 command,
513532 & cwd,
514533 env,
534+ match stdin {
535+ ExecStdin :: Closed => None ,
536+ ExecStdin :: Bytes ( bytes) => Some ( bytes) ,
537+ } ,
515538 timeout_ms,
516539 windows_sandbox_private_desktop,
517540 )
@@ -917,6 +940,7 @@ async fn exec(
917940 mut env,
918941 network,
919942 arg0,
943+ stdin,
920944 expiration,
921945 windows_sandbox_level : _,
922946 ..
@@ -944,12 +968,13 @@ async fn exec(
944968 network : None ,
945969 stdio_policy : StdioPolicy :: RedirectForShellTool ,
946970 env,
971+ stdin_open : matches ! ( stdin, ExecStdin :: Bytes ( _) ) ,
947972 } )
948973 . await ?;
949974 if let Some ( after_spawn) = after_spawn {
950975 after_spawn ( ) ;
951976 }
952- consume_truncated_output ( child, expiration, stdout_stream) . await
977+ consume_truncated_output ( child, stdin , expiration, stdout_stream) . await
953978}
954979
955980#[ cfg_attr( not( target_os = "windows" ) , allow( dead_code) ) ]
@@ -1007,9 +1032,18 @@ fn windows_restricted_token_sandbox_support(
10071032/// use as the output of a `shell` tool call. Also enforces specified timeout.
10081033async fn consume_truncated_output (
10091034 mut child : Child ,
1035+ stdin : ExecStdin ,
10101036 expiration : ExecExpiration ,
10111037 stdout_stream : Option < StdoutStream > ,
10121038) -> Result < RawExecToolCallOutput > {
1039+ let stdin_task = match ( child. stdin . take ( ) , stdin) {
1040+ ( Some ( mut child_stdin) , ExecStdin :: Bytes ( bytes) ) => Some ( tokio:: spawn ( async move {
1041+ child_stdin. write_all ( & bytes) . await ?;
1042+ child_stdin. shutdown ( ) . await
1043+ } ) ) ,
1044+ _ => None ,
1045+ } ;
1046+
10131047 // Both stdout and stderr were configured with `Stdio::piped()`
10141048 // above, therefore `take()` should normally return `Some`. If it doesn't
10151049 // we treat it as an exceptional I/O error
@@ -1089,6 +1123,13 @@ async fn consume_truncated_output(
10891123 Duration :: from_millis ( IO_DRAIN_TIMEOUT_MS ) ,
10901124 )
10911125 . await ?;
1126+ if let Some ( stdin_task) = stdin_task {
1127+ match stdin_task. await {
1128+ Ok ( Ok ( ( ) ) ) => { }
1129+ Ok ( Err ( err) ) => return Err ( CodexErr :: Io ( err) ) ,
1130+ Err ( join_err) => return Err ( CodexErr :: Io ( io:: Error :: other ( join_err) ) ) ,
1131+ }
1132+ }
10921133 let aggregated_output = aggregate_output ( & stdout, & stderr) ;
10931134
10941135 Ok ( RawExecToolCallOutput {
0 commit comments