Fix browser URL intent routing
Limit the Browser URL-intent handler to web URL schemes so custom Agent Zero schemes fall through to their owning surfaces. Document the scheme-ownership contract and cover the a0-editor misroute regression in the desktop/editor routing test.
Alessandro committed
Jul 3, 2026 at 01:07 UTC
05481c222aa3d8da936fd2a8780597332898456d
3 files changed
+20
plugins/_browser/AGENTS.md
+1
@@ -20,6 +20,7 @@
20
- Keep the WebUI Browser inside its own modal/canvas affordance; do not replace it with page-level navigation.
21
- Default the visible WebUI Browser to live CDP screencast for responsiveness. Keep lightweight CDP/DOM state snapshots as the fallback transport.
22
- Keep narrow WebUI Browser controls usable by grouping navigation with Annotate/settings above a full-width address bar.
23
+- Browser URL-intent handling must only claim web URL schemes and leave custom Agent Zero schemes to their owning surfaces.
24
- Prefer DOM/CDP browser actions with refs, selectors, frame-chain refs, and screenshots over viewport coordinate input. Coordinates remain a visual fallback.
25
- Do not hardcode user-specific browser paths or secrets.
26
plugins/_browser/webui/browser-store.js
+13
@@ -2846,8 +2846,21 @@ const model = {
2846
2847
export const store = createStore("browserPage", model);
2848
2849
+const WEB_INTENT_SCHEMES = new Set(["http", "https", "file", "about"]);
2850
+
2851
+function isWebUrlIntent(url = "") {
2852
+ const value = String(url || "").trim();
2853
+ if (!value) return true;
2854
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value);
2855
+ if (!scheme) return true;
2856
+ return WEB_INTENT_SCHEMES.has(scheme[1].toLowerCase());
2857
+}
2858
+
2859
registerUrlHandler(async (intent = {}) => {
2860
const url = String(intent.url || "").trim();
2861
+ // Custom schemes such as a0-editor: belong to other surfaces; claiming them
2862
+ // here would navigate the browser to an unloadable URL.
2863
+ if (!isWebUrlIntent(url)) return false;
2864
const payload = { url, source: intent.source || "surface-url-intent" };
2865
await openLatestSurface("browser", payload);
2866
return await store.openUrlIntent(url, { source: payload.source });
tests/test_office_canvas_setup.py
+6
@@ -592,6 +592,7 @@ def test_desktop_text_open_with_routes_to_editor_surface():
592
desktop_session = read("plugins", "_desktop", "helpers", "desktop_session.py")
593
desktop_store = read("plugins", "_desktop", "webui", "desktop-store.js")
594
editor_store = read("plugins", "_editor", "webui", "editor-store.js")
595
+ browser_store = read("plugins", "_browser", "webui", "browser-store.js")
596
597
assert 'EDITOR_HANDLER_DESKTOP_ID = "agent-zero-editor.desktop"' in desktop_session
598
assert "def _write_editor_bridge_script" in desktop_session
@@ -607,6 +608,11 @@ def test_desktop_text_open_with_routes_to_editor_surface():
608
assert "handleEditorUrlIntent" in editor_store
609
assert 'openLatestSurface("editor"' in editor_store
610
611
+ # The browser surface must not claim a0-editor: intents, otherwise the
612
+ # editor "Open With" handler lands on an unloadable about:blank page.
613
+ assert "function isWebUrlIntent" in browser_store
614
+ assert "if (!isWebUrlIntent(url)) return false;" in browser_store
615
+
616
617
def test_office_and_desktop_skills_are_rehomed_and_renamed():
618
office_skills = PROJECT_ROOT / "plugins" / "_office" / "skills"