Cleanup post-review QW1 :
- Émission bus lea:monitor_routed dans /replay/next (idx, source, replay_id, action_id, offset, wh)
via logger.info "[BUS] lea:monitor_routed ..." (le serveur streaming n'a pas
de SocketIO local, agent_chat émet déjà lea:* sur 5004 ; ici on logge en INFO
bien lisible, prêt pour un parser/pont futur)
- Executor Agent V1 (deploy/windows_client) lit action.monitor_resolution.{offset_x, offset_y, idx}
et applique l'offset aux coords absolues du clic/type/scroll/popup quand idx >= 0
- composite_fallback (idx=-1) : pas d'offset appliqué (backward compat mono-écran)
- Log INFO "QW1 monitor cible idx=N source=X offset=(dx,dy) — appliqué aux coords"
émis une fois par action quand un offset non nul s'applique
Tests : baseline 95 passed (e2e + phase0_integration + stream_processor + monitor_router + grounding_offset)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Côté client Agent V1 :
- helpers _get_monitors_geometry() / _get_active_monitor_index() via screeninfo
(fallback gracieux [] / None si screeninfo absent)
- _enrich_with_monitor_info() ajouté aux payloads dict de capture_dual,
capture_active_window, et heartbeat_event poussé par main.py
- screeninfo>=0.8 ajouté aux requirements (source + deploy Windows)
- Deploy capturer.py reçoit l'enrichissement de manière additive (pas de
copie verbatim qui aurait introduit BLUR_SENSITIVE absent côté deploy)
Côté serveur :
- import resolve_target_monitor depuis monitor_router (créé en QW1.1)
- /replay/next : enrichissement action.monitor_resolution avant envoi
au client (idx, offset_x/y, w, h, source de la décision)
- live_session_manager.add_event : propagation monitor_index +
monitors_geometry depuis window_capture ET depuis le payload event
brut (cas heartbeat enrichi sans window/window_title)
Cascade de résolution (cf monitor_router.py) :
1. action.monitor_index (hérité de la session source)
2. session.last_focused_monitor (focus actif vu en dernier heartbeat)
3. composite_fallback (offset 0,0) — backward compat strict
Backward 100% : si geometry vide, fallback composite identique au
comportement actuel mss.monitors[0].
Tests : baseline 89/89 préservée, monitor_router 4/4 OK (total 93/93).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
_capture_screen() accepte un monitor_idx optionnel (None = composite legacy).
Index logique 0..N-1 mappé sur mss.monitors[idx+1] (mss[0] = composite).
Les 3 niveaux de grounding (OCR, UI-TARS, VLM) propagent l'offset retourné
par la capture pour traduire les coordonnées locales monitor en coordonnées
absolues écran (correct pour pyautogui.click).
find_element_on_screen() accepte monitor_idx et le forwarde aux 3 niveaux.
Backward 100% : monitor_idx=None partout → comportement strictement actuel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six modifications structurelles côté serveur, non destructives, aboutissant à un
pipeline replay bien plus stable pour la démo GHT Sud 95 (Urgences UHCD).
1. visual_workflow_builder/backend/app.py
load_dotenv() chargeait .env (cwd) au lieu de .env.local racine projet.
Conséquence : RPA_API_TOKEN absent après chaque restart manuel du backend
et tous les proxies VWB→streaming échouaient en 401 « Token API invalide ».
Charge maintenant explicitement .env.local du project root.
2. visual_workflow_builder/backend/api_v3/learned_workflows.py
Quatre appels proxy /api/v1/traces/stream/* ne portaient pas le Bearer.
Helper _stream_headers() factorisé et appliqué (workflows list/detail,
workflow detail, reload-workflows).
3. visual_workflow_builder/backend/api_v3/dag_execute.py
_ANCHOR_CLICK_TYPES excluait type_text/type_secret : pas de pre-click de
focus avant la frappe → texte tapé sans focus → textareas vides au replay.
Helper _inject_anchor_targeting() factorisé (centre bbox + visual_mode +
target_spec) appliqué aux click_anchor* ET aux type_text/type_secret dès
qu'un anchor_id est présent. Workflows historiques sans anchor sur
type_text → comportement inchangé.
4. agent_v0/server_v1/api_stream.py — endpoint /replay/next
_replay_lock (threading.Lock global) tenu pendant les actions serveur
lentes (extract_text OCR ~5s, t2a_decision LLM ~8-13s). Comme le handler
est async def, l'event loop FastAPI était bloqué : les polls clients
timeout à 5s, leurs actions étaient popped serveur sans destinataire,
perdues silencieusement. Mesure : 8 actions/25 perdues sur replay Urgence.
acquire(timeout=4.5) puis run_in_executor pour libérer l'event loop
pendant l'attente du lock ET pendant les handlers serveur synchrones.
Pendant un t2a_decision en cours, les polls concurrents reçoivent
immédiatement {action: null, server_busy: true} → l'agent ne timeout
plus, aucune action n'est popped sans destinataire.
5. agent_v0/server_v1/resolve_engine.py — _validate_resolution_quality
Drift > 0.20 par rapport aux coords enregistrées → fallback aux coords
enregistrées même quand le template matching trouve l'image avec un
score quasi parfait. Or un score >= 0.95 signifie que l'image EST
visuellement à l'écran à l'endroit indiqué, le drift reflète juste
un changement de layout (scroll, F11, redimensionnement), pas une
erreur. Exception ajoutée : score >= 0.95 sur template_matching →
ignore drift check, utilise position visuelle.
6. core/llm/t2a_decision.py — prompt T2A/PMSI
Ancien prompt autorisait « Critère non validé » en fallback creux.
Nouveau prompt impose au moins une CITATION LITTÉRALE entre « ... »
du DPI dans chaque preuve_critereN, qu'elle soutienne ou infirme le
critère. Si non validé : factualisation explicite (« Aucune ... »,
« Sortie à H+2 ») citée du dossier. Sortie = preuves cliniques
traçables et professionnelles, pas du remplissage.
État DB : aucun changement net (bbox patchés puis revertés depuis backup
visual_anchors_backup_20260501 ; by_text re-aligné sur 25003284). Le
re-enregistrement du workflow Urgence en conditions bureau standard
(Chrome normal, taille fenêtre standard) est l'étape suivante côté Dom.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dag_execute.py /execute-windows :
- Bearer token sur appels VWB→streaming (machines, replay/raw).
Sans cela : 401 Unauthorized et le workflow ne démarre pas.
- Auto-injection session_id='agent_demo_user' si absent.
Sans cela : /replay/raw bascule sur l'auto-détection sess_* et lève
"Aucune session Agent V1 active" après tout restart du streaming server.
- Propagation by_text dans target_spec pour ciblage textuel
(résolution hybrid_text_direct côté executor) — utile quand
deux numéros se ressemblent visuellement (ex 25003284 vs 2500341).
t2a_decision.py : prompt enrichi avec decision_court (UHCD / Forfait
Urgences) + 3 critères PMSI (preuve_critereN + critereN_valide booléen)
pour piloter case-à-cocher dans l'arbre décisionnel. num_predict=1500,
num_ctx=16384.
resolve_engine.py : un drift trop grand bascule sur les coords
enregistrées (fallback_recorded_coords, resolved=True) au lieu de
rejeter la résolution. Permet au replay de continuer en cas de scroll
plutôt que de s'arrêter net.
workflows.db : by_text='25003284' sur le step de sélection patient
du workflow Urgence (démo GHT Sud 95).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sert le statique de docs/clients/ght_sud_95/mockup_easily_assure/
sur le port 8765 (auto-restart, démarre au boot). Proxifié en
HTTPS via NPM sur urgence.labs.laurinebazin.design avec Basic Auth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cause racine : le useEffect d'ajout à la bibliothèque écoutait la prop
'capture' venant du parent. Le path 'agent Windows distant' (doSmartCapture
quand l'agent V1 répond) faisait setCurrentCapture(state local) mais ne
déclenchait jamais la prop parente — donc useEffect [capture] ne tirait pas,
donc addCaptureToLibrary jamais appelé. La capture s'affichait, mais rien
n'était persisté côté backend.
Fix :
- Factorisation de l'ajout dans un useCallback addToLibrary(cap)
- Appel explicite après setCurrentCapture dans doSmartCapture
- Le path fallback local (via prop capture) garde le useEffect [capture]
qui appelle aussi addToLibrary
Erreurs d'upload (réseau, backend down) avalées silencieusement avec
console.warn — la capture locale reste utilisable même si le backend
de bibliothèque est indisponible.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug terrain : le bouton 'Timer' déclenchait toujours une capture immédiate
même après sélection d'un délai dans le menu déroulant. Le retour utilisateur
'le bouton ne change pas' a confirmé qu'il n'y avait aucun feedback visuel
sur le délai sélectionné, donc impossible de diagnostiquer.
Changements :
- timerSeconds default 5s (préférence Dom) au lieu de 0 (Immediat)
- Label dynamique du bouton :
countdown actif → '5…' '4…' etc.
délai 0 → 'Timer' (capture immédiate)
délai > 0 → 'Capturer dans 5s'
- Select préfixé par 'Délai :' pour clarifier
- Conversion explicite String(timerSeconds) sur value du select pour éviter
toute ambiguïté number/string
- console.log temporaire au changement de select pour faciliter le diagnostic
si le bug persiste (à retirer après validation)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Le tooltip natif HTML montrait juste le label ('Clic'). Maintenant il affiche :
- Le label
- La description complète (existait déjà dans types.ts mais non exposée)
- L'indication 'ancre requise' si applicable
- La liste des paramètres configurables
Le badge 🎯 a aussi son propre tooltip explicatif.
Aide à la prise en main du VWB pour la construction de workflows démo
(retour terrain Dom : 'il y a des outils dont je ne sais pas à quoi ils servent').
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nouvelle action native VWB qui force le replay à basculer en paused_need_help
avec un message custom. Quand Léa atteint cette étape, elle ne tente pas
d'exécuter — elle pose immédiatement le state, ce qui déclenche la bulle
interactive ChatWindow (J3.5) avec boutons Continuer / Annuler.
Asset démo majeur GHT Sud 95 : permet de scénariser le moment "Léa doute"
au bon endroit dans le workflow, sans dépendre d'un échec aléatoire.
Chaîne complète :
- VWB UI (types.ts) : nouvelle entrée ACTIONS catégorie 'logic', icône ⏸,
paramètre 'message' éditable (textarea).
- Bridge VWB → core (learned_workflow_bridge.py) : passthrough du type +
préservation du message dans parameters.
- Pipeline replay (replay_engine.py) : type ajouté à _ALLOWED_ACTION_TYPES,
conversion edge → action normalisée préserve le message.
- Streaming server (api_stream.py /replay/next) : interception avant envoi
à l'Agent V1 → bascule state en paused_need_help avec pause_message,
retourne {action: None, replay_paused: True}.
- L'action n'est jamais transmise à l'Agent V1 — pure logique serveur.
10 nouveaux tests pytest. Total branche : 57/57 verts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Quand Léa bascule en pause supervisée (event 'lea:paused'), affichage d'une
bulle dédiée dans ChatWindow avec encadré orangé, raison de la pause, et deux
boutons Continuer/Annuler. C'est le moment qui incarne la différence RPA classique
vs Léa devant Carvella : Léa SAIT qu'elle ne sait pas et demande de l'aide.
Architecture (canal SocketIO bidirectionnel, pas de nouvel endpoint streaming) :
ChatWindow ──[lea:replay_resume]──> agent_chat ──POST /resume──> streaming
ChatWindow ──[lea:replay_abort ]──> agent_chat (running=False local)
Composants ajoutés :
- agent_chat/app.py : handlers 'lea:replay_resume' / 'lea:replay_abort' +
acks 'lea:resume_acked' / 'lea:abort_acked' pour feedback côté client
- network/feedback_bus.py : méthodes resume_replay() / abort_replay() avec
helper _safe_emit (silencieux + retourne bool succès)
- ui/chat_window.py : palette PAUSED_*, _add_paused_bubble(),
_render_paused_bubble(), _close_active_paused_bubble() (auto-fermeture
sur lea:resumed/done), _on_paused_resume/abort
8 nouveaux tests pytest (4 handlers serveur + 4 méthodes client).
Total branche : 29/29 verts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Import fail-safe : si python-socketio manquant (ancienne install Pauline),
_HAS_FEEDBACK_BUS=False, ChatWindow tourne normalement sans bus
- Bus démarré à la fin de _run_tk_loop si LEA_FEEDBACK_BUS=1 dans l'env
- Callback _on_lea_event → _add_lea_message (thread-safe via root.after)
- Cleanup : _bus.stop() ajouté dans _do_destroy avant la destruction tkinter
Formatage des bulles minimal pour J3.3 (texte brut "[event] key=value").
Le style mixte métier+tech viendra en J3.4. La bulle paused interactive J3.5.
Aucun crash si bus indisponible. Aucun changement de comportement si flag off.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consomme les events 'lea:*' émis par agent_chat (port 5004) et les dispatche
vers un callback fourni par ChatWindow (J3.3 à venir).
Caractéristiques :
- Connexion en thread daemon (non-bloquant pour la mainloop tkinter)
- Reconnect auto illimité (delay 2s → 30s exponentiel)
- Auth Bearer Token via header HTTP au handshake
- Fail-safe : connect échoué, callback qui raise, disconnect qui raise
→ tout silencieusement loggé, ChatWindow continue normalement
13 tests pytest verts (tests/integration/test_feedback_bus_client.py).
Pas de connexion réseau réelle dans les tests (python-socketio mocké).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Compatible Flask-SocketIO 5.3.x côté serveur. Ajouté aux deux requirements
client (agent_v1/ et deploy/windows_client/) — le second est utilisé par
l'installeur Pauline (setup_v1.bat).
ATTENTION : redéploiement client requis (PC Windows + VM Linux) avant la démo
GHT Sud 95. La dep ne sert à rien tant que J3.2 (FeedbackBusClient) n'est pas en
place ; aucun impact runtime sur l'agent V1 actuel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Le pHash global 8x8 sur écran 1920x1080 ne détecte pas l'ouverture d'un
dialog modal dans une VM QEMU (un dialog 800x500 couvre ~3 pixels pHash,
distance Hamming typique = 1-2, sous le seuil de 3). Découvert sur Win11/
Notepad : Ctrl+Shift+S ouvrait bien le dialog mais Léa abortait à tort.
_handle_post_shortcut() poll désormais DialogHandler.handle_if_dialog()
toutes les 500ms (EasyOCR + KNOWN_DIALOGS). 8s pour le premier dialog,
3s de stabilité entre dialogs successifs, 60s budget total.
KNOWN_DIALOGS réordonné : popups modaux (confirmer/remplacer/écraser)
prioritaires sur fenêtres parents (enregistrer sous/save as) car l'OCR
full-screen capte les deux simultanément.
DialogHandler bascule sur UITarsGrounder subprocess one-shot (au lieu
du serveur HTTP localhost:8200 qui n'existait plus). InfiGUI worker,
think_arbiter et ui_tars_grounder alignés sur le même contrat.
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
docs/CARTOGRAPHY.md :
- Carte complète des 2 chemins d'exécution (Legacy vs ORA)
- 12 systèmes de grounding identifiés dont 3 morts
- Trace du champ target_text de la capture au clic
- Fonctions existantes non branchées (verify, recovery, ShadowLearningHook)
- Budget VRAM, fichiers critiques, règles de modification
Fix target_text ORA (observe_reason_act.py:217) :
- Détecte les target_text absurdes ("click_anchor")
- Appelle _describe_anchor_image() (VLM) pour décrire le crop
- Même logique que le legacy execute.py:893
Worker InfiGUI via fichiers /tmp :
- Communication par fichiers (pas subprocess pipes, pas HTTP)
- Process indépendant lancé avant le backend
- Résout le crash CUDA dans Flask/FastAPI/uvicorn
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Nouveaux patterns :
- dialog_overwrite : "voulez-vous remplacer/écraser", "fichier existe déjà" → Oui
- dialog_dont_save : "ne pas enregistrer", "quitter sans enregistrer" → Ne pas enregistrer
Handler amélioré (handle_detected_pattern) :
- EasyOCR au lieu de docTR (meilleure lecture des boutons GUI)
- Match par inclusion (pas seulement exact)
- Suppression fallback VLM (Ollama n'a plus de VRAM)
- Prints visibles pour diagnostic
28 patterns au total, testés sur 6 dialogues types.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Serveur de grounding (server.py) :
- InfiGUI-G1-3B au lieu de UI-TARS-1.5-7B
- VRAM : 2.25 GB au lieu de 8.4 GB (6.6 GB libres)
- Prompt officiel InfiGUI (system <think> + user point_2d JSON)
- max_new_tokens=512, parsing JSON point_2d
- 4/4 éléments trouvés : Demo 5px, Chrome 98px, Corbeille 15px, Search 66px
- Fallback UI-TARS via env GROUNDING_MODEL=ByteDance-Seed/UI-TARS-1.5-7B
EasyOCR : retour sur GPU (assez de VRAM maintenant) → 192ms au lieu de 2.5s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FastDetector : EasyOCR GPU en singleton (~192ms vs 1300ms docTR = 6.8x)
- "Corbeille" lu correctement (docTR lisait "Gorbeille")
- "Google Chrome" en deux mots propres
- Détection complète (RF-DETR + OCR) en 313ms à chaud
- Fallback docTR si EasyOCR non disponible
TitleVerifier : EasyOCR pour le crop titre (fallback docTR)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Réflexe check : déclenché uniquement si pHash change (popup inattendu),
plus d'OCR full screen systématique à chaque step. Gain ~9s/workflow.
Serveur grounding : max_new_tokens 256→64 (la réponse fait ~20 tokens).
Validé : 5+ tests consécutifs 7/7, apprentissage actif
(CR_patient en fast_exact_text 2.2s, Feuille calcul en template 83ms).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ShadowLearningHook (core/grounding/shadow_learning_hook.py) :
- Hook optionnel pour le ShadowObserver
- Chaque clic humain observé → FastDetector détecte l'élément sous le clic
- SignatureStore enrichie avec texte, type, position, voisins (conf=1.0)
- Au replay : SmartMatcher utilise la signature apprise → matching < 1ms
Validé : 3 clics simulés → 3 signatures créées avec les bonnes métadonnées.
Module standalone — ne modifie pas le ShadowObserver existant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_act_click() utilise maintenant le pipeline FAST→SMART→THINK :
- Feature flag RPA_USE_FAST_PIPELINE=1 (activé par défaut)
- RPA_USE_FAST_PIPELINE=0 pour rollback sur l'ancien pipeline
- Si le nouveau pipeline échoue → fallback automatique template→OCR→static
- Pre-check VLM désactivé (le pipeline valide visuellement)
- Capture unique de l'écran partagée entre tous les layers
Rollback instantané : unset RPA_USE_FAST_PIPELINE
Tests : 37 passed, 0 régression
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FastSmartThinkPipeline (core/grounding/fast_pipeline.py) :
- Cascade : FAST detect (120ms) → SMART match (<1ms) → THINK VLM si doute (3s)
- Seuils : ≥0.90 action directe, 0.60-0.90 VLM confirme, <0.60 VLM cherche
- Apprentissage automatique : SignatureStore enrichie à chaque succès
- Ancien pipeline en fallback (safety net)
- Singleton via get_instance()
Validé sur 5 éléments :
- 1ère exécution : 5/5 OK via smart_think_confirmed (24.5s total)
- 2ème exécution : 4/5 en FAST direct, 1/5 en THINK (10.5s total)
- L'apprentissage réduit le temps de 20x par élément connu
Module standalone — aucun impact sur le système existant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ThinkArbiter (core/grounding/think_arbiter.py) :
- Client HTTP vers le serveur UI-TARS (port 8200)
- Appelé uniquement si SmartMatcher score < 0.60
- Vérifie la disponibilité du serveur avant appel
- Validé : Demo trouvé à (1479, 183) en 3.6s
SignatureStore (core/grounding/element_signature.py) :
- Stockage SQLite des signatures d'éléments UI apprises
- record_success() enrichit la signature (texte, type, position, voisins)
- record_failure() incrémente le compteur d'échecs
- lookup() avec fallback (contexte exact → toutes variantes)
- Validé : 3 succès → conf_moy=0.917, voisins enrichis
Modules standalone — aucun impact sur le système existant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Architecture grounding complète :
- core/grounding/server.py : serveur FastAPI (port 8200) avec UI-TARS-1.5-7B en 4-bit NF4
Process séparé avec son propre contexte CUDA (résout le crash Flask/CUDA)
- core/grounding/pipeline.py : orchestrateur cascade template→OCR→UI-TARS→static
- core/grounding/template_matcher.py : TemplateMatcher centralisé (remplace 5 copies)
- core/grounding/ui_tars_grounder.py : client HTTP vers le serveur de grounding
- core/grounding/target.py : GroundingTarget + GroundingResult
ORA modifié :
- _act_click() : capture unique de l'écran envoyée au serveur de grounding
- Pre-check VLM skippé pour ui_tars (redondant, et Ollama n'a plus de VRAM)
- verify_level='none' par défaut (vérification titre OCR prévue en Phase 2)
- Détection réponses négatives UI-TARS ("I don't see it" → fallback OCR)
Nettoyage :
- 9 fichiers morts archivés dans _archive/ (~6300 lignes supprimées)
- 21 tests ajoutés pour TemplateMatcher
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Double-clic : moveTo + 2 clics explicites (pyautogui.doubleClick ne
traverse pas toujours la VM). Délai 80ms entre les clics.
Vérification : un double-clic DOIT produire un changement majeur
(ouverture fichier/dossier). Changement mineur = échec → retry.
Les clics simples et hotkeys gardent la tolérance actuelle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pattern.get() crashait car pattern=None quand l'overlay n'est pas
un dialogue connu. Ajout de guard None.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pyautogui.hotkey('super','d') ne traverse pas la VM.
xdotool key super+d avec setxkbmap fr fonctionne.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pyautogui.click cliquait SUR Chrome. xdotool search --name QEMU
trouve la fenêtre VM et la force au premier plan avant Win+D.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Alt+Tab bascule entre fenêtres. Win+D affiche le bureau Windows.
Plus fiable quand l'élément cible est sur le bureau.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
windowminimize minimisait en boucle toutes les fenêtres (VM incluse).
Alt+Tab bascule juste le focus sans rien fermer/minimiser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quand la pré-vérification dit NO et qu'aucun pattern de dialogue n'est
détecté, c'est une fenêtre quelconque qui masque la cible (Chrome, etc).
xdotool windowminimize pour la dégager.
Classification améliorée : pré-check rejeté → OVERLAY_BLOCKING
(avant c'était ELEMENT_NOT_FOUND → scroll inutile).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Avant de cliquer, crop 200x100 autour de la position cible envoyé
au VLM (qwen2.5vl:3b) : "Is this UI element 'CR_patient_demo'? YES/NO"
Si NO → abandon du clic, évite les clics erronés.
Si erreur VLM → laisse passer (pas bloquant).
Skippé pour le template matching (confiance pixel suffisante).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Le logging Python ne traverse pas le nohup de Flask. Tous les autres
modules (execute.py, intelligent_executor.py) utilisent print().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ordre : UI-TARS (3s, 94%) → Template (80ms) → OCR (1s)
UI-TARS dit "click on CR_patient_demo" et trouve les coordonnées
comme un humain. Le template matching échoue sur les icônes Windows
(micro-différences visuelles → score 0.38 au lieu de 0.95).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
L'OCR seul donnait du bruit (\"- C\", \"emo\"). Le VLM (qwen2.5vl:3b)
est maintenant appelé systématiquement pour décrire l'ancre en 5 mots
(\"folder icon named Demo\", \"search bar with magnifier icon\").
Le target_text utilise l'OCR si lisible, sinon la description VLM.
La description VLM est toujours stockée dans ocr_description.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Logs : forcer le handler stdout pour que les logs ORA apparaissent
dans nohup (logger.info n'écrivait nulle part).
Vérification : un clic avec confiance >= 0.7 est accepté même si
l'écran ne change pas (pHash same). Un clic sur un champ de saisie
ne modifie quasi pas l'écran mais est légitime.
Changement mineur toujours accepté (plus de condition confiance > 0.9).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>