From 05a52af097baf2e799e688300bc8d2dbd6e4ecf0 Mon Sep 17 00:00:00 2001 From: Phil Larson Date: Sat, 1 Aug 2026 21:32:30 -0700 Subject: [PATCH] fix(detect): recognize versioned Python agent wrappers --- src/detect/mod.rs | 65 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/src/detect/mod.rs b/src/detect/mod.rs index 29115dfff..de590f823 100644 --- a/src/detect/mod.rs +++ b/src/detect/mod.rs @@ -350,11 +350,11 @@ fn normalized_process_name(process: &crate::platform::ForegroundProcess) -> Stri fn wrapped_agent_name_from_runtime_argv(runtime: &str, argv: Option<&[String]>) -> Option { let argv = argv?; - let runtime = normalized_agent_lookup_name(path_basename(runtime)); + let runtime_name = normalized_agent_lookup_name(path_basename(runtime)); - match runtime.as_str() { + match runtime_name.as_str() { "node" | "bun" => script_arg_agent_name(argv, &["-e", "--eval", "-p", "--print"], &[]), - "python" | "python3" => script_arg_agent_name(argv, &["-c"], &["-m"]), + name if is_python_runtime(name) => script_arg_agent_name(argv, &["-c"], &["-m"]), "sh" | "bash" | "zsh" | "fish" => script_arg_agent_name(argv, &["-c"], &[]), "cmd" => windows_cmd_arg_agent_name(argv), "powershell" | "pwsh" => powershell_arg_agent_name(argv), @@ -594,20 +594,29 @@ fn process_priority(process: &crate::platform::ForegroundProcess, normalized_nam fn is_generic_runtime_or_shell(name: &str) -> bool { let name = normalized_agent_lookup_name(path_basename(name)); - matches!( - name.as_str(), - "sh" | "bash" - | "zsh" - | "fish" - | "tmux" - | "node" - | "bun" - | "python" - | "python3" - | "cmd" - | "powershell" - | "pwsh" - ) + is_python_runtime(&name) + || matches!( + name.as_str(), + "sh" | "bash" + | "zsh" + | "fish" + | "tmux" + | "node" + | "bun" + | "cmd" + | "powershell" + | "pwsh" + ) +} + +fn is_python_runtime(name: &str) -> bool { + name == "python" + || name.strip_prefix("python").is_some_and(|version| { + !version.is_empty() + && version + .split('.') + .all(|part| !part.is_empty() && part.chars().all(|ch| ch.is_ascii_digit())) + }) } // --------------------------------------------------------------------------- @@ -851,6 +860,28 @@ mod tests { ); } + #[test] + fn identify_agent_in_job_detects_python_version_wrapped_hermes() { + let job = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process( + 123, + "python3.12", + &[ + "/nix/store/example/bin/python3.12", + "/nix/store/example/bin/hermes", + "--resume", + "session-id", + ], + )], + }; + + assert_eq!( + identify_agent_in_job(&job), + Some((Agent::Hermes, "hermes".to_string())) + ); + } + #[test] fn identify_agent_in_job_detects_nix_wrapped_codex_from_cmdline_argv0() { let job = crate::platform::ForegroundJob {