OSWright

Automatización de escritorio de Windows para agentes de IA que mantiene un modelo de la pantalla entre acciones y relee solo las regiones que cambiaron, en lugar de devolver una captura de pantalla tras cada paso. En una tarea de entrada de datos de ocho campos, esto midió 25 122 → 3 404 tokens (7×), con el resultado verificado contra el estado propio de la aplicación mediante UI Automation. 43 herramientas. Instalación: uvx oswright

Documentación

OSWright

PyPI Tests Python License

Automatización de escritorio para agentes de IA, sin pagar por una captura de pantalla en cada paso.

mcp-name: io.github.Ask-812/oswright

Un servidor MCP que permite a un LLM controlar aplicaciones de escritorio reales — el equivalente de escritorio de Playwright MCP. Mantiene un modelo de la pantalla entre acciones y vuelve a leer solo las partes que cambiaron, por lo que el mismo trabajo cuesta un orden de magnitud menos de tokens.

OSWright transcribing an invoice into an expense form

Ocho campos leídos de una factura y escritos en un formulario de gastos, verificados por la aplicación misma. Misma tarea, mismo resultado, 7,4× menos contexto que devolver una captura de pantalla después de cada acción. Cada número en pantalla se mide durante la ejecución — regenera todo con python benchmarks/record_demo.py.

Por qué existe esto

La mayoría de los agentes GUI vuelven a percibir toda la pantalla en cada paso: captura de pantalla, OCR, entregar la imagen al modelo, repetir. Medido en un escritorio en vivo, la observación mediana cambia el 0,012% de los píxeles de la pantalla. Volver a leer todo hace mucho más trabajo del que justifica el cambio, y cobra ~2.800 tokens de imagen tanto si ocurrió algo como si no.

OSWright pregunta al compositor qué cambió, vuelve a escanear solo eso, y responde las búsquedas de elementos desde la fuente más barata que pueda. Las afirmaciones a continuación se miden en esta máquina y son reproducibles desde benchmarks/ — incluyendo las que no resultaron a su favor.

Características clave

  • Multiplataforma. Windows (API Win32), Linux (pynput/X11), macOS (pynput/Quartz).
  • Árbol de accesibilidad. Encuentra elementos de forma determinista por rol y nombre mediante Windows UI Automation — 100% preciso, instantáneo, sin necesidad de modelo.
  • OCR rápido. OCR de Windows (integrado, instantáneo) con respaldo EasyOCR para Linux/macOS. Los resultados se almacenan en caché automáticamente.
  • Ligero en Windows. Sin descarga de PyTorch — Windows usa el motor OCR integrado, por lo que una instalación completa son unos pocos MB en lugar de unos pocos GB.
  • Coincidencia de imágenes. Localiza elementos mediante imagen de plantilla con OpenCV.
  • Gestión de ventanas. Listar, enfocar, minimizar, cerrar y capturar ventanas específicas.
  • Diferenciación de capturas. Detecta cuándo cambia la pantalla con wait_for_change.
  • Acceso al portapapeles. Lee y escribe el portapapeles del sistema para transferencia de datos.
  • Lanzador de aplicaciones. Inicia aplicaciones y espera a que se carguen.
  • Captura automática. Cada acción devuelve una captura de pantalla para que el agente siempre vea el estado actual.
  • 43 herramientas MCP. Pantalla, OCR, UIA, ratón, teclado, ventanas, portapapeles y acciones compuestas.
  • Percepción incremental. Vuelve a escanear solo las partes de la pantalla que cambiaron, y puede devolver qué cambió en lugar de una captura completa — ~21× menos tokens por paso.
  • Memoria de pantalla. Reconoce pantallas que ya ha leído antes y las reutiliza, verificadas por píxeles — 89× más barato que volver a leer.
  • Percepción especulativa. Aprende qué hacen las acciones y confirma el resultado esperado en lugar de volver a leer — 19–23× más barato, con un informe surprise cuando la interfaz hace algo inesperado.
  • Espera adaptativa. Espera a que la pantalla realmente se estabilice en lugar de dormir un fijo de 300 ms — 11,9 s ahorrados en una tarea de 50 pasos.
  • Cascada de resolución. Las búsquedas de elementos se detienen en el método más barato que funcione; las búsquedas repetidas cuestan ~0,05 ms.
  • Correcto con DPI. Las coordenadas son píxeles físicos en todas partes, por lo que los clics aterrizan correctamente en pantallas escaladas.
  • Suite de pruebas. 259 pruebas automatizadas; las que manejan el escritorio se omiten cuando no hay pantalla disponible.

Requisitos

  • Python 3.10 o más reciente
  • VS Code, Cursor, Windsurf, Claude Desktop, o cualquier otro cliente MCP

Primeros pasos

Primero, instala el servidor MCP de OSWright con tu cliente.

Configuración estándar funciona en la mayoría de las herramientas:

{
  "mcpServers": {
    "oswright": {
      "command": "uvx",
      "args": ["oswright"]
    }
  }
}

Nota: Si no tienes uvx, puedes usar pip install oswright y luego establecer "command": "oswright" directamente.

Claude Desktop

Sigue la guía de instalación de MCP, usa la configuración estándar anterior.

Claude Code
claude mcp add oswright uvx oswright
VS Code

Añade a tu settings.json de usuario o espacio de trabajo bajo mcp.servers:

{
  "mcp": {
    "servers": {
      "oswright": {
        "command": "uvx",
        "args": ["oswright"]
      }
    }
  }
}

O usa la CLI de VS Code:

code --add-mcp '{"name":"oswright","command":"uvx","args":["oswright"]}'
Cursor

Ve a Cursor Settings -> MCP -> Add new MCP Server. Nómbralo oswright, usa el tipo command con el comando uvx oswright.

Windsurf

Sigue la documentación de MCP de Windsurf. Usa la configuración estándar anterior.

Cline

Añade a tu cline_mcp_settings.json:

{
  "mcpServers": {
    "oswright": {
      "type": "stdio",
      "command": "uvx",
      "args": ["oswright"],
      "disabled": false
    }
  }
}
Goose

Ve a Advanced settings -> Extensions -> Add custom extension. Nómbralo oswright, usa el tipo STDIO, y establece el command a uvx oswright.

Usando pip en lugar de uvx

Si prefieres una instalación estándar con pip:

pip install oswright

Luego usa esta configuración:

{
  "mcpServers": {
    "oswright": {
      "command": "oswright"
    }
  }
}

O ejecuta directamente:

python -m oswright

Habilidad del agente

skills/oswright/SKILL.md le dice a un agente cuándo recurrir a OSWright y cuáles herramientas son las baratas. Sigue el formato abierto de Agent Skills. Copia la carpeta skills/oswright al directorio de habilidades de tu agente — ~/.claude/skills/ para Claude Code, o el equivalente para tu cliente.

La habilidad establece --observation-mode delta. Sin ella, cada acción devuelve una captura de pantalla completa, que es el costo que OSWright existe para evitar.

Percepción incremental

La mayoría de los agentes GUI vuelven a percibir toda la pantalla en cada paso: captura completa, OCR completo, y luego entregan al modelo una imagen nueva. Medido en un escritorio en vivo, la observación mediana cambia el 0,012% de los píxeles — por lo que un rescan completo hace aproximadamente 240× más trabajo del que justifica el cambio, y la captura que devuelve cuesta ~2.800 tokens de imagen tanto si ocurrió algo como si no.

OSWright mantiene un modelo de la pantalla entre observaciones y vuelve a escanear solo las regiones que realmente se movieron.

observe()  ->  {"changed": true,
                "added":   [{"text": "Saved", "x": 812, "y": 447}],
                "removed": ["Unsaved changes"],
                "screen_fraction_scanned": 0.015}

Medido en esta máquina durante un bucle de agente de 14 pasos:

v0.4.0 (OCR completo + captura)incremental
Latencia mediana por paso212 ms33 ms
Tokens por observación~2.764~49
Tokens en 14 pasos38.6961.025
Relectura de pantalla100%16%

Cuanto más ocupada está la pantalla, mayor es la brecha: el OCR completo escala con cuánto texto hay en pantalla, mientras que la ruta incremental escala con cuánto cambió. La misma comparación mide 6,5× en un escritorio tranquilo y 14,3× con una página web densa abierta. Vuelve a medir con benchmarks/ en lugar de confiar en estos números.

El costo es un proxy, sin embargo, y una ruta de percepción más barata que degradara silenciosamente la precisión sería peor que ninguna. Por eso se verifica contra la finalización de tareas: tareas con script que manejan la superficie real de la herramienta en cuatro aplicaciones, evaluadas contra el estado propio de cada aplicación — UI Automation para Calculadora y Explorador, el título de la ventana para Chrome y VS Code — nunca contra OCR.

configuraciónCalculadoraExplorador de archivosChrometokens
estilo v0.4 (captura completa)9/93/33/3118.858
solo delta9/93/33/35.252
delta + memoria9/93/33/35.099
delta + memoria + predicción9/93/33/37.981

La precisión es idéntica en todas las configuraciones mientras el costo en tokens cae 23×. Ejecútalo con python benchmarks/bench_tasks.py.

Por qué tanto píxeles como accesibilidad

El diseño apuesta a que ninguna ruta de percepción gana en todas partes. Apagar cada mitad mide eso en lugar de afirmarlo:

configuraciónCalculadoraExplorador de archivosChrome
cascada completa9/93/33/3
solo accesibilidad9/90/30/3
solo píxeles6/93/33/3

Solo accesibilidad — la postura que toman la mayoría de los agentes GUI de Windows — es perfecta en XAML y ciega en una vista de lista Win32 y en contenido web. Probado contra VS Code ve 18 elementos, siendo todo el IDE un único nodo llamado Chrome Legacy Window, mientras que el OCR lee 94 incluyendo cada nombre de archivo.

Solo píxeles falla en los botones de la Calculadora, porque el botón que un humano lee como 7 se llama Seven, y el OCR de Windows no devuelve dígitos de la Calculadora en absoluto.

La cascada es la única configuración que pasa en todas partes.

La cascada de resolución

find_element y click_element se detienen en el primer método que puede responder, por lo que el costo sigue cuán novedosa es la solicitud en lugar de cuán grande es la pantalla:

EscalónMétodoCosto típico
0Ya está en el modelo de pantalla~0,05 ms
1Volver a escanear solo lo que cambió~70 ms
2Árbol de accesibilidad (sabe que un Botón es un botón)~40 ms
3Búfer de texto propio de la app mediante UIA TextPattern — caracteres exactos~400 ms
4OCR de pantalla completa~250 ms

Buscar texto que el modelo ya conoce es ~5.000× más barato que la ruta v0.4.0 (0,05 ms frente a 244 ms). La respuesta informa qué escalón respondió, para que puedas ver qué está costando realmente una tarea.

El escalón 3 vale la pena entenderlo: el TextRange.FindText de UIA busca en el búfer de texto propio de la aplicación y devuelve rectángulos delimitadores exactos. Es inmune a fuente, DPI, antialiasing y error de OCR. Está debajo de los escalones de píxeles solo porque escanear los controles de una ventana cuesta unos cientos de milisegundos de COM entre procesos — es el escalón preciso, no el rápido.

Nota sobre el orden. Estos escalones están ordenados por medición, no por teoría. El consejo común es hacer primario el árbol de accesibilidad, pero en aplicaciones reales no siempre es más barato: recorrer el árbol de Chrome tomó 537 ms aquí, más lento que un pase de OCR de pantalla completa, y VS Code expuso solo 18 elementos. Ni los píxeles ni la accesibilidad ganan en todas partes, por eso esto es una cascada en lugar de una elección.

Preguntar al compositor en lugar de mirar

En Windows, el compositor del escritorio ya sabe qué píxeles cambiaron y los expone mediante DXGI Desktop Duplication. Preguntarle cuesta 0,14 ms y no transfiere píxeles, frente a decenas de milisegundos para capturar un fotograma y descubrir que era idéntico — por lo que una observación inactiva omite la captura por completo.

Cuando algo ha cambiado, el compositor se queda con ese fotograma, por lo que sus píxeles se leen directamente de la GPU en lugar de capturarse una segunda vez mediante una API diferente — 1,5–2,3× más rápido que mss en las mediciones aquí.

El compositor se usa solo como un negativo rápido para la detección de cambios. Cuando informa un cambio, las regiones sucias aún provienen del hash del fotograma capturado: los dos se miden en intervalos ligeramente diferentes, por lo que los rectángulos del compositor pueden subestimar en relación con los píxeles realmente capturados, y una región subestimada es texto que nunca se vuelve a leer. Degrada silenciosamente al hash de mosaicos y a la captura normal donde Desktop Duplication no esté disponible.

Habilita las observaciones delta para las herramientas de acción con --observation-mode delta. El valor predeterminado sigue siendo screenshot para compatibilidad con clientes existentes.

Reproduce todo esto tú mismo: consulta benchmarks/. El razonamiento detrás de cada decisión, incluidos los callejones sin salida, está en docs/ENGINEERING_LOG.md.

Configuración

El servidor MCP de OSWright admite los siguientes argumentos. Se pueden proporcionar en la configuración JSON como parte de la lista "args":

OpciónDescripciónVariable de entorno
--port <port>Puerto para transporte SSE. Si se omite, usa stdio (predeterminado).FASTMCP_PORT
--host <host>Host al que vincular el servidor HTTP/SSE. Predeterminado: 127.0.0.1.FASTMCP_HOST
--transport <mode>Protocolo de transporte: stdio, sse, streamable-http. Se detecta automáticamente desde --port.
--ocr-languages <langs>Idiomas de OCR (predeterminado: en). Ejemplo: --ocr-languages en es frOSWRIGHT_OCR_LANGUAGES
--timeout <seconds>Tiempo de espera predeterminado para operaciones de espera automática (predeterminado: 10).OSWRIGHT_TIMEOUT
--snapshot-max-width <px>Reducir la escala de la captura automática devuelta después de cada acción. 0 (predeterminado) mantiene la resolución completa. Valores más bajos reducen significativamente el costo de tokens.OSWRIGHT_SNAPSHOT_MAX_WIDTH
--observation-mode <mode>Qué devuelven las herramientas de acción: screenshot (predeterminado), delta (solo lo que cambió, ~30× menos tokens), o both.OSWRIGHT_OBSERVATION_MODE
--no-atlasNo recordar pantallas entre visitas.OSWRIGHT_NO_ATLAS
--no-speculateNo predecir el resultado de las acciones.OSWRIGHT_NO_SPECULATE
--allow-remoteRequerido para vincular una dirección que no sea de bucle local. Ver Seguridad.
--log-level <level>Nivel de registro: DEBUG, INFO, WARNING, ERROR. Predeterminado: INFO.OSWRIGHT_LOG_LEVEL

Una bandera explícita de línea de comandos siempre tiene prioridad sobre la variable de entorno correspondiente.

Ejemplo: OCR multilingüe

{
  "mcpServers": {
    "oswright": {
      "command": "uvx",
      "args": ["oswright", "--ocr-languages", "en", "es", "fr"]
    }
  }
}

Servidor MCP independiente (SSE)

Cuando se ejecuta desde un proceso de trabajo u otra máquina, use transporte SSE:

uvx oswright --port 8931

Luego, en la configuración de su cliente MCP:

{
  "mcpServers": {
    "oswright": {
      "url": "http://127.0.0.1:8931/sse"
    }
  }
}

Seguridad

OSWright no tiene autenticación. Cualquiera que pueda alcanzar el puerto obtiene control total de teclado, mouse, pantalla y portapapeles de la máquina: es una toma de control remota del escritorio, no una API aislada.

Por lo tanto, el servidor se vincula a 127.0.0.1 de forma predeterminada y se niega a iniciarse en una dirección que no sea de bucle local a menos que pase --allow-remote. Para alcanzarlo desde otra máquina, prefiera un túnel SSH sobre exponer el puerto:

ssh -L 8931:127.0.0.1:8931 user@desktop-host

El transporte Stdio (el predeterminado, usado por cada configuración de cliente MCP anterior) no está expuesto a la red en absoluto y es la forma recomendada de ejecutar OSWright.

Las herramientas que pueden destruir trabajo están anotadas en consecuencia: close_window está marcada como destructiva, y launch_app inicia programas arbitrarios. Las herramientas de captura de pantalla se niegan a sobrescribir un save_path existente.

Notas de plataforma

PlataformaBackend de entradaBackend de OCRDescargas adicionales
WindowsAPI Win32 (SendInput)OCR de Windows (instantáneo, integrado)Ninguna. Sin PyTorch. UI Automation incluida.
Linuxpynput (X11)EasyOCRPyTorch (~2.5 GB). Requiere X11; Wayland tiene soporte limitado.
macOSpynput (Quartz)EasyOCRPyTorch (~2.5 GB). Conceda permisos de Accesibilidad en Configuración del Sistema > Privacidad > Accesibilidad.

En Windows, EasyOCR no está instalado, porque el motor OCR integrado de Windows es más rápido y no necesita descarga de modelos. Instálelo solo si necesita un idioma que el OCR de Windows no soporta:

pip install "oswright[easyocr]"

Coordenadas

Todas las coordenadas devueltas por OCR, coincidencia de imágenes y UI Automation son píxeles físicos absolutos de pantalla, listos para pasar directamente a mouse_click. Esto se aplica a subregiones y a configuraciones de múltiples monitores donde el escritorio virtual comienza en un origen negativo. screenshot también informa origin_x/origin_y, la posición absoluta del píxel superior izquierdo de la imagen, para cuando lea una coordenada de la imagen usted mismo.

Herramientas

Pantalla
  • screenshot -- Toma una captura de pantalla de la pantalla o una región. Devuelve la imagen como contenido de imagen MCP nativo. Opcionalmente guarda en una ruta de archivo.

    • Solo lectura: true
  • get_screen_info -- Obtiene las dimensiones de la pantalla y el número de monitores.

    • Solo lectura: true
OCR / Búsqueda de texto
  • find_text_on_screen -- Encuentra todas las apariciones de texto en pantalla usando OCR. Devuelve coincidencias con coordenadas y confianza.

    • Parámetros: text, exact, límites de región, monitor
    • Solo lectura: true
  • read_screen_text -- Lee TODO el texto visible en la pantalla usando OCR. Devuelve cada elemento de texto detectado con su posición.

    • Parámetros: límites de región, monitor
    • Solo lectura: true
Coincidencia de imágenes
  • find_image_on_screen -- Encuentra todas las apariciones de una imagen plantilla en pantalla usando coincidencia de plantillas de OpenCV.
    • Parámetros: template_path, threshold, monitor
    • Solo lectura: true
Mouse
  • mouse_click -- Hace clic con el mouse en coordenadas o en la posición actual. Devuelve una captura de pantalla.

    • Parámetros: x, y, button, clicks
  • mouse_double_click -- Doble clic en coordenadas o en la posición actual. Devuelve una captura de pantalla.

  • mouse_move -- Mueve el cursor del mouse a coordenadas de pantalla.

  • mouse_scroll -- Desplaza la rueda del mouse. Devuelve una captura de pantalla.

    • Parámetros: amount, x, y
  • mouse_drag -- Arrastra de un punto a otro. Devuelve una captura de pantalla.

    • Parámetros: start_x, start_y, end_x, end_y, button, duration
  • get_mouse_position -- Obtiene la posición actual del cursor del mouse.

    • Solo lectura: true
Teclado
  • type_text -- Escribe texto carácter por carácter. Devuelve una captura de pantalla.

    • Parámetros: text, delay
  • press_key -- Presiona una tecla o combinación como Enter, Ctrl+C, Alt+Tab. Devuelve una captura de pantalla.

    • Parámetros: key
Acciones compuestas
  • click_text -- Encuentra texto mediante OCR y hace clic en él. Reintenta automáticamente hasta encontrarlo o agotar el tiempo de espera. Devuelve una captura de pantalla.

    • Parámetros: text, exact, button, timeout, poll_interval, monitor
  • double_click_text -- Encuentra texto mediante OCR y hace doble clic en él. Devuelve una captura de pantalla.

  • right_click_text -- Encuentra texto mediante OCR y hace clic derecho en él. Devuelve una captura de pantalla.

  • hover_text -- Encuentra texto mediante OCR y pasa el cursor sobre él. Devuelve una captura de pantalla.

  • fill_field -- Encuentra una etiqueta, hace clic en ella, la limpia y escribe un valor. Devuelve una captura de pantalla.

    • Parámetros: target_text, value, exact, timeout, monitor
  • fill_form -- Completa múltiples campos en una sola llamada. Reduce los viajes de ida y vuelta.

    • Parámetros: fields (lista de {label, value}), timeout, monitor
  • wait_for_text -- Espera a que aparezca texto en pantalla. Sondea mediante OCR.

    • Parámetros: text, exact, timeout, poll_interval, monitor
    • Solo lectura: true
  • wait_for_text_gone -- Espera a que el texto desaparezca de la pantalla.

    • Parámetros: text, exact, timeout, poll_interval, monitor
    • Solo lectura: true
  • wait_for_time -- Espera una duración especificada (máximo 30s), luego toma una captura de pantalla.

Gestión de ventanas
  • list_windows -- Lista todas las ventanas visibles. Opcionalmente filtra por subcadena del título.

    • Parámetros: title_filter
    • Solo lectura: true
  • focus_window -- Trae una ventana al primer plano por título. Devuelve una captura de pantalla.

    • Parámetros: title
  • close_window -- Cierra una ventana por título (envía WM_CLOSE). Devuelve una captura de pantalla.

    • Parámetros: title
  • minimize_window -- Minimiza una ventana por título. Devuelve una captura de pantalla.

    • Parámetros: title
  • screenshot_window -- Captura una captura de pantalla de solo una ventana.

    • Parámetros: title, save_path
    • Solo lectura: true
Portapapeles
  • get_clipboard -- Obtiene el contenido de texto actual del portapapeles del sistema.

    • Solo lectura: true
  • set_clipboard -- Copia texto al portapapeles del sistema.

    • Parámetros: text
Gestión de aplicaciones
  • launch_app -- Lanza una aplicación y opcionalmente espera a que cargue. Ejecuta el programa directamente, nunca a través de un shell.

    • Parámetros: command, args, wait_text, timeout
    • Informa wait_text_found para que pueda saber si la aplicación realmente cargó.
  • get_ocr_info -- Obtiene información sobre el backend de OCR activo y los backends disponibles.

    • Solo lectura: true
Percepción incremental
  • observe -- Informa qué cambió en pantalla desde la última observación. Vuelve a escanear solo las regiones que se movieron. Prefiera esto sobre screenshot para rastrear el estado.

    • Parámetros: force_full
    • Solo lectura: true
  • find_element -- Encuentra texto en pantalla usando el método más barato que pueda responder. Informa qué nivel de la cascada respondió.

    • Parámetros: text, exact, window_title
    • Solo lectura: true
  • click_element -- Encuentra texto mediante la cascada y hace clic en él. La alternativa barata a click_text.

    • Parámetros: text, exact, button, window_title
  • read_model_text -- Lee texto en pantalla del modelo incremental sin volver a hacer OCR de la pantalla.

    • Parámetros: query, limit
    • Solo lectura: true
  • perception_stats -- Informa cuánto trabajo de percepción ha evitado el modelo.

    • Solo lectura: true
  • remember_screen -- Recuerda la pantalla actual para que futuras visitas omitan leerla. Persiste entre sesiones.

    • Solo lectura: true
  • atlas_stats -- Informa qué ha recordado el atlas de pantalla y con qué frecuencia ayudó.

    • Solo lectura: true
Accesibilidad / UI Automation (Windows)
  • get_ui_tree -- Obtiene el árbol de accesibilidad de la ventana enfocada. Devuelve todos los elementos interactivos con nombres, tipos, posiciones. Determinista e instantáneo.

    • Parámetros: window_title, max_depth
    • Solo lectura: true
  • click_ui_element -- Hace clic en un elemento de UI usando el árbol de accesibilidad. Más confiable que OCR.

    • Parámetros: name, control_type, automation_id, window_title
  • fill_ui_element -- Establece el valor de un elemento de UI (por ejemplo, cuadro de texto). Más confiable que el relleno basado en OCR.

    • Parámetros: value, name, automation_id, window_title
Pantalla avanzada
  • get_active_window -- Obtiene información sobre la ventana actualmente enfocada.

    • Solo lectura: true
  • wait_for_change -- Espera a que la pantalla cambie visualmente. Toma una captura de referencia, sondea hasta que sea diferente.

    • Parámetros: timeout, poll_interval

Biblioteca de Python

OSWright también funciona como una biblioteca de Python independiente con una API estilo Playwright:

from oswright import OSWright

with OSWright() as ow:
    screen = ow.screen()
    screen.click(text="Start")
    screen.type_text("Hello World")
    screen.press("Ctrl+S")
    screen.screenshot("desktop.png")

See the examples/ directory for more.

Arquitectura

oswright/
  __init__.py          # Package entry point (single source of __version__)
  core.py              # OSWright class (= Browser)
  screen.py            # Screen class (= Page)
  locator.py           # Locator + Assertions (= Locator + expect)
  capture.py           # Screen capture (mss - cross-platform, thread-safe)
  dirty.py             # Change detection - which parts of the screen moved
  screenmodel.py       # Persistent screen model, updated incrementally
  cascade.py           # Resolution cascade - cheapest method that can answer
  atlas.py             # Remembers screens across visits and sessions
  settle.py            # Knowing when the screen has finished responding
  speculate.py         # Predicting what an action does, instead of looking
  textprovider.py      # Exact text from the app itself via UIA TextPattern
  detect.py            # OCR dispatcher with caching (auto-selects best backend)
  _ocr_windows.py      # Windows OCR backend (instant, built-in)
  accessibility.py     # Windows UI Automation (deterministic element finding)
  cache.py             # Screenshot diffing, image hashing, OCR result cache
  _dpi.py              # Process DPI awareness (keeps every API in physical pixels)
  _dxgi_windows.py     # Compositor dirty rectangles via DXGI Desktop Duplication
  input.py             # Platform dispatcher for input backends
  _input_windows.py    # Windows input backend (Win32 API)
  _input_pynput.py     # Linux/macOS input backend (pynput)
  window.py            # Window management (list, focus, close)
  clipboard.py         # Clipboard read/write (cross-platform)
  mcp_server.py        # MCP server (43 tools for AI agents)
tests/
  conftest.py          # Fixtures that skip when no display/OCR is available
  test_core.py         # Unit tests (no desktop required)
  test_perception.py   # Incremental perception (stubbed, runs headless)
  test_atlas.py        # Screen memory and its failure modes (headless)
  test_speculate.py    # Prediction, settling, and their limits (headless)
  test_e2e.py          # End-to-end tests against the real desktop (marked `e2e`)

Recordando pantallas

Las aplicaciones son deterministas: el mismo diálogo tiene el mismo diseño cada vez. OSWright recuerda las pantallas que ha leído y las reutiliza en la siguiente visita, entre sesiones: 125 ms de lectura en frío → 1.4 ms de recuperación en caliente (89×).

Una pantalla recordada nunca se confía solo en el reconocimiento. Algunas regiones se verifican por píxeles antes de reutilizar el diseño, de modo que una pantalla que ha cambiado se rechaza en lugar de actuar sobre ella. La verificación falla de forma segura: una pantalla sin nada comprobable no se recuerda en absoluto.

Desactivar con --no-atlas. Las pantallas recordadas viven en ~/.oswright/atlas.json.

Prediciendo acciones en lugar de observarlas

Las aplicaciones son deterministas: hacer clic en Guardar produce el mismo diálogo cada vez, así que después de la primera observación el resultado de una acción ya se conoce. OSWright aprende qué hacen las acciones y confirma la pantalla esperada en lugar de leerla de nuevo: 19–23× más barato que observar (2.3 ms frente a 43–50 ms).

Una predicción debe verse dos veces antes de confiarse, se retira si resulta incorrecta y se verifica de las mismas dos maneras que una pantalla recordada. Una predicción fallida se informa al agente como un surprise: la interfaz hizo algo que no hace normalmente, lo cual vale la pena saber en lugar de absorberlo en silencio.

Lo que garantiza una predicción confirmada: el diseño: los mismos controles en los mismos lugares. No que cada carácter sea idéntico. Un solo dígito cambiado altera menos píxeles que un cursor parpadeante, así que ninguna verificación de pantalla completa puede separarlos a ninguna resolución. Usa observe(force_full=True) cuando el texto exacto importe.

Desactivar con --no-speculate.

Esperando solo lo necesario

Las herramientas de acción solían dormir un fijo de 300 ms, elegido para el caso más lento, así que cada acción pagaba el peor caso. El compositor sabe cuándo la pantalla deja de cambiar, así que la espera ahora termina cuando la interfaz realmente se asienta:

Sueño fijo anterior300 ms
Espera real mediana61.5 ms
Ahorrado en una tarea de 50 pasos11.9 s

"Asentado" significa sin cambio grande reciente, no sin cambio: un escritorio real nunca está quieto: un cursor y un reloj producen un evento de cambio cada ~18 ms que cubre alrededor de 32 píxeles, mientras que los cambios genuinos de interfaz cubren decenas de miles.

Pendiente

  • Inyección de entrada en Wayland, y macOS AXTextMarker como equivalente de TextPattern.
  • Un nivel de modelo de visión para superficies que no son ni accesibles ni legibles como texto: juegos, lienzos, editores de imágenes.
  • Transiciones basadas en más que la pantalla anterior, para acciones cuyo resultado depende de un estado que no es visible.

Qué se mide y qué no

El costo de percepción y el éxito de la tarea se miden ambos en esta máquina y son reproducibles mediante benchmarks/: en cuatro aplicaciones, una percepción más barata no cuesta precisión, y la división píxeles/accesibilidad se mide en lugar de argumentarse.

Contra Windows-MCP

Mismas tareas, cuatro escenarios, cada uno calificado por la propia aplicación. Ninguna herramienta se califica a sí misma, y Windows-MCP se ejecuta con sus propios valores predeterminados:

CalculadoraExploradorChromeChrome, 2 pasosaprobadostokens
oswright5/54/55/55/519/20832
Windows-MCP, captura por acción5/55/55/55/520/2014,053
Windows-MCP, captura una vez5/55/55/55/520/208,214

Léelo con honestidad: Windows-MCP fue más confiable, y oswright fue 16.9x más barato. oswright falló un clic de veinte, en una ventana que acababa de abrirse.

La diferencia de costo es estructural, no un ajuste de sintonía. Windows-MCP devuelve la pantalla al agente -- Snapshot renderiza el árbol de accesibilidad como (x,y) button "Seven" [action: click] -- y toma las coordenadas de vuelta, así que una descripción de la pantalla se carga en el contexto del modelo en cada acción. oswright toma el texto y devuelve el resultado.

La brecha de confiabilidad puede estar causada por la velocidad: oswright resuelve y hace clic en ~100 ms, a veces antes de que una ventana recién enfocada esté lista para entrada, donde un bucle más lento le da a la aplicación el tiempo que nunca tuvo que pedir. Eso es una hipótesis, no un hallazgo: agregar un asentamiento previo a la acción no hizo una diferencia medible en diez pruebas, así que se registra en lugar de arreglarse.

El escenario Chrome, 2 steps existe porque cada otra tarea aquí es lo suficientemente corta como para que una herramienta pueda leer la pantalla una vez y reutilizar esas coordenadas. Allí el primer clic mueve los controles 325 px hacia abajo en la página, y la configuración de captura única tuvo que releer la pantalla -- así que en tareas cuya interfaz se mueve, su número barato no existe y su costo real es el de por acción.

Reproduce con python benchmarks/bench_head_to_head.py (configuración en el docstring del archivo).

Lo que esto no establece: cuatro tareas cortas en una laptop. Nada sobre trabajo largo de múltiples pasos, recuperación o madurez del producto: Windows-MCP tiene OAuth, analítica, un vigilante y un instalador; oswright no tiene ninguno de esos. Su recorrido de accesibilidad también lee el contenido de la página de Chrome, que el propio nivel de accesibilidad de oswright no hace. "Sustancialmente más barato por acción, a un pequeño costo de confiabilidad" es la afirmación. "Mejor producto" no lo es.

Desarrollo

pip install -e ".[dev]"

pytest tests/                # everything available on this machine
pytest tests/ -m "not e2e"   # unit tests only, no desktop needed
ruff check oswright tests    # lint
python benchmarks/bench_pipeline.py   # reproduce the performance numbers
python benchmarks/bench_tasks.py      # task success (opens Calculator repeatedly)

Las decisiones de diseño, mediciones y callejones sin salida se registran en docs/ENGINEERING_LOG.md.

Licencia

MIT