Manual de operación del escritorio AgentOS (válido para cualquier agente)
Este manual es autocontenido: si tu framework puede montar servidores MCP stdio (Goose / Claude Code / Z Code / Cursor, etc.),
al montarlos según §1 obtendrás todas las capacidades descritas abajo. Todos los elementos han sido probados en hardware real (Fedora + niri Wayland,
evidencias de extremo a extremo en el repositorio docs/capabilities.md, octubre 2026).
0. Modelo mental: cuatro capas de capacidad, en orden de prioridad descendente
- Capa L0 — Sistema: ventanas/espacios de trabajo/apertura de aplicaciones/archivos/notificaciones/portapapeles (extensiones niri/desktop/system/files). La más estable, úsala primero.
- Capa L1 — Árbol de accesibilidad: clics por nombre y lectura/escritura de texto dentro de aplicaciones GUI (extensión a11y, AT-SPI2). No roba el foco, no molesta al usuario. Probado en GTK3/GTK4/Qt (el interruptor global de Qt y
toolkit-accessibilityde Chromium ya están activados por defecto en el sistema desde el 18 de octubre de 2026). El árbol de Chromium se puede leer y escribir texto (los clics en páginas van por L2). Java no está instalado (consulta el apéndice B decapabilities.md). Las aplicaciones puramente X11 (tipo xterm) no tienen árbol, solo L3. - Capa L2 — Navegador CDP: todas las operaciones en páginas web (extensión browser, Chromium sin cabeza dedicado al agente).
- Capa L3 — Visual/entrada de último recurso: captura de pantalla con grim (fiable), inyección de teclado (
scripts/kb-inject.sh: wtype a nivel de usuario como principal, fiable para texto y combinaciones de teclas; las teclas de control como Enter/Tab recurren a ydotool que requiere sudo), inyección de puntero (no fiable: se desplaza con la aceleración, solo como último recurso).
Si una aplicación “no se abre / no funciona bien”: primero piensa si no se cumple una condición previa de L1 (interruptor de accesibilidad), luego usa una captura de pantalla L3 para ver el estado real. No lo reintentes a ciegas.
1. Montaje (una sola vez)
En Goose (~/.config/goose/config.yaml, formato probado en v1.54; el campo name: es obligatorio; si falta, toda la extensión se descarta silenciosamente):
extensions:
niri:
type: stdio
name: niri
enabled: true
cmd: /ruta/a/AgentOS/scripts/env/with-niri-env.sh
args: []
env_keys: []
envs: {}
timeout: 300 # segundos
# desktop/system/files/session/a11y/browser/cred son iguales,
# cmd usa respectivamente with-desktop-env.sh / with-system-env.sh / ... (ocho lanzadores en scripts/env/)
Otros frameworks (Claude Code / Z Code, etc., estilo JSON mcpServers):
{
"mcpServers": {
"niri": { "command": "/ruta/a/AgentOS/scripts/env/with-niri-env.sh" },
"desktop": { "command": "/ruta/a/AgentOS/scripts/env/with-desktop-env.sh" },
"system": { "command": "/ruta/a/AgentOS/scripts/env/with-system-env.sh" },
"files": { "command": "/ruta/a/AgentOS/scripts/env/with-files-env.sh" },
"session": { "command": "/ruta/a/AgentOS/scripts/env/with-session-env.sh" },
"a11y": { "command": "/ruta/a/AgentOS/scripts/env/with-atspi-env.sh" },
"browser": { "command": "/ruta/a/AgentOS/scripts/env/with-browser-env.sh" },
"cred": { "command": "/ruta/a/AgentOS/scripts/env/with-cred-env.sh" }
}
}
Puntos clave:
- Usa los scripts lanzadores, no apuntes directamente a
node dist/index.js— los lanzadores se encargan de descubrir el socket IPC de niri (la ruta contiene el PID del compositor, cambia en cada sesión, p. ej./run/user/1000/niri.wayland-1.2762.sock) y de completar las variables de entorno Wayland/dbus (XDG_RUNTIME_DIR/WAYLAND_DISPLAY/DBUS_SESSION_BUS_ADDRESS). Solo así las herramientas funcionan cuando se arrancan desde SSH o un entorno de servicio. - Prueba manual del lanzador:
timeout 2 scripts/env/with-niri-env.sh </dev/null; si devuelve exit 0, funciona. - Requisitos previos: Node ≥ 20, el repositorio ha ejecutado
npm install && npm run build, y se ejecuta bajo el mismo usuario que tiene la sesión gráfica de niri.
2. Referencia rápida de herramientas (agrupadas por extensión; nombre de herramienta = <extensión>__<herramienta>)
niri — Ventanas/espacios de trabajo/apertura (L0)
| Herramienta | Propósito | Parámetros clave |
|---|---|---|
list_windows | Listar todas las ventanas (id/app_id/título/espacio/foco) | — |
list_workspaces | Listar espacios de trabajo (idx numérico e id son dos cosas distintas; move usa número o nombre) | — |
list_outputs | Listar monitores | — |
focus_window | Cambiar el foco al frente (cambia la vista del usuario, úsalo con cuidado) | id |
focus_workspace | Cambiar de espacio de trabajo (cambia la vista del usuario) | reference: "3" o nombre |
move_window_to_workspace | Mover ventana (sin robar foco, para organización) | id? (por defecto = ventana con foco), reference |
close_window | Cerrar ventana | id? |
open_app | Iniciar aplicación en el escritorio del usuario | command: array, ej. ["ghostty"] |
a11y — Operaciones dentro de aplicaciones GUI (L1, AT-SPI2)
| Herramienta | Propósito | Parámetros clave |
|---|---|---|
a11y_list_apps | Listar aplicaciones registradas en el bus de accesibilidad | — |
a11y_dump_tree | Volcar el árbol de accesibilidad de una aplicación (para encontrar nombres de botones/estructura) | app |
a11y_click | Hacer clic por nombre accesible (sin robar foco, sin molestar al usuario) | app, buttonName |
a11y_read_texts | Leer todo el texto no vacío de una aplicación (pantalla/resultados/etiquetas) | app |
a11y_set_text | Escribir en un campo de texto por nombre de nodo | app, match (subcadena del nombre, por defecto * = primero), text |
Disciplina probada: dirígete por nombre, no por índice del árbol (la estructura del árbol se reconstruye al reconectar AT); el árbol de GTK4 solo es completo después de que un cliente AT se registre; en caso de múltiples instancias con el mismo nombre, el puente resuelve a la primera registrada — primero lee el texto para distinguir la instancia y, si es necesario, cierra las sobrantes. Excepción de Chromium: el árbol se construye de forma perezosa (el puente lo activa automáticamente; la primera volcada puede tardar 1-2 segundos, es normal); para escribir en campos de texto usa la combinación “grabFocus + kb-inject type” (las entradas de Chromium no aceptan setTextContents); los clics en botones de la página no funcionan (doAction se envía pero onclick no se ejecuta); todas las operaciones en páginas deben hacerse con la extensión browser.
browser — Operaciones web (L2, Chromium sin cabeza dedicado al agente)
| Herramienta | Propósito | Parámetros clave |
|---|---|---|
browser_open | Navegar (la primera llamada inicia el navegador automáticamente) | url |
browser_eval | Ejecutar JS en la página; rellenar formularios, hacer clics, leer contenido, todo con esto | Código JS |
browser_screenshot | Captura de pantalla (el navegador no tiene cabeza, así que la visión es solo mediante capturas) | — |
Acción estándar para rellenar formularios: después de el.value = "..." es obligatorio el.dispatchEvent(new Event("input", {bubbles: true})); de lo contrario, los frameworks modernos no reciben la entrada. Si aparece un captcha: detente e informa al usuario; nunca lo intentes saltar automáticamente.
files — Caja de arena de archivos (L0, raíz = ~/Agent/; todas las rutas son relativas)
| Herramienta | Propósito | Parámetros clave |
|---|---|---|
read_file / write_file | Leer/escribir texto (crea automáticamente los directorios padre) | path, content |
list_dir / create_dir | Listar/crear directorios | path |
move / delete | Mover / eliminar (va a .trash, recuperable; no uses shell rm) | source/dest, path |
find_by_name | Buscar archivos por nombre | pattern |
download | Descargar http(s) a la caja de arena (≤50MB / 60s; no uses shell curl) | url, path |
desktop — Presentación/notificaciones/portapapeles/confirmación (L0)
| Herramienta | Propósito | Parámetros clave |
|---|---|---|
open | Abrir un artefacto: URL o archivo; para documentos de Office es obligatorio app: "libreoffice" (lista blanca; las asociaciones MIME del sistema no son fiables) | target, app? |
notify | Notificación de escritorio (tres niveles, ver §3) | summary (obligatorio), body, urgency, action? |
confirm | Pedir autorización/decisión al usuario (T1 cuatro botones: Permitir/Permitir esta sesión/Denegar/Nunca; 30s sin respuesta = denegar) | summary (sin contenido sensible), tier? (t1/t2, afecta el texto) |
clipboard_read / clipboard_write | Leer/escribir portapapeles | — |
session — Acuario (sesión niri anidada exclusiva del agente; todas las tareas GUI se hacen aquí)
| Herramienta | Propósito |
|---|---|
agent_session_start / agent_session_stop / agent_session_status | Ciclo de vida de la sesión anidada (aparece una ventana que se puede observar en el escritorio) |
agent_open_app | Iniciar aplicación dentro del acuario (p. ej. gnome-calculator, chromium) |
agent_list_windows | Listar ventanas dentro del acuario |
agent_screenshot | Capturar la pantalla del acuario (con grim) |
system — Consultas del sistema (L0 solo lectura, todo permitido)
system_status / list_services / service_status / journal / network_info / list_packages / package_info / package_install (T2 confirmación fuerte: muestra cuadro de diálogo polkit con contraseña; requiere presencia humana; si se ejecuta sin supervisión, timeout de 300s lo deniega automáticamente — no lo llames en modo desatendido).
cred — Credenciales (el agente obtiene referencias, no texto plano)
cred_list (credenciales disponibles y su referencia/nombre de variable de entorno) · cred_autofill (canal A: autocompletado en página, el texto plano se descarta inmediatamente) · cred_exec (canal B: ejecución delegada con lista blanca + salida censurada) · cred_inject (canal C: inyección de variables de entorno con TTL corto, se reúnen todas al expirar). Cualquier operación que requiera contraseña/token solo debe hacerse a través de cred; está prohibido pedir texto plano al usuario.
3. Convenciones de comportamiento (disciplinas probadas y efectivas)
- Todos los artefactos van a
~/Agent/, clasificados endownloads/ inbox/ reports/ notes/ canvas/ screenshots/; al guardarlos entran automáticamente en el índice de artefactos (http://127.0.0.1:7788/artifacts). Cuando el usuario pregunte “¿dónde está mi informe anterior?”, abre esa página o informa la ruta relativa. - Prioridad de presentación: paneles/informes/contenido interactivo → escribe un archivo HTML autocontenido en
canvas/<nombre>/index.htmly luegoopenpara abrirhttp://127.0.0.1:7788/<nombre>/; documentos de Office →openconapp: "libreoffice"; páginas web → extensión browser. - Tres niveles de notificación: completado →
notifycon urgency=low y acción ({kind:"open_path", path:"reports/x.fodt"}o{kind:"open_url", url:...}, al hacer clic en la notificación se accede directamente al artefacto); fallo/se necesita saber urgentemente → urgency=critical con una frase de conclusión + causa y sugerencia; cuando se necesita una decisión del usuario → no uses notify, usaconfirm(notify no tiene semántica de elección). Los resultados asíncronos siempre deben notificarse; no los completes en silencio. - Confirmación y moderación: no toques la ventana que el usuario está usando a menos que se te pida explícitamente (primero haz
list_windowspara identificar el objetivo); si hay una denegación por política (isError con mensaje “denegado por política”), infórmalo con honestidad, no intentes sortearla; las operaciones GUI se realizan dentro del acuario, sin robar el foco del usuario. - Las credenciales solo se usan mediante referencias de cred-mcp (la disciplina que aparece al final de la tabla de §2, sección cred).
4. Problemas comunes de entorno (todos probados en la práctica)
| Problema | Síntoma | Solución |
|---|---|---|
| La ruta del socket IPC de niri cambia cada sesión | niri msg no conecta | Usa el lanzador (descubre automáticamente NIRI_SOCKET); no lo codifiques |
| El llavero de Chromium bloquea servicios de red | El navegador se queda colgado al iniciar | Inícialo con --password-store=basic (ya está incorporado en la extensión browser; si inicias chromium por tu cuenta, recuerda añadirlo) |
| Bloqueo de instancia única de soffice | Al volver a abrir un documento similar con LibreOffice ya abierto, puede que no aparezca ventana o se quede colgado | Primero verifica el estado del proceso soffice; para abrir documentos usa app:"libreoffice" que sigue la semántica del lanzador; si la salida anterior fue anormal, puede aparecer una ventana “Document Recovery” (probado el 18-10-2026); al listar ventanas, no la confundas con la ventana del documento objetivo |
| Asociación MIME de LibreOffice no fiable | Fedora identifica los archivos fodt como text/xml y los abre con un editor de texto | open debe incluir app:"libreoffice" |
| Las aplicaciones Qt no aparecen en el bus a11y | a11y_list_apps no las muestra | El sistema ya tiene activada la variable global (QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1 en /etc/environment, persiste tras reinicio); si sigue sin verlas, la aplicación se inició antes de que la variable estuviera disponible o no es una vinculación oficial de Qt; reinicia la aplicación |
| El árbol a11y de Chromium/Electron está vacío | El volcado no muestra contenido | ① Inicia con --force-renderer-accessibility (la extensión browser ya lo incluye por defecto); ② toolkit-accessibility debe estar a true antes de iniciar la aplicación (ya está activado globalmente; cambiarlo después no tiene efecto; hay que reiniciar la aplicación); ③ el árbol se construye de forma perezosa; haz un volcado para activar el puente (ver disciplina de §2 a11y) |
| Elección de método de inyección de teclado | Necesitas escribir texto o usar atajos de teclado | scripts/kb-inject.sh: para texto y combinaciones usa wtype (nivel de usuario); para teclas de control como Enter/Tab, wtype no funciona en GTK, usa ydotool (sudo); el chino puede perder el primer carácter; después de escribir, verifica leyendo el árbol a11y |
| El acuario tiene una superposición de teclas de niri al inicio | La captura de pantalla aparece tapada por “Important Hotkeys” | Inyecta Esc para cerrarla; el sandbox del agente no puede conectar al socket de ydotoold (root 600); usa niri__open_app para ejecutar la inyección en el entorno del escritorio real |
| Deriva del puntero de ydotool | El posicionamiento absoluto es impreciso (la aceleración de libinput ≈0.5) | No confíes en el puntero; la inyección de teclado es fiable; presta atención a la diferencia de ruta del socket (/tmp/.ydotool_socket vs $XDG_RUNTIME_DIR) |
| El árbol de GTK4 no está completo inicialmente | El volcado solo muestra unas decenas de nodos | Primero llama a a11y_list_apps/dump una vez para que el cliente AT se registre; luego el segundo volcado será completo (de 16 a 105 nodos, probado) |
| Confirmaciones T1/T2 en modo desatendido | 30s/300s sin respuesta, se deniega automáticamente | Comportamiento esperado: las operaciones de escritura requieren que el usuario esté presente; en pruebas, se puede responder mediante programación con org.agentos.Confirm1.Respond |
| Instalación de paquetes bloqueada por packagekitd | dnf falla esporádicamente | Antes de instalar, ejecuta systemctl stop packagekit (el helper T2 ya lo maneja) |
5. Ruta de actualización (cuando la aplicación objetivo no se abre, prueba en este orden)
L0 (capa de sistema, pruébala siempre primero) → L1 (a11y: GTK3/GTK4/Qt ya accesibles por defecto; Chromium verifica el flag y el orden de toolkit-accessibility; Electron de terceros añade el flag según el apéndice B de capabilities.md) → L2 (contenido web, obligatorio) → L3 último recurso (grim para ver + kb-inject para teclado; el puntero no es fiable).
Tipos que aún no se pueden abrir: aplicaciones puramente X11 (tipo xterm, sin árbol, L3 es el final), Java (requiere JAB, consulta el apéndice B de capabilities.md), la interfaz propia del compositor solo admite inyección de entrada — tabla detallada en el repositorio docs/capabilities.md.