Compare commits

...

1 Commits

Author SHA1 Message Date
Owen Lin
0f2bc75b26 feat(core): add zsh fork feature and config scaffolding 2026-02-17 12:14:51 -08:00
5 changed files with 79 additions and 0 deletions

View File

@@ -269,6 +269,9 @@
"shell_tool": {
"type": "boolean"
},
"shell_zsh_fork": {
"type": "boolean"
},
"skill_env_var_dependency_prompt": {
"type": "boolean"
},
@@ -287,6 +290,9 @@
"unified_exec": {
"type": "boolean"
},
"unified_exec_zsh_fork": {
"type": "boolean"
},
"use_linux_sandbox_bwrap": {
"type": "boolean"
},
@@ -357,6 +363,14 @@
}
],
"default": null
},
"zsh_path": {
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Optional absolute path to patched zsh used by sidecar-backed shell execution."
}
},
"type": "object"
@@ -1402,6 +1416,9 @@
"shell_tool": {
"type": "boolean"
},
"shell_zsh_fork": {
"type": "boolean"
},
"skill_env_var_dependency_prompt": {
"type": "boolean"
},
@@ -1420,6 +1437,9 @@
"unified_exec": {
"type": "boolean"
},
"unified_exec_zsh_fork": {
"type": "boolean"
},
"use_linux_sandbox_bwrap": {
"type": "boolean"
},
@@ -1758,6 +1778,14 @@
"windows_wsl_setup_acknowledged": {
"description": "Tracks whether the Windows onboarding screen has been acknowledged.",
"type": "boolean"
},
"zsh_path": {
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Optional absolute path to patched zsh used by sidecar-backed shell execution."
}
},
"title": "ConfigToml",

View File

@@ -328,6 +328,8 @@ pub struct Config {
/// Optional absolute path to the Node runtime used by `js_repl`.
pub js_repl_node_path: Option<PathBuf>,
/// Optional absolute path to patched zsh used by sidecar-backed shell execution.
pub zsh_path: Option<PathBuf>,
/// Value to use for `reasoning.effort` when making a request using the
/// Responses API.
@@ -962,6 +964,8 @@ pub struct ConfigToml {
/// Optional absolute path to the Node runtime used by `js_repl`.
pub js_repl_node_path: Option<AbsolutePathBuf>,
/// Optional absolute path to patched zsh used by sidecar-backed shell execution.
pub zsh_path: Option<AbsolutePathBuf>,
/// Profile to use from the `profiles` map.
pub profile: Option<String>,
@@ -1311,6 +1315,7 @@ pub struct ConfigOverrides {
pub config_profile: Option<String>,
pub codex_linux_sandbox_exe: Option<PathBuf>,
pub js_repl_node_path: Option<PathBuf>,
pub zsh_path: Option<PathBuf>,
pub base_instructions: Option<String>,
pub developer_instructions: Option<String>,
pub personality: Option<Personality>,
@@ -1438,6 +1443,7 @@ impl Config {
config_profile: config_profile_key,
codex_linux_sandbox_exe,
js_repl_node_path: js_repl_node_path_override,
zsh_path: zsh_path_override,
base_instructions,
developer_instructions,
personality,
@@ -1674,6 +1680,9 @@ impl Config {
let js_repl_node_path = js_repl_node_path_override
.or(config_profile.js_repl_node_path.map(Into::into))
.or(cfg.js_repl_node_path.map(Into::into));
let zsh_path = zsh_path_override
.or(config_profile.zsh_path.map(Into::into))
.or(cfg.zsh_path.map(Into::into));
let review_model = override_review_model.or(cfg.review_model);
@@ -1796,6 +1805,7 @@ impl Config {
file_opener: cfg.file_opener.unwrap_or(UriBasedFileOpener::VsCode),
codex_linux_sandbox_exe,
js_repl_node_path,
zsh_path,
hide_agent_reasoning: cfg.hide_agent_reasoning.unwrap_or(false),
show_raw_agent_reasoning: cfg
@@ -4125,6 +4135,7 @@ model_verbosity = "high"
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
js_repl_node_path: None,
zsh_path: None,
hide_agent_reasoning: false,
show_raw_agent_reasoning: false,
model_reasoning_effort: Some(ReasoningEffort::High),
@@ -4236,6 +4247,7 @@ model_verbosity = "high"
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
js_repl_node_path: None,
zsh_path: None,
hide_agent_reasoning: false,
show_raw_agent_reasoning: false,
model_reasoning_effort: None,
@@ -4345,6 +4357,7 @@ model_verbosity = "high"
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
js_repl_node_path: None,
zsh_path: None,
hide_agent_reasoning: false,
show_raw_agent_reasoning: false,
model_reasoning_effort: None,
@@ -4440,6 +4453,7 @@ model_verbosity = "high"
file_opener: UriBasedFileOpener::VsCode,
codex_linux_sandbox_exe: None,
js_repl_node_path: None,
zsh_path: None,
hide_agent_reasoning: false,
show_raw_agent_reasoning: false,
model_reasoning_effort: Some(ReasoningEffort::High),

View File

@@ -30,6 +30,8 @@ pub struct ConfigProfile {
pub chatgpt_base_url: Option<String>,
/// Optional path to a file containing model instructions.
pub model_instructions_file: Option<AbsolutePathBuf>,
/// Optional absolute path to patched zsh used by sidecar-backed shell execution.
pub zsh_path: Option<AbsolutePathBuf>,
pub js_repl_node_path: Option<AbsolutePathBuf>,
/// Deprecated: ignored. Use `model_instructions_file`.
#[schemars(skip)]

View File

@@ -84,6 +84,10 @@ pub enum Feature {
JsReplToolsOnly,
/// Use the single unified PTY-backed exec tool.
UnifiedExec,
/// Route shell tool execution through the zsh sidecar.
ShellZshFork,
/// Route unified exec execution through the zsh sidecar.
UnifiedExecZshFork,
/// Include the freeform apply_patch tool.
ApplyPatchFreeform,
/// Allow the model to request web searches that fetch live content.
@@ -428,6 +432,18 @@ pub const FEATURES: &[FeatureSpec] = &[
stage: Stage::Stable,
default_enabled: !cfg!(windows),
},
FeatureSpec {
id: Feature::ShellZshFork,
key: "shell_zsh_fork",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::UnifiedExecZshFork,
key: "unified_exec_zsh_fork",
stage: Stage::UnderDevelopment,
default_enabled: false,
},
FeatureSpec {
id: Feature::ShellSnapshot,
key: "shell_snapshot",

View File

@@ -67,6 +67,8 @@ impl ToolsConfig {
let shell_type = if !features.enabled(Feature::ShellTool) {
ConfigShellToolType::Disabled
} else if features.enabled(Feature::ShellZshFork) {
ConfigShellToolType::ShellCommand
} else if features.enabled(Feature::UnifiedExec) {
// If ConPTY not supported (for old Windows versions), fallback on ShellCommand.
if codex_utils_pty::conpty_supported() {
@@ -2336,6 +2338,23 @@ mod tests {
assert_contains_tool_names(&tools, &subset);
}
#[test]
fn shell_zsh_fork_prefers_shell_command_over_unified_exec() {
let config = test_config();
let model_info = ModelsManager::construct_model_info_offline_for_tests("o3", &config);
let mut features = Features::with_defaults();
features.enable(Feature::UnifiedExec);
features.enable(Feature::ShellZshFork);
let tools_config = ToolsConfig::new(&ToolsConfigParams {
model_info: &model_info,
features: &features,
web_search_mode: Some(WebSearchMode::Live),
});
assert_eq!(tools_config.shell_type, ConfigShellToolType::ShellCommand);
}
#[test]
#[ignore]
fn test_parallel_support_flags() {