backup: snapshot post-démo GHT 2026-05-19
Some checks failed
tests / Lint (ruff + black) (push) Successful in 1m50s
tests / Tests unitaires (sans GPU) (push) Failing after 1m50s
tests / Tests sécurité (critique) (push) Has been skipped

Backup état complet après enregistrement vidéo démo de bout en bout.
À utiliser comme point de référence pour la consolidation post-démo.

Changements majeurs de la session 18-19 mai :
- AIVA-URGENCE : page autonome avec preset URL + auto-focus chain
- Workflow Demo_urgence_3_db : merge linux_db + steps AIVA + pause humaine NoMachine
- Bypass LLM (static_result / static_text) dans replay_engine
  pour démos déterministes sans appel Ollama
- Fix api_stream:3013 — replay_paused au premier polling /next
- dag_execute : lift duration_ms vers top-level pour wait runtime
- NPM bypass auth /aiva-urgence/ via location ^~ (proxy_host/10.conf hors git)
- scripts/cancel-replays.sh — workaround Stop VWB qui ne purge pas la queue

Anchors visuels (468) forcés dans le commit pour garantir restorabilité.
DB workflows actuelle + ~12 .bak DB de la journée incluses.

Sujets identifiés pour consolidation post-démo (TODO) :
1. Bug VWB recapture anchor ne régénère pas le PNG
2. Léa client accumule état mémoire (restart périodique requis)
3. Stop VWB ne purge pas la queue serveur (lien manquant vers /replay/cancel)
4. Bug coord client mss tronqué 2560x60 → mapping Y cassé
5. delay_before/delay_after ignorés au runtime (fix partiel duration_ms)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-05-19 14:55:06 +02:00
parent f2212e77e3
commit 5ea4960e65
627 changed files with 211348 additions and 169 deletions

View File

@@ -41,6 +41,7 @@ _ALLOWED_ACTION_TYPES = {
"_concat_text_vars", # Action serveur interne (générée par expansion extract_text_scroll)
"t2a_decision", # Analyse LLM facturation T2A → variable workflow
"llm_generate", # Génération texte libre côté serveur → variable workflow
"paste_and_execute", # Bypass NoMachine : ydotool Ctrl+V+Ctrl+Enter dans VM via SSH
}
# Types d'actions exécutées CÔTÉ SERVEUR (jamais transmises à l'Agent V1).
@@ -52,6 +53,7 @@ _SERVER_SIDE_ACTION_TYPES = {
"t2a_decision",
"llm_generate",
"_concat_text_vars",
"paste_and_execute",
}
# Pause par défaut entre Ctrl+End/Home et la capture suivante (ms).
@@ -890,6 +892,11 @@ def _edge_to_normalized_actions(edge, params: Dict[str, Any]) -> List[Dict[str,
}
return [normalized]
elif action_type == "paste_and_execute":
normalized["type"] = "paste_and_execute"
normalized["parameters"] = {}
return [normalized]
elif action_type == "extract_table":
normalized["type"] = "extract_table"
normalized["parameters"] = {
@@ -1216,6 +1223,18 @@ def _handle_t2a_decision_action(
dpi_text = (params.get("input_template") or params.get("dpi") or "").strip()
model = params.get("model") or None # None → DEFAULT_MODEL
# Bypass LLM : si static_result est fourni dans les params, on l'utilise
# tel quel comme résultat. Utile pour les démos déterministes (pas de
# hallucination, pas de latence, pas de truncation de prompt).
static_result = params.get("static_result")
if isinstance(static_result, dict) and static_result.get("decision"):
replay_state.setdefault("variables", {})[output_var] = static_result
logger.info(
"t2a_decision (STATIC) → variable '%s' decision=%s replay %s",
output_var, static_result.get("decision"), replay_state.get("replay_id", "?"),
)
return True
if not dpi_text:
logger.warning(
"t2a_decision : input vide — variable '%s' = {decision: 'INDETERMINE'}", output_var,
@@ -1289,6 +1308,18 @@ def _handle_llm_generate_action(
or params.get("variable_name")
or "generated_text"
).strip()
# Bypass LLM : si static_text est fourni dans les params, on l'utilise
# tel quel. Utile pour les démos déterministes.
static_text = params.get("static_text")
if isinstance(static_text, str) and static_text.strip():
replay_state.setdefault("variables", {})[output_var] = static_text
logger.info(
"llm_generate (STATIC) → variable '%s' (%d chars) replay %s",
output_var, len(static_text), replay_state.get("replay_id", "?"),
)
return True
prompt = str(params.get("prompt") or "").strip()
context = str(params.get("context") or "")
model = params.get("model") or None
@@ -1384,6 +1415,40 @@ def _handle_concat_text_vars_action(
return bool(merged)
def _handle_paste_and_execute_action(
action: Dict[str, Any],
replay_state: Dict[str, Any],
) -> bool:
"""Action serveur : invoque scripts/paste_and_execute_linuxdb.sh pour
déclencher Ctrl+V + Ctrl+Enter dans DBeaver de la VM via ydotool.
Bypasse Léa/NoMachine (Ctrl mangé par NoMachine passive grab).
Cf. handoff 2026-05-16_handoff_ydotool_clipboard.md.
"""
import subprocess
script_path = "/home/dom/ai/rpa_vision_v3/scripts/paste_and_execute_linuxdb.sh"
try:
result = subprocess.run(
[script_path],
timeout=30,
capture_output=True,
text=True,
)
if result.returncode != 0:
logger.warning(
"paste_and_execute échoué (rc=%d) stderr=%s",
result.returncode, (result.stderr or "")[:500],
)
return False
logger.info("paste_and_execute OK replay %s", replay_state.get("replay_id", "?"))
return True
except subprocess.TimeoutExpired:
logger.warning("paste_and_execute timeout (30s)")
return False
except Exception as e:
logger.warning("paste_and_execute exception : %s", e)
return False
def _expand_extract_text_scroll(
base: Dict[str, Any],
final_var: str,