From e3a37a88f584b649df90b7757de0217a7882fdb8 Mon Sep 17 00:00:00 2001 From: Sehoon Kim Date: Tue, 26 May 2026 15:40:02 -0700 Subject: [PATCH 1/5] Expose agentic XPath finder in SDK --- .../src/narada_core/actions/models.py | 10 ++++++++++ packages/narada-pyodide/src/narada/window.py | 16 ++++++++++++++++ packages/narada/src/narada/window.py | 16 ++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/packages/narada-core/src/narada_core/actions/models.py b/packages/narada-core/src/narada_core/actions/models.py index 89b72c6..7d64b86 100644 --- a/packages/narada-core/src/narada_core/actions/models.py +++ b/packages/narada-core/src/narada_core/actions/models.py @@ -200,6 +200,15 @@ class AgenticSelectorResponse(BaseModel): value: str | None +class AgenticXPathFinderRequest(BaseModel): + name: Literal["agentic_xpath_finder"] = "agentic_xpath_finder" + prompt: str + + +class AgenticXPathFinderResponse(BaseModel): + xpaths: list[str] + + class WaitForElementRequest(BaseModel): name: Literal["wait_for_element"] = "wait_for_element" selectors: AgenticSelectors @@ -422,6 +431,7 @@ class UserApprovalResponse(BaseModel): type ExtensionActionRequest = ( AgenticSelectorRequest + | AgenticXPathFinderRequest | AgenticMouseActionRequest | CloseWindowRequest | GoToUrlRequest diff --git a/packages/narada-pyodide/src/narada/window.py b/packages/narada-pyodide/src/narada/window.py index 4822167..94cc271 100644 --- a/packages/narada-pyodide/src/narada/window.py +++ b/packages/narada-pyodide/src/narada/window.py @@ -28,6 +28,8 @@ AgenticSelectorRequest, AgenticSelectorResponse, AgenticSelectors, + AgenticXPathFinderRequest, + AgenticXPathFinderResponse, AgentResponse, AgentUsage, CloseWindowRequest, @@ -746,6 +748,20 @@ async def agentic_selector( return result + async def agentic_xpath_finder( + self, + *, + prompt: str, + timeout: int | None = 300, + ) -> list[str]: + """Finds all visible page elements matching a prompt and returns XPath selectors.""" + result = await self._run_extension_action( + AgenticXPathFinderRequest(prompt=prompt), + AgenticXPathFinderResponse, + timeout=timeout, + ) + return result.xpaths + async def agentic_mouse_action( self, *, diff --git a/packages/narada/src/narada/window.py b/packages/narada/src/narada/window.py index 49d9465..06efa5a 100644 --- a/packages/narada/src/narada/window.py +++ b/packages/narada/src/narada/window.py @@ -29,6 +29,8 @@ AgenticSelectorRequest, AgenticSelectorResponse, AgenticSelectors, + AgenticXPathFinderRequest, + AgenticXPathFinderResponse, AgentResponse, AgentUsage, CloseWindowRequest, @@ -696,6 +698,20 @@ async def agentic_selector( return result + async def agentic_xpath_finder( + self, + *, + prompt: str, + timeout: int | None = 300, + ) -> list[str]: + """Finds all visible page elements matching a prompt and returns XPath selectors.""" + result = await self._run_extension_action( + AgenticXPathFinderRequest(prompt=prompt), + AgenticXPathFinderResponse, + timeout=timeout, + ) + return result.xpaths + async def agentic_mouse_action( self, *, From 4b09a43bcf3891f5d80c663133fa5bc7330dbfae Mon Sep 17 00:00:00 2001 From: Sehoon Kim Date: Tue, 26 May 2026 16:39:33 -0700 Subject: [PATCH 2/5] Return selectors from matching element finder --- .../src/narada_core/actions/models.py | 12 ++++--- packages/narada-pyodide/src/narada/window.py | 32 +++++++++++++++---- packages/narada/src/narada/window.py | 32 +++++++++++++++---- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/packages/narada-core/src/narada_core/actions/models.py b/packages/narada-core/src/narada_core/actions/models.py index 7d64b86..1049bcb 100644 --- a/packages/narada-core/src/narada_core/actions/models.py +++ b/packages/narada-core/src/narada_core/actions/models.py @@ -200,13 +200,15 @@ class AgenticSelectorResponse(BaseModel): value: str | None -class AgenticXPathFinderRequest(BaseModel): - name: Literal["agentic_xpath_finder"] = "agentic_xpath_finder" +class AgenticMatchingElementFinderRequest(BaseModel): + name: Literal["agentic_matching_element_finder"] = ( + "agentic_matching_element_finder" + ) prompt: str -class AgenticXPathFinderResponse(BaseModel): - xpaths: list[str] +class AgenticMatchingElementFinderResponse(BaseModel): + selectors: list[AgenticSelectors] class WaitForElementRequest(BaseModel): @@ -431,7 +433,7 @@ class UserApprovalResponse(BaseModel): type ExtensionActionRequest = ( AgenticSelectorRequest - | AgenticXPathFinderRequest + | AgenticMatchingElementFinderRequest | AgenticMouseActionRequest | CloseWindowRequest | GoToUrlRequest diff --git a/packages/narada-pyodide/src/narada/window.py b/packages/narada-pyodide/src/narada/window.py index 94cc271..f11e6ca 100644 --- a/packages/narada-pyodide/src/narada/window.py +++ b/packages/narada-pyodide/src/narada/window.py @@ -24,12 +24,12 @@ from narada_core.actions.models import ( AgenticMouseAction, AgenticMouseActionRequest, + AgenticMatchingElementFinderRequest, + AgenticMatchingElementFinderResponse, AgenticSelectorAction, AgenticSelectorRequest, AgenticSelectorResponse, AgenticSelectors, - AgenticXPathFinderRequest, - AgenticXPathFinderResponse, AgentResponse, AgentUsage, CloseWindowRequest, @@ -748,19 +748,37 @@ async def agentic_selector( return result + async def agentic_matching_element_finder( + self, + *, + prompt: str, + timeout: int | None = 300, + ) -> list[AgenticSelectors]: + """Finds all visible page elements matching a prompt and returns selectors.""" + result = await self._run_extension_action( + AgenticMatchingElementFinderRequest(prompt=prompt), + AgenticMatchingElementFinderResponse, + timeout=timeout, + ) + return result.selectors + async def agentic_xpath_finder( self, *, prompt: str, timeout: int | None = 300, ) -> list[str]: - """Finds all visible page elements matching a prompt and returns XPath selectors.""" - result = await self._run_extension_action( - AgenticXPathFinderRequest(prompt=prompt), - AgenticXPathFinderResponse, + """Finds matching page elements and returns their XPath selectors.""" + selectors = await self.agentic_matching_element_finder( + prompt=prompt, timeout=timeout, ) - return result.xpaths + xpaths: list[str] = [] + for selector in selectors: + xpath = selector.get("xpath") + if isinstance(xpath, str): + xpaths.append(xpath) + return xpaths async def agentic_mouse_action( self, diff --git a/packages/narada/src/narada/window.py b/packages/narada/src/narada/window.py index 06efa5a..8da6bab 100644 --- a/packages/narada/src/narada/window.py +++ b/packages/narada/src/narada/window.py @@ -25,12 +25,12 @@ from narada_core.actions.models import ( AgenticMouseAction, AgenticMouseActionRequest, + AgenticMatchingElementFinderRequest, + AgenticMatchingElementFinderResponse, AgenticSelectorAction, AgenticSelectorRequest, AgenticSelectorResponse, AgenticSelectors, - AgenticXPathFinderRequest, - AgenticXPathFinderResponse, AgentResponse, AgentUsage, CloseWindowRequest, @@ -698,19 +698,37 @@ async def agentic_selector( return result + async def agentic_matching_element_finder( + self, + *, + prompt: str, + timeout: int | None = 300, + ) -> list[AgenticSelectors]: + """Finds all visible page elements matching a prompt and returns selectors.""" + result = await self._run_extension_action( + AgenticMatchingElementFinderRequest(prompt=prompt), + AgenticMatchingElementFinderResponse, + timeout=timeout, + ) + return result.selectors + async def agentic_xpath_finder( self, *, prompt: str, timeout: int | None = 300, ) -> list[str]: - """Finds all visible page elements matching a prompt and returns XPath selectors.""" - result = await self._run_extension_action( - AgenticXPathFinderRequest(prompt=prompt), - AgenticXPathFinderResponse, + """Finds matching page elements and returns their XPath selectors.""" + selectors = await self.agentic_matching_element_finder( + prompt=prompt, timeout=timeout, ) - return result.xpaths + xpaths: list[str] = [] + for selector in selectors: + xpath = selector.get("xpath") + if isinstance(xpath, str): + xpaths.append(xpath) + return xpaths async def agentic_mouse_action( self, From f25d2ddaf89f8d6a81cc593fb80f5f2dc280ab1d Mon Sep 17 00:00:00 2001 From: Sehoon Kim Date: Tue, 26 May 2026 16:43:48 -0700 Subject: [PATCH 3/5] Expose matching element finder as SDK primitive --- packages/narada-pyodide/src/narada/window.py | 18 ------------------ packages/narada/src/narada/window.py | 18 ------------------ 2 files changed, 36 deletions(-) diff --git a/packages/narada-pyodide/src/narada/window.py b/packages/narada-pyodide/src/narada/window.py index f11e6ca..3c2bee4 100644 --- a/packages/narada-pyodide/src/narada/window.py +++ b/packages/narada-pyodide/src/narada/window.py @@ -762,24 +762,6 @@ async def agentic_matching_element_finder( ) return result.selectors - async def agentic_xpath_finder( - self, - *, - prompt: str, - timeout: int | None = 300, - ) -> list[str]: - """Finds matching page elements and returns their XPath selectors.""" - selectors = await self.agentic_matching_element_finder( - prompt=prompt, - timeout=timeout, - ) - xpaths: list[str] = [] - for selector in selectors: - xpath = selector.get("xpath") - if isinstance(xpath, str): - xpaths.append(xpath) - return xpaths - async def agentic_mouse_action( self, *, diff --git a/packages/narada/src/narada/window.py b/packages/narada/src/narada/window.py index 8da6bab..f0d2fef 100644 --- a/packages/narada/src/narada/window.py +++ b/packages/narada/src/narada/window.py @@ -712,24 +712,6 @@ async def agentic_matching_element_finder( ) return result.selectors - async def agentic_xpath_finder( - self, - *, - prompt: str, - timeout: int | None = 300, - ) -> list[str]: - """Finds matching page elements and returns their XPath selectors.""" - selectors = await self.agentic_matching_element_finder( - prompt=prompt, - timeout=timeout, - ) - xpaths: list[str] = [] - for selector in selectors: - xpath = selector.get("xpath") - if isinstance(xpath, str): - xpaths.append(xpath) - return xpaths - async def agentic_mouse_action( self, *, From 16c80ae1c9de5085141bdc84db600f71982de8fd Mon Sep 17 00:00:00 2001 From: Sehoon Kim Date: Tue, 26 May 2026 17:22:17 -0700 Subject: [PATCH 4/5] Rename matching selectors finder SDK primitive --- .../narada-core/src/narada_core/actions/models.py | 10 +++++----- packages/narada-pyodide/src/narada/window.py | 12 ++++++------ packages/narada/src/narada/window.py | 12 ++++++------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/narada-core/src/narada_core/actions/models.py b/packages/narada-core/src/narada_core/actions/models.py index 1049bcb..cd4ac82 100644 --- a/packages/narada-core/src/narada_core/actions/models.py +++ b/packages/narada-core/src/narada_core/actions/models.py @@ -200,14 +200,14 @@ class AgenticSelectorResponse(BaseModel): value: str | None -class AgenticMatchingElementFinderRequest(BaseModel): - name: Literal["agentic_matching_element_finder"] = ( - "agentic_matching_element_finder" +class AgenticMatchingSelectorsFinderRequest(BaseModel): + name: Literal["agentic_matching_selectors_finder"] = ( + "agentic_matching_selectors_finder" ) prompt: str -class AgenticMatchingElementFinderResponse(BaseModel): +class AgenticMatchingSelectorsFinderResponse(BaseModel): selectors: list[AgenticSelectors] @@ -433,7 +433,7 @@ class UserApprovalResponse(BaseModel): type ExtensionActionRequest = ( AgenticSelectorRequest - | AgenticMatchingElementFinderRequest + | AgenticMatchingSelectorsFinderRequest | AgenticMouseActionRequest | CloseWindowRequest | GoToUrlRequest diff --git a/packages/narada-pyodide/src/narada/window.py b/packages/narada-pyodide/src/narada/window.py index 3c2bee4..9df31f6 100644 --- a/packages/narada-pyodide/src/narada/window.py +++ b/packages/narada-pyodide/src/narada/window.py @@ -24,8 +24,8 @@ from narada_core.actions.models import ( AgenticMouseAction, AgenticMouseActionRequest, - AgenticMatchingElementFinderRequest, - AgenticMatchingElementFinderResponse, + AgenticMatchingSelectorsFinderRequest, + AgenticMatchingSelectorsFinderResponse, AgenticSelectorAction, AgenticSelectorRequest, AgenticSelectorResponse, @@ -748,16 +748,16 @@ async def agentic_selector( return result - async def agentic_matching_element_finder( + async def agentic_matching_selectors_finder( self, *, prompt: str, timeout: int | None = 300, ) -> list[AgenticSelectors]: - """Finds all visible page elements matching a prompt and returns selectors.""" + """Finds all visible targets matching a prompt and returns selectors.""" result = await self._run_extension_action( - AgenticMatchingElementFinderRequest(prompt=prompt), - AgenticMatchingElementFinderResponse, + AgenticMatchingSelectorsFinderRequest(prompt=prompt), + AgenticMatchingSelectorsFinderResponse, timeout=timeout, ) return result.selectors diff --git a/packages/narada/src/narada/window.py b/packages/narada/src/narada/window.py index f0d2fef..14b7d99 100644 --- a/packages/narada/src/narada/window.py +++ b/packages/narada/src/narada/window.py @@ -25,8 +25,8 @@ from narada_core.actions.models import ( AgenticMouseAction, AgenticMouseActionRequest, - AgenticMatchingElementFinderRequest, - AgenticMatchingElementFinderResponse, + AgenticMatchingSelectorsFinderRequest, + AgenticMatchingSelectorsFinderResponse, AgenticSelectorAction, AgenticSelectorRequest, AgenticSelectorResponse, @@ -698,16 +698,16 @@ async def agentic_selector( return result - async def agentic_matching_element_finder( + async def agentic_matching_selectors_finder( self, *, prompt: str, timeout: int | None = 300, ) -> list[AgenticSelectors]: - """Finds all visible page elements matching a prompt and returns selectors.""" + """Finds all visible targets matching a prompt and returns selectors.""" result = await self._run_extension_action( - AgenticMatchingElementFinderRequest(prompt=prompt), - AgenticMatchingElementFinderResponse, + AgenticMatchingSelectorsFinderRequest(prompt=prompt), + AgenticMatchingSelectorsFinderResponse, timeout=timeout, ) return result.selectors From b6b1838ba1df070af9959a22f3e21ff787022caa Mon Sep 17 00:00:00 2001 From: Sehoon Kim Date: Wed, 27 May 2026 13:09:50 -0700 Subject: [PATCH 5/5] Bump SDK package versions --- packages/narada-core/pyproject.toml | 2 +- packages/narada-pyodide/pyproject.toml | 4 ++-- packages/narada/pyproject.toml | 4 ++-- uv.lock | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/narada-core/pyproject.toml b/packages/narada-core/pyproject.toml index 94f03b6..160be52 100644 --- a/packages/narada-core/pyproject.toml +++ b/packages/narada-core/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "narada-core" -version = "0.0.25" +version = "0.0.26" description = "Code shared by the `narada` and `narada-pyodide` packages." license = "Apache-2.0" readme = "README.md" diff --git a/packages/narada-pyodide/pyproject.toml b/packages/narada-pyodide/pyproject.toml index bd16fca..0a90196 100644 --- a/packages/narada-pyodide/pyproject.toml +++ b/packages/narada-pyodide/pyproject.toml @@ -1,14 +1,14 @@ [project] name = "narada-pyodide" -version = "0.0.56" +version = "0.0.57" description = "Pyodide-compatible Python client SDK for Narada" license = "Apache-2.0" readme = "README.md" authors = [{ name = "Narada", email = "support@narada.ai" }] requires-python = ">=3.12" dependencies = [ - "narada-core==0.0.25", + "narada-core==0.0.26", # Must be a supported version in https://pyodide.org/en/stable/usage/packages-in-pyodide.html "packaging==24.2", ] diff --git a/packages/narada/pyproject.toml b/packages/narada/pyproject.toml index c07f1b2..9154cd0 100644 --- a/packages/narada/pyproject.toml +++ b/packages/narada/pyproject.toml @@ -1,13 +1,13 @@ [project] name = "narada" -version = "0.1.53a5" +version = "0.1.53a6" description = "Python client SDK for Narada" license = "Apache-2.0" readme = "README.md" authors = [{ name = "Narada", email = "support@narada.ai" }] requires-python = ">=3.12" dependencies = [ - "narada-core==0.0.25", + "narada-core==0.0.26", "aiohttp>=3.12.13", "playwright>=1.53.0", "rich>=14.0.0", diff --git a/uv.lock b/uv.lock index 00f0daf..2194bba 100644 --- a/uv.lock +++ b/uv.lock @@ -312,7 +312,7 @@ wheels = [ [[package]] name = "narada" -version = "0.1.53a5" +version = "0.1.53a6" source = { editable = "packages/narada" } dependencies = [ { name = "aiohttp" }, @@ -345,7 +345,7 @@ dev = [ [[package]] name = "narada-core" -version = "0.0.25" +version = "0.0.26" source = { editable = "packages/narada-core" } dependencies = [ { name = "pydantic" }, @@ -356,7 +356,7 @@ requires-dist = [{ name = "pydantic", specifier = "==2.12.5" }] [[package]] name = "narada-pyodide" -version = "0.0.56" +version = "0.0.57" source = { editable = "packages/narada-pyodide" } dependencies = [ { name = "narada-core" },