ai-dev-browser 0.58.0__tar.gz → 0.58.2__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 (360) hide show
  1. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.github/workflows/ci.yml +25 -3
  2. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/PKG-INFO +11 -1
  3. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/README.md +10 -0
  4. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_extension_lifecycle.py +10 -2
  5. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_session.py +35 -21
  6. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_tab.py +70 -16
  7. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/ax.py +19 -1
  8. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/browser.py +29 -2
  9. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/chrome.py +10 -1
  10. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/elements.py +3 -0
  11. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/errors.py +12 -1
  12. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/ext_bridge.py +1 -1
  13. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/extension.py +29 -0
  14. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/mouse.py +9 -3
  15. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/window.py +54 -9
  16. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/background.js +32 -1
  17. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/manifest.json +1 -1
  18. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/page.py +102 -3
  19. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/page_view.js +26 -3
  20. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/session.py +10 -0
  21. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/window.py +35 -4
  22. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/worker.py +14 -1
  23. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser.egg-info/PKG-INFO +11 -1
  24. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser.egg-info/SOURCES.txt +9 -0
  25. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser.egg-info/scm_file_list.json +9 -0
  26. ai_dev_browser-0.58.2/docs/BR-real-profile-hidden-input-resolution.md +83 -0
  27. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/docs/remote-views.md +27 -2
  28. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/pyproject.toml +5 -1
  29. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/conftest.py +16 -3
  30. ai_dev_browser-0.58.2/tests/integration/extension_window_support.py +312 -0
  31. ai_dev_browser-0.58.2/tests/integration/native_window_occluder.py +99 -0
  32. ai_dev_browser-0.58.2/tests/integration/scenarios_extension_window.json +23 -0
  33. ai_dev_browser-0.58.2/tests/integration/scenarios_extension_window_previous.json +22 -0
  34. ai_dev_browser-0.58.2/tests/integration/test_extension_window_native.py +77 -0
  35. ai_dev_browser-0.58.2/tests/integration/test_extension_window_previous.py +67 -0
  36. ai_dev_browser-0.58.2/tests/integration/test_remote_selection_live.py +368 -0
  37. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_remote_view.py +7 -1
  38. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/extension_ownership.test.cjs +31 -1
  39. ai_dev_browser-0.58.2/tests/unit/test_window_input_contract.py +119 -0
  40. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.dockerignore +0 -0
  41. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.gitattributes +0 -0
  42. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.github/workflows/publish.yml +0 -0
  43. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.gitignore +0 -0
  44. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.gitmodules +0 -0
  45. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.pre-commit-config.yaml +0 -0
  46. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/.secrets.baseline +0 -0
  47. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/LICENSE +0 -0
  48. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/__init__.py +0 -0
  49. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/_cli.py +0 -0
  50. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/_version.py +0 -0
  51. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/__init__.py +0 -0
  52. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/accessibility.py +0 -0
  53. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/animation.py +0 -0
  54. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/audits.py +0 -0
  55. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/autofill.py +0 -0
  56. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/background_service.py +0 -0
  57. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/bluetooth_emulation.py +0 -0
  58. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/browser.py +0 -0
  59. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/cache_storage.py +0 -0
  60. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/cast.py +0 -0
  61. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/console.py +0 -0
  62. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/crash_report_context.py +0 -0
  63. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/css.py +0 -0
  64. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/debugger.py +0 -0
  65. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/device_access.py +0 -0
  66. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/device_orientation.py +0 -0
  67. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/dom.py +0 -0
  68. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/dom_debugger.py +0 -0
  69. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/dom_snapshot.py +0 -0
  70. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/dom_storage.py +0 -0
  71. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/emulation.py +0 -0
  72. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/event_breakpoints.py +0 -0
  73. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/extensions.py +0 -0
  74. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/fed_cm.py +0 -0
  75. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/fetch.py +0 -0
  76. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/file_system.py +0 -0
  77. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/headless_experimental.py +0 -0
  78. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/heap_profiler.py +0 -0
  79. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/indexed_db.py +0 -0
  80. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/input_.py +0 -0
  81. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/inspector.py +0 -0
  82. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/io.py +0 -0
  83. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/layer_tree.py +0 -0
  84. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/log.py +0 -0
  85. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/media.py +0 -0
  86. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/memory.py +0 -0
  87. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/network.py +0 -0
  88. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/overlay.py +0 -0
  89. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/page.py +0 -0
  90. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/performance.py +0 -0
  91. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/performance_timeline.py +0 -0
  92. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/preload.py +0 -0
  93. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/profiler.py +0 -0
  94. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/pwa.py +0 -0
  95. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/py.typed +0 -0
  96. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/runtime.py +0 -0
  97. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/schema.py +0 -0
  98. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/security.py +0 -0
  99. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/service_worker.py +0 -0
  100. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/smart_card_emulation.py +0 -0
  101. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/storage.py +0 -0
  102. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/system_info.py +0 -0
  103. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/target.py +0 -0
  104. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/tethering.py +0 -0
  105. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/tracing.py +0 -0
  106. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/util.py +0 -0
  107. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/web_audio.py +0 -0
  108. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/web_authn.py +0 -0
  109. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/cdp/web_mcp.py +0 -0
  110. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/__init__.py +0 -0
  111. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_browser_guidance.py +0 -0
  112. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_case.py +0 -0
  113. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_demo.py +0 -0
  114. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_element.py +0 -0
  115. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_image_cap.py +0 -0
  116. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_js.py +0 -0
  117. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_recorder.py +0 -0
  118. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_ref.py +0 -0
  119. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/_transport.py +0 -0
  120. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/cdp.py +0 -0
  121. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/cleanup.py +0 -0
  122. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/config.py +0 -0
  123. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/connection.py +0 -0
  124. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/cookies.py +0 -0
  125. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/cookies_import.py +0 -0
  126. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/dialog.py +0 -0
  127. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/download.py +0 -0
  128. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/human.py +0 -0
  129. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/identity.py +0 -0
  130. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/login.py +0 -0
  131. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/navigation.py +0 -0
  132. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/overlays.py +0 -0
  133. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/page.py +0 -0
  134. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/port.py +0 -0
  135. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/process.py +0 -0
  136. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/recording.py +0 -0
  137. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/registry.py +0 -0
  138. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/snapshot.py +0 -0
  139. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/storage.py +0 -0
  140. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/tabs.py +0 -0
  141. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/core/text_match.py +0 -0
  142. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/README.md +0 -0
  143. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/icon128.png +0 -0
  144. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/icon16.png +0 -0
  145. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/icon48.png +0 -0
  146. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/extension/managed-schema.json +0 -0
  147. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/pool/__init__.py +0 -0
  148. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/pool/job.py +0 -0
  149. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/pool/persistence.py +0 -0
  150. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/pool/pool.py +0 -0
  151. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/pool/worker.py +0 -0
  152. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/profile.py +0 -0
  153. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/py.typed +0 -0
  154. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/__init__.py +0 -0
  155. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/cdp.py +0 -0
  156. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/control.py +0 -0
  157. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/existing.py +0 -0
  158. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/files.py +0 -0
  159. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/mcp-tools.json +0 -0
  160. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/profile_lease.py +0 -0
  161. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/provision.py +0 -0
  162. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/remote/vnc.py +0 -0
  163. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/__init__.py +0 -0
  164. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/_generate.py +0 -0
  165. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_cleanup.py +0 -0
  166. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_connect.py +0 -0
  167. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_disconnect.py +0 -0
  168. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_list.py +0 -0
  169. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_reconnect.py +0 -0
  170. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_restart.py +0 -0
  171. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_start.py +0 -0
  172. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/browser_stop.py +0 -0
  173. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/cdp_send.py +0 -0
  174. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/click_by_html_id.py +0 -0
  175. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/click_by_ref.py +0 -0
  176. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/click_by_text.py +0 -0
  177. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/click_by_xpath.py +0 -0
  178. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/click_row_by_text.py +0 -0
  179. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/cookies_extract_live.py +0 -0
  180. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/cookies_extract_offline.py +0 -0
  181. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/cookies_import.py +0 -0
  182. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/cookies_load.py +0 -0
  183. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/cookies_save.py +0 -0
  184. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/dialog_respond.py +0 -0
  185. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/download.py +0 -0
  186. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/download_link.py +0 -0
  187. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/drag_by_ref.py +0 -0
  188. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/find_by_html_id.py +0 -0
  189. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/find_by_text.py +0 -0
  190. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/find_by_xpath.py +0 -0
  191. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/focus_by_ref.py +0 -0
  192. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/highlight_by_ref.py +0 -0
  193. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/hover_by_ref.py +0 -0
  194. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/html_by_ref.py +0 -0
  195. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/js_evaluate.py +0 -0
  196. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/login_interactive.py +0 -0
  197. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/mouse_click.py +0 -0
  198. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/mouse_drag.py +0 -0
  199. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/mouse_move.py +0 -0
  200. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_discover.py +0 -0
  201. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_emulate_focus.py +0 -0
  202. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_goto.py +0 -0
  203. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_html.py +0 -0
  204. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_info.py +0 -0
  205. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_pdf.py +0 -0
  206. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_record_start.py +0 -0
  207. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_record_stop.py +0 -0
  208. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_reload.py +0 -0
  209. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_screenshot.py +0 -0
  210. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_scroll.py +0 -0
  211. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_wait_element.py +0 -0
  212. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_wait_ready.py +0 -0
  213. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/page_wait_url.py +0 -0
  214. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/press_key.py +0 -0
  215. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/screenshot_by_ref.py +0 -0
  216. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/select_by_ref.py +0 -0
  217. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/select_text.py +0 -0
  218. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/storage_get.py +0 -0
  219. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/storage_set.py +0 -0
  220. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/tab_close.py +0 -0
  221. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/tab_list.py +0 -0
  222. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/tab_new.py +0 -0
  223. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/tab_switch.py +0 -0
  224. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/type_by_ref.py +0 -0
  225. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/type_by_text.py +0 -0
  226. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/upload_by_ref.py +0 -0
  227. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser/tools/window_set.py +0 -0
  228. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser.egg-info/dependency_links.txt +0 -0
  229. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser.egg-info/requires.txt +0 -0
  230. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/ai_dev_browser.egg-info/top_level.txt +0 -0
  231. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/deploy/managed-chrome/Dockerfile +0 -0
  232. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/deploy/managed-chrome/configure_extension.py +0 -0
  233. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/docs/navigation-ownership.md +0 -0
  234. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/scripts/build_release_assets.py +0 -0
  235. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/scripts/live_browser_lifecycle_steering.py +0 -0
  236. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/scripts/live_grok_pool_persistence.py +0 -0
  237. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/scripts/live_recording_steering.py +0 -0
  238. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/scripts/sync_cdp.py +0 -0
  239. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/setup.cfg +0 -0
  240. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/__init__.py +0 -0
  241. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/conftest.py +0 -0
  242. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/__init__.py +0 -0
  243. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/browser_lifecycle_acceptance.json +0 -0
  244. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/browser_lifecycle_journeys.py +0 -0
  245. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/browser_lifecycle_support.py +0 -0
  246. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/browser_lifecycle_ui.py +0 -0
  247. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/cli_steering_acceptance.json +0 -0
  248. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/cli_steering_support.py +0 -0
  249. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/extension_ownership_support.py +0 -0
  250. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/live_cleanup.py +0 -0
  251. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/navigation_startup_support.py +0 -0
  252. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/pdf_readiness_support.py +0 -0
  253. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/pool-persistence-acceptance.json +0 -0
  254. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/recording_demo_acceptance.json +0 -0
  255. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/recording_demo_support.py +0 -0
  256. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/run_managed_extension.py +0 -0
  257. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_browser_lifecycle.json +0 -0
  258. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_cli_extension.json +0 -0
  259. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_cli_steering.json +0 -0
  260. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_extension_ownership.json +0 -0
  261. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_extension_ownership_native.json +0 -0
  262. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_navigation_startup.json +0 -0
  263. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_pdf_readiness.json +0 -0
  264. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_public_web.json +0 -0
  265. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_recording.json +0 -0
  266. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_recording_demo.json +0 -0
  267. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/scenarios_workspace.json +0 -0
  268. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_background_processes.py +0 -0
  269. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_control_live.py +0 -0
  270. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_files_live.py +0 -0
  271. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_inventory.py +0 -0
  272. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_lifecycle.py +0 -0
  273. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_session_live.py +0 -0
  274. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_start_retry.py +0 -0
  275. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_browser_worker_live.py +0 -0
  276. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cdp_send_warning.py +0 -0
  277. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_ci_integration.py +0 -0
  278. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cli_extension_workflows.py +0 -0
  279. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cli_steering.py +0 -0
  280. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cli_unicode_help.py +0 -0
  281. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_click_robust.py +0 -0
  282. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_config_resolvers.py +0 -0
  283. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cookies_extract_hermetic.py +0 -0
  284. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cookies_extract_live.py +0 -0
  285. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cookies_extract_windows.py +0 -0
  286. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_cookies_import.py +0 -0
  287. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_dialog_auto.py +0 -0
  288. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_dialog_respond.py +0 -0
  289. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_dom_discovery.py +0 -0
  290. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_download_link.py +0 -0
  291. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_download_link_frame.py +0 -0
  292. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_existing_worker_live.py +0 -0
  293. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_ext_bridge.py +0 -0
  294. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_extension_ownership.py +0 -0
  295. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_extension_ownership_native.py +0 -0
  296. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_find_and_interact_workflows.py +0 -0
  297. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_image_cap.py +0 -0
  298. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_js_contract.py +0 -0
  299. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_js_evaluate_frame.py +0 -0
  300. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_locator_workflows.py +0 -0
  301. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_mouse_click.py +0 -0
  302. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_mouse_drag_move.py +0 -0
  303. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_multi_profile_pool.py +0 -0
  304. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_navigation_startup.py +0 -0
  305. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_page_discover.py +0 -0
  306. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_page_goto.py +0 -0
  307. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_page_pdf.py +0 -0
  308. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_page_reload.py +0 -0
  309. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_page_scroll_verify.py +0 -0
  310. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_page_wait_element.py +0 -0
  311. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_pdf_readiness.py +0 -0
  312. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_pool_model_persistence.py +0 -0
  313. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_port_detection.py +0 -0
  314. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_press_key.py +0 -0
  315. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_proxy_identity.py +0 -0
  316. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_public_web_workflows.py +0 -0
  317. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_recording_demo.py +0 -0
  318. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_recording_workflows.py +0 -0
  319. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_remote_vnc.py +0 -0
  320. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_remote_window_live.py +0 -0
  321. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_screenshot_coords.py +0 -0
  322. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_scroll_embedded_target.py +0 -0
  323. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_select_text.py +0 -0
  324. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_startup_timeout.py +0 -0
  325. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_stealth_defaults.py +0 -0
  326. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_tab_close.py +0 -0
  327. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_tab_url_matching.py +0 -0
  328. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_text_locator.py +0 -0
  329. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_timeout_and_retry.py +0 -0
  330. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_trusted_click.py +0 -0
  331. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_type_verified_fill.py +0 -0
  332. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_viewport.py +0 -0
  333. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_worker_managed_status_live.py +0 -0
  334. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/integration/test_workspace_workflows.py +0 -0
  335. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_browser_control.py +0 -0
  336. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_build_info.py +0 -0
  337. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_case.py +0 -0
  338. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cdp_coerce.py +0 -0
  339. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cdp_session_scoped.py +0 -0
  340. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cdp_unbound.py +0 -0
  341. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cli_docstring_args.py +0 -0
  342. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cli_error_envelope.py +0 -0
  343. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cli_param_type.py +0 -0
  344. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_cli_steering_contract.py +0 -0
  345. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_demo_encoding.py +0 -0
  346. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_extension_ownership.py +0 -0
  347. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_find_hint.py +0 -0
  348. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_launch_window_size.py +0 -0
  349. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_lifecycle_handshake_errors.py +0 -0
  350. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_live_cleanup.py +0 -0
  351. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_no_acronym_rot.py +0 -0
  352. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_pdf_units.py +0 -0
  353. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_recording.py +0 -0
  354. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_release_version.py +0 -0
  355. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_remote_files.py +0 -0
  356. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_remote_navigation.py +0 -0
  357. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_session_failure_state.py +0 -0
  358. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_tool_registry.py +0 -0
  359. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/tests/unit/test_worker_lifecycle_status.py +0 -0
  360. {ai_dev_browser-0.58.0 → ai_dev_browser-0.58.2}/uv.lock +0 -0
@@ -31,6 +31,9 @@ jobs:
31
31
  - name: Install package
32
32
  run: uv sync --extra dev --extra remote
33
33
 
34
+ - name: Check extension window and ownership contracts
35
+ run: node --test tests/unit/extension_ownership.test.cjs
36
+
34
37
  # ── Tool dispatch smoke test ──
35
38
  # Verifies python -m entry point works on all platforms.
36
39
  - name: Test tool dispatch
@@ -158,19 +161,19 @@ jobs:
158
161
  - run: python -m pip install ".[dev,remote]"
159
162
  - name: Install the real X11 and noVNC viewer
160
163
  if: runner.os == 'Linux'
161
- run: sudo apt-get update -qq && sudo apt-get install -y xvfb xauth x11vnc novnc websockify
164
+ run: sudo apt-get update -qq && sudo apt-get install -y xvfb xauth x11vnc novnc websockify openbox xdotool xclip
162
165
  - name: Run page, extension and full-window workflows on Linux
163
166
  if: runner.os == 'Linux'
164
167
  env:
165
168
  AI_DEV_BROWSER_CHROME: ${{ steps.chrome.outputs.chrome-path }}
166
169
  AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
167
- run: xvfb-run -a -s "-screen 0 1280x1000x24" python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_remote_vnc.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
170
+ run: xvfb-run -a -s "-screen 0 1280x1000x24" python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_remote_selection_live.py tests/integration/test_remote_vnc.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
168
171
  - name: Run page and extension workflows on Windows
169
172
  if: runner.os == 'Windows'
170
173
  env:
171
174
  AI_DEV_BROWSER_CHROME: ${{ steps.chrome.outputs.chrome-path }}
172
175
  AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
173
- run: python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
176
+ run: python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_remote_view.py tests/integration/test_remote_selection_live.py tests/integration/test_browser_control_live.py tests/integration/test_browser_session_live.py tests/integration/test_browser_files_live.py tests/integration/test_browser_worker_live.py tests/integration/test_existing_worker_live.py tests/integration/test_navigation_startup.py --basetemp remote-results
174
177
  - name: Retain real viewer evidence
175
178
  if: always()
176
179
  uses: actions/upload-artifact@v4
@@ -249,6 +252,21 @@ jobs:
249
252
  AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
250
253
  run: uv run --no-sync python -m pytest -v --full-trace -o faulthandler_timeout=180 tests/integration/test_cli_extension_workflows.py tests/integration/test_recording_workflows.py tests/integration/test_recording_demo.py tests/integration/test_browser_lifecycle.py tests/integration/test_remote_view.py tests/integration/test_extension_ownership.py tests/integration/test_cli_steering.py -k "real_extension or outbound_proxy" --basetemp extension-results
251
254
 
255
+ - name: Install the unchanged released extension fixture
256
+ run: uv pip install --target previous-extension-fixture --no-deps ai-dev-browser==0.57.7
257
+
258
+ - name: Verify live upgrade diagnostics against the released extension
259
+ env:
260
+ AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
261
+ AI_DEV_BROWSER_TEST_PREVIOUS_EXTENSION: ${{ github.workspace }}/previous-extension-fixture/ai_dev_browser/extension
262
+ run: uv run --no-sync python -m pytest -v tests/integration/test_extension_window_previous.py --basetemp previous-extension-results
263
+
264
+ - name: Honor genuine Windows window visibility before input
265
+ if: runner.os == 'Windows'
266
+ env:
267
+ AI_DEV_BROWSER_TEST_EXTENSION_CHROME: ${{ steps.chrome.outputs.chrome-path }}
268
+ run: uv run --no-sync python -m pytest -v tests/integration/test_extension_window_native.py --basetemp native-window-results
269
+
252
270
  - name: Install native Chrome interaction tools
253
271
  if: runner.os == 'Linux'
254
272
  run: sudo apt-get update -qq && sudo apt-get install -y xvfb xauth openbox xdotool
@@ -285,6 +303,10 @@ jobs:
285
303
  extension-results/**/extension-chrome.log
286
304
  extension-results/**/extension-cleanup-stacks.txt
287
305
  native-ownership-results/**
306
+ native-window-results/**/native-window-evidence.json
307
+ native-window-results/**/native-window-accepted.png
308
+ native-window-results/**/extension-chrome.log
309
+ previous-extension-results/**/previous-extension-evidence.json
288
310
 
289
311
  # ─────────────────────────────────────────────────────────────────────
290
312
  # CI-integration dogfood: prove the documented "consumer's CI" recipe
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-dev-browser
3
- Version: 0.58.0
3
+ Version: 0.58.2
4
4
  Summary: A browser for AI to develop web automation — human-like automation that works seamlessly in a world designed for humans
5
5
  Author: sudoprivacy
6
6
  License: AGPL-3.0
@@ -454,6 +454,16 @@ Each GitHub release includes the same wheel and source distribution sent to PyPI
454
454
  the bundled Chrome extension as an unpackable ZIP, and `SHA256SUMS`.
455
455
  For extension mode, extract the ZIP and load its directory in `chrome://extensions`.
456
456
  After upgrading, reload the extension and restart an already running bridge.
457
+ `browser_connect` reports the actual loaded `extension_version`, the
458
+ `bundled_extension_version`, and whether they match. Updating Python alone does
459
+ not reload Chrome's extension. Reload can reset control; inspect the session and
460
+ its owned page before resuming input.
461
+
462
+ For a hidden extension page, `window_set --state normal --focus` requests real
463
+ window restoration and reports observed `visibility`, `focused`, and
464
+ `input_ready`. A focused window may still be occluded. Keep it genuinely visible
465
+ before input; focus emulation does not resolve a hidden-page refusal. See the
466
+ [live window investigation](docs/BR-real-profile-hidden-input-resolution.md).
457
467
 
458
468
  Maintainers: run live browser acceptance and wait for CI on the release commit.
459
469
  Create an annotated `vX.Y.Z` tag with `git tag -a --cleanup=verbatim` and use the
@@ -412,6 +412,16 @@ Each GitHub release includes the same wheel and source distribution sent to PyPI
412
412
  the bundled Chrome extension as an unpackable ZIP, and `SHA256SUMS`.
413
413
  For extension mode, extract the ZIP and load its directory in `chrome://extensions`.
414
414
  After upgrading, reload the extension and restart an already running bridge.
415
+ `browser_connect` reports the actual loaded `extension_version`, the
416
+ `bundled_extension_version`, and whether they match. Updating Python alone does
417
+ not reload Chrome's extension. Reload can reset control; inspect the session and
418
+ its owned page before resuming input.
419
+
420
+ For a hidden extension page, `window_set --state normal --focus` requests real
421
+ window restoration and reports observed `visibility`, `focused`, and
422
+ `input_ready`. A focused window may still be occluded. Keep it genuinely visible
423
+ before input; focus emulation does not resolve a hidden-page refusal. See the
424
+ [live window investigation](docs/BR-real-profile-hidden-input-resolution.md).
415
425
 
416
426
  Maintainers: run live browser acceptance and wait for CI on the release commit.
417
427
  Create an annotated `vX.Y.Z` tag with `git tag -a --cleanup=verbatim` and use the
@@ -66,6 +66,7 @@ async def start(
66
66
  url,
67
67
  startup_timeout,
68
68
  extra_args,
69
+ display=None,
69
70
  ) -> dict:
70
71
  if not user_data_dir:
71
72
  return _session.failure(
@@ -105,7 +106,9 @@ async def start(
105
106
  if (
106
107
  not headless
107
108
  and sys.platform.startswith("linux")
108
- and not (os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"))
109
+ and not (
110
+ display or os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")
111
+ )
109
112
  ):
110
113
  return _session.failure(
111
114
  "unsupported",
@@ -174,7 +177,11 @@ async def start(
174
177
  command += arguments + [
175
178
  f"http://127.0.0.1:{EXTENSION_BRIDGE_PORT}/adb-launch/{token}"
176
179
  ]
177
- process = _spawn_chrome(command, silent_stderr=True)
180
+ process = _spawn_chrome(
181
+ command,
182
+ silent_stderr=True,
183
+ **({"display": display} if display is not None else {}),
184
+ )
178
185
  observed = _session.process_info(process.pid)
179
186
  record = {
180
187
  "transport": "extension",
@@ -193,6 +200,7 @@ async def start(
193
200
  "headless": headless,
194
201
  "startup_timeout": startup_timeout,
195
202
  "extra_args": arguments,
203
+ "display": display,
196
204
  },
197
205
  }
198
206
  if observed:
@@ -211,24 +211,38 @@ def capabilities(record: dict) -> dict:
211
211
 
212
212
 
213
213
  def describe(record: dict) -> dict:
214
- return {
215
- key: record.get(key)
216
- for key in (
217
- "session_id",
218
- "transport",
219
- "owner",
220
- "profile_owner",
221
- "pid",
222
- "port",
223
- "chrome_path",
224
- "user_data_dir",
225
- "profile_directory",
226
- "profile_id",
227
- "headless",
228
- "browser_version",
229
- "automation_target_id",
230
- )
231
- } | {
232
- "launch_chrome_path": (record.get("start_options") or {}).get("chrome_path"),
233
- "capabilities": capabilities(record),
234
- }
214
+ return (
215
+ {
216
+ key: record.get(key)
217
+ for key in (
218
+ "session_id",
219
+ "transport",
220
+ "owner",
221
+ "profile_owner",
222
+ "pid",
223
+ "port",
224
+ "chrome_path",
225
+ "user_data_dir",
226
+ "profile_directory",
227
+ "profile_id",
228
+ "headless",
229
+ "browser_version",
230
+ "automation_target_id",
231
+ )
232
+ }
233
+ | {
234
+ "launch_chrome_path": (record.get("start_options") or {}).get(
235
+ "chrome_path"
236
+ ),
237
+ "capabilities": capabilities(record),
238
+ }
239
+ | {
240
+ key: record.get(key)
241
+ for key in (
242
+ "extension_version",
243
+ "bundled_extension_version",
244
+ "extension_version_matches_bundle",
245
+ )
246
+ if record.get("transport") == "extension"
247
+ }
248
+ )
@@ -918,7 +918,25 @@ class Tab:
918
918
  async def _set_window_state(self, state: str):
919
919
  window_id, _ = await self.get_window()
920
920
  bounds = cdp_browser.Bounds(window_state=cdp_browser.WindowState(state))
921
- await self.send(cdp_browser.set_window_bounds(window_id, bounds))
921
+ try:
922
+ await self.send(cdp_browser.set_window_bounds(window_id, bounds))
923
+ except ProtocolException as error:
924
+ if getattr(
925
+ self._browser, "transport", "cdp"
926
+ ) != "extension" or not _is_cdp_method_not_found(error):
927
+ raise
928
+ from .errors import InputUnavailableError
929
+
930
+ observed_state: dict = {"visibility": "unknown", "focused": None}
931
+ try:
932
+ observed_state = await self._input_state(timeout=1)
933
+ except Exception:
934
+ pass # A diagnostic read must not replace the unsupported-route error.
935
+ raise InputUnavailableError(
936
+ "The loaded extension does not support Browser.setWindowBounds; "
937
+ "the requested window state was not applied and no input was sent",
938
+ details=await self._input_diagnostics(observed_state),
939
+ ) from error
922
940
 
923
941
  async def maximize(self):
924
942
  await self._set_window_state("maximized")
@@ -941,6 +959,45 @@ class Tab:
941
959
  """Alias for activate()."""
942
960
  await self.activate()
943
961
 
962
+ async def _input_state(self, *, timeout: float | None = None) -> dict:
963
+ """Read genuine page visibility and document focus independently."""
964
+ return await self.evaluate(
965
+ "({visibility:document.visibilityState,focused:document.hasFocus()})",
966
+ timeout=timeout,
967
+ )
968
+
969
+ async def _wait_input_visibility(self) -> dict:
970
+ deadline = time.monotonic() + 2
971
+ state = await self._input_state()
972
+ while state["visibility"] != "visible" and time.monotonic() < deadline:
973
+ await asyncio.sleep(0.05)
974
+ state = await self._input_state()
975
+ return state
976
+
977
+ async def _input_diagnostics(self, state: dict) -> dict:
978
+ """Bounded, read-only support data; never include flow URLs or accounts."""
979
+ from .extension import extension_version_info, query_extension_version
980
+
981
+ details = {
982
+ "input_sent": False,
983
+ "input_scope": "current_dispatch",
984
+ "target_id": str(self._target.target_id),
985
+ **state,
986
+ }
987
+ if getattr(self._browser, "transport", "cdp") != "extension":
988
+ return details
989
+ loaded = await query_extension_version(self._browser)
990
+ details.update(extension_version_info(loaded))
991
+ try:
992
+ window_id, bounds = await self.send(
993
+ cdp_browser.get_window_for_target(self._target.target_id), timeout=1
994
+ )
995
+ details["window_state"] = bounds.window_state.value
996
+ details["window_id"] = window_id
997
+ except Exception:
998
+ pass # Preserve the original refusal if diagnostic support is incomplete.
999
+ return details
1000
+
944
1001
  async def _prepare_input(self, allow_cached: bool = False):
945
1002
  """Wake only the selected extension page before trusted input."""
946
1003
  if getattr(self._browser, "transport", "cdp") != "extension":
@@ -950,26 +1007,23 @@ class Tab:
950
1007
  and time.monotonic() - getattr(self, "_input_checked_at", 0) < 0.5
951
1008
  ):
952
1009
  return
953
- visible = await self.evaluate("document.visibilityState === 'visible'")
954
- if not visible:
1010
+ state = await self._input_state()
1011
+ if state["visibility"] != "visible":
955
1012
  await self.bring_to_front()
956
- deadline = time.monotonic() + 2
957
- while time.monotonic() < deadline:
958
- if await self.evaluate("document.visibilityState === 'visible'"):
959
- # Let the restored renderer paint before its first pointer event.
960
- await self.evaluate(
961
- "new Promise(resolve => requestAnimationFrame(() => resolve(true)))",
962
- await_promise=True,
963
- timeout=2,
964
- )
965
- break
966
- await asyncio.sleep(0.05)
967
- else:
1013
+ state = await self._wait_input_visibility()
1014
+ if state["visibility"] != "visible":
968
1015
  from .errors import InputUnavailableError
969
1016
 
970
1017
  raise InputUnavailableError(
971
- "The automation page is still hidden after activation; this input was not sent. Show its Chrome window, then inspect page state before retrying"
1018
+ "The automation page is still hidden after activation; this input was not sent. Show its Chrome window, then inspect page state before retrying",
1019
+ details=await self._input_diagnostics(state),
972
1020
  )
1021
+ # Let the restored renderer paint before its first pointer event.
1022
+ await self.evaluate(
1023
+ "new Promise(resolve => requestAnimationFrame(() => resolve(true)))",
1024
+ await_promise=True,
1025
+ timeout=2,
1026
+ )
973
1027
  self._input_checked_at = time.monotonic()
974
1028
 
975
1029
  async def close(self):
@@ -871,10 +871,14 @@ async def _type_keystrokes(tab: Tab, text: str) -> None:
871
871
  async def _focus_el(element: Element) -> None:
872
872
  try:
873
873
  await element.focus() # CDP DOM.focus
874
+ except SessionError:
875
+ raise
874
876
  except Exception:
875
877
  pass
876
878
  try:
877
879
  await element.apply("(el) => { if (el && el.focus) el.focus(); }")
880
+ except SessionError:
881
+ raise
878
882
  except Exception:
879
883
  pass
880
884
 
@@ -883,10 +887,12 @@ async def _clear_el(tab: Tab, element: Element) -> None:
883
887
  try:
884
888
  await element.apply(
885
889
  "(el) => { if (!el) return; if (el.focus) el.focus();"
886
- " if ('value' in el) { el.value = '';"
890
+ " if ('value' in el && el.value !== '') { el.value = '';"
887
891
  " el.dispatchEvent(new Event('input', {bubbles: true})); }"
888
892
  " else if (el.isContentEditable) { el.textContent = ''; } }"
889
893
  )
894
+ except SessionError:
895
+ raise
890
896
  except Exception:
891
897
  pass
892
898
 
@@ -908,6 +914,8 @@ async def _field_state(element: Element, text: str) -> dict:
908
914
  if isinstance(st, dict)
909
915
  else {"matches": False, "empty": True, "length": 0}
910
916
  )
917
+ except SessionError:
918
+ raise
911
919
  except Exception:
912
920
  return {"matches": False, "empty": True, "length": 0}
913
921
 
@@ -948,6 +956,11 @@ async def _fill_verified(
948
956
  tried: list[str] = []
949
957
  st: dict | None = None
950
958
  for i, method in enumerate(order):
959
+ prepare_input = getattr(tab, "_prepare_input", None)
960
+ if prepare_input:
961
+ # This precedes even clearing/focusing: a hidden-page or ownership
962
+ # refusal must not turn into a value setter on a later rung.
963
+ await prepare_input()
951
964
  await _clear_el(tab, element)
952
965
  await _focus_el(element)
953
966
  tried.append(method)
@@ -960,6 +973,8 @@ async def _fill_verified(
960
973
  await human.type_text(tab, text, element, humanize=True)
961
974
  elif method == "native":
962
975
  await _native_set(element, text)
976
+ except SessionError:
977
+ raise
963
978
  except Exception:
964
979
  continue
965
980
  st = await _field_state(element, text)
@@ -994,6 +1009,9 @@ async def type_by_ref(
994
1009
  (plus `entered` when `enter=True`) — `verified` already tells you the value
995
1010
  actually landed, so don't screenshot or re-read the field to check.
996
1011
 
1012
+ In extension mode, hidden-page or ownership refusals stop the operation
1013
+ before clearing; input fallbacks never override these refusals.
1014
+
997
1015
  If you can identify the input by its visible label / placeholder, skip
998
1016
  `page_discover` and use `type_by_text` directly.
999
1017
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  import asyncio
4
4
  import os
5
+ import re
5
6
  import time
6
7
  from pathlib import Path
7
8
 
@@ -23,6 +24,8 @@ from .extension import (
23
24
  EXTENSION_BRIDGE_PORT,
24
25
  extension_dir,
25
26
  extension_load_instructions,
27
+ extension_version_info,
28
+ query_extension_version,
26
29
  )
27
30
  from .port import (
28
31
  _cleanup_temp_profile,
@@ -202,6 +205,7 @@ def browser_start(
202
205
  chrome_path: str | None = None,
203
206
  user_data_dir: str | None = None,
204
207
  profile_directory: str = "Default",
208
+ display: str | None = None,
205
209
  ) -> dict:
206
210
  """Use when: you need a new isolated CDP session or explicitly launch a configured extension profile; returns session_id and actual capabilities.
207
211
 
@@ -236,6 +240,9 @@ def browser_start(
236
240
  transport: "cdp" (isolated automation, default) or "extension"
237
241
  (explicitly launch an already configured real Chrome/profile).
238
242
  chrome_path: Chrome executable to launch; otherwise find_chrome resolves it.
243
+ display: Optional local X11 display ID, such as ":2", for a host-owned
244
+ GUI. Passed only to this Chrome child; the calling process keeps
245
+ its DISPLAY unchanged. Omit to use the existing desktop display.
239
246
  user_data_dir: Existing Chrome user-data root, required for extension
240
247
  startup. CDP uses temp/named adb profiles; this option is extension only.
241
248
  profile_directory: Chrome folder inside user_data_dir, e.g. "Default"
@@ -341,6 +348,14 @@ def browser_start(
341
348
  replay input implicitly after failure.
342
349
  """
343
350
  start_options = dict(locals())
351
+ if display is not None and (
352
+ not isinstance(display, str) or not re.fullmatch(r":\d+(?:\.\d+)?", display)
353
+ ):
354
+ return _session.failure(
355
+ "validation",
356
+ "An explicit display must be a local X11 display ID",
357
+ transport=transport,
358
+ )
344
359
  startup_timeout = resolve_startup_timeout(startup_timeout)
345
360
  if transport == "extension":
346
361
  if (
@@ -369,6 +384,7 @@ def browser_start(
369
384
  url=url,
370
385
  startup_timeout=startup_timeout,
371
386
  extra_args=extra_args,
387
+ display=display,
372
388
  )
373
389
  )
374
390
  if (
@@ -386,7 +402,7 @@ def browser_start(
386
402
  import sys
387
403
 
388
404
  if sys.platform.startswith("linux") and not (
389
- os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")
405
+ display or os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY")
390
406
  ):
391
407
  return _session.failure(
392
408
  "unsupported",
@@ -490,6 +506,8 @@ def browser_start(
490
506
  from typing import Any
491
507
 
492
508
  launch_options: dict[str, Any] = {"chrome_path": chrome_path} if chrome_path else {}
509
+ if display is not None:
510
+ launch_options["display"] = display
493
511
  for attempt in range(max_attempts):
494
512
  process = launch_chrome(
495
513
  port=port,
@@ -791,6 +809,10 @@ async def browser_connect(
791
809
  tab_switch/tab_close; automation_target_id tracks the main page identity.
792
810
  For visible local collaboration, show the dedicated page with tab_switch
793
811
  using its targets index so the human and agent work on that same page.
812
+ Extension results report the loaded `extension_version` separately from
813
+ `bundled_extension_version`; an unknown version stays null. Updating Python
814
+ does not reload Chrome's extension. Ordinary extension reload can reset
815
+ control: inspect the session and original owned page before further input.
794
816
 
795
817
  Args:
796
818
  transport: "cdp" (default, existing debug Chrome) or "extension"
@@ -806,7 +828,8 @@ async def browser_connect(
806
828
  Returns:
807
829
  dict with connected, transport, session_id, capabilities, profile_id,
808
830
  owner, profile_owner, actual headless (null if unknown), account for
809
- extension, port for CDP, tabs and tab_count. Includes version/git_commit.
831
+ extension, port for CDP, tabs and tab_count. Includes version/git_commit
832
+ and loaded/bundled extension versions with their observed match status.
810
833
 
811
834
  Failure:
812
835
  Read error_code and setup_instructions. Missing bridge/extension needs
@@ -933,6 +956,9 @@ async def browser_connect(
933
956
  {"tab_id": str(t._target.target_id), "url": t._target.url or ""}
934
957
  for t in browser.tabs
935
958
  ]
959
+ loaded_version = await query_extension_version(
960
+ browser, status.get("extension_version")
961
+ )
936
962
  finally:
937
963
  await browser.close()
938
964
  main = status.get("automation_target_id")
@@ -951,6 +977,7 @@ async def browser_connect(
951
977
  "profile_id": status["profile_id"],
952
978
  "automation_target_id": main,
953
979
  "browser_version": status.get("browser_version"),
980
+ **extension_version_info(loaded_version),
954
981
  }
955
982
  )
956
983
  _session.write(record)
@@ -17,6 +17,7 @@ import shutil
17
17
  import subprocess
18
18
  import tempfile
19
19
  from pathlib import Path
20
+ from typing import Any
20
21
 
21
22
  from .config import DEFAULT_DEBUG_PORT, DEFAULT_PROFILE_PREFIX, resolve_viewport
22
23
 
@@ -155,6 +156,7 @@ def launch_chrome(
155
156
  hide_crash_restore_bubble: bool = True,
156
157
  stealth: bool = True,
157
158
  chrome_path: str | None = None,
159
+ display: str | None = None,
158
160
  ) -> subprocess.Popen:
159
161
  """
160
162
  Launch Chrome with remote debugging enabled.
@@ -338,10 +340,14 @@ def launch_chrome(
338
340
  if start_url:
339
341
  args.append(start_url)
340
342
 
343
+ if display is not None:
344
+ return _spawn_chrome(args, silent_stderr=silent_stderr, display=display)
341
345
  return _spawn_chrome(args, silent_stderr=silent_stderr)
342
346
 
343
347
 
344
- def _spawn_chrome(args: list[str], silent_stderr: bool = False) -> subprocess.Popen:
348
+ def _spawn_chrome(
349
+ args: list[str], silent_stderr: bool = False, *, display: str | None = None
350
+ ) -> subprocess.Popen:
345
351
  """Shared detached process primitive for CDP and configured-profile startup."""
346
352
  # Start Chrome process
347
353
  try:
@@ -349,6 +355,7 @@ def _spawn_chrome(args: list[str], silent_stderr: bool = False) -> subprocess.Po
349
355
  # On Unix, use start_new_session to detach from parent
350
356
  # On Windows, CREATE_NEW_PROCESS_GROUP for similar isolation
351
357
  stderr_target = subprocess.DEVNULL if silent_stderr else subprocess.PIPE
358
+ popen_kwargs: dict[str, Any]
352
359
  if platform.system() == "Windows":
353
360
  popen_kwargs = {
354
361
  "stdout": subprocess.DEVNULL,
@@ -362,6 +369,8 @@ def _spawn_chrome(args: list[str], silent_stderr: bool = False) -> subprocess.Po
362
369
  "start_new_session": True,
363
370
  }
364
371
 
372
+ if display is not None:
373
+ popen_kwargs["env"] = {**os.environ, "DISPLAY": display}
365
374
  logger.debug("Launching Chrome executable %s", args[0])
366
375
  process = subprocess.Popen(args, **popen_kwargs) # type: ignore[call-overload]
367
376
 
@@ -1239,6 +1239,9 @@ async def type_by_text(
1239
1239
  `{typed, verified, method, name}` (plus `entered` when `enter=True`) —
1240
1240
  `verified` already confirms the value landed, so don't re-read to check.
1241
1241
 
1242
+ In extension mode, hidden-page or ownership refusals stop the operation
1243
+ before clearing; input fallbacks never override these refusals.
1244
+
1242
1245
  Prefer over `type_by_ref` when you can identify the input by its
1243
1246
  human-visible label rather than needing a prior `page_discover` ref. The
1244
1247
  `clear` / `enter` / `keystrokes` / `human_like` options mirror `type_by_ref`
@@ -27,8 +27,19 @@ class SessionError(Exception):
27
27
  class InputUnavailableError(SessionError):
28
28
  """A requested input was withheld because its page could not be made visible."""
29
29
 
30
- def __init__(self, message: str):
30
+ def __init__(self, message: str, *, details: dict | None = None):
31
31
  super().__init__(message, error_code="unsupported")
32
+ self.details = details or {}
33
+ self.hint = (
34
+ "This input dispatch was not sent. Show the selected Chrome tab/window "
35
+ "and inspect current state before resuming; earlier steps may have "
36
+ "completed. If the loaded "
37
+ "extension version is unknown or differs from the bundle, arrange an "
38
+ "ordinary update/reload in chrome://extensions; reloading can reset "
39
+ "control, so inspect session ownership again afterwards. Do not replay "
40
+ "earlier submissions, spoof visibility/focus, switch transport or use "
41
+ "OS input to bypass the refusal."
42
+ )
32
43
 
33
44
 
34
45
  class RecordingError(Exception):
@@ -180,7 +180,7 @@ class _Bridge:
180
180
  # ------------------------------------------------------------------ drivers
181
181
  _LOGGED = (
182
182
  "Page.navigate",
183
- "Input.dispatch",
183
+ "Input.", # Command names only: never include typed text in the log.
184
184
  "Runtime.evaluate",
185
185
  "Runtime.callFunctionOn",
186
186
  )
@@ -10,6 +10,7 @@ package ships and the steps to relay to the user.
10
10
  from __future__ import annotations
11
11
 
12
12
  from pathlib import Path
13
+ import json
13
14
  import os
14
15
 
15
16
  # Fixed local port the extension's background.js dials and the adb-side bridge
@@ -26,6 +27,34 @@ def extension_dir() -> Path:
26
27
  return Path(__file__).resolve().parent.parent / "extension"
27
28
 
28
29
 
30
+ def extension_version_info(loaded_version: str | None) -> dict:
31
+ """Compare a running extension's report with the bundled manifest, not Python."""
32
+ bundled = json.loads(
33
+ (extension_dir() / "manifest.json").read_text(encoding="utf-8")
34
+ )["version"]
35
+ return {
36
+ "extension_version": loaded_version,
37
+ "bundled_extension_version": bundled,
38
+ "extension_version_matches_bundle": loaded_version == bundled
39
+ if loaded_version
40
+ else None,
41
+ }
42
+
43
+
44
+ async def query_extension_version(browser, fallback: str | None = None) -> str | None:
45
+ """Read the live extension, including when an older bridge drops hello fields."""
46
+ from .cdp import _get_cdp_command
47
+
48
+ try:
49
+ state = await browser.connection.send(
50
+ _get_cdp_command("AiDevBrowser.debugState", {}), timeout=1
51
+ )
52
+ version = state.get("extensionVersion")
53
+ return version if isinstance(version, str) else fallback
54
+ except Exception:
55
+ return fallback # Diagnostics never replace the actual operation's error.
56
+
57
+
29
58
  def extension_load_instructions(extension_dir_path: str | None = None) -> str:
30
59
  """Step-by-step 'Load unpacked' guidance, with the resolved package path.
31
60