Refresh BYOB browser inventory

Poll connected A0 CLI host-browser metadata while Browser Settings is open so newly authorized endpoints appear without saving or reopening. Extend Browser Settings labels and remote-debugging guidance for Brave, Opera, Vivaldi, and Chromium-family host browsers with regression coverage.

Alessandro committed Jul 4, 2026 at 19:22 UTC 7168f0984e78c51d2d3d86d2be48ffee77215064
8 files changed +34 -4
plugins/_browser/AGENTS.md
+1
@@ -25,6 +25,7 @@
25 - Keep WebUI Browser tabs scoped to the active chat context by default; aggregate tabs from other AgentContext runtimes only when the Browser settings tab scope is `shared`.
26 - Keep narrow WebUI Browser controls usable by grouping navigation with Annotate/settings above a full-width address bar.
27 - For Bring Your Own Browser with an existing host profile, `host_browser_selection` may target automatic CLI selection, a browser family/id, or an explicit CDP endpoint and must be forwarded to the connector runtime as `browser_selection`.
28 +- Browser Settings must refresh connected A0 CLI host-browser inventory while the settings view is open so newly authorized endpoints appear without saving or reopening.
29 - Browser URL-intent handling must only claim web URL schemes and leave custom Agent Zero schemes to their owning surfaces.
30 - Prefer DOM/CDP browser actions with refs, selectors, frame-chain refs, and screenshots over viewport coordinate input. Coordinates remain a visual fallback.
31 - Do not hardcode user-specific browser paths or secrets.
plugins/_browser/helpers/connector_runtime.py
+6 -2
@@ -83,7 +83,8 @@ _REQUIRED_API_NAMES_RE = re.compile(
83 re.S,
84 )
85 _HOST_BROWSER_REMOTE_DEBUGGING_HELP = (
86 - 'For an already-open Chrome-family browser, open `chrome://inspect/#remote-debugging`, '
86 + "For an already-open Chromium-family browser, open its inspect page, such as "
87 + "`chrome://inspect/#remote-debugging` or `opera://inspect/#remote-debugging`, "
88 'enable "Allow remote debugging for this browser instance", run `/browser host on`, '
89 "and retry."
90 )
@@ -519,7 +520,10 @@ class ConnectorBrowserRuntime:
520 if not message:
521 message = "Host browser operation failed"
522 normalized = message.lower()
522 - if "chrome://inspect/#remote-debugging" in normalized:
523 + if (
524 + "chrome://inspect/#remote-debugging" in normalized
525 + or "opera://inspect/#remote-debugging" in normalized
526 + ):
527 return _append_docker_browser_recovery(message)
528 if any(token in normalized for token in _REMOTE_DEBUGGING_ERROR_TOKENS):
529 return _append_docker_browser_recovery(
plugins/_browser/prompts/agent.system.tool.browser.md
+1 -1
@@ -3,7 +3,7 @@ Rendered browser automation for pages that need interaction, JavaScript, forms,
3
4 Prefer `search_engine` or `document_query` for plain text research. The tool must not open a Browser surface automatically. Use the tool headlessly unless the user opens the Browser surface or asks for the optional visible WebUI viewer.
5
6 -When the user asks for "my browser", "host browser", "local browser", local Chrome, or opening a URL in their host browser, use this `browser` tool. Do not substitute `computer_use_remote`, `code_execution_remote`, `xdg-open`, `sensible-browser`, or Python `webbrowser.open`. If setup fails and mentions remote debugging, tell the user to open `chrome://inspect/#remote-debugging`, enable "Allow remote debugging for this browser instance", run `/browser host on`, and retry.
6 +When the user asks for "my browser", "host browser", "local browser", a local Chromium browser, or opening a URL in their host browser, use this `browser` tool. Do not substitute `computer_use_remote`, `code_execution_remote`, `xdg-open`, `sensible-browser`, or Python `webbrowser.open`. If setup fails and mentions remote debugging, tell the user to open the browser inspect page, such as `chrome://inspect/#remote-debugging` or `opera://inspect/#remote-debugging`, enable "Allow remote debugging for this browser instance", run `/browser host on`, and retry.
7
8 For rendered browsing workflows, multi-step interaction, screenshots, downloads, uploads, forms, or host/container mode decisions, first load `browser-automation` with `skills_tool:load`, then call this tool using the loaded instructions. For fragile forms, `browser-automation` links to `browser-form-workflows`; load it when selects, checkboxes, radios, uploads, contenteditable fields, validation, or submission state are central.
9
plugins/_browser/webui/browser-config-store.js
+21
@@ -10,6 +10,7 @@ const HOST_PROFILE_MODES = new Set(["existing", "agent"]);
10 const DEFAULT_MAX_OPEN_TABS = 32;
11 const MIN_MAX_OPEN_TABS = 1;
12 const HARD_MAX_OPEN_TABS = 50;
13 +const HOST_BROWSER_STATUS_REFRESH_MS = 3000;
14
15 function normalizePathList(value) {
16 const source = Array.isArray(value)
@@ -91,6 +92,9 @@ function hostBrowserFamilyLabel(value) {
92 chromium: "Chromium",
93 edge: "Edge",
94 "edge-dev": "Edge Dev",
95 + brave: "Brave",
96 + opera: "Opera",
97 + vivaldi: "Vivaldi",
98 };
99 const label = labels[base] || "Host browser";
100 if (remoteDebugging) return `${label} (allowed)`;
@@ -116,13 +120,16 @@ export const store = createStore("browserConfig", {
120 extensionDeleteLoadingPath: "",
121 hostBrowserStatus: null,
122 hostBrowserStatusLoading: false,
123 + hostBrowserStatusRefreshTimer: null,
124
125 async init(config) {
126 this.bindConfig(config);
127 await Promise.all([this.loadExtensionsList(), this.loadHostBrowserStatus()]);
128 + this.startHostBrowserStatusRefresh();
129 },
130
131 cleanup() {
132 + this.stopHostBrowserStatusRefresh();
133 this.config = null;
134 this.extensionsList = [];
135 this.extensionsError = "";
@@ -132,6 +139,20 @@ export const store = createStore("browserConfig", {
139 this.hostBrowserStatusLoading = false;
140 },
141
142 + startHostBrowserStatusRefresh() {
143 + this.stopHostBrowserStatusRefresh();
144 + this.hostBrowserStatusRefreshTimer = window.setInterval(
145 + () => this.loadHostBrowserStatus(),
146 + HOST_BROWSER_STATUS_REFRESH_MS,
147 + );
148 + },
149 +
150 + stopHostBrowserStatusRefresh() {
151 + if (!this.hostBrowserStatusRefreshTimer) return;
152 + window.clearInterval(this.hostBrowserStatusRefreshTimer);
153 + this.hostBrowserStatusRefreshTimer = null;
154 + },
155 +
156 bindConfig(config) {
157 const safeConfig = ensureConfig(config);
158 if (!safeConfig) return;
plugins/_browser/webui/config.html
+1 -1
@@ -37,7 +37,7 @@
37 class="browser-config-field-help"
38 x-show="$store.browserConfig.config.runtime_backend === 'host_required'"
39 >
40 - Uses Chrome, Edge, or Chromium on the A0 CLI host. Keep A0 CLI connected; the browser opens when the agent first needs it. For an already-open browser, use chrome://inspect/#remote-debugging and enable "Allow remote debugging for this browser instance".
40 + Uses Chrome, Edge, Brave, Opera, Vivaldi, or Chromium on the A0 CLI host. Keep A0 CLI connected; the browser opens when the agent first needs it. For an already-open browser, use its inspect page, such as chrome://inspect/#remote-debugging or opera://inspect/#remote-debugging, and enable "Allow remote debugging for this browser instance".
41 </span>
42 </label>
43
tests/test_a0_connector_prompt_gating.py
+1
@@ -624,6 +624,7 @@ def test_host_browser_requests_route_to_browser_tool_not_desktop_or_shell_fallba
624 assert "code_execution_remote" in browser_prompt
625 assert "Python `webbrowser.open`" in browser_prompt
626 assert "chrome://inspect/#remote-debugging" in browser_prompt
627 + assert "opera://inspect/#remote-debugging" in browser_prompt
628 assert "Do not start `computer_use_remote` for web-page navigation" in computer_skill
629 assert (
630 "Do not fall back to `code_execution_remote`, `xdg-open`, `sensible-browser`, "
tests/test_browser_agent_regressions.py
+2
@@ -995,10 +995,12 @@ def test_browser_tool_does_not_auto_open_canvas_policy_is_documented():
995 assert "`browser-automation` links to `browser-form-workflows`" in prompt
996 assert "does not automatically load screenshots" in prompt
997 assert "chrome://inspect/#remote-debugging" in prompt
998 + assert "opera://inspect/#remote-debugging" in prompt
999 assert tokens.approximate_tokens(prompt) <= 650
1000 assert "already open" in config
1001 assert "already-open Browser surface" in config_html
1002 assert "chrome://inspect/#remote-debugging" in config_html
1003 + assert "opera://inspect/#remote-debugging" in config_html
1004
1005
1006 def test_browser_skills_are_plugin_owned_and_progressively_linked():
tests/test_host_browser_connector.py
+1
@@ -308,6 +308,7 @@ def test_connector_runtime_adds_remote_debugging_help_to_cdp_errors():
308 )
309
310 assert "chrome://inspect/#remote-debugging" in message
311 + assert "opera://inspect/#remote-debugging" in message
312 assert "Allow remote debugging for this browser instance" in message
313 assert "/browser host on" in message
314 assert "Internal Docker browser" in message