diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-08-13 03:00:35 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-13 03:00:35 +0300 |
| commit | a4d52ab60282b2ad021cb90978a7ed4d3898704b (patch) | |
| tree | cdd5f55d6d392d54541b86da7f1a00a48c211d03 /src/detect/mod.rs | |
| parent | 8b635c918066dd184bacc29b4c06c40a3efea3f5 (diff) | |
feat: add qwen code detection and restore (#2743)
refs #2730
Diffstat (limited to 'src/detect/mod.rs')
| -rw-r--r-- | src/detect/mod.rs | 54 |
1 files changed, 51 insertions, 3 deletions
diff --git a/src/detect/mod.rs b/src/detect/mod.rs index b9f9655f..f78567f6 100644 --- a/src/detect/mod.rs +++ b/src/detect/mod.rs @@ -61,11 +61,12 @@ pub enum Agent { Hermes, Kilo, Qodercli, + Qwen, Maki, } impl Agent { - pub const ALL: [Self; 21] = [ + pub const ALL: [Self; 22] = [ Self::Pi, Self::Claude, Self::Codex, @@ -86,10 +87,11 @@ impl Agent { Self::Hermes, Self::Kilo, Self::Qodercli, + Self::Qwen, Self::Maki, ]; - pub const SCREEN_MANIFEST_AGENTS: [Self; 19] = [ + pub const SCREEN_MANIFEST_AGENTS: [Self; 20] = [ Self::Pi, Self::Claude, Self::Codex, @@ -108,6 +110,7 @@ impl Agent { Self::Hermes, Self::Kilo, Self::Qodercli, + Self::Qwen, Self::Maki, ]; } @@ -134,6 +137,7 @@ pub fn agent_label(agent: Agent) -> &'static str { Agent::Hermes => "hermes", Agent::Kilo => "kilo", Agent::Qodercli => "qodercli", + Agent::Qwen => "qwen", Agent::Maki => "maki", } } @@ -166,6 +170,7 @@ pub fn interactive_agent_executable(agent: Agent) -> &'static str { Agent::Hermes => "hermes", Agent::Kilo => "kilo", Agent::Qodercli => "qodercli", + Agent::Qwen => "qwen", Agent::Maki => "maki", } } @@ -202,6 +207,7 @@ fn lookup_agent(name: &str) -> Option<Agent> { "hermes" | "hermes-agent" => Some(Agent::Hermes), "kilo" | "kilo-code" | "kilo code" => Some(Agent::Kilo), "qodercli" | "qoderclicn" | "qoder" | "qodercn" => Some(Agent::Qodercli), + "qwen" | "qwen-code" | "qwen code" => Some(Agent::Qwen), "maki" => Some(Agent::Maki), _ => None, } @@ -301,7 +307,7 @@ pub(crate) fn full_lifecycle_hook_authority(source: &str, agent_label: &str) -> pub(crate) fn session_identity_only_integration(source: &str, agent_label: &str) -> bool { matches!( (source, agent_label), - ("herdr:hermes", "hermes") | ("herdr:antigravity_cli", "agy") + ("herdr:hermes", "hermes") | ("herdr:qwen", "qwen") | ("herdr:antigravity_cli", "agy") ) } @@ -345,6 +351,19 @@ fn normalized_process_name(process: &crate::platform::ForegroundProcess) -> Stri return effective.to_string(); } + if let Some(runtime) = process.argv.as_deref().and_then(|argv| argv.first()) { + let runtime_name = normalized_agent_lookup_name(path_basename(runtime)); + if matches!(runtime_name.as_str(), "node" | "bun") { + if let Some(wrapped_agent) = + wrapped_agent_name_from_runtime_argv(runtime, process.argv.as_deref()) + { + if identify_agent(&wrapped_agent) == Some(Agent::Qwen) { + return wrapped_agent; + } + } + } + } + if let Some(wrapped_agent) = argv0_agent_name(process.argv.as_deref()) .or_else(|| cmdline_argv0_agent_name(process.cmdline.as_deref().unwrap_or_default())) { @@ -550,6 +569,9 @@ fn agent_name_from_known_package_path(path: &str) -> Option<String> { { return Some(agent_label(Agent::Pi).to_string()); } + if window == ["node_modules", "@qwen-code", "qwen-code", "dist", "index"] { + return Some(agent_label(Agent::Qwen).to_string()); + } } for window in components.windows(4) { if window == ["node_modules", "mastracode", "dist", "cli"] { @@ -709,6 +731,8 @@ mod tests { assert_eq!(identify_agent("hermes-agent"), Some(Agent::Hermes)); assert_eq!(identify_agent("kilo"), Some(Agent::Kilo)); assert_eq!(identify_agent("kilo-code"), Some(Agent::Kilo)); + assert_eq!(identify_agent("qwen"), Some(Agent::Qwen)); + assert_eq!(identify_agent("Qwen Code"), Some(Agent::Qwen)); assert_eq!(identify_agent("maki"), Some(Agent::Maki)); } @@ -734,6 +758,7 @@ mod tests { assert_eq!(parse_agent_label("kiro-cli"), Some(Agent::Kiro)); assert_eq!(parse_agent_label("grok-build"), Some(Agent::Grok)); assert_eq!(parse_agent_label("hermes-agent"), Some(Agent::Hermes)); + assert_eq!(parse_agent_label("qwen-code"), Some(Agent::Qwen)); assert_eq!(parse_agent_label("maki"), Some(Agent::Maki)); assert_eq!(parse_agent_label("kilo-code"), Some(Agent::Kilo)); } @@ -777,6 +802,7 @@ mod tests { (Agent::Hermes, "hermes"), (Agent::Kilo, "kilo"), (Agent::Qodercli, "qodercli"), + (Agent::Qwen, "qwen"), (Agent::Maki, "maki"), ]; assert_eq!(expected.len(), Agent::ALL.len()); @@ -806,6 +832,7 @@ mod tests { fn session_identity_integrations_leave_state_to_screen_detection() { for (source, label, agent) in [ ("herdr:hermes", "hermes", Agent::Hermes), + ("herdr:qwen", "qwen", Agent::Qwen), ("herdr:antigravity_cli", "agy", Agent::Antigravity), ] { assert!(!full_lifecycle_hook_authority(source, label)); @@ -847,6 +874,27 @@ mod tests { } #[test] + fn identify_agent_in_job_detects_node_wrapped_qwen() { + for argv in [ + vec!["node", "/home/user/.fnm/bin/qwen"], + vec![ + "node.exe", + r"C:\Users\user\AppData\Roaming\npm\node_modules\@qwen-code\qwen-code\dist\index.js", + ], + ] { + let job = crate::platform::ForegroundJob { + process_group_id: 123, + processes: vec![foreground_process(123, "MainThread", &argv)], + }; + + assert_eq!( + identify_agent_in_job(&job), + Some((Agent::Qwen, "qwen".to_string())) + ); + } + } + + #[test] fn identify_agent_in_job_prefers_recognized_process_group_leader() { let job = crate::platform::ForegroundJob { process_group_id: 42, |
