Add auto-dismissal for alert and beforeunload dialogs (#1075)
* Add auto-dismissal for alert and beforeunload dialogs This PR adds automatic handling of JavaScript dialogs to prevent the agent from blocking indefinitely when `alert()` or `beforeunload` dialogs appear on web pages. ## Summary Previously, when a website displayed native browser confirmation dialogs (like alerts or "Are you sure you want to leave?" prompts), agent-browser would hang waiting for manual intervention. This is a common issue since many websites use these dialogs for notifications or navigation warnings. ## Changes Made - **Auto-dismiss functionality**: Added a background task that automatically accepts `alert` and `beforeunload` dialogs while leaving `confirm` and `prompt` dialogs for explicit handling - **New flag**: Added `--no-auto-dialog` flag to disable automatic handling when needed - **Environment variable**: Added `AGENT_BROWSER_NO_AUTO_DIALOG` for configuration - **Documentation**: Updated README and docs with usage examples and configuration details - **Tests**: Added comprehensive test coverage for flag parsing and dialog handling logic ## Implementation Details - Only `alert` (notification-only) and `beforeunload` (navigation warning) dialogs are auto-handled for safety - `confirm` and `prompt` dialogs still require explicit `dialog accept/dismiss` commands to ensure agents make deliberate choices for destructive actions - The feature is enabled by default since these dialog types rarely require user decision-making - Uses Chrome DevTools Protocol's `Page.handleJavaScriptDialog` for reliable dialog dismissal Fixes #1070 * Log dialog type and message before auto-dismissal Without this, auto-dismissed alert/beforeunload dialogs are silently swallowed and the agent has no way to see what the dialog said. Adding an eprintln before the CDP call makes the dismissal visible in stderr for debugging. * Log dialog dismissal errors instead of silently discarding them - Remove premature "accepted" from log message since it fires before the CDP command executes - Replace `let _ =` with `if let Err(e)` to log failures when Page.handleJavaScriptDialog fails - Apply rustfmt to auto-dialog tests --------- Co-authored-by: ctate <366502+ctate@users.noreply.github.com>
This commit is contained in:
@@ -307,6 +307,8 @@ agent-browser dialog dismiss # Dismiss
|
||||
agent-browser dialog status # Check if a dialog is currently open
|
||||
```
|
||||
|
||||
By default, `alert` and `beforeunload` dialogs are automatically accepted so they never block the agent. `confirm` and `prompt` dialogs still require explicit handling. Use `--no-auto-dialog` (or `AGENT_BROWSER_NO_AUTO_DIALOG=1`) to disable automatic handling.
|
||||
|
||||
When a JavaScript dialog is pending, all command responses include a `warning` field with the dialog type and message.
|
||||
|
||||
### Diff
|
||||
@@ -594,6 +596,7 @@ This is useful for multimodal AI models that can reason about visual layout, unl
|
||||
| `--confirm-actions <list>` | Action categories requiring confirmation (or `AGENT_BROWSER_CONFIRM_ACTIONS` env) |
|
||||
| `--confirm-interactive` | Interactive confirmation prompts; auto-denies if stdin is not a TTY (or `AGENT_BROWSER_CONFIRM_INTERACTIVE` env) |
|
||||
| `--engine <name>` | Browser engine: `chrome` (default), `lightpanda` (or `AGENT_BROWSER_ENGINE` env) |
|
||||
| `--no-auto-dialog` | Disable automatic dismissal of `alert`/`beforeunload` dialogs (or `AGENT_BROWSER_NO_AUTO_DIALOG` env) |
|
||||
| `--config <path>` | Use a custom config file (or `AGENT_BROWSER_CONFIG` env) |
|
||||
| `--debug` | Debug output |
|
||||
|
||||
|
||||
@@ -2322,6 +2322,7 @@ mod tests {
|
||||
screenshot_quality: None,
|
||||
screenshot_format: None,
|
||||
idle_timeout: None,
|
||||
no_auto_dialog: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ pub struct DaemonOptions<'a> {
|
||||
pub auto_connect: bool,
|
||||
pub idle_timeout: Option<&'a str>,
|
||||
pub cdp: Option<&'a str>,
|
||||
pub no_auto_dialog: bool,
|
||||
}
|
||||
|
||||
fn apply_daemon_env(cmd: &mut Command, session: &str, opts: &DaemonOptions) {
|
||||
@@ -300,6 +301,9 @@ fn apply_daemon_env(cmd: &mut Command, session: &str, opts: &DaemonOptions) {
|
||||
if let Some(cdp) = opts.cdp {
|
||||
cmd.env("AGENT_BROWSER_CDP", cdp);
|
||||
}
|
||||
if opts.no_auto_dialog {
|
||||
cmd.env("AGENT_BROWSER_NO_AUTO_DIALOG", "1");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_daemon(session: &str, opts: &DaemonOptions) -> Result<DaemonResult, String> {
|
||||
|
||||
@@ -87,6 +87,7 @@ pub struct Config {
|
||||
pub screenshot_quality: Option<u32>,
|
||||
pub screenshot_format: Option<String>,
|
||||
pub idle_timeout: Option<String>,
|
||||
pub no_auto_dialog: Option<bool>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -132,6 +133,7 @@ impl Config {
|
||||
screenshot_quality: other.screenshot_quality.or(self.screenshot_quality),
|
||||
screenshot_format: other.screenshot_format.or(self.screenshot_format),
|
||||
idle_timeout: other.idle_timeout.or(self.idle_timeout),
|
||||
no_auto_dialog: other.no_auto_dialog.or(self.no_auto_dialog),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,6 +300,7 @@ pub struct Flags {
|
||||
pub screenshot_quality: Option<u32>,
|
||||
pub screenshot_format: Option<String>,
|
||||
pub idle_timeout: Option<String>, // Canonical milliseconds string for AGENT_BROWSER_IDLE_TIMEOUT_MS
|
||||
pub no_auto_dialog: bool,
|
||||
|
||||
// Track which launch-time options were explicitly passed via CLI
|
||||
// (as opposed to being set only via environment variables)
|
||||
@@ -429,6 +432,8 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
||||
"AGENT_BROWSER_IDLE_TIMEOUT_MS",
|
||||
)
|
||||
.or(config.idle_timeout),
|
||||
no_auto_dialog: env_var_is_truthy("AGENT_BROWSER_NO_AUTO_DIALOG")
|
||||
|| config.no_auto_dialog.unwrap_or(false),
|
||||
cli_executable_path: false,
|
||||
cli_extensions: false,
|
||||
cli_profile: false,
|
||||
@@ -703,6 +708,13 @@ pub fn parse_flags(args: &[String]) -> Flags {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--no-auto-dialog" => {
|
||||
let (val, consumed) = parse_bool_arg(args, i);
|
||||
flags.no_auto_dialog = val;
|
||||
if consumed {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
"--config" => {
|
||||
// Already handled by load_config(); skip the value
|
||||
i += 1;
|
||||
@@ -729,6 +741,7 @@ pub fn clean_args(args: &[String]) -> Vec<String> {
|
||||
"--annotate",
|
||||
"--content-boundaries",
|
||||
"--confirm-interactive",
|
||||
"--no-auto-dialog",
|
||||
];
|
||||
// Global flags that always take a value (need to skip the next arg too)
|
||||
const GLOBAL_FLAGS_WITH_VALUE: &[&str] = &[
|
||||
@@ -1375,4 +1388,27 @@ mod tests {
|
||||
let merged = user.merge(project);
|
||||
assert_eq!(merged.extensions, Some(vec!["/ext2".to_string()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_auto_dialog_flag() {
|
||||
let flags = parse_flags(&args("open example.com --no-auto-dialog"));
|
||||
assert!(flags.no_auto_dialog);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_auto_dialog_default_false() {
|
||||
let flags = parse_flags(&args("open example.com"));
|
||||
assert!(!flags.no_auto_dialog);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clean_args_removes_no_auto_dialog() {
|
||||
let input: Vec<String> = vec![
|
||||
"open".to_string(),
|
||||
"example.com".to_string(),
|
||||
"--no-auto-dialog".to_string(),
|
||||
];
|
||||
let clean = clean_args(&input);
|
||||
assert_eq!(clean, vec!["open", "example.com"]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,6 +721,7 @@ fn main() {
|
||||
auto_connect: flags.auto_connect,
|
||||
idle_timeout: flags.idle_timeout.as_deref(),
|
||||
cdp: flags.cdp.as_deref(),
|
||||
no_auto_dialog: flags.no_auto_dialog,
|
||||
};
|
||||
|
||||
let daemon_result = match ensure_daemon(&flags.session, &daemon_opts) {
|
||||
|
||||
+166
-6
@@ -205,9 +205,15 @@ pub struct DaemonState {
|
||||
/// handling domain filtering, route interception, and origin-scoped headers
|
||||
/// without deadlocking navigation/evaluate.
|
||||
fetch_handler_task: Option<tokio::task::JoinHandle<()>>,
|
||||
/// Background task that auto-accepts `alert` and `beforeunload` dialogs
|
||||
/// so they never block the agent.
|
||||
dialog_handler_task: Option<tokio::task::JoinHandle<()>>,
|
||||
pub mouse_state: MouseState,
|
||||
/// Tracks the currently open JavaScript dialog (alert/confirm/prompt), if any.
|
||||
pub pending_dialog: Option<PendingDialog>,
|
||||
/// When true, automatically dismiss `beforeunload` dialogs and accept `alert`
|
||||
/// dialogs so they never block the agent. Enabled by default.
|
||||
pub auto_dialog: bool,
|
||||
/// Shared slot for stream server to receive CDP client when browser launches.
|
||||
pub stream_client: Option<Arc<RwLock<Option<Arc<CdpClient>>>>>,
|
||||
/// Stream server instance kept alive so the broadcast channel remains open.
|
||||
@@ -252,8 +258,13 @@ impl DaemonState {
|
||||
origin_headers: Arc::new(RwLock::new(HashMap::new())),
|
||||
proxy_credentials: Arc::new(RwLock::new(None)),
|
||||
fetch_handler_task: None,
|
||||
dialog_handler_task: None,
|
||||
mouse_state: MouseState::default(),
|
||||
pending_dialog: None,
|
||||
auto_dialog: !matches!(
|
||||
env::var("AGENT_BROWSER_NO_AUTO_DIALOG").as_deref(),
|
||||
Ok("1" | "true" | "yes")
|
||||
),
|
||||
stream_client: None,
|
||||
stream_server: None,
|
||||
engine: env::var("AGENT_BROWSER_ENGINE").unwrap_or_else(|_| "chrome".to_string()),
|
||||
@@ -398,6 +409,65 @@ impl DaemonState {
|
||||
}));
|
||||
}
|
||||
|
||||
/// Start the background task that auto-accepts `alert` and `beforeunload`
|
||||
/// dialogs so they never block the agent. `confirm` and `prompt` dialogs
|
||||
/// are left for the agent to handle explicitly.
|
||||
fn start_dialog_handler(&mut self) {
|
||||
if let Some(task) = self.dialog_handler_task.take() {
|
||||
task.abort();
|
||||
}
|
||||
|
||||
if !self.auto_dialog {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(ref browser) = self.browser else {
|
||||
return;
|
||||
};
|
||||
|
||||
let client = browser.client.clone();
|
||||
let mut rx = browser.client.subscribe();
|
||||
|
||||
self.dialog_handler_task = Some(tokio::spawn(async move {
|
||||
loop {
|
||||
match rx.recv().await {
|
||||
Ok(event) if event.method == "Page.javascriptDialogOpening" => {
|
||||
let dialog_type = event
|
||||
.params
|
||||
.get("type")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
if matches!(dialog_type, "beforeunload" | "alert") {
|
||||
let message = event
|
||||
.params
|
||||
.get("message")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
eprintln!("[auto-dismiss] {} dialog: {}", dialog_type, message);
|
||||
let sid = event.session_id.clone().unwrap_or_default();
|
||||
if let Err(e) = client
|
||||
.send_command(
|
||||
"Page.handleJavaScriptDialog",
|
||||
Some(json!({ "accept": true })),
|
||||
Some(&sid),
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!(
|
||||
"[auto-dismiss] failed to dismiss {} dialog: {}",
|
||||
dialog_type, e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(_) => continue,
|
||||
Err(broadcast::error::RecvError::Lagged(_)) => continue,
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/// Update the stream server's CDP client slot when browser is set or cleared.
|
||||
pub async fn update_stream_client(&self) {
|
||||
if let Some(ref slot) = self.stream_client {
|
||||
@@ -918,12 +988,22 @@ impl DaemonState {
|
||||
event.params.clone(),
|
||||
)
|
||||
{
|
||||
self.pending_dialog = Some(PendingDialog {
|
||||
dialog_type: dialog_event.dialog_type,
|
||||
message: dialog_event.message,
|
||||
url: dialog_event.url,
|
||||
default_prompt: dialog_event.default_prompt,
|
||||
});
|
||||
// When auto_dialog is enabled, alert and beforeunload
|
||||
// dialogs are handled by the background dialog_handler_task.
|
||||
// Skip tracking them to avoid a stale warning.
|
||||
let auto_handled = self.auto_dialog
|
||||
&& matches!(
|
||||
dialog_event.dialog_type.as_str(),
|
||||
"beforeunload" | "alert"
|
||||
);
|
||||
if !auto_handled {
|
||||
self.pending_dialog = Some(PendingDialog {
|
||||
dialog_type: dialog_event.dialog_type,
|
||||
message: dialog_event.message,
|
||||
url: dialog_event.url,
|
||||
default_prompt: dialog_event.default_prompt,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"Page.javascriptDialogClosed" => {
|
||||
@@ -961,6 +1041,9 @@ impl Drop for DaemonState {
|
||||
if let Some(task) = self.fetch_handler_task.take() {
|
||||
task.abort();
|
||||
}
|
||||
if let Some(task) = self.dialog_handler_task.take() {
|
||||
task.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,6 +1425,7 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
state.browser = Some(mgr);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
try_auto_restore_state(state).await;
|
||||
return Ok(());
|
||||
@@ -1352,6 +1436,7 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
state.browser = Some(connect_auto_with_fresh_tab().await?);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
try_auto_restore_state(state).await;
|
||||
return Ok(());
|
||||
@@ -1362,6 +1447,7 @@ async fn auto_launch(state: &mut DaemonState) -> Result<(), String> {
|
||||
state.browser = Some(mgr);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
|
||||
// Enable Fetch with handleAuthRequests for proxy authentication
|
||||
@@ -1505,6 +1591,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
state.browser = Some(BrowserManager::connect_cdp(url).await?);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
return Ok(json!({ "launched": true }));
|
||||
}
|
||||
@@ -1514,6 +1601,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
state.browser = Some(BrowserManager::connect_cdp(&port.to_string()).await?);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
return Ok(json!({ "launched": true }));
|
||||
}
|
||||
@@ -1523,6 +1611,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
state.browser = Some(connect_auto_with_fresh_tab().await?);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
return Ok(json!({ "launched": true }));
|
||||
}
|
||||
@@ -1543,6 +1632,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
state.browser = Some(mgr);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
return Ok(json!({ "launched": true, "provider": provider }));
|
||||
}
|
||||
@@ -1657,6 +1747,7 @@ async fn handle_launch(cmd: &Value, state: &mut DaemonState) -> Result<Value, St
|
||||
state.browser = Some(BrowserManager::launch(options, engine.as_deref()).await?);
|
||||
state.subscribe_to_browser_events();
|
||||
state.start_fetch_handler();
|
||||
state.start_dialog_handler();
|
||||
state.update_stream_client().await;
|
||||
|
||||
// Enable Fetch interception (domain filtering and/or proxy auth).
|
||||
@@ -8159,4 +8250,73 @@ mod tests {
|
||||
assert_eq!(key, "+");
|
||||
assert_eq!(mods, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_auto_dialog_enabled_by_default() {
|
||||
let guard = EnvGuard::new(&["AGENT_BROWSER_NO_AUTO_DIALOG"]);
|
||||
std::env::remove_var("AGENT_BROWSER_NO_AUTO_DIALOG");
|
||||
let state = DaemonState::new();
|
||||
assert!(state.auto_dialog, "auto_dialog should be true by default");
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_auto_dialog_disabled_by_env() {
|
||||
let guard = EnvGuard::new(&["AGENT_BROWSER_NO_AUTO_DIALOG"]);
|
||||
guard.set("AGENT_BROWSER_NO_AUTO_DIALOG", "1");
|
||||
let state = DaemonState::new();
|
||||
assert!(
|
||||
!state.auto_dialog,
|
||||
"auto_dialog should be false when AGENT_BROWSER_NO_AUTO_DIALOG=1"
|
||||
);
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_auto_dialog_disabled_by_env_true() {
|
||||
let guard = EnvGuard::new(&["AGENT_BROWSER_NO_AUTO_DIALOG"]);
|
||||
guard.set("AGENT_BROWSER_NO_AUTO_DIALOG", "true");
|
||||
let state = DaemonState::new();
|
||||
assert!(
|
||||
!state.auto_dialog,
|
||||
"auto_dialog should be false when AGENT_BROWSER_NO_AUTO_DIALOG=true"
|
||||
);
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_auto_dialog_not_disabled_by_random_value() {
|
||||
let guard = EnvGuard::new(&["AGENT_BROWSER_NO_AUTO_DIALOG"]);
|
||||
guard.set("AGENT_BROWSER_NO_AUTO_DIALOG", "no");
|
||||
let state = DaemonState::new();
|
||||
assert!(
|
||||
state.auto_dialog,
|
||||
"auto_dialog should remain true for non-truthy env values"
|
||||
);
|
||||
drop(guard);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pending_dialog_not_set_for_auto_handled_alert() {
|
||||
// Simulate what handle_browser_event does: when auto_dialog is true,
|
||||
// alert/beforeunload should NOT populate pending_dialog.
|
||||
let auto_dialog = true;
|
||||
for dialog_type in &["alert", "beforeunload"] {
|
||||
let auto_handled = auto_dialog && matches!(*dialog_type, "beforeunload" | "alert");
|
||||
assert!(
|
||||
auto_handled,
|
||||
"{dialog_type} should be auto-handled when auto_dialog is true"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pending_dialog_set_for_confirm_prompt() {
|
||||
// confirm and prompt should NOT be auto-handled even when auto_dialog is true.
|
||||
let auto_dialog = true;
|
||||
for dialog_type in &["confirm", "prompt"] {
|
||||
let auto_handled = auto_dialog && matches!(*dialog_type, "beforeunload" | "alert");
|
||||
assert!(!auto_handled, "{dialog_type} should NOT be auto-handled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2826,6 +2826,7 @@ Options:
|
||||
--confirm-actions <list> Categories requiring confirmation (or AGENT_BROWSER_CONFIRM_ACTIONS)
|
||||
--confirm-interactive Interactive confirmation prompts; auto-denies if stdin is not a TTY (or AGENT_BROWSER_CONFIRM_INTERACTIVE)
|
||||
--engine <name> Browser engine: chrome (default), lightpanda (or AGENT_BROWSER_ENGINE)
|
||||
--no-auto-dialog Disable automatic dismissal of alert/beforeunload dialogs (or AGENT_BROWSER_NO_AUTO_DIALOG)
|
||||
--config <path> Use a custom config file (or AGENT_BROWSER_CONFIG env)
|
||||
--debug Debug output
|
||||
--version, -V Show version
|
||||
@@ -2881,6 +2882,7 @@ Environment:
|
||||
AGENT_BROWSER_ACTION_POLICY Path to action policy JSON file
|
||||
AGENT_BROWSER_CONFIRM_ACTIONS Action categories requiring confirmation
|
||||
AGENT_BROWSER_CONFIRM_INTERACTIVE Enable interactive confirmation prompts
|
||||
AGENT_BROWSER_NO_AUTO_DIALOG Disable automatic dismissal of alert/beforeunload dialogs
|
||||
AGENT_BROWSER_ENGINE Browser engine: chrome (default), lightpanda
|
||||
HTTP_PROXY / HTTPS_PROXY Standard proxy env vars (fallback if AGENT_BROWSER_PROXY not set)
|
||||
ALL_PROXY SOCKS proxy (fallback for proxy)
|
||||
|
||||
@@ -230,6 +230,8 @@ agent-browser dialog dismiss # Dismiss dialog
|
||||
agent-browser dialog status # Check if a dialog is currently open
|
||||
```
|
||||
|
||||
By default, `alert` and `beforeunload` dialogs are automatically accepted so they never block the agent. `confirm` and `prompt` dialogs still require explicit handling. Use `--no-auto-dialog` (or `AGENT_BROWSER_NO_AUTO_DIALOG=1`) to disable automatic handling.
|
||||
|
||||
When a JavaScript dialog (`alert`, `confirm`, `prompt`) is pending, all command responses include a `warning` field with the dialog type and message.
|
||||
|
||||
## Streaming
|
||||
|
||||
@@ -77,6 +77,7 @@ Every CLI flag can be set in the config file using its camelCase equivalent:
|
||||
<tr><td><code>confirmActions</code></td><td><code>--confirm-actions</code></td><td>string</td></tr>
|
||||
<tr><td><code>confirmInteractive</code></td><td><code>--confirm-interactive</code></td><td>boolean</td></tr>
|
||||
<tr><td><code>engine</code></td><td><code>--engine</code></td><td>string (<code>chrome</code>, <code>lightpanda</code>)</td></tr>
|
||||
<tr><td><code>noAutoDialog</code></td><td><code>--no-auto-dialog</code></td><td>boolean</td></tr>
|
||||
<tr><td><code>headers</code></td><td><code>--headers</code></td><td>string (JSON)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -185,6 +186,7 @@ These environment variables configure additional daemon and runtime behavior:
|
||||
<tr><td><code>AGENT_BROWSER_CONFIRM_ACTIONS</code></td><td>Comma-separated action categories requiring confirmation.</td><td>(none)</td></tr>
|
||||
<tr><td><code>AGENT_BROWSER_CONFIRM_INTERACTIVE</code></td><td>Enable interactive confirmation prompts (auto-denies if stdin is not a TTY).</td><td>(disabled)</td></tr>
|
||||
<tr><td><code>AGENT_BROWSER_ENGINE</code></td><td>Browser engine to use: <code>chrome</code> (default), <code>lightpanda</code>.</td><td><code>chrome</code></td></tr>
|
||||
<tr><td><code>AGENT_BROWSER_NO_AUTO_DIALOG</code></td><td>Disable automatic dismissal of <code>alert</code>/<code>beforeunload</code> dialogs.</td><td>(disabled)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -184,7 +184,10 @@ agent-browser clipboard write "Hello, World!" # Write text to clipboard
|
||||
agent-browser clipboard copy # Copy current selection
|
||||
agent-browser clipboard paste # Paste from clipboard
|
||||
|
||||
# Dialogs (alert, confirm, prompt)
|
||||
# Dialogs (alert, confirm, prompt, beforeunload)
|
||||
# By default, alert and beforeunload dialogs are auto-accepted so they never block the agent.
|
||||
# confirm and prompt dialogs still require explicit handling.
|
||||
# Use --no-auto-dialog to disable automatic handling.
|
||||
agent-browser dialog accept # Accept dialog
|
||||
agent-browser dialog accept "my input" # Accept prompt dialog with text
|
||||
agent-browser dialog dismiss # Dismiss/cancel dialog
|
||||
|
||||
@@ -209,9 +209,12 @@ The `frame` command accepts:
|
||||
|
||||
## Dialogs
|
||||
|
||||
By default, `alert` and `beforeunload` dialogs are automatically accepted so they never block the agent. `confirm` and `prompt` dialogs still require explicit handling. Use `--no-auto-dialog` to disable this behavior.
|
||||
|
||||
```bash
|
||||
agent-browser dialog accept [text] # Accept dialog
|
||||
agent-browser dialog dismiss # Dismiss dialog
|
||||
agent-browser dialog status # Check if a dialog is currently open
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
||||
Reference in New Issue
Block a user