From af13cd80ff24700d9962fc74075580cd5958f7f9 Mon Sep 17 00:00:00 2001 From: Dom Date: Tue, 5 May 2026 23:33:04 +0200 Subject: [PATCH] feat(vwb): PauseDialog + ChecklistPanel + extension PropertiesPanel pour safety_checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PauseDialog (composant nouveau) : - 2 modes selon payload : bulle simple legacy si safety_checks vide, ChecklistPanel sinon - Continuer désactivé tant que required non cochés - Badge [obligatoire] et [Léa] (avec evidence en tooltip) - POST /api/v3/replay/resume avec acknowledged_check_ids quand replay_id présent, fallback api.resumeExecution() pour la voie locale types.ts : SafetyCheck, SafetyLevel, extension Execution (pause_reason, pause_message, safety_checks, replay_id, status 'paused_need_help'). Action pause_for_human enrichie de safety_level et safety_checks dans le catalogue ACTIONS. PropertiesPanel : éditeur safety_level (dropdown standard/medical_critical) + liste éditable de safety_checks (id/label/required + ajout/suppression). App.tsx : rendu conditionnel du PauseDialog en overlay quand status == paused_need_help, ou paused avec safety_checks. Backward 100% : workflows existants sans safety_checks affichent la bulle legacy. CSS : .pause-dialog-overlay/.pause-dialog-checks/.checklist-panel/ .check-item/.badge-required/.badge-lea/.check-editor-row. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../frontend_v4/src/App.tsx | 42 ++++++ .../src/components/PauseDialog.tsx | 126 +++++++++++++++++ .../src/components/PropertiesPanel.tsx | 130 ++++++++++++++++++ .../frontend_v4/src/styles.css | 83 +++++++++++ .../frontend_v4/src/types.ts | 23 +++- 5 files changed, 402 insertions(+), 2 deletions(-) create mode 100644 visual_workflow_builder/frontend_v4/src/components/PauseDialog.tsx diff --git a/visual_workflow_builder/frontend_v4/src/App.tsx b/visual_workflow_builder/frontend_v4/src/App.tsx index ee35ed69a..b4f2c56fe 100644 --- a/visual_workflow_builder/frontend_v4/src/App.tsx +++ b/visual_workflow_builder/frontend_v4/src/App.tsx @@ -25,6 +25,7 @@ import ExecutionOverlay from './components/ExecutionOverlay'; import type { Variable } from './components/VariableManager'; import RightPanel from './components/RightPanel'; import SelfHealingDialog from './components/SelfHealingDialog'; +import PauseDialog from './components/PauseDialog'; import ConfidenceDashboard from './components/ConfidenceDashboard'; import WorkflowValidation from './components/WorkflowValidation'; import ReviewModal from './components/ReviewModal'; @@ -569,6 +570,47 @@ function App() { }} /> + {/* QW4 — Pause supervisée (safety_checks). + Affiché si le serveur renvoie status == paused_need_help, ou + status == paused avec un payload de checks. Backward 100% : si + safety_checks vide, PauseDialog rend la bulle simple legacy. */} + {(appState?.execution?.status === 'paused_need_help' || + (appState?.execution?.status === 'paused' && + (appState?.execution?.safety_checks?.length ?? 0) > 0)) && ( +
+ { + const replayId = appState.execution?.replay_id || appState.execution?.id; + if (replayId) { + // Voie streaming server (Agent V1 / replay distant) + const resp = await fetch('/api/v3/replay/resume', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + replay_id: replayId, + acknowledged_check_ids: ackIds, + }), + }); + if (!resp.ok) { + const err = await resp.json().catch(() => ({})); + throw new Error(err?.detail?.error || resp.statusText); + } + } else { + // Voie locale (execute/resume) + await api.resumeExecution(); + } + await loadState(); + }} + onCancel={() => { + handleStopExecution(); + }} + /> +
+ )} + {/* ConfidenceDashboard déplacé dans le header */} ); diff --git a/visual_workflow_builder/frontend_v4/src/components/PauseDialog.tsx b/visual_workflow_builder/frontend_v4/src/components/PauseDialog.tsx new file mode 100644 index 000000000..73b34a91b --- /dev/null +++ b/visual_workflow_builder/frontend_v4/src/components/PauseDialog.tsx @@ -0,0 +1,126 @@ +// QW4 — PauseDialog : bulle de pause supervisée avec ChecklistPanel intégré. +// +// 2 modes de rendu : +// - safety_checks vide -> bulle simple legacy (Continuer / Annuler) +// - safety_checks fournis -> ChecklistPanel ; bouton Continuer désactivé +// tant qu'un check `required` n'est pas coché. +// +// Les checks `llm_contextual` portent un badge [Léa] avec evidence en tooltip. + +import { useState, useMemo } from 'react'; +import type { SafetyCheck } from '../types'; + +interface Props { + pauseMessage: string; + pauseReason?: string; + safetyChecks: SafetyCheck[]; + onResume: (acknowledgedIds: string[]) => Promise; + onCancel: () => void; +} + +export default function PauseDialog({ + pauseMessage, + pauseReason, + safetyChecks, + onResume, + onCancel, +}: Props) { + const [checked, setChecked] = useState>({}); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(null); + + const allRequiredOK = useMemo(() => { + return safetyChecks + .filter((c) => c.required) + .every((c) => checked[c.id] === true); + }, [safetyChecks, checked]); + + const toggle = (id: string) => { + setChecked((prev) => ({ ...prev, [id]: !prev[id] })); + }; + + const handleResume = async () => { + setSubmitting(true); + setError(null); + try { + const acknowledgedIds = Object.entries(checked) + .filter(([, v]) => v) + .map(([k]) => k); + await onResume(acknowledgedIds); + } catch (e: any) { + setError(e?.message || 'Erreur lors de la reprise'); + } finally { + setSubmitting(false); + } + }; + + // Backward compat : pas de checks -> bulle simple legacy + if (safetyChecks.length === 0) { + return ( +
+

{pauseMessage}

+ {pauseReason && Raison : {pauseReason}} +
+ + +
+
+ ); + } + + return ( +
+

Pause supervisée

+

{pauseMessage}

+ {pauseReason && ( +
+ Raison : {pauseReason} +
+ )} + +
    + {safetyChecks.map((c) => ( +
  • + + {c.source === 'llm_contextual' && c.evidence && ( + -> {c.evidence} + )} +
  • + ))} +
+ + {error &&
{error}
} + +
+ + +
+
+ ); +} diff --git a/visual_workflow_builder/frontend_v4/src/components/PropertiesPanel.tsx b/visual_workflow_builder/frontend_v4/src/components/PropertiesPanel.tsx index 2711f1a94..76efa92c1 100644 --- a/visual_workflow_builder/frontend_v4/src/components/PropertiesPanel.tsx +++ b/visual_workflow_builder/frontend_v4/src/components/PropertiesPanel.tsx @@ -1353,6 +1353,136 @@ export default function PropertiesPanel({ step, onUpdateParams, onDelete }: Prop ); + case 'pause_for_human': { + // QW4 — éditeur safety_level + safety_checks (déclaratifs) + const safetyChecks = Array.isArray(params.safety_checks) + ? (params.safety_checks as Array<{ id?: string; label?: string; required?: boolean }>) + : []; + return ( + <> +
+ +