agentmux_srv/server/agent_handlers/
bash_prefix.rs1pub(crate) fn apply_for_spawn(env_vars: &mut std::collections::HashMap<String, String>, block_id: &str) {
14 let mode = mode_for(block_id);
15 apply_bash_shell_prefix(env_vars, crate::backend::tool_store::bashwrap_path(), mode.as_deref());
16}
17
18fn mode_for(block_id: &str) -> Option<String> {
20 if crate::backend::mcp_health::is_safe_profile(block_id) {
21 tracing::info!(block_id, "MCP self-heal safe profile: starting the agent without the shell prefix");
22 return Some("rewrite".to_string());
23 }
24 std::env::var(BASHWRAP_MODE_ENV).ok()
25}
26
27const BASHWRAP_MODE_ENV: &str = "AGENTMUX_BASHWRAP_MODE";
30
31fn apply_bash_shell_prefix(
41 env_vars: &mut std::collections::HashMap<String, String>,
42 bashwrap: Option<std::path::PathBuf>,
43 mode: Option<&str>,
44) {
45 const PREFIX_ENV: &str = "CLAUDE_CODE_SHELL_PREFIX";
46 if env_vars.contains_key(PREFIX_ENV) || mode.is_some_and(|m| m.eq_ignore_ascii_case("rewrite")) {
47 return;
48 }
49 if let Some(path) = bashwrap {
50 env_vars.insert(PREFIX_ENV.to_string(), path.to_string_lossy().into_owned());
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::apply_bash_shell_prefix;
57
58 const PREFIX: &str = "CLAUDE_CODE_SHELL_PREFIX";
59
60 #[test]
61 fn the_shell_prefix_names_the_bundled_wrapper() {
62 let mut env = std::collections::HashMap::new();
63 let wrapper = std::path::PathBuf::from("/opt/agentmux/tools/bin/agentmux-bashwrap");
64 apply_bash_shell_prefix(&mut env, Some(wrapper.clone()), None);
65 assert_eq!(env.get(PREFIX).map(String::as_str), Some(wrapper.to_str().unwrap()));
66 }
67
68 #[test]
69 fn no_shell_prefix_without_the_wrapper_or_when_switched_off() {
70 let mut env = std::collections::HashMap::new();
71 apply_bash_shell_prefix(&mut env, None, None);
72 assert!(!env.contains_key(PREFIX));
73 let wrapper = Some(std::path::PathBuf::from("/x/agentmux-bashwrap"));
74 apply_bash_shell_prefix(&mut env, wrapper.clone(), Some("rewrite"));
75 apply_bash_shell_prefix(&mut env, wrapper, Some("REWRITE"));
76 assert!(!env.contains_key(PREFIX));
77 }
78
79 #[test]
80 fn an_agents_own_shell_prefix_is_kept() {
81 let mut env = std::collections::HashMap::from([(PREFIX.to_string(), "/home/me/log-commands.sh".to_string())]);
82 apply_bash_shell_prefix(&mut env, Some("/x/agentmux-bashwrap".into()), None);
83 assert_eq!(env[PREFIX], "/home/me/log-commands.sh");
84 }
85
86 #[test]
89 fn a_pane_in_the_safe_profile_gets_no_prefix() {
90 use crate::backend::mcp_health::{on_reconnect, on_status, Status};
91 let block = format!("safe-profile-test-{}", uuid::Uuid::new_v4());
92 on_status(&block, "p1", Status::Failed, None, true, 0);
93 on_reconnect(&block, "p1", Err("Connection closed".into()), 1);
94 on_reconnect(&block, "p1", Err("Connection closed".into()), 2);
95 assert_eq!(super::mode_for(&block).as_deref(), Some("rewrite"));
96 if std::env::var("AGENTMUX_BASHWRAP_MODE").is_err() {
97 assert_eq!(super::mode_for("another-pane"), None);
98 }
99 }
100}