Chuyển tới nội dung chính

Trình giám sát CDP của trình duyệt - Thiết kế

Trạng thái: Đã giao hàng (PR 14540) Cập nhật lần cuối: 2026-04-23 Tác giả: @teknium1

Vấn đề

Các hộp thoại JS gốc (alert/confirm/prompt/Beforeunload) và iframe là hai lỗ hổng lớn nhất trong công cụ trình duyệt của chúng tôi:

  1. Hộp thoại chặn luồng JS. Mọi thao tác trên trang đều bị đình trệ cho đến khi hộp thoại được xử lý. Trước công việc này, tác nhân không có cách nào để biết hộp thoại đã mở - các lệnh gọi công cụ tiếp theo sẽ bị treo hoặc đưa ra các lỗi không rõ ràng.
  2. Iframe là vô hình. Tác nhân có thể thấy các nút iframe trong DOM ảnh chụp nhanh nhưng không thể nhấp, nhập hoặc đánh giá bên trong chúng - đặc biệt là iframe có nguồn gốc chéo (OOPIF) tồn tại trong các quy trình Chrome riêng biệt.

PR #12550 đã đề xuất một trình bao bọc browser_dialog không trạng thái. Điều đó không giải quyết được việc phát hiện - đó là một lệnh gọi CDP sạch hơn khi tác nhân đã biết (thông qua các triệu chứng) rằng hộp thoại đang mở. Đóng như thay thế.

Ma trận khả năng phụ trợ (được xác minh trực tiếp vào ngày 23-04-2026)

Sử dụng tập lệnh thăm dò dùng một lần đối với trang URL dữ liệu kích hoạt cảnh báo trong khung chính và trong iframe srcdoc có cùng nguồn gốc, cộng với một nguồn gốc chéo https://example.com iframe:

BackendDialog detectDialog respondFrame treeOOPIF Runtime.evaluate via browser_cdp(frame_id=...)
Local Chrome (--remote-debugging-port) / /browser connect✓ full workflow
Browserbase✓ (via bridge)✓ full workflow (via bridge)✓ (document.title = "Example Domain" verified on real cross-origin iframe)
Camofox✗ no CDP (REST-only)partial via DOM snapshot

Cách phản hồi của Browserbase hoạt động. Proxy CDP của Browserbase sử dụng Playwright nội bộ và tự động loại bỏ các hộp thoại gốc trong vòng ~10 mili giây, vì vậy Page.handleJavaScriptDialog không thể theo kịp. Để giải quyết vấn đề này, người giám sát chèn một kịch bản cầu nối thông qua Page.addScriptToEvaluateOnNewDocument ghi đè window.alert/confirm/prompt với XHR đồng bộ với máy chủ ma thuật (hermes-dialog-bridge.invalid). Fetch.enable chặn các XHR đó trước khi họ chạm vào mạng — hộp thoại sẽ trở thành Fetch.requestPaused sự kiện mà người giám sát nắm bắt và respond_to_dialog thực hiện thông qua Fetch.fulfillRequest với nội dung JSON mà tập lệnh được chèn sẽ giải mã.

Kết quả thực: từ góc nhìn của trang, prompt() vẫn trả về chuỗi do đại lý cung cấp. Từ góc độ của đại lý, nó giống nhau Dù sao đi nữa, API `browser_dialog(action=...)``. Đã được thử nghiệm từ đầu đến cuối phiên cơ sở trình duyệt thực - 4/4 (cảnh báo/nhắc/xác nhận-chấp nhận/xác nhận-loại bỏ) chuyển bao gồm cả giá trị ngắt vòng trở lại trang JS.

Camofox vẫn không được hỗ trợ cho hoạt động PR này; vấn đề tiếp theo được lên kế hoạch tại jo-inc/camofox-browser yêu cầu điểm cuối thăm dò hộp thoại.

Ngành kiến ​​​​trúc

CDPGiám sát viên

Một asyncio.Task chạy trong chuỗi daemon nền trên mỗi task_id của Hermes. Giữ một WebSocket liên tục cho điểm cuối CDP của chương trình phụ trợ. Duy trì:

  • Hàng đợi hộp thoạiList[PendingDialog] với {id, type, message, default_prompt, session_id, open_at}
  • Cây khungDict[frame_id, FrameInfo] với các mối quan hệ gốc, URL, nguồn gốc, cho dù phiên con có nhiều nguồn gốc hay không
  • Bản đồ phiênDict[session_id, SessionInfo] để các công cụ tương tác có thể định tuyến đến phiên đính kèm phù hợp cho các hoạt động OOPIF
  • Lỗi bảng điều khiển gần đây — vòng đệm của 50 vòng cuối cùng (đối với chẩn đoán PR 2)

Đăng ký trên tệp đính kèm:

  • Page.enablejavascriptDialogOpening, frameAttached, frameNavigated, frameDetached
  • Runtime.enableexecutionContextCreated, consoleAPICalled, ExceptionThrown
  • Target.setAutoAttach {autoAttach: true, Flatten: true} — hiển thị các mục tiêu OOPIF con; người giám sát kích hoạt Trang+Runtime trên mỗi trang

Truy cập trạng thái an toàn theo luồng thông qua khóa chụp nhanh; trình xử lý công cụ (đồng bộ) đọc ảnh chụp nhanh bị đóng băng mà không cần chờ đợi.

Vòng đời

  • Bắt đầu: SupervisorRegistry.get_or_start(task_id, cdp_url) — được gọi bởi browser_navigate, tạo phiên Browserbase, /browser connect. Bình thường.
  • Dừng: ngắt phiên hoặc /ngắt kết nối trình duyệt. Hủy asyncio tác vụ, đóng WebSocket, loại bỏ trạng thái.
  • Rebind: nếu URL CDP thay đổi (người dùng kết nối lại với Chrome mới), hãy dừng người giám sát cũ và bắt đầu mới - không bao giờ sử dụng lại trạng thái trên các điểm cuối.

Chính sách đối thoại

Có thể định cấu hình thông qua config.yaml trong browser.dialog_policy:

  • must_respond (mặc định) — chụp, hiển thị trong browser_snapshot, đợi cho lệnh gọi browser_dialog(action=...) rõ ràng. Sau thời gian chờ an toàn 300 giây không có phản hồi, tự động loại bỏ và đăng nhập. Ngăn chặn tác nhân gây lỗi bị đình trệ mãi mãi.
  • auto_dismiss — ghi lại và loại bỏ ngay lập tức; đại lý nhìn thấy nó sau khi thực tế thông qua browser_state bên trong browser_snapshot.
  • auto_accept — ghi lại và chấp nhận (hữu ích cho trước khi tải khi người dùng muốn điều hướng đi một cách rõ ràng).

Chính sách là cho mỗi nhiệm vụ; không có phần ghi đè trên mỗi hộp thoại trong v1.

Bề mặt tác nhân (PR 1)

Một công cụ mới

browser_dialog(action, prompt_text=None, dialog_id=None)
  • action="accept" / "dismiss" → phản hồi hộp thoại được chỉ định hoặc duy nhất đang chờ xử lý (bắt buộc)
  • prompt_text=... → văn bản để cung cấp cho hộp thoại prompt()
  • dialog_id=... → phân biệt khi có nhiều hộp thoại xếp hàng (hiếm)

Công cụ chỉ có tính năng phản hồi. Tác nhân đọc các hộp thoại đang chờ xử lý từ browser_snapshot đầu ra trước khi gọi.

tiện ích mở rộng browser_snapshot

Thêm ba trường tùy chọn vào đầu ra ảnh chụp nhanh hiện có khi người giám sát được đính kèm:

{
"pending_dialogs": [
{"id": "d-1", "type": "alert", "message": "Hello", "opened_at": 1650000000.0}
],
"recent_dialogs": [
{"id": "d-1", "type": "alert", "message": "...", "opened_at": 1650000000.0,
"closed_at": 1650000000.1, "closed_by": "remote"}
],
"frame_tree": {
"top": {"frame_id": "FRAME_A", "url": "https://example.com/", "origin": "https://example.com"},
"children": [
{"frame_id": "FRAME_B", "url": "about:srcdoc", "is_oopif": false},
{"frame_id": "FRAME_C", "url": "https://ads.example.net/", "is_oopif": true, "session_id": "SID_C"}
],
"truncated": false
}
}
  • pending_dialogs: các hộp thoại hiện đang chặn chuỗi JS của trang. Tác nhân phải gọi browser_dialog(action=...) để phản hồi. trống trên Cơ sở trình duyệt vì proxy CDP của họ tự động loại bỏ trong vòng ~ 10 mili giây.

  • recent_dialogs: bộ đệm vòng lên tới 20 hộp thoại đã đóng gần đây với thẻ closed_by"agent" (chúng tôi đã phản hồi), "auto_policy" (địa phương auto_dismiss/auto_accept), "watchdog" (lần truy cập hết thời gian chờ must_respond) hoặc "remote" (trình duyệt/phụ trợ đã đóng nó đối với chúng tôi, ví dụ: Browserbase). Đây là cách các đặc vụ trên Browserbase vẫn có thể nhìn thấy được những gì đã xảy ra.

  • frame_tree: cấu trúc khung bao gồm các phần tử con có nguồn gốc chéo (OOPIF). Giới hạn ở 30 mục + OOPIF độ sâu 2 đến kích thước ảnh chụp nhanh giới hạn trên quảng cáo nặng trang. bị cắt ngắn: đúng hiển thị khi đạt đến giới hạn; đại lý cần cây đầy đủ có thể sử dụng browser_cdp với Page.getFrameTree.

Không có sơ đồ công cụ mới nào cho bất kỳ công cụ nào trong số này — tác nhân đọc ảnh chụp nhanh nó đã yêu cầu rồi.

Kiểm soát tính khả dụng

Cổng cả hai bề mặt trên _browser_cdp_check (người giám sát chỉ có thể chạy khi CDP điểm cuối có thể truy cập được). Trên các phiên Camofox/không có phụ trợ, công cụ hộp thoại là ẩn và ảnh chụp nhanh bỏ qua các trường mới — không có sơ đồ phình to.

Tương tác iframe nhiều nguồn gốc

Mở rộng công việc phát hiện hộp thoại, browser_cdp(frame_id=...) định tuyến CDP các cuộc gọi (đặc biệt là Runtime.evaluate) thông qua người giám sát đã được kết nối WebSocket sử dụng sessionId con của OOPIF. Đại lý chọn frame_ids trong số browser_snapshot.frame_tree.children[] trong đó is_oopif=true và chuyển chúng tới browser_cdp. Đối với các iframe có cùng nguồn gốc (không có phiên CDP chuyên dụng), tác nhân sử dụng contentWindow/contentDocument từ cấp cao nhất Thay vào đó, Runtime.evaluate — người giám sát đưa ra lỗi khi chỉ vào đó dự phòng khi frame_id thuộc về một không phải OOPIF.

Trên Browserbase, đây là đường dẫn đáng tin cậy DUY NHẤT cho tương tác iframe — kết nối CDP không trạng thái (được mở cho mỗi lệnh gọi browser_cdp) nhấn vào URL đã ký hết hạn, trong khi kết nối lâu dài của người giám sát vẫn giữ phiên hợp lệ.

Camofox (tiếp theo)

Vấn đề được lên kế hoạch chống lại jo-inc/camofox-browser thêm:

  • Nhà viết kịch page.on('dialog', handler) mỗi phiên
  • Điểm cuối thăm dò GET /tabs/:tabId/dialogs
  • POST /tabs/:tabId/dialogs/:id để chấp nhận/bỏ qua
  • Điểm cuối xem xét nội tâm cây khung

Đã chạm vào tệp (PR 1)

Mới

  • tools/browser_supervisor.pyCDPSupervisor, SupervisorRegistry, PendingDialog, FrameInfo
  • tools/browser_dialog_tool.py — trình xử lý công cụ browser_dialog
  • tests/tools/test_browser_supervisor.py — mô phỏng máy chủ CDP WebSocket + kiểm tra vòng đời/trạng thái
  • website/docs/developer-guide/browser-supervisor.md — tập tin này

Đã sửa đổi

  • toolsets.py — đăng ký browser_dialog trong browser, hermes-acp, hermes-api-server, bộ công cụ cốt lõi (được kiểm soát dựa trên khả năng tiếp cận CDP)
  • tools/browser_tool.py
    • browser_navigate start-hook: nếu URL CDP có thể phân giải được, SupervisorRegistry.get_or_start(task_id, cdp_url)
    • browser_snapshot (tại ~line 1536): hợp nhất trạng thái giám sát vào tải trọng trả về
    • Trình xử lý /browser connect: khởi động lại trình giám sát với điểm cuối mới
    • Móc phân tách phiên trong _cleanup_browser_session
  • hermes_cli/config.py — thêm browser.dialog_policybrowser.dialog_timeout_s vào DEFAULT_CONFIG
  • Tài liệu: website/docs/user-guide/features/browser.md, website/docs/reference/tools-reference.md, website/docs/reference/toolsets-reference.md

Không có mục tiêu

  • Phát hiện/tương tác với Camofox (khoảng cách ngược dòng; được theo dõi riêng)
  • Truyền trực tiếp các sự kiện hộp thoại/khung tới người dùng (sẽ yêu cầu móc nối cổng)
  • Lịch sử hộp thoại liên tục qua các phiên (chỉ trong bộ nhớ)
  • Chính sách hộp thoại trên mỗi khung nội tuyến (tác nhân có thể thể hiện điều này thông qua dialog_id)
  • Thay thế browser_cdp — nó vẫn là lối thoát cho phần đuôi dài (cookie, khung nhìn, điều chỉnh mạng)

##Thử nghiệm

Các bài kiểm tra đơn vị sử dụng máy chủ CDP giả asyncio để nói đủ về giao thức để thực hiện tất cả các chuyển đổi trạng thái: đính kèm, kích hoạt, điều hướng, kích hoạt hộp thoại, loại bỏ hộp thoại, đính kèm/tách khung, đính kèm mục tiêu con, phá bỏ phiên. E2E phụ trợ thực (Browserbase + Chrome cục bộ) là thủ công - tập thể dục qua /trình duyệt kết nối với Chrome trực tiếp và chạy các trường hợp kiểm tra hộp thoại/khung được mô tả ở trên.