Saltar a contenido

La aplicación de escritorio

AgenticOS se ejecuta en un navegador, y así es como lo usa la mayoría. La aplicación de escritorio es un añadido para quien la quiera en el dock: la consola en una ventana propia, más una mascota y un atajo de captura de pantalla. Nada de la plataforma la necesita.

Amigo, la mascota de escritorio, diciendo: No more caramba.

Amigo, una de las cinco mascotas. No more caramba in your AI.

La aplicación que hay dentro de la ventana es la misma consola Next.js que el servidor ya sirve, cargada desde el servidor, así que lleva el mismo inicio de sesión, los mismos permisos y las mismas comprobaciones de inquilino que llevaría una pestaña del navegador.

Es un envoltorio Tauri en desktop/, y guarda exactamente un ajuste: la dirección del servidor. El primer arranque la pide, "Shell → Change server…" la vuelve a pedir, y todo lo que hay en medio es la consola.

Es un envoltorio, no un build del frontend

frontend/ es una aplicación Next.js con una mitad de servidor — noventa y tantos route handlers que hacen de proxy hacia el backend, cookies de sesión que ellos fijan, middleware que elige la locale y páginas renderizadas en el servidor. Nada de eso corre dentro de un binario de escritorio, así que el envoltorio ni lo intenta: abre la URL del deployment igual que lo haría un navegador. Un deployment es lo que instalas; la app es cómo lo abres.

Ejecutarla

Rust desde rustup y el webview de la plataforma — WebKit en macOS, WebView2 en Windows, WebKitGTK en Linux — son los requisitos previos; la página de requisitos del propio Tauri tiene la lista por plataforma. La CLI de Tauri está fijada en desktop/package.json, y make install la descarga con todo lo demás.

make desktop-dev     # opens the shell; type the address of a console
make desktop-build   # a .app / .dmg, .msi or .deb/.AppImage for this machine
make desktop-check   # rustfmt, clippy with warnings denied, and the tests

La primera vez, la ventana muestra un formulario que pregunta dónde está el servidor, relleno con http://localhost:3000 — un stack de make dev. Un host a secas (agenticos.acme.com) se abre por HTTPS. Todo lo que no sea una dirección web se rechaza en el formulario.

El http:// a secas es solo para esta máquina

A localhost, 127.0.0.1 y ::1 se puede llegar en claro; cualquier otro host se rechaza hasta que sea https://. La consola envía la contraseña y recibe de vuelta el token por esa conexión, y una LAN es exactamente el sitio donde otro puede leerlo.

Una dirección en la que no responde nada también se rechaza: el envoltorio abre una conexión TCP antes de apuntar el webview a ninguna parte, porque WebKit pinta una conexión rechazada como una ventana en blanco. La misma sonda corre en cada arranque, así que un stack caído te devuelve al formulario con el motivo en lugar de dejarte ante una ventana vacía.

La respuesta se guarda como server.json en el directorio de configuración que la plataforma tiene para la app — ~/Library/Application Support/co.vstorm.agenticos/ en macOS, %APPDATA%\\co.vstorm.agenticos\\ en Windows, ~/.config/co.vstorm.agenticos/ en Linux — y se vuelve a leer en cada arranque.

Un archivo que ya no se puede parsear — una errata al editarlo a mano — se aparta como server.json.invalid en el siguiente arranque, y el formulario de conexión guarda uno nuevo.

Una dirección equivocada se corrige de cuatro maneras, la que quede más a mano: Settings… (⌘,, también en el icono de la bandeja y en el menú contextual de la mascota, así que se alcanza incluso cuando la ventana muestra el sitio equivocado), Shell → Change server…, editando ese archivo, o arrancando con --server http://localhost:3000 desde un terminal, que además la guarda.

La mascota

Una pequeña criatura de pixel art en una ventana propia, transparente y siempre encima: se queda en el escritorio mientras la consola está abierta, minimizada o cerrada, igual que las mascotas de Codex. Arrástrala a donde quieras; haz clic y saluda; haz doble clic y da un brinco y trae la consola al frente, reabriéndola si habías cerrado la ventana. Si la dejas en paz, está a su aire, mira alrededor, pasea un trecho por la pantalla y da la vuelta al llegar al borde, y de vez en cuando echa una cabezada.

Pasa el cursor por encima y aparece un botón + New chat sobre su cabeza; pone la consola en una conversación nueva y abre la ventana si estaba cerrada. Haz clic y dice algo, en un bocadillo, con voz propia. Acaríciala — el cursor adelante y atrás sobre ella unas cuantas veces — y cierra los ojos y sube un corazón. Mientras está ahí parada, sus ojos siguen al cursor. Al caer la noche echa una cabezada donde habría paseado. Soltada tras un arrastre, aterriza con un pequeño brinco.

Haz clic derecho en la mascota para su menú: un chat nuevo, la consola, qué mascota es y si se muestra. El mismo menú está en el icono de la bandeja (el extra de la barra de menús en macOS) y bajo Pet en la barra de menús, y los tres son un único conjunto de entradas, así que una marca cambiada en uno queda cambiada en los otros.

Show pet (Cmd/Ctrl+Shift+P) la esconde y la trae de vuelta; el mismo menú elige qué mascota es — Orbit, una redonda con antena; Boxy, un terminal con patas; Ghost, que flota; Sprout, una semilla con una hoja; Amigo, con sombrero y bigote, para no more caramba in your AI. Dónde se quedó, si se muestra y qué mascota es se guardan junto a la dirección del servidor, así que en el siguiente arranque la mascota está donde la pusiste. Con el ajuste de reducción de movimiento del sistema operativo se queda quieta, y se sigue pudiendo arrastrar.

El dibujo se compone al vuelo en desktop/ui/pet-sprites.js: cada mascota es un cuerpo y una descripción de dónde van ojos, pies y brazo, y cada animación son unas líneas de posiciones compartidas por las cinco, no un sprite sheet por mascota. Lo que dice cada una está en pet-lines.js; el comportamiento, y cada regla sobre cuándo cuenta una caricia o adónde miran los ojos, está en pet-engine.js, puro y con tests. Su ventana propia es lo que la hace mascota y no widget, y lo que cuesta: macOSPrivateApi en tauri.conf.json, porque una ventana transparente en macOS lo exige, lo que descarta la Mac App Store — no era sitio para una consola autoalojada.

Todavía no sabe qué están haciendo los agents

La mascota de Codex lleva un bocadillo que dice que hay un run en curso o una aprobación esperando. La nuestra aún no puede: la consola es una página remota sin IPC hacia el envoltorio, y dárselo significa una capability con URLs remote más un hook en el frontend que informe de la actividad. Eso es lo que viene después; la mascota de aquí es compañía, no un piloto de estado.

Captura de pantalla a un chat nuevo

Pulsa el atajo — ⌘⇧A por defecto, estés donde estés — y aparece la cruceta de Cmd+Shift+4. Elige una región y la consola viene al frente en un chat nuevo con la imagen ya adjunta, lista para la pregunta. Escape cancela. La misma acción está en el menú de la mascota y en el icono de la bandeja.

Settings… (⌘,, bajo Shell, en el icono de la bandeja y en el menú contextual de la mascota) lo reasigna: haz clic en el campo, mantén los modificadores y pulsa una tecla. Una combinación necesita al menos un modificador — un atajo global sobre una letra a secas se tragaría lo que escribes en cualquier aplicación — y una que ya tenga otra aplicación se rechaza conservando la asignación anterior. Clear lo apaga. Las asignaciones se guardan junto a la dirección del servidor. Una asignación que no se pudo tomar al arrancar, porque otra aplicación llegó antes, se nombra en esa misma página para sustituirla, en vez de que se quede ahí aparentando estar asignada.

Dos modificadores por sí solos no pueden ser un atajo

Command izquierda más Command derecha es un acorde, no una tecla: el registro de hotkeys del sistema operativo, que es lo que usa el envoltorio, necesita en la combinación una tecla que no sea modificador. Detectar un acorde de solo modificadores significa un event tap de accesibilidad que vigila cada pulsación, y pedirle a cada usuario que lo conceda. No en esta versión.

La primera pulsación pregunta a macOS si AgenticOS puede grabar la pantalla. Mientras no pueda, no se captura nada — la herramienta del propio sistema termina en silencio sin cruceta —, así que el envoltorio comprueba primero, abre System Settings → Privacy & Security → Screen Recording, y la mascota dice "Allow screen recording, then restart me." El permiso va a la aplicación responsable: el bundle de AgenticOS una vez empaquetado, pero bajo make desktop-dev el terminal desde el que se lanzó el binario, o el IDE que lo alberga — que es el que hay que marcar en esa lista. Un permiso surte efecto tras reiniciar.

La imagen llega al compositor igual que un archivo elegido: el envoltorio ejecuta un script en la página de la consola que entrega el PNG al campo de archivo del compositor, así que la subida, el límite de tamaño y la vista previa son los de la propia consola. Solo se entrega a la página de chat en el origin del servidor configurado — nunca a otro sitio al que la ventana haya podido ir a parar — y solo dentro de los dos minutos siguientes a la pulsación; una captura que no llegó a ninguna parte se descarta. En Windows y Linux todavía no hay captura cableada.

Lo que a propósito no hace

  • Nada de ejecución local. El envoltorio no tiene acceso a la máquina más allá de un único archivo JSON. Un agent que ejecuta comandos en el portátil desde el que se abre es otra funcionalidad con otro modelo de permisos — un tercer tipo de conexión de sandbox junto a Docker y Daytona, ligado a la máquina de una persona en lugar de a la organización — y no es esta.
  • Nada de modo sin conexión. Con el servidor inalcanzable, la ventana muestra la página de error del propio webview; "Shell → Reload" reintenta.
  • Nada de guardia de navegación. Un enlace que sale del origin del servidor se abre dentro de la ventana en lugar de en el navegador del sistema, porque un flujo OAuth — iniciar sesión, conectar un servidor MCP — sale del origin y tiene que volver al mismo webview para que su cookie aterrice. Mientras la ventana muestra cualquier otro sitio, su título nombra ese host — la única pieza de la interfaz del navegador que una página no puede dibujar, ya que no hay barra de direcciones — y ⌘, o "Shell → Change server…" es el camino de vuelta si una página no tiene enlace a casa. Llevar esos flujos al navegador del sistema es #1532.
  • El inicio de sesión se queda en la ventana, y la ventana dice que es Safari. El user agent desnudo de WebKit es lo que Google rechaza como navegador incrustado (disallowed_useragent); la ventana de la consola lleva los tokens de versión de Safari sobre el mismo motor, así que el inicio de sesión con Google funciona. El traspaso que Google prefiere — el navegador del sistema y un deep link de vuelta — necesita un intercambio de un solo uso que el backend todavía no tiene, y es #1532.

Dónde queda en el árbol

desktop/ui/ es el formulario de conexión y la mascota, archivos estáticos sin paso de build; bun test ejecuta ahí los tests de sprites y de comportamiento de la mascota. desktop/src-tauri/ es la parte de Rust: los comandos que llaman las dos páginas, las dos ventanas y el menú. desktop-check no forma parte de make lint ni de make check: CI todavía no tiene un toolchain de Rust, y tests/test_ci_parity.py rechazaría un check que ejecutase un paso que CI no ejecuta. Ejecútalo antes de pushear un cambio bajo desktop/.

Resumen

  • La consola se queda en el servidor. El envoltorio la abre; no se empaqueta nada.
  • Un archivo de ajustes. La dirección del servidor, preguntada una vez, y dónde se dejó la mascota, ambas cambiables desde el menú.
  • Una captura está a un atajo de distancia. ⌘⇧A, una región, un chat nuevo con ella adjunta; se reasigna en Settings (⌘,).
  • Nada local todavía. La ejecución local es un tipo de conexión de sandbox por diseñar, no un interruptor de este envoltorio.