From 4f6e5e9cb8879e137fb9eac1bb1b00cd2f9e7417 Mon Sep 17 00:00:00 2001 From: Ilya Gusev Date: Mon, 15 Jun 2026 11:40:01 +0000 Subject: [PATCH] feat(web): send honest nanobot UA for Keenable Keenable is a first-party API, so identify as nanobot/ instead of the shared spoofed-browser User-Agent used by scraping providers. Co-Authored-By: Claude Opus 4.8 (1M context) --- nanobot/agent/tools/web.py | 4 +++- tests/tools/test_web_search_tool.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nanobot/agent/tools/web.py b/nanobot/agent/tools/web.py index f2a5e0e5..e1e1976b 100644 --- a/nanobot/agent/tools/web.py +++ b/nanobot/agent/tools/web.py @@ -489,8 +489,10 @@ class WebSearchTool(Tool): return f"Error: {e}" async def _search_keenable(self, query: str, n: int) -> str: + from nanobot import __version__ api_key = self.config.api_key or os.environ.get("KEENABLE_API_KEY", "") - headers = {"Content-Type": "application/json", "User-Agent": self.user_agent} + # First-party API: identify honestly instead of the spoofed browser UA. + headers = {"Content-Type": "application/json", "User-Agent": f"nanobot/{__version__}"} if api_key: headers["X-API-Key"] = api_key try: diff --git a/tests/tools/test_web_search_tool.py b/tests/tools/test_web_search_tool.py index 85870fd9..9cda6758 100644 --- a/tests/tools/test_web_search_tool.py +++ b/tests/tools/test_web_search_tool.py @@ -143,7 +143,7 @@ async def test_keenable_search(monkeypatch): async def mock_post(self, url, **kw): assert "keenable" in url assert kw["headers"]["X-API-Key"] == "keen-key" - assert kw["headers"]["User-Agent"] == "nanobot-search-test" + assert kw["headers"]["User-Agent"].startswith("nanobot/") return _response(json={ "results": [{ "title": "Keen",