From 01bba7bc6c35bfa88efffc5ec9ceacf27a61ac01 Mon Sep 17 00:00:00 2001 From: Dom Date: Mon, 13 Apr 2026 09:27:01 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20wrong=5Fwindow=20d=C3=A9clenche=20le=20?= =?UTF-8?q?mode=20apprentissage=20au=20lieu=20de=20bloquer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- agent_v0/agent_v1/core/executor.py | 48 +++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/agent_v0/agent_v1/core/executor.py b/agent_v0/agent_v1/core/executor.py index f0f7ebbfa..9e65529be 100644 --- a/agent_v0/agent_v1/core/executor.py +++ b/agent_v0/agent_v1/core/executor.py @@ -620,14 +620,54 @@ class ActionExecutorV1: f"[LEA] Fenêtre incorrecte : attendu '{expected_title}', " f"actuel '{current_title}'" ) - print(f" [PRÉ-VÉRIF] STOP — fenêtre '{current_title}' ≠ attendu '{expected_title}'") + print( + f" [PRÉ-VÉRIF] Fenêtre '{current_title}' ≠ " + f"attendu '{expected_title}' → mode apprentissage" + ) try: self.notifier.replay_wrong_window(current_title, expected_title) except Exception: pass - result["success"] = False - result["error"] = f"Fenêtre incorrecte: '{current_title}' (attendu: '{expected_title}')" - result["warning"] = "wrong_window" + + # Mode apprentissage : la fenêtre attendue n'est + # pas là. Soit l'action précédente a changé l'état + # (ex: Ctrl+S a sauvé sans dialogue), soit l'app + # est dans un état différent. L'humain montre. + human_actions = self._capture_human_correction( + timeout_s=120, + ) + if human_actions: + result["success"] = True + result["resolution_method"] = "human_supervised" + result["warning"] = "human_supervised_wrong_window" + last_click = None + for ha in reversed(human_actions): + if ha.get("type") == "click": + last_click = ha + break + if last_click: + result["actual_position"] = { + "x_pct": last_click["x_pct"], + "y_pct": last_click["y_pct"], + } + result["correction"] = { + "actions": human_actions, + "action_count": len(human_actions), + "last_click": last_click, + "trigger": "wrong_window", + "expected_window": expected_title, + "actual_window": current_title, + } + else: + # Timeout ou pas d'action → skipper cette action + # L'état est peut-être déjà correct (ex: Ctrl+S + # a sauvé sans dialogue → action de dialogue inutile) + result["success"] = True + result["warning"] = "wrong_window_skipped" + logger.info( + f"[LEA] Wrong window sans correction → skip " + f"(l'état est peut-être déjà atteint)" + ) return result else: logger.info(f"[LEA] Pré-vérif OK : '{current_title}'")