OSWright

Automação de desktop Windows para agentes de IA que mantém um modelo da tela entre ações e relê apenas as regiões que mudaram, em vez de retornar uma captura de tela após cada etapa. Em uma tarefa de entrada de dados com oito campos, isso mediu 25.122 → 3.404 tokens (7×), com o resultado verificado contra o estado do próprio aplicativo via UI Automation. 43 ferramentas. Instalação: uvx oswright

Documentação

OSWright

PyPI Tests Python License

Automação de desktop para agentes de IA, sem pagar por uma captura de tela a cada etapa.

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

Um servidor MCP que permite a um LLM controlar aplicativos de desktop reais — o equivalente de desktop do Playwright MCP. Ele mantém um modelo da tela entre as ações e relê apenas as partes que mudaram, então o mesmo trabalho custa uma ordem de magnitude a menos em tokens.

OSWright transcribing an invoice into an expense form

Oito campos lidos de uma fatura e digitados em um formulário de despesas, verificados pelo próprio aplicativo. Mesma tarefa, mesmo resultado, 7,4× menos contexto do que retornar uma captura de tela após cada ação. Cada número na tela é medido durante a execução — regenere tudo com python benchmarks/record_demo.py.

Por que isso existe

A maioria dos agentes de GUI re-percebe a tela inteira a cada etapa: captura de tela, OCR, entrega a imagem ao modelo, repete. Medido em um desktop ativo, a observação mediana muda 0,012% dos pixels da tela. Reler tudo faz muito mais trabalho do que a mudança justifica, e cobra ~2.800 tokens de imagem, quer algo tenha acontecido ou não.

O OSWright pergunta ao compositor o que mudou, reescaneia apenas isso e responde consultas de elementos pela fonte mais barata que puder. As afirmações abaixo são medidas nesta máquina e reproduzíveis a partir de benchmarks/ — incluindo aquelas que não saíram a seu favor.

Principais recursos

  • Multiplataforma. Windows (API Win32), Linux (pynput/X11), macOS (pynput/Quartz).
  • Árvore de acessibilidade. Encontre elementos de forma determinística por função e nome via Windows UI Automation — 100% preciso, instantâneo, sem necessidade de modelo.
  • OCR rápido. OCR do Windows (integrado, instantâneo) com fallback para EasyOCR no Linux/macOS. Os resultados são armazenados em cache automaticamente.
  • Leve no Windows. Sem download de PyTorch — o Windows usa o mecanismo de OCR integrado, então uma instalação completa tem alguns MB em vez de alguns GB.
  • Correspondência de imagens. Localiza elementos por imagem de modelo via OpenCV.
  • Gerenciamento de janelas. Liste, foque, minimize, feche e capture telas de janelas específicas.
  • Comparação de capturas de tela. Detecte quando a tela muda com wait_for_change.
  • Acesso à área de transferência. Leia e escreva na área de transferência do sistema para transferência de dados.
  • Iniciador de aplicativos. Inicie aplicativos e aguarde o carregamento.
  • Captura automática. Cada ação retorna uma captura de tela para que o agente sempre veja o estado atual.
  • 43 ferramentas MCP. Tela, OCR, UIA, mouse, teclado, janelas, área de transferência e ações compostas.
  • Percepção incremental. Reescaneia apenas as partes da tela que mudaram e pode retornar o que mudou em vez de uma captura de tela completa — ~21× menos tokens por etapa.
  • Memória de tela. Reconhece telas que já leu antes e as reutiliza, verificadas por pixels — 89× mais barato do que ler novamente.
  • Percepção especulativa. Aprende o que as ações fazem e confirma o resultado esperado em vez de reler — 19–23× mais barato, com um relatório surprise quando a interface faz algo inesperado.
  • Espera adaptativa. Aguarda a tela realmente estabilizar em vez de dormir por 300 ms fixos — 11,9 s economizados em uma tarefa de 50 etapas.
  • Cascata de resolução. Consultas de elementos param no método mais barato que funciona; consultas repetidas custam ~0,05 ms.
  • Correto para DPI. As coordenadas são pixels físicos em todos os lugares, então os cliques aterrissam corretamente em telas com escala.
  • Suíte de testes. 259 testes automatizados; os que dirigem o desktop se pulam quando não há display disponível.

Requisitos

  • Python 3.10 ou mais recente
  • VS Code, Cursor, Windsurf, Claude Desktop ou qualquer outro cliente MCP

Começando

Primeiro, instale o servidor MCP OSWright com seu cliente.

Configuração padrão funciona na maioria das ferramentas:

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

Nota: Se você não tiver uvx, pode usar pip install oswright e depois definir "command": "oswright" diretamente.

Claude Desktop

Siga o guia de instalação do MCP, use a configuração padrão acima.

Claude Code
claude mcp add oswright uvx oswright
VS Code

Adicione ao seu settings.json de usuário ou espaço de trabalho sob mcp.servers:

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

Ou use a CLI do VS Code:

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

Vá para Cursor Settings -> MCP -> Add new MCP Server. Nomeie como oswright, use o tipo command com o comando uvx oswright.

Windsurf

Siga a documentação do MCP do Windsurf. Use a configuração padrão acima.

Cline

Adicione ao seu cline_mcp_settings.json:

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

Vá para Advanced settings -> Extensions -> Add custom extension. Nomeie como oswright, use o tipo STDIO e defina o command para uvx oswright.

Usando pip em vez de uvx

Se você preferir uma instalação padrão com pip:

pip install oswright

Então use esta configuração:

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

Ou execute diretamente:

python -m oswright

Habilidade do agente

skills/oswright/SKILL.md diz a um agente quando recorrer ao OSWright e quais ferramentas são as baratas. Ele segue o formato aberto Agent Skills. Copie a pasta skills/oswright para o diretório de habilidades do seu agente — ~/.claude/skills/ para Claude Code, ou o equivalente para seu cliente.

A habilidade define --observation-mode delta. Sem ela, cada ação retorna uma captura de tela completa, que é o custo que o OSWright existe para evitar.

Percepção incremental

A maioria dos agentes de GUI re-percebe a tela inteira a cada etapa: captura de tela completa, OCR completo, e então entrega uma imagem nova ao modelo. Medido em um desktop ativo, a observação mediana muda 0,012% dos pixels — então um reescaneamento completo faz aproximadamente 240× mais trabalho do que a mudança justifica, e a captura de tela que retorna custa ~2.800 tokens de imagem, quer algo tenha acontecido ou não.

O OSWright mantém um modelo da tela entre observações e reescaneia apenas as regiões que realmente se moveram.

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

Medido nesta máquina em um loop de agente de 14 etapas:

v0.4.0 (OCR completo + captura de tela)incremental
Latência mediana por etapa212 ms33 ms
Tokens por observação~2.764~49
Tokens em 14 etapas38.6961.025
Releitura de tela100%16%

Quanto mais movimentada a tela, maior a diferença: OCR completo escala com a quantidade de texto na tela, enquanto o caminho incremental escala com quanto mudou. A mesma comparação mede 6,5× em um desktop tranquilo e 14,3× com uma página web densa aberta. Re-meça com benchmarks/ em vez de confiar nesses números.

Custo é um proxy, no entanto, e um caminho de percepção mais barato que silenciosamente degradasse a precisão seria pior do que nenhum. Então isso é verificado contra a conclusão de tarefas: tarefas scriptadas dirigindo a superfície real de ferramentas em quatro aplicativos, avaliadas contra o estado de cada aplicativo — UI Automation para Calculadora e Explorer, o título da janela para Chrome e VS Code — nunca contra OCR.

configuraçãoCalculadoraExplorador de ArquivosChrometokens
estilo v0.4 (captura de tela completa)9/93/33/3118.858
apenas delta9/93/33/35.252
delta + memória9/93/33/35.099
delta + memória + previsão9/93/33/37.981

A precisão é idêntica em todas as configurações enquanto o custo em tokens cai 23×. Execute com python benchmarks/bench_tasks.py.

Por que pixels e acessibilidade

O design aposta que nenhum caminho de percepção vence em todos os lugares. Desligar cada metade mede isso em vez de afirmar:

configuraçãoCalculadoraExplorador de ArquivosChrome
cascata completa9/93/33/3
apenas acessibilidade9/90/30/3
apenas pixels6/93/33/3

Apenas acessibilidade — a postura que a maioria dos agentes de GUI do Windows adota — é perfeita em XAML e cega em uma lista do Win32 e em conteúdo web. Testado contra o VS Code, ela vê 18 elementos, o IDE inteiro sendo um único nó chamado Chrome Legacy Window, enquanto o OCR lê 94 incluindo cada nome de arquivo.

Apenas pixels falha nos botões da Calculadora, porque o botão que um humano lê como 7 é nomeado Seven, e o OCR do Windows não retorna dígitos da Calculadora.

A cascata é a única configuração que passa em todos os lugares.

A cascata de resolução

find_element e click_element param no primeiro método que pode responder, então o custo acompanha o quão nova é a solicitação em vez de quão grande é a tela:

NívelMétodoCusto típico
0Já no modelo de tela~0,05 ms
1Reescaneia apenas o que mudou~70 ms
2Árvore de acessibilidade (sabe que um Botão é um botão)~40 ms
3Buffer de texto do próprio aplicativo via UIA TextPattern — caracteres exatos~400 ms
4OCR de tela inteira~250 ms

Consultar texto que o modelo já conhece é ~5.000× mais barato do que o caminho v0.4.0 (0,05 ms versus 244 ms). A resposta informa qual nível respondeu, então você pode ver o que uma tarefa está realmente custando.

O nível 3 vale a pena entender: o TextRange.FindText da UIA pesquisa o buffer de texto próprio do aplicativo e retorna retângulos delimitadores exatos. Ele é imune a fonte, DPI, antialiasing e erro de OCR. Ele fica abaixo dos níveis de pixels apenas porque escanear os controles de uma janela custa algumas centenas de milissegundos de COM entre processos — é o nível preciso, não o rápido.

Nota sobre a ordem. Esses níveis são ordenados por medição, não por teoria. O conselho comum é tornar a árvore de acessibilidade primária, mas em aplicativos reais ela nem sempre é mais barata: percorrer a árvore do Chrome levou 537 ms aqui, mais lento do que uma passada de OCR de tela inteira, e o VS Code expôs apenas 18 elementos a ela. Nem pixels nem acessibilidade vencem em todos os lugares, e é por isso que isso é uma cascata em vez de uma escolha.

Perguntando ao compositor em vez de olhar

No Windows, o compositor de desktop já sabe quais pixels mudaram e os expõe através do DXGI Desktop Duplication. Perguntar a ele custa 0,14 ms e não transfere pixels, contra dezenas de milissegundos para capturar um quadro e descobrir que era idêntico — então uma observação ociosa pula a captura completamente.

Quando algo mudou, o compositor fica segurando aquele quadro, então seus pixels são lidos diretamente da GPU em vez de capturados uma segunda vez através de uma API diferente — 1,5–2,3× mais rápido do que mss nas medições aqui.

O compositor é usado apenas como um negativo rápido para detecção de mudanças. Quando ele relata uma mudança, as regiões sujas ainda vêm do hash do quadro capturado: os dois são medidos em intervalos ligeiramente diferentes, então retângulos do compositor podem sub-relatar em relação aos pixels realmente capturados, e uma região sub-relatada é texto que nunca é relido. Ele degrada silenciosamente para hash de blocos e captura normal onde o Desktop Duplication não está disponível.

Habilite observações delta para ferramentas de ação com --observation-mode delta. O padrão permanece screenshot para compatibilidade com clientes existentes.

Reproduza tudo isso você mesmo: veja benchmarks/. O raciocínio por trás de cada decisão, incluindo os becos sem saída, está em docs/ENGINEERING_LOG.md.

Configuração

O servidor MCP OSWright suporta os seguintes argumentos. Eles podem ser fornecidos na configuração JSON como parte da lista "args":

OpçãoDescriçãoVariável de Ambiente
--port <port>Porta para transporte SSE. Se omitido, usa stdio (padrão).FASTMCP_PORT
--host <host>Host para vincular o servidor HTTP/SSE. Padrão: 127.0.0.1.FASTMCP_HOST
--transport <mode>Protocolo de transporte: stdio, sse, streamable-http. Detectado automaticamente a partir de --port.
--ocr-languages <langs>Idiomas de OCR (padrão: en). Exemplo: --ocr-languages en es frOSWRIGHT_OCR_LANGUAGES
--timeout <seconds>Tempo limite padrão para operações de espera automática (padrão: 10).OSWRIGHT_TIMEOUT
--snapshot-max-width <px>Reduz a escala do snapshot automático retornado após cada ação. 0 (padrão) mantém a resolução total. Valores menores reduzem significativamente o custo de tokens.OSWRIGHT_SNAPSHOT_MAX_WIDTH
--observation-mode <mode>O que as ferramentas de ação retornam: screenshot (padrão), delta (apenas o que mudou, ~30× menos tokens), ou both.OSWRIGHT_OBSERVATION_MODE
--no-atlasNão lembrar telas entre visitas.OSWRIGHT_NO_ATLAS
--no-speculateNão prever o resultado das ações.OSWRIGHT_NO_SPECULATE
--allow-remoteNecessário para vincular um endereço não-loopback. Veja Segurança.
--log-level <level>Nível de registro: DEBUG, INFO, WARNING, ERROR. Padrão: INFO.OSWRIGHT_LOG_LEVEL

Uma flag explícita de linha de comando sempre vence a variável de ambiente correspondente.

Exemplo: OCR multilíngue

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

Servidor MCP autônomo (SSE)

Ao executar a partir de um processo de trabalho ou outra máquina, use transporte SSE:

uvx oswright --port 8931

Em seguida, na configuração do seu cliente MCP:

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

Segurança

OSWright não possui autenticação. Qualquer pessoa que alcance a porta obtém controle total de teclado, mouse, tela e área de transferência da máquina — é uma tomada de controle remoto da área de trabalho, não uma API isolada.

O servidor, portanto, vincula-se a 127.0.0.1 por padrão e recusa-se a iniciar em um endereço não-loopback, a menos que você passe --allow-remote. Para alcançá-lo a partir de outra máquina, prefira um túnel SSH a expor a porta:

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

O transporte Stdio (o padrão, usado por todas as configurações de cliente MCP acima) não é exposto à rede e é a forma recomendada de executar o OSWright.

Ferramentas que podem destruir trabalho são anotadas de acordo: close_window é marcada como destrutiva, e launch_app inicia programas arbitrários. Ferramentas de captura de tela recusam-se a sobrescrever um save_path existente.

Notas de Plataforma

PlataformaBackend de EntradaBackend de OCRDownloads extras
WindowsAPI Win32 (SendInput)OCR do Windows (instantâneo, integrado)Nenhum. Sem PyTorch. Automação de UI incluída.
Linuxpynput (X11)EasyOCRPyTorch (~2,5 GB). Requer X11; Wayland tem suporte limitado.
macOSpynput (Quartz)EasyOCRPyTorch (~2,5 GB). Conceda permissões de Acessibilidade em Configurações do Sistema > Privacidade > Acessibilidade.

No Windows, o EasyOCR não é instalado, porque o mecanismo de OCR integrado do Windows é mais rápido e não requer download de modelo. Instale-o apenas se precisar de um idioma que o OCR do Windows não suporta:

pip install "oswright[easyocr]"

Coordenadas

Todas as coordenadas retornadas por OCR, correspondência de imagens e Automação de UI são pixels físicos absolutos da tela, prontos para serem passados diretamente para mouse_click. Isso vale para sub-regiões e para configurações de múltiplos monitores onde a área de trabalho virtual começa em uma origem negativa. screenshot também relata origin_x/origin_y, a posição absoluta do pixel superior esquerdo da imagem, para quando você lê uma coordenada da própria imagem.

Ferramentas

Tela
  • screenshot -- Tira uma captura de tela da tela ou de uma região. Retorna a imagem como conteúdo de imagem MCP nativo. Opcionalmente, salva em um caminho de arquivo.

    • Somente leitura: true
  • get_screen_info -- Obtém dimensões da tela e número de monitores.

    • Somente leitura: true
OCR / Localização de Texto
  • find_text_on_screen -- Encontra todas as ocorrências de texto na tela usando OCR. Retorna correspondências com coordenadas e confiança.

    • Parâmetros: text, exact, limites da região, monitor
    • Somente leitura: true
  • read_screen_text -- Lê TODO o texto visível na tela usando OCR. Retorna cada elemento de texto detectado com posição.

    • Parâmetros: limites da região, monitor
    • Somente leitura: true
Correspondência de Imagens
  • find_image_on_screen -- Encontra todas as ocorrências de uma imagem modelo na tela usando correspondência de modelos OpenCV.
    • Parâmetros: template_path, threshold, monitor
    • Somente leitura: true
Mouse
  • mouse_click -- Clica o mouse em coordenadas ou na posição atual. Retorna captura de tela.

    • Parâmetros: x, y, button, clicks
  • mouse_double_click -- Duplo clique em coordenadas ou na posição atual. Retorna captura de tela.

  • mouse_move -- Move o cursor do mouse para coordenadas da tela.

  • mouse_scroll -- Rola a roda do mouse. Retorna captura de tela.

    • Parâmetros: amount, x, y
  • mouse_drag -- Arrasta de um ponto a outro. Retorna captura de tela.

    • Parâmetros: start_x, start_y, end_x, end_y, button, duration
  • get_mouse_position -- Obtém a posição atual do cursor do mouse.

    • Somente leitura: true
Teclado
  • type_text -- Digita texto caractere por caractere. Retorna captura de tela.

    • Parâmetros: text, delay
  • press_key -- Pressiona uma tecla ou combinação como Enter, Ctrl+C, Alt+Tab. Retorna captura de tela.

    • Parâmetros: key
Ações Compostas
  • click_text -- Encontra texto via OCR e clica nele. Repete automaticamente até encontrar ou atingir o tempo limite. Retorna captura de tela.

    • Parâmetros: text, exact, button, timeout, poll_interval, monitor
  • double_click_text -- Encontra texto via OCR e dá duplo clique nele. Retorna captura de tela.

  • right_click_text -- Encontra texto via OCR e clica com o botão direito nele. Retorna captura de tela.

  • hover_text -- Encontra texto via OCR e passa o mouse sobre ele. Retorna captura de tela.

  • fill_field -- Encontra um rótulo, clica nele, limpa e digita um valor. Retorna captura de tela.

    • Parâmetros: target_text, value, exact, timeout, monitor
  • fill_form -- Preenche vários campos em uma única chamada. Reduz idas e voltas.

    • Parâmetros: fields (lista de {label, value}), timeout, monitor
  • wait_for_text -- Espera o texto aparecer na tela. Consulta via OCR.

    • Parâmetros: text, exact, timeout, poll_interval, monitor
    • Somente leitura: true
  • wait_for_text_gone -- Espera o texto desaparecer da tela.

    • Parâmetros: text, exact, timeout, poll_interval, monitor
    • Somente leitura: true
  • wait_for_time -- Espera por uma duração especificada (limitada a 30s), depois tira captura de tela.

Gerenciamento de Janelas
  • list_windows -- Lista todas as janelas visíveis. Opcionalmente, filtra por substring do título.

    • Parâmetros: title_filter
    • Somente leitura: true
  • focus_window -- Traz uma janela para o primeiro plano pelo título. Retorna captura de tela.

    • Parâmetros: title
  • close_window -- Fecha uma janela pelo título (envia WM_CLOSE). Retorna captura de tela.

    • Parâmetros: title
  • minimize_window -- Minimiza uma janela pelo título. Retorna captura de tela.

    • Parâmetros: title
  • screenshot_window -- Captura uma captura de tela de apenas uma janela.

    • Parâmetros: title, save_path
    • Somente leitura: true
Área de Transferência
  • get_clipboard -- Obtém o conteúdo de texto atual da área de transferência do sistema.

    • Somente leitura: true
  • set_clipboard -- Copia texto para a área de transferência do sistema.

    • Parâmetros: text
Gerenciamento de Aplicativos
  • launch_app -- Inicia um aplicativo e opcionalmente espera ele carregar. Executa o programa diretamente, nunca através de um shell.

    • Parâmetros: command, args, wait_text, timeout
    • Relata wait_text_found para que você possa saber se o aplicativo realmente carregou.
  • get_ocr_info -- Obtém informações sobre o backend de OCR ativo e backends disponíveis.

    • Somente leitura: true
Percepção Incremental
  • observe -- Relata o que mudou na tela desde a última observação. Reexamina apenas as regiões que se moveram. Prefira isso em vez de screenshot para rastrear estado.

    • Parâmetros: force_full
    • Somente leitura: true
  • find_element -- Encontra texto na tela usando o método mais barato que pode responder. Relata qual nível da cascata respondeu.

    • Parâmetros: text, exact, window_title
    • Somente leitura: true
  • click_element -- Encontra texto via cascata e clica nele. A alternativa barata para click_text.

    • Parâmetros: text, exact, button, window_title
  • read_model_text -- Lê texto na tela do modelo incremental sem reexecutar OCR na tela.

    • Parâmetros: query, limit
    • Somente leitura: true
  • perception_stats -- Relata quanto trabalho de percepção o modelo evitou.

    • Somente leitura: true
  • remember_screen -- Lembra a tela atual para que visitas futuras pulem a leitura dela. Persiste entre sessões.

    • Somente leitura: true
  • atlas_stats -- Relata o que o atlas de tela lembrou e com que frequência ajudou.

    • Somente leitura: true
Acessibilidade / Automação de UI (Windows)
  • get_ui_tree -- Obtém a árvore de acessibilidade da janela focada. Retorna todos os elementos interativos com nomes, tipos, posições. Determinístico e instantâneo.

    • Parâmetros: window_title, max_depth
    • Somente leitura: true
  • click_ui_element -- Clica em um elemento de UI usando a árvore de acessibilidade. Mais confiável que OCR.

    • Parâmetros: name, control_type, automation_id, window_title
  • fill_ui_element -- Define o valor de um elemento de UI (por exemplo, caixa de texto). Mais confiável que preenchimento baseado em OCR.

    • Parâmetros: value, name, automation_id, window_title
Tela Avançada
  • get_active_window -- Obtém informações sobre a janela atualmente focada.

    • Somente leitura: true
  • wait_for_change -- Espera a tela mudar visualmente. Tira uma captura de tela de referência, consulta até que seja diferente.

    • Parâmetros: timeout, poll_interval

Biblioteca Python

OSWright também funciona como uma biblioteca Python autônoma com uma 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")

Consulte o diretório examples/ para mais exemplos.

Arquitetura

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`)

Lembrando telas

Aplicações são determinísticas — o mesmo diálogo tem o mesmo layout toda vez. OSWright lembra telas que já leu e as reutiliza na próxima visita, entre sessões: leitura a frio de 125 ms → recall a quente de 1,4 ms (89×).

Uma tela lembrada nunca é confiada apenas pelo reconhecimento. Algumas regiões são verificadas por pixels antes de o layout ser reutilizado, então uma tela que mudou é rejeitada em vez de ser usada. A verificação falha de forma segura: uma tela sem nada verificável não é lembrada.

Desative com --no-atlas. Telas lembradas ficam em ~/.oswright/atlas.json.

Prevendo ações em vez de observá-las

Aplicações são determinísticas — clicar em Salvar produz o mesmo diálogo toda vez — então, após a primeira observação, o resultado de uma ação já é conhecido. OSWright aprende o que as ações fazem e confirma a tela esperada em vez de lê-la novamente: 19–23× mais barato que observar (2,3 ms versus 43–50 ms).

Uma previsão precisa ser vista duas vezes antes de ser confiada, é descartada se se mostrar errada e é verificada das mesmas duas formas que uma tela lembrada. Uma previsão falha é reportada ao agente como um surprise — a interface fez algo que não faz normalmente, o que vale saber em vez de absorver silenciosamente.

O que uma previsão confirmada garante: o layout — os mesmos controles nos mesmos lugares. Não que cada caractere seja idêntico. Um único dígito alterado muda menos pixels do que um cursor piscando, então nenhuma verificação de tela inteira pode separá-los em qualquer resolução. Use observe(force_full=True) quando o texto exato importa.

Desative com --no-speculate.

Esperando apenas o necessário

Ferramentas de ação costumavam dormir por 300 ms fixos, escolhidos para o pior caso, então cada ação pagava o pior cenário. O compositor sabe quando a tela para de mudar, então a espera agora termina quando a interface realmente estabiliza:

Sono fixo anterior300 ms
Espera real mediana61,5 ms
Economia em uma tarefa de 50 passos11,9 s

"Estabilizada" significa nenhuma mudança grande recente, não nenhuma mudança: uma área de trabalho real nunca fica parada — um cursor e um relógio produzem um evento de mudança a cada ~18 ms cobrindo cerca de 32 pixels, enquanto mudanças reais de interface cobrem dezenas de milhares.

Ainda não feito

  • Injeção de entrada no Wayland, e macOS AXTextMarker como um equivalente a TextPattern.
  • Um nível de modelo de visão para superfícies que não são nem acessíveis nem legíveis por texto: jogos, canvases, editores de imagem.
  • Transições baseadas em mais do que a tela anterior, para ações cujo resultado depende de estado que não é visível.

O que é medido, e o que não é

O custo de percepção e o sucesso da tarefa são ambos medidos nesta máquina e reproduzíveis via benchmarks/ — em quatro aplicações, percepção mais barata não custa precisão, e a divisão pixel/acessibilidade é medida em vez de argumentada.

Contra Windows-MCP

Mesmas tarefas, quatro cenários, cada um avaliado pela própria aplicação. Nenhuma ferramenta se autoavalia, e Windows-MCP roda com seus próprios padrões:

CalculadoraExplorerChromeChrome, 2 passosaprovadostokens
oswright5/54/55/55/519/20832
Windows-MCP, snapshot por ação5/55/55/55/520/2014.053
Windows-MCP, snapshot único5/55/55/55/520/208.214

Leia isso honestamente: Windows-MCP foi mais confiável, e oswright foi 16,9x mais barato. oswright errou um clique em vinte, em uma janela que acabara de abrir.

A diferença de custo é estrutural, não um ajuste de otimização. Windows-MCP retorna a tela ao agente — Snapshot renderiza a árvore de acessibilidade como (x,y) button "Seven" [action: click] — e recebe coordenadas de volta, então uma descrição da tela é cobrada no contexto do modelo a cada ação. oswright pega o texto e retorna o resultado.

A lacuna de confiabilidade pode ser causada pela velocidade: oswright resolve e clica em ~100 ms, às vezes antes de uma janela recém-focada estar pronta para entrada, onde um loop mais lento dá à aplicação o tempo que ela nunca precisou pedir. Isso é uma hipótese, não uma descoberta — adicionar uma estabilização pré-ação não fez diferença mensurável em dez tentativas, então está registrado em vez de corrigido.

O cenário Chrome, 2 steps existe porque toda outra tarefa aqui é curta o suficiente para que uma ferramenta leia a tela uma vez e reutilize essas coordenadas. Lá, o primeiro clique move os controles 325 px para baixo na página, e a configuração de snapshot único teve que reler a tela — então em tarefas cuja interface se move, seu número barato não existe e seu custo real é o por ação.

Reproduza com python benchmarks/bench_head_to_head.py (configuração no docstring do arquivo).

O que isso não estabelece: quatro tarefas curtas em um laptop. Nada sobre trabalho longo de múltiplos passos, recuperação ou maturidade do produto — Windows-MCP tem OAuth, analytics, um watchdog e um instalador; oswright não tem nenhum desses. Sua travessia de acessibilidade também lê o conteúdo da página do Chrome, o que o próprio nível de acessibilidade do oswright não faz. "Substancialmente mais barato por ação, a um pequeno custo de confiabilidade" é a afirmação. "Produto melhor" não é.

Desenvolvimento

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)

Decisões de design, medições e becos sem saída estão registrados em docs/ENGINEERING_LOG.md.

Licença

MIT