agent-cdp 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. agent_cdp-0.1.0/.claude/settings.local.json +34 -0
  2. agent_cdp-0.1.0/.claude/worktrees/agent-a0b334a7/.claude/settings.local.json +22 -0
  3. agent_cdp-0.1.0/.claude/worktrees/agent-a0b334a7/.git +1 -0
  4. agent_cdp-0.1.0/.claude/worktrees/agent-a0b334a7/.gitignore +39 -0
  5. agent_cdp-0.1.0/.claude/worktrees/agent-a0b334a7/CLAUDE.md +182 -0
  6. agent_cdp-0.1.0/.claude/worktrees/agent-a0b334a7/pyproject.toml +6 -0
  7. agent_cdp-0.1.0/.claude/worktrees/agent-a76c879b/.claude/settings.local.json +22 -0
  8. agent_cdp-0.1.0/.claude/worktrees/agent-a76c879b/.git +1 -0
  9. agent_cdp-0.1.0/.claude/worktrees/agent-a76c879b/.gitignore +39 -0
  10. agent_cdp-0.1.0/.claude/worktrees/agent-a76c879b/CLAUDE.md +182 -0
  11. agent_cdp-0.1.0/.claude/worktrees/agent-a76c879b/pyproject.toml +6 -0
  12. agent_cdp-0.1.0/.claude/worktrees/agent-aaf55f3e/.claude/settings.local.json +22 -0
  13. agent_cdp-0.1.0/.claude/worktrees/agent-aaf55f3e/.git +1 -0
  14. agent_cdp-0.1.0/.claude/worktrees/agent-aaf55f3e/.gitignore +39 -0
  15. agent_cdp-0.1.0/.claude/worktrees/agent-aaf55f3e/CLAUDE.md +182 -0
  16. agent_cdp-0.1.0/.claude/worktrees/agent-aaf55f3e/pyproject.toml +6 -0
  17. agent_cdp-0.1.0/.claude/worktrees/agent-afe0f0f9/.claude/settings.local.json +22 -0
  18. agent_cdp-0.1.0/.claude/worktrees/agent-afe0f0f9/.git +1 -0
  19. agent_cdp-0.1.0/.claude/worktrees/agent-afe0f0f9/.gitignore +39 -0
  20. agent_cdp-0.1.0/.claude/worktrees/agent-afe0f0f9/CLAUDE.md +182 -0
  21. agent_cdp-0.1.0/.claude/worktrees/agent-afe0f0f9/pyproject.toml +6 -0
  22. agent_cdp-0.1.0/.gitignore +39 -0
  23. agent_cdp-0.1.0/CLAUDE.md +112 -0
  24. agent_cdp-0.1.0/LICENSE +21 -0
  25. agent_cdp-0.1.0/PKG-INFO +332 -0
  26. agent_cdp-0.1.0/README.md +303 -0
  27. agent_cdp-0.1.0/analysis/bubus_design_gaps_in_agent_browser_scenario.md +353 -0
  28. agent_cdp-0.1.0/analysis/development_plan_detailed.md +1491 -0
  29. agent_cdp-0.1.0/analysis/draft_agent_plan.md +941 -0
  30. agent_cdp-0.1.0/analysis/proposal_scoped_eventbus.md +1096 -0
  31. agent_cdp-0.1.0/demo/__init__.py +0 -0
  32. agent_cdp-0.1.0/demo/bench.py +517 -0
  33. agent_cdp-0.1.0/demo/cdp_client.py +92 -0
  34. agent_cdp-0.1.0/demo/chrome.py +62 -0
  35. agent_cdp-0.1.0/demo/events.py +117 -0
  36. agent_cdp-0.1.0/demo/main.py +370 -0
  37. agent_cdp-0.1.0/demo/multi_tab.py +601 -0
  38. agent_cdp-0.1.0/demo/screenshot.png +0 -0
  39. agent_cdp-0.1.0/demo/screenshots/bench_bilibili.png +0 -0
  40. agent_cdp-0.1.0/demo/screenshots/bench_google.png +0 -0
  41. agent_cdp-0.1.0/demo/screenshots/bench_recaptcha_demo.png +0 -0
  42. agent_cdp-0.1.0/demo/screenshots/bench_xiaohongshu.png +0 -0
  43. agent_cdp-0.1.0/demo/screenshots/multi_bilibili.png +0 -0
  44. agent_cdp-0.1.0/demo/screenshots/multi_google.png +0 -0
  45. agent_cdp-0.1.0/demo/screenshots/multi_recaptcha.png +0 -0
  46. agent_cdp-0.1.0/demo/screenshots/multi_xiaohongshu.png +0 -0
  47. agent_cdp-0.1.0/demo/timing.py +130 -0
  48. agent_cdp-0.1.0/demo/watchdogs.py +460 -0
  49. agent_cdp-0.1.0/demo_conscribe_pydantic.py +137 -0
  50. agent_cdp-0.1.0/pyproject.toml +70 -0
  51. agent_cdp-0.1.0/qt_ref/qt/01_signals_and_slots.md +212 -0
  52. agent_cdp-0.1.0/qt_ref/qt/02_connection_type_enum.md +21 -0
  53. agent_cdp-0.1.0/qt_ref/qt/03_qobject_connect.md +153 -0
  54. agent_cdp-0.1.0/qt_ref/qt/04_event_system.md +126 -0
  55. agent_cdp-0.1.0/qt_ref/qt/05_qevent.md +69 -0
  56. agent_cdp-0.1.0/qt_ref/qt/06_send_post_event.md +137 -0
  57. agent_cdp-0.1.0/qt_ref/qt/07_threads_and_qobjects.md +63 -0
  58. agent_cdp-0.1.0/qt_ref/qt/08_qeventloop.md +58 -0
  59. agent_cdp-0.1.0/qt_ref/qt/09_qthread.md +94 -0
  60. agent_cdp-0.1.0/qt_ref/qt/10_event_filter.md +75 -0
  61. agent_cdp-0.1.0/qt_ref/qt/11_property_system.md +169 -0
  62. agent_cdp-0.1.0/qt_ref/qt/12_state_machine.md +108 -0
  63. agent_cdp-0.1.0/qt_ref/qt/13_meta_object_system.md +37 -0
  64. agent_cdp-0.1.0/qt_ref/qt/14_moc.md +116 -0
  65. agent_cdp-0.1.0/qt_ref/qt/15_woboq_signal_internals.md +102 -0
  66. agent_cdp-0.1.0/qt_ref/qt/16_gobject_signals.md +71 -0
  67. agent_cdp-0.1.0/qt_ref/qt/README.md +81 -0
  68. agent_cdp-0.1.0/src/agent_cdp/__init__.py +1 -0
  69. agent_cdp-0.1.0/src/agent_cdp/_context.py +18 -0
  70. agent_cdp-0.1.0/src/agent_cdp/_registry.py +30 -0
  71. agent_cdp-0.1.0/src/agent_cdp/advanced/__init__.py +7 -0
  72. agent_cdp-0.1.0/src/agent_cdp/advanced/cycle_detect.py +3 -0
  73. agent_cdp-0.1.0/src/agent_cdp/advanced/event_log.py +60 -0
  74. agent_cdp-0.1.0/src/agent_cdp/advanced/expect.py +69 -0
  75. agent_cdp-0.1.0/src/agent_cdp/advanced/timeout.py +28 -0
  76. agent_cdp-0.1.0/src/agent_cdp/connection/__init__.py +6 -0
  77. agent_cdp-0.1.0/src/agent_cdp/connection/connection.py +115 -0
  78. agent_cdp-0.1.0/src/agent_cdp/connection/types.py +11 -0
  79. agent_cdp-0.1.0/src/agent_cdp/events/__init__.py +27 -0
  80. agent_cdp-0.1.0/src/agent_cdp/events/aggregation.py +179 -0
  81. agent_cdp-0.1.0/src/agent_cdp/events/base.py +158 -0
  82. agent_cdp-0.1.0/src/agent_cdp/events/result.py +80 -0
  83. agent_cdp-0.1.0/src/agent_cdp/scope/__init__.py +6 -0
  84. agent_cdp-0.1.0/src/agent_cdp/scope/_helpers.py +45 -0
  85. agent_cdp-0.1.0/src/agent_cdp/scope/event_loop.py +185 -0
  86. agent_cdp-0.1.0/src/agent_cdp/scope/group.py +169 -0
  87. agent_cdp-0.1.0/src/agent_cdp/scope/scope.py +367 -0
  88. agent_cdp-0.1.0/tests/__init__.py +0 -0
  89. agent_cdp-0.1.0/tests/conftest.py +1 -0
  90. agent_cdp-0.1.0/tests/test_step_1_1_event_result.py +194 -0
  91. agent_cdp-0.1.0/tests/test_step_1_2_base_event.py +241 -0
  92. agent_cdp-0.1.0/tests/test_step_1_3_aggregation.py +271 -0
  93. agent_cdp-0.1.0/tests/test_step_2_1_connection.py +237 -0
  94. agent_cdp-0.1.0/tests/test_step_2_2_scope_emit.py +570 -0
  95. agent_cdp-0.1.0/tests/test_step_2_3_event_loop.py +327 -0
  96. agent_cdp-0.1.0/tests/test_step_3_1_scope_group.py +263 -0
  97. agent_cdp-0.1.0/tests/test_step_3_2_lifecycle.py +348 -0
  98. agent_cdp-0.1.0/tests/test_step_4_1_expect_timeout.py +265 -0
  99. agent_cdp-0.1.0/tests/test_step_4_2_event_log_cycle.py +185 -0
  100. agent_cdp-0.1.0/uv.lock +425 -0
@@ -0,0 +1,34 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:doc.qt.io)",
5
+ "WebFetch(domain:woboq.com)",
6
+ "WebFetch(domain:docs.gtk.org)",
7
+ "Bash(grep:*)",
8
+ "Bash(wc:*)",
9
+ "Bash(pip show:*)",
10
+ "Bash(python:*)",
11
+ "Bash(ls:*)",
12
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog -type f -name *.lock -o -name *.toml -o -name *.md)",
13
+ "Bash(python3:*)",
14
+ "Bash(uv sync:*)",
15
+ "Bash(uv run:*)",
16
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/tests -type f -name *.py)",
17
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src -type f -name *.py)",
18
+ "Bash(find /root/.claude/projects -name *.md -type f)",
19
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src/agent_cdp -name \"*.py\" -type f -exec wc -l {} +)",
20
+ "Bash(git -C .claude/worktrees/agent-aaf55f3e status --short)",
21
+ "Bash(git -C .claude/worktrees/agent-a76c879b status --short)",
22
+ "Bash(grep -E '\\\\.\\(py\\)$')",
23
+ "Bash(sort -k2)",
24
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/bu_ref -name *watchdog*.py -type f)",
25
+ "Bash(echo \"DISPLAY=$DISPLAY\")",
26
+ "Bash(echo \"WAYLAND=$WAYLAND_DISPLAY\")",
27
+ "Bash(pkill -f 'google-chrome.*remote-debugging-port')",
28
+ "Bash(pkill -f 'python -m demo.main')",
29
+ "Bash(pkill -f 'chrome')",
30
+ "Bash(pkill -9 -f 'chrome')",
31
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/bu_ref -path *cdp*browseruse*events* -type f)"
32
+ ]
33
+ }
34
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:doc.qt.io)",
5
+ "WebFetch(domain:woboq.com)",
6
+ "WebFetch(domain:docs.gtk.org)",
7
+ "Bash(grep:*)",
8
+ "Bash(wc:*)",
9
+ "Bash(pip show:*)",
10
+ "Bash(python:*)",
11
+ "Bash(ls:*)",
12
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog -type f -name *.lock -o -name *.toml -o -name *.md)",
13
+ "Bash(python3:*)",
14
+ "Bash(uv sync:*)",
15
+ "Bash(uv run:*)",
16
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/tests -type f -name *.py)",
17
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src -type f -name *.py)",
18
+ "Bash(find /root/.claude/projects -name *.md -type f)",
19
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src/agent_cdp -name \"*.py\" -type f -exec wc -l {} +)"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ gitdir: /root/QLY/code/event_driven_browser_watchdog/.git/worktrees/agent-a0b334a7
@@ -0,0 +1,39 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ sdist/
11
+ wheels/
12
+ *.whl
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ ENV/
18
+
19
+ # IDE
20
+ .idea/
21
+ .vscode/
22
+ *.swp
23
+ *.swo
24
+ *~
25
+
26
+ # Testing / Coverage
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+
37
+ # Project-specific
38
+ bu_ref/
39
+ references/
@@ -0,0 +1,182 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Purpose
6
+
7
+ This repo is an exploration workspace for browser event-driven design (browser-use edition). It contains three independent but interconnected libraries that together form a layered browser automation stack:
8
+
9
+ ```
10
+ Agent (browser-use) ←→ EventBus (bubus) ←→ CDPClient (cdp-use) ←→ Chrome WebSocket
11
+ ```
12
+
13
+ The goal is to study how browser-use implements event-driven coordination across ~15 watchdog services, identify architectural strengths and gaps, and experiment with improvements.
14
+
15
+ ## Repository Layout
16
+
17
+ | Directory | Package | Description |
18
+ |-----------|---------|-------------|
19
+ | `bubus/` | `bubus==1.5.6` | Pydantic-powered async event bus (pub/sub framework) |
20
+ | `cdp-use/` | `cdp-use==1.4.5` | Auto-generated type-safe Python bindings for Chrome DevTools Protocol |
21
+ | `browser-use/` | `browser-use==0.12.2` | AI browser automation agent that wires the above two together |
22
+
23
+ Each is a standalone `uv`-managed Python project with its own `.venv`, `pyproject.toml`, and test suite. For local development where browser-use depends on local bubus/cdp-use, uncomment `[tool.uv.sources]` in `browser-use/pyproject.toml`.
24
+
25
+ ## Development Commands
26
+
27
+ All three use `uv` (Python >=3.11). Run commands from each package's directory.
28
+
29
+ ### bubus
30
+
31
+ ```bash
32
+ cd bubus
33
+ uv sync
34
+ uv run pytest -vxs tests/ # all tests
35
+ uv run pytest -vxs tests/test_eventbus.py # single test file
36
+ uv run ruff check --fix && uv run ruff format # lint + format
37
+ uv run pyright # type check (strict mode)
38
+ ```
39
+
40
+ ### cdp-use
41
+
42
+ ```bash
43
+ cd cdp-use
44
+ uv sync
45
+ uv run python -m cdp_use.generator # regenerate CDP types from protocol JSON
46
+ uv run ruff check cdp_use/ --statistics # lint
47
+ uv run ruff format cdp_use/ # format
48
+ # Or use: task generate / task lint / task format (Taskfile.yml)
49
+ ```
50
+
51
+ ### browser-use
52
+
53
+ ```bash
54
+ cd browser-use
55
+ uv sync
56
+ uv run pytest -vxs tests/ci # CI test suite (default set)
57
+ uv run pytest -vxs tests/ci/test_foo.py # single test
58
+ uv run ruff check --fix && uv run ruff format # lint + format
59
+ uv run pyright # type check (basic mode)
60
+ uv run pre-commit run --all-files # all pre-commit hooks
61
+ ```
62
+
63
+ ## Architecture
64
+
65
+ ### Layer 1: bubus — Event Bus
66
+
67
+ Core primitive. Provides `EventBus` and `BaseEvent[T_EventResultType]` (generic over return type).
68
+
69
+ Key capabilities: async dispatch with FIFO queue, `expect()` to await future events, event result aggregation (flat dict / list / by handler ID), handler timeouts, retry decorator with semaphore-based concurrency, event forwarding between bus instances (with loop prevention), Write-Ahead Logging for persistence, child/nested events with parent tracking.
70
+
71
+ ### Layer 2: cdp-use — CDP Client
72
+
73
+ Thin typed wrapper over Chrome DevTools Protocol WebSocket. Two main pieces:
74
+
75
+ 1. **Generator** (`cdp_use/generator/`) — downloads Chrome protocol JSON specs and generates Python TypedDict classes for all 50+ CDP domains (Page, Runtime, DOM, Network, Target, etc.)
76
+ 2. **Client** (`cdp_use/client.py`) — WebSocket client exposing `cdp.send.Domain.method(params={...})` and `cdp.register.Domain.eventName(callback)` with full type safety
77
+
78
+ No event registration via `cdp.on(...)` — only `cdp.register.Domain.event(callback)`.
79
+
80
+ ### Layer 3: browser-use — Agent + Watchdogs
81
+
82
+ The orchestration layer. Key architectural patterns:
83
+
84
+ **Service/Views pattern**: each component has `service.py` (logic) and `views.py` (Pydantic models).
85
+
86
+ **BrowserSession** (`browser_use/browser/session.py`) is the hub — manages CDP connections, tab lifecycle, and coordinates watchdogs through a central `EventBus`.
87
+
88
+ **Watchdog pattern** (`browser_use/browser/watchdog_base.py`): Each watchdog is a `BaseWatchdog` subclass (Pydantic model) that declares `LISTENS_TO` and `EMITS` class vars. Handlers auto-register by naming convention: `on_{EventClassName}(self, event)`.
89
+
90
+ 15 watchdogs in `browser_use/browser/watchdogs/`:
91
+ - `dom_watchdog` — DOM snapshots, element highlighting, accessibility tree
92
+ - `screenshot_watchdog` — screenshot capture
93
+ - `downloads_watchdog` — file downloads, PDF auto-download
94
+ - `popups_watchdog` — JS dialogs
95
+ - `security_watchdog` — domain restrictions
96
+ - `aboutblank_watchdog` — empty page handling
97
+ - `local_browser_watchdog` — local Chrome process lifecycle
98
+ - `permissions_watchdog` — browser permissions
99
+ - `crash_watchdog` — target crash monitoring
100
+ - `storage_state_watchdog` — cookie/localStorage persistence
101
+ - `captcha_watchdog` — CAPTCHA solver integration
102
+ - `har_recording_watchdog` — network recording (HAR)
103
+ - `recording_watchdog` — browser automation recording
104
+ - `default_action_watchdog` — default action handlers
105
+
106
+ **Events** (`browser_use/browser/events.py`): All events inherit `BaseEvent[T]`. Two categories:
107
+ - Action events (Agent → Browser): `NavigateToUrlEvent`, `ClickElementEvent`, `TypeTextEvent`, `ScreenshotEvent`, `BrowserStartEvent`, etc.
108
+ - Notification events (Browser → Agent/Watchdogs): `BrowserConnectedEvent`, `TabCreatedEvent`, `NavigationCompleteEvent`, `DownloadStartedEvent`, `DialogOpenedEvent`, etc.
109
+
110
+ Event timeouts are configurable via environment variables (`TIMEOUT_NavigateToUrlEvent`, etc.).
111
+
112
+ ## Code Style Differences
113
+
114
+ | | bubus | cdp-use | browser-use |
115
+ |---|---|---|---|
116
+ | Indent | spaces | (generated) | **tabs** |
117
+ | Quotes | single | — | single |
118
+ | pyright | strict | — | basic |
119
+ | Line length | 130 | — | 130 |
120
+
121
+ browser-use uses **tabs** for indentation. bubus uses **spaces**. Both use single quotes and ruff for linting/formatting.
122
+
123
+ ## Testing Conventions (browser-use)
124
+
125
+ - Tests that pass go into `tests/ci/` — this is the CI-discovered default set
126
+ - **No mocking** except for LLM responses (use `conftest.py` fixtures for LLM mocking)
127
+ - Use `pytest-httpserver` for all test HTML/responses — never use real remote URLs
128
+ - Modern pytest-asyncio: no `@pytest.mark.asyncio` decorator needed, just write `async def test_*()`
129
+ - `asyncio_mode = "auto"` with session-scoped event loop (browser-use) or function-scoped (bubus)
130
+
131
+ ## Key Design Decisions to Be Aware Of
132
+
133
+ 1. **Shared state belongs on BrowserSession, not on individual watchdogs.** Watchdogs expose state/helpers via events if other watchdogs need access.
134
+ 2. **CDP event forwarding**: BrowserSession forwards raw CDP events to watchdogs through the EventBus, not direct CDP callbacks.
135
+ 3. **BaseEvent is generic over result type** (`BaseEvent[T_EventResultType]`) — dispatchers can aggregate results from multiple handlers via `event.event_result()`, `event.event_results_list()`, etc.
136
+ 4. **Lazy imports** in `browser_use/__init__.py` for startup performance.
137
+ 5. **browser-use has its own CLAUDE.md** (`browser-use/CLAUDE.md`) with detailed per-package conventions — read it when working inside browser-use specifically.
138
+
139
+ ## Qt Reference Materials
140
+
141
+ `references/qt/` contains 16 Qt/GObject reference documents for systematic comparison with bubus/browser-use's event-driven architecture. The full index with source URLs, per-file rationale, and bubus architecture summary is in `references/qt/README.md`.
142
+
143
+ ### Reference File Map
144
+
145
+ | # | File | Topic | Compared Against (bubus/browser-use) |
146
+ |---|------|-------|--------------------------------------|
147
+ | 01 | `01_signals_and_slots.md` | Qt Signal/Slot core mechanism | `EventBus.on()` + `dispatch()` |
148
+ | 02 | `02_connection_type_enum.md` | 6 ConnectionType values (Auto/Direct/Queued/BlockingQueued/Unique/SingleShot) | bubus has only Queued mode |
149
+ | 03 | `03_qobject_connect.md` | connect/disconnect overloads, lifecycle, auto-disconnect on destruction | bubus lacks auto-disconnect |
150
+ | 04 | `04_event_system.md` | Event propagation chain: accept/ignore, event filters | bubus has no accept/ignore |
151
+ | 05 | `05_qevent.md` | QEvent class, 100+ event types, accept()/ignore() | `BaseEvent[T]` |
152
+ | 06 | `06_send_post_event.md` | sendEvent (sync) vs postEvent (queue) + priority | bubus only has queue (postEvent) |
153
+ | 07 | `07_threads_and_qobjects.md` | Thread affinity, cross-thread auto-Queued | bubus is single-threaded asyncio |
154
+ | 08 | `08_qeventloop.md` | Nested event loops, processEvents | bubus `inside_handler_context` polling |
155
+ | 09 | `09_qthread.md` | QThread, moveToThread, Worker pattern | N/A (future multi-thread consideration) |
156
+ | 10 | `10_event_filter.md` | installEventFilter chain, return true to stop propagation | watchdog circuit breaker (ad-hoc) |
157
+ | 11 | `11_property_system.md` | Q_PROPERTY + NOTIFY auto-signal on change | watchdog state change is manual dispatch |
158
+ | 12 | `12_state_machine.md` | QStateMachine hierarchical FSM | BrowserSession lifecycle (flag-based) |
159
+ | 13 | `13_meta_object_system.md` | Meta-Object System overview | bubus `dir()` + naming convention reflection |
160
+ | 14 | `14_moc.md` | MOC compiler: codegen vs runtime reflection | bubus runtime `on_` method scanning |
161
+ | 15 | `15_woboq_signal_internals.md` | Qt internals: connection list, 64-bit bitmask fast-path, O(1) cleanup | bubus handler list + WeakSet |
162
+ | 16 | `16_gobject_signals.md` | GObject 6-phase emission, accumulator, detail filtering | bubus `event_results` aggregation |
163
+
164
+ ### Key Design Gaps Identified (bubus vs Qt)
165
+
166
+ These are the primary areas where Qt's mature design reveals potential improvements for bubus:
167
+
168
+ 1. **Connection types**: bubus only supports Queued dispatch (via `asyncio.Queue`). Qt offers Direct (synchronous inline), Queued, BlockingQueued, and Auto (auto-selects based on thread affinity). Adding at least a Direct mode would enable zero-overhead handler invocation for same-context calls.
169
+
170
+ 2. **Event propagation control**: bubus has no accept/ignore mechanism. Once dispatched, an event reaches all registered handlers unconditionally. Qt allows handlers to accept (consume) or ignore (pass through) events, and event filters can intercept and stop propagation before the target sees it.
171
+
172
+ 3. **Event filter chain**: bubus's event interception is ad-hoc — `_would_create_loop()` and the watchdog circuit breaker wrapper are hardcoded special cases. Qt provides a general `installEventFilter()` mechanism where any object can intercept events for any other object, with LIFO ordering and return-true-to-stop semantics.
173
+
174
+ 4. **Auto-disconnect on destruction**: Qt automatically severs all signal/slot connections when either sender or receiver is destroyed. bubus has no equivalent — `BaseWatchdog` has no `detach_from_session()` method, and `__del__` only cancels asyncio tasks.
175
+
176
+ 5. **Handler priority**: bubus handlers execute in registration order (FIFO) with no priority mechanism. Qt's `postEvent()` accepts integer priority for queue ordering. GObject goes further with 6 emission phases (RUN_FIRST → EMISSION_HOOK → HANDLER_RUN_FIRST → RUN_LAST → HANDLER_RUN_LAST → RUN_CLEANUP).
177
+
178
+ 6. **State machine for lifecycle**: BrowserSession manages connection state via boolean flags (`is_cdp_connected`, `is_reconnecting`). Qt's `QStateMachine` provides formal hierarchical state machines with signal-driven transitions, error states, and property animation — a more robust pattern for complex lifecycle management.
179
+
180
+ 7. **Property change notifications**: Watchdog state changes require manual `dispatch()` calls. Qt's `Q_PROPERTY(... NOTIFY signal)` automatically emits a signal when a property changes, eliminating boilerplate.
181
+
182
+ 8. **Result accumulation**: bubus has flexible result aggregation (`event_result()`, `event_results_flat_dict()`, etc.). GObject's accumulator pattern is more formalized — an accumulator function receives each handler's return value and can short-circuit emission by returning FALSE. This is worth studying for bubus's `event_results_filtered()` design.
@@ -0,0 +1,6 @@
1
+ [project]
2
+ name = "agent-cdp"
3
+ version = "0.1.0"
4
+ description = ""
5
+ requires-python = ">=3.11"
6
+ dependencies = []
@@ -0,0 +1,22 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:doc.qt.io)",
5
+ "WebFetch(domain:woboq.com)",
6
+ "WebFetch(domain:docs.gtk.org)",
7
+ "Bash(grep:*)",
8
+ "Bash(wc:*)",
9
+ "Bash(pip show:*)",
10
+ "Bash(python:*)",
11
+ "Bash(ls:*)",
12
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog -type f -name *.lock -o -name *.toml -o -name *.md)",
13
+ "Bash(python3:*)",
14
+ "Bash(uv sync:*)",
15
+ "Bash(uv run:*)",
16
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/tests -type f -name *.py)",
17
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src -type f -name *.py)",
18
+ "Bash(find /root/.claude/projects -name *.md -type f)",
19
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src/agent_cdp -name \"*.py\" -type f -exec wc -l {} +)"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ gitdir: /root/QLY/code/event_driven_browser_watchdog/.git/worktrees/agent-a76c879b
@@ -0,0 +1,39 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ sdist/
11
+ wheels/
12
+ *.whl
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ ENV/
18
+
19
+ # IDE
20
+ .idea/
21
+ .vscode/
22
+ *.swp
23
+ *.swo
24
+ *~
25
+
26
+ # Testing / Coverage
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+
37
+ # Project-specific
38
+ bu_ref/
39
+ references/
@@ -0,0 +1,182 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Purpose
6
+
7
+ This repo is an exploration workspace for browser event-driven design (browser-use edition). It contains three independent but interconnected libraries that together form a layered browser automation stack:
8
+
9
+ ```
10
+ Agent (browser-use) ←→ EventBus (bubus) ←→ CDPClient (cdp-use) ←→ Chrome WebSocket
11
+ ```
12
+
13
+ The goal is to study how browser-use implements event-driven coordination across ~15 watchdog services, identify architectural strengths and gaps, and experiment with improvements.
14
+
15
+ ## Repository Layout
16
+
17
+ | Directory | Package | Description |
18
+ |-----------|---------|-------------|
19
+ | `bubus/` | `bubus==1.5.6` | Pydantic-powered async event bus (pub/sub framework) |
20
+ | `cdp-use/` | `cdp-use==1.4.5` | Auto-generated type-safe Python bindings for Chrome DevTools Protocol |
21
+ | `browser-use/` | `browser-use==0.12.2` | AI browser automation agent that wires the above two together |
22
+
23
+ Each is a standalone `uv`-managed Python project with its own `.venv`, `pyproject.toml`, and test suite. For local development where browser-use depends on local bubus/cdp-use, uncomment `[tool.uv.sources]` in `browser-use/pyproject.toml`.
24
+
25
+ ## Development Commands
26
+
27
+ All three use `uv` (Python >=3.11). Run commands from each package's directory.
28
+
29
+ ### bubus
30
+
31
+ ```bash
32
+ cd bubus
33
+ uv sync
34
+ uv run pytest -vxs tests/ # all tests
35
+ uv run pytest -vxs tests/test_eventbus.py # single test file
36
+ uv run ruff check --fix && uv run ruff format # lint + format
37
+ uv run pyright # type check (strict mode)
38
+ ```
39
+
40
+ ### cdp-use
41
+
42
+ ```bash
43
+ cd cdp-use
44
+ uv sync
45
+ uv run python -m cdp_use.generator # regenerate CDP types from protocol JSON
46
+ uv run ruff check cdp_use/ --statistics # lint
47
+ uv run ruff format cdp_use/ # format
48
+ # Or use: task generate / task lint / task format (Taskfile.yml)
49
+ ```
50
+
51
+ ### browser-use
52
+
53
+ ```bash
54
+ cd browser-use
55
+ uv sync
56
+ uv run pytest -vxs tests/ci # CI test suite (default set)
57
+ uv run pytest -vxs tests/ci/test_foo.py # single test
58
+ uv run ruff check --fix && uv run ruff format # lint + format
59
+ uv run pyright # type check (basic mode)
60
+ uv run pre-commit run --all-files # all pre-commit hooks
61
+ ```
62
+
63
+ ## Architecture
64
+
65
+ ### Layer 1: bubus — Event Bus
66
+
67
+ Core primitive. Provides `EventBus` and `BaseEvent[T_EventResultType]` (generic over return type).
68
+
69
+ Key capabilities: async dispatch with FIFO queue, `expect()` to await future events, event result aggregation (flat dict / list / by handler ID), handler timeouts, retry decorator with semaphore-based concurrency, event forwarding between bus instances (with loop prevention), Write-Ahead Logging for persistence, child/nested events with parent tracking.
70
+
71
+ ### Layer 2: cdp-use — CDP Client
72
+
73
+ Thin typed wrapper over Chrome DevTools Protocol WebSocket. Two main pieces:
74
+
75
+ 1. **Generator** (`cdp_use/generator/`) — downloads Chrome protocol JSON specs and generates Python TypedDict classes for all 50+ CDP domains (Page, Runtime, DOM, Network, Target, etc.)
76
+ 2. **Client** (`cdp_use/client.py`) — WebSocket client exposing `cdp.send.Domain.method(params={...})` and `cdp.register.Domain.eventName(callback)` with full type safety
77
+
78
+ No event registration via `cdp.on(...)` — only `cdp.register.Domain.event(callback)`.
79
+
80
+ ### Layer 3: browser-use — Agent + Watchdogs
81
+
82
+ The orchestration layer. Key architectural patterns:
83
+
84
+ **Service/Views pattern**: each component has `service.py` (logic) and `views.py` (Pydantic models).
85
+
86
+ **BrowserSession** (`browser_use/browser/session.py`) is the hub — manages CDP connections, tab lifecycle, and coordinates watchdogs through a central `EventBus`.
87
+
88
+ **Watchdog pattern** (`browser_use/browser/watchdog_base.py`): Each watchdog is a `BaseWatchdog` subclass (Pydantic model) that declares `LISTENS_TO` and `EMITS` class vars. Handlers auto-register by naming convention: `on_{EventClassName}(self, event)`.
89
+
90
+ 15 watchdogs in `browser_use/browser/watchdogs/`:
91
+ - `dom_watchdog` — DOM snapshots, element highlighting, accessibility tree
92
+ - `screenshot_watchdog` — screenshot capture
93
+ - `downloads_watchdog` — file downloads, PDF auto-download
94
+ - `popups_watchdog` — JS dialogs
95
+ - `security_watchdog` — domain restrictions
96
+ - `aboutblank_watchdog` — empty page handling
97
+ - `local_browser_watchdog` — local Chrome process lifecycle
98
+ - `permissions_watchdog` — browser permissions
99
+ - `crash_watchdog` — target crash monitoring
100
+ - `storage_state_watchdog` — cookie/localStorage persistence
101
+ - `captcha_watchdog` — CAPTCHA solver integration
102
+ - `har_recording_watchdog` — network recording (HAR)
103
+ - `recording_watchdog` — browser automation recording
104
+ - `default_action_watchdog` — default action handlers
105
+
106
+ **Events** (`browser_use/browser/events.py`): All events inherit `BaseEvent[T]`. Two categories:
107
+ - Action events (Agent → Browser): `NavigateToUrlEvent`, `ClickElementEvent`, `TypeTextEvent`, `ScreenshotEvent`, `BrowserStartEvent`, etc.
108
+ - Notification events (Browser → Agent/Watchdogs): `BrowserConnectedEvent`, `TabCreatedEvent`, `NavigationCompleteEvent`, `DownloadStartedEvent`, `DialogOpenedEvent`, etc.
109
+
110
+ Event timeouts are configurable via environment variables (`TIMEOUT_NavigateToUrlEvent`, etc.).
111
+
112
+ ## Code Style Differences
113
+
114
+ | | bubus | cdp-use | browser-use |
115
+ |---|---|---|---|
116
+ | Indent | spaces | (generated) | **tabs** |
117
+ | Quotes | single | — | single |
118
+ | pyright | strict | — | basic |
119
+ | Line length | 130 | — | 130 |
120
+
121
+ browser-use uses **tabs** for indentation. bubus uses **spaces**. Both use single quotes and ruff for linting/formatting.
122
+
123
+ ## Testing Conventions (browser-use)
124
+
125
+ - Tests that pass go into `tests/ci/` — this is the CI-discovered default set
126
+ - **No mocking** except for LLM responses (use `conftest.py` fixtures for LLM mocking)
127
+ - Use `pytest-httpserver` for all test HTML/responses — never use real remote URLs
128
+ - Modern pytest-asyncio: no `@pytest.mark.asyncio` decorator needed, just write `async def test_*()`
129
+ - `asyncio_mode = "auto"` with session-scoped event loop (browser-use) or function-scoped (bubus)
130
+
131
+ ## Key Design Decisions to Be Aware Of
132
+
133
+ 1. **Shared state belongs on BrowserSession, not on individual watchdogs.** Watchdogs expose state/helpers via events if other watchdogs need access.
134
+ 2. **CDP event forwarding**: BrowserSession forwards raw CDP events to watchdogs through the EventBus, not direct CDP callbacks.
135
+ 3. **BaseEvent is generic over result type** (`BaseEvent[T_EventResultType]`) — dispatchers can aggregate results from multiple handlers via `event.event_result()`, `event.event_results_list()`, etc.
136
+ 4. **Lazy imports** in `browser_use/__init__.py` for startup performance.
137
+ 5. **browser-use has its own CLAUDE.md** (`browser-use/CLAUDE.md`) with detailed per-package conventions — read it when working inside browser-use specifically.
138
+
139
+ ## Qt Reference Materials
140
+
141
+ `references/qt/` contains 16 Qt/GObject reference documents for systematic comparison with bubus/browser-use's event-driven architecture. The full index with source URLs, per-file rationale, and bubus architecture summary is in `references/qt/README.md`.
142
+
143
+ ### Reference File Map
144
+
145
+ | # | File | Topic | Compared Against (bubus/browser-use) |
146
+ |---|------|-------|--------------------------------------|
147
+ | 01 | `01_signals_and_slots.md` | Qt Signal/Slot core mechanism | `EventBus.on()` + `dispatch()` |
148
+ | 02 | `02_connection_type_enum.md` | 6 ConnectionType values (Auto/Direct/Queued/BlockingQueued/Unique/SingleShot) | bubus has only Queued mode |
149
+ | 03 | `03_qobject_connect.md` | connect/disconnect overloads, lifecycle, auto-disconnect on destruction | bubus lacks auto-disconnect |
150
+ | 04 | `04_event_system.md` | Event propagation chain: accept/ignore, event filters | bubus has no accept/ignore |
151
+ | 05 | `05_qevent.md` | QEvent class, 100+ event types, accept()/ignore() | `BaseEvent[T]` |
152
+ | 06 | `06_send_post_event.md` | sendEvent (sync) vs postEvent (queue) + priority | bubus only has queue (postEvent) |
153
+ | 07 | `07_threads_and_qobjects.md` | Thread affinity, cross-thread auto-Queued | bubus is single-threaded asyncio |
154
+ | 08 | `08_qeventloop.md` | Nested event loops, processEvents | bubus `inside_handler_context` polling |
155
+ | 09 | `09_qthread.md` | QThread, moveToThread, Worker pattern | N/A (future multi-thread consideration) |
156
+ | 10 | `10_event_filter.md` | installEventFilter chain, return true to stop propagation | watchdog circuit breaker (ad-hoc) |
157
+ | 11 | `11_property_system.md` | Q_PROPERTY + NOTIFY auto-signal on change | watchdog state change is manual dispatch |
158
+ | 12 | `12_state_machine.md` | QStateMachine hierarchical FSM | BrowserSession lifecycle (flag-based) |
159
+ | 13 | `13_meta_object_system.md` | Meta-Object System overview | bubus `dir()` + naming convention reflection |
160
+ | 14 | `14_moc.md` | MOC compiler: codegen vs runtime reflection | bubus runtime `on_` method scanning |
161
+ | 15 | `15_woboq_signal_internals.md` | Qt internals: connection list, 64-bit bitmask fast-path, O(1) cleanup | bubus handler list + WeakSet |
162
+ | 16 | `16_gobject_signals.md` | GObject 6-phase emission, accumulator, detail filtering | bubus `event_results` aggregation |
163
+
164
+ ### Key Design Gaps Identified (bubus vs Qt)
165
+
166
+ These are the primary areas where Qt's mature design reveals potential improvements for bubus:
167
+
168
+ 1. **Connection types**: bubus only supports Queued dispatch (via `asyncio.Queue`). Qt offers Direct (synchronous inline), Queued, BlockingQueued, and Auto (auto-selects based on thread affinity). Adding at least a Direct mode would enable zero-overhead handler invocation for same-context calls.
169
+
170
+ 2. **Event propagation control**: bubus has no accept/ignore mechanism. Once dispatched, an event reaches all registered handlers unconditionally. Qt allows handlers to accept (consume) or ignore (pass through) events, and event filters can intercept and stop propagation before the target sees it.
171
+
172
+ 3. **Event filter chain**: bubus's event interception is ad-hoc — `_would_create_loop()` and the watchdog circuit breaker wrapper are hardcoded special cases. Qt provides a general `installEventFilter()` mechanism where any object can intercept events for any other object, with LIFO ordering and return-true-to-stop semantics.
173
+
174
+ 4. **Auto-disconnect on destruction**: Qt automatically severs all signal/slot connections when either sender or receiver is destroyed. bubus has no equivalent — `BaseWatchdog` has no `detach_from_session()` method, and `__del__` only cancels asyncio tasks.
175
+
176
+ 5. **Handler priority**: bubus handlers execute in registration order (FIFO) with no priority mechanism. Qt's `postEvent()` accepts integer priority for queue ordering. GObject goes further with 6 emission phases (RUN_FIRST → EMISSION_HOOK → HANDLER_RUN_FIRST → RUN_LAST → HANDLER_RUN_LAST → RUN_CLEANUP).
177
+
178
+ 6. **State machine for lifecycle**: BrowserSession manages connection state via boolean flags (`is_cdp_connected`, `is_reconnecting`). Qt's `QStateMachine` provides formal hierarchical state machines with signal-driven transitions, error states, and property animation — a more robust pattern for complex lifecycle management.
179
+
180
+ 7. **Property change notifications**: Watchdog state changes require manual `dispatch()` calls. Qt's `Q_PROPERTY(... NOTIFY signal)` automatically emits a signal when a property changes, eliminating boilerplate.
181
+
182
+ 8. **Result accumulation**: bubus has flexible result aggregation (`event_result()`, `event_results_flat_dict()`, etc.). GObject's accumulator pattern is more formalized — an accumulator function receives each handler's return value and can short-circuit emission by returning FALSE. This is worth studying for bubus's `event_results_filtered()` design.
@@ -0,0 +1,6 @@
1
+ [project]
2
+ name = "agent-cdp"
3
+ version = "0.1.0"
4
+ description = ""
5
+ requires-python = ">=3.11"
6
+ dependencies = []
@@ -0,0 +1,22 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:doc.qt.io)",
5
+ "WebFetch(domain:woboq.com)",
6
+ "WebFetch(domain:docs.gtk.org)",
7
+ "Bash(grep:*)",
8
+ "Bash(wc:*)",
9
+ "Bash(pip show:*)",
10
+ "Bash(python:*)",
11
+ "Bash(ls:*)",
12
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog -type f -name *.lock -o -name *.toml -o -name *.md)",
13
+ "Bash(python3:*)",
14
+ "Bash(uv sync:*)",
15
+ "Bash(uv run:*)",
16
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/tests -type f -name *.py)",
17
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src -type f -name *.py)",
18
+ "Bash(find /root/.claude/projects -name *.md -type f)",
19
+ "Bash(find /root/QLY/code/event_driven_browser_watchdog/src/agent_cdp -name \"*.py\" -type f -exec wc -l {} +)"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ gitdir: /root/QLY/code/event_driven_browser_watchdog/.git/worktrees/agent-aaf55f3e
@@ -0,0 +1,39 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ *.egg
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ sdist/
11
+ wheels/
12
+ *.whl
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ ENV/
18
+
19
+ # IDE
20
+ .idea/
21
+ .vscode/
22
+ *.swp
23
+ *.swo
24
+ *~
25
+
26
+ # Testing / Coverage
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+
33
+ # OS
34
+ .DS_Store
35
+ Thumbs.db
36
+
37
+ # Project-specific
38
+ bu_ref/
39
+ references/