From 0d6d1edf59cc96612451b2b3d9a1fef979c8c557 Mon Sep 17 00:00:00 2001 From: Volodymyr Kasaraba Date: Wed, 27 May 2026 16:44:47 -0400 Subject: [PATCH 1/4] add sidepanel heartbeat for workflows that spawn cloud browser sessions --- packages/narada-pyodide/src/narada/client.py | 11 ++++++- .../tests/test_cloud_browser.py | 32 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/packages/narada-pyodide/src/narada/client.py b/packages/narada-pyodide/src/narada/client.py index fb7576a..f90a472 100644 --- a/packages/narada-pyodide/src/narada/client.py +++ b/packages/narada-pyodide/src/narada/client.py @@ -84,11 +84,20 @@ async def open_and_initialize_cloud_browser_window( user_id=self._user_id, env=self._env, ) - request_body = { + request_body: dict[str, Any] = { "session_name": session_name, "session_timeout": session_timeout, "require_extension": require_extension, } + # When launched from Agent Studio, the frontend injects + # NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID (initiator requestId). + initiator_remote_dispatch_request_id = os.environ.get( + "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", "" + ).strip() + if initiator_remote_dispatch_request_id: + request_body["initiator_remote_dispatch_request_id"] = ( + initiator_remote_dispatch_request_id + ) response = None max_attempts = 3 diff --git a/packages/narada-pyodide/tests/test_cloud_browser.py b/packages/narada-pyodide/tests/test_cloud_browser.py index c5b4081..ea22ec1 100644 --- a/packages/narada-pyodide/tests/test_cloud_browser.py +++ b/packages/narada-pyodide/tests/test_cloud_browser.py @@ -96,6 +96,7 @@ def new() -> SimpleNamespace: async def test_open_and_initialize_cloud_browser_window_maps_response( monkeypatch: pytest.MonkeyPatch, ) -> None: + monkeypatch.delenv("NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", raising=False) pyfetch = AsyncMock( return_value=_FakeResponse( json_data={ @@ -135,6 +136,37 @@ async def test_open_and_initialize_cloud_browser_window_maps_response( } +@pytest.mark.asyncio +async def test_open_and_initialize_cloud_browser_window_includes_initiator_request( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv( + "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", " request-local-123 " + ) + pyfetch = AsyncMock( + return_value=_FakeResponse( + json_data={ + "session_id": "session-123", + "session_name": "demo", + "browser_window_id": "browser-window-123", + } + ) + ) + narada_pkg, _, _ = _import_pyodide_narada(monkeypatch, pyfetch=pyfetch) + + client = narada_pkg.Narada(api_key="test-api-key") + await client.open_and_initialize_cloud_browser_window() + + call = pyfetch.await_args + assert call is not None + assert json.loads(call.kwargs["body"]) == { + "session_name": None, + "session_timeout": None, + "require_extension": True, + "initiator_remote_dispatch_request_id": "request-local-123", + } + + @pytest.mark.asyncio async def test_open_and_initialize_cloud_browser_window_supports_frontend_bearer_auth( monkeypatch: pytest.MonkeyPatch, From 4671a5244d95d5c7bd80c45d2ab680a181f8080e Mon Sep 17 00:00:00 2001 From: Volodymyr Kasaraba Date: Thu, 28 May 2026 11:17:28 -0400 Subject: [PATCH 2/4] bump pyodide version --- packages/narada-pyodide/pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/narada-pyodide/pyproject.toml b/packages/narada-pyodide/pyproject.toml index 0a90196..a77c8bd 100644 --- a/packages/narada-pyodide/pyproject.toml +++ b/packages/narada-pyodide/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "narada-pyodide" -version = "0.0.57" +version = "0.0.58" description = "Pyodide-compatible Python client SDK for Narada" license = "Apache-2.0" readme = "README.md" diff --git a/uv.lock b/uv.lock index 2194bba..06f9677 100644 --- a/uv.lock +++ b/uv.lock @@ -356,7 +356,7 @@ requires-dist = [{ name = "pydantic", specifier = "==2.12.5" }] [[package]] name = "narada-pyodide" -version = "0.0.57" +version = "0.0.58" source = { editable = "packages/narada-pyodide" } dependencies = [ { name = "narada-core" }, From 7a7ea65e8fffb76aa3d2391ec02f259c682e3748 Mon Sep 17 00:00:00 2001 From: Volodymyr Kasaraba Date: Thu, 28 May 2026 13:52:49 -0400 Subject: [PATCH 3/4] address PR comments --- packages/narada-pyodide/src/narada/client.py | 11 +++++---- .../tests/test_cloud_browser.py | 23 ++++++++++++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/packages/narada-pyodide/src/narada/client.py b/packages/narada-pyodide/src/narada/client.py index f90a472..12dad87 100644 --- a/packages/narada-pyodide/src/narada/client.py +++ b/packages/narada-pyodide/src/narada/client.py @@ -89,15 +89,16 @@ async def open_and_initialize_cloud_browser_window( "session_timeout": session_timeout, "require_extension": require_extension, } - # When launched from Agent Studio, the frontend injects - # NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID (initiator requestId). initiator_remote_dispatch_request_id = os.environ.get( "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", "" ).strip() - if initiator_remote_dispatch_request_id: - request_body["initiator_remote_dispatch_request_id"] = ( - initiator_remote_dispatch_request_id + if not initiator_remote_dispatch_request_id: + raise ValueError( + "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID is required" ) + request_body["initiator_remote_dispatch_request_id"] = ( + initiator_remote_dispatch_request_id + ) response = None max_attempts = 3 diff --git a/packages/narada-pyodide/tests/test_cloud_browser.py b/packages/narada-pyodide/tests/test_cloud_browser.py index ea22ec1..7098da3 100644 --- a/packages/narada-pyodide/tests/test_cloud_browser.py +++ b/packages/narada-pyodide/tests/test_cloud_browser.py @@ -96,7 +96,9 @@ def new() -> SimpleNamespace: async def test_open_and_initialize_cloud_browser_window_maps_response( monkeypatch: pytest.MonkeyPatch, ) -> None: - monkeypatch.delenv("NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", raising=False) + monkeypatch.setenv( + "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", "request-maps-123" + ) pyfetch = AsyncMock( return_value=_FakeResponse( json_data={ @@ -133,9 +135,25 @@ async def test_open_and_initialize_cloud_browser_window_maps_response( "session_name": "demo", "session_timeout": 321, "require_extension": False, + "initiator_remote_dispatch_request_id": "request-maps-123", } +@pytest.mark.asyncio +async def test_open_and_initialize_cloud_browser_window_requires_initiator_env( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.delenv("NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", raising=False) + pyfetch = AsyncMock() + narada_pkg, _, _ = _import_pyodide_narada(monkeypatch, pyfetch=pyfetch) + + client = narada_pkg.Narada(api_key="test-api-key") + with pytest.raises(ValueError, match="NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID"): + await client.open_and_initialize_cloud_browser_window() + + pyfetch.assert_not_awaited() + + @pytest.mark.asyncio async def test_open_and_initialize_cloud_browser_window_includes_initiator_request( monkeypatch: pytest.MonkeyPatch, @@ -174,6 +192,9 @@ async def test_open_and_initialize_cloud_browser_window_supports_frontend_bearer monkeypatch.delenv("NARADA_API_KEY", raising=False) monkeypatch.setenv("NARADA_USER_ID", "user-123") monkeypatch.setenv("NARADA_ENV", "dev") + monkeypatch.setenv( + "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", "request-bearer-123" + ) pyfetch = AsyncMock( side_effect=[ From c18a049a078b78e370518a50e3a87a8453d0535f Mon Sep 17 00:00:00 2001 From: Volodymyr Kasaraba Date: Thu, 28 May 2026 16:15:57 -0400 Subject: [PATCH 4/4] formatting upd --- packages/narada-pyodide/src/narada/client.py | 4 +--- packages/narada/src/narada/window.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/narada-pyodide/src/narada/client.py b/packages/narada-pyodide/src/narada/client.py index 12dad87..9d6886d 100644 --- a/packages/narada-pyodide/src/narada/client.py +++ b/packages/narada-pyodide/src/narada/client.py @@ -93,9 +93,7 @@ async def open_and_initialize_cloud_browser_window( "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID", "" ).strip() if not initiator_remote_dispatch_request_id: - raise ValueError( - "NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID is required" - ) + raise ValueError("NARADA_INITIATOR_REMOTE_DISPATCH_REQUEST_ID is required") request_body["initiator_remote_dispatch_request_id"] = ( initiator_remote_dispatch_request_id ) diff --git a/packages/narada/src/narada/window.py b/packages/narada/src/narada/window.py index 494a976..e5b963a 100644 --- a/packages/narada/src/narada/window.py +++ b/packages/narada/src/narada/window.py @@ -24,10 +24,10 @@ from narada_core.actions.critic import run_critic from narada_core.actions.models import ( DEFAULT_HITL_TIMEOUT_SECONDS, - AgenticMouseAction, - AgenticMouseActionRequest, AgenticMatchingSelectorsFinderRequest, AgenticMatchingSelectorsFinderResponse, + AgenticMouseAction, + AgenticMouseActionRequest, AgenticSelectorAction, AgenticSelectorRequest, AgenticSelectorResponse,