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>
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>
Pipeline V4 câblé de bout en bout :
RawTrace (avec uia_snapshot) → IRBuilder → Action._enrichment
WorkflowIR → ExecutionCompiler (avec SurfaceProfile) → ExecutionPlan
ExecutionPlan → runner → target_spec (avec uia_target + resolve_order)
ResolutionStrategy étendu :
- Champs UIA : uia_name, uia_control_type, uia_automation_id, uia_parent_path
- Champs DOM : dom_selector, dom_xpath, dom_url_pattern (préparation web)
ExecutionCompiler.compile(surface_profile=...) :
- Timeouts/retries tirés du profil (citrix=15s/3x, web=5s/1x, natif=8s/2x)
- UIA primaire seulement si surface=WINDOWS_NATIVE et uia_available
- Citrix ignore UIA même si snapshot présent (UIA ne marche pas dans Citrix)
IRBuilder lit evt['uia_snapshot'] et le stocke dans action._enrichment
(à remplir par l'agent Windows pendant l'enregistrement via lea_uia.exe)
execution_plan_runner propage uia_target et dom_target dans target_spec
pour que l'agent Windows puisse les consommer au runtime.
11 tests de câblage E2E :
- Profils (Citrix/web/natif) imposent bien les timeouts
- Stratégie UIA créée quand snapshot+surface OK
- Stratégie UIA bloquée sur Citrix
- IRBuilder propage uia_snapshot
- Runner produit target_spec avec uia_target + resolve_order=['uia', 'ocr', 'vlm']
496 tests au total, 0 régression.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Format canonique entre RawTrace (capture) et ExecutionPlan (exécution).
C'est ce que Léa a COMPRIS en observant l'utilisateur.
- WorkflowIR : steps, variables, intentions, pré/postconditions
- IRBuilder : transforme les événements bruts en WorkflowIR via gemma4
- Générique : fonctionne pour TIM, compta, RH, stocks — le domaine est une couche par-dessus
- Versionné, sérialisable JSON, save/load
- Détection automatique des variables (texte saisi → substituable)
- 18 tests (format, sérialisation, builder, segmentation, variables)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>