From 4e8419dadb502f762d09dad5de6537847ae10318 Mon Sep 17 00:00:00 2001 From: loulanyue <260355617@qq.com> Date: Sun, 9 Aug 2026 08:32:20 +0800 Subject: [PATCH] fix(gateway): honor approval scope capabilities --- gateway/platforms/api_server.py | 14 +++++++-- tests/gateway/test_api_server_runs.py | 26 ++++++++++------- tests/gateway/test_tui_approval_redaction.py | 30 ++++++++++++++++++++ tui_gateway/server.py | 12 +++++--- 4 files changed, 65 insertions(+), 17 deletions(-) diff --git a/gateway/platforms/api_server.py b/gateway/platforms/api_server.py index a9bdea8efe..980659c934 100644 --- a/gateway/platforms/api_server.py +++ b/gateway/platforms/api_server.py @@ -117,10 +117,17 @@ _BROWSER_CONTROL_PROTOCOL_VERSION = 1 _BROWSER_CONTROL_WS_PROTOCOL = "hermes-browser-control-v1" _BROWSER_CONTROL_TICKET_PROTOCOL_PREFIX = "hermes-browser-control-ticket." -def _approval_event_choices(*, smart_denied: bool, allow_permanent: bool) -> list[str]: - if smart_denied: + +def _approval_event_choices( + *, smart_denied: bool, allow_session: bool, allow_permanent: bool +) -> list[str]: + if smart_denied or not allow_session: return ["once", "deny"] - return ["once", "session", "always", "deny"] if allow_permanent else ["once", "session", "deny"] + return ( + ["once", "session", "always", "deny"] + if allow_permanent + else ["once", "session", "deny"] + ) try: @@ -7750,6 +7757,7 @@ class APIServerAdapter(BasePlatformAdapter): "timestamp": time.time(), "choices": _approval_event_choices( smart_denied=bool(event.get("smart_denied")), + allow_session=event.get("allow_session") is not False, allow_permanent=event.get("allow_permanent") is not False, ), }) diff --git a/tests/gateway/test_api_server_runs.py b/tests/gateway/test_api_server_runs.py index d3edc9d151..0573e48ec7 100644 --- a/tests/gateway/test_api_server_runs.py +++ b/tests/gateway/test_api_server_runs.py @@ -34,21 +34,27 @@ from tools import approval as approval_mod @pytest.mark.parametrize( - ("smart_denied", "allow_permanent", "expected"), + ("smart_denied", "allow_session", "allow_permanent", "expected"), [ - (False, True, ["once", "session", "always", "deny"]), - (False, False, ["once", "session", "deny"]), - (True, True, ["once", "deny"]), - (True, False, ["once", "deny"]), + (False, True, True, ["once", "session", "always", "deny"]), + (False, True, False, ["once", "session", "deny"]), + (False, False, True, ["once", "deny"]), + (False, False, False, ["once", "deny"]), + (True, True, True, ["once", "deny"]), + (True, False, False, ["once", "deny"]), ], ) def test_approval_event_choices_follow_backend_capabilities( - smart_denied, allow_permanent, expected + smart_denied, allow_session, allow_permanent, expected ): - assert _approval_event_choices( - smart_denied=smart_denied, - allow_permanent=allow_permanent, - ) == expected + assert ( + _approval_event_choices( + smart_denied=smart_denied, + allow_session=allow_session, + allow_permanent=allow_permanent, + ) + == expected + ) def _make_adapter(api_key: str = "") -> APIServerAdapter: diff --git a/tests/gateway/test_tui_approval_redaction.py b/tests/gateway/test_tui_approval_redaction.py index 818dcadecc..af7f498f3d 100644 --- a/tests/gateway/test_tui_approval_redaction.py +++ b/tests/gateway/test_tui_approval_redaction.py @@ -34,4 +34,34 @@ class TestTuiApprovalEmitRedaction: assert emitted["payload"]["description"] == "x" assert "github.com" in emitted["payload"]["command"] + @pytest.mark.parametrize( + ("allow_session", "allow_permanent", "expected"), + [ + (True, True, ["once", "session", "always", "deny"]), + (True, False, ["once", "session", "deny"]), + (False, False, ["once", "deny"]), + ], + ) + def test_emit_approval_request_honors_allowed_scopes( + self, monkeypatch, allow_session, allow_permanent, expected + ): + from tui_gateway import server as tui_server + + emitted = {} + monkeypatch.setattr( + tui_server, + "_emit", + lambda event, sid, payload=None: emitted.update({"payload": payload}), + ) + + tui_server._emit_approval_request( + "sess-1", + { + "allow_permanent": allow_permanent, + "allow_session": allow_session, + "command": "", + }, + ) + + assert emitted["payload"]["choices"] == expected diff --git a/tui_gateway/server.py b/tui_gateway/server.py index 63b382c7dc..aa74daea42 100644 --- a/tui_gateway/server.py +++ b/tui_gateway/server.py @@ -1953,10 +1953,14 @@ def _approval_request_payload(data: dict | None) -> dict: if "choices" not in payload: if payload.get("smart_denied"): payload["choices"] = ["once", "deny"] - elif payload.get("allow_permanent") is False: - payload["choices"] = ["once", "session", "deny"] - elif "allow_permanent" in payload: - payload["choices"] = ["once", "session", "always", "deny"] + else: + choices = ["once"] + if payload.get("allow_session") is not False: + choices.append("session") + if payload.get("allow_permanent") is not False: + choices.append("always") + choices.append("deny") + payload["choices"] = choices if "command" in payload: from gateway.run import _redact_approval_command