You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
browser_manager.py is now a pure facade + composition root (1,284 → 769 lines). The domain logic that remained after the v1.2 service extraction moved into app/browser/services/ (fork / shadow-browse / network log → sessions & diagnostics; settle / action verification → actions; platform detection + auth-state info → auth profiles), and ~50 delegation shims with zero callers were deleted. The public API and the private seams that tests patch are unchanged.
Fork state exports are encrypted at rest.fork_session now routes its storage-state export through AuthStateManager, so exported cookies/localStorage are Fernet-encrypted whenever AUTH_STATE_ENCRYPTION_KEY is set (previously always plaintext JSON, regardless of settings).
Shadow-browse state never touches disk.enable_shadow_browse hands the exported storage state to the headed context as an in-memory dict instead of writing a plaintext temp file.
Fixed
Download capture tasks can no longer be garbage-collected mid-flight. Page download handlers now go through spawn_background_task; the event loop keeps only weak task references, so the old bare asyncio.create_task could silently drop a download and its audit record.
Shadow-browse failures roll back cleanly. If navigation in the headed clone fails, the headed Chromium process is closed and the half-registered session is removed (previously both leaked until manual cleanup).
Page listeners survive object-id reuse. Sessions track listener-attached pages in a WeakSet instead of raw id() values, so a recycled id can no longer cause a new page to skip listener attachment.
shutdown() is idempotent (the Playwright handle is cleared after stop), and _assert_url_allowed matches host patterns case-insensitively on all platforms.