Manuel d'opération du bureau AgentOS (universel pour tout agent)
Ce manuel est autonome : tant que votre framework peut monter un serveur MCP stdio (Goose / Claude Code / Z Code / Cursor, etc.), montez-le conformément au §1 et vous obtiendrez toutes les capacités ci-dessous. Toutes les entrées sont testées sur machine réelle (Fedora + niri Wayland, preuves de bout en bout datant d'octobre 2026 dans le dépôt sous docs/capabilities.md).
0. Modèle mental : quatre niveaux de capacité, par priorité décroissante
- L0 Couche système — fenêtres/espaces de travail/lancement/fichiers/notifications/presse-papier (extensions niri/desktop/system/files) : le plus stable, à utiliser en priorité ;
- L1 Arbre d'accessibilité — clics par nom/lecture et écriture de texte dans les applications GUI (extension a11y, AT-SPI2) : sans besoin de focus, ne dérange pas l'utilisateur, testé avec succès sur GTK3/GTK4/Qt (l'interrupteur global de Qt et toolkit-accessibility de Chromium sont activés par défaut système depuis le 18 octobre 2026) ; l'arbre Chromium est lisible et la saisie de texte possible (les clics sur les pages passent par L2) ; Java non installé (voir annexe B de capabilities.md) ; les applications purement X11 (type xterm) n'ont pas d'arbre, seulement L3 ;
- L2 Navigateur CDP — toutes les opérations sur les pages web (extension browser, Chromium headless dédié à l'agent) ;
- L3 Visuel/saisie de secours — capture d'écran grim (fiable), injection clavier (
scripts/kb-inject.sh: wtype en mode utilisateur principal, fiable pour le texte et les combinaisons ; les touches de contrôle (Entrée/Tab) passent par ydotool avec sudo), injection pointeur (non fiable : dérive due à l'accélération, à utiliser seulement en dernier recours).
Face à une application qui « ne s'ouvre pas / ne fonctionne pas bien » : vérifiez d'abord si les prérequis L1 sont remplis (interrupteur a11y), puis utilisez L3 pour capturer l'état réel, ne refaites pas aveuglément.
1. Montage (une seule fois)
goose (~/.config/goose/config.yaml, format testé en version 1.54 ; le champ name: est obligatoire, son absence entraîne le rejet silencieux de l'extension) :
extensions:
niri:
type: stdio
name: niri
enabled: true
cmd: /chemin/vers/AgentOS/scripts/env/with-niri-env.sh
args: []
env_keys: []
envs: {}
timeout: 300 # en secondes
# desktop/system/files/session/a11y/browser/cred : structure identique,
# cmd avec respectivement with-desktop-env.sh / with-system-env.sh / ... (huit lanceurs dans scripts/env/)
Autres frameworks (style JSON mcpServers pour Claude Code / Z Code, etc.) :
{
"mcpServers": {
"niri": { "command": "/chemin/vers/AgentOS/scripts/env/with-niri-env.sh" },
"desktop": { "command": "/chemin/vers/AgentOS/scripts/env/with-desktop-env.sh" },
"system": { "command": "/chemin/vers/AgentOS/scripts/env/with-system-env.sh" },
"files": { "command": "/chemin/vers/AgentOS/scripts/env/with-files-env.sh" },
"session": { "command": "/chemin/vers/AgentOS/scripts/env/with-session-env.sh" },
"a11y": { "command": "/chemin/vers/AgentOS/scripts/env/with-atspi-env.sh" },
"browser": { "command": "/chemin/vers/AgentOS/scripts/env/with-browser-env.sh" },
"cred": { "command": "/chemin/vers/AgentOS/scripts/env/with-cred-env.sh" }
}
}
Points importants :
- Utilisez le script de lancement, ne pointez pas directement vers node dist/index.js — le lanceur se charge de découvrir le socket IPC niri
(chemin contenant le PID du compositeur, différent à chaque session, ex.
/run/user/1000/niri.wayland-1.2762.sock) et de compléter l'environnement Wayland/dbus (XDG_RUNTIME_DIR/WAYLAND_DISPLAY/DBUS_SESSION_BUS_ADDRESS), afin que les outils soient disponibles même lorsqu'ils sont lancés depuis un environnement SSH/service ; - Test manuel du lanceur :
timeout 2 scripts/env/with-niri-env.sh </dev/null, exit 0 signifie que tout est correct ; - Prérequis : Node ≥ 20, le dépôt a exécuté
npm install && npm run build, et s'exécute sous l'utilisateur propriétaire de la session graphique niri.
2. Aide-mémoire des outils (groupés par extension, nom d'outil = <extension>__<outil>)
niri — Fenêtres/espaces de travail/lancement (L0)
| Outil | Utilisation | Paramètres clés |
|---|---|---|
list_windows | Lister toutes les fenêtres (id/app_id/titre/espace de travail/focus) | — |
list_workspaces | Lister les espaces de travail (l'index idx et l'id sont deux choses différentes, move utilise l'index ou le nom) | — |
list_outputs | Lister les écrans | — |
focus_window | Mettre au premier plan avec focus (change la vue utilisateur, à utiliser avec précaution) | id |
focus_workspace | Changer d'espace de travail (change la vue utilisateur) | reference: "3" ou nom |
move_window_to_workspace | Déplacer une fenêtre (sans voler le focus, pour organiser) | id? (défaut = fenêtre focus), reference |
close_window | Fermer une fenêtre | id? |
open_app | Lancer un programme sur le bureau de l'utilisateur | command: tableau, ex. ["ghostty"] |
a11y — Opérations dans les applications GUI (L1, AT-SPI2)
| Outil | Utilisation | Paramètres clés |
|---|---|---|
a11y_list_apps | Lister les applications enregistrées sur le bus d'accessibilité | — |
a11y_dump_tree | Décharger l'arbre d'accessibilité d'une application (trouver le nom du bouton/la structure) | app |
a11y_click | Cliquer selon le nom accessible (sans focus, ne dérange pas l'utilisateur) | app, buttonName |
a11y_read_texts | Lire tout le texte non vide d'une application (écran/résultat/étiquette) | app |
a11y_set_text | Écrire dans un champ de texte par nom de nœud | app, match (sous-chaîne du nom, défaut * premier), text |
Discipline testée : adressez-vous par nom, pas par index de l'arbre (la structure de l'arbre est reconstruite à chaque connexion AT) ; l'arbre GTK4 n'est complet qu'après enregistrement du client AT ; en cas de plusieurs instances d'un même nom, le pont résout la première enregistrée — lisez d'abord le texte pour discriminer l'état de l'instance, fermez les instances supplémentaires si nécessaire. Exception Chromium : l'arbre est construit paresseusement (le pont se déclenche automatiquement, un premier dump peut prendre 1 à 2 secondes de plus, c'est normal) ; pour écrire dans un champ de texte, utilisez la combinaison "grabFocus + kb-inject type" (les entrées de Chromium n'acceptent pas setTextContents) ; les clics sur les boutons de page ne fonctionnent pas (doAction est envoyé mais onclick ne s'exécute pas), les opérations sur les pages passent exclusivement par l'extension browser.
browser — Opérations web (L2, Chromium headless dédié à l'agent)
| Outil | Utilisation | Paramètres clés |
|---|---|---|
browser_open | Naviguer (le premier appel lance automatiquement le navigateur) | url |
browser_eval | Exécuter du JS dans la page, remplir des formulaires/cliquer/lire le contenu, tout passe par là | Code JS |
browser_screenshot | Capture d'écran (navigateur headless, la partie visuelle repose entièrement sur cela) | — |
Action standard pour remplir un formulaire : après el.value = "...", exécuter obligatoirement el.dispatchEvent(new Event("input", {bubbles: true})),
sinon les frameworks modernes ne reçoivent pas la saisie. En cas de CAPTCHA : arrêtez-vous et informez l'utilisateur, ne le contournez jamais automatiquement.
files — Bac à sable fichiers (L0, racine = ~/Agent/, tous les chemins en relatif)
| Outil | Utilisation | Paramètres clés |
|---|---|---|
read_file / write_file | Lire/écrire du texte (crée automatiquement les répertoires parents) | path, content |
list_dir / create_dir | Lister/créer un répertoire | path |
move / delete | Déplacer/supprimer (va dans .trash, récupérable, n'utilisez pas rm shell) | source/dest, path |
find_by_name | Trouver un fichier par son nom | pattern |
download | Téléchargement http(s) dans le bac à sable (≤50 Mo / 60s, n'utilisez pas curl shell) | url, path |
desktop — Affichage/Notifications/Presse-papier/Confirmation (L0)
| Outil | Utilisation | Paramètres clés |
|---|---|---|
open | Ouvrir un artefact : URL ou fichier ; pour les documents office, obligation de spécifier app: "libreoffice" (liste blanche, l'association MIME système n'est pas fiable) | target, app? |
notify | Notification bureau (trois niveaux voir §3) | summary (obligatoire), body, urgency, action? |
confirm | Demander une décision à l'utilisateur (T1 : quatre bannières : autoriser/autoriser pour la session/refuser/refuser définitivement, 30s sans réponse = refus) | summary (pas de contenu sensible), tier? (t1/t2, influence le texte) |
clipboard_read / clipboard_write | Lire/écrire le presse-papier | — |
session — Aquarium (session niri imbriquée dédiée à l'agent, tout le travail GUI s'y fait)
| Outil | Utilisation |
|---|---|
agent_session_start / agent_session_stop / agent_session_status | Cycle de vie de la session imbriquée (une fenêtre observable apparaît sur le bureau) |
agent_open_app | Lancer une application dans l'aquarium (ex. gnome-calculator, chromium) |
agent_list_windows | Lister les fenêtres dans l'aquarium |
agent_screenshot | Capture d'écran ciblée de l'intérieur de l'aquarium (grim) |
system — Requêtes système (L0 lecture seule, tout autorisé)
system_status / list_services / service_status / journal / network_info /
list_packages / package_info / package_install (T2 confirmation forte : affiche une boîte de mot de passe polkit, nécessite une présence humaine ;
en mode headless, timeout de 300s puis refus — ne pas appeler en mode non supervisé).
cred — Identifiants (l'agent obtient une référence, pas le texte clair)
cred_list (identifiants disponibles avec leur référence/nom de variable d'environnement) · cred_autofill (canal A : remplissage automatique de page, le texte clair est jeté immédiatement) ·
cred_exec (canal B : exécution déléguée via liste blanche + sortie masquée) · cred_inject (canal C : injection de variables d'environnement à courte durée de vie, révoquées en bloc à l'expiration).
Toute opération nécessitant un mot de passe/token passe exclusivement par cred, il est interdit de demander le texte clair à l'utilisateur.
3. Conventions de comportement (discipline testée et efficace)
- Les artefacts sont stockés dans
~/Agent/, classés pardownloads/ inbox/ reports/ notes/ canvas/ screenshots/; leur emplacement est automatiquement indexé dans les artefacts (http://127.0.0.1:7788/artifacts), si l'utilisateur demande « où est mon rapport précédent » → ouvrez cette page ou donnez le chemin relatif ; - Priorité d'affichage : tableaux de bord/rapports/contenu interactif → écrivez un fichier HTML autonome dans
canvas/<nom>/index.htmlpuisopenpour ouvrirhttp://127.0.0.1:7788/<nom>/; documents office →openavecapp: "libreoffice"; pages web → extension browser ; - Trois niveaux de notification : achèvement →
notifyurgency=low + action ({kind:"open_path", path:"reports/x.fodt"}ou{kind:"open_url", url:...}, cliquer sur la notification mène directement à l'artefact) ; échec/nécessite attention rapide → urgency=critical + une phrase de conclusion + suggestion de cause ; pour une décision utilisateur → n'utilisez pas notify, passez parconfirm(notify n'a pas de sémantique de choix). Tout résultat asynchrone doit être notifié, ne terminez pas en silence ; - Confirmation et retenue : ne touchez pas à la fenêtre que l'utilisateur est en train d'utiliser, sauf demande explicite (d'abord
list_windowspour identifier la cible) ; en cas de refus stratégique (isError avec mention « refus stratégique »), informez honnêtement, ne contournez pas ; les opérations GUI se font dans l'aquarium, ne volez pas le focus de l'utilisateur ; - Les identifiants passent uniquement par la référence cred-mcp (discipline mentionnée après le tableau cred du §2).
4. Pièges d'environnement (tous déjà rencontrés)
| Piège | Phénomène | Solution |
|---|---|---|
| Le chemin du socket IPC niri change à chaque session | niri msg ne se connecte pas | Utilisez le lanceur (découverte automatique de NIRI_SOCKET), ne codez pas en dur |
| Le trousseau Chromium bloque les services réseau | Le navigateur se lance et se bloque | Lancez avec --password-store=basic (déjà intégré dans l'extension browser ; si vous lancez chromium vous-même, pensez à l'ajouter) |
| Verrouillage monoposte de soffice | Si LibreOffice est déjà ouvert, ouvrir un autre document du même type peut rester sans fenêtre | Vérifiez d'abord l'état du processus soffice ; pour ouvrir un document, utilisez app:"libreoffice" qui suit la sémantique du lanceur ; une sortie anormale précédente peut encore afficher une fenêtre "Document Recovery" (testé le 18 octobre 2026), en listant les fenêtres, ne la confondez pas avec la fenêtre du document cible |
| L'association MIME de LibreOffice n'est pas fiable | Fedora identifie fodt comme text/xml et l'ouvre avec un éditeur | open doit impérativement inclure app:"libreoffice" |
| Les applications Qt n'apparaissent pas sur le bus a11y | a11y_list_apps ne les voit pas | L'activation système est déjà globale (QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1 dans /etc/environment, persistant après redémarrage) ; si toujours invisible = application démarrée avant la mise en place de l'environnement ou non liée officiellement à Qt, redémarrez l'application |
| L'arbre a11y de Chromium/Electron est vide | Le dump ne produit rien | ① Lancez avec --force-renderer-accessibility (déjà par défaut dans l'extension browser) ; ② toolkit-accessibility doit être true avant le démarrage de l'application (déjà global ; une modification ultérieure est inefficace, il faut redémarrer l'application) ; ③ construction paresseuse de l'arbre, un premier dump déclenche le pont (voir discipline a11y §2) |
| Choix de l'injection clavier | Pour taper du texte ou des raccourcis | scripts/kb-inject.sh : utilise wtype pour le texte et les combinaisons (utilisateur) ; les touches de contrôle (Entrée/Tab) ne fonctionnent pas avec wtype sur GTK, passez par ydotool (sudo) ; le chinois peut perdre le premier caractère, après la saisie vérifiez en lisant l'arbre a11y |
| L'aquarium a initialement la superposition des touches de raccourci niri | La capture d'écran est masquée par "Important Hotkeys" | Injectez Esc pour fermer ; dans le bac à sable de l'agent, la connexion au socket ydotoold est impossible (root 600), utilisez niri__open_app pour exécuter l'injection dans l'environnement de bureau réel |
| Dérive du pointeur ydotool | Le positionnement absolu est imprécis (accélération libinput ≈0.5) | Le pointeur n'est pas fiable ; l'injection clavier est fiable ; attention aux différences de chemin du socket /tmp/.ydotool_socket vs $XDG_RUNTIME_DIR |
| L'arbre GTK4 initial n'est pas complet | Le dump ne montre qu'une douzaine de nœuds | Faites d'abord un a11y_list_apps/dump pour que le client AT s'enregistre, puis le deuxième dump sera complet (testé : 16→105 nœuds) |
| Confirmation T1/T2 en mode headless | 30s/300s sans réponse = refus automatique | Comportement attendu : les opérations d'écriture nécessitent la présence de l'utilisateur, ou en test, répondez par programme via org.agentos.Confirm1.Respond |
| Installation de paquet bloqué par le verrou packagekitd | dnf échoue parfois | Avant l'installation, systemctl stop packagekit (déjà géré par le helper T2) |
5. Chemin de montée en puissance (quand l'application cible ne s'ouvre pas, essayez dans l'ordre)
L0 (couche système, toujours en premier) → L1 (a11y : GTK3/4/Qt déjà accessibles par défaut ; Chromium vérifiez le flag et l'ordre de toolkit-accessibility ; Electron tiers ajoutez le flag selon l'annexe B de capabilities.md) →
L2 (pages web obligatoires) → L3 de secours (grim pour voir + kb-inject pour le clavier ; pointeur non fiable).
Types encore inaccessibles : applications purement X11 (type xterm, sans arbre, L3 est le terminus), Java (nécessite JAB, voir annexe B de capabilities.md), UI du compositeur lui-même uniquement par injection de saisie — voir le tableau détaillé dans le dépôt sous docs/capabilities.md.