From c8d399566566c79b659ea26fb769a9f42c2963ff Mon Sep 17 00:00:00 2001 From: Saiyam Jain Date: Sat, 7 Nov 2026 03:55:09 +0530 Subject: [PATCH 1/2] fix(website-browser): ensure download ended_at is set when hashing fails --- .../website_browser/control.py | 7 +++++-- .../tests/test_website_browser.py | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/agentenv-protocol/src/agentenv_protocol/website_browser/control.py b/packages/agentenv-protocol/src/agentenv_protocol/website_browser/control.py index 8add4f5b..81666e83 100644 --- a/packages/agentenv-protocol/src/agentenv_protocol/website_browser/control.py +++ b/packages/agentenv-protocol/src/agentenv_protocol/website_browser/control.py @@ -617,8 +617,11 @@ def _on_Browser_downloadProgress(self, params: dict, session: Optional[str]) -> download["ended_at"] = _now() async def _hashed(self, download: dict, path: Path) -> None: - size, sha256 = await asyncio.to_thread(_size_and_sha256, path) - download.update(size=size, sha256=sha256, ended_at=_now()) + try: + size, sha256 = await asyncio.to_thread(_size_and_sha256, path) + download.update(size=size, sha256=sha256) + finally: + download["ended_at"] = _now() def _spawn(self, work: Awaitable) -> None: task = asyncio.ensure_future(work) diff --git a/packages/agentenv-protocol/tests/test_website_browser.py b/packages/agentenv-protocol/tests/test_website_browser.py index 07f906d9..b9294f7c 100644 --- a/packages/agentenv-protocol/tests/test_website_browser.py +++ b/packages/agentenv-protocol/tests/test_website_browser.py @@ -909,6 +909,27 @@ async def test_its_control_records_a_completed_download_with_its_size_and_sha256 assert done["sha256"] == hashlib.sha256(b"%PDF-1.7 report").hexdigest() +@pytest.mark.asyncio +async def test_its_control_records_ended_at_when_download_hashing_fails(tmp_path): + devtools = StubDevTools() + control = BrowserControl(devtools, "agent") + missing_file = tmp_path / "nonexistent.pdf" + + devtools.send("Browser.downloadWillBegin", {"guid": "g2", "url": "https://a.test/missing.pdf", + "suggestedFilename": "missing.pdf"}) + devtools.send("Browser.downloadProgress", {"guid": "g2", "state": "completed", + "filePath": str(missing_file)}) + for _ in range(int(WAIT_S / POLL_S)): + if "ended_at" in control.downloads.get("g2", {}): + break + await asyncio.sleep(POLL_S) + + done = control.downloads["g2"] + assert done["state"] == "completed" + assert "ended_at" in done + assert "sha256" not in done + + @pytest.mark.asyncio async def test_its_control_says_the_tab_whose_page_has_focus_is_active_else_the_first_visible_one(tmp_path): states = {"s-a": ["hidden", False], "s-b": ["visible", False], "s-c": ["visible", True]} From 996e3059691ddd7cdf0a3faafbadd7efc31d9df8 Mon Sep 17 00:00:00 2001 From: Saiyam Jain Date: Sat, 7 Nov 2026 07:41:27 +0530 Subject: [PATCH 2/2] docs(website-browser): clarify that size and sha256 are optional on completed downloads --- .../src/agentenv_protocol/website_browser/shapes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/agentenv-protocol/src/agentenv_protocol/website_browser/shapes.py b/packages/agentenv-protocol/src/agentenv_protocol/website_browser/shapes.py index d4bfc8a1..74b6c675 100644 --- a/packages/agentenv-protocol/src/agentenv_protocol/website_browser/shapes.py +++ b/packages/agentenv-protocol/src/agentenv_protocol/website_browser/shapes.py @@ -97,7 +97,7 @@ class ClosedTab(BaseModel): class Download(BaseModel): - """A download into ``~/Downloads``; completed, with where it landed, its size and its sha256.""" + """A download into ``~/Downloads``; when completed, with where it landed, and its size and sha256 when available.""" guid: str url: str filename: str