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>
This commit is contained in:
Dom
2026-04-13 09:27:01 +02:00
parent d5285de99c
commit 01bba7bc6c

View File

@@ -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}'")