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.
This commit is contained in:
Dom
2026-05-07 10:34:29 +02:00
parent f62fda575f
commit 7233df2bb9
2 changed files with 19 additions and 2 deletions

View File

@@ -563,6 +563,11 @@ class RawReplayRequest(BaseModel):
session_id: str = ""
machine_id: Optional[str] = None # Machine cible (multi-machine)
task_description: str = ""
# Paramètres runtime du replay (lus dans replay_state.params côté pipeline).
# Notamment execution_mode : "autonomous" (défaut, pause_for_human skippée)
# ou "supervised" (pause_for_human bloque jusqu'à validation humaine via
# PauseDialog VWB). Cf. replay_engine.py / api_stream.py:2964.
params: Optional[Dict[str, Any]] = None
class SingleActionRequest(BaseModel):
@@ -2137,7 +2142,7 @@ async def start_raw_replay(request: RawReplayRequest):
workflow_id=f"free_task:{task[:50]}",
session_id=session_id,
total_actions=len(actions),
params={},
params=dict(request.params or {}),
machine_id=resolved_machine_id,
actions=actions,
)
@@ -4411,7 +4416,10 @@ async def resolve_target(request: ResolveTargetRequest):
# par le dernier heartbeat avant la cascade _resolve_target_sync.
effective_w = request.screen_width
effective_h = request.screen_height
if img.height < 200 or img.width < 400:
# Seuil large : un écran moderne fait 2560x1600 ou plus. Tout en dessous
# de 1200x800 est suspect — bug client mss.monitors[1] qui crop sur
# barre des tâches (2560x60), Edge fenêtré (622x856), etc.
if img.height < 800 or img.width < 1200:
logger.warning(
"[RESOLVE_TARGET] Image client tronquée %dx%d (declared %dx%d) — "
"fallback heartbeat full screen",

View File

@@ -1082,6 +1082,15 @@ def execute_windows():
if not data.get('session_id'):
data['session_id'] = 'agent_demo_user'
# Forcer le mode supervisé : pause_for_human DÉCLENCHE au lieu d'être
# skippée. Le médecin valide la décision Léa avant que les saisies
# type_text ne s'exécutent dans l'onglet Codage. Crucial pour la démo
# GHT : Léa propose, humain valide, Léa finalise (cf. workflow Urgence).
# Sans ça, mode "autonomous" par défaut → pause skippée → saisies
# tentées sans validation → désordre visuel.
data.setdefault('params', {})
data['params'].setdefault('execution_mode', 'supervised')
# Injecter le machine_id pour le ciblage multi-machine.
# Cibler la machine Windows la plus récemment active (heartbeat last_activity)
# plutôt que la première dans l'ordre arbitraire renvoyé par /machines :