- Token obligatoire (RPA_API_TOKEN) sur /capture et /file-action
- Bind 127.0.0.1 par défaut, 0.0.0.0 exige token (fail-closed)
- /health reste public pour monitoring
- VWB backend injecte le Bearer pour les proxys distants
- hmac.compare_digest pour comparaison temps constant
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Nouveau module persistent_buffer.py (SQLite WAL, thread-safe)
- Purge automatique des captures locales après ACK 200 serveur
- Drain loop 15s, retry exponentiel, plafonds tentatives
- Enum ImageSendResult.{OK, FAILED, FILE_GONE} pour distinguer les cas
- FileNotFoundError n'est plus un faux succès (P0-E audit)
- 14 tests intégration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
"Fichier" dans "*,Ceci est un test – Bloc-notes" était rejeté
parce que le titre attendu était "test.txt – Bloc-notes".
Maintenant la comparaison extrait le nom d'app (Bloc-notes)
et accepte le match si c'est la même application.
Résout : "Ajouter un nouvel onglet" bloqué quand un fichier
différent est ouvert dans Bloc-notes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Le session_cleaner détecte les dialogues système (Enregistrer sous,
Ouvrir, Confirmer, etc.) et marque les actions correspondantes comme
conditionnelles. Au replay, si le dialogue n'apparaît pas (ex: Ctrl+S
sauve silencieusement car le fichier existe), les actions du dialogue
sont skippées automatiquement.
Détection basée sur des patterns de noms de dialogues Windows FR/EN.
Testé : seul le clic dans "Enregistrer sous" est conditionnel,
les actions Bloc-notes/Rechercher/systray restent normales.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quand la fenêtre attendue ne correspond pas (ex: Ctrl+S a sauvé sans
dialogue "Enregistrer sous"), Léa passe en mode capture au lieu de
retourner paused_need_help. Si l'humain ne fait rien pendant 10s,
l'action est skippée (l'état est considéré déjà atteint).
4 déclencheurs apprentissage maintenant couverts :
- retry_failed : grounding + retry échouent
- no_screen_change : clic sans effet visible
- wrong_window : fenêtre attendue absente
- SUPERVISE direct : Policy décide de demander
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Premier replay fonctionnel de bout en bout (Bloc-notes, Chrome).
Corrections critiques :
- Fix double-lancement agent (Lea.bat start /b + verrou PID)
- Sérialisation replay (threading.Lock dans poll_and_execute)
- Garde UIA bbox >50% écran (rejet conteneurs "Bureau")
- Filtre fenêtres bruit système (systray overflow)
- Auto-nettoyage replays bloqués (paused_need_help)
Cascade visuelle complète dans session_cleaner :
- UIA local (10ms) → template matching (100ms) → serveur docTR/VLM
- Nettoyage bureau pré-replay (clic "Afficher le bureau")
- Crops 80x80 + vlm_description pour chaque clic
Grounding contraint à la fenêtre active :
- Capture croppée à la fenêtre au lieu de l'écran entier
- Conversion coordonnées fenêtre → écran
- Élimine les faux positifs taskbar/systray
Mode apprentissage supervisé (SUPERVISE → capture humaine) :
- Léa passe en mode capture quand elle est perdue
- Capture mini-workflow humain (clics + frappes + combos)
- Fin par Ctrl+Shift+L ou timeout inactivité 10s
- Correction stockée dans target_memory.db via serveur
Deploy Windows complet (grounding.py, policy.py, uia_helper.py).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Symétrie avec le fix 7cc03f6f1 (no_screen_change strict → paused_need_help).
Avant : si l'agent détecte en pré-vérification que la fenêtre active
n'est pas celle attendue, l'erreur retombait dans la branche retry+stop
legacy → 3 retries inutiles puis status=error et queue vidée.
C'est une violation de feedback_failure_is_learning.md : un échec Léa
n'est jamais un "stop avec error", c'est un moment pédagogique.
Maintenant :
1. L'agent envoie warning="wrong_window" dans le résultat (en plus
de l'error textuel existant). Ajouté aux 2 chemins :
- pré-vérif (expected_window_before mismatch, executor.py ~587)
- post-vérif strict (expected_window_title timeout, executor.py ~820)
2. Le serveur détecte warning="wrong_window" AVANT la branche
retry+stop legacy → redirection vers paused_need_help
3. pause_message explicite : "Je m'attendais à voir la bonne fenêtre
mais je vois autre chose. Peux-tu vérifier que l'application est
au premier plan ?"
4. Queue intacte (l'action reste en tête, prête à être relancée)
5. log_replay_failure pour l'apprentissage futur
Cause fréquente identifiée : les popups de Léa elle-même (notifications,
fenêtre de chat) volent le focus Windows pendant le replay → l'app cible
perd le premier plan → pré-vérif détecte le mismatch. Bug UX séparé à
traiter (Léa ne devrait pas prendre le focus pendant un replay actif).
Appliqué aux 2 copies de l'agent (dev + deploy).
Tests : 56 E2E + Phase0 passent, 0 régression.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ajoute creationflags=CREATE_NO_WINDOW (0x08000000) au subprocess.run()
qui appelle lea_uia.exe dans UIAHelper._run(). Sans ce flag, Windows
ouvre brièvement une fenêtre cmd noire à CHAQUE appel — et le captor
appelle UIA à chaque clic utilisateur pendant l'enregistrement.
Symptômes rapportés par Dom :
- Flash de fenêtre terminal à chaque clic (visible à l'œil)
- Ralentissement de la souris pendant les enregistrements
- Pollution des données d'apprentissage : le VLM de post-analyse
"voit" la fenêtre cmd et l'enregistre comme élément cliqué
(log serveur : "gemma4 a lu l'élément : 'C:\\Lea\\helpers\\lea_uia.exe'")
Implémentation portable :
- Flag calculé au niveau module : 0x08000000 sur Windows, 0 sur Linux/Mac
- getattr(subprocess, "CREATE_NO_WINDOW", ...) pour gérer l'absence de
la constante sur Linux
- creationflags=0 est un no-op sur Linux, safe
Appliqué aux 2 copies synchronisées :
- agent_v0/agent_v1/core/uia_helper.py (source active pour l'agent)
- core/workflow/uia_helper.py (copie identique)
85 tests in silico OK (29 UIA + 56 E2E/Phase0). Le vrai test c'est
Dom qui refait un enregistrement et vérifie qu'il n'y a plus de
flash de terminal.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Greffe minimale du mécanisme d'apprentissage persistant (Fiche #18,
target_memory_store.py) sur le pipeline streaming V4 sans toucher à V3.
Architecture (docs/PLAN_APPRENTISSAGE_LEA.md) :
- Lookup mémoire AVANT la cascade résolution coûteuse OCR/template/VLM
dans _resolve_target_sync → hit = <10ms, miss = overhead zéro
- Record APRÈS validation post-condition (title_match strict)
dans /replay/result → 2 succès → cristallisation par répétition
- Single source of truth : l'agent remplit report.actual_position avec
les coords effectivement cliquées, le serveur les lit directement.
Pas de cache intermédiaire (option C du plan).
Signature écran V4 : sha256(normalize(window_title))[:16]. Robuste aux
données variables, faux positifs rattrapés par le post-cond qui
décrémente la fiabilité via record_failure().
Fichiers :
- agent_v0/server_v1/replay_memory.py : nouveau wrapper 316 lignes
exposant compute_screen_sig/memory_lookup/record_success/failure,
lazy-init du store, normalisation texte stable, garde sanity coords
- agent_v0/server_v1/resolve_engine.py : lookup mémoire en tête de
_resolve_target_sync (30 lignes)
- agent_v0/server_v1/replay_engine.py : _create_replay_state stocke
une copie slim des actions (sans anchor base64) pour retrouver le
target_spec par current_action_index
- agent_v0/server_v1/api_stream.py : 4 callers passent actions=...,
record success/failure dans /replay/result lit actual_position
du rapport (click-only), correction du commentaire Pydantic
- agent_v0/agent_v1/core/executor.py : remplit result["actual_position"]
après self._click(), transmis dans le report de poll_and_execute
Tests : 56 E2E + Phase0 passent, zéro régression. Cycle Phase 1 validé
en simulation : miss → record → miss → record → HIT au 3ème passage.
Le deploy copy executor.py a une divergence pré-existante de 1302
lignes non committées — traité séparément lors du cleanup prochain.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Filtre d'événements parasites basé sur la CIBLE UIA :
- Un clic n'est filtré que si son uia_snapshot indique que l'élément
cliqué (ou un parent) est dans la fenêtre de Léa.
- Avant : on filtrait sur window.title qui pouvait être "Lea" même
quand le clic visait la taskbar (Léa au premier plan).
- Après : on regarde où va VRAIMENT le clic via parent_path UIA.
Extraction du expected_window depuis le parent_path UIA :
- Priorité au nom de la fenêtre racine du parent_path (plus fiable).
- Fallback sur window.title si pas de snapshot UIA ou pas de racine.
- Les fenêtres Léa sont neutralisées (effective_title="").
Pré-vérif avec polling tolérant (executor.py) :
- 5 tentatives avec 300ms entre chaque (total 1.5s max).
- Ignore les transitions "unknown_window" et fenêtre Léa.
- Évite les faux négatifs sur fenêtres en cours de changement.
Note : le filtrage reste basé sur des heuristiques. Un tri intelligent
par gemma4 au build reste à implémenter pour gérer les workflows
enregistrés avec des actions parasites (mail, chat, etc.).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Corrections critiques après test E2E qui montrait des clics au mauvais endroit :
1. Routage par machine_id (api_stream.py)
Quand 2 machines partagent le même session_id (agent_demo_user),
les actions d'un replay pour la VM ne doivent PLUS être distribuées
au PC physique. Vérification que le replay_state appartient bien à
la machine qui poll avant de consommer la queue.
2. IRBuilder extrait expected_window_before/after (ir_builder.py)
Pour chaque action click/type/key_combo, stocke le titre de la fenêtre
au moment du clic (before) et le titre du prochain événement (after).
Ces champs alimentent le contrôle strict au runtime.
3. ExecutionCompiler crée SuccessCondition title_match (execution_compiler.py)
Quand expected_window_after est défini, crée une condition de succès
STRICTE avec method="title_match" et expected_title. Plus de simple
"l'écran a changé" — on vérifie la fenêtre résultante.
4. Runner propage expected_window_before et success_strict
Le flag success_strict indique à l'agent que le contrôle post-action
DOIT être strict (STOP sur mismatch au lieu de warning).
5. UIA strict sur parent_path (executor.py)
_resolve_via_uia_local REJETTE un match si l'élément trouvé n'est pas
dans la bonne fenêtre parente (évite ex: "Rechercher" taskbar confondu
avec "Rechercher" explorateur).
6. Pré/post vérif stricte et bloquante (executor.py)
- expected_window_before lu en priorité depuis l'action (plan V4)
- Post-vérif : si success_strict=True et timeout, result.success=False
→ le replay s'arrête au lieu de continuer avec des warnings.
Validé sur la VM :
- Le replay s'arrête proprement quand l'étape 2 aboutit dans "Propriétés de
Internet" au lieu de "blocnote.txt - Bloc-notes"
- Plus de clics en aveugle / saisie au mauvais endroit
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Câblage agent Windows pour le pipeline V4 :
captor.py — capture UIA pendant l'enregistrement
- _inject_uia_snapshot() appelé après chaque clic
- Ajoute evt['uia_snapshot'] = {name, control_type, parent_path, ...}
- Non-bloquant : fallback silencieux si helper absent
- ~10-20ms par clic, pas de ralentissement perceptible
executor.py — résolution UIA locale au replay
- _resolve_via_uia_local() : appelle lea_uia.exe find via UIAHelper
- Court-circuit prioritaire avant le GroundingEngine serveur
- Activé quand resolve_order[0] == "uia" et target_spec.uia_target présent
- Coordonnées pixel-perfect (bounding_rect → center)
- Fallback transparent vers le grounding serveur si UIA échoue
uia_helper.py copié dans agent_v1/core/ (wrapper Python pour lea_uia.exe)
Auto-détection du binaire dans C:\Lea\helpers\lea_uia.exe
Singleton partagé get_shared_helper()
Déployé et validé sur la VM Windows :
- query_at(100,100) → "Bureau 1" en 10ms depuis Python
- Binaire lea_uia.exe trouvé et fonctionnel
- Les 3 modules Python sont dans C:\Lea\agent_v1\core\
Ce qui est maintenant possible (après redémarrage de Léa sur la VM) :
- Enregistrer un workflow : chaque clic aura un uia_snapshot
- Compiler via /workflow/compile : plan V4 avec stratégie UIA primaire
- Rejouer via /replay/plan : l'agent utilise UIA (10-20ms) au lieu de VLM (2-5s)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quand le magnétoscope ne trouve pas la cible, au lieu de la pause
supervisée, gemma4 (Docker port 11435, think=True) reçoit le contexte
(action prévue + fenêtre active) et décide :
- PASSER : le résultat est déjà atteint (onglet actif, dialog ouvert)
- STOPPER : état incohérent (mauvaise app)
- EXECUTER : fallback vers la pause supervisée
Testé : gemma4 décide PASSER quand l'onglet est déjà actif (5s).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
La description de la cible dans les notifications et logs utilise
by_text et window_title au lieu de by_role="yolo" qui n'a pas de
sens pour l'utilisateur.
Testé : gemma4 en mode texte (CPU, 0.2s) prend la décision "PASSER"
quand l'onglet est déjà actif. Base pour l'acteur intelligent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compare 'Bloc-notes' (après le –) au lieu du titre complet.
'blocnote.txt – Bloc-notes' et 'voiture.txt – Bloc-notes'
sont la même app → pré-vérif et post-vérif passent.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Après chaque clic, poll le titre de la fenêtre active toutes les 300ms
jusqu'à ce qu'il corresponde au titre attendu (max 10s).
100% visuel — pas de wait arbitraire.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pré-vérification : avant chaque clic, vérifie que le titre de la
fenêtre active correspond à celui de l'enregistrement. Stop si mismatch.
Post-vérification : après chaque clic, vérifie que le titre a changé
vers expected_window_title (titre du prochain clic). Warning si mismatch.
expected_window_title enrichi dans build_replay depuis la séquence des clics.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pour les environnements Citrix avec détection de robots :
- Souris : courbe de Bézier quadratique avec déviation aléatoire
et vitesse variable (25 étapes, plus lent début/fin)
- Texte : frappe caractère par caractère via KeyCode.from_char()
avec délai aléatoire 40-120ms (pas de copier-coller)
- Plus de presse-papiers (Ctrl+V détectable)
Annulation du fix raw_keys→clipboard (plus nécessaire).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Crop réduit de 150x150 à 80x80 (config + fallback serveur)
Plus discriminant pour les icônes de barre de titre
2. Email AZERTY : supprimer raw_keys quand le texte contient des
chars fusionnés depuis key_combos (@ de AltGr) → copier-coller
Le @ était perdu car absent des raw_keys individuels
3. Anchor match : template matching sur screenshot entier puis
élément SomEngine le plus proche (max 100px)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Le template matching compare des pixels et donne des faux positifs
quand l'écran n'est pas dans le même état que l'enregistrement.
SomEngine + VLM comprend sémantiquement ce qu'on cherche.
Nouvelle cascade :
1. Serveur SomEngine + VLM (compréhension sémantique)
2. Template matching local (fallback si serveur down)
3. VLM local (fallback dev/test)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- from .config → from ..config (executor.py est dans core/, config dans agent_v1/)
- run_agent_v1.py charge config.txt et .env au démarrage (fonctionne sans Lea.bat)
- Ajout file logging dans agent_debug.log pour diagnostic Windows
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Feature 4 — Résolution serveur :
- Nouvelle méthode _server_resolve_target() dans executor.py
- Cascade : template local → serveur /resolve_target → VLM local (fallback)
- Popup handling via serveur aussi
- L'agent Windows peut maintenant résoudre les clics via SomEngine+VLM
Feature 5 — VLM multi-image :
- _resolve_by_som() envoie l'anchor crop en 2ème image au VLM
- Le VLM voit les marks numérotés + le crop de l'élément recherché
Feature 6 — Métriques de résolution :
- resolution_method, resolution_score, resolution_elapsed_ms
- Propagés agent → serveur via /replay/result
- Résumé en fin de replay (méthodes, score moyen, temps moyen)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auto-stop : notification 10 min avant, arrêt automatique après MAX_SESSION_DURATION_S (1h)
- Lea.bat : kill des anciens process (python, pythonw, rpa-agent) au démarrage
- LISEZMOI : simplifié pour les collaborateurs (pas de replay, juste collecte)
- Chat server (5004) vérifié fonctionnel
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Popup handling via double appel VLM (détection + localisation précise du bouton)
- Reconstruction texte depuis raw_keys (numpad /, @ AltGr fusionné)
- Clipboard paste pour texte riche, raw_keys pour commandes simples (Win+R)
- Skip des release orphelins dans raw_keys (fix menu Démarrer parasite)
- Auth Bearer sur toutes les requêtes agent → streaming server
- Endpoints /replay/next et /stream/image publics (agent Rust legacy)
- alt_gr ajouté dans _MODIFIER_ONLY_KEYS
- _key_combo_printable_char détecte ctrl+@ comme caractère imprimable
- start.bat tue les anciens process (python + rpa-agent) au démarrage
- Heartbeat avec token Bearer dans main.py et deploy/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Worker VLM séparé :
- run_worker.py : process distinct du serveur HTTP
- Communication par fichiers (_worker_queue.txt + _replay_active.lock)
- Service systemd rpa-worker.service
- Le serveur HTTP ne charge plus CLIP/VLM (mode léger)
- StreamProcessor._ensure_initialized() désactivé dans le serveur
VLM direct depuis l'agent :
- L'agent appelle Ollama directement (port 11434, LAN)
- Ollama configuré sur 0.0.0.0 (OLLAMA_HOST)
- Pas de passage par le serveur streaming (évite le blocage GIL)
- Fallback serveur supprimé (VLM direct ou STOP)
Popup handler hybride :
- VLM identifie le bouton ("Oui", "OK") — pas de coordonnées
- Template matching localise le texte sur l'écran (PIL + cv2)
- _find_text_on_screen() : rend le texte en image, matchTemplate
- _vlm_identify_popup_button() : prompt simple, prefill texte
Resolve visuel hybride :
- Cascade : template anchor → VLM+template texte → VLM direct (legacy)
- _hybrid_vlm_resolve() : VLM identifie + template localise
- _template_match_anchor() : match direct crop, seuil 0.80
- Seuil strict 0.90 pour template matching en mode replay
Analyse VLM temps réel désactivée :
- process_screenshot() ne fait plus de VLM (stockage uniquement)
- L'analyse est différée au worker séparé
- Le serveur HTTP reste réactif en permanence
VLM prefill fix :
- num_ctx augmenté (2048 → 8192 pour images 1080p)
- bbox_2d au lieu de click_point (plus fiable)
- Coordonnées 0-1000 (format natif qwen3-vl)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Léa se présente comme "assistante basée sur l'intelligence artificielle"
- Dialog consentement avant enregistrement (capture écran/clavier)
- Rétention logs 180 jours (Article 12 + 26(6))
- Bouton ARRÊT D'URGENCE toujours visible (Article 14)
- Transparence mode autonome explicite (Article 50)
- Rapport conformité AI Act en français (docs/CONFORMITE_AI_ACT.md)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Floutage (conformité AI Act) :
- Détection OpenCV des champs de saisie (rectangles clairs avec texte)
- Flou gaussien avant stockage/envoi
- Activé par défaut (RPA_BLUR_SENSITIVE=true)
- <200ms par screenshot, 12 tests
Fix actions fichiers VWB :
- Pas de wait 5s pour les actions fichiers (inutile)
- Routing direct vers agent port 5006
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 5 actions : lister, créer dossier, déplacer, copier, classer par extension
- Exécution sur Windows via agent port 5006
- Sécurité chemins (bloque C:\Windows, /etc, etc.)
- Propriétés panel + preview canvas pour chaque action
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CaptureServer : serveur HTTP daemon sur l'agent Windows
- Capture fraîche mss en ~94ms à chaque requête
- Plus de lecture de vieux heartbeats sur disque
- Fallback capture locale si agent indisponible
- Firewall Windows port 5006 configuré
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Suppression du .git embarqué dans agent_v0/ — le code est maintenant
tracké normalement dans le repo principal.
Inclut : agent_v1 (client), server_v1 (streaming), lea_ui (chat client)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>