Skip to content

local-remote-split-type-fix-auto-enter #2636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 208 commits into
base: main
Choose a base branch
from

Conversation

MagMueller
Copy link
Collaborator

@MagMueller MagMueller commented Aug 10, 2025

Auto-generated PR for branch: local-remote-split-type-fix-auto-enter


Summary by cubic

Migrated browser automation from Playwright to a pure Chrome DevTools Protocol (CDP) event-driven architecture, refactoring BrowserSession and related modules for improved reliability, extensibility, and testability.

  • New Architecture
    • Replaced Playwright with direct CDP communication using an event bus and modular watchdogs for browser actions, DOM, downloads, security, and crash handling.
    • Added new event models, refactored DOM extraction and serialization, and introduced enhanced DOM tree types.
    • Updated tests, examples, and documentation to match the new event-driven system.

mertunsall and others added 8 commits August 10, 2025 00:21
…detection/download. mostly coded by claude-opus-4.1
…Introduced methods for cleaning HTML, validating content relevance, and intelligently truncating long content. Enhanced markdown conversion and improved error handling for content extraction failures.
…TML to markdown conversion using markdownify, improved error handling, and streamlined memory management for extracted content. Removed obsolete methods related to HTML cleaning and content validation.
Copy link

cursor bot commented Aug 10, 2025

🚨 Bugbot Trial Expired

Your Bugbot trial has expired. Please purchase a license in the Cursor dashboard to continue using Bugbot.

assert len(browser_session.tabs) == initial_tabs_count + 1

# Verify focus switched to the new tab
assert 'example.com' in current_url

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High test

The string
example.com
may be at an arbitrary position in the sanitized URL.

Copilot Autofix

AI about 16 hours ago

To fix the problem, we should parse the current_url using Python's urllib.parse module and check the hostname component directly, rather than using a substring check. Specifically, we should use urlparse(current_url).hostname and verify that it matches example.com or ends with .example.com (if subdomains are allowed). In this test, since the navigation is to 'https://example.com', a strict equality check is appropriate.

Steps:

  • Import urlparse from urllib.parse at the top of the file (if not already imported).
  • Replace the substring check ('example.com' in current_url) with a check on the parsed hostname: urlparse(current_url).hostname == 'example.com'.

Suggested changeset 1
tests/ci/test_action_NavigateToUrlEvent.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/ci/test_action_NavigateToUrlEvent.py b/tests/ci/test_action_NavigateToUrlEvent.py
--- a/tests/ci/test_action_NavigateToUrlEvent.py
+++ b/tests/ci/test_action_NavigateToUrlEvent.py
@@ -4,3 +4,3 @@
 from pytest_httpserver import HTTPServer
-
+from urllib.parse import urlparse
 from browser_use.agent.views import ActionModel, ActionResult
@@ -398,3 +398,3 @@
 		# Verify focus switched to the new tab
-		assert 'example.com' in current_url
+		assert urlparse(current_url).hostname == 'example.com'
 		assert current_url != initial_url
EOF
@@ -4,3 +4,3 @@
from pytest_httpserver import HTTPServer

from urllib.parse import urlparse
from browser_use.agent.views import ActionModel, ActionResult
@@ -398,3 +398,3 @@
# Verify focus switched to the new tab
assert 'example.com' in current_url
assert urlparse(current_url).hostname == 'example.com'
assert current_url != initial_url
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cubic analysis

27 issues found across 90 files • Review in cubic

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

params={'expression': 'window.pageYOffset', 'returnByValue': True},
session_id=cdp_session.session_id,
)
main_y = main_scroll.get('result', {}).get('value', 0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable main_y is assigned but never used, which will raise an unused-variable warning in most linters and can block CI. Replace it with _ or remove the assignment to indicate the value is intentionally ignored. (Based on your team's feedback about keeping the codebase free of linter warnings.)

Prompt for AI agents
Address the following comment on tests/ci/test_action_ScrollEvent.py at line 379:

<comment>The variable main_y is assigned but never used, which will raise an unused-variable warning in most linters and can block CI. Replace it with _ or remove the assignment to indicate the value is intentionally ignored. (Based on your team&#39;s feedback about keeping the codebase free of linter warnings.)</comment>

<file context>
@@ -0,0 +1,423 @@
+import asyncio
+
+import pytest
+from pytest_httpserver import HTTPServer
+
+from browser_use.agent.views import ActionModel, ActionResult
+from browser_use.browser import BrowserSession
+from browser_use.browser.profile import BrowserProfile
+from browser_use.controller.service import Controller
</file context>
Suggested change
main_y = main_scroll.get('result', {}).get('value', 0)
_ = main_scroll.get('result', {}).get('value', 0)

session = BrowserSession(browser_profile=profile)

# Track BrowserErrorEvents
error_events = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test collects BrowserErrorEvent instances but never asserts that the list remains empty, so the test will pass even when unexpected errors occur.

Prompt for AI agents
Address the following comment on tests/ci/test_security_watchdog.py at line 80:

<comment>The test collects BrowserErrorEvent instances but never asserts that the list remains empty, so the test will pass even when unexpected errors occur.</comment>

<file context>
@@ -0,0 +1,155 @@
+&quot;&quot;&quot;Test navigation and security functionality.&quot;&quot;&quot;
+
+from typing import cast
+
+import pytest
+
+from browser_use.browser.events import (
+	BrowserConnectedEvent,
+	BrowserErrorEvent,
</file context>

cdp_timing: dict[str, float]


@dataclass(slots=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutable dataclass defines a custom hash but is not frozen, so the hash can change after mutation, breaking dictionary/set invariants and leading to hard-to-trace bugs.

Prompt for AI agents
Address the following comment on browser_use/dom/views.py at line 52:

<comment>Mutable dataclass defines a custom __hash__ but is not frozen, so the hash can change after mutation, breaking dictionary/set invariants and leading to hard-to-trace bugs.</comment>

<file context>
@@ -70,220 +31,467 @@ def __json__(self) -&gt; dict:
 ]
 
 
-@dataclass(frozen=False)
-class DOMElementNode(DOMBaseNode):
+@dataclass
+class CurrentPageTargets:
+	page_session: TargetInfo
+	iframe_sessions: list[TargetInfo]
</file context>
Suggested change
@dataclass(slots=True)
@dataclass(slots=True, frozen=True)

async def test_no_leaked_asyncio_tasks():
"""Test that no asyncio tasks remain after agent shutdown."""
# Get initial tasks
initial_tasks = asyncio.all_tasks(asyncio.get_event_loop())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling asyncio.get_event_loop() inside a running coroutine is deprecated; use asyncio.get_running_loop() or omit the argument to all_tasks() instead.

Prompt for AI agents
Address the following comment on tests/ci/test_agent_shutdown.py at line 43:

<comment>Calling asyncio.get_event_loop() inside a running coroutine is deprecated; use asyncio.get_running_loop() or omit the argument to all_tasks() instead.</comment>

<file context>
@@ -0,0 +1,163 @@
+&quot;&quot;&quot;Test agent shutdown and cleanup.&quot;&quot;&quot;
+
+import asyncio
+import logging
+import threading
+import time
+
+from browser_use import Agent, BrowserProfile, BrowserSession
+from tests.ci.conftest import create_mock_llm
</file context>


await inject_highlighting_script(dom_service, serialized_dom_state.selector_map)

input('Done. Press Enter to continue...')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling the synchronous built-in input() inside an async loop blocks the event loop, defeating concurrency and potentially freezing other coroutines.

Prompt for AI agents
Address the following comment on browser_use/dom/playground/tree.py at line 124:

<comment>Calling the synchronous built-in input() inside an async loop blocks the event loop, defeating concurrency and potentially freezing other coroutines.</comment>

<file context>
@@ -0,0 +1,128 @@
+import asyncio
+import json
+import time
+
+import aiofiles
+
+from browser_use.browser import BrowserProfile, BrowserSession
+from browser_use.browser.types import ViewportSize
+from browser_use.dom.debug.highlights import inject_highlighting_script, remove_highlighting_script
</file context>
Suggested change
input('Done. Press Enter to continue...')
await asyncio.to_thread(input, 'Done. Press Enter to continue...')

page = await browser_session.get_current_page()
page_info = browser_session.browser_state_summary.page_info
# Get browser state summary
state = await browser_session.get_browser_state_summary()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

page is now undefined due to removal of its assignment, causing a runtime NameError when handle_model_action(page, action) is called. (Based on your team's feedback about prioritizing critical runtime issues)

Prompt for AI agents
Address the following comment on examples/custom-functions/cua.py at line 134:

<comment>`page` is now undefined due to removal of its assignment, causing a runtime NameError when `handle_model_action(page, action)` is called. (Based on your team&#39;s feedback about prioritizing critical runtime issues)</comment>

<file context>
@@ -130,14 +130,15 @@ async def openai_cua_fallback(params: OpenAICUAAction, browser_session: BrowserS
 	print(f&#39;🎯 CUA Action Starting - Goal: {params.description}&#39;)
 
 	try:
-		page = await browser_session.get_current_page()
-		page_info = browser_session.browser_state_summary.page_info
+		# Get browser state summary
+		state = await browser_session.get_browser_state_summary()
+		page_info = state.page_info
 		if not page_info:
</file context>



@pytest.mark.asyncio
async def test_watchdog_integration_with_session_lifecycle():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first test is missing a try/finally tear-down; if an earlier assertion fails the browser session is left alive, leaking resources and jeopardising later tests.

Prompt for AI agents
Address the following comment on tests/ci/test_watchdog.py at line 21:

<comment>The first test is missing a try/finally tear-down; if an earlier assertion fails the browser session is left alive, leaking resources and jeopardising later tests.</comment>

<file context>
@@ -0,0 +1,107 @@
+&quot;&quot;&quot;Test browser watchdog integration and lifecycle.&quot;&quot;&quot;
+
+from typing import cast
+
+import pytest
+
+from browser_use.browser.events import (
+	BrowserConnectedEvent,
+	BrowserStartEvent,
</file context>

)
url = result.get('result', {}).get('value', '')
return 'login' in url.lower() or 'signin' in url.lower()
except:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bare except clause suppresses all exceptions and obscures underlying errors; catch a specific exception class instead or use "except Exception" with logging. (Based on your team's feedback about improving error handling and avoiding overly broad exception catching.)

Prompt for AI agents
Address the following comment on examples/custom-functions/action_filters.py at line 71:

<comment>Bare except clause suppresses all exceptions and obscures underlying errors; catch a specific exception class instead or use &quot;except Exception&quot; with logging. (Based on your team&#39;s feedback about improving error handling and avoiding overly broad exception catching.)</comment>

<file context>
@@ -39,29 +38,58 @@ async def some_action(browser_session: BrowserSession):
 
 # Action will only be available to Agent on Google domains because of the domain filter
 @registry.action(description=&#39;Trigger disco mode&#39;, domains=[&#39;google.com&#39;, &#39;*.google.com&#39;])
-async def disco_mode(page: Page):
-	await page.evaluate(&quot;&quot;&quot;() =&gt; { 
-        // define the wiggle animation
-        document.styleSheets[0].insertRule(&#39;@keyframes wiggle { 0% { transform: rotate(0deg); } 50% { transform: rotate(10deg); } 100% { transform: rotate(0deg); } }&#39;);
-        
-        document.querySelectorAll(&quot;*&quot;).forEach(element =&gt; {
</file context>
Suggested change
except:
except Exception:

event_names = {
name.split('[')[0]
for name in globals().keys()
if not name.startswith('_') and issubclass(globals()[name], BaseEvent) and name != 'BaseEvent'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling issubclass() on non-class globals will raise TypeError and crash the module import; ensure the object is a type before invoking issubclass() (Based on your team's feedback about catching runtime errors early).

Prompt for AI agents
Address the following comment on browser_use/browser/events.py at line 372:

<comment>Calling issubclass() on non-class globals will raise TypeError and crash the module import; ensure the object is a type before invoking issubclass() (Based on your team&#39;s feedback about catching runtime errors early).</comment>

<file context>
@@ -0,0 +1,380 @@
+&quot;&quot;&quot;Event definitions for browser communication.&quot;&quot;&quot;
+
+from typing import Any, Literal
+
+from bubus import BaseEvent
+from bubus.models import T_EventResultType
+from pydantic import Field, field_validator
+
+from browser_use.browser.views import BrowserStateSummary
</file context>

# Navigate to a test page
await page.goto('https://example.com', wait_until='load')
# Navigate to a test page using events
nav_event = browser_session.event_bus.dispatch(NavigateToUrlEvent(url='https://example.com'))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awaiting the dispatched NavigateToUrlEvent only waits for handlers to finish, but BrowserSession.on_NavigateToUrlEvent does not block until the requested page is fully loaded (it only sleeps 0.5 s). Subsequent size checks may run before navigation completes, leading to flaky example behaviour.

Prompt for AI agents
Address the following comment on examples/browser/window_sizing.py at line 65:

<comment>Awaiting the dispatched NavigateToUrlEvent only waits for handlers to finish, but BrowserSession.on_NavigateToUrlEvent does not block until the requested page is fully loaded (it only sleeps 0.5 s).  Subsequent size checks may run before navigation completes, leading to flaky example behaviour.</comment>

<file context>
@@ -40,17 +61,15 @@ async def example_custom_window_size():
 		)
 		await browser_session.start()
 
-		# Get the current page
-		page = await browser_session.get_current_page()
-
-		# Navigate to a test page
-		await page.goto(&#39;https://example.com&#39;, wait_until=&#39;load&#39;)
+		# Navigate to a test page using events
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants