Report Host access control failures

Use Agent Zero's shared notification system when Launcher gateway mutations fail, then refresh the popover state as before.

Alessandro committed Jul 14, 2026 at 22:43 UTC 3271ff43e3510503ebe6c5eb979233a130160583
3 files changed +20 -1
plugins/_a0_connector/AGENTS.md
+2 -1
@@ -42,7 +42,8 @@
42 not expose it. Keep its trigger icon-only at rest and reveal the status label
43 on hover, keyboard focus, or while open. Connected and disconnected states
44 share the computer glyph; the adjacent status dot communicates connectivity.
45 - Keep the open popover above the compact right-canvas rail.
45 + Keep the open popover above the compact right-canvas rail and report control
46 + failures through the shared notification system.
47 - File operation results may arrive as chunked JSON/base64
48 `connector_file_op_result` frames; resolve the pending file operation only
49 after all chunks for the `op_id` are assembled.
plugins/_a0_connector/webui/launcher-gateway-store.js
+5
@@ -1,5 +1,6 @@
1 import { createStore } from "/js/AlpineStore.js";
2 import { callJsonApi } from "/js/api.js";
3 +import { toastFrontendError } from "/components/notifications/notification-store.js";
4
5 const STATUS_API = "/plugins/_a0_connector/v1/launcher_gateway_status";
6 const CONTROL_API = "/plugins/_a0_connector/v1/launcher_gateway_control";
@@ -120,6 +121,10 @@ const model = {
121 this.status = response?.status || this.status;
122 } catch (error) {
123 console.error("Failed to control Launcher host:", error);
124 + void toastFrontendError(
125 + error?.message || "Host access change failed.",
126 + "Host access",
127 + );
128 await this.refresh();
129 } finally {
130 this.saving = false;
tests/test_a0_connector_launcher_gateway.py
+13
@@ -1,6 +1,7 @@
1 from __future__ import annotations
2
3 import asyncio
4 +from pathlib import Path
5 import uuid
6
7 from plugins._a0_connector.api.v1 import launcher_gateway_control
@@ -37,6 +38,18 @@ def test_launcher_gateway_features_are_negotiated_on_http_and_websocket() -> Non
38 assert LauncherGatewayStatus.requires_auth() is True
39
40
41 +def test_launcher_gateway_control_errors_use_webui_notifications() -> None:
42 + source = (
43 + Path(__file__).parents[1]
44 + / "plugins"
45 + / "_a0_connector"
46 + / "webui"
47 + / "launcher-gateway-store.js"
48 + ).read_text(encoding="utf-8")
49 + assert 'import { toastFrontendError }' in source
50 + assert 'void toastFrontendError(' in source
51 +
52 +
53 def test_launcher_gateway_is_fallback_after_context_bound_cli() -> None:
54 context_id = f"ctx-{uuid.uuid4()}"
55 cli_sid = _sid("cli")