Plan de refactorización de Browser Evaluate con CDP

Contexto

act:evaluate ejecuta JavaScript proporcionado por el usuario en la página. Hoy se ejecuta mediante Playwright (page.evaluate o locator.evaluate). Playwright serializa los comandos CDP por página, por lo que un evaluate atascado o de larga duración puede bloquear la cola de comandos de la página y hacer que cada acción posterior en esa pestaña parezca “atascada”.

El PR #13498 agrega una red de seguridad pragmática (evaluate acotado, propagación de abort y recuperación best-effort). Este documento describe una refactorización mayor que hace que act:evaluate esté inherentemente aislado de Playwright para que un evaluate atascado no pueda bloquear las operaciones normales de Playwright.

Objetivos

  • act:evaluate no puede bloquear permanentemente acciones posteriores del navegador en la misma pestaña.
  • Los timeouts son una única fuente de verdad de extremo a extremo para que el llamador pueda confiar en un presupuesto.
  • El abort y el timeout se tratan de la misma manera en dispatch HTTP y en proceso.
  • Se soporta targeting de elementos para evaluate sin cambiar todo fuera de Playwright.
  • Mantener compatibilidad hacia atrás para llamadores y payloads existentes.

No objetivos

  • Reemplazar todas las acciones del navegador (click, type, wait, etc.) con implementaciones CDP.
  • Eliminar la red de seguridad existente introducida en el PR #13498 (sigue siendo un fallback útil).
  • Introducir nuevas capacidades inseguras más allá del gate browser.evaluateEnabled existente.
  • Agregar aislamiento de proceso (worker process/thread) para evaluate. Si seguimos viendo estados atascados difíciles de recuperar después de esta refactorización, es una idea de seguimiento.

Arquitectura propuesta

1. Propagación de deadline

Introducir un concepto de presupuesto único y derivar todo de él:

  • El llamador establece timeoutMs (o un deadline en el futuro).
  • El timeout de la solicitud exterior, la lógica del manejador de ruta y el presupuesto de ejecución dentro de la página usan todos el mismo presupuesto, con pequeño margen donde sea necesario para overhead de serialización.
  • El abort se propaga como un AbortSignal en todas partes para que la cancelación sea consistente.

2. Motor de evaluate separado (ruta CDP)

Agregar una implementación de evaluate basada en CDP que no comparta la cola de comandos por página de Playwright. La propiedad clave es que el transporte de evaluate es una conexión WebSocket separada y una sesión CDP separada adjunta al objetivo.

3. Historia de refs (targeting de elementos sin reescritura completa)

La parte difícil es el targeting de elementos. CDP necesita un handle DOM o backendDOMNodeId, mientras que hoy la mayoría de las acciones del navegador usan locators de Playwright basados en refs de snapshots.

Enfoque recomendado: mantener las refs existentes, pero adjuntar un ID resoluble por CDP opcional.

4. Mantener una ruta de recuperación de último recurso

Incluso con evaluate CDP, hay otras formas de bloquear una pestaña o una conexión. Mantener los mecanismos de recuperación existentes (terminar ejecución + desconectar Playwright) como último recurso.

Plan de testing

  • Tests unitarios:
    • Lógica de coincidencia (role, name, nth) entre refs de roles y nodos del árbol AX.
    • Comportamiento del helper de presupuesto (margen, matemáticas de tiempo restante).
  • Tests de integración:
    • El timeout de evaluate CDP retorna dentro del presupuesto y no bloquea la siguiente acción.
    • El abort cancela evaluate y dispara terminación best-effort.
  • Tests de contrato:
    • Asegurar que BrowserActRequest y BrowserActResponse siguen siendo compatibles.

Riesgos y mitigaciones

  • El mapeo es imperfecto:
    • Mitigación: mapeo best-effort, fallback a evaluate Playwright, y agregar herramientas de depuración.
  • Runtime.terminateExecution tiene efectos secundarios:
    • Mitigación: usar solo en timeout/abort y documentar el comportamiento en errores.
  • Overhead extra:
    • Mitigación: solo obtener el árbol AX cuando se solicitan snapshots, cachear por objetivo, y mantener la sesión CDP de corta duración.

Preguntas abiertas

  • ¿El nuevo motor debería ser configurable como playwright, cdp o auto?
  • ¿Queremos exponer un nuevo formato “nodeRef” para usuarios avanzados, o mantener solo ref?
  • ¿Cómo deberían participar los snapshots de frames y snapshots con alcance de selector en el mapeo AX?