Skip to content

Commit 9fb1a0c

Browse files
use feature
1 parent 6c18f9c commit 9fb1a0c

File tree

4 files changed

+71
-22
lines changed

4 files changed

+71
-22
lines changed

codex-rs/core/src/features.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ pub enum Feature {
9393
Tui2,
9494
/// Enable discovery and injection of skills.
9595
Skills,
96+
/// Enforce UTF8 output in Powershell.
97+
PowershellUtf8,
9698
}
9799

98100
impl Feature {
@@ -403,6 +405,12 @@ pub const FEATURES: &[FeatureSpec] = &[
403405
stage: Stage::Experimental,
404406
default_enabled: false,
405407
},
408+
FeatureSpec {
409+
id: Feature::PowershellUtf8,
410+
key: "powershell_utf8",
411+
stage: Stage::Experimental,
412+
default_enabled: false,
413+
},
406414
FeatureSpec {
407415
id: Feature::Tui2,
408416
key: "tui2",

codex-rs/core/src/shell.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use serde::Serialize;
33
use std::path::PathBuf;
44
use std::sync::Arc;
55

6-
use crate::powershell::prefix_utf8_output;
76
use crate::shell_snapshot::ShellSnapshot;
87

98
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
@@ -53,7 +52,7 @@ impl Shell {
5352
}
5453

5554
args.push("-Command".to_string());
56-
args.push(prefix_utf8_output(command));
55+
args.push(command.to_string());
5756
args
5857
}
5958
ShellType::Cmd => {

codex-rs/core/src/tools/handlers/shell.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ use crate::codex::TurnContext;
77
use crate::exec::ExecParams;
88
use crate::exec_env::create_env;
99
use crate::exec_policy::create_exec_approval_requirement_for_command;
10+
use crate::features::Feature;
1011
use crate::function_tool::FunctionCallError;
1112
use crate::is_safe_command::is_known_safe_command;
13+
use crate::powershell::prefix_utf8_output;
1214
use crate::protocol::ExecCommandSource;
1315
use crate::shell::Shell;
16+
use crate::shell::ShellType;
1417
use crate::tools::context::ToolInvocation;
1518
use crate::tools::context::ToolOutput;
1619
use crate::tools::context::ToolPayload;
@@ -54,7 +57,14 @@ impl ShellCommandHandler {
5457
turn_context: &TurnContext,
5558
) -> ExecParams {
5659
let shell = session.user_shell();
57-
let command = Self::base_command(shell.as_ref(), &params.command, params.login);
60+
let command_str = if matches!(shell.shell_type, ShellType::PowerShell)
61+
&& session.features().enabled(Feature::PowershellUtf8)
62+
{
63+
prefix_utf8_output(&params.command)
64+
} else {
65+
params.command.to_string()
66+
};
67+
let command = Self::base_command(shell.as_ref(), &command_str, params.login);
5868

5969
ExecParams {
6070
command,

codex-rs/core/src/tools/handlers/unified_exec.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use crate::features::Feature;
12
use crate::function_tool::FunctionCallError;
23
use crate::is_safe_command::is_known_safe_command;
4+
use crate::powershell::prefix_utf8_output;
35
use crate::protocol::EventMsg;
46
use crate::protocol::ExecCommandSource;
57
use crate::protocol::TerminalInteractionEvent;
68
use crate::sandboxing::SandboxPermissions;
79
use crate::shell::Shell;
10+
use crate::shell::ShellType;
811
use crate::shell::get_shell_by_model_provided_path;
912
use crate::tools::context::ToolInvocation;
1013
use crate::tools::context::ToolOutput;
@@ -23,7 +26,6 @@ use crate::unified_exec::WriteStdinRequest;
2326
use async_trait::async_trait;
2427
use serde::Deserialize;
2528
use std::path::PathBuf;
26-
use std::sync::Arc;
2729

2830
pub 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

265288
fn 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

Comments
 (0)