diff --git a/hermes_cli/tools_config.py b/hermes_cli/tools_config.py index bc9c38fd85..71d2ed2593 100644 --- a/hermes_cli/tools_config.py +++ b/hermes_cli/tools_config.py @@ -790,11 +790,12 @@ def _resolved_cua_driver_cmd() -> Optional[str]: def _cua_driver_env() -> dict: - """Return the shared cua-driver child environment. + """cua-driver child env with the Hermes telemetry policy applied. - Delegates to ``cua_backend.cua_driver_child_env`` so install and status use - the same policy as runtime and doctor. Falls back to the current environment - if the helper cannot be imported. + Delegates to ``cua_backend.cua_driver_child_env`` (telemetry disabled by + default; user opt-in via ``computer_use.cua_telemetry``). Falls back to the + current environment if the helper can't be imported, so install/status + never break on a telemetry-helper error. """ try: from tools.computer_use.cua_backend import cua_driver_child_env diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 560014d621..bc0db86e04 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -1522,7 +1522,9 @@ _CATEGORY_MERGE: Dict[str, str] = { # the MCP tab) — fold it into the agent tab rather than spawning a # one-field orphan category. "mcp": "agent", - # Keep the small computer_use settings group in the agent tab. + # `computer_use.cua_telemetry` is the only schema-surfaced computer_use + # field — fold it into the agent tab rather than spawning a one-field + # orphan category. "computer_use": "agent", # `telemetry.shared_metrics.enabled` is the only schema-surfaced telemetry # field — fold it into security alongside the other privacy-posture toggles. diff --git a/tests/computer_use/test_cua_wayland_env.py b/tests/computer_use/test_cua_wayland_env.py index 3c5a3da684..df06e132d6 100644 --- a/tests/computer_use/test_cua_wayland_env.py +++ b/tests/computer_use/test_cua_wayland_env.py @@ -6,26 +6,17 @@ from tools.computer_use import cua_backend _VAR = "CUA_DRIVER_RS_ENABLE_WAYLAND" +def _child_env(base_env, native_wayland): + config = {"computer_use": {"native_wayland": native_wayland}} + with patch("hermes_cli.config.load_config", return_value=config), \ + patch.object(cua_backend.sys, "platform", "linux"): + return cua_backend.cua_driver_child_env(base_env) + + def test_configured_native_wayland_reaches_linux_wayland_child(): - config = {"computer_use": {"native_wayland": True}} - with patch("hermes_cli.config.load_config", return_value=config), \ - patch.object(cua_backend.sys, "platform", "linux"): - env = cua_backend.cua_driver_child_env({"WAYLAND_DISPLAY": "wayland-1"}) - assert env[_VAR] == "1" + assert _child_env({"WAYLAND_DISPLAY": "wayland-1"}, True)[_VAR] == "1" -def test_configured_native_wayland_does_not_enable_x11_child(): - config = {"computer_use": {"native_wayland": True}} - with patch("hermes_cli.config.load_config", return_value=config), \ - patch.object(cua_backend.sys, "platform", "linux"): - env = cua_backend.cua_driver_child_env({"DISPLAY": ":0"}) - assert _VAR not in env - - -def test_default_preserves_manual_environment_opt_in(): - config = {"computer_use": {"native_wayland": False}} - base_env = {"WAYLAND_DISPLAY": "wayland-1", _VAR: "1"} - with patch("hermes_cli.config.load_config", return_value=config), \ - patch.object(cua_backend.sys, "platform", "linux"): - env = cua_backend.cua_driver_child_env(base_env) - assert env[_VAR] == "1" +def test_native_wayland_not_injected_without_wayland_display_or_opt_in(): + assert _VAR not in _child_env({"DISPLAY": ":0"}, True) + assert _VAR not in _child_env({"WAYLAND_DISPLAY": "wayland-1"}, False) diff --git a/tests/computer_use/test_doctor.py b/tests/computer_use/test_doctor.py index 664efbeab4..ad7c699c50 100644 --- a/tests/computer_use/test_doctor.py +++ b/tests/computer_use/test_doctor.py @@ -267,49 +267,6 @@ class TestJsonOutput: assert "hermes_identity" in parsed assert parsed["hermes_identity"]["resolved_binary"] - def test_linux_wayland_output_identifies_cli_environment_scope(self, monkeypatch): - from tools.computer_use import doctor - - monkeypatch.setenv("WAYLAND_DISPLAY", "wayland-1") - monkeypatch.setenv("DISPLAY", ":0") - report = _ok_report() - report["platform"] = "linux" - proc = _fake_proc_with_responses( - {"jsonrpc": "2.0", "id": 1, "result": {}}, - {"jsonrpc": "2.0", "id": 2, "result": {"structuredContent": report}}, - ) - - with patch("shutil.which", return_value="/fake/cua-driver"), \ - patch("subprocess.Popen", return_value=proc), \ - patch("sys.stdout", new_callable=StringIO) as out: - doctor.run_doctor(json_output=True) - - context = json.loads(out.getvalue())["hermes_environment"] - assert context == { - "gateway_environment_checked": False, - "scope": "cli_process", - } - - def test_linux_wayland_text_warns_gateway_environment_may_differ(self, monkeypatch): - from tools.computer_use import doctor - - monkeypatch.setenv("WAYLAND_DISPLAY", "wayland-1") - report = _ok_report() - report["platform"] = "linux" - proc = _fake_proc_with_responses( - {"jsonrpc": "2.0", "id": 1, "result": {}}, - {"jsonrpc": "2.0", "id": 2, "result": {"structuredContent": report}}, - ) - - with patch("shutil.which", return_value="/fake/cua-driver"), \ - patch("subprocess.Popen", return_value=proc), \ - patch("sys.stdout", new_callable=StringIO) as out: - doctor.run_doctor() - - text = out.getvalue().lower() - assert "current cli process" in text - assert "gateway environment was not checked" in text - # ── HERMES_CUA_DRIVER_CMD resolution ───────────────────────────────────────