231 Commits

Author SHA1 Message Date
Dom
f8dc3c3af4 docs(audit): rapport curateur mémoire Claude — santé index 7 mai 2026
Some checks failed
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Audit exhaustif des 101 fichiers .md de ~/.claude/projects/-home-dom-ai-rpa-vision-v3/memory/.
Aucun fichier mémoire modifié — diagnostic seul, à valider par Dom.

Constats critiques :
- MEMORY.md = 273 lignes (limite chargement 200) → ~73 lignes
  silencieusement perdues à chaque démarrage de session
- ~50% des fichiers réels ne sont pas indexés dans MEMORY.md
- Référence cassée : MEMORY ligne 257 pointe vers
  feedback_pull_not_push.md qui n'existe pas
- 3 feedback NEW créés le 7 mai (non ajoutés à l'index) sont
  précisément les règles qui sécurisent la démo GHT jeudi 8 mai :
  * feedback_orphans_are_projections.md
  * feedback_verifier_avant_apres_clic.md
  * architecture_lea_v1_find_text_client.md

Risque concret : un Claude futur (sans ces feedback en mémoire active)
va reproposer les bourdes que Dom a explicitement nommées hier soir :
"re-capturer les ancres" et "nettoyer les modules orphelins".

Top 7 feedback proposés en TOP CRITICAL :
1. prendre_le_temps (DEVISE)
2. orphans_are_projections (NEW)
3. verifier_avant_apres_clic (NEW)
4. lea_v1_find_text_client (NEW architecture)
5. ollama_vs_transformers
6. no_rustine
7. anonymisation_stricte

Proposition réorganisation 4 zones :
- 🔥 TOP CRITICAL ~12 fichiers
- 📌 ACTIVE ~25 fichiers
- 📚 REFERENCE ~12 fichiers
- 🗄️ ARCHIVE ~50 fichiers

Compactage cible : MEMORY.md → 150 lignes (marge 50 avant
retrigger limite chargement).

4 décisions ouvertes pour Dom (cf rapport §8) :
1. feedback_pull_not_push.md — créer ou supprimer la référence
2. Valider l'archivage des ~45 fichiers proposés
3. Trancher 4 fichiers INCERTAIN (dashboard_config, data_extraction,
   objectif_6avril, actor_*)
4. Approuver 7 règles de gestion future (1 feedback = 1 violation
   observée, MEMORY ≤ 180 lignes, rotation sessions > 21j, etc.)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 05:11:08 +02:00
Dom
ca81850a20 docs(audit): rapport médecin DIM senior + TIM sur arbre décisionnel UHCD/Forfait
Some checks failed
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Audit du cœur métier de la démo GHT Sud 95 (8 mai 2026), du point de vue
d'un médecin DIM senior qui se ferait challenger par le DSI Carvella.
Confronte : arbre officiel RPU UHCD IA.pptx (7 slides), code métier
agent_chat/urgences_orchestrator.py + core/llm/t2a_decision.py, prompts
LLM en place, 11 dossiers anonymisés data.js, bench Dom 18 modèles,
référentiels officiels (SFMU 2024, instructions DGOS, arrêtés 2021/2024
ATIH, recommandations IPAQSS).

Findings critiques (avant démo) :

1. Bug silencieux modèle — t2a_decision.py:28 met DEFAULT_MODEL=qwen2.5:7b
   (64 % accuracy au bench Dom) alors que gemma3:27b-cloud (73 %) est
   retenu par BENCH_T2A_DECISION_11DOSSIERS. Si T2A_MODEL pas posé via
   env, on tourne sur le mauvais modèle. 9 points d'accuracy laissés
   sur la table.

2. Règle de combinaison incorrecte dans le prompt — code dit "au moins
   2 sur 3 ⇒ REQUALIFICATION" alors que l'arbre PPTX d'Eaubonne dit
   "si oui aux 3 critères". Cause probable des faux positifs UHCD du
   bench (25003284, 25056615). Quick win = passer à 3/3.

3. Trous métier dans le prompt : aucune mention CCMU, GEMSA, durée,
   mode de sortie, type de forfait précis (SU2/PE2/Standard). C'est
   exactement où se loge le ROI 100k€/mois. 5 quick wins prompt
   rédigés prêts à coller dans §E.4 du rapport.

4. Trois dossiers à NE PAS montrer en démo (25056615, 25151530, 25003475,
   25048485) — trop ambigus, hallucinations LLM, structure non tranchée.

5. Trois dossiers à mettre en avant (25003451 SU2 plaie 2h, 25010621
   PE2 laryngite, 25003364 UHCD pneumo SLA) — décisions justes,
   justifications béton.

Argumentaire pré-démo : 9 questions/réponses face à Carvella
(instructions DGOS, SFMU, cumul SU2+PE2, hallucination LLM, ROI 100k€).

Roadmap post-démo pour Amina : bench étendu 50-100 dossiers + 3
inférences/dossier, fine-tune t2a-gemma3-27b, distinction forfaits
fine, module ATIH-aware, couverture pédia/géria/psy, sortie contre
avis, transferts.

Note : aucun changement de code dans ce commit. Rapport seul. Les
quick wins identifiés (3/3, modèle par défaut, prompts enrichis)
sont à appliquer demain matin avec validation Dom + Amina.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:21:13 +02:00
Dom
35fd6cf4c5 test(e2e): harness replay reproductible — mock client Léa V1 contre serveur réel
Some checks failed
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Réduit le cycle debug d'un workflow de 1-2 min (replay manuel via
Windows + Léa V1 + maquette) à ~2-5s (mock client Linux contre
serveur de streaming localhost:5005). 30-60× plus rapide.

Architecture :
- tools/test_replay_e2e.py — harness CLI (~580 lignes), reproduit la
  chaîne réelle : VWB /api/v3/execute-windows → streaming /replay/raw
  → boucle /replay/next côté harness avec resolve_target sur un
  screenshot fixture → POST /replay/result. Pas de modification serveur.
- tests/e2e/test_urgence_aiva_demo.py — wrapper pytest (smoke).
- tests/e2e/urgence_aiva_demo_expected.yaml — référence générée par
  --export-expected, pour comparaison régression auto.
- pytest.ini — ajout du marqueur e2e.

Usage :
    python tools/test_replay_e2e.py --execution-mode autonomous --max-iter 120 --verbose
    python tools/test_replay_e2e.py --single-step 8 --shot <heartbeat>.png
    python tools/test_replay_e2e.py --expected tests/e2e/urgence_aiva_demo_expected.yaml
    pytest tests/e2e -v -m e2e

Sortie : tableau Markdown step × méthode × score × pos × status × diag.

Limitations connues (extensions post-démo) :
- Une seule fixture screenshot pour tout le replay → click_anchor réalistes
  échouent dès qu'on dépasse l'écran fixture. Carte step_id → fixture à venir.
- extract_text/table/t2a_decision exécutés côté serveur, observables mais
  pas modifiables.
- Pas de simulation screenshot_after → ReplayVerifier (Critic VLM) ne tourne pas.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:11:07 +02:00
Dom
7847a0e829 feat(agent_v1): toast paused supervisée Tkinter + Plan B + threshold FIND-TEXT 0.75
Some checks failed
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Démo GHT 8 mai 2026 — Dom utilise UNIQUEMENT Léa V1 sur Windows pendant
la démo (pas le frontend VWB Linux), donc les pause_message du serveur
doivent être visuellement évidents sur l'écran Windows. Modifications
client validées par Dom + redéployées via SCP (procédure 2026-04-28).

1. ui/paused_toast.py (NEW) — Toast Tkinter custom autonome :
   Toplevel topmost overrideredirect, fond bleu Léa (#2563EB), 380px,
   haut-droite, auto-close 15s, click-to-close. Re-pin -topmost à
   100/500/2000 ms (Windows démet le flag quand le focus part). Rate
   limit 3s sur message identique. Aucune dépendance externe (tkinter
   stdlib uniquement). Thread-safe : root.after si Tk root existe,
   sinon Tk dédié dans un daemon thread. Remplace plyer qui s'avère
   silencieux sur Windows 11 (Focus Assist + manque app-id COM).

2. ui/chat_window.py — _add_paused_bubble force la visibilité :
   La fenêtre Léa démarrait avec root.withdraw() — la bulle paused
   était bien rendue mais invisible. Ajout deiconify+lift+focus_force
   avant render, plus appel à show_paused_toast en complément.

3. ui/notifications.py — niveau BLOCAGE déclenche aussi le toast :
   Quand notify_message reçoit un MessageUtilisateur.BLOCAGE (cible
   non trouvée, mode apprentissage, fenêtre incorrecte), appelle
   show_paused_toast en plus de plyer. Couvre la branche supervision
   client (executor.py:1012) qui ne passe pas par Plan B serveur.

4. core/executor.py — Plan B replay_paused (lignes 1812-1850) :
   Intercepte data["replay_paused"]=True dans la réponse /replay/next,
   appelle chat_window._add_paused_bubble si _chat_window_ref défini,
   sinon fallback notifier.notify. Idempotence via _last_pause_msg_shown
   pour ne pas spammer (1 toast par (replay_id, message) unique).
   Threshold FIND-TEXT _find_text_on_screen : 0.50 → 0.75 pour rejeter
   les faux positifs (placeholders italiques, tabs voisins) et tomber
   en mode apprentissage humain plutôt qu'un clic au pif.

5. main.py — Wiring ChatWindow → Executor pour Plan B.

6. tools/test_lea_toast.py + ui/_test_paused_toast.py (NEW) — Scripts
   de test isolé pour validation visuelle rapide sans relancer un
   replay complet (commande dans les docstrings).

Validé visuellement sur DESKTOP-58D5CAC. Toasts apparaissent en haut-
droite, fond bleu, auto-close 15s. Test isolé Dom : 3 toasts successifs
visibles sans accroc.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:03:51 +02:00
Dom
40440f1ca0 fix(replay): cure régression b584bbabc — fallback recorded_coords aveugle
Trois changements complémentaires dans la cascade de résolution serveur,
finis ce soir 7 mai pour la démo GHT 8 mai. Restaure le comportement strict
d'avril 2026 (workflow qui passait 20 fois d'affilée sans incident).

1. resolve_engine.py — _validate_resolution_quality (lignes 2255-2289) :
   Le commit b584bbabc du 1er mai 2026 ("fix(stream): démo UHCD") avait
   transformé le rejet strict (resolved=False, method="rejected_drift_*")
   en fallback aveugle (resolved=True, method="fallback_recorded_coords",
   coords du record). Symptôme observé : Léa cliquait sur "Dossier en
   cours" du menu au lieu de "Synthèse Urgences" du tab — le VLM Quick
   Find Ollama hallucinait à (0.526, 0.918), drift dépassé, fallback
   ratait. Restauré : resolved=False explicite, le client passe en
   pause supervisée comme prévu (philosophie échec = apprentissage).

2. resolve_engine.py — exemption high-confidence élargie :
   L'exemption drift>0.20 IGNORÉ ne couvrait que template_matching ≥ 0.95
   (commit 35b27ae49 du 2 mai). Étendue à hybrid_text_direct ≥ 0.80 :
   un OCR direct qui trouve le texte cible exact à score 0.80+ est aussi
   sûr qu'un template à 0.95 — la position est sémantiquement vraie,
   le drift reflète juste un changement de layout (résolution écran,
   refonte UI, scroll), pas une erreur de résolution.

3. resolve_engine.py + api_stream.py — pré-check OCR sémantique :
   Nouvelle fonction _validate_text_at_position (singleton EasyOCR fr+en,
   crop 200px autour de la coord résolue, fuzzy match 60% des tokens
   ≥3 caractères de l'expected_text). Câblée dans api_stream.py juste
   après _validate_resolution_quality. Si le by_text attendu n'est PAS
   présent dans la zone autour de la coord résolue → resolved=False
   method="rejected_text_mismatch" → pause supervisée.

Pattern Verification-Aware Planning (state of the art 2026 — voir
recommandations agent archéologue + agent SOTA review) : le serveur
ne renvoie une coord que s'il est sémantiquement sûr du résultat.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:03:18 +02:00
Dom
7233df2bb9 fix(replay): câblage execution_mode supervised + seuil large fallback heartbeat
Deux corrections liées au scenario démo Urgence GHT (workflow lecture
multi-onglets + t2a_decision + pause_for_human + saisies dans Codage) :

1. Mode supervised propagé jusqu'au pipeline replay
---------------------------------------------------

Symptôme constaté ce 7 mai : Léa lit les onglets, t2a_decision tourne
(variable `dec` présente avec decision="FORFAIT_URGENCE"), mais la
pause_for_human est SKIPPÉE silencieusement et les saisies type_text
s'enchaînent dans le mauvais écran.

Cause : api_stream.py:2140 passait `params={}` codé en dur lors de la
création du replay_state. Conséquence : le code en aval qui lit
`replay_state.params.execution_mode` (api_stream.py:2964) avait toujours
le défaut "autonomous" → branche QW4 :

    # Mode autonome sans safety_checks → skip (comportement legacy)
    logger.info("pause_for_human ignorée (mode autonome)")

Modifications :
- RawReplayRequest gagne un champ `params: Optional[Dict[str, Any]]`
- start_raw_replay propage `request.params or {}` à _create_replay_state
- dag_execute.execute_windows force par défaut
  `data['params']['execution_mode'] = 'supervised'` quand le frontend
  ne précise rien (cas démo VWB → Windows). Override possible.

Conséquence : la pause_for_human du workflow Urgence déclenche bien la
PauseDialog VWB ("Décision : {{dec.decision_court}}"). Le médecin valide
ou annule avant que les saisies type_text ne s'exécutent dans Codage.

Note pour la démo réelle (post-aujourd'hui) : le scénario crédible
veut que Léa soit déclenchée depuis SON chat (port 5004), pas depuis
VWB. C'est un autre commit à venir — pour l'instant VWB suffit pour
le développement (cf. handoff session).

2. Seuil détection image tronquée élargi
----------------------------------------

Le seuil initial (height < 200 OR width < 400) ne capturait que les
cas extrêmes 2560x60 / 600x72. Mais le client envoie aussi 622x856
(Edge en fenêtre réduite ?) qui passait sous le radar. Élargi à
height < 800 OR width < 1200 — un écran moderne fait toujours ≥
1920x1080, donc le seuil est sain.

Sans ce fallback élargi, _resolve_target_sync recevait une image
trop petite pour matcher l'anchor → cascade VLM hallucinante.
2026-05-07 10:34:29 +02:00
Dom
f62fda575f fix(stream): /resolve_target — fallback heartbeat full si image client tronquée
Bug client constaté ce 2026-05-07 sur PC Windows 192.168.1.11 (agent V1) :
mss.monitors[1] retourne parfois une image tronquée type 2560x60, 2560x108,
600x72 — possiblement la barre des tâches Windows confondue avec un monitor,
ou un état mss corrompu. Reproduit même PC en mono physique. Cause exacte
non isolée côté client.

Sans cette image, _resolve_target_sync ne peut rien résoudre :
- Template matching échoue (anchor 104x31 vs image 600x72)
- OCR direct ne trouve pas la cible (texte hors de l'image tronquée)
- VLM Quick Find hallucine systématiquement la même position
- Fallback recorded_coords clique au mauvais endroit

Conséquence reproduite hier soir : "Léa clique partout au pif"
(cf. session_20260506_handoff_v2.md).

Filet de sécurité côté serveur : si l'image reçue est anormalement
tronquée (height < 200 ou width < 400), le serveur la remplace par le
dernier heartbeat full screen avant la cascade _resolve_target_sync.

Sources de fallback dans l'ordre :
1. _last_heartbeat (mémoire, peuplé par /stream/image en runtime)
2. Scan disque data/training/live_sessions/*/bg_*/shots/heartbeat_*.png
   (utile après restart serveur ou si l'agent V1 ne polle pas)

Validé en isolation : image tronquée 600x60 → fallback heartbeat 2560x1600
→ template matching score 0.999 → coords (0.0312, 0.3500) = exactement
la position de l'IPP cible '25003284' en première ligne d'Easily Assure.

Bug client à traiter post-démo. Le fallback heartbeat reste utile en
roadmap autonome (résilience aux états mss transitoires).

Note : également retiré un import os local redondant dans le finally
(masquait la variable globale et provoquait UnboundLocalError dans
le scope du bloc fallback).
2026-05-07 09:31:07 +02:00
Dom
22c0a2ba61 revert: désactiver self-healing Win+D auto (cercle vicieux)
Revert effectif du commit c969f93a2.

Le Win+D auto au retry 1 produit un cercle vicieux quand combiné avec
le VLM-first qui hallucine systématiquement (positions répétitives
type 0.529/0.874 avec confidence 0.93 sans justification) :

  click rate (cible mal localisée par VLM) → no_screen_change
  → Win+D auto → minimise Easily Assure
  → retry click → cible plus visible (Easily masquée par Win+D)
  → no_screen_change → Win+D encore → boucle infernale

Reproduit ce 2026-05-06 sur le workflow Urgence : 10 Win+D dispatchés
en moins de 2 minutes. Régression majeure ressentie par Dom :
"clic partout au pif, aucune action contrôlée".

L'idée du self-healing par gesture reste valide mais demande :
1. un déclenchement plus sélectif (genre overlay/popup détecté
   visuellement, pas no_screen_change générique)
2. ou un Alt+Tab plutôt que Win+D (fait passer la fenêtre arrière
   sans minimiser l'app cible)
3. ou une vraie analyse "y a-t-il une fenêtre qui obstrue ma cible"
   avant de décider du gesture

À retravailler post-démo avec un vrai détecteur d'obstruction.
2026-05-06 20:31:31 +02:00
Dom
6fdedbfe9d fix(vwb): execute-windows route vers la machine la plus active (pas alphabétique)
Quand le frontend ne passe pas de machine_id explicite, le backend VWB
auto-sélectionne une machine Windows en interrogeant /api/v1/traces/
stream/machines. Le code prenait la première de la liste sans tri, donc
l'ordre dépendait de l'ordre arbitraire renvoyé par le streaming server.

Conséquence reproduite ce 2026-05-06 : un replay du workflow Urgence a
été dispatché vers DESKTOP-ST3VBSD_windows alors que l'agent V1 actif
polait depuis DESKTOP-58D5CAC_windows. /replay/next ne dispatchait
aucune action puisque state.machine_id != polling_machine_id.
Symptôme côté Dom : "rien ne se passe sur Windows".

Correction : tri explicite par last_activity desc avant sélection.
La machine retenue est désormais celle qui a heartbeaté le plus
récemment (= celle qui POLLE actuellement le serveur).

Le workflow.machine_id (machine d'origine d'enregistrement) reste
distinct de la cible d'exécution : un workflow enregistré sur PC A
peut être rejoué sur PC B grâce au pipeline 100% visuel qui recalcule
anchors et coordonnées selon la résolution courante. C'était la
vraie intention architecturale, masquée par le bug de tri.
2026-05-06 20:23:44 +02:00
Dom
c969f93a23 fix(replay): self-healing Win+D auto au retry 1 (verification_failed)
Audit project-quality-guardian (2026-05-06) Cas #2 : le mécanisme
qui invoquait gesture_catalog.win_minimize_all (Win+D) en cas
d'échec de grounding a été archivé le 24/04 dans
_archive/dead_code_20260424/core/visual/rpa_integration_manager.py
(_attempt_self_healing_resolution). Le catalogue
agent_chat/gesture_catalog.py:84 reste intact mais orphelin —
aucun caller actif.

Conséquence : quand une fenêtre/popup obstrue la cible, Léa
retente N fois la même action ratée puis pose une pause supervisée,
alors qu'un Win+D ("Afficher le bureau") règle souvent le problème
en 200 ms.

L'audit proposait observe_reason_act.py mais ce module est utilisé
uniquement par /execute/instruction (lui aussi sans client actif,
Cas #10). Le bon point d'insertion dans le pipeline replay actif
est _schedule_retry (replay_engine.py) — la fonction qui construit
la liste d'actions à réinjecter en tête de queue avant chaque retry.

Modification :

Au next_retry == 1 ET reason in ("verification_failed",
"no_screen_change"), insertion en tête de queue de :

  1. Action key_combo {keys: ["super", "d"]} (format reconnu par
     agent_v1/core/executor.py:1151), tagué
     _recovery_gesture: "win_minimize_all" pour audit.
  2. Wait 500 ms pour laisser l'OS terminer l'animation Win+D.
  3. Le retry de l'action originale.

Au retry 2 et au-delà, comportement inchangé (wait 2s + retry).

Tests : 27/27 baseline sprint QW verts.
2026-05-06 19:27:16 +02:00
Dom
1cbec2806e fix(resolve): rebrancher hybrid_text_direct dans _resolve_target_sync
Audit project-quality-guardian (2026-05-06) : la fonction
_resolve_by_ocr_text (resolve_engine.py:1447) existait déjà mais
n'était appelée QUE depuis _resolve_with_precompiled_order (V4),
endpoint sans client côté frontend (Cas #5 du même audit). La
cascade legacy _resolve_target_sync sautait directement d'étape 0
(grounding-window) → étape 0' (template icônes) → étape 1 (VLM
Quick Find) sans tenter l'OCR direct.

Conséquence reproduite ce 2026-05-06 sur le workflow Urgence :
chaque action visuelle avec by_text payait 2-23 s de VLM Quick
Find (ui-tars-1.5-7b-q8_0 sur Ollama) au lieu de <500 ms d'OCR
direct, total replay > 10 min vs quelques secondes attendues.
Constat utilisateur : "habituellement on est plutôt à quelques
secondes". Régression silencieuse.

Modification :

Étape 0.5 ajoutée entre l'étape 0' (template icônes) et l'étape 1
(VLM Quick Find). Si by_text_strict est non vide, appel à
_resolve_by_ocr_text — fonction docTR existante, cache singleton
_V4_OCR_PREDICTOR, score 1.0 si match exact, 0.9 si mot exact,
0.8 si contenu. Seuil de retour : 0.80 (cohérent avec
_RESOLUTION_MIN_SCORES["hybrid_text_direct"]).

Le method retourné est rebadgé "hybrid_text_direct" pour cohérence
avec :
- _RESOLUTION_MIN_SCORES (seuil 0.80, ligne 2092)
- agent_v0/agent_v1/core/executor.py:1534 (client Windows)
- logs Learning historiques ([hybrid_text_direct])

Tests : 39/39 sprint QW + grounding/resolver verts.
2026-05-06 19:24:53 +02:00
Dom
864530c851 fix(stream): _async_replay_lock helper + 17 endpoints async non-bloquants
Suite directe des commits 35b27ae49 (lock async sur /replay/next) et
87dbe8c5f (get_replay_status non-bloquant) qui n'avaient traité que
2 endpoints sur les 19 utilisant _replay_lock dans api_stream.py.

Reproduit aujourd'hui en pré-démo : un replay urgences a réussi
extract_text + t2a_decision (50s, OK), puis a hang sur l'action
suivante. start_raw_replay (POST /replay) du nouveau replay a tenté
`with _replay_lock:` synchrone à la ligne 2085 → MainThread asyncio
gelé → tous les endpoints derrière. Stack via py-spy confirmée.

Le pattern systémique : 17 sites `with _replay_lock:` synchrones
dans des handlers `async def` (start_replay, start_raw_replay,
replay_from_session, enqueue_single_action, launch_replay_from_plan,
get_next_action [×3], report_action_result [×5], register_error_callback,
list_replays, resume_replay, cancel_replay). Chacun gèle l'event
loop FastAPI dès qu'un autre thread tient le lock.

Modifications :

1. Helper _async_replay_lock(timeout=4.5) (api_stream.py:516).
   Acquire via run_in_executor (event loop libre pendant l'attente),
   timeout 4.5s puis HTTPException 503 plutôt que gel infini.
   Sémantique acquire+release identique au `with` synchrone.

2. Remplacement automatisé des 17 sites async :
   `with _replay_lock:` → `async with _async_replay_lock():`
   2 sites sync intentionnellement préservés (cleanup loop ligne 689,
   chat_status_provider ligne 5048 — pas dans des handlers async).

3. Import contextlib ajouté en haut du fichier.

Tests : 27/27 baseline sprint QW verts, /health 200 (3ms),
/replays 200 (2ms — endpoint qui utilise le nouveau helper).
2026-05-06 18:06:42 +02:00
Dom
d1ebf62217 fix(infra): durcissement headless — pyautogui robuste + cleanup .service
Suite à la mise à jour système qui a basculé Dom de Xorg vers Wayland,
les 4 services systemd côté serveur partaient en boucle restart :
pyautogui levait DisplayConnectionError / KeyError(DISPLAY) à l'import
dans 3 modules, mais l'except n'attrapait qu'ImportError → crash fatal.

Le contournement « ajouter DISPLAY=:1 + XAUTHORITY=/run/user/1000/gdm/
Xauthority dans .service » introduit fin avril était fragile : chemin
invalide en Wayland (Mutter utilise un Xauthority à suffixe aléatoire
qui change à chaque login). Le bon fix est de rendre les imports
pyautogui robustes — le serveur n'a aucun usage légitime de pyautogui,
c'est le client Agent V1 Windows qui pilote souris/clavier.

Modifications :

1. Élargi `except ImportError` → `except Exception` pour pyautogui :
   - agent_chat/autonomous_planner.py
   - core/execution/input_handler.py
   - core/execution/observe_reason_act.py
   (action_executor.py était déjà robuste avec except Exception.)

2. Retiré DISPLAY/XAUTHORITY des 4 .service (rustines) :
   - rpa-streaming.service
   - rpa-vision-v3-{api,worker,dashboard}.service
   Block grounding (RPA_GROUNDING_SOCKET) préservé (initiative
   séparée de partage VRAM, in-flight).

PYAUTOGUI_AVAILABLE=False est désormais attendu côté serveur Linux ;
les chemins aval (action_executor, autonomous_planner) gèrent déjà
ce cas via des branches "actions simulées" / "pyautogui non disponible".

Prépare la roadmap autonome (Léa daemon Linux + VM Windows) qui
tournera headless via systemd au boot, sans dépendre d'aucune
session graphique active.

Tests : 27/27 baseline sprint QW verts.
2026-05-06 17:19:18 +02:00
Dom
87dbe8c5ff fix(stream): get_replay_status non-bloquant + bornage actions serveur
Suite du commit 35b27ae49 (lock async sur /replay/next) qui n'avait
traité que la moitié du problème. Le sprint QW4 (commit f5c33477f)
a recâblé le polling frontend PauseDialog vers /replay/{replay_id} →
get_replay_status, qui gardait un `with _replay_lock:` synchrone.
Conséquence : dès qu'une action serveur (extract_text/extract_table/
t2a_decision) tient le lock, l'event loop FastAPI gèle entièrement
(heartbeats Windows, polls replay/next, get_replay_status, tout).

Reproduit aujourd'hui en pré-démo : un replay urgences a fait
extract_text → la queue suivante a tenu le lock → polling VWB sur
get_replay_status a bloqué le MainThread asyncio → 23 minutes de
gel total (py-spy a confirmé MainThread sur api_stream.py:4117).

Modifications :

1. get_replay_status : acquire timeboxé 0.5s via run_in_executor
   (même pattern que /replay/next ligne 2815). Si le lock est tenu,
   retour immédiat {status: "busy"} → le frontend retentera dans 1s.
   Aucun cas où ce poll bloque l'event loop.

2. Actions serveur lignes 2994/3000/3006 : enveloppées dans
   asyncio.wait_for(timeout=180). Borne dure pour qu'un hang
   d'EasyOCR / Ollama / I/O ne tienne plus jamais le lock
   indéfiniment. TimeoutError est rattrapée par l'except Exception
   existant → queue.pop(0) → on continue.

Tests : 27/27 baseline sprint QW verts.
2026-05-06 17:19:05 +02:00
Dom
0a02a6ec9c feat(qw4): bench rigoureux LLM safety_checks → gemma4:latest par défaut
Some checks failed
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Bench 5 modèles × 5 scénarios × cold+warm sur RTX 5070 :
- gemma4:latest : warm 2.9s, JSON 92%, détection 46% → gagnant
- qwen2.5vl:7b : warm 6.6s, détection 23% (trop lent)
- qwen2.5vl:3b : warm 2.0s, détection 8% (vérifie pour vérifier)
- medgemma:4b : warm 0.5s, détection 0% (refuse de signaler) → mauvais
  défaut initial, corrigé
- qwen3-vl:8b : 0% JSON valide (ignore format=json Ollama) → écarté

Modifications safety_checks_provider.py :
- RPA_SAFETY_CHECKS_LLM_MODEL défaut: medgemma:4b → gemma4:latest
- RPA_SAFETY_CHECKS_LLM_TIMEOUT_S défaut: 5 → 7 (warm 2.9s + marge)

Doc complète : docs/BENCH_SAFETY_CHECKS_2026-05-06.md
Script : tools/bench_safety_checks_models.py (reproductible, ~10-15 min)

Limite assumée : 46% de détection. À présenter en démo comme aide médecin,
pas certification. Amélioration V2 = prompt plus dirigé sur champs à vérifier.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 09:23:09 +02:00
Dom
83be93e121 chore(qw): cleanup post-review (préfixes BUS, événements monitor, import io)
Some checks failed
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
- safety_checks_provider : tous les logger.warning d'échec LLM préfixés
  [BUS] lea:safety_checks_llm_failed avec une raison spécifique
  (exception, http_status, timeout, network, json_decode).
- monitor_router : émission [BUS] lea:monitor_invalid_index si l'index
  explicite passé dans l'action est hors limites de monitors_geometry,
  et [BUS] lea:monitor_unavailable si focus actif demandé mais introuvable.
  Ces deux events permettent au bus de tracer chaque fallback de la cascade
  de routage QW1.
- safety_checks_provider : import io supprimé (inutilisé).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 00:08:22 +02:00
Dom
f5c33477f0 fix(qw4): câblage polling frontend → streaming pour PauseDialog
Avant ce fix, le frontend VWB ne savait pas qu'un replay Agent V1 (Windows)
était en pause supervisée : le seul polling (App.tsx) interrogeait
/execute/status (exécution locale Linux) et n'avait jamais l'info
safety_checks / pause_message du replay distant.

Côté backend (dag_execute.py) :
- ajout du proxy GET /api/v3/replay/state/<replay_id> qui forward vers
  /api/v1/traces/stream/replay/<id> avec Bearer token.

Côté frontend :
- ExecutionControls : nouvelle prop onWindowsReplayStarted, appelée avec
  le replay_id retourné par /api/v3/execute-windows.
- App.tsx : nouveau state streamingReplayId + useEffect qui poll
  /api/v3/replay/state/<id> toutes les secondes et fusionne status,
  pause_message, pause_reason, safety_checks dans appState.execution.
  Le PauseDialog existant s'affiche donc automatiquement quand
  status = paused_need_help.

Le polling s'arrête quand le replay est completed/error/cancelled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 00:06:20 +02:00
Dom
b1a3aa16f1 fix(qw1): enrichir heartbeat Windows avec monitor_index + monitors_geometry
Avant ce fix, le _heartbeat_loop côté Agent V1 deploy Windows
n'enrichissait pas son payload, donc QW1 multi-écran ne s'activait sur Windows
que via les events window_capture (déclenchés par les clics), pas en continu.

La source agent_v0/agent_v1/main.py portait déjà l'enrichissement (commit 2d71e2a24)
mais le snapshot deploy/windows_client/agent_v1/main.py n'avait pas été synchronisé.

Désormais chaque heartbeat porte monitor_index + monitors_geometry, le serveur
peut donc résoudre l'écran cible en permanence, même sans clic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 00:02:11 +02:00
Dom
0bcfddbbc4 docs(qw): plan de smoke tests manuels pour validation 2026-05-06
Some checks failed
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Plan exécutable seul par Dom : 9 sections (préflight, QW1 mono/multi-écran,
QW2 boucle, QW4 backward/déclaratif/medical_critical, bus events, kill-switches,
rollback) avec checklist OK/KO et procédures d'urgence en pleine démo.

Validation pour démo GHT (1ère sem mai 2026).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 00:01:21 +02:00
Dom
aa47172f0f docs(qw): synthèse de livraison QW suite mai 2026
Some checks failed
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Doc condensée des 3 quick wins livrés (QW1 multi-écrans, QW2 LoopDetector,
QW4 safety_checks hybrides) avec :
- procédures kill-switch et rollback
- table des env vars
- smoke tests manuels à effectuer avant démo GHT
- statut composant par composant

Pointe vers spec et plan d'exécution complets.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:48:26 +02:00
Dom
65da557310 feat(qw4): hook safety_checks_provider + extension /replay/resume avec acquittements
Some checks failed
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
replay_state enrichi de safety_checks, checks_acknowledged, pause_reason,
pause_payload (audit trail).

Branche supervisée pause_for_human :
- appel build_pause_payload() avant bascule paused_need_help
- log [BUS] lea:safety_checks_generated (count, sources)
- fallback safe sur exception (pause sans checks plutôt que crash)
- déclenchement si safety_level/safety_checks déclarés OU execution_mode != autonomous
- sinon comportement legacy (skip silencieux)

POST /replay/resume :
- accepte body { acknowledged_check_ids: [...] }
- vérifie tous les checks required acquittés, sinon 400 required_checks_missing
- stocke checks_acknowledged comme audit trail
- nettoie safety_checks/pause_payload après reprise

Proxy VWB /api/v3/replay/resume → streaming /replay/{id}/resume (forward bearer
token + acknowledged_check_ids).

Backward 100% : workflows sans safety_checks → resume sans acquittement requis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:45:22 +02:00
Dom
af13cd80ff feat(vwb): PauseDialog + ChecklistPanel + extension PropertiesPanel pour safety_checks
PauseDialog (composant nouveau) :
- 2 modes selon payload : bulle simple legacy si safety_checks vide,
  ChecklistPanel sinon
- Continuer désactivé tant que required non cochés
- Badge [obligatoire] et [Léa] (avec evidence en tooltip)
- POST /api/v3/replay/resume avec acknowledged_check_ids quand replay_id
  présent, fallback api.resumeExecution() pour la voie locale

types.ts : SafetyCheck, SafetyLevel, extension Execution
(pause_reason, pause_message, safety_checks, replay_id, status
'paused_need_help'). Action pause_for_human enrichie de safety_level
et safety_checks dans le catalogue ACTIONS.

PropertiesPanel : éditeur safety_level (dropdown standard/medical_critical)
+ liste éditable de safety_checks (id/label/required + ajout/suppression).

App.tsx : rendu conditionnel du PauseDialog en overlay quand
status == paused_need_help, ou paused avec safety_checks. Backward 100% :
workflows existants sans safety_checks affichent la bulle legacy.

CSS : .pause-dialog-overlay/.pause-dialog-checks/.checklist-panel/
.check-item/.badge-required/.badge-lea/.check-editor-row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:33:04 +02:00
Dom
7c6945171e feat(qw4): SafetyChecksProvider hybride déclaratif + LLM contextuel
build_pause_payload(action, state, last_screenshot) → PausePayload
- Toujours inclure les checks déclaratifs (workflow.parameters.safety_checks)
- Si safety_level=medical_critical ET RPA_SAFETY_CHECKS_LLM_ENABLED=1 :
    appel LLM (medgemma:4b par défaut) en format=json strict, timeout 5s,
    max 3 checks ajoutés (configurables via env vars)
- Tous les chemins d'erreur (timeout, HTTP, JSON parse, exception) loggent
  et retournent [] (fallback safe : déclaratifs seuls)

Tests : 7 cas (déclaratif seul, hybride OK, timeout, LLM invalide,
kill-switch, max_checks, déclaratif vide).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:29:38 +02:00
Dom
ca0b436a61 feat(qw2): hook LoopDetector dans api_stream + extension replay_state
Some checks failed
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 17s
tests / Tests sécurité (critique) (push) Has been skipped
replay_state enrichi de _screenshot_history (5 dernières images PIL) et
_action_history (5 dernières signatures action).

report_action_result :
- met à jour les deux anneaux après chaque action
- évalue le LoopDetector (singleton lazy avec _clip_embedder serveur)
- si detected → bascule paused_need_help avec pause_reason="loop_detected"
  et bus event lea:loop_detected (signal + evidence)

Tous les chemins d'erreur (embedder absent, OOM, exception) loggent et
laissent le replay continuer — aucun blocage par la couche détection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:25:04 +02:00
Dom
fc01afa59c fix(qw1): bus event lea:monitor_routed + cablage offset côté executor Agent V1
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>
2026-05-05 23:16:06 +02:00
Dom
2a51a844b9 feat(qw2): LoopDetector composite (screen_static + action_repeat + retry)
Module isolé, 3 signaux indépendants :
- screen_static : CLIP similarity > 0.99 sur N captures consécutives
- action_repeat : N actions identiques (type+coords)
- retry_threshold : retried_actions >= seuil

Premier signal positif → LoopVerdict.detected=True (caller responsable de
la bascule en paused_need_help).

Configurable env vars : RPA_LOOP_DETECTOR_ENABLED (kill-switch),
RPA_LOOP_SCREEN_STATIC_N/THRESHOLD, RPA_LOOP_ACTION_REPEAT_N,
RPA_LOOP_RETRY_THRESHOLD.

Tests : 8 cas (chaque signal isolé, kill-switch, embedder absent, exception).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 23:09:43 +02:00
Dom
2d71e2a249 feat(qw1): enrichissement Agent V1 (monitor_index + monitors_geometry) + hook serveur
Some checks failed
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-05-05 23:05:44 +02:00
Dom
fae95c5366 feat(qw1): capture par monitor + propagation offsets dans grounding cascade
_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>
2026-05-05 22:55:04 +02:00
Dom
6582a69d31 feat(qw1): MonitorRouter — résolution de l'écran cible pour le replay
Module isolé qui choisit l'écran cible avec stratégie en cascade :
1. action.monitor_index (session source) → cible explicite
2. session.last_focused_monitor → fallback focus actif
3. composite (offset 0,0) → backward compat (comportement actuel)

Backward 100% : actions sans monitor_index → fallback composite identique
au comportement mss.monitors[0] actuel.

Tests : 4 cas (cible OK, fallback focus, fallback composite, index invalide).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 22:50:22 +02:00
Dom
5543e25f9d docs(qw): plan d'implémentation QW suite mai 2026 (~30 tasks bite-sized TDD)
Some checks failed
tests / Lint (ruff + black) (push) Successful in 18s
tests / Tests unitaires (sans GPU) (push) Failing after 17s
tests / Tests sécurité (critique) (push) Has been skipped
Plan d'exécution détaillé pour le sprint QW1+QW2+QW4 :
- Section 0 (preflight) : backup branche+tag Gitea, baseline E2E, smoke démo
- Section 1 (QW1 multi-écrans) : tests + monitor_router + input_handler + Agent V1
- Section 2 (QW2 LoopDetector) : tests + module + hooks api_stream/replay_engine
- Section 3 (QW4 safety_checks) : tests + provider + endpoint + frontend VWB
- Section 4 (docs) : QW_SUITE_MAI.md + maj MEMORY

Chaque task = 4-7 steps de 2-5 min, code complet par step (modules nouveaux),
diffs ciblés (modifs ciblées), commands exactes avec output attendu.

Discipline TDD légère : test rouge → implem → test vert → re-run baseline → commit.

Référence spec : docs/superpowers/specs/2026-05-05-qw-suite-mai-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 22:34:13 +02:00
Dom
2a07d8084b docs(qw): spec design QW suite mai 2026 (multi-écrans + LoopDetector + safety_checks hybrides)
Spec issu d'un brainstorming structuré (7 questions clarifiantes,
décisions tranchées) inspiré par l'exploration comparative de 5 frameworks
computer-use (Simular Agent-S, browser-use, OpenAI CUA sample, Coasty
open-cu, Showlab OOTB).

3 quick wins ciblés :
- QW1 multi-écrans : capture/grounding par monitor_index avec fallbacks
- QW2 LoopDetector composite : screen_static (CLIP) + action_repeat + retry
- QW4 safety_checks hybrides : déclaratif workflow + LLM contextuel
  (medgemma:4b, timeout 5s, fallback safe, kill-switch env)

Contraintes inviolables : 100% vision, 100% local Ollama, backward compat.
Plan livraison : QW1+QW2 avant démo GHT, QW4 enchaîné dès validation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 22:23:10 +02:00
Dom
35b27ae492 fix(stream+vwb): chaîne replay robuste — auth, anchor type_text, lock async, drift, prompt LLM
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>
2026-05-02 00:32:57 +02:00
Dom
b584bbabc3 fix(stream): robustesse proxy VWB→streaming + ciblage textuel pour démo UHCD
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>
2026-05-01 15:52:22 +02:00
Dom
8817f527e7 feat(deploy): service systemd pour la maquette Easily Assure (démo GHT)
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>
2026-04-30 14:52:27 +02:00
Dom
964856ab30 feat(workflow): variables runtime + extract_text serveur + t2a_decision LLM
Pipeline streaming étendu pour supporter des actions exécutées entièrement
côté serveur (jamais transmises à l'Agent V1) qui produisent des variables
réutilisables dans les steps suivants via templating {{var}} ou {{var.field}}.

== Variables d'exécution ==
- replay_state["variables"] : Dict[str, Any] initialisé vide à la création
- _resolve_runtime_vars() : résout {{var}} et {{var.field}} récursivement
  dans str/dict/list. Variables absentes laissées intactes.
- /replay/next applique la résolution sur l'action AVANT toute interception
  ou envoi à l'Agent V1.

== Boucle d'exécution serveur ==
- _SERVER_SIDE_ACTION_TYPES = {"extract_text", "t2a_decision"}
- /replay/next pop+execute en boucle ces actions jusqu'à trouver une action
  visuelle (à transmettre Agent V1) ou un pause_for_human (qui bloque).
- Latence acceptable : t2a_decision = 5-10s côté serveur, l'Agent V1 attend
  la réponse HTTP.

== Action extract_text ==
- Handler côté serveur réutilisant le dernier heartbeat (max 5s d'âge)
- core/llm/ocr_extractor.py : EasyOCR fr+en singleton + extract_text_from_image
- Stockage dans replay_state["variables"][output_var]
- Robuste : pas de heartbeat → variable = "" + log warning, pipeline continue

== Action t2a_decision ==
- core/llm/t2a_decision.py : refactor de demo_app.py query_model en module
  importable. Prompt expert DIM T2A/PMSI, qwen2.5:7b par défaut (100% bench).
- Handler côté serveur appelle analyze_dpi(input_template_resolved)
- Stockage du JSON décision dans replay_state["variables"][output_var]
- Erreurs (Ollama down, parse) → variable = INDETERMINE + _error, pipeline continue

== VWB UI ==
- types.ts : nouveau type 't2a_decision' (icône 🧠 catégorie logic)
- extract_text refondu : needsAnchor=false, paramètre output_var (au lieu de
  variable_name legacy — bridge accepte les deux pour compat)
- Bridge VWB→core : passthrough des deux types + paramètres préservés

== Tests ==
- tests/integration/test_t2a_extract.py : 25 tests verts
  - templating runtime (8 tests)
  - handler extract_text (3 tests, OCR mocké)
  - handler t2a_decision (3 tests, analyze_dpi mocké)
  - edge → action normalisée (2 tests)
  - bridge VWB → core (5 tests)
  - workflow chain extract→t2a→pause→clic (1 test)

Total branche : 82/82 verts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:47:31 +02:00
Dom
a67d896104 fix(vwb): bibliothèque de capture restait vide après 'Capturer'
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>
2026-04-29 21:13:56 +02:00
Dom
90c1d8036f ux(vwb): timer capture — default 5s, label dynamique, log diagnostic
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>
2026-04-29 18:20:16 +02:00
Dom
6261002039 ux(vwb): tooltip enrichi sur les outils de la palette
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>
2026-04-29 17:42:55 +02:00
Dom
0e6e61f2b1 feat(workflow): action 'pause_for_human' — pause supervisée scriptée dans VWB
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>
2026-04-29 16:37:46 +02:00
Dom
41c1250c99 feat(lea): bulles 'Léa exécute' stylisées + templates par event
J3.4 — distinction visuelle entre :
- Bulles chat normales (fond bleu clair, prefixe 💬, taille standard)
- Bulles d'action Léa (fond gris clair, encadré subtil, icône sémantique
  en couleur, libellé court, métadonnées discrètes en pied)
- Bulle paused supervisée (jaune, boutons interactifs — déjà en J3.5)

Templates de libellés volontairement neutres : le contexte métier (UHCD,
peakflow, J12.1, IPP 25003284…) provient des payloads émis par le pipeline
côté serveur, pas de hardcoding dans le client.

Mappage events → bulles :
  lea:action_started   ▶ bleu  "Démarrage : {workflow}"
  lea:action_progress  ⋯ bleu  "{step}" ou "Étape {current}/{total}"
  lea:done             ✓ vert / ✗ rouge selon success
  lea:need_confirm     ?  bleu  "{action.description}"
  lea:step_result      ✓ / ✗ / · selon status
  lea:resumed          → vert  "Reprise"
  lea:resume_acked     (silencieux côté UI)
  lea:abort_acked      (silencieux côté UI)
  événement inconnu    · gris  fallback neutre

18 nouveaux tests pytest (templates + extract_meta).
Total branche : 47/47 verts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 10:18:52 +02:00
Dom
2af3bc3b93 feat(lea): bulle paused_need_help interactive — asset démo majeur
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>
2026-04-28 10:08:32 +02:00
Dom
6154423a91 feat(agent_v1): brancher FeedbackBusClient dans ChatWindow tkinter
- 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>
2026-04-28 09:19:41 +02:00
Dom
41eba898c0 feat(agent_v1): FeedbackBusClient — client SocketIO pour bus 'lea:*'
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>
2026-04-28 08:43:26 +02:00
Dom
9452e86fd1 deps(agent_v1): python-socketio[client] pour bus feedback Léa
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>
2026-04-27 21:53:40 +02:00
Dom
5e31cdf666 feat(agent_chat): bus feedback Léa 'lea:*' derrière flag LEA_FEEDBACK_BUS
Surface d'observation pour bulles temps réel ChatWindow (J2 démo GHT Sud 95).

- Helper _emit_lea(event, payload): no-op silencieux si flag off
- Helper _emit_dual(legacy, lea, payload): émet event existant + alias 'lea:*'
- Détection paused_need_help dans _poll_replay_progress → lea:paused
- Détection sortie de pause → lea:resumed
- Timeout étendu (120s→600s) pendant pause supervisée
- 12 emits SocketIO existants aliasés (execution_started/progress/completed,
  copilot_step/step_result/complete) — payloads identiques, zéro régression

Flag LEA_FEEDBACK_BUS=0 par défaut. Comportement legacy strictement préservé.
8 tests pytest verts (tests/integration/test_feedback_bus.py).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:48:38 +02:00
Dom
487bcb8618 feat(execution): cascade post-raccourci pilotée par DialogHandler/OCR
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>
2026-04-26 20:19:39 +02:00
Dom
3d6868f029 docs: cartographie complète d'exécution + fix target_text ORA + worker InfiGUI fichiers
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>
2026-04-26 12:37:43 +02:00
Dom
f73a2a59a9 feat(réflexes): patterns overwrite/dont_save + handler EasyOCR + prints diagnostic
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>
2026-04-26 04:26:32 +02:00
Dom
77faa03ec9 feat(grounding): InfiGUI-G1-3B remplace UI-TARS 7B — 3.5x moins de VRAM
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>
2026-04-26 04:07:00 +02:00
Dom
343d6fbe95 perf(ocr): EasyOCR remplace docTR dans FastDetector + TitleVerifier
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>
2026-04-26 03:32:43 +02:00
Dom
cc64439738 feat(grounding): vérification titre OCR post-action (non-bloquante)
TitleVerifier (core/grounding/title_verifier.py) :
- Crop 45px barre de titre → OCR → compare avant/après (~280ms)
- Titres < 3 chars ignorés (bruit OCR sur VM)
- Non-bloquant : échec = warning, pas stop

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 03:22:50 +02:00
Dom
90007cc7c1 perf(grounding): réflexe pHash-only + max_new_tokens 64
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>
2026-04-26 03:07:35 +02:00
Dom
73cea2385e feat(grounding): Phase 6 — Shadow Learning Hook
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>
2026-04-25 21:00:11 +02:00
Dom
e2046837cf feat(grounding): Phase 5 — intégration pipeline FAST→SMART→THINK dans ORA
_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>
2026-04-25 20:57:56 +02:00
Dom
b30d4b6656 feat(grounding): Phase 4 — Pipeline orchestré FAST→SMART→THINK
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>
2026-04-25 20:54:40 +02:00
Dom
e4a48e78bf feat(grounding): Phase 3 — ThinkArbiter + SignatureStore
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>
2026-04-25 20:44:12 +02:00
Dom
ea36bba5cc feat(grounding): Phase 1-2 pipeline FAST→SMART — détection + matching
Phase 1 — FastDetector (core/grounding/fast_detector.py) :
- Détection RF-DETR de tous les éléments UI (~120ms à chaud)
- Enrichissement OCR (texte, voisins, position relative)
- Cache pHash (même écran → résultat instantané)
- 23 éléments détectés sur le benchmark, positions correctes

Phase 2 — SmartMatcher (core/grounding/smart_matcher.py) :
- Matching déterministe : texte exact (score 0.95) puis fuzzy (0.70+)
- Matching probabiliste : type, position, voisins contextuels
- Score combiné pondéré → seuil de confiance
- 5/5 éléments trouvés en < 1ms, 0 faux positif
- "Gorbeille" matche "Corbeille" par fuzzy (score 0.678)

Structures (core/grounding/fast_types.py) :
- DetectedUIElement, ScreenSnapshot, MatchCandidate, LocateResult
- Compatible GroundingResult via to_grounding_result()

Modules standalone — aucun impact sur le système existant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:37:14 +02:00
Dom
9da589c8c2 feat(grounding): pipeline centralisé + serveur UI-TARS transformers + nettoyage code mort
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>
2026-04-25 17:48:18 +02:00
Dom
16ff396dbf chore: sauvegarde pré-stabilisation — audit 66/66 tests OK
Some checks failed
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
security-audit / Bandit (scan statique) (push) Successful in 1m7s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 7s
Audit qualité : 0 bug critique, 5 points dette technique (post-démo).
Boucle ORA fonctionnelle : UI-TARS + pré-vérification + recovery Win+D.
Script test_instruction.sh ajouté.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-23 09:14:56 +02:00
Dom
e44fd7b328 fix(ORA): double-clic fiable + vérification stricte
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-23 08:45:40 +02:00
Dom
66815b7a1a fix(ORA): pattern None quand overlay est une fenêtre (pas un dialogue)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-23 08:22:12 +02:00
Dom
c6b695eca8 fix(ORA): Win+D via xdotool key au lieu de pyautogui.hotkey
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-23 08:15:47 +02:00
Dom
99d2083dea fix(ORA): moveTo + pause + click + pause + Win+D (séquence validée par Dom)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 20:06:55 +02:00
Dom
a718086140 fix(ORA): xdotool windowactivate QEMU + key super+d pour focus VM
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 10s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 18:08:10 +02:00
Dom
c82979e72b fix(ORA): clic centre écran pour focus VM avant Win+D
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 17:45:05 +02:00
Dom
2185c41cc1 fix(ORA): Win+D au lieu de Alt+Tab pour le recovery overlay
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 13s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 17:19:06 +02:00
Dom
26804eb123 fix(ORA): Alt+Tab au lieu de windowminimize pour le recovery overlay
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 17:09:38 +02:00
Dom
d71d5df4a8 fix(ORA): overlay = minimiser la fenêtre devant, pas juste chercher OK
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 17:03:18 +02:00
Dom
6829ad8e79 feat(ORA): classification erreurs + recovery intelligent
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 13s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
4 types d'erreurs : ELEMENT_NOT_FOUND, OVERLAY_BLOCKING,
WRONG_SCREEN, ACTION_NO_EFFECT.

Recovery spécialisé par type :
- Element introuvable → attente + scroll + retry UI-TARS élargi
- Overlay bloquant → détection pattern + fermeture auto + retry
- Mauvais écran → description VLM + Alt+Tab + recherche taskbar
- Pas d'effet → double-clic + délai + coordonnées décalées

Intégré dans run_workflow() : classification → recovery → re-vérif.
Échec total → pause supervisée (pas de stop brutal).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 16:44:31 +02:00
Dom
8903f35433 feat(ORA): vérification pré-action — VLM confirme avant chaque clic
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 16:22:37 +02:00
Dom
4ab2c15e5c fix(ORA): logger.info→print pour que les logs apparaissent dans nohup
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 16:16:25 +02:00
Dom
eba6fea779 refactor(ORA): UI-TARS en PREMIER pour les clics
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 15s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 15:59:45 +02:00
Dom
f04398d5a7 fix: VLM décrit TOUJOURS l'ancre à la capture, pas seulement si OCR échoue
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 15:30:19 +02:00
Dom
4ce9c47f45 fix(ORA): logs stdout + vérification pHash tolérante pour clics
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
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>
2026-04-22 15:04:13 +02:00
Dom
9dfcdb5fb0 fix: ajouter 'verified' dans la liste des modes du toggle
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 19s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 14:48:41 +02:00
Dom
3efe15d2c7 feat(vwb): ajout mode 'Vérifié' dans le sélecteur d'exécution
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 14:36:06 +02:00
Dom
9d87ed64c5 fix: corrections audit qualité — stop/pause ORA + nettoyage debug
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
CRITIQUE : ajout should_continue callback dans ORALoop pour supporter
les boutons Stop/Pause du frontend en mode verified et instruction.

HAUTE : suppression sys.stdout.write de debug, logger.warning→debug
dans _grounding_ocr.

BASSE : suppression import mort 'field' dans observe_reason_act.py.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 11:17:20 +02:00
Dom
00134963e5 test: 16 tests unitaires pour la boucle ORA
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 9s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Tests ORALoop init, Decision, reason_workflow_step (click, type,
hotkey, wait, passthrough), verify (none, wait, done), run_workflow
(empty, too_many), run_instruction (méthodes existent).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 10:43:28 +02:00
Dom
0ec5e2a25b feat: instructions en langage naturel via boucle ORA
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 11s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
reason_instruction() : le VLM regarde l'écran, décide la prochaine
action atomique (click/type/hotkey/scroll/done), retourne un Decision
avec expected_after pour la vérification.

run_instruction() : boucle ORA complète pour instructions texte.
CognitiveContext mis à jour à chaque étape (objectif, historique,
faits appris, confiance).

POST /api/v3/execute/instruction : endpoint API pour lancer une
instruction en langage naturel. Thread daemon, polling du résultat
via GET /api/v3/execute/instruction/result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 09:09:53 +02:00
Dom
0c5fffe951 feat: boucle ORA (observe→raisonne→agit) avec vérification post-action
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Nouveau module core/execution/observe_reason_act.py (794 lignes) :
- ORALoop : boucle unifiée pour workflow VWB et instructions
- observe() : capture écran + pHash + titre fenêtre
- reason_workflow_step() : mappe step VWB → Decision (sans VLM)
- act() : template matching → find_element → pyautogui
- verify() : Level 1 pHash + Level 2 VLM conditionnel
- run_workflow() : boucle complète avec retries et callbacks

Nouveau mode execution_mode='verified' dans execute.py :
- run_workflow_verified() utilise ORALoop
- Modes basic/intelligent/debug inchangés (zéro risque)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-22 09:02:54 +02:00
Dom
5027ed9a23 chore: sauvegarde workflows.db après 23 tests de fiabilité réussis
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
23/24 tests du workflow Demo PMSI réussis (1 échec = main sur souris).
Template matching en premier (~80ms), CLIP batch en fallback (~4.5s).
Total workflow : ~20s (était 131s il y a 24h).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 20:15:22 +02:00
Dom
6caab2c600 perf: boucle fermée pHash (2s→150ms) + batch CLIP (90 appels→1)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Boucle fermée : time.sleep(2.0) remplacé par _wait_for_screen_change()
qui poll le pHash toutes les 150ms. Sort dès que l'écran change.
4 occurrences remplacées.

Batch CLIP : filtre par distance AVANT le CLIP (90→~20 éléments),
puis embed_image_batch() en un seul appel GPU + np.dot vectorisé.

Estimé : 42s→~20s total workflow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:33:42 +02:00
Dom
552e66dbf6 fix: import io manquant dans template matching
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:21:15 +02:00
Dom
de1026ee2e perf: template matching direct en PREMIER (~1-10ms)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
cv2.matchTemplate cherche l'ancre directement dans le screenshot.
Pas de RF-DETR, pas de CLIP, pas de 90 comparaisons.
Seuil 0.75 pour éviter les faux positifs.

Ordre : template (1ms) → CLIP (fallback) → OCR/UI-TARS (dernier recours)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 19:17:08 +02:00
Dom
7b50725bf8 perf: RF-DETR sur GPU (cuda) — était sur CPU = 28s par étape
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
RF-DETR détecte 90+ éléments UI par screenshot. Sur CPU = 28s.
Sur GPU RTX 5070 = devrait être 1-3s.

CLIP auto-GPU déjà en place (vérifie 1.5 Go VRAM libre).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:54:19 +02:00
Dom
7feef3b6a9 fix: CLIP en premier, suppression vérification OCR croisée, fix indentation
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:36:20 +02:00
Dom
0b06db222d fix: activer la fenêtre cible après minimisation du navigateur VWB
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Après minimisation du navigateur, xdotool active la fenêtre suivante
(VM QEMU, app cible). Avant, le terminal restait au premier plan →
mss capturait le terminal au lieu de la VM.

Cause racine de tous les échecs de matching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 18:21:55 +02:00
Dom
74ee0dadee perf: pré-chargement docTR au démarrage + nettoyage debug logs
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
docTR se chargeait au premier appel OCR (~30s). Maintenant pré-chargé
au démarrage du backend → premier clic rapide.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 17:25:35 +02:00
Dom
0b452f975a fix: pénaliser matchs OCR partiels trop courts (demo dans CR_patient_demo)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 16:49:22 +02:00
Dom
6ab385d671 fix(grounding): OCR collecte TOUS les matchs + choisit le plus proche de l'ancre
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Avant : OCR retournait le premier match → cliquait sur la barre de titre
("CR_patient_demo" dans le path) au lieu du fichier dans la liste.

Après : collecte tous les matchs, choisit le plus proche de la position
originale de l'ancre (anchor_bbox). Si pas de bbox, prend le plus central.

Élimine les clics sur les barres de titre, breadcrumbs, menus.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 16:40:15 +02:00
Dom
b3eab83a0f fix: variable 'result' non définie quand grounding réussit sans CLIP
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 16:26:45 +02:00
Dom
27490849a8 refactor: OCR/UI-TARS en PREMIER, CLIP en fallback
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Le grounding par texte (OCR → UI-TARS) est maintenant la méthode
PRINCIPALE. CLIP n'est appelé que si le grounding échoue.

Avant : CLIP (faux positifs confiants) → cascade grounding (rarement atteinte)
Après : OCR 1s → UI-TARS 3s → CLIP (fallback visuel pur)

C'est comme ça que font UI-TARS, Agent-S3 et AppAgent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 14:40:38 +02:00
Dom
cebbf0809a fix: timeout VLM 15→60s + OCR zone élargie autour de l'ancre
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 14:05:38 +02:00
Dom
3e227d28ad fix(vwb): image plein écran — calcul dimensions JS explicite (fix définitif)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Cause racine : max-width/max-height CSS ne font pas GRANDIR une image.
Fix : calcul explicite width/height en JS via Math.min(ratio).
min-height:0 sur le conteneur flex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 12:19:30 +02:00
Dom
8ce63fcba2 fix(vwb): CSS max-height 100% → calc(100vh-70px) — cause racine du timbre poste
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 17s
tests / Tests sécurité (critique) (push) Has been skipped
Le fichier CSS avait max-height:100% sur .fullscreen-content img
qui écrasait le style inline calc(100vh-70px). 100% d'un conteneur
flex sans hauteur explicite = taille naturelle de l'image = minuscule.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 12:10:24 +02:00
Dom
4202431421 fix(vwb): image plein écran maxHeight calc(100vh-70px) basé sur viewport
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 17s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:58:58 +02:00
Dom
4923623dd4 fix(vwb): bibliothèque ne s'écrase plus au chargement
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 16s
tests / Tests sécurité (critique) (push) Has been skipped
Le useEffect(saveLibrary) se déclenchait avec library=[] avant que
loadLibraryAsync ait fini → écrasait le fichier serveur avec un
tableau vide. Ajout d'un flag libraryLoaded pour ne sauvegarder
qu'après le chargement initial.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:54:16 +02:00
Dom
84181cc982 feat: analyse OCR+VLM de l'ancre à la capture (pas à l'exécution)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Quand l'utilisateur sélectionne une ancre dans le VWB :
1. OCR docTR extrait le texte du crop → target_text
2. Si texte < 3 chars → VLM qwen2.5vl:3b décrit en 5 mots
3. Stocké en BDD (VisualAnchor.target_text + ocr_description)
4. Injecté automatiquement dans les params à l'exécution

L'exécution sait maintenant QUOI chercher dès le départ :
- CLIP vérifie par OCR que le texte correspond
- Le grounding cascade a un vrai target_text
- Plus besoin de deviner à chaque run

Migration SQLite gracieuse (ALTER TABLE si colonnes absentes).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:26:30 +02:00
Dom
7355d315a3 fix: vérification croisée CLIP+OCR + description ancre avant exécution
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Quand CLIP dit "trouvé", on vérifie par OCR que le texte à cette
position correspond au target. Si CLIP clique sur "Ce PC" au lieu
de "CR_patient_demo", l'OCR le rejette → fallback sur la cascade.

Description VLM de l'ancre AVANT le CLIP quand le label est un
type d'action (double_click_anchor → "text file icon CR_patient").
Le target_text enrichi sert à la vérification croisée ET au grounding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 11:10:01 +02:00
Dom
c50adab3a1 fix: aligner capture monitors[0] partout (cause de la régression)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
La capture VWB utilisait monitors[0] (composite) mais l'exécution
utilisait monitors[1] (premier écran). Images incompatibles → CLIP
retournait 0.00 sur un écran identique.

Tous les fichiers alignés sur monitors[0].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:52:13 +02:00
Dom
2fbb305f65 fix: remonter seuil CLIP à 0.45 — le 0.20 créait des faux positifs
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Le seuil 0.20 faisait que CLIP cliquait sur Chrome au lieu du dossier
Demo (score 0.25 accepté = faux positif). Le seuil 0.45 rejette les
matchs faibles et la cascade OCR/UI-TARS prend le relais proprement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:39:02 +02:00
Dom
ff581be397 perf: seuil CLIP 0.45→0.20 + cache singleton IntelligentExecutor
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Seuil CLIP abaissé pour les icônes génériques (dossier, fichier)
qui obtenaient 0.25 au lieu de 0.45.

IntelligentExecutor en singleton — CLIP et RF-DETR chargés une
seule fois et réutilisés entre les étapes. Élimine le rechargement
de ~40s par étape.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:29:15 +02:00
Dom
203e5cc6c1 fix(grounding): désactiver orchestrateur VRAM pendant exécution + qwen2.5vl:3b pour description
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 16s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
L'orchestrateur VRAM redémarrait Ollama en pleine exécution → timeout.
Désactivé pendant le workflow. L'orchestrateur reste disponible pour
bascule manuelle avant/après.

Description ancre via qwen2.5vl:3b (3 Go) au lieu de 7b — tient en VRAM
sans décharger CLIP ni RF-DETR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:16:27 +02:00
Dom
d1b556b6cd fix(grounding): supprimer SeeClick cassé + log description ancre
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
SeeClick supprimé : modèle HF incompatible (QWenConfig non reconnu),
crashait à chaque exécution et polluait les logs.
Remplacé par UI-TARS via la chaîne de grounding.

Log warning visible quand la description VLM de l'ancre échoue
(pour diagnostiquer les problèmes de VRAM).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 10:05:29 +02:00
Dom
729cd67743 feat(grounding): description VLM de l'ancre quand le label est vide
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Quand le target_text est vide ou identique au type d'action
(click_anchor, double_click_anchor...), le VLM décrit l'image
de l'ancre en 5 mots ("folder icon named Demo").

Cette description est ensuite passée à UI-TARS pour le grounding
("click on folder icon named Demo") et à l'OCR pour la recherche.

Chaîne complète : VLM décrit → OCR cherche → UI-TARS grounding → VLM raisonne.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:44:19 +02:00
Dom
73ddcdb29d feat: chaîne de grounding 3 niveaux + refonte capture écran
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Grounding en cascade quand CLIP/template échouent :
1. OCR (docTR) → cherche le texte exact sur l'écran (~1s)
2. UI-TARS grounding → "click on X" → coordonnées (~3s, 94% ScreenSpot)
3. VLM reasoning → raisonnement complet + confirmation OCR (~10s)

find_element_on_screen() dans input_handler.py (partagé VWB + Léa).
Câblé dans find_and_click() et execute_action() comme fallback.

Refonte capture écran :
- mss.monitors[0] (composite) pour capturer la VM en plein écran
- FullscreenSelector réécrit : overlay via getBoundingClientRect()
- Bboxes et sélection alignées avec l'image (calcul JS, pas CSS)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:31:38 +02:00
Dom
14a9442343 refactor(vwb): refonte complète capture écran — stable définitivement
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
FullscreenSelector réécrit :
- Overlay unique positionné via getBoundingClientRect()
- Recalcul auto au resize
- Coordonnées souris relatives à l'image
- Plus de décalage bboxes/sélection

Capture backend :
- mss.monitors[0] (écran composite) au lieu de pyautogui.screenshot()
- Capture la VM en plein écran correctement

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 09:03:19 +02:00
Dom
5da4581e76 feat(cognition): orchestrateur VRAM + VLM 7b par défaut
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
VRAMOrchestrator : bascule automatique entre modes SHADOW et REPLAY.
- SHADOW : streaming server + agent_chat actifs
- REPLAY : VLM qwen2.5vl:7b chargé, services non-essentiels stoppés

vlm_reason_about_screen() appelle ensure_reasoning_ready() avant
chaque raisonnement — libère la VRAM si nécessaire.

Benchmark : qwen2.5vl:7b en 10s (warm) vs 44s quand VRAM saturée.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 22:13:29 +02:00
Dom
cbe8dc95d2 feat(cognition): timing + écran attendu + auto-apprentissage Shadow + VLM qwen2.5vl
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Mémoire de travail enrichie :
- Timing par étape (durée, moyenne, alerte si lent)
- Écran attendu vs observation réelle
- Contexte VLM étendu

VLM reasoning : default qwen2.5vl:3b (gemma4 ne supporte pas vision)

Auto-apprentissage Shadow :
- stream_processor apprend les dialogues automatiquement
- Clic utilisateur après dialogue → pattern mémorisé
- Sauvegardé dans data/learned_patterns.json

GUI-R1 : 10 patterns additionnels extraits du dataset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 21:52:45 +02:00
Dom
04a14a56b2 feat(cognition): mémoire de travail — Léa sait où elle en est
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
CognitiveContext : bloc-notes interne réinjecté à chaque décision.
- objective : ce que Léa essaie de faire
- current_step : progression dans le plan
- action_history : les N dernières actions (succès/échec)
- learned_facts : faits appris pendant l'exécution
- confidence : auto-évaluation (baisse sur échec)
- needs_help : demande d'aide à l'humain
- to_prompt_context() : génère le texte pour le VLM

Module standalone, pas encore câblé dans l'executor.
Testé sur scénario de facturation OSIRIS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 21:41:10 +02:00
Dom
2290f1846b feat(cognition): raisonnement VLM quand les réflexes ne suffisent pas
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
vlm_reason_about_screen() : capture l'écran, envoie au VLM local
(gemma4/Ollama) avec l'objectif et le contexte, retourne une action
en JSON (click/type/wait/nothing + target + reasoning).

Chaîne de décision :
1. Réflexes (UIPatternLibrary) → instantané
2. OCR bouton (docTR) → rapide
3. VLM reasoning (Ollama) → intelligent, ~2-5s

Le VLM intervient UNIQUEMENT quand 1 et 2 échouent — pas de latence
ajoutée quand les réflexes suffisent.

UIPatternLibrary enrichie : charge builtin + GUI-R1 + learned patterns.
save_learned_pattern() persiste les patterns appris par Shadow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 21:37:03 +02:00
Dom
c57b40ae1d feat: CLIP auto-GPU si >1.5 Go VRAM libre + index FAISS IVF 11.5x plus rapide
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
CLIP embedder : auto-détection GPU avec vérification VRAM disponible.
Si >1.5 Go libre → CUDA, sinon → CPU. Évite les OOM quand Ollama
utilise déjà la VRAM.

FAISS : migration Flat → IVF (116 clusters, nprobe=8).
Benchmark : 0.46ms → 0.04ms par recherche (11.5x).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 21:27:01 +02:00
Dom
bc21b27da7 fix(dashboard): diagrammes BPMN/DFG grande taille (DPI 150, layout vertical)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Les images générées par PM4Py étaient trop petites et illisibles.
- DPI 150, taille 40x20 pouces, layout vertical (TB)
- La modale plein écran permet le défilement (scroll)
- Fallback sur pm4py.save_vis si le rendu Graphviz échoue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 18:29:49 +02:00
Dom
6a2248ddcd feat(dashboard): clic plein écran sur les images cartographie
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Modale fullscreen au clic sur les diagrammes BPMN/DFG.
Fermeture par clic ou Échap. Les images sont illisibles en miniature.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 18:26:05 +02:00
Dom
82d7b38cff feat(dashboard): page Base de connaissances — métriques FAISS, sessions, patterns
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Nouvelle page /knowledge-base avec :
- Mémoire visuelle : 331 vecteurs FAISS / 13666 embeddings (alerte consolidation)
- Sessions observées : 56 sessions, 6.66 Go, 3 machines
- Réflexes natifs : 16 patterns UI en 6 catégories
- Workflows appris : 29

Onglet 📚 Connaissances ajouté dans toute la navigation.
Tout en français, dark theme, zéro jargon.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:41:23 +02:00
Dom
6c7f88c05d refactor: factorisation input_handler partagé + page cartographie processus
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
core/execution/input_handler.py (NOUVEAU) :
- safe_type_text() : setxkbmap fr + xdotool, partagé entre les 2 executors
- check_screen_for_patterns() : détection dialogues UI via OCR
- handle_detected_pattern() : clic bouton par OCR (mot exact, le plus bas)
- post_execution_cleanup() : vérification post-workflow

VWB executor : suppression du code dupliqué, alias vers input_handler
Core executor : pyautogui.write() remplacé par safe_type_text()

Page dashboard "Cartographie des processus" :
- GET /process-mining : vue analyse des flux de travail
- POST /api/process-mining/discover : génère BPMN + indicateurs
- 4 cartes indicateurs, diagramme, points d'attention, variantes
- Dark theme, français, zéro jargon technique
- Onglet ajouté dans la navigation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:08:37 +02:00
Dom
447fbb2c6e chore: sauvegarde complète avant factorisation executor
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Point de sauvegarde incluant les fichiers non committés des sessions
précédentes (systemd, docs, agents, GPU manager).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 17:03:44 +02:00
Dom
623be15bfe fix(knowledge): triggers courts en mot entier + cookies trigger enrichi
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 12s
tests / Tests unitaires (sans GPU) (push) Failing after 12s
tests / Tests sécurité (critique) (push) Has been skipped
Les triggers ≤3 chars (ok, no) utilisent maintenant des frontières
de mots (\b) pour éviter les faux positifs (ok dans cookies).
Trigger "utilise des cookies" ajouté pour le pattern cookie_accept.

7/7 patterns validés en test terrain simulé.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 15:45:58 +02:00
Dom
55d5aebbd2 feat(knowledge): vérification post-workflow — dialogues restants
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 9s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Après la dernière étape, Léa vérifie l'écran et gère les dialogues
restants (jusqu'à 3 vérifications en cascade). Le workflow laisse
l'écran propre à la fin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:53:38 +02:00
Dom
73b731fef8 fix(knowledge): seuil OCR bouton 3→2 chars pour supporter OK et No
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 18s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Le filtre len<3 bloquait les boutons "OK" (2 chars) et "No" (2 chars).
Seuil abaissé à 2 — filtre les lettres isolées mais laisse passer
les boutons courts courants des dialogues Windows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:09:10 +02:00
Dom
ffd97ae9a5 feat(knowledge): détection et gestion automatique des dialogues UI
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 12s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
UIPatternLibrary câblée dans l'executor et le stream processor.
Pendant un wait_for_anchor, Léa surveille l'écran toutes les secondes :
1. OCR plein écran (docTR)
2. Pattern matching (dialogues Save, OK, Cancel, cookies...)
3. OCR ciblé pour trouver le bouton par son texte réel
4. Clic sur le match le plus bas (bouton, pas titre)

Fix : seuil ratio supprimé (trigger trouvé = match, quelle que soit
la longueur du texte OCR). Matching strict mot exact ≥3 chars
(évite les faux positifs sur lettres isolées). Fallback recherche
partielle pour les lettres soulignées (E_nregistrer).

Plus aucune coordonnée hardcodée — 100% vision.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:06:17 +02:00
Dom
d168833609 fix: import Optional/Dict/Any pour _check_screen_for_patterns
Some checks failed
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 9s
security-audit / Scan secrets (grep) (push) Successful in 7s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 10:55:26 +02:00
Dom
23a06a744c feat(knowledge): câblage UIPatternLibrary dans executor + stream processor
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
VWB Executor :
- _check_screen_for_patterns() : capture écran + OCR + pattern matching
- _handle_detected_pattern() : clic automatique sur dialogues connus
- Vérifie entre chaque étape en mode intelligent/debug
- Si un dialogue bloque (OK, Save, Cancel), Léa le gère seule

Stream Processor :
- Enrichit les ScreenState avec ui_pattern/ui_pattern_action/ui_pattern_target
- Les patterns détectés sont loggés et stockés dans les résultats
- Permet au GraphBuilder de savoir quels écrans sont des dialogues

Phase 2 du plan "connaissance native de l'environnement".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 10:54:19 +02:00
Dom
af4eae28b9 feat(knowledge): base de connaissances UI — réflexes natifs pour Léa
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
UIPatternLibrary : 16 patterns builtin (dialogues, menus, formulaires,
popups, raccourcis) qui donnent à Léa des réflexes immédiats.

Quand Léa reconnaît "Voulez-vous enregistrer ?" elle sait cliquer
sur "Enregistrer" sans apprentissage préalable.

- core/knowledge/ui_patterns.py : bibliothèque avec find_pattern(),
  get_dialog_handler(), add_pattern() pour patterns appris
- Métadonnées GUI-R1 (3K exemples) extraites dans data/ (gitignored)

Phase 1 du plan "connaissance native de l'environnement".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 10:44:45 +02:00
Dom
c198c930a1 fix(vwb): capture plein écran — retirer height:0 + wrapper flex
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 9s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 12s
tests / Tests sécurité (critique) (push) Has been skipped
Le conteneur .fullscreen-content avait height:0 + min-height:0
qui écrasait la hauteur du flex child → image minuscule.
Le wrapper inline-block limitait aussi le dimensionnement.

Fix : overflow:hidden sans height forcée, wrapper en flex 100%.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 10:28:16 +02:00
Dom
e3efef2fe7 fix(vwb): noms workflows lisibles + bibliothèque captures persistante
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
CSS : le dropdown héritait color:white du header → forcé #212121
sur .workflow-dropdown et .dropdown-item .item-name

Bibliothèque : migration localStorage → backend (capture_library.json)
- GET/POST /api/v3/capture/library (max 50 captures)
- loadLibraryAsync() charge depuis backend, fallback localStorage
- saveLibrary() écrit dans les deux (localStorage + backend)
- capture_library.json gitignored

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 00:04:30 +02:00
Dom
95fddeebb3 fix(typing): setxkbmap fr avant xdotool type — fix AZERTY dans VM QEMU
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Le refresh du layout X11 juste avant xdotool type force le bon keymap.
Sans ça, xdotool envoie des keycodes décalés (: → M, / → !, etc.)
dans les VM spice/QEMU.

Solution trouvée via askubuntu.com/questions/914718.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:52:19 +02:00
Dom
71523cebd3 fix(typing): presse-papier en priorité (fonctionne avec spice-vdagent)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Remet xclip+Ctrl+V comme méthode prioritaire. Les spice tools sont
installés dans la VM → le presse-papier est partagé → pas de problème
de mapping clavier. xdotool envoie des événements synthétiques X11
que spice/QEMU traite différemment des vraies frappes (: → M).

Citrix partage aussi le clipboard nativement → même méthode en prod.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:27:54 +02:00
Dom
3aa806a630 fix(typing): hybride xdotool type+key — rapide et compatible AZERTY/VM
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 7s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
xdotool type pour les segments alphanumériques (un seul appel, rapide),
xdotool key avec keysym uniquement pour les caractères spéciaux
(:, /, @, etc.) qui cassent en AZERTY dans les VM.

Évite le subprocess par caractère (trop lent, effet visuel désagréable).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:18:21 +02:00
Dom
588c8f22c1 fix(typing): xdotool key par keysym au lieu de type (fix AZERTY dans VM)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
xdotool type envoie des scancodes QWERTY — dans une VM AZERTY,
':' devient 'M', '/' devient '!', etc.

Nouvelle approche : xdotool key avec les noms de keysym X11
(colon, slash, period, etc.) qui sont indépendants du layout.
Chaque caractère est envoyé individuellement — plus lent mais
100% fiable en AZERTY/QWERTY, local ou VM.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:15:44 +02:00
Dom
3d243d731d fix: xdotool prioritaire sur clipboard (VM/Citrix), cosmétique sidebar
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
safe_type_text() : xdotool type en priorité au lieu du presse-papier.
Le clipboard xclip ne traverse pas les VM (QEMU) ni Citrix/RDP.
xdotool envoie des frappes X11 réelles que les VM capturent.
Délai 20ms entre caractères pour fiabilité.

Cosmétique : couleur texte forcée sur les items workflow du sidebar
(color: var(--text-primary)) — était blanc sur blanc.

Logs diagnostic ajoutés dans execute_workflow_thread et execute_action.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:11:10 +02:00
Dom
2431a6c9e9 fix(vision): dernier seuil distance hardcodé (150px→500px) + nettoyage commentaires
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
MAX_TEMPLATE_DISTANCE dans zoned_template_match était encore à 150px.
Tous les seuils de distance sont maintenant alignés à 500px :
- MAX_DISTANCE_PX (CLIP) : 500
- MAX_GLOBAL_DISTANCE (template global) : 500
- MAX_SEECLICK_DISTANCE : 500
- MAX_TEMPLATE_DISTANCE (template zonée) : 500

Commentaires périmés corrigés (plus de références aux anciennes valeurs).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 19:52:20 +02:00
Dom
969236da03 fix(vision): distance max 500px pour template global et SeeClick
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Le template matching global trouvait l'icône Chrome à 0.99 de confiance
mais la rejetait car elle avait bougé de >150px. Même problème pour
SeeClick (>200px). Aligné tous les seuils de distance à 500px
pour supporter les workflows VWB cross-résolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 19:48:26 +02:00
Dom
f30461b88c fix(vision): seuils grounding assouplis pour VWB cross-résolution
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 11s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
MAX_DISTANCE_PX 120→500 (ancre peut être loin si résolution différente)
MIN_CLIP_SCORE 0.55→0.50 (tolérance basique suffisante)
MIN_COMBINED_SCORE 0.5→0.45 (accepter les matchs raisonnables)

L'icône Chrome à 81% de confiance était rejetée à cause de la distance.
Les workflows VWB manuels capturent sur un écran et s'exécutent
potentiellement sur un autre — la tolérance de distance doit être large.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 17:09:08 +02:00
Dom
f34eca20f9 fix(vwb): double accolades JSX dans CapturePanel et CaptureLibrary
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Corrige les src={{b64ImgSrc(...)}} → src={b64ImgSrc(...)} causés par
le replace_all sur les template literals. Corrige aussi l'appel
b64ImgSrc dans du code JS pur (pas de {} autour).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:49:58 +02:00
Dom
309dfd5287 feat: process mining BPMN, détection changement écran pHash, OCR docTR
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 15s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
Process Mining (core/analytics/process_mining_bridge.py) :
- Bridge PM4Py : conversion sessions Shadow → event log → BPMN XML + PNG
- KPIs automatiques : durée, variantes, goulots, distribution par app
- Support sessions JSONL brutes et workflows core JSON
- 42 tests (dont 1 sur données réelles)

Détection changement d'écran (core/analytics/screen_change_detector.py) :
- pHash (imagehash) : ~16ms par screenshot, seuils SAME/MINOR/MAJOR
- 8 tests sur screenshots réels

OCR docTR dans execute_extract_text :
- docTR par défaut pour lecture simple (rapide, CPU)
- Ollama VLM en fallback ou sur demande explicite (mode "vlm"/"ai")
- Dual-mode adaptatif selon extraction_mode

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 13:07:56 +02:00
Dom
f5a672d7b9 fix(vwb): capture plein écran + auto-détection MIME PNG/JPEG des ancres
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 12s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
- CSS fullscreen-content : height:0 + min-height:0 pour forcer flex fill
- Image fullscreen : max-height calc(100vh - 60px) + object-fit contain
- Fonction b64ImgSrc() détecte automatiquement PNG vs JPEG depuis le base64
- Corrige l'affichage des thumbnails compressés JPEG dans la bibliothèque
- Appliqué dans CapturePanel + CaptureLibrary (toutes les occurrences)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:55:51 +02:00
Dom
1acea85fa6 feat(vwb): câblage 19 blocs, OCR réel, screenshots ancres, configs déploiement
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Dispatch execute_action élargi de 12 à 19 blocs opérationnels :
- 4 blocs souris (hover, drag_drop, scroll, focus) avec pyautogui
- extract_text via Ollama VLM (remplace stub hardcodé)
- 5 blocs ai_* redirigés vers execute_ai_analyze avec prompts adaptés
- screenshot_evidence (capture + sauvegarde PNG)
- verify_element_exists (détection visuelle CLIP)

Import workflows Léa enrichi :
- Bridge extrait anchor_image_base64 des edges
- Import crée VisualAnchor en DB + fichiers thumbnail sur disque
- PropertiesPanel affiche automatiquement les screenshots

Frontend :
- visual_condition et loop_visual masqués (hidden: true)
- Filtre dans ToolPalette pour exclure les blocs cachés

Déploiement :
- 2 configs agent (TIM Pauline + Dev Windows) avec machine_id unique
- 2 workflows démo dans la BDD (batch factures + extraction IA)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 09:40:28 +02:00
Dom
4f61741420 feat: journée 17 avril — tests E2E validés, dashboard fleet+audit, VWB bridge, cleaner C2
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 14s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Pipeline E2E complet validé :
  Capture VM → streaming → serveur → cleaner → replay → audit trail
  Mode apprentissage supervisé fonctionne (Léa échoue → humain → reprise)

Dashboard :
  - Cleanup 14→10 onglets (RCE supprimée)
  - Fleet : enregistrer/révoquer agents, tokens, ZIP pré-configuré téléchargeable
  - Audit trail MVP (/audit) : filtres, tableau, export CSV, conformité AI Act/RGPD
  - Formulaire Fleet simplifié (nom + email, machine_id auto)

VWB bridge Léa→VWB :
  - Compound décomposés en N steps (saisie + raccourci visibles)
  - Layout serpentin 3 colonnes (plus colonne verticale)
  - Badge OS 🪟/🐧, filtre OS retiré (admin Linux voit Windows)
  - Fix import SQLite readonly

Cleaner intelligent :
  - Descriptions lisibles (UIA/C2) + détection doublons
  - Logique C2 : UIElement identifié = jamais parasite
  - Patterns parasites resserrés
  - Message Léa : "Je n'y arrive pas, montrez-moi comment faire"

Config agent (INC-1 à INC-7) :
  - SERVER_URL + SERVER_BASE unifiés
  - RPA_OLLAMA_HOST séparé
  - allow_redirects=False sur POST
  - Middleware réécriture URL serveur

CI Gitea : fix token + Flask-SocketIO + ruff propre
Fleet endpoints : /agents/enroll|uninstall|fleet + agent_registry SQLite
Backup : script quotidien workflows.db + audit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 17:46:40 +02:00
Dom
2fa864b5c7 chore(ops): script de backup quotidien workflows.db + audit
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 10s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Critique avant POC Anouste — trou identifié par le challenge du 16
avril. Sans backup, une perte de workflows.db = perte directe du
travail client (workflows, historique d'exécutions, ancres visuelles).

Script scripts/backup_vwb_and_audit.sh :
- Copie workflows.db via `sqlite3 .backup` (snapshot cohérent, même
  si le backend Flask tient la BDD ouverte) → ~/backups/vwb/
- Copie data/audit/*.jsonl → ~/backups/audit/audit_YYYY-MM-DD/
- Rétention automatique 30 jours (override via RETENTION_DAYS env)
- Destination override : BACKUP_ROOT=/chemin env var
- Log horodaté : ~/backups/backup.log

Installation (non automatique — à la main, cf. consigne) :
  crontab -e
  0 2 * * * /home/dom/ai/rpa_vision_v3/scripts/backup_vwb_and_audit.sh

Procédure de restore documentée dans ~/backups/README.md (créé hors
repo, volontairement).

Testé : 458752 octets restaurés à partir de workflows.db actuel
(3 workflows, 115 exécutions, 18 steps, intégrité OK).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:43:31 +02:00
Dom
10739c33fa feat(vwb): nom par défaut explicite pour workflows importés de Léa (B2)
Avant : tous les workflows importés s'appelaient « Unnamed Workflow »
→ la liste devenait illisible dès qu'il y en avait plusieurs.

Après : génération d'un nom explicite par _derive_default_name :
  1. Premier `template.window.title_pattern` utile dans les nodes
     (filtrage de "Unknown" / "unknown_window"), avec extraction de
     l'app derrière le séparateur Windows « – » / « - »
     (ex: « Sans titre – Bloc-notes » → « Bloc-notes »).
  2. Premier `template.window.process_name` non-null
     (ex: « explorer.exe »).
  3. Fallback : 8 premiers caractères du workflow_id, après
     nettoyage des préfixes techniques ("workflow_sess_", ...).

Le nom final inclut toujours la date de l'import :
    « Léa Bloc-notes — 2026-04-16 08:41 »
    « Léa explorer.exe — 2026-04-16 08:41 »
    « Léa 20260404 — 2026-04-16 08:41 » (fallback)

Ne se déclenche que si le nom entrant est vide,
« Unnamed Workflow » ou « Workflow importé » (insensible à la
casse). Le paramètre `name` explicite de la requête reste
prioritaire. L'utilisateur peut renommer via le bouton éditer.

Pas de modification du schema workflow (champ `name` existant).

Tests manuels sur données réelles :
- notepad_enriched.json (tous nodes "Unknown") → fallback id OK
- Bloc-notes, Explorateur et Recherche (2) → « Léa Rechercher »
- workflow construit avec title 'Sans titre – Bloc-notes'
  → « Léa Bloc-notes » OK

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:42:11 +02:00
Dom
39bea1b042 fix(vwb): bibliothèque de captures persistée en localStorage (B1)
Avant : CaptureLibrary.tsx utilisait sessionStorage (purgé à la
fermeture d'onglet), et CapturePanel.tsx maintenait une liste
concurrente sous une clé différente (captureLibrary vs
captureLibrary_v2) → deux vues désynchronisées qui s'effacent
toutes les deux dès qu'on ferme le navigateur.

Après :
- Nouveau service captureLibraryStorage.ts (load/save/compress)
  comme point unique d'accès.
- Stockage en localStorage (persiste entre onglets et sessions).
- Clé unifiée 'captureLibrary_v2'.
- Migration automatique de sessionStorage → localStorage et de
  l'ancienne clé 'captureLibrary' → nouvelle, lors du premier load.
- Thumbnails compressés JPEG qualité 80% et redimensionnés à
  320×240 max avant stockage pour rester sous le quota navigateur
  (5–10 MB selon navigateur).
- Gestion QuotaExceededError dans saveLibrary : élague les items
  les plus anciens jusqu'à ce que ça passe (5 tentatives).
- Les deux composants consomment le même helper : fin de la
  divergence de format (sessionId/favorite).

Diagnostic (bug reproduit par lecture du code, pas besoin de
navigateur) :
- CaptureLibrary.tsx:28,42,62 → sessionStorage/captureLibrary_v2
- CapturePanel.tsx:53,61       → sessionStorage/captureLibrary
→ Deux sources, toutes deux éphémères.

Vérif : `npx tsc --noEmit` passe (EXITCODE=0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:40:01 +02:00
Dom
26b4e6d8ce chore(vwb): supprime la BDD fantôme vwb_v3.db (B3)
Fichier SQLite vide (toutes tables à 0 lignes), tracé en git mais
jamais peuplé. La vraie source de vérité est `workflows.db`
(DATABASE_URL dans backend/.env → 3 workflows, 115 exécutions,
920 steps).

Risque éliminé : si `.env` n'était pas chargé (ex : systemd mal
configuré), SQLAlchemy retombait sur le fallback
`sqlite:///vwb_v3.db` et l'app créait/utilisait une BDD
complètement vide à côté de la vraie. Foot-gun classique.

Correctif :
- Fallback de app.py aligné sur workflows.db.
- Fichier vwb_v3.db supprimé du repo.

workflows.db reste seule source de vérité.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:38:00 +02:00
Dom
4fb84b1090 chore(vwb): hygiène (B4+B6+B7)
- B4 : supprime le double logging dans backend/app.py.
  app.py est importé 2 fois (une fois comme __main__ via `python app.py`,
  une fois comme module `app` via `from app import socketio` dans
  api/websocket_handlers.py). Le RotatingFileHandler était donc ajouté
  2× au root logger → chaque ligne loguée dupliquée. Fix : garde
  idempotente qui vérifie si un handler vers vwb.log existe déjà.
- B6 : supprime les fichiers .pid résiduels (.backend.pid,
  .frontend.pid, .frontend_v4.pid) et les ajoute au .gitignore
  (avec *.lock, *.orig, *.bak).
- B7 : ajoute launch.sh (wrapper → run_v4.sh par défaut, legacy
  → run.sh), clarifie en tête de run.sh et run_v4.sh la distinction
  frontend/ (legacy v3) vs frontend_v4/ (actif), et rectifie le
  README.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 08:37:12 +02:00
Dom
7f2bc6fe97 feat(graph): enrichissement visuel des workflows (C2)
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 12s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 14s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped
GraphBuilder construit maintenant des ScreenState enrichis
(ui_elements + detected_text) au lieu de stubs vides, et associe
les clics aux UIElement par proximité spatiale.

Détails :
- __init__ accepte ui_detector, screen_analyzer, enable_ui_enrichment,
  element_proximity_max_px (+ lazy resolver via singleton C1)
- _create_screen_states délègue à ScreenAnalyzer.analyze() — remplace
  l'appel à _extract_text() qui n'existait plus depuis le Lot C
  (bug silencieux : OCR cassé en prod depuis ce jour, caught except)
- _find_clicked_element : bbox contenant strict + fallback proximité
  ≤50px, préfère le plus petit bbox (form vs button)
- _build_click_target_spec : TargetSpec(by_role, by_text,
  selection_policy="by_similarity") avec ancres dans context_hints
  (anchor_element_id, anchor_bbox, anchor_center)
- _build_edges propage le ScreenState source aux builders d'action
- WorkflowPipeline passe ui_detector + enable_ui_enrichment au builder

Impact : matching prod 3-5x plus précis, TargetSpec ne sont plus
des "unknown_element" génériques, UIConstraint.required_roles se
remplit correctement via _extract_common_ui_elements (qui marchait
depuis toujours mais sur des state.ui_elements vides).

Tests e2e migrés vers enable_ui_enrichment=False (2.9s vs 67s) —
ils valident le pipeline DBSCAN/edges, pas la détection UI réelle.

15 nouveaux tests, 178 tests passants au total (incluant Lots A-E).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:02:30 +02:00
Dom
eded968c70 ci(fix): RPA_API_TOKEN + Flask-SocketIO dans CI
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 11s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 15s
tests / Tests sécurité (critique) (push) Has been skipped
Deux fixes pour que la CI collecte tous les tests unitaires :

1. RPA_API_TOKEN défini dans les env du workflow
   - Sans : agent_v0/server_v1/api_stream.py fait sys.exit(1)
     au module load (fail-closed P0-C), ce qui casse la collection
     de tests/unit/test_env_setup.py (qui importe api_stream)
   - Avec : token bidon qui permet aux imports de passer,
     les tests mockent les vraies requêtes

2. Flask-SocketIO + deps socketio ajoutés à requirements-ci.txt
   - web_dashboard/app.py importe `from flask_socketio import SocketIO`
   - test_dashboard_routes.py importait app -> ModuleNotFoundError en CI
   - Ces packages étaient explicitement exclus avant, mais sont
     nécessaires pour les 37 tests du dashboard

Résultat local : 1723 passed, 39 failed (dette pré-existante
documentée dans l'audit — contamination entre tests, à traiter
séparément).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:12:58 +02:00
Dom
53d29d9b24 fix(lint): ruff passe propre — 2 vrais bugs + suppression fichier corrompu
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 13s
security-audit / pip-audit (CVE dépendances) (push) Successful in 12s
security-audit / Scan secrets (grep) (push) Successful in 9s
tests / Lint (ruff + black) (push) Successful in 13s
tests / Tests unitaires (sans GPU) (push) Failing after 14s
tests / Tests sécurité (critique) (push) Has been skipped
Vrais bugs corrigés :
- core/execution/target_resolver.py : suppression de 5 lignes de dead code
  après un return (vestige de refacto incomplète référençant des params
  jamais assignés à self : similarity_threshold, use_spatial_fallback)
- agent_v0/agent_v1/core/executor.py:2180 : variable `prefill` référencée
  mais jamais définie. Initialisation explicite ajoutée en amont
  (conditionnée sur _is_thinking_popup, cohérent avec l'append du message)

Fichier supprimé :
- core/security/input_validator_new.py : contenu corrompu (texte inversé,
  artefact de copier-coller), jamais importé nulle part, 550 erreurs ruff
  à lui seul

Workflow CI :
- Exclusions ajoutées pour dossiers legacy connus cassés :
    - agent_v0/deploy/windows_client/ (clone obsolète)
    - tests/property/ (cf. MEMORY.md — imports cassés)
    - tests/integration/test_visual_rpa_checkpoint.py (VisualMetadata
      inexistant, déjà documenté)

Résultat : "ruff All checks passed!" sur core/ agent_v0/ tests/
(avec E9,F63,F7,F82 — syntax + undefined critiques).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:01:11 +02:00
Dom
690053bd57 ci: retrigger après fix network container runner
Some checks failed
security-audit / Bandit (scan statique) (push) Successful in 24s
security-audit / pip-audit (CVE dépendances) (push) Successful in 15s
security-audit / Scan secrets (grep) (push) Successful in 8s
tests / Lint (ruff + black) (push) Failing after 12s
tests / Tests unitaires (sans GPU) (push) Failing after 30s
tests / Tests sécurité (critique) (push) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:50:04 +02:00
Dom
c7b0649716 docs(ci): note d'activation CI Gitea + runner dom-local-runner
Some checks failed
security-audit / Bandit (scan statique) (push) Failing after 1m29s
security-audit / pip-audit (CVE dépendances) (push) Failing after 33s
security-audit / Scan secrets (grep) (push) Failing after 25s
tests / Lint (ruff + black) (push) Failing after 24s
tests / Tests unitaires (sans GPU) (push) Failing after 30s
tests / Tests sécurité (critique) (push) Has been skipped
Commit trivial pour valider le déclenchement de la CI Gitea Actions
après enregistrement du runner.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:32:56 +02:00
Dom
2bfcfa4535 ci: Gitea Actions workflows + requirements-ci allégé
Workflows :
  .gitea/workflows/tests.yml          -> lint + unit + security (PR + push)
  .gitea/workflows/security-audit.yml -> bandit + pip-audit + grep secrets
                                         (hebdo + push main)

requirements-ci.txt : sous-ensemble léger de requirements.txt
  - Sans torch, transformers, CUDA, FAISS binaire, Ollama, PyQt5, doctr
  - Gain ~3 Go + ~2 min d'install CI
  - À resynchroniser manuellement si nouveau test importe un package absent

Tests slow/gpu/integration/performance/visual/smoke exclus volontairement
(nécessitent CUDA, Ollama localhost:11434, serveur complet).

Temps estimé par run :
  - Cold : ~3 min
  - Warm (cache pip) : ~1m30

Security-tests (test_security_safe_condition + test_security_signed_serializer)
marqués bloquants : régression sur ast eval safe ou pickle HMAC casse la CI.

docs/CI_SETUP.md : activation Gitea Actions, enregistrement runner,
skip CI, troubleshooting.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:07:40 +02:00
Dom
b808e48b1f feat(fleet): endpoints /agents/enroll|uninstall|fleet + SQLite
Endpoints REST pour le fleet management (utilisés par installeur Inno Setup) :
  POST /api/v1/agents/enroll    -> 201 {status, machine_id, api_token, agent}
  POST /api/v1/agents/uninstall -> 200 {status, machine_id, agent}
  GET  /api/v1/agents/fleet     -> 200 {active, uninstalled, totals}

Tous protégés par Bearer token (conforme _PUBLIC_PATHS existant).

Nouveau module agent_v0/server_v1/agent_registry.py :
  - Classe AgentRegistry (sqlite3 stdlib, WAL, thread-safe via Lock)
  - CRUD + soft-delete (uninstall = status="uninstalled", historique préservé)
  - Table enrolled_agents créée via IF NOT EXISTS (pas de migration nécessaire)
  - Ré-enrollment après uninstall = réactivation auto (allow_reactivate=True)
  - Chemin DB configurable via RPA_AGENTS_DB_PATH (défaut data/databases/rpa_data.db)

Fix fixture test_stream_processor : autouse RPA_API_TOKEN dans
TestAPIEndpoints pour éviter SystemExit P0-C au module load.

13 tests intégration (enroll/uninstall/fleet + auth + edge cases).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:07:19 +02:00
Dom
78ee962918 feat(matching): match_current_state_from_state consomme enrichi (Lot E)
Nouvelle méthode match_current_state_from_state(screen_state, workflow_id)
qui utilise directement le ScreenState enrichi (window_title, detected_text,
ui_elements) fourni par ExecutionLoop au lieu de reconstruire un stub
ScreenState("Unknown", ui_elements=[], ...).

Préfère HierarchicalMatcher si workflow chargeable, fallback FAISS sinon.

L'ancienne API match_current_state(screenshot_path, workflow_id) est
convertie en wrapper : appelle ScreenAnalyzer.analyze() puis délègue.
Rétrocompat préservée.

ExecutionLoop._execute_step utilise la nouvelle méthode -> plus de double
analyze() dans le chemin d'exécution (économie latence).

Premier vrai matching context-aware. 11 nouveaux tests + 2 tests
integration loop. 172 tests non-régression verts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:07:04 +02:00
Dom
c8a3618e27 feat(cache): ScreenStateCache clé composite context-aware (Lot D)
Avant : clé = phash seul
-> deux contextes différents avec même screenshot partageaient
la même entrée cache -> collisions silencieuses.

Après : clé composite {phash}|{md5(ctx)[:16]} avec ctx =
  - window_title
  - app_name
  - enable_ocr
  - enable_ui_detection
  - workflow_id (isolation inter-workflows)

get_or_compute() kwargs-only. TTL 2s et éviction LRU inchangés.
invalidate_if_changed() continue de comparer uniquement les phash.

ExecutionLoop propage tout le contexte au cache.

8 nouveaux tests prouvant :
  - même image + window différent = miss
  - même image + app différent = miss
  - même image + flags différents = miss
  - même image + workflow_id différent = miss
  - même image + même contexte = hit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:06:51 +02:00
Dom
9ca277a63f refactor(pipeline): ScreenAnalyzer thread-safe et isolé (Lot C)
Retrait de l'état global toxique :
  - analyze() : kwargs-only enable_ocr, enable_ui_detection, session_id
  - Ne mute JAMAIS self pour les flags (variables locales + branches)
  - _resolve_ocr_instance() / _resolve_ui_detector_instance() : lecture seule
  - _init_lock par instance pour lazy init concurrent safe
  - session_id par appel, plus via mutation singleton

Avant : ExecutionLoop mutait analyzer._ocr, _ui_detector,
_ocr_initialized, _ui_detector_initialized pour désactiver OCR/UI.
Deux loops partageant le singleton se polluaient mutuellement.

Après : deux loops partageant l'analyzer sont complètement isolés.
Preuve par TestAnalyzerIsolationBetweenLoops (3 tests).

Singleton get_screen_analyzer() préservé — garde uniquement les
ressources lourdes, plus de contexte d'exécution.

9 nouveaux tests (3 isolation + 6 kwargs-only/lazy-init).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:06:41 +02:00
Dom
8c7b6e5696 feat(scoring): EdgeScorer utilise la vraie source_similarity (Lot B)
Avant : source_similarity=1.0 hardcodé dans _check_preconditions
-> la contrainte EdgeConstraints.min_source_similarity était
silencieusement désactivée. Un edge passait toujours.

Après : propagation ExecutionLoop -> workflow_pipeline -> EdgeScorer
  - select_best/rank/score_edge/_check_preconditions acceptent
    source_similarity: float (kwargs-only)
  - get_next_action() le propage
  - execution_loop passe la confidence issue de match_current_state

La contrainte min_source_similarity est opérationnelle pour la
première fois. Preuve concrète par test_min_source_similarity_fail
et test_low_similarity_blocks_edge (edge rejeté si sim < seuil).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:06:28 +02:00
Dom
af4ffa189a feat(analytics): normalise API + contrat explicite get_next_action (Lot A)
Contrat get_next_action() — suppression du None ambigu :
  {"status": "selected", "edge": ..., ...}
  {"status": "terminal"}
  {"status": "blocked", "reason": "no_valid_edge" | ...}

ExecutionLoop dispatche proprement : blocked -> PAUSED + _pause_requested,
terminal -> succès légitime. Rétrocompat défensive (None legacy -> blocked).

Analytics API normalisée (kwargs-only) :
  on_execution_complete(duration_ms, status, steps_total|completed|failed)
  on_step_complete(duration_ms, ...)
  on_recovery_attempt(duration_ms, ...)

Découverte critique : les anciens appels utilisaient des méthodes et champs
inexistants (ExecutionMetrics.duration, metrics_collector.record_execution).
Le code n'avait jamais tourné au runtime — zéro analytics remontée.
L'exception était avalée par le try/except englobant.

58 tests (18 analytics + 11 contrat + 20 ExecutionLoop + 12 edge_scorer
non-régression). Migration complète, pas de pont legacy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 09:06:19 +02:00
Dom
42f571d496 docs(audit): README honnête + STATUS + DEV_SETUP + cleanup build
- README.md : bandeau POC, date 14 avril 2026, retrait claims
  "production-ready 77%" (alignement code/doc post-audit)
- docs/STATUS.md : état réel par module (opérationnel/alpha/en cours)
- docs/DEV_SETUP.md : gestion worktrees Claude
- QUICK_START.md : gemma4:latest au lieu de qwen3-vl:8b
- deploy/build_package.sh : +9 fichiers dans REQUIRED_FILES
  (system_dialog_guard.py, persistent_buffer.py, grounding.py, etc.)
- agent_v0/deploy_windows.py : marqué OBSOLÈTE (legacy)
- .gitignore : ajout data/, .hypothesis, .deps_installed, buffer/,
  instance/*.db, caches SQLite

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:49:29 +02:00
Dom
36737cfe9d feat(security): eval()→AST parseur + pickle→JSON+HMAC signé
Vulnérabilité 1 — eval() dans DAG executor :
- Nouveau module safe_condition_evaluator.py
- Parseur AST avec whitelist (Constants, Names, Compare, BoolOp, BinOp)
- Rejet explicite Call/Lambda/Import/__dunder__/walrus/comprehensions
- Expression non sûre → logged ERROR + évaluée à False (pas de crash)
- 31 tests (12 valides, 17 malveillantes rejetées, 2 intégration)

Vulnérabilité 2 — 3× pickle.load() non sécurisés :
- Nouveau module signed_serializer.py (JSON+HMAC-SHA256)
- Format : RPA_SIGNED_V1\\n + JSON(hmac + payload base64)
- Migration automatique transparente au premier chargement
- Fallback pickle avec WARNING (désactivable RPA_ALLOW_PICKLE_FALLBACK=0)
- Remplacement dans faiss_manager, visual_embedding_manager,
  visual_persistence_manager
- 13 tests

Clé signature : RPA_SIGNING_KEY (fallback TOKEN_SECRET_KEY puis hostname-derived).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:49:17 +02:00
Dom
93ef93e563 feat(security): API streaming fail-closed + /image privé + target_memory prefix fix
P0-B — /api/v1/traces/stream/image retiré de _PUBLIC_PATHS :
- Bearer token obligatoire pour upload d'image
- Évite uploads anonymes de contenu arbitraire

P0-C — Fail-closed si RPA_API_TOKEN absent :
- sys.exit(1) au démarrage avec message fatal
- Mode dev : RPA_AUTH_DISABLED=true pour désactiver explicitement
- Log INFO des 8 premiers chars du token (diagnostic)

Fix target_memory prefix empilé :
- Strip "memory_" répétés avant stockage dans replay_memory.py
- Évite "memory_memory_memory_template_matching" en base

live_session_manager : améliorations mineures de la gestion sessions.

10 tests auth API stream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:49:02 +02:00
Dom
376e4a88b3 feat(deploy): installeur Inno Setup pour déploiement professionnel
- Lea.iss : script Inno Setup 6 (enrollment 2 pages, licence, machine_id)
- build_installer.sh : staging + ISCC (compatible Wine sur Linux)
- uninstall_lea.ps1 : kill PID + cleanup + notif serveur
- configure_embed.ps1 : Python 3.12 embedded optionnel
- config_template.txt : modèle pour installation silencieuse
- LICENSE.txt : CGU AI Act Art. 50
- README.md : doc build, signing, déploiement silencieux

Paramètres d'installation silencieuse :
  Lea-Setup-v1.0.0.exe /VERYSILENT /CONFIG=enroll.txt /LOG=install.log

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:48:48 +02:00
Dom
bb4ed2a75d feat(dashboard): session cleaner intégré + auth + nettoyage UI
- Onglet "🧹 Nettoyage" dans le dashboard (iframe vers port 5006)
- Indicateur d'état + bouton de démarrage si cleaner down
- Service systemd rpa-session-cleaner intégré au target rpa-vision
- svc.sh et services.conf incluent session-cleaner (port 5006)

P0-A — Auth dashboard Flask :
- HTTP Basic obligatoire sur tous les endpoints (sauf /health, /healthz)
- Credentials via DASHBOARD_USER + DASHBOARD_PASSWORD
- 13 tests

Nettoyage UI :
- Section "Détection Visuelle" OWL retirée (modèle remplacé par pipeline VLM)
- Dashboard préfère auto shot_*_blurred.png (avec ?raw=1 pour brut)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:48:36 +02:00
Dom
f7b8cddd2b feat(anonymisation): blur PII côté serveur via EDS-NLP + VLM local-first
Blur PII server-side (core/anonymisation/pii_blur.py) :
- Pipeline OCR (docTR) → NER (EDS-NLP + fallback regex)
- Détection ciblée noms/prénoms/adresses/NIR/téléphone/email
- Protection explicite CIM-10, CCAM, montants €, dates, IDs techniques
- Dual-storage : shot_XXXX_full.png (brut) + _blurred.png (affichage)
- 18 tests

Client :
- RPA_BLUR_SENSITIVE=false par défaut (blur serveur uniquement)
- Zéro overhead côté poste utilisateur

VLM config :
- vlm_config.py : gemma4:latest, fallbacks qwen3-vl:8b + UI-TARS
- think=false auto pour gemma4 (bug Ollama 0.20.x)
- VLM provider VWB : local-first (Ollama), cloud opt-in via VLM_ALLOW_CLOUD

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:48:23 +02:00
Dom
a9a99953dd fix(agent): Lea.bat kill par PID + LeaServerClient URL
- Lea.bat ne tue plus TOUS les pythonw.exe du poste (Jupyter, Spyder)
  Kill ciblé uniquement sur le PID lu dans lea_agent.lock
- LeaServerClient utilise RPA_SERVER_URL (HTTPS prod) au lieu de
  hardcode http://:5005
- Normalisation du slash final de l'URL

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:48:09 +02:00
Dom
aee64f54b1 feat(security): détection dialogues système Windows + fail-closed
Nouveau module system_dialog_guard.py :
- Détection UAC, CredUI, SmartScreen, Defender, Driver install
- Multi-signal (ClassName UIA, process, title FR/EN, parent_path)
- Faux positifs validés (OSIRIS, OBSIUS, MEDSPHERE, Chrome, Excel)

Intégration dans executor.py et policy.py :
- 6 points de décision (avant click/type/key_combo, VLM, policy)
- Pause supervisée au lieu de clic aveugle
- Fail-closed en cas d'exception (P0-D audit)
- Notification systray + remontée serveur

Fix mock test policy engine pour compat _system_dialog_pause=None.
39 + 5 tests unitaires.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:48:00 +02:00
Dom
c77844fa9a feat(capture_server): auth Bearer + bind localhost + anti-path-traversal
- 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>
2026-04-14 16:47:45 +02:00
Dom
013fe071a2 feat(streamer): purge après ACK + buffering SQLite persistant
- 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>
2026-04-14 16:47:35 +02:00
Dom
203dc00d53 fix: UIA compare les noms d'app au lieu des titres complets
"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>
2026-04-13 10:27:08 +02:00
Dom
e9a028134a feat: blocs conditionnels — skip automatique des dialogues absents
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>
2026-04-13 10:20:00 +02:00
Dom
01bba7bc6c feat: wrong_window déclenche le mode apprentissage au lieu de bloquer
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>
2026-04-13 09:27:01 +02:00
Dom
d5285de99c feat: mode apprentissage — retry échoué + écran inchangé déclenchent la capture humaine
Trois chemins vers le mode apprentissage supervisé :
1. Grounding échoue → Policy RETRY → retry échoue → capture humaine
2. Clic visuel sans effet (écran inchangé 3s) → capture humaine
3. Policy SUPERVISE direct → capture humaine

La capture enregistre un mini-workflow complet (clics + frappes + combos)
jusqu'à Ctrl+Shift+L ou 10s d'inactivité. Correction envoyée au serveur.

Testé E2E : workflow Chrome avec résultats Google dynamiques +
bandeau cookies — Léa demande l'aide, capture, reprend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 08:33:57 +02:00
Dom
33c198b827 feat: premier replay E2E + mode apprentissage supervisé
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>
2026-04-13 07:42:50 +02:00
Dom
816b37af98 fix: session_cleaner utilise le fallback simple exclusivement
build_replay_from_raw_events transforme les events (réordonne, injecte
du setup "ouvrir l'app", fusionne les actions, ajoute des waits) ce qui
décale les clics par rapport à l'enregistrement original. Le texte était
saisi dans le mauvais champ parce que les actions n'étaient plus en 1:1
avec la session.

Le fallback _simple_build_replay reproduit les events tels quels en
coords brutes — exactement ce qu'on veut pour "nettoyer et rejouer".
Le session_cleaner l'utilise maintenant exclusivement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 16:29:07 +02:00
Dom
d82aad984f fix: session_cleaner force visual_mode=False sur les clics
Contournement temporaire du crash agent "cannot unpack non-iterable
NoneType object" qui se produit quand l'agent Windows tente une
résolution visuelle (visual_mode=True) sur les actions replay.

Les actions construites par build_replay_from_raw_events gardent
leurs coordonnées enrichies (x_pct, y_pct calculés depuis la
session) mais sont envoyées avec visual_mode=False pour que l'agent
clique aux coords brutes sans passer par le grounding.

C'est un compromis temporaire : moins intelligent (pas de résolution
adaptative) mais fonctionnel (les clics arrivent aux bonnes coords).
Le mode visuel sera réactivé quand le bug agent sera diagnostiqué
et corrigé (le traceback n'est pas visible côté serveur, le
redéploiement de l'agent avec debug n'a pas pris effet).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 16:13:43 +02:00
Dom
057c37131f fix: session_cleaner fallback — x_pct/y_pct + visual_mode=False
Deux bugs dans _simple_build_replay :

1. Mauvais noms de champs : x_percent/y_percent au lieu de x_pct/y_pct
   attendus par l'agent executor. Et valeurs en 0-100 au lieu de 0-1.
   Résultat : l'agent recevait x_pct=None → crash "cannot unpack
   non-iterable NoneType object".

2. Pas de visual_mode=False explicite. Sans enrichissement
   (target_spec vide, pas d'anchor), l'agent tentait une résolution
   visuelle sur du vide → crash.

Aussi : la condition de fallback empêchait le déclenchement quand
build_replay_from_raw_events crashait (error_message non vide bloquait
la branche). Corrigé : le fallback se déclenche sur `not replay_actions`
(couvre None, liste vide, et crash du build principal).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 15:51:40 +02:00
Dom
9bcce3fc68 feat: session_cleaner — outil leger de nettoyage de sessions avant replay
Petit serveur Flask standalone (tools/session_cleaner.py) qui permet de :
- Lister les sessions enregistrees recentes
- Visualiser chaque session avec ses screenshots (crop + full)
- Marquer les clics parasites a supprimer (auto-detection des toasts,
  clics droit, fenetres Lea/systray, derniers 3 evenements)
- Re-construire un replay nettoye et l'injecter dans la queue via
  POST /api/v1/traces/stream/replay/raw

Option A du rapport audit VWB : "Le besoin reel est supprimer 3 clics
parasites et relancer — c'est 30 secondes d'UX, pas un Visual Workflow
Builder."

Port : 5006
Dependencies : Flask (deja dans le venv), aucune nouvelle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:35:31 +02:00
Dom
f96f6322ec chore: nettoyage code mort — suppression _a_trier/, archives/, .bak, scaffold vide
Supprime ~8.2 Go de fichiers parasites qui polluent les grep, consomment
des tokens, et ajoutent du bruit au repo :

- _a_trier/ (561 Mo) — scripts legacy, backups, sessions logs, démos
- archives/ (21 Mo) — copie figée code décembre 2024 (déjà dans git history)
- visual_workflow_builder/_a_trier/ (7.6 Go) — backups VWB legacy + anciens frontends
- web_dashboard/app.py.bak_20260304_2225 — fichier .bak oublié
- agent_v1/ (top-level) — scaffold vide jamais alimenté
- core/detection/ui_detector_old.py.bak — .bak traqué par erreur

Retire aussi du tracking git :
- 2 fichiers __pycache__ traqués par erreur dans VWB backend

Met à jour .gitignore pour prévenir la récurrence :
- *.bak, *.bak_*, *.orig, *.old
- _a_trier/, archives/

Tout ce contenu reste récupérable via git history (tag pre-cleanup-phase1-20260410).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 11:35:31 +02:00
Dom
02ee2d7b5b fix: Fenêtre incorrecte strict → pause supervisée pour apprentissage
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>
2026-04-12 10:41:29 +02:00
Dom
47993e2ee9 chore: ajouter replay_failure_logger.py au tracking git
Ce fichier existe sur disque depuis le 4 avril mais n'a jamais été ajouté
à git. Il est importé par api_stream.py (ligne 29) — un fresh clone sans
ce fichier ne peut pas démarrer le serveur streaming.

Découvert par le project-quality-guardian lors de l'audit global du
11 avril (item C1, priorité P0 bloquant absolu).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 10:35:51 +02:00
Dom
7cc03f6f10 fix: no_screen_change strict → pause supervisée pour apprentissage
Rectification de la branche C introduite dans a21f1ea9f.

## Ce qui était faux

a21f1ea9f faisait :
  strict + no_screen_change → retry × 3 → status=error → queue vidée

C'est le réflexe d'un RPA classique qui se casse la figure quand ça
rate. Ce n'est PAS la philosophie Léa.

Dom m'a rappelé que j'avais oublié ma propre vision documentée dans
project_lea_apprentissage_plan.md et feedback_not_a_click_box.md :
*"Quand elle dit qu'elle n'a pas trouvé X, elle demande montre-moi.
C'est à ce moment qu'il faudrait passer en mode apprentissage."*

## Ce qui est correct maintenant

  strict + no_screen_change
    → status = "paused_need_help"
    → failed_action stocké (target, screenshot, method, score, reason)
    → pause_message demandant l'intervention humaine
    → queue intacte (l'action reste en tête, prête à être relancée)
    → log_replay_failure pour l'apprentissage futur
    → l'agent reçoit replay_paused=True dans /replay/next et s'arrête
    → l'humain corrige physiquement sur la machine cible
    → le replay reprend via /replay/{replay_id}/resume

Redirection vers le mécanisme paused_need_help qui existe déjà pour le
cas target_not_found. Zéro nouveau code de pause, juste une 2ème entrée
dans ce mécanisme.

Le comportement legacy (success_strict=False) reste inchangé : on
log un warning et on continue, comportement tolérant pour les actions
non-critiques.

## Lesson apprises

1. Toujours relire les fichiers mémoire pertinents AVANT d'implémenter
   une branche de gestion d'erreur (nouvelle règle dans
   feedback_reread_before_code.md)
2. Un échec Léa n'est jamais un "stop avec error" — c'est un moment
   pédagogique (nouvelle règle dans feedback_failure_is_learning.md)
3. Ne pas s'auto-presser quand Dom n'a jamais demandé d'aller vite

## Tests

- 56 tests E2E + Phase0 passent, 0 régression
- Comportement vérifié par inspection du code : pause_message formé
  correctement, queue préservée, log_replay_failure appelé

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 09:27:45 +02:00
Dom
a21f1ea9fa feat: garde qualité résolution (B) + no_screen_change strict (C)
Deux garde-fous qui ferment des trous identifiés lors du test de replay
chirurgical du 11 avril 2026 sur sess_20260411T084629_2d588e.

## B — Garde qualité en sortie de cascade (_validate_resolution_quality)

Couche de validation ajoutée en sortie du handler /resolve_target, après
que la cascade (_resolve_target_sync) a produit son meilleur candidat.
Single point of insertion, n'altère pas la cascade existante.

Deux checks :

  1. Seuil de score minimum par méthode (_RESOLUTION_MIN_SCORES)
     - hybrid_text_direct ≥ 0.80
     - som_anchor_match / som_text_match ≥ 0.75
     - template_matching ≥ 0.85
     - vlm_* / grounding ≥ 0.60
     - memory_* : pas de seuil (confiance cristallisée)
     - v4_uia_local / uia ≥ 0.90

  2. Garde de proximité contre coords enregistrées
     Si fallback_x/y_pct sont significatifs (pas placeholder 0.5/0.5 ni
     0.0/0.0), rejette si drift > 20% de l'écran dans un axe.
     Reproduit un faux positif vu en production : SoM a trouvé
     "Enregistrer" à (0.505, 0.770) alors que l'enregistrement était à
     (0.093, 0.356) — écart de 0.41.

Quand un check rejette : retourne resolved=False avec method=
"rejected_low_score_*" ou "rejected_drift_*" et reason détaillée.
L'action passe alors par le chemin "visual_resolve_failed" côté agent
→ Policy → pause supervisée ou retry selon contexte.

7 tests unitaires inline validés (score bas, drift, mémoire qui passe
toujours, placeholders V4 qui skip la garde drift, etc.).

## C — no_screen_change devient un échec strict en mode strict

Avant : si un clic retourne warning='no_screen_change' (écran inchangé
après action), le replay loggait un warning et CONTINUAIT à l'action
suivante. Trop indulgent pour les workflows critiques.

Maintenant : la branche no_screen_change consulte le flag
success_strict de l'action courante.

  - success_strict=True : traité comme vrai échec
      → retry si retry_count < MAX_RETRIES_PER_ACTION
      → stop définitif sinon (status=error, queue vidée, callback)

  - success_strict=False (legacy) : comportement inchangé, on continue

Prérequis : _create_replay_state copie maintenant success_strict,
expected_window_before, expected_window_title, intention dans la
version slim de actions stockée dans replay_state. Nécessaire pour
lire le flag depuis current_action_index dans /replay/result.

## Tests

- 7 tests unitaires inline sur _validate_resolution_quality
- 56 tests E2E + Phase0 passent, zéro régression
- Instrumentation [REPLAY] reste pleinement fonctionnelle

## Limites non traitées ici (explicites)

- La latence de 14s entre deux clics (pre-analyze + cascade + agent
  polling) reste inchangée. Les menus déroulants Windows peuvent encore
  se refermer avant le 2ème clic. Piste A du plan, à traiter séparément.
- L'intégration d'OS-Atlas-Base-7B comme grounder spécialisé reste
  dans les cartons (recommandation du rapport état de l'art).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 09:11:41 +02:00
Dom
9188bd7df1 fix: masquer la fenêtre console lors du spawn lea_uia.exe sur Windows
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>
2026-04-10 22:18:11 +02:00
Dom
f82753debe chore: instrumentation [REPLAY] pour diagnostic chaîne replay
Ajoute 6 points de log structurés homogénéisés avec le préfixe [REPLAY]
aux endroits clés de la chaîne de replay, pour permettre de suivre
précisément ce qui se passe pendant un test humain et diagnostiquer
les points de rupture sans déduire à l'aveugle.

Points de log :
1. DISPATCH          — /replay/next envoie une action (expected_before/after,
                       resolve_order, has_uia, has_anchor, by_text, strict)
2. RESOLVE_ENTRY     — _resolve_target_sync reçoit la demande (window_title,
                       uia_target, anchor, strict_mode)
3. RESOLVE_EXIT      — résolution terminée (method, coords, score, from_memory)
4. RESOLVE_EXCEPTION — crash rare dans la résolution
5. REPORT            — /replay/result reçoit le rapport agent (success, error,
                       warning, resolution_method, actual_position)
6. VERIFY            — décision finale post-vérification (agent_success,
                       ver_verified, sem_verified, final_success)

Usage : journalctl --user -u rpa-streaming -f | grep REPLAY

Aucune modif de logique, uniquement des logger.info() aux points de
décision critiques. 56 tests E2E + Phase0 restent verts.

Ces logs sont là pour stabiliser la chaîne après les modifications
robustesse du matin (strict control, UIA strict, filtre UIA-aware)
qui ont cassé les replays réels de Dom et ne se voient pas dans les
tests automatisés in silico.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:07:56 +02:00
Dom
b92cb9db03 feat: Phase 1 apprentissage — greffe TargetMemoryStore sur V4
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>
2026-04-10 21:08:14 +02:00
Dom
e66629ce1a fix: filtre UIA-aware + polling pré-vérif tolérant
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>
2026-04-10 14:25:40 +02:00
Dom
cecdf417b7 fix: contrôle strict des étapes + routage par machine_id
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>
2026-04-10 14:05:23 +02:00
Dom
56e3cc052a feat: agent Windows consomme UIA — capture + résolution
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>
2026-04-10 11:18:25 +02:00
Dom
332366b58c feat: câblage complet V4 — stratégie UIA + surface profile
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>
2026-04-10 11:02:51 +02:00
Dom
ac9c207474 feat: SurfaceClassifier + UIAHelper — détection et wrapper Python
SurfaceClassifier — détecte le type d'application au runtime
- 4 surfaces : citrix / windows_native / web_local / unknown
- Paramètres adaptés par surface :
  * Citrix : OCR 0.65, timeouts 15s, retries 3x (compression JPEG tolérée)
  * Windows natif : OCR 0.75, timeouts 8s, UIA bonus si dispo
  * Web : OCR 0.80, timeouts 5s, paramètres rapides
  * Unknown : fallback sûr
- resolve_order() construit la chaîne selon les capacités disponibles
- Détection UIA via health check du helper Rust
- Détection CDP via localhost:9222

UIAHelper — wrapper Python pour lea_uia.exe
- Subprocess + JSON stdin/stdout
- 3 méthodes : query_at(x,y), find_by_name(name,...), capture_focused()
- Fallback silencieux (None) si helper absent, timeout, crash
- Singleton global get_shared_helper()
- Dataclass UiaElement avec center(), is_clickable(), path_signature()

29 nouveaux tests (détection 4 surfaces, dataclass, wrapper, mocks).
485 tests au total, 0 régression.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 10:54:19 +02:00
Dom
f85d56ac05 feat: lea_uia — helper Rust Windows UI Automation (cross-compilé)
Premier pas de l'Option B hybride : vision + UIA pour Windows natif.

Pourquoi Rust ?
- Binaire standalone ~500 Ko, aucune dépendance runtime
- 5-10x plus rapide que pywinauto (10-20ms par query vs 50-200ms)
- Compilation cross-platform depuis Linux (x86_64-pc-windows-gnu)
- Safe : pas de crash sur null pointer ou memory leak
- Préparation d'un déploiement industriel robuste

Commandes :
- query --x N --y N         : élément UIA à cette position
- find --name "..." --control-type "..." : recherche par nom
- capture --max-depth N     : élément focus + hiérarchie
- health                    : vérifier que UIA est dispo

Sortie JSON structurée (stdin/stdout pour IPC avec Python).
Stub Linux pour dev/tests sans Windows.

Validé sur VM Windows :
- query (100,100) → "Bureau 1" en 18ms
- query (500,400) → "Bureau 1" en 12ms
- find "Rechercher" → not_found en 11ms (normal, rien d'ouvert)

Le binaire lea_uia.exe sera packagé avec Léa dans C:\Lea\helpers\

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:30:45 +02:00
Dom
172167f6c0 feat: Léa apprentissage — mode Shadow amélioré (observation + validation)
Aspect 3/4 Léa : Léa montre ce qu'elle comprend pendant l'enregistrement.

ShadowObserver (observation temps réel) :
- Segmentation incrémentale en UnderstoodStep (changement app, pause, Ctrl+S)
- Détection de variables pendant la saisie (typage : date, email, code, texte)
- Notifications 4 niveaux : INFO, DECOUVERTE, QUESTION, VARIABLE
- Heartbeat périodique, hook gemma4 optionnel (asynchrone)
- Thread-safe (RLock), singleton partagé
- Performance : 1000 events en < 500ms

ShadowValidator (feedback utilisateur) :
- 6 actions : validate, correct, undo, cancel, merge_next, split
- Reconstruit un WorkflowIR propre avec variables substituées
- Historique complet des feedbacks

5 endpoints REST /api/v1/shadow/* :
- start, stop, feedback, understanding, build

Hook non-bloquant dans stream_event() (try/except, no-op si inactif).
Mode optionnel : pas d'impact tant que shadow/start n'est pas appelé.

54 tests (26 observer + 28 validator), 0 régression.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:04:37 +02:00
Dom
42d49dd8bd feat: Léa personnalité — langage métier multi-domaines
Aspect 4/4 Léa : Léa parle le langage du métier, pas du robot.

DomainContext enrichi avec 5 domaines :
- tim_codage : CIM-10, CCAM, GHM, DP/DAS (enrichi)
- comptabilite : factures HT/TVA/TTC, OCR, lettrage, PCG
- rh_paie : bulletins, DSN, brut/net, congés, IJSS
- stocks_logistique : BC/BL/BR, SKU, inventaires, picking
- generic : fallback

Nouvelle API DomainContext :
- summarize_action(action, params) — click "DP" → "saisir le diagnostic principal"
- pose_clarification_question(context) — question pertinente quand Léa bloque
- describe_workflow_outcome(...) — rapport final en langage métier

Exemples :
  TIM : "J'ai codé 14 dossiers sur 15. 1 en attente — codes CIM-10 ambigus."
  Compta : "Je ne trouve pas le champ montant de TVA. C'est bien la facture F2026-0145 ?"

Intégration ui/messages.py :
- Import lazy (pas de dépendance circulaire)
- formatter_cible_non_trouvee utilise les templates de clarification métier
- Rétro-compat : tous les anciens appels sans domain_id fonctionnent

47 nouveaux tests, 0 régression.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:01:52 +02:00
Dom
f541bb8ce4 feat: Léa chat + IRBuilder enrichi (stratégies V4 complètes)
Aspect 2/4 Léa : interface conversationnelle
- chat_interface.py : ChatSession thread-safe, états idle/planning/awaiting/executing/done
- 5 endpoints REST : /api/v1/chat/* (session, message, history, confirm, sessions)
- web_dashboard/chat.html + chat.js : UI minimaliste, polling 2s, pas de framework
- Proxy Flask /api/chat/* → serveur streaming
- 34 tests (happy path, abandon, refus, erreurs, gemma4 down)

IRBuilder enrichi pour plans V4 complets
- _event_to_action() appelle enrich_click_from_screenshot() quand session_dir dispo
- Chaque clic porte _enrichment (by_text OCR, anchor_image_base64, vlm_description)
- ExecutionCompiler consomme l'enrichissement pour produire 3 stratégies par clic
  Avant : [ocr] uniquement, target="unknown_window"
  Après : [ocr, template, vlm] avec vrai texte OCR ("Rechercher", "Ouvrir")

Validé sur session réelle : 10/10 clics enrichis (by_text + anchor + vlm_description)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 09:01:13 +02:00
Dom
a6eb4c168f feat: Léa UX — messages français naturels + feedback temps réel
Aspect 1/4 de Léa (agent Windows) : rendre Léa humaine.

Nouveaux modules :
- agent_v1/ui/messages.py : 11 formatters (cible non trouvée, mauvaise fenêtre,
  écran inchangé, connexion, workflow, retry, ralentissement, erreur générique)
- agent_v1/ui/activity_panel.py : panneau tkinter lazy avec état courant,
  action, progression X/Y, temps écoulé, 7 états (OBSERVE/CHERCHE/AGIT/VERIFIE...)

Hiérarchie de notifications :
- INFO (4s, vert) — début workflow, étape en cours
- ATTENTION (7s, orange) — retry, ralentissement
- BLOCAGE (15s, rouge, persistent, bypass rate-limit) — cible introuvable, mauvaise fenêtre

Transformations de messages :
  AVANT : "target_not_found: dans *bonjour, – Bloc-notes"
  APRÈS : "Léa a besoin d'aide"
          "Je ne trouve pas « bonjour » dans Bloc-notes.
           Peux-tu cliquer dessus toi-même ? Je reprends ensuite."

Robustesse :
- Détection fenêtre Léa via regex word-boundaries (évite cléa.txt, leapfrog.exe)
- Centralisée dans messages.est_fenetre_lea() — source unique de vérité
- Noop stub universel via __getattr__ (plus besoin de lister les méthodes)
- Thread-safe (RLock + snapshots immutables)
- Fallback silencieux si tkinter/plyer absent

101 nouveaux tests, aucune régression.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:42:01 +02:00
Dom
f6ad5ff2b2 feat: runtime V4 honore resolve_order pré-compilé (zéro VLM au runtime)
Le resolve_engine suit désormais l'ordre de méthodes décidé par l'ExecutionCompiler
au lieu de sa cascade improvisée. C'est la pièce maîtresse du V4 :

- execution_plan_runner.py : ajout de 'resolve_order' dans target_spec
  ["ocr", "template", "vlm"] = stratégies dans l'ordre de préférence

- resolve_engine.py : _resolve_with_precompiled_order() honore l'ordre
  - Court-circuite la cascade legacy quand resolve_order est présent
  - Fallback sur la cascade si toutes les méthodes V4 échouent

- _resolve_by_ocr_text() : résolution OCR directe via docTR (~200ms)
  Chemin rapide V4 — pas de VLM pour les éléments avec texte visible

- 12 nouveaux tests : propagation resolve_order, cascade, fallback, pipeline E2E

220 tests passent (208 existants + 12 nouveaux), 0 régression.

"Le LLM compile. Le runtime exécute."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:28:55 +02:00
Dom
2ac781343a feat: runtime V4 — endpoints /workflow/compile et /replay/plan
Pipeline V4 complet disponible en API :
  RawTrace → /workflow/compile → WorkflowIR + ExecutionPlan → /replay/plan → Runtime

- execution_plan_runner.py : adaptateur ExecutionNode → action executor
- Substitution variables {var} dans target/text
- Fusion stratégies primary + fallbacks (OCR, template, VLM)
- Clicks: coordonnées neutralisées, resolve_engine trouve au runtime
- 35 nouveaux tests (conversion, substitution, injection queue, pipeline E2E)
- Ancien chemin build_replay_from_raw_events() préservé (coexistence)

208 tests passent, 0 régression.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:09:05 +02:00
Dom
bffcfb2db3 feat: ExecutionCompiler — compile WorkflowIR en plan d'exécution borné
Pièce maîtresse de l'architecture V4 :
- ExecutionPlan : nœuds avec stratégies de résolution pré-compilées
- ExecutionCompiler : WorkflowIR → ExecutionPlan déterministe
- Résolution : OCR (primaire, 100ms) > template > VLM (exception handler)
- Chaque nœud : timeout, max_retries, recovery, condition de succès
- Variables substituables, versionné, sérialisable JSON
- 18 tests (compilation, stratégies, fallbacks, variables, roundtrip)

"Le LLM compile. Le runtime exécute."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 22:21:40 +02:00
Dom
cc673755f7 feat: WorkflowIR — représentation intermédiaire du savoir-faire
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>
2026-04-09 21:50:32 +02:00
Dom
4509038bf0 refactor: éclater api_stream.py (6400→3350 lignes) en modules
- resolve_engine.py (1953 lignes) — résolution visuelle (template, VLM, SoM, YOLO)
- replay_engine.py (1284 lignes) — gestion des replays (queue, setup, retry, validation)
- api_stream.py (3352 lignes) — routeur principal (endpoints HTTP thin layer)

Préparation V4 : base propre pour le WorkflowIR et l'ExecutionCompiler.
137 tests passent, 0 régression, aucun endpoint modifié.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:37:44 +02:00
Dom
99041f0117 feat: pipeline complet MACRO/MÉSO/MICRO — Critic, Observer, Policy, Recovery, Learning, Audit Trail, TaskPlanner
Architecture 3 niveaux implémentée et testée (137 tests unitaires + 21 visuels) :

MÉSO (acteur intelligent) :
- P0 Critic : vérification sémantique post-action via gemma4 (replay_verifier.py)
- P1 Observer : pré-analyse écran avant chaque action (api_stream.py /pre_analyze)
- P2 Grounding/Policy : séparation localisation (grounding.py) et décision (policy.py)
- P3 Recovery : rollback automatique Ctrl+Z/Escape/Alt+F4 (recovery.py)
- P4 Learning : apprentissage runtime avec boucle de consolidation (replay_learner.py)

MACRO (planificateur) :
- TaskPlanner : comprend les ordres en langage naturel via gemma4 (task_planner.py)
- Contexte métier TIM/CIM-10 pour les hôpitaux (domain_context.py)
- Endpoint POST /api/v1/task pour l'exécution par instruction

Traçabilité :
- Audit trail complet avec 18 champs par action (audit_trail.py)
- Endpoints GET /audit/history, /audit/summary, /audit/export (CSV)

Grounding :
- Fix parsing bbox_2d qwen2.5vl (pixels relatifs, pas grille 1000x1000)
- Benchmarks visuels sur captures réelles (3 approches : baseline, zoom, Citrix)
- Reproductibilité validée : variance < 0.008 sur 10 itérations

Sécurité :
- Tokens de production retirés du code source → .env.local
- Secret key aléatoire si non configuré
- Suppression logs qui leakent les tokens

Résultats : 80% de replay (vs 12.5% avant), 100% détection visuelle Citrix JPEG Q20

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:03:25 +02:00
Dom
72a9651b94 docs: consolidation 5 avril — état des lieux complet
Pipeline entraînement validé (15.7s, extrapolation 1h = 10 min).
CLIP vérification validée (sim 0.87-0.99 sur fenêtres).
Acteur gemma4 branché (PASSER/EXECUTER/STOPPER, think=True).
Grounding fenêtre + template taskbar fonctionnels.
Problèmes identifiés : ambiguïté Rechercher, éléments VLM.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:25:10 +02:00
Dom
8589e87a13 fix: grounding uniquement dans les fenêtres, template pour la taskbar
Les clics taskbar (sans window_capture.rect) ne passent plus par le
grounding VLM qui trouve "Rechercher" dans l'explorateur au lieu de
la taskbar. Le template matching du crop 80x80 est utilisé à la place.

Règle : fenêtre = grounding, taskbar = template matching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 21:19:36 +02:00
Dom
8a1dfc6e8b feat: acteur gemma4 — décide PASSER/EXECUTER/STOPPER quand target_not_found
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>
2026-04-05 21:05:37 +02:00
Dom
3bcf59e16f fix: message notification humain (plus de "yolo") + description cible améliorée
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>
2026-04-05 20:52:34 +02:00
Dom
46206d9396 feat: vérification CLIP avant chaque clic (filet de sécurité app)
Avant la résolution visuelle, compare l'embedding CLIP de l'écran
actuel (fenêtre) avec l'embedding de référence (enregistrement).
Si similarité < 0.75 → mauvaise application → STOP.

CLIP sur fenêtre = insensible au fond d'écran.
CLIP ne distingue pas les états fins (texte différent) → le titre
de fenêtre reste la vérification principale.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:49:19 +02:00
Dom
d3e928bebe feat: branchement workflow — actions magnétoscope enrichies avec CLIP
Approche hybride :
- Actions du magnétoscope (by_text, target_spec, grounding)
- Embeddings CLIP du workflow (512D par screenshot de clic)
- Au replay : CLIP vérifie l'état de l'écran AVANT chaque clic

Pipeline complet mesuré :
- ScreenAnalyzer (OCR) : 1.05s/screenshot
- CLIP embeddings : 0.093s/screenshot
- FAISS : <0.01s pour 13 vecteurs
- GraphBuilder : 0.7s (13 nodes, 12 edges)
- Total : 15.7s pour 1.5 min de session
- Extrapolation 1h : ~10 min

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:30:27 +02:00
Dom
a679fbb62b docs: Plan Acteur Intelligent V1 — architecture 3 niveaux
MACRO : planificateur LLM (décompose "traite les dossiers de janvier")
MÉSO : acteur décisionnel (regarde, comprend, décide, agit)
MICRO : grounding + exécution (localise et clique)

Phase 1 = workflows comme templates avec variables
Phase 2 = acteur qui compare états et décide
Phase 3 = planificateur macro avec boucles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:41:01 +02:00
Dom
f0b311306d fix: grounding pour TOUT texte visible (OCR + VLM), auto-unload gemma4
1. Le grounding se déclenche pour by_text_source="vlm" (pas juste "ocr")
   Les textes lus par gemma4 (onglets, labels) sont du texte visible,
   le grounding doit les chercher comme n'importe quel texte OCR.

2. gemma4 est automatiquement déchargé après le build_replay
   pour libérer la VRAM et permettre à qwen2.5vl de charger au replay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:24:44 +02:00
Dom
1c5ff42006 fix: ajouter position relative au prompt grounding (désambiguïsation)
Quand plusieurs éléments ont le même texte ("Rechercher" dans la taskbar
ET dans l'explorateur), la position relative (en bas, en haut, à gauche)
aide le VLM à choisir le bon.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 12:04:46 +02:00
Dom
b09a3df054 fix: _app_name déplacé hors du bloc if (scope error) 2026-04-05 11:29:51 +02:00
Dom
fceb76de1f feat: gemma4 enrichit les éléments sans OCR via Docker (port 11435)
Quand l'OCR et SomEngine ne trouvent pas de texte sur un élément cliqué,
gemma4 (Ollama 0.20 Docker) analyse le screenshot fenêtre + position du
clic pour identifier l'élément ("voiture elec", "Settings", etc.).

Résultat : 0 clic sans by_text (vs 3 avant). Validation locale 7/8 (87%).
L'onglet Bloc-notes est maintenant correctement identifié.

Docker : ollama/ollama:0.20.2 sur port 11435 (GEMMA4_PORT env var).
Host : Ollama 0.16.3 sur port 11434 (qwen2.5vl grounding).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 11:21:02 +02:00
Dom
6d4ff4f215 fix: vérification par nom d'APPLICATION, pas par titre exact
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>
2026-04-05 09:16:48 +02:00
Dom
2486e43def fix: cropper la fenêtre depuis le screenshot live (pas chercher _window.png)
Le resolve_target reçoit un screenshot temp de l'agent — le fichier
_window.png n'existe pas à cet emplacement. Au lieu de chercher un
fichier, on crop directement la fenêtre depuis le full screenshot
en utilisant window_rect du target_spec.

Fonctionne au replay (screenshot live) comme à l'enregistrement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:09:13 +02:00
Dom
20b74286f7 feat: polling titre fenêtre au lieu de wait fixe (post-vérification)
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>
2026-04-05 08:49:48 +02:00
Dom
a1c97504ab feat: Phase 1 acteur — pré/post vérification titre fenêtre
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>
2026-04-05 00:09:08 +02:00
Dom
d6c7346898 fix: ne pas couper le replay au début (taskbar = unknown_window)
Le premier clic (barre de recherche Windows) a un titre
"unknown_window" qui déclenchait la coupure de fin de session.
Ajout d'un guard : pas de coupure avant 3 actions significatives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 23:33:52 +02:00
Dom
90ee8ca8f4 fix: template matching sur fenêtre active + seuil 0.90
Template matching des icônes limité à la fenêtre active (window.png)
pour éviter les faux positifs sur le full screen. Seuil relevé de
0.70 à 0.90. Coordonnées fenêtre converties en coordonnées écran.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 23:23:21 +02:00
Dom
84a91630e9 feat: grounding sur image fenêtre au lieu du full screen
Utilise shot_XXXX_window.png (capture fenêtre active) au lieu du
full screen pour le grounding VLM. Image plus petite, ciblée,
sans bruit (taskbar, autres fenêtres).

Coordonnées fenêtre converties en coordonnées écran via window_rect.
window_capture (rect, window_size, click_relative) ajouté au target_spec.

Résultat : 50% → 80% de précision sur la session VM (16/20 clics).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 23:12:30 +02:00
Dom
91614fbff0 fix: prompt natif bbox_2d pour le grounding Qwen2.5-VL
Le prompt JSON ("Answer ONLY: {x, y}") ne fonctionne plus — retourne
[0.0, 0.0] systématiquement. Le prompt natif "Detect X with a bounding
box" retourne des bbox_2d précis. C'est le format pour lequel
Qwen2.5-VL est entraîné.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 22:43:46 +02:00
Dom
c1ce6a3964 fix: séparer grounding (qwen2.5vl) et compréhension (gemma4)
- Grounding : qwen2.5vl:7b hardcodé (seul modèle avec bbox_2d précis)
- Compréhension/VLM : gemma4:e4b via RPA_VLM_MODEL (description, identification)
- Ajout think=False + num_predict=200 pour éviter le mode thinking gemma4
- Variable RPA_GROUNDING_MODEL pour override si besoin

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 18:48:00 +02:00
Dom
0bd0fbb8c5 fix: SomEngine sur CPU pour cohabiter avec Qwen2.5-VL GPU
Qwen2.5-VL occupe 9.8 GB de VRAM → plus de place pour YOLO.
SomEngine passe en CPU (1.4s au lieu de 0.1s, acceptable car
utilisé uniquement pendant le build_replay, pas le replay).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 09:30:00 +02:00
Dom
394342be7e feat: support vLLM (GPU) comme moteur de grounding, Ollama en fallback
_resolve_by_grounding() essaie vLLM d'abord (API OpenAI-compatible,
port 8100) puis Ollama en fallback. vLLM utilise Qwen2.5-VL-7B-AWQ
sur GPU (~2-3s) vs Ollama sur CPU (~16s).

Config via env vars : VLLM_PORT (défaut 8100), VLLM_MODEL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:37:12 +02:00
Dom
6724f43950 fix: stratégie hybride OCR→grounding VLM / icônes→template matching
Résolution 4/4 (100%) validée localement :
- Texte OCR (by_text_source="ocr") → grounding Qwen2.5-VL (dist < 0.04)
- Icônes sans texte (by_text_source="") → template matching crop 80x80 (dist = 0.000)

Le VLM identify element est supprimé pour les icônes (descriptions
non-déterministes qui faisaient échouer le grounding). Le template
matching est instantané et parfait quand le crop est net (80x80).

Ajout de by_text_source dans target_spec pour distinguer OCR vs VLM.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:21:06 +02:00
Dom
d99b17394a feat: VLM grounding direct (Qwen2.5-VL) — nouvelle stratégie de résolution
Nouvelle approche basée sur les recherches état de l'art :
- _resolve_by_grounding() : le VLM retourne directement les coordonnées
  (pas de SomEngine + numérotation intermédiaire)
- Utilise Qwen2.5-VL (entraîné pour le GUI grounding) au lieu de qwen3-vl
- Parse les formats natifs : bbox_2d, JSON x/y, arrays bruts
- Fallback multi-image : screenshot + crop → grounding sans description
- Identification des icônes via Qwen2.5-VL (meilleur que qwen3-vl)

Résultats sur session réelle (validation locale) :
- Éléments avec texte (Word, Document, Fichier) : 100% corrects
- Icônes sans texte (Windows logo, disquette) : en cours d'amélioration

Cascade strict mode :
0. Grounding VLM direct (Qwen2.5-VL) — NOUVEAU
0.5. Template matching pour icônes
1. VLM Quick Find (fallback)
1.5. SoM + VLM
2. Template matching strict

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:55:00 +02:00
Dom
875367dea9 fix: template matching prioritaire pour icônes sans texte (by_text vide)
Quand by_text est vide (icônes : logo Windows, disquette, croix),
le template matching du crop 80x80 est plus fiable que le VLM qui
choisit des éléments au hasard.

Cascade strict mode :
0. Template matching (si by_text vide) — crop 80x80 discriminant
1. VLM Quick Find (compréhension sémantique)
1.5. SoM + VLM
2. Template matching (fallback avec seuil 0.90)
3. Échec → STOP

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 18:11:24 +02:00
Dom
a74056ca22 feat: anti-détection robot — Bézier mouse + frappe char-by-char
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>
2026-03-31 16:25:43 +02:00
Dom
6937b94f2a fix: 3 corrections — crop 80px, email AZERTY (@), icônes anchor match
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>
2026-03-31 16:21:02 +02:00
Dom
4f5c518d3a fix: anchor match sur screenshot entier + proximité élément SomEngine
Le template matching du crop anchor contre les régions YOLO échouait
car l'anchor (150x150) est plus grand que les éléments détectés.
Maintenant : match sur le screenshot entier → centre du match →
élément SomEngine le plus proche (max 100px).

Fonctionne pour les icônes mais limité par la taille du crop
(150x150 de barre de titre matche à plusieurs endroits).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:51:18 +02:00
Dom
7dec3ab63a fix: rejeter bavardage VLM dans _vlm_identify_element
Le VLM 8B répond souvent avec "several UI elements", "I can see",
etc. au lieu d'un label court. Ces réponses remplissaient by_text
avec du non-sens, empêchant le som_anchor_match de se déclencher
pour les icônes sans texte (disquette, fermer, etc.).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:44:56 +02:00
Dom
68d5bb7dd1 fix: som_anchor_match déclenché quand by_text vide (icônes sans texte)
La condition vérifiait anchor_label (du SomEngine) au lieu de by_text.
Pour les icônes (disquette, loupe), by_text est vide même si anchor_label
contient du bavardage VLM. Maintenant le template matching anchor vs YOLO
se déclenche correctement.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:38:38 +02:00
Dom
ef5d595d98 fix: by_text dans build_replay + anchor matching pour icônes sans texte
build_replay (stream_processor.py) :
- Remplir by_text depuis vision_info.text ou som_element.label
- VLM identification pour les éléments sans texte (icônes)
- Nettoyage du bavardage VLM (retrait préfixes courants)

resolve_target (api_stream.py) :
- Nouveau som_anchor_match : template matching du crop anchor vs régions YOLO
- Pour les icônes sans texte (disquette, loupe, etc.)
- Cascade : text match → anchor match → VLM

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 15:28:31 +02:00
Dom
5ceee9c393 fix: cascade serveur-first — SomEngine avant template matching
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>
2026-03-31 14:08:21 +02:00
78031 changed files with 82395 additions and 5311700 deletions

View File

@@ -30,7 +30,9 @@ DASHBOARD_PORT=5001
CLIP_MODEL=ViT-B-32 CLIP_MODEL=ViT-B-32
CLIP_PRETRAINED=openai CLIP_PRETRAINED=openai
CLIP_DEVICE=cpu # cpu or cuda CLIP_DEVICE=cpu # cpu or cuda
VLM_MODEL=qwen3-vl:8b RPA_VLM_MODEL=gemma4:latest # gemma4:latest (défaut), qwen3-vl:8b, ui-tars (fallback)
VLM_MODEL=gemma4:latest # alias de compatibilité
# VLM_ALLOW_CLOUD=false # true pour activer les APIs cloud en fallback (OpenAI, Gemini, Anthropic)
VLM_ENDPOINT=http://localhost:11434 VLM_ENDPOINT=http://localhost:11434
OWL_MODEL=google/owlv2-base-patch16-ensemble OWL_MODEL=google/owlv2-base-patch16-ensemble
OWL_CONFIDENCE_THRESHOLD=0.1 OWL_CONFIDENCE_THRESHOLD=0.1
@@ -44,6 +46,14 @@ LOGS_PATH=logs
UPLOADS_PATH=data/training/uploads UPLOADS_PATH=data/training/uploads
SESSIONS_PATH=data/training/sessions SESSIONS_PATH=data/training/sessions
# ============================================================================
# Feedback Bus (Léa parle pendant exécution)
# ============================================================================
# Bus SocketIO unifié 'lea:*' (action_started, action_done, need_confirm, paused).
# Désactivé par défaut. Mettre à 1 pour activer les bulles temps réel dans ChatWindow.
# Si la connexion bus échoue, l'exécution continue normalement (fail-safe).
LEA_FEEDBACK_BUS=0
# ============================================================================ # ============================================================================
# FAISS # FAISS
# ============================================================================ # ============================================================================

View File

@@ -0,0 +1,207 @@
# ------------------------------------------------------------------
# Audit sécurité — bandit + pip-audit + scan secrets
# ------------------------------------------------------------------
# Jamais bloquant : on reporte les warnings, on ne casse pas la CI.
# Utile pour détecter les dérives progressives (nouveaux CVE, secrets
# oubliés dans un commit, patterns risqués).
#
# Fréquence : à chaque push sur main + hebdo (cron).
# ------------------------------------------------------------------
name: security-audit
on:
push:
branches:
- main
schedule:
# Tous les lundis à 6h UTC (8h Paris hiver, 7h Paris été).
- cron: "0 6 * * 1"
workflow_dispatch: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ----------------------------------------------------------------
# Job 1 — bandit (bonnes pratiques sécu Python)
# ----------------------------------------------------------------
bandit:
name: Bandit (scan statique)
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Installation bandit
run: |
python -m pip install --upgrade pip
pip install "bandit[toml]==1.7.10"
- name: Scan bandit sur core/
run: |
# -ll : niveau LOW minimum (remonte tout)
# -ii : confiance LOW minimum
# --skip B101 : on ignore les asserts (usuels en tests/validation)
bandit -r core/ \
--skip B101,B404,B603 \
--format txt \
--exit-zero \
--output bandit-report.txt
echo "=== RAPPORT BANDIT ==="
cat bandit-report.txt
- name: Upload rapport bandit
if: always()
uses: actions/upload-artifact@v3
with:
name: bandit-report
path: bandit-report.txt
retention-days: 30
if-no-files-found: ignore
# ----------------------------------------------------------------
# Job 2 — pip-audit (CVE sur requirements)
# ----------------------------------------------------------------
pip-audit:
name: pip-audit (CVE dépendances)
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Installation pip-audit
run: |
python -m pip install --upgrade pip
pip install "pip-audit==2.7.3"
- name: Audit CVE sur requirements-ci.txt
run: |
if [ -f requirements-ci.txt ]; then
pip-audit -r requirements-ci.txt \
--format json \
--output pip-audit-ci.json \
--progress-spinner off \
--disable-pip || echo "::warning::CVE détectées dans requirements-ci.txt"
echo "=== RAPPORT pip-audit (CI) ==="
cat pip-audit-ci.json || true
else
echo "::notice::requirements-ci.txt absent — skip"
fi
- name: Audit CVE sur requirements.txt (best-effort)
run: |
# Timeout généreux car requirements.txt est massif (torch, CUDA).
timeout 120 pip-audit -r requirements.txt \
--format json \
--output pip-audit-full.json \
--progress-spinner off \
--disable-pip 2>&1 | head -200 || \
echo "::warning::pip-audit sur requirements.txt a timeout ou échoué (non bloquant)"
- name: Upload rapports pip-audit
if: always()
uses: actions/upload-artifact@v3
with:
name: pip-audit-reports
path: |
pip-audit-ci.json
pip-audit-full.json
retention-days: 30
if-no-files-found: ignore
# ----------------------------------------------------------------
# Job 3 — Scan secrets en clair (grep simple)
# ----------------------------------------------------------------
# Patterns recherchés : clés API Anthropic (sk-ant-), OpenAI (sk-),
# Google (AIzaSy), AWS (AKIA), tokens Hugging Face (hf_).
# Ne cherche QUE dans les fichiers trackés (pas .env, pas .venv).
# ----------------------------------------------------------------
secrets-scan:
name: Scan secrets (grep)
runs-on: ubuntu-latest
timeout-minutes: 3
continue-on-error: true
steps:
- name: Checkout (historique complet)
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan patterns de secrets
run: |
# Chemins exclus : venvs, caches, data, htmlcov, models.
EXCLUDES='--exclude-dir=.venv --exclude-dir=venv_v3 --exclude-dir=.git \
--exclude-dir=node_modules --exclude-dir=htmlcov --exclude-dir=models \
--exclude-dir=data --exclude-dir=__pycache__ --exclude-dir=.pytest_cache \
--exclude=*.lock --exclude=*.log --exclude=*.md'
echo "=== Recherche de secrets potentiels ==="
FOUND=0
# Anthropic
if grep -rnI $EXCLUDES -E 'sk-ant-[a-zA-Z0-9_-]{20,}' . 2>/dev/null; then
echo "::warning::Clé Anthropic potentielle détectée"
FOUND=1
fi
# OpenAI
if grep -rnI $EXCLUDES -E 'sk-proj-[a-zA-Z0-9_-]{20,}|sk-[a-zA-Z0-9]{40,}' . 2>/dev/null; then
echo "::warning::Clé OpenAI potentielle détectée"
FOUND=1
fi
# Google Cloud / API Keys
if grep -rnI $EXCLUDES -E 'AIzaSy[a-zA-Z0-9_-]{33}' . 2>/dev/null; then
echo "::warning::Clé Google API potentielle détectée"
FOUND=1
fi
# AWS
if grep -rnI $EXCLUDES -E 'AKIA[0-9A-Z]{16}' . 2>/dev/null; then
echo "::warning::Clé AWS potentielle détectée"
FOUND=1
fi
# Hugging Face
if grep -rnI $EXCLUDES -E 'hf_[a-zA-Z0-9]{30,}' . 2>/dev/null; then
echo "::warning::Token Hugging Face potentiel détecté"
FOUND=1
fi
# Mots-clés suspects à côté d'assignations
if grep -rnI $EXCLUDES -E '(password|passwd|secret|api_key|apikey|token)\s*=\s*["\x27][a-zA-Z0-9_\-!@#\$%]{12,}["\x27]' . 2>/dev/null \
| grep -viE '(example|dummy|placeholder|test|fake|xxx|changeme|\$\{)' 2>/dev/null; then
echo "::warning::Assignation suspecte d'un secret détectée"
FOUND=1
fi
if [ "$FOUND" -eq 0 ]; then
echo "Aucun secret détecté par les patterns de base."
else
echo ""
echo "::notice::Vérifier manuellement les occurrences ci-dessus."
echo "::notice::Si faux positif : ajouter le fichier aux exclusions ou reformater."
fi
# Toujours succès (job non bloquant).
exit 0

214
.gitea/workflows/tests.yml Normal file
View File

@@ -0,0 +1,214 @@
# ------------------------------------------------------------------
# CI principale — Tests unitaires + lint léger
# ------------------------------------------------------------------
# Déclenchement : push / pull_request sur n'importe quelle branche.
# Objectif : feedback rapide (< 3 min) sans GPU ni Ollama.
# Runner : self-hosted (label "ubuntu-latest" ou équivalent).
#
# Les tests marqués `slow`, `gpu`, `integration`, `performance`,
# `visual` et `smoke` sont exclus volontairement — ils nécessitent
# CUDA, Ollama, ou des captures d'écran réelles.
# ------------------------------------------------------------------
name: tests
on:
push:
branches:
- "**"
pull_request:
branches:
- "**"
# Permet à une nouvelle exécution d'annuler les précédentes
# sur la même branche (évite l'engorgement du runner local).
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
# Empêche l'import accidentel de torch/CUDA pendant la CI.
PYTHONDONTWRITEBYTECODE: "1"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
PIP_NO_PYTHON_VERSION_WARNING: "1"
# Les modules d'exécution lisent parfois ces vars ; valeurs neutres en CI.
RPA_VISION_CI: "1"
RPA_AUTH_VAULT_PATH: "/tmp/ci_vault.enc"
# api_stream.py a un fail-closed P0-C : si RPA_API_TOKEN absent, sys.exit(1)
# au module load. On fournit un token bidon pour que les imports passent en CI.
# (Le token n'est jamais utilisé réellement — les tests mockent les requêtes.)
RPA_API_TOKEN: "ci_test_token_not_used_for_real_auth_just_to_pass_import_check_0123456789"
jobs:
# ----------------------------------------------------------------
# Job 1 — Lint (ruff + black --check)
# ----------------------------------------------------------------
# Non-bloquant : si ruff/black ne sont pas installables, on log
# un warning et on continue. L'objectif ici est d'alerter, pas de
# casser la CI pour des espaces en trop.
# ----------------------------------------------------------------
lint:
name: Lint (ruff + black)
runs-on: ubuntu-latest
timeout-minutes: 5
continue-on-error: true
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
- name: Installation des linters
run: |
python -m pip install --upgrade pip
pip install "ruff==0.6.9" "black==23.12.1" || {
echo "::warning::Impossible d'installer ruff/black — job ignoré"
exit 0
}
- name: Ruff (lint rapide)
run: |
if command -v ruff >/dev/null 2>&1; then
# Ruff : erreurs critiques uniquement (E9 syntax, F63 invalid print,
# F7 syntax, F82 undefined in __all__).
# F821 (undefined name) volontairement exclu le temps de nettoyer
# la dette technique préexistante (voir docs/STATUS.md).
# Dossiers legacy exclus :
# - agent_v0/deploy/windows_client/ : clone obsolète (marqué OBSOLÈTE)
# - tests/property/ : tests cassés connus (cf. MEMORY.md)
ruff check --select=E9,F63,F7,F82 --output-format=github \
--exclude "agent_v0/deploy/windows_client" \
--exclude "tests/property" \
--exclude "tests/integration/test_visual_rpa_checkpoint.py" \
core/ agent_v0/ tests/ || {
echo "::warning::Ruff a trouvé des erreurs critiques"
exit 1
}
else
echo "::warning::ruff indisponible — skip"
fi
- name: Black (format check)
run: |
if command -v black >/dev/null 2>&1; then
# --check : ne modifie pas, signale juste.
# Dossiers legacy exclus (cohérent avec ruff).
black --check --diff \
--exclude "agent_v0/deploy/windows_client|tests/property" \
core/ agent_v0/ tests/ || {
echo "::warning::Black suggère un reformatage — non bloquant"
exit 0
}
else
echo "::warning::black indisponible — skip"
fi
# ----------------------------------------------------------------
# Job 2 — Tests unitaires
# ----------------------------------------------------------------
# Exclut tous les marqueurs lourds. Utilise requirements-ci.txt
# pour éviter torch/CUDA (économie ~3 Go + ~2 min).
# ----------------------------------------------------------------
unit-tests:
name: Tests unitaires (sans GPU)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
requirements-ci.txt
requirements.txt
- name: Installation des dépendances CI
run: |
python -m pip install --upgrade pip
if [ -f requirements-ci.txt ]; then
echo "Utilisation de requirements-ci.txt (léger, sans torch)"
pip install -r requirements-ci.txt
else
echo "::warning::requirements-ci.txt absent — fallback requirements.txt (lourd)"
pip install -r requirements.txt
fi
- name: Vérification imports critiques
run: |
python -c "import pytest; print(f'pytest {pytest.__version__}')"
python -c "import sys; sys.path.insert(0, '.'); import core; print('core OK')" || {
echo "::error::Impossible d'importer core.*"
exit 1
}
- name: Tests unitaires (hors slow/gpu/integration)
run: |
python -m pytest tests/unit/ \
-m "not slow and not gpu and not integration and not performance and not visual" \
--tb=short \
--strict-markers \
-q \
--maxfail=10 \
-o cache_dir=/tmp/.pytest_cache_ci
- name: Upload logs si échec
if: failure()
uses: actions/upload-artifact@v3
with:
name: pytest-logs
path: |
/tmp/.pytest_cache_ci
logs/
retention-days: 3
if-no-files-found: ignore
# ----------------------------------------------------------------
# Job 3 — Tests sécurité (bloquant)
# ----------------------------------------------------------------
# Les tests `test_security_*` valident des invariants critiques
# (évaluation sûre, sérialisation signée). Aucune régression tolérée.
# ----------------------------------------------------------------
security-tests:
name: Tests sécurité (critique)
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [unit-tests]
steps:
- name: Checkout du code
uses: actions/checkout@v4
- name: Setup Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip"
cache-dependency-path: |
requirements-ci.txt
requirements.txt
- name: Installation des dépendances CI
run: |
python -m pip install --upgrade pip
if [ -f requirements-ci.txt ]; then
pip install -r requirements-ci.txt
else
pip install -r requirements.txt
fi
- name: Tests sécurité (test_security_*)
run: |
python -m pytest tests/unit/test_security_*.py \
--tb=long \
--strict-markers \
-v \
-o cache_dir=/tmp/.pytest_cache_ci_sec

36
.gitignore vendored
View File

@@ -75,3 +75,39 @@ htmlcov/
# === Backups === # === Backups ===
*_backup_* *_backup_*
backups/ backups/
*.bak
*.bak_*
*.orig
*.old
# === Legacy / Triage ===
_a_trier/
archives/
# === Claude Code — worktrees et données locales ===
# Worktrees générés par la CLI Claude Code lors d'exécutions d'agents
# parallèles. Peuvent atteindre plusieurs centaines de Mo chacun.
# Ne jamais committer — gérer via `git worktree list` / `git worktree remove`.
.claude/
.kiro/
.mcp.json
.snapshots/
# === Données runtime (sessions, learning, buffer, config local) ===
data/
**/capture_library.json
.hypothesis/
.deps_installed
# Buffers SQLite locaux (streamer, cache)
**/buffer/
**/pending_events.db
# Databases applicatives (instance Flask)
**/instance/*.db
**/instance/*.sqlite
**/instance/*.sqlite3
# Caches et index locaux
*.sqlite
*.sqlite3
*.db-journal
*.db-wal
*.db-shm

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/gpu/clip_manager.py
# hypothesis_version: 6.148.3
['CLIP model unloaded', 'ViT-B-32', 'cpu', 'cuda', 'openai']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/spatial_analyzer.py
# hypothesis_version: 6.148.3
[0.2, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 5.0, 20.0, 50.0, 'SpatialRelation', 'above', 'adjacent', 'bbox', 'below', 'bounds', 'button', 'combobox', 'confidence', 'container_id', 'container_type', 'contains', 'dialog', 'distance', 'element_id', 'element_ids', 'form', 'height', 'id', 'input', 'inside', 'label', 'left_of', 'link', 'list', 'menu', 'menuitem', 'metadata', 'option', 'overlaps', 'panel', 'relation', 'right_of', 'role', 'source', 'tab_group', 'table', 'target', 'text', 'textarea', 'textbox', 'toolbar', 'unknown', 'width', 'x', 'y']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/Geniusia_v2/rpa_vision_v3/__init__.py
# hypothesis_version: 6.148.2
['0.1.0']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/Geniusia_v2/rpa_vision_v3/core/models/state_embedding.py
# hypothesis_version: 6.148.2
[-1.0, 0.0, 1e-06, 0.1, 0.3, 0.5, 1.0, 'EmbeddingComponent', 'StateEmbedding', 'components', 'concat_projection', 'dimensions', 'embedding_id', 'fusion_method', 'image', 'metadata', 'r', 'source_text', 'text', 'title', 'ui', 'utf-8', 'vector_id', 'w', 'weight', 'weighted']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/Geniusia_v2/rpa_vision_v3/core/models/__init__.py
# hypothesis_version: 6.148.2
['Event', 'RawSession', 'Screenshot', 'WindowContext']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/models/ui_element.py
# hypothesis_version: 6.148.3
[0.0, 1.0, 'UIElement', 'UIElementEmbeddings', 'VisualFeatures', 'action_trigger', 'bbox', 'button', 'cancel', 'center', 'checkbox', 'confidence', 'container', 'dangerous_action', 'data_display', 'delete_action', 'dominant_color', 'dropdown', 'element_id', 'embeddings', 'form_input', 'has_icon', 'icon', 'image', 'label', 'label_confidence', 'link', 'menu_item', 'metadata', 'navigation', 'primary_action', 'radio', 'role', 'search_field', 'secondary_action', 'selectable_item', 'shape', 'size_category', 'status_indicator', 'submit', 'tab', 'table_row', 'tags', 'text', 'text_input', 'type', 'visual_features']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/training/__init__.py
# hypothesis_version: 6.148.3
['ClusterMetrics', 'DuplicateAnalysis', 'FrameQuality', 'QualityReport', 'SessionAnalyzer', 'SessionQualityReport', 'TimingAnalysis', 'ValidationResult']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/healing_engine.py
# hypothesis_version: 6.148.3
[0.0, 0.3, 1.0, 2.0, 3.0, 5.0, 10.0, 30.0, 'all_failed', 'data/healing', 'delete', 'input', 'none', 'save', 'submit', 'type', 'update']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/execution/action_executor.py
# hypothesis_version: 6.148.3
[0.0, 0.05, 0.1, 0.2, 0.5, 0.75, 1.0, 1000.0, 100, 500, 1000, 5000, '; ', 'action', 'actions', 'actual', 'all', 'all_fallbacks_failed', 'approximate_position', 'click_offset', 'confidence', 'edge_id', 'elapsed_ms', 'element_id', 'expected', 'expected_role', 'failed', 'fallback_attempt', 'fallback_success', 'historical_bbox', 'inf', 'is_problematic', 'missing_ui_element', 'position_fallback', 'postcondition_failed', 'precondition_failed', 'repeat_policy', 'resolved_element_id', 'screen_state_id', 'source_node', 'state_id', 'stats', 'success', 'target_node', 'target_not_found', 'text', 'timeout', 'timeout_ms', 'to_dict', 'type', 'until_success', 'violations', 'visual_similarity', 'wait_after_ms', 'window_change']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/roi_optimizer.py
# hypothesis_version: 6.148.3
[0.0, 0.3, 0.5, 0.7, 0.8, 0.95, 1.0, 100, 150, 1000, 1080, 1920, 'cache', 'confidence', 'contour', 'full_frame', 'h', 'hit_rate', 'hits', 'max_size', 'merged', 'misses', 'processing_time', 'resize_rate', 'roi_type', 'rois', 'size', 'text', 'timestamp', 'total_frames_resized', 'total_time_saved_ms', 'w', 'x', 'y']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/__init__.py
# hypothesis_version: 6.148.3
['DetectionConfig', 'OllamaClient', 'UIDetector', 'create_detector', 'create_ollama_client']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/__init__.py
# hypothesis_version: 6.148.3
['ConfidenceScorer', 'LearningRepository', 'RecoveryContext', 'RecoveryPattern', 'RecoveryResult', 'SelfHealingEngine']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/confidence_scorer.py
# hypothesis_version: 6.148.3
[0.0, 0.1, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, 1.05, 1.1, 1.5, 100.0, 'application', 'element_type', 'semantic_variant', 'spatial_fallback', 'timing_adaptation']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/sequence_extractor.py
# hypothesis_version: 6.148.3
['SequenceExtractor', 'high', 'low', 'medium', 'min_sequence_length']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/execution/target_resolver.py
# hypothesis_version: 6.148.3
[0.0, 0.5, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 100, 200, 'ResolvedTarget', 'anchor', 'below_text', 'btn', 'button', 'by_context', 'by_embedding', 'by_hierarchy', 'by_position', 'by_role', 'by_similarity', 'by_strategy', 'by_text', 'cache_hits', 'candidates_evaluated', 'candidates_found', 'check', 'checkbox', 'clickable', 'combobox', 'composite', 'contains', 'context_hints', 'criteria_used', 'distance', 'dropdown', 'edittext', 'embedding', 'embedding_ref', 'exact', 'failed', 'fallbacks_used', 'first', 'fuzzy', 'highest_confidence', 'hints_applied', 'hyperlink', 'icon', 'image', 'img', 'input', 'label', 'largest', 'last', 'link', 'list', 'listbox', 'listview', 'match_type', 'max_distance', 'menu', 'menubar', 'menuitem', 'near_text', 'nearest', 'nearest_to_previous', 'partial', 'perception', 'picker', 'picture', 'policy_applied', 'position', 'right_of_text', 'role', 'role_searched', 'screen_state_id', 'select', 'similarity', 'statictext', 'submit', 'successful', 'tab', 'tabitem', 'tabpanel', 'text', 'text_input', 'textbox', 'textfield', 'toggle', 'total_resolutions', 'type', 'ui_elements', 'unknown']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/models/screen_state.py
# hypothesis_version: 6.148.3
['ContextLevel', 'EmbeddingRef', 'PerceptionLevel', 'RawLevel', 'ScreenState', 'WindowContext', 'app_name', 'business_variables', 'capture_method', 'confidence_avg', 'context', 'detected_text', 'dimensions', 'embedding', 'file_size_bytes', 'main', 'metadata', 'perception', 'provider', 'r', 'raw', 'screen_resolution', 'screen_state_id', 'screenshot_path', 'session_id', 'tags', 'timestamp', 'to_dict', 'ui_elements', 'user_id', 'utf-8', 'vector_id', 'w', 'window', 'window_title', 'workflow_step', 'workspace']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/__init__.py
# hypothesis_version: 6.148.3
['BranchConfig', 'ChainConfig', 'ChainResult', 'CommonSequence', 'ConcurrencyMode', 'ConditionType', 'ConditionalNode', 'DependencyGraph', 'ExecutionContext', 'ExecutionResult', 'FailureAction', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopState', 'LoopType', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SemanticMatcher', 'SequenceOccurrence', 'SequencePriority', 'TriggerConfig', 'TriggerContext', 'TriggerType', 'UsageStats', 'ValidationResult', 'VariableDefinition', 'VariableManager', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'WorkflowMatch', 'WorkflowMetadata', 'trigger_from_dict']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/owl_detector.py
# hypothesis_version: 6.148.3
[0.1, 'OWL-v2 chargé', '__main__', 'bbox', 'boxes', 'button', 'center', 'checkbox', 'confidence', 'cpu', 'cuda', 'dropdown menu', 'height', 'icon', 'icon button', 'input box', 'label', 'labels', 'link', 'pt', 'radio button', 'red', 'scores', 'tab', 'text field', 'width']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/trigger_manager.py
# hypothesis_version: 6.148.3
['TriggerManager', 'concurrency_mode', 'detected_element', 'file_path', 'queue', 'trigger_type', 'triggers', 'unknown']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/server/processing_pipeline.py
# hypothesis_version: 6.148.3
[1.0, 512, '\nRésultats:', '*/*.json', '__main__', 'agent_v0', 'app_name', 'completed_at', 'data/training', 'embeddings_generated', 'error', 'errors', 'id', 'none', 'openclip', 'pending', 'primary_resolution', 'processing_pipeline', 'provider', 'screen', 'screen_state_id', 'session_id', 'sessions', 'started_at', 'state', 'status', 'success', 't', 'title', 'training_label', 'ui_elements_detected', 'unknown', 'workflow', 'workflow_created']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/strategies/semantic_variants.py
# hypothesis_version: 6.148.3
[0.6, 0.75, 0.85, 'abort', 'accept', 'accepter', 'add', 'ajouter', 'annuler', 'apply', 'arrière', 'authentifier', 'avancer', 'back', 'backward', 'cancel', 'change', 'changer', 'chercher', 'close', 'confirm', 'confirmer', 'connect', 'connexion', 'continue', 'continuer', 'create', 'créer', 'decline', 'delete', 'disconnect', 'dismiss', 'décliner', 'déconnexion', 'edit', 'effacer', 'element_changed', 'element_not_found', 'envoyer', 'erase', 'fermer', 'find', 'forward', 'found_variant', 'insert', 'log in', 'log out', 'login', 'logout', 'lookup', 'modifier', 'modify', 'new', 'next', 'no', 'non', 'nouveau', 'ok', 'original_text', 'oui', 'previous', 'précédent', 'query', 'rechercher', 'refuser', 'reject', 'remove', 'retirer', 'retour', 'save', 'se connecter', 'se déconnecter', 'search', 'semantic_variant', 'send', 'sign in', 'sign out', 'soumettre', 'submit', 'suivant', 'supprimer', 'trash', 'trouver', 'update', 'valider', 'yes', 'éditer']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/semantic_matcher.py
# hypothesis_version: 6.148.3
[0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 1.0, ' | ', '"([^"]+)"', '*.json', 'No workflows loaded', '[^\\w\\s]', 'a', 'action', 'an', 'and', 'are', 'at', 'au', 'aux', 'be', 'been', 'but', 'by', 'car', 'client', 'data/workflows', 'de', 'des', 'description', 'description_match', 'donc', 'du', 'edges', 'end', 'et', 'exact_name', 'for', 'from', 'in', 'invoice', 'is', 'la', 'le', 'les', 'mais', 'name', 'ni', 'of', 'on', 'or', 'ou', 'param_patterns', 'que', 'qui', 'quoi', 'r', 'start', 'tags', 'the', 'to', 'type', 'un', 'une', 'utf-8', 'was', 'were', 'with', 'à']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/Geniusia_v2/rpa_vision_v3/core/models/raw_session.py
# hypothesis_version: 6.148.2
['Event', 'RawSession', 'Screenshot', 'WindowContext', 'agent_version', 'app_name', 'captured_at', 'context', 'ended_at', 'environment', 'events', 'r', 'rawsession_v1', 'relative_path', 'schema_version', 'screenshot_id', 'screenshots', 'session_id', 'started_at', 't', 'title', 'type', 'user', 'utf-8', 'w', 'window']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/clip_embedder.py
# hypothesis_version: 6.148.3
[224, 'ViT-B-32', 'cpu', 'openai']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/gpu/vram_monitor.py
# hypothesis_version: 6.148.3
[1000.0, 100, 1000, 1024, ',', '--query-gpu=name', 'nvidia-smi', 'utf-8']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/strategies/__init__.py
# hypothesis_version: 6.148.3
['RecoveryStrategy']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/__init__.py
# hypothesis_version: 6.148.3
['CLIPEmbedder', 'EmbedderBase', 'EmbeddingCache', 'FAISSManager', 'FusionConfig', 'FusionEngine', 'PrototypeCache', 'SearchResult', 'angular_distance', 'compute_centroid', 'compute_variance', 'cosine_similarity', 'create_builder', 'create_clip_embedder', 'create_flat_index', 'create_ivf_index', 'dot_product', 'euclidean_distance', 'get_default_embedder', 'hamming_distance', 'is_normalized', 'jaccard_similarity', 'manhattan_distance', 'normalize_l1', 'normalize_l2', 'normalize_vector', 'validate_weights']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/server/storage_encrypted.py
# hypothesis_version: 6.148.3
[128, 100000, '.enc', '.zip', 'ENCRYPTION_PASSWORD', '__main__', 'rb', 'sha256', 'utf-8', 'wb']

View File

@@ -1,4 +0,0 @@
# file: /usr/lib/python3.12/sitecustomize.py
# hypothesis_version: 6.148.2
[]

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/graph/graph_builder.py
# hypothesis_version: 6.148.3
[0.0, 0.15, 0.7, 0.8, 0.85, 200, 300, 500, 512, 1080, 1920, 3000, 5000, '+00:00', 'OBSERVATION', 'Unknown', 'Unnamed Workflow', 'Z', '__main__', 'auto_generated', 'button', 'cosine', 'created_from_event', 'embedding_prototype', 'event_time', 'event_type', 'first', 'id', 'inf', 'key_press', 'keyboard_input', 'keys', 'left', 'main', 'mouse_click', 'mss', 'openclip_ViT-B-32', 'pending', 'pos', 'position', 'primary_resolution', 'screen', 'screenshot_id', 'state_id', 'tags', 'template', 'text', 'text_field', 'text_input', 'unknown', 'unknown_element', 'visual_similarity', 'wait_after_ms']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/base_embedder.py
# hypothesis_version: 6.148.3
[]

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/models/workflow_graph.py
# hypothesis_version: 6.148.3
[0.0, 0.8, 0.85, 0.9, 0.95, 100, 3000, 5000, '.*', '1.0.0', 'AUTO_CANDIDATE', 'AUTO_CONFIRMED', 'Action', 'COACHING', 'EdgeConstraints', 'EdgeStats', 'EmbeddingPrototype', 'LearningConfig', 'OBSERVATION', 'OK', 'PostConditions', 'SafetyRules', 'ScreenTemplate', 'TargetSpec', 'TextConstraint', 'UIConstraint', 'WindowConstraint', 'Workflow', 'WorkflowEdge', 'WorkflowNode', 'WorkflowStats', 'action', 'assist_runs', 'auto_candidate_runs', 'auto_confirmed_runs', 'avg_similarity', 'by_position', 'by_role', 'by_similarity', 'by_text', 'coaching_threshold', 'compound', 'compute_similarity', 'constraints', 'context_hints', 'created_at', 'description', 'detected_texts', 'edge_id', 'edges', 'embedding', 'end_nodes', 'entry_nodes', 'execution_count', 'expected_node', 'failure_count', 'fallback_strategy', 'first', 'forbidden_text', 'forbidden_texts', 'forbidden_windows', 'from_node', 'is_end', 'is_entry', 'key_press', 'last', 'last_executed', 'learning', 'learning_state', 'max_auto_executions', 'max_wait_time_ms', 'metadata', 'min_element_count', 'mouse_click', 'name', 'node_id', 'nodes', 'none', 'observed_runs', 'parameters', 'perception', 'post_conditions', 'pre_conditions', 'process', 'process_name', 'provider', 'r', 'recent_confidence', 'required_confidence', 'required_elements', 'required_roles', 'required_text', 'required_texts', 'required_types', 'role', 'safety_rules', 'sample_count', 'selection_policy', 'similarity_threshold', 'source_node', 'stats', 'success_count', 'target', 'target_node', 'template', 'text', 'text_input', 'timeout_ms', 'title', 'title_contains', 'title_pattern', 'to_node', 'total_executions', 'type', 'ui', 'ui_elements', 'updated_at', 'utf-8', 'vector_id', 'version', 'visual_similarity', 'w', 'weighted', 'window', 'workflow_id']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/persistence/storage_manager.py
# hypothesis_version: 6.148.3
[0.0, 1024, '%Y%m%d_%H%M%S', '%Y-%m-%d', '*', '*.json', '*.npy', '-_', '.json', '_', '_metadata', 'base_path', 'data', 'dimension', 'dtype', 'edges', 'embedding_id', 'embedding_type', 'embeddings', 'events', 'faiss_index', 'filepath', 'index_name', 'main', 'metadata_store', 'nodes', 'num_edges', 'num_events', 'num_nodes', 'num_screenshots', 'num_vectors', 'r', 'rawsession_v1', 'saved_at', 'schema_version', 'screen_states', 'screenshots', 'screenstate_v1', 'session_*.json', 'session_id', 'sessions', 'shape', 'size_kb', 'state', 'state_id', 'total_size_mb', 'unknown', 'unnamed', 'utf-8', 'w', 'workflow_*.json', 'workflow_id', 'workflow_name', 'workflow_v1', 'workflows']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/dependency_graph.py
# hypothesis_version: 6.148.3
[' -> ', 'DependencyGraph', 'UsageStats', 'dependencies', 'dependency_count', 'dependent_count', 'dependents', 'reference_counts', 'total_references']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/execution_logger.py
# hypothesis_version: 6.148.3
['ExecutionLogger', 'action', 'end', 'entries', 'error', 'final_variables', 'start', 'success', 'warning']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/persistence/__init__.py
# hypothesis_version: 6.148.3
['StorageManager']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/ollama_client.py
# hypothesis_version: 6.148.3
[0.0, 0.1, 0.7, 0.9, 200, 500, 600, 2048, ', ', '/', 'PNG', 'confidence', 'elements', 'error', 'images', 'model', 'models', 'name', 'num_ctx', 'num_predict', 'options', 'prompt', 'qwen', 'qwen3-vl:8b', 'raw_response', 'rb', 'response', 'role', 'status', 'stream', 'success', 'system', 'temperature', 'text', 'top_k', 'type', 'unknown', 'utf-8']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/conditional_evaluator.py
# hypothesis_version: 6.148.3
['ConditionalEvaluator', 'detected_text', 'element_absent', 'element_ids', 'element_present', 'elements', 'has_target_resolver', 'id', 'name', 'text_contains', 'text_equals', 'texts', 'type']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/state_embedding_builder.py
# hypothesis_version: 6.148.3
[512, '%Y%m%d_%H%M%S_%f', 'Traceback:', 'created_at', 'data/embeddings', 'detected_texts', 'embed_image', 'embed_text', 'embeddings', 'encode_image', 'encode_text', 'image', 'image_embedding_id', 'label', 'perception', 'raw', 'screen_state_id', 'text', 'timestamp', 'title', 'ui', 'ui_elements', 'window', 'window_title']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/gpu/__init__.py
# hypothesis_version: 6.148.3
['CLIPManager', 'ExecutionMode', 'GPUResourceConfig', 'GPUResourceManager', 'GPUResourceStatus', 'ModelState', 'OllamaManager', 'ResourceChangedEvent', 'VRAMInfo', 'VRAMMonitor']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/loop_executor.py
# hypothesis_version: 6.148.3
[1000, 'LoopExecutor', 'active_loops', 'condition', 'configs', 'count', 'safety_limit']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/ollama_client.py
# hypothesis_version: 6.148.3
[0.0, 0.1, 0.7, 0.9, 200, 500, 600, 2048, ', ', '/', '<think>', '<|im_end|>', 'PNG', 'confidence', 'elements', 'error', 'images', 'model', 'models', 'name', 'num_ctx', 'num_predict', 'options', 'prompt', 'qwen', 'qwen3-vl:8b', 'raw_response', 'rb', 'response', 'role', 'status', 'stop', 'stream', 'success', 'system', 'temperature', 'text', 'type', 'unknown', 'utf-8']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/execution/error_handler.py
# hypothesis_version: 6.148.3
[0.7, 5000, '%Y%m%d_%H%M%S_%f', '__main__', 'a', 'abort', 'action', 'action_type', 'best_confidence', 'candidate_nodes', 'ctrl', 'current_similarity', 'data/errors', 'delete', 'details', 'edge', 'error_counts', 'error_id', 'error_report.json', 'error_type', 'execution_timeout', 'expected_node', 'fallback', 'label', 'matching_failed', 'message', 'node', 'node_id', 'num_candidates', 'pause', 'postcondition_failed', 'problematic_edges', 'recovery_attempts', 'retry', 'role', 'rollback', 'rollback_message', 'screenshot.png', 'screenshot_path', 'skip', 'state', 'target_not_found', 'target_role', 'target_text', 'text_pattern', 'threshold', 'timeout_ms', 'timestamp', 'total_errors', 'ui_change_detected', 'ui_changed', 'ui_elements_count', 'unknown', 'w', 'window_title']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/server/processing_pipeline.py
# hypothesis_version: 6.148.3
[1.0, 512, '\nRésultats:', '*/*.json', '.enc', '.zip', '__main__', 'agent_v0', 'app_name', 'cleaned_files', 'cleanup_error', 'completed_at', 'data/training', 'embeddings_generated', 'error', 'errors', 'id', 'none', 'openclip', 'pending', 'primary_resolution', 'processing_pipeline', 'provider', 'screen', 'screen_state_id', 'session_id', 'sessions', 'started_at', 'state', 'status', 'success', 't', 'title', 'training_label', 'ui_elements_detected', 'unknown', 'uploads', 'workflow', 'workflow_created']

View File

@@ -1,4 +0,0 @@
# file: /tmp/tmpdh0hjylw/_remote_module_non_scriptable.py
# hypothesis_version: 6.148.3
['cuda']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/models/workflow_graph.py
# hypothesis_version: 6.148.3
[0.0, 0.7, 0.8, 0.85, 0.9, 0.95, 1.0, 100, 3000, 5000, '.*', '1.0.0', 'AUTO_CANDIDATE', 'AUTO_CONFIRMED', 'Action', 'COACHING', 'EdgeConstraints', 'EdgeStats', 'EmbeddingPrototype', 'LearningConfig', 'NodeVariant', 'OBSERVATION', 'OK', 'PostConditions', 'PrototypeVersion', 'SafetyRules', 'ScreenTemplate', 'SpatialRelation', 'TargetSpec', 'TextConstraint', 'UIConstraint', 'WindowConstraint', 'Workflow', 'WorkflowEdge', 'WorkflowNode', 'WorkflowStats', 'action', 'assist_runs', 'auto_candidate_runs', 'auto_confirmed_runs', 'avg_similarity', 'by_position', 'by_role', 'by_similarity', 'by_text', 'cluster_metrics', 'coaching_threshold', 'compound', 'compute_similarity', 'confidence', 'constraints', 'container_type', 'context_hints', 'created_at', 'description', 'detected_texts', 'distance_pixels', 'edge_id', 'edges', 'embedding', 'embedding_path', 'end_nodes', 'entry_nodes', 'execution_count', 'expected_node', 'failure_count', 'fallback_strategy', 'first', 'forbidden_text', 'forbidden_texts', 'forbidden_windows', 'from_node', 'is_end', 'is_entry', 'key_press', 'last', 'last_executed', 'learning', 'learning_state', 'max_auto_executions', 'max_variants', 'max_wait_time_ms', 'metadata', 'min_element_count', 'mouse_click', 'name', 'node_id', 'nodes', 'none', 'observation_count', 'observed_runs', 'parameters', 'perception', 'post_conditions', 'pre_conditions', 'primary_variant_id', 'process', 'process_name', 'prototype_history', 'prototype_version', 'provider', 'quality_score', 'r', 'reason', 'recent_confidence', 'relation_id', 'relation_type', 'required_confidence', 'required_elements', 'required_roles', 'required_text', 'required_texts', 'required_types', 'role', 'safety_rules', 'sample_count', 'selection_policy', 'similarity_threshold', 'source_element_id', 'source_node', 'spatial_relations', 'stats', 'success_count', 'success_rate', 'target', 'target_element_id', 'target_node', 'template', 'text', 'text_input', 'timeout_ms', 'title', 'title_contains', 'title_pattern', 'to_node', 'total_executions', 'type', 'ui', 'ui_elements', 'ui_state', 'unknown', 'updated_at', 'utf-8', 'variant_id', 'variants', 'vector_id', 'version', 'version_id', 'visual_similarity', 'w', 'weighted', 'window', 'workflow_id']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/workflow_chainer.py
# hypothesis_version: 6.148.3
['Unknown', 'WorkflowChainer', 'chain_end', 'chain_error', 'chain_start', 'chains', 'error', 'execution_log', 'start', 'success', 'unknown', 'variable_manager', 'workflow_end', 'workflow_start']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/variable_manager.py
# hypothesis_version: 6.148.3
['1', 'VariableDefinition', 'VariableManager', 'boolean', 'default_value', 'definitions', 'description', 'integer', 'list', 'name', 'number', 'oui', 'required', 'string', 'true', 'type', 'validation_pattern', 'values', 'variables', 'yes']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/strategies/timing_adaptation.py
# hypothesis_version: 6.148.3
[0.5, 0.8, 1.0, 1.2, 1.5, 3.0, 5.0, 30.0, 'element', 'new_wait_time', 'original_wait', 'timeout', 'timing_adaptation', 'wait_time']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/models.py
# hypothesis_version: 6.148.3
[0.0, 0.7, 'RecoveryPattern', 'attempt_count', 'confidence_score', 'confidence_threshold', 'context_metadata', 'created_at', 'error_message', 'execution_time', 'failure_count', 'failure_reason', 'last_used', 'learned_pattern', 'max_attempts', 'metadata', 'new_element', 'node_id', 'original_action', 'original_failure', 'pattern_id', 'recovery_strategy', 'requires_user_input', 'screenshot_path', 'strategy_used', 'success', 'success_count', 'target_element', 'workflow_id']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/strategies/format_transformation.py
# hypothesis_version: 6.148.3
[0.6, 0.7, 0.75, 0.85, '%B %d, %Y', '%Y-%m-%d', '%Y/%m/%d', '%d %B %Y', '%d-%m-%Y', '%d.%m.%Y', '%d/%m/%Y', '%m/%d/%Y', '1', '\\D', 'clean_whitespace', 'confidence', 'date', 'format_error', 'formatted_value', 'input_rejected', 'input_type', 'input_value', 'max_length', 'new_format', 'original_format', 'phone', 'text', 'transformation', 'validation_failed']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/detection/ui_detector.py
# hypothesis_version: 6.148.3
[0.0, 0.02, 0.1, 0.3, 0.33, 0.5, 0.6, 0.7, 0.8, 1.0, 1.2, 100, 128, 150, 255, 600, 1000, 10000, 'BoundingBox', 'bbox', 'button', 'checkbox', 'confidence', 'detected_by', 'detection_method', 'form_input', 'horizontal_bar', 'hybrid', 'hybrid_fallback', 'large', 'medium', 'merged', 'qwen3-vl:8b', 'rectangle', 'rectangle_detection', 'role', 'screenshot_path', 'small', 'square', 'success', 'text', 'text_detection', 'text_input', 'type', 'unknown', 'vertical_bar', 'vlm_model', '✓ OWL-v2 initialized']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/composition_models.py
# hypothesis_version: 6.148.3
[1000, 'BranchConfig', 'ChainConfig', 'ChainResult', 'CommonSequence', 'ConditionalNode', 'ExecutionContext', 'ExecutionResult', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopState', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SequenceOccurrence', 'TriggerContext', 'ValidationResult', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'abort', 'action_a', 'action_b', 'body_nodes', 'branch_id', 'branches', 'chain_id', 'chains', 'completed_at', 'concurrent', 'condition', 'condition_type', 'conditionals', 'conflicts', 'count', 'cron_expression', 'current_iteration', 'current_workflow_id', 'default_branch', 'default_value', 'description', 'details', 'detected_element', 'element_absent', 'element_present', 'enabled', 'end_index', 'error_context', 'error_message', 'errors', 'event_type', 'execution_log', 'exit_condition', 'expected_text', 'file', 'file_path', 'file_pattern', 'final_variables', 'fired_at', 'global_variables', 'high', 'input_bindings', 'input_parameters', 'interval_seconds', 'is_valid', 'iteration', 'loop_id', 'loop_type', 'loops', 'low', 'max_iterations', 'max_retries', 'medium', 'name', 'new_value', 'node_id', 'nodes', 'occurrences', 'old_value', 'on_failure', 'output_bindings', 'output_values', 'param_type', 'priority', 'queue', 'references', 'required', 'retry', 'safety_limit', 'schedule', 'shared_nodes', 'should_continue', 'similarity_score', 'skip', 'source_workflow', 'source_workflow_id', 'start_index', 'started_at', 'string', 'sub_workflow_id', 'sub_workflows', 'success', 'target_element', 'target_node', 'target_workflow_id', 'text_contains', 'text_equals', 'timestamp', 'trigger_context', 'trigger_id', 'trigger_type', 'triggers', 'variable_mapping', 'variable_name', 'visual', 'warnings', 'watch_directory', 'workflow_a_id', 'workflow_b_id', 'workflow_id', 'workflows_executed']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/training/session_analyzer.py
# hypothesis_version: 6.148.3
[0.0, 0.1, 0.3, 0.4, 0.5, 0.6, 0.8, 0.95, 1.0, 2.0, 127.5, 255.0, 100, 500, 1000, 10000, 'Image non chargée', 'acceptable_frames', 'created_at', 'duplicate_count', 'duplicate_ratio', 'duplicates', 'events', 'frame_count', 'is_acceptable', 'is_consistent', 'mean_interval', 'outlier_count', 'overall_score', 'recommendations', 'relative_path', 'screenshots', 'session_id', 'std_interval', 't', 'timing', 'unknown']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/gpu/ollama_manager.py
# hypothesis_version: 6.148.3
[200, '/', '5m', ':', 'keep_alive', 'model', 'models', 'name', 'prompt', 'qwen3-vl:8b', 'stream']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/graph/node_matcher.py
# hypothesis_version: 6.148.3
[0.0, 0.05, 0.7, 0.85, '%Y%m%d_%H%M%S', '__main__', 'best_confidence', 'data/failed_matches', 'failed_match_id', 'matched', 'matching_results', 'metadata', 'node_id', 'node_label', 'num_candidates', 'report.json', 'screenshot.png', 'screenshot_path', 'similarities', 'similarity', 'state', 'state_embedding.npy', 'suggestions', 'threshold', 'timestamp', 'ui_elements_count', 'w', 'window_title']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/embedding_cache.py
# hypothesis_version: 6.148.3
[0.0, 500.0, 100, 1000, 1024, 'evictions', 'hit_rate', 'hits', 'max_memory_mb', 'max_size', 'memory_mb', 'memory_usage_pct', 'misses', 'size', 'total_accesses']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/__init__.py
# hypothesis_version: 6.148.3
['BranchConfig', 'ChainConfig', 'ChainResult', 'CommonSequence', 'ConcurrencyMode', 'ConditionType', 'ConditionalNode', 'ExecutionContext', 'ExecutionResult', 'FailureAction', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopState', 'LoopType', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SemanticMatcher', 'SequenceOccurrence', 'SequencePriority', 'TriggerConfig', 'TriggerContext', 'TriggerType', 'ValidationResult', 'VariableDefinition', 'VariableManager', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'WorkflowMatch', 'WorkflowMetadata', 'trigger_from_dict']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/strategies/spatial_fallback.py
# hypothesis_version: 6.148.3
[0.0, 0.4, 0.5, 0.6, 1.0, 100.0, 100, 200, 400, ')', ',', '@(', 'element', 'element_moved', 'element_not_found', 'found_position', 'original_position', 'position', 'search_radius', 'similarity', 'spatial_fallback']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/graph/__init__.py
# hypothesis_version: 6.148.3
['GraphBuilder', 'NodeMatcher']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/similarity.py
# hypothesis_version: 6.148.3
[-1.0, 0.0, 1e-10, 1e-06, 1.0, 'angular', 'cosine', 'euclidean', 'l1', 'l2', 'manhattan']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/training/quality_validator.py
# hypothesis_version: 6.148.3
[0.0, 0.2, 0.3, 0.5, 0.7, 0.8, 0.85, 1.5, 'cluster_count', 'created_at', 'inf', 'is_production_ready', 'matched_node_id', 'nodes', 'observation_count', 'outlier_count', 'overall_score', 'recommendations', 'unknown', 'validation_accuracy', 'workflow_id']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/strategies/base_strategy.py
# hypothesis_version: 6.148.3
[0.5]

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/execution/__init__.py
# hypothesis_version: 6.148.3
['ActionExecutor', 'ErrorHandler', 'ErrorType', 'RecoveryStrategy', 'ResolvedTarget', 'TargetResolver']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/__init__.py
# hypothesis_version: 6.148.3
['BranchConfig', 'ChainConfig', 'ChainResult', 'CommonSequence', 'ConcurrencyMode', 'ConditionType', 'ConditionalNode', 'DependencyGraph', 'ExecutionContext', 'ExecutionResult', 'FailureAction', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopState', 'LoopType', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SemanticMatcher', 'SequenceOccurrence', 'SequencePriority', 'TriggerConfig', 'TriggerContext', 'TriggerType', 'UsageStats', 'ValidationResult', 'VariableDefinition', 'VariableManager', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'WorkflowMatch', 'WorkflowMetadata', 'trigger_from_dict']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/fusion_engine.py
# hypothesis_version: 6.148.3
[0.0, 1e-10, 0.99, 1.0, 1.01, 'Empty batch provided', 'concat_projection', 'weighted']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/embedding/faiss_manager.py
# hypothesis_version: 6.148.3
[0.5, 1.0, 100, 1000, 10000, 65536, 'FAISSManager', 'Flat', 'HNSW', 'IVF', 'auto_optimize', 'cosine', 'dimensions', 'embedding_id', 'faiss.Index', 'index_type', 'ip', 'is_trained', 'l2', 'metadata', 'metadata_count', 'metadata_store', 'metric', 'next_id', 'nlist', 'nlist_efficiency', 'nprobe', 'optimal_nlist', 'rb', 'total_vectors', 'use_gpu', 'wb']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/__init__.py
# hypothesis_version: 6.148.3
['BranchConfig', 'ChainConfig', 'ChainResult', 'CommonSequence', 'ConcurrencyMode', 'ConditionType', 'ConditionalNode', 'DependencyGraph', 'ExecutionContext', 'ExecutionResult', 'FailureAction', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopExecutor', 'LoopSafetyLimitError', 'LoopState', 'LoopType', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SemanticMatcher', 'SequenceOccurrence', 'SequencePriority', 'TriggerConfig', 'TriggerContext', 'TriggerType', 'UsageStats', 'ValidationResult', 'VariableDefinition', 'VariableManager', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'WorkflowMatch', 'WorkflowMetadata', 'trigger_from_dict']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/healing/learning_repository.py
# hypothesis_version: 6.148.3
[0.2, 0.3, '.tmp', 'application', 'element_type', 'original_action', 'patterns.json', 'r', 'target_element', 'unknown', 'w', 'workflow_id', '|']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/gpu/gpu_resource_manager.py
# hypothesis_version: 6.148.3
[0.5, 300, 1024, 'CLIP migrated to CPU', 'CLIP migrated to GPU', 'GPUResourceManager', 'Loading VLM model...', 'VLM unload timeout', 'ViT-B-32', 'autopilot', 'clip', 'cpu', 'cuda', 'device', 'device_changed', 'error', 'idle', 'loaded', 'loading', 'model', 'model_loaded', 'model_unloaded', 'qwen3-vl:8b', 'recording', 'unloaded', 'unloading', 'vram_freed_mb']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/models/__init__.py
# hypothesis_version: 6.148.3
['Action', 'ActionType', 'ContextLevel', 'EdgeConstraints', 'EdgeStats', 'EmbeddingComponent', 'EmbeddingPrototype', 'Event', 'LearningConfig', 'LearningState', 'PerceptionLevel', 'PostConditions', 'RawLevel', 'RawSession', 'SafetyRules', 'ScreenState', 'ScreenTemplate', 'Screenshot', 'SelectionPolicy', 'StateEmbedding', 'TargetSpec', 'TextConstraint', 'UIConstraint', 'UIElement', 'UIElementEmbeddings', 'VisualFeatures', 'WindowConstraint', 'WindowContext', 'Workflow', 'WorkflowEdge', 'WorkflowNode', 'WorkflowStats']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/workflow_merger.py
# hypothesis_version: 6.148.3
[0.0, 0.9, 'WorkflowMerger', 'similarity_threshold']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/global_variable_manager.py
# hypothesis_version: 6.148.3
['global_vars', 'override_log', 'source_workflow']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/__init__.py
# hypothesis_version: 6.148.3
['BranchConfig', 'ChainConfig', 'ChainExecutionError', 'ChainResult', 'ChainValidationError', 'CommonSequence', 'ConcurrencyMode', 'ConditionType', 'ConditionalEvaluator', 'ConditionalNode', 'DependencyGraph', 'ExecutionContext', 'ExecutionLogger', 'ExecutionResult', 'FailureAction', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopExecutor', 'LoopSafetyLimitError', 'LoopState', 'LoopType', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SemanticMatcher', 'SequenceExtractor', 'SequenceOccurrence', 'SequencePriority', 'SubWorkflowRegistry', 'TriggerConfig', 'TriggerContext', 'TriggerManager', 'TriggerType', 'UsageStats', 'ValidationResult', 'VariableDefinition', 'VariableManager', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'WorkflowChainer', 'WorkflowMatch', 'WorkflowMerger', 'WorkflowMetadata', 'trigger_from_dict']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/__init__.py
# hypothesis_version: 6.148.3
['BranchConfig', 'ChainConfig', 'ChainResult', 'CommonSequence', 'ConcurrencyMode', 'ConditionType', 'ConditionalEvaluator', 'ConditionalNode', 'DependencyGraph', 'ExecutionContext', 'ExecutionResult', 'FailureAction', 'FileTrigger', 'IterationResult', 'LogEntry', 'LoopConfig', 'LoopExecutor', 'LoopSafetyLimitError', 'LoopState', 'LoopType', 'MergeCandidate', 'NodeConflict', 'ParameterDef', 'ReferenceNode', 'ScheduleTrigger', 'SemanticMatcher', 'SequenceOccurrence', 'SequencePriority', 'TriggerConfig', 'TriggerContext', 'TriggerType', 'UsageStats', 'ValidationResult', 'VariableDefinition', 'VariableManager', 'VariableOverride', 'VisualCondition', 'VisualTrigger', 'WorkflowMatch', 'WorkflowMetadata', 'trigger_from_dict']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/core/workflow/subworkflow_registry.py
# hypothesis_version: 6.148.3
['SubWorkflowRegistry', 'definitions', 'dependency_graph', 'inputs', 'parent', 'references']

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/Geniusia_v2/rpa_vision_v3/core/__init__.py
# hypothesis_version: 6.148.2
[]

View File

@@ -1,4 +0,0 @@
# file: /home/dom/ai/rpa_vision_v3/server/storage_encrypted.py
# hypothesis_version: 6.148.3
[100000, '.zip', 'ENCRYPTION_PASSWORD', 'Padding invalide', '__main__', 'rb', 'utf-8', 'wb']

View File

@@ -1 +0,0 @@
Uـà¢ين6ô`^ة—لa‡$½lû_©؟ق†„iأeکم-®0X

View File

@@ -1,3 +0,0 @@
 ”˙04FZE]?|@<40>hŢ”%ÎÉ\m3Q@ű^Ö{ă&ymŐ9Ĺ
 

View File

@@ -1 +0,0 @@
_Ej╩█<=)З╨{,0cV(─PZИ╛iЯЁ╨кАZНоXш╩╨vЕу0╢╣

View File

@@ -1 +0,0 @@
С¤-щY<N$MюНWs+ЗiUь¶крыгV5вXІ ¶‡92ўы"Ѕ4I

View File

@@ -1 +0,0 @@
Ѥ-ùY<N$Mþ•ÍWs+ÇiUü¶êðûãV5â ¶‡92¢û"½4I.secondary

View File

@@ -1 +0,0 @@
•ñpÉzpó€¼áÀ-6@ÔÌ(Þõv>"<22>'<27>ƒ¾«‰c6Ù¥f³#p*däÙM.secondary

View File

@@ -1 +0,0 @@
•ñpÉzpó€¼áÀ-6@ÔÌ(Þõv>"<22>'<27>ƒ¾«‰c6Ù¥f³#p*däÙM

View File

@@ -1 +0,0 @@
_Ej»<EFBFBD><=)úº{,0cV(€PZé¬iñ³ºËáZîÏXÛ»ºvåÕ0´µ.secondary

View File

@@ -1,2 +0,0 @@
ã<­“Ñ3ì·¤\*{àen†ã^‡ð‘ü@y ;]
…¯[Èýº…ç

View File

@@ -1,2 +0,0 @@
ã<­“Ñ3ì·¤\*{àen†ã^‡ð‘ü@y ;]
…¯[Èýº…ç3Ô.secondary

Some files were not shown because too many files have changed in this diff Show More