1+ use crate :: features:: Feature ;
12use crate :: function_tool:: FunctionCallError ;
23use crate :: is_safe_command:: is_known_safe_command;
4+ use crate :: powershell:: prefix_utf8_output;
35use crate :: protocol:: EventMsg ;
46use crate :: protocol:: ExecCommandSource ;
57use crate :: protocol:: TerminalInteractionEvent ;
68use crate :: sandboxing:: SandboxPermissions ;
79use crate :: shell:: Shell ;
10+ use crate :: shell:: ShellType ;
811use crate :: shell:: get_shell_by_model_provided_path;
912use crate :: tools:: context:: ToolInvocation ;
1013use crate :: tools:: context:: ToolOutput ;
@@ -23,7 +26,6 @@ use crate::unified_exec::WriteStdinRequest;
2326use async_trait:: async_trait;
2427use serde:: Deserialize ;
2528use std:: path:: PathBuf ;
26- use std:: sync:: Arc ;
2729
2830pub struct UnifiedExecHandler ;
2931
@@ -92,7 +94,14 @@ impl ToolHandler for UnifiedExecHandler {
9294 let Ok ( params) = serde_json:: from_str :: < ExecCommandArgs > ( arguments) else {
9395 return true ;
9496 } ;
95- let command = get_command ( & params, invocation. session . user_shell ( ) ) ;
97+ let command = get_command (
98+ & params,
99+ invocation. session . user_shell ( ) . as_ref ( ) ,
100+ invocation
101+ . session
102+ . features ( )
103+ . enabled ( Feature :: PowershellUtf8 ) ,
104+ ) ;
96105 !is_known_safe_command ( & command)
97106 }
98107
@@ -127,7 +136,11 @@ impl ToolHandler for UnifiedExecHandler {
127136 ) )
128137 } ) ?;
129138 let process_id = manager. allocate_process_id ( ) . await ;
130- let command = get_command ( & args, session. user_shell ( ) ) ;
139+ let command = get_command (
140+ & args,
141+ session. user_shell ( ) . as_ref ( ) ,
142+ session. features ( ) . enabled ( Feature :: PowershellUtf8 ) ,
143+ ) ;
131144
132145 let ExecCommandArgs {
133146 workdir,
@@ -250,16 +263,26 @@ impl ToolHandler for UnifiedExecHandler {
250263 }
251264}
252265
253- fn get_command ( args : & ExecCommandArgs , session_shell : Arc < Shell > ) -> Vec < String > {
266+ fn get_command (
267+ args : & ExecCommandArgs ,
268+ session_shell : & Shell ,
269+ powershell_utf8_enabled : bool ,
270+ ) -> Vec < String > {
254271 let model_shell = args. shell . as_ref ( ) . map ( |shell_str| {
255272 let mut shell = get_shell_by_model_provided_path ( & PathBuf :: from ( shell_str) ) ;
256273 shell. shell_snapshot = None ;
257274 shell
258275 } ) ;
276+ let shell = model_shell. as_ref ( ) . unwrap_or ( session_shell) ;
259277
260- let shell = model_shell. as_ref ( ) . unwrap_or ( session_shell. as_ref ( ) ) ;
278+ let command_str =
279+ if matches ! ( shell. shell_type, ShellType :: PowerShell ) && powershell_utf8_enabled {
280+ prefix_utf8_output ( & args. cmd )
281+ } else {
282+ args. cmd . to_string ( )
283+ } ;
261284
262- shell. derive_exec_args ( & args . cmd , args. login )
285+ shell. derive_exec_args ( & command_str , args. login )
263286}
264287
265288fn format_response ( response : & UnifiedExecResponse ) -> String {
@@ -297,7 +320,6 @@ mod tests {
297320 use crate :: powershell:: prefix_utf8_output;
298321 use crate :: shell:: ShellType ;
299322 use crate :: shell:: default_user_shell;
300- use std:: sync:: Arc ;
301323
302324 #[ test]
303325 fn test_get_command_uses_default_shell_when_unspecified ( ) {
@@ -308,14 +330,16 @@ mod tests {
308330
309331 assert ! ( args. shell. is_none( ) ) ;
310332
311- let session_shell = Arc :: new ( default_user_shell ( ) ) ;
312- let expected_script = if session_shell. shell_type == ShellType :: PowerShell {
313- prefix_utf8_output ( "echo hello" )
314- } else {
315- "echo hello" . to_string ( )
316- } ;
333+ let session_shell = default_user_shell ( ) ;
334+ let powershell_utf8_enabled = false ;
335+ let expected_script =
336+ if session_shell. shell_type == ShellType :: PowerShell && powershell_utf8_enabled {
337+ prefix_utf8_output ( "echo hello" )
338+ } else {
339+ "echo hello" . to_string ( )
340+ } ;
317341
318- let command = get_command ( & args, session_shell) ;
342+ let command = get_command ( & args, & session_shell, powershell_utf8_enabled ) ;
319343
320344 assert_eq ! ( command. len( ) , 3 ) ;
321345 assert_eq ! ( command. last( ) , Some ( & expected_script) ) ;
@@ -330,7 +354,7 @@ mod tests {
330354
331355 assert_eq ! ( args. shell. as_deref( ) , Some ( "/bin/bash" ) ) ;
332356
333- let command = get_command ( & args, Arc :: new ( default_user_shell ( ) ) ) ;
357+ let command = get_command ( & args, & default_user_shell ( ) , false ) ;
334358
335359 assert_eq ! ( command. last( ) , Some ( & "echo hello" . to_string( ) ) ) ;
336360 if command
@@ -350,9 +374,17 @@ mod tests {
350374
351375 assert_eq ! ( args. shell. as_deref( ) , Some ( "powershell" ) ) ;
352376
353- let command = get_command ( & args, Arc :: new ( default_user_shell ( ) ) ) ;
354-
355- assert_eq ! ( command[ 2 ] , prefix_utf8_output( "echo hello" ) ) ;
377+ let expected_shell = get_shell_by_model_provided_path ( & PathBuf :: from ( "powershell" ) ) ;
378+ let powershell_utf8_enabled = true ;
379+ let command = get_command ( & args, & default_user_shell ( ) , powershell_utf8_enabled) ;
380+ let expected_script =
381+ if expected_shell. shell_type == ShellType :: PowerShell && powershell_utf8_enabled {
382+ prefix_utf8_output ( "echo hello" )
383+ } else {
384+ "echo hello" . to_string ( )
385+ } ;
386+
387+ assert_eq ! ( command[ 2 ] , expected_script) ;
356388 }
357389
358390 #[ test]
@@ -364,7 +396,7 @@ mod tests {
364396
365397 assert_eq ! ( args. shell. as_deref( ) , Some ( "cmd" ) ) ;
366398
367- let command = get_command ( & args, Arc :: new ( default_user_shell ( ) ) ) ;
399+ let command = get_command ( & args, & default_user_shell ( ) , false ) ;
368400
369401 assert_eq ! ( command[ 2 ] , "echo hello" ) ;
370402 }
0 commit comments