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>
This commit is contained in:
@@ -2853,6 +2853,35 @@ async def get_next_action(session_id: str, machine_id: str = "default"):
|
||||
# Peek à la prochaine action SANS la retirer (pour le pre-check)
|
||||
action = queue[0]
|
||||
|
||||
# ── pause_for_human : interception avant exécution ──
|
||||
# Cette action n'est jamais transmise à l'Agent V1. Elle bascule
|
||||
# le replay en paused_need_help avec le message custom, ce qui
|
||||
# déclenche la bulle interactive ChatWindow (J3.5).
|
||||
if action.get("type") == "pause_for_human" and owning_replay is not None:
|
||||
params = action.get("parameters") or {}
|
||||
message = params.get("message") or "Validation requise"
|
||||
queue.pop(0)
|
||||
_replay_queues[session_id] = queue
|
||||
owning_replay["status"] = "paused_need_help"
|
||||
owning_replay["pause_message"] = message
|
||||
owning_replay["failed_action"] = {
|
||||
"action_id": action.get("action_id", ""),
|
||||
"type": "pause_for_human",
|
||||
"reason": "user_request",
|
||||
}
|
||||
logger.info(
|
||||
f"Replay {owning_replay['replay_id']} pause supervisée demandée "
|
||||
f"par le workflow : {message[:80]}"
|
||||
)
|
||||
return {
|
||||
"action": None,
|
||||
"session_id": session_id,
|
||||
"machine_id": machine_id,
|
||||
"replay_paused": True,
|
||||
"pause_message": message,
|
||||
"replay_id": owning_replay["replay_id"],
|
||||
}
|
||||
|
||||
# ---- Pre-check écran (optionnel, non bloquant) ----
|
||||
# Ne s'applique qu'aux actions qui ont un from_node (actions de workflow,
|
||||
# pas les wait/retry auto-injectés ni les actions Copilot/Agent Libre)
|
||||
|
||||
Reference in New Issue
Block a user