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>
This commit is contained in:
Dom
2026-04-10 11:02:51 +02:00
parent ac9c207474
commit 332366b58c
5 changed files with 480 additions and 14 deletions

View File

@@ -114,6 +114,8 @@ def _strategy_to_target_spec(
by_text_candidate = ""
anchor_candidate = ""
vlm_candidate = ""
uia_data: Dict[str, Any] = {}
dom_data: Dict[str, Any] = {}
resolve_order: List[str] = []
seen_methods: set = set()
@@ -129,6 +131,19 @@ def _strategy_to_target_spec(
by_text_candidate = strat.target_text
elif strat.method == "vlm" and strat.vlm_description and not vlm_candidate:
vlm_candidate = strat.vlm_description
elif strat.method == "uia" and strat.uia_name and not uia_data:
uia_data = {
"name": strat.uia_name,
"control_type": strat.uia_control_type,
"automation_id": strat.uia_automation_id,
"parent_path": strat.uia_parent_path,
}
elif strat.method == "dom" and strat.dom_selector and not dom_data:
dom_data = {
"selector": strat.dom_selector,
"xpath": strat.dom_xpath,
"url_pattern": strat.dom_url_pattern,
}
# Construire l'ordre des méthodes (dans l'ordre primaire → fallbacks)
if strat.method and strat.method not in seen_methods:
@@ -145,6 +160,14 @@ def _strategy_to_target_spec(
# L'intention métier devient le prompt VLM de dernier recours
spec["vlm_description"] = intent
# Données UIA — consommées par l'agent Windows via lea_uia.exe
if uia_data:
spec["uia_target"] = uia_data
# Données DOM — consommées par l'agent Windows via CDP (futur)
if dom_data:
spec["dom_target"] = dom_data
# Ordre de résolution pré-compilé — c'est LA pièce centrale du V4
if resolve_order:
spec["resolve_order"] = resolve_order