From 6ecda4d70604821ea57251a97e7b7a07bf30438e Mon Sep 17 00:00:00 2001 From: leeguooooo Date: Thu, 11 Jun 2026 22:29:39 +0900 Subject: [PATCH] fix: per-invocation env (CLICK_MODE / HUMANIZE) reaches a running daemon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause behind Hermes #1 (CLICK_MODE=dom "does nothing") and #2 (--humanize "does nothing"): both are env vars the daemon reads, but the daemon's env is frozen at spawn — set them on a command to an already-running daemon and they were silently ignored. (Confirmed: setting CLICK_MODE=dom at daemon spawn made dom_click fire; setting it later did not.) Fix: the client forwards AGENT_BROWSER_CLICK_MODE / AGENT_BROWSER_HUMANIZE in the command envelope (_clickMode/_humanize); execute_command applies them per command — mirrors CLICK_MODE into the process env (interaction::click reads it fresh) and sets the humanize session level. Each command is authoritative. Verified on an already-running daemon: CLICK_MODE=dom now fires dom_click (hits 0→1); --humanize human typing applies. --- cli/src/connection.rs | 16 +++++++++++++++- cli/src/native/actions.rs | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/cli/src/connection.rs b/cli/src/connection.rs index 2ba1f93..e394113 100644 --- a/cli/src/connection.rs +++ b/cli/src/connection.rs @@ -821,7 +821,21 @@ fn connect(session: &str) -> Result { } } -pub fn send_command(cmd: Value, session: &str) -> Result { +pub fn send_command(mut cmd: Value, session: &str) -> Result { + // Forward per-invocation env to the daemon. The daemon's environment is + // frozen at spawn, so settings like AGENT_BROWSER_CLICK_MODE / + // AGENT_BROWSER_HUMANIZE (incl. the --humanize flag, which sets the latter) + // are otherwise silently ignored on an already-running daemon. Carry them in + // the envelope so they apply to THIS command. + if let Some(obj) = cmd.as_object_mut() { + if let Ok(m) = std::env::var("AGENT_BROWSER_CLICK_MODE") { + obj.insert("_clickMode".to_string(), Value::String(m)); + } + if let Ok(h) = std::env::var("AGENT_BROWSER_HUMANIZE") { + obj.insert("_humanize".to_string(), Value::String(h)); + } + } + // Retry logic for transient errors (EAGAIN/EWOULDBLOCK/connection issues) const MAX_RETRIES: u32 = 5; const RETRY_DELAY_MS: u64 = 200; diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index d2ab992..636a599 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -1160,6 +1160,23 @@ impl Drop for DaemonState { pub async fn execute_command(cmd: &Value, state: &mut DaemonState) -> Value { let action = cmd.get("action").and_then(|v| v.as_str()).unwrap_or(""); + + // Apply per-invocation overrides the client forwarded (the daemon's own env + // is frozen at spawn). CLICK_MODE is read fresh from the process env by + // interaction::click, so mirror it here — set when this command provided it, + // clear otherwise, so a value from an earlier command never leaks forward. + match cmd.get("_clickMode").and_then(|v| v.as_str()) { + Some(m) if !m.is_empty() => std::env::set_var("AGENT_BROWSER_CLICK_MODE", m), + _ => std::env::remove_var("AGENT_BROWSER_CLICK_MODE"), + } + // Humanize: set the session level from the client's --humanize / env. Only + // set when provided (don't clear — the adaptive per-navigation detector also + // owns this level between explicit overrides). + if let Some(h) = cmd.get("_humanize").and_then(|v| v.as_str()) { + if let Some(level) = super::humanize::HumanizeLevel::parse(h) { + super::humanize::set_detected_level(level); + } + } let id = cmd .get("id") .and_then(|v| v.as_str())