diff --git a/cli/src/native/actions.rs b/cli/src/native/actions.rs index d8b49ba..60014ba 100644 --- a/cli/src/native/actions.rs +++ b/cli/src/native/actions.rs @@ -1607,8 +1607,9 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> { To let agent-browser work with your existing Chrome (recommended):\n\ {}\n\n\ Or start a standalone browser with: agent-browser --launch open \n\n\ - Tip: On Chrome 144+, you can enable CDP without restarting:\n\ - Open chrome://inspect/#remote-debugging and toggle it on.", + Note: chrome://inspect/#remote-debugging only enables remote *target discovery* — \ + it does NOT expose the standard CDP HTTP API on /json/version. \ + A full restart with --remote-debugging-port= is required.", chrome_relaunch_hint(), )); } @@ -2135,8 +2136,9 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result\n\n\ - Tip: On Chrome 144+, you can enable CDP without restarting:\n\ - Open chrome://inspect/#remote-debugging and toggle it on.", + Note: chrome://inspect/#remote-debugging only enables remote *target discovery* — \ + it does NOT expose the standard CDP HTTP API on /json/version. \ + A full restart with --remote-debugging-port= is required.", chrome_relaunch_hint(), )); } diff --git a/cli/src/native/element.rs b/cli/src/native/element.rs index 3c997d2..bbb5316 100644 --- a/cli/src/native/element.rs +++ b/cli/src/native/element.rs @@ -396,9 +396,23 @@ async fn verify_ref_identity( "backendNodeId": backend_node_id, "fetchRelatives": false, }); - let resp: Result = client - .send_command_typed("Accessibility.getPartialAXTree", ¶ms, Some(session_id)) - .await; + // Tight 1s timeout: this is a defensive guard, not a critical path. + // The default 30s CDP timeout was the dominant factor in the + // "click hangs 5+ minutes" report — three CDP calls (verify + + // resolveNode + paint-settle) at 30s each, multiplied by parallel + // click invocations queueing on the daemon, totalled multi-minute + // user-visible hangs. Cap our own helper so a stuck AX query + // doesn't make `click` worse than the no-guard version was. + let resp: Result = match tokio::time::timeout( + std::time::Duration::from_secs(1), + client.send_command_typed("Accessibility.getPartialAXTree", ¶ms, Some(session_id)), + ) + .await + { + Ok(r) => r, + // Timeout: skip identity verification rather than block the click. + Err(_) => return Ok(()), + }; let Ok(tree) = resp else { // Node likely gone; let the box-model call fail and trigger fallback. return Ok(()); diff --git a/cli/src/native/interaction.rs b/cli/src/native/interaction.rs index 3375ace..26758e6 100644 --- a/cli/src/native/interaction.rs +++ b/cli/src/native/interaction.rs @@ -903,8 +903,15 @@ async fn wait_for_paint_settled(client: &CdpClient, session_id: &str) { requestAnimationFrame(() => \ requestAnimationFrame(() => \ queueMicrotask(() => resolve(true)))))"; - let _ = client - .send_command_typed::<_, Value>( + // Tight 500ms timeout. RAF normally fires at 16ms, two RAFs total ~33ms. + // If the tab is hidden / throttled / page is doing something pathological + // and RAF doesn't fire in 500ms, we'd rather return now than stall the + // user's click. Without this cap, a stuck RAF inherited the default 30s + // CDP timeout and was the main contributor to the "click hangs 5+ min" + // user report. + let _ = tokio::time::timeout( + std::time::Duration::from_millis(500), + client.send_command_typed::<_, Value>( "Runtime.evaluate", &EvaluateParams { expression: script.to_string(), @@ -912,8 +919,9 @@ async fn wait_for_paint_settled(client: &CdpClient, session_id: &str) { await_promise: Some(true), }, Some(session_id), - ) - .await; + ), + ) + .await; } async fn dispatch_click(